Skip to main content

twine_codec/radio/
channel.rs

1// Copyright (c) 2025 Jake Swensen
2// SPDX-License-Identifier: MPL-2.0
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8use bytes::{Buf, BufMut};
9use typed_builder::TypedBuilder;
10
11use twine_rs_macros::Tlv;
12use twine_tlv::prelude::*;
13
14use crate::error::TwineCodecError;
15
16/// IEEE 802.15.4 channel
17#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Tlv, TypedBuilder)]
18#[tlv(tlv_type = 0x00, tlv_length = 3)]
19pub struct Channel {
20    channel: u16,
21    page: u8,
22}
23
24impl Channel {
25    /// Generate a random page 0 channel between 11 and 26
26    pub fn random() -> Self {
27        let channel = crate::random_range_u16(11..=26);
28        let page: u8 = 0;
29        Self { channel, page }
30    }
31
32    pub fn new(page: u8, channel: u16) -> Self {
33        Self { channel, page }
34    }
35
36    pub fn channel(&self) -> u16 {
37        self.channel
38    }
39
40    pub fn page(&self) -> u8 {
41        self.page
42    }
43
44    pub fn from_str_channel_only(s: &str) -> Result<Self, TwineCodecError> {
45        let channel = s
46            .parse::<u16>()
47            .map_err(|_| TwineCodecError::StringParseError)?;
48        Ok(Self::new(0, channel))
49    }
50}
51
52impl DecodeTlvValueUnchecked for Channel {
53    fn decode_tlv_value_unchecked(buffer: impl AsRef<[u8]>) -> Self {
54        let mut buffer = buffer.as_ref();
55        let page = buffer.get_u8();
56        let channel = buffer.get_u16();
57
58        Self { channel, page }
59    }
60}
61
62impl TryEncodeTlvValue for Channel {
63    fn try_encode_tlv_value(&self, buffer: &mut [u8]) -> Result<usize, TwineTlvError> {
64        let mut buffer = buffer;
65        buffer.put_u8(self.page());
66        buffer.put_u16(self.channel());
67        Ok(self.tlv_len())
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use std::println;
74
75    use super::*;
76
77    const CHANNEL_TLV_BYTES: [u8; 5] = [0x00, 0x03, 0x00, 0x00, 0x16];
78
79    #[test]
80    fn test_channel_random() {
81        let channel = Channel::random();
82        assert!(channel.channel() >= 11 && channel.channel() <= 26);
83        assert_eq!(channel.page(), 0);
84    }
85
86    #[test]
87    fn success_decode_unchecked_meshcop_tlv_for_channel() {
88        let test = Channel::decode_tlv_unchecked(CHANNEL_TLV_BYTES);
89        assert_eq!(test.page(), 0);
90        assert_eq!(test.channel(), 22);
91    }
92
93    #[test]
94    fn success_try_encode_meshcop_tlv_for_channel() {
95        let channel = Channel::new(0, 22);
96        let mut test_buffer = [0_u8; 10];
97        let bytes_written = channel
98            .try_encode_tlv(&mut test_buffer)
99            .expect("Could not encode Channel");
100        println!("Encoded Channel TLV: {:?}", &test_buffer[..]);
101        assert_eq!(bytes_written, Channel::tlv_total_constant_len());
102        assert_eq!(CHANNEL_TLV_BYTES.as_ref(), &test_buffer[..5]);
103    }
104
105    #[test]
106    fn success_from_str_channel_only() {
107        let channel =
108            Channel::from_str_channel_only("16").expect("Could not parse Channel from string");
109        assert_eq!(channel.page(), 0);
110        assert_eq!(channel.channel(), 16);
111    }
112}