use bacnet_encoding::constructed::{
decode_object_property_reference_at, decode_setpoint_reference_at,
encode_object_property_reference, encode_setpoint_reference,
};
use bacnet_encoding::tags::Tag;
use bacnet_types::constructed::BACnetObjectPropertyReference;
use bacnet_types::enums::{ObjectType, PropertyIdentifier};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use bytes::BytesMut;
use crate::{common, device_reference};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ReferenceFrame {
Bare,
Setpoint,
}
pub(crate) fn unset_reference(object_type: ObjectType) -> BACnetObjectPropertyReference {
BACnetObjectPropertyReference::new(
device_reference::unset_identifier(object_type),
PropertyIdentifier::PRESENT_VALUE.to_raw(),
)
}
pub(crate) fn set_or_unset(
reference: BACnetObjectPropertyReference,
) -> Option<BACnetObjectPropertyReference> {
(!reference.is_unset()).then_some(reference)
}
pub(crate) fn object_property_reference_value(
reference: Option<&BACnetObjectPropertyReference>,
unset_type: ObjectType,
) -> PropertyValue {
let mut encoded = BytesMut::new();
match reference {
Some(reference) => encode_object_property_reference(&mut encoded, reference),
None => encode_object_property_reference(&mut encoded, &unset_reference(unset_type)),
}
PropertyValue::ApplicationData(encoded.to_vec())
}
pub(crate) fn setpoint_reference_value(
reference: Option<&BACnetObjectPropertyReference>,
) -> PropertyValue {
let mut encoded = BytesMut::new();
if let Some(reference) = reference {
encode_setpoint_reference(&mut encoded, reference);
}
PropertyValue::ApplicationData(encoded.to_vec())
}
pub(crate) fn decode_reference_write(
value: &PropertyValue,
frame: ReferenceFrame,
) -> Result<Option<BACnetObjectPropertyReference>, Error> {
match frame {
ReferenceFrame::Bare => {
common::decode_single_element(value, opens_bare, decode_object_property_reference_at)
.map(set_or_unset)
}
ReferenceFrame::Setpoint => {
if common::chunks(value)?.iter().all(|chunk| chunk.is_empty()) {
return Ok(None);
}
common::decode_single_element(value, opens_setpoint, decode_setpoint_reference_at)
.map(Some)
}
}
}
fn opens_bare(tag: &Tag) -> bool {
tag.is_context(0)
}
fn opens_setpoint(tag: &Tag) -> bool {
tag.is_opening_tag(0)
}
#[cfg(test)]
mod tests;