Skip to main content

knx_core/
apci.rs

1use core::convert::TryFrom;
2
3use crate::{KnxError, Result};
4
5const APCI_MASK: u8 = 0xc0;
6const GROUP_VALUE_READ_BITS: u8 = 0x00;
7const GROUP_VALUE_RESPONSE_BITS: u8 = 0x40;
8const GROUP_VALUE_WRITE_BITS: u8 = 0x80;
9
10/// Only group-value services are modeled; all other APCI codes decode as `InvalidFrame`
11/// (intentional scope limit).
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum Apci {
14    /// Asks the group to state its value. It is the 1 group service that
15    /// carries no data of its own.
16    GroupValueRead,
17    /// States the group's value in answer to a read.
18    GroupValueResponse,
19    /// States a new value for the group, unprompted.
20    GroupValueWrite,
21}
22
23impl Apci {
24    /// The service bits this variant sets in the APDU's second octet: the top
25    /// 2 bits, which is what tells these 3 services apart.
26    ///
27    /// The low 6 bits are left clear here, because that is where the
28    /// optimized form's data goes - a payload the caller has already decided
29    /// the form of is placed over them.
30    pub const fn service_bits(self) -> u8 {
31        match self {
32            Self::GroupValueRead => GROUP_VALUE_READ_BITS,
33            Self::GroupValueResponse => GROUP_VALUE_RESPONSE_BITS,
34            Self::GroupValueWrite => GROUP_VALUE_WRITE_BITS,
35        }
36    }
37}
38
39impl TryFrom<u8> for Apci {
40    type Error = KnxError;
41
42    fn try_from(value: u8) -> Result<Self> {
43        match value & APCI_MASK {
44            GROUP_VALUE_READ_BITS => Ok(Self::GroupValueRead),
45            GROUP_VALUE_RESPONSE_BITS => Ok(Self::GroupValueResponse),
46            GROUP_VALUE_WRITE_BITS => Ok(Self::GroupValueWrite),
47            _ => Err(KnxError::InvalidFrame("unsupported APCI service")),
48        }
49    }
50}