use std::collections::BTreeMap;
use std::io;
use std::time::Duration;
use apis_saltans_core::{Endpoint, Profile};
use apis_saltans_hw::{Frame, Metadata, NcpDriver};
use log::debug;
use macaddr::MacAddr8;
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::sync::oneshot::channel;
use tokio::task::{JoinError, JoinHandle};
use self::builder::Builder;
use self::event_handler::Message;
use crate::ember::message::Destination;
use crate::ember::{aps, concentrator};
use crate::error::Status;
use crate::ezsp::network::scan;
use crate::types::ByteSizedVec;
use crate::{Callback, Configuration, Error, Messaging, Networking, Security, Utilities, ember};
mod builder;
mod event_handler;
#[derive(Debug)]
pub struct EzspNetworkManager<T> {
transport: T,
profile: Profile,
aps_options: aps::Options,
message_tag: u8,
aps_seq: u8,
transaction_seq: u8,
event_handler_proxy: Sender<Message>,
event_handler_handle: JoinHandle<()>,
}
impl<T> EzspNetworkManager<T> {
#[must_use]
pub(crate) const fn new(
transport: T,
profile: Profile,
aps_options: aps::Options,
event_handler_proxy: Sender<Message>,
event_handler_handle: JoinHandle<()>,
) -> Self {
Self {
transport,
profile,
aps_options,
message_tag: 0,
aps_seq: 0,
transaction_seq: 0,
event_handler_proxy,
event_handler_handle,
}
}
#[must_use]
pub const fn build(transport: T, callbacks: Receiver<Callback>) -> Builder<T> {
Builder::new(transport, callbacks)
}
const fn next_message_tag(&mut self) -> u8 {
let tag = self.message_tag;
self.message_tag = self.message_tag.wrapping_add(1);
tag
}
const fn next_aps_seq(&mut self) -> u8 {
let seq = self.aps_seq;
self.aps_seq = self.aps_seq.wrapping_add(1);
seq
}
fn next_aps_frame(
&mut self,
aps_metadata: &Metadata,
destination_endpoint: Endpoint,
group_id: u16,
) -> aps::Frame {
aps::Frame::new(
aps_metadata.profile().unwrap_or(self.profile).into(),
aps_metadata.cluster_id(),
aps_metadata.source_endpoint().map_or(0x01, u8::from),
destination_endpoint.into(),
self.aps_options,
group_id,
self.next_aps_seq(),
)
}
pub async fn terminate(self) -> Result<T, JoinError> {
self.event_handler_proxy
.send(Message::Terminate)
.await
.expect("Failed to send terminate message to message handler actor");
self.event_handler_handle.await.map(|()| self.transport)
}
}
#[cfg(feature = "ashv2")]
impl EzspNetworkManager<crate::uart::Uart> {
pub fn ashv2<P>(serial_port: P) -> Result<Builder<crate::uart::Uart>, Error>
where
P: ashv2::SerialPort + ashv2::TryCloneNative + Sync + 'static,
{
Builder::ashv2(serial_port)
}
}
impl<T> NcpDriver for EzspNetworkManager<T>
where
T: Configuration + Security + Messaging + Networking + Utilities + Send + Sync,
{
fn next_transaction_seq(&mut self) -> u8 {
let seq = self.transaction_seq;
self.transaction_seq = self.transaction_seq.wrapping_add(1);
seq
}
async fn get_pan_id(&mut self) -> Result<u16, apis_saltans_hw::Error> {
Ok(self.transport.get_node_id().await?)
}
async fn get_ieee_address(&mut self) -> Result<MacAddr8, apis_saltans_hw::Error> {
Ok(self.transport.get_eui64().await?)
}
async fn scan_networks(
&mut self,
channel_mask: u32,
duration: u8,
) -> Result<Vec<apis_saltans_hw::FoundNetwork>, apis_saltans_hw::Error> {
let (tx, rx) = channel();
self.event_handler_proxy.send(tx.into()).await?;
self.transport
.start_scan(scan::Type::ActiveScan, channel_mask, duration)
.await?;
Ok(rx.await?)
}
async fn scan_channels(
&mut self,
channel_mask: u32,
duration: u8,
) -> Result<Vec<apis_saltans_hw::ScannedChannel>, apis_saltans_hw::Error> {
let (tx, rx) = channel();
self.event_handler_proxy.send(tx.into()).await?;
self.transport
.start_scan(scan::Type::EnergyScan, channel_mask, duration)
.await?;
Ok(rx.await?)
}
async fn allow_joins(
&mut self,
duration: Duration,
) -> Result<Duration, apis_saltans_hw::Error> {
let seconds = u8::try_from(duration.as_secs()).unwrap_or(u8::MAX);
self.transport.permit_joining(seconds.into()).await?;
Ok(Duration::from_secs(u64::from(seconds)))
}
async fn get_neighbors(&mut self) -> Result<BTreeMap<MacAddr8, u16>, apis_saltans_hw::Error> {
let mut neighbors = BTreeMap::new();
for index in 0..=u8::MAX {
match self.transport.get_neighbor(index).await {
Ok(neighbor) => {
neighbors.insert(neighbor.long_id(), neighbor.short_id());
}
Err(error) => match error {
Error::Status(Status::Ember(Ok(ember::Status::ErrFatal))) => break,
error => return Err(error.into()),
},
}
}
Ok(neighbors)
}
async fn route_request(&mut self, radius: u8) -> Result<(), apis_saltans_hw::Error> {
Ok(self
.transport
.send_many_to_one_route_request(concentrator::Type::HighRam, radius)
.await?)
}
async fn short_id_to_ieee_address(
&mut self,
short_id: u16,
) -> Result<MacAddr8, apis_saltans_hw::Error> {
Ok(self.transport.lookup_eui64_by_node_id(short_id).await?)
}
async fn ieee_address_to_short_id(
&mut self,
ieee_address: MacAddr8,
) -> Result<u16, apis_saltans_hw::Error> {
Ok(self.transport.lookup_node_id_by_eui64(ieee_address).await?)
}
async fn unicast(
&mut self,
short_id: u16,
destination_endpoint: Endpoint,
frame: Frame,
) -> Result<u8, apis_saltans_hw::Error> {
let (aps_metadata, payload) = frame.into_parts();
let tag = self.next_message_tag();
let message = ByteSizedVec::from_slice(&payload)
.map_err(io::Error::other)
.map_err(Error::from)?;
let aps_frame = self.next_aps_frame(&aps_metadata, destination_endpoint, 0x0000);
let destination = Destination::Direct(short_id);
debug!(
"Sending unicast to: {destination:?}, APS Frame: {aps_frame}, Tag: {tag:#04X}, Message: {:#04X?}",
message.as_slice()
);
let (tx, rx) = channel();
self.event_handler_proxy
.send(Message::Sent { tag, sender: tx })
.await?;
let seq = self
.transport
.send_unicast(destination, aps_frame, tag, message)
.await?;
match rx.await? {
Ok(ember::Status::Success) => Ok(seq),
other => Err(Error::Status(Status::Ember(other)).into()),
}
}
async fn multicast(
&mut self,
group_id: u16,
hops: u8,
radius: u8,
frame: Frame,
) -> Result<u8, apis_saltans_hw::Error> {
let (aps_metadata, payload) = frame.into_parts();
let tag = self.next_message_tag();
let message = ByteSizedVec::from_slice(&payload)
.map_err(io::Error::other)
.map_err(Error::from)?;
let aps_frame = self.next_aps_frame(&aps_metadata, Endpoint::Data, group_id);
debug!(
"Sending multicast: Hops: {hops}, Radius: {radius:#04X}, APS Frame: {aps_frame}, Tag: {tag:#04X}, Message: {:#04X?}",
message.as_slice()
);
let (tx, rx) = channel();
self.event_handler_proxy
.send(Message::Sent { tag, sender: tx })
.await?;
let seq = self
.transport
.send_multicast(aps_frame, hops, radius, tag, message)
.await?;
match rx.await? {
Ok(ember::Status::Success) => Ok(seq),
other => Err(Error::Status(Status::Ember(other)).into()),
}
}
async fn broadcast(
&mut self,
short_id: u16,
radius: u8,
frame: Frame,
) -> Result<u8, apis_saltans_hw::Error> {
let (aps_metadata, payload) = frame.into_parts();
let tag = self.next_message_tag();
let message = ByteSizedVec::from_slice(&payload)
.map_err(io::Error::other)
.map_err(Error::from)?;
let aps_frame = self.next_aps_frame(&aps_metadata, Endpoint::Broadcast, 0x0000);
debug!(
"Sending broadcast to: {short_id:#06X}, Radius: {radius:#04X}, APS Frame: {aps_frame}, Tag: {tag:#04X}, Message: {:#04X?}",
message.as_slice()
);
let (tx, rx) = channel();
self.event_handler_proxy
.send(Message::Sent { tag, sender: tx })
.await?;
let seq = self
.transport
.send_broadcast(short_id, aps_frame, radius, tag, message)
.await?;
match rx.await? {
Ok(ember::Status::Success) => Ok(seq),
other => Err(Error::Status(Status::Ember(other)).into()),
}
}
}