#![allow(dead_code)]
use core::ops::Not;
use basemgt::ApsmeAddGroupConfirm;
use basemgt::ApsmeAddGroupRequest;
use basemgt::ApsmeBindConfirm;
use basemgt::ApsmeBindRequest;
use basemgt::ApsmeBindRequestStatus;
use basemgt::ApsmeGetConfirm;
use basemgt::ApsmeGetConfirmStatus;
use basemgt::ApsmeRemoveAllGroupsConfirm;
use basemgt::ApsmeRemoveAllGroupsRequest;
use basemgt::ApsmeRemoveGroupConfirm;
use basemgt::ApsmeRemoveGroupRequest;
use basemgt::ApsmeSetConfirm;
use basemgt::ApsmeUnbindConfirm;
use basemgt::ApsmeUnbindRequest;
use basemgt::ApsmeUnbindRequestStatus;
use super::aib::AIBAttribute;
use super::aib::ApsInformationBase;
use super::binding::ApsBindingTable;
use super::types::Address;
use crate::nwk::nlme::management::NlmeJoinRequest;
use crate::nwk::nlme::management::NlmeJoinStatus;
use crate::nwk::nlme::management::NlmeNetworkDiscoveryRequest;
use crate::nwk::nlme::Nlme;
use crate::nwk::nlme::NlmeSap;
pub mod basemgt;
pub mod groupmgt;
pub trait ApsmeSap {
fn bind_request(&mut self, request: ApsmeBindRequest) -> ApsmeBindConfirm;
fn unbind_request(&mut self, request: ApsmeUnbindRequest) -> ApsmeUnbindConfirm;
fn get(&self, attribute: u8) -> ApsmeGetConfirm;
fn set(&mut self, attribute: AIBAttribute) -> ApsmeSetConfirm;
fn add_group(&self, request: ApsmeAddGroupRequest) -> ApsmeAddGroupConfirm;
fn remove_group(&self, request: ApsmeRemoveGroupRequest) -> ApsmeRemoveGroupConfirm;
fn remove_all_groups(
&self,
request: ApsmeRemoveAllGroupsRequest,
) -> ApsmeRemoveAllGroupsConfirm;
}
pub(crate) struct Apsme {
pub(crate) supports_binding_table: bool,
pub(crate) binding_table: ApsBindingTable,
pub(crate) joined_network: Option<Address>,
pub(crate) aib: ApsInformationBase,
pub(crate) nwk: Nlme,
}
impl Apsme {
pub(crate) fn new() -> Self {
Self {
supports_binding_table: true,
binding_table: ApsBindingTable::new(),
joined_network: None,
aib: ApsInformationBase::new(),
nwk: Nlme::new(),
}
}
fn is_joined(&self) -> bool {
self.joined_network.is_some()
}
pub(crate) fn start_network_discovery(&self) {
let request = NlmeNetworkDiscoveryRequest {
scan_channels_list_structure: [0, 0, 0, 0, 0, 0, 0, 0],
scan_duration: 10u8,
};
let confirm = self.nwk.network_discovery(request);
match confirm.status {
crate::nwk::nlme::management::NlmeNetworkDiscoveryStatus::Successful => {
}
}
}
pub(crate) fn join_network(&self) {
let request = NlmeJoinRequest {
extended_pan_id: 0x0015_8D00_01AB_CD12,
rejoin_network: 0u8,
scan_duration: 10u8,
security_enabled: false,
};
let confirm = self.nwk.join(request);
if let NlmeJoinStatus::Success = confirm.status {
} else {
}
}
}
impl ApsmeSap for Apsme {
fn bind_request(&mut self, request: ApsmeBindRequest) -> ApsmeBindConfirm {
let status = if !self.is_joined() || !self.supports_binding_table {
ApsmeBindRequestStatus::IllegalRequest
} else if self.binding_table.is_full() {
ApsmeBindRequestStatus::TableFull
} else {
match self.binding_table.create_binding_link(&request) {
Ok(_) => ApsmeBindRequestStatus::Success,
Err(_) => ApsmeBindRequestStatus::IllegalRequest,
}
};
ApsmeBindConfirm {
status,
src_address: request.src_address,
src_endpoint: request.src_endpoint,
cluster_id: request.cluster_id,
dst_addr_mode: request.dst_addr_mode,
dst_address: request.dst_address,
dst_endpoint: request.dst_endpoint,
}
}
fn unbind_request(&mut self, request: ApsmeUnbindRequest) -> ApsmeUnbindConfirm {
let status = if self.is_joined().not() {
ApsmeUnbindRequestStatus::IllegalRequest
} else {
let res = self.binding_table.remove_binding_link(&request);
match res {
Ok(_) => ApsmeUnbindRequestStatus::Success,
Err(err) => match err {
crate::aps::binding::BindingError::IllegalRequest => {
ApsmeUnbindRequestStatus::IllegalRequest
}
crate::aps::binding::BindingError::InvalidBinding => {
ApsmeUnbindRequestStatus::InvalidBinding
}
_ => ApsmeUnbindRequestStatus::IllegalRequest,
},
}
};
ApsmeUnbindConfirm {
status,
src_address: request.src_address,
src_endpoint: request.src_endpoint,
cluster_id: request.cluster_id,
dst_addr_mode: request.dst_addr_mode,
dst_address: request.dst_address,
dst_endpoint: request.dst_endpoint,
}
}
fn get(&self, identifier: u8) -> ApsmeGetConfirm {
let attr = self.aib.get_attribute(identifier);
attr.map_or(
ApsmeGetConfirm {
status: ApsmeGetConfirmStatus::UnsupportedAttribute,
attribute: identifier,
attribute_length: 0,
attribute_value: None,
},
|attr| ApsmeGetConfirm {
status: ApsmeGetConfirmStatus::Success,
attribute: attr.id(),
attribute_length: attr.length(),
attribute_value: Some(attr.value()),
},
)
}
fn set(&mut self, attribute: AIBAttribute) -> ApsmeSetConfirm {
let id = attribute.id();
match self.aib.write_attribute_value(id, attribute) {
Ok(_) => ApsmeSetConfirm {
status: basemgt::ApsmeSetConfirmStatus::Success,
identifier: id,
},
Err(_) => todo!(),
}
}
fn add_group(&self, _request: ApsmeAddGroupRequest) -> ApsmeAddGroupConfirm {
ApsmeAddGroupConfirm {}
}
fn remove_group(&self, _request: ApsmeRemoveGroupRequest) -> ApsmeRemoveGroupConfirm {
todo!()
}
fn remove_all_groups(
&self,
_request: ApsmeRemoveAllGroupsRequest,
) -> ApsmeRemoveAllGroupsConfirm {
todo!()
}
}
#[cfg(test)]
mod tests {
use basemgt::ApsmeBindRequestStatus;
use super::*;
use crate::aps::types::SrcEndpoint;
#[test]
fn bind_request_device_does_not_support_binding_should_fail() {
let mut apsme = Apsme::new();
apsme.supports_binding_table = false;
let request = ApsmeBindRequest {
src_address: Address::Extended(0u64),
src_endpoint: SrcEndpoint::new(10).unwrap_or(SrcEndpoint { value: 0 }),
cluster_id: 1u16,
dst_addr_mode: 0u8,
dst_address: 1u8,
dst_endpoint: 2u8,
};
let result = apsme.bind_request(request);
assert_eq!(result.status, ApsmeBindRequestStatus::IllegalRequest);
}
#[test]
fn bind_request_from_an_unjoined_device_should_fail() {
let mut apsme = Apsme::new();
let request = ApsmeBindRequest {
src_address: Address::Extended(0u64),
src_endpoint: SrcEndpoint::new(10).unwrap_or(SrcEndpoint { value: 0 }),
cluster_id: 1u16,
dst_addr_mode: 0u8,
dst_address: 1u8,
dst_endpoint: 2u8,
};
let result = apsme.bind_request(request);
assert_eq!(result.status, ApsmeBindRequestStatus::IllegalRequest);
}
#[test]
fn bind_request_with_full_table_should_fail() {
let mut apsme = Apsme::new();
apsme.joined_network = Some(Address::Extended(10u64));
for n in 0..265u64 {
let request = ApsmeBindRequest {
src_address: Address::Extended(n),
src_endpoint: SrcEndpoint::new(10).unwrap_or(SrcEndpoint { value: 0 }),
cluster_id: 1u16,
dst_addr_mode: 0u8,
dst_address: 1u8,
dst_endpoint: 2u8,
};
let _ = apsme.bind_request(request);
}
let request = ApsmeBindRequest {
src_address: Address::Extended(999u64),
src_endpoint: SrcEndpoint::new(10).unwrap_or(SrcEndpoint { value: 0 }),
cluster_id: 1u16,
dst_addr_mode: 0u8,
dst_address: 1u8,
dst_endpoint: 2u8,
};
let result = apsme.bind_request(request);
assert_eq!(result.status, ApsmeBindRequestStatus::TableFull);
}
#[test]
fn bind_request_with_valid_request_should_succeed() {
let mut apsme = Apsme::new();
apsme.joined_network = Some(Address::Extended(10u64));
let request = ApsmeBindRequest {
src_address: Address::Extended(999u64),
src_endpoint: SrcEndpoint::new(10).unwrap_or(SrcEndpoint { value: 0 }),
cluster_id: 1u16,
dst_addr_mode: 0u8,
dst_address: 1u8,
dst_endpoint: 2u8,
};
let result = apsme.bind_request(request);
assert_eq!(result.status, ApsmeBindRequestStatus::Success);
}
}