1use core::fmt::Debug;
21
22use rs_matter_macros::{FromTLV, ToTLV};
23
24use crate::dm::{ArrayAttributeRead, Dataver, InvokeContext, ReadContext};
25use crate::error::{Error, ErrorCode};
26use crate::tlv::{
27 Nullable, NullableBuilder, Octets, OctetsBuilder, TLVBuilderParent, ToTLVArrayBuilder,
28 ToTLVBuilder, Utf8StrBuilder,
29};
30use crate::with;
31
32pub use crate::dm::clusters::decl::thread_network_diagnostics::*;
33
34use super::wifi_diag::WirelessDiag;
35
36#[derive(Debug, Clone, Eq, PartialEq, Hash, FromTLV, ToTLV)]
38#[cfg_attr(feature = "defmt", derive(defmt::Format))]
39pub struct NeighborTable {
40 pub ext_address: u64,
41 pub age: u32,
42 pub rloc16: u16,
43 pub link_frame_counter: u32,
44 pub mle_frame_counter: u32,
45 pub lqi: u8,
46 pub average_rssi: Option<i8>,
47 pub last_rssi: Option<i8>,
48 pub frame_error_rate: u8,
49 pub message_error_rate: u8,
50 pub rx_on_when_idle: bool,
51 pub full_thread_device: bool,
52 pub full_network_data: bool,
53 pub is_child: bool,
54}
55
56impl NeighborTable {
57 fn read_into<P: TLVBuilderParent>(
59 &self,
60 builder: NeighborTableStructBuilder<P>,
61 ) -> Result<P, Error> {
62 builder
63 .ext_address(self.ext_address)?
64 .age(self.age)?
65 .rloc_16(self.rloc16)?
66 .link_frame_counter(self.link_frame_counter)?
67 .mle_frame_counter(self.mle_frame_counter)?
68 .lqi(self.lqi)?
69 .average_rssi(Nullable::new(self.average_rssi))?
70 .last_rssi(Nullable::new(self.last_rssi))?
71 .frame_error_rate(self.frame_error_rate)?
72 .message_error_rate(self.message_error_rate)?
73 .rx_on_when_idle(self.rx_on_when_idle)?
74 .full_thread_device(self.full_thread_device)?
75 .full_network_data(self.full_network_data)?
76 .is_child(self.is_child)?
77 .end()
78 }
79}
80
81#[derive(Debug, Clone, Eq, PartialEq, Hash, FromTLV, ToTLV)]
83#[cfg_attr(feature = "defmt", derive(defmt::Format))]
84pub struct RouteTable {
85 pub ext_address: u64,
86 pub rloc16: u16,
87 pub router_id: u8,
88 pub next_hop: u8,
89 pub path_cost: u8,
90 pub lqi_in: u8,
91 pub lqi_out: u8,
92 pub age: u8,
93 pub allocated: bool,
94 pub link_established: bool,
95}
96
97impl RouteTable {
98 fn read_into<P: TLVBuilderParent>(
100 &self,
101 builder: RouteTableStructBuilder<P>,
102 ) -> Result<P, Error> {
103 builder
104 .ext_address(self.ext_address)?
105 .rloc_16(self.rloc16)?
106 .router_id(self.router_id)?
107 .next_hop(self.next_hop)?
108 .path_cost(self.path_cost)?
109 .lqi_in(self.lqi_in)?
110 .lqi_out(self.lqi_out)?
111 .age(self.age)?
112 .allocated(self.allocated)?
113 .link_established(self.link_established)?
114 .end()
115 }
116}
117
118#[derive(Debug, Clone, Eq, PartialEq, Hash, FromTLV, ToTLV)]
120#[cfg_attr(feature = "defmt", derive(defmt::Format))]
121pub struct SecurityPolicy {
122 pub rotation_time: u16,
123 pub flags: u16,
124}
125
126#[derive(Debug, Clone, Eq, PartialEq, Hash, FromTLV, ToTLV)]
128#[cfg_attr(feature = "defmt", derive(defmt::Format))]
129pub struct OperationalDatasetComponents {
130 pub active_timestamp_present: bool,
131 pub pending_timestamp_present: bool,
132 pub master_key_present: bool,
133 pub network_name_present: bool,
134 pub extended_pan_id_present: bool,
135 pub mesh_local_prefix_present: bool,
136 pub delay_present: bool,
137 pub pan_id_present: bool,
138 pub channel_present: bool,
139 pub pskc_present: bool,
140 pub security_policy_present: bool,
141 pub channel_mask_present: bool,
142}
143
144impl OperationalDatasetComponents {
145 fn read_into<P: TLVBuilderParent>(
147 &self,
148 builder: OperationalDatasetComponentsBuilder<P>,
149 ) -> Result<P, Error> {
150 builder
151 .active_timestamp_present(self.active_timestamp_present)?
152 .pending_timestamp_present(self.pending_timestamp_present)?
153 .master_key_present(self.master_key_present)?
154 .network_name_present(self.network_name_present)?
155 .extended_pan_id_present(self.extended_pan_id_present)?
156 .mesh_local_prefix_present(self.mesh_local_prefix_present)?
157 .delay_present(self.delay_present)?
158 .pan_id_present(self.pan_id_present)?
159 .channel_present(self.channel_present)?
160 .pskc_present(self.pskc_present)?
161 .security_policy_present(self.security_policy_present)?
162 .channel_mask_present(self.channel_mask_present)?
163 .end()
164 }
165}
166
167pub trait ThreadDiag: WirelessDiag {
172 fn channel(&self) -> Result<Option<u16>, Error> {
173 Ok(None)
174 }
175
176 fn routing_role(&self) -> Result<Option<RoutingRoleEnum>, Error> {
177 Ok(None)
178 }
179
180 fn network_name(
181 &self,
182 f: &mut dyn FnMut(Option<&str>) -> Result<(), Error>,
183 ) -> Result<(), Error> {
184 f(None)
185 }
186
187 fn pan_id(&self) -> Result<Option<u16>, Error> {
188 Ok(None)
189 }
190
191 fn extended_pan_id(&self) -> Result<Option<u64>, Error> {
192 Ok(None)
193 }
194
195 #[allow(clippy::type_complexity)]
196 fn mesh_local_prefix(
197 &self,
198 f: &mut dyn FnMut(Option<&[u8]>) -> Result<(), Error>,
199 ) -> Result<(), Error> {
200 f(None)
201 }
202
203 fn neighbor_table(
204 &self,
205 _f: &mut dyn FnMut(&NeighborTable) -> Result<(), Error>,
206 ) -> Result<(), Error> {
207 Ok(())
208 }
209
210 fn route_table(
211 &self,
212 _f: &mut dyn FnMut(&RouteTable) -> Result<(), Error>,
213 ) -> Result<(), Error> {
214 Ok(())
215 }
216
217 fn partition_id(&self) -> Result<Option<u32>, Error> {
218 Ok(None)
219 }
220
221 fn weighting(&self) -> Result<Option<u16>, Error> {
222 Ok(None)
223 }
224
225 fn data_version(&self) -> Result<Option<u16>, Error> {
226 Ok(None)
227 }
228
229 fn stable_data_version(&self) -> Result<Option<u16>, Error> {
230 Ok(None)
231 }
232
233 fn leader_router_id(&self) -> Result<Option<u8>, Error> {
234 Ok(None)
235 }
236
237 fn ext_address(&self) -> Result<Option<u64>, Error> {
238 Ok(None)
239 }
240
241 fn rloc_16(&self) -> Result<Option<u16>, Error> {
242 Ok(None)
243 }
244
245 fn security_policy(&self) -> Result<Option<SecurityPolicy>, Error> {
246 Ok(None)
247 }
248
249 #[allow(clippy::type_complexity)]
250 fn channel_page0_mask(
251 &self,
252 f: &mut dyn FnMut(Option<&[u8]>) -> Result<(), Error>,
253 ) -> Result<(), Error> {
254 f(None)
255 }
256
257 #[allow(clippy::type_complexity)]
258 fn operational_dataset_components(
259 &self,
260 f: &mut dyn FnMut(Option<&OperationalDatasetComponents>) -> Result<(), Error>,
261 ) -> Result<(), Error> {
262 f(None)
263 }
264
265 #[allow(clippy::type_complexity)]
266 fn active_network_faults_list(
267 &self,
268 _f: &mut dyn FnMut(NetworkFaultEnum) -> Result<(), Error>,
269 ) -> Result<(), Error> {
270 Ok(())
271 }
272}
273
274impl<T> ThreadDiag for &T
275where
276 T: ThreadDiag,
277{
278 fn channel(&self) -> Result<Option<u16>, Error> {
279 (*self).channel()
280 }
281
282 fn routing_role(&self) -> Result<Option<RoutingRoleEnum>, Error> {
283 (*self).routing_role()
284 }
285
286 fn network_name(
287 &self,
288 f: &mut dyn FnMut(Option<&str>) -> Result<(), Error>,
289 ) -> Result<(), Error> {
290 (*self).network_name(f)
291 }
292
293 fn pan_id(&self) -> Result<Option<u16>, Error> {
294 (*self).pan_id()
295 }
296
297 fn extended_pan_id(&self) -> Result<Option<u64>, Error> {
298 (*self).extended_pan_id()
299 }
300
301 fn mesh_local_prefix(
302 &self,
303 f: &mut dyn FnMut(Option<&[u8]>) -> Result<(), Error>,
304 ) -> Result<(), Error> {
305 (*self).mesh_local_prefix(f)
306 }
307
308 fn neighbor_table(
309 &self,
310 f: &mut dyn FnMut(&NeighborTable) -> Result<(), Error>,
311 ) -> Result<(), Error> {
312 (*self).neighbor_table(f)
313 }
314
315 fn route_table(
316 &self,
317 f: &mut dyn FnMut(&RouteTable) -> Result<(), Error>,
318 ) -> Result<(), Error> {
319 (*self).route_table(f)
320 }
321
322 fn partition_id(&self) -> Result<Option<u32>, Error> {
323 (*self).partition_id()
324 }
325
326 fn weighting(&self) -> Result<Option<u16>, Error> {
327 (*self).weighting()
328 }
329
330 fn data_version(&self) -> Result<Option<u16>, Error> {
331 (*self).data_version()
332 }
333
334 fn stable_data_version(&self) -> Result<Option<u16>, Error> {
335 (*self).stable_data_version()
336 }
337
338 fn leader_router_id(&self) -> Result<Option<u8>, Error> {
339 (*self).leader_router_id()
340 }
341
342 fn ext_address(&self) -> Result<Option<u64>, Error> {
343 (*self).ext_address()
344 }
345
346 fn rloc_16(&self) -> Result<Option<u16>, Error> {
347 (*self).rloc_16()
348 }
349
350 fn security_policy(&self) -> Result<Option<SecurityPolicy>, Error> {
351 (*self).security_policy()
352 }
353
354 fn channel_page0_mask(
355 &self,
356 f: &mut dyn FnMut(Option<&[u8]>) -> Result<(), Error>,
357 ) -> Result<(), Error> {
358 (*self).channel_page0_mask(f)
359 }
360
361 fn operational_dataset_components(
362 &self,
363 f: &mut dyn FnMut(Option<&OperationalDatasetComponents>) -> Result<(), Error>,
364 ) -> Result<(), Error> {
365 (*self).operational_dataset_components(f)
366 }
367
368 fn active_network_faults_list(
369 &self,
370 f: &mut dyn FnMut(NetworkFaultEnum) -> Result<(), Error>,
371 ) -> Result<(), Error> {
372 (*self).active_network_faults_list(f)
373 }
374}
375
376impl ThreadDiag for () {}
377
378#[derive(Clone)]
380pub struct ThreadDiagHandler<'a> {
381 dataver: Dataver,
382 diag: &'a dyn ThreadDiag,
383}
384
385impl<'a> ThreadDiagHandler<'a> {
386 pub const fn new(dataver: Dataver, diag: &'a dyn ThreadDiag) -> Self {
388 Self { dataver, diag }
389 }
390
391 pub const fn adapt(self) -> HandlerAdaptor<Self> {
393 HandlerAdaptor(self)
394 }
395}
396
397impl ClusterHandler for ThreadDiagHandler<'_> {
398 const CLUSTER: crate::dm::Cluster<'static> =
399 FULL_CLUSTER.with_attrs(with!(required)).with_cmds(with!());
400
401 fn dataver(&self) -> u32 {
402 self.dataver.get()
403 }
404
405 fn dataver_changed(&self) {
406 self.dataver.changed();
407 }
408
409 fn channel(&self, _ctx: impl ReadContext) -> Result<Nullable<u16>, Error> {
410 Ok(Nullable::new(self.diag.channel()?))
411 }
412
413 fn routing_role(&self, _ctx: impl ReadContext) -> Result<Nullable<RoutingRoleEnum>, Error> {
414 Ok(Nullable::new(self.diag.routing_role()?))
415 }
416
417 fn network_name<P: TLVBuilderParent>(
418 &self,
419 _ctx: impl ReadContext,
420 builder: NullableBuilder<P, Utf8StrBuilder<P>>,
421 ) -> Result<P, Error> {
422 let mut builder = Some(builder);
423 let mut parent = None;
424
425 self.diag.network_name(&mut |name| {
426 if let Some(name) = name {
427 parent = Some(unwrap!(builder.take()).non_null()?.set(name)?);
428 } else {
429 parent = Some(unwrap!(builder.take()).null()?);
430 }
431
432 Ok(())
433 })?;
434
435 Ok(unwrap!(parent))
436 }
437
438 fn pan_id(&self, _ctx: impl ReadContext) -> Result<Nullable<u16>, Error> {
439 Ok(Nullable::new(self.diag.pan_id()?))
440 }
441
442 fn extended_pan_id(&self, _ctx: impl ReadContext) -> Result<Nullable<u64>, Error> {
443 Ok(Nullable::new(self.diag.extended_pan_id()?))
444 }
445
446 fn mesh_local_prefix<P: TLVBuilderParent>(
447 &self,
448 _ctx: impl ReadContext,
449 builder: NullableBuilder<P, OctetsBuilder<P>>,
450 ) -> Result<P, Error> {
451 let mut builder = Some(builder);
452 let mut parent = None;
453
454 self.diag.mesh_local_prefix(&mut |prefix| {
455 if let Some(prefix) = prefix {
456 parent = Some(
457 unwrap!(builder.take())
458 .non_null()?
459 .set(Octets::new(prefix))?,
460 );
461 } else {
462 parent = Some(unwrap!(builder.take()).null()?);
463 }
464
465 Ok(())
466 })?;
467
468 Ok(unwrap!(parent))
469 }
470
471 fn neighbor_table<P: TLVBuilderParent>(
472 &self,
473 _ctx: impl ReadContext,
474 builder: ArrayAttributeRead<
475 NeighborTableStructArrayBuilder<P>,
476 NeighborTableStructBuilder<P>,
477 >,
478 ) -> Result<P, Error> {
479 match builder {
480 ArrayAttributeRead::ReadAll(builder) => {
481 let mut builder = Some(builder);
482
483 self.diag.neighbor_table(&mut |item| {
484 builder = Some(item.read_into(unwrap!(builder.take()).push()?)?);
485
486 Ok(())
487 })?;
488
489 unwrap!(builder).end()
490 }
491 ArrayAttributeRead::ReadOne(index, builder) => {
492 let mut builder = Some(builder);
493 let mut parent = None;
494 let mut current = 0;
495
496 self.diag.neighbor_table(&mut |item| {
497 if index == current {
498 parent = Some(item.read_into(unwrap!(builder.take()))?);
499 }
500
501 current += 1;
502
503 Ok(())
504 })?;
505
506 if let Some(parent) = parent {
507 Ok(parent)
508 } else {
509 Err(ErrorCode::InvalidAction.into())
510 }
511 }
512 ArrayAttributeRead::ReadNone(builder) => builder.end(),
513 }
514 }
515
516 fn route_table<P: TLVBuilderParent>(
517 &self,
518 _ctx: impl ReadContext,
519 builder: ArrayAttributeRead<RouteTableStructArrayBuilder<P>, RouteTableStructBuilder<P>>,
520 ) -> Result<P, Error> {
521 match builder {
522 ArrayAttributeRead::ReadAll(builder) => {
523 let mut builder = Some(builder);
524
525 self.diag.route_table(&mut |item| {
526 builder = Some(item.read_into(unwrap!(builder.take()).push()?)?);
527
528 Ok(())
529 })?;
530
531 unwrap!(builder).end()
532 }
533 ArrayAttributeRead::ReadOne(index, builder) => {
534 let mut builder = Some(builder);
535 let mut parent = None;
536 let mut current = 0;
537
538 self.diag.route_table(&mut |item| {
539 if index == current {
540 parent = Some(item.read_into(unwrap!(builder.take()))?);
541 }
542
543 current += 1;
544
545 Ok(())
546 })?;
547
548 if let Some(parent) = parent {
549 Ok(parent)
550 } else {
551 Err(ErrorCode::InvalidAction.into())
552 }
553 }
554 ArrayAttributeRead::ReadNone(builder) => builder.end(),
555 }
556 }
557
558 fn partition_id(&self, _ctx: impl ReadContext) -> Result<Nullable<u32>, Error> {
559 Ok(Nullable::new(self.diag.partition_id()?))
560 }
561
562 fn weighting(&self, _ctx: impl ReadContext) -> Result<Nullable<u16>, Error> {
563 Ok(Nullable::new(self.diag.weighting()?))
564 }
565
566 fn data_version(&self, _ctx: impl ReadContext) -> Result<Nullable<u16>, Error> {
567 Ok(Nullable::new(self.diag.data_version()?))
568 }
569
570 fn stable_data_version(&self, _ctx: impl ReadContext) -> Result<Nullable<u16>, Error> {
571 Ok(Nullable::new(self.diag.stable_data_version()?))
572 }
573
574 fn leader_router_id(&self, _ctx: impl ReadContext) -> Result<Nullable<u8>, Error> {
575 Ok(Nullable::new(self.diag.leader_router_id()?))
576 }
577
578 fn security_policy<P: TLVBuilderParent>(
579 &self,
580 _ctx: impl ReadContext,
581 builder: NullableBuilder<P, SecurityPolicyBuilder<P>>,
582 ) -> Result<P, Error> {
583 let security_policy = self.diag.security_policy()?;
584 if let Some(security_policy) = security_policy {
585 builder
586 .non_null()?
587 .rotation_time(security_policy.rotation_time)?
588 .flags(security_policy.flags)?
589 .end()
590 } else {
591 builder.null()
592 }
593 }
594
595 fn channel_page_0_mask<P: TLVBuilderParent>(
596 &self,
597 _ctx: impl ReadContext,
598 builder: NullableBuilder<P, OctetsBuilder<P>>,
599 ) -> Result<P, Error> {
600 let mut builder = Some(builder);
601 let mut parent = None;
602
603 self.diag.channel_page0_mask(&mut |mask| {
604 if let Some(mask) = mask {
605 parent = Some(unwrap!(builder.take()).non_null()?.set(Octets::new(mask))?);
606 } else {
607 parent = Some(unwrap!(builder.take()).null()?);
608 }
609
610 Ok(())
611 })?;
612
613 Ok(unwrap!(parent.take()))
614 }
615
616 fn operational_dataset_components<P: TLVBuilderParent>(
617 &self,
618 _ctx: impl ReadContext,
619 builder: NullableBuilder<P, OperationalDatasetComponentsBuilder<P>>,
620 ) -> Result<P, Error> {
621 let mut builder = Some(builder);
622 let mut parent = None;
623
624 self.diag.operational_dataset_components(&mut |dsc| {
625 if let Some(dsc) = dsc {
626 parent = Some(dsc.read_into(unwrap!(builder.take()).non_null()?)?);
627 } else {
628 parent = Some(unwrap!(builder.take()).null()?);
629 }
630
631 Ok(())
632 })?;
633
634 Ok(unwrap!(parent))
635 }
636
637 fn active_network_faults_list<P: TLVBuilderParent>(
638 &self,
639 _ctx: impl ReadContext,
640 builder: ArrayAttributeRead<
641 ToTLVArrayBuilder<P, NetworkFaultEnum>,
642 ToTLVBuilder<P, NetworkFaultEnum>,
643 >,
644 ) -> Result<P, Error> {
645 match builder {
646 ArrayAttributeRead::ReadAll(builder) => {
647 let mut builder = Some(builder);
648
649 self.diag.active_network_faults_list(&mut |fault| {
650 builder = Some(unwrap!(builder.take()).push(&fault)?);
651
652 Ok(())
653 })?;
654
655 unwrap!(builder.take()).end()
656 }
657 ArrayAttributeRead::ReadOne(index, builder) => {
658 let mut builder = Some(builder);
659 let mut parent = None;
660 let mut current = 0;
661
662 self.diag.active_network_faults_list(&mut |fault| {
663 if index == current {
664 parent = Some(unwrap!(builder.take()).set(&fault)?);
665 }
666
667 current += 1;
668
669 Ok(())
670 })?;
671
672 if let Some(parent) = parent {
673 Ok(parent)
674 } else {
675 Err(ErrorCode::InvalidAction.into())
676 }
677 }
678 ArrayAttributeRead::ReadNone(builder) => builder.end(),
679 }
680 }
681 fn ext_address(&self, _ctx: impl ReadContext) -> Result<Nullable<u64>, Error> {
682 Ok(Nullable::new(self.diag.ext_address()?))
683 }
684
685 fn rloc_16(&self, _ctx: impl ReadContext) -> Result<Nullable<u16>, Error> {
686 Ok(Nullable::new(self.diag.rloc_16()?))
687 }
688
689 fn handle_reset_counts(&self, _ctx: impl InvokeContext) -> Result<(), Error> {
690 Err(ErrorCode::CommandNotFound.into())
691 }
692}
693
694impl Debug for ThreadDiagHandler<'_> {
695 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
696 f.debug_struct("ThreadDiagHandler")
697 .field("dataver", &self.dataver)
698 .finish()
699 }
700}
701
702#[cfg(feature = "defmt")]
703impl defmt::Format for ThreadDiagHandler<'_> {
704 fn format(&self, f: defmt::Formatter) {
705 defmt::write!(f, "ThreadDiagHandler {{ dataver: {} }}", self.dataver.get());
706 }
707}