bacnet_emb/network_protocol/
data_link.rs

1use crate::{
2    application_protocol::{application_pdu::ApplicationPdu, confirmed::ConfirmedRequest},
3    common::{
4        error::Error,
5        io::{Reader, Writer},
6    },
7    network_protocol::network_pdu::{MessagePriority, NetworkMessage, NetworkPdu},
8};
9
10// Bacnet Virtual Link Control
11#[derive(Debug, Clone)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct DataLink<'a> {
14    pub function: DataLinkFunction,
15    pub npdu: Option<NetworkPdu<'a>>,
16}
17
18#[derive(Debug, Clone)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[repr(u8)]
21pub enum DataLinkFunction {
22    Result = 0,
23    WriteBroadcastDistributionTable = 1,
24    ReadBroadcastDistTable = 2,
25    ReadBroadcastDistTableAck = 3,
26    ForwardedNpdu = 4,
27    RegisterForeignDevice = 5,
28    ReadForeignDeviceTable = 6,
29    ReadForeignDeviceTableAck = 7,
30    DeleteForeignDeviceTableEntry = 8,
31    DistributeBroadcastToNetwork = 9,
32    OriginalUnicastNpdu = 10,
33    OriginalBroadcastNpdu = 11,
34}
35
36impl TryFrom<u8> for DataLinkFunction {
37    type Error = u8;
38
39    fn try_from(value: u8) -> Result<Self, Self::Error> {
40        match value {
41            0 => Ok(Self::Result),
42            1 => Ok(Self::WriteBroadcastDistributionTable),
43            2 => Ok(Self::ReadBroadcastDistTable),
44            3 => Ok(Self::ReadBroadcastDistTableAck),
45            4 => Ok(Self::ForwardedNpdu),
46            5 => Ok(Self::RegisterForeignDevice),
47            6 => Ok(Self::ReadForeignDeviceTable),
48            7 => Ok(Self::ReadForeignDeviceTableAck),
49            8 => Ok(Self::DeleteForeignDeviceTableEntry),
50            9 => Ok(Self::DistributeBroadcastToNetwork),
51            10 => Ok(Self::OriginalUnicastNpdu),
52            11 => Ok(Self::OriginalBroadcastNpdu),
53            x => Err(x),
54        }
55    }
56}
57
58const BVLL_TYPE_BACNET_IP: u8 = 0x81;
59
60impl<'a> DataLink<'a> {
61    //    const BVLC_ORIGINAL_UNICAST_NPDU: u8 = 10;
62    //    const BVLC_ORIGINAL_BROADCAST_NPDU: u8 = 11;
63
64    pub fn new(function: DataLinkFunction, npdu: Option<NetworkPdu<'a>>) -> Self {
65        Self { function, npdu }
66    }
67
68    pub fn new_confirmed_req(req: ConfirmedRequest<'a>) -> Self {
69        let apdu = ApplicationPdu::ConfirmedRequest(req);
70        let message = NetworkMessage::Apdu(apdu);
71        let npdu = NetworkPdu::new(None, None, true, MessagePriority::Normal, message);
72        DataLink::new(DataLinkFunction::OriginalUnicastNpdu, Some(npdu))
73    }
74
75    pub fn encode(&self, writer: &mut Writer) {
76        writer.push(BVLL_TYPE_BACNET_IP);
77        writer.push(self.function.clone() as u8);
78        match &self.function {
79            DataLinkFunction::OriginalBroadcastNpdu | DataLinkFunction::OriginalUnicastNpdu => {
80                writer.extend_from_slice(&[0, 0]); // length placeholder
81                self.npdu.as_ref().unwrap().encode(writer); // should be ok to unwrap here since it has already been checked
82                Self::update_len(writer);
83            }
84            _ => todo!(),
85        }
86    }
87
88    fn update_len(writer: &mut Writer) {
89        let len = writer.index as u16;
90        let src = len.to_be_bytes();
91        writer.buf[2..4].copy_from_slice(&src);
92    }
93
94    #[cfg_attr(feature = "alloc", bacnet_macros::remove_lifetimes_from_fn_args)]
95    pub fn decode(reader: &mut Reader, buf: &'a [u8]) -> Result<Self, Error> {
96        let bvll_type = reader.read_byte(buf)?;
97        if bvll_type != BVLL_TYPE_BACNET_IP {
98            return Err(Error::InvalidValue("only BACNET_IP supported"));
99        }
100
101        let function = reader
102            .read_byte(buf)?
103            .try_into()
104            .map_err(|_| Error::InvalidValue("bvll function value out of range"))?;
105        let len: u16 = u16::from_be_bytes(reader.read_bytes(buf)?);
106
107        if len as usize > buf.len() {
108            return Err(Error::Length((
109                "read buffer too small to fit entire bacnet payload",
110                len as u32,
111            )));
112        }
113        reader.set_len(len as usize);
114
115        let npdu = match function {
116            // see h_bbmd.c for all the types (only 2 are supported here)
117            DataLinkFunction::OriginalBroadcastNpdu | DataLinkFunction::OriginalUnicastNpdu => {
118                Some(NetworkPdu::decode(reader, buf)?)
119            }
120            _ => None,
121        };
122
123        Ok(Self { function, npdu })
124    }
125}