Implement removing device from Relay filter list.

This commit is contained in:
Orne Brocaar 2023-07-31 16:00:08 +01:00
parent 050610de55
commit 4359e90613
2 changed files with 140 additions and 14 deletions

View File

@ -1652,12 +1652,12 @@ impl Data {
async fn _update_filter_list(&mut self) -> Result<()> { async fn _update_filter_list(&mut self) -> Result<()> {
trace!("Updating Relay filter list"); trace!("Updating Relay filter list");
// Get the current relay state. if self.device_session.relay.is_none() {
let mut relay = if let Some(r) = &self.device_session.relay { self.device_session.relay = Some(internal::Relay::default());
r.clone() }
} else {
internal::Relay::default() // Get a copy of the current relay state.
}; let relay = self.device_session.relay.as_ref().unwrap().clone();
// Get devices that must be configured on the relay. // Get devices that must be configured on the relay.
let relay_devices = relay::list_devices( let relay_devices = relay::list_devices(
@ -1669,22 +1669,52 @@ impl Data {
) )
.await?; .await?;
// We filter out the devices that are no longer configured on the relay. // Get DevEUIs of Relay EDs.
// This way we can combine the delete + add by just overwriting an old slot.
// Note that index 0 has a special meaning.
let relay_devices_dev_euis: Vec<Vec<u8>> = let relay_devices_dev_euis: Vec<Vec<u8>> =
relay_devices.iter().map(|d| d.dev_eui.to_vec()).collect(); relay_devices.iter().map(|d| d.dev_eui.to_vec()).collect();
relay
// Calculate removed slots.
let removed_slots: Vec<u32> = relay
.filters .filters
.retain(|f| f.index == 0 || relay_devices_dev_euis.contains(&f.dev_eui)); .iter()
.filter(|f| f.index != 0 && !relay_devices_dev_euis.contains(&f.dev_eui))
.map(|f| f.index)
.collect();
// Calculate free slots. // Calculate free slots.
// Note that the first slot is used as "catch-all" filter. // Note that the first slot is used as "catch-all" filter.
let used_slots: Vec<u32> = relay.filters.iter().map(|f| f.index).collect(); let used_slots: Vec<u32> = relay
.filters
.iter()
.filter(|f| f.index == 0 || relay_devices_dev_euis.contains(&f.dev_eui))
.map(|f| f.index)
.collect();
let free_slots: Vec<u32> = (1..15).filter(|x| !used_slots.contains(x)).collect(); let free_slots: Vec<u32> = (1..15).filter(|x| !used_slots.contains(x)).collect();
// Update device-session. // Unset slots of devices that are no longer configured.
self.device_session.relay = Some(relay); if !removed_slots.is_empty() {
let mut commands: Vec<lrwn::MACCommand> = Vec::new();
for slot in removed_slots {
commands.push(lrwn::MACCommand::FilterListReq(
lrwn::FilterListReqPayload {
filter_list_idx: slot as u8,
filter_list_action: lrwn::FilterListAction::NoRule,
filter_list_eui: vec![],
},
));
if commands.len() > 5 {
commands.drain(5..);
}
}
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.
return Ok(());
}
// Make sure the first item contains the "catch-all" filter. // Make sure the first item contains the "catch-all" filter.
// This is needed to make sure that only the rest of the filter items are allowed to join // This is needed to make sure that only the rest of the filter items are allowed to join
@ -3230,6 +3260,59 @@ mod test {
..Default::default() ..Default::default()
}, },
}, },
Test {
name: "remove filter".into(),
device_session: internal::DeviceSession {
relay: Some(internal::Relay {
filters: vec![
internal::RelayFilter {
index: 0,
action: 2,
provisioned: true,
..Default::default()
},
internal::RelayFilter {
index: 1,
action: 1,
dev_eui: vec![2, 2, 2, 2, 2, 2, 2, 0],
join_eui: vec![2, 2, 2, 2, 2, 2, 2, 1],
provisioned: true,
},
],
..Default::default()
}),
..Default::default()
},
relay_devices: vec![],
expected_mac_commands: vec![lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::FilterListReq(lrwn::FilterListReqPayload {
filter_list_idx: 1,
filter_list_action: lrwn::FilterListAction::NoRule,
filter_list_eui: vec![],
}),
])],
expected_device_session: internal::DeviceSession {
relay: Some(internal::Relay {
filters: vec![
internal::RelayFilter {
index: 0,
action: 2,
provisioned: true,
..Default::default()
},
internal::RelayFilter {
index: 1,
action: 1,
dev_eui: vec![2, 2, 2, 2, 2, 2, 2, 0],
join_eui: vec![2, 2, 2, 2, 2, 2, 2, 1],
provisioned: true,
},
],
..Default::default()
}),
..Default::default()
},
},
]; ];
let _guard = test::prepare().await; let _guard = test::prepare().await;

View File

@ -44,6 +44,13 @@ pub fn handle(
f.provisioned = true; f.provisioned = true;
} }
} }
// If the action was NoRule, we remove the filter from the relay filters.
if req_pl.filter_list_action == lrwn::FilterListAction::NoRule {
relay
.filters
.retain(|f| f.index != req_pl.filter_list_idx as u32);
}
} }
} else { } else {
error!( error!(
@ -168,6 +175,42 @@ mod test {
}, },
expected_error: None, expected_error: None,
}, },
Test {
name: "acked removing filter".into(),
device_session: internal::DeviceSession {
relay: Some(internal::Relay {
filters: vec![internal::RelayFilter {
index: 1,
provisioned: true,
..Default::default()
}],
..Default::default()
}),
..Default::default()
},
filter_list_req: Some(lrwn::MACCommandSet::new(vec![
lrwn::MACCommand::FilterListReq(lrwn::FilterListReqPayload {
filter_list_idx: 1,
filter_list_action: lrwn::FilterListAction::NoRule,
filter_list_eui: vec![],
}),
])),
filter_list_ans: lrwn::MACCommandSet::new(vec![lrwn::MACCommand::FilterListAns(
lrwn::FilterListAnsPayload {
filter_list_action_ack: true,
filter_list_len_ack: true,
combined_rules_ack: true,
},
)]),
expected_device_session: internal::DeviceSession {
relay: Some(internal::Relay {
filters: vec![],
..Default::default()
}),
..Default::default()
},
expected_error: None,
},
]; ];
for tst in &tests { for tst in &tests {