1use crate::error::{S101Error, Result};
4use super::constants::*;
5use super::crc;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum MessageType {
11 Ember = 0x0E,
13}
14
15impl TryFrom<u8> for MessageType {
16 type Error = S101Error;
17
18 fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
19 match value {
20 0x0E => Ok(MessageType::Ember),
21 _ => Err(S101Error::InvalidMessageType(value)),
22 }
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28#[repr(u8)]
29pub enum Command {
30 Ember = 0x00,
32 KeepAliveRequest = 0x01,
34 KeepAliveResponse = 0x02,
36}
37
38impl TryFrom<u8> for Command {
39 type Error = S101Error;
40
41 fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
42 match value {
43 0x00 => Ok(Command::Ember),
44 0x01 => Ok(Command::KeepAliveRequest),
45 0x02 => Ok(Command::KeepAliveResponse),
46 _ => Err(S101Error::InvalidCommand(value)),
47 }
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
53pub struct EmberFlags {
54 pub first_packet: bool,
56 pub last_packet: bool,
58 pub empty_packet: bool,
60}
61
62impl EmberFlags {
63 pub fn single_packet() -> Self {
65 EmberFlags {
66 first_packet: true,
67 last_packet: true,
68 empty_packet: false,
69 }
70 }
71
72 pub fn encode(&self) -> u8 {
74 let mut flags = 0u8;
75 if self.first_packet {
76 flags |= 0x80;
77 }
78 if self.last_packet {
79 flags |= 0x40;
80 }
81 if self.empty_packet {
82 flags |= 0x20;
83 }
84 flags
85 }
86
87 pub fn decode(byte: u8) -> Self {
89 EmberFlags {
90 first_packet: (byte & 0x80) != 0,
91 last_packet: (byte & 0x40) != 0,
92 empty_packet: (byte & 0x20) != 0,
93 }
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum S101Message {
100 EmberData {
102 dtd_type: u8,
104 app_bytes: Vec<u8>,
106 flags: EmberFlags,
108 payload: Vec<u8>,
110 },
111 KeepAliveRequest,
113 KeepAliveResponse,
115}
116
117impl S101Message {
118 pub fn ember_data(payload: Vec<u8>) -> Self {
120 S101Message::EmberData {
121 dtd_type: 0x01, app_bytes: vec![0x01, 0x31], flags: EmberFlags::single_packet(),
124 payload,
125 }
126 }
127
128 pub fn is_keepalive_request(&self) -> bool {
130 matches!(self, S101Message::KeepAliveRequest)
131 }
132
133 pub fn is_keepalive_response(&self) -> bool {
135 matches!(self, S101Message::KeepAliveResponse)
136 }
137
138 pub fn payload(&self) -> Option<&[u8]> {
140 match self {
141 S101Message::EmberData { payload, .. } => Some(payload),
142 _ => None,
143 }
144 }
145}
146
147#[derive(Debug, Clone)]
149pub struct S101Frame {
150 pub slot: u8,
152 pub message_type: MessageType,
154 pub command: Command,
156 pub payload: Vec<u8>,
158}
159
160impl S101Frame {
161 pub fn ember_data(payload: Vec<u8>) -> Self {
163 S101Frame {
164 slot: 0,
165 message_type: MessageType::Ember,
166 command: Command::Ember,
167 payload,
168 }
169 }
170
171 pub fn keepalive_request() -> Self {
173 S101Frame {
174 slot: 0,
175 message_type: MessageType::Ember,
176 command: Command::KeepAliveRequest,
177 payload: vec![],
178 }
179 }
180
181 pub fn keepalive_response() -> Self {
183 S101Frame {
184 slot: 0,
185 message_type: MessageType::Ember,
186 command: Command::KeepAliveResponse,
187 payload: vec![],
188 }
189 }
190
191 pub fn encode(&self) -> Vec<u8> {
193 let mut output = Vec::new();
194 output.push(BOF);
195
196 let mut content = Vec::new();
198
199 content.push(self.slot);
201 content.push(self.message_type as u8);
202 content.push(self.command as u8);
203
204 match self.command {
207 Command::KeepAliveRequest | Command::KeepAliveResponse => {
208 content.push(0x01); }
210 Command::Ember => {
211 content.push(0x01); content.push(0xC0); content.push(0x01); content.push(0x02); content.push(0x1F); content.push(0x02); content.extend(&self.payload);
218 }
219 }
220
221 for &byte in &content {
223 if byte >= S101_INV {
224 output.push(ESCAPE_BYTE);
225 output.push(byte ^ ESCAPE_XOR);
226 } else {
227 output.push(byte);
228 }
229 }
230
231 let crc = crc::calculate_crc_escaped(&output[1..]);
233 let crc_lo = (crc & 0xFF) as u8;
234 let crc_hi = ((crc >> 8) & 0xFF) as u8;
235
236 if crc_lo >= S101_INV {
238 output.push(ESCAPE_BYTE);
239 output.push(crc_lo ^ ESCAPE_XOR);
240 } else {
241 output.push(crc_lo);
242 }
243 if crc_hi >= S101_INV {
244 output.push(ESCAPE_BYTE);
245 output.push(crc_hi ^ ESCAPE_XOR);
246 } else {
247 output.push(crc_hi);
248 }
249
250 output.push(EOF);
251 output
252 }
253
254 pub fn decode(data: &[u8]) -> Result<Self> {
256 if data.len() < 2 {
257 return Err(S101Error::IncompleteFrame.into());
258 }
259
260 if data[0] != BOF {
262 return Err(S101Error::InvalidFrameMarker(data[0]).into());
263 }
264 if data[data.len() - 1] != EOF {
265 return Err(S101Error::InvalidFrameMarker(data[data.len() - 1]).into());
266 }
267
268 let inner_data = &data[1..data.len() - 1];
270 let mut unescaped = Vec::with_capacity(inner_data.len());
271 let mut i = 0;
272
273 while i < inner_data.len() {
274 if inner_data[i] == ESCAPE_BYTE {
275 if i + 1 >= inner_data.len() {
276 return Err(S101Error::InvalidEscapeSequence.into());
277 }
278 unescaped.push(inner_data[i + 1] ^ ESCAPE_XOR);
279 i += 2;
280 } else {
281 unescaped.push(inner_data[i]);
282 i += 1;
283 }
284 }
285
286 if !crc::verify_crc(&unescaped) {
288 let len = unescaped.len();
289 if len >= 2 {
290 let received = u16::from_be_bytes([unescaped[len - 2], unescaped[len - 1]]);
291 let calculated = crc::calculate_crc(&unescaped[..len - 2]);
292 return Err(S101Error::CrcMismatch {
293 expected: calculated,
294 actual: received,
295 }.into());
296 }
297 return Err(S101Error::IncompleteFrame.into());
298 }
299
300 let unescaped = &unescaped[..unescaped.len() - 2];
302
303 if unescaped.len() < 3 {
304 return Err(S101Error::IncompleteFrame.into());
305 }
306
307 let slot = unescaped[SLOT_OFFSET];
308 let message_type = MessageType::try_from(unescaped[MESSAGE_TYPE_OFFSET])?;
309 let command = Command::try_from(unescaped[COMMAND_OFFSET])?;
310
311 let payload = match command {
313 Command::KeepAliveRequest | Command::KeepAliveResponse => {
314 vec![]
316 }
317 Command::Ember => {
318 if unescaped.len() < 7 {
320 return Err(S101Error::IncompleteFrame.into());
321 }
322 let app_bytes_count = unescaped[6] as usize;
323 let payload_start = 7 + app_bytes_count;
324 if payload_start > unescaped.len() {
325 return Err(S101Error::IncompleteFrame.into());
326 }
327 unescaped[payload_start..].to_vec()
328 }
329 };
330
331 Ok(S101Frame {
332 slot,
333 message_type,
334 command,
335 payload,
336 })
337 }
338
339 pub fn to_message(&self) -> S101Message {
341 match self.command {
342 Command::Ember => S101Message::EmberData {
343 dtd_type: 0x01,
344 app_bytes: vec![0x1F, 0x02],
345 flags: EmberFlags::single_packet(),
346 payload: self.payload.clone(),
347 },
348 Command::KeepAliveRequest => S101Message::KeepAliveRequest,
349 Command::KeepAliveResponse => S101Message::KeepAliveResponse,
350 }
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357
358 #[test]
359 fn test_frame_roundtrip() {
360 let frame = S101Frame::ember_data(vec![0x01, 0x02, 0x03, 0x04]);
361 let encoded = frame.encode();
362 let decoded = S101Frame::decode(&encoded).unwrap();
363
364 assert_eq!(frame.slot, decoded.slot);
365 assert_eq!(frame.message_type, decoded.message_type);
366 assert_eq!(frame.command, decoded.command);
367 assert_eq!(frame.payload, decoded.payload);
368 }
369
370 #[test]
371 fn test_keepalive_frame() {
372 let request = S101Frame::keepalive_request();
373 let encoded = request.encode();
374 let decoded = S101Frame::decode(&encoded).unwrap();
375
376 assert_eq!(decoded.message_type, MessageType::Ember);
377 assert_eq!(decoded.command, Command::KeepAliveRequest);
378 }
379
380 #[test]
381 fn test_escape_sequences() {
382 let frame = S101Frame::ember_data(vec![BOF, ESCAPE_BYTE, 0x00, 0x1F]);
384 let encoded = frame.encode();
385 let decoded = S101Frame::decode(&encoded).unwrap();
386
387 assert_eq!(frame.payload, decoded.payload);
388 }
389}