1use std::ops::{Deref, DerefMut};
2use std::sync::Mutex;
3use std::time::Duration;
4use std::{collections::HashMap, sync::Arc, time::Instant};
5
6use futures::future::join_all;
7use tokio::task::JoinHandle;
8use zencan_common::constants::object_ids::{
9 RPDO_COMM_BASE, RPDO_MAP_BASE, TPDO_COMM_BASE, TPDO_MAP_BASE,
10};
11use zencan_common::lss::{LssIdentity, LssState};
12use zencan_common::messages::{NmtCommand, NmtCommandSpecifier, SyncObject, ZencanMessage};
13use zencan_common::nmt::NmtState;
14use zencan_common::node_id::ConfiguredNodeId;
15use zencan_common::pdo::PdoCommParameter;
16use zencan_common::sdo::AbortCode;
17use zencan_common::{
18 node_configuration::PdoConfig,
19 pdo::PdoMapping,
20 traits::{AsyncCanReceiver, AsyncCanSender},
21 CanId, NodeId,
22};
23
24use super::shared_sender::SharedSender;
25use crate::sdo_client::{SdoClient, SdoClientError};
26use crate::{LssError, LssMaster, RawAbortCode};
27
28use super::shared_receiver::{SharedReceiver, SharedReceiverChannel};
29
30#[derive(Debug, Clone)]
31pub struct NodeInfo {
32 pub node_id: u8,
33 pub identity: Option<LssIdentity>,
34 pub device_name: Option<String>,
35 pub software_version: Option<String>,
36 pub hardware_version: Option<String>,
37 pub last_seen: Instant,
38 pub nmt_state: Option<NmtState>,
39}
40
41impl core::fmt::Display for NodeInfo {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 writeln!(
44 f,
45 "Node {}: {}",
46 self.node_id,
47 self.nmt_state
48 .map(|s| s.to_string())
49 .unwrap_or("Unknown State".into())
50 )?;
51 match self.identity {
52 Some(id) => writeln!(
53 f,
54 " Identity vendor: {:X}, product: {:X}, revision: {:X}, serial: {:X}",
55 id.vendor_id, id.product_code, id.revision, id.serial
56 )?,
57 None => writeln!(f, " Identity: Unknown")?,
58 }
59 writeln!(
60 f,
61 " Device Name: '{}'",
62 self.device_name.as_deref().unwrap_or("Unknown")
63 )?;
64 writeln!(
65 f,
66 " Versions: '{}' SW, '{}' HW",
67 self.software_version.as_deref().unwrap_or("Unknown"),
68 self.hardware_version.as_deref().unwrap_or("Unknown")
69 )?;
70 let age = Instant::now().duration_since(self.last_seen);
71 writeln!(f, " Last Seen: {}s ago", age.as_secs())?;
72
73 Ok(())
74 }
75}
76
77impl NodeInfo {
78 pub fn new(node_id: u8) -> Self {
79 Self {
80 node_id,
81 last_seen: Instant::now(),
82 device_name: None,
83 identity: None,
84 software_version: None,
85 hardware_version: None,
86 nmt_state: None,
87 }
88 }
89
90 pub fn update(&mut self, info: &NodeInfo) {
92 if info.device_name.is_some() {
93 self.device_name = info.device_name.clone();
94 }
95 if info.identity.is_some() {
96 self.identity = info.identity;
97 }
98 if info.software_version.is_some() {
99 self.software_version = info.software_version.clone();
100 }
101 if info.hardware_version.is_some() {
102 self.hardware_version = info.hardware_version.clone();
103 }
104 if info.nmt_state.is_some() {
105 self.nmt_state = info.nmt_state;
106 }
107 self.last_seen = Instant::now();
108 }
109}
110
111async fn scan_node<S: AsyncCanSender + Sync + Send>(
112 node_id: u8,
113 clients: &SdoClientMutex<S>,
114) -> Result<Option<NodeInfo>, SdoClientError> {
115 let mut sdo_client = clients.lock(node_id);
116 log::info!("Scanning Node {node_id}");
117
118 let identity = match sdo_client.read_identity().await {
119 Ok(id) => Some(id),
120 Err(SdoClientError::NoResponse) => {
122 log::info!("No response from node {node_id}");
123 return Ok(None);
124 }
125 Err(e) => return Err(e),
126 };
127 let device_name = match sdo_client.read_device_name().await {
128 Ok(s) => Some(s),
129 Err(e) => {
130 log::error!("SDO Abort Response scanning node {node_id} device name: {e:?}");
131 None
132 }
133 };
134 let software_version = match sdo_client.read_software_version().await {
135 Ok(s) => Some(s),
136 Err(e) => {
137 log::error!("SDO Abort Response scanning node {node_id} SW version: {e:?}");
138 None
139 }
140 };
141 let hardware_version = match sdo_client.read_hardware_version().await {
142 Ok(s) => Some(s),
143 Err(e) => {
144 log::error!("SDO Abort Response scanning node {node_id} HW version: {e:?}");
145 None
146 }
147 };
148 Ok(Some(NodeInfo {
149 node_id,
150 identity,
151 device_name,
152 software_version,
153 hardware_version,
154 nmt_state: None,
155 last_seen: Instant::now(),
156 }))
157}
158
159#[derive(Clone, Debug)]
161pub struct PdoScanResult {
162 pub tpdos: Vec<PdoConfig>,
164 pub rpdos: Vec<PdoConfig>,
166}
167
168async fn read_pdos<S: AsyncCanSender + Sync + Send, R: AsyncCanReceiver>(
169 mut comm_base: u16,
170 mut mapping_base: u16,
171 client: &mut SdoClient<S, R>,
172) -> Result<Vec<PdoConfig>, SdoClientError> {
173 let mut result = Vec::new();
174
175 loop {
176 let _comm_max_sub = match client.read_u8(comm_base, 0).await {
177 Ok(val) => val,
178 Err(SdoClientError::ServerAbort {
180 index: _,
181 sub: _,
182 abort_code: RawAbortCode::Valid(AbortCode::NoSuchObject),
183 }) => break,
184 Err(e) => {
186 return Err(e);
187 }
188 };
189
190 let cob_value = client.read_u32(comm_base, 1).await?;
191 let transmission_type = client.read_u8(comm_base, 2).await?;
192
193 let frame = (cob_value & (1 << 29)) != 0;
194 let rtr_disabled = (cob_value & (1 << 30)) != 0;
195 let valid = (cob_value & (1 << 31)) == 0;
196
197 let cob_id = cob_value & 0x1FFFFFFF;
198 let cob_id = if frame {
199 CanId::extended(cob_id)
200 } else {
201 CanId::std((cob_id & 0x7ff) as u16)
202 };
203 let num_mappings = client.read_u8(mapping_base, 0).await?;
204 let mut mappings = Vec::new();
205 for i in 0..num_mappings {
206 let map_param = client.read_u32(mapping_base, i + 1).await?;
207 mappings.push(PdoMapping::from_object_value(map_param));
208 }
209
210 result.push(PdoConfig {
211 comm: PdoCommParameter {
212 cob_id,
213 valid,
214 rtr_disabled,
215 transmission_type,
216 },
217 mappings,
218 });
219 comm_base += 1;
220 mapping_base += 1;
221 }
222 Ok(result)
223}
224
225async fn read_rpdo_config<S: AsyncCanSender + Sync + Send, R: AsyncCanReceiver>(
226 client: &mut SdoClient<S, R>,
227) -> Result<Vec<PdoConfig>, SdoClientError> {
228 read_pdos(RPDO_COMM_BASE, RPDO_MAP_BASE, client).await
229}
230
231async fn read_tpdo_config<S: AsyncCanSender + Sync + Send, R: AsyncCanReceiver>(
232 client: &mut SdoClient<S, R>,
233) -> Result<Vec<PdoConfig>, SdoClientError> {
234 read_pdos(TPDO_COMM_BASE, TPDO_MAP_BASE, client).await
235}
236
237#[derive(Debug)]
238pub struct SdoClientGuard<'a, S, R>
239where
240 S: AsyncCanSender,
241 R: AsyncCanReceiver,
242{
243 _guard: std::sync::MutexGuard<'a, ()>,
244 client: SdoClient<S, R>,
245}
246
247impl<S, R> Deref for SdoClientGuard<'_, S, R>
248where
249 S: AsyncCanSender,
250 R: AsyncCanReceiver,
251{
252 type Target = SdoClient<S, R>;
253
254 fn deref(&self) -> &Self::Target {
255 &self.client
256 }
257}
258
259impl<S, R> DerefMut for SdoClientGuard<'_, S, R>
260where
261 S: AsyncCanSender,
262 R: AsyncCanReceiver,
263{
264 fn deref_mut(&mut self) -> &mut Self::Target {
265 &mut self.client
266 }
267}
268
269#[derive(Debug)]
270struct SdoClientMutex<S>
271where
272 S: AsyncCanSender + Sync,
273{
274 sender: SharedSender<S>,
275 receiver: SharedReceiver,
276 clients: HashMap<u8, Mutex<()>>,
277}
278
279impl<S> SdoClientMutex<S>
280where
281 S: AsyncCanSender + Sync,
282{
283 pub fn new(sender: SharedSender<S>, receiver: SharedReceiver) -> Self {
284 let mut clients = HashMap::new();
285 for i in 0u8..128 {
286 clients.insert(i, Mutex::new(()));
287 }
288
289 Self {
290 sender,
291 receiver,
292 clients,
293 }
294 }
295
296 pub fn lock(&self, id: u8) -> SdoClientGuard<'_, SharedSender<S>, SharedReceiverChannel> {
297 if !(1..=127).contains(&id) {
298 panic!("ID {} out of range", id);
299 }
300 let guard = self.clients.get(&id).unwrap().lock().unwrap();
301 let client = SdoClient::new_std(id, self.sender.clone(), self.receiver.create_rx());
302 SdoClientGuard {
303 _guard: guard,
304 client,
305 }
306 }
307}
308
309#[derive(Debug)]
311pub struct BusManager<S: AsyncCanSender + Sync + Send> {
312 sender: SharedSender<S>,
313 receiver: SharedReceiver,
314 nodes: Arc<tokio::sync::Mutex<HashMap<u8, NodeInfo>>>,
315 sdo_clients: SdoClientMutex<S>,
316 _monitor_task: JoinHandle<()>,
317}
318
319impl<S: AsyncCanSender + Sync + Send> BusManager<S> {
320 pub fn new(sender: S, receiver: impl AsyncCanReceiver + Sync + 'static) -> Self {
330 let receiver = SharedReceiver::new(receiver);
331 let sender = SharedSender::new(Arc::new(tokio::sync::Mutex::new(sender)));
332 let sdo_clients = SdoClientMutex::new(sender.clone(), receiver.clone());
333
334 let mut state_rx = receiver.create_rx();
335 let nodes = Arc::new(tokio::sync::Mutex::new(HashMap::new()));
336
337 let monitor_task = {
338 let nodes = nodes.clone();
339 tokio::spawn(async move {
340 loop {
341 if let Ok(msg) = state_rx.recv().await {
342 if let Ok(ZencanMessage::Heartbeat(heartbeat)) =
343 ZencanMessage::try_from(msg)
344 {
345 let id_num = heartbeat.node;
346 if let Ok(node_id) = NodeId::try_from(id_num) {
347 let mut nodes = nodes.lock().await;
348 if let std::collections::hash_map::Entry::Vacant(e) =
349 nodes.entry(id_num)
350 {
351 e.insert(NodeInfo::new(node_id.raw()));
352 } else {
353 let node = nodes.get_mut(&id_num).unwrap();
354 node.nmt_state = Some(heartbeat.state);
355 node.last_seen = Instant::now();
356 }
357 } else {
358 log::warn!("Invalid heartbeat node ID {id_num} received");
359 }
360 }
361 }
362 }
363 })
364 };
365
366 Self {
367 sender,
368 receiver,
369 sdo_clients,
370 nodes,
371 _monitor_task: monitor_task,
372 }
373 }
374
375 pub fn sdo_client(
380 &self,
381 node_id: u8,
382 ) -> SdoClientGuard<'_, SharedSender<S>, SharedReceiverChannel> {
383 self.sdo_clients.lock(node_id)
384 }
385
386 pub async fn node_list(&self) -> Vec<NodeInfo> {
388 let node_map = self.nodes.lock().await;
389 let mut nodes = Vec::with_capacity(node_map.len());
390 for n in node_map.values() {
391 nodes.push(n.clone());
392 }
393
394 nodes.sort_by_key(|n| n.node_id);
395 nodes
396 }
397
398 pub async fn scan_nodes(&mut self) -> Result<Vec<NodeInfo>, SdoClientError> {
406 const N_PARALLEL: usize = 10;
407
408 let ids = Vec::from_iter(1..128u8);
409 let mut nodes: Vec<NodeInfo> = Vec::new();
410
411 let mut chunks = Vec::new();
412 for chunk in ids.chunks(128 / N_PARALLEL) {
413 chunks.push(Vec::from_iter(chunk.iter().cloned()));
414 }
415
416 let mut futures = Vec::new();
417
418 for block in chunks {
419 futures.push(async {
420 let mut block_nodes = Vec::new();
421 for id in block {
422 block_nodes.push(scan_node(id, &self.sdo_clients).await);
423 }
424 block_nodes
425 });
426 }
427
428 let results = join_all(futures).await;
430 for r in results {
431 let r: Result<Vec<Option<NodeInfo>>, SdoClientError> = r.into_iter().collect();
432 nodes.extend(r?.into_iter().flatten());
433 }
434
435 let mut node_map = self.nodes.lock().await;
436 for n in &nodes {
438 if let std::collections::hash_map::Entry::Vacant(e) = node_map.entry(n.node_id) {
439 e.insert(n.clone());
440 } else {
441 node_map.get_mut(&n.node_id).unwrap().update(n);
442 }
443 }
444
445 Ok(nodes
450 .iter()
451 .map(|n| node_map.get(&n.node_id).unwrap().clone())
452 .collect())
453 }
454
455 pub async fn lss_fastscan(&mut self, timeout: Duration) -> Vec<LssIdentity> {
464 let mut devices = Vec::new();
465 let mut lss = LssMaster::new(self.sender.clone(), self.receiver.create_rx());
466
467 lss.set_global_mode(LssState::Waiting).await;
469
470 while let Some(id) = lss.fast_scan(timeout).await {
473 devices.push(id);
474 }
475
476 lss.set_global_mode(LssState::Waiting).await;
477
478 devices
479 }
480
481 pub async fn lss_activate(&mut self, ident: LssIdentity) -> Result<(), LssError> {
490 let mut lss = LssMaster::new(self.sender.clone(), self.receiver.create_rx());
491 lss.set_global_mode(LssState::Waiting).await;
492 lss.enter_config_by_identity(
493 ident.vendor_id,
494 ident.product_code,
495 ident.revision,
496 ident.serial,
497 )
498 .await
499 }
500
501 pub async fn lss_set_node_id(&mut self, node_id: NodeId) -> Result<(), LssError> {
506 let mut lss = LssMaster::new(self.sender.clone(), self.receiver.create_rx());
507 lss.set_node_id(node_id).await?;
508 Ok(())
509 }
510
511 pub async fn lss_store_config(&mut self) -> Result<(), LssError> {
516 let mut lss = LssMaster::new(self.sender.clone(), self.receiver.create_rx());
517 lss.store_config().await
518 }
519
520 pub async fn lss_set_global_mode(&mut self, mode: LssState) {
522 let mut lss = LssMaster::new(self.sender.clone(), self.receiver.create_rx());
523 lss.set_global_mode(mode).await;
524 }
525
526 pub async fn nmt_reset_app(&mut self, node: u8) {
530 self.send_nmt_cmd(NmtCommandSpecifier::ResetApp, node).await
531 }
532
533 pub async fn nmt_reset_comms(&mut self, node: u8) {
537 self.send_nmt_cmd(NmtCommandSpecifier::ResetComm, node)
538 .await
539 }
540
541 pub async fn nmt_start(&mut self, node: u8) {
545 self.send_nmt_cmd(NmtCommandSpecifier::Start, node).await
546 }
547
548 pub async fn nmt_stop(&mut self, node: u8) {
552 self.send_nmt_cmd(NmtCommandSpecifier::Stop, node).await
553 }
554
555 pub async fn sync(&mut self, count: Option<u8>) {
557 let sync_obj = SyncObject::new(count);
558 self.sender.send(sync_obj.into()).await.ok();
559 }
560
561 async fn send_nmt_cmd(&mut self, cmd: NmtCommandSpecifier, node: u8) {
562 let message = NmtCommand { cs: cmd, node };
563 self.sender.send(message.into()).await.ok();
564 }
565
566 pub async fn read_pdo_config(
570 &mut self,
571 node: ConfiguredNodeId,
572 ) -> Result<PdoScanResult, SdoClientError> {
573 let mut client = self.sdo_client(node.raw());
574
575 let tpdos = read_tpdo_config(&mut client).await?;
576 let rpdos = read_rpdo_config(&mut client).await?;
577
578 Ok(PdoScanResult { tpdos, rpdos })
579 }
580}