From 22f148d7ef387eaf423fd8266410028d841b2b93 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Fri, 5 Jul 2024 14:25:27 +0100 Subject: [PATCH 1/9] Add support for decoding ReadRangeValueType (bool, unsigned, signed and NULL). --- .../services/read_range.rs | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/application_protocol/services/read_range.rs b/src/application_protocol/services/read_range.rs index a72fdf5..2d194b1 100644 --- a/src/application_protocol/services/read_range.rs +++ b/src/application_protocol/services/read_range.rs @@ -369,27 +369,27 @@ impl<'a> ReadRangeItem<'a> { reader, buf, TagNumber::ContextSpecificOpening(Self::DATE_TIME_TAG), - "ReadRangeItem decode", + "ReadRangeItem decode open date time", )?; Tag::decode_expected( reader, buf, TagNumber::Application(ApplicationTagNumber::Date), - "ReadRangeItem decode", + "ReadRangeItem decode date", )?; let date = Date::decode(reader, buf)?; Tag::decode_expected( reader, buf, TagNumber::Application(ApplicationTagNumber::Time), - "ReadRangeItem decode", + "ReadRangeItem decode time", )?; let time = Time::decode(reader, buf)?; Tag::decode_expected( reader, buf, TagNumber::ContextSpecificClosing(Self::DATE_TIME_TAG), - "ReadRangeItem decode", + "ReadRangeItem decode close date time", )?; // value @@ -397,7 +397,7 @@ impl<'a> ReadRangeItem<'a> { reader, buf, TagNumber::ContextSpecificOpening(Self::VALUE_TAG), - "ReadRangeItem decode", + "ReadRangeItem decode open value", )?; let tag = Tag::decode(reader, buf)?; let value_type: ReadRangeValueType = match tag.number { @@ -410,14 +410,29 @@ impl<'a> ReadRangeItem<'a> { ReadRangeValueType::Real => { let value = f32::from_be_bytes(reader.read_bytes(buf)?); ReadRangeValue::Real(value) - } + }, + ReadRangeValueType::Bool => { + let bytes = reader.read_byte(buf)?; + ReadRangeValue::Bool(bytes > 0) + }, + ReadRangeValueType::Unsigned => { + let value = u32::from_be_bytes(reader.read_bytes(buf)?); + ReadRangeValue::Unsigned(value) + }, + ReadRangeValueType::Signed => { + let value = i32::from_be_bytes(reader.read_bytes(buf)?); + ReadRangeValue::Signed(value) + }, + ReadRangeValueType::Null => { + ReadRangeValue::Null + }, x => return Err(Error::Unimplemented(Unimplemented::ReadRangeValueType(x))), }; Tag::decode_expected( reader, buf, TagNumber::ContextSpecificClosing(Self::VALUE_TAG), - "ReadRangeItem decode", + "ReadRangeItem decode close value", )?; // status flags @@ -425,7 +440,7 @@ impl<'a> ReadRangeItem<'a> { reader, buf, TagNumber::ContextSpecific(Self::STATUS_FLAGS_TAG), - "ReadRangeItem decode", + "ReadRangeItem decode status", )?; let status_flags = BitString::decode(&PropertyId::PropStatusFlags, tag.value, reader, buf)?; From a5948f96ecc182a771dfdbf502c5fa212600a9ef Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Thu, 4 Jul 2024 15:01:39 +0100 Subject: [PATCH 2/9] Include reference to raw payload in DataLink and NPDU. --- src/network_protocol/data_link.rs | 14 ++++++++++++-- src/network_protocol/network_pdu.rs | 5 +++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/network_protocol/data_link.rs b/src/network_protocol/data_link.rs index 712612f..5422754 100644 --- a/src/network_protocol/data_link.rs +++ b/src/network_protocol/data_link.rs @@ -13,6 +13,7 @@ use crate::{ pub struct DataLink<'a> { pub function: DataLinkFunction, pub npdu: Option>, + pub raw_payload: &'a [u8], } #[derive(Debug, Clone)] @@ -62,7 +63,11 @@ impl<'a> DataLink<'a> { // const BVLC_ORIGINAL_BROADCAST_NPDU: u8 = 11; pub fn new(function: DataLinkFunction, npdu: Option>) -> Self { - Self { function, npdu } + Self { + function, + npdu, + raw_payload: &[], + } } pub fn new_confirmed_req(req: ConfirmedRequest<'a>) -> Self { @@ -112,6 +117,7 @@ impl<'a> DataLink<'a> { } reader.set_len(len as usize); + let npdu_start_index = reader.index; let npdu = match function { // see h_bbmd.c for all the types (only 2 are supported here) DataLinkFunction::OriginalBroadcastNpdu | DataLinkFunction::OriginalUnicastNpdu => { @@ -120,6 +126,10 @@ impl<'a> DataLink<'a> { _ => None, }; - Ok(Self { function, npdu }) + Ok(Self { + function, + npdu, + raw_payload: &buf[npdu_start_index..], + }) } } diff --git a/src/network_protocol/network_pdu.rs b/src/network_protocol/network_pdu.rs index 140c9fa..e4c3c6c 100644 --- a/src/network_protocol/network_pdu.rs +++ b/src/network_protocol/network_pdu.rs @@ -15,6 +15,8 @@ pub struct NetworkPdu<'a> { pub expect_reply: bool, pub message_priority: MessagePriority, pub network_message: NetworkMessage<'a>, + + pub raw_payload: &'a [u8], } // NOTE: this is actually a control flag @@ -135,6 +137,7 @@ impl<'a> NetworkPdu<'a> { expect_reply, message_priority, network_message: message, + raw_payload: &[], // empty unless decoding } } @@ -240,6 +243,7 @@ impl<'a> NetworkPdu<'a> { None }; + let payload_start_index = reader.index; let network_message = if is_network_message { let message_type = reader.read_byte(buf)?; match message_type.try_into() { @@ -257,6 +261,7 @@ impl<'a> NetworkPdu<'a> { expect_reply, message_priority, network_message, + raw_payload: &buf[payload_start_index..], }) } } From 987134d8d31fd8e4334964ebeb9bf6cb753826e6 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Tue, 9 Jul 2024 16:52:37 +0100 Subject: [PATCH 3/9] add MSTP address format --- src/network_protocol/network_pdu.rs | 34 ++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/network_protocol/network_pdu.rs b/src/network_protocol/network_pdu.rs index e4c3c6c..00e175a 100644 --- a/src/network_protocol/network_pdu.rs +++ b/src/network_protocol/network_pdu.rs @@ -268,11 +268,24 @@ impl<'a> NetworkPdu<'a> { #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Addr { - pub ipv4: [u8; 4], +pub struct Ipv4Addr { + pub addr: [u8; 4], pub port: u16, } +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Addr { + Ipv4(Ipv4Addr), + Mac(u8), +} + +impl Addr { + pub fn new_ipv4(addr: [u8; 4], port: u16) -> Self { + Self::Ipv4(Ipv4Addr { addr, port }) + } +} + const IPV4_ADDR_LEN: u8 = 6; pub type SourceAddress = NetworkAddress; @@ -305,9 +318,18 @@ impl NetworkAddress { writer.extend_from_slice(&self.net.to_be_bytes()); match self.addr.as_ref() { Some(addr) => { - writer.push(IPV4_ADDR_LEN); - writer.extend_from_slice(&addr.ipv4); - writer.extend_from_slice(&addr.port.to_be_bytes()); + match addr { + Addr::Mac(mac) => { + let encoded = &mac.to_be_bytes(); + writer.push(encoded.len() as u8); + writer.extend_from_slice(encoded); + }, + Addr::Ipv4(addr) => { + writer.push(IPV4_ADDR_LEN); + writer.extend_from_slice(&addr.addr); + writer.extend_from_slice(&addr.port.to_be_bytes()); + } + } } None => writer.push(0), } @@ -323,7 +345,7 @@ impl NetworkAddress { Ok(Self { net, - addr: Some(Addr { ipv4, port }), + addr: Some(Addr::Ipv4(Ipv4Addr { port, addr: ipv4 })), }) } 0 => Ok(Self { net, addr: None }), From 44edc598c7cd0cb5013e96689ebaa51cd73878a3 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Tue, 5 Nov 2024 16:04:28 +0000 Subject: [PATCH 4/9] Remove raw_payload --- src/network_protocol/data_link.rs | 4 ---- src/network_protocol/network_pdu.rs | 5 ----- 2 files changed, 9 deletions(-) diff --git a/src/network_protocol/data_link.rs b/src/network_protocol/data_link.rs index 5422754..99f4ec7 100644 --- a/src/network_protocol/data_link.rs +++ b/src/network_protocol/data_link.rs @@ -13,7 +13,6 @@ use crate::{ pub struct DataLink<'a> { pub function: DataLinkFunction, pub npdu: Option>, - pub raw_payload: &'a [u8], } #[derive(Debug, Clone)] @@ -66,7 +65,6 @@ impl<'a> DataLink<'a> { Self { function, npdu, - raw_payload: &[], } } @@ -117,7 +115,6 @@ impl<'a> DataLink<'a> { } reader.set_len(len as usize); - let npdu_start_index = reader.index; let npdu = match function { // see h_bbmd.c for all the types (only 2 are supported here) DataLinkFunction::OriginalBroadcastNpdu | DataLinkFunction::OriginalUnicastNpdu => { @@ -129,7 +126,6 @@ impl<'a> DataLink<'a> { Ok(Self { function, npdu, - raw_payload: &buf[npdu_start_index..], }) } } diff --git a/src/network_protocol/network_pdu.rs b/src/network_protocol/network_pdu.rs index 00e175a..833719f 100644 --- a/src/network_protocol/network_pdu.rs +++ b/src/network_protocol/network_pdu.rs @@ -15,8 +15,6 @@ pub struct NetworkPdu<'a> { pub expect_reply: bool, pub message_priority: MessagePriority, pub network_message: NetworkMessage<'a>, - - pub raw_payload: &'a [u8], } // NOTE: this is actually a control flag @@ -137,7 +135,6 @@ impl<'a> NetworkPdu<'a> { expect_reply, message_priority, network_message: message, - raw_payload: &[], // empty unless decoding } } @@ -243,7 +240,6 @@ impl<'a> NetworkPdu<'a> { None }; - let payload_start_index = reader.index; let network_message = if is_network_message { let message_type = reader.read_byte(buf)?; match message_type.try_into() { @@ -261,7 +257,6 @@ impl<'a> NetworkPdu<'a> { expect_reply, message_priority, network_message, - raw_payload: &buf[payload_start_index..], }) } } From f8d2a1dbe02ff4edd42136d9787d5d3fa9c595bc Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Sun, 10 Nov 2024 21:48:09 +0000 Subject: [PATCH 5/9] Address helpers + remove PHANTOM from segment. --- src/application_protocol/application_pdu.rs | 2 +- src/application_protocol/segment.rs | 10 +++----- src/network_protocol/network_pdu.rs | 28 ++++++++++++++++++--- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/application_protocol/application_pdu.rs b/src/application_protocol/application_pdu.rs index 393dfc7..961e06c 100644 --- a/src/application_protocol/application_pdu.rs +++ b/src/application_protocol/application_pdu.rs @@ -19,7 +19,7 @@ pub enum ApplicationPdu<'a> { ComplexAck(ComplexAck<'a>), SimpleAck(SimpleAck), Error(ConfirmedBacnetError), - Segment(Segment<'a>), + Segment(Segment), SegmentAck(SegmentAck), // add more here (see ApduType) } diff --git a/src/application_protocol/segment.rs b/src/application_protocol/segment.rs index 9639721..41d8dc9 100644 --- a/src/application_protocol/segment.rs +++ b/src/application_protocol/segment.rs @@ -3,7 +3,6 @@ #[cfg(feature = "alloc")] use { - crate::common::spooky::Phantom, alloc::{vec, vec::Vec}, }; @@ -34,7 +33,7 @@ pub struct Segment<'a> { #[cfg(feature = "alloc")] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug, Clone)] -pub struct Segment<'a> { +pub struct Segment { pub apdu_type: ApduType, pub more_follows: bool, @@ -45,10 +44,9 @@ pub struct Segment<'a> { // apdu data pub data: Vec, - _phantom: &'a Phantom, } -impl<'a> Segment<'a> { +impl Segment { #[cfg(feature = "alloc")] pub fn new( apdu_type: ApduType, @@ -59,7 +57,6 @@ impl<'a> Segment<'a> { service_choice: u8, data: Vec, ) -> Self { - use crate::common::spooky::PHANTOM; Segment { apdu_type, @@ -68,8 +65,7 @@ impl<'a> Segment<'a> { sequence_number, window_size, service_choice, - data, - _phantom: &PHANTOM, + data } } diff --git a/src/network_protocol/network_pdu.rs b/src/network_protocol/network_pdu.rs index 833719f..cd80ef4 100644 --- a/src/network_protocol/network_pdu.rs +++ b/src/network_protocol/network_pdu.rs @@ -282,6 +282,8 @@ impl Addr { } const IPV4_ADDR_LEN: u8 = 6; +const MAC_ADDR_LEN: u8 = 1; +const BROADCAST_ADDR_LEN: u8 = 0; pub type SourceAddress = NetworkAddress; @@ -292,6 +294,20 @@ pub struct NetworkAddress { pub addr: Option, } +impl NetworkAddress { + pub fn new_global_broadcast() -> Self { + Self { net: 0xFFFF, addr: None } + } + + pub fn new_remote_broadcast(net: u16) -> Self { + Self { net, addr: None } + } + + pub fn new_remote_station(net: u16, addr: Addr) -> Self { + Self { net, addr: Some(addr) } + } +} + #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DestinationAddress { @@ -316,7 +332,7 @@ impl NetworkAddress { match addr { Addr::Mac(mac) => { let encoded = &mac.to_be_bytes(); - writer.push(encoded.len() as u8); + writer.push(MAC_ADDR_LEN); writer.extend_from_slice(encoded); }, Addr::Ipv4(addr) => { @@ -326,7 +342,7 @@ impl NetworkAddress { } } } - None => writer.push(0), + None => writer.push(BROADCAST_ADDR_LEN), } } @@ -343,9 +359,13 @@ impl NetworkAddress { addr: Some(Addr::Ipv4(Ipv4Addr { port, addr: ipv4 })), }) } - 0 => Ok(Self { net, addr: None }), + MAC_ADDR_LEN => { + let addr = u8::from_be_bytes(reader.read_bytes(buf)?); + Ok(Self { net, addr: Some(Addr::Mac(addr)) }) + } + BROADCAST_ADDR_LEN => Ok(Self { net, addr: None }), x => Err(Error::Length(( - "NetworkAddress decode ip len can only be 6 or 0", + "NetworkAddress decode ip len can only be 6 (IP), 1 (MSTP) or 0", x as u32, ))), } From a43a84a5af9cb2bca7de487e725862e83f4cdf48 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Mon, 11 Nov 2024 16:41:47 +0000 Subject: [PATCH 6/9] make Segmentation equalable --- src/common/spec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/spec.rs b/src/common/spec.rs index 22e9b09..5443663 100644 --- a/src/common/spec.rs +++ b/src/common/spec.rs @@ -11,7 +11,7 @@ pub const BACNET_MAX_PRIORITY: u32 = 16; /* TODO: use derive_more when it reaches 1.0 (to automatically impl TryFrom for all enums) -#[derive(Debug, Clone, derive_more::TryFrom)] +#[derive(Debug, Clone, derive_more::TryFrom, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[try_from(repr)] @@ -25,7 +25,7 @@ pub enum Segmentation { } */ -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[repr(u32)] From 41b7ee306907c45535d665e22647f0d23fc25524 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Mon, 11 Nov 2024 17:11:59 +0000 Subject: [PATCH 7/9] Make max_apdu the enum type. --- src/application_protocol/services/i_am.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/application_protocol/services/i_am.rs b/src/application_protocol/services/i_am.rs index bceb133..1e0e4f1 100644 --- a/src/application_protocol/services/i_am.rs +++ b/src/application_protocol/services/i_am.rs @@ -1,5 +1,5 @@ use crate::{ - application_protocol::unconfirmed::UnconfirmedServiceChoice, + application_protocol::{application_pdu::MaxAdpu, unconfirmed::UnconfirmedServiceChoice}, common::{ error::Error, helper::{ @@ -17,7 +17,7 @@ use crate::{ #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct IAm { pub device_id: ObjectId, - pub max_apdu: usize, + pub max_apdu: MaxAdpu, pub segmentation: Segmentation, pub vendor_id: u16, } @@ -26,7 +26,7 @@ impl IAm { pub fn encode(&self, writer: &mut Writer) { writer.push(UnconfirmedServiceChoice::IAm as u8); encode_application_object_id(writer, &self.device_id); - encode_application_unsigned(writer, self.max_apdu as u64); + encode_application_unsigned(writer, self.max_apdu.clone() as u64); encode_application_enumerated(writer, self.segmentation.clone() as u32); encode_application_unsigned(writer, self.vendor_id as u64); } @@ -54,7 +54,7 @@ impl IAm { )); } let max_apdu = decode_unsigned(tag.value, reader, buf)?; - let max_apdu = max_apdu as usize; + let max_apdu: MaxAdpu = (max_apdu as u8).into(); // parse a tag then segmentation let tag = Tag::decode(reader, buf)?; From 2cdf8af29355c8a7534efaca966b47dcf1972a41 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Mon, 11 Nov 2024 19:38:34 +0000 Subject: [PATCH 8/9] Add fixtures + add tests + equality for some enums --- Cargo.lock | 7 +++++ Cargo.toml | 1 + fixtures/datalink_no_segment | 1 + src/application_protocol/application_pdu.rs | 2 +- src/application_protocol/services/i_am.rs | 16 ++++++++++-- src/common/error.rs | 1 + src/lib.rs | 2 +- src/network_protocol/data_link.rs | 29 +++++++++++++++++++++ src/network_protocol/network_pdu.rs | 4 +-- 9 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 fixtures/datalink_no_segment diff --git a/Cargo.lock b/Cargo.lock index ff18ad5..b0eb2c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,12 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "1.3.2" @@ -259,6 +265,7 @@ name = "embedded-bacnet" version = "0.4.0" dependencies = [ "bacnet-macros", + "base64", "chrono", "clap", "defmt", diff --git a/Cargo.toml b/Cargo.toml index 00fec86..f25657e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ bacnet-macros = { path = "./bacnet-macros", version = "0.1.0" } [dev-dependencies] simple_logger = "5.0.0" +base64 = "0.22.1" chrono = { version = "0.4.28" } clap = { version = "4.5.4", features = ["derive"] } tokio = { version = "1.40.0", features = ["rt-multi-thread", "net", "macros"] } diff --git a/fixtures/datalink_no_segment b/fixtures/datalink_no_segment new file mode 100644 index 0000000..eeb2df9 --- /dev/null +++ b/fixtures/datalink_no_segment @@ -0,0 +1 @@ +gQoCTgEI/iABCTCZDgwAAABkHilNTnUYAEktMzNAOV9RQUMyX1ZvbHRTeXN0UGhOTylVXpEAkQNfKXVOkQVPKW9OggQATx8MAAAAZR4pTU51GQBJLTMzQDlfUUFDMl9Wb2x0U3lzdFBoUGhPKVVekQCRA18pdU6RBU8pb06CBABPHwwAAABmHilNTnUYAEktMzNAOV9RQUMyX0N1cnJlbnRTeXN0TylVXpEAkQNfKXVOkQNPKW9OggQATx8MAAAAZx4pTU51FgBJLTMzQDlfUUFDMl9GcmVxdWVuY3lPKVVOREJH+uFPKXVOkRtPKW9OggQATx8MAAAAaB4pTU51FgBJLTMzQDlfUUFDMl9Wb2x0UGhOVjFPKVVORENoYUhPKXVOkQVPKW9OggQATx8MAAAAaR4pTU51FgBJLTMzQDlfUUFDMl9Wb2x0UGhOVjJPKVVORENouFJPKXVOkQVPKW9OggQATx8MAAAAah4pTU51FgBJLTMzQDlfUUFDMl9Wb2x0UGhOVjNPKVVORENoczNPKXVOkQVPKW9OggQATx8MAAAAax4pTU51FgBJLTMzQDlfUUFDMl9Wb2x0UGhOVm5PKVVekQCRA18pdU6RBU8pb06CBABPHwwAAABsHilNTnUYAEktMzNAOV9RQUMyX1ZvbHRQaFBoVTEyTylVTkRDyXcKTyl1TpEFTylvToIEAE8fDAAAAG0eKU1OdRgASS0zM0A5X1FBQzJfVm9sdFBoUGhVMjNPKVVOREPJVHtPKXVOkQVPKW9OggQATx8= diff --git a/src/application_protocol/application_pdu.rs b/src/application_protocol/application_pdu.rs index 961e06c..44b2107 100644 --- a/src/application_protocol/application_pdu.rs +++ b/src/application_protocol/application_pdu.rs @@ -88,7 +88,7 @@ impl From for MaxSegments { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum MaxAdpu { diff --git a/src/application_protocol/services/i_am.rs b/src/application_protocol/services/i_am.rs index 1e0e4f1..7178eb7 100644 --- a/src/application_protocol/services/i_am.rs +++ b/src/application_protocol/services/i_am.rs @@ -53,8 +53,20 @@ impl IAm { "expected unsigned_int tag type for IAm max_apdu field", )); } - let max_apdu = decode_unsigned(tag.value, reader, buf)?; - let max_apdu: MaxAdpu = (max_apdu as u8).into(); + let raw_max_apdu = decode_unsigned(tag.value, reader, buf)?; + let max_apdu: MaxAdpu = match raw_max_apdu { + 0 => MaxAdpu::_0, + 128 => MaxAdpu::_128, + 206 => MaxAdpu::_206, + 480 => MaxAdpu::_480, + 1024 => MaxAdpu::_1024, + 1476 => MaxAdpu::_1476, + _ => { + return Err(Error::InvalidValueValue( + "unexpected value for maximum apdu size", raw_max_apdu + )); + } + }; // parse a tag then segmentation let tag = Tag::decode(reader, buf)?; diff --git a/src/common/error.rs b/src/common/error.rs index ecd77ef..fc23642 100644 --- a/src/common/error.rs +++ b/src/common/error.rs @@ -12,6 +12,7 @@ use crate::{ pub enum Error { Length((&'static str, u32)), InvalidValue(&'static str), + InvalidValueValue(&'static str, u64), InvalidVariant((&'static str, u32)), Unimplemented(Unimplemented), SegmentationNotSupported, diff --git a/src/lib.rs b/src/lib.rs index cef80ef..53bab59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] #![allow(clippy::large_enum_variant)] // This library supports the IP version of bacnet and this is how the network packet is wrapped: diff --git a/src/network_protocol/data_link.rs b/src/network_protocol/data_link.rs index 99f4ec7..4822588 100644 --- a/src/network_protocol/data_link.rs +++ b/src/network_protocol/data_link.rs @@ -129,3 +129,32 @@ impl<'a> DataLink<'a> { }) } } + +#[cfg(test)] +mod tests { + use std::{fs, path::Path}; + + use crate::{common::io::Reader, network_protocol::network_pdu::Addr}; + use base64::{Engine as _, engine::general_purpose::STANDARD}; + use super::DataLink; + + fn load_fixture(name: &str) -> std::io::Result> { + let path = Path::new("fixtures").join(name); + let base64_string = fs::read_to_string(path)?; + Ok(STANDARD.decode(base64_string.strip_suffix("\n").unwrap()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?) + } + + #[test] + fn decode_no_segment() -> std::io::Result<()> { + let buf = load_fixture("datalink_no_segment")?; + let mut reader = Reader::new_with_len(buf.len()); + let datalink = DataLink::decode(&mut reader, &buf).unwrap(); + let npdu = datalink.npdu.unwrap(); + let source = npdu.src.unwrap(); + assert_eq!(source.net, 65056); + assert_eq!(source.addr.unwrap(), Addr::Mac(9)); + assert!(npdu.dst.is_none()); + Ok(()) + } +} diff --git a/src/network_protocol/network_pdu.rs b/src/network_protocol/network_pdu.rs index cd80ef4..2571736 100644 --- a/src/network_protocol/network_pdu.rs +++ b/src/network_protocol/network_pdu.rs @@ -261,14 +261,14 @@ impl<'a> NetworkPdu<'a> { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Ipv4Addr { pub addr: [u8; 4], pub port: u16, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Addr { Ipv4(Ipv4Addr), From bc3c458977eaadabca56d4e6d72d380df1c67688 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Tue, 12 Nov 2024 10:46:54 +0000 Subject: [PATCH 9/9] add support for time value --- src/common/time_value.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/time_value.rs b/src/common/time_value.rs index 357aa13..e4ccaff 100644 --- a/src/common/time_value.rs +++ b/src/common/time_value.rs @@ -20,6 +20,7 @@ pub enum SimpleApplicationDataValue { Real(f32), Double(f64), Enumerated(Enumerated), + Null, } impl SimpleApplicationDataValue { @@ -37,6 +38,9 @@ impl SimpleApplicationDataValue { Self::Enumerated(_) => { Tag::new(TagNumber::Application(ApplicationTagNumber::Enumerated), 1) } + Self::Null => { + Tag::new(TagNumber::Application(ApplicationTagNumber::Null), 0) + } } } pub fn decode(tag: &Tag, reader: &mut Reader, buf: &[u8]) -> Result { @@ -75,6 +79,9 @@ impl SimpleApplicationDataValue { let value = Enumerated::Binary(value); Ok(SimpleApplicationDataValue::Enumerated(value)) } + ApplicationTagNumber::Null => { + Ok(SimpleApplicationDataValue::Null) + } x => Err(Error::Unimplemented(Unimplemented::ApplicationTagNumber( x.clone(),