use core::fmt::Debug;
use core::net::{Ipv4Addr, Ipv6Addr};
use crate::dm::{ArrayAttributeRead, Cluster, Dataver, InvokeContext, ReadContext};
use crate::error::{Error, ErrorCode};
use crate::tlv::{Nullable, Octets, TLVBuilder, TLVBuilderParent};
use crate::utils::epoch::MATTER_EPOCH_SECS;
use crate::utils::sync::DynBase;
use crate::with;
pub use crate::dm::clusters::decl::general_diagnostics::*;
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NetifInfo<'a> {
pub name: &'a str,
pub operational: bool,
pub offprem_svc_reachable_ipv4: Option<bool>,
pub offprem_svc_reachable_ipv6: Option<bool>,
pub hw_addr: &'a [u8; 8],
pub ipv4_addrs: &'a [Ipv4Addr],
pub ipv6_addrs: &'a [Ipv6Addr],
pub netif_type: InterfaceTypeEnum,
pub netif_index: u32,
}
impl NetifInfo<'_> {
fn read_into<P: TLVBuilderParent>(
&self,
builder: NetworkInterfaceBuilder<P>,
) -> Result<P, Error> {
builder
.name(self.name)?
.is_operational(self.operational)?
.off_premise_services_reachable_i_pv_4(Nullable::new(self.offprem_svc_reachable_ipv4))?
.off_premise_services_reachable_i_pv_6(Nullable::new(self.offprem_svc_reachable_ipv6))?
.hardware_address(Octets::new(self.hw_addr))?
.i_pv_4_addresses()?
.with(|mut builder| {
for addr in self.ipv4_addrs {
builder = builder.push(Octets::new(&addr.octets()))?;
}
builder.end()
})?
.i_pv_6_addresses()?
.with(|mut builder| {
for addr in self.ipv6_addrs {
builder = builder.push(Octets::new(&addr.octets()))?;
}
builder.end()
})?
.r#type(self.netif_type)?
.end()
}
}
pub trait GenDiag: DynBase {
fn reboot_count(&self) -> Result<u16, Error>;
fn uptime_ms(&self) -> Result<u64, Error>;
fn test_event_triggers_enabled(&self) -> Result<bool, Error>;
fn test_event_trigger(&self, key: &[u8], trigger: u64) -> Result<(), Error>;
}
impl<T> GenDiag for &T
where
T: GenDiag,
{
fn reboot_count(&self) -> Result<u16, Error> {
(**self).reboot_count()
}
fn uptime_ms(&self) -> Result<u64, Error> {
(**self).uptime_ms()
}
fn test_event_triggers_enabled(&self) -> Result<bool, Error> {
(**self).test_event_triggers_enabled()
}
fn test_event_trigger(&self, key: &[u8], trigger: u64) -> Result<(), Error> {
(**self).test_event_trigger(key, trigger)
}
}
impl GenDiag for () {
fn reboot_count(&self) -> Result<u16, Error> {
Ok(0)
}
fn uptime_ms(&self) -> Result<u64, Error> {
Ok(embassy_time::Instant::now().as_millis())
}
fn test_event_triggers_enabled(&self) -> Result<bool, Error> {
Ok(false)
}
fn test_event_trigger(&self, _key: &[u8], _trigger: u64) -> Result<(), Error> {
Err(ErrorCode::ConstraintError.into())
}
}
pub trait NetifDiag: DynBase {
fn netifs(&self, f: &mut dyn FnMut(&NetifInfo) -> Result<(), Error>) -> Result<(), Error>;
}
impl<T> NetifDiag for &T
where
T: NetifDiag,
{
fn netifs(&self, f: &mut dyn FnMut(&NetifInfo) -> Result<(), Error>) -> Result<(), Error> {
(*self).netifs(f)
}
}
impl NetifDiag for () {
fn netifs(&self, _f: &mut dyn FnMut(&NetifInfo) -> Result<(), Error>) -> Result<(), Error> {
Ok(())
}
}
#[derive(Clone)]
pub struct GenDiagHandler<'a> {
dataver: Dataver,
diag: &'a dyn GenDiag,
netif_diag: &'a dyn NetifDiag,
}
impl<'a> GenDiagHandler<'a> {
pub const fn new(
dataver: Dataver,
diag: &'a dyn GenDiag,
netif_diag: &'a dyn NetifDiag,
) -> Self {
Self {
dataver,
diag,
netif_diag,
}
}
pub const fn adapt(self) -> HandlerAdaptor<Self> {
HandlerAdaptor(self)
}
}
impl ClusterHandler for GenDiagHandler<'_> {
const CLUSTER: Cluster<'static> = FULL_CLUSTER
.with_attrs(with!(required; AttributeId::UpTime))
.with_cmds(with!(CommandId::TestEventTrigger | CommandId::TimeSnapshot));
fn dataver(&self) -> u32 {
self.dataver.get()
}
fn dataver_changed(&self) {
self.dataver.changed();
}
fn network_interfaces<P: TLVBuilderParent>(
&self,
_ctx: impl ReadContext,
builder: ArrayAttributeRead<NetworkInterfaceArrayBuilder<P>, NetworkInterfaceBuilder<P>>,
) -> Result<P, Error> {
match builder {
ArrayAttributeRead::ReadAll(builder) => {
let mut builder = Some(builder);
self.netif_diag.netifs(&mut |netif| {
builder = Some(netif.read_into(unwrap!(builder.take()).push()?)?);
Ok(())
})?;
builder.take().unwrap().end()
}
ArrayAttributeRead::ReadOne(index, builder) => {
let mut builder = Some(builder);
let mut parent_builder = None;
let mut current = 0;
self.netif_diag.netifs(&mut |netif| {
if current == index as usize {
parent_builder = Some(netif.read_into(unwrap!(builder.take()))?);
}
current += 1;
Ok(())
})?;
parent_builder
.take()
.ok_or_else(|| ErrorCode::ConstraintError.into())
}
ArrayAttributeRead::ReadNone(builder) => builder.end(),
}
}
fn reboot_count(&self, _ctx: impl ReadContext) -> Result<u16, Error> {
self.diag.reboot_count()
}
fn up_time(&self, _ctx: impl ReadContext) -> Result<u64, Error> {
self.diag.uptime_ms().map(|uptime| uptime / 1000)
}
fn test_event_triggers_enabled(&self, _ctx: impl ReadContext) -> Result<bool, Error> {
self.diag.test_event_triggers_enabled()
}
fn handle_test_event_trigger(
&self,
_ctx: impl InvokeContext,
request: TestEventTriggerRequest<'_>,
) -> Result<(), Error> {
let key = request.enable_key()?.0;
let trigger = request.event_trigger()?;
self.diag.test_event_trigger(key, trigger)
}
fn handle_time_snapshot<P: TLVBuilderParent>(
&self,
ctx: impl InvokeContext,
response: TimeSnapshotResponseBuilder<P>,
) -> Result<P, Error> {
let system_time_ms = self.diag.uptime_ms()?;
let posix_time_ms = Nullable::new(
ctx.matter()
.with_state(|state| state.rtc.utc_time())
.reliable()
.map(|us| us / 1000)
.map(|ms| ms.saturating_add(MATTER_EPOCH_SECS.saturating_mul(1000))),
);
response
.system_time_ms(system_time_ms)?
.posix_time_ms(posix_time_ms)?
.end()
}
fn handle_payload_test_request<P: TLVBuilderParent>(
&self,
_ctx: impl InvokeContext,
_request: PayloadTestRequestRequest<'_>,
_response: PayloadTestResponseBuilder<P>,
) -> Result<P, Error> {
Err(ErrorCode::CommandNotFound.into())
}
}
impl Debug for GenDiagHandler<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("GenDiagHandler")
.field("dataver", &self.dataver)
.finish()
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for GenDiagHandler<'_> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "GenDiagHandler {{ dataver: {} }}", self.dataver);
}
}