embedded_bacnet/network_protocol/
data_link.rs1use 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#[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 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]); self.npdu.as_ref().unwrap().encode(writer); 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 DataLinkFunction::OriginalBroadcastNpdu | DataLinkFunction::OriginalUnicastNpdu => {
118 Some(NetworkPdu::decode(reader, buf)?)
119 }
120 _ => None,
121 };
122
123 Ok(Self { function, npdu })
124 }
125}