zigbee_cluster_library/header/
frame_control.rs

1//! Frame Control
2use core::fmt;
3use core::fmt::Debug;
4use core::mem;
5
6use zigbee::internal::macros::impl_byte;
7
8impl_byte! {
9    /// See Section 2.4.1.1
10    #[derive(Clone, Copy, PartialEq, Eq)]
11    pub struct FrameControl(pub u8);
12}
13
14/// Frame Type
15///
16/// See Section 2.4.1.1.1.
17#[allow(missing_docs)]
18#[repr(u8)]
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum FrameType {
21    GlobalCommand = 0b00,
22    ClusterCommand = 0b01,
23    Reserved = 0b10,
24}
25
26impl FrameControl {
27    /// See Section 2.4.1.1.1
28    ///
29    /// Returns `true` if command is global
30    pub fn frame_type(self) -> FrameType {
31        // SAFETY: any 2 bit permutation is a valid FrameType
32        unsafe { mem::transmute((self.0 & mask::FRAME_TYPE) >> offset::FRAME_TYPE) }
33    }
34
35    /// The Manufacturer Specific field specifies whether this command refers to
36    /// a manufacturer specific extension.
37    ///
38    /// If this value is set to 1, the manufacturer code field SHALL be present
39    /// in the ``ZCLframe``.
40    ///
41    /// See Section 2.4.1.1.2
42    pub fn is_manufacturer_specific(self) -> bool {
43        ((self.0 & mask::MANUFACTURER_SPECIFIC) >> offset::MANUFACTURER_SPECIFIC) != 0
44    }
45
46    /// The direction specifies the client/server direction for this command.
47    /// If set to 1, the command is being sent from the server side of a
48    /// cluster to the client side of a cluster.
49    ///
50    /// See Section 2.4.1.1.3
51    pub fn direction(self) -> bool {
52        (self.0 & mask::DIRECTION) != 0
53    }
54
55    /// See Section 2.4.1.1.4
56    pub fn disable_default_response(self) -> bool {
57        (self.0 & mask::DEFAULT_RESPONSE) != 0
58    }
59}
60
61mod mask {
62    pub(super) const FRAME_TYPE: u8 = 0b0000_0011; // 2 bits
63    pub(super) const MANUFACTURER_SPECIFIC: u8 = 0b0000_0100; // 1 bit
64    pub(super) const DIRECTION: u8 = 0b0000_1000; // 1 bit
65    pub(super) const DEFAULT_RESPONSE: u8 = 0b0001_0000; // 1 bit
66}
67mod offset {
68    pub(super) const FRAME_TYPE: u8 = 0;
69    pub(super) const MANUFACTURER_SPECIFIC: u8 = 2;
70}
71
72impl Debug for FrameControl {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.debug_struct("FrameControl")
75            .field("frame_type", &self.frame_type())
76            .field("manufacturer_specific", &self.is_manufacturer_specific())
77            .field("direction", &self.direction())
78            .field("disable_default_response", &self.disable_default_response())
79            .finish()
80    }
81}
82
83#[cfg(test)]
84mod tests {
85
86    use byte::TryRead;
87
88    use super::*;
89
90    #[test]
91    fn unpack_frame_control() {
92        // given
93        let input = [0x18];
94
95        // when
96        let (frame_control, _) =
97            FrameControl::try_read(&input, ()).expect("Could not read FrameControl in test.");
98
99        // then
100        assert_eq!(frame_control.frame_type(), FrameType::GlobalCommand);
101        assert!(!frame_control.is_manufacturer_specific());
102        assert!(frame_control.direction());
103        assert!(frame_control.disable_default_response());
104    }
105
106    #[test]
107    fn frame_control_with_local_command() {
108        // given
109        let input = [0x19];
110
111        // when
112        let (frame_control, _) =
113            FrameControl::try_read(&input, ()).expect("Could not read FrameControl in test.");
114
115        // then
116        assert_eq!(frame_control.frame_type(), FrameType::ClusterCommand);
117        assert!(!frame_control.is_manufacturer_specific());
118        assert!(frame_control.direction());
119        assert!(frame_control.disable_default_response());
120    }
121
122    #[test]
123    fn frame_control_with_manufacturer_specific_flag() {
124        // given
125        let input = [0x1d];
126
127        // when
128        let (frame_control, _) =
129            FrameControl::try_read(&input, ()).expect("Could not read FrameControl in test.");
130
131        // then
132        assert_eq!(frame_control.frame_type(), FrameType::ClusterCommand);
133        assert!(frame_control.is_manufacturer_specific());
134        assert!(frame_control.direction());
135        assert!(frame_control.disable_default_response());
136    }
137    #[test]
138    fn frame_control_with_direction_server_to_client() {
139        // given
140        let input = [0x0d];
141
142        // when
143        let (frame_control, _) =
144            FrameControl::try_read(&input, ()).expect("Could not read FrameControl in test.");
145
146        // then
147        assert_eq!(frame_control.frame_type(), FrameType::ClusterCommand);
148        assert!(frame_control.is_manufacturer_specific());
149        assert!(frame_control.direction());
150        assert!(!frame_control.disable_default_response());
151    }
152}