Skip to main content

bacnet_objects/device/
audit_recipient.rs

1//! Device-owned recipient state and the synchronous runtime mutation capability.
2use super::*;
3use alloc::sync::Weak;
4use bacnet_types::constructed::{BACnetAddress, BACnetRecipient};
5
6/// Actual remote requester context, supplied only after service authorization.
7#[doc(hidden)]
8#[derive(Clone)]
9pub struct AuditWriteSource {
10    pub device: BACnetRecipient,
11    pub invoke_id: u8,
12}
13
14/// Installed target runtime. Implementations must prepare both deliveries before
15/// changing `current`, then commit and register their owned worker atomically.
16#[doc(hidden)]
17pub trait AuditRecipientChangeSink: Send + Sync {
18    fn change(
19        &self,
20        current: &mut BACnetRecipient,
21        new: BACnetRecipient,
22        source: Option<&AuditWriteSource>,
23        clock: Option<ClockFrame>,
24    ) -> Result<(), Error>;
25    fn is_active(&self) -> bool;
26}
27
28#[derive(Default)]
29pub(super) struct RecipientState {
30    value: Option<BACnetRecipient>,
31    sink: Option<Weak<dyn AuditRecipientChangeSink>>,
32}
33
34fn property_error(code: ErrorCode) -> Error {
35    Error::protocol(ErrorClass::PROPERTY, code)
36}
37
38/// A scoped operation capability for the built-in Device. It deliberately does
39/// not expose a mutable Device reference or permit replacing its installed state.
40#[doc(hidden)]
41pub struct DeviceAuthority<'a>(pub(super) &'a mut DeviceObject);
42
43impl DeviceAuthority<'_> {
44    pub fn object_identifier(&self) -> ObjectIdentifier {
45        self.0.oid
46    }
47    pub fn set_services_supported(&mut self, services: &[ServiceSupported]) {
48        self.0.set_services_supported(services);
49    }
50    pub fn set_object_list(&mut self, oids: Vec<ObjectIdentifier>) {
51        self.0.set_object_list(oids);
52    }
53    pub fn set_system_status(&mut self, status: DeviceStatus) {
54        self.0.set_system_status(status);
55    }
56    pub fn set_database_revision(&mut self, revision: u32) {
57        self.0.set_database_revision(revision);
58    }
59    pub fn set_backup_and_restore_state(&mut self, state: BackupAndRestoreState) {
60        self.0.set_backup_and_restore_state(state);
61    }
62    pub fn set_last_restore_time(&mut self, time: BACnetTimeStamp) {
63        self.0.set_last_restore_time(time);
64    }
65    pub fn set_configuration_files(&mut self, files: Vec<ObjectIdentifier>) {
66        self.0.set_configuration_files(files);
67    }
68    pub fn write_property(
69        &mut self,
70        property: PropertyIdentifier,
71        index: Option<u32>,
72        value: PropertyValue,
73        priority: Option<u8>,
74    ) -> Result<(), Error> {
75        self.0.write_property(property, index, value, priority)
76    }
77    pub fn provision_audit_recipient(&mut self, recipient: BACnetRecipient) -> Result<(), Error> {
78        self.0.provision_audit_recipient(recipient)
79    }
80    pub fn provisioned_audit_recipient(&self) -> Option<&BACnetRecipient> {
81        self.0.recipient.value.as_ref()
82    }
83    pub fn validate_audit_recipient_installation(&self) -> Result<(), Error> {
84        if self.0.recipient.value.is_none()
85            || self
86                .0
87                .recipient
88                .sink
89                .as_ref()
90                .and_then(Weak::upgrade)
91                .is_some()
92        {
93            return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
94        }
95        Ok(())
96    }
97    pub fn install_audit_recipient(
98        &mut self,
99        sink: &Arc<dyn AuditRecipientChangeSink>,
100    ) -> Result<(), Error> {
101        self.validate_audit_recipient_installation()?;
102        self.0.recipient.sink = Some(Arc::downgrade(sink));
103        Ok(())
104    }
105    pub fn uninstall_audit_recipient(&mut self, sink: &Arc<dyn AuditRecipientChangeSink>) {
106        if !sink.is_active()
107            && self
108                .0
109                .recipient
110                .sink
111                .as_ref()
112                .and_then(Weak::upgrade)
113                .is_some_and(|current| Arc::ptr_eq(&current, sink))
114        {
115            self.0.recipient.sink = None;
116        }
117    }
118    pub fn write_audit_recipient(
119        &mut self,
120        index: Option<u32>,
121        value: PropertyValue,
122        priority: Option<u8>,
123        source: Option<&AuditWriteSource>,
124    ) -> Result<(), Error> {
125        self.0.write_audit_recipient(index, value, priority, source)
126    }
127}
128
129impl DeviceObject {
130    /// Provision the initial target Audit recipient before installing a server
131    /// runtime. This alone does not expose the network property. Active changes
132    /// must use WriteProperty so both old and new recipients are notified.
133    pub fn provision_audit_recipient(&mut self, recipient: BACnetRecipient) -> Result<(), Error> {
134        validate_recipient(&recipient)?;
135        if self
136            .recipient
137            .sink
138            .as_ref()
139            .and_then(Weak::upgrade)
140            .is_some()
141        {
142            return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
143        }
144        self.recipient.value = Some(recipient);
145        Ok(())
146    }
147
148    pub(super) fn audit_recipient_present(&self) -> bool {
149        self.recipient.value.is_some()
150            && self
151                .recipient
152                .sink
153                .as_ref()
154                .and_then(Weak::upgrade)
155                .is_some()
156    }
157
158    pub(super) fn read_audit_recipient(&self, index: Option<u32>) -> Result<PropertyValue, Error> {
159        if !self.audit_recipient_present() {
160            return Err(property_error(ErrorCode::UNKNOWN_PROPERTY));
161        }
162        if index.is_some() {
163            return Err(property_error(ErrorCode::PROPERTY_IS_NOT_AN_ARRAY));
164        }
165        let mut bytes = bytes::BytesMut::new();
166        bacnet_encoding::constructed::encode_recipient(
167            &mut bytes,
168            self.recipient.value.as_ref().expect("present"),
169        )?;
170        Ok(PropertyValue::ApplicationData(bytes.to_vec()))
171    }
172
173    pub(super) fn write_audit_recipient(
174        &mut self,
175        index: Option<u32>,
176        value: PropertyValue,
177        priority: Option<u8>,
178        source: Option<&AuditWriteSource>,
179    ) -> Result<(), Error> {
180        let sink = self
181            .recipient
182            .sink
183            .as_ref()
184            .and_then(Weak::upgrade)
185            .ok_or_else(|| property_error(ErrorCode::UNKNOWN_PROPERTY))?;
186        if !sink.is_active() {
187            return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
188        }
189        if index.is_some() {
190            return Err(property_error(ErrorCode::PROPERTY_IS_NOT_AN_ARRAY));
191        }
192        if priority.is_some_and(|p| !(1..=16).contains(&p)) {
193            return Err(Error::protocol(
194                ErrorClass::SERVICES,
195                ErrorCode::PARAMETER_OUT_OF_RANGE,
196            ));
197        }
198        if value == PropertyValue::Null {
199            return Ok(());
200        }
201        let PropertyValue::ApplicationData(bytes) = value else {
202            return Err(property_error(ErrorCode::INVALID_DATA_TYPE));
203        };
204        let (new, end) = bacnet_encoding::constructed::decode_recipient(&bytes, 0)
205            .map_err(|_| property_error(ErrorCode::INVALID_DATA_ENCODING))?;
206        if end != bytes.len() {
207            return Err(property_error(ErrorCode::INVALID_DATA_ENCODING));
208        }
209        validate_recipient(&new)?;
210        let clock = self.clock_frame();
211        let current = self.recipient.value.as_mut().expect("installed recipient");
212        if *current == new {
213            return Ok(());
214        }
215        sink.change(current, new, source, clock)
216    }
217}
218
219/// A recipient the property can hold: a concrete Device, or an address whose
220/// MAC is within the configured-recipient bound. Provisioning checks the MAC
221/// here because nothing decodes it on the way in (#1124); a write already has.
222fn validate_recipient(recipient: &BACnetRecipient) -> Result<(), Error> {
223    match recipient {
224        BACnetRecipient::Device(oid)
225            if oid.object_type() != ObjectType::DEVICE
226                || oid.instance_number() == ObjectIdentifier::MAX_INSTANCE =>
227        {
228            Err(property_error(ErrorCode::INVALID_DATA_ENCODING))
229        }
230        BACnetRecipient::Address(address)
231            if address.mac_address.len() > BACnetAddress::MAX_MAC_LEN =>
232        {
233            Err(property_error(ErrorCode::INVALID_DATA_ENCODING))
234        }
235        _ => Ok(()),
236    }
237}