1use heapless::Vec;
4
5use crate::address::Address;
6use crate::flags::Flags;
7
8#[derive(Debug)]
9pub enum PacketError {
10 DataTooLong,
11 DataTooShort,
12}
13
14#[derive(Debug, Clone)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct Packet {
18 pub src: Address,
19 pub dst: Address,
20 pub flags: Flags,
21 pub data: Vec<u8, 61>,
22 pub rssi: Option<i16>,
23}
24
25impl Packet {
26 const MIN_VALID_PACKET_LEN: u8 = 3;
28
29 const MAX_PAYLOAD_DATA_LENGTH: usize = 61;
30
31 pub fn new(src: Address, dst: Address, flags: Flags, data: &[u8]) -> Result<Packet, PacketError> {
32 if data.len() > Self::MAX_PAYLOAD_DATA_LENGTH {
33 return Err(PacketError::DataTooLong);
34 }
35 Ok(Self {
36 src,
37 dst,
38 flags,
39 data: Vec::from_slice(data).unwrap(),
40 rssi: None,
41 })
42 }
43
44 pub fn from_rx_data(len: u8, raw: &[u8], rssi: i16) -> Result<Packet, PacketError> {
45 if len < Self::MIN_VALID_PACKET_LEN {
46 return Err(PacketError::DataTooShort);
47 }
48 Ok(Self {
49 src: Address::from_u8(raw[0]),
50 dst: Address::from_u8(raw[1]),
51 flags: Flags::from_u8(raw[2]),
52 data: Vec::from_slice(&raw[3..len as usize]).unwrap(),
53 rssi: Some(rssi),
54 })
55 }
56
57 pub(crate) fn to_slice(&self, raw: &mut [u8; 65]) -> Result<u8, PacketError> {
64 let fifo_len = self.data.len() as u8 + Self::MIN_VALID_PACKET_LEN;
66
67 raw[0] = fifo_len;
68 raw[1] = self.src.as_u8();
69 raw[2] = self.dst.as_u8();
70 raw[3] = self.flags.as_u8();
71 raw[4..4 + self.data.len()].copy_from_slice(self.data.as_slice());
72 Ok(fifo_len + 1)
73 }
74
75 pub fn is_ack(&self) -> bool {
76 matches!(self.flags, Flags::Ack(_))
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn new_accepts_empty_payload() {
86 let p = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &[]).unwrap();
87 assert_eq!(p.data.len(), 0);
88 assert_eq!(p.rssi, None);
89 }
90
91 #[test]
92 fn new_accepts_max_payload() {
93 let payload = [0xAA; 61];
94 let p = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &payload).unwrap();
95 assert_eq!(p.data.len(), 61);
96 assert_eq!(p.data.as_slice(), &payload);
97 }
98
99 #[test]
100 fn new_rejects_oversized_payload() {
101 let payload = [0; 62];
102 assert!(matches!(
103 Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &payload),
104 Err(PacketError::DataTooLong)
105 ));
106 }
107
108 #[test]
109 fn from_rx_data_rejects_len_below_header() {
110 for len in 0..3u8 {
113 assert!(matches!(
114 Packet::from_rx_data(len, &[0; 4], -100),
115 Err(PacketError::DataTooShort)
116 ));
117 }
118 }
119
120 #[test]
121 fn from_rx_data_parses_header_only() {
122 let raw = [42, 70, 1];
124 let p = Packet::from_rx_data(3, &raw, -42).unwrap();
125 assert_eq!(p.src, Address::Unicast(42));
126 assert_eq!(p.dst, Address::Unicast(70));
127 assert_eq!(p.flags, Flags::Ack(0));
128 assert_eq!(p.data.len(), 0);
129 assert_eq!(p.rssi, Some(-42));
130 }
131
132 #[test]
133 fn from_rx_data_parses_payload_and_captures_rssi() {
134 let raw = [1, 255, 0, 0xDE, 0xAD];
136 let p = Packet::from_rx_data(5, &raw, -91).unwrap();
137 assert_eq!(p.src, Address::Unicast(1));
138 assert_eq!(p.dst, Address::Broadcast);
139 assert_eq!(p.flags, Flags::None);
140 assert_eq!(p.data.as_slice(), &[0xDE, 0xAD]);
141 assert_eq!(p.rssi, Some(-91));
142 }
143
144 #[test]
145 fn to_slice_serializes_header_only() {
146 let p = Packet::new(Address::Unicast(42), Address::Unicast(70), Flags::None, &[]).unwrap();
147 let mut buf = [0u8; 65];
148 let total = p.to_slice(&mut buf).unwrap();
149 assert_eq!(total, 4);
151 assert_eq!(&buf[..4], &[3, 42, 70, 0]);
152 }
153
154 #[test]
155 fn to_slice_serializes_payload_and_returns_correct_length() {
156 let payload = [0x01, 0x02, 0x03];
157 let p = Packet::new(
158 Address::Unicast(42),
159 Address::Broadcast,
160 Flags::Ack(2), &payload,
162 )
163 .unwrap();
164 let mut buf = [0u8; 65];
165 let total = p.to_slice(&mut buf).unwrap();
166 assert_eq!(total, 7);
168 assert_eq!(&buf[..7], &[6, 42, 255, 3, 0x01, 0x02, 0x03]);
169 }
170
171 #[test]
172 fn to_slice_then_from_rx_data_roundtrips() {
173 let payload = [0xCA, 0xFE, 0xBA, 0xBE];
174 let original = Packet::new(Address::Unicast(7), Address::Unicast(11), Flags::Ack(3), &payload).unwrap();
175 let mut buf = [0u8; 65];
176 let total = original.to_slice(&mut buf).unwrap();
177
178 let len_byte = buf[0];
181 let parsed = Packet::from_rx_data(len_byte, &buf[1..total as usize], -55).unwrap();
182 assert_eq!(parsed.src, original.src);
183 assert_eq!(parsed.dst, original.dst);
184 assert_eq!(parsed.flags, original.flags);
185 assert_eq!(parsed.data.as_slice(), original.data.as_slice());
186 assert_eq!(parsed.rssi, Some(-55));
187 }
188
189 #[test]
190 fn is_ack_distinguishes_flags() {
191 let none = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::None, &[]).unwrap();
192 let ack0 = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::Ack(0), &[]).unwrap();
193 let ack3 = Packet::new(Address::Unicast(1), Address::Unicast(2), Flags::Ack(3), &[]).unwrap();
194 assert!(!none.is_ack());
195 assert!(ack0.is_ack());
196 assert!(ack3.is_ack());
197 }
198}