use bacnet_encoding::constructed::decode_device_object_property_reference;
use bacnet_objects::traits::BACnetObject;
use bacnet_types::constructed::{BACnetDeviceObjectPropertyReference, BACnetEventParameter};
use bacnet_types::enums::{EventType, PropertyIdentifier};
use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
use super::LocalConfigurationReadError;
use super::support::classify_required_property_read_error;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) struct MonitoredReference {
pub(super) object_identifier: ObjectIdentifier,
pub(super) property_identifier: PropertyIdentifier,
pub(super) array_index: Option<u32>,
pub(super) device_identifier: Option<ObjectIdentifier>,
}
impl MonitoredReference {
#[cfg(test)]
pub(super) fn local(
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
array_index: Option<u32>,
) -> Self {
Self {
object_identifier,
property_identifier,
array_index,
device_identifier: None,
}
}
}
pub(crate) fn decode_reference_value(
value: &PropertyValue,
) -> Option<BACnetDeviceObjectPropertyReference> {
let PropertyValue::ApplicationData(bytes) = value else {
return None;
};
let (reference, end) = decode_device_object_property_reference(bytes, 0).ok()?;
(end == bytes.len() && !reference.is_unset()).then_some(reference)
}
pub(super) fn read_object_property_ref(
enrollment: &dyn BACnetObject,
) -> Result<MonitoredReference, LocalConfigurationReadError> {
match enrollment.read_property(PropertyIdentifier::OBJECT_PROPERTY_REFERENCE, None) {
Ok(value) => {
let reference =
decode_reference_value(&value).ok_or(LocalConfigurationReadError::Malformed)?;
Ok(MonitoredReference {
object_identifier: reference.object_identifier,
property_identifier: PropertyIdentifier::from_raw(reference.property_identifier),
array_index: reference.property_array_index,
device_identifier: reference.device_identifier,
})
}
Err(error) => Err(classify_required_property_read_error(&error)),
}
}
pub(super) fn params_fingerprint(
params: &BACnetEventParameter,
normal_delay: u64,
event_type: EventType,
monitored: &MonitoredReference,
) -> Result<u64, bacnet_types::error::Error> {
let mut buf = bytes::BytesMut::new();
bacnet_encoding::constructed::encode_event_parameter(&mut buf, params)?;
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for b in buf
.iter()
.copied()
.chain(normal_delay.to_le_bytes())
.chain(event_type.to_raw().to_le_bytes())
.chain(monitored.object_identifier.encode())
.chain(monitored.property_identifier.to_raw().to_le_bytes())
.chain([u8::from(monitored.array_index.is_some())])
.chain(monitored.array_index.unwrap_or_default().to_le_bytes())
{
h = (h ^ b as u64).wrapping_mul(0x0000_0100_0000_01b3);
}
Ok(h)
}