use super::*;
use bacnet_types::constructed::{BACnetAddress, BACnetRecipient};
use std::sync::Weak;
#[doc(hidden)]
#[derive(Clone)]
pub struct AuditWriteSource {
pub device: BACnetRecipient,
pub invoke_id: u8,
}
#[doc(hidden)]
pub trait AuditRecipientChangeSink: Send + Sync {
fn change(
&self,
current: &mut BACnetRecipient,
new: BACnetRecipient,
source: Option<&AuditWriteSource>,
clock: Option<ClockFrame>,
) -> Result<(), Error>;
fn is_active(&self) -> bool;
}
#[derive(Default)]
pub(super) struct RecipientState {
value: Option<BACnetRecipient>,
sink: Option<Weak<dyn AuditRecipientChangeSink>>,
}
fn property_error(code: ErrorCode) -> Error {
Error::Protocol {
class: ErrorClass::PROPERTY.to_raw() as u32,
code: code.to_raw() as u32,
}
}
#[doc(hidden)]
pub struct DeviceAuthority<'a>(pub(super) &'a mut DeviceObject);
impl DeviceAuthority<'_> {
pub fn object_identifier(&self) -> ObjectIdentifier {
self.0.oid
}
pub fn set_services_supported(&mut self, services: &[ServiceSupported]) {
self.0.set_services_supported(services);
}
pub fn write_property(
&mut self,
property: PropertyIdentifier,
index: Option<u32>,
value: PropertyValue,
priority: Option<u8>,
) -> Result<(), Error> {
self.0.write_property(property, index, value, priority)
}
pub fn provision_audit_recipient(&mut self, recipient: BACnetRecipient) -> Result<(), Error> {
self.0.provision_audit_recipient(recipient)
}
pub fn provisioned_audit_recipient(&self) -> Option<&BACnetRecipient> {
self.0.recipient.value.as_ref()
}
pub fn validate_audit_recipient_installation(&self) -> Result<(), Error> {
if self.0.recipient.value.is_none()
|| self
.0
.recipient
.sink
.as_ref()
.and_then(Weak::upgrade)
.is_some()
{
return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
}
Ok(())
}
pub fn install_audit_recipient(
&mut self,
sink: &Arc<dyn AuditRecipientChangeSink>,
) -> Result<(), Error> {
self.validate_audit_recipient_installation()?;
self.0.recipient.sink = Some(Arc::downgrade(sink));
Ok(())
}
pub fn uninstall_audit_recipient(&mut self, sink: &Arc<dyn AuditRecipientChangeSink>) {
if !sink.is_active()
&& self
.0
.recipient
.sink
.as_ref()
.and_then(Weak::upgrade)
.is_some_and(|current| Arc::ptr_eq(¤t, sink))
{
self.0.recipient.sink = None;
}
}
pub fn write_audit_recipient(
&mut self,
index: Option<u32>,
value: PropertyValue,
priority: Option<u8>,
source: Option<&AuditWriteSource>,
) -> Result<(), Error> {
self.0.write_audit_recipient(index, value, priority, source)
}
}
impl DeviceObject {
pub fn provision_audit_recipient(&mut self, recipient: BACnetRecipient) -> Result<(), Error> {
validate_recipient(&recipient)?;
if self
.recipient
.sink
.as_ref()
.and_then(Weak::upgrade)
.is_some()
{
return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
}
self.recipient.value = Some(recipient);
Ok(())
}
pub(super) fn audit_recipient_present(&self) -> bool {
self.recipient.value.is_some()
&& self
.recipient
.sink
.as_ref()
.and_then(Weak::upgrade)
.is_some()
}
pub(super) fn read_audit_recipient(&self, index: Option<u32>) -> Result<PropertyValue, Error> {
if !self.audit_recipient_present() {
return Err(property_error(ErrorCode::UNKNOWN_PROPERTY));
}
if index.is_some() {
return Err(property_error(ErrorCode::PROPERTY_IS_NOT_AN_ARRAY));
}
let mut bytes = bytes::BytesMut::new();
bacnet_encoding::constructed::encode_recipient(
&mut bytes,
self.recipient.value.as_ref().expect("present"),
)?;
Ok(PropertyValue::ApplicationData(bytes.to_vec()))
}
pub(super) fn write_audit_recipient(
&mut self,
index: Option<u32>,
value: PropertyValue,
priority: Option<u8>,
source: Option<&AuditWriteSource>,
) -> Result<(), Error> {
let sink = self
.recipient
.sink
.as_ref()
.and_then(Weak::upgrade)
.ok_or_else(|| property_error(ErrorCode::UNKNOWN_PROPERTY))?;
if !sink.is_active() {
return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
}
if index.is_some() {
return Err(property_error(ErrorCode::PROPERTY_IS_NOT_AN_ARRAY));
}
if priority.is_some_and(|p| !(1..=16).contains(&p)) {
return Err(Error::Protocol {
class: ErrorClass::SERVICES.to_raw() as u32,
code: ErrorCode::PARAMETER_OUT_OF_RANGE.to_raw() as u32,
});
}
if value == PropertyValue::Null {
return Ok(());
}
let PropertyValue::ApplicationData(bytes) = value else {
return Err(property_error(ErrorCode::INVALID_DATA_TYPE));
};
let (new, end) = bacnet_encoding::constructed::decode_recipient(&bytes, 0)
.map_err(|_| property_error(ErrorCode::INVALID_DATA_ENCODING))?;
if end != bytes.len() {
return Err(property_error(ErrorCode::INVALID_DATA_ENCODING));
}
validate_recipient(&new)?;
let clock = self.clock_frame();
let current = self.recipient.value.as_mut().expect("installed recipient");
if *current == new {
return Ok(());
}
sink.change(current, new, source, clock)
}
}
fn validate_recipient(recipient: &BACnetRecipient) -> Result<(), Error> {
match recipient {
BACnetRecipient::Device(oid)
if oid.object_type() != ObjectType::DEVICE
|| oid.instance_number() == ObjectIdentifier::MAX_INSTANCE =>
{
Err(property_error(ErrorCode::INVALID_DATA_ENCODING))
}
BACnetRecipient::Address(address)
if address.mac_address.len() > BACnetAddress::MAX_MAC_LEN =>
{
Err(property_error(ErrorCode::INVALID_DATA_ENCODING))
}
_ => Ok(()),
}
}