Skip to main content

apis_saltans_nwk/
lib.rs

1//! Network-layer receive context types for Zigbee.
2//!
3//! This crate provides small, transport-neutral value types used to describe
4//! Zigbee network-layer sources, per-frame metadata, and envelopes that attach
5//! that information to an arbitrary payload.
6//!
7//! The crate can optionally derive `serde` serialization implementations
8//! through the `serde` feature.
9
10use core::fmt::{self, Display, Formatter, LowerHex, UpperHex};
11
12use zb_core::IeeeAddress;
13
14/// A payload together with its network-layer source and metadata.
15///
16/// `Envelope` is generic over the carried payload so higher layers can attach
17/// NWK context without tying this crate to APS, ZCL, ZDP, or hardware-specific
18/// frame types.
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
21pub struct Envelope<T> {
22    source: Source,
23    metadata: Metadata,
24    payload: T,
25}
26
27impl<T> Envelope<T> {
28    /// Create a new envelope.
29    #[must_use]
30    pub const fn new(source: Source, metadata: Metadata, payload: T) -> Self {
31        Self {
32            source,
33            metadata,
34            payload,
35        }
36    }
37
38    /// Return the network-layer source.
39    #[must_use]
40    pub const fn source(&self) -> Source {
41        self.source
42    }
43
44    /// Return the network-layer metadata.
45    #[must_use]
46    pub const fn metadata(&self) -> Metadata {
47        self.metadata
48    }
49
50    /// Return the enclosed payload by reference.
51    #[must_use]
52    pub const fn payload(&self) -> &T {
53        &self.payload
54    }
55
56    /// Split the envelope into source, metadata, and payload.
57    #[must_use]
58    pub fn into_parts(self) -> (Source, Metadata, T) {
59        (self.source, self.metadata, self.payload)
60    }
61}
62
63/// Network-layer source information.
64///
65/// The source always includes the 16-bit network address. The IEEE address is
66/// optional because it may not be known for every incoming frame.
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
69pub struct Source {
70    node_id: u16,
71    ieee_address: Option<IeeeAddress>,
72}
73
74impl Source {
75    /// Create source information from a network address and optional IEEE address.
76    #[must_use]
77    pub const fn new(node_id: u16, ieee_address: Option<IeeeAddress>) -> Self {
78        Self {
79            node_id,
80            ieee_address,
81        }
82    }
83
84    /// Return the 16-bit network address.
85    #[must_use]
86    pub const fn node_id(&self) -> u16 {
87        self.node_id
88    }
89
90    /// Return the IEEE address of the source, if known.
91    #[must_use]
92    pub const fn ieee_address(&self) -> Option<IeeeAddress> {
93        self.ieee_address
94    }
95
96    /// Split the source into its network address and optional IEEE address.
97    #[must_use]
98    pub const fn into_parts(self) -> (u16, Option<IeeeAddress>) {
99        (self.node_id, self.ieee_address)
100    }
101}
102
103/// Metadata associated with a received network-layer frame.
104///
105/// Each field is optional because different backends and frame paths may expose
106/// different subsets of NWK metadata.
107#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
108#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
109pub struct Metadata {
110    last_hop_lqi: Option<u8>,
111    last_hop_rssi: Option<i16>,
112    binding_index: Option<usize>,
113    source_route_overhead: Option<u8>,
114}
115
116impl Metadata {
117    /// Create network-layer metadata.
118    #[must_use]
119    pub const fn new(
120        last_hop_lqi: Option<u8>,
121        last_hop_rssi: Option<i16>,
122        binding_index: Option<usize>,
123        source_route_overhead: Option<u8>,
124    ) -> Self {
125        Self {
126            last_hop_lqi,
127            last_hop_rssi,
128            binding_index,
129            source_route_overhead,
130        }
131    }
132
133    /// Return the link quality indicator reported for the last hop.
134    #[must_use]
135    pub const fn last_hop_lqi(&self) -> Option<u8> {
136        self.last_hop_lqi
137    }
138
139    /// Return the received signal strength indicator reported for the last hop.
140    #[must_use]
141    pub const fn last_hop_rssi(&self) -> Option<i16> {
142        self.last_hop_rssi
143    }
144
145    /// Return the binding table index associated with the frame.
146    #[must_use]
147    pub const fn binding_index(&self) -> Option<usize> {
148        self.binding_index
149    }
150
151    /// Return the source-route overhead reported for the frame.
152    #[must_use]
153    pub const fn source_route_overhead(&self) -> Option<u8> {
154        self.source_route_overhead
155    }
156}
157
158macro_rules! impl_fmt_for_source {
159    ($($tr:path),+ $(,)?) => {
160        $(
161            impl $tr for Source {
162                fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
163                    <u16 as $tr>::fmt(&self.node_id, f)?;
164                    f.write_str(" (")?;
165
166                    if let Some(ieee_address) = self.ieee_address {
167                        <IeeeAddress as $tr>::fmt(&ieee_address, f)?;
168                    } else {
169                        f.write_str("N/A")?;
170                    }
171
172                    f.write_str(")")
173                }
174            }
175        )+
176    }
177}
178
179impl_fmt_for_source! { Display, UpperHex, LowerHex }