#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use bacnet_types::constructed::{
BACnetCOVMultipleSubscription, BACnetCOVReference, BACnetCOVSubscription,
BACnetCOVSubscriptionSpecification, BACnetRecipientProcess,
};
use bacnet_types::enums::PropertyIdentifier;
use bacnet_types::error::Error;
use bytes::BytesMut;
use crate::{primitives, tags};
use super::recipient::{check_encoded_recipient, write_recipient};
use super::tagged::{
decode_ctx_boolean, decode_ctx_constructed, decode_ctx_object_id, decode_ctx_real,
decode_ctx_unsigned, decode_optional_ctx, expect_closing, expect_opening, next_is_closing,
};
use super::{
decode_object_property_reference, decode_recipient, encode_object_property_reference,
MAX_FRAMED_ITEMS,
};
pub fn encode_cov_subscription(
buf: &mut BytesMut,
subscription: &BACnetCOVSubscription,
) -> Result<(), Error> {
check_encoded_recipient(&subscription.recipient.recipient)?;
write_cov_subscription(buf, subscription);
Ok(())
}
fn write_cov_subscription(buf: &mut BytesMut, subscription: &BACnetCOVSubscription) {
tags::encode_opening_tag(buf, 0);
tags::encode_opening_tag(buf, 0);
write_recipient(buf, &subscription.recipient.recipient);
tags::encode_closing_tag(buf, 0);
primitives::encode_ctx_unsigned(buf, 1, subscription.recipient.process_identifier as u64);
tags::encode_closing_tag(buf, 0);
tags::encode_opening_tag(buf, 1);
encode_object_property_reference(buf, &subscription.monitored_property_reference);
tags::encode_closing_tag(buf, 1);
primitives::encode_ctx_boolean(buf, 2, subscription.issue_confirmed_notifications);
primitives::encode_ctx_unsigned(buf, 3, subscription.time_remaining as u64);
if let Some(increment) = subscription.cov_increment {
primitives::encode_ctx_real(buf, 4, increment);
}
}
pub fn encode_cov_subscription_list(
buf: &mut BytesMut,
subscriptions: &[BACnetCOVSubscription],
) -> Result<(), Error> {
for subscription in subscriptions {
check_encoded_recipient(&subscription.recipient.recipient)?;
}
for subscription in subscriptions {
write_cov_subscription(buf, subscription);
}
Ok(())
}
pub fn encode_cov_multiple_subscription(
buf: &mut BytesMut,
subscription: &BACnetCOVMultipleSubscription,
) -> Result<(), Error> {
check_encoded_recipient(&subscription.recipient.recipient)?;
write_cov_multiple_subscription(buf, subscription);
Ok(())
}
fn write_cov_multiple_subscription(
buf: &mut BytesMut,
subscription: &BACnetCOVMultipleSubscription,
) {
tags::encode_opening_tag(buf, 0);
tags::encode_opening_tag(buf, 0);
write_recipient(buf, &subscription.recipient.recipient);
tags::encode_closing_tag(buf, 0);
primitives::encode_ctx_unsigned(buf, 1, subscription.recipient.process_identifier as u64);
tags::encode_closing_tag(buf, 0);
primitives::encode_ctx_boolean(buf, 1, subscription.issue_confirmed_notifications);
primitives::encode_ctx_unsigned(buf, 2, subscription.time_remaining as u64);
primitives::encode_ctx_unsigned(buf, 3, subscription.max_notification_delay as u64);
tags::encode_opening_tag(buf, 4);
for specification in &subscription.list_of_cov_subscription_specifications {
primitives::encode_ctx_object_id(buf, 0, &specification.monitored_object_identifier);
tags::encode_opening_tag(buf, 1);
for reference in &specification.list_of_cov_references {
tags::encode_opening_tag(buf, 0);
primitives::encode_ctx_unsigned(buf, 0, reference.property_identifier.to_raw() as u64);
if let Some(index) = reference.property_array_index {
primitives::encode_ctx_unsigned(buf, 1, index as u64);
}
tags::encode_closing_tag(buf, 0);
if let Some(increment) = reference.cov_increment {
primitives::encode_ctx_real(buf, 1, increment);
}
primitives::encode_ctx_boolean(buf, 2, reference.timestamped);
}
tags::encode_closing_tag(buf, 1);
}
tags::encode_closing_tag(buf, 4);
}
pub fn encode_cov_multiple_subscription_list(
buf: &mut BytesMut,
subscriptions: &[BACnetCOVMultipleSubscription],
) -> Result<(), Error> {
for subscription in subscriptions {
check_encoded_recipient(&subscription.recipient.recipient)?;
}
for subscription in subscriptions {
write_cov_multiple_subscription(buf, subscription);
}
Ok(())
}
pub fn decode_cov_subscription(
data: &[u8],
offset: usize,
) -> Result<(BACnetCOVSubscription, usize), Error> {
let what = "BACnetCOVSubscription";
let (recipient, pos) = decode_recipient_process(data, offset, what)?;
let (reference, pos) = decode_ctx_constructed(data, pos, 1, what)?;
let monitored_property_reference = decode_object_property_reference(reference)?;
let (issue_confirmed_notifications, pos) = decode_ctx_boolean(data, pos, 2, what)?;
let (time_remaining, pos) = decode_ctx_unsigned::<u32>(data, pos, 3, what)?;
let (cov_increment, pos) = decode_optional_ctx(data, pos, 4, what, decode_ctx_real)?;
Ok((
BACnetCOVSubscription {
recipient,
monitored_property_reference,
issue_confirmed_notifications,
time_remaining,
cov_increment,
},
pos,
))
}
pub fn decode_cov_multiple_subscription(
data: &[u8],
offset: usize,
) -> Result<(BACnetCOVMultipleSubscription, usize), Error> {
let what = "BACnetCOVMultipleSubscription";
let (recipient, pos) = decode_recipient_process(data, offset, what)?;
let (issue_confirmed_notifications, pos) = decode_ctx_boolean(data, pos, 1, what)?;
let (time_remaining, pos) = decode_ctx_unsigned::<u32>(data, pos, 2, what)?;
let (max_notification_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 3, what)?;
let mut pos = expect_opening(data, pos, 4, what)?;
let mut specifications = Vec::new();
while !is_closing(data, pos, 4)? {
if specifications.len() >= MAX_FRAMED_ITEMS {
return Err(Error::overflow(
pos,
format!("{what}: specification count exceeds limit"),
));
}
let (specification, end) = decode_specification(data, pos, what)?;
specifications.push(specification);
pos = end;
}
let pos = expect_closing(data, pos, 4, what)?;
Ok((
BACnetCOVMultipleSubscription {
recipient,
issue_confirmed_notifications,
time_remaining,
max_notification_delay,
list_of_cov_subscription_specifications: specifications,
},
pos,
))
}
fn decode_specification(
data: &[u8],
offset: usize,
what: &str,
) -> Result<(BACnetCOVSubscriptionSpecification, usize), Error> {
let (monitored_object_identifier, pos) = decode_ctx_object_id(data, offset, 0, what)?;
let mut pos = expect_opening(data, pos, 1, what)?;
let mut references = Vec::new();
while !is_closing(data, pos, 1)? {
if references.len() >= MAX_FRAMED_ITEMS {
return Err(Error::overflow(
pos,
format!("{what}: COV reference count exceeds limit"),
));
}
let (reference, end) = decode_cov_reference(data, pos, what)?;
references.push(reference);
pos = end;
}
Ok((
BACnetCOVSubscriptionSpecification {
monitored_object_identifier,
list_of_cov_references: references,
},
expect_closing(data, pos, 1, what)?,
))
}
fn decode_cov_reference(
data: &[u8],
offset: usize,
what: &str,
) -> Result<(BACnetCOVReference, usize), Error> {
let pos = expect_opening(data, offset, 0, what)?;
let (property_identifier, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (property_array_index, pos) =
decode_optional_ctx(data, pos, 1, what, decode_ctx_unsigned::<u32>)?;
let pos = expect_closing(data, pos, 0, what)?;
let (cov_increment, pos) = decode_optional_ctx(data, pos, 1, what, decode_ctx_real)?;
let (timestamped, pos) = decode_ctx_boolean(data, pos, 2, what)?;
Ok((
BACnetCOVReference {
property_identifier: PropertyIdentifier::from_raw(property_identifier),
property_array_index,
cov_increment,
timestamped,
},
pos,
))
}
fn decode_recipient_process(
data: &[u8],
offset: usize,
what: &str,
) -> Result<(BACnetRecipientProcess, usize), Error> {
let pos = expect_opening(data, offset, 0, what)?;
let pos = expect_opening(data, pos, 0, what)?;
let (recipient, pos) = decode_recipient(data, pos)?;
let pos = expect_closing(data, pos, 0, what)?;
let (process_identifier, pos) = decode_ctx_unsigned::<u32>(data, pos, 1, what)?;
let pos = expect_closing(data, pos, 0, what)?;
Ok((
BACnetRecipientProcess {
recipient,
process_identifier,
},
pos,
))
}
fn is_closing(data: &[u8], offset: usize, tag: u8) -> Result<bool, Error> {
if offset >= data.len() {
return Err(Error::buffer_too_short(offset + 1, data.len()));
}
next_is_closing(data, offset, tag)
}