Skip to main content

meterbus_wired_datalink/frame/
long.rs

1//! Variable-format long frames with user data.
2//!
3//! Long frames carry [`Control`], [`Address`], a raw control-information byte,
4//! and 1 to 252 bytes of user data.
5//!
6//! ```text
7//! +------+---+---+------+---+---+----+-----------+----------+------+
8//! | 0x68 | L | L | 0x68 | C | A | CI | user data | checksum | 0x16 |
9//! +------+---+---+------+---+---+----+-----------+----------+------+
10//! ```
11//!
12//! `L` counts `C`, `A`, `CI`, and the user-data bytes. It therefore ranges
13//! from 4 to 255. The complete encoded frame ranges from
14//! [`LongFrame::MIN_LEN`] to [`LongFrame::MAX_LEN`] bytes. The checksum is the
15//! wrapping eight-bit sum from `C` through the final user-data byte.
16//!
17//! [`LongFrame::new`] checks the control value and payload length, then copies
18//! the payload into fixed-capacity storage owned by the frame. This does not
19//! require the `alloc` feature. [`LongFrame::decode`] also checks the repeated
20//! length, start and stop bytes, exact total size, and checksum.
21//!
22//! [`LongFrame::encode_into`] is allocation-free. With the `alloc` feature,
23//! [`LongFrame::encode`] returns an allocated vector. The control-information
24//! byte and user data are not interpreted by this crate.
25//!
26//! # Example
27//!
28//! ```
29//! use meterbus_wired_datalink::{Address, Control, LongFrame};
30//!
31//! # fn main() -> Result<(), meterbus_wired_datalink::LongFrameError> {
32//! let frame = LongFrame::new(
33//!     Control::snd_ud(false),
34//!     Address::new(254),
35//!     0x50,
36//!     &[0x10],
37//! )?;
38//! let mut output = [0_u8; LongFrame::MIN_LEN];
39//! assert_eq!(
40//!     frame.encode_into(&mut output)?,
41//!     [0x68, 0x04, 0x04, 0x68, 0x53, 0xfe, 0x50, 0x10, 0xb1, 0x16],
42//! );
43//! # Ok(())
44//! # }
45//! ```
46
47use core::fmt;
48
49#[cfg(feature = "alloc")]
50use alloc::{vec, vec::Vec};
51
52use super::field::{Address, Control, ControlError};
53
54/// A variable-format frame containing user data.
55#[derive(Clone, Debug, Eq, PartialEq)]
56pub struct LongFrame {
57    control: Control,
58    address: Address,
59    control_information: u8,
60    user_data: [u8; Self::MAX_USER_DATA_LEN],
61    user_data_len: u8,
62}
63
64impl LongFrame {
65    /// Initial and repeated start byte.
66    pub const START: u8 = 0x68;
67    /// Number of checksum-covered bytes before user data.
68    pub const FIXED_DATA_LEN: usize = 3;
69    /// Minimum user-data length.
70    pub const MIN_USER_DATA_LEN: usize = 1;
71    /// Maximum user-data length permitted by the one-byte length field.
72    pub const MAX_USER_DATA_LEN: usize = 252;
73    /// Final frame byte.
74    pub const STOP: u8 = 0x16;
75    /// Minimum encoded frame length.
76    pub const MIN_LEN: usize = 10;
77    /// Maximum encoded frame length.
78    pub const MAX_LEN: usize = 261;
79
80    /// Creates a validated long frame and copies `user_data` into inline storage.
81    pub fn new(
82        control: Control,
83        address: Address,
84        control_information: u8,
85        user_data: &[u8],
86    ) -> Result<Self, LongFrameError> {
87        control
88            .validate_variable_frame()
89            .map_err(LongFrameError::Control)?;
90        if !(Self::MIN_USER_DATA_LEN..=Self::MAX_USER_DATA_LEN).contains(&user_data.len()) {
91            return Err(LongFrameError::InvalidUserDataLength {
92                actual: user_data.len(),
93            });
94        }
95        let mut stored = [0; Self::MAX_USER_DATA_LEN];
96        stored[..user_data.len()].copy_from_slice(user_data);
97        Ok(Self {
98            control,
99            address,
100            control_information,
101            user_data: stored,
102            user_data_len: user_data.len() as u8,
103        })
104    }
105
106    /// Returns the control field.
107    #[must_use]
108    pub const fn control(&self) -> Control {
109        self.control
110    }
111
112    /// Returns the address field.
113    #[must_use]
114    pub const fn address(&self) -> Address {
115        self.address
116    }
117
118    /// Returns the control-information byte.
119    #[must_use]
120    pub const fn control_information(&self) -> u8 {
121        self.control_information
122    }
123
124    /// Returns the user-data bytes.
125    #[must_use]
126    pub fn user_data(&self) -> &[u8] {
127        &self.user_data[..usize::from(self.user_data_len)]
128    }
129
130    /// Decodes exactly one long frame.
131    pub fn decode(bytes: &[u8]) -> Result<Self, LongFrameError> {
132        if bytes.len() < 4 {
133            return Err(LongFrameError::IncompleteHeader {
134                actual: bytes.len(),
135            });
136        }
137        if bytes[0] != Self::START {
138            return Err(LongFrameError::InvalidStart {
139                index: 0,
140                actual: bytes[0],
141            });
142        }
143        if bytes[1] != bytes[2] {
144            return Err(LongFrameError::MismatchedDataLengths {
145                first: bytes[1],
146                second: bytes[2],
147            });
148        }
149        if usize::from(bytes[1]) < Self::FIXED_DATA_LEN + Self::MIN_USER_DATA_LEN {
150            return Err(LongFrameError::InvalidDataLength { actual: bytes[1] });
151        }
152        let expected_len = usize::from(bytes[1]) + 6;
153        if bytes.len() != expected_len {
154            return Err(LongFrameError::InvalidLength {
155                expected: expected_len,
156                actual: bytes.len(),
157            });
158        }
159        if bytes[3] != Self::START {
160            return Err(LongFrameError::InvalidStart {
161                index: 3,
162                actual: bytes[3],
163            });
164        }
165        if bytes[expected_len - 1] != Self::STOP {
166            return Err(LongFrameError::InvalidStop {
167                actual: bytes[expected_len - 1],
168            });
169        }
170        let expected_checksum = Self::checksum(&bytes[4..expected_len - 2]);
171        let actual_checksum = bytes[expected_len - 2];
172        if actual_checksum != expected_checksum {
173            return Err(LongFrameError::InvalidChecksum {
174                expected: expected_checksum,
175                actual: actual_checksum,
176            });
177        }
178        Self::new(
179            Control::new(bytes[4]),
180            Address::new(bytes[5]),
181            bytes[6],
182            &bytes[7..expected_len - 2],
183        )
184    }
185
186    /// Encodes the frame into `output` and returns the encoded portion.
187    pub fn encode_into<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], LongFrameError> {
188        let required = self.required_len();
189        if output.len() < required {
190            return Err(LongFrameError::OutputTooSmall {
191                required,
192                actual: output.len(),
193            });
194        }
195        let data_len = (Self::FIXED_DATA_LEN + self.user_data().len()) as u8;
196        output[0] = Self::START;
197        output[1] = data_len;
198        output[2] = data_len;
199        output[3] = Self::START;
200        output[4] = self.control.value();
201        output[5] = self.address.value();
202        output[6] = self.control_information;
203        output[7..required - 2].copy_from_slice(self.user_data());
204        output[required - 2] = Self::checksum(&output[4..required - 2]);
205        output[required - 1] = Self::STOP;
206        Ok(&output[..required])
207    }
208
209    /// Encodes the frame into a newly allocated vector.
210    #[cfg(feature = "alloc")]
211    pub fn encode(&self) -> Vec<u8> {
212        let mut output = vec![0; self.required_len()];
213        let required = output.len();
214        let data_len = (Self::FIXED_DATA_LEN + self.user_data().len()) as u8;
215        output[..7].copy_from_slice(&[
216            Self::START,
217            data_len,
218            data_len,
219            Self::START,
220            self.control.value(),
221            self.address.value(),
222            self.control_information,
223        ]);
224        output[7..required - 2].copy_from_slice(self.user_data());
225        output[required - 2] = Self::checksum(&output[4..required - 2]);
226        output[required - 1] = Self::STOP;
227        output
228    }
229
230    fn required_len(&self) -> usize {
231        self.user_data().len() + 9
232    }
233
234    fn checksum(bytes: &[u8]) -> u8 {
235        bytes
236            .iter()
237            .fold(0, |checksum, byte| checksum.wrapping_add(*byte))
238    }
239}
240
241/// Error produced while constructing, encoding, or decoding a long frame.
242#[derive(Clone, Copy, Debug, Eq, PartialEq)]
243#[allow(missing_docs)]
244pub enum LongFrameError {
245    /// Fewer than four header bytes were supplied.
246    IncompleteHeader { actual: usize },
247    /// The encoded length differed from that declared by the frame.
248    InvalidLength { expected: usize, actual: usize },
249    /// A start byte was invalid.
250    InvalidStart { index: usize, actual: u8 },
251    /// The repeated length fields differed.
252    MismatchedDataLengths { first: u8, second: u8 },
253    /// The length field did not leave room for user data.
254    InvalidDataLength { actual: u8 },
255    /// The stop byte was invalid.
256    InvalidStop { actual: u8 },
257    /// The checksum did not match the frame contents.
258    InvalidChecksum { expected: u8, actual: u8 },
259    /// The control field is invalid for a variable-format frame.
260    Control(ControlError),
261    /// User data was empty or exceeded 252 bytes.
262    InvalidUserDataLength { actual: usize },
263    /// The output buffer was too small.
264    OutputTooSmall { required: usize, actual: usize },
265}
266
267impl fmt::Display for LongFrameError {
268    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
269        match self {
270            Self::IncompleteHeader { actual } => write!(
271                formatter,
272                "incomplete long frame header: expected at least 4 bytes, got {actual}"
273            ),
274            Self::InvalidLength { expected, actual } => write!(
275                formatter,
276                "invalid long frame length: expected {expected}, got {actual}"
277            ),
278            Self::InvalidStart { index, actual } => write!(
279                formatter,
280                "invalid long frame start at {index}: expected 0x68, got 0x{actual:02x}"
281            ),
282            Self::MismatchedDataLengths { first, second } => write!(
283                formatter,
284                "mismatched long frame data lengths: {first} and {second}"
285            ),
286            Self::InvalidDataLength { actual } => write!(
287                formatter,
288                "invalid long frame data length: expected at least 4, got {actual}"
289            ),
290            Self::InvalidStop { actual } => write!(
291                formatter,
292                "invalid long frame stop: expected 0x16, got 0x{actual:02x}"
293            ),
294            Self::InvalidChecksum { expected, actual } => write!(
295                formatter,
296                "invalid long frame checksum: expected 0x{expected:02x}, got 0x{actual:02x}"
297            ),
298            Self::Control(error) => error.fmt(formatter),
299            Self::InvalidUserDataLength { actual } => write!(
300                formatter,
301                "invalid long frame user-data length: expected 1 through 252, got {actual}"
302            ),
303            Self::OutputTooSmall { required, actual } => write!(
304                formatter,
305                "long frame output buffer too small: expected {required}, got {actual}"
306            ),
307        }
308    }
309}
310
311impl core::error::Error for LongFrameError {
312    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
313        match self {
314            Self::Control(error) => Some(error),
315            _ => None,
316        }
317    }
318}
319
320#[cfg(test)]
321#[cfg_attr(coverage_nightly, coverage(off))]
322mod tests {
323    use super::*;
324    #[cfg(feature = "alloc")]
325    use alloc::string::ToString;
326
327    const FRAME: [u8; 10] = [0x68, 0x04, 0x04, 0x68, 0x53, 0xfe, 0x50, 0x10, 0xb1, 0x16];
328
329    #[test]
330    fn encodes_and_decodes_known_frame() {
331        let frame = LongFrame::new(Control::new(0x53), Address::new(254), 0x50, &[0x10]).unwrap();
332        let mut output = [0; LongFrame::MAX_LEN];
333        assert_eq!(frame.encode_into(&mut output).unwrap(), FRAME);
334        assert_eq!(LongFrame::decode(&FRAME), Ok(frame.clone()));
335        assert_eq!(frame.control(), Control::new(0x53));
336        assert_eq!(frame.address(), Address::new(254));
337        assert_eq!(frame.control_information(), 0x50);
338        assert_eq!(frame.user_data(), [0x10]);
339    }
340
341    #[cfg(feature = "alloc")]
342    #[test]
343    fn allocates_encoded_frame() {
344        assert_eq!(LongFrame::decode(&FRAME).unwrap().encode(), FRAME);
345    }
346
347    #[test]
348    fn owns_payload_and_validates_boundaries() {
349        let mut source = [1, 2];
350        let frame = LongFrame::new(Control::new(0x53), Address::new(252), 0, &source).unwrap();
351        source[0] = 9;
352        assert_eq!(source[0], 9);
353        assert_eq!(frame.user_data(), [1, 2]);
354        assert_eq!(
355            LongFrame::new(Control::new(0x53), Address::new(1), 0, &[]),
356            Err(LongFrameError::InvalidUserDataLength { actual: 0 })
357        );
358        let error = LongFrameError::Control(ControlError::InvalidForVariableFrame { value: 0x40 });
359        assert!(core::error::Error::source(&error).is_some());
360        assert!(
361            core::error::Error::source(&LongFrameError::IncompleteHeader { actual: 0 }).is_none()
362        );
363        assert_eq!(
364            LongFrame::new(Control::new(0x53), Address::new(1), 0, &[0; 253]),
365            Err(LongFrameError::InvalidUserDataLength { actual: 253 })
366        );
367        assert_eq!(
368            LongFrame::new(Control::new(0x40), Address::new(1), 0, &[1]),
369            Err(LongFrameError::Control(
370                ControlError::InvalidForVariableFrame { value: 0x40 }
371            ))
372        );
373    }
374
375    #[test]
376    fn supports_maximum_frame() {
377        let frame =
378            LongFrame::new(Control::new(0x53), Address::new(255), 255, &[0xff; 252]).unwrap();
379        let mut output = [0; LongFrame::MAX_LEN];
380        let encoded = frame.encode_into(&mut output).unwrap();
381        assert_eq!(encoded.len(), LongFrame::MAX_LEN);
382        assert_eq!(encoded[1], 0xff);
383        assert_eq!(LongFrame::decode(encoded), Ok(frame));
384    }
385
386    #[test]
387    fn rejects_header_and_length_errors() {
388        assert_eq!(
389            LongFrame::decode(&[0; 3]),
390            Err(LongFrameError::IncompleteHeader { actual: 3 })
391        );
392        let mut bytes = FRAME;
393        bytes[0] = 0;
394        assert_eq!(
395            LongFrame::decode(&bytes),
396            Err(LongFrameError::InvalidStart {
397                index: 0,
398                actual: 0
399            })
400        );
401        bytes = FRAME;
402        bytes[2] = 5;
403        assert_eq!(
404            LongFrame::decode(&bytes),
405            Err(LongFrameError::MismatchedDataLengths {
406                first: 4,
407                second: 5
408            })
409        );
410        bytes = FRAME;
411        bytes[1] = 3;
412        bytes[2] = 3;
413        assert_eq!(
414            LongFrame::decode(&bytes),
415            Err(LongFrameError::InvalidDataLength { actual: 3 })
416        );
417        assert_eq!(
418            LongFrame::decode(&FRAME[..9]),
419            Err(LongFrameError::InvalidLength {
420                expected: 10,
421                actual: 9
422            })
423        );
424        let mut trailing = [0; 11];
425        trailing[..10].copy_from_slice(&FRAME);
426        assert_eq!(
427            LongFrame::decode(&trailing),
428            Err(LongFrameError::InvalidLength {
429                expected: 10,
430                actual: 11
431            })
432        );
433    }
434
435    #[test]
436    fn rejects_body_errors() {
437        let mut bytes = FRAME;
438        bytes[3] = 0;
439        assert_eq!(
440            LongFrame::decode(&bytes),
441            Err(LongFrameError::InvalidStart {
442                index: 3,
443                actual: 0
444            })
445        );
446        bytes = FRAME;
447        bytes[9] = 0;
448        assert_eq!(
449            LongFrame::decode(&bytes),
450            Err(LongFrameError::InvalidStop { actual: 0 })
451        );
452        bytes = FRAME;
453        bytes[8] = 0;
454        assert_eq!(
455            LongFrame::decode(&bytes),
456            Err(LongFrameError::InvalidChecksum {
457                expected: 0xb1,
458                actual: 0
459            })
460        );
461        bytes = [0x68, 4, 4, 0x68, 0x40, 1, 0, 1, 0x42, 0x16];
462        assert_eq!(
463            LongFrame::decode(&bytes),
464            Err(LongFrameError::Control(
465                ControlError::InvalidForVariableFrame { value: 0x40 }
466            ))
467        );
468    }
469
470    #[test]
471    fn rejects_small_output() {
472        let frame = LongFrame::decode(&FRAME).unwrap();
473        assert_eq!(
474            frame.encode_into(&mut [0; 9]),
475            Err(LongFrameError::OutputTooSmall {
476                required: 10,
477                actual: 9
478            })
479        );
480    }
481
482    #[cfg(feature = "alloc")]
483    #[test]
484    fn formats_errors() {
485        let errors = [
486            (
487                LongFrameError::IncompleteHeader { actual: 3 }.to_string(),
488                "incomplete long frame header: expected at least 4 bytes, got 3",
489            ),
490            (
491                LongFrameError::InvalidLength {
492                    expected: 10,
493                    actual: 9,
494                }
495                .to_string(),
496                "invalid long frame length: expected 10, got 9",
497            ),
498            (
499                LongFrameError::InvalidStart {
500                    index: 3,
501                    actual: 0,
502                }
503                .to_string(),
504                "invalid long frame start at 3: expected 0x68, got 0x00",
505            ),
506            (
507                LongFrameError::MismatchedDataLengths {
508                    first: 4,
509                    second: 5,
510                }
511                .to_string(),
512                "mismatched long frame data lengths: 4 and 5",
513            ),
514            (
515                LongFrameError::InvalidDataLength { actual: 3 }.to_string(),
516                "invalid long frame data length: expected at least 4, got 3",
517            ),
518            (
519                LongFrameError::InvalidStop { actual: 0 }.to_string(),
520                "invalid long frame stop: expected 0x16, got 0x00",
521            ),
522            (
523                LongFrameError::InvalidChecksum {
524                    expected: 1,
525                    actual: 2,
526                }
527                .to_string(),
528                "invalid long frame checksum: expected 0x01, got 0x02",
529            ),
530            (
531                LongFrameError::Control(ControlError::InvalidForVariableFrame { value: 0x40 })
532                    .to_string(),
533                "control value 0x40 is invalid for a variable-format frame",
534            ),
535            (
536                LongFrameError::InvalidUserDataLength { actual: 0 }.to_string(),
537                "invalid long frame user-data length: expected 1 through 252, got 0",
538            ),
539            (
540                LongFrameError::OutputTooSmall {
541                    required: 10,
542                    actual: 9,
543                }
544                .to_string(),
545                "long frame output buffer too small: expected 10, got 9",
546            ),
547        ];
548        for (actual, expected) in errors {
549            assert_eq!(actual, expected);
550        }
551    }
552}