mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-07-13 21:39:43 +00:00
Compare commits
11 Commits
api/go/v4.
...
v4.13.0-te
Author | SHA1 | Date | |
---|---|---|---|
e8d001441c | |||
10e7907251 | |||
39df55afb4 | |||
038e45e8f0 | |||
5cf1120f20 | |||
b8e14058f2 | |||
10731c2be5 | |||
f1d46b1bc9 | |||
c954cd3645 | |||
188ef3d8f3 | |||
0cff864f60 |
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -636,7 +636,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "backend"
|
||||
version = "4.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
dependencies = [
|
||||
"aes-kw",
|
||||
"anyhow",
|
||||
@ -838,7 +838,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chirpstack"
|
||||
version = "4.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
@ -930,7 +930,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chirpstack_api"
|
||||
version = "4.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"pbjson",
|
||||
@ -947,7 +947,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chirpstack_integration"
|
||||
version = "4.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -2794,7 +2794,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lrwn"
|
||||
version = "4.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
@ -2807,7 +2807,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lrwn_filters"
|
||||
version = "4.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"lrwn",
|
||||
|
2532
api/go/gw/gw.pb.go
vendored
2532
api/go/gw/gw.pb.go
vendored
File diff suppressed because it is too large
Load Diff
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.1-test.1",
|
||||
"version": "4.13.0-test.1",
|
||||
"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.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
|
||||
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.1-test.1",
|
||||
"version": "4.13.0-test.1",
|
||||
"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.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
|
||||
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.1-test.1",
|
||||
"version": "4.13.0-test.1",
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"grpc/grpc": "^v1.57.0",
|
||||
|
125
api/proto/gw/gw.proto
vendored
125
api/proto/gw/gw.proto
vendored
@ -104,6 +104,82 @@ enum TxAckStatus {
|
||||
DUTY_CYCLE_OVERFLOW = 11;
|
||||
}
|
||||
|
||||
// Gateway events as reported by the ChirpStack Concentratord ZMQ interface.
|
||||
message Event {
|
||||
oneof event {
|
||||
// Uplink frame.
|
||||
UplinkFrame uplink_frame = 1;
|
||||
|
||||
// Gateway stats.
|
||||
GatewayStats gateway_stats = 2;
|
||||
|
||||
// Gateway Mesh Event.
|
||||
MeshEvent mesh = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Commands that can be sent to the ChirpStack Concentratord ZMQ interface.
|
||||
message Command {
|
||||
oneof command {
|
||||
// Downlink frame.
|
||||
DownlinkFrame send_downlink_frame = 1;
|
||||
|
||||
// Gateway configuration.
|
||||
GatewayConfiguration set_gateway_configuration = 2;
|
||||
|
||||
// Get Gateway ID.
|
||||
GetGatewayIdRequest get_gateway_id = 3;
|
||||
|
||||
// Get location.
|
||||
GetLocationRequest get_location = 4;
|
||||
|
||||
// Gateway Mesh Command.
|
||||
MeshCommand mesh = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message MeshEvent {
|
||||
// Gateway ID (of the Border Gateway).
|
||||
string gateway_id = 1;
|
||||
|
||||
// Relay ID.
|
||||
string relay_id = 2;
|
||||
|
||||
// Timestamp (second precision).
|
||||
google.protobuf.Timestamp time = 3;
|
||||
|
||||
// Mesh events.
|
||||
repeated MeshEventItem events = 4;
|
||||
}
|
||||
|
||||
message MeshEventItem {
|
||||
oneof event {
|
||||
// Proprietary Mesh event.
|
||||
MeshEventProprietary proprietary = 1;
|
||||
|
||||
// Mesh heartbeat.
|
||||
MeshEventHeartbeat heartbeat = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message MeshCommand {
|
||||
// Gateway ID (of the Border Gateway).
|
||||
string gateway_id = 1;
|
||||
|
||||
// Relay ID.
|
||||
string relay_id = 2;
|
||||
|
||||
// Mesh events.
|
||||
repeated MeshCommandItem commands = 3;
|
||||
}
|
||||
|
||||
message MeshCommandItem {
|
||||
oneof command {
|
||||
// Proprietary Mesh command.
|
||||
MeshCommandProprietary proprietary = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message Modulation {
|
||||
oneof parameters {
|
||||
// LoRa modulation information.
|
||||
@ -611,6 +687,23 @@ message GatewayConfiguration {
|
||||
google.protobuf.Duration stats_interval = 4;
|
||||
}
|
||||
|
||||
message GetGatewayIdRequest {}
|
||||
|
||||
message GetGatewayIdResponse {
|
||||
// Gateway ID.
|
||||
string gateway_id = 1;
|
||||
}
|
||||
|
||||
message GetLocationRequest {}
|
||||
|
||||
message GetLocationResponse {
|
||||
// Location.
|
||||
common.Location location = 1;
|
||||
|
||||
// Last updated at.
|
||||
google.protobuf.Timestamp updated_at = 2;
|
||||
}
|
||||
|
||||
message ChannelConfiguration {
|
||||
// Frequency (Hz).
|
||||
uint32 frequency = 1;
|
||||
@ -751,21 +844,13 @@ message ConnState {
|
||||
}
|
||||
|
||||
// Gateway Mesh heartbeat (sent periodically by the Relay Gateways).
|
||||
message MeshHeartbeat {
|
||||
// Gateway ID (of the Border Gateway).
|
||||
string gateway_id = 1;
|
||||
|
||||
// Relay ID.
|
||||
string relay_id = 2;
|
||||
|
||||
// Timestamp (second precision).
|
||||
google.protobuf.Timestamp time = 3;
|
||||
message MeshEventHeartbeat {
|
||||
|
||||
// Relay path.
|
||||
repeated MeshHeartbeatRelayPath relay_path = 4;
|
||||
repeated MeshEventHeartbeatRelayPath relay_path = 4;
|
||||
}
|
||||
|
||||
message MeshHeartbeatRelayPath {
|
||||
message MeshEventHeartbeatRelayPath {
|
||||
// Relay ID.
|
||||
string relay_id = 1;
|
||||
|
||||
@ -775,3 +860,21 @@ message MeshHeartbeatRelayPath {
|
||||
// SNR.
|
||||
int32 snr = 3;
|
||||
}
|
||||
|
||||
// Proprietary mesh event.
|
||||
message MeshEventProprietary {
|
||||
// Event type.
|
||||
uint32 event_type = 1;
|
||||
|
||||
// Payload.
|
||||
bytes payload = 2;
|
||||
}
|
||||
|
||||
// Proprietary mesh command.
|
||||
message MeshCommandProprietary {
|
||||
// Command type.
|
||||
uint32 command_type = 1;
|
||||
|
||||
// Payload.
|
||||
bytes payload = 2;
|
||||
}
|
||||
|
3
api/proto/internal/internal.proto
vendored
3
api/proto/internal/internal.proto
vendored
@ -142,6 +142,9 @@ message DeviceSession {
|
||||
|
||||
// Relay state.
|
||||
Relay relay = 41;
|
||||
|
||||
// Pending mac-commands.
|
||||
map<uint32, bytes> mac_command_pending = 43;
|
||||
}
|
||||
|
||||
message UplinkAdrHistory {
|
||||
|
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.1-test.1",
|
||||
version = "4.13.0-test.1",
|
||||
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.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
license = "MIT"
|
||||
homepage = "https://www.chirpstack.io"
|
||||
|
125
api/rust/proto/chirpstack/gw/gw.proto
vendored
125
api/rust/proto/chirpstack/gw/gw.proto
vendored
@ -104,6 +104,82 @@ enum TxAckStatus {
|
||||
DUTY_CYCLE_OVERFLOW = 11;
|
||||
}
|
||||
|
||||
// Gateway events as reported by the ChirpStack Concentratord ZMQ interface.
|
||||
message Event {
|
||||
oneof event {
|
||||
// Uplink frame.
|
||||
UplinkFrame uplink_frame = 1;
|
||||
|
||||
// Gateway stats.
|
||||
GatewayStats gateway_stats = 2;
|
||||
|
||||
// Gateway Mesh Event.
|
||||
MeshEvent mesh = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Commands that can be sent to the ChirpStack Concentratord ZMQ interface.
|
||||
message Command {
|
||||
oneof command {
|
||||
// Downlink frame.
|
||||
DownlinkFrame send_downlink_frame = 1;
|
||||
|
||||
// Gateway configuration.
|
||||
GatewayConfiguration set_gateway_configuration = 2;
|
||||
|
||||
// Get Gateway ID.
|
||||
GetGatewayIdRequest get_gateway_id = 3;
|
||||
|
||||
// Get location.
|
||||
GetLocationRequest get_location = 4;
|
||||
|
||||
// Gateway Mesh Command.
|
||||
MeshCommand mesh = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message MeshEvent {
|
||||
// Gateway ID (of the Border Gateway).
|
||||
string gateway_id = 1;
|
||||
|
||||
// Relay ID.
|
||||
string relay_id = 2;
|
||||
|
||||
// Timestamp (second precision).
|
||||
google.protobuf.Timestamp time = 3;
|
||||
|
||||
// Mesh events.
|
||||
repeated MeshEventItem events = 4;
|
||||
}
|
||||
|
||||
message MeshEventItem {
|
||||
oneof event {
|
||||
// Proprietary Mesh event.
|
||||
MeshEventProprietary proprietary = 1;
|
||||
|
||||
// Mesh heartbeat.
|
||||
MeshEventHeartbeat heartbeat = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message MeshCommand {
|
||||
// Gateway ID (of the Border Gateway).
|
||||
string gateway_id = 1;
|
||||
|
||||
// Relay ID.
|
||||
string relay_id = 2;
|
||||
|
||||
// Mesh events.
|
||||
repeated MeshCommandItem commands = 3;
|
||||
}
|
||||
|
||||
message MeshCommandItem {
|
||||
oneof command {
|
||||
// Proprietary Mesh command.
|
||||
MeshCommandProprietary proprietary = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message Modulation {
|
||||
oneof parameters {
|
||||
// LoRa modulation information.
|
||||
@ -611,6 +687,23 @@ message GatewayConfiguration {
|
||||
google.protobuf.Duration stats_interval = 4;
|
||||
}
|
||||
|
||||
message GetGatewayIdRequest {}
|
||||
|
||||
message GetGatewayIdResponse {
|
||||
// Gateway ID.
|
||||
string gateway_id = 1;
|
||||
}
|
||||
|
||||
message GetLocationRequest {}
|
||||
|
||||
message GetLocationResponse {
|
||||
// Location.
|
||||
common.Location location = 1;
|
||||
|
||||
// Last updated at.
|
||||
google.protobuf.Timestamp updated_at = 2;
|
||||
}
|
||||
|
||||
message ChannelConfiguration {
|
||||
// Frequency (Hz).
|
||||
uint32 frequency = 1;
|
||||
@ -751,21 +844,13 @@ message ConnState {
|
||||
}
|
||||
|
||||
// Gateway Mesh heartbeat (sent periodically by the Relay Gateways).
|
||||
message MeshHeartbeat {
|
||||
// Gateway ID (of the Border Gateway).
|
||||
string gateway_id = 1;
|
||||
|
||||
// Relay ID.
|
||||
string relay_id = 2;
|
||||
|
||||
// Timestamp (second precision).
|
||||
google.protobuf.Timestamp time = 3;
|
||||
message MeshEventHeartbeat {
|
||||
|
||||
// Relay path.
|
||||
repeated MeshHeartbeatRelayPath relay_path = 4;
|
||||
repeated MeshEventHeartbeatRelayPath relay_path = 4;
|
||||
}
|
||||
|
||||
message MeshHeartbeatRelayPath {
|
||||
message MeshEventHeartbeatRelayPath {
|
||||
// Relay ID.
|
||||
string relay_id = 1;
|
||||
|
||||
@ -775,3 +860,21 @@ message MeshHeartbeatRelayPath {
|
||||
// SNR.
|
||||
int32 snr = 3;
|
||||
}
|
||||
|
||||
// Proprietary mesh event.
|
||||
message MeshEventProprietary {
|
||||
// Event type.
|
||||
uint32 event_type = 1;
|
||||
|
||||
// Payload.
|
||||
bytes payload = 2;
|
||||
}
|
||||
|
||||
// Proprietary mesh command.
|
||||
message MeshCommandProprietary {
|
||||
// Command type.
|
||||
uint32 command_type = 1;
|
||||
|
||||
// Payload.
|
||||
bytes payload = 2;
|
||||
}
|
||||
|
@ -142,6 +142,9 @@ message DeviceSession {
|
||||
|
||||
// Relay state.
|
||||
Relay relay = 41;
|
||||
|
||||
// Pending mac-commands.
|
||||
map<uint32, bytes> mac_command_pending = 43;
|
||||
}
|
||||
|
||||
message UplinkAdrHistory {
|
||||
|
9
api/rust/src/lib.rs
vendored
9
api/rust/src/lib.rs
vendored
@ -1,14 +1,17 @@
|
||||
pub use prost;
|
||||
pub use prost_types;
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
pub use pbjson_types;
|
||||
pub use prost;
|
||||
#[cfg(feature = "api")]
|
||||
pub use tonic;
|
||||
|
||||
#[cfg(feature = "api")]
|
||||
pub mod api;
|
||||
#[cfg(feature = "internal")]
|
||||
pub mod internal;
|
||||
|
||||
pub mod common;
|
||||
pub mod gw;
|
||||
pub mod integration;
|
||||
#[cfg(feature = "internal")]
|
||||
pub mod internal;
|
||||
pub mod stream;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "backend"
|
||||
version = "4.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
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.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/chirpstack/chirpstack"
|
||||
|
||||
[dependencies]
|
||||
chirpstack_api = { path = "../api/rust", version = "4.12.1-test.1" }
|
||||
chirpstack_api = { path = "../api/rust", version = "4.13.0-test.1" }
|
||||
redis = { version = "0.29", 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.12.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
@ -352,6 +352,7 @@ pub struct AmqpIntegration {
|
||||
pub url: String,
|
||||
pub json: bool,
|
||||
pub event_routing_key: String,
|
||||
pub exchange: String,
|
||||
}
|
||||
|
||||
impl Default for AmqpIntegration {
|
||||
@ -361,6 +362,7 @@ impl Default for AmqpIntegration {
|
||||
json: true,
|
||||
event_routing_key: "application.{{application_id}}.device.{{dev_eui}}.event.{{event}}"
|
||||
.to_string(),
|
||||
exchange: "amq.topic".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -707,7 +707,8 @@ impl Data {
|
||||
fn set_phy_payloads(&mut self) -> Result<()> {
|
||||
trace!("Setting downlink PHYPayloads");
|
||||
let mut f_pending = self.more_device_queue_items;
|
||||
let ds = self.device.get_device_session()?;
|
||||
let dev_addr = self.device.get_dev_addr()?;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
|
||||
for item in self.downlink_frame_items.iter_mut() {
|
||||
let mut mac_size: usize = 0;
|
||||
@ -729,6 +730,8 @@ impl Data {
|
||||
for mac in &**mac_set {
|
||||
mac_commands.push(mac.clone());
|
||||
}
|
||||
|
||||
mac_command::set_pending(ds, mac_set)?;
|
||||
}
|
||||
|
||||
// LoRaWAN MHDR
|
||||
@ -740,7 +743,7 @@ impl Data {
|
||||
// LoRaWAN MAC payload
|
||||
let mut mac_pl = lrwn::MACPayload {
|
||||
fhdr: lrwn::FHDR {
|
||||
devaddr: self.device.get_dev_addr()?,
|
||||
devaddr: dev_addr,
|
||||
f_cnt: ds.n_f_cnt_down,
|
||||
f_ctrl: lrwn::FCtrl {
|
||||
adr: !self.network_conf.adr_disabled,
|
||||
@ -1196,8 +1199,6 @@ impl Data {
|
||||
if let Some(block) =
|
||||
maccommand::new_channel::request(3, ¤t_channels, &wanted_channels)
|
||||
{
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::NewChannelReq, &block)
|
||||
.await?;
|
||||
self.mac_commands.push(block);
|
||||
}
|
||||
|
||||
@ -1207,7 +1208,7 @@ impl Data {
|
||||
// Note: this must come before ADR!
|
||||
async fn _request_channel_mask_reconfiguration(&mut self) -> Result<()> {
|
||||
trace!("Requesting channel-mask reconfiguration");
|
||||
let ds = self.device.get_device_session()?;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
|
||||
let enabled_uplink_channel_indices: Vec<usize> = ds
|
||||
.enabled_uplink_channel_indices
|
||||
@ -1239,7 +1240,6 @@ impl Data {
|
||||
.collect(),
|
||||
);
|
||||
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::LinkADRReq, &set).await?;
|
||||
self.mac_commands.push(set);
|
||||
|
||||
Ok(())
|
||||
@ -1257,12 +1257,15 @@ impl Data {
|
||||
.get_data_rate(self.uplink_frame_set.as_ref().unwrap().dr)?;
|
||||
|
||||
let ufs = self.uplink_frame_set.as_ref().unwrap();
|
||||
let ds = self.device.get_device_session()?;
|
||||
let dev_eui = self.device.dev_eui;
|
||||
let device_variables = self.device.variables.into_hashmap();
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
|
||||
let req = adr::Request {
|
||||
dev_eui,
|
||||
device_variables,
|
||||
region_config_id: ufs.region_config_id.clone(),
|
||||
region_common_name: ufs.region_common_name,
|
||||
dev_eui: self.device.dev_eui,
|
||||
mac_version: self.device_profile.mac_version,
|
||||
reg_params_revision: self.device_profile.reg_params_revision,
|
||||
adr: ds.adr,
|
||||
@ -1291,7 +1294,6 @@ impl Data {
|
||||
max_dr: self.network_conf.max_dr,
|
||||
uplink_history: ds.uplink_adr_history.clone(),
|
||||
skip_f_cnt_check: ds.skip_f_cnt_check,
|
||||
device_variables: self.device.variables.into_hashmap(),
|
||||
};
|
||||
|
||||
let resp = adr::handle(&self.device_profile.adr_algorithm_id, &req).await;
|
||||
@ -1304,24 +1306,14 @@ impl Data {
|
||||
{
|
||||
let mut adr_set = false;
|
||||
for set in self.mac_commands.iter_mut() {
|
||||
let mut is_link_adr_set = false;
|
||||
|
||||
for mac in &mut **set {
|
||||
if let lrwn::MACCommand::LinkADRReq(pl) = mac {
|
||||
pl.dr = resp.dr;
|
||||
pl.tx_power = resp.tx_power_index;
|
||||
pl.redundancy.nb_rep = resp.nb_trans;
|
||||
|
||||
adr_set = true;
|
||||
is_link_adr_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
if is_link_adr_set {
|
||||
// We need to update the pending mac-command.
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::LinkADRReq, set)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
// There was no existing LinkADRReq to be sent, we need to construct a new one.
|
||||
@ -1358,7 +1350,6 @@ impl Data {
|
||||
},
|
||||
)]);
|
||||
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::LinkADRReq, &set).await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
}
|
||||
@ -1406,7 +1397,7 @@ impl Data {
|
||||
async fn _request_rejoin_param_setup(&mut self) -> Result<()> {
|
||||
trace!("Requesting rejoin param setup");
|
||||
|
||||
let ds = self.device.get_device_session()?;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
|
||||
// Rejoin-request is disabled or device does not support LoRaWAN 1.1.
|
||||
if !self.network_conf.rejoin_request.enabled
|
||||
@ -1423,8 +1414,6 @@ impl Data {
|
||||
self.network_conf.rejoin_request.max_time_n,
|
||||
self.network_conf.rejoin_request.max_count_n,
|
||||
);
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::RejoinParamSetupReq, &set)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
@ -1434,7 +1423,7 @@ impl Data {
|
||||
async fn _set_ping_slot_parameters(&mut self) -> Result<()> {
|
||||
trace!("Setting ping-slot parameters");
|
||||
|
||||
let ds = self.device.get_device_session()?;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
|
||||
if !self.device_profile.supports_class_b {
|
||||
return Ok(());
|
||||
@ -1447,8 +1436,6 @@ impl Data {
|
||||
self.network_conf.class_b.ping_slot_dr,
|
||||
self.network_conf.class_b.ping_slot_frequency,
|
||||
);
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::PingSlotChannelReq, &set)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
@ -1457,7 +1444,7 @@ impl Data {
|
||||
|
||||
async fn _set_rx_parameters(&mut self) -> Result<()> {
|
||||
trace!("Setting rx parameters");
|
||||
let ds = self.device.get_device_session()?;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
|
||||
if ds.rx2_frequency != self.network_conf.rx2_frequency
|
||||
|| ds.rx2_dr as u8 != self.network_conf.rx2_dr
|
||||
@ -1468,8 +1455,6 @@ impl Data {
|
||||
self.network_conf.rx2_frequency,
|
||||
self.network_conf.rx2_dr,
|
||||
);
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::RxParamSetupReq, &set)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
@ -1481,8 +1466,6 @@ impl Data {
|
||||
|
||||
if dev_rx1_delay != req_rx1_delay {
|
||||
let set = maccommand::rx_timing_setup::request(req_rx1_delay);
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::RxTimingSetupReq, &set)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
@ -1491,7 +1474,7 @@ impl Data {
|
||||
|
||||
async fn _set_tx_parameters(&mut self) -> Result<()> {
|
||||
trace!("Setting tx parameters");
|
||||
let ds = self.device.get_device_session()?;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
|
||||
if !self
|
||||
.region_conf
|
||||
@ -1512,8 +1495,6 @@ impl Data {
|
||||
self.network_conf.downlink_dwell_time_400ms,
|
||||
uplink_eirp_index,
|
||||
);
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::TxParamSetupReq, &set)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
@ -1567,8 +1548,8 @@ impl Data {
|
||||
|| rd.uplink_limit_reload_rate
|
||||
!= device.relay_ed_uplink_limit_reload_rate as u32
|
||||
{
|
||||
let d = device::get(&device.dev_eui).await?;
|
||||
let ds = match d.get_device_session() {
|
||||
let mut d = device::get(&device.dev_eui).await?;
|
||||
let ds = match d.get_device_session_mut() {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
// It is valid that the device is no longer activated.
|
||||
@ -1595,13 +1576,17 @@ impl Data {
|
||||
},
|
||||
),
|
||||
]);
|
||||
mac_command::set_pending(
|
||||
&dev_eui,
|
||||
lrwn::CID::UpdateUplinkListReq,
|
||||
&set,
|
||||
self.mac_commands.push(set);
|
||||
|
||||
// Update device-session of device.
|
||||
device::partial_update(
|
||||
d.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
device_session: Some(d.device_session.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
|
||||
rd.dev_addr = dev_addr.to_vec();
|
||||
rd.root_wor_s_key = root_wor_s_key.to_vec();
|
||||
@ -1651,8 +1636,6 @@ impl Data {
|
||||
root_wor_s_key,
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&dev_eui, lrwn::CID::UpdateUplinkListReq, &set)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
|
||||
ds.relay
|
||||
@ -1788,8 +1771,6 @@ impl Data {
|
||||
|
||||
if !commands.is_empty() {
|
||||
let set = lrwn::MACCommandSet::new(commands);
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::CtrlUplinkListReq, &set)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
@ -1799,7 +1780,6 @@ impl Data {
|
||||
async fn _configure_fwd_limit_req(&mut self) -> Result<()> {
|
||||
trace!("Configuring Relay Fwd Limit");
|
||||
|
||||
let dev_eui = self.device.dev_eui;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
let relay_params = self.device_profile.relay_params.clone().unwrap_or_default();
|
||||
|
||||
@ -1843,7 +1823,6 @@ impl Data {
|
||||
},
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&dev_eui, lrwn::CID::ConfigureFwdLimitReq, &set).await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
@ -1915,7 +1894,6 @@ impl Data {
|
||||
}
|
||||
|
||||
let set = lrwn::MACCommandSet::new(commands);
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::FilterListReq, &set).await?;
|
||||
self.mac_commands.push(set);
|
||||
|
||||
// The deletes needs to be processed before we can add new entries.
|
||||
@ -1944,8 +1922,6 @@ impl Data {
|
||||
filter_list_eui: vec![],
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&self.device.dev_eui, lrwn::CID::FilterListReq, &set)
|
||||
.await?;
|
||||
self.mac_commands.push(set);
|
||||
|
||||
// Return because we can't add multiple sets and if we would combine
|
||||
@ -1977,7 +1953,6 @@ impl Data {
|
||||
filter_list_eui: eui,
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&dev_eui, lrwn::CID::FilterListReq, &set).await?;
|
||||
self.mac_commands.push(set);
|
||||
|
||||
f.join_eui = device.join_eui.to_vec();
|
||||
@ -2009,7 +1984,6 @@ impl Data {
|
||||
filter_list_eui: eui,
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&dev_eui, lrwn::CID::FilterListReq, &set).await?;
|
||||
self.mac_commands.push(set);
|
||||
|
||||
ds.relay
|
||||
@ -2037,7 +2011,6 @@ impl Data {
|
||||
async fn _update_relay_conf(&mut self) -> Result<()> {
|
||||
trace!("Updating Relay Conf");
|
||||
|
||||
let dev_eui = self.device.dev_eui;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
let relay_params = self.device_profile.relay_params.clone().unwrap_or_default();
|
||||
|
||||
@ -2075,7 +2048,6 @@ impl Data {
|
||||
second_ch_freq: relay_params.second_channel_freq,
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&dev_eui, lrwn::CID::RelayConfReq, &set).await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
@ -2087,7 +2059,6 @@ impl Data {
|
||||
async fn _update_end_device_conf(&mut self) -> Result<()> {
|
||||
trace!("Updating End Device Conf");
|
||||
|
||||
let dev_eui = self.device.dev_eui;
|
||||
let ds = self.device.get_device_session_mut()?;
|
||||
let relay_params = self.device_profile.relay_params.clone().unwrap_or_default();
|
||||
|
||||
@ -2124,7 +2095,6 @@ impl Data {
|
||||
second_ch_freq: relay_params.second_channel_freq,
|
||||
},
|
||||
)]);
|
||||
mac_command::set_pending(&dev_eui, lrwn::CID::EndDeviceConfReq, &set).await?;
|
||||
self.mac_commands.push(set);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::sync::{LazyLock, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
@ -352,7 +351,7 @@ async fn message_callback(
|
||||
.inc();
|
||||
let mut event = match json {
|
||||
true => serde_json::from_slice(&p.payload)?,
|
||||
false => chirpstack_api::gw::UplinkFrame::decode(&mut Cursor::new(&p.payload))?,
|
||||
false => chirpstack_api::gw::UplinkFrame::decode(p.payload.as_ref())?,
|
||||
};
|
||||
|
||||
if v4_migrate {
|
||||
@ -377,7 +376,7 @@ async fn message_callback(
|
||||
.inc();
|
||||
let mut event = match json {
|
||||
true => serde_json::from_slice(&p.payload)?,
|
||||
false => chirpstack_api::gw::GatewayStats::decode(&mut Cursor::new(&p.payload))?,
|
||||
false => chirpstack_api::gw::GatewayStats::decode(p.payload.as_ref())?,
|
||||
};
|
||||
|
||||
if v4_migrate {
|
||||
@ -401,7 +400,7 @@ async fn message_callback(
|
||||
.inc();
|
||||
let mut event = match json {
|
||||
true => serde_json::from_slice(&p.payload)?,
|
||||
false => chirpstack_api::gw::DownlinkTxAck::decode(&mut Cursor::new(&p.payload))?,
|
||||
false => chirpstack_api::gw::DownlinkTxAck::decode(p.payload.as_ref())?,
|
||||
};
|
||||
|
||||
if v4_migrate {
|
||||
@ -410,18 +409,18 @@ async fn message_callback(
|
||||
|
||||
set_gateway_json(&event.gateway_id, json);
|
||||
tokio::spawn(downlink::tx_ack::TxAck::handle(event));
|
||||
} else if topic.ends_with("/mesh-heartbeat") {
|
||||
} else if topic.ends_with("/mesh") {
|
||||
EVENT_COUNTER
|
||||
.get_or_create(&EventLabels {
|
||||
event: "mesh-heartbeat".to_string(),
|
||||
event: "mesh".to_string(),
|
||||
})
|
||||
.inc();
|
||||
let event = match json {
|
||||
true => serde_json::from_slice(&p.payload)?,
|
||||
false => chirpstack_api::gw::MeshHeartbeat::decode(&mut Cursor::new(&p.payload))?,
|
||||
false => chirpstack_api::gw::MeshEvent::decode(p.payload.as_ref())?,
|
||||
};
|
||||
|
||||
tokio::spawn(uplink::mesh::MeshHeartbeat::handle(event));
|
||||
tokio::spawn(uplink::mesh::Mesh::handle(event));
|
||||
} else {
|
||||
return Err(anyhow!("Unknown event type"));
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ pub struct Integration<'a> {
|
||||
templates: Handlebars<'a>,
|
||||
json: bool,
|
||||
url: String,
|
||||
exchange: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@ -49,6 +50,7 @@ impl<'a> Integration<'a> {
|
||||
templates,
|
||||
url: conf.url.clone(),
|
||||
json: conf.json,
|
||||
exchange: conf.exchange.clone(),
|
||||
};
|
||||
i.connect().await?;
|
||||
|
||||
@ -90,7 +92,7 @@ impl<'a> Integration<'a> {
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.basic_publish(
|
||||
"amq.topic",
|
||||
&self.exchange,
|
||||
&routing_key,
|
||||
BasicPublishOptions::default(),
|
||||
b,
|
||||
|
@ -77,7 +77,7 @@ pub async fn handle_uplink(
|
||||
);
|
||||
|
||||
// Get pending mac-command block, this could return None.
|
||||
let pending = match mac_command::get_pending(&dev.dev_eui, cid).await {
|
||||
let pending = match mac_command::get_pending(dev.get_device_session_mut()?, cid).await {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!(dev_eui = %dev.dev_eui, cid = %cid, error = %e, "Get pending mac-command block error");
|
||||
@ -85,13 +85,6 @@ pub async fn handle_uplink(
|
||||
}
|
||||
};
|
||||
|
||||
// Delete the pending mac-command.
|
||||
if pending.is_some() {
|
||||
if let Err(e) = mac_command::delete_pending(&dev.dev_eui, cid).await {
|
||||
error!(dev_eui = %dev.dev_eui, cid = %cid, error = %e, "Delete pending mac-command error");
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the mac-command, which might return a block to answer the uplink mac-command
|
||||
// request.
|
||||
let res = match handle(
|
||||
|
@ -327,6 +327,15 @@ pub async fn get_for_phypayload_and_incr_f_cnt_up(
|
||||
return Err(Error::InvalidPayload("MacPayload".to_string()));
|
||||
};
|
||||
|
||||
// We calculate the scheduler_run_after timestamp, such that we can update
|
||||
// it directly when updating the device-session (to update the frame-counter).
|
||||
// This way, we limit the risk of overlapping Class-A downlinks with Class-B / -C
|
||||
// downlinks.
|
||||
let conf = config::get();
|
||||
let scheduler_run_after = Utc::now()
|
||||
+ Duration::from_std(conf.network.scheduler.class_a_lock_duration)
|
||||
.map_err(anyhow::Error::new)?;
|
||||
|
||||
let mut c = get_async_db_conn().await?;
|
||||
|
||||
db_transaction::<ValidationStatus, Error, _>(&mut c, |c| {
|
||||
@ -427,10 +436,20 @@ pub async fn get_for_phypayload_and_incr_f_cnt_up(
|
||||
let ds_f_cnt_up = ds.f_cnt_up;
|
||||
ds.f_cnt_up = full_f_cnt + 1;
|
||||
|
||||
let _ = diesel::update(device::dsl::device.find(d.dev_eui))
|
||||
.set(device::device_session.eq(&ds.clone()))
|
||||
.execute(c)
|
||||
.await?;
|
||||
if scheduler_run_after > d.scheduler_run_after.unwrap_or_default() {
|
||||
let _ = diesel::update(device::dsl::device.find(d.dev_eui))
|
||||
.set((
|
||||
device::device_session.eq(&ds.clone()),
|
||||
device::scheduler_run_after.eq(&scheduler_run_after),
|
||||
))
|
||||
.execute(c)
|
||||
.await?;
|
||||
} else {
|
||||
let _ = diesel::update(device::dsl::device.find(d.dev_eui))
|
||||
.set(device::device_session.eq(&ds.clone()))
|
||||
.execute(c)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// We do return the device-session with original frame-counter
|
||||
ds.f_cnt_up = ds_f_cnt_up;
|
||||
|
@ -24,7 +24,10 @@ pub async fn save(df: &internal::DownlinkFrame) -> Result<()> {
|
||||
|
||||
pub async fn get_and_del(id: u32) -> Result<internal::DownlinkFrame, Error> {
|
||||
let key = redis_key(format!("frame:{}", id));
|
||||
let v: Vec<u8> = redis::cmd("GETDEL")
|
||||
let (v, _): (Vec<u8>, u8) = redis::pipe()
|
||||
.cmd("GET")
|
||||
.arg(key.clone())
|
||||
.cmd("DEL")
|
||||
.arg(key)
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
|
@ -1,34 +1,24 @@
|
||||
use anyhow::Result;
|
||||
use tracing::info;
|
||||
|
||||
use super::{get_async_redis_conn, redis_key};
|
||||
use crate::config;
|
||||
use lrwn::EUI64;
|
||||
use chirpstack_api::internal;
|
||||
|
||||
pub async fn set_pending(dev_eui: &EUI64, cid: lrwn::CID, set: &lrwn::MACCommandSet) -> Result<()> {
|
||||
let conf = config::get();
|
||||
|
||||
let key = redis_key(format!("device:{}:mac:pending:{}", dev_eui, cid.to_u8()));
|
||||
let ttl = conf.network.device_session_ttl.as_millis() as usize;
|
||||
pub fn set_pending(ds: &mut internal::DeviceSession, set: &lrwn::MACCommandSet) -> Result<()> {
|
||||
let cid = set.cid()?;
|
||||
let b = set.to_vec()?;
|
||||
|
||||
() = redis::cmd("PSETEX")
|
||||
.arg(key)
|
||||
.arg(ttl)
|
||||
.arg(b)
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
|
||||
info!(dev_eui = %dev_eui, cid = %cid, "Pending mac-command block set");
|
||||
ds.mac_command_pending.insert(cid.to_u8().into(), b);
|
||||
info!(cid = %cid, "Pending mac-command block set");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_pending(dev_eui: &EUI64, cid: lrwn::CID) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let key = redis_key(format!("device:{}:mac:pending:{}", dev_eui, cid.to_u8()));
|
||||
let b: Vec<u8> = redis::cmd("GET")
|
||||
.arg(key)
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
pub async fn get_pending(
|
||||
ds: &mut internal::DeviceSession,
|
||||
cid: lrwn::CID,
|
||||
) -> Result<Option<lrwn::MACCommandSet>> {
|
||||
let b = ds
|
||||
.mac_command_pending
|
||||
.remove(&cid.to_u8().into())
|
||||
.unwrap_or_default();
|
||||
|
||||
let out = if !b.is_empty() {
|
||||
let mut mac = lrwn::MACCommandSet::from_slice(&b);
|
||||
@ -44,49 +34,3 @@ pub async fn get_pending(dev_eui: &EUI64, cid: lrwn::CID) -> Result<Option<lrwn:
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub async fn delete_pending(dev_eui: &EUI64, cid: lrwn::CID) -> Result<()> {
|
||||
let key = redis_key(format!("device:{}:mac:pending:{}", dev_eui, cid.to_u8()));
|
||||
|
||||
() = redis::cmd("DEL")
|
||||
.arg(key)
|
||||
.query_async(&mut get_async_redis_conn().await?)
|
||||
.await?;
|
||||
|
||||
info!(dev_eui = %dev_eui, cid = %cid, "Pending mac-command block deleted");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::test;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mac_command() {
|
||||
let _guard = test::prepare().await;
|
||||
|
||||
let dev_eui = EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
let mac = lrwn::MACCommandSet::new(vec![lrwn::MACCommand::DevStatusReq]);
|
||||
|
||||
// set
|
||||
set_pending(&dev_eui, lrwn::CID::DevStatusReq, &mac)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// get
|
||||
let mac_get = get_pending(&dev_eui, lrwn::CID::DevStatusReq)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(mac, mac_get.unwrap());
|
||||
|
||||
// delete
|
||||
delete_pending(&dev_eui, lrwn::CID::DevStatusReq)
|
||||
.await
|
||||
.unwrap();
|
||||
let resp = get_pending(&dev_eui, lrwn::CID::DevStatusReq)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(resp.is_none());
|
||||
}
|
||||
}
|
||||
|
@ -4230,33 +4230,30 @@ async fn test_lorawan_10_adr() {
|
||||
name: "acknowledgement of pending adr request".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui;
|
||||
Box::pin(async move {
|
||||
mac_command::set_pending(
|
||||
&dev_eui,
|
||||
lrwn::CID::LinkADRReq,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
|
||||
lrwn::LinkADRReqPayload {
|
||||
dr: 0,
|
||||
tx_power: 3,
|
||||
ch_mask: lrwn::ChMask::new([
|
||||
true, true, true, false, false, false, false, false, false,
|
||||
false, false, false, false, false, false, false,
|
||||
]),
|
||||
redundancy: lrwn::Redundancy {
|
||||
ch_mask_cntl: 0,
|
||||
nb_rep: 1,
|
||||
},
|
||||
},
|
||||
)]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
})),
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
device_session: Some(ds.clone()),
|
||||
device_session: Some({
|
||||
let mut ds = ds.clone();
|
||||
mac_command::set_pending(
|
||||
&mut ds,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
|
||||
lrwn::LinkADRReqPayload {
|
||||
dr: 0,
|
||||
tx_power: 3,
|
||||
ch_mask: lrwn::ChMask::new([
|
||||
true, true, true, false, false, false, false, false, false, false,
|
||||
false, false, false, false, false, false,
|
||||
]),
|
||||
redundancy: lrwn::Redundancy {
|
||||
ch_mask_cntl: 0,
|
||||
nb_rep: 1,
|
||||
},
|
||||
},
|
||||
)]),
|
||||
)
|
||||
.unwrap();
|
||||
ds
|
||||
}),
|
||||
tx_info: tx_info.clone(),
|
||||
rx_info: rx_info.clone(),
|
||||
phy_payload: lrwn::PhyPayload {
|
||||
@ -4298,33 +4295,30 @@ async fn test_lorawan_10_adr() {
|
||||
name: "negative acknowledgement of pending adr request".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui;
|
||||
Box::pin(async move {
|
||||
mac_command::set_pending(
|
||||
&dev_eui,
|
||||
lrwn::CID::LinkADRReq,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
|
||||
lrwn::LinkADRReqPayload {
|
||||
dr: 0,
|
||||
tx_power: 3,
|
||||
ch_mask: lrwn::ChMask::new([
|
||||
true, true, true, false, false, false, false, false, false,
|
||||
false, false, false, false, false, false, false,
|
||||
]),
|
||||
redundancy: lrwn::Redundancy {
|
||||
ch_mask_cntl: 0,
|
||||
nb_rep: 1,
|
||||
},
|
||||
},
|
||||
)]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
})),
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
device_session: Some(ds.clone()),
|
||||
device_session: Some({
|
||||
let mut ds = ds.clone();
|
||||
mac_command::set_pending(
|
||||
&mut ds,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
|
||||
lrwn::LinkADRReqPayload {
|
||||
dr: 0,
|
||||
tx_power: 3,
|
||||
ch_mask: lrwn::ChMask::new([
|
||||
true, true, true, false, false, false, false, false, false, false,
|
||||
false, false, false, false, false, false,
|
||||
]),
|
||||
redundancy: lrwn::Redundancy {
|
||||
ch_mask_cntl: 0,
|
||||
nb_rep: 1,
|
||||
},
|
||||
},
|
||||
)]),
|
||||
)
|
||||
.unwrap();
|
||||
ds
|
||||
}),
|
||||
tx_info: tx_info.clone(),
|
||||
rx_info: rx_info.clone(),
|
||||
phy_payload: lrwn::PhyPayload {
|
||||
@ -4540,33 +4534,30 @@ async fn test_lorawan_10_adr() {
|
||||
name: "new channel re-configuration ack-ed".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui;
|
||||
Box::pin(async move {
|
||||
mac_command::set_pending(
|
||||
&dev_eui,
|
||||
lrwn::CID::LinkADRReq,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
|
||||
lrwn::LinkADRReqPayload {
|
||||
dr: 0,
|
||||
tx_power: 1,
|
||||
ch_mask: lrwn::ChMask::new([
|
||||
true, true, true, false, false, false, false, false, false,
|
||||
false, false, false, false, false, false, false,
|
||||
]),
|
||||
redundancy: lrwn::Redundancy {
|
||||
ch_mask_cntl: 0,
|
||||
nb_rep: 0,
|
||||
},
|
||||
},
|
||||
)]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
})),
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
device_session: Some(ds_7chan.clone()),
|
||||
device_session: Some({
|
||||
let mut ds = ds_7chan.clone();
|
||||
mac_command::set_pending(
|
||||
&mut ds,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
|
||||
lrwn::LinkADRReqPayload {
|
||||
dr: 0,
|
||||
tx_power: 1,
|
||||
ch_mask: lrwn::ChMask::new([
|
||||
true, true, true, false, false, false, false, false, false, false,
|
||||
false, false, false, false, false, false,
|
||||
]),
|
||||
redundancy: lrwn::Redundancy {
|
||||
ch_mask_cntl: 0,
|
||||
nb_rep: 0,
|
||||
},
|
||||
},
|
||||
)]),
|
||||
)
|
||||
.unwrap();
|
||||
ds
|
||||
}),
|
||||
tx_info: tx_info.clone(),
|
||||
rx_info: rx_info.clone(),
|
||||
phy_payload: lrwn::PhyPayload {
|
||||
@ -4606,33 +4597,30 @@ async fn test_lorawan_10_adr() {
|
||||
name: "new channel re-configuration not ack-ed".into(),
|
||||
dev_eui: dev.dev_eui,
|
||||
device_queue_items: vec![],
|
||||
before_func: Some(Box::new(move || {
|
||||
let dev_eui = dev.dev_eui;
|
||||
Box::pin(async move {
|
||||
mac_command::set_pending(
|
||||
&dev_eui,
|
||||
lrwn::CID::LinkADRReq,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
|
||||
lrwn::LinkADRReqPayload {
|
||||
dr: 0,
|
||||
tx_power: 1,
|
||||
ch_mask: lrwn::ChMask::new([
|
||||
true, true, true, false, false, false, false, false, false,
|
||||
false, false, false, false, false, false, false,
|
||||
]),
|
||||
redundancy: lrwn::Redundancy {
|
||||
ch_mask_cntl: 0,
|
||||
nb_rep: 0,
|
||||
},
|
||||
},
|
||||
)]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
})),
|
||||
before_func: None,
|
||||
after_func: None,
|
||||
device_session: Some(ds_7chan.clone()),
|
||||
device_session: Some({
|
||||
let mut ds = ds_7chan.clone();
|
||||
mac_command::set_pending(
|
||||
&mut ds,
|
||||
&lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
|
||||
lrwn::LinkADRReqPayload {
|
||||
dr: 0,
|
||||
tx_power: 1,
|
||||
ch_mask: lrwn::ChMask::new([
|
||||
true, true, true, false, false, false, false, false, false, false,
|
||||
false, false, false, false, false, false,
|
||||
]),
|
||||
redundancy: lrwn::Redundancy {
|
||||
ch_mask_cntl: 0,
|
||||
nb_rep: 0,
|
||||
},
|
||||
},
|
||||
)]),
|
||||
)
|
||||
.unwrap();
|
||||
ds
|
||||
}),
|
||||
tx_info: tx_info.clone(),
|
||||
rx_info: rx_info.clone(),
|
||||
phy_payload: lrwn::PhyPayload {
|
||||
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use chrono::{DateTime, Duration, Local, Utc};
|
||||
use chrono::{DateTime, Local, Utc};
|
||||
use tracing::{debug, error, info, span, trace, warn, Instrument, Level};
|
||||
|
||||
use super::error::Error;
|
||||
@ -126,7 +126,6 @@ impl Data {
|
||||
ctx.set_device_info()?;
|
||||
ctx.set_device_gateway_rx_info()?;
|
||||
ctx.handle_retransmission_reset().await?;
|
||||
ctx.set_scheduler_run_after().await?;
|
||||
ctx.decrypt_f_opts_mac_commands()?;
|
||||
ctx.decrypt_frm_payload()?;
|
||||
ctx.log_uplink_frame_set().await?;
|
||||
@ -522,36 +521,6 @@ impl Data {
|
||||
Err(Error::Abort)
|
||||
}
|
||||
|
||||
// For Class-B and Class-C devices, set the scheduler_run_after timestamp to avoid collisions with
|
||||
// the Class-A downlink and Class-B/C scheduler.
|
||||
async fn set_scheduler_run_after(&mut self) -> Result<()> {
|
||||
let dev = self.device.as_mut().unwrap();
|
||||
let conf = config::get();
|
||||
|
||||
if dev.enabled_class == DeviceClass::B || dev.enabled_class == DeviceClass::C {
|
||||
trace!("Setting scheduler_run_after for device");
|
||||
let scheduler_run_after =
|
||||
Utc::now() + Duration::from_std(conf.network.scheduler.class_a_lock_duration)?;
|
||||
|
||||
// Only set the new scheduler_run_after if it is currently None
|
||||
// or when the current value is before the calculated scheduler_run_after.
|
||||
if dev.scheduler_run_after.is_none()
|
||||
|| scheduler_run_after > dev.scheduler_run_after.unwrap()
|
||||
{
|
||||
*dev = device::partial_update(
|
||||
dev.dev_eui,
|
||||
&device::DeviceChangeset {
|
||||
scheduler_run_after: Some(Some(scheduler_run_after)),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn filter_rx_info_by_tenant(&mut self) -> Result<()> {
|
||||
trace!("Filtering rx_info by tenant_id");
|
||||
|
||||
|
@ -14,14 +14,15 @@ use crate::storage::{
|
||||
};
|
||||
use lrwn::EUI64;
|
||||
|
||||
pub struct MeshHeartbeat {
|
||||
pub struct Mesh {
|
||||
gateway_id: EUI64,
|
||||
relay_id: RelayId,
|
||||
mesh_stats: gw::MeshHeartbeat,
|
||||
time: DateTime<Utc>,
|
||||
mesh_event: gw::MeshEvent,
|
||||
}
|
||||
|
||||
impl MeshHeartbeat {
|
||||
pub async fn handle(s: gw::MeshHeartbeat) {
|
||||
impl Mesh {
|
||||
pub async fn handle(s: gw::MeshEvent) {
|
||||
let gateway_id = match EUI64::from_str(&s.gateway_id) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
@ -38,9 +39,9 @@ impl MeshHeartbeat {
|
||||
}
|
||||
};
|
||||
|
||||
let span = span!(Level::INFO, "mesh_stats", gateway_id = %gateway_id, relay_id = %relay_id);
|
||||
let span = span!(Level::INFO, "mesh", gateway_id = %gateway_id, relay_id = %relay_id);
|
||||
|
||||
if let Err(e) = MeshHeartbeat::_handle(gateway_id, relay_id, s)
|
||||
if let Err(e) = Mesh::_handle(gateway_id, relay_id, s)
|
||||
.instrument(span)
|
||||
.await
|
||||
{
|
||||
@ -48,52 +49,61 @@ impl MeshHeartbeat {
|
||||
Some(Error::NotFound(_)) => {
|
||||
let conf = config::get();
|
||||
if !conf.gateway.allow_unknown_gateways {
|
||||
error!(error = %e.full(), "Handle mesh-stats error");
|
||||
error!(error = %e.full(), "Handle mesh error");
|
||||
}
|
||||
}
|
||||
Some(_) | None => {
|
||||
error!(error = %e.full(), "Handle mesh-stats error");
|
||||
error!(error = %e.full(), "Handle mesh error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn _handle(gateway_id: EUI64, relay_id: RelayId, s: gw::MeshHeartbeat) -> Result<()> {
|
||||
let mut ctx = MeshHeartbeat {
|
||||
async fn _handle(gateway_id: EUI64, relay_id: RelayId, s: gw::MeshEvent) -> Result<()> {
|
||||
let ctx = Mesh {
|
||||
gateway_id,
|
||||
relay_id,
|
||||
mesh_stats: s,
|
||||
time: s
|
||||
.time
|
||||
.ok_or_else(|| anyhow!("Time field is empty"))?
|
||||
.try_into()
|
||||
.map_err(|e| anyhow!("Covert time error: {}", e))?,
|
||||
mesh_event: s,
|
||||
};
|
||||
|
||||
ctx.update_or_create_relay_gateway().await?;
|
||||
ctx.handle_events().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_or_create_relay_gateway(&mut self) -> Result<()> {
|
||||
trace!("Getting Border Gateway");
|
||||
let border_gw = gateway::get(&self.gateway_id).await?;
|
||||
async fn handle_events(&self) -> Result<()> {
|
||||
trace!("Handling mesh events");
|
||||
|
||||
let ts: DateTime<Utc> = match &self.mesh_stats.time {
|
||||
Some(v) => (*v)
|
||||
.try_into()
|
||||
.map_err(|e| anyhow!("Convert time error: {}", e))?,
|
||||
None => {
|
||||
warn!("Stats message does not have time field set");
|
||||
return Ok(());
|
||||
for event in &self.mesh_event.events {
|
||||
match &event.event {
|
||||
Some(gw::mesh_event_item::Event::Proprietary(_)) | None => continue,
|
||||
Some(gw::mesh_event_item::Event::Heartbeat(v)) => self._handle_heartbeat(v).await?,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn _handle_heartbeat(&self, _pl: &gw::MeshEventHeartbeat) -> Result<()> {
|
||||
trace!("Handling heartbeat event");
|
||||
|
||||
let border_gw = gateway::get(&self.gateway_id).await?;
|
||||
|
||||
match gateway::get_relay_gateway(border_gw.tenant_id.into(), self.relay_id).await {
|
||||
Ok(mut v) => {
|
||||
if let Some(last_seen_at) = v.last_seen_at {
|
||||
if last_seen_at > ts {
|
||||
warn!("Time is less than last seen timestamp, ignoring stats");
|
||||
if last_seen_at > self.time {
|
||||
warn!("Time is less than last seen timestamp, ignoring heartbeat");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
v.last_seen_at = Some(ts);
|
||||
v.last_seen_at = Some(self.time);
|
||||
v.region_config_id = border_gw
|
||||
.properties
|
||||
.get("region_config_id")
|
||||
@ -106,7 +116,7 @@ impl MeshHeartbeat {
|
||||
tenant_id: border_gw.tenant_id,
|
||||
relay_id: self.relay_id,
|
||||
name: self.relay_id.to_string(),
|
||||
last_seen_at: Some(ts),
|
||||
last_seen_at: Some(self.time),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
@ -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.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
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.1-test.1"
|
||||
version = "4.13.0-test.1"
|
||||
authors = ["Orne Brocaar <info@brocaar.com>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/chirpstack/chirpstack"
|
||||
|
@ -402,6 +402,15 @@ impl MACCommandSet {
|
||||
MACCommandSet(macs)
|
||||
}
|
||||
|
||||
// This reads the CID from the first mac-command in the set. It is assumed
|
||||
// that all mac-commands in the set share the same CID.
|
||||
pub fn cid(&self) -> Result<CID> {
|
||||
self.0
|
||||
.first()
|
||||
.map(|v| v.cid())
|
||||
.ok_or_else(|| anyhow!("Set is empty"))
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Result<usize> {
|
||||
let b = self.to_vec()?;
|
||||
Ok(b.len())
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chirpstack-ui",
|
||||
"version": "4.12.1-test.1",
|
||||
"version": "4.13.0-test.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
@ -19,11 +19,9 @@ interface IProps {
|
||||
function CodeEditor(props: IProps) {
|
||||
const form = Form.useFormInstance();
|
||||
const [value, setValue] = useState<string>("");
|
||||
const [reloadKey, setReloadKey] = useState<number>(1);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(form.getFieldValue(props.name));
|
||||
setReloadKey(k => k + 1);
|
||||
setValue(form.getFieldValue(props.name) || "");
|
||||
}, [form, props]);
|
||||
|
||||
const onChange = (newValue: string) => {
|
||||
@ -41,7 +39,6 @@ function CodeEditor(props: IProps) {
|
||||
theme="github"
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
name={`code-editor-refresh-${reloadKey}`}
|
||||
width="100%"
|
||||
height="600px"
|
||||
editorProps={{ $blockScrolling: true }}
|
||||
|
Reference in New Issue
Block a user