Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ serde = { version = "1.0", default-features = false, features = [
maybe-async = { version = "0.2.10", default-features = false }
bacnet-macros = { path = "./bacnet-macros", version = "0.1.0" }

# https://github.com/cbiffle/corncobs/pull/5
corncobs = { git = "https://github.com/de-vri-es/corncobs", rev = "68025686e0547fac483baabb63c8066065023d88" }
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be a crates.io dependency rather


# TODO: add this when it reaches 1.0
# derive_more = { version = "^1.0.0-beta.6", default-features = false, features = [ "try_from"] }

Expand Down
6 changes: 3 additions & 3 deletions examples/troubleshoot.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
// use this example to troubleshoot an invalid BACnet packet
// RUST_BACKTRACE=1 cargo run --example troubleshoot

use embedded_bacnet::{common::io::Reader, network_protocol::data_link::DataLink};
use embedded_bacnet::{common::io::Reader, network_protocol::ip::IpFrame};

fn main() {
// a valid BACnet packet
let buf = vec![
129, 10, 0, 21, 1, 4, 2, 117, 1, 14, 12, 2, 0, 3, 243, 30, 9, 56, 9, 57, 31,
];

// use the DataLink codec to decode the bytes
// decode the bytes as BACnet/IP frame
let mut reader = Reader::default();

// if the packet is invalid this should panid
let message = DataLink::decode(&mut reader, &buf).unwrap();
let message = IpFrame::decode(&mut reader, &buf).unwrap();

println!("{:?}", message)
}
6 changes: 3 additions & 3 deletions examples/who_is_broadcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use embedded_bacnet::{
},
common::io::{Reader, Writer},
network_protocol::{
data_link::{DataLink, DataLinkFunction},
ip::{BvllFunction, IpFrame},
network_pdu::{DestinationAddress, MessagePriority, NetworkMessage, NetworkPdu},
},
};
Expand Down Expand Up @@ -58,7 +58,7 @@ fn main() -> Result<(), Error> {
let dst = Some(DestinationAddress::new(0xffff, None));
let message = NetworkMessage::Apdu(apdu);
let npdu = NetworkPdu::new(None, dst, false, MessagePriority::Normal, message);
let data_link = DataLink::new(DataLinkFunction::OriginalBroadcastNpdu, Some(npdu));
let data_link = IpFrame::new(BvllFunction::OriginalBroadcastNpdu, Some(npdu));

let mut buffer = vec![0; 1500];

Expand All @@ -75,7 +75,7 @@ fn main() -> Result<(), Error> {
let payload = &buffer[..n];
println!("Received: {:02x?} from {:?}", payload, peer);
let mut reader = Reader::default();
let message = DataLink::decode(&mut reader, payload);
let message = IpFrame::decode(&mut reader, payload);
println!("Decoded: {:?}\n", message);
}
}
62 changes: 55 additions & 7 deletions src/application_protocol/confirmed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
spec::{ErrorClass, ErrorCode},
tag::{ApplicationTagNumber, Tag, TagNumber},
},
network_protocol::{data_link::DataLink, network_pdu::NetworkMessage},
network_protocol::{ip::IpFrame, mstp::MstpFrame, network_pdu::NetworkMessage},
};

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -221,10 +221,26 @@ pub struct SimpleAck {
pub service_choice: ConfirmedServiceChoice,
}

