use num_enum::TryFromPrimitive;
use crate::error::{Error, ErrorCode};
use crate::tlv::{FromTLV, TLVElement, TLVTag, TLVWrite, TagType, ToTLV, TLV};
use super::{ClusterId, EndptId, EventId, EventNumber, GenericPath, IMStatusCode, NodeId, Status};
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EventFilter {
pub node: Option<NodeId>,
pub event_min: Option<EventNumber>,
}
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[tlvargs(datatype = "list")]
pub struct EventPath {
pub node: Option<NodeId>,
pub endpoint: Option<EndptId>,
pub cluster: Option<ClusterId>,
pub event: Option<EventId>,
pub is_urgent: Option<bool>,
}
impl EventPath {
pub const fn from_gp(path: &GenericPath) -> Self {
Self {
node: None,
endpoint: path.endpoint,
cluster: path.cluster,
event: path.leaf,
is_urgent: None,
}
}
pub const fn to_gp(&self) -> GenericPath {
GenericPath::new(self.endpoint, self.cluster, self.event)
}
pub const fn is_wildcard(&self) -> bool {
self.endpoint.is_none() || self.cluster.is_none() || self.event.is_none()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum EventRespTag {
Status = 0,
Data = 1,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TryFromPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum EventDataTag {
Path = 0,
EventNumber = 1,
Priority = 2,
EpochTimestamp = 3,
SystemTimestamp = 4,
DeltaEpochTimestamp = 5,
DeltaSystemTimestamp = 6,
Data = 7,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EventStatus {
pub path: EventPath,
pub status: Status,
}
impl EventStatus {
pub const fn new(path: EventPath, status: IMStatusCode, cluster_status: Option<u16>) -> Self {
Self {
path,
status: Status::new(status, cluster_status),
}
}
pub const fn from_gp(
path: &GenericPath,
status: IMStatusCode,
cluster_status: Option<u16>,
) -> Self {
Self::new(EventPath::from_gp(path), status, cluster_status)
}
}
#[derive(Clone, FromTLV, ToTLV, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[tlvargs(lifetime = "'a")]
pub enum EventResp<'a> {
Status(EventStatus),
Data(EventData<'a>),
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EventData<'a> {
pub path: EventPath,
pub event_number: EventNumber,
pub priority: EventPriority,
pub timestamp: EventDataTimestamp,
pub data: TLVElement<'a>,
}
impl<'a> EventData<'a> {
pub const fn new(
path: EventPath,
event_number: EventNumber,
priority: EventPriority,
timestamp: EventDataTimestamp,
data: TLVElement<'a>,
) -> Self {
Self {
path,
event_number,
priority,
timestamp,
data,
}
}
pub fn write_preamble<T: TLVWrite>(&self, tag: &TLVTag, mut tw: T) -> Result<(), Error> {
tw.start_struct(tag)?;
self.path
.to_tlv(&TagType::Context(EventDataTag::Path as _), &mut tw)?;
tw.u64(
&TagType::Context(EventDataTag::EventNumber as _),
self.event_number,
)?;
tw.u8(
&TagType::Context(EventDataTag::Priority as _),
self.priority as _,
)?;
match self.timestamp {
EventDataTimestamp::EpochTimestamp(ts) => {
tw.u64(&TagType::Context(EventDataTag::EpochTimestamp as _), ts)?
}
EventDataTimestamp::SystemTimestamp(ts) => {
tw.u64(&TagType::Context(EventDataTag::SystemTimestamp as _), ts)?
}
EventDataTimestamp::DeltaEpochTimestamp(ts) => tw.u64(
&TagType::Context(EventDataTag::DeltaEpochTimestamp as _),
ts,
)?,
EventDataTimestamp::DeltaSystemTimestamp(ts) => tw.u64(
&TagType::Context(EventDataTag::DeltaSystemTimestamp as _),
ts,
)?,
}
Ok(())
}
}
impl<'a> ToTLV for EventData<'a> {
fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
self.write_preamble(tag, &mut tw)?;
self.data
.to_tlv(&TagType::Context(EventDataTag::Data as _), &mut tw)?;
tw.end_container()
}
fn tlv_iter(&self, tag: crate::tlv::TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
let (timestamp_tag, timestamp_val) = match self.timestamp {
EventDataTimestamp::EpochTimestamp(ts) => (EventDataTag::EpochTimestamp, ts),
EventDataTimestamp::SystemTimestamp(ts) => (EventDataTag::SystemTimestamp, ts),
EventDataTimestamp::DeltaEpochTimestamp(ts) => (EventDataTag::DeltaEpochTimestamp, ts),
EventDataTimestamp::DeltaSystemTimestamp(ts) => {
(EventDataTag::DeltaSystemTimestamp, ts)
}
};
let header = [Ok(TLV::structure(tag))].into_iter();
let middle_fields = [
Ok(TLV::u64(
TLVTag::Context(EventDataTag::EventNumber as _),
self.event_number,
)),
Ok(TLV::u8(
TLVTag::Context(EventDataTag::Priority as _),
self.priority as _,
)),
Ok(TLV::u64(TLVTag::Context(timestamp_tag as _), timestamp_val)),
]
.into_iter();
let trailer = [Ok(TLV::end_container())].into_iter();
header
.chain(self.path.tlv_iter(TLVTag::Context(EventDataTag::Path as _)))
.chain(middle_fields)
.chain(self.data.tlv_iter(TLVTag::Context(EventDataTag::Data as _)))
.chain(trailer)
}
}
impl<'a> FromTLV<'a> for EventData<'a> {
fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
let mut path = None;
let mut event_number = None;
let mut priority = None;
let mut timestamp = None;
let mut data = None;
for field in element.structure()?.iter() {
let el = field?;
match el.tag()? {
TLVTag::Context(tag) => match EventDataTag::try_from(tag)? {
EventDataTag::Path => path = Some(EventPath::from_tlv(&el)?),
EventDataTag::EventNumber => event_number = Some(el.u64()?),
EventDataTag::Priority => priority = Some(EventPriority::from_tlv(&el)?),
EventDataTag::EpochTimestamp => {
timestamp = Some(EventDataTimestamp::EpochTimestamp(el.u64()?))
}
EventDataTag::SystemTimestamp => {
timestamp = Some(EventDataTimestamp::SystemTimestamp(el.u64()?))
}
EventDataTag::DeltaEpochTimestamp => {
timestamp = Some(EventDataTimestamp::DeltaEpochTimestamp(el.u64()?))
}
EventDataTag::DeltaSystemTimestamp => {
timestamp = Some(EventDataTimestamp::DeltaSystemTimestamp(el.u64()?))
}
EventDataTag::Data => data = Some(el),
},
_ => return Err(Error::new(ErrorCode::Invalid)),
}
}
Ok(EventData::new(
path.ok_or(Error::new(ErrorCode::Invalid))?,
event_number.ok_or(Error::new(ErrorCode::Invalid))?,
priority.ok_or(Error::new(ErrorCode::Invalid))?,
timestamp.ok_or(Error::new(ErrorCode::Invalid))?,
data.ok_or(Error::new(ErrorCode::Invalid))?,
))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[tlvargs(datatype = "u8")]
#[repr(u8)]
pub enum EventPriority {
Debug = 0,
Info = 1,
Critical = 2,
}
impl EventPriority {
pub const fn next(&self) -> Option<Self> {
match self {
Self::Debug => Some(Self::Info),
Self::Info => Some(Self::Critical),
Self::Critical => None,
}
}
pub const fn prev(&self) -> Option<Self> {
match self {
Self::Debug => None,
Self::Info => Some(Self::Debug),
Self::Critical => Some(Self::Info),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EventDataTimestamp {
EpochTimestamp(u64),
SystemTimestamp(u64),
DeltaEpochTimestamp(u64),
DeltaSystemTimestamp(u64),
}