mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-07-13 21:39:43 +00:00
Compare commits
8 Commits
api/go/v4.
...
master
Author | SHA1 | Date | |
---|---|---|---|
b821c38cac | |||
f42337f411 | |||
c5fa5e04c3 | |||
b7d5394644 | |||
984300e936 | |||
c58cc7ad8b | |||
952a49f35e | |||
2565cca14a |
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -636,7 +636,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "backend"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
dependencies = [
|
||||
"aes-kw",
|
||||
"anyhow",
|
||||
@ -838,7 +838,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chirpstack"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
@ -930,7 +930,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chirpstack_api"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"pbjson",
|
||||
@ -947,7 +947,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chirpstack_integration"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -2767,7 +2767,7 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "lrwn"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
@ -2780,7 +2780,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lrwn_filters"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"lrwn",
|
||||
|
5
api/go/api/device.pb.go
vendored
5
api/go/api/device.pb.go
vendored
@ -1175,12 +1175,13 @@ type DeviceActivation struct {
|
||||
// Application session key (HEX encoded).
|
||||
AppSKey string `protobuf:"bytes,3,opt,name=app_s_key,json=appSKey,proto3" json:"app_s_key,omitempty"`
|
||||
// Network session encryption key (HEX encoded).
|
||||
// Note: For ABP in LoRaWAN 1.0.x, use this, the serving and the forwarding
|
||||
// network session integrity key fields with the LoRaWAN 1.0.x 'NwkSKey`!
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
NwkSEncKey string `protobuf:"bytes,4,opt,name=nwk_s_enc_key,json=nwkSEncKey,proto3" json:"nwk_s_enc_key,omitempty"`
|
||||
// Serving network session integrity key (HEX encoded).
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
SNwkSIntKey string `protobuf:"bytes,8,opt,name=s_nwk_s_int_key,json=sNwkSIntKey,proto3" json:"s_nwk_s_int_key,omitempty"`
|
||||
// Forwarding network session integrity key (HEX encoded).
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
FNwkSIntKey string `protobuf:"bytes,9,opt,name=f_nwk_s_int_key,json=fNwkSIntKey,proto3" json:"f_nwk_s_int_key,omitempty"`
|
||||
// Uplink frame-counter.
|
||||
FCntUp uint32 `protobuf:"varint,5,opt,name=f_cnt_up,json=fCntUp,proto3" json:"f_cnt_up,omitempty"`
|
||||
|
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.13.0-test.2",
|
||||
"version": "4.13.0",
|
||||
"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.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
2
api/js/package.json
vendored
2
api/js/package.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@chirpstack/chirpstack-api",
|
||||
"version": "4.13.0-test.2",
|
||||
"version": "4.13.0",
|
||||
"description": "Chirpstack JS and TS API",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
|
6
api/js/yarn.lock
vendored
6
api/js/yarn.lock
vendored
@ -146,9 +146,9 @@ balanced-match@^1.0.0:
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
version "1.1.12"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
|
||||
integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
2
api/kotlin/build.gradle.kts
vendored
2
api/kotlin/build.gradle.kts
vendored
@ -9,7 +9,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "io.chirpstack"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
|
||||
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.13.0-test.2",
|
||||
"version": "4.13.0",
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"grpc/grpc": "^v1.57.0",
|
||||
|
5
api/proto/api/device.proto
vendored
5
api/proto/api/device.proto
vendored
@ -412,14 +412,15 @@ message DeviceActivation {
|
||||
string app_s_key = 3;
|
||||
|
||||
// Network session encryption key (HEX encoded).
|
||||
// Note: For ABP in LoRaWAN 1.0.x, use this, the serving and the forwarding
|
||||
// network session integrity key fields with the LoRaWAN 1.0.x 'NwkSKey`!
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
string nwk_s_enc_key = 4;
|
||||
|
||||
// Serving network session integrity key (HEX encoded).
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
string s_nwk_s_int_key = 8;
|
||||
|
||||
// Forwarding network session integrity key (HEX encoded).
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
string f_nwk_s_int_key = 9;
|
||||
|
||||
// Uplink frame-counter.
|
||||
|
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.13.0-test.2",
|
||||
version = "4.13.0",
|
||||
url='https://github.com/brocaar/chirpstack-api',
|
||||
author='Orne Brocaar',
|
||||
author_email='info@brocaar.com',
|
||||
|
2
api/rust/Cargo.toml
vendored
2
api/rust/Cargo.toml
vendored
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "chirpstack_api"
|
||||
description = "ChirpStack Protobuf / gRPC API definitions."
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
license = "MIT"
|
||||
homepage = "https://www.chirpstack.io"
|
||||
|
5
api/rust/proto/chirpstack/api/device.proto
vendored
5
api/rust/proto/chirpstack/api/device.proto
vendored
@ -412,14 +412,15 @@ message DeviceActivation {
|
||||
string app_s_key = 3;
|
||||
|
||||
// Network session encryption key (HEX encoded).
|
||||
// Note: For ABP in LoRaWAN 1.0.x, use this, the serving and the forwarding
|
||||
// network session integrity key fields with the LoRaWAN 1.0.x 'NwkSKey`!
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
string nwk_s_enc_key = 4;
|
||||
|
||||
// Serving network session integrity key (HEX encoded).
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
string s_nwk_s_int_key = 8;
|
||||
|
||||
// Forwarding network session integrity key (HEX encoded).
|
||||
// Note: For LoRaWAN 1.0.x devices, set this to the NwkSKey.
|
||||
string f_nwk_s_int_key = 9;
|
||||
|
||||
// Uplink frame-counter.
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "backend"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
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.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/chirpstack/chirpstack"
|
||||
|
||||
[dependencies]
|
||||
chirpstack_api = { path = "../api/rust", version = "4.13.0-test.2" }
|
||||
chirpstack_api = { path = "../api/rust", version = "4.13.0" }
|
||||
redis = { version = "0.31", features = [
|
||||
"cluster-async",
|
||||
"tokio-rustls-comp",
|
||||
|
@ -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.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
@ -172,6 +172,15 @@ pub fn run() {
|
||||
# ChirpStack will be allowed.
|
||||
allow_unknown_gateways={{ gateway.allow_unknown_gateways }}
|
||||
|
||||
# RX timestamp max. drift.
|
||||
#
|
||||
# If the delta between the gateway reported RX timestamp vs ChirpStack
|
||||
# server time is bigger than the configured value, then ChirpStack will
|
||||
# ignore it. ChirpStack will then use the RX timestamp from the other
|
||||
# receiving gateways, or failing that, will fall back onto the current
|
||||
# server time.
|
||||
rx_timestamp_max_drift="{{ gateway.rx_timestamp_max_drift }}"
|
||||
|
||||
|
||||
# Network related configuration.
|
||||
[network]
|
||||
|
@ -137,6 +137,8 @@ pub struct Gateway {
|
||||
pub ca_cert: String,
|
||||
pub ca_key: String,
|
||||
pub allow_unknown_gateways: bool,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub rx_timestamp_max_drift: Duration,
|
||||
}
|
||||
|
||||
impl Default for Gateway {
|
||||
@ -146,6 +148,7 @@ impl Default for Gateway {
|
||||
ca_cert: "".to_string(),
|
||||
ca_key: "".to_string(),
|
||||
allow_unknown_gateways: false,
|
||||
rx_timestamp_max_drift: Duration::from_secs(30),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,12 @@ use std::str::FromStr;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
|
||||
use crate::gpstime::ToDateTime;
|
||||
use crate::region;
|
||||
use chirpstack_api::{common, gw};
|
||||
|
||||
use crate::{config, gpstime::ToDateTime, region};
|
||||
|
||||
pub fn get_uplink_dr(
|
||||
region_config_id: &str,
|
||||
tx_info: &chirpstack_api::gw::UplinkTxInfo,
|
||||
@ -54,6 +54,9 @@ pub fn get_uplink_ch(region_config_id: &str, frequency: u32, dr: u8) -> Result<u
|
||||
}
|
||||
|
||||
pub fn get_rx_timestamp(rx_info: &[gw::UplinkRxInfo]) -> SystemTime {
|
||||
let conf = config::get();
|
||||
let rx_timestamp_max_drift = conf.gateway.rx_timestamp_max_drift;
|
||||
|
||||
// First search for time_since_gps_epoch.
|
||||
for rxi in rx_info {
|
||||
if let Some(gps_time) = &rxi.time_since_gps_epoch {
|
||||
@ -71,16 +74,26 @@ pub fn get_rx_timestamp(rx_info: &[gw::UplinkRxInfo]) -> SystemTime {
|
||||
if let Some(ts) = &rxi.gw_time {
|
||||
let ts: Result<DateTime<Utc>> = (*ts).try_into().map_err(anyhow::Error::msg);
|
||||
if let Ok(ts) = ts {
|
||||
let mut delta = Utc::now() - ts;
|
||||
if delta < TimeDelta::default() {
|
||||
delta = -delta;
|
||||
}
|
||||
let delta = delta.to_std().unwrap_or_default();
|
||||
if delta < rx_timestamp_max_drift {
|
||||
return ts.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// last resort use systemtime of NS
|
||||
SystemTime::now()
|
||||
}
|
||||
|
||||
pub fn get_rx_timestamp_chrono(rx_info: &[gw::UplinkRxInfo]) -> DateTime<Utc> {
|
||||
let conf = config::get();
|
||||
let rx_timestamp_max_drift = conf.gateway.rx_timestamp_max_drift;
|
||||
|
||||
// First search for time_since_gps_epoch.
|
||||
for rxi in rx_info {
|
||||
if let Some(gps_time) = &rxi.time_since_gps_epoch {
|
||||
@ -98,10 +111,17 @@ pub fn get_rx_timestamp_chrono(rx_info: &[gw::UplinkRxInfo]) -> DateTime<Utc> {
|
||||
if let Some(ts) = &rxi.gw_time {
|
||||
let ts: Result<DateTime<Utc>> = (*ts).try_into().map_err(anyhow::Error::msg);
|
||||
if let Ok(ts) = ts {
|
||||
let mut delta = Utc::now() - ts;
|
||||
if delta < TimeDelta::default() {
|
||||
delta = -delta;
|
||||
}
|
||||
let delta = delta.to_std().unwrap_or_default();
|
||||
if delta < rx_timestamp_max_drift {
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// last resort use systemtime of NS
|
||||
Utc::now()
|
||||
@ -188,3 +208,74 @@ pub fn set_uplink_modulation(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_rx_timestamp_no_drift() {
|
||||
let now = Utc::now();
|
||||
let rx_info = gw::UplinkRxInfo {
|
||||
gw_time: Some(now.try_into().unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let res: DateTime<Utc> = get_rx_timestamp(&[rx_info]).into();
|
||||
assert_eq!(res, now);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_rx_timestamp_drift() {
|
||||
let now = Utc::now() - chrono::Duration::seconds(60);
|
||||
let rx_info = gw::UplinkRxInfo {
|
||||
gw_time: Some(now.try_into().unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let res: DateTime<Utc> = get_rx_timestamp(&[rx_info]).into();
|
||||
assert_ne!(res, now);
|
||||
|
||||
let now = Utc::now() + chrono::Duration::seconds(60);
|
||||
let rx_info = gw::UplinkRxInfo {
|
||||
gw_time: Some(now.try_into().unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let res: DateTime<Utc> = get_rx_timestamp(&[rx_info]).into();
|
||||
assert_ne!(res, now);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_rx_timestamp_chrono_no_drift() {
|
||||
let now = Utc::now();
|
||||
let rx_info = gw::UplinkRxInfo {
|
||||
gw_time: Some(now.try_into().unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let res = get_rx_timestamp_chrono(&[rx_info]);
|
||||
assert_eq!(res, now);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_rx_timestamp_chrono_drift() {
|
||||
let now = Utc::now() - chrono::Duration::seconds(60);
|
||||
let rx_info = gw::UplinkRxInfo {
|
||||
gw_time: Some(now.try_into().unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let res = get_rx_timestamp_chrono(&[rx_info]);
|
||||
assert_ne!(res, now);
|
||||
|
||||
let now = Utc::now() + chrono::Duration::seconds(60);
|
||||
let rx_info = gw::UplinkRxInfo {
|
||||
gw_time: Some(now.try_into().unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let res = get_rx_timestamp_chrono(&[rx_info]);
|
||||
assert_ne!(res, now);
|
||||
}
|
||||
}
|
||||
|
@ -475,10 +475,14 @@ impl JoinRequest {
|
||||
level: integration_pb::LogLevel::Error.into(),
|
||||
code: integration_pb::LogCode::Otaa.into(),
|
||||
description: "DevNonce has already been used".into(),
|
||||
context: [(
|
||||
context: [
|
||||
(
|
||||
"deduplication_id".to_string(),
|
||||
self.uplink_frame_set.uplink_set_id.to_string(),
|
||||
)]
|
||||
),
|
||||
("join_eui".to_string(), join_request.join_eui.to_string()),
|
||||
("dev_nonce".to_string(), join_request.dev_nonce.to_string()),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
|
@ -1,6 +1,6 @@
|
||||
module frame-log
|
||||
|
||||
go 1.18
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/chirpstack/chirpstack/api/go/v4 v4.6.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.36.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
)
|
||||
|
@ -5,19 +5,26 @@ github.com/chirpstack/chirpstack/api/go/v4 v4.6.0/go.mod h1:6+68s1PGHq2QWZ216RTw
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
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=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
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=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
@ -3,7 +3,7 @@
|
||||
description = "Library for filtering LoRaWAN payloads on DevAddr and JoinEUIs prefixes"
|
||||
homepage = "https://www.chirpstack.io/"
|
||||
license = "MIT"
|
||||
version = "4.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
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.13.0-test.2"
|
||||
version = "4.13.0"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/chirpstack/chirpstack"
|
||||
|
@ -23,6 +23,7 @@ pkgs.mkShell {
|
||||
pkgs.cargo-cross # cross-compiling
|
||||
pkgs.cargo-deb # deb packaging
|
||||
pkgs.diesel-cli # diesel cli
|
||||
pkgs.jq # json query cli tool
|
||||
];
|
||||
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||
BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.llvmPackages.libclang.lib}/lib/clang/${pkgs.llvmPackages.libclang.version}/include";
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chirpstack-ui",
|
||||
"version": "4.13.0-test.2",
|
||||
"version": "4.13.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import { Spin, Button, Space, Timeline, Row, Col, TimelineProps, Card, Tag, Popover, Table } from "antd";
|
||||
import { LoadingOutlined, ReloadOutlined } from "@ant-design/icons";
|
||||
import { LoadingOutlined, ReloadOutlined, ClockCircleOutlined } from "@ant-design/icons";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import { format } from "date-fns";
|
||||
|
||||
@ -23,17 +23,25 @@ interface IProps {
|
||||
|
||||
function FuotaDeploymentDashboard(props: IProps) {
|
||||
const [fuotaJobs, setFuotaJobs] = useState<FuotaDeploymentJob.AsObject[]>([]);
|
||||
const [now, setNow] = useState<Date>(new Date());
|
||||
|
||||
useEffect(() => {
|
||||
getFuotaJobs();
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const getFuotaJobsInterval = setInterval(() => {
|
||||
if (!props.getFuotaDeploymentResponse.getCompletedAt()) {
|
||||
getFuotaJobs();
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
const getNowInterval = setInterval(() => {
|
||||
setNow(new Date());
|
||||
});
|
||||
|
||||
return () => {
|
||||
clearInterval(getFuotaJobsInterval);
|
||||
clearInterval(getNowInterval);
|
||||
};
|
||||
}, [props.getFuotaDeploymentResponse]);
|
||||
|
||||
const jobs: Record<string, string> = {
|
||||
@ -62,6 +70,15 @@ function FuotaDeploymentDashboard(props: IProps) {
|
||||
</Popover>
|
||||
);
|
||||
} else if (!record.completedAt) {
|
||||
if (record.schedulerRunAfter) {
|
||||
const schedulerRunAfter = new Date(0);
|
||||
schedulerRunAfter.setUTCSeconds(record.schedulerRunAfter.seconds);
|
||||
|
||||
if (schedulerRunAfter > now) {
|
||||
return <ClockCircleOutlined />;
|
||||
}
|
||||
}
|
||||
|
||||
return <Spin indicator={<LoadingOutlined spin />} size="small" />;
|
||||
} else if (record.warningMsg !== "") {
|
||||
return (
|
||||
@ -88,6 +105,13 @@ function FuotaDeploymentDashboard(props: IProps) {
|
||||
render: (_text, record) => format_dt_from_secs(record.createdAt?.seconds),
|
||||
width: 250,
|
||||
},
|
||||
{
|
||||
title: "Run at",
|
||||
dataIndex: "schedulerRunAfter",
|
||||
key: "schedulerRunAfter",
|
||||
render: (_text, record) => format_dt_from_secs(record.schedulerRunAfter?.seconds),
|
||||
width: 250,
|
||||
},
|
||||
{
|
||||
title: "Completed at",
|
||||
dataIndex: "completedAt",
|
||||
|
Reference in New Issue
Block a user