use std::time::Instant;
use zencan_common::{
messages::{CanMessage, NmtCommand, NmtCommandSpecifier, ZencanMessage},
nmt::NmtState,
traits::{AsyncCanReceiver, AsyncCanSender},
};
type Result<T> = std::result::Result<T, ()>;
#[derive(Copy, Clone, Debug)]
pub struct Node {
pub id: u8,
pub state: NmtState,
pub last_status: Instant,
last_toggle: bool,
}
impl Default for Node {
fn default() -> Self {
Self {
id: 0,
state: NmtState::Bootup,
last_status: Instant::now(),
last_toggle: true,
}
}
}
const MAX_NODES: usize = 127;
#[derive(Debug)]
pub struct NmtMaster<S, R> {
sender: S,
receiver: R,
nodes: [Node; MAX_NODES],
}
impl<S: AsyncCanSender, R: AsyncCanReceiver> NmtMaster<S, R> {
pub fn new(sender: S, receiver: R) -> Self {
let nodes = [Node::default(); MAX_NODES];
Self {
sender,
receiver,
nodes,
}
}
pub fn process_rx(&mut self) {
while let Some(msg) = self.receiver.try_recv() {
self.handle_message(msg);
}
}
fn handle_message(&mut self, msg: CanMessage) {
let open_msg: ZencanMessage = match msg.try_into() {
Ok(m) => m,
Err(_) => return,
};
if let ZencanMessage::Heartbeat(heartbeat) = open_msg {
self.handle_heartbeat(heartbeat.node, heartbeat.state, heartbeat.toggle)
}
}
pub fn get_nodes(&mut self) -> &[Node] {
self.process_rx();
let n = self
.nodes
.iter()
.position(|n| n.id == 0)
.unwrap_or(MAX_NODES);
&self.nodes[0..n]
}
fn handle_heartbeat(&mut self, node: u8, state: NmtState, toggle: bool) {
for i in 0..self.nodes.len() {
let list_node = &mut self.nodes[i];
if list_node.id == node {
list_node.last_status = Instant::now();
list_node.last_toggle = toggle;
list_node.state = state;
break;
} else if list_node.id == 0 || list_node.id > node {
for j in self.nodes.len() - 1..i {
self.nodes[j] = self.nodes[j - 1];
}
self.nodes[i] = Node {
id: node,
state,
last_status: Instant::now(),
last_toggle: toggle,
};
break;
}
}
}
pub async fn nmt_reset_app(&mut self, node: u8) -> Result<()> {
self.send_nmt_cmd(NmtCommandSpecifier::ResetApp, node).await
}
pub async fn nmt_reset_comms(&mut self, node: u8) -> Result<()> {
self.send_nmt_cmd(NmtCommandSpecifier::ResetComm, node)
.await
}
pub async fn nmt_start(&mut self, node: u8) -> Result<()> {
self.send_nmt_cmd(NmtCommandSpecifier::Start, node).await
}
pub async fn nmt_stop(&mut self, node: u8) -> Result<()> {
self.send_nmt_cmd(NmtCommandSpecifier::Stop, node).await
}
async fn send_nmt_cmd(&mut self, cmd: NmtCommandSpecifier, node: u8) -> Result<()> {
let message = NmtCommand { cs: cmd, node };
self.sender.send(message.into()).await.map_err(|_| ())?;
Ok(())
}
}