bacnet_objects/traits.rs
1//! BACnetObject trait — the interface all BACnet objects implement.
2
3#[path = "object_storage.rs"]
4mod object_storage;
5pub(crate) use object_storage::ObjectStorageAccess;
6
7use std::borrow::Cow;
8use std::sync::Arc;
9use std::time::Duration;
10
11use bacnet_types::bitstring::EventTransitionBits;
12use bacnet_types::calendar::SpecificDate;
13use bacnet_types::constructed::{
14 BACnetEventLogRecord, BACnetLogMultipleRecord, BACnetLogRecord, BACnetObjectPropertyReference,
15};
16use bacnet_types::enums::{
17 ErrorClass, ErrorCode, EventState, LifeSafetyOperation, ObjectType, PropertyIdentifier,
18 Reliability,
19};
20use bacnet_types::error::Error;
21use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, Time};
22
23use crate::audit::{AuditLogNotificationSink, AuditLogStorage};
24use crate::clock::ClockReader;
25use crate::durable::DurableWrites;
26use crate::event::{
27 EnrollmentSummaryCapability, EventStateChange, EventTransitionCommit,
28 EventTransitionCommitError, TransitionOutcome,
29};
30use crate::event_enrollment::{
31 EventEnrollmentEvalState, EventEnrollmentMonitoredSource, EventEnrollmentReliabilityCommit,
32};
33use crate::file::{FileConfiguration, FileStorage};
34use crate::log_buffer::{LogBufferRecords, LogRecordIdentity};
35use crate::log_reporting::BufferReadyReport;
36use crate::schedule::{ScheduleTargetOutcome, ScheduleWrite};
37
38/// Process-local monotonic time source used by internal object lifecycles.
39#[doc(hidden)]
40pub type MonotonicClock = dyn Fn() -> Duration + Send + Sync;
41
42/// Wakes the server's monotonic task to read object deadlines again (#1384).
43#[doc(hidden)]
44pub type DeadlineWaker = dyn Fn() + Send + Sync;
45
46mod defaults;
47use defaults::{
48 array_property_default, cov_reported_properties_default, historical_writable_default,
49 list_property_default,
50};
51
52/// One property a whole-object (SubscribeCOV) notification reports after
53/// Present_Value and Status_Flags, from the object type's Table 13-1 row.
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum CovReportedProperty {
56 /// Carried in each notification; a change of it alone sends none.
57 Value(PropertyIdentifier),
58 /// Carried in each notification, and any change of it sends one.
59 Trigger(PropertyIdentifier),
60}
61
62impl CovReportedProperty {
63 /// The reported property.
64 pub const fn property(self) -> PropertyIdentifier {
65 match self {
66 Self::Value(property) | Self::Trigger(property) => property,
67 }
68 }
69
70 /// Whether any change of the property triggers a notification.
71 pub const fn triggers(self) -> bool {
72 matches!(self, Self::Trigger(_))
73 }
74}
75
76/// Whether the stack's classification table marks `property` as a BACnetARRAY
77/// on `object_type`: the answer the default [`BACnetObject::is_array_property`]
78/// gives, and every built-in object's. The table holds every property a Clause
79/// 12 object table of the 2020 standard types as an array, served or not. A
80/// client can ask it about a remote object, before or without any value, to
81/// learn the property's shape; a vendor-defined array isn't in it.
82pub fn standard_array_property(object_type: ObjectType, property: PropertyIdentifier) -> bool {
83 array_property_default(object_type, property)
84}
85
86/// Whether the stack's classification table marks `property` as a BACnetLIST
87/// on `object_type`: the answer the default [`BACnetObject::is_list_property`]
88/// gives, and every built-in object's, covering every list of the 2020 object
89/// tables. Usable for a remote object as [`standard_array_property`] is.
90pub fn standard_list_property(object_type: ObjectType, property: PropertyIdentifier) -> bool {
91 list_property_default(object_type, property)
92}
93
94/// Result of applying a LifeSafetyOperation to an object.
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum LifeSafetyOperationEffect {
97 /// The operation changed object state.
98 Applied,
99 /// The requested idempotent state was already present.
100 AlreadyApplied,
101}
102
103/// Result of applying a `LifeSafetyOperation` to object-owned state.
104///
105/// `changed_properties` contains exact committed readback changes in the object's
106/// stable reporting order, with each property at most once. Implementations own
107/// this projection; the server does not infer custom object mutations.
108/// [`LifeSafetyOperationEffect::AlreadyApplied`] means no state changed and must
109/// carry an empty property list.
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct LifeSafetyOperationOutcome {
112 /// Whether the operation committed a change or was already applied.
113 pub effect: LifeSafetyOperationEffect,
114 /// Exact properties whose committed readback changed.
115 pub changed_properties: Vec<PropertyIdentifier>,
116}
117
118/// Result of one object-owned reliability evaluation pass.
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum ReliabilityEvaluation {
121 /// The object made no reliability-related mutation.
122 Unchanged,
123 /// The object successfully mutated its `Reliability` value.
124 Changed {
125 /// Reliability before the successful mutation.
126 old_reliability: Reliability,
127 /// Reliability after the successful mutation.
128 new_reliability: Reliability,
129 },
130}
131
132/// The core trait for all BACnet objects.
133///
134/// Stored objects have crate-authorized concrete access; borrowed read views
135/// remain valid implementors. Applications cannot manufacture its access permit:
136/// ```compile_fail
137/// use bacnet_objects::traits::ObjectStorageAccess;
138/// let permit = ObjectStorageAccess(());
139/// ```
140/// Implementors represent a single BACnet object (Device, AnalogInput, etc.)
141/// and provide read/write access to their properties.
142pub trait BACnetObject: Send + Sync + object_storage::StoredObject {
143 /// Typed local Device authority for composition-owned configuration.
144 /// This does not grant network write access to the object database.
145 #[doc(hidden)]
146 fn device_authority_internal(&mut self) -> Option<crate::device::DeviceAuthority<'_>> {
147 None
148 }
149
150 /// Snapshot of object-owned target Audit overrides. Absent fields inherit
151 /// the selected Reporter; source reporting does not inspect remote policies.
152 #[doc(hidden)]
153 fn audit_object_policy_internal(&self) -> crate::audit::ObjectAuditPolicy {
154 crate::audit::ObjectAuditPolicy::default()
155 }
156
157 /// Optional sealed built-in AV/BV policy assignment authority. Custom objects
158 /// remain on their ordinary writer path; adapters may forward this capability.
159 #[doc(hidden)]
160 fn audit_policy_authority_internal(
161 &mut self,
162 ) -> Option<crate::audit::AuditPolicyAuthority<'_>> {
163 None
164 }
165
166 /// The optional built-in Reporter authority.
167 #[doc(hidden)]
168 fn audit_reporter_internal(&self) -> Option<&crate::audit::AuditReporterObject> {
169 None
170 }
171
172 /// Scoped built-in Reporter mutation authority; absent for other objects.
173 #[doc(hidden)]
174 fn audit_reporter_authority_internal(
175 &mut self,
176 ) -> Option<crate::audit::AuditReporterAuthority<'_>> {
177 None
178 }
179
180 /// Atomically replace all trusted local Reporter settings without a network write.
181 ///
182 /// The default opts out. Supporting implementations must reject invalid
183 /// settings before any mutation. `None` selectors remove Monitored_Objects
184 /// (catch-all); `Some(vec![])` retains the property and selects no ordinary
185 /// targets. An installed target owner prepares required records and lifecycle admission.
186 ///
187 /// Supply every field together, including the optional paired delay capability.
188 /// Active target ownership fixes pair presence; source ownership rejects it.
189 /// `None` omits both delay/control properties; a present zero is immediate.
190 #[doc(hidden)]
191 fn configure_audit_reporter_internal(
192 &mut self,
193 _level: bacnet_types::enums::AuditLevel,
194 _operations: bacnet_types::bitstring::AuditOperationFlags,
195 _confirmed: bool,
196 _selectors: Option<Vec<bacnet_types::constructed::BACnetObjectSelector>>,
197 _priorities: bacnet_types::bitstring::BACnetPriorityFilter,
198 _maximum_send_delay: Option<crate::audit::AuditSendDelay>,
199 ) -> Result<(), Error> {
200 Err(Error::Protocol {
201 class: ErrorClass::OBJECT.to_raw() as u32,
202 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
203 })
204 }
205
206 /// The object's identifier (type + instance).
207 fn object_identifier(&self) -> ObjectIdentifier;
208
209 /// The object's name.
210 fn object_name(&self) -> &str;
211
212 /// Read a property value.
213 fn read_property(
214 &self,
215 property: PropertyIdentifier,
216 array_index: Option<u32>,
217 ) -> Result<PropertyValue, Error>;
218
219 /// Write a property value.
220 ///
221 /// Returning `Err` MUST leave the object unchanged. WritePropertyMultiple
222 /// retains earlier successful writes and cannot undo a mutation made by the
223 /// currently failing write, including for a write-only property.
224 ///
225 /// Refusing one element of a list value, an implementation may name it:
226 /// `Error::Structured` with `ErrorDetail::FirstFailedElementNumber`
227 /// holding the element's position, from 1, in the list `value` carries.
228 /// The server's AddListElement handler then reports the request element
229 /// at that position in its ChangeList-Error (#1048); a plain
230 /// `Error::Protocol` there names the first element the list would gain.
231 ///
232 /// Make the checks that don't depend on the value (the property exists,
233 /// it is writable now, the array index is in range) before judging the
234 /// value's datatype, and refuse a value of the wrong datatype with
235 /// PROPERTY / INVALID_DATA_TYPE. The bundled server reads that refusal of
236 /// a NULL, on a property that isn't commandable and has no NULL in its
237 /// datatype, as a success that leaves the property as it is (Clauses
238 /// 15.9.2 and 19.2.1, #1396), so an access check made after the datatype
239 /// check would never be seen for a NULL.
240 fn write_property(
241 &mut self,
242 property: PropertyIdentifier,
243 array_index: Option<u32>,
244 value: PropertyValue,
245 priority: Option<u8>,
246 ) -> Result<(), Error>;
247
248 /// Write with explicit actual command provenance. Standalone callers assert
249 /// the origin; server boundaries additionally validate ingress or membership.
250 /// Decorators must forward this hook without discarding the origin.
251 /// The default preserves custom/unaffected object behavior. First-party
252 /// source-tracked commands require this hook and deny context-free writing.
253 fn write_property_from(
254 &mut self,
255 property: PropertyIdentifier,
256 array_index: Option<u32>,
257 value: PropertyValue,
258 priority: Option<u8>,
259 _origin: &crate::command_source::CommandOrigin,
260 ) -> Result<(), Error> {
261 self.write_property(property, array_index, value, priority)
262 }
263
264 /// Return canonical metadata for this object's effective property rows.
265 ///
266 /// Migrated implementations return every supported standard row for the
267 /// current instance, including `PROPERTY_LIST`. Borrowed rows cover static
268 /// or object-owned metadata; owned rows support dynamically assembled
269 /// per-instance sets. An empty borrowed default marks an object as unmigrated.
270 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
271 Cow::Borrowed(&[])
272 }
273
274 /// List all properties this object supports in the legacy projection.
275 ///
276 /// For migrated objects this includes Object_Identifier, Object_Name, and
277 /// Object_Type but omits Property_List. Reading the BACnet Property_List
278 /// property applies the additional wire-level universal-property filter.
279 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]>;
280
281 /// Bind or remove the database's shared wall-clock reader.
282 ///
283 /// Objects that do not expose clock-derived state ignore this internal
284 /// lifecycle hook.
285 #[doc(hidden)]
286 fn bind_clock_internal(&mut self, _clock: Option<Arc<dyn ClockReader>>) {}
287
288 /// Advance object-owned operations that use monotonic elapsed time.
289 ///
290 /// The server calls this internal hook from a dedicated lifecycle task.
291 /// `true` means readable state changed and generic COV processing is
292 /// required. The default is a source-compatible no-op for downstream
293 /// object implementations.
294 #[doc(hidden)]
295 fn advance_time_internal(&mut self, _elapsed: Duration) -> bool {
296 false
297 }
298
299 /// Bind the process-local monotonic source used when operations arm.
300 #[doc(hidden)]
301 fn bind_monotonic_clock_internal(&mut self, _clock: Option<Arc<MonotonicClock>>) {}
302
303 /// Bind the waker an object calls when a write arms a monotonic deadline,
304 /// so the server's task looks again sooner than its next routine pass.
305 #[doc(hidden)]
306 fn bind_deadline_waker_internal(&mut self, _waker: Option<Arc<DeadlineWaker>>) {}
307
308 /// Advance operations to an absolute process-local monotonic instant.
309 #[doc(hidden)]
310 fn advance_monotonic_time_internal(&mut self, _now: Duration) -> bool {
311 false
312 }
313
314 /// Return the next absolute process-local operation deadline, if any.
315 #[doc(hidden)]
316 fn next_monotonic_deadline_internal(&self) -> Option<Duration> {
317 None
318 }
319
320 /// Freeze COV-readable state while the object's mutation lock is held.
321 #[doc(hidden)]
322 fn cov_snapshot_internal(&self) -> Option<Box<dyn BACnetObject>> {
323 None
324 }
325
326 /// Return how many blink-warn notifications a lighting object has
327 /// requested, for objects that model them.
328 ///
329 /// This is an internal conformance-test channel, not a BACnet property,
330 /// host callback, or physical-output claim.
331 #[doc(hidden)]
332 fn lighting_blink_count_internal(&self) -> u64 {
333 0
334 }
335
336 /// Whether a property write route can accept `property` for this object.
337 ///
338 /// PICS generation and runtime dispatch MUST consult this (or
339 /// `write_property_from` where source authority is required) rather than a
340 /// separate heuristic, so the PICS
341 /// writable flags cannot drift from the actual write routes. The default
342 /// reproduces the historical PICS heuristic (see
343 /// `historical_writable_default`) so unmigrated object types keep their
344 /// current PICS output. Object implementations override to mirror their
345 /// real write capabilities, including required command origin and ownership.
346 ///
347 /// Universal read-only properties (`OBJECT_IDENTIFIER`, `OBJECT_TYPE`,
348 /// `PROPERTY_LIST`, `STATUS_FLAGS`) are always non-writable and are
349 /// excluded by the default; overrides should preserve that invariant.
350 ///
351 /// Built-in analog, binary, multi-state, Event Enrollment, and Alert
352 /// Enrollment objects also deny network writes to `ACKED_TRANSITIONS`.
353 /// The property descriptions accompanying ASHRAE 135-2020 Tables 12-2/3/4,
354 /// 12-6/8/10, 12-21/22/23, 12-14, and 12-61 explicitly make it read-only;
355 /// the table's `R`/`O` classification alone is not a write prohibition.
356 /// Clause 13.2.3 assigns acknowledgment-bit maintenance to internal
357 /// transition and acknowledgment processing. Those lifecycle hooks and
358 /// supported AcknowledgeAlarm paths do not use `write_property`, so this
359 /// network restriction does not prevent their internal updates.
360 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
361 let metadata = self.property_metadata();
362 if metadata.is_empty() {
363 historical_writable_default(self.object_identifier().object_type(), property)
364 } else {
365 crate::property_metadata::is_writable_in_metadata(metadata.as_ref(), property)
366 }
367 }
368
369 /// Whether `property` accepts an array index on this object.
370 ///
371 /// Per Clause 12.1.5.1, only BACnetARRAY (and BACnetARRAY of BACnetLIST)
372 /// properties accept an array index; Clause 12.1.5.2 makes ReadRange the
373 /// only positional access to a BACnetLIST. The RP/RPM/WP/WPM service
374 /// handlers gate the request's array index on this query and reject a
375 /// supplied index on a non-array property with PROPERTY /
376 /// PROPERTY_IS_NOT_AN_ARRAY (Clause 15.5.1.3, Clause 15.9.1.3).
377 ///
378 /// The default reproduces the standard's classification (see
379 /// `array_property_default`): identifier-stable arrays are admitted
380 /// without consulting the object type, the identifiers whose datatype
381 /// changes with the object type (ACTION, ALARM_VALUES / FAULT_VALUES,
382 /// LIST_OF_OBJECT_PROPERTY_REFERENCES, LOG_DEVICE_OBJECT_PROPERTY,
383 /// PRESENT_VALUE) classify by `object_identifier().object_type()`, and
384 /// everything else — scalars and BACnetLIST properties — rejects the
385 /// index. The built-in objects keep the default, so clients that classify
386 /// with [`standard_array_property`] agree with them; object
387 /// implementations with vendor array properties override.
388 fn is_array_property(&self, property: PropertyIdentifier) -> bool {
389 array_property_default(self.object_identifier().object_type(), property)
390 }
391
392 /// Whether `property` is a BACnetLIST on this object.
393 ///
394 /// AddListElement and RemoveListElement (Clauses 15.1 and 15.2) edit only
395 /// BACnetLIST properties, and ReadRange (Clause 15.8) reads only them. The
396 /// server's handlers ask this before decoding any element or selecting any
397 /// item, and refuse every other target, including an array element, with
398 /// SERVICES / PROPERTY_IS_NOT_A_LIST. Like
399 /// [`Self::is_array_property`], the answer follows the property's datatype,
400 /// not the shape of a value read: a whole array also reads as a list, and
401 /// constructed single values often read as framed bytes.
402 ///
403 /// The default reproduces the standard's classification (see
404 /// `list_property_default`): identifier-stable lists are admitted on every
405 /// object type, and the identifiers whose datatype changes with the object
406 /// type (ALARM_VALUES / FAULT_VALUES, LIST_OF_OBJECT_PROPERTY_REFERENCES,
407 /// PRESENT_VALUE, MEMBER_OF) classify by `object_identifier().object_type()`.
408 /// The built-in objects keep the default; object implementations with
409 /// vendor list properties override.
410 fn is_list_property(&self, property: PropertyIdentifier) -> bool {
411 list_property_default(self.object_identifier().object_type(), property)
412 }
413
414 /// Whether this object type can be created at runtime via CreateObject.
415 ///
416 /// Default `false`; override `true` only for types the network factory
417 /// (`handle_create_object`) actually constructs, so PICS createability
418 /// matches the runtime factory with no separate list to drift.
419 fn is_createable(&self) -> bool {
420 false
421 }
422
423 /// The properties whose whole value a CreateObject initial value may set
424 /// on this object although WriteProperty can't change it afterwards.
425 ///
426 /// Clause 15.3 doesn't limit initial values to the properties
427 /// WriteProperty can change, so a device may take some of its read-only
428 /// properties at creation. The bundled server hands such an initial value,
429 /// written without an array index, to [`Self::initialize_property`]
430 /// instead of the write route; every other initial value goes the way a
431 /// WriteProperty does. The PICS lists the set for each createable type.
432 /// Default empty: no property is set only at creation.
433 fn creation_only_properties(&self) -> &'static [PropertyIdentifier] {
434 &[]
435 }
436
437 /// Apply a CreateObject initial value to one of
438 /// [`Self::creation_only_properties`], written whole.
439 ///
440 /// The server calls this only on the object it is creating, before any
441 /// other request can see it. Check the value as a write would, and on
442 /// `Err` leave the object unchanged: the server then refuses the request,
443 /// naming the initial value, and drops the object. As on the write
444 /// route, an initial value that is one application NULL and that this
445 /// refuses with PROPERTY / INVALID_DATA_TYPE leaves the property as it
446 /// is and counts as applied, so refuse a NULL that way. The default
447 /// refuses every property with PROPERTY / WRITE_ACCESS_DENIED, the
448 /// answer for a property that can't be initialized at creation (Clause
449 /// 15.3.1.3.1).
450 ///
451 /// The bundled server's CreateObject builds only built-in objects today
452 /// (Analog Input and Output, the binary types and the multi-state
453 /// types), so on a custom object this hook is reached only by a caller
454 /// that builds the object itself.
455 fn initialize_property(
456 &mut self,
457 _property: PropertyIdentifier,
458 _value: PropertyValue,
459 ) -> Result<(), Error> {
460 Err(Error::Protocol {
461 class: ErrorClass::PROPERTY.to_raw() as u32,
462 code: ErrorCode::WRITE_ACCESS_DENIED.to_raw() as u32,
463 })
464 }
465
466 /// Whether this object type can be deleted at runtime via DeleteObject.
467 ///
468 /// Default `true`; override `false` on object types that are not
469 /// deleteable (e.g. `Device`, `NetworkPort`).
470 fn is_deleteable(&self) -> bool {
471 true
472 }
473
474 /// List the REQUIRED properties for this object type.
475 ///
476 /// Migrated objects derive this set from canonical `R` and `W` rows,
477 /// including Property_List. Unmigrated objects retain the historical four
478 /// universal properties. Service-specific consumers may exclude
479 /// Property_List where their protocol contract requires it.
480 fn required_properties(&self) -> Cow<'static, [PropertyIdentifier]> {
481 let metadata = self.property_metadata();
482 if !metadata.is_empty() {
483 return crate::property_metadata::required_properties_from_metadata(metadata.as_ref());
484 }
485 static UNIVERSAL: [PropertyIdentifier; 4] = [
486 PropertyIdentifier::OBJECT_IDENTIFIER,
487 PropertyIdentifier::OBJECT_NAME,
488 PropertyIdentifier::OBJECT_TYPE,
489 PropertyIdentifier::PROPERTY_LIST,
490 ];
491 Cow::Borrowed(&UNIVERSAL)
492 }
493
494 /// Whether this object type supports COV notifications.
495 ///
496 /// Override to return `true` for object types that can generate COV
497 /// notifications (analog, binary, multi-state I/O/V). Default is `false`.
498 /// This answer admits SubscribeCOV, the whole-object form, and is the
499 /// default for
500 /// [`supports_subscribe_cov_property`](Self::supports_subscribe_cov_property).
501 fn supports_cov(&self) -> bool {
502 false
503 }
504
505 /// Whether SubscribeCOVProperty and SubscribeCOVPropertyMultiple may
506 /// monitor this object's properties.
507 ///
508 /// Defaults to [`supports_cov`](Self::supports_cov). An object that has
509 /// no whole-object COV criteria, because Clause 13.1's Table 13-1 doesn't
510 /// list its type, can still return `true` here while `supports_cov`
511 /// stays `false`: its property subscriptions then follow Table 13-1a and
512 /// SubscribeCOV is refused. The built-in Averaging object does this.
513 /// [`supports_cov_property`](Self::supports_cov_property) still decides
514 /// each property.
515 fn supports_subscribe_cov_property(&self) -> bool {
516 self.supports_cov()
517 }
518
519 /// Take pending local target work from a Staging object.
520 ///
521 /// The default keeps every non-Staging object source-compatible. The
522 /// bundled server calls this only while it owns the database mutation
523 /// guard, then performs the returned writes after releasing that guard.
524 #[doc(hidden)]
525 fn take_staging_write_plan_internal(&mut self) -> Option<crate::staging::StagingWritePlan> {
526 None
527 }
528
529 /// Return the current Staging transition generation, if applicable.
530 #[doc(hidden)]
531 fn staging_generation_internal(&self) -> Option<u64> {
532 None
533 }
534
535 /// Commit one guarded Staging plan's success or failure to Reliability.
536 ///
537 /// Returns whether readable source state changed. Implementations ignore a
538 /// stale generation so older work cannot fault a newer transition.
539 #[doc(hidden)]
540 fn complete_staging_write_plan_internal(&mut self, _generation: u64, _success: bool) -> bool {
541 false
542 }
543
544 /// Take the writes a Command or Channel object's Present_Value write
545 /// queued: a Command's selected list, or a Channel's value for its
546 /// members.
547 ///
548 /// The default keeps every other object source-compatible. The bundled
549 /// server takes it under the guard that committed the write, then makes
550 /// the writes after releasing that guard.
551 #[doc(hidden)]
552 fn take_command_run_internal(&mut self) -> Option<crate::command::CommandRun> {
553 None
554 }
555
556 /// Return the current Command or Channel run generation, if applicable.
557 #[doc(hidden)]
558 fn command_generation_internal(&self) -> Option<u64> {
559 None
560 }
561
562 /// Record how command `command` of a Command's running list fared.
563 ///
564 /// Returns whether the run is still the current one. Implementations
565 /// ignore a stale generation, so older work can't mark a newer run.
566 #[doc(hidden)]
567 fn record_command_write_internal(
568 &mut self,
569 _generation: u64,
570 _command: usize,
571 _success: bool,
572 ) -> bool {
573 false
574 }
575
576 /// Keep `datatype`, learned for a Channel's member in another device, for
577 /// the distributions after this one, or forget the one kept with `None`:
578 /// the member at zero-based `slot` of List_Of_Object_Property_References,
579 /// if that slot still holds `reference`. Writing the member, or the whole
580 /// list, forgets it too.
581 #[doc(hidden)]
582 fn remember_member_datatype_internal(
583 &mut self,
584 _slot: usize,
585 _reference: &bacnet_types::constructed::BACnetDeviceObjectPropertyReference,
586 _datatype: Option<crate::channel::MemberDatatype>,
587 ) {
588 }
589
590 /// End the current run, setting a Command's All_Writes_Successful or a
591 /// Channel's Write_Status and Reliability. `outcome` is `Ok` when every
592 /// write was made and succeeded, otherwise the run's first failure.
593 ///
594 /// Returns whether readable state changed; a stale generation is ignored.
595 #[doc(hidden)]
596 fn complete_command_run_internal(
597 &mut self,
598 _generation: u64,
599 _outcome: Result<(), crate::command::WriteFailure>,
600 ) -> bool {
601 false
602 }
603
604 /// Return this object's GetEnrollmentSummary event capability.
605 ///
606 /// The default opts custom and downstream objects out. Implementations opt
607 /// in only when they own an actual configured or implied event algorithm
608 /// and shared committed-transition history.
609 #[doc(hidden)]
610 fn enrollment_summary_capability_internal(&self) -> Option<EnrollmentSummaryCapability> {
611 None
612 }
613
614 /// Whether a readable property supports property-specific COV.
615 ///
616 /// The default preserves the existing behavior of every other COV-capable
617 /// object family while enforcing the bounded standardized Life Safety
618 /// surface for source-compatible custom Point and Zone implementations.
619 /// Other types answer
620 /// [`supports_subscribe_cov_property`](Self::supports_subscribe_cov_property)
621 /// for every property.
622 fn supports_cov_property(&self, property: PropertyIdentifier) -> bool {
623 match self.object_identifier().object_type() {
624 ObjectType::LIFE_SAFETY_POINT | ObjectType::LIFE_SAFETY_ZONE => matches!(
625 property,
626 PropertyIdentifier::PRESENT_VALUE
627 | PropertyIdentifier::STATUS_FLAGS
628 | PropertyIdentifier::TRACKING_VALUE
629 | PropertyIdentifier::SILENCED
630 | PropertyIdentifier::OPERATION_EXPECTED
631 ),
632 _ => self.supports_subscribe_cov_property(),
633 }
634 }
635
636 /// COV increment for this object (objects with a COV_Increment property).
637 ///
638 /// Returns `Some(increment)` for objects that use COV_Increment filtering
639 /// (e.g., AnalogInput, AnalogOutput, AnalogValue, Loop, Staging, and the
640 /// Integer, Positive Integer and Large Analog Value types). A notification
641 /// fires only when the numeric Present_Value delta reaches the increment.
642 /// Property COV inherits this increment only for numeric Present_Value;
643 /// other selected properties use their own supplied increment or typed
644 /// change reporting.
645 ///
646 /// The increment is an `f64` so every COV_Increment datatype fits without
647 /// rounding: a REAL widens exactly, a Large Analog Value's Double is kept
648 /// as is, and an Unsigned increment is exact up to 2^53.
649 ///
650 /// Returns `None` for objects that notify on any state change (binary, multi-state).
651 fn cov_increment(&self) -> Option<f64> {
652 None
653 }
654
655 /// Properties a whole-object (SubscribeCOV) notification reports after
656 /// its leading value and Status_Flags, in report order.
657 ///
658 /// The leading value is Present_Value, except on Access Point, whose
659 /// Table 13-1 row leads with Access_Event; the server chooses it by object
660 /// type. The default follows the object type's Table 13-1 row:
661 ///
662 /// - Access Door: Door_Alarm_State, a trigger.
663 /// - Access Point: Access_Event_Tag, Access_Event_Time (a trigger),
664 /// Access_Event_Credential and Access_Event_Authentication_Factor.
665 /// - Credential Data Input: Update_Time, a trigger.
666 /// - Load Control: Requested_Shed_Level, Start_Time, Shed_Duration and
667 /// Duty_Window, all triggers.
668 /// - Loop: Setpoint and Controlled_Variable_Value.
669 /// - Pulse Converter: Update_Time.
670 /// - Staging: Present_Stage, a trigger.
671 ///
672 /// A trigger's change sends a notification by itself; any other listed
673 /// value only rides along. Every other type reports nothing more. The
674 /// server leaves out a listed property the object's Property_List lacks.
675 /// Property subscriptions (SubscribeCOVProperty and
676 /// SubscribeCOVPropertyMultiple) report their own property instead.
677 fn cov_reported_properties(&self) -> &'static [CovReportedProperty] {
678 cov_reported_properties_default(self.object_identifier().object_type())
679 }
680
681 /// Set the OVERRIDDEN bit in StatusFlags.
682 ///
683 /// For software-only objects this is always FALSE per spec. Hardware
684 /// integrations can override to set TRUE when present_value is overridden
685 /// by physical means (e.g., a manual switch on an output).
686 fn set_overridden(&mut self, _overridden: bool) {}
687
688 /// Evaluate intrinsic reporting after a present_value change.
689 ///
690 /// This is the per-write entry point: it seeds (or cancels) a pending
691 /// delayed transition and fires immediately only when `Time_Delay == 0`.
692 /// It never advances the `Time_Delay` countdown — repeated writes to the
693 /// same value do not shorten the delay (ASHRAE 135-2020 Clause 13.3 counts
694 /// the delay in seconds, so the countdown advances once per elapsed second
695 /// via [`tick_intrinsic_reporting`](Self::tick_intrinsic_reporting)).
696 ///
697 /// Returns `Some(TransitionOutcome)` whenever a transition is ready to be
698 /// committed, or
699 /// `None` when none did (no change, delay seeded, or the object does not
700 /// support intrinsic reporting). A cleared `Event_Enable` bit sets the
701 /// outcome's `distribute` flag to false rather than withholding the
702 /// transition. Implementations must leave `Event_State`,
703 /// `Acked_Transitions`, event history, and fire-ready detector state
704 /// unchanged until [`commit_event_transition_internal`](Self::commit_event_transition_internal)
705 /// succeeds. Clause 13.2.2.1.4's transition actions run either way, and
706 /// `Event_Enable` disables only external distribution, downstream in the
707 /// notification-distribution process (Clause 13.2.5).
708 fn evaluate_intrinsic_reporting(&mut self) -> Option<TransitionOutcome> {
709 None
710 }
711
712 /// Advance the `Time_Delay` countdown for a pending transition.
713 ///
714 /// Called by the server's one-second intrinsic-reporting task. Fires the
715 /// pending transition when its delay elapses this tick, cancels it if the
716 /// triggering condition reverted, and returns `Some(TransitionOutcome)`
717 /// when a transition is ready. A fire-ready proposal must remain
718 /// retryable until the commit hook succeeds. As with
719 /// [`evaluate_intrinsic_reporting`](Self::evaluate_intrinsic_reporting),
720 /// `Event_Enable` is reported via `distribute`, not by returning `None`.
721 /// Objects without a delayed transition return `None`.
722 fn tick_intrinsic_reporting(&mut self) -> Option<TransitionOutcome> {
723 None
724 }
725
726 /// Atomically commit all object-owned state for one event transition.
727 ///
728 /// The caller supplies an exact state change, its transition coordinate,
729 /// the resolved Notification Class `Ack_Required` value, a typed
730 /// timestamp, and an optional message. Implementations must validate the
731 /// coordinate and source state before changing `Event_State`,
732 /// `Acked_Transitions`, `Event_Time_Stamps`, or stored message state, and
733 /// must leave every value unchanged on error. A successful
734 /// implementation also finalizes the detector's pending and fault-edge
735 /// state; a failed commit leaves that state retryable.
736 ///
737 /// This internal channel is deliberately separate from network property
738 /// writes and notification distribution. The default fails closed so an
739 /// object participates only after implementing this contract. Custom objects
740 /// can own and commit their state directly using the public
741 /// [`EventTransitionCommit`] and [`EventTransitionCommitError`] types;
742 /// no built-in commit kernel is required. Both server intrinsic paths
743 /// require success before distributing a notification.
744 #[doc(hidden)]
745 fn commit_event_transition_internal(
746 &mut self,
747 _commit: EventTransitionCommit,
748 ) -> Result<(), EventTransitionCommitError> {
749 Err(EventTransitionCommitError::Unsupported)
750 }
751
752 /// Atomically commit Event Enrollment Reliability and transition state.
753 ///
754 /// This Event Enrollment-specific channel joins `Reliability` to the
755 /// existing atomic `Event_State`, `Acked_Transitions`, and
756 /// `Event_Time_Stamps` commit. Implementations must stage every supplied
757 /// value before assigning object-owned fields and leave them unchanged on
758 /// error. The default fails closed for custom objects that have not adopted
759 /// the stronger contract.
760 #[doc(hidden)]
761 fn commit_event_enrollment_reliability_internal(
762 &mut self,
763 _commit: EventEnrollmentReliabilityCommit,
764 ) -> Result<(), EventTransitionCommitError> {
765 Err(EventTransitionCommitError::Unsupported)
766 }
767
768 /// Evaluate this object's schedule at `time` on `today` (Clause 12.24.4).
769 ///
770 /// `calendar_active` answers for a special event whose period references
771 /// a Calendar: whether that Calendar is TRUE on `today`. Returns the
772 /// writes owed when Present_Value changed, the object has just entered
773 /// its Effective_Period, or its references or priority changed since the
774 /// last write, with the complete local references, target array indices
775 /// included. Otherwise, while some references refused their last write,
776 /// returns a [`retry`](ScheduleWrite::retry) of the current value to
777 /// those references only (#1436). Only meaningful for Schedule objects;
778 /// default returns `None`.
779 fn tick_schedule(
780 &mut self,
781 _today: SpecificDate,
782 _time: Time,
783 _calendar_active: &dyn Fn(ObjectIdentifier) -> bool,
784 ) -> Option<ScheduleWrite> {
785 None
786 }
787
788 /// Take the writes this schedule owes its targets apart from its
789 /// calculation, each once, in order: a NULL to every priority slot a
790 /// change of List_Of_Object_Property_References or Priority_For_Writing
791 /// left behind (#1088), then the Present_Value owed out of service, for a
792 /// client's write (Clause 12.24.14) or a change of those two properties.
793 ///
794 /// A schedule pass collects them before calling
795 /// [`tick_schedule`](Self::tick_schedule), so they reach the targets ahead
796 /// of any calculated value; they need no clock. Only meaningful for
797 /// Schedule objects; default returns none.
798 fn take_owed_schedule_writes(&mut self) -> Vec<ScheduleWrite> {
799 Vec::new()
800 }
801
802 /// Report how each target took `write`, one of this schedule's writes:
803 /// `outcomes` holds one entry per member of `write.references`, in order.
804 ///
805 /// A Schedule judges the reference half of its Reliability from these
806 /// (Clause 12.24.13, #1086). Returns whether a readable property, such as
807 /// Reliability, changed. Only meaningful for Schedule objects; default
808 /// returns `false`.
809 fn complete_schedule_write(
810 &mut self,
811 _write: &ScheduleWrite,
812 _outcomes: &[ScheduleTargetOutcome],
813 ) -> bool {
814 false
815 }
816
817 /// The [`retry`](ScheduleWrite::retry) of the current value to the
818 /// references whose last write was refused and that name `target`, an
819 /// object just added to the database (#1440), so the refusal can clear
820 /// without waiting for the next pass. `None` when no such refusal
821 /// stands, or when the pass itself would retry nothing: out of service,
822 /// outside Effective_Period, or with a NULL Present_Value. Only
823 /// meaningful for Schedule objects; default returns `None`.
824 fn retry_refusals_naming(&self, _target: ObjectIdentifier) -> Option<ScheduleWrite> {
825 None
826 }
827
828 /// Whether this Calendar's Date_List matches `day`: its Present_Value on
829 /// that day. `None` for an object that does not evaluate a date list; the
830 /// schedule tick then reads its Present_Value instead.
831 #[doc(hidden)]
832 fn calendar_state_internal(&self, _day: SpecificDate) -> Option<bool> {
833 None
834 }
835
836 /// Acknowledge an alarm transition. Sets the `transition_bit` flags in acked_transitions.
837 /// Returns Ok(()) if the object supports event detection, Err otherwise.
838 fn acknowledge_alarm(
839 &mut self,
840 _transition_bit: EventTransitionBits,
841 ) -> Result<(), bacnet_types::error::Error> {
842 Err(bacnet_types::error::Error::Protocol {
843 class: bacnet_types::enums::ErrorClass::OBJECT.to_raw() as u32,
844 code: bacnet_types::enums::ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw()
845 as u32,
846 })
847 }
848
849 /// Correlate an acknowledgment with the latest committed transition.
850 #[doc(hidden)]
851 fn acknowledge_alarm_correlated_internal(
852 &mut self,
853 _event_state: EventState,
854 _timestamp: &BACnetTimeStamp,
855 ) -> Result<(), Error> {
856 Err(Error::Protocol {
857 class: ErrorClass::OBJECT.to_raw() as u32,
858 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
859 })
860 }
861
862 /// Correlate an acknowledgment and report exact object-owned history.
863 ///
864 /// The default delegates to the source-compatible coarse hook and returns
865 /// no notification context. Custom objects that implement only the coarse
866 /// hook therefore retain their accepted-acknowledgment behavior without the
867 /// bundled server guessing historical states for an ACK notification.
868 #[doc(hidden)]
869 fn acknowledge_alarm_correlated_detailed_internal(
870 &mut self,
871 event_state: EventState,
872 timestamp: &BACnetTimeStamp,
873 ) -> Result<Option<EventStateChange>, Error> {
874 self.acknowledge_alarm_correlated_internal(event_state, timestamp)
875 .map(|()| None)
876 }
877
878 /// Apply a LifeSafetyOperation atomically to this object.
879 ///
880 /// Implementations must leave the object unchanged when returning `Err`.
881 /// They run synchronously under the object-database write lock and must be
882 /// fast, nonblocking, and panic-free. External or irreversible actuation
883 /// also needs an application-owned idempotency/replay contract. The default
884 /// reports that the object does not support this service. Successful
885 /// implementations return an outcome with exact, ordered, deduplicated
886 /// committed property changes. `AlreadyApplied` leaves all state unchanged
887 /// and reports no deltas. The bundled server uses these changes for COV
888 /// after releasing the database lock and dispatching the service response.
889 fn apply_life_safety_operation(
890 &mut self,
891 _operation: LifeSafetyOperation,
892 ) -> Result<LifeSafetyOperationOutcome, Error> {
893 Err(Error::Protocol {
894 class: ErrorClass::OBJECT.to_raw() as u32,
895 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
896 })
897 }
898
899 /// Set the next LifeSafetyOperation expected by trusted local logic.
900 ///
901 /// This is an application-facing state channel, not a network property
902 /// write. The default reports that the object does not support the state.
903 fn set_life_safety_operation_expected_internal(
904 &mut self,
905 _operation: LifeSafetyOperation,
906 ) -> Result<(), Error> {
907 Err(Error::Protocol {
908 class: ErrorClass::OBJECT.to_raw() as u32,
909 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
910 })
911 }
912
913 /// Apply an internally-detected `Event_State` transition.
914 ///
915 /// This is the **internal** lifecycle path for the algorithmically-derived
916 /// `Event_State` on objects such as Event Enrollment (ASHRAE 135-2020
917 /// Clause 12.12). It is deliberately distinct from the network
918 /// [`write_property`](Self::write_property) route: `Event_State` is
919 /// read-only over the network, so network writes are rejected while the
920 /// server's evaluator reaches the field through this method. Objects
921 /// without an algorithmic `Event_State` return
922 /// `OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED`.
923 ///
924 /// The **default** returns `Err`, so objects without an algorithmic
925 /// `Event_State` opt out. Objects that do model one (e.g.
926 /// `EventEnrollmentObject`) override this to store the value verbatim:
927 /// the only caller is a trusted internal evaluator that passes a modeled
928 /// [`EventState`], mirroring the inherent `set_event_state` builder and
929 /// the existing read arm. Network-facing validation — rejecting all
930 /// `Event_State` writes — lives in [`write_property`](Self::write_property),
931 /// not here. Implementations must leave `Event_State` unchanged when they
932 /// return `Err`.
933 fn set_event_state_internal(&mut self, _state: EventState) -> Result<(), Error> {
934 Err(Error::Protocol {
935 class: ErrorClass::OBJECT.to_raw() as u32,
936 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
937 })
938 }
939
940 /// Snapshot this object's Event Enrollment evaluation state, if it models one.
941 ///
942 /// This is the read half of the internal channel the server's Event
943 /// Enrollment evaluator uses to persist per-enrollment algorithm state
944 /// across evaluation cycles: the pending (delayed) transition countdown,
945 /// the CHANGE_OF_VALUE detection baseline (the monitored sample at the
946 /// latest NORMAL indication, per Clause 13.3.3), and
947 /// the monitored value behind the most recent OFFNORMAL transition (Clause
948 /// 13.3.2 condition (c)). Like [`set_event_state_internal`](Self::set_event_state_internal)
949 /// it deliberately bypasses the network property model: none of the three
950 /// slots is a BACnet property, and 135-2020 assigns their initialization
951 /// to local matters.
952 ///
953 /// The default returns `None` — objects without algorithmic event
954 /// detection carry no such state, and the evaluator treats `None` as an
955 /// empty state it cannot write back (delay honoring and the COV baseline
956 /// then stay unavailable, matching this crate's pre-delay behavior).
957 fn enrollment_eval_state_internal(&self) -> Option<EventEnrollmentEvalState> {
958 None
959 }
960
961 /// Store this object's Event Enrollment evaluation state.
962 ///
963 /// The write half of [`enrollment_eval_state_internal`](Self::enrollment_eval_state_internal).
964 /// The only caller is the trusted server evaluator, passing a state it
965 /// derived from a prior snapshot plus the current cycle's evaluation.
966 /// Implementations enforce the Clause 13.2.2.1 invariant by construction:
967 /// transitions are prohibited while `Event_Detection_Enable` is FALSE,
968 /// so a write arriving then is refused rather than queued (and the
969 /// detection-disable reset has already cleared the fields).
970 ///
971 /// The **default** returns `Err`, so objects without enrollment evaluation
972 /// state opt out and the evaluator's write-back is dropped, never stored
973 /// into an object that does not model it.
974 fn set_enrollment_eval_state_internal(
975 &mut self,
976 _state: EventEnrollmentEvalState,
977 ) -> Result<(), Error> {
978 Err(Error::Protocol {
979 class: ErrorClass::OBJECT.to_raw() as u32,
980 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
981 })
982 }
983
984 /// Snapshot the monitored source that owns Event Enrollment private state.
985 ///
986 /// The outer `Option` indicates whether the object supports this channel;
987 /// the inner `Option` is empty before a source has been established.
988 /// The server stores source ownership in its object database when an
989 /// object implements evaluation state but leaves this channel unsupported.
990 fn enrollment_eval_source_internal(&self) -> Option<Option<EventEnrollmentMonitoredSource>> {
991 None
992 }
993
994 /// Store or clear the monitored source that owns Event Enrollment state.
995 fn set_enrollment_eval_source_internal(
996 &mut self,
997 _source: Option<EventEnrollmentMonitoredSource>,
998 ) -> Result<(), Error> {
999 Err(Error::Protocol {
1000 class: ErrorClass::OBJECT.to_raw() as u32,
1001 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1002 })
1003 }
1004
1005 /// Set or clear one `Acked_Transitions` bit on a received event-state
1006 /// transition.
1007 ///
1008 /// Implements the alarm-acknowledgment half of Clause 13.2.2.1.4's fourth
1009 /// transition action: pass the transition to alarm-acknowledgment handling.
1010 /// Under Clause 13.2.3, receiving a transition updates its Acked_Transitions
1011 /// bit to the inverse of the corresponding Ack_Required bit. The caller (the
1012 /// server evaluator) resolves `Ack_Required` from the referenced
1013 /// Notification Class object and passes the outcome as `acknowledged`;
1014 /// this method performs only the bit maintenance.
1015 ///
1016 /// `transition_bit` is the transition direction's flag, as returned by
1017 /// [`EventTransition::bit_mask`](crate::event::EventTransition::bit_mask). The set half overlaps the
1018 /// network-reachable [`acknowledge_alarm`](Self::acknowledge_alarm), which
1019 /// also ORs the bit in per Clause 13.2.3's acknowledgment-indication
1020 /// paragraph; the clear half has no network route by design (a property
1021 /// write could fabricate or erase acknowledgments — see the
1022 /// `write_generic_event_properties!` denial comment).
1023 ///
1024 /// The **default** returns `Err`, so objects without an algorithmic
1025 /// `Acked_Transitions` opt out.
1026 fn set_acked_transitions_internal(
1027 &mut self,
1028 _transition_bit: EventTransitionBits,
1029 _acknowledged: bool,
1030 ) -> Result<(), Error> {
1031 Err(Error::Protocol {
1032 class: ErrorClass::OBJECT.to_raw() as u32,
1033 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1034 })
1035 }
1036
1037 /// Evaluate and, when necessary, mutate this object's `Reliability`.
1038 ///
1039 /// Reliability evaluation is object-owned: an implementation decides
1040 /// whether and how its internal conditions affect `Reliability`, performs
1041 /// the mutation itself, and returns [`ReliabilityEvaluation::Changed`] only
1042 /// after that mutation succeeds. [`ReliabilityEvaluation::Unchanged`] means
1043 /// the object made no mutation. Returning `Err` MUST leave all object state
1044 /// unchanged.
1045 ///
1046 /// The default opts out without changing state, preserving source
1047 /// compatibility for object implementations that do not own a reliability
1048 /// evaluation algorithm.
1049 fn evaluate_reliability_internal(&mut self) -> Result<ReliabilityEvaluation, Error> {
1050 Ok(ReliabilityEvaluation::Unchanged)
1051 }
1052
1053 /// Whether periodic object-owned reliability evaluation is currently
1054 /// inhibited.
1055 ///
1056 /// The default is FALSE so existing and downstream object implementations
1057 /// remain source-compatible. Objects that elect the optional
1058 /// Reliability_Evaluation_Inhibit property override this internal
1059 /// predicate; reporting of an already-applied Reliability transition is
1060 /// intentionally unaffected.
1061 #[doc(hidden)]
1062 fn reliability_evaluation_inhibited_internal(&self) -> bool {
1063 false
1064 }
1065
1066 /// Apply an internally-derived `Reliability` value.
1067 ///
1068 /// This is the **internal** reliability-evaluation path, distinct from the
1069 /// network [`write_property`](Self::write_property) route. Implementations
1070 /// enforce symmetric ownership: clients may write while `Out_Of_Service`
1071 /// is TRUE, and internal evaluation may write while it is FALSE. ASHRAE
1072 /// 135-2020 Clause 3.2 describes reliability evaluation as the object's
1073 /// assessment of its reliability, producing its Reliability property value.
1074 ///
1075 /// The default rejects the operation, so object types without an internal
1076 /// reliability-evaluation process remain unaffected.
1077 fn set_reliability_internal(&mut self, _reliability: Reliability) -> Result<(), Error> {
1078 Err(Error::Protocol {
1079 class: ErrorClass::OBJECT.to_raw() as u32,
1080 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1081 })
1082 }
1083
1084 /// Apply the logical `Present_Value` supplied by the local application.
1085 ///
1086 /// This narrow internal hook is distinct from the network
1087 /// [`write_property`](Self::write_property) route. Built-in Analog Input,
1088 /// Binary Input, and Multi-state Input objects opt in. Their implementations
1089 /// accept application updates only while in service; rejecting them while
1090 /// `Out_Of_Service` is TRUE is a local ownership policy that protects the
1091 /// client's simulation value, not a requirement imposed by the Standard.
1092 ///
1093 /// Analog, Binary, and Multi-state Value objects opt in unless their
1094 /// [`PresentValueAccess`](crate::present_value_access::PresentValueAccess)
1095 /// is `Commandable`. For them, rejecting updates while `Out_Of_Service` is
1096 /// TRUE is required: their Out_Of_Service clause keeps software local to
1097 /// the device from changing Present_Value.
1098 ///
1099 /// Life Safety Point and Zone opt in with a BACnetLifeSafetyState. Clients
1100 /// never write their Present_Value, so they take the application's value in
1101 /// service and out of service alike, and the hook leaves Tracking_Value,
1102 /// Silenced and Operation_Expected as they are: any latching rule belongs
1103 /// to the application.
1104 ///
1105 /// The default fails closed so commandable and other object families do not
1106 /// acquire privileged `Present_Value` write authority through this hook.
1107 fn set_present_value_internal(&mut self, _value: PropertyValue) -> Result<(), Error> {
1108 Err(Error::Protocol {
1109 class: ErrorClass::OBJECT.to_raw() as u32,
1110 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1111 })
1112 }
1113
1114 /// Apply the `Tracking_Value` the local application derived.
1115 ///
1116 /// Only the built-in Life Safety Point and Zone opt in, with a
1117 /// BACnetLifeSafetyState; the property stays read-only over the network in
1118 /// service. While `Out_Of_Service` is TRUE a client's simulated value keeps
1119 /// being served and this one takes over on the return to service. The
1120 /// default fails closed with the same error as
1121 /// [`set_present_value_internal`](Self::set_present_value_internal).
1122 fn set_tracking_value_internal(&mut self, _value: PropertyValue) -> Result<(), Error> {
1123 Err(Error::Protocol {
1124 class: ErrorClass::OBJECT.to_raw() as u32,
1125 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1126 })
1127 }
1128
1129 /// Apply the Controlled_Variable_Value measured by the local application.
1130 ///
1131 /// Only the built-in Loop opts in. Its control algorithm runs in the
1132 /// application, which feeds the measured value here while it does so; the
1133 /// property stays read-only over the network. The default fails closed
1134 /// with the same error as [`set_present_value_internal`](Self::set_present_value_internal).
1135 fn set_controlled_variable_value_internal(
1136 &mut self,
1137 _value: PropertyValue,
1138 ) -> Result<(), Error> {
1139 Err(Error::Protocol {
1140 class: ErrorClass::OBJECT.to_raw() as u32,
1141 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1142 })
1143 }
1144
1145 /// Record one sample for an Averaging object.
1146 ///
1147 /// Only the built-in Averaging object opts in. The value is the reading
1148 /// of the referenced property, or `None` when the attempt produced no
1149 /// value, and the object updates its sample window, statistics and counts
1150 /// together. The application calls it for the samples it takes, and the
1151 /// database for the ones the object's own schedule asks for (see
1152 /// [`take_due_averaging_sample_internal`](Self::take_due_averaging_sample_internal)).
1153 /// The default fails closed with the same error as
1154 /// [`set_present_value_internal`](Self::set_present_value_internal).
1155 fn add_averaging_sample_internal(
1156 &mut self,
1157 _sample: Option<PropertyValue>,
1158 ) -> Result<(), Error> {
1159 Err(Error::Protocol {
1160 class: ErrorClass::OBJECT.to_raw() as u32,
1161 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1162 })
1163 }
1164
1165 /// Claim the Averaging sample the object's schedule has due at the
1166 /// process-local monotonic instant `now`.
1167 ///
1168 /// When one is due the object steps its schedule past it and returns the
1169 /// property to read; the caller reads it while it still holds exclusive
1170 /// access and records the outcome with
1171 /// [`add_averaging_sample_internal`](Self::add_averaging_sample_internal).
1172 /// An object that answers here reports the due time through
1173 /// [`next_monotonic_deadline_internal`](Self::next_monotonic_deadline_internal),
1174 /// so the server's monotonic task wakes for it. The built-in Averaging
1175 /// object opts in while it holds an Object_Property_Reference and a
1176 /// monotonic clock is bound; the default has nothing to sample.
1177 #[doc(hidden)]
1178 fn take_due_averaging_sample_internal(
1179 &mut self,
1180 _now: Duration,
1181 ) -> Option<BACnetObjectPropertyReference> {
1182 None
1183 }
1184
1185 /// The local property a Pulse Converter counts its input from, its
1186 /// Input_Reference (Clause 12.23.6): `Some(None)` while it holds none,
1187 /// and `None` for an object that counts no referenced input, which the
1188 /// default is.
1189 ///
1190 /// The database judges the reference against its own objects
1191 /// (`ObjectDatabase::check_input_reference`) and reports the verdict
1192 /// through [`set_input_usable_internal`](Self::set_input_usable_internal).
1193 #[doc(hidden)]
1194 fn input_reference_internal(&self) -> Option<Option<&BACnetObjectPropertyReference>> {
1195 None
1196 }
1197
1198 /// Take the database's verdict on the reference
1199 /// [`input_reference_internal`](Self::input_reference_internal) returned:
1200 /// `false` when it names a property the object can't count from, which
1201 /// Clause 12.23.9 reports as a CONFIGURATION_ERROR Reliability. Returns
1202 /// whether a readable property changed, so a caller owes COV. The
1203 /// default has no input to judge.
1204 #[doc(hidden)]
1205 fn set_input_usable_internal(&mut self, _usable: bool) -> bool {
1206 false
1207 }
1208
1209 /// Take a reading of the property
1210 /// [`input_reference_internal`](Self::input_reference_internal) names,
1211 /// or `None` when there is nothing to read. A Pulse Converter counts each
1212 /// increase over the last reading into Count (Clause 12.23.14), across a
1213 /// wrap when the reading carries its bound. Returns whether a readable
1214 /// property changed, so a caller owes COV. The default counts nothing.
1215 #[doc(hidden)]
1216 fn take_input_reading_internal(
1217 &mut self,
1218 _reading: Option<crate::accumulator::InputReading>,
1219 ) -> bool {
1220 false
1221 }
1222
1223 /// Borrow this object's Audit Log query storage, if it has any.
1224 ///
1225 /// This read-only, type-erased channel lets the bundled server execute an
1226 /// AuditLogQuery against an object's already-loaded retained records. It
1227 /// deliberately does not expose persistence, mutation, or object
1228 /// downcasting. The **default** returns `None`; an Audit Log object that
1229 /// does not opt in is reported as SERVICES /
1230 /// OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.
1231 fn audit_log_storage_internal(&self) -> Option<&dyn AuditLogStorage> {
1232 None
1233 }
1234
1235 /// Instance-owned optional Audit Log forwarding profile, never durable pending work.
1236 #[doc(hidden)]
1237 fn audit_log_forwarding_internal(&self) -> Option<Arc<crate::audit::AuditLogForwarding>> {
1238 None
1239 }
1240
1241 /// Set a trusted local Audit Log parent without downcasting or a second object store.
1242 ///
1243 /// The default opts out. This is not a network property-write capability;
1244 /// callers own configuration admission, and the server still validates the route.
1245 #[doc(hidden)]
1246 fn set_audit_log_parent_internal(
1247 &mut self,
1248 _parent: bacnet_types::constructed::BACnetDeviceObjectReference,
1249 ) -> Result<(), Error> {
1250 Err(Error::Protocol {
1251 class: ErrorClass::OBJECT.to_raw() as u32,
1252 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1253 })
1254 }
1255
1256 /// Mutably borrow this object's Audit notification receiver capability.
1257 ///
1258 /// The default opts out. Implementations own their persistence transaction
1259 /// and must leave memory unchanged when the batch cannot be committed.
1260 fn audit_log_notification_sink_internal(
1261 &mut self,
1262 ) -> Option<&mut dyn AuditLogNotificationSink> {
1263 None
1264 }
1265
1266 /// Mutably borrow this object's staged-write capability (#1270).
1267 ///
1268 /// An object that saves a written state before serving it opts in, so the
1269 /// server can stage the write and run the save without the database
1270 /// guard. The default opts out: the object's writes run as they always
1271 /// do. See [`crate::durable`].
1272 #[doc(hidden)]
1273 fn durable_writes_internal(&mut self) -> Option<&mut dyn DurableWrites> {
1274 None
1275 }
1276
1277 /// Borrow this object's trusted local File configuration capability.
1278 ///
1279 /// The default returns `None`. The built-in [`crate::file::FileObject`]
1280 /// opts in so bindings can configure its pending payload, access method,
1281 /// and growth limits without general object downcasting or a parallel
1282 /// state cache.
1283 #[doc(hidden)]
1284 fn file_configuration_internal(&self) -> Option<&dyn FileConfiguration> {
1285 None
1286 }
1287
1288 /// Mutably borrow this object's trusted local File configuration capability.
1289 ///
1290 /// The default returns `None`; payload mutations remain object-owned and
1291 /// must preserve the same accounting and metadata behavior as the built-in
1292 /// File setters.
1293 #[doc(hidden)]
1294 fn file_configuration_internal_mut(&mut self) -> Option<&mut dyn FileConfiguration> {
1295 None
1296 }
1297
1298 /// Borrow this object's File storage, if it has any.
1299 ///
1300 /// This is the read half of the **internal** channel the server's
1301 /// AtomicReadFile handler uses to reach file contents. Like
1302 /// [`set_event_state_internal`](Self::set_event_state_internal) it
1303 /// bypasses the property model on purpose: Table 12-16 (ASHRAE 135-2020
1304 /// Clause 12.13) defines no File Data property, so file contents are
1305 /// reachable only through the Clause 14 File Access Services.
1306 ///
1307 /// The **default** returns `None`, so object types without a file opt
1308 /// out; the server reports `None` on a File-typed object as SERVICES /
1309 /// FILE_ACCESS_DENIED (Clause 18 covers locked or inaccessible files)
1310 /// rather than reading it as empty.
1311 /// Applications backing a File object with their own storage — a disk
1312 /// file, a firmware partition — implement [`FileStorage`] and return
1313 /// `Some`.
1314 fn file_storage_internal(&self) -> Option<&dyn FileStorage> {
1315 None
1316 }
1317
1318 /// Mutably borrow this object's File storage, if it has any.
1319 ///
1320 /// The write half of
1321 /// [`file_storage_internal`](Self::file_storage_internal), used by the
1322 /// AtomicWriteFile handler after the read-only and access-method gates
1323 /// have passed. The **default** returns `None`.
1324 fn file_storage_internal_mut(&mut self) -> Option<&mut dyn FileStorage> {
1325 None
1326 }
1327
1328 /// Return stable identities aligned with this object's resident log records.
1329 ///
1330 /// Implementing log objects return identities oldest-to-newest, in the
1331 /// same order as their public resident-record view and their
1332 /// [`log_buffer_internal`](Self::log_buffer_internal) records. Sequence
1333 /// numbers are object-owned metadata and never part of an encoded BACnet
1334 /// record. Non-log objects return `None`.
1335 fn log_record_identities_internal(&self) -> Option<Vec<LogRecordIdentity>> {
1336 None
1337 }
1338
1339 /// Borrow this object's Log_Buffer records as ReadRange pages them.
1340 ///
1341 /// The built-in Trend Log, Trend Log Multiple and Event Log objects
1342 /// answer a ReadProperty of Log_Buffer with PROPERTY / READ_ACCESS_DENIED,
1343 /// since their clauses (12.25.14, 12.30.19, 12.27.13) open the buffer to
1344 /// ReadRange only; the server's ReadRange handler reads it through this
1345 /// channel instead, one encoded record per item. The **default** returns
1346 /// `None`, which leaves ReadRange reading the property through
1347 /// [`read_property`](Self::read_property).
1348 fn log_buffer_internal(&self) -> Option<&dyn LogBufferRecords> {
1349 None
1350 }
1351
1352 /// Submit a trend record to an object's log lifecycle.
1353 ///
1354 /// `Ok(())` means the operation was accepted, not that an ordinary record
1355 /// became resident: disabled logs ignore it and zero-capacity logs may only
1356 /// increment their count. A required status transition can replace the
1357 /// ordinary record. Timestamp failures return an error without mutation.
1358 /// Objects without trend insertion return OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.
1359 fn add_trend_record(&mut self, _record: BACnetLogRecord) -> Result<(), Error> {
1360 Err(Error::Protocol {
1361 class: ErrorClass::OBJECT.to_raw() as u32,
1362 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1363 })
1364 }
1365
1366 /// Submit a Trend Log Multiple record, one value per monitored member, to
1367 /// an object's log lifecycle.
1368 ///
1369 /// The outcomes match [`add_trend_record`](Self::add_trend_record).
1370 /// Objects without Trend Log Multiple insertion return
1371 /// OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.
1372 fn add_trend_multiple_record(&mut self, _record: BACnetLogMultipleRecord) -> Result<(), Error> {
1373 Err(Error::Protocol {
1374 class: ErrorClass::OBJECT.to_raw() as u32,
1375 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1376 })
1377 }
1378
1379 /// Submit an Event Log record to an object's log lifecycle.
1380 ///
1381 /// The server hands the event notifications this device builds to its
1382 /// Event Logs through this hook
1383 /// ([`ObjectDatabase::log_event_notification`](crate::database::ObjectDatabase::log_event_notification)).
1384 /// The outcomes match [`add_trend_record`](Self::add_trend_record).
1385 /// Objects without Event Log insertion return
1386 /// OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.
1387 fn add_event_log_record(&mut self, _record: BACnetEventLogRecord) -> Result<(), Error> {
1388 Err(Error::Protocol {
1389 class: ErrorClass::OBJECT.to_raw() as u32,
1390 code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32,
1391 })
1392 }
1393
1394 /// Look at a log's Start_Time / Stop_Time window against the bound
1395 /// clock, recording the LOG_DISABLED change when it opened or closed
1396 /// since the last look while Enable is TRUE.
1397 ///
1398 /// The trend poller calls this for every Trend Log, Trend Log Multiple
1399 /// and Event Log on each pass, so a window that moves while no record
1400 /// arrives is still logged within one pass. Returns whether a record was
1401 /// added. The default, for objects without a window, does nothing and
1402 /// returns `false`.
1403 #[doc(hidden)]
1404 fn refresh_log_window_internal(&mut self) -> bool {
1405 false
1406 }
1407
1408 /// Whether this Event Log takes the event notifications the device
1409 /// receives, through
1410 /// [`ObjectDatabase::log_received_event_notification`](crate::database::ObjectDatabase::log_received_event_notification).
1411 /// The default, and an Event Log that hasn't opted in, says no.
1412 #[doc(hidden)]
1413 fn logs_received_event_notifications_internal(&self) -> bool {
1414 false
1415 }
1416
1417 /// The counts the last committed BUFFER_READY transition reported, for
1418 /// the server to carry as its event values (Clause 13.3.7). `None` for an
1419 /// object that hasn't reported one, or doesn't run that algorithm.
1420 #[doc(hidden)]
1421 fn buffer_ready_report_internal(&self) -> Option<BufferReadyReport> {
1422 None
1423 }
1424
1425 /// The local property this object's Event_Algorithm_Inhibit follows, its
1426 /// Event_Algorithm_Inhibit_Ref (#1329); `None` while it holds none, and
1427 /// for an object without one, which the default is. The database reads
1428 /// the property and hands the value over through
1429 /// [`follow_event_algorithm_inhibit_internal`](Self::follow_event_algorithm_inhibit_internal).
1430 #[doc(hidden)]
1431 fn event_algorithm_inhibit_reference_internal(&self) -> Option<BACnetObjectPropertyReference> {
1432 None
1433 }
1434
1435 /// Take the value the database read from the property
1436 /// [`event_algorithm_inhibit_reference_internal`](Self::event_algorithm_inhibit_reference_internal)
1437 /// names as Event_Algorithm_Inhibit; returns whether it changed. The
1438 /// default follows nothing.
1439 #[doc(hidden)]
1440 fn follow_event_algorithm_inhibit_internal(&mut self, _inhibit: bool) -> bool {
1441 false
1442 }
1443}