impl<'a> TryFrom<DataLink<'a>> for SimpleAck {
impl<'a> TryFrom<IpFrame<'a>> for SimpleAck {
type Error = Error;

fn try_from(value: DataLink<'a>) -> Result<Self, Self::Error> {
fn try_from(value: IpFrame<'a>) -> Result<Self, Self::Error> {
match value.npdu {
Some(x) => match x.network_message {
NetworkMessage::Apdu(ApplicationPdu::SimpleAck(ack)) => Ok(ack),
_ => Err(Error::ConvertDataLink(
"npdu message is not an apdu simple ack",
)),
},
_ => Err(Error::ConvertDataLink("no npdu defined in message")),
}
}
}

impl<'a> TryFrom<MstpFrame<'a>> for SimpleAck {
type Error = Error;

fn try_from(value: MstpFrame<'a>) -> Result<Self, Self::Error> {
match value.npdu {
Some(x) => match x.network_message {
NetworkMessage::Apdu(ApplicationPdu::SimpleAck(ack)) => Ok(ack),
Expand Down Expand Up @@ -315,10 +331,26 @@ pub struct ComplexAck<'a> {
pub service: ComplexAckService<'a>,
}

impl<'a> TryFrom<DataLink<'a>> for ComplexAck<'a> {
impl<'a> TryFrom<IpFrame<'a>> for ComplexAck<'a> {
type Error = Error;

fn try_from(value: IpFrame<'a>) -> Result<Self, Self::Error> {
match value.npdu {
Some(x) => match x.network_message {
NetworkMessage::Apdu(ApplicationPdu::ComplexAck(ack)) => Ok(ack),
_ => Err(Error::ConvertDataLink(
"npdu message is not an apdu complex ack",
)),
},
_ => Err(Error::ConvertDataLink("no npdu defined in message")),
}
}
}

impl<'a> TryFrom<MstpFrame<'a>> for ComplexAck<'a> {
type Error = Error;

fn try_from(value: DataLink<'a>) -> Result<Self, Self::Error> {
fn try_from(value: MstpFrame<'a>) -> Result<Self, Self::Error> {
match value.npdu {
Some(x) => match x.network_message {
NetworkMessage::Apdu(ApplicationPdu::ComplexAck(ack)) => Ok(ack),
Expand Down Expand Up @@ -442,10 +474,26 @@ pub struct SegmentAck {
pub proposed_window_size: u8,
}

impl<'a> TryFrom<DataLink<'a>> for SegmentAck {
impl<'a> TryFrom<IpFrame<'a>> for SegmentAck {
type Error = Error;

fn try_from(value: IpFrame<'a>) -> Result<Self, Self::Error> {
match value.npdu {
Some(x) => match x.network_message {
NetworkMessage::Apdu(ApplicationPdu::SegmentAck(ack)) => Ok(ack),
_ => Err(Error::ConvertDataLink(
"npdu message is not an apdu simple ack",
)),
},
_ => Err(Error::ConvertDataLink("no npdu defined in message")),
}
}
}

impl<'a> TryFrom<MstpFrame<'a>> for SegmentAck {
type Error = Error;

fn try_from(value: DataLink<'a>) -> Result<Self, Self::Error> {
fn try_from(value: MstpFrame<'a>) -> Result<Self, Self::Error> {
match value.npdu {
Some(x) => match x.network_message {
NetworkMessage::Apdu(ApplicationPdu::SegmentAck(ack)) => Ok(ack),
Expand Down
20 changes: 17 additions & 3 deletions src/application_protocol/services/read_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
spec::BACNET_ARRAY_ALL,
tag::{ApplicationTagNumber, Tag, TagNumber},
},
network_protocol::data_link::DataLink,
network_protocol::{ip::IpFrame, mstp::MstpFrame},
};

#[cfg(feature = "alloc")]
Expand Down Expand Up @@ -155,10 +155,24 @@ pub struct ReadPropertyAck<'a> {
pub property_value: ReadPropertyValue<'a>,
}

impl<'a> TryFrom<DataLink<'a>> for ReadPropertyAck<'a> {
impl<'a> TryFrom<IpFrame<'a>> for ReadPropertyAck<'a> {
type Error = Error;

fn try_from(value: DataLink<'a>) -> Result<Self, Self::Error> {
fn try_from(value: IpFrame<'a>) -> Result<Self, Self::Error> {
let ack: ComplexAck = value.try_into()?;
match ack.service {
ComplexAckService::ReadProperty(ack) => Ok(ack),
_ => Err(Error::ConvertDataLink(
"apdu message is not a ComplexAckService ReadPropertyAck",
)),
}
}
}

impl<'a> TryFrom<MstpFrame<'a>> for ReadPropertyAck<'a> {
type Error = Error;

fn try_from(value: MstpFrame<'a>) -> Result<Self, Self::Error> {
let ack: ComplexAck = value.try_into()?;
match ack.service {
ComplexAckService::ReadProperty(ack) => Ok(ack),
Expand Down
20 changes: 17 additions & 3 deletions src/application_protocol/services/read_property_multiple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
spec::{ErrorClass, ErrorCode, BACNET_ARRAY_ALL},
tag::{ApplicationTagNumber, Tag, TagNumber},
},
network_protocol::data_link::DataLink,
network_protocol::{ip::IpFrame, mstp::MstpFrame},
};

#[cfg(feature = "alloc")]
Expand Down Expand Up @@ -57,10 +57,24 @@ impl<'a> IntoIterator for &'_ ReadPropertyMultipleAck<'a> {
}
}

impl<'a> TryFrom<DataLink<'a>> for ReadPropertyMultipleAck<'a> {
impl<'a> TryFrom<IpFrame<'a>> for ReadPropertyMultipleAck<'a> {
type Error = Error;

fn try_from(value: DataLink<'a>) -> Result<Self, Self::Error> {
fn try_from(value: IpFrame<'a>) -> Result<Self, Self::Error> {
let ack: ComplexAck = value.try_into()?;
match ack.service {
ComplexAckService::ReadPropertyMultiple(ack) => Ok(ack),
_ => Err(Error::ConvertDataLink(
"apdu message is not a ComplexAckService ReadPropertyMultipleAck",
)),
}
}
}

impl<'a> TryFrom<MstpFrame<'a>> for ReadPropertyMultipleAck<'a> {
type Error = Error;

fn try_from(value: MstpFrame<'a>) -> Result<Self, Self::Error> {
let ack: ComplexAck = value.try_into()?;
match ack.service {
ComplexAckService::ReadPropertyMultiple(ack) => Ok(ack),
Expand Down
1 change: 1 addition & 0 deletions src/common/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Error {
InvalidProtocolVersion(u8),
Length((&'static str, u32)),
InvalidValue(&'static str),
InvalidVariant((&'static str, u32)),
Expand Down
18 changes: 9 additions & 9 deletions src/network_protocol/data_link.rs → src/network_protocol/ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ use crate::{
// Bacnet Virtual Link Control
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DataLink<'a> {
pub function: DataLinkFunction,
pub struct IpFrame<'a> {
pub function: BvllFunction,
pub npdu: Option<NetworkPdu<'a>>,
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum DataLinkFunction {
pub enum BvllFunction {
Result = 0,
WriteBroadcastDistributionTable = 1,
ReadBroadcastDistTable = 2,
Expand All @@ -33,7 +33,7 @@ pub enum DataLinkFunction {
OriginalBroadcastNpdu = 11,
}

impl TryFrom<u8> for DataLinkFunction {
impl TryFrom<u8> for BvllFunction {
type Error = u8;

fn try_from(value: u8) -> Result<Self, Self::Error> {
Expand All @@ -57,26 +57,26 @@ impl TryFrom<u8> for DataLinkFunction {

const BVLL_TYPE_BACNET_IP: u8 = 0x81;

impl<'a> DataLink<'a> {
impl<'a> IpFrame<'a> {
// const BVLC_ORIGINAL_UNICAST_NPDU: u8 = 10;
// const BVLC_ORIGINAL_BROADCAST_NPDU: u8 = 11;

pub fn new(function: DataLinkFunction, npdu: Option<NetworkPdu<'a>>) -> Self {
pub fn new(function: BvllFunction, npdu: Option<NetworkPdu<'a>>) -> Self {
Self { function, npdu }
}

pub fn new_confirmed_req(req: ConfirmedRequest<'a>) -> Self {
let apdu = ApplicationPdu::ConfirmedRequest(req);
let message = NetworkMessage::Apdu(apdu);
let npdu = NetworkPdu::new(None, None, true, MessagePriority::Normal, message);
DataLink::new(DataLinkFunction::OriginalUnicastNpdu, Some(npdu))
Self::new(BvllFunction::OriginalUnicastNpdu, Some(npdu))
}

pub fn encode(&self, writer: &mut Writer) {
writer.push(BVLL_TYPE_BACNET_IP);
writer.push(self.function.clone() as u8);
match &self.function {
DataLinkFunction::OriginalBroadcastNpdu | DataLinkFunction::OriginalUnicastNpdu => {
BvllFunction::OriginalBroadcastNpdu | BvllFunction::OriginalUnicastNpdu => {
writer.extend_from_slice(&[0, 0]); // length placeholder
self.npdu.as_ref().unwrap().encode(writer); // should be ok to unwrap here since it has already been checked
Self::update_len(writer);
Expand Down Expand Up @@ -114,7 +114,7 @@ impl<'a> DataLink<'a> {

let npdu = match function {
// see h_bbmd.c for all the types (only 2 are supported here)
DataLinkFunction::OriginalBroadcastNpdu | DataLinkFunction::OriginalUnicastNpdu => {
BvllFunction::OriginalBroadcastNpdu | BvllFunction::OriginalUnicastNpdu => {
Some(NetworkPdu::decode(reader, buf)?)
}
_ => None,
Expand Down
11 changes: 10 additions & 1 deletion src/network_protocol/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
pub mod data_link;
pub mod ip;
pub mod mstp;
pub mod network_pdu;

pub mod data_link {
#[deprecated(note = "use IpFrame")]
pub type DataLink<'a> = super::ip::IpFrame<'a>;

#[deprecated(note = "use IpDataLinkFunction")]
pub type DataLinkFunction = super::ip::BvllFunction;
}
Loading