mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-04-19 08:36:19 +00:00
Compare commits
23 Commits
v4.12.0-te
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
6d084b3b21 | ||
|
75e9106bbb | ||
|
4ce4828a78 | ||
|
9ecf4fef1b | ||
|
8f9316af2c | ||
|
1f2a7d390a | ||
|
990bf57da5 | ||
|
b336690a65 | ||
|
7597bcaabf | ||
|
0ae1294a63 | ||
|
8e0a29ed55 | ||
|
5fd57de6ce | ||
|
d91fb77617 | ||
|
fa63c306fd | ||
|
7d1e85e575 | ||
|
e392f52444 | ||
|
e30a2e0e77 | ||
|
b2adac5a49 | ||
|
ca7b0a2e61 | ||
|
849d27f148 | ||
|
5ce35eef5e | ||
|
236b468aa4 | ||
|
c130be9dd0 |
491
Cargo.lock
generated
491
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
2
Makefile
2
Makefile
@ -6,9 +6,7 @@ dist:
|
||||
cd chirpstack && make dist
|
||||
|
||||
# Install dev dependencies
|
||||
# TODO: test latest cargo-deb and move it to shell.nix.
|
||||
dev-dependencies:
|
||||
cargo install cargo-deb --version 1.43.1 --locked
|
||||
cargo install cargo-generate-rpm --version 0.12.1 --locked
|
||||
|
||||
# Set the versions
|
||||
|
7
api/go/go.mod
vendored
7
api/go/go.mod
vendored
@ -1,6 +1,7 @@
|
||||
module github.com/chirpstack/chirpstack/api/go/v4
|
||||
|
||||
go 1.21
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa
|
||||
@ -10,8 +11,8 @@ require (
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect
|
||||
)
|
||||
|
12
api/go/go.sum
vendored
12
api/go/go.sum
vendored
@ -2,12 +2,12 @@ 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=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa h1:Jt1XW5PaLXF1/ePZrznsh/aAUvI7Adfc3LY1dAKlzRs=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:K4kfzHtI0kqWA79gecJarFtDn/Mls+GxQcg3Zox91Ac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1:RBgMaUMP+6soRkik4VoN8ojR2nex2TqZwjSSogic+eo=
|
||||
|
2
api/grpc-web/package.json
vendored
2
api/grpc-web/package.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@chirpstack/chirpstack-api-grpc-web",
|
||||
"version": "4.12.0-test.2",
|
||||
"version": "4.12.0-test.3",
|
||||
"description": "Chirpstack gRPC-web API",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
|
2
api/java/build.gradle.kts
vendored
2
api/java/build.gradle.kts
vendored
@ -8,7 +8,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "io.chirpstack"
|
||||
version = "4.12.0-test.2"
|
||||
version = "4.12.0-test.3"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
2
api/js/package.json
vendored
2
api/js/package.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@chirpstack/chirpstack-api",
|
||||
"version": "4.12.0-test.2",
|
||||
"version": "4.12.0-test.3",
|
||||
"description": "Chirpstack JS and TS API",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
|
2
api/kotlin/build.gradle.kts
vendored
2
api/kotlin/build.gradle.kts
vendored
@ -9,7 +9,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "io.chirpstack"
|
||||
version = "4.12.0-test.2"
|
||||
version = "4.12.0-test.3"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
2
api/php/composer.json
vendored
2
api/php/composer.json
vendored
@ -3,7 +3,7 @@
|
||||
"description": "Chirpstack PHP API",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"version": "4.12.0-test.2",
|
||||
"version": "4.12.0-test.3",
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"grpc/grpc": "^v1.57.0",
|
||||
|
2
api/python/src/setup.py
vendored
2
api/python/src/setup.py
vendored
@ -18,7 +18,7 @@ CLASSIFIERS = [
|
||||
|
||||
setup(
|
||||
name='chirpstack-api',
|
||||
version = "4.12.0-test.2",
|
||||
version = "4.12.0-test.3",
|
||||
url='https://github.com/brocaar/chirpstack-api',
|
||||
author='Orne Brocaar',
|
||||
author_email='info@brocaar.com',
|
||||
|
6
api/rust/Cargo.toml
vendored
6
api/rust/Cargo.toml
vendored
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "chirpstack_api"
|
||||
description = "ChirpStack Protobuf / gRPC API definitions."
|
||||
version = "4.12.0-test.2"
|
||||
version = "4.12.0-test.3"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
license = "MIT"
|
||||
homepage = "https://www.chirpstack.io"
|
||||
@ -19,7 +19,7 @@
|
||||
prost-types = "0.13"
|
||||
hex = "0.4"
|
||||
rand = "0.9"
|
||||
tonic = { version = "0.12", features = [
|
||||
tonic = { version = "0.13", features = [
|
||||
"codegen",
|
||||
"prost",
|
||||
], default-features = false, optional = true }
|
||||
@ -29,7 +29,7 @@
|
||||
serde = { version = "1.0", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = { version = "0.12", features = [
|
||||
tonic-build = { version = "0.13", features = [
|
||||
"prost",
|
||||
], default-features = false }
|
||||
pbjson-build = "0.7"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "backend"
|
||||
version = "4.12.0-test.2"
|
||||
version = "4.12.0-test.3"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
@ -3,13 +3,13 @@
|
||||
description = "Library for building external ChirpStack integrations"
|
||||
homepage = "https://www.chirpstack.io/"
|
||||
license = "MIT"
|
||||
version = "4.12.0-test.2"
|
||||
version = "4.12.0-test.3"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/chirpstack/chirpstack"
|
||||
|
||||
[dependencies]
|
||||
chirpstack_api = { path = "../api/rust", version = "4.12.0-test.2" }
|
||||
chirpstack_api = { path = "../api/rust", version = "4.12.0-test.3" }
|
||||
redis = { version = "0.29", features = [
|
||||
"cluster-async",
|
||||
"tokio-rustls-comp",
|
||||
@ -24,6 +24,5 @@
|
||||
async-trait = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tokio = { version = "1.44", features = ["macros", "rt-multi-thread"] }
|
||||
lazy_static = "1.5"
|
||||
serde_json = "1.0"
|
||||
toml = "0.8"
|
||||
|
@ -1,8 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
@ -13,10 +11,8 @@ use tracing_subscriber::{filter, prelude::*};
|
||||
|
||||
use chirpstack_api::{integration as integration_pb, prost::Message};
|
||||
|
||||
lazy_static! {
|
||||
static ref INTEGRATION: RwLock<Option<Box<dyn IntegrationTrait + Sync + Send>>> =
|
||||
RwLock::new(None);
|
||||
}
|
||||
static INTEGRATION: LazyLock<RwLock<Option<Box<dyn IntegrationTrait + Sync + Send>>>> =
|
||||
LazyLock::new(|| RwLock::new(None));
|
||||
|
||||
#[derive(Default, Deserialize, Clone)]
|
||||
#[serde(default)]
|
||||
@ -203,7 +199,7 @@ impl Integration {
|
||||
|
||||
for stream_key in &srr.keys {
|
||||
for stream_id in &stream_key.ids {
|
||||
redis::cmd("XACK")
|
||||
let _: () = redis::cmd("XACK")
|
||||
.arg(&key)
|
||||
.arg(&self.consumer_group)
|
||||
.arg(&stream_id.id)
|
||||
|
@ -3,7 +3,7 @@
|
||||
description = "ChirpStack is an open-source LoRaWAN(TM) Network Server"
|
||||
repository = "https://github.com/chirpstack/chirpstack"
|
||||
homepage = "https://www.chirpstack.io/"
|
||||
version = "4.12.0-test.2"
|
||||
version = "4.12.0-test.3"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
@ -78,9 +78,9 @@
|
||||
] }
|
||||
|
||||
# gRPC and Protobuf
|
||||
tonic = "0.12"
|
||||
tonic-web = "0.12"
|
||||
tonic-reflection = "0.12"
|
||||
tonic = "0.13"
|
||||
tonic-web = "0.13"
|
||||
tonic-reflection = "0.13"
|
||||
tokio = { version = "1.44", features = ["macros", "rt-multi-thread"] }
|
||||
tokio-stream = "0.1"
|
||||
prost-types = "0.13"
|
||||
@ -95,7 +95,7 @@
|
||||
futures-util = "0.3"
|
||||
http = "1.3"
|
||||
http-body = "1.0"
|
||||
rust-embed = "8.6"
|
||||
rust-embed = "8.7"
|
||||
mime_guess = "2.0"
|
||||
tower-http = { version = "0.6", features = ["trace", "auth"] }
|
||||
|
||||
@ -137,7 +137,6 @@
|
||||
] }
|
||||
|
||||
# Misc
|
||||
lazy_static = "1.5"
|
||||
uuid = { version = "1.16", features = ["v4", "serde"] }
|
||||
chrono = "0.4"
|
||||
async-trait = "0.1"
|
||||
@ -147,7 +146,7 @@
|
||||
async-recursion = "1.1"
|
||||
regex = "1.11"
|
||||
petgraph = "0.7"
|
||||
prometheus-client = "0.22"
|
||||
prometheus-client = "0.23"
|
||||
pin-project = "1.1"
|
||||
scoped-futures = { version = "0.1", features = ["std"] }
|
||||
signal-hook = "0.3"
|
||||
|
@ -26,7 +26,7 @@ insert into "user" (
|
||||
password_hash,
|
||||
note
|
||||
) values (
|
||||
'05244f12-6daf-4e1f-8315-c66783a0ab56',
|
||||
gen_random_uuid(),
|
||||
now(),
|
||||
now(),
|
||||
true,
|
||||
@ -63,7 +63,7 @@ insert into "tenant" (
|
||||
max_gateway_count,
|
||||
private_gateways
|
||||
) values (
|
||||
'52f14cd4-c6f1-4fbd-8f87-4025e1d49242',
|
||||
gen_random_uuid(),
|
||||
now(),
|
||||
now(),
|
||||
'ChirpStack',
|
||||
|
@ -1,3 +1,6 @@
|
||||
alter table device
|
||||
drop column app_layer_params;
|
||||
|
||||
alter table device_keys
|
||||
drop column gen_app_key;
|
||||
|
||||
|
@ -73,3 +73,9 @@ alter table device_keys
|
||||
|
||||
alter table device_keys
|
||||
alter column gen_app_key drop default;
|
||||
|
||||
alter table device
|
||||
add column app_layer_params jsonb not null default '{}';
|
||||
|
||||
alter table device
|
||||
alter column app_layer_params drop default;
|
||||
|
@ -1,3 +1,6 @@
|
||||
alter table device
|
||||
drop column app_layer_params;
|
||||
|
||||
alter table device_keys
|
||||
drop column gen_app_key;
|
||||
|
||||
|
@ -70,3 +70,6 @@ create index idx_fuota_deployment_job_scheduler_run_after on fuota_deployment_jo
|
||||
|
||||
alter table device_keys
|
||||
add column gen_app_key blob not null default x'00000000000000000000000000000000';
|
||||
|
||||
alter table device
|
||||
add column app_layer_params text not null default '{}';
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
@ -14,10 +15,8 @@ pub mod lora_lr_fhss;
|
||||
pub mod lr_fhss;
|
||||
pub mod plugin;
|
||||
|
||||
lazy_static! {
|
||||
static ref ADR_ALGORITHMS: RwLock<HashMap<String, Box<dyn Handler + Sync + Send>>> =
|
||||
RwLock::new(HashMap::new());
|
||||
}
|
||||
static ADR_ALGORITHMS: LazyLock<RwLock<HashMap<String, Box<dyn Handler + Sync + Send>>>> =
|
||||
LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
pub async fn setup() -> Result<()> {
|
||||
info!("Setting up adr algorithms");
|
||||
|
@ -2010,7 +2010,7 @@ pub mod test {
|
||||
let mut create_req = Request::new(create_req);
|
||||
create_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let create_resp = service.create(create_req).await.unwrap();
|
||||
let create_resp = create_resp.get_ref();
|
||||
|
||||
@ -2021,7 +2021,7 @@ pub mod test {
|
||||
let mut get_req = Request::new(get_req);
|
||||
get_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let get_resp = service.get(get_req).await.unwrap();
|
||||
assert_eq!(
|
||||
Some(api::Application {
|
||||
@ -2045,7 +2045,7 @@ pub mod test {
|
||||
let mut up_req = Request::new(up_req);
|
||||
up_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.update(up_req).await.unwrap();
|
||||
|
||||
//get
|
||||
@ -2055,7 +2055,7 @@ pub mod test {
|
||||
let mut get_req = Request::new(get_req);
|
||||
get_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let get_resp = service.get(get_req).await.unwrap();
|
||||
assert_eq!(
|
||||
Some(api::Application {
|
||||
@ -2077,7 +2077,7 @@ pub mod test {
|
||||
let mut list_req = Request::new(list_req);
|
||||
list_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let list_resp = service.list(list_req).await.unwrap();
|
||||
assert_eq!(1, list_resp.get_ref().total_count);
|
||||
assert_eq!(1, list_resp.get_ref().result.len());
|
||||
@ -2089,7 +2089,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.delete(del_req).await.unwrap();
|
||||
|
||||
let del_req = api::DeleteApplicationRequest {
|
||||
@ -2098,7 +2098,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let del_resp = service.delete(del_req).await;
|
||||
assert!(del_resp.is_err());
|
||||
}
|
||||
|
@ -2483,7 +2483,7 @@ pub mod test {
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_user.id.into(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -2491,7 +2491,7 @@ pub mod test {
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_admin.id.into(),
|
||||
user_id: tenant_admin.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -2727,7 +2727,7 @@ pub mod test {
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_admin.id.into(),
|
||||
user_id: tenant_admin.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -2735,21 +2735,21 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_user.id.into(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_user.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_user_other.id.into(),
|
||||
user_id: tenant_user_other.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -3091,7 +3091,7 @@ pub mod test {
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_admin.id.into(),
|
||||
user_id: tenant_admin.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -3099,7 +3099,7 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_device_admin.id.into(),
|
||||
user_id: tenant_device_admin.id,
|
||||
is_device_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -3107,7 +3107,7 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_gateway_admin.id.into(),
|
||||
user_id: tenant_gateway_admin.id,
|
||||
is_gateway_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -3115,7 +3115,7 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_user.id.into(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -3538,7 +3538,7 @@ pub mod test {
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_admin.id.into(),
|
||||
user_id: tenant_admin.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -3546,7 +3546,7 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_device_admin.id.into(),
|
||||
user_id: tenant_device_admin.id,
|
||||
is_device_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -3554,7 +3554,7 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_gateway_admin.id.into(),
|
||||
user_id: tenant_gateway_admin.id,
|
||||
is_gateway_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -3562,7 +3562,7 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_user.id.into(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -3840,32 +3840,32 @@ pub mod test {
|
||||
.await;
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_admin.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_admin.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_device_admin.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_device_admin.id,
|
||||
is_device_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_gateway_admin.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_gateway_admin.id,
|
||||
is_gateway_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_user.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -4093,8 +4093,8 @@ pub mod test {
|
||||
.await;
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_user.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -4237,7 +4237,7 @@ pub mod test {
|
||||
let gw_api_key_tenant = gateway::create(gateway::Gateway {
|
||||
name: "test-gw-tenant".into(),
|
||||
gateway_id: EUI64::from_str("0202030405060708").unwrap(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -4245,7 +4245,7 @@ pub mod test {
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_admin.id.into(),
|
||||
user_id: tenant_admin.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -4253,7 +4253,7 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_gateway_admin.id.into(),
|
||||
user_id: tenant_gateway_admin.id,
|
||||
is_gateway_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
@ -4261,7 +4261,7 @@ pub mod test {
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: tenant_a.id,
|
||||
user_id: tenant_user.id.into(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -4513,32 +4513,32 @@ pub mod test {
|
||||
.await;
|
||||
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_admin.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_admin.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_device_admin.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_device_admin.id,
|
||||
is_device_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_gateway_admin.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_gateway_admin.id,
|
||||
is_gateway_admin: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
user_id: tenant_user.id.into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_user.id,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
@ -4901,7 +4901,7 @@ pub mod test {
|
||||
.await
|
||||
.unwrap();
|
||||
tenant::add_user(tenant::TenantUser {
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap().into(),
|
||||
tenant_id: api_key_tenant.tenant_id.unwrap(),
|
||||
user_id: tenant_device_admin.id,
|
||||
is_device_admin: true,
|
||||
..Default::default()
|
||||
|
@ -88,7 +88,7 @@ impl DeviceProfileService for DeviceProfile {
|
||||
rx1_delay: req_dp.abp_rx1_delay as u8,
|
||||
rx1_dr_offset: req_dp.abp_rx1_dr_offset as u8,
|
||||
rx2_dr: req_dp.abp_rx2_dr as u8,
|
||||
rx2_freq: req_dp.abp_rx2_freq as u32,
|
||||
rx2_freq: req_dp.abp_rx2_freq,
|
||||
})
|
||||
},
|
||||
class_b_params: if req_dp.supports_class_b {
|
||||
@ -96,7 +96,7 @@ impl DeviceProfileService for DeviceProfile {
|
||||
timeout: req_dp.class_b_timeout as u16,
|
||||
ping_slot_nb_k: req_dp.class_b_ping_slot_nb_k as u8,
|
||||
ping_slot_dr: req_dp.class_b_ping_slot_dr as u8,
|
||||
ping_slot_freq: req_dp.class_b_ping_slot_freq as u32,
|
||||
ping_slot_freq: req_dp.class_b_ping_slot_freq,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@ -116,7 +116,7 @@ impl DeviceProfileService for DeviceProfile {
|
||||
relay_enabled: req_dp.relay_enabled,
|
||||
relay_cad_periodicity: req_dp.relay_cad_periodicity as u8,
|
||||
default_channel_index: req_dp.relay_default_channel_index as u8,
|
||||
second_channel_freq: req_dp.relay_second_channel_freq as u32,
|
||||
second_channel_freq: req_dp.relay_second_channel_freq,
|
||||
second_channel_dr: req_dp.relay_second_channel_dr as u8,
|
||||
second_channel_ack_offset: req_dp.relay_second_channel_ack_offset as u8,
|
||||
ed_activation_mode: req_dp.relay_ed_activation_mode().from_proto(),
|
||||
@ -344,7 +344,7 @@ impl DeviceProfileService for DeviceProfile {
|
||||
rx1_delay: req_dp.abp_rx1_delay as u8,
|
||||
rx1_dr_offset: req_dp.abp_rx1_dr_offset as u8,
|
||||
rx2_dr: req_dp.abp_rx2_dr as u8,
|
||||
rx2_freq: req_dp.abp_rx2_freq as u32,
|
||||
rx2_freq: req_dp.abp_rx2_freq,
|
||||
})
|
||||
},
|
||||
class_b_params: if req_dp.supports_class_b {
|
||||
@ -352,7 +352,7 @@ impl DeviceProfileService for DeviceProfile {
|
||||
timeout: req_dp.class_b_timeout as u16,
|
||||
ping_slot_nb_k: req_dp.class_b_ping_slot_nb_k as u8,
|
||||
ping_slot_dr: req_dp.class_b_ping_slot_dr as u8,
|
||||
ping_slot_freq: req_dp.class_b_ping_slot_freq as u32,
|
||||
ping_slot_freq: req_dp.class_b_ping_slot_freq,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@ -372,7 +372,7 @@ impl DeviceProfileService for DeviceProfile {
|
||||
relay_enabled: req_dp.relay_enabled,
|
||||
relay_cad_periodicity: req_dp.relay_cad_periodicity as u8,
|
||||
default_channel_index: req_dp.relay_default_channel_index as u8,
|
||||
second_channel_freq: req_dp.relay_second_channel_freq as u32,
|
||||
second_channel_freq: req_dp.relay_second_channel_freq,
|
||||
second_channel_dr: req_dp.relay_second_channel_dr as u8,
|
||||
second_channel_ack_offset: req_dp.relay_second_channel_ack_offset as u8,
|
||||
ed_activation_mode: req_dp.relay_ed_activation_mode().from_proto(),
|
||||
@ -406,7 +406,6 @@ impl DeviceProfileService for DeviceProfile {
|
||||
ts004_f_port: app_layer_params.ts004_f_port as u8,
|
||||
ts005_version: app_layer_params.ts005_version().from_proto(),
|
||||
ts005_f_port: app_layer_params.ts005_f_port as u8,
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
..Default::default()
|
||||
|
@ -50,9 +50,9 @@ impl ToStatus for storage::error::Error {
|
||||
storage::error::Error::ValidatorValidate(_) => {
|
||||
Status::new(Code::InvalidArgument, format!("{:#}", self))
|
||||
}
|
||||
storage::error::Error::MultiError(errors) => {
|
||||
storage::error::Error::Multi(errors) => {
|
||||
let errors = errors
|
||||
.into_iter()
|
||||
.iter()
|
||||
.map(|e| e.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
@ -346,11 +346,11 @@ impl FuotaService for Fuota {
|
||||
started_at: d
|
||||
.started_at
|
||||
.as_ref()
|
||||
.map(|ts| helpers::datetime_to_prost_timestamp(ts)),
|
||||
.map(helpers::datetime_to_prost_timestamp),
|
||||
completed_at: d
|
||||
.completed_at
|
||||
.as_ref()
|
||||
.map(|ts| helpers::datetime_to_prost_timestamp(ts)),
|
||||
.map(helpers::datetime_to_prost_timestamp),
|
||||
name: d.name.clone(),
|
||||
})
|
||||
.collect(),
|
||||
@ -462,23 +462,23 @@ impl FuotaService for Fuota {
|
||||
completed_at: d
|
||||
.completed_at
|
||||
.as_ref()
|
||||
.map(|ts| helpers::datetime_to_prost_timestamp(ts)),
|
||||
.map(helpers::datetime_to_prost_timestamp),
|
||||
mc_group_setup_completed_at: d
|
||||
.mc_group_setup_completed_at
|
||||
.as_ref()
|
||||
.map(|ts| helpers::datetime_to_prost_timestamp(ts)),
|
||||
.map(helpers::datetime_to_prost_timestamp),
|
||||
mc_session_completed_at: d
|
||||
.mc_session_completed_at
|
||||
.as_ref()
|
||||
.map(|ts| helpers::datetime_to_prost_timestamp(ts)),
|
||||
.map(helpers::datetime_to_prost_timestamp),
|
||||
frag_session_setup_completed_at: d
|
||||
.frag_session_setup_completed_at
|
||||
.as_ref()
|
||||
.map(|ts| helpers::datetime_to_prost_timestamp(ts)),
|
||||
.map(helpers::datetime_to_prost_timestamp),
|
||||
frag_status_completed_at: d
|
||||
.frag_status_completed_at
|
||||
.as_ref()
|
||||
.map(|ts| helpers::datetime_to_prost_timestamp(ts)),
|
||||
.map(helpers::datetime_to_prost_timestamp),
|
||||
error_msg: d.error_msg.clone(),
|
||||
})
|
||||
.collect(),
|
||||
@ -624,7 +624,7 @@ impl FuotaService for Fuota {
|
||||
completed_at: j
|
||||
.completed_at
|
||||
.as_ref()
|
||||
.map(|ts| helpers::datetime_to_prost_timestamp(ts)),
|
||||
.map(helpers::datetime_to_prost_timestamp),
|
||||
max_retry_count: j.max_retry_count as u32,
|
||||
attempt_count: j.attempt_count as u32,
|
||||
scheduler_run_after: Some(helpers::datetime_to_prost_timestamp(
|
||||
|
@ -1036,7 +1036,7 @@ pub mod test {
|
||||
let mut create_req = Request::new(create_req);
|
||||
create_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.create(create_req).await.unwrap();
|
||||
|
||||
// get
|
||||
@ -1046,7 +1046,7 @@ pub mod test {
|
||||
let mut get_req = Request::new(get_req);
|
||||
get_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let get_resp = service.get(get_req).await.unwrap();
|
||||
assert_eq!(
|
||||
Some(api::Gateway {
|
||||
@ -1082,7 +1082,7 @@ pub mod test {
|
||||
let mut up_req = Request::new(up_req);
|
||||
up_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.update(up_req).await.unwrap();
|
||||
|
||||
// get
|
||||
@ -1092,7 +1092,7 @@ pub mod test {
|
||||
let mut get_req = Request::new(get_req);
|
||||
get_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let get_resp = service.get(get_req).await.unwrap();
|
||||
assert_eq!(
|
||||
Some(api::Gateway {
|
||||
@ -1121,7 +1121,7 @@ pub mod test {
|
||||
let mut list_req = Request::new(list_req);
|
||||
list_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let list_resp = service.list(list_req).await.unwrap();
|
||||
assert_eq!(1, list_resp.get_ref().total_count);
|
||||
assert_eq!(1, list_resp.get_ref().result.len());
|
||||
@ -1133,7 +1133,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.delete(del_req).await.unwrap();
|
||||
|
||||
let del_req = api::DeleteGatewayRequest {
|
||||
@ -1142,7 +1142,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let del_resp = service.delete(del_req).await;
|
||||
assert!(del_resp.is_err());
|
||||
}
|
||||
@ -1220,7 +1220,7 @@ pub mod test {
|
||||
let mut stats_req = Request::new(stats_req);
|
||||
stats_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let stats_resp = service.get_metrics(stats_req).await.unwrap();
|
||||
let stats_resp = stats_resp.get_ref();
|
||||
assert_eq!(
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::sync::LazyLock;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{
|
||||
future::Future,
|
||||
@ -5,7 +6,7 @@ use std::{
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context as AnyhowContext, Result};
|
||||
use axum::{response::IntoResponse, routing::get, Router};
|
||||
use http::{
|
||||
header::{self, HeaderMap, HeaderValue},
|
||||
@ -67,33 +68,31 @@ pub mod relay;
|
||||
pub mod tenant;
|
||||
pub mod user;
|
||||
|
||||
lazy_static! {
|
||||
static ref GRPC_COUNTER: Family<GrpcLabels, Counter> = {
|
||||
let counter = Family::<GrpcLabels, Counter>::default();
|
||||
prometheus::register(
|
||||
"api_requests_handled",
|
||||
"Number of API requests handled by service, method and status code",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
};
|
||||
static ref GRPC_HISTOGRAM: Family<GrpcLabels, Histogram> = {
|
||||
let histogram = Family::<GrpcLabels, Histogram>::new_with_constructor(|| {
|
||||
Histogram::new(
|
||||
[
|
||||
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
|
||||
]
|
||||
.into_iter(),
|
||||
)
|
||||
});
|
||||
prometheus::register(
|
||||
"api_requests_handled_seconds",
|
||||
"Duration of API requests handled by service, method and status code",
|
||||
histogram.clone(),
|
||||
);
|
||||
histogram
|
||||
};
|
||||
}
|
||||
static GRPC_COUNTER: LazyLock<Family<GrpcLabels, Counter>> = LazyLock::new(|| {
|
||||
let counter = Family::<GrpcLabels, Counter>::default();
|
||||
prometheus::register(
|
||||
"api_requests_handled",
|
||||
"Number of API requests handled by service, method and status code",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
});
|
||||
static GRPC_HISTOGRAM: LazyLock<Family<GrpcLabels, Histogram>> = LazyLock::new(|| {
|
||||
let histogram = Family::<GrpcLabels, Histogram>::new_with_constructor(|| {
|
||||
Histogram::new(
|
||||
[
|
||||
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
|
||||
]
|
||||
.into_iter(),
|
||||
)
|
||||
});
|
||||
prometheus::register(
|
||||
"api_requests_handled_seconds",
|
||||
"Duration of API requests handled by service, method and status code",
|
||||
histogram.clone(),
|
||||
);
|
||||
histogram
|
||||
});
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../ui/build"]
|
||||
@ -103,7 +102,7 @@ type BoxError = Box<dyn std::error::Error + Send + Sync>;
|
||||
|
||||
pub async fn setup() -> Result<()> {
|
||||
let conf = config::get();
|
||||
let bind = conf.api.bind.parse()?;
|
||||
let bind = conf.api.bind.parse().context("Parse api.bind config")?;
|
||||
|
||||
info!(bind = %bind, "Setting up API interface");
|
||||
|
||||
@ -114,7 +113,7 @@ pub async fn setup() -> Result<()> {
|
||||
.route("/auth/oauth2/callback", get(oauth2::callback_handler))
|
||||
.fallback(service_static_handler)
|
||||
.into_service()
|
||||
.map_response(|r| r.map(tonic::body::boxed));
|
||||
.map_response(|r| r.map(tonic::body::Body::new));
|
||||
|
||||
let grpc = TonicServer::builder()
|
||||
.accept_http1(true)
|
||||
|
@ -484,7 +484,7 @@ pub mod test {
|
||||
let mut create_req = Request::new(create_req);
|
||||
create_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let create_resp = service.create(create_req).await.unwrap();
|
||||
|
||||
// get
|
||||
@ -494,7 +494,7 @@ pub mod test {
|
||||
let mut get_req = Request::new(get_req);
|
||||
get_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let get_resp = service.get(get_req).await.unwrap();
|
||||
assert_eq!(
|
||||
Some(api::Tenant {
|
||||
@ -524,7 +524,7 @@ pub mod test {
|
||||
let mut up_req = Request::new(up_req);
|
||||
up_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.update(up_req).await.unwrap();
|
||||
|
||||
// get
|
||||
@ -534,7 +534,7 @@ pub mod test {
|
||||
let mut get_req = Request::new(get_req);
|
||||
get_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let get_resp = service.get(get_req).await.unwrap();
|
||||
assert_eq!(
|
||||
Some(api::Tenant {
|
||||
@ -559,7 +559,7 @@ pub mod test {
|
||||
let mut list_req = Request::new(list_req);
|
||||
list_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let list_resp = service.list(list_req).await.unwrap();
|
||||
assert_eq!(1, list_resp.get_ref().total_count);
|
||||
assert_eq!(1, list_resp.get_ref().result.len());
|
||||
@ -571,7 +571,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.delete(del_req).await.unwrap();
|
||||
|
||||
let del_req = api::DeleteTenantRequest {
|
||||
@ -580,7 +580,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let del_resp = service.delete(del_req).await;
|
||||
assert!(del_resp.is_err());
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ pub mod test {
|
||||
let mut create_req = Request::new(create_req);
|
||||
create_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let create_resp = service.create(create_req).await.unwrap();
|
||||
|
||||
// get
|
||||
@ -304,7 +304,7 @@ pub mod test {
|
||||
let mut get_req = Request::new(get_req);
|
||||
get_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let get_resp = service.get(get_req).await.unwrap();
|
||||
assert_eq!(
|
||||
Some(api::User {
|
||||
@ -332,7 +332,7 @@ pub mod test {
|
||||
let mut up_req = Request::new(up_req);
|
||||
up_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.update(up_req).await.unwrap();
|
||||
|
||||
// get
|
||||
@ -342,7 +342,7 @@ pub mod test {
|
||||
let mut get_req = Request::new(get_req);
|
||||
get_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let get_resp = service.get(get_req).await.unwrap();
|
||||
assert_eq!(
|
||||
Some(api::User {
|
||||
@ -364,7 +364,7 @@ pub mod test {
|
||||
let mut up_req = Request::new(up_req);
|
||||
up_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.update_password(up_req).await.unwrap();
|
||||
|
||||
// list
|
||||
@ -375,7 +375,7 @@ pub mod test {
|
||||
let mut list_req = Request::new(list_req);
|
||||
list_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let list_resp = service.list(list_req).await.unwrap();
|
||||
// * Admin from migrations
|
||||
// * User that we created for auth
|
||||
@ -390,7 +390,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let _ = service.delete(del_req).await.unwrap();
|
||||
|
||||
let del_req = api::DeleteUserRequest {
|
||||
@ -399,7 +399,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let del_resp = service.delete(del_req).await;
|
||||
assert!(del_resp.is_err());
|
||||
|
||||
@ -409,7 +409,7 @@ pub mod test {
|
||||
let mut del_req = Request::new(del_req);
|
||||
del_req
|
||||
.extensions_mut()
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id).clone()));
|
||||
.insert(AuthID::User(Into::<uuid::Uuid>::into(u.id)));
|
||||
let del_resp = service.delete(del_req).await;
|
||||
assert!(del_resp.is_err());
|
||||
}
|
||||
|
@ -33,11 +33,8 @@ async fn handle_uplink_v100(
|
||||
) -> Result<()> {
|
||||
let pl = clocksync::v1::Payload::from_slice(true, data)?;
|
||||
|
||||
match pl {
|
||||
clocksync::v1::Payload::AppTimeReq(pl) => {
|
||||
handle_v1_app_time_req(dev, dp, rx_info, pl).await?
|
||||
}
|
||||
_ => {}
|
||||
if let clocksync::v1::Payload::AppTimeReq(pl) = pl {
|
||||
handle_v1_app_time_req(dev, dp, rx_info, pl).await?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -51,11 +48,8 @@ async fn handle_uplink_v200(
|
||||
) -> Result<()> {
|
||||
let pl = clocksync::v2::Payload::from_slice(true, data)?;
|
||||
|
||||
match pl {
|
||||
clocksync::v2::Payload::AppTimeReq(pl) => {
|
||||
handle_v2_app_time_req(dev, dp, rx_info, pl).await?
|
||||
}
|
||||
_ => {}
|
||||
if let clocksync::v2::Payload::AppTimeReq(pl) = pl {
|
||||
handle_v2_app_time_req(dev, dp, rx_info, pl).await?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -181,7 +175,7 @@ mod test {
|
||||
Test {
|
||||
name: "device synced".into(),
|
||||
rx_info: gw::UplinkRxInfo {
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).into()),
|
||||
..Default::default()
|
||||
},
|
||||
req: clocksync::v1::AppTimeReqPayload {
|
||||
@ -196,7 +190,7 @@ mod test {
|
||||
Test {
|
||||
name: "device synced - ans required".into(),
|
||||
rx_info: gw::UplinkRxInfo {
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).into()),
|
||||
..Default::default()
|
||||
},
|
||||
req: clocksync::v1::AppTimeReqPayload {
|
||||
@ -214,7 +208,7 @@ mod test {
|
||||
Test {
|
||||
name: "device not synced (positive correction)".into(),
|
||||
rx_info: gw::UplinkRxInfo {
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).into()),
|
||||
..Default::default()
|
||||
},
|
||||
req: clocksync::v1::AppTimeReqPayload {
|
||||
@ -232,7 +226,7 @@ mod test {
|
||||
Test {
|
||||
name: "device not synced (negative correction)".into(),
|
||||
rx_info: gw::UplinkRxInfo {
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1200).try_into().unwrap()),
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1200).into()),
|
||||
..Default::default()
|
||||
},
|
||||
req: clocksync::v1::AppTimeReqPayload {
|
||||
@ -330,7 +324,7 @@ mod test {
|
||||
Test {
|
||||
name: "device synced".into(),
|
||||
rx_info: gw::UplinkRxInfo {
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).into()),
|
||||
..Default::default()
|
||||
},
|
||||
req: clocksync::v2::AppTimeReqPayload {
|
||||
@ -345,7 +339,7 @@ mod test {
|
||||
Test {
|
||||
name: "device synced - ans required".into(),
|
||||
rx_info: gw::UplinkRxInfo {
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).into()),
|
||||
..Default::default()
|
||||
},
|
||||
req: clocksync::v2::AppTimeReqPayload {
|
||||
@ -363,7 +357,7 @@ mod test {
|
||||
Test {
|
||||
name: "device not synced (positive correction)".into(),
|
||||
rx_info: gw::UplinkRxInfo {
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1234).into()),
|
||||
..Default::default()
|
||||
},
|
||||
req: clocksync::v2::AppTimeReqPayload {
|
||||
@ -381,7 +375,7 @@ mod test {
|
||||
Test {
|
||||
name: "device not synced (negative correction)".into(),
|
||||
rx_info: gw::UplinkRxInfo {
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1200).try_into().unwrap()),
|
||||
time_since_gps_epoch: Some(Duration::from_secs(1200).into()),
|
||||
..Default::default()
|
||||
},
|
||||
req: clocksync::v2::AppTimeReqPayload {
|
||||
|
@ -397,6 +397,20 @@ impl Flow {
|
||||
)
|
||||
.to_vec()?,
|
||||
Some(Ts004Version::V200) => {
|
||||
let dev = device::get(&fuota_dev.dev_eui).await?;
|
||||
let session_cnt = dev.app_layer_params.ts004_session_cnt[0];
|
||||
let mut app_layer_params = dev.app_layer_params.clone();
|
||||
app_layer_params.ts004_session_cnt[0] += 1;
|
||||
|
||||
device::partial_update(
|
||||
fuota_dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
app_layer_params: Some(app_layer_params),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let dev_keys = device_keys::get(&fuota_dev.dev_eui).await?;
|
||||
let data_block_int_key = match self.device_profile.mac_version {
|
||||
MacVersion::LORAWAN_1_0_0
|
||||
@ -412,7 +426,7 @@ impl Flow {
|
||||
};
|
||||
let mic = fragmentation::v2::calculate_mic(
|
||||
data_block_int_key,
|
||||
0,
|
||||
session_cnt,
|
||||
0,
|
||||
[0, 0, 0, 0],
|
||||
&self.fuota_deployment.payload,
|
||||
@ -434,7 +448,7 @@ impl Flow {
|
||||
},
|
||||
descriptor: [0, 0, 0, 0],
|
||||
mic,
|
||||
session_cnt: 0,
|
||||
session_cnt,
|
||||
},
|
||||
)
|
||||
.to_vec()?
|
||||
@ -500,7 +514,7 @@ impl Flow {
|
||||
FuotaJob::Enqueue,
|
||||
self.fuota_deployment
|
||||
.multicast_session_start
|
||||
.unwrap_or_else(|| Utc::now()),
|
||||
.unwrap_or_else(Utc::now),
|
||||
)));
|
||||
}
|
||||
|
||||
@ -657,7 +671,7 @@ impl Flow {
|
||||
FuotaJob::Enqueue,
|
||||
self.fuota_deployment
|
||||
.multicast_session_start
|
||||
.unwrap_or_else(|| Utc::now()),
|
||||
.unwrap_or_else(Utc::now),
|
||||
)))
|
||||
}
|
||||
}
|
||||
@ -756,7 +770,7 @@ impl Flow {
|
||||
FuotaJob::DeleteMcGroup,
|
||||
self.fuota_deployment
|
||||
.multicast_session_end
|
||||
.unwrap_or_else(|| Utc::now()),
|
||||
.unwrap_or_else(Utc::now),
|
||||
))),
|
||||
RequestFragmentationSessionStatus::AfterFragEnqueue => {
|
||||
Ok(Some((FuotaJob::FragStatus, Utc::now())))
|
||||
@ -765,7 +779,7 @@ impl Flow {
|
||||
FuotaJob::FragStatus,
|
||||
self.fuota_deployment
|
||||
.multicast_session_end
|
||||
.unwrap_or_else(|| Utc::now()),
|
||||
.unwrap_or_else(Utc::now),
|
||||
))),
|
||||
}
|
||||
}
|
||||
@ -852,7 +866,7 @@ impl Flow {
|
||||
FuotaJob::DeleteMcGroup,
|
||||
self.fuota_deployment
|
||||
.multicast_session_end
|
||||
.unwrap_or_else(|| Utc::now()),
|
||||
.unwrap_or_else(Utc::now),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::sync::RwLock;
|
||||
@ -8,9 +8,8 @@ use crate::{config, stream};
|
||||
use backend::{Client, ClientConfig};
|
||||
use lrwn::{EUI64Prefix, EUI64};
|
||||
|
||||
lazy_static! {
|
||||
static ref CLIENTS: RwLock<Vec<(EUI64Prefix, Arc<Client>)>> = RwLock::new(vec![]);
|
||||
}
|
||||
static CLIENTS: LazyLock<RwLock<Vec<(EUI64Prefix, Arc<Client>)>>> =
|
||||
LazyLock::new(|| RwLock::new(vec![]));
|
||||
|
||||
pub async fn setup() -> Result<()> {
|
||||
info!("Setting up Join Server clients");
|
||||
@ -30,6 +29,11 @@ pub async fn setup() -> Result<()> {
|
||||
tls_key: js.tls_key.clone(),
|
||||
async_timeout: js.async_timeout,
|
||||
request_log_sender: stream::backend_interfaces::get_log_sender().await,
|
||||
authorization: if js.authorization_header.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(js.authorization_header.clone())
|
||||
},
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::{Duration, DurationRound};
|
||||
@ -15,9 +15,8 @@ use backend::{Client, ClientConfig, GWInfoElement, ULMetaData};
|
||||
use chirpstack_api::{common, gw};
|
||||
use lrwn::{region, DevAddr, NetID, EUI64};
|
||||
|
||||
lazy_static! {
|
||||
static ref CLIENTS: RwLock<HashMap<NetID, Arc<Client>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
static CLIENTS: LazyLock<RwLock<HashMap<NetID, Arc<Client>>>> =
|
||||
LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
pub async fn setup() -> Result<()> {
|
||||
info!("Setting up roaming clients");
|
||||
|
@ -797,6 +797,11 @@ pub fn run() {
|
||||
# #
|
||||
# # Set this to enable client-certificate authentication with the join-server.
|
||||
# tls_key="/path/to/tls_key.pem"
|
||||
|
||||
# # Authorization header.
|
||||
# #
|
||||
# # Optional value of the Authorization header, e.g. token or password.
|
||||
# authorization_header="Bearer sometoken"
|
||||
{{#each join_server.servers}}
|
||||
|
||||
[[join_server.servers]]
|
||||
@ -807,6 +812,7 @@ pub fn run() {
|
||||
ca_cert="{{ this.ca_cert }}"
|
||||
tls_cert="{{ this.tls_cert }}"
|
||||
tls_key="{{ this.tls_key }}"
|
||||
authorization_header="{{ this.authorization_header }}"
|
||||
{{/each}}
|
||||
|
||||
|
||||
@ -1000,7 +1006,7 @@ pub fn run() {
|
||||
let conf = config::get();
|
||||
println!(
|
||||
"{}",
|
||||
reg.render_template(&template, &conf)
|
||||
reg.render_template(template, &conf)
|
||||
.expect("render configfile error")
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, LazyLock, Mutex};
|
||||
use std::time::Duration;
|
||||
use std::{env, fs};
|
||||
|
||||
@ -9,9 +9,8 @@ use serde::{Deserialize, Serialize};
|
||||
use lrwn::region::CommonName;
|
||||
use lrwn::{AES128Key, DevAddrPrefix, EUI64Prefix, NetID};
|
||||
|
||||
lazy_static! {
|
||||
static ref CONFIG: Mutex<Arc<Configuration>> = Mutex::new(Arc::new(Default::default()));
|
||||
}
|
||||
static CONFIG: LazyLock<Mutex<Arc<Configuration>>> =
|
||||
LazyLock::new(|| Mutex::new(Arc::new(Default::default())));
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone)]
|
||||
#[serde(default)]
|
||||
@ -525,6 +524,7 @@ pub struct JoinServerServer {
|
||||
pub ca_cert: String,
|
||||
pub tls_cert: String,
|
||||
pub tls_key: String,
|
||||
pub authorization_header: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use aes::cipher::generic_array::GenericArray;
|
||||
use aes::cipher::{BlockEncrypt, KeyInit};
|
||||
use aes::{Aes128, Block};
|
||||
@ -7,14 +9,11 @@ use tracing::debug;
|
||||
|
||||
use lrwn::DevAddr;
|
||||
|
||||
lazy_static! {
|
||||
static ref BEACON_PERIOD: Duration = Duration::try_seconds(128).unwrap();
|
||||
static ref BEACON_RESERVED: Duration = Duration::try_milliseconds(2120).unwrap();
|
||||
static ref BEACON_GUARD: Duration = Duration::try_seconds(3).unwrap();
|
||||
static ref BEACON_WINDOW: Duration = Duration::try_milliseconds(122880).unwrap();
|
||||
static ref PING_PERIOD_BASE: usize = 1 << 12;
|
||||
static ref SLOT_LEN: Duration = Duration::try_milliseconds(30).unwrap();
|
||||
}
|
||||
static BEACON_PERIOD: LazyLock<Duration> = LazyLock::new(|| Duration::try_seconds(128).unwrap());
|
||||
static BEACON_RESERVED: LazyLock<Duration> =
|
||||
LazyLock::new(|| Duration::try_milliseconds(2120).unwrap());
|
||||
static PING_PERIOD_BASE: usize = 1 << 12;
|
||||
static SLOT_LEN: LazyLock<Duration> = LazyLock::new(|| Duration::try_milliseconds(30).unwrap());
|
||||
|
||||
pub fn get_beacon_start(ts: Duration) -> Duration {
|
||||
Duration::try_seconds(ts.num_seconds() - (ts.num_seconds() % BEACON_PERIOD.num_seconds()))
|
||||
@ -26,7 +25,7 @@ pub fn get_ping_offset(beacon_ts: Duration, dev_addr: &DevAddr, ping_nb: usize)
|
||||
return Err(anyhow!("ping_nb must be > 0"));
|
||||
}
|
||||
|
||||
let ping_period = *PING_PERIOD_BASE / ping_nb;
|
||||
let ping_period = PING_PERIOD_BASE / ping_nb;
|
||||
let beacon_time = (beacon_ts.num_seconds() % (1 << 32)) as u32;
|
||||
|
||||
let key_bytes: [u8; 16] = [0x00; 16];
|
||||
@ -54,7 +53,7 @@ pub fn get_next_ping_slot_after(
|
||||
}
|
||||
|
||||
let mut beacon_start_ts = get_beacon_start(after_gps_epoch_ts);
|
||||
let ping_period = *PING_PERIOD_BASE / ping_nb;
|
||||
let ping_period = PING_PERIOD_BASE / ping_nb;
|
||||
|
||||
loop {
|
||||
let ping_offset = get_ping_offset(beacon_start_ts, dev_addr, ping_nb)?;
|
||||
@ -122,7 +121,7 @@ pub mod test {
|
||||
for k in 0..8 {
|
||||
let mut beacon_ts = Duration::zero();
|
||||
let ping_nb: usize = 1 << k;
|
||||
let ping_period = *PING_PERIOD_BASE / ping_nb;
|
||||
let ping_period = PING_PERIOD_BASE / ping_nb;
|
||||
let dev_addr = DevAddr::from_be_bytes([0, 0, 0, 0]);
|
||||
|
||||
for _ in 0..100000 {
|
||||
|
@ -1827,20 +1827,19 @@ impl Data {
|
||||
let set = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::ConfigureFwdLimitReq(
|
||||
lrwn::ConfigureFwdLimitReqPayload {
|
||||
reload_rate: lrwn::FwdLimitReloadRatePL {
|
||||
overall_reload_rate: relay_params.relay_overall_limit_reload_rate as u8,
|
||||
overall_reload_rate: relay_params.relay_overall_limit_reload_rate,
|
||||
global_uplink_reload_rate: relay_params
|
||||
.relay_global_uplink_limit_reload_rate
|
||||
as u8,
|
||||
notify_reload_rate: relay_params.relay_notify_limit_reload_rate as u8,
|
||||
join_req_reload_rate: relay_params.relay_join_req_limit_reload_rate as u8,
|
||||
.relay_global_uplink_limit_reload_rate,
|
||||
notify_reload_rate: relay_params.relay_notify_limit_reload_rate,
|
||||
join_req_reload_rate: relay_params.relay_join_req_limit_reload_rate,
|
||||
reset_limit_counter: lrwn::ResetLimitCounter::NoChange,
|
||||
},
|
||||
load_capacity: lrwn::FwdLimitLoadCapacityPL {
|
||||
overall_limit_size: relay_params.relay_overall_limit_bucket_size as u8,
|
||||
global_uplink_limit_size: relay_params.relay_global_uplink_limit_bucket_size
|
||||
as u8,
|
||||
notify_limit_size: relay_params.relay_notify_limit_bucket_size as u8,
|
||||
join_req_limit_size: relay_params.relay_join_req_limit_bucket_size as u8,
|
||||
overall_limit_size: relay_params.relay_overall_limit_bucket_size,
|
||||
global_uplink_limit_size: relay_params
|
||||
.relay_global_uplink_limit_bucket_size,
|
||||
notify_limit_size: relay_params.relay_notify_limit_bucket_size,
|
||||
join_req_limit_size: relay_params.relay_join_req_limit_bucket_size,
|
||||
},
|
||||
},
|
||||
)]);
|
||||
@ -2052,7 +2051,7 @@ impl Data {
|
||||
if relay.enabled != relay_params.relay_enabled
|
||||
|| relay.cad_periodicity != relay_params.relay_cad_periodicity as u32
|
||||
|| relay.default_channel_index != relay_params.default_channel_index as u32
|
||||
|| relay.second_channel_freq != relay_params.second_channel_freq as u32
|
||||
|| relay.second_channel_freq != relay_params.second_channel_freq
|
||||
|| relay.second_channel_dr != relay_params.second_channel_dr as u32
|
||||
|| relay.second_channel_ack_offset != relay_params.second_channel_ack_offset as u32
|
||||
{
|
||||
@ -2063,17 +2062,17 @@ impl Data {
|
||||
true => 1,
|
||||
false => 0,
|
||||
},
|
||||
cad_periodicity: relay_params.relay_cad_periodicity as u8,
|
||||
default_ch_idx: relay_params.default_channel_index as u8,
|
||||
cad_periodicity: relay_params.relay_cad_periodicity,
|
||||
default_ch_idx: relay_params.default_channel_index,
|
||||
second_ch_idx: if relay_params.second_channel_freq > 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
second_ch_dr: relay_params.second_channel_dr as u8,
|
||||
second_ch_ack_offset: relay_params.second_channel_ack_offset as u8,
|
||||
second_ch_dr: relay_params.second_channel_dr,
|
||||
second_ch_ack_offset: relay_params.second_channel_ack_offset,
|
||||
},
|
||||
second_ch_freq: relay_params.second_channel_freq as u32,
|
||||
second_ch_freq: relay_params.second_channel_freq,
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&dev_eui, lrwn::CID::RelayConfReq, &set).await?;
|
||||
@ -2102,7 +2101,7 @@ impl Data {
|
||||
if relay.ed_activation_mode != relay_params.ed_activation_mode.to_u8() as u32
|
||||
|| relay.ed_smart_enable_level != relay_params.ed_smart_enable_level as u32
|
||||
|| relay.ed_back_off != relay_params.ed_back_off as u32
|
||||
|| relay.second_channel_freq != relay_params.second_channel_freq as u32
|
||||
|| relay.second_channel_freq != relay_params.second_channel_freq
|
||||
|| relay.second_channel_dr != relay_params.second_channel_dr as u32
|
||||
|| relay.second_channel_ack_offset != relay_params.second_channel_ack_offset as u32
|
||||
{
|
||||
@ -2110,19 +2109,19 @@ impl Data {
|
||||
lrwn::EndDeviceConfReqPayload {
|
||||
activation_relay_mode: lrwn::ActivationRelayMode {
|
||||
relay_mode_activation: relay_params.ed_activation_mode,
|
||||
smart_enable_level: relay_params.ed_smart_enable_level as u8,
|
||||
smart_enable_level: relay_params.ed_smart_enable_level,
|
||||
},
|
||||
channel_settings_ed: lrwn::ChannelSettingsED {
|
||||
second_ch_ack_offset: relay_params.second_channel_ack_offset as u8,
|
||||
second_ch_dr: relay_params.second_channel_dr as u8,
|
||||
second_ch_ack_offset: relay_params.second_channel_ack_offset,
|
||||
second_ch_dr: relay_params.second_channel_dr,
|
||||
second_ch_idx: if relay_params.second_channel_freq > 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
backoff: relay_params.ed_back_off as u8,
|
||||
backoff: relay_params.ed_back_off,
|
||||
},
|
||||
second_ch_freq: relay_params.second_channel_freq as u32,
|
||||
second_ch_freq: relay_params.second_channel_freq,
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&dev_eui, lrwn::CID::EndDeviceConfReq, &set).await?;
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use tokio::sync::RwLock;
|
||||
@ -6,11 +8,10 @@ use chirpstack_api::gw;
|
||||
|
||||
use super::GatewayBackend;
|
||||
|
||||
lazy_static! {
|
||||
static ref DOWNLINK_FRAMES: RwLock<Vec<gw::DownlinkFrame>> = RwLock::new(Vec::new());
|
||||
static ref GATEWAY_CONFIGURATIONS: RwLock<Vec<gw::GatewayConfiguration>> =
|
||||
RwLock::new(Vec::new());
|
||||
}
|
||||
static DOWNLINK_FRAMES: LazyLock<RwLock<Vec<gw::DownlinkFrame>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
static GATEWAY_CONFIGURATIONS: LazyLock<RwLock<Vec<gw::GatewayConfiguration>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
|
||||
pub async fn reset() {
|
||||
DOWNLINK_FRAMES.write().await.drain(..);
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
@ -11,10 +12,8 @@ use crate::config;
|
||||
pub mod mock;
|
||||
mod mqtt;
|
||||
|
||||
lazy_static! {
|
||||
static ref BACKENDS: RwLock<HashMap<String, Box<dyn GatewayBackend + Sync + Send>>> =
|
||||
RwLock::new(HashMap::new());
|
||||
}
|
||||
static BACKENDS: LazyLock<RwLock<HashMap<String, Box<dyn GatewayBackend + Sync + Send>>>> =
|
||||
LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
#[async_trait]
|
||||
pub trait GatewayBackend {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::{LazyLock, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
@ -38,27 +38,26 @@ struct CommandLabels {
|
||||
command: String,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref EVENT_COUNTER: Family<EventLabels, Counter> = {
|
||||
let counter = Family::<EventLabels, Counter>::default();
|
||||
prometheus::register(
|
||||
"gateway_backend_mqtt_events",
|
||||
"Number of events received",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
};
|
||||
static ref COMMAND_COUNTER: Family<CommandLabels, Counter> = {
|
||||
let counter = Family::<CommandLabels, Counter>::default();
|
||||
prometheus::register(
|
||||
"gateway_backend_mqtt_commands",
|
||||
"Number of commands sent",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
};
|
||||
static ref GATEWAY_JSON: RwLock<HashMap<String, bool>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
static EVENT_COUNTER: LazyLock<Family<EventLabels, Counter>> = LazyLock::new(|| {
|
||||
let counter = Family::<EventLabels, Counter>::default();
|
||||
prometheus::register(
|
||||
"gateway_backend_mqtt_events",
|
||||
"Number of events received",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
});
|
||||
static COMMAND_COUNTER: LazyLock<Family<CommandLabels, Counter>> = LazyLock::new(|| {
|
||||
let counter = Family::<CommandLabels, Counter>::default();
|
||||
prometheus::register(
|
||||
"gateway_backend_mqtt_commands",
|
||||
"Number of commands sent",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
});
|
||||
static GATEWAY_JSON: LazyLock<RwLock<HashMap<String, bool>>> =
|
||||
LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
pub struct MqttBackend<'a> {
|
||||
client: AsyncClient,
|
||||
|
@ -1,82 +1,85 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use chrono::{DateTime, Duration, TimeZone, Utc};
|
||||
|
||||
lazy_static! {
|
||||
static ref GPS_EPOCH_TIME: DateTime<Utc> = Utc.with_ymd_and_hms(1980, 1, 6, 0, 0, 0).unwrap();
|
||||
static ref LEAP_SECONDS_TABLE: Vec<(DateTime<Utc>, Duration)> = vec![
|
||||
static GPS_EPOCH_TIME: LazyLock<DateTime<Utc>> =
|
||||
LazyLock::new(|| Utc.with_ymd_and_hms(1980, 1, 6, 0, 0, 0).unwrap());
|
||||
static LEAP_SECONDS_TABLE: LazyLock<Vec<(DateTime<Utc>, Duration)>> = LazyLock::new(|| {
|
||||
vec![
|
||||
(
|
||||
Utc.with_ymd_and_hms(1981, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1982, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1983, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1985, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1987, 12, 31, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1989, 12, 31, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1990, 12, 31, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1992, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1993, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1994, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1995, 12, 31, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1997, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(1998, 12, 31, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(2005, 12, 31, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(2008, 12, 31, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(2012, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(2015, 6, 30, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
(
|
||||
Utc.with_ymd_and_hms(2016, 12, 31, 23, 59, 59).unwrap(),
|
||||
Duration::try_seconds(1).unwrap()
|
||||
Duration::try_seconds(1).unwrap(),
|
||||
),
|
||||
];
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
pub trait ToGpsTime {
|
||||
fn to_gps_time(&self) -> Duration;
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
@ -19,10 +20,8 @@ use chirpstack_api::integration;
|
||||
// implement re-connect on error. To reconnect within the Integration struct would require
|
||||
// mutability of the Integration struct, which is not possible without changing the
|
||||
// IntegrationTrait as we would need to change the (&self, ...) signatures to (&mut self, ...).
|
||||
lazy_static! {
|
||||
static ref CONNECTION: RwLock<Option<Connection>> = RwLock::new(None);
|
||||
static ref CHANNEL: RwLock<Option<Channel>> = RwLock::new(None);
|
||||
}
|
||||
static CONNECTION: LazyLock<RwLock<Option<Connection>>> = LazyLock::new(|| RwLock::new(None));
|
||||
static CHANNEL: LazyLock<RwLock<Option<Channel>>> = LazyLock::new(|| RwLock::new(None));
|
||||
|
||||
pub struct Integration<'a> {
|
||||
templates: Handlebars<'a>,
|
||||
@ -128,7 +127,7 @@ impl<'a> Integration<'a> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a> IntegrationTrait for Integration<'a> {
|
||||
impl IntegrationTrait for Integration<'_> {
|
||||
async fn uplink_event(
|
||||
&self,
|
||||
_vars: &HashMap<String, String>,
|
||||
|
@ -107,7 +107,7 @@ impl<'a> Integration<'a> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a> IntegrationTrait for Integration<'a> {
|
||||
impl IntegrationTrait for Integration<'_> {
|
||||
async fn uplink_event(
|
||||
&self,
|
||||
_vars: &HashMap<String, String>,
|
||||
|
@ -539,7 +539,7 @@ impl<'de> Deserialize<'de> for Eui64Wrapper {
|
||||
|
||||
struct Eui64WrapperVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for Eui64WrapperVisitor {
|
||||
impl Visitor<'_> for Eui64WrapperVisitor {
|
||||
type Value = Eui64Wrapper;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
@ -8,17 +9,22 @@ use chirpstack_api::integration;
|
||||
|
||||
use super::Integration as IntegrationTrait;
|
||||
|
||||
lazy_static! {
|
||||
static ref UPLINK_EVENTS: RwLock<Vec<integration::UplinkEvent>> = RwLock::new(Vec::new());
|
||||
static ref JOIN_EVENTS: RwLock<Vec<integration::JoinEvent>> = RwLock::new(Vec::new());
|
||||
static ref ACK_EVENTS: RwLock<Vec<integration::AckEvent>> = RwLock::new(Vec::new());
|
||||
static ref TXACK_EVENTS: RwLock<Vec<integration::TxAckEvent>> = RwLock::new(Vec::new());
|
||||
static ref LOG_EVENTS: RwLock<Vec<integration::LogEvent>> = RwLock::new(Vec::new());
|
||||
static ref STATUS_EVENTS: RwLock<Vec<integration::StatusEvent>> = RwLock::new(Vec::new());
|
||||
static ref LOCATION_EVENTS: RwLock<Vec<integration::LocationEvent>> = RwLock::new(Vec::new());
|
||||
static ref INTEGRATION_EVENTS: RwLock<Vec<integration::IntegrationEvent>> =
|
||||
RwLock::new(Vec::new());
|
||||
}
|
||||
static UPLINK_EVENTS: LazyLock<RwLock<Vec<integration::UplinkEvent>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
static JOIN_EVENTS: LazyLock<RwLock<Vec<integration::JoinEvent>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
static ACK_EVENTS: LazyLock<RwLock<Vec<integration::AckEvent>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
static TXACK_EVENTS: LazyLock<RwLock<Vec<integration::TxAckEvent>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
static LOG_EVENTS: LazyLock<RwLock<Vec<integration::LogEvent>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
static STATUS_EVENTS: LazyLock<RwLock<Vec<integration::StatusEvent>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
static LOCATION_EVENTS: LazyLock<RwLock<Vec<integration::LocationEvent>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
static INTEGRATION_EVENTS: LazyLock<RwLock<Vec<integration::IntegrationEvent>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
|
||||
pub async fn reset() {
|
||||
UPLINK_EVENTS.write().await.drain(..);
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
@ -33,11 +34,11 @@ mod postgresql;
|
||||
mod redis;
|
||||
mod thingsboard;
|
||||
|
||||
lazy_static! {
|
||||
static ref GLOBAL_INTEGRATIONS: RwLock<Vec<Box<dyn Integration + Sync + Send>>> =
|
||||
RwLock::new(Vec::new());
|
||||
static ref MOCK_INTEGRATION: RwLock<bool> = RwLock::new(false);
|
||||
}
|
||||
static GLOBAL_INTEGRATIONS: LazyLock<RwLock<Vec<Box<dyn Integration + Sync + Send>>>> =
|
||||
LazyLock::new(|| RwLock::new(Vec::new()));
|
||||
|
||||
#[cfg(test)]
|
||||
static MOCK_INTEGRATION: LazyLock<RwLock<bool>> = LazyLock::new(|| RwLock::new(false));
|
||||
|
||||
pub async fn setup() -> Result<()> {
|
||||
info!("Setting up global integrations");
|
||||
|
@ -33,7 +33,7 @@ pub mod update_uplink_list;
|
||||
// This returns the mac-commands which must be sent back to the device as response and a bool
|
||||
// indicating if a downlink must be sent. For some mac-commands, no mac-command answer is required,
|
||||
// but the device expects a downlink as confirmation, even if the downlink frame is empty.
|
||||
pub async fn handle_uplink<'a>(
|
||||
pub async fn handle_uplink(
|
||||
uplink_frame_set: &UplinkFrameSet,
|
||||
cmds: &lrwn::MACCommandSet,
|
||||
tenant: &tenant::Tenant,
|
||||
|
@ -1,7 +1,5 @@
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate diesel_migrations;
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
@ -1,12 +1,10 @@
|
||||
use std::sync::RwLock;
|
||||
use std::sync::{LazyLock, RwLock};
|
||||
|
||||
use anyhow::Result;
|
||||
use prometheus_client::encoding::text::encode;
|
||||
use prometheus_client::registry::{Metric, Registry};
|
||||
|
||||
lazy_static! {
|
||||
static ref REGISTRY: RwLock<Registry> = RwLock::new(<Registry>::default());
|
||||
}
|
||||
static REGISTRY: LazyLock<RwLock<Registry>> = LazyLock::new(|| RwLock::new(<Registry>::default()));
|
||||
|
||||
pub fn encode_to_string() -> Result<String> {
|
||||
let registry_r = REGISTRY.read().unwrap();
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::{Arc, LazyLock, RwLock};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use tracing::{info, span, trace, Level};
|
||||
@ -7,10 +7,8 @@ use tracing::{info, span, trace, Level};
|
||||
use crate::config;
|
||||
use lrwn::region;
|
||||
|
||||
lazy_static! {
|
||||
static ref REGIONS: RwLock<HashMap<String, Arc<Box<dyn region::Region + Sync + Send>>>> =
|
||||
RwLock::new(HashMap::new());
|
||||
}
|
||||
static REGIONS: LazyLock<RwLock<HashMap<String, Arc<Box<dyn region::Region + Sync + Send>>>>> =
|
||||
LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
pub fn setup() -> Result<()> {
|
||||
info!("Setting up regions");
|
||||
|
@ -116,6 +116,7 @@ pub struct Device {
|
||||
pub join_eui: EUI64,
|
||||
pub secondary_dev_addr: Option<DevAddr>,
|
||||
pub device_session: Option<fields::DeviceSession>,
|
||||
pub app_layer_params: fields::device::AppLayerParams,
|
||||
}
|
||||
|
||||
#[derive(AsChangeset, Debug, Clone, Default)]
|
||||
@ -133,6 +134,7 @@ pub struct DeviceChangeset {
|
||||
pub battery_level: Option<Option<fields::BigDecimal>>,
|
||||
pub scheduler_run_after: Option<Option<DateTime<Utc>>>,
|
||||
pub is_disabled: Option<bool>,
|
||||
pub app_layer_params: Option<fields::device::AppLayerParams>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@ -190,6 +192,7 @@ impl Default for Device {
|
||||
join_eui: EUI64::default(),
|
||||
secondary_dev_addr: None,
|
||||
device_session: None,
|
||||
app_layer_params: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -552,6 +555,7 @@ pub async fn update(d: Device) -> Result<Device, Error> {
|
||||
device::tags.eq(&d.tags),
|
||||
device::variables.eq(&d.variables),
|
||||
device::join_eui.eq(&d.join_eui),
|
||||
device::app_layer_params.eq(&d.app_layer_params),
|
||||
))
|
||||
.get_result(&mut get_async_db_conn().await?)
|
||||
.await
|
||||
|
@ -109,7 +109,7 @@ impl DeviceProfile {
|
||||
|
||||
if let Some(class_b_params) = &self.class_b_params {
|
||||
ds.class_b_ping_slot_dr = class_b_params.ping_slot_dr as u32;
|
||||
ds.class_b_ping_slot_freq = class_b_params.ping_slot_freq as u32;
|
||||
ds.class_b_ping_slot_freq = class_b_params.ping_slot_freq;
|
||||
ds.class_b_ping_slot_nb = 1 << class_b_params.ping_slot_nb_k as u32;
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ impl DeviceProfile {
|
||||
ds.rx1_delay = abp_params.rx1_delay as u32;
|
||||
ds.rx1_dr_offset = abp_params.rx1_dr_offset as u32;
|
||||
ds.rx2_dr = abp_params.rx2_dr as u32;
|
||||
ds.rx2_frequency = abp_params.rx2_freq as u32;
|
||||
ds.rx2_frequency = abp_params.rx2_freq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ pub enum Error {
|
||||
NotAllowed(String),
|
||||
|
||||
#[error("Multiple errors")]
|
||||
MultiError(Vec<Error>),
|
||||
Multi(Vec<Error>),
|
||||
|
||||
#[error(transparent)]
|
||||
Diesel(#[from] diesel::result::Error),
|
||||
|
52
chirpstack/src/storage/fields/device.rs
Normal file
52
chirpstack/src/storage/fields/device.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use diesel::backend::Backend;
|
||||
use diesel::{deserialize, serialize};
|
||||
#[cfg(feature = "postgres")]
|
||||
use diesel::{pg::Pg, sql_types::Jsonb};
|
||||
#[cfg(feature = "sqlite")]
|
||||
use diesel::{sql_types::Text, sqlite::Sqlite};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, AsExpression, FromSqlRow)]
|
||||
#[cfg_attr(feature = "postgres", diesel(sql_type = Jsonb))]
|
||||
#[cfg_attr(feature = "sqlite", diesel(sql_type = Text))]
|
||||
#[serde(default)]
|
||||
#[derive(Default)]
|
||||
pub struct AppLayerParams {
|
||||
pub ts004_session_cnt: [u16; 4],
|
||||
}
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
impl deserialize::FromSql<Jsonb, Pg> for AppLayerParams {
|
||||
fn from_sql(value: <Pg as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
|
||||
let value = <serde_json::Value as deserialize::FromSql<Jsonb, Pg>>::from_sql(value)?;
|
||||
Ok(serde_json::from_value(value)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
impl serialize::ToSql<Jsonb, Pg> for AppLayerParams {
|
||||
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
|
||||
let value = serde_json::to_value(self)?;
|
||||
<serde_json::Value as serialize::ToSql<Jsonb, Pg>>::to_sql(&value, &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
impl deserialize::FromSql<Text, Sqlite> for AppLayerParams
|
||||
where
|
||||
*const str: deserialize::FromSql<Text, Sqlite>,
|
||||
{
|
||||
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
|
||||
let s =
|
||||
<*const str as deserialize::FromSql<diesel::sql_types::Text, Sqlite>>::from_sql(value)?;
|
||||
Ok(serde_json::from_str(unsafe { &*s })?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
impl serialize::ToSql<Text, Sqlite> for AppLayerParams {
|
||||
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Sqlite>) -> serialize::Result {
|
||||
out.set_value(serde_json::to_string(&self)?);
|
||||
Ok(serialize::IsNull::No)
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ impl deserialize::FromSql<Jsonb, Pg> for AbpParams {
|
||||
#[cfg(feature = "postgres")]
|
||||
impl serialize::ToSql<Jsonb, Pg> for AbpParams {
|
||||
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
|
||||
let value = serde_json::to_value(&self)?;
|
||||
let value = serde_json::to_value(self)?;
|
||||
<serde_json::Value as serialize::ToSql<Jsonb, Pg>>::to_sql(&value, &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,7 @@ impl deserialize::FromSql<Jsonb, Pg> for ClassBParams {
|
||||
#[cfg(feature = "postgres")]
|
||||
impl serialize::ToSql<Jsonb, Pg> for ClassBParams {
|
||||
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
|
||||
let value = serde_json::to_value(&self)?;
|
||||
let value = serde_json::to_value(self)?;
|
||||
<serde_json::Value as serialize::ToSql<Jsonb, Pg>>::to_sql(&value, &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
@ -122,7 +122,7 @@ impl deserialize::FromSql<Jsonb, Pg> for ClassCParams {
|
||||
#[cfg(feature = "postgres")]
|
||||
impl serialize::ToSql<Jsonb, Pg> for ClassCParams {
|
||||
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
|
||||
let value = serde_json::to_value(&self)?;
|
||||
let value = serde_json::to_value(self)?;
|
||||
<serde_json::Value as serialize::ToSql<Jsonb, Pg>>::to_sql(&value, &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
@ -189,7 +189,7 @@ impl deserialize::FromSql<Jsonb, Pg> for RelayParams {
|
||||
#[cfg(feature = "postgres")]
|
||||
impl serialize::ToSql<Jsonb, Pg> for RelayParams {
|
||||
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
|
||||
let value = serde_json::to_value(&self)?;
|
||||
let value = serde_json::to_value(self)?;
|
||||
<serde_json::Value as serialize::ToSql<Jsonb, Pg>>::to_sql(&value, &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
@ -263,14 +263,9 @@ impl Default for AppLayerParams {
|
||||
|
||||
impl AppLayerParams {
|
||||
pub fn is_app_layer_f_port(&self, f_port: u8) -> bool {
|
||||
if (self.ts003_version.is_some() && self.ts003_f_port == f_port)
|
||||
(self.ts003_version.is_some() && self.ts003_f_port == f_port)
|
||||
|| (self.ts004_version.is_some() && self.ts004_f_port == f_port)
|
||||
|| (self.ts005_version.is_some() && self.ts005_f_port == f_port)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,7 +280,7 @@ impl deserialize::FromSql<Jsonb, Pg> for AppLayerParams {
|
||||
#[cfg(feature = "postgres")]
|
||||
impl serialize::ToSql<Jsonb, Pg> for AppLayerParams {
|
||||
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
|
||||
let value = serde_json::to_value(&self)?;
|
||||
let value = serde_json::to_value(self)?;
|
||||
<serde_json::Value as serialize::ToSql<Jsonb, Pg>>::to_sql(&value, &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod big_decimal;
|
||||
mod dev_nonces;
|
||||
pub mod device;
|
||||
pub mod device_profile;
|
||||
mod device_session;
|
||||
mod fuota;
|
||||
|
@ -336,7 +336,7 @@ pub async fn add_devices(fuota_deployment_id: Uuid, dev_euis: Vec<EUI64>) -> Res
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::MultiError(errors))
|
||||
Err(Error::Multi(errors))
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,7 +552,7 @@ pub async fn add_gateways(fuota_deployment_id: Uuid, gateway_ids: Vec<EUI64>) ->
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::MultiError(errors))
|
||||
Err(Error::Multi(errors))
|
||||
}
|
||||
}
|
||||
|
||||
@ -789,7 +789,7 @@ pub fn get_multicast_timeout(d: &FuotaDeployment) -> Result<usize> {
|
||||
conf.network.scheduler.multicast_class_c_margin.as_secs() as usize;
|
||||
|
||||
// Multiply by the number of fragments (+1 for additional margin).
|
||||
let mc_class_c_duration_secs = mc_class_c_margin_secs * (total_fragments + 1 as usize);
|
||||
let mc_class_c_duration_secs = mc_class_c_margin_secs * (total_fragments + 1);
|
||||
|
||||
// Calculate the timeout value. In case of Class-C, timeout is defined as seconds,
|
||||
// where the number of seconds is 2^timeout.
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::RwLock;
|
||||
use std::time::Instant;
|
||||
|
||||
@ -47,19 +48,18 @@ pub mod user;
|
||||
|
||||
use crate::monitoring::prometheus;
|
||||
|
||||
lazy_static! {
|
||||
static ref ASYNC_REDIS_POOL: TokioRwLock<Option<AsyncRedisPool>> = TokioRwLock::new(None);
|
||||
static ref REDIS_PREFIX: RwLock<String> = RwLock::new("".to_string());
|
||||
static ref STORAGE_REDIS_CONN_GET: Histogram = {
|
||||
let histogram = Histogram::new(exponential_buckets(0.001, 2.0, 12));
|
||||
prometheus::register(
|
||||
"storage_redis_conn_get_duration_seconds",
|
||||
"Time between requesting a Redis connection and the connection-pool returning it",
|
||||
histogram.clone(),
|
||||
);
|
||||
histogram
|
||||
};
|
||||
}
|
||||
static ASYNC_REDIS_POOL: LazyLock<TokioRwLock<Option<AsyncRedisPool>>> =
|
||||
LazyLock::new(|| TokioRwLock::new(None));
|
||||
static REDIS_PREFIX: LazyLock<RwLock<String>> = LazyLock::new(|| RwLock::new("".to_string()));
|
||||
static STORAGE_REDIS_CONN_GET: LazyLock<Histogram> = LazyLock::new(|| {
|
||||
let histogram = Histogram::new(exponential_buckets(0.001, 2.0, 12));
|
||||
prometheus::register(
|
||||
"storage_redis_conn_get_duration_seconds",
|
||||
"Time between requesting a Redis connection and the connection-pool returning it",
|
||||
histogram.clone(),
|
||||
);
|
||||
histogram
|
||||
});
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations_postgres");
|
||||
|
@ -1012,7 +1012,7 @@ pub mod test {
|
||||
// invalid f_port
|
||||
assert!(enqueue(
|
||||
MulticastGroupQueueItem {
|
||||
multicast_group_id: mg.id.into(),
|
||||
multicast_group_id: mg.id,
|
||||
gateway_id: gw.gateway_id,
|
||||
f_cnt: 1,
|
||||
f_port: 0,
|
||||
@ -1026,7 +1026,7 @@ pub mod test {
|
||||
|
||||
assert!(enqueue(
|
||||
MulticastGroupQueueItem {
|
||||
multicast_group_id: mg.id.into(),
|
||||
multicast_group_id: mg.id,
|
||||
gateway_id: gw.gateway_id,
|
||||
f_cnt: 1,
|
||||
f_port: 256,
|
||||
@ -1041,7 +1041,7 @@ pub mod test {
|
||||
// Enqueue (Class-C) (delay)
|
||||
let (ids, f_cnt) = enqueue(
|
||||
MulticastGroupQueueItem {
|
||||
multicast_group_id: mg.id.into(),
|
||||
multicast_group_id: mg.id,
|
||||
gateway_id: gw.gateway_id,
|
||||
f_cnt: 1,
|
||||
f_port: 2,
|
||||
@ -1071,7 +1071,7 @@ pub mod test {
|
||||
let mut mg = update(mg).await.unwrap();
|
||||
let (ids, f_cnt) = enqueue(
|
||||
MulticastGroupQueueItem {
|
||||
multicast_group_id: mg.id.into(),
|
||||
multicast_group_id: mg.id,
|
||||
gateway_id: gw.gateway_id,
|
||||
f_cnt: 1,
|
||||
f_port: 2,
|
||||
@ -1098,7 +1098,7 @@ pub mod test {
|
||||
let mg = update(mg).await.unwrap();
|
||||
let (ids, f_cnt) = enqueue(
|
||||
MulticastGroupQueueItem {
|
||||
multicast_group_id: mg.id.into(),
|
||||
multicast_group_id: mg.id,
|
||||
gateway_id: gw.gateway_id,
|
||||
f_cnt: 1,
|
||||
f_port: 2,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::sync::RwLock;
|
||||
use std::sync::{LazyLock, RwLock};
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
@ -20,18 +20,16 @@ use crate::helpers::tls::get_root_certs;
|
||||
pub type AsyncPgPool = DeadpoolPool<AsyncPgConnection>;
|
||||
pub type AsyncPgPoolConnection = DeadpoolObject<AsyncPgConnection>;
|
||||
|
||||
lazy_static! {
|
||||
static ref ASYNC_PG_POOL: RwLock<Option<AsyncPgPool>> = RwLock::new(None);
|
||||
static ref STORAGE_PG_CONN_GET: Histogram = {
|
||||
let histogram = Histogram::new(exponential_buckets(0.001, 2.0, 12));
|
||||
prometheus::register(
|
||||
"storage_pg_conn_get_duration_seconds",
|
||||
"Time between requesting a PostgreSQL connection and the connection-pool returning it",
|
||||
histogram.clone(),
|
||||
);
|
||||
histogram
|
||||
};
|
||||
}
|
||||
static ASYNC_PG_POOL: LazyLock<RwLock<Option<AsyncPgPool>>> = LazyLock::new(|| RwLock::new(None));
|
||||
static STORAGE_PG_CONN_GET: LazyLock<Histogram> = LazyLock::new(|| {
|
||||
let histogram = Histogram::new(exponential_buckets(0.001, 2.0, 12));
|
||||
prometheus::register(
|
||||
"storage_pg_conn_get_duration_seconds",
|
||||
"Time between requesting a PostgreSQL connection and the connection-pool returning it",
|
||||
histogram.clone(),
|
||||
);
|
||||
histogram
|
||||
});
|
||||
|
||||
pub fn setup(conf: &config::Postgresql) -> Result<()> {
|
||||
info!("Setting up PostgreSQL connection pool");
|
||||
|
@ -65,6 +65,7 @@ diesel::table! {
|
||||
join_eui -> Bytea,
|
||||
secondary_dev_addr -> Nullable<Bytea>,
|
||||
device_session -> Nullable<Bytea>,
|
||||
app_layer_params -> Jsonb,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,7 @@ diesel::table! {
|
||||
join_eui -> Binary,
|
||||
secondary_dev_addr -> Nullable<Binary>,
|
||||
device_session -> Nullable<Binary>,
|
||||
app_layer_params -> Text,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use diesel_async::RunQueryDsl;
|
||||
@ -8,9 +9,7 @@ use uuid::Uuid;
|
||||
use super::{error::Error, fields, get_async_db_conn};
|
||||
use lrwn::EUI64;
|
||||
|
||||
lazy_static! {
|
||||
static ref SEARCH_TAG_RE: Regex = Regex::new(r"([^ ]+):([^ ]+)").unwrap();
|
||||
}
|
||||
static SEARCH_TAG_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"([^ ]+):([^ ]+)").unwrap());
|
||||
|
||||
#[derive(QueryableByName, PartialEq, Debug)]
|
||||
pub struct SearchResult {
|
||||
@ -449,8 +448,8 @@ pub mod test {
|
||||
let _d = device::create(device::Device {
|
||||
dev_eui: EUI64::from_str("0203040506070809").unwrap(),
|
||||
name: "test-device".into(),
|
||||
application_id: a.id.clone(),
|
||||
device_profile_id: dp.id.clone(),
|
||||
application_id: a.id,
|
||||
device_profile_id: dp.id,
|
||||
tags: build_tags(&[("common_tag", "value"), ("mytag", "dev_value")]),
|
||||
..Default::default()
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::sync::RwLock;
|
||||
use std::sync::{LazyLock, RwLock};
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
@ -19,18 +19,17 @@ use crate::config;
|
||||
pub type AsyncSqlitePool = DeadpoolPool<SyncConnectionWrapper<SqliteConnection>>;
|
||||
pub type AsyncSqlitePoolConnection = DeadpoolObject<SyncConnectionWrapper<SqliteConnection>>;
|
||||
|
||||
lazy_static! {
|
||||
static ref ASYNC_SQLITE_POOL: RwLock<Option<AsyncSqlitePool>> = RwLock::new(None);
|
||||
static ref STORAGE_SQLITE_CONN_GET: Histogram = {
|
||||
let histogram = Histogram::new(exponential_buckets(0.001, 2.0, 12));
|
||||
prometheus::register(
|
||||
"storage_sqlite_conn_get_duration_seconds",
|
||||
"Time between requesting a SQLite connection and the connection-pool returning it",
|
||||
histogram.clone(),
|
||||
);
|
||||
histogram
|
||||
};
|
||||
}
|
||||
static ASYNC_SQLITE_POOL: LazyLock<RwLock<Option<AsyncSqlitePool>>> =
|
||||
LazyLock::new(|| RwLock::new(None));
|
||||
static STORAGE_SQLITE_CONN_GET: LazyLock<Histogram> = LazyLock::new(|| {
|
||||
let histogram = Histogram::new(exponential_buckets(0.001, 2.0, 12));
|
||||
prometheus::register(
|
||||
"storage_sqlite_conn_get_duration_seconds",
|
||||
"Time between requesting a SQLite connection and the connection-pool returning it",
|
||||
histogram.clone(),
|
||||
);
|
||||
histogram
|
||||
});
|
||||
|
||||
pub fn setup(conf: &config::Sqlite) -> Result<()> {
|
||||
info!("Setting up SQLite connection pool");
|
||||
|
@ -325,7 +325,6 @@ pub mod test {
|
||||
use crate::storage::user::test::create_user;
|
||||
use crate::test;
|
||||
use chrono::SubsecRound;
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
|
||||
struct FilterTest<'a> {
|
||||
@ -357,10 +356,19 @@ pub mod test {
|
||||
async fn test_tenant() {
|
||||
let _guard = test::prepare().await;
|
||||
|
||||
// delete default tenant
|
||||
delete(&Uuid::from_str("52f14cd4-c6f1-4fbd-8f87-4025e1d49242").unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
// delete existing tenants.
|
||||
let tenants = list(
|
||||
10,
|
||||
0,
|
||||
&Filters {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
for t in &tenants {
|
||||
delete(&t.id).await.unwrap();
|
||||
}
|
||||
|
||||
let mut t = create_tenant().await;
|
||||
|
||||
@ -379,7 +387,7 @@ pub mod test {
|
||||
|
||||
let tu = TenantUser {
|
||||
tenant_id: t.id,
|
||||
user_id: user.id.into(),
|
||||
user_id: user.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
};
|
||||
@ -480,7 +488,7 @@ pub mod test {
|
||||
|
||||
let tu = TenantUser {
|
||||
tenant_id: t.id,
|
||||
user_id: user.id.into(),
|
||||
user_id: user.id,
|
||||
is_admin: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
@ -14,7 +14,6 @@ use crate::storage::{get_async_redis_conn, redis_key};
|
||||
use chirpstack_api::{api, integration};
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
|
||||
pub async fn log_event_for_device(typ: &str, dev_eui: &str, b: &[u8]) -> Result<()> {
|
||||
let conf = config::get();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::future::Future;
|
||||
use std::io::Cursor;
|
||||
use std::pin::Pin;
|
||||
use std::sync::LazyLock;
|
||||
use std::time::Duration;
|
||||
|
||||
use prost::Message;
|
||||
@ -17,9 +18,7 @@ use crate::storage::{
|
||||
use chirpstack_api::{gw, integration as integration_pb, internal, stream};
|
||||
use lrwn::EUI64;
|
||||
|
||||
lazy_static! {
|
||||
static ref LAST_DOWNLINK_ID: RwLock<u32> = RwLock::new(0);
|
||||
}
|
||||
static LAST_DOWNLINK_ID: LazyLock<RwLock<u32>> = LazyLock::new(|| RwLock::new(0));
|
||||
|
||||
pub type Validator = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()>>>>;
|
||||
|
||||
|
@ -150,7 +150,7 @@ async fn test_fns_uplink() {
|
||||
// Simulate uplink
|
||||
uplink::handle_uplink(
|
||||
CommonName::EU868,
|
||||
"eu868".into(),
|
||||
"eu868",
|
||||
Uuid::new_v4(),
|
||||
gw::UplinkFrameSet {
|
||||
phy_payload: data_phy.to_vec().unwrap(),
|
||||
|
@ -5558,7 +5558,7 @@ async fn run_test(t: &Test) {
|
||||
|
||||
uplink::handle_uplink(
|
||||
CommonName::EU868,
|
||||
"eu868".into(),
|
||||
"eu868",
|
||||
Uuid::new_v4(),
|
||||
gw::UplinkFrameSet {
|
||||
phy_payload: t.phy_payload.to_vec().unwrap(),
|
||||
|
@ -462,7 +462,7 @@ async fn run_uplink_test(t: &UplinkTest) {
|
||||
|
||||
uplink::handle_uplink(
|
||||
CommonName::EU868,
|
||||
"eu868".into(),
|
||||
"eu868",
|
||||
Uuid::new_v4(),
|
||||
gw::UplinkFrameSet {
|
||||
phy_payload: t.phy_payload.to_vec().unwrap(),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::env;
|
||||
use std::sync::{Mutex, Once};
|
||||
use std::sync::{LazyLock, Mutex, Once};
|
||||
|
||||
use crate::{adr, config, region, storage};
|
||||
|
||||
@ -17,9 +17,7 @@ mod relay_otaa_test;
|
||||
|
||||
static TRACING_INIT: Once = Once::new();
|
||||
|
||||
lazy_static! {
|
||||
static ref TEST_MUX: Mutex<()> = Mutex::new(());
|
||||
}
|
||||
static TEST_MUX: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
pub async fn prepare<'a>() -> std::sync::MutexGuard<'a, ()> {
|
||||
dotenv::dotenv().ok();
|
||||
|
@ -367,7 +367,7 @@ async fn run_test(t: &Test) {
|
||||
|
||||
uplink::handle_uplink(
|
||||
CommonName::EU868,
|
||||
"eu868".into(),
|
||||
"eu868",
|
||||
Uuid::new_v4(),
|
||||
gw::UplinkFrameSet {
|
||||
phy_payload: t.phy_payload.to_vec().unwrap(),
|
||||
|
@ -190,7 +190,7 @@ async fn test_fns() {
|
||||
// Simulate uplink
|
||||
uplink::handle_uplink(
|
||||
CommonName::EU868,
|
||||
"eu868".into(),
|
||||
"eu868",
|
||||
Uuid::new_v4(),
|
||||
gw::UplinkFrameSet {
|
||||
phy_payload: jr_phy.to_vec().unwrap(),
|
||||
|
@ -1250,7 +1250,7 @@ async fn run_test(t: &Test) {
|
||||
|
||||
uplink::handle_uplink(
|
||||
CommonName::EU868,
|
||||
"eu868".into(),
|
||||
"eu868",
|
||||
Uuid::new_v4(),
|
||||
gw::UplinkFrameSet {
|
||||
phy_payload: t.phy_payload.to_vec().unwrap(),
|
||||
|
@ -821,7 +821,7 @@ async fn run_test(t: &Test) {
|
||||
|
||||
uplink::handle_uplink(
|
||||
CommonName::EU868,
|
||||
"eu868".into(),
|
||||
"eu868",
|
||||
Uuid::new_v4(),
|
||||
gw::UplinkFrameSet {
|
||||
phy_payload: t.phy_payload.to_vec().unwrap(),
|
||||
|
@ -220,7 +220,7 @@ async fn test_lorawan_10() {
|
||||
|
||||
uplink::handle_uplink(
|
||||
CommonName::EU868,
|
||||
"eu868".into(),
|
||||
"eu868",
|
||||
Uuid::new_v4(),
|
||||
gw::UplinkFrameSet {
|
||||
phy_payload: phy_relay_jr.to_vec().unwrap(),
|
||||
@ -293,8 +293,7 @@ async fn test_lorawan_10() {
|
||||
nb_trans: 1,
|
||||
region_config_id: "eu868".to_string(),
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
},
|
||||
),
|
||||
assert::downlink_frame(gw::DownlinkFrame {
|
||||
items: vec![
|
||||
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
@ -41,35 +42,33 @@ struct UplinkLabels {
|
||||
m_type: String,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref UPLINK_COUNTER: Family<UplinkLabels, Counter> = {
|
||||
let counter = Family::<UplinkLabels, Counter>::default();
|
||||
prometheus::register(
|
||||
"uplink_count",
|
||||
"Number of received uplinks (after deduplication)",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
};
|
||||
static ref DEDUPLICATE_LOCKED_COUNTER: Family<(), Counter> = {
|
||||
let counter = Family::<(), Counter>::default();
|
||||
prometheus::register(
|
||||
static UPLINK_COUNTER: LazyLock<Family<UplinkLabels, Counter>> = LazyLock::new(|| {
|
||||
let counter = Family::<UplinkLabels, Counter>::default();
|
||||
prometheus::register(
|
||||
"uplink_count",
|
||||
"Number of received uplinks (after deduplication)",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
});
|
||||
static DEDUPLICATE_LOCKED_COUNTER: LazyLock<Counter> = LazyLock::new(|| {
|
||||
let counter = Counter::default();
|
||||
prometheus::register(
|
||||
"deduplicate_locked_count",
|
||||
"Number of times the deduplication function was called and the deduplication was already locked",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
};
|
||||
static ref DEDUPLICATE_NO_LOCK_COUNTER: Family<(), Counter> = {
|
||||
let counter = Family::<(), Counter>::default();
|
||||
prometheus::register(
|
||||
"deduplicate_no_lock_count",
|
||||
"Number of times the deduplication function was called and it was not yet locked",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
};
|
||||
}
|
||||
counter
|
||||
});
|
||||
static DEDUPLICATE_NO_LOCK_COUNTER: LazyLock<Counter> = LazyLock::new(|| {
|
||||
let counter = Counter::default();
|
||||
prometheus::register(
|
||||
"deduplicate_no_lock_count",
|
||||
"Number of times the deduplication function was called and it was not yet locked",
|
||||
counter.clone(),
|
||||
);
|
||||
counter
|
||||
});
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RelayContext {
|
||||
@ -203,12 +202,12 @@ async fn _deduplicate_uplink(
|
||||
"Deduplication is already locked by an other process"
|
||||
);
|
||||
|
||||
DEDUPLICATE_LOCKED_COUNTER.get_or_create(&()).inc();
|
||||
DEDUPLICATE_LOCKED_COUNTER.inc();
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
DEDUPLICATE_NO_LOCK_COUNTER.get_or_create(&()).inc();
|
||||
DEDUPLICATE_NO_LOCK_COUNTER.inc();
|
||||
|
||||
trace!(
|
||||
key = key.as_str(),
|
||||
|
@ -25,7 +25,7 @@ services:
|
||||
- "8080:8080"
|
||||
|
||||
postgres:
|
||||
image: postgres:11-alpine
|
||||
image: postgres:13-alpine
|
||||
volumes:
|
||||
- ./.docker-compose/postgresql/initdb:/docker-entrypoint-initdb.d
|
||||
environment:
|
||||
|
145
examples/adr_plugins/default.js
Normal file
145
examples/adr_plugins/default.js
Normal file
@ -0,0 +1,145 @@
|
||||
export function name() {
|
||||
return "JS example for default ADR algorithm";
|
||||
}
|
||||
|
||||
export function id() {
|
||||
return "js_example_default";
|
||||
}
|
||||
|
||||
export function handle(req) {
|
||||
let resp = {
|
||||
dr: req.dr,
|
||||
txPowerIndex: req.txPowerIndex,
|
||||
nbTrans: req.nbTrans,
|
||||
};
|
||||
|
||||
if (!req.adr) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
if (req.dr > req.maxDr) {
|
||||
resp.dr = req.maxDr;
|
||||
}
|
||||
|
||||
// Set the new Nb Trans.
|
||||
resp.nbTrans = getNbTrans(req.nbTrans, getPacketLossPercentage(req));
|
||||
|
||||
// Calculate the number of steps.
|
||||
let snrMax = getMaxSnr(req);
|
||||
let snrMargin = snrMax - req.requiredSnrForDr - req.installationMargin;
|
||||
let nStep = Math.floor(snrMargin / 3);
|
||||
|
||||
// In case of negative steps the ADR algorithm will increase the TxPower
|
||||
// if possible. To avoid up / down / up / down TxPower changes, wait until
|
||||
// we have at least the required number of uplink history elements.
|
||||
if (nStep < 0 && getHistoryCount(req) != requiredHistoryCount()) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
let [desiredTxPowerIndex, desiredDr] = getIdealTxPowerIndexAndDr(
|
||||
nStep,
|
||||
resp.txPowerIndex,
|
||||
resp.dr,
|
||||
req.maxTxPowerIndex,
|
||||
req.maxDr,
|
||||
);
|
||||
|
||||
resp.dr = desiredDr;
|
||||
resp.txPowerIndex = desiredTxPowerIndex;
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
function getIdealTxPowerIndexAndDr(nbStep, txPowerIndex, dr, maxTxPowerIndex, maxDr) {
|
||||
while (nbStep !== 0) {
|
||||
if (nbStep > 0) {
|
||||
if (dr < maxDr) {
|
||||
// Increase the DR.
|
||||
dr++;
|
||||
} else if (txPowerIndex < maxTxPowerIndex) {
|
||||
// Decrease the Tx Power.
|
||||
txPowerIndex++;
|
||||
}
|
||||
nbStep--;
|
||||
} else {
|
||||
// Incease the TxPower.
|
||||
if (txPowerIndex > 0) {
|
||||
txPowerIndex--;
|
||||
}
|
||||
nbStep++;
|
||||
}
|
||||
}
|
||||
|
||||
return [txPowerIndex, dr];
|
||||
}
|
||||
|
||||
function requiredHistoryCount() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
function getHistoryCount(req) {
|
||||
let count = 0;
|
||||
for (let uh of req.uplinkHistory) {
|
||||
if (uh.txPowerIndex === req.txPowerIndex) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
function getMaxSnr(req) {
|
||||
let maxSnr = -999.0;
|
||||
|
||||
for (let uh of req.uplinkHistory) {
|
||||
if (uh.maxSnr > maxSnr) {
|
||||
maxSnr = uh.maxSnr;
|
||||
}
|
||||
}
|
||||
|
||||
return maxSnr;
|
||||
}
|
||||
|
||||
function getNbTrans(currentNbTrans, pktLossRate) {
|
||||
const pktLossTable = [
|
||||
[1, 1, 2],
|
||||
[1, 2, 3],
|
||||
[2, 3, 3],
|
||||
[3, 3, 3],
|
||||
];
|
||||
|
||||
if (currentNbTrans < 1) {
|
||||
currentNbTrans = 1;
|
||||
}
|
||||
if (currentNbTrans > 3) {
|
||||
currentNbTrans = 3;
|
||||
}
|
||||
|
||||
const nbTransIndex = currentNbTrans - 1;
|
||||
|
||||
if (pktLossRate < 5.0) {
|
||||
return pktLossTable[0][nbTransIndex];
|
||||
} else if (pktLossRate < 10.0) {
|
||||
return pktLossTable[1][nbTransIndex];
|
||||
} else if (pktLossRate < 30.0) {
|
||||
return pktLossTable[2][nbTransIndex];
|
||||
}
|
||||
|
||||
return pktLossTable[3][nbTransIndex];
|
||||
}
|
||||
|
||||
function getPacketLossPercentage(req) {
|
||||
if (req.uplinkHistory.length < requiredHistoryCount()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let lostPackets = 0;
|
||||
let previousFCnt = req.uplinkHistory[0].fCnt;
|
||||
|
||||
for (let uh of req.uplinkHistory.slice(1)) {
|
||||
lostPackets += uh.fCnt - previousFCnt - 1;
|
||||
previousFCnt = uh.fCnt;
|
||||
}
|
||||
|
||||
return lostPackets / req.uplinkHistory.length * 100.0;
|
||||
}
|
@ -12,6 +12,6 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
)
|
||||
|
@ -12,11 +12,11 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
|
@ -3,7 +3,7 @@
|
||||
description = "Library for filtering LoRaWAN payloads on DevAddr and JoinEUIs prefixes"
|
||||
homepage = "https://www.chirpstack.io/"
|
||||
license = "MIT"
|
||||
version = "4.12.0-test.2"
|
||||
version = "4.12.0-test.3"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/chirpstack/chirpstack"
|
||||
|
@ -3,7 +3,7 @@
|
||||
description = "Library for encoding / decoding LoRaWAN frames."
|
||||
homepage = "https://www.chirpstack.io"
|
||||
license = "MIT"
|
||||
version = "4.12.0-test.2"
|
||||
version = "4.12.0-test.3"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/chirpstack/chirpstack"
|
||||
@ -22,9 +22,6 @@
|
||||
thiserror = "2.0"
|
||||
anyhow = "1.0"
|
||||
|
||||
# Misc
|
||||
lazy_static = "1.5"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
diesel = ["dep:diesel", "serde"]
|
||||
|
@ -95,7 +95,7 @@ impl<'de> Deserialize<'de> for AES128Key {
|
||||
struct Aes128KeyVisitor;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Visitor<'de> for Aes128KeyVisitor {
|
||||
impl Visitor<'_> for Aes128KeyVisitor {
|
||||
type Value = AES128Key;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -266,7 +266,7 @@ impl PayloadCodec for FragSessionSetupReqPayload {
|
||||
mc_group_bit_mask: {
|
||||
let mut mask = [false; 4];
|
||||
for (i, v) in mask.iter_mut().enumerate() {
|
||||
*v = b[0] & 1 << i != 0;
|
||||
*v = b[0] & (1 << i) != 0;
|
||||
}
|
||||
mask
|
||||
},
|
||||
|
@ -302,7 +302,7 @@ impl PayloadCodec for FragSessionSetupReqPayload {
|
||||
mc_group_bit_mask: {
|
||||
let mut mask = [false; 4];
|
||||
for (i, v) in mask.iter_mut().enumerate() {
|
||||
*v = b[0] & 1 << i != 0;
|
||||
*v = b[0] & (1 << i) != 0;
|
||||
}
|
||||
mask
|
||||
},
|
||||
|
@ -92,7 +92,7 @@ impl<'de> Deserialize<'de> for DevAddrPrefix {
|
||||
struct DevAddrPrefixVisitor;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Visitor<'de> for DevAddrPrefixVisitor {
|
||||
impl Visitor<'_> for DevAddrPrefixVisitor {
|
||||
type Value = DevAddrPrefix;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -103,7 +103,7 @@ impl<'de> Deserialize<'de> for EUI64 {
|
||||
struct Eui64Visitor;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Visitor<'de> for Eui64Visitor {
|
||||
impl Visitor<'_> for Eui64Visitor {
|
||||
type Value = EUI64;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
@ -251,7 +251,7 @@ impl<'de> Deserialize<'de> for EUI64Prefix {
|
||||
struct EUI64PrefixVisitor;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Visitor<'de> for EUI64PrefixVisitor {
|
||||
impl Visitor<'_> for EUI64PrefixVisitor {
|
||||
type Value = EUI64Prefix;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -3,8 +3,6 @@
|
||||
extern crate diesel;
|
||||
#[macro_use]
|
||||
extern crate anyhow;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
pub use self::aes128::*;
|
||||
pub use self::cflist::*;
|
||||
@ -43,26 +41,24 @@ mod relay;
|
||||
|
||||
pub const LA_FPORT_RELAY: u8 = 226;
|
||||
|
||||
lazy_static! {
|
||||
static ref EIRP_INDEX: Vec<f32> = vec![
|
||||
8.0, // 0
|
||||
10.0, // 1
|
||||
12.0, // 2
|
||||
13.0, // 3
|
||||
14.0, // 4
|
||||
16.0, // 5
|
||||
18.0, // 6
|
||||
20.0, // 7
|
||||
21.0, // 8
|
||||
24.0, // 9
|
||||
26.0, // 10
|
||||
27.0, // 11
|
||||
29.0, // 12
|
||||
30.0, // 13
|
||||
33.0, // 14
|
||||
36.0, // 15
|
||||
];
|
||||
}
|
||||
const EIRP_INDEX: [f32; 16] = [
|
||||
8.0, // 0
|
||||
10.0, // 1
|
||||
12.0, // 2
|
||||
13.0, // 3
|
||||
14.0, // 4
|
||||
16.0, // 5
|
||||
18.0, // 6
|
||||
20.0, // 7
|
||||
21.0, // 8
|
||||
24.0, // 9
|
||||
26.0, // 10
|
||||
27.0, // 11
|
||||
29.0, // 12
|
||||
30.0, // 13
|
||||
33.0, // 14
|
||||
36.0, // 15
|
||||
];
|
||||
|
||||
pub fn get_tx_param_setup_eirp_index(eirp: f32) -> u8 {
|
||||
let mut out: u8 = 0;
|
||||
|
@ -1910,7 +1910,7 @@ impl ChannelSettingsED {
|
||||
ChannelSettingsED {
|
||||
second_ch_ack_offset: b[0] & 0x07,
|
||||
second_ch_dr: (b[0] & 0x78) >> 3,
|
||||
second_ch_idx: (b[0] & 0x80) >> 7 | (b[1] & 0x01) << 1,
|
||||
second_ch_idx: ((b[0] & 0x80) >> 7) | ((b[1] & 0x01) << 1),
|
||||
backoff: (b[1] & 0x7e) >> 1,
|
||||
}
|
||||
}
|
||||
@ -2419,7 +2419,7 @@ impl PowerLevel {
|
||||
let wor_snr = (wor_snr + 20) as u8;
|
||||
let wor_rssi = -(wor_rssi + 15) as u8;
|
||||
|
||||
[wor_snr | wor_rssi << 5, wor_rssi >> 3]
|
||||
[wor_snr | (wor_rssi << 5), wor_rssi >> 3]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ impl<'de> Deserialize<'de> for NetID {
|
||||
struct NetIdVisitor;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Visitor<'de> for NetIdVisitor {
|
||||
impl Visitor<'_> for NetIdVisitor {
|
||||
type Value = NetID;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -892,7 +892,7 @@ impl Region for Configuration {
|
||||
min_dr
|
||||
} else {
|
||||
dr as u8
|
||||
} as u8;
|
||||
};
|
||||
|
||||
Ok(if dr > 5 { 5 } else { dr })
|
||||
}
|
||||
|
@ -40,7 +40,11 @@ impl UplinkMetadata {
|
||||
let snr = (snr + 20) as u8;
|
||||
let rssi = -(rssi + 15) as u8;
|
||||
|
||||
Ok([self.dr | snr << 4, snr >> 4 | rssi << 1, self.wor_channel])
|
||||
Ok([
|
||||
self.dr | (snr << 4),
|
||||
(snr >> 4) | (rssi << 1),
|
||||
self.wor_channel,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ pkgs.mkShell {
|
||||
pkgs.sqlite # sqlite binary + library for diesel
|
||||
pkgs.postgresql # psql binary + library for diesel
|
||||
pkgs.cargo-cross # cross-compiling
|
||||
pkgs.cargo-deb # deb packaging
|
||||
pkgs.diesel-cli # diesel cli
|
||||
];
|
||||
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chirpstack-ui",
|
||||
"version": "4.12.0-test.2",
|
||||
"version": "4.12.0-test.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@ -51,12 +51,12 @@
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.13.1",
|
||||
"@typescript-eslint/parser": "^7.13.1",
|
||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||
"@vitejs/plugin-react-swc": "^3.9.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"prettier": "^3.3.2",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.4.12"
|
||||
"vite": "^6.2.6"
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ function FuotaDeploymentLayout(props: IProps) {
|
||||
description="Are you sure you want to start the deploymen? Once started, you will not be able to make changes."
|
||||
onConfirm={startFuotaDeployment}
|
||||
>
|
||||
<Button type="primary">Start deployment</Button>
|
||||
<Button type="primary" disabled={getFuotaDeploymentResponse.getStartedAt() !== undefined}>Start deployment</Button>
|
||||
</Popconfirm>
|
||||
<DeleteConfirm typ="FUOTA deployment" confirm={d.getName()} onConfirm={deleteFuotaDeployment}>
|
||||
<Button danger type="primary">
|
||||
|
1636
ui/yarn.lock
1636
ui/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user