use bitflags::bitflags;
use crate::dm::{ArrayAttributeRead, Cluster, Dataver, InvokeContext, ReadContext};
use crate::error::{Error, ErrorCode};
use crate::tlv::TLVBuilderParent;
use crate::with;
pub use crate::dm::clusters::decl::software_diagnostics::*;
bitflags! {
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Options: u8 {
const HEAP = 0x1;
const WATERMARKS = 0x2;
const THREAD = 0x4;
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct ThreadMetric<'a> {
pub id: u64,
pub name: Option<&'a str>,
pub stack_free_current: Option<u32>,
pub stack_free_minimum: Option<u32>,
pub stack_size: Option<u32>,
}
pub trait SwDiag {
fn current_heap_free(&self) -> Result<u64, Error>;
fn current_heap_used(&self) -> Result<u64, Error>;
fn current_heap_high_watermark(&self) -> Result<u64, Error>;
fn thread_metrics(
&self,
_visit: &mut dyn FnMut(&ThreadMetric<'_>) -> Result<(), Error>,
) -> Result<(), Error>;
fn reset_watermarks(&self) -> Result<(), Error>;
}
impl<T> SwDiag for &T
where
T: SwDiag,
{
fn current_heap_free(&self) -> Result<u64, Error> {
(*self).current_heap_free()
}
fn current_heap_used(&self) -> Result<u64, Error> {
(*self).current_heap_used()
}
fn current_heap_high_watermark(&self) -> Result<u64, Error> {
(*self).current_heap_high_watermark()
}
fn thread_metrics(
&self,
visit: &mut dyn FnMut(&ThreadMetric<'_>) -> Result<(), Error>,
) -> Result<(), Error> {
(*self).thread_metrics(visit)
}
fn reset_watermarks(&self) -> Result<(), Error> {
(*self).reset_watermarks()
}
}
impl SwDiag for () {
fn current_heap_free(&self) -> Result<u64, Error> {
Ok(0)
}
fn current_heap_used(&self) -> Result<u64, Error> {
Ok(0)
}
fn current_heap_high_watermark(&self) -> Result<u64, Error> {
Ok(0)
}
fn thread_metrics(
&self,
_visit: &mut dyn FnMut(&ThreadMetric<'_>) -> Result<(), Error>,
) -> Result<(), Error> {
Ok(())
}
fn reset_watermarks(&self) -> Result<(), Error> {
Err(ErrorCode::UnsupportedAccess.into())
}
}
pub const fn cluster(options: Options) -> Cluster<'static> {
let heap = options.bits() & Options::HEAP.bits() != 0
|| options.bits() & Options::WATERMARKS.bits() != 0;
let watermarks = options.bits() & Options::WATERMARKS.bits() != 0;
let thread = options.bits() & Options::THREAD.bits() != 0;
let matter_features = if watermarks {
Feature::WATERMARKS.bits()
} else {
0
};
let cluster = FULL_CLUSTER.with_features(matter_features);
match (heap, watermarks, thread) {
(false, _, false) => cluster.with_attrs(with!(required)).with_cmds(with!()),
(false, _, true) => cluster
.with_attrs(with!(required; AttributeId::ThreadMetrics))
.with_cmds(with!()),
(true, false, false) => cluster
.with_attrs(with!(required;
AttributeId::CurrentHeapFree | AttributeId::CurrentHeapUsed))
.with_cmds(with!()),
(true, false, true) => cluster
.with_attrs(with!(required;
AttributeId::ThreadMetrics
| AttributeId::CurrentHeapFree
| AttributeId::CurrentHeapUsed))
.with_cmds(with!()),
(true, true, false) => cluster
.with_attrs(with!(required;
AttributeId::CurrentHeapFree
| AttributeId::CurrentHeapUsed
| AttributeId::CurrentHeapHighWatermark))
.with_cmds(with!(CommandId::ResetWatermarks)),
(true, true, true) => cluster
.with_attrs(with!(required;
AttributeId::ThreadMetrics
| AttributeId::CurrentHeapFree
| AttributeId::CurrentHeapUsed
| AttributeId::CurrentHeapHighWatermark))
.with_cmds(with!(CommandId::ResetWatermarks)),
}
}
#[derive(Clone)]
pub struct SwDiagHandler<'a> {
dataver: Dataver,
sw_diag: &'a dyn SwDiag,
}
impl<'a> SwDiagHandler<'a> {
pub const fn new(dataver: Dataver, sw_diag: &'a dyn SwDiag) -> Self {
Self { dataver, sw_diag }
}
pub const fn adapt(self) -> HandlerAdaptor<Self> {
HandlerAdaptor(self)
}
}
impl ClusterHandler for SwDiagHandler<'_> {
const CLUSTER: Cluster<'static> = cluster(Options::empty());
fn dataver(&self) -> u32 {
self.dataver.get()
}
fn dataver_changed(&self) {
self.dataver.changed();
}
fn current_heap_free(&self, _ctx: impl ReadContext) -> Result<u64, Error> {
self.sw_diag.current_heap_free()
}
fn current_heap_used(&self, _ctx: impl ReadContext) -> Result<u64, Error> {
self.sw_diag.current_heap_used()
}
fn current_heap_high_watermark(&self, _ctx: impl ReadContext) -> Result<u64, Error> {
self.sw_diag.current_heap_high_watermark()
}
fn thread_metrics<P: TLVBuilderParent>(
&self,
_ctx: impl ReadContext,
builder: ArrayAttributeRead<
ThreadMetricsStructArrayBuilder<P>,
ThreadMetricsStructBuilder<P>,
>,
) -> Result<P, Error> {
match builder {
ArrayAttributeRead::ReadAll(array) => {
let mut array_opt = Some(array);
self.sw_diag.thread_metrics(&mut |m| {
let array = unwrap!(array_opt.take());
let next = array
.push()?
.id(m.id)?
.name(m.name)?
.stack_free_current(m.stack_free_current)?
.stack_free_minimum(m.stack_free_minimum)?
.stack_size(m.stack_size)?
.end()?;
array_opt = Some(next);
Ok(())
})?;
unwrap!(array_opt.take()).end()
}
ArrayAttributeRead::ReadOne(index, item_builder) => {
let mut item_opt = Some(item_builder);
let mut returned: Option<P> = None;
let mut current = 0u16;
self.sw_diag.thread_metrics(&mut |m| {
if returned.is_none() && current == index {
let b = unwrap!(item_opt.take());
returned = Some(
b.id(m.id)?
.name(m.name)?
.stack_free_current(m.stack_free_current)?
.stack_free_minimum(m.stack_free_minimum)?
.stack_size(m.stack_size)?
.end()?,
);
}
current = current.saturating_add(1);
Ok(())
})?;
returned.ok_or_else(|| ErrorCode::ConstraintError.into())
}
ArrayAttributeRead::ReadNone(array) => array.end(),
}
}
fn handle_reset_watermarks(&self, _ctx: impl InvokeContext) -> Result<(), Error> {
self.sw_diag.reset_watermarks()
}
}
impl core::fmt::Debug for SwDiagHandler<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SwDiagHandler")
.field("dataver", &self.dataver)
.finish()
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for SwDiagHandler<'_> {
fn format(&self, f: defmt::Formatter) {
defmt::write!(f, "SwDiagHandler {{ dataver: {} }}", self.dataver.get());
}
}