From f930bd6b96d917acfe08269210a580d77e74d51f Mon Sep 17 00:00:00 2001 From: Orne Brocaar Date: Wed, 19 Jul 2023 11:27:55 +0100 Subject: [PATCH] Wait for first uplink for Class-C scheduling. --- chirpstack/src/downlink/data.rs | 13 ++++++++++++- chirpstack/src/test/class_c_test.rs | 29 +++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/chirpstack/src/downlink/data.rs b/chirpstack/src/downlink/data.rs index ee20126d..9f659c71 100644 --- a/chirpstack/src/downlink/data.rs +++ b/chirpstack/src/downlink/data.rs @@ -291,6 +291,7 @@ impl Data { ctx.select_downlink_gateway()?; if ctx._is_class_c() { + ctx.check_for_first_uplink()?; ctx.get_class_c_device_lock().await?; ctx.set_immediately()?; ctx.set_tx_info_for_rx2()?; @@ -705,7 +706,7 @@ impl Data { // this is not an ACK, then DownlinkDataMIC will zero out ConfFCnt. phy.set_downlink_data_mic( self.device_session.mac_version().from_proto(), - self.device_session.f_cnt_up - 1, + self.device_session.f_cnt_up.overflowing_sub(1).0, &lrwn::AES128Key::from_slice(&self.device_session.s_nwk_s_int_key)?, ) .context("Set downlink data MIC")?; @@ -861,6 +862,16 @@ impl Data { Ok(()) } + fn check_for_first_uplink(&self) -> Result<()> { + trace!("Checking if device has sent its first uplink already"); + + if self.device_session.f_cnt_up == 0 { + return Err(anyhow!("Device must send its first uplink first")); + } + + Ok(()) + } + async fn get_class_c_device_lock(&self) -> Result<()> { trace!("Getting Class-C device lock"); let conf = config::get(); diff --git a/chirpstack/src/test/class_c_test.rs b/chirpstack/src/test/class_c_test.rs index 9a530d72..5a85f8b1 100644 --- a/chirpstack/src/test/class_c_test.rs +++ b/chirpstack/src/test/class_c_test.rs @@ -105,6 +105,31 @@ async fn test_downlink_scheduler() { ..Default::default() }; + let ds_no_uplink = internal::DeviceSession { + f_cnt_up: 0, + ..ds.clone() + }; + + run_scheduler_test(&DownlinkTest { + name: "device has not yet sent an uplink".into(), + device_queue_items: vec![device_queue::DeviceQueueItem { + id: Uuid::nil(), + dev_eui: dev.dev_eui.clone(), + f_port: 10, + data: vec![1, 2, 3], + ..Default::default() + }], + device_session: Some(ds_no_uplink.clone()), + device_gateway_rx_info: Some(device_gateway_rx_info.clone()), + assert: vec![assert::no_downlink_frame()], + }) + .await; + + // remove the schedule run after + device::set_scheduler_run_after(&dev.dev_eui.clone(), None) + .await + .unwrap(); + run_scheduler_test(&DownlinkTest { name: "unconfirmed data".into(), device_queue_items: vec![device_queue::DeviceQueueItem { @@ -172,7 +197,7 @@ async fn test_downlink_scheduler() { .unwrap(); run_scheduler_test(&DownlinkTest { - name: "cunconfirmed data".into(), + name: "unconfirmed data".into(), device_queue_items: vec![device_queue::DeviceQueueItem { id: Uuid::nil(), dev_eui: dev.dev_eui.clone(), @@ -226,7 +251,7 @@ async fn test_downlink_scheduler() { .unwrap(); run_scheduler_test(&DownlinkTest { - name: "cunconfirmed data".into(), + name: "unconfirmed data".into(), device_queue_items: vec![device_queue::DeviceQueueItem { id: Uuid::nil(), dev_eui: dev.dev_eui.clone(),