use bacnet_encoding::constructed::{
decode_device_object_property_reference, decode_device_object_reference,
encode_device_object_property_reference, encode_device_object_reference,
};
use bacnet_encoding::tags::Tag;
use bacnet_types::constructed::{
BACnetDeviceObjectPropertyReference, BACnetDeviceObjectReference, BACnetObjectPropertyReference,
};
use bacnet_types::enums::{ErrorClass, ErrorCode, ObjectType, PropertyIdentifier};
use bacnet_types::error::Error;
use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
use bytes::BytesMut;
use crate::common;
pub(crate) trait DeviceReference: Sized {
fn encode(&self, buf: &mut BytesMut);
fn decode(bytes: &[u8], offset: usize) -> Result<(Self, usize), Error>;
fn starts(tag: &Tag) -> bool;
fn device(&self) -> Option<ObjectIdentifier>;
}
impl DeviceReference for BACnetDeviceObjectPropertyReference {
fn encode(&self, buf: &mut BytesMut) {
encode_device_object_property_reference(buf, self);
}
fn decode(bytes: &[u8], offset: usize) -> Result<(Self, usize), Error> {
decode_device_object_property_reference(bytes, offset)
}
fn starts(tag: &Tag) -> bool {
tag.is_context(0)
}
fn device(&self) -> Option<ObjectIdentifier> {
self.device_identifier
}
}
impl DeviceReference for BACnetDeviceObjectReference {
fn encode(&self, buf: &mut BytesMut) {
encode_device_object_reference(buf, self);
}
fn decode(bytes: &[u8], offset: usize) -> Result<(Self, usize), Error> {
decode_device_object_reference(bytes, offset)
}
fn starts(tag: &Tag) -> bool {
tag.is_context(0) || tag.is_context(1)
}
fn device(&self) -> Option<ObjectIdentifier> {
self.device_identifier
}
}
pub(crate) fn reference_value<R: DeviceReference>(reference: &R) -> PropertyValue {
let mut encoded = BytesMut::new();
reference.encode(&mut encoded);
PropertyValue::ApplicationData(encoded.to_vec())
}
pub(crate) fn reference_elements<R: DeviceReference>(references: &[R]) -> Vec<PropertyValue> {
references.iter().map(reference_value).collect()
}
pub(crate) fn reference_list<R: DeviceReference>(references: &[R]) -> PropertyValue {
PropertyValue::List(reference_elements(references))
}
pub(crate) fn reference_run<R: DeviceReference>(references: &[R]) -> PropertyValue {
let mut encoded = BytesMut::new();
for reference in references {
reference.encode(&mut encoded);
}
PropertyValue::ApplicationData(encoded.to_vec())
}
pub(crate) fn unset_identifier(object_type: ObjectType) -> ObjectIdentifier {
ObjectIdentifier::new(object_type, ObjectIdentifier::WILDCARD_INSTANCE)
.expect("a standard object type at the reserved instance is a valid identifier")
}
pub(crate) fn unset_reference(object_type: ObjectType) -> BACnetDeviceObjectPropertyReference {
BACnetDeviceObjectPropertyReference::new_local(
unset_identifier(object_type),
PropertyIdentifier::PRESENT_VALUE.to_raw(),
)
}
pub(crate) fn optional_reference_value(
reference: Option<&BACnetDeviceObjectPropertyReference>,
unset_type: ObjectType,
) -> PropertyValue {
match reference {
Some(reference) => reference_value(reference),
None => reference_value(&unset_reference(unset_type)),
}
}
pub(crate) fn set_or_unset(
reference: BACnetDeviceObjectPropertyReference,
) -> Option<BACnetDeviceObjectPropertyReference> {
(!reference.is_unset()).then_some(reference)
}
pub(crate) fn local_property_reference(
reference: &BACnetObjectPropertyReference,
) -> BACnetDeviceObjectPropertyReference {
BACnetDeviceObjectPropertyReference {
object_identifier: reference.object_identifier,
property_identifier: reference.property_identifier,
property_array_index: reference.property_array_index,
device_identifier: None,
}
}
fn decode_all<R: DeviceReference>(value: &PropertyValue) -> Result<Vec<R>, (Option<usize>, Error)> {
let mut references = Vec::new();
for bytes in common::chunks(value).map_err(|error| (None, error))? {
let mut offset = 0;
while offset < bytes.len() {
let (reference, end) = common::decode_element(bytes, offset, R::starts, R::decode)
.map_err(|error| (Some(references.len()), error))?;
references.push(reference);
offset = end;
}
}
Ok(references)
}
pub(crate) fn decode_references<R: DeviceReference>(
value: &PropertyValue,
) -> Result<Vec<R>, Error> {
decode_all(value).map_err(|(_, error)| error)
}
pub(crate) fn decode_references_at<R: DeviceReference>(
value: &PropertyValue,
) -> Result<Vec<R>, Error> {
decode_all(value).map_err(|(element, error)| match element {
Some(index) => common::at_list_element(error, index),
None => error,
})
}
pub(crate) fn decode_reference<R: DeviceReference>(value: &PropertyValue) -> Result<R, Error> {
common::decode_single_element(value, R::starts, R::decode)
}
pub(crate) fn check_device_member(device: Option<ObjectIdentifier>) -> Result<(), Error> {
if bacnet_types::constructed::device_identifier_is_device(device) {
Ok(())
} else {
Err(common::value_out_of_range_error())
}
}
pub(crate) fn check_device_members<R: DeviceReference>(references: &[R]) -> Result<(), Error> {
references
.iter()
.try_for_each(|reference| check_device_member(reference.device()))
}
pub(crate) fn check_local_member(device: Option<ObjectIdentifier>) -> Result<(), Error> {
check_device_member(device)?;
match device {
None => Ok(()),
Some(_) => Err(common::protocol_error(
ErrorClass::PROPERTY,
ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
)),
}
}
pub(crate) fn into_local_property_reference(
reference: BACnetDeviceObjectPropertyReference,
) -> Result<BACnetObjectPropertyReference, Error> {
check_local_member(reference.device_identifier)?;
Ok(BACnetObjectPropertyReference {
object_identifier: reference.object_identifier,
property_identifier: reference.property_identifier,
property_array_index: reference.property_array_index,
})
}
#[cfg(test)]
#[path = "device_reference_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "device_reference_users_tests.rs"]
mod users_tests;
#[cfg(test)]
#[path = "device_reference_setter_tests.rs"]
mod setter_tests;