Fix filtering on mac-command conflict and error count.

This commit is contained in:
Orne Brocaar 2022-09-05 14:07:16 +01:00
parent 32889d7052
commit 8a4b537645

View File

@ -389,6 +389,8 @@ impl Data {
self._set_rx_parameters().await?;
self._set_tx_parameters().await?;
self.mac_commands = filter_mac_commands(&self.device_session, &self.mac_commands);
Ok(())
}
@ -820,6 +822,18 @@ impl Data {
// Note: this must come before ADR!
async fn _request_channel_mask_reconfiguration(&mut self) -> Result<()> {
trace!("Requesting channel-mask reconfiguration");
// The channel-mask reconfiguration (using LinkADRReq) is incompatible with the
// NewChannelReq.
for set in &self.mac_commands {
for mac in &**set {
if let lrwn::MACCommand::NewChannelReq(_) = &mac {
trace!("Skipping channel-mask reconfiguration due to pending NewChannelReq");
return Ok(());
}
}
}
let enabled_uplink_channel_indices: Vec<usize> = self
.device_session
.enabled_uplink_channel_indices
@ -1421,3 +1435,233 @@ impl Data {
Ok(false)
}
}
fn filter_mac_commands(
device_session: &internal::DeviceSession,
mac_commands: &[lrwn::MACCommandSet],
) -> Vec<lrwn::MACCommandSet> {
let incompatible_macs: HashMap<lrwn::CID, Vec<lrwn::CID>> = [
(lrwn::CID::NewChannelReq, vec![lrwn::CID::LinkADRReq]),
(lrwn::CID::LinkADRReq, vec![lrwn::CID::NewChannelReq]),
]
.iter()
.cloned()
.collect();
let mut filtered_mac_commands: Vec<lrwn::MACCommandSet> = Vec::new();
'outer: for mac_command_set in mac_commands {
for mac_command in &**mac_command_set {
// Check if it doesn't exceed the max error error count.
if device_session
.mac_command_error_count
.get(&(mac_command.cid().byte() as u32))
.cloned()
.unwrap_or_default()
> 1
{
continue 'outer;
}
// Check if there aren't any conflicting mac-commands.
if let Some(incompatible_cids) = incompatible_macs.get(&mac_command.cid()) {
for mac_command_set in &filtered_mac_commands {
for mac_command in &**mac_command_set {
if incompatible_cids.contains(&mac_command.cid()) {
continue 'outer;
}
}
}
}
}
filtered_mac_commands.push(mac_command_set.clone());
}
filtered_mac_commands
}
#[cfg(test)]
mod test {
use super::*;
struct Test {
device_session: internal::DeviceSession,
mac_commands: Vec<lrwn::MACCommandSet>,
expected_mac_commands: Vec<lrwn::MACCommandSet>,
}
#[test]
fn test_filter_mac_commands() {
let tests = vec![
// No mac-commands set.
Test {
device_session: internal::DeviceSession {
..Default::default()
},
mac_commands: Vec::new(),
expected_mac_commands: Vec::new(),
},
// One LinkADRReq, no errors.
Test {
device_session: internal::DeviceSession {
mac_command_error_count: [(lrwn::CID::LinkADRReq.byte() as u32, 0)]
.iter()
.cloned()
.collect(),
..Default::default()
},
mac_commands: vec![lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::LinkADRReq(lrwn::LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: lrwn::ChMask::new([false; 16]),
redundancy: lrwn::Redundancy {
ch_mask_cntl: 0,
nb_rep: 0,
},
}),
])],
expected_mac_commands: vec![lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::LinkADRReq(lrwn::LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: lrwn::ChMask::new([false; 16]),
redundancy: lrwn::Redundancy {
ch_mask_cntl: 0,
nb_rep: 0,
},
}),
])],
},
// One LinkADRReq, 0 errors (HashMap contains the CID, but count = 0).
Test {
device_session: internal::DeviceSession {
mac_command_error_count: [(lrwn::CID::LinkADRReq.byte() as u32, 0)]
.iter()
.cloned()
.collect(),
..Default::default()
},
mac_commands: vec![lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::LinkADRReq(lrwn::LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: lrwn::ChMask::new([false; 16]),
redundancy: lrwn::Redundancy {
ch_mask_cntl: 0,
nb_rep: 0,
},
}),
])],
expected_mac_commands: vec![lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::LinkADRReq(lrwn::LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: lrwn::ChMask::new([false; 16]),
redundancy: lrwn::Redundancy {
ch_mask_cntl: 0,
nb_rep: 0,
},
}),
])],
},
// One LinkADRReq, exceeding error count.
Test {
device_session: internal::DeviceSession {
mac_command_error_count: [(lrwn::CID::LinkADRReq.byte() as u32, 2)]
.iter()
.cloned()
.collect(),
..Default::default()
},
mac_commands: vec![lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::LinkADRReq(lrwn::LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: lrwn::ChMask::new([false; 16]),
redundancy: lrwn::Redundancy {
ch_mask_cntl: 0,
nb_rep: 0,
},
}),
])],
expected_mac_commands: Vec::new(),
},
// NewChannelReq + LinkADRReq
Test {
device_session: Default::default(),
mac_commands: vec![
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::NewChannelReq(
lrwn::NewChannelReqPayload {
ch_index: 3,
freq: 867100000,
min_dr: 0,
max_dr: 5,
},
)]),
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
lrwn::LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: lrwn::ChMask::new([false; 16]),
redundancy: lrwn::Redundancy {
ch_mask_cntl: 0,
nb_rep: 0,
},
},
)]),
],
expected_mac_commands: vec![lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::NewChannelReq(lrwn::NewChannelReqPayload {
ch_index: 3,
freq: 867100000,
min_dr: 0,
max_dr: 5,
}),
])],
},
// LinkADRReq + NewChannelReq (this order should never happen)
Test {
device_session: Default::default(),
mac_commands: vec![
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::LinkADRReq(
lrwn::LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: lrwn::ChMask::new([false; 16]),
redundancy: lrwn::Redundancy {
ch_mask_cntl: 0,
nb_rep: 0,
},
},
)]),
lrwn::MACCommandSet::new(vec![lrwn::MACCommand::NewChannelReq(
lrwn::NewChannelReqPayload {
ch_index: 3,
freq: 867100000,
min_dr: 0,
max_dr: 5,
},
)]),
],
expected_mac_commands: vec![lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::LinkADRReq(lrwn::LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: lrwn::ChMask::new([false; 16]),
redundancy: lrwn::Redundancy {
ch_mask_cntl: 0,
nb_rep: 0,
},
}),
])],
},
];
for test in &tests {
let out = filter_mac_commands(&test.device_session, &test.mac_commands);
assert_eq!(test.expected_mac_commands, out);
}
}
}