Skip to main content

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