embedded_bacnet/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};
8
9use super::network_pdu::{MessagePriority, NetworkMessage, NetworkPdu};
10
11// Bacnet Virtual Link Control
12#[derive(Debug, Clone)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub struct DataLink<'a> {
15    pub function: DataLinkFunction,
16    pub npdu: Option<NetworkPdu<'a>>,
17}
18
19#[derive(Debug, Clone)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[repr(u8)]
22pub enum DataLinkFunction {
23    Result = 0,
24    WriteBroadcastDistributionTable = 1,
25    ReadBroadcastDistTable = 2,
26    ReadBroadcastDistTableAck = 3,
27    ForwardedNpdu = 4,
28    RegisterForeignDevice = 5,
29    ReadForeignDeviceTable = 6,
30    ReadForeignDeviceTableAck = 7,
31    DeleteForeignDeviceTableEntry = 8,
32    DistributeBroadcastToNetwork = 9,
33    OriginalUnicastNpdu = 10,
34    OriginalBroadcastNpdu = 11,
35}
36
37impl TryFrom<u8> for DataLinkFunction {
38    type Error = u8;
39
40    fn try_from(value: u8) -> Result<Self, Self::Error> {
41        match value {
42            0 => Ok(Self::Result),
43            1 => Ok(Self::WriteBroadcastDistributionTable),
44            2 => Ok(Self::ReadBroadcastDistTable),
45            3 => Ok(Self::ReadBroadcastDistTableAck),
46            4 => Ok(Self::ForwardedNpdu),
47            5 => Ok(Self::RegisterForeignDevice),
48            6 => Ok(Self::ReadForeignDeviceTable),
49            7 => Ok(Self::ReadForeignDeviceTableAck),
50            8 => Ok(Self::DeleteForeignDeviceTableEntry),
51            9 => Ok(Self::DistributeBroadcastToNetwork),
52            10 => Ok(Self::OriginalUnicastNpdu),
53            11 => Ok(Self::OriginalBroadcastNpdu),
54            x => Err(x),
55        }
56    }
57}
58
59const BVLL_TYPE_BACNET_IP: u8 = 0x81;
60
61impl<'a> DataLink<'a> {
62    //    const BVLC_ORIGINAL_UNICAST_NPDU: u8 = 10;
63    //    const BVLC_ORIGINAL_BROADCAST_NPDU: u8 = 11;
64
65    pub fn new(function: DataLinkFunction, npdu: Option<NetworkPdu<'a>>) -> Self {
66        Self { function, npdu }
67    }
68
69    pub fn new_confirmed_req(req: ConfirmedRequest<'a>) -> Self {
70        let apdu = ApplicationPdu::ConfirmedRequest(req);
71        let message = NetworkMessage::Apdu(apdu);
72        let npdu = NetworkPdu::new(None, None, true, MessagePriority::Normal, message);
73        DataLink::new(DataLinkFunction::OriginalUnicastNpdu, Some(npdu))
74    }
75
76    pub fn encode(&self, writer: &mut Writer) {
77        writer.push(BVLL_TYPE_BACNET_IP);
78        writer.push(self.function.clone() as u8);
79        match &self.function {
80            DataLinkFunction::OriginalBroadcastNpdu | DataLinkFunction::OriginalUnicastNpdu => {
81                writer.extend_from_slice(&[0, 0]); // length placeholder
82                self.npdu.as_ref().unwrap().encode(writer); // should be ok to unwrap here since it has already been checked
83                Self::update_len(writer);
84            }
85            _ => todo!(),
86        }
87    }
88
89    fn update_len(writer: &mut Writer) {
90        let len = writer.index as u16;
91        let src = len.to_be_bytes();
92        writer.buf[2..4].copy_from_slice(&src);
93    }
94
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}