1
0
mirror of https://github.com/chirpstack/chirpstack.git synced 2025-04-19 23:22:55 +00:00

Compare commits

..

44 Commits

Author SHA1 Message Date
4968f5d792 Bump version to 4.8.1 2024-05-13 16:47:14 +01:00
36bbf8f153 api: Fix empty JS packages.
NPM uses the .gitignore file, which includes the generated JS code. This
caused the v4.8.0 JS packages to be completely empty. Adding an empty
.npmignore file resolves the issue as if a .npmignore file is detected,
the .gitignore file is ignored.
2024-05-13 16:32:59 +01:00
c3de89a4a4 Bump version to 4.8.0 2024-05-13 15:35:00 +01:00
73ad79a356 Bump version to 4.8.0-test.5 2024-05-09 13:45:24 +01:00
a65ac0077e Update Rust to v1.78. 2024-05-09 13:17:36 +01:00
370b84cb09 Revert "Update dependencies."
This reverts commit f475e4f64222adad346e321db75e611a2df39009. One of the
updated dependencies introduces aws-lc-rs as dependency, which fails to
build on ARMv7.
2024-05-09 13:02:59 +01:00
d03870ff11 Bump version to 4.8.0-test.4 2024-05-09 11:22:22 +01:00
f62d8b325c Revert "Use mold linker for development."
This reverts commit 4f1a1bbce56a421afbc8ade3d84530d9f751bf1e. This was
intended to improve the development speed, but it breaks the build. This
is because even when cross-compiling, the Rust compiler does compile
some code for the host target for code-generation.
2024-05-09 10:53:50 +01:00
e0a9e52cca Bump version to 4.8.0-test.3 2024-05-09 08:48:43 +01:00
b7dad2a7d0 Add RP002-1.0.4 option to device-profile. 2024-05-08 15:05:14 +01:00
2e65c5baa9 Set CARGO_INCREMENTAL=0 for CI builds. 2024-05-08 14:12:04 +01:00
640a0f348e Fix formatting after cargo clippy --fix. 2024-05-08 12:09:30 +01:00
057aed7f50 Fix cargo clippy warnings. 2024-05-08 11:53:34 +01:00
66d0ec2f6f Speed up cargo check.
See also:
https://corrode.dev/blog/tips-for-faster-rust-compile-times/#avoid-procedural-macro-crates.
2024-05-08 11:46:35 +01:00
f475e4f642 Update dependencies. 2024-05-08 10:59:27 +01:00
17ae49ca7d Bump ejs from 3.1.9 to 3.1.10 in /ui ()
Bumps [ejs](https://github.com/mde/ejs) from 3.1.9 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](https://github.com/mde/ejs/compare/v3.1.9...v3.1.10)

---
updated-dependencies:
- dependency-name: ejs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-08 10:56:22 +01:00
18e56957e2 lrwn: Validate max payload sizes for RP002-1.0.4. 2024-05-08 10:41:42 +01:00
99dbefdfec Add support for handling HomeNSReq requests.
In this case, we return the (primary) NetID of the ChirpStack instance.
Supporting the HomeNSReq is useful in the case the roaming ChirpStack
instances are setup to resolve the servers using DNS. In case of OTAA,
the fNS resolves the JoinEUI to a hostname to which it must make the
HomeNSReq. This returns the HNetID, which then can be used to resolve
the hostname of the hNS to which the join-request must be forwarded.
2024-05-07 15:55:37 +01:00
26832f3a5f api: add --depth=1 to googleapis and protobuf clones () 2024-05-07 12:21:14 +01:00
40cfa3ddf2 Update rquickjs dependency. 2024-05-07 12:15:24 +01:00
ed9a9b0c88 Exclude disabled devices from Class-B/C scheduler.
Closes .
2024-05-07 09:31:53 +01:00
4f1a1bbce5 Use mold linker for development.
This speeds up check and build times.
2024-05-03 16:32:06 +01:00
c51653d1bc Fix loading options (onFocus > onDropdownVisibleChange).
onFocus is only triggered when when dropdown doesn't have focus where
onDropdownVisibleChange is always triggered when opening / closing the
dropdown.

Closes .
2024-05-03 12:42:33 +01:00
3ec9ee2031 Convert Local to NaiveDateTime before calculating intervals.
This makes it a lot easier to iterate over the intervals, as we no
longer have to take into account DST changes that could either result in
an invalid or ambiguous date, or not incrementing by the expected
interval. E.g. incrementing by 1 day resulting in a 23 hour increment
because or DST change.

On returning the metrics, we try to convert the NaiveDateTime back into
a DateTime<Local>, failing that, we skip it rather than failing on it.

Closes .
2024-05-03 12:25:51 +01:00
5f6ccc35fb Auto-detect if MQTT broker supports shared-subscriptions.
Closes .
2024-05-02 14:12:43 +01:00
0d11ad609a Bump golang.org/x/net from 0.22.0 to 0.23.0 in /api/go ()
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 10:39:09 +01:00
fed94a9b75 Bump golang.org/x/net from 0.19.0 to 0.23.0 in /examples/frame_log/go ()
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.19.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.19.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 10:38:59 +01:00
13e26977f4 Bump golang.org/x/net from 0.19.0 to 0.23.0 in /examples/request_log/go ()
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.19.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.19.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 10:38:49 +01:00
0b665c7a11 Bump protobufjs from 7.2.4 to 7.2.6 in /api/js ()
Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 7.2.4 to 7.2.6.
- [Release notes](https://github.com/protobufjs/protobuf.js/releases)
- [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/protobufjs/protobuf.js/compare/protobufjs-v7.2.4...protobufjs-v7.2.6)

---
updated-dependencies:
- dependency-name: protobufjs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 10:38:29 +01:00
3ee4598320 Bump tar from 6.1.15 to 6.2.1 in /api/grpc-web ()
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 10:38:15 +01:00
8adc80bf4e Bump tar from 6.1.15 to 6.2.1 in /api/js ()
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 10:38:04 +01:00
487f785302 Add option to increase RX1 Delay in device-profile.
In general ChirpStack is configured with a rx1_delay configuration
matching the network latency (e.g. on cellular, one might want to set
this to rx1_delay=3). However, this does not take into account a
possible latency of the end-application. Handling the uplink and
enqueueing a downlink might take more time than the configured rx1_delay
(and get_downlink_data_delay) allows.

This option makes it possible to increase the RX1 Delay in the
device-profile. If the RX1 Delay has between increased relative to
the system default, then the get_downlink_data_delay will be
incremented with the same amount of seconds.
2024-05-02 10:29:10 +01:00
a5ff416fa2 Implement storing + display duty-cycle stats.
Note that these stats are only available for Concentratord based
gateways as these metrics must be aggregated by the gateway.
2024-04-17 14:34:11 +01:00
2889da37c2 Exclude offline gateways from multicast scheduling. 2024-04-15 14:09:38 +01:00
378b314a32 Update rcgen dependency + cargo update. 2024-04-10 11:52:23 +01:00
9207effb5b Bump version to 4.8.0-test.2 2024-04-10 10:12:06 +01:00
6ef100c9b7 api: Add back rust/proto directory.
This needs to be tracked by git to make cargo publish work. If this
folder is in the .gitignore, then cargo publish will ignore this folder
as well and the publish command will fail because of missing .proto
files. If we would temporarily remove / rename the .gitignore file, then
cargo publish will error because the git state is dirty.
2024-04-04 15:55:59 +01:00
b3ba23f32c Bump version to 4.8.0-test.1 2024-04-04 15:25:59 +01:00
f283261636 api: Remove Python proto folder.
There is no point in tracking this folder in git, as on each make we
will copy it from the original api/proto folder.
2024-04-04 14:30:03 +01:00
ac9923e120 api: Add duty-cycle stats messages + types. 2024-04-04 14:28:24 +01:00
2bb05fba58 Update dependencies. 2024-04-02 11:40:47 +01:00
161cd9008c Fix multicast class-b ping-slot migration in case value is 0. 2024-04-02 09:25:58 +01:00
c238e8f6da api: Use protoc-gen-go & -go-grpc from nix. 2024-04-01 14:37:41 +01:00
d170c7dd79 api: Remove generated API files from repo + update build.
All these files can be generated using the `make api` command and there
is no real need to commit these into the repo. Only the api/go files
need to be comitted of how the Go import system works.

This also updates the Rust, Go, JS and gRPC-web (JS) code generation and
UI build to use the nix-shell environment instead of using Docker.
2024-04-01 14:27:15 +01:00
131 changed files with 4051 additions and 1865 deletions
.cargo
.github/workflows
.gitignoreCargo.lockCargo.tomlMakefile
api
backend
chirpstack-integration
chirpstack
examples
frame_log/go
request_log/go
lrwn-filters
lrwn
rust-toolchain.tomlshell.nix
ui

@ -6,3 +6,6 @@ rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-s", "-C", "lin
[target.armv7-unknown-linux-musleabihf]
rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-s", "-C", "link-arg=-lc", "-C", "link-arg=-lgcc"]
[profile.dev.build-override]
opt-level = 3

@ -7,6 +7,9 @@ on:
- 'v*'
pull_request:
env:
CARGO_INCREMENTAL: 0
jobs:
tests:
runs-on: ubuntu-latest

1
.gitignore vendored

@ -3,6 +3,7 @@
!/chirpstack/.rpm
!/.cargo
!/.env
!.npmignore
# Log files
*.log

886
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -15,10 +15,5 @@ lto = true
codegen-units = 1
[patch.crates-io]
# Remove if diesel > 2.1.4
diesel = { git = "https://github.com/diesel-rs/diesel.git", rev = "566dcccc6df6adb6ceddef8df5e1806e2a065c40" }
# Remove if diesel-async > 0.4.1
diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "017ebe2fb7a2709ab5db92148dea5ce812a35e09" }
deadpool-redis = { git = "http://github.com/brocaar/deadpool.git", branch = "update_redis" }

@ -8,7 +8,7 @@ dist:
# Install dev dependencies
dev-dependencies:
cargo install cross --version 0.2.5
cargo install diesel_cli --version 2.1.0 --no-default-features --features postgres
cargo install diesel_cli --version 2.1.1 --no-default-features --features postgres
cargo install cargo-deb --version 1.43.1
cargo install cargo-generate-rpm --version 0.12.1

@ -3,7 +3,7 @@ FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
ENV PROJECT_PATH=/chirpstack/api
RUN apt update && apt install -y make git
RUN git clone --depth=1 --branch=master https://github.com/googleapis/googleapis.git /googleapis
RUN git clone --depth=1 https://github.com/googleapis/googleapis.git /googleapis
RUN mkdir -p /googleproto/google/api/
RUN mv /googleapis/google/api/http.proto /googleproto/google/api/ && mv /googleapis/google/api/annotations.proto /googleproto/google/api/
RUN rm -rf /googleapis

2
api/Dockerfile-md vendored

@ -3,7 +3,7 @@ FROM golang:1.19.3-alpine
ENV PROJECT_PATH=/chirpstack/api
RUN apk add --no-cache make git bash protobuf protobuf-dev
RUN git clone https://github.com/googleapis/googleapis.git /googleapis
RUN git clone --depth=1 https://github.com/googleapis/googleapis.git /googleapis
RUN mkdir -p $PROJECT_PATH
WORKDIR $PROJECT_PATH

@ -2,8 +2,8 @@ FROM python:3.11.0
ENV PROJECT_PATH=/chirpstack/api
RUN git clone https://github.com/protocolbuffers/protobuf.git /protobuf
RUN git clone https://github.com/googleapis/googleapis.git /googleapis
RUN git clone --depth=1 https://github.com/protocolbuffers/protobuf.git /protobuf
RUN git clone --depth=1 https://github.com/googleapis/googleapis.git /googleapis
RUN mkdir -p PROJECT_PATH
WORKDIR $PROJECT_PATH

5
api/go/Makefile vendored

@ -4,11 +4,6 @@ PROTOC_ARGS := -I=../proto --go_out=. --go_opt=paths=source_relative --go-grpc_o
all: requirements common gw api integration stream
requirements:
go mod download
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
common:
protoc ${PROTOC_ARGS} common/common.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/application.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/device.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/device_profile.proto
@ -544,6 +544,14 @@ type DeviceProfile struct {
//
// If set to true, it means that the device is allowed to use roaming.
AllowRoaming bool `protobuf:"varint,52,opt,name=allow_roaming,json=allowRoaming,proto3" json:"allow_roaming,omitempty"`
// RX1 Delay.
//
// This makes it possible to override the system RX1 Delay. Please note that
// this values only has effect in case it is higher than the system value.
// In other words, it can be used to increase the RX1 Delay but not to decrease
// it.
// Valid options are 1 - 15 (0 = always use system RX1 Delay).
Rx1Delay uint32 `protobuf:"varint,53,opt,name=rx1_delay,json=rx1Delay,proto3" json:"rx1_delay,omitempty"`
}
func (x *DeviceProfile) Reset() {
@ -942,6 +950,13 @@ func (x *DeviceProfile) GetAllowRoaming() bool {
return false
}
func (x *DeviceProfile) GetRx1Delay() uint32 {
if x != nil {
return x.Rx1Delay
}
return 0
}
type Measurement struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -1692,7 +1707,7 @@ var file_api_device_profile_proto_rawDesc = []byte{
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f,
0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x22, 0xd3, 0x16, 0x0a, 0x0d, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x22, 0xf0, 0x16, 0x0a, 0x0d, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12,
@ -1864,187 +1879,189 @@ var file_api_device_profile_proto_rawDesc = []byte{
0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x75, 0x63, 0x6b, 0x65,
0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x72,
0x6f, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x34, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c,
0x6c, 0x6f, 0x77, 0x52, 0x6f, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61,
0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
0x02, 0x38, 0x01, 0x1a, 0x51, 0x0a, 0x11, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x0b, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x6b, 0x69, 0x6e,
0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65,
0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b,
0x69, 0x6e, 0x64, 0x22, 0xd2, 0x03, 0x0a, 0x15, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a,
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x64, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f,
0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12,
0x33, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61,
0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x49, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x5f, 0x70, 0x61, 0x72, 0x61,
0x6d, 0x73, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x67, 0x50, 0x61,
0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x72, 0x65,
0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12,
0x23, 0x0a, 0x0d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x6f, 0x74, 0x61, 0x61,
0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73,
0x4f, 0x74, 0x61, 0x61, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73,
0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e,
0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x12, 0x28,
0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73,
0x5f, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
0x74, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x22, 0x57, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x52, 0x0d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x65, 0x22, 0x2d, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63,
0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x22, 0x29, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f,
0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0xcb, 0x01, 0x0a, 0x18,
0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0e, 0x64, 0x65, 0x76, 0x69,
0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f,
0x66, 0x69, 0x6c, 0x65, 0x52, 0x0d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66,
0x69, 0x6c, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39,
0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x57, 0x0a, 0x1a, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0e, 0x64, 0x65, 0x76, 0x69, 0x63,
0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66,
0x69, 0x6c, 0x65, 0x52, 0x0d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x22, 0x2c, 0x0a, 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69,
0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x22, 0x7e, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a,
0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69,
0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73,
0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x61,
0x72, 0x63, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64,
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x22, 0x71, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f,
0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
0x32, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66,
0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x22, 0x7c, 0x0a, 0x26, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63,
0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72,
0x69, 0x74, 0x68, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a,
0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x31,
0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
0x6d, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x22, 0x3a, 0x0a, 0x14, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
0x6d, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x31, 0x0a,
0x0c, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x08, 0x0a,
0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x41, 0x59, 0x45, 0x4e,
0x4e, 0x45, 0x5f, 0x4c, 0x50, 0x50, 0x10, 0x01, 0x12, 0x06, 0x0a, 0x02, 0x4a, 0x53, 0x10, 0x02,
0x2a, 0x50, 0x0a, 0x0f, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b,
0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00,
0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0c, 0x0a,
0x08, 0x41, 0x42, 0x53, 0x4f, 0x4c, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x47,
0x41, 0x55, 0x47, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47,
0x10, 0x04, 0x2a, 0x55, 0x0a, 0x0e, 0x43, 0x61, 0x64, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69,
0x63, 0x69, 0x74, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x45, 0x43, 0x5f, 0x31, 0x10, 0x00, 0x12,
0x0a, 0x0a, 0x06, 0x4d, 0x53, 0x5f, 0x35, 0x30, 0x30, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d,
0x53, 0x5f, 0x32, 0x35, 0x30, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x53, 0x5f, 0x31, 0x30,
0x30, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x53, 0x5f, 0x35, 0x30, 0x10, 0x04, 0x12, 0x09,
0x0a, 0x05, 0x4d, 0x53, 0x5f, 0x32, 0x30, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x11, 0x53, 0x65, 0x63,
0x6f, 0x6e, 0x64, 0x43, 0x68, 0x41, 0x63, 0x6b, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x09,
0x0a, 0x05, 0x4b, 0x48, 0x5a, 0x5f, 0x30, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x48, 0x5a,
0x5f, 0x32, 0x30, 0x30, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x48, 0x5a, 0x5f, 0x34, 0x30,
0x30, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x48, 0x5a, 0x5f, 0x38, 0x30, 0x30, 0x10, 0x03,
0x12, 0x0c, 0x0a, 0x08, 0x4b, 0x48, 0x5a, 0x5f, 0x31, 0x36, 0x30, 0x30, 0x10, 0x04, 0x12, 0x0c,
0x0a, 0x08, 0x4b, 0x48, 0x5a, 0x5f, 0x33, 0x32, 0x30, 0x30, 0x10, 0x05, 0x2a, 0x6c, 0x0a, 0x13,
0x52, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x52,
0x45, 0x4c, 0x41, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x45,
0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x52, 0x45, 0x4c, 0x41, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45,
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x10, 0x02, 0x12,
0x19, 0x0a, 0x15, 0x45, 0x4e, 0x44, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x43, 0x4f,
0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x32, 0xb8, 0x05, 0x0a, 0x14, 0x44,
0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x12, 0x6c, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63,
0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x73, 0x12, 0x65, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47,
0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x76, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44,
0x6c, 0x6f, 0x77, 0x52, 0x6f, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x78,
0x31, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x35, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72,
0x78, 0x31, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
0x1a, 0x51, 0x0a, 0x11, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x61,
0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x0b, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x61, 0x73, 0x75,
0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64,
0x22, 0xd2, 0x03, 0x0a, 0x15, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72,
0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x05,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65,
0x67, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x0b,
0x6d, 0x61, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x63, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x49, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f,
0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d,
0x73, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x72, 0x65, 0x67, 0x50, 0x61,
0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d,
0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x6f, 0x74, 0x61, 0x61, 0x18, 0x08, 0x20,
0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x4f, 0x74, 0x61,
0x61, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x63, 0x6c,
0x61, 0x73, 0x73, 0x5f, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x75, 0x70,
0x70, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x12, 0x28, 0x0a, 0x10, 0x73,
0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x63, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x43,
0x6c, 0x61, 0x73, 0x73, 0x43, 0x22, 0x57, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44,
0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x33, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x2d, 0x3a, 0x01, 0x2a, 0x1a, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76,
0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65,
0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x69, 0x64, 0x7d,
0x12, 0x64, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f,
0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x65, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1e,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65,
0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x85, 0x01,
0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
0x68, 0x6d, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2b, 0x2e, 0x61, 0x70,
0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52,
0x0d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x2d,
0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x29, 0x0a,
0x17, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0xcb, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f,
0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x65, 0x52, 0x0d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x57, 0x0a, 0x1a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x52, 0x0d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22,
0x2c, 0x0a, 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x7e, 0x0a,
0x19, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69,
0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x61, 0x72,
0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x71, 0x0a,
0x1a, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74,
0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x06,
0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
0x22, 0x7c, 0x0a, 0x26, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f,
0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x31, 0x0a, 0x06, 0x72,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x4c, 0x69,
0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3a,
0x0a, 0x14, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x4c, 0x69,
0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x31, 0x0a, 0x0c, 0x43, 0x6f,
0x64, 0x65, 0x63, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f,
0x4e, 0x45, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x41, 0x59, 0x45, 0x4e, 0x4e, 0x45, 0x5f,
0x4c, 0x50, 0x50, 0x10, 0x01, 0x12, 0x06, 0x0a, 0x02, 0x4a, 0x53, 0x10, 0x02, 0x2a, 0x50, 0x0a,
0x0f, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x69, 0x6e, 0x64,
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a,
0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x42,
0x53, 0x4f, 0x4c, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47,
0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x2a,
0x55, 0x0a, 0x0e, 0x43, 0x61, 0x64, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x69, 0x74,
0x79, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x45, 0x43, 0x5f, 0x31, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
0x4d, 0x53, 0x5f, 0x35, 0x30, 0x30, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x53, 0x5f, 0x32,
0x35, 0x30, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x53, 0x5f, 0x31, 0x30, 0x30, 0x10, 0x03,
0x12, 0x09, 0x0a, 0x05, 0x4d, 0x53, 0x5f, 0x35, 0x30, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4d,
0x53, 0x5f, 0x32, 0x30, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x11, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64,
0x43, 0x68, 0x41, 0x63, 0x6b, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x09, 0x0a, 0x05, 0x4b,
0x48, 0x5a, 0x5f, 0x30, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x48, 0x5a, 0x5f, 0x32, 0x30,
0x30, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x48, 0x5a, 0x5f, 0x34, 0x30, 0x30, 0x10, 0x02,
0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x48, 0x5a, 0x5f, 0x38, 0x30, 0x30, 0x10, 0x03, 0x12, 0x0c, 0x0a,
0x08, 0x4b, 0x48, 0x5a, 0x5f, 0x31, 0x36, 0x30, 0x30, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x4b,
0x48, 0x5a, 0x5f, 0x33, 0x32, 0x30, 0x30, 0x10, 0x05, 0x2a, 0x6c, 0x0a, 0x13, 0x52, 0x65, 0x6c,
0x61, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x16, 0x0a, 0x12, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x52, 0x45, 0x4c, 0x41,
0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x45, 0x4e, 0x41, 0x42,
0x4c, 0x45, 0x5f, 0x52, 0x45, 0x4c, 0x41, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12,
0x0b, 0x0a, 0x07, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15,
0x45, 0x4e, 0x44, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52,
0x4f, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x32, 0xb8, 0x05, 0x0a, 0x14, 0x44, 0x65, 0x76, 0x69,
0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0x6c, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f,
0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64,
0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x65,
0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44,
0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76,
0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73,
0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x76, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12,
0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69,
0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d,
0x3a, 0x01, 0x2a, 0x1a, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63,
0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a,
0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64,
0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x7b,
0x69, 0x64, 0x7d, 0x12, 0x65, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66,
0x69, 0x6c, 0x65, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25,
0x12, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x72, 0x2d, 0x61, 0x6c, 0x67, 0x6f, 0x72,
0x69, 0x74, 0x68, 0x6d, 0x73, 0x42, 0x6a, 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72,
0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x12, 0x44, 0x65, 0x76, 0x69,
0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x69,
0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61,
0x63, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x61, 0x70, 0x69,
0xaa, 0x02, 0x0e, 0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x41, 0x70,
0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66,
0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63,
0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x11, 0x4c,
0x69, 0x73, 0x74, 0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x73,
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x41, 0x64, 0x72, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x70, 0x72, 0x6f, 0x66, 0x69,
0x6c, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x72, 0x2d, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
0x6d, 0x73, 0x42, 0x6a, 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74,
0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73,
0x74, 0x61, 0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x61, 0x70, 0x69, 0xaa, 0x02, 0x0e,
0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x41, 0x70, 0x69, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/device_profile_template.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/gateway.proto
@ -1029,6 +1029,129 @@ func (x *GetGatewayMetricsResponse) GetTxPacketsPerStatus() *common.Metric {
return nil
}
type GetGatewayDutyCycleMetricsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Gateway ID (EUI64).
GatewayId string `protobuf:"bytes,1,opt,name=gateway_id,json=gatewayId,proto3" json:"gateway_id,omitempty"`
// Interval start timestamp.
Start *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start,proto3" json:"start,omitempty"`
// Interval end timestamp.
End *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=end,proto3" json:"end,omitempty"`
}
func (x *GetGatewayDutyCycleMetricsRequest) Reset() {
*x = GetGatewayDutyCycleMetricsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_gateway_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetGatewayDutyCycleMetricsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetGatewayDutyCycleMetricsRequest) ProtoMessage() {}
func (x *GetGatewayDutyCycleMetricsRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_gateway_proto_msgTypes[13]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetGatewayDutyCycleMetricsRequest.ProtoReflect.Descriptor instead.
func (*GetGatewayDutyCycleMetricsRequest) Descriptor() ([]byte, []int) {
return file_api_gateway_proto_rawDescGZIP(), []int{13}
}
func (x *GetGatewayDutyCycleMetricsRequest) GetGatewayId() string {
if x != nil {
return x.GatewayId
}
return ""
}
func (x *GetGatewayDutyCycleMetricsRequest) GetStart() *timestamppb.Timestamp {
if x != nil {
return x.Start
}
return nil
}
func (x *GetGatewayDutyCycleMetricsRequest) GetEnd() *timestamppb.Timestamp {
if x != nil {
return x.End
}
return nil
}
type GetGatewayDutyCycleMetricsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Percentage relative to max load.
MaxLoadPercentage *common.Metric `protobuf:"bytes,1,opt,name=max_load_percentage,json=maxLoadPercentage,proto3" json:"max_load_percentage,omitempty"`
// Percentage relative to tracking window.
WindowPercentage *common.Metric `protobuf:"bytes,2,opt,name=window_percentage,json=windowPercentage,proto3" json:"window_percentage,omitempty"`
}
func (x *GetGatewayDutyCycleMetricsResponse) Reset() {
*x = GetGatewayDutyCycleMetricsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_gateway_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetGatewayDutyCycleMetricsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetGatewayDutyCycleMetricsResponse) ProtoMessage() {}
func (x *GetGatewayDutyCycleMetricsResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_gateway_proto_msgTypes[14]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetGatewayDutyCycleMetricsResponse.ProtoReflect.Descriptor instead.
func (*GetGatewayDutyCycleMetricsResponse) Descriptor() ([]byte, []int) {
return file_api_gateway_proto_rawDescGZIP(), []int{14}
}
func (x *GetGatewayDutyCycleMetricsResponse) GetMaxLoadPercentage() *common.Metric {
if x != nil {
return x.MaxLoadPercentage
}
return nil
}
func (x *GetGatewayDutyCycleMetricsResponse) GetWindowPercentage() *common.Metric {
if x != nil {
return x.WindowPercentage
}
return nil
}
var File_api_gateway_proto protoreflect.FileDescriptor
var file_api_gateway_proto_rawDesc = []byte{
@ -1203,67 +1326,98 @@ var file_api_gateway_proto_rawDesc = []byte{
0x74, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x12, 0x74, 0x78, 0x50,
0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x50, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2a,
0x37, 0x0a, 0x0c, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
0x0e, 0x0a, 0x0a, 0x4e, 0x45, 0x56, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x45, 0x4e, 0x10, 0x00, 0x12,
0x0a, 0x0a, 0x06, 0x4f, 0x4e, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4f,
0x46, 0x46, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x02, 0x32, 0x91, 0x06, 0x0a, 0x0e, 0x47, 0x61, 0x74,
0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x06, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12,
0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
0x79, 0x73, 0x12, 0x5a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77,
0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6a,
0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x27, 0x3a, 0x01, 0x2a, 0x1a, 0x22, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74,
0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x67,
0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x5f, 0x0a, 0x06, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x2a,
0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b,
0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x52, 0x0a, 0x04, 0x4c,
0x69, 0x73, 0x74, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61,
0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f,
0x12, 0x0d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12,
0xb1, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65,
0x77, 0x61, 0x79, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61,
0x79, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x31, 0x22, 0x2f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67,
0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x2d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x65, 0x12, 0x77, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
0x73, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77,
0x61, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61,
0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67,
0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x50, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
0xa2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x44, 0x75,
0x74, 0x79, 0x43, 0x79, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77,
0x61, 0x79, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x03, 0x65, 0x6e, 0x64, 0x22, 0xa1, 0x01, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65,
0x77, 0x61, 0x79, 0x44, 0x75, 0x74, 0x79, 0x43, 0x79, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x72,
0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x13, 0x6d,
0x61, 0x78, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61,
0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x11, 0x6d, 0x61, 0x78, 0x4c, 0x6f, 0x61,
0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x11, 0x77,
0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x50, 0x65,
0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x2a, 0x37, 0x0a, 0x0c, 0x47, 0x61, 0x74, 0x65,
0x77, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x45, 0x56, 0x45,
0x52, 0x5f, 0x53, 0x45, 0x45, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4f, 0x4e, 0x4c, 0x49,
0x4e, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x46, 0x46, 0x4c, 0x49, 0x4e, 0x45, 0x10,
0x02, 0x32, 0xb1, 0x07, 0x0a, 0x0e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x19,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77,
0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x5a, 0x0a, 0x03, 0x47,
0x65, 0x74, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65,
0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65,
0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6a, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x61,
0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x3a, 0x01, 0x2a, 0x1a,
0x22, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b,
0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f,
0x69, 0x64, 0x7d, 0x12, 0x5f, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x19, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61,
0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x2a, 0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67,
0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x64, 0x0a, 0x11,
0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70,
0x69, 0x42, 0x0c, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50,
0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68,
0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74,
0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x61, 0x70,
0x69, 0xaa, 0x02, 0x0e, 0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x41,
0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x5f, 0x69, 0x64, 0x7d, 0x12, 0x52, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73,
0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0xb1, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x6e,
0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69,
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x6e,
0x65, 0x72, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72,
0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x22, 0x2f, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65,
0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65,
0x2d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x77, 0x0a, 0x0a,
0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69,
0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x24, 0x12, 0x22, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73,
0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, 0x65,
0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x44, 0x75, 0x74,
0x79, 0x43, 0x79, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x26, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x44, 0x75,
0x74, 0x79, 0x43, 0x79, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x47,
0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x44, 0x75, 0x74, 0x79, 0x43, 0x79, 0x63, 0x6c, 0x65, 0x4d,
0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x61, 0x74,
0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69,
0x64, 0x7d, 0x2f, 0x64, 0x75, 0x74, 0x79, 0x2d, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x2d, 0x6d, 0x65,
0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x64, 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72,
0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x0c, 0x47, 0x61, 0x74, 0x65,
0x77, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63,
0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x61, 0x70, 0x69, 0xaa, 0x02, 0x0e, 0x43, 0x68, 0x69,
0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x41, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
@ -1279,7 +1433,7 @@ func file_api_gateway_proto_rawDescGZIP() []byte {
}
var file_api_gateway_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_api_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
var file_api_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
var file_api_gateway_proto_goTypes = []interface{}{
(GatewayState)(0), // 0: api.GatewayState
(*Gateway)(nil), // 1: api.Gateway
@ -1295,62 +1449,70 @@ var file_api_gateway_proto_goTypes = []interface{}{
(*GenerateGatewayClientCertificateResponse)(nil), // 11: api.GenerateGatewayClientCertificateResponse
(*GetGatewayMetricsRequest)(nil), // 12: api.GetGatewayMetricsRequest
(*GetGatewayMetricsResponse)(nil), // 13: api.GetGatewayMetricsResponse
nil, // 14: api.Gateway.TagsEntry
nil, // 15: api.Gateway.MetadataEntry
nil, // 16: api.GatewayListItem.PropertiesEntry
(*common.Location)(nil), // 17: common.Location
(*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp
(common.Aggregation)(0), // 19: common.Aggregation
(*common.Metric)(nil), // 20: common.Metric
(*emptypb.Empty)(nil), // 21: google.protobuf.Empty
(*GetGatewayDutyCycleMetricsRequest)(nil), // 14: api.GetGatewayDutyCycleMetricsRequest
(*GetGatewayDutyCycleMetricsResponse)(nil), // 15: api.GetGatewayDutyCycleMetricsResponse
nil, // 16: api.Gateway.TagsEntry
nil, // 17: api.Gateway.MetadataEntry
nil, // 18: api.GatewayListItem.PropertiesEntry
(*common.Location)(nil), // 19: common.Location
(*timestamppb.Timestamp)(nil), // 20: google.protobuf.Timestamp
(common.Aggregation)(0), // 21: common.Aggregation
(*common.Metric)(nil), // 22: common.Metric
(*emptypb.Empty)(nil), // 23: google.protobuf.Empty
}
var file_api_gateway_proto_depIdxs = []int32{
17, // 0: api.Gateway.location:type_name -> common.Location
14, // 1: api.Gateway.tags:type_name -> api.Gateway.TagsEntry
15, // 2: api.Gateway.metadata:type_name -> api.Gateway.MetadataEntry
17, // 3: api.GatewayListItem.location:type_name -> common.Location
16, // 4: api.GatewayListItem.properties:type_name -> api.GatewayListItem.PropertiesEntry
18, // 5: api.GatewayListItem.created_at:type_name -> google.protobuf.Timestamp
18, // 6: api.GatewayListItem.updated_at:type_name -> google.protobuf.Timestamp
18, // 7: api.GatewayListItem.last_seen_at:type_name -> google.protobuf.Timestamp
19, // 0: api.Gateway.location:type_name -> common.Location
16, // 1: api.Gateway.tags:type_name -> api.Gateway.TagsEntry
17, // 2: api.Gateway.metadata:type_name -> api.Gateway.MetadataEntry
19, // 3: api.GatewayListItem.location:type_name -> common.Location
18, // 4: api.GatewayListItem.properties:type_name -> api.GatewayListItem.PropertiesEntry
20, // 5: api.GatewayListItem.created_at:type_name -> google.protobuf.Timestamp
20, // 6: api.GatewayListItem.updated_at:type_name -> google.protobuf.Timestamp
20, // 7: api.GatewayListItem.last_seen_at:type_name -> google.protobuf.Timestamp
0, // 8: api.GatewayListItem.state:type_name -> api.GatewayState
1, // 9: api.CreateGatewayRequest.gateway:type_name -> api.Gateway
1, // 10: api.GetGatewayResponse.gateway:type_name -> api.Gateway
18, // 11: api.GetGatewayResponse.created_at:type_name -> google.protobuf.Timestamp
18, // 12: api.GetGatewayResponse.updated_at:type_name -> google.protobuf.Timestamp
18, // 13: api.GetGatewayResponse.last_seen_at:type_name -> google.protobuf.Timestamp
20, // 11: api.GetGatewayResponse.created_at:type_name -> google.protobuf.Timestamp
20, // 12: api.GetGatewayResponse.updated_at:type_name -> google.protobuf.Timestamp
20, // 13: api.GetGatewayResponse.last_seen_at:type_name -> google.protobuf.Timestamp
1, // 14: api.UpdateGatewayRequest.gateway:type_name -> api.Gateway
2, // 15: api.ListGatewaysResponse.result:type_name -> api.GatewayListItem
18, // 16: api.GenerateGatewayClientCertificateResponse.expires_at:type_name -> google.protobuf.Timestamp
18, // 17: api.GetGatewayMetricsRequest.start:type_name -> google.protobuf.Timestamp
18, // 18: api.GetGatewayMetricsRequest.end:type_name -> google.protobuf.Timestamp
19, // 19: api.GetGatewayMetricsRequest.aggregation:type_name -> common.Aggregation
20, // 20: api.GetGatewayMetricsResponse.rx_packets:type_name -> common.Metric
20, // 21: api.GetGatewayMetricsResponse.tx_packets:type_name -> common.Metric
20, // 22: api.GetGatewayMetricsResponse.tx_packets_per_freq:type_name -> common.Metric
20, // 23: api.GetGatewayMetricsResponse.rx_packets_per_freq:type_name -> common.Metric
20, // 24: api.GetGatewayMetricsResponse.tx_packets_per_dr:type_name -> common.Metric
20, // 25: api.GetGatewayMetricsResponse.rx_packets_per_dr:type_name -> common.Metric
20, // 26: api.GetGatewayMetricsResponse.tx_packets_per_status:type_name -> common.Metric
3, // 27: api.GatewayService.Create:input_type -> api.CreateGatewayRequest
4, // 28: api.GatewayService.Get:input_type -> api.GetGatewayRequest
6, // 29: api.GatewayService.Update:input_type -> api.UpdateGatewayRequest
7, // 30: api.GatewayService.Delete:input_type -> api.DeleteGatewayRequest
8, // 31: api.GatewayService.List:input_type -> api.ListGatewaysRequest
10, // 32: api.GatewayService.GenerateClientCertificate:input_type -> api.GenerateGatewayClientCertificateRequest
12, // 33: api.GatewayService.GetMetrics:input_type -> api.GetGatewayMetricsRequest
21, // 34: api.GatewayService.Create:output_type -> google.protobuf.Empty
5, // 35: api.GatewayService.Get:output_type -> api.GetGatewayResponse
21, // 36: api.GatewayService.Update:output_type -> google.protobuf.Empty
21, // 37: api.GatewayService.Delete:output_type -> google.protobuf.Empty
9, // 38: api.GatewayService.List:output_type -> api.ListGatewaysResponse
11, // 39: api.GatewayService.GenerateClientCertificate:output_type -> api.GenerateGatewayClientCertificateResponse
13, // 40: api.GatewayService.GetMetrics:output_type -> api.GetGatewayMetricsResponse
34, // [34:41] is the sub-list for method output_type
27, // [27:34] is the sub-list for method input_type
27, // [27:27] is the sub-list for extension type_name
27, // [27:27] is the sub-list for extension extendee
0, // [0:27] is the sub-list for field type_name
20, // 16: api.GenerateGatewayClientCertificateResponse.expires_at:type_name -> google.protobuf.Timestamp
20, // 17: api.GetGatewayMetricsRequest.start:type_name -> google.protobuf.Timestamp
20, // 18: api.GetGatewayMetricsRequest.end:type_name -> google.protobuf.Timestamp
21, // 19: api.GetGatewayMetricsRequest.aggregation:type_name -> common.Aggregation
22, // 20: api.GetGatewayMetricsResponse.rx_packets:type_name -> common.Metric
22, // 21: api.GetGatewayMetricsResponse.tx_packets:type_name -> common.Metric
22, // 22: api.GetGatewayMetricsResponse.tx_packets_per_freq:type_name -> common.Metric
22, // 23: api.GetGatewayMetricsResponse.rx_packets_per_freq:type_name -> common.Metric
22, // 24: api.GetGatewayMetricsResponse.tx_packets_per_dr:type_name -> common.Metric
22, // 25: api.GetGatewayMetricsResponse.rx_packets_per_dr:type_name -> common.Metric
22, // 26: api.GetGatewayMetricsResponse.tx_packets_per_status:type_name -> common.Metric
20, // 27: api.GetGatewayDutyCycleMetricsRequest.start:type_name -> google.protobuf.Timestamp
20, // 28: api.GetGatewayDutyCycleMetricsRequest.end:type_name -> google.protobuf.Timestamp
22, // 29: api.GetGatewayDutyCycleMetricsResponse.max_load_percentage:type_name -> common.Metric
22, // 30: api.GetGatewayDutyCycleMetricsResponse.window_percentage:type_name -> common.Metric
3, // 31: api.GatewayService.Create:input_type -> api.CreateGatewayRequest
4, // 32: api.GatewayService.Get:input_type -> api.GetGatewayRequest
6, // 33: api.GatewayService.Update:input_type -> api.UpdateGatewayRequest
7, // 34: api.GatewayService.Delete:input_type -> api.DeleteGatewayRequest
8, // 35: api.GatewayService.List:input_type -> api.ListGatewaysRequest
10, // 36: api.GatewayService.GenerateClientCertificate:input_type -> api.GenerateGatewayClientCertificateRequest
12, // 37: api.GatewayService.GetMetrics:input_type -> api.GetGatewayMetricsRequest
14, // 38: api.GatewayService.GetDutyCycleMetrics:input_type -> api.GetGatewayDutyCycleMetricsRequest
23, // 39: api.GatewayService.Create:output_type -> google.protobuf.Empty
5, // 40: api.GatewayService.Get:output_type -> api.GetGatewayResponse
23, // 41: api.GatewayService.Update:output_type -> google.protobuf.Empty
23, // 42: api.GatewayService.Delete:output_type -> google.protobuf.Empty
9, // 43: api.GatewayService.List:output_type -> api.ListGatewaysResponse
11, // 44: api.GatewayService.GenerateClientCertificate:output_type -> api.GenerateGatewayClientCertificateResponse
13, // 45: api.GatewayService.GetMetrics:output_type -> api.GetGatewayMetricsResponse
15, // 46: api.GatewayService.GetDutyCycleMetrics:output_type -> api.GetGatewayDutyCycleMetricsResponse
39, // [39:47] is the sub-list for method output_type
31, // [31:39] is the sub-list for method input_type
31, // [31:31] is the sub-list for extension type_name
31, // [31:31] is the sub-list for extension extendee
0, // [0:31] is the sub-list for field type_name
}
func init() { file_api_gateway_proto_init() }
@ -1515,6 +1677,30 @@ func file_api_gateway_proto_init() {
return nil
}
}
file_api_gateway_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetGatewayDutyCycleMetricsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_gateway_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetGatewayDutyCycleMetricsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@ -1522,7 +1708,7 @@ func file_api_gateway_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_gateway_proto_rawDesc,
NumEnums: 1,
NumMessages: 16,
NumMessages: 18,
NumExtensions: 0,
NumServices: 1,
},

@ -27,6 +27,7 @@ const (
GatewayService_List_FullMethodName = "/api.GatewayService/List"
GatewayService_GenerateClientCertificate_FullMethodName = "/api.GatewayService/GenerateClientCertificate"
GatewayService_GetMetrics_FullMethodName = "/api.GatewayService/GetMetrics"
GatewayService_GetDutyCycleMetrics_FullMethodName = "/api.GatewayService/GetDutyCycleMetrics"
)
// GatewayServiceClient is the client API for GatewayService service.
@ -47,6 +48,9 @@ type GatewayServiceClient interface {
GenerateClientCertificate(ctx context.Context, in *GenerateGatewayClientCertificateRequest, opts ...grpc.CallOption) (*GenerateGatewayClientCertificateResponse, error)
// GetMetrics returns the gateway metrics.
GetMetrics(ctx context.Context, in *GetGatewayMetricsRequest, opts ...grpc.CallOption) (*GetGatewayMetricsResponse, error)
// GetDutyCycleMetrics returns the duty-cycle metrics.
// Note that only the last 2 hours of data are stored. Currently only per minute aggregation is available.
GetDutyCycleMetrics(ctx context.Context, in *GetGatewayDutyCycleMetricsRequest, opts ...grpc.CallOption) (*GetGatewayDutyCycleMetricsResponse, error)
}
type gatewayServiceClient struct {
@ -120,6 +124,15 @@ func (c *gatewayServiceClient) GetMetrics(ctx context.Context, in *GetGatewayMet
return out, nil
}
func (c *gatewayServiceClient) GetDutyCycleMetrics(ctx context.Context, in *GetGatewayDutyCycleMetricsRequest, opts ...grpc.CallOption) (*GetGatewayDutyCycleMetricsResponse, error) {
out := new(GetGatewayDutyCycleMetricsResponse)
err := c.cc.Invoke(ctx, GatewayService_GetDutyCycleMetrics_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GatewayServiceServer is the server API for GatewayService service.
// All implementations must embed UnimplementedGatewayServiceServer
// for forward compatibility
@ -138,6 +151,9 @@ type GatewayServiceServer interface {
GenerateClientCertificate(context.Context, *GenerateGatewayClientCertificateRequest) (*GenerateGatewayClientCertificateResponse, error)
// GetMetrics returns the gateway metrics.
GetMetrics(context.Context, *GetGatewayMetricsRequest) (*GetGatewayMetricsResponse, error)
// GetDutyCycleMetrics returns the duty-cycle metrics.
// Note that only the last 2 hours of data are stored. Currently only per minute aggregation is available.
GetDutyCycleMetrics(context.Context, *GetGatewayDutyCycleMetricsRequest) (*GetGatewayDutyCycleMetricsResponse, error)
mustEmbedUnimplementedGatewayServiceServer()
}
@ -166,6 +182,9 @@ func (UnimplementedGatewayServiceServer) GenerateClientCertificate(context.Conte
func (UnimplementedGatewayServiceServer) GetMetrics(context.Context, *GetGatewayMetricsRequest) (*GetGatewayMetricsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMetrics not implemented")
}
func (UnimplementedGatewayServiceServer) GetDutyCycleMetrics(context.Context, *GetGatewayDutyCycleMetricsRequest) (*GetGatewayDutyCycleMetricsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetDutyCycleMetrics not implemented")
}
func (UnimplementedGatewayServiceServer) mustEmbedUnimplementedGatewayServiceServer() {}
// UnsafeGatewayServiceServer may be embedded to opt out of forward compatibility for this service.
@ -305,6 +324,24 @@ func _GatewayService_GetMetrics_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _GatewayService_GetDutyCycleMetrics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetGatewayDutyCycleMetricsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GatewayServiceServer).GetDutyCycleMetrics(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: GatewayService_GetDutyCycleMetrics_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GatewayServiceServer).GetDutyCycleMetrics(ctx, req.(*GetGatewayDutyCycleMetricsRequest))
}
return interceptor(ctx, in, info, handler)
}
// GatewayService_ServiceDesc is the grpc.ServiceDesc for GatewayService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -340,6 +377,10 @@ var GatewayService_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetMetrics",
Handler: _GatewayService_GetMetrics_Handler,
},
{
MethodName: "GetDutyCycleMetrics",
Handler: _GatewayService_GetDutyCycleMetrics_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/gateway.proto",

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/internal.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/multicast_group.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/relay.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/tenant.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: api/user.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: common/common.proto
@ -308,6 +308,7 @@ const (
RegParamsRevision_RP002_1_0_1 RegParamsRevision = 3
RegParamsRevision_RP002_1_0_2 RegParamsRevision = 4
RegParamsRevision_RP002_1_0_3 RegParamsRevision = 5
RegParamsRevision_RP002_1_0_4 RegParamsRevision = 6
)
// Enum value maps for RegParamsRevision.
@ -319,6 +320,7 @@ var (
3: "RP002_1_0_1",
4: "RP002_1_0_2",
5: "RP002_1_0_3",
6: "RP002_1_0_4",
}
RegParamsRevision_value = map[string]int32{
"A": 0,
@ -327,6 +329,7 @@ var (
"RP002_1_0_1": 3,
"RP002_1_0_2": 4,
"RP002_1_0_3": 5,
"RP002_1_0_4": 6,
}
)
@ -434,6 +437,8 @@ const (
Aggregation_DAY Aggregation = 1
// Month.
Aggregation_MONTH Aggregation = 2
// Minute.
Aggregation_MINUTE Aggregation = 3
)
// Enum value maps for Aggregation.
@ -442,11 +447,13 @@ var (
0: "HOUR",
1: "DAY",
2: "MONTH",
3: "MINUTE",
}
Aggregation_value = map[string]int32{
"HOUR": 0,
"DAY": 1,
"MONTH": 2,
"HOUR": 0,
"DAY": 1,
"MONTH": 2,
"MINUTE": 3,
}
)
@ -530,6 +537,54 @@ func (MetricKind) EnumDescriptor() ([]byte, []int) {
return file_common_common_proto_rawDescGZIP(), []int{7}
}
type Regulation int32
const (
// Unknown.
Regulation_REGULATION_UNKNOWN Regulation = 0
// ETSI EN 300 220.
Regulation_ETSI_EN_300_220 Regulation = 1
)
// Enum value maps for Regulation.
var (
Regulation_name = map[int32]string{
0: "REGULATION_UNKNOWN",
1: "ETSI_EN_300_220",
}
Regulation_value = map[string]int32{
"REGULATION_UNKNOWN": 0,
"ETSI_EN_300_220": 1,
}
)
func (x Regulation) Enum() *Regulation {
p := new(Regulation)
*p = x
return p
}
func (x Regulation) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Regulation) Descriptor() protoreflect.EnumDescriptor {
return file_common_common_proto_enumTypes[8].Descriptor()
}
func (Regulation) Type() protoreflect.EnumType {
return &file_common_common_proto_enumTypes[8]
}
func (x Regulation) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Regulation.Descriptor instead.
func (Regulation) EnumDescriptor() ([]byte, []int) {
return file_common_common_proto_rawDescGZIP(), []int{8}
}
type DeviceClass int32
const (
@ -566,11 +621,11 @@ func (x DeviceClass) String() string {
}
func (DeviceClass) Descriptor() protoreflect.EnumDescriptor {
return file_common_common_proto_enumTypes[8].Descriptor()
return file_common_common_proto_enumTypes[9].Descriptor()
}
func (DeviceClass) Type() protoreflect.EnumType {
return &file_common_common_proto_enumTypes[8]
return &file_common_common_proto_enumTypes[9]
}
func (x DeviceClass) Number() protoreflect.EnumNumber {
@ -579,7 +634,7 @@ func (x DeviceClass) Number() protoreflect.EnumNumber {
// Deprecated: Use DeviceClass.Descriptor instead.
func (DeviceClass) EnumDescriptor() ([]byte, []int) {
return file_common_common_proto_rawDescGZIP(), []int{8}
return file_common_common_proto_rawDescGZIP(), []int{9}
}
type Location struct {
@ -991,39 +1046,44 @@ var file_common_common_proto_rawDesc = []byte{
0x41, 0x4e, 0x5f, 0x31, 0x5f, 0x30, 0x5f, 0x33, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x4f,
0x52, 0x41, 0x57, 0x41, 0x4e, 0x5f, 0x31, 0x5f, 0x30, 0x5f, 0x34, 0x10, 0x04, 0x12, 0x11, 0x0a,
0x0d, 0x4c, 0x4f, 0x52, 0x41, 0x57, 0x41, 0x4e, 0x5f, 0x31, 0x5f, 0x31, 0x5f, 0x30, 0x10, 0x05,
0x2a, 0x65, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x76,
0x2a, 0x76, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x76,
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x05, 0x0a, 0x01, 0x41, 0x10, 0x00, 0x12, 0x05, 0x0a, 0x01,
0x42, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x50, 0x30, 0x30, 0x32, 0x5f, 0x31, 0x5f, 0x30,
0x5f, 0x30, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x50, 0x30, 0x30, 0x32, 0x5f, 0x31, 0x5f,
0x30, 0x5f, 0x31, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x50, 0x30, 0x30, 0x32, 0x5f, 0x31,
0x5f, 0x30, 0x5f, 0x32, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x50, 0x30, 0x30, 0x32, 0x5f,
0x31, 0x5f, 0x30, 0x5f, 0x33, 0x10, 0x05, 0x2a, 0x8e, 0x01, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e,
0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x50, 0x53, 0x10, 0x01,
0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11,
0x47, 0x45, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, 0x52, 0x5f, 0x54, 0x44, 0x4f,
0x41, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c,
0x56, 0x45, 0x52, 0x5f, 0x52, 0x53, 0x53, 0x49, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45,
0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, 0x52, 0x5f, 0x47, 0x4e, 0x53, 0x53, 0x10,
0x05, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45,
0x52, 0x5f, 0x57, 0x49, 0x46, 0x49, 0x10, 0x06, 0x2a, 0x2b, 0x0a, 0x0b, 0x41, 0x67, 0x67, 0x72,
0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52, 0x10,
0x00, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x59, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x4f,
0x4e, 0x54, 0x48, 0x10, 0x02, 0x2a, 0x32, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b,
0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00,
0x12, 0x0c, 0x0a, 0x08, 0x41, 0x42, 0x53, 0x4f, 0x4c, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, 0x09,
0x0a, 0x05, 0x47, 0x41, 0x55, 0x47, 0x45, 0x10, 0x02, 0x2a, 0x34, 0x0a, 0x0b, 0x44, 0x65, 0x76,
0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x53,
0x53, 0x5f, 0x41, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x42,
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x43, 0x10, 0x02, 0x42,
0x69, 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b,
0x2e, 0x61, 0x70, 0x69, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74,
0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70,
0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0xaa, 0x02, 0x11, 0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74,
0x61, 0x63, 0x6b, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x31, 0x5f, 0x30, 0x5f, 0x33, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x50, 0x30, 0x30, 0x32,
0x5f, 0x31, 0x5f, 0x30, 0x5f, 0x34, 0x10, 0x06, 0x2a, 0x8e, 0x01, 0x0a, 0x0e, 0x4c, 0x6f, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55,
0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x50, 0x53, 0x10,
0x01, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x02, 0x12, 0x15, 0x0a,
0x11, 0x47, 0x45, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, 0x52, 0x5f, 0x54, 0x44,
0x4f, 0x41, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f,
0x4c, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x53, 0x53, 0x49, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x47,
0x45, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, 0x52, 0x5f, 0x47, 0x4e, 0x53, 0x53,
0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56,
0x45, 0x52, 0x5f, 0x57, 0x49, 0x46, 0x49, 0x10, 0x06, 0x2a, 0x37, 0x0a, 0x0b, 0x41, 0x67, 0x67,
0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52,
0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x59, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x4d,
0x4f, 0x4e, 0x54, 0x48, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45,
0x10, 0x03, 0x2a, 0x32, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64,
0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0c, 0x0a,
0x08, 0x41, 0x42, 0x53, 0x4f, 0x4c, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x47,
0x41, 0x55, 0x47, 0x45, 0x10, 0x02, 0x2a, 0x39, 0x0a, 0x0a, 0x52, 0x65, 0x67, 0x75, 0x6c, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41, 0x54, 0x49,
0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f,
0x45, 0x54, 0x53, 0x49, 0x5f, 0x45, 0x4e, 0x5f, 0x33, 0x30, 0x30, 0x5f, 0x32, 0x32, 0x30, 0x10,
0x01, 0x2a, 0x34, 0x0a, 0x0b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73,
0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x41, 0x10, 0x00, 0x12, 0x0b, 0x0a,
0x07, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x42, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c,
0x41, 0x53, 0x53, 0x5f, 0x43, 0x10, 0x02, 0x42, 0x69, 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x63, 0x68,
0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x0b, 0x43, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61,
0x63, 0x6b, 0x2f, 0x63, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x34, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0xaa, 0x02,
0x11, 0x43, 0x68, 0x69, 0x72, 0x70, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x43, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1038,7 +1098,7 @@ func file_common_common_proto_rawDescGZIP() []byte {
return file_common_common_proto_rawDescData
}
var file_common_common_proto_enumTypes = make([]protoimpl.EnumInfo, 9)
var file_common_common_proto_enumTypes = make([]protoimpl.EnumInfo, 10)
var file_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_common_common_proto_goTypes = []interface{}{
(Modulation)(0), // 0: common.Modulation
@ -1049,20 +1109,21 @@ var file_common_common_proto_goTypes = []interface{}{
(LocationSource)(0), // 5: common.LocationSource
(Aggregation)(0), // 6: common.Aggregation
(MetricKind)(0), // 7: common.MetricKind
(DeviceClass)(0), // 8: common.DeviceClass
(*Location)(nil), // 9: common.Location
(*KeyEnvelope)(nil), // 10: common.KeyEnvelope
(*Metric)(nil), // 11: common.Metric
(*MetricDataset)(nil), // 12: common.MetricDataset
(*JoinServerContext)(nil), // 13: common.JoinServerContext
(*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp
(Regulation)(0), // 8: common.Regulation
(DeviceClass)(0), // 9: common.DeviceClass
(*Location)(nil), // 10: common.Location
(*KeyEnvelope)(nil), // 11: common.KeyEnvelope
(*Metric)(nil), // 12: common.Metric
(*MetricDataset)(nil), // 13: common.MetricDataset
(*JoinServerContext)(nil), // 14: common.JoinServerContext
(*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
}
var file_common_common_proto_depIdxs = []int32{
5, // 0: common.Location.source:type_name -> common.LocationSource
14, // 1: common.Metric.timestamps:type_name -> google.protobuf.Timestamp
12, // 2: common.Metric.datasets:type_name -> common.MetricDataset
15, // 1: common.Metric.timestamps:type_name -> google.protobuf.Timestamp
13, // 2: common.Metric.datasets:type_name -> common.MetricDataset
7, // 3: common.Metric.kind:type_name -> common.MetricKind
10, // 4: common.JoinServerContext.app_s_key:type_name -> common.KeyEnvelope
11, // 4: common.JoinServerContext.app_s_key:type_name -> common.KeyEnvelope
5, // [5:5] is the sub-list for method output_type
5, // [5:5] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
@ -1142,7 +1203,7 @@ func file_common_common_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_common_common_proto_rawDesc,
NumEnums: 9,
NumEnums: 10,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,

5
api/go/go.mod vendored

@ -1,6 +1,6 @@
module github.com/chirpstack/chirpstack/api/go/v4
go 1.18
go 1.21
require (
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa
@ -10,9 +10,8 @@ require (
require (
github.com/golang/protobuf v1.5.4 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect
)

7
api/go/go.sum vendored

@ -1,8 +1,9 @@
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
@ -13,7 +14,5 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1:
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=

1466
api/go/gw/gw.pb.go vendored

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: integration/integration.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: stream/api_request.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: stream/backend_interfaces.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: stream/frame.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.31.0
// protoc v4.24.4
// source: stream/meta.proto

0
api/grpc-web/.npmignore vendored Normal file

@ -1,6 +1,6 @@
{
"name": "@chirpstack/chirpstack-api-grpc-web",
"version": "4.7.0",
"version": "4.8.1",
"description": "Chirpstack gRPC-web API",
"license": "MIT",
"devDependencies": {

@ -353,9 +353,9 @@ strip-ansi@^6.0.1:
ansi-regex "^5.0.1"
tar@^6.1.11:
version "6.1.15"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69"
integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==
version "6.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"

@ -8,7 +8,7 @@ plugins {
}
group = "io.chirpstack"
version = "4.7.0"
version = "4.8.1"
repositories {
mavenCentral()

0
api/js/.npmignore vendored Normal file

2
api/js/package.json vendored

@ -1,6 +1,6 @@
{
"name": "@chirpstack/chirpstack-api",
"version": "4.7.0",
"version": "4.8.1",
"description": "Chirpstack JS and TS API",
"license": "MIT",
"devDependencies": {

12
api/js/yarn.lock vendored

@ -411,9 +411,9 @@ path-is-absolute@^1.0.0:
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
protobufjs@^7.2.4:
version "7.2.4"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae"
integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==
version "7.2.6"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215"
integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==
dependencies:
"@protobufjs/aspromise" "^1.1.2"
"@protobufjs/base64" "^1.1.2"
@ -500,9 +500,9 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
ansi-regex "^5.0.1"
tar@^6.1.11:
version "6.1.15"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69"
integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==
version "6.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"

@ -9,7 +9,7 @@ plugins {
}
group = "io.chirpstack"
version = "4.7.0"
version = "4.8.1"
repositories {
mavenCentral()

@ -410,6 +410,15 @@ message DeviceProfile {
//
// If set to true, it means that the device is allowed to use roaming.
bool allow_roaming = 52;
// RX1 Delay.
//
// This makes it possible to override the system RX1 Delay. Please note that
// this values only has effect in case it is higher than the system value.
// In other words, it can be used to increase the RX1 Delay but not to decrease
// it.
// Valid options are 1 - 15 (0 = always use system RX1 Delay).
uint32 rx1_delay = 53;
}
message Measurement {

@ -65,6 +65,14 @@ service GatewayService {
get: "/api/gateways/{gateway_id}/metrics"
};
}
// GetDutyCycleMetrics returns the duty-cycle metrics.
// Note that only the last 2 hours of data are stored. Currently only per minute aggregation is available.
rpc GetDutyCycleMetrics(GetGatewayDutyCycleMetricsRequest) returns (GetGatewayDutyCycleMetricsResponse) {
option(google.api.http) = {
get: "/api/gateways/{gateway_id}/duty-cycle-metrics"
};
}
}
enum GatewayState {
@ -255,3 +263,22 @@ message GetGatewayMetricsResponse {
// TX packets per status.
common.Metric tx_packets_per_status = 7;
}
message GetGatewayDutyCycleMetricsRequest {
// Gateway ID (EUI64).
string gateway_id = 1;
// Interval start timestamp.
google.protobuf.Timestamp start = 2;
// Interval end timestamp.
google.protobuf.Timestamp end = 3;
}
message GetGatewayDutyCycleMetricsResponse {
// Percentage relative to max load.
common.Metric max_load_percentage = 1;
// Percentage relative to tracking window.
common.Metric window_percentage = 2;
}

@ -107,6 +107,7 @@ enum RegParamsRevision {
RP002_1_0_1 = 3;
RP002_1_0_2 = 4;
RP002_1_0_3 = 5;
RP002_1_0_4 = 6;
}
enum LocationSource {
@ -141,6 +142,9 @@ enum Aggregation {
// Month.
MONTH = 2;
// Minute.
MINUTE = 3;
}
enum MetricKind {
@ -155,6 +159,14 @@ enum MetricKind {
GAUGE = 2;
}
enum Regulation {
// Unknown.
REGULATION_UNKNOWN = 0;
// ETSI EN 300 220.
ETSI_EN_300_220 = 1;
}
message Location {
// Latitude.
double latitude = 1;
@ -221,4 +233,4 @@ message JoinServerContext {
// AppSKey envelope.
KeyEnvelope app_s_key = 2;
}
}

34
api/proto/gw/gw.proto vendored

@ -97,6 +97,9 @@ enum TxAckStatus {
// Internal error.
INTERNAL_ERROR = 10;
// Duty-cycle overflow.
DUTY_CYCLE_OVERFLOW = 11;
}
message Modulation {
@ -249,6 +252,9 @@ message GatewayStats {
// Tx packets per status.
map<string, uint32> tx_packets_per_status = 16;
// Duty-cycle statistics (Concentratord only).
DutyCycleStats duty_cycle_stats = 18;
}
message PerModulationCount {
@ -259,6 +265,34 @@ message PerModulationCount {
uint32 count = 2;
}
message DutyCycleStats {
// Implemented regulation.
common.Regulation regulation = 1;
// Tracking window.
google.protobuf.Duration window = 2;
// Bands.
repeated DutyCycleBand bands = 3;
}
message DutyCycleBand {
// Band name.
string name = 1;
// Min frequency for this band.
uint32 frequency_min = 2;
// Max frequency for this band.
uint32 frequency_max = 3;
// Max. allowed load.
google.protobuf.Duration load_max = 4;
// Tracked load (within the window of the regionlation).
google.protobuf.Duration load_tracked = 5;
}
message UplinkRxInfoLegacy {
// Gateway ID.
bytes gateway_id = 1;

@ -18,7 +18,7 @@ CLASSIFIERS = [
setup(
name='chirpstack-api',
version = "4.7.0",
version = "4.8.1",
url='https://github.com/brocaar/chirpstack-api',
author='Orne Brocaar',
author_email='info@brocaar.com',

1
api/rust/.gitignore vendored

@ -1 +0,0 @@
/proto

2
api/rust/Cargo.toml vendored

@ -1,7 +1,7 @@
[package]
name = "chirpstack_api"
description = "ChirpStack Protobuf / gRPC API definitions."
version = "4.7.0"
version = "4.8.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
license = "MIT"
homepage = "https://www.chirpstack.io"

1
api/rust/build.rs vendored

@ -74,6 +74,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// internal
{
#[allow(unused_mut)]
let mut builder = tonic_build::configure()
.out_dir(out_dir.join("internal"))
.file_descriptor_set_path(out_dir.join("internal").join("proto_descriptor.bin"))

@ -8,7 +8,7 @@ option java_multiple_files = true;
option java_outer_classname = "DeviceProto";
option csharp_namespace = "Chirpstack.Api";
import "chirpstack-api/common/common.proto";
import "common/common.proto";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/struct.proto";

@ -11,7 +11,7 @@ option csharp_namespace = "Chirpstack.Api";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
import "chirpstack-api/common/common.proto";
import "common/common.proto";
enum CodecRuntime {
// None.
@ -410,6 +410,15 @@ message DeviceProfile {
//
// If set to true, it means that the device is allowed to use roaming.
bool allow_roaming = 52;
// RX1 Delay.
//
// This makes it possible to override the system RX1 Delay. Please note that
// this values only has effect in case it is higher than the system value.
// In other words, it can be used to increase the RX1 Delay but not to decrease
// it.
// Valid options are 1 - 15 (0 = always use system RX1 Delay).
uint32 rx1_delay = 53;
}
message Measurement {

@ -11,8 +11,8 @@ option csharp_namespace = "Chirpstack.Api";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
import "chirpstack-api/common/common.proto";
import "chirpstack-api/api/device_profile.proto";
import "common/common.proto";
import "api/device_profile.proto";
// DeviceProfileTemplateService is the service providing API methods for managing device-profile templates.
service DeviceProfileTemplateService {

@ -11,7 +11,7 @@ option csharp_namespace = "Chirpstack.Api";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
import "chirpstack-api/common/common.proto";
import "common/common.proto";
// GatewayService is the service providing API methods for managing gateways.
service GatewayService {
@ -65,6 +65,14 @@ service GatewayService {
get: "/api/gateways/{gateway_id}/metrics"
};
}
// GetDutyCycleMetrics returns the duty-cycle metrics.
// Note that only the last 2 hours of data are stored. Currently only per minute aggregation is available.
rpc GetDutyCycleMetrics(GetGatewayDutyCycleMetricsRequest) returns (GetGatewayDutyCycleMetricsResponse) {
option(google.api.http) = {
get: "/api/gateways/{gateway_id}/duty-cycle-metrics"
};
}
}
enum GatewayState {
@ -255,3 +263,22 @@ message GetGatewayMetricsResponse {
// TX packets per status.
common.Metric tx_packets_per_status = 7;
}
message GetGatewayDutyCycleMetricsRequest {
// Gateway ID (EUI64).
string gateway_id = 1;
// Interval start timestamp.
google.protobuf.Timestamp start = 2;
// Interval end timestamp.
google.protobuf.Timestamp end = 3;
}
message GetGatewayDutyCycleMetricsResponse {
// Percentage relative to max load.
common.Metric max_load_percentage = 1;
// Percentage relative to tracking window.
common.Metric window_percentage = 2;
}

@ -10,8 +10,8 @@ option csharp_namespace = "Chirpstack.Api";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
import "chirpstack-api/common/common.proto";
import "chirpstack-api/api/user.proto";
import "common/common.proto";
import "api/user.proto";
// InternalService is the service providing API endpoints for internal usage.
service InternalService {

@ -11,7 +11,7 @@ option csharp_namespace = "Chirpstack.Api";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
import "chirpstack-api/common/common.proto";
import "common/common.proto";
// MulticastGroupService is the service managing multicast-groups.

@ -107,6 +107,7 @@ enum RegParamsRevision {
RP002_1_0_1 = 3;
RP002_1_0_2 = 4;
RP002_1_0_3 = 5;
RP002_1_0_4 = 6;
}
enum LocationSource {
@ -141,6 +142,9 @@ enum Aggregation {
// Month.
MONTH = 2;
// Minute.
MINUTE = 3;
}
enum MetricKind {
@ -155,6 +159,14 @@ enum MetricKind {
GAUGE = 2;
}
enum Regulation {
// Unknown.
REGULATION_UNKNOWN = 0;
// ETSI EN 300 220.
ETSI_EN_300_220 = 1;
}
message Location {
// Latitude.
double latitude = 1;
@ -221,4 +233,4 @@ message JoinServerContext {
// AppSKey envelope.
KeyEnvelope app_s_key = 2;
}
}

@ -8,7 +8,7 @@ option java_multiple_files = true;
option java_outer_classname = "GatewayProto";
option csharp_namespace = "Chirpstack.Gateway";
import "chirpstack-api/common/common.proto";
import "common/common.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
@ -97,6 +97,9 @@ enum TxAckStatus {
// Internal error.
INTERNAL_ERROR = 10;
// Duty-cycle overflow.
DUTY_CYCLE_OVERFLOW = 11;
}
message Modulation {
@ -249,6 +252,9 @@ message GatewayStats {
// Tx packets per status.
map<string, uint32> tx_packets_per_status = 16;
// Duty-cycle statistics (Concentratord only).
DutyCycleStats duty_cycle_stats = 18;
}
message PerModulationCount {
@ -259,6 +265,34 @@ message PerModulationCount {
uint32 count = 2;
}
message DutyCycleStats {
// Implemented regulation.
common.Regulation regulation = 1;
// Tracking window.
google.protobuf.Duration window = 2;
// Bands.
repeated DutyCycleBand bands = 3;
}
message DutyCycleBand {
// Band name.
string name = 1;
// Min frequency for this band.
uint32 frequency_min = 2;
// Max frequency for this band.
uint32 frequency_max = 3;
// Max. allowed load.
google.protobuf.Duration load_max = 4;
// Tracked load (within the window of the regionlation).
google.protobuf.Duration load_tracked = 5;
}
message UplinkRxInfoLegacy {
// Gateway ID.
bytes gateway_id = 1;

@ -8,8 +8,8 @@ option java_multiple_files = true;
option java_outer_classname = "IntegrationProto";
option csharp_namespace = "Chirpstack.Integration";
import "chirpstack-api/common/common.proto";
import "chirpstack-api/gw/gw.proto";
import "common/common.proto";
import "gw/gw.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/struct.proto";

@ -2,8 +2,8 @@ syntax = "proto3";
package internal;
import "chirpstack-api/common/common.proto";
import "chirpstack-api/gw/gw.proto";
import "common/common.proto";
import "gw/gw.proto";
import "google/protobuf/timestamp.proto";
message DeviceSession {

@ -9,8 +9,8 @@ option java_outer_classname = "ApiRequestProto";
option csharp_namespace = "Chirpstack.Stream";
import "google/protobuf/timestamp.proto";
import "chirpstack-api/common/common.proto";
import "chirpstack-api/gw/gw.proto";
import "common/common.proto";
import "gw/gw.proto";
message ApiRequestLog {
// API service name.

@ -9,8 +9,8 @@ option java_outer_classname = "FrameProto";
option csharp_namespace = "Chirpstack.Stream";
import "google/protobuf/timestamp.proto";
import "chirpstack-api/common/common.proto";
import "chirpstack-api/gw/gw.proto";
import "common/common.proto";
import "gw/gw.proto";
message UplinkFrameLog {
// PHYPayload.

@ -8,8 +8,8 @@ option java_multiple_files = true;
option java_outer_classname = "MetaProto";
option csharp_namespace = "Chirpstack.Stream";
import "chirpstack-api/common/common.proto";
import "chirpstack-api/gw/gw.proto";
import "common/common.proto";
import "gw/gw.proto";
message UplinkMeta {
// Device EUI (EUI64).

@ -0,0 +1,31 @@
// Copyright 2015 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package google.api;
import "google/api/http.proto";
import "google/protobuf/descriptor.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "AnnotationsProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
extend google.protobuf.MethodOptions {
// See `HttpRule`.
HttpRule http = 72295728;
}

@ -0,0 +1,379 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package google.api;
option cc_enable_arenas = true;
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "HttpProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// Defines the HTTP configuration for an API service. It contains a list of
// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
// to one or more HTTP REST API methods.
message Http {
// A list of HTTP configuration rules that apply to individual API methods.
//
// **NOTE:** All service configuration rules follow "last one wins" order.
repeated HttpRule rules = 1;
// When set to true, URL path parameters will be fully URI-decoded except in
// cases of single segment matches in reserved expansion, where "%2F" will be
// left encoded.
//
// The default behavior is to not decode RFC 6570 reserved characters in multi
// segment matches.
bool fully_decode_reserved_expansion = 2;
}
// # gRPC Transcoding
//
// gRPC Transcoding is a feature for mapping between a gRPC method and one or
// more HTTP REST endpoints. It allows developers to build a single API service
// that supports both gRPC APIs and REST APIs. Many systems, including [Google
// APIs](https://github.com/googleapis/googleapis),
// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
// and use it for large scale production services.
//
// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
// how different portions of the gRPC request message are mapped to the URL
// path, URL query parameters, and HTTP request body. It also controls how the
// gRPC response message is mapped to the HTTP response body. `HttpRule` is
// typically specified as an `google.api.http` annotation on the gRPC method.
//
// Each mapping specifies a URL path template and an HTTP method. The path
// template may refer to one or more fields in the gRPC request message, as long
// as each field is a non-repeated field with a primitive (non-message) type.
// The path template controls how fields of the request message are mapped to
// the URL path.
//
// Example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get: "/v1/{name=messages/*}"
// };
// }
// }
// message GetMessageRequest {
// string name = 1; // Mapped to URL path.
// }
// message Message {
// string text = 1; // The resource content.
// }
//
// This enables an HTTP REST to gRPC mapping as below:
//
// HTTP | gRPC
// -----|-----
// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")`
//
// Any fields in the request message which are not bound by the path template
// automatically become HTTP query parameters if there is no HTTP request body.
// For example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get:"/v1/messages/{message_id}"
// };
// }
// }
// message GetMessageRequest {
// message SubMessage {
// string subfield = 1;
// }
// string message_id = 1; // Mapped to URL path.
// int64 revision = 2; // Mapped to URL query parameter `revision`.
// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`.
// }
//
// This enables a HTTP JSON to RPC mapping as below:
//
// HTTP | gRPC
// -----|-----
// `GET /v1/messages/123456?revision=2&sub.subfield=foo` |
// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield:
// "foo"))`
//
// Note that fields which are mapped to URL query parameters must have a
// primitive type or a repeated primitive type or a non-repeated message type.
// In the case of a repeated type, the parameter can be repeated in the URL
// as `...?param=A&param=B`. In the case of a message type, each field of the
// message is mapped to a separate parameter, such as
// `...?foo.a=A&foo.b=B&foo.c=C`.
//
// For HTTP methods that allow a request body, the `body` field
// specifies the mapping. Consider a REST update method on the
// message resource collection:
//
// service Messaging {
// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
// option (google.api.http) = {
// patch: "/v1/messages/{message_id}"
// body: "message"
// };
// }
// }
// message UpdateMessageRequest {
// string message_id = 1; // mapped to the URL
// Message message = 2; // mapped to the body
// }
//
// The following HTTP JSON to RPC mapping is enabled, where the
// representation of the JSON in the request body is determined by
// protos JSON encoding:
//
// HTTP | gRPC
// -----|-----
// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
// "123456" message { text: "Hi!" })`
//
// The special name `*` can be used in the body mapping to define that
// every field not bound by the path template should be mapped to the
// request body. This enables the following alternative definition of
// the update method:
//
// service Messaging {
// rpc UpdateMessage(Message) returns (Message) {
// option (google.api.http) = {
// patch: "/v1/messages/{message_id}"
// body: "*"
// };
// }
// }
// message Message {
// string message_id = 1;
// string text = 2;
// }
//
//
// The following HTTP JSON to RPC mapping is enabled:
//
// HTTP | gRPC
// -----|-----
// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
// "123456" text: "Hi!")`
//
// Note that when using `*` in the body mapping, it is not possible to
// have HTTP parameters, as all fields not bound by the path end in
// the body. This makes this option more rarely used in practice when
// defining REST APIs. The common usage of `*` is in custom methods
// which don't use the URL at all for transferring data.
//
// It is possible to define multiple HTTP methods for one RPC by using
// the `additional_bindings` option. Example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get: "/v1/messages/{message_id}"
// additional_bindings {
// get: "/v1/users/{user_id}/messages/{message_id}"
// }
// };
// }
// }
// message GetMessageRequest {
// string message_id = 1;
// string user_id = 2;
// }
//
// This enables the following two alternative HTTP JSON to RPC mappings:
//
// HTTP | gRPC
// -----|-----
// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id:
// "123456")`
//
// ## Rules for HTTP mapping
//
// 1. Leaf request fields (recursive expansion nested messages in the request
// message) are classified into three categories:
// - Fields referred by the path template. They are passed via the URL path.
// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They
// are passed via the HTTP
// request body.
// - All other fields are passed via the URL query parameters, and the
// parameter name is the field path in the request message. A repeated
// field can be represented as multiple query parameters under the same
// name.
// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL
// query parameter, all fields
// are passed via URL path and HTTP request body.
// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP
// request body, all
// fields are passed via URL path and URL query parameters.
//
// ### Path template syntax
//
// Template = "/" Segments [ Verb ] ;
// Segments = Segment { "/" Segment } ;
// Segment = "*" | "**" | LITERAL | Variable ;
// Variable = "{" FieldPath [ "=" Segments ] "}" ;
// FieldPath = IDENT { "." IDENT } ;
// Verb = ":" LITERAL ;
//
// The syntax `*` matches a single URL path segment. The syntax `**` matches
// zero or more URL path segments, which must be the last part of the URL path
// except the `Verb`.
//
// The syntax `Variable` matches part of the URL path as specified by its
// template. A variable template must not contain other variables. If a variable
// matches a single path segment, its template may be omitted, e.g. `{var}`
// is equivalent to `{var=*}`.
//
// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
// contains any reserved character, such characters should be percent-encoded
// before the matching.
//
// If a variable contains exactly one path segment, such as `"{var}"` or
// `"{var=*}"`, when such a variable is expanded into a URL path on the client
// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
// server side does the reverse decoding. Such variables show up in the
// [Discovery
// Document](https://developers.google.com/discovery/v1/reference/apis) as
// `{var}`.
//
// If a variable contains multiple path segments, such as `"{var=foo/*}"`
// or `"{var=**}"`, when such a variable is expanded into a URL path on the
// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
// The server side does the reverse decoding, except "%2F" and "%2f" are left
// unchanged. Such variables show up in the
// [Discovery
// Document](https://developers.google.com/discovery/v1/reference/apis) as
// `{+var}`.
//
// ## Using gRPC API Service Configuration
//
// gRPC API Service Configuration (service config) is a configuration language
// for configuring a gRPC service to become a user-facing product. The
// service config is simply the YAML representation of the `google.api.Service`
// proto message.
//
// As an alternative to annotating your proto file, you can configure gRPC
// transcoding in your service config YAML files. You do this by specifying a
// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
// effect as the proto annotation. This can be particularly useful if you
// have a proto that is reused in multiple services. Note that any transcoding
// specified in the service config will override any matching transcoding
// configuration in the proto.
//
// Example:
//
// http:
// rules:
// # Selects a gRPC method and applies HttpRule to it.
// - selector: example.v1.Messaging.GetMessage
// get: /v1/messages/{message_id}/{sub.subfield}
//
// ## Special notes
//
// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
// proto to JSON conversion must follow the [proto3
// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
//
// While the single segment variable follows the semantics of
// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
// Expansion, the multi segment variable **does not** follow RFC 6570 Section
// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
// does not expand special characters like `?` and `#`, which would lead
// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
// for multi segment variables.
//
// The path variables **must not** refer to any repeated or mapped field,
// because client libraries are not capable of handling such variable expansion.
//
// The path variables **must not** capture the leading "/" character. The reason
// is that the most common use case "{var}" does not capture the leading "/"
// character. For consistency, all path variables must share the same behavior.
//
// Repeated message fields must not be mapped to URL query parameters, because
// no client library can support such complicated mapping.
//
// If an API needs to use a JSON array for request or response body, it can map
// the request or response body to a repeated field. However, some gRPC
// Transcoding implementations may not support this feature.
message HttpRule {
// Selects a method to which this rule applies.
//
// Refer to [selector][google.api.DocumentationRule.selector] for syntax
// details.
string selector = 1;
// Determines the URL pattern is matched by this rules. This pattern can be
// used with any of the {get|put|post|delete|patch} methods. A custom method
// can be defined using the 'custom' field.
oneof pattern {
// Maps to HTTP GET. Used for listing and getting information about
// resources.
string get = 2;
// Maps to HTTP PUT. Used for replacing a resource.
string put = 3;
// Maps to HTTP POST. Used for creating a resource or performing an action.
string post = 4;
// Maps to HTTP DELETE. Used for deleting a resource.
string delete = 5;
// Maps to HTTP PATCH. Used for updating a resource.
string patch = 6;
// The custom pattern is used for specifying an HTTP method that is not
// included in the `pattern` field, such as HEAD, or "*" to leave the
// HTTP method unspecified for this rule. The wild-card rule is useful
// for services that provide content to Web (HTML) clients.
CustomHttpPattern custom = 8;
}
// The name of the request field whose value is mapped to the HTTP request
// body, or `*` for mapping all request fields not captured by the path
// pattern to the HTTP body, or omitted for not having any HTTP request body.
//
// NOTE: the referred field must be present at the top-level of the request
// message type.
string body = 7;
// Optional. The name of the response field whose value is mapped to the HTTP
// response body. When omitted, the entire response message will be used
// as the HTTP response body.
//
// NOTE: The referred field must be present at the top-level of the response
// message type.
string response_body = 12;
// Additional HTTP bindings for the selector. Nested bindings must
// not contain an `additional_bindings` field themselves (that is,
// the nesting may only be one level deep).
repeated HttpRule additional_bindings = 11;
}
// A custom pattern is used for defining custom HTTP verb.
message CustomHttpPattern {
// The name of this custom HTTP verb.
string kind = 1;
// The path matched by this custom verb.
string path = 2;
}

@ -122,6 +122,7 @@ impl Into<String> for RegParamsRevision {
RegParamsRevision::Rp002101 => "RP002_1.0.1",
RegParamsRevision::Rp002102 => "RP002_1.0.2",
RegParamsRevision::Rp002103 => "RP002_1.0.3",
RegParamsRevision::Rp002104 => "RP002_1.0.4",
}
.to_string()
}
@ -138,6 +139,7 @@ impl FromStr for RegParamsRevision {
"RP002_1.0.1" => RegParamsRevision::Rp002101,
"RP002_1.0.2" => RegParamsRevision::Rp002102,
"RP002_1.0.3" => RegParamsRevision::Rp002103,
"RP002_1.0.4" => RegParamsRevision::Rp002104,
_ => {
return Err("invalid reg param revision".into());
}

1
api/rust/src/gw.rs vendored

@ -67,6 +67,7 @@ impl Into<String> for TxAckStatus {
TxAckStatus::GpsUnlocked => "GPS_UNLOCKED",
TxAckStatus::QueueFull => "QUEUE_FULL",
TxAckStatus::InternalError => "INTERNAL_ERROR",
TxAckStatus::DutyCycleOverflow => "DUTY_CYCLE_OVERFLOW",
}
.to_string()
}

@ -1,6 +1,6 @@
[package]
name = "backend"
version = "4.7.0"
version = "4.8.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
edition = "2018"
publish = false
@ -14,7 +14,7 @@ tracing = "0.1"
hex = "0.4"
rand = "0.8"
aes-kw = "0.2"
reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features = false }
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
chrono = { version = "0.4", features = ["serde"] }
tokio = { version = "1.36", features = ["macros" ] }
chirpstack_api = { path = "../api/rust", default-features = false, features = ["json"] }

@ -160,7 +160,7 @@ impl Client {
pl: &mut JoinReqPayload,
async_resp: Option<Receiver<Vec<u8>>>,
) -> Result<JoinAnsPayload> {
pl.base.sender_id = self.config.sender_id.clone();
pl.base.sender_id.clone_from(&self.config.sender_id);
pl.base.receiver_id = receiver_id;
pl.base.message_type = MessageType::JoinReq;
@ -174,8 +174,8 @@ impl Client {
pl: &mut RejoinReqPayload,
async_resp: Option<Receiver<Vec<u8>>>,
) -> Result<RejoinAnsPayload> {
pl.base.sender_id = self.config.sender_id.clone();
pl.base.receiver_id = self.config.receiver_id.clone();
pl.base.sender_id.clone_from(&self.config.sender_id);
pl.base.receiver_id.clone_from(&self.config.receiver_id);
pl.base.message_type = MessageType::RejoinReq;
let mut ans: RejoinAnsPayload = Default::default();
@ -188,8 +188,8 @@ impl Client {
pl: &mut AppSKeyReqPayload,
async_resp: Option<Receiver<Vec<u8>>>,
) -> Result<AppSKeyAnsPayload> {
pl.base.sender_id = self.config.sender_id.clone();
pl.base.receiver_id = self.config.receiver_id.clone();
pl.base.sender_id.clone_from(&self.config.sender_id);
pl.base.receiver_id.clone_from(&self.config.receiver_id);
pl.base.message_type = MessageType::AppSKeyReq;
let mut ans: AppSKeyAnsPayload = Default::default();
@ -203,8 +203,8 @@ impl Client {
pl: &mut PRStartReqPayload,
async_resp: Option<Receiver<Vec<u8>>>,
) -> Result<PRStartAnsPayload> {
pl.base.sender_id = self.config.sender_id.clone();
pl.base.receiver_id = self.config.receiver_id.clone();
pl.base.sender_id.clone_from(&self.config.sender_id);
pl.base.receiver_id.clone_from(&self.config.receiver_id);
pl.base.message_type = MessageType::PRStartReq;
let mut ans: PRStartAnsPayload = Default::default();
@ -223,8 +223,8 @@ impl Client {
pl: &mut PRStopReqPayload,
async_resp: Option<Receiver<Vec<u8>>>,
) -> Result<PRStopAnsPayload> {
pl.base.sender_id = self.config.sender_id.clone();
pl.base.receiver_id = self.config.receiver_id.clone();
pl.base.sender_id.clone_from(&self.config.sender_id);
pl.base.receiver_id.clone_from(&self.config.receiver_id);
pl.base.message_type = MessageType::PRStopReq;
let mut ans: PRStopAnsPayload = Default::default();
@ -243,7 +243,7 @@ impl Client {
pl: &mut HomeNSReqPayload,
async_resp: Option<Receiver<Vec<u8>>>,
) -> Result<HomeNSAnsPayload> {
pl.base.sender_id = self.config.sender_id.clone();
pl.base.sender_id.clone_from(&self.config.sender_id);
pl.base.receiver_id = receiver_id;
pl.base.message_type = MessageType::HomeNSReq;
@ -252,14 +252,18 @@ impl Client {
Ok(ans)
}
pub async fn home_ns_ans(&self, target_role: Role, pl: &HomeNSAnsPayload) -> Result<()> {
self.response_request(Some(target_role), pl).await
}
pub async fn xmit_data_req(
&self,
target_role: Role,
pl: &mut XmitDataReqPayload,
async_resp: Option<Receiver<Vec<u8>>>,
) -> Result<XmitDataAnsPayload> {
pl.base.sender_id = self.config.sender_id.clone();
pl.base.receiver_id = self.config.receiver_id.clone();
pl.base.sender_id.clone_from(&self.config.sender_id);
pl.base.receiver_id.clone_from(&self.config.receiver_id);
pl.base.message_type = MessageType::XmitDataReq;
let mut ans: XmitDataAnsPayload = Default::default();
@ -372,7 +376,7 @@ impl Client {
};
let body = serde_json::to_string(&pl)?;
be_req_log.request_body = body.clone();
be_req_log.request_body.clone_from(&body);
info!(server = %server, async_interface = %async_resp.is_some(), "Making request");
@ -403,7 +407,7 @@ impl Client {
None => res.text().await?,
};
be_req_log.response_body = resp_json.clone();
be_req_log.response_body.clone_from(&resp_json);
let base: BasePayloadResult = serde_json::from_str(&resp_json)?;
be_req_log.result_code = format!("{:?}", base.result.result_code);

@ -9,7 +9,7 @@
repository = "https://github.com/chirpstack/chirpstack"
[dependencies]
chirpstack_api = { path = "../api/rust", version = "4.7.0-test.3" }
chirpstack_api = { path = "../api/rust", version = "4.8.0" }
redis = { version = "0.25", features = [
"cluster-async",
"tokio-rustls-comp",

@ -3,7 +3,7 @@ name = "chirpstack"
description = "ChirpStack is an open-source LoRaWAN(TM) Network Server"
repository = "https://github.com/chirpstack/chirpstack"
homepage = "https://www.chirpstack.io/"
version = "4.7.0"
version = "4.8.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
edition = "2021"
publish = false
@ -38,7 +38,7 @@ tokio-postgres = "0.7"
tokio-postgres-rustls = "0.11"
bigdecimal = "0.4"
redis = { version = "0.25.2", features = [ "tls-rustls", "tokio-rustls-comp"] }
deadpool-redis = { version = "0.14", features = ["cluster"] }
deadpool-redis = { version = "0.15", features = ["cluster"] }
# Logging
tracing = "0.1"
@ -54,13 +54,13 @@ lrwn = { path = "../lrwn", features = ["serde", "diesel", "regions", "crypto"] }
backend = { path = "../backend" }
# HTTP
reqwest = { version = "0.11", features = [
reqwest = { version = "0.12", features = [
"json",
"rustls-tls-native-roots",
], default-features = false }
# Integrations
aws-sign-v4 = "0.2"
aws-sign-v4 = "0.3"
hmac = "0.12"
sha2 = "0.10"
urlencoding = "2.1"
@ -104,10 +104,12 @@ jsonwebtoken = "9.2"
rustls = "0.22"
rustls-native-certs = "0.7"
rustls-pemfile = "2.1"
pem = "3.0"
x509-parser = "0.16"
rsa = "0.9"
elliptic-curve = { version = "0.13", features = ["pem"] }
p256 = "0.13"
rcgen = { version = "0.12", features = [ "x509-parser" ] }
rcgen = { version = "0.13.1", features = [ "x509-parser" ] }
openidconnect = { version = "3.5", features = ["accept-rfc3339-timestamps"] }
oauth2 = "4.4"
@ -116,7 +118,7 @@ rumqttc = { version = "0.24", features = ["url"] }
hex = "0.4"
# Codecs
rquickjs = { version = "0.5", features = [
rquickjs = { version = "0.6", features = [
"bindgen",
"loader",
"array-buffer",

@ -1,7 +1,7 @@
alter table multicast_group
alter column class_b_ping_slot_nb_k type integer;
update multicast_group set class_b_ping_slot_nb_k = pow(2, class_b_ping_slot_nb_k) * 32;
update multicast_group set class_b_ping_slot_nb_k = coalesce(pow(2, nullif(class_b_ping_slot_nb_k, 0)) * 32, 0);
alter table multicast_group
rename column class_b_ping_slot_nb_k to class_b_ping_slot_period;

@ -1,7 +1,7 @@
alter table multicast_group
rename column class_b_ping_slot_period to class_b_ping_slot_nb_k;
update multicast_group set class_b_ping_slot_nb_k = log(2, class_b_ping_slot_nb_k / 32);
update multicast_group set class_b_ping_slot_nb_k = coalesce(log(2, nullif(class_b_ping_slot_nb_k, 0) / 32), 0);
alter table multicast_group
alter column class_b_ping_slot_nb_k type smallint;

@ -0,0 +1,2 @@
alter table device_profile
drop column rx1_delay;

@ -0,0 +1,5 @@
alter table device_profile
add column rx1_delay smallint not null default 0;
alter table device_profile
alter column rx1_delay drop default;

@ -18,9 +18,10 @@ impl Plugin {
let script = fs::read_to_string(file_path).context("Read ADR plugin")?;
let (id, name) = ctx.with::<_, Result<(String, String)>>(|ctx| {
let m = ctx
.compile("script", script.clone())
.context("Compile script")?;
let m = rquickjs::Module::declare(ctx, "script", script.clone())
.context("Declare script")?;
let (m, m_promise) = m.eval().context("Evaluate script")?;
m_promise.finish()?;
let id_func: rquickjs::Function = m.get("id").context("Get id function")?;
let name_func: rquickjs::Function = m.get("name").context("Get name function")?;
@ -51,10 +52,10 @@ impl Handler for Plugin {
let ctx = rquickjs::Context::full(&rt)?;
ctx.with::<_, Result<Response>>(|ctx| {
let m = ctx
.clone()
.compile("script", self.script.clone())
.context("Compile script")?;
let m = rquickjs::Module::declare(ctx.clone(), "script", self.script.clone())
.context("Declare script")?;
let (m, m_promise) = m.eval().context("Evaluate script")?;
m_promise.finish()?;
let func: rquickjs::Function = m.get("handle").context("Get handle function")?;
let device_variables = rquickjs::Object::new(ctx.clone())?;

@ -163,6 +163,7 @@ pub async fn _handle_request(bp: BasePayload, b: Vec<u8>) -> http::Response<hype
MessageType::PRStartReq => handle_pr_start_req(sender_client, bp, &b).await,
MessageType::PRStopReq => handle_pr_stop_req(sender_client, bp, &b).await,
MessageType::XmitDataReq => handle_xmit_data_req(sender_client, bp, &b).await,
MessageType::HomeNSReq => handle_home_ns_req(sender_client, bp, &b).await,
// Unknown message
_ => warp::reply::with_status(
"Handler for {:?} is not implemented",
@ -523,6 +524,68 @@ async fn _handle_xmit_data_req(
})
}
async fn handle_home_ns_req(
sender_client: Arc<backend::Client>,
bp: backend::BasePayload,
b: &[u8],
) -> http::Response<hyper::Body> {
let pl: backend::HomeNSReqPayload = match serde_json::from_slice(b) {
Ok(v) => v,
Err(e) => {
let ans = err_to_response(anyhow::Error::new(e), &bp);
log_request_response(&bp, b, &ans).await;
return warp::reply::json(&ans).into_response();
}
};
if sender_client.is_async() {
let b = b.to_vec();
task::spawn(async move {
let ans = match _handle_home_ns_req(pl).await {
Ok(v) => v,
Err(e) => {
let msg = e.to_string();
backend::HomeNSAnsPayload {
base: bp.to_base_payload_result(err_to_result_code(e), &msg),
h_net_id: Vec::new(),
}
}
};
log_request_response(&bp, &b, &ans).await;
if let Err(e) = sender_client.home_ns_ans(backend::Role::FNS, &ans).await {
error!(error = %e.full(), "Send async HomeNSAns error");
}
});
warp::reply::with_status("", StatusCode::OK).into_response()
} else {
match _handle_home_ns_req(pl).await {
Ok(ans) => {
log_request_response(&bp, b, &ans).await;
warp::reply::json(&ans).into_response()
}
Err(e) => {
let ans = err_to_response(e, &bp);
log_request_response(&bp, b, &ans).await;
warp::reply::json(&ans).into_response()
}
}
}
}
async fn _handle_home_ns_req(pl: backend::HomeNSReqPayload) -> Result<backend::HomeNSAnsPayload> {
let conf = config::get();
Ok(backend::HomeNSAnsPayload {
base: pl
.base
.to_base_payload_result(backend::ResultCode::Success, ""),
h_net_id: conf.network.net_id.to_vec(),
})
}
async fn handle_async_ans(bp: &BasePayload, b: &[u8]) -> Result<http::Response<hyper::Body>> {
let transaction_id = bp.transaction_id;

@ -113,6 +113,7 @@ impl DeviceProfileService for DeviceProfile {
as i16,
relay_overall_limit_bucket_size: req_dp.relay_overall_limit_bucket_size as i16,
allow_roaming: req_dp.allow_roaming,
rx1_delay: req_dp.rx1_delay as i16,
..Default::default()
};
@ -214,6 +215,7 @@ impl DeviceProfileService for DeviceProfile {
as u32,
relay_overall_limit_bucket_size: dp.relay_overall_limit_bucket_size as u32,
allow_roaming: dp.allow_roaming,
rx1_delay: dp.rx1_delay as u32,
}),
created_at: Some(helpers::datetime_to_prost_timestamp(&dp.created_at)),
updated_at: Some(helpers::datetime_to_prost_timestamp(&dp.updated_at)),
@ -313,6 +315,7 @@ impl DeviceProfileService for DeviceProfile {
as i16,
relay_overall_limit_bucket_size: req_dp.relay_overall_limit_bucket_size as i16,
allow_roaming: req_dp.allow_roaming,
rx1_delay: req_dp.rx1_delay as i16,
..Default::default()
})
.await

@ -614,11 +614,180 @@ impl GatewayService for Gateway {
Ok(resp)
}
async fn get_duty_cycle_metrics(
&self,
request: Request<api::GetGatewayDutyCycleMetricsRequest>,
) -> Result<Response<api::GetGatewayDutyCycleMetricsResponse>, Status> {
let req = request.get_ref();
let gateway_id = EUI64::from_str(&req.gateway_id).map_err(|e| e.status())?;
self.validator
.validate(
request.extensions(),
validator::ValidateGatewayAccess::new(validator::Flag::Read, gateway_id),
)
.await?;
let start = SystemTime::try_from(
req.start
.as_ref()
.ok_or_else(|| anyhow!("start is None"))
.map_err(|e| e.status())?
.clone(),
)
.map_err(|e| e.status())?;
let end = SystemTime::try_from(
req.end
.as_ref()
.ok_or_else(|| anyhow!("end is None"))
.map_err(|e| e.status())?
.clone(),
)
.map_err(|e| e.status())?;
let start: DateTime<Local> = start.into();
let end: DateTime<Local> = end.into();
let dc_metrics = metrics::get(
&format!("gw:dc:{}", gateway_id),
metrics::Kind::COUNTER,
metrics::Aggregation::MINUTE,
start,
end,
)
.await
.map_err(|e| e.status())?;
let out = api::GetGatewayDutyCycleMetricsResponse {
max_load_percentage: Some({
// discover all data-sets
let mut datasets: HashSet<String> = HashSet::new();
for m in &dc_metrics {
for k in m.metrics.keys() {
if k.starts_with("max_load_perc_") {
datasets.insert(k.to_string());
}
}
}
common::Metric {
name: "Percentage of max tx duty-cycle".into(),
timestamps: dc_metrics
.iter()
.map(|row| {
let ts: DateTime<Utc> = row.time.into();
let ts: pbjson_types::Timestamp = ts.into();
ts
})
.collect(),
datasets: datasets
.iter()
.map(|key| common::MetricDataset {
label: {
let s = key.strip_prefix("max_load_perc_").unwrap_or_default();
let s: Vec<&str> = s.split('_').collect();
format!(
"{} ({:.2}MHz - {:.2}MHz: {:.2}%)",
s.first().unwrap_or(&""),
s.get(1)
.unwrap_or(&"")
.parse::<f64>()
.map(|v| v / 1_000_000.0)
.unwrap_or(0.0),
s.get(2)
.unwrap_or(&"")
.parse::<f64>()
.map(|v| v / 1_000_000.0)
.unwrap_or(0.0),
s.get(3)
.unwrap_or(&"")
.parse::<f64>()
.map(|v| v / 10.0)
.unwrap_or(0.0),
)
},
data: dc_metrics
.iter()
.map(|row| row.metrics.get(key).cloned().unwrap_or(0.0) as f32)
.collect(),
})
.collect(),
kind: common::MetricKind::Absolute.into(),
}
}),
window_percentage: Some({
// discover all data-sets
let mut datasets: HashSet<String> = HashSet::new();
for m in &dc_metrics {
for k in m.metrics.keys() {
if k.starts_with("window_perc_") {
datasets.insert(k.to_string());
}
}
}
common::Metric {
name: "Tx duty-cycle".into(),
timestamps: dc_metrics
.iter()
.map(|row| {
let ts: DateTime<Utc> = row.time.into();
let ts: pbjson_types::Timestamp = ts.into();
ts
})
.collect(),
datasets: datasets
.iter()
.map(|key| common::MetricDataset {
label: {
let s = key.strip_prefix("window_perc_").unwrap_or_default();
let s: Vec<&str> = s.split('_').collect();
format!(
"{} ({:.2}MHz - {:.2}MHz: {:.2}%)",
s.first().unwrap_or(&""),
s.get(1)
.unwrap_or(&"")
.parse::<f64>()
.map(|v| v / 1_000_000.0)
.unwrap_or(0.0),
s.get(2)
.unwrap_or(&"")
.parse::<f64>()
.map(|v| v / 1_000_000.0)
.unwrap_or(0.0),
s.get(3)
.unwrap_or(&"")
.parse::<f64>()
.map(|v| v / 10.0)
.unwrap_or(0.0),
)
},
data: dc_metrics
.iter()
.map(|row| row.metrics.get(key).cloned().unwrap_or(0.0) as f32)
.collect(),
})
.collect(),
kind: common::MetricKind::Absolute.into(),
}
}),
};
let mut resp = Response::new(out);
resp.metadata_mut()
.insert("x-log-gateway_id", req.gateway_id.parse().unwrap());
Ok(resp)
}
}
#[cfg(test)]
pub mod test {
use chrono::{Datelike, Local, TimeZone};
use chrono::{Datelike, Local, TimeZone, Timelike};
use std::collections::HashMap;
use super::*;
@ -823,7 +992,13 @@ pub mod test {
m.metrics.insert("tx_freq_868200000".into(), 5.0);
m.metrics.insert("tx_dr_4".into(), 5.0);
metrics::save("gw:0102030405060708", &m).await.unwrap();
metrics::save(
"gw:0102030405060708",
&m,
&metrics::Aggregation::default_aggregations(),
)
.await
.unwrap();
// setup api
let service = Gateway::new(RequestValidator::new());
@ -862,4 +1037,130 @@ pub mod test {
stats_resp.rx_packets
);
}
#[tokio::test]
async fn test_gateway_duty_cycle_stats() {
let _guard = test::prepare().await;
// setup admin user
let u = user::User {
is_admin: true,
is_active: true,
email: "admin@admin".into(),
email_verified: true,
..Default::default()
};
let u = user::create(u).await.unwrap();
// create tenant
let t = tenant::create(tenant::Tenant {
name: "test-tenant".into(),
can_have_gateways: true,
max_gateway_count: 10,
..Default::default()
})
.await
.unwrap();
// create gateway
let _ = gateway::create(gateway::Gateway {
gateway_id: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
tenant_id: t.id.clone(),
name: "test-gw".into(),
..Default::default()
})
.await
.unwrap();
let now = Local::now();
// insert stats
let mut m = metrics::Record {
kind: metrics::Kind::COUNTER,
time: now.into(),
metrics: HashMap::new(),
};
m.metrics
.insert("window_perc_M_868000000_868600000_10".into(), 0.5);
m.metrics
.insert("max_load_perc_L_865000000_868000000_10".into(), 5.0);
metrics::save(
"gw:dc:0102030405060708",
&m,
&[metrics::Aggregation::MINUTE],
)
.await
.unwrap();
// setup api
let service = Gateway::new(RequestValidator::new());
// request stats
let now_st: SystemTime = now.into();
let stats_req = api::GetGatewayDutyCycleMetricsRequest {
gateway_id: "0102030405060708".into(),
start: Some(now_st.into()),
end: Some(now_st.into()),
};
let mut stats_req = Request::new(stats_req);
stats_req
.extensions_mut()
.insert(AuthID::User(u.id.clone()));
let stats_resp = service.get_duty_cycle_metrics(stats_req).await.unwrap();
let stats_resp = stats_resp.get_ref();
assert_eq!(
Some(common::Metric {
name: "Percentage of max tx duty-cycle".into(),
timestamps: vec![{
let ts = Local
.with_ymd_and_hms(
now.year(),
now.month(),
now.day(),
now.hour(),
now.minute(),
0,
)
.unwrap();
//let ts: SystemTime = ts.into();
let ts: DateTime<Utc> = ts.into();
ts.into()
}],
datasets: vec![common::MetricDataset {
label: "L (865.00MHz - 868.00MHz: 1.00%)".into(),
data: vec![5.0],
}],
kind: common::MetricKind::Absolute.into(),
}),
stats_resp.max_load_percentage
);
assert_eq!(
Some(common::Metric {
name: "Tx duty-cycle".into(),
timestamps: vec![{
let ts = Local
.with_ymd_and_hms(
now.year(),
now.month(),
now.day(),
now.hour(),
now.minute(),
0,
)
.unwrap();
//let ts: SystemTime = ts.into();
let ts: DateTime<Utc> = ts.into();
ts.into()
}],
datasets: vec![common::MetricDataset {
label: "M (868.00MHz - 868.60MHz: 1.00%)".into(),
data: vec![0.5],
}],
kind: common::MetricKind::Absolute.into(),
}),
stats_resp.window_percentage
);
}
}

@ -66,6 +66,7 @@ impl FromProto<Revision> for common::RegParamsRevision {
common::RegParamsRevision::Rp002101 => Revision::RP002_1_0_1,
common::RegParamsRevision::Rp002102 => Revision::RP002_1_0_2,
common::RegParamsRevision::Rp002103 => Revision::RP002_1_0_3,
common::RegParamsRevision::Rp002104 => Revision::RP002_1_0_4,
}
}
}
@ -78,7 +79,8 @@ impl ToProto<common::RegParamsRevision> for Revision {
Revision::RP002_1_0_0 => common::RegParamsRevision::Rp002100,
Revision::RP002_1_0_1 => common::RegParamsRevision::Rp002101,
Revision::RP002_1_0_2 => common::RegParamsRevision::Rp002102,
Revision::RP002_1_0_3 | Revision::Latest => common::RegParamsRevision::Rp002103,
Revision::RP002_1_0_3 => common::RegParamsRevision::Rp002103,
Revision::RP002_1_0_4 | Revision::Latest => common::RegParamsRevision::Rp002104,
}
}
}
@ -169,6 +171,7 @@ impl FromProto<MeasurementKind> for api::MeasurementKind {
impl ToProto<common::Aggregation> for Aggregation {
fn to_proto(self) -> common::Aggregation {
match self {
Aggregation::MINUTE => common::Aggregation::Minute,
Aggregation::HOUR => common::Aggregation::Hour,
Aggregation::DAY => common::Aggregation::Day,
Aggregation::MONTH => common::Aggregation::Month,
@ -179,6 +182,7 @@ impl ToProto<common::Aggregation> for Aggregation {
impl FromProto<Aggregation> for common::Aggregation {
fn from_proto(self) -> Aggregation {
match self {
common::Aggregation::Minute => Aggregation::MINUTE,
common::Aggregation::Hour => Aggregation::HOUR,
common::Aggregation::Day => Aggregation::DAY,
common::Aggregation::Month => Aggregation::MONTH,

@ -583,7 +583,7 @@ impl InternalService for Internal {
// update the user
// in case it was fetched using the external id, this will make sure we sync with any
// possible email change.
u.email = oauth_user.email.clone();
u.email.clone_from(&oauth_user.email);
u.email_verified = email_verified;
let u = user::update(u).await.map_err(|e| e.status())?;
@ -933,14 +933,14 @@ impl InternalService for Internal {
for region_conf in &conf.regions {
if req.id == region_conf.id {
out.id = region_conf.id.clone();
out.id.clone_from(&region_conf.id);
out.description = if region_conf.description.is_empty() {
region_conf.id.clone()
} else {
region_conf.description.clone()
};
out.region = region_conf.common_name.to_proto().into();
out.user_info = region_conf.user_info.clone();
out.user_info.clone_from(&region_conf.user_info);
out.rx1_delay = region_conf.network.rx1_delay as u32;
out.rx1_dr_offset = region_conf.network.rx1_dr_offset as u32;
out.rx2_dr = region_conf.network.rx2_dr as u32;

@ -3,6 +3,7 @@ use std::time::SystemTime;
use anyhow::{Context, Result};
use rcgen::{
Certificate, CertificateParams, DnType, ExtendedKeyUsagePurpose, KeyPair, KeyUsagePurpose,
SignatureAlgorithm,
};
use tokio::fs;
use uuid::Uuid;
@ -11,8 +12,14 @@ use crate::config;
use crate::helpers::tls::private_key_to_pkcs8;
use lrwn::EUI64;
fn gen_client_cert(id: &str, not_before: SystemTime, not_after: SystemTime) -> Result<Certificate> {
let mut params = CertificateParams::new(vec![id.to_string()]);
fn gen_client_cert(
id: &str,
not_before: SystemTime,
not_after: SystemTime,
issuer: &Certificate,
issuer_key: &KeyPair,
) -> Result<(Certificate, KeyPair)> {
let mut params = CertificateParams::new(vec![id.to_string()])?;
params
.distinguished_name
.push(DnType::CommonName, id.to_string());
@ -24,10 +31,11 @@ fn gen_client_cert(id: &str, not_before: SystemTime, not_after: SystemTime) -> R
.extended_key_usages
.push(ExtendedKeyUsagePurpose::ClientAuth);
Ok(Certificate::from_params(params)?)
let kp = KeyPair::generate()?;
Ok((params.signed_by(&kp, issuer, issuer_key)?, kp))
}
async fn get_ca_cert(ca_cert_file: &str, ca_key_file: &str) -> Result<Certificate> {
async fn get_ca_cert(ca_cert_file: &str, ca_key_file: &str) -> Result<(Certificate, KeyPair)> {
let ca_cert_s = fs::read_to_string(ca_cert_file)
.await
.context("Read gateway ca_cert")?;
@ -35,19 +43,14 @@ async fn get_ca_cert(ca_cert_file: &str, ca_key_file: &str) -> Result<Certificat
.await
.context("Read gateway ca_key")?;
let ca_key_s = private_key_to_pkcs8(&ca_key_s)?;
let ca_key_algo = read_algo(&ca_cert_s)?;
let ca_key = KeyPair::from_pem(&ca_key_s).context("Parse gateway CA key")?;
let params = CertificateParams::from_ca_cert_pem(&ca_cert_s, ca_key)
.context("Parse gateway CA certificate")?;
// Workaround for:
// https://github.com/rustls/rcgen/issues/193
let ca_key =
KeyPair::from_pem_and_sign_algo(&ca_key_s, params.alg).context("Parse gateway CA key")?;
let params = CertificateParams::from_ca_cert_pem(&ca_cert_s, ca_key)
.context("Parse gateway CA certificate")?;
KeyPair::from_pem_and_sign_algo(&ca_key_s, ca_key_algo).context("Parse gateway CA key")?;
let params =
CertificateParams::from_ca_cert_pem(&ca_cert_s).context("Parse gateway CA certificate")?;
Certificate::from_params(params).context("Init Certificate struct")
Ok((params.self_signed(&ca_key)?, ca_key))
}
// This returns the CA, certificate and private-key as PEM encoded strings.
@ -55,21 +58,25 @@ pub async fn client_cert_for_gateway_id(
gateway_id: &EUI64,
) -> Result<(SystemTime, String, String, String)> {
let conf = config::get();
let ca_cert = get_ca_cert(&conf.gateway.ca_cert, &conf.gateway.ca_key)
let (ca_cert, ca_key) = get_ca_cert(&conf.gateway.ca_cert, &conf.gateway.ca_key)
.await
.context("Get CA cert")?;
let not_before = SystemTime::now();
let not_after = SystemTime::now() + conf.gateway.client_cert_lifetime;
let gw_cert = gen_client_cert(&gateway_id.to_string(), not_before, not_after)
.context("Generate client certificate")?;
let (gw_cert, gw_key) = gen_client_cert(
&gateway_id.to_string(),
not_before,
not_after,
&ca_cert,
&ca_key,
)
.context("Generate client certificate")?;
Ok((
not_after,
ca_cert.serialize_pem().context("Serialize CA cert")?,
gw_cert
.serialize_pem_with_signer(&ca_cert)
.context("Serialize client cert")?,
gw_cert.serialize_private_key_pem(),
ca_cert.pem(),
gw_cert.pem(),
gw_key.serialize_pem(),
))
}
@ -77,19 +84,40 @@ pub async fn client_cert_for_application_id(
application_id: &Uuid,
) -> Result<(SystemTime, String, String, String)> {
let conf = config::get();
let ca_cert = get_ca_cert(
let (ca_cert, ca_key) = get_ca_cert(
&conf.integration.mqtt.client.ca_cert,
&conf.integration.mqtt.client.ca_key,
)
.await?;
let not_before = SystemTime::now();
let not_after = SystemTime::now() + conf.integration.mqtt.client.client_cert_lifetime;
let app_cert = gen_client_cert(&application_id.to_string(), not_before, not_after)?;
let (app_cert, app_key) = gen_client_cert(
&application_id.to_string(),
not_before,
not_after,
&ca_cert,
&ca_key,
)?;
Ok((
not_after,
ca_cert.serialize_pem()?,
app_cert.serialize_pem_with_signer(&ca_cert)?,
app_cert.serialize_private_key_pem(),
ca_cert.pem(),
app_cert.pem(),
app_key.serialize_pem(),
))
}
// we are using String here, because else we run into lifetime issues.
fn read_algo(cert: &str) -> Result<&'static SignatureAlgorithm> {
let cert = pem::parse(cert).context("Parse PEM")?;
let (_remainder, x509) =
x509_parser::parse_x509_certificate(cert.contents()).context("Parse x509")?;
let alg_oid = x509
.signature_algorithm
.algorithm
.iter()
.ok_or_else(|| anyhow!("Parse certificate error"))?
.collect::<Vec<_>>();
Ok(SignatureAlgorithm::from_oid(&alg_oid)?)
}

@ -785,6 +785,50 @@ pub fn run() {
# Resolve NetID domain suffix.
resolve_net_id_domain_suffix="{{ backend_interfaces.resolve_net_id_domain_suffix }}"
# Default roaming server.
[roaming.default]
# Enable default roaming server.
enabled={{roaming.default.enabled}}
# Async timeout (set to 0 to disable async interface).
async_timeout="{{roaming.default.async_timeout}}"
# Passive-roaming session lifetime (set to 0 for stateless).
passive_roaming_lifetime="{{roaming.default.passive_roaming_lifetime}}"
# Passive-roaming KEK label (optional).
#
# If set, the session-keys will be encrypted using the given KEK.
passive_roaming_kek_label="{{roaming.default.passive_roaming_kek_label}}"
# Server.
#
# If set, this will bypass the DNS resolving of the server.
server="{{roaming.default.server}}"
# Use target role suffix.
#
# Depending the context of the remote server, this will add
# the /sns or /fns path to the server endpoint.
use_target_role_suffix={{roaming.default.use_target_role_suffix}}
# CA certificate (path).
ca_cert="{{roaming.default.ca_cert}}"
# TLS certificate (path).
tls_cert="{{roaming.default.tls_cert}}"
# TLS key (PKCS#8) (path).
tls_key="{{roaming.default.tls_key}}"
# Authorization header.
#
# Optional value of the Authorization header, e.g. token or password.
authorization_header="{{roaming.default.authorization_header}}"
# Per server roaming configuration (this can be repeated).
# Example:
# [[roaming.servers]]

@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::time::SystemTime;
use anyhow::Result;
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use rquickjs::{CatchResultExt, IntoJs};
@ -50,13 +50,17 @@ pub async fn decode(
let out = ctx.with(|ctx| -> Result<pbjson_types::Struct> {
// We need to export the Buffer class, as eval / eval_with_options
// does not allow using import statement.
let buff: rquickjs::Module = ctx.clone().compile(
let buff = rquickjs::Module::declare(
ctx.clone(),
"b",
r#"
import { Buffer } from "buffer";
export { Buffer }
"#,
)?;
)
.context("Declare script")?;
let (buff, buff_promise) = buff.eval().context("Evalulate script")?;
buff_promise.finish()?;
let buff: rquickjs::Function = buff.get("Buffer")?;
let input = rquickjs::Object::new(ctx.clone())?;
@ -69,14 +73,11 @@ pub async fn decode(
globals.set("chirpstack_input", input)?;
globals.set("Buffer", buff)?;
let mut eval_options = rquickjs::context::EvalOptions::default();
eval_options.strict = false;
let res: rquickjs::Object = ctx
.eval_with_options(
script,
rquickjs::context::EvalOptions {
strict: false,
..Default::default()
},
)
.eval_with_options(script, eval_options)
.catch(&ctx)
.map_err(|e| anyhow!("JS error: {}", e))?;
@ -137,13 +138,17 @@ pub async fn encode(
ctx.with(|ctx| {
// We need to export the Buffer class, as eval / eval_with_options
// does not allow using import statement.
let buff: rquickjs::Module = ctx.clone().compile(
let buff = rquickjs::Module::declare(
ctx.clone(),
"b",
r#"
import { Buffer } from "buffer";
export { Buffer }
"#,
)?;
)
.context("Declare script")?;
let (buff, buff_promise) = buff.eval().context("Evaluate script")?;
buff_promise.finish()?;
let buff: rquickjs::Function = buff.get("Buffer")?;
let input = rquickjs::Object::new(ctx.clone())?;
@ -155,14 +160,11 @@ pub async fn encode(
globals.set("chirpstack_input", input)?;
globals.set("Buffer", buff)?;
let mut eval_options = rquickjs::context::EvalOptions::default();
eval_options.strict = false;
let res: rquickjs::Object = ctx
.eval_with_options(
script,
rquickjs::context::EvalOptions {
strict: false,
..Default::default()
},
)
.eval_with_options(script, eval_options)
.catch(&ctx)
.map_err(|e| anyhow!("JS error: {}", e))?;

@ -1,3 +1,4 @@
use std::cmp;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
@ -1437,9 +1438,14 @@ impl Data {
self.mac_commands.push(set);
}
let rx1_delay = ds.rx1_delay as u8;
if rx1_delay != self.network_conf.rx1_delay {
let set = maccommand::rx_timing_setup::request(self.network_conf.rx1_delay);
let dev_rx1_delay = ds.rx1_delay as u8;
let req_rx1_delay = cmp::max(
self.network_conf.rx1_delay,
self.device_profile.rx1_delay as u8,
);
if dev_rx1_delay != req_rx1_delay {
let set = maccommand::rx_timing_setup::request(req_rx1_delay);
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::RxTimingSetupReq, &set)
.await?;
self.mac_commands.push(set);

@ -76,7 +76,9 @@ impl Data {
.cloned()
.ok_or_else(|| anyhow!("rx_info is empty"))?;
self.downlink_frame.gateway_id = rx_info.gateway_id.clone();
self.downlink_frame
.gateway_id
.clone_from(&rx_info.gateway_id);
if self.dl_meta_data.dl_freq_1.is_some()
&& self.dl_meta_data.data_rate_1.is_some()
&& self.dl_meta_data.rx_delay_1.is_some()

@ -452,7 +452,7 @@ impl JoinAccept<'_> {
let phy_b = self.join_accept.to_vec()?;
for i in &mut self.downlink_frame.items {
i.phy_payload = phy_b.clone();
i.phy_payload.clone_from(&phy_b);
}
Ok(())
@ -507,7 +507,7 @@ impl JoinAccept<'_> {
let relay_phy_b = relay_phy.to_vec()?;
for i in &mut self.downlink_frame.items {
i.phy_payload = relay_phy_b.clone();
i.phy_payload.clone_from(&relay_phy_b);
}
Ok(())

@ -185,10 +185,16 @@ impl<'a> MqttBackend<'a> {
} else {
conf.event_topic.clone()
};
let event_topic = format!("$share/{}/{}", conf.share_name, event_topic);
let share_name = conf.share_name.clone();
async move {
while connect_rx.recv().await.is_some() {
while let Some(shared_sub_support) = connect_rx.recv().await {
let event_topic = if shared_sub_support {
format!("$share/{}/{}", share_name, event_topic)
} else {
event_topic.clone()
};
info!(region_id = %region_config_id, event_topic = %event_topic, "Subscribing to gateway event topic");
if let Err(e) = client.subscribe(&event_topic, qos).await {
error!(region_id = %region_config_id, event_topic = %event_topic, error = %e, "MQTT subscribe error");
@ -222,7 +228,18 @@ impl<'a> MqttBackend<'a> {
}
Event::Incoming(Incoming::ConnAck(v)) => {
if v.code == ConnectReturnCode::Success {
if let Err(e) = connect_tx.try_send(()) {
// Per specification:
// A value of 1 means Shared Subscriptions are supported. If not present, then Shared Subscriptions are supported.
let shared_sub_support = v
.properties
.map(|v| {
v.shared_subscription_available
.map(|v| v == 1)
.unwrap_or(true)
})
.unwrap_or(true);
if let Err(e) = connect_tx.try_send(shared_sub_support) {
error!(error = %e, "Send to subscribe channel error");
}
} else {

@ -121,6 +121,7 @@ pub struct DeviceChangeset {
pub external_power_source: Option<bool>,
pub battery_level: Option<Option<BigDecimal>>,
pub scheduler_run_after: Option<Option<DateTime<Utc>>>,
pub is_disabled: Option<bool>,
}
impl Device {
@ -699,6 +700,7 @@ pub async fn get_with_class_b_c_queue_items(limit: usize) -> Result<Vec<Device>>
where
d.enabled_class in ('B', 'C')
and (d.scheduler_run_after is null or d.scheduler_run_after < $2)
and d.is_disabled = false
and exists (
select
1
@ -961,11 +963,26 @@ pub mod test {
let res = get_with_class_b_c_queue_items(10).await.unwrap();
assert_eq!(0, res.len());
// Class-C item pending, but device is disabled.
let d = partial_update(
d.dev_eui,
&DeviceChangeset {
scheduler_run_after: Some(None),
is_disabled: Some(true),
..Default::default()
},
)
.await
.unwrap();
let res = get_with_class_b_c_queue_items(10).await.unwrap();
assert_eq!(0, res.len());
// device in class C / downlink is pending.
let _ = partial_update(
d.dev_eui,
&DeviceChangeset {
scheduler_run_after: Some(None),
is_disabled: Some(false),
..Default::default()
},
)

@ -73,6 +73,7 @@ pub struct DeviceProfile {
pub relay_global_uplink_limit_bucket_size: i16,
pub relay_overall_limit_bucket_size: i16,
pub allow_roaming: bool,
pub rx1_delay: i16,
}
impl DeviceProfile {
@ -80,6 +81,11 @@ impl DeviceProfile {
if self.name.is_empty() {
return Err(Error::Validation("name is not set".into()));
}
if self.rx1_delay < 0 || self.rx1_delay > 15 {
return Err(Error::Validation("RX1 Delay must be between 0 - 15".into()));
}
Ok(())
}
}
@ -143,6 +149,7 @@ impl Default for DeviceProfile {
relay_global_uplink_limit_bucket_size: 0,
relay_overall_limit_bucket_size: 0,
allow_roaming: false,
rx1_delay: 0,
}
}
}
@ -279,6 +286,7 @@ pub async fn update(dp: DeviceProfile) -> Result<DeviceProfile, Error> {
.eq(&dp.relay_global_uplink_limit_bucket_size),
device_profile::relay_overall_limit_bucket_size.eq(&dp.relay_overall_limit_bucket_size),
device_profile::allow_roaming.eq(&dp.allow_roaming),
device_profile::rx1_delay.eq(&dp.rx1_delay),
))
.get_result(&mut get_async_db_conn().await?)
.await

@ -3,7 +3,10 @@ use std::fmt;
use std::time::Duration;
use anyhow::Result;
use chrono::{DateTime, Datelike, Duration as ChronoDuration, Local, TimeZone, Timelike};
use chrono::{
DateTime, Datelike, Duration as ChronoDuration, Local, Months, NaiveDate, NaiveDateTime,
Timelike,
};
use serde::{Deserialize, Serialize};
use tracing::info;
@ -13,11 +16,18 @@ use crate::storage::{get_async_redis_conn, redis_key};
#[allow(non_camel_case_types)]
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Eq, PartialEq)]
pub enum Aggregation {
MINUTE,
HOUR,
DAY,
MONTH,
}
impl Aggregation {
pub fn default_aggregations() -> Vec<Aggregation> {
vec![Aggregation::HOUR, Aggregation::DAY, Aggregation::MONTH]
}
}
impl fmt::Display for Aggregation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
@ -48,17 +58,14 @@ pub struct Record {
fn get_ttl(a: Aggregation) -> Duration {
match a {
Aggregation::MINUTE => Duration::from_secs(60 * 60 * 2), // two hours
Aggregation::HOUR => Duration::from_secs(60 * 60 * 24 * 2), // two days
Aggregation::DAY => Duration::from_secs(60 * 60 * 24 * 31 * 2), // two months
Aggregation::MONTH => Duration::from_secs(60 * 60 * 24 * 365 * 2), // two years
}
}
fn get_aggregations() -> Vec<Aggregation> {
vec![Aggregation::HOUR, Aggregation::DAY, Aggregation::MONTH]
}
fn get_key(name: &str, a: Aggregation, dt: DateTime<Local>) -> String {
fn get_key(name: &str, a: Aggregation, dt: NaiveDateTime) -> String {
redis_key(format!(
"metrics:{{{}}}:{}:{}",
name,
@ -82,7 +89,7 @@ pub async fn save_state(name: &str, state: &str) -> Result<()> {
Ok(())
}
pub async fn save(name: &str, record: &Record) -> Result<()> {
pub async fn save(name: &str, record: &Record, aggregations: &[Aggregation]) -> Result<()> {
if record.metrics.is_empty() {
return Ok(());
}
@ -90,36 +97,37 @@ pub async fn save(name: &str, record: &Record) -> Result<()> {
let mut pipe = redis::pipe();
pipe.atomic();
for a in get_aggregations() {
let ttl = get_ttl(a);
for a in aggregations {
let ttl = get_ttl(*a);
let ts: DateTime<Local> = match a {
Aggregation::HOUR => Local
.with_ymd_and_hms(
record.time.year(),
record.time.month(),
record.time.day(),
record.time.hour(),
0,
0,
)
.unwrap(),
Aggregation::DAY => Local
.with_ymd_and_hms(
record.time.year(),
record.time.month(),
record.time.day(),
0,
0,
0,
)
.unwrap(),
Aggregation::MONTH => Local
.with_ymd_and_hms(record.time.year(), record.time.month(), 1, 0, 0, 0)
.unwrap(),
let ts: NaiveDateTime = match a {
Aggregation::MINUTE => {
NaiveDate::from_ymd_opt(record.time.year(), record.time.month(), record.time.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(record.time.hour(), record.time.minute(), 0)
.ok_or_else(|| anyhow!("Invalid time"))?
}
Aggregation::HOUR => {
NaiveDate::from_ymd_opt(record.time.year(), record.time.month(), record.time.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(record.time.hour(), 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?
}
Aggregation::DAY => {
NaiveDate::from_ymd_opt(record.time.year(), record.time.month(), record.time.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(0, 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?
}
Aggregation::MONTH => {
NaiveDate::from_ymd_opt(record.time.year(), record.time.month(), 1)
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(0, 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?
}
};
let key = get_key(name, a, ts);
let key = get_key(name, *a, ts);
for (k, v) in &record.metrics {
// Passing a reference to hincr will return a runtime error.
@ -175,78 +183,73 @@ pub async fn get(
end: DateTime<Local>,
) -> Result<Vec<Record>> {
let mut keys: Vec<String> = Vec::new();
let mut timestamps: Vec<DateTime<Local>> = Vec::new();
let mut timestamps: Vec<NaiveDateTime> = Vec::new();
match a {
Aggregation::HOUR => {
let mut ts = Local
.with_ymd_and_hms(start.year(), start.month(), start.day(), start.hour(), 0, 0)
.unwrap();
let end = Local
.with_ymd_and_hms(end.year(), end.month(), end.day(), end.hour(), 0, 0)
.unwrap();
Aggregation::MINUTE => {
let mut ts = NaiveDate::from_ymd_opt(start.year(), start.month(), start.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(start.hour(), start.minute(), 0)
.ok_or_else(|| anyhow!("Invalid time"))?;
let end = NaiveDate::from_ymd_opt(end.year(), end.month(), end.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(end.hour(), end.minute(), 0)
.ok_or_else(|| anyhow!("Invalid time"))?;
while ts.le(&end) {
timestamps.push(ts);
keys.push(get_key(name, a, ts));
ts += ChronoDuration::try_hours(1).unwrap();
ts += ChronoDuration::minutes(1);
}
}
Aggregation::HOUR => {
let mut ts = NaiveDate::from_ymd_opt(start.year(), start.month(), start.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(start.hour(), 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?;
let end = NaiveDate::from_ymd_opt(end.year(), end.month(), end.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(end.hour(), 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?;
while ts.le(&end) {
timestamps.push(ts);
keys.push(get_key(name, a, ts));
ts += ChronoDuration::hours(1);
}
}
Aggregation::DAY => {
let mut ts = Local
.with_ymd_and_hms(start.year(), start.month(), start.day(), 0, 0, 0)
.unwrap();
let end = Local
.with_ymd_and_hms(end.year(), end.month(), end.day(), 0, 0, 0)
.unwrap();
let mut ts = NaiveDate::from_ymd_opt(start.year(), start.month(), start.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(0, 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?;
let end = NaiveDate::from_ymd_opt(end.year(), end.month(), end.day())
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(0, 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?;
while ts.le(&end) {
timestamps.push(ts);
keys.push(get_key(name, a, ts));
ts = {
if (ts + ChronoDuration::try_days(1).unwrap()).day() == ts.day() {
// In case of DST to non-DST transition, the ts is incremented with less
// than 24h and we end up with the same day. Therefore we increment by two
// days.
(ts + ChronoDuration::try_days(2).unwrap())
.date_naive()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_local_timezone(Local)
.unwrap()
} else {
// Make sure that the timestamp stays at midnight in case of non-DST to DST
// change.
(ts + ChronoDuration::try_days(1).unwrap())
.date_naive()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_local_timezone(Local)
.unwrap()
}
};
ts += ChronoDuration::days(1);
}
}
Aggregation::MONTH => {
let mut ts = Local
.with_ymd_and_hms(start.year(), start.month(), 1, 0, 0, 0)
.unwrap();
let end = Local
.with_ymd_and_hms(end.year(), end.month(), 1, 0, 0, 0)
.unwrap();
let mut ts = NaiveDate::from_ymd_opt(start.year(), start.month(), 1)
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(0, 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?;
let end = NaiveDate::from_ymd_opt(end.year(), end.month(), 1)
.ok_or_else(|| anyhow!("Invalid date"))?
.and_hms_opt(0, 0, 0)
.ok_or_else(|| anyhow!("Invalid time"))?;
while ts.le(&end) {
timestamps.push(ts);
keys.push(get_key(name, a, ts));
ts = if ts.month() == 12 {
Local
.with_ymd_and_hms(ts.year() + 1, 1, 1, 0, 0, 0)
.unwrap()
} else {
Local
.with_ymd_and_hms(ts.year(), ts.month() + 1, 1, 0, 0, 0)
.unwrap()
};
ts = ts
.checked_add_months(Months::new(1))
.ok_or_else(|| anyhow!("Add month error"))?;
}
}
}
@ -266,6 +269,11 @@ pub async fn get(
let mut out: Vec<Record> = Vec::new();
for (i, r) in res.iter().enumerate() {
let tz = match timestamps[i].and_local_timezone(Local) {
chrono::LocalResult::Single(v) => v,
_ => continue,
};
let mut metrics = r.clone();
// In case of GAUGE values, the total aggregated value must be divided by the
@ -286,7 +294,7 @@ pub async fn get(
}
out.push(Record {
time: timestamps[i],
time: tz,
kind,
metrics: metrics
.iter()
@ -303,6 +311,42 @@ pub async fn get(
pub mod test {
use super::*;
use crate::test;
use chrono::TimeZone;
#[tokio::test]
async fn test_minute() {
let _guard = test::prepare().await;
let records = vec![
Record {
time: Local.with_ymd_and_hms(2018, 1, 1, 1, 1, 0).unwrap(),
kind: Kind::ABSOLUTE,
metrics: [("foo".into(), 1f64), ("bar".into(), 2f64)]
.iter()
.cloned()
.collect(),
},
Record {
time: Local.with_ymd_and_hms(2018, 1, 1, 1, 1, 10).unwrap(),
kind: Kind::ABSOLUTE,
metrics: [("foo".into(), 4f64), ("bar".into(), 4f64)]
.iter()
.cloned()
.collect(),
},
Record {
time: Local.with_ymd_and_hms(2018, 1, 1, 1, 2, 0).unwrap(),
kind: Kind::ABSOLUTE,
metrics: [("foo".into(), 5f64), ("bar".into(), 6f64)]
.iter()
.cloned()
.collect(),
},
];
for r in &records {
save("test", r, &[Aggregation::MINUTE]).await.unwrap();
}
}
#[tokio::test]
async fn test_hour() {
@ -335,7 +379,7 @@ pub mod test {
},
];
for r in &records {
save("test", r).await.unwrap();
save("test", r, &[Aggregation::HOUR]).await.unwrap();
}
let resp = get(
@ -402,7 +446,7 @@ pub mod test {
},
];
for r in &records {
save("test", r).await.unwrap();
save("test", r, &[Aggregation::DAY]).await.unwrap();
}
let resp = get(
@ -469,7 +513,7 @@ pub mod test {
},
];
for r in &records {
save("test", r).await.unwrap();
save("test", r, &[Aggregation::DAY]).await.unwrap();
}
let resp = get(
@ -536,7 +580,7 @@ pub mod test {
},
];
for r in &records {
save("test", r).await.unwrap();
save("test", r, &[Aggregation::MONTH]).await.unwrap();
}
let resp = get(
@ -595,7 +639,7 @@ pub mod test {
},
];
for r in &records {
save("test", r).await.unwrap();
save("test", r, &[Aggregation::HOUR]).await.unwrap();
}
let resp = get(
@ -644,7 +688,7 @@ pub mod test {
},
];
for r in &records {
save("test", r).await.unwrap();
save("test", r, &[Aggregation::HOUR]).await.unwrap();
}
let resp = get(
@ -693,7 +737,7 @@ pub mod test {
},
];
for r in &records {
save("test", r).await.unwrap();
save("test", r, &[Aggregation::HOUR]).await.unwrap();
}
let resp = get(

@ -148,7 +148,10 @@ pub async fn setup() -> Result<()> {
if !conf.redis.key_prefix.is_empty() {
info!(prefix = %conf.redis.key_prefix, "Setting Redis prefix");
*REDIS_PREFIX.write().unwrap() = conf.redis.key_prefix.clone();
REDIS_PREFIX
.write()
.unwrap()
.clone_from(&conf.redis.key_prefix);
}
Ok(())

@ -601,13 +601,16 @@ pub async fn get_schedulable_queue_items(limit: usize) -> Result<Vec<MulticastGr
where
id in (
select
id
qi.id
from
multicast_group_queue_item
multicast_group_queue_item qi
inner join gateway g
on g.gateway_id = qi.gateway_id
where
scheduler_run_after <= $2
qi.scheduler_run_after <= $2
and now() - make_interval(secs => g.stats_interval_secs * 2) <= g.last_seen_at
order by
created_at
qi.created_at
limit $1
)
returning *
@ -919,6 +922,8 @@ pub mod test {
gateway_id: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
name: "test-gw".into(),
tenant_id: t.id,
stats_interval_secs: 30,
last_seen_at: Some(Utc::now()),
..Default::default()
})
.await

@ -143,6 +143,7 @@ diesel::table! {
relay_global_uplink_limit_bucket_size -> Int2,
relay_overall_limit_bucket_size -> Int2,
allow_roaming -> Bool,
rx1_delay -> Int2,
}
}

@ -81,7 +81,7 @@ pub async fn get_event_logs(
for stream_key in &srr.keys {
for stream_id in &stream_key.ids {
last_id = stream_id.id.clone();
last_id.clone_from(&stream_id.id);
for (k, v) in &stream_id.map {
let res = handle_stream(&last_id, &channel, k, v).await;

@ -245,7 +245,7 @@ pub async fn get_frame_logs(
for stream_key in &srr.keys {
for stream_id in &stream_key.ids {
last_id = stream_id.id.clone();
last_id.clone_from(&stream_id.id);
for (k, v) in &stream_id.map {
let res = handle_stream(&last_id, &channel, k, v).await;

@ -172,7 +172,7 @@ async fn test_gateway_filtering() {
}),
mic: Some([48, 94, 26, 239]),
},
assert: vec![assert::f_cnt_up(dev.dev_eui.clone(), 8)],
assert: vec![assert::f_cnt_up(dev.dev_eui, 8)],
},
Test {
name: "private gateway other tenant".into(),
@ -337,7 +337,7 @@ async fn test_region_config_id_filtering() {
}),
mic: Some([48, 94, 26, 239]),
},
assert: vec![assert::f_cnt_up(dev.dev_eui.clone(), 8)],
assert: vec![assert::f_cnt_up(dev.dev_eui, 8)],
},
Test {
name: "non-matching configuration id".into(),
@ -364,7 +364,7 @@ async fn test_region_config_id_filtering() {
}),
mic: Some([48, 94, 26, 239]),
},
assert: vec![assert::f_cnt_up(dev.dev_eui.clone(), 7)],
assert: vec![assert::f_cnt_up(dev.dev_eui, 7)],
},
];
@ -858,8 +858,8 @@ async fn test_lorawan_10_skip_f_cnt() {
f_port: 1,
..Default::default()
}),
assert::f_cnt_up(dev.dev_eui.clone(), 8),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 8),
assert::n_f_cnt_down(dev.dev_eui, 5),
],
},
Test {
@ -907,8 +907,8 @@ async fn test_lorawan_10_skip_f_cnt() {
f_port: 1,
..Default::default()
}),
assert::f_cnt_up(dev.dev_eui.clone(), 1),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 1),
assert::n_f_cnt_down(dev.dev_eui, 5),
],
},
];
@ -1032,8 +1032,8 @@ async fn test_lorawan_10_device_disabled() {
},
assert: vec![
assert::no_uplink_event(),
assert::f_cnt_up(dev.dev_eui.clone(), 7),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 7),
assert::n_f_cnt_down(dev.dev_eui, 5),
],
}];
@ -1165,8 +1165,8 @@ async fn test_lorawan_10_uplink() {
mic: Some([104, 147, 35, 121]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -1237,8 +1237,8 @@ async fn test_lorawan_10_uplink() {
mic: Some([104, 147, 35, 121]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -1267,7 +1267,7 @@ async fn test_lorawan_10_uplink() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 1,
f_cnt_down: Some(4),
is_pending: true,
@ -1299,8 +1299,8 @@ async fn test_lorawan_10_uplink() {
mic: Some([132, 250, 228, 10]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::ack_event(integration_pb::AckEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -1367,8 +1367,8 @@ async fn test_lorawan_10_uplink() {
mic: Some([160, 195, 68, 8]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -1418,8 +1418,8 @@ async fn test_lorawan_10_uplink() {
mic: Some([69, 90, 200, 95]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -1530,8 +1530,8 @@ async fn test_lorawan_10_uplink() {
mic: Some([210, 52, 52, 94]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -1667,9 +1667,9 @@ async fn test_lorawan_10_uplink() {
mic: Some([104, 147, 35, 121]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::scheduler_run_after_set(dev.dev_eui.clone()),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::scheduler_run_after_set(dev.dev_eui),
],
},
];
@ -1897,7 +1897,7 @@ async fn test_lorawan_10_end_to_end_enc() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 1,
data: vec![1, 2, 3, 4],
f_cnt_down: Some(10),
@ -1954,8 +1954,8 @@ async fn test_lorawan_10_end_to_end_enc() {
}),
..Default::default()
}),
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -2131,8 +2131,8 @@ async fn test_lorawan_11_uplink() {
mic: Some([104, 147, 104, 147]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -2161,7 +2161,7 @@ async fn test_lorawan_11_uplink() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 1,
f_cnt_down: Some(4),
is_pending: true,
@ -2193,8 +2193,8 @@ async fn test_lorawan_11_uplink() {
mic: Some([76, 46, 132, 250]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::ack_event(integration_pb::AckEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -2275,7 +2275,7 @@ async fn test_lorawan_10_rx_delay() {
.await
.unwrap();
let dp = device_profile::create(device_profile::DeviceProfile {
let mut dp = device_profile::create(device_profile::DeviceProfile {
name: "dp".into(),
tenant_id: t.id.clone(),
region: lrwn::region::CommonName::EU868,
@ -2368,8 +2368,8 @@ async fn test_lorawan_10_rx_delay() {
mic: Some([210, 52, 52, 94]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -2457,6 +2457,238 @@ async fn test_lorawan_10_rx_delay() {
for tst in &tests {
run_test(tst).await;
}
// Test with RX1 Delay decreased in device-profile.
// This should make any difference as we only accept users to increase the RX1 Delay.
dp.rx1_delay = 2;
let mut dp = device_profile::update(dp).await.unwrap();
let tests = vec![Test {
name: "confirmed uplink without payload (dp.rx1_delay=2, used rx_delay = 3)".into(),
dev_eui: dev.dev_eui,
device_queue_items: vec![],
before_func: None,
after_func: None,
device_session: Some(ds.clone()),
tx_info: tx_info.clone(),
rx_info: rx_info.clone(),
phy_payload: lrwn::PhyPayload {
mhdr: lrwn::MHDR {
m_type: lrwn::MType::ConfirmedDataUp,
major: lrwn::Major::LoRaWANR1,
},
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
fhdr: lrwn::FHDR {
devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]),
f_cnt: 10,
..Default::default()
},
f_port: Some(1),
frm_payload: None,
}),
mic: Some([210, 52, 52, 94]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
tenant_id: t.id.to_string(),
application_name: app.name.clone(),
application_id: app.id.to_string(),
device_profile_name: dp.name.clone(),
device_profile_id: dp.id.to_string(),
device_name: dev.name.clone(),
dev_eui: dev.dev_eui.to_string(),
..Default::default()
}),
dev_addr: "01020304".into(),
tx_info: Some(tx_info.clone()),
rx_info: vec![rx_info.clone()],
f_cnt: 10,
f_port: 1,
confirmed: true,
dr: 0,
..Default::default()
}),
assert::downlink_frame(gw::DownlinkFrame {
gateway_id: "0102030405060708".into(),
items: vec![
gw::DownlinkFrameItem {
phy_payload: vec![96, 4, 3, 2, 1, 160, 5, 0, 161, 179, 218, 104],
tx_info_legacy: None,
tx_info: Some(gw::DownlinkTxInfo {
frequency: 868100000,
power: 16,
modulation: Some(gw::Modulation {
parameters: Some(gw::modulation::Parameters::Lora(
gw::LoraModulationInfo {
bandwidth: 125000,
spreading_factor: 12,
code_rate: gw::CodeRate::Cr45.into(),
polarization_inversion: true,
..Default::default()
},
)),
}),
timing: Some(gw::Timing {
parameters: Some(gw::timing::Parameters::Delay(
gw::DelayTimingInfo {
delay: Some(Duration::from_secs(3).into()),
},
)),
}),
..Default::default()
}),
},
gw::DownlinkFrameItem {
phy_payload: vec![96, 4, 3, 2, 1, 160, 5, 0, 161, 179, 218, 104],
tx_info_legacy: None,
tx_info: Some(gw::DownlinkTxInfo {
frequency: 869525000,
power: 29,
modulation: Some(gw::Modulation {
parameters: Some(gw::modulation::Parameters::Lora(
gw::LoraModulationInfo {
bandwidth: 125000,
spreading_factor: 12,
code_rate: gw::CodeRate::Cr45.into(),
polarization_inversion: true,
..Default::default()
},
)),
}),
timing: Some(gw::Timing {
parameters: Some(gw::timing::Parameters::Delay(
gw::DelayTimingInfo {
delay: Some(Duration::from_secs(4).into()),
},
)),
}),
..Default::default()
}),
},
],
..Default::default()
}),
],
}];
for tst in &tests {
run_test(tst).await;
}
// Test with RX1 Delay increased in device-profile.
// This should trigger a mac-command.
dp.rx1_delay = 5;
let dp = device_profile::update(dp).await.unwrap();
let tests = vec![Test {
name: "confirmed uplink without payload (dp.rx1_delay=5, expects mac-command)".into(),
dev_eui: dev.dev_eui,
device_queue_items: vec![],
before_func: None,
after_func: None,
device_session: Some(ds.clone()),
tx_info: tx_info.clone(),
rx_info: rx_info.clone(),
phy_payload: lrwn::PhyPayload {
mhdr: lrwn::MHDR {
m_type: lrwn::MType::ConfirmedDataUp,
major: lrwn::Major::LoRaWANR1,
},
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
fhdr: lrwn::FHDR {
devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]),
f_cnt: 10,
..Default::default()
},
f_port: Some(1),
frm_payload: None,
}),
mic: Some([210, 52, 52, 94]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::uplink_event(integration_pb::UplinkEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
tenant_id: t.id.to_string(),
application_name: app.name.clone(),
application_id: app.id.to_string(),
device_profile_name: dp.name.clone(),
device_profile_id: dp.id.to_string(),
device_name: dev.name.clone(),
dev_eui: dev.dev_eui.to_string(),
..Default::default()
}),
dev_addr: "01020304".into(),
tx_info: Some(tx_info.clone()),
rx_info: vec![rx_info.clone()],
f_cnt: 10,
f_port: 1,
confirmed: true,
dr: 0,
..Default::default()
}),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
m_type: lrwn::MType::UnconfirmedDataDown,
major: lrwn::Major::LoRaWANR1,
},
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
fhdr: lrwn::FHDR {
devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]),
f_cnt: 5,
f_ctrl: lrwn::FCtrl {
adr: true,
ack: true,
f_opts_len: 2,
..Default::default()
},
f_opts: lrwn::MACCommandSet::new(vec![lrwn::MACCommand::Raw(vec![
8, 5,
])]),
},
f_port: None,
frm_payload: None,
}),
mic: Some([37, 72, 226, 133]),
},
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
m_type: lrwn::MType::UnconfirmedDataDown,
major: lrwn::Major::LoRaWANR1,
},
payload: lrwn::Payload::MACPayload(lrwn::MACPayload {
fhdr: lrwn::FHDR {
devaddr: lrwn::DevAddr::from_be_bytes([1, 2, 3, 4]),
f_cnt: 5,
f_ctrl: lrwn::FCtrl {
adr: true,
ack: true,
f_opts_len: 2,
..Default::default()
},
f_opts: lrwn::MACCommandSet::new(vec![lrwn::MACCommand::Raw(vec![
8, 5,
])]),
},
f_port: None,
frm_payload: None,
}),
mic: Some([37, 72, 226, 133]),
},
]),
],
}];
for tst in &tests {
run_test(tst).await;
}
}
#[tokio::test]
@ -2596,8 +2828,8 @@ async fn test_lorawan_10_mac_commands() {
mic: Some([122, 152, 152, 220]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -2654,7 +2886,7 @@ async fn test_lorawan_10_mac_commands() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 1,
data: vec![1, 2, 3, 4],
..Default::default()
@ -2695,8 +2927,8 @@ async fn test_lorawan_10_mac_commands() {
mic: Some([122, 152, 152, 220]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -2778,7 +3010,7 @@ async fn test_lorawan_10_mac_commands() {
mic: Some([0xb6, 0x20, 0xd2, 0x14]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -2965,7 +3197,7 @@ async fn test_lorawan_11_mac_commands() {
rx_info: rx_info.clone(),
phy_payload: phy,
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -3120,7 +3352,7 @@ async fn test_lorawan_10_device_queue() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![1, 2, 3, 4],
..Default::default()
@ -3147,8 +3379,8 @@ async fn test_lorawan_10_device_queue() {
mic: Some([160, 195, 68, 8]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -3199,14 +3431,14 @@ async fn test_lorawan_10_device_queue() {
device_queue_items: vec![
device_queue::DeviceQueueItem {
id: Uuid::new_v4(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![1, 2, 3, 4],
..Default::default()
},
device_queue::DeviceQueueItem {
id: Uuid::new_v4(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![2, 2, 3, 4],
..Default::default()
@ -3234,8 +3466,8 @@ async fn test_lorawan_10_device_queue() {
mic: Some([160, 195, 68, 8]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -3289,7 +3521,7 @@ async fn test_lorawan_10_device_queue() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![1, 2, 3, 4],
confirmed: true,
@ -3317,8 +3549,8 @@ async fn test_lorawan_10_device_queue() {
mic: Some([160, 195, 68, 8]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -3368,7 +3600,7 @@ async fn test_lorawan_10_device_queue() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![0; 52],
..Default::default()
@ -3395,7 +3627,7 @@ async fn test_lorawan_10_device_queue() {
mic: Some([160, 195, 68, 8]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::no_downlink_frame(),
assert::integration_log(vec!["Device queue-item discarded because it exceeds the max. payload size".into()]),
],
@ -3405,7 +3637,7 @@ async fn test_lorawan_10_device_queue() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![0; 51],
..Default::default()
@ -3437,7 +3669,7 @@ async fn test_lorawan_10_device_queue() {
mic: Some([106, 14, 124, 212]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -3598,7 +3830,7 @@ async fn test_lorawan_11_device_queue() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![1, 2, 3, 4],
..Default::default()
@ -3625,8 +3857,8 @@ async fn test_lorawan_11_device_queue() {
mic: Some([160, 195, 160, 195]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -3677,14 +3909,14 @@ async fn test_lorawan_11_device_queue() {
device_queue_items: vec![
device_queue::DeviceQueueItem {
id: Uuid::new_v4(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![1, 2, 3, 4],
..Default::default()
},
device_queue::DeviceQueueItem {
id: Uuid::new_v4(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![2, 2, 3, 4],
..Default::default()
@ -3712,8 +3944,8 @@ async fn test_lorawan_11_device_queue() {
mic: Some([160, 195, 160, 195]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -3767,7 +3999,7 @@ async fn test_lorawan_11_device_queue() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![1, 2, 3, 4],
confirmed: true,
@ -3795,8 +4027,8 @@ async fn test_lorawan_11_device_queue() {
mic: Some([160, 195, 160, 195]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -3846,7 +4078,7 @@ async fn test_lorawan_11_device_queue() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![0; 52],
..Default::default()
@ -3873,7 +4105,7 @@ async fn test_lorawan_11_device_queue() {
mic: Some([160, 195, 160, 195]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::no_downlink_frame(),
assert::integration_log(vec!["Device queue-item discarded because it exceeds the max. payload size".into()]),
],
@ -3883,7 +4115,7 @@ async fn test_lorawan_11_device_queue() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 10,
data: vec![0; 51],
..Default::default()
@ -3917,7 +4149,7 @@ async fn test_lorawan_11_device_queue() {
mic: Some([204, 225, 204, 225]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -4121,8 +4353,8 @@ async fn test_lorawan_10_adr() {
mic: Some([187, 243, 244, 117]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads_decoded_f_opts(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -4225,7 +4457,7 @@ async fn test_lorawan_10_adr() {
mic: Some([122, 152, 152, 220]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::no_downlink_frame(),
],
},
@ -4234,7 +4466,7 @@ async fn test_lorawan_10_adr() {
dev_eui: dev.dev_eui,
device_queue_items: vec![],
before_func: Some(Box::new(move || {
let dev_eui = dev.dev_eui.clone();
let dev_eui = dev.dev_eui;
Box::pin(async move {
mac_command::set_pending(
&dev_eui,
@ -4289,12 +4521,12 @@ async fn test_lorawan_10_adr() {
mic: Some([235, 224, 96, 3]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::tx_power_index(dev.dev_eui.clone(), 3),
assert::dr(dev.dev_eui.clone(), 0),
assert::nb_trans(dev.dev_eui.clone(), 1),
assert::enabled_uplink_channel_indices(dev.dev_eui.clone(), vec![0, 1, 2]),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::tx_power_index(dev.dev_eui, 3),
assert::dr(dev.dev_eui, 0),
assert::nb_trans(dev.dev_eui, 1),
assert::enabled_uplink_channel_indices(dev.dev_eui, vec![0, 1, 2]),
],
},
Test {
@ -4302,7 +4534,7 @@ async fn test_lorawan_10_adr() {
dev_eui: dev.dev_eui,
device_queue_items: vec![],
before_func: Some(Box::new(move || {
let dev_eui = dev.dev_eui.clone();
let dev_eui = dev.dev_eui;
Box::pin(async move {
mac_command::set_pending(
&dev_eui,
@ -4357,12 +4589,12 @@ async fn test_lorawan_10_adr() {
mic: Some([252, 17, 226, 74]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::tx_power_index(dev.dev_eui.clone(), 0),
assert::dr(dev.dev_eui.clone(), 0),
assert::nb_trans(dev.dev_eui.clone(), 0),
assert::enabled_uplink_channel_indices(dev.dev_eui.clone(), vec![0, 1, 2]),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::tx_power_index(dev.dev_eui, 0),
assert::dr(dev.dev_eui, 0),
assert::nb_trans(dev.dev_eui, 0),
assert::enabled_uplink_channel_indices(dev.dev_eui, vec![0, 1, 2]),
],
},
Test {
@ -4395,9 +4627,9 @@ async fn test_lorawan_10_adr() {
mic: Some([73, 26, 32, 42]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::enabled_uplink_channel_indices(dev.dev_eui.clone(), vec![0, 1, 2]),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::enabled_uplink_channel_indices(dev.dev_eui, vec![0, 1, 2]),
assert::downlink_phy_payloads(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -4468,8 +4700,8 @@ async fn test_lorawan_10_adr() {
mic: Some([122, 152, 152, 220]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::downlink_phy_payloads_decoded_f_opts(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -4536,10 +4768,7 @@ async fn test_lorawan_10_adr() {
mic: Some([0x8, 0xee, 0xdd, 0x34]),
},
]),
assert::enabled_uplink_channel_indices(
dev.dev_eui.clone(),
vec![0, 1, 2, 3, 4, 5, 6, 7],
),
assert::enabled_uplink_channel_indices(dev.dev_eui, vec![0, 1, 2, 3, 4, 5, 6, 7]),
],
},
Test {
@ -4547,7 +4776,7 @@ async fn test_lorawan_10_adr() {
dev_eui: dev.dev_eui,
device_queue_items: vec![],
before_func: Some(Box::new(move || {
let dev_eui = dev.dev_eui.clone();
let dev_eui = dev.dev_eui;
Box::pin(async move {
mac_command::set_pending(
&dev_eui,
@ -4602,10 +4831,10 @@ async fn test_lorawan_10_adr() {
mic: Some([235, 224, 96, 3]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::no_downlink_frame(),
assert::enabled_uplink_channel_indices(dev.dev_eui.clone(), vec![0, 1, 2]),
assert::enabled_uplink_channel_indices(dev.dev_eui, vec![0, 1, 2]),
],
},
Test {
@ -4613,7 +4842,7 @@ async fn test_lorawan_10_adr() {
dev_eui: dev.dev_eui,
device_queue_items: vec![],
before_func: Some(Box::new(move || {
let dev_eui = dev.dev_eui.clone();
let dev_eui = dev.dev_eui;
Box::pin(async move {
mac_command::set_pending(
&dev_eui,
@ -4668,12 +4897,9 @@ async fn test_lorawan_10_adr() {
mic: Some([252, 17, 226, 74]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::enabled_uplink_channel_indices(
dev.dev_eui.clone(),
vec![0, 1, 2, 3, 4, 5, 6, 7],
),
assert::mac_command_error_count(dev.dev_eui.clone(), lrwn::CID::LinkADRReq, 1),
assert::f_cnt_up(dev.dev_eui, 11),
assert::enabled_uplink_channel_indices(dev.dev_eui, vec![0, 1, 2, 3, 4, 5, 6, 7]),
assert::mac_command_error_count(dev.dev_eui, lrwn::CID::LinkADRReq, 1),
],
},
Test {
@ -4706,12 +4932,9 @@ async fn test_lorawan_10_adr() {
mic: Some([187, 243, 244, 117]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::n_f_cnt_down(dev.dev_eui.clone(), 5),
assert::enabled_uplink_channel_indices(
dev.dev_eui.clone(),
vec![0, 1, 2, 3, 4, 5, 6, 7],
),
assert::f_cnt_up(dev.dev_eui, 11),
assert::n_f_cnt_down(dev.dev_eui, 5),
assert::enabled_uplink_channel_indices(dev.dev_eui, vec![0, 1, 2, 3, 4, 5, 6, 7]),
assert::downlink_phy_payloads_decoded_f_opts(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -4811,11 +5034,11 @@ async fn test_lorawan_10_adr() {
mic: Some([187, 243, 244, 117]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::dr(dev.dev_eui.clone(), 0),
assert::tx_power_index(dev.dev_eui.clone(), 0),
assert::f_cnt_up(dev.dev_eui, 11),
assert::dr(dev.dev_eui, 0),
assert::tx_power_index(dev.dev_eui, 0),
assert::uplink_adr_history(
dev.dev_eui.clone(),
dev.dev_eui,
vec![internal::UplinkAdrHistory {
f_cnt: 10,
max_snr: 0.0,
@ -4956,7 +5179,7 @@ async fn test_lorawan_10_device_status_request() {
mic: Some([122, 152, 152, 220]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_phy_payloads_decoded_f_opts(vec![
lrwn::PhyPayload {
mhdr: lrwn::MHDR {
@ -5033,7 +5256,7 @@ async fn test_lorawan_10_device_status_request() {
mic: Some([122, 152, 152, 220]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::no_downlink_frame(),
],
},
@ -5073,7 +5296,7 @@ async fn test_lorawan_10_device_status_request() {
mic: Some([29, 141, 54, 155]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::status_event(integration_pb::StatusEvent {
device_info: Some(integration_pb::DeviceInfo {
tenant_name: t.name.clone(),
@ -5205,7 +5428,7 @@ async fn test_lorawan_11_receive_window_selection() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 1,
data: vec![1],
..Default::default()
@ -5232,7 +5455,7 @@ async fn test_lorawan_11_receive_window_selection() {
mic: Some([104, 147, 104, 147]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_frame(gw::DownlinkFrame {
gateway_id: "0102030405060708".into(),
items: vec![gw::DownlinkFrameItem {
@ -5275,7 +5498,7 @@ async fn test_lorawan_11_receive_window_selection() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 1,
data: vec![1],
..Default::default()
@ -5302,7 +5525,7 @@ async fn test_lorawan_11_receive_window_selection() {
mic: Some([104, 147, 104, 147]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_frame(gw::DownlinkFrame {
gateway_id: "0102030405060708".into(),
items: vec![gw::DownlinkFrameItem {
@ -5345,7 +5568,7 @@ async fn test_lorawan_11_receive_window_selection() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 1,
data: vec![1],
..Default::default()
@ -5372,7 +5595,7 @@ async fn test_lorawan_11_receive_window_selection() {
mic: Some([104, 147, 104, 147]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_frame(gw::DownlinkFrame {
gateway_id: "0102030405060708".into(),
items: vec![
@ -5444,7 +5667,7 @@ async fn test_lorawan_11_receive_window_selection() {
dev_eui: dev.dev_eui,
device_queue_items: vec![device_queue::DeviceQueueItem {
id: Uuid::nil(),
dev_eui: dev.dev_eui.clone(),
dev_eui: dev.dev_eui,
f_port: 1,
data: vec![0; 100],
..Default::default()
@ -5471,7 +5694,7 @@ async fn test_lorawan_11_receive_window_selection() {
mic: Some([0xd4, 0x59, 0x68, 0x93]),
},
assert: vec![
assert::f_cnt_up(dev.dev_eui.clone(), 11),
assert::f_cnt_up(dev.dev_eui, 11),
assert::downlink_frame(gw::DownlinkFrame {
gateway_id: "0102030405060708".into(),
items: vec![

@ -1,3 +1,5 @@
use chrono::Utc;
use super::assert;
use crate::storage::{
application, device, device_gateway, device_profile, fields, gateway, multicast, tenant,
@ -37,6 +39,8 @@ async fn test_multicast() {
.cloned()
.collect(),
),
stats_interval_secs: 30,
last_seen_at: Some(Utc::now()),
..Default::default()
})
.await

@ -1045,7 +1045,12 @@ impl Data {
metrics: [("value".to_string(), v)].iter().cloned().collect(),
};
metrics::save(&format!("device:{}:{}", dev.dev_eui, k), &record).await?;
metrics::save(
&format!("device:{}:{}", dev.dev_eui, k),
&record,
&metrics::Aggregation::default_aggregations(),
)
.await?;
}
pbjson_types::value::Kind::StringValue(v) => {
metrics::save_state(
@ -1101,7 +1106,8 @@ impl Data {
trace!("Setting region_config_id to device-session");
let d = self.device.as_mut().unwrap();
let ds = d.get_device_session_mut()?;
ds.region_config_id = self.uplink_frame_set.region_config_id.clone();
ds.region_config_id
.clone_from(&self.uplink_frame_set.region_config_id);
Ok(())
}
@ -1215,7 +1221,12 @@ impl Data {
let dev = self.device.as_ref().unwrap();
metrics::save(&format!("device:{}", dev.dev_eui), &record).await?;
metrics::save(
&format!("device:{}", dev.dev_eui),
&record,
&metrics::Aggregation::default_aggregations(),
)
.await?;
Ok(())
}
@ -1246,7 +1257,12 @@ impl Data {
let dev = self.device.as_ref().unwrap();
metrics::save(&format!("device:{}", dev.dev_eui), &record).await?;
metrics::save(
&format!("device:{}", dev.dev_eui),
&record,
&metrics::Aggregation::default_aggregations(),
)
.await?;
Ok(())
}
@ -1254,8 +1270,23 @@ impl Data {
async fn start_downlink_data_flow(&mut self) -> Result<()> {
trace!("Starting downlink data flow");
let conf = config::get();
tokio::time::sleep(conf.network.get_downlink_data_delay).await;
// We sleep get_downlink_data_delay to give the end-user application some time
// to enqueue data before the downlink flow starts. In case the user has increased
// the RX1 Delay relative to the system RX1 Delay, then we add the additional
// seconds to this wait.
{
let conf = config::get();
let ds = self.device.as_ref().unwrap().get_device_session()?;
let network_conf = config::get_region_network(&ds.region_config_id)?;
let dev_rx1_delay = ds.rx1_delay as u8;
let sys_rx1_delay = network_conf.rx1_delay;
let rx1_delay_increase = dev_rx1_delay.checked_sub(sys_rx1_delay).unwrap_or_default();
let rx1_delay_increase = std::time::Duration::from_secs(rx1_delay_increase.into());
tokio::time::sleep(conf.network.get_downlink_data_delay + rx1_delay_increase).await;
}
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
downlink::data::Data::handle_response(
@ -1278,8 +1309,26 @@ impl Data {
async fn start_downlink_data_flow_relayed(&mut self) -> Result<()> {
trace!("Starting relayed downlink data flow");
let conf = config::get();
tokio::time::sleep(conf.network.get_downlink_data_delay).await;
// We sleep get_downlink_data_delay to give the end-user application some time
// to enqueue data before the downlink flow starts. In case the user has increased
// the RX1 Delay relative to the system RX1 Delay, then we add the additional
// seconds to this wait.
// Note: In this case we use the RX1 Delay from the Relay device-session.
{
let conf = config::get();
let relay_ctx = self.relay_context.as_ref().unwrap();
let ds = relay_ctx.device.get_device_session()?;
let network_conf = config::get_region_network(&ds.region_config_id)?;
let dev_rx1_delay = ds.rx1_delay as u8;
let sys_rx1_delay = network_conf.rx1_delay;
let rx1_delay_increase = dev_rx1_delay.checked_sub(sys_rx1_delay).unwrap_or_default();
let rx1_delay_increase = std::time::Duration::from_secs(rx1_delay_increase.into());
tokio::time::sleep(conf.network.get_downlink_data_delay + rx1_delay_increase).await;
}
if let lrwn::Payload::MACPayload(pl) = &self.phy_payload.payload {
downlink::data::Data::handle_response_relayed(

Some files were not shown because too many files have changed in this diff Show More