Skip to main content

ObjectDatabase

Struct ObjectDatabase 

Source
pub struct ObjectDatabase { /* private fields */ }
Expand description

A collection of BACnet objects, keyed by ObjectIdentifier.

Enforces Object_Name uniqueness within a device. Maintains secondary indexes for O(1) name lookup and O(1) type lookup.

Implementations§

Source§

impl ObjectDatabase

Source

pub fn sample_due_averaging_objects( &mut self, now: Duration, ) -> Vec<ObjectIdentifier>

Take every Averaging sample due at the monotonic instant now and return the objects that recorded one, for COV processing.

The caller must hold exclusive database access for the whole call. A due object names the local property it averages (take_due_averaging_sample_internal); this reads it and records the value. An attempt that yields nothing usable is recorded as a miss, which counts toward Attempted_Samples but not Valid_Samples: a referenced object or property that doesn’t exist, an array index on a property that isn’t an array, any other read error, and a value the object refuses to average (a datatype outside the five Clause 12.5 accepts, or a REAL that isn’t finite).

Without a bound monotonic clock nothing is ever due. Reads go through BACnetObject::read_property, so they must stay bounded, as for Trend Log polling.

Source§

impl ObjectDatabase

Source

pub fn follow_event_algorithm_inhibit(&mut self, oid: &ObjectIdentifier) -> bool

Read the property the object oid’s Event_Algorithm_Inhibit_Ref names and hand the value over as its Event_Algorithm_Inhibit; returns whether that changed. An object with no reference, one that doesn’t report intrinsically, or one this database doesn’t hold is left alone.

Source§

impl ObjectDatabase

Source

pub fn log_event_notification( &mut self, notification: &EventNotificationRequest, )

Record an event notification this device generated in each of its Event Log objects, as a notification record stamped with the Device clock’s local date and time.

The server calls this for each notification it builds once the recipient lookup has read the Notification Class. Each log’s own lifecycle decides what it keeps, as for a record the application adds through add_record: a disabled log ignores it, and a full log drops its oldest record or, with Stop_When_Full, stops and records that instead.

Two kinds go in no log, so that a report can’t prompt the next without end, through the log it watches or crosswise through another log watched in turn. One is a BUFFER_READY report on an Event Log’s buffer (see reports_an_event_log_buffer), as for received notifications. The other is any report from an Event Enrollment of this device monitoring a property of an Event Log here: whatever its algorithm, it watches something each record changes, such as Total_Record_Count, so it isn’t only BUFFER_READY that would answer its own record. A Trend Log’s or Trend Log Multiple’s report is logged like any other notification: those logs take no notifications, so it can’t count toward the next.

Without a valid Device clock no record can carry its timestamp, so nothing is logged. A log that refuses the record keeps its state; the refusal goes to the trace log and the other logs still take it. The notification is stored as given, Process Identifier included: Clause 12.27 leaves that parameter of a locally generated record to the device.

Source

pub fn collects_received_event_notifications(&self) -> bool

Whether any Event Log has opted in to the notifications the device receives. The server asks before it decodes one.

Source

pub fn takes_received_event_notification( &self, notification: &EventNotificationRequest, ) -> bool

Whether log_received_event_notification would record notification, received from another device, anywhere: some Event Log has opted in, and the notification is one a log may take. The server asks before it spends any of a source’s allowance.

Source

pub fn log_received_event_notification( &mut self, notification: &EventNotificationRequest, )

Record an event notification this device received, as it decoded, in each Event Log that has opted in with EventLogObject::set_log_received_notifications, stamped with the Device clock’s local date and time. Each log’s lifecycle decides what it keeps, as for the device’s own notifications, and each record counts toward the log’s Notification_Threshold.

Two kinds are kept out. A BUFFER_READY report on an Event Log’s buffer in any device isn’t logged (see reports_an_event_log_buffer), whatever object reports it: another vendor’s Event Enrollment running the algorithm on its Event Log names the log only there. Logging it could prompt a report here that, logged there in turn, prompts the next. Any other notification about an Event Log is logged. And one whose Initiating Device Identifier names this device isn’t logged a second time: the device logged it when it built it.

This records whatever it is given. The server calls it only for a notification that decoded in full, after holding each source to a few records a second, so a flood of them can’t push the device’s own records out of a log faster than that bound.

Source§

impl ObjectDatabase

Source

pub fn check_input_reference(&mut self, oid: &ObjectIdentifier) -> bool

Judge the Input_Reference of the Pulse Converter oid against this database and hand it the verdict; returns whether its Reliability changed, so the caller owes COV for it. Any other object, or one this database doesn’t hold, is left alone.

The bundled server calls this as a write of Input_Reference commits, over WriteProperty, WritePropertyMultiple or the local write path; adding and removing objects calls it for the converters concerned.

Source

pub fn count_pulse_inputs(&mut self) -> Vec<ObjectIdentifier>

Judge each Pulse Converter’s Input_Reference again and hand over a reading of the property it names, so the converter counts its increase into Count (Clause 12.23.14); returns the converters whose Reliability or Count changed, for COV. A converter with nothing to read gets None, which drops its baseline.

The bundled server runs this on every wake of its monotonic operation task, so at least once a second. Reads go through BACnetObject::read_property, so they must stay bounded, as for Averaging sampling.

Source§

impl ObjectDatabase

Source

pub fn selected_device(&self) -> Option<ObjectIdentifier>

The Device object this database represents, for wildcard reads, discovery, notifications and reference resolution.

Clause 12.11 expects one Device per BACnet device. When a database holds several, the lowest instance wins: a deterministic local policy, independent of hash or insertion order. A concrete instance always precedes the wildcard instance, which is the highest; a database whose only Device is the wildcard selects it. No Device yields None.

Source

pub fn local_device(&self) -> LocalDevice

The Device a reference must name to stay inside this device: the selected Device when its instance is concrete.

Source§

impl ObjectDatabase

Source

pub fn poll_trend_logs(&mut self) -> Duration

Poll every due local Trend Log or Trend Log Multiple synchronously and return the next wait.

Each acquisition reads every reference of the log: a Trend Log’s one, or each Log_DeviceObjectProperty element of a Trend Log Multiple, whose record then carries one value per element in array order (#1203). Only this database is read. A reference whose Device member names another device (see ObjectDatabase::local_device) is never read here, even when a same-numbered local object exists: its value is a failure, PROPERTY / OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED. A Trend Log Multiple element naming instance 4194303 is empty and fails with PROPERTY / NO_PROPERTY_SPECIFIED. A reference with an array index reads that element (#1205). A local read that fails yields the error a ReadProperty of it would report: OBJECT / UNKNOWN_OBJECT for a missing object, PROPERTY / PROPERTY_IS_NOT_AN_ARRAY for an index on a property that isn’t an array, or the read’s own error, such as INVALID_ARRAY_INDEX for an index past the end (#1183).

Only POLLED logs with a nonzero Log_Interval and at least one reference are polled. A TRIGGERED log whose Trigger reads TRUE makes one acquisition, and the object clears Trigger once it accepts the record (Clauses 12.25.29 and 12.30.16). A Trend Log Multiple without members records no values for it, and a Trend Log without a reference records PROPERTY / NO_PROPERTY_SPECIFIED, so Trigger never stays TRUE. With Align_Intervals TRUE and a Log_Interval that divides a day, a POLLED log acquires when the Device clock’s time of day is Interval_Offset (modulo the interval) past a multiple of the interval (Clauses 12.25.27-28 and 12.30.14-15); the first acquisition waits for such a boundary. Such a log is judged against the Device clock on every pass, so a clock change moves its plan with it (see the alignment module).

Every pass also lets each Trend Log, Trend Log Multiple and Event Log look at its Start_Time / Stop_Time window, with or without a monotonic clock, so one that opens or closes is recorded within a pass even when no record arrives. An Event Log takes its records from the application and the device’s notifications, never from this poller, but this pass is the one regular visit every log gets.

The caller must hold exclusive database access for this whole call. The bound monotonic clock drives scheduling; the shared Device clock provides each actual acquisition timestamp. Without either clock an attempt cannot succeed. A successful call to an object’s insertion hook, including its accepted disabled/count-only outcomes, starts the configured interval.

Log_Interval is in hundredths. Deadlines follow actual completion, without catch-up bursts. Invalid timestamps and insertion errors preserve the last success and retry after 100 ms. The returned wait is at most 100 ms so a timer-driven caller also reconciles configuration changes and idle logs. Slow synchronous work can make another object due; a minimum 1 ms yield avoids a zero-delay loop. These bounds are local policy, not real-time guarantees. Clock, object reads and insertion hooks must remain bounded.

Source§

impl ObjectDatabase

Source

pub fn new() -> Self

Create an empty database.

Source

pub fn add(&mut self, object: Box<dyn BACnetObject>) -> Result<(), Error>

Add an object to the database.

Returns PROPERTY / DUPLICATE_NAME if another object already has the same object_name(), as check_name_available does. Replacing an object with the same OID is allowed unless an installed Audit runtime protects its membership. Protection is checked before any binding.

Once the object is in, every Pulse Converter whose Input_Reference names it, or the object itself when it is one, has the reference judged again (ObjectDatabase::check_input_reference); one whose Reliability changes is queued for the server’s COV fanout. A Schedule holding a refused reference to it is queued for the server to retry that reference (#1440).

Source

pub fn find_by_name(&self, name: &str) -> Option<&dyn BACnetObject>

Look up an object by its name. O(1) via the name index.

Source

pub fn check_name_available( &self, oid: &ObjectIdentifier, new_name: &str, ) -> Result<(), Error>

Check whether new_name is available for object oid.

Returns Ok(()) if the name is unused or already belongs to oid, and PROPERTY / DUPLICATE_NAME if another object owns it. Clause 18.3 lists that code under the PROPERTY class, and the WriteProperty and WritePropertyMultiple error tables (15.9.1.3.1, 15.10.1.3.1) give the same pair; CreateObject’s table (15.3.1.3.1) has no row for a name in use, so its initial values answer as WriteProperty does.

Source

pub fn update_name_index(&mut self, oid: &ObjectIdentifier)

Update the name index after a successful Object_Name write.

Call this after write_property(OBJECT_NAME, …) succeeds.

Source

pub fn get(&self, oid: &ObjectIdentifier) -> Option<&dyn BACnetObject>

Get a shared reference to an object by identifier.

Source

pub fn get_mut( &mut self, oid: &ObjectIdentifier, ) -> Option<&mut (dyn BACnetObject + '_)>

Get a mutable reference to an object by identifier.

Source

pub fn with_object_adapter<R, F>( &mut self, oid: &ObjectIdentifier, adapt: F, ) -> Result<Option<R>, Error>
where F: for<'slot> FnOnce(&'slot mut Box<dyn BACnetObject>) -> R,

Install an identity-preserving adapter through scoped structural access.

The callback must preserve the object’s identifier, name and type. It is responsible for allocating/validating before changing the slot. This hook neither rebinds clocks nor updates indexes, and does not provide rollback. Poll ownership is retired before invocation, even if the callback returns an error or unwinds after modifying the slot. Slot borrows cannot escape. An installed Audit runtime can reject protected members before the callback.

ⓘ
use bacnet_objects::{database::ObjectDatabase, traits::BACnetObject};
use bacnet_types::primitives::ObjectIdentifier;
fn escape<'a>(db: &'a mut ObjectDatabase, oid: &ObjectIdentifier)
    -> Result<Option<&'a mut Box<dyn BACnetObject>>, bacnet_types::error::Error> {
    db.with_object_adapter(oid, |slot| slot)
}
Source

pub fn enrollment_eval_state_invalidated(&self, oid: &ObjectIdentifier) -> bool

Whether an Event Enrollment object’s private evaluator state requires a successful reset before reuse.

Source

pub fn set_enrollment_eval_state_invalidated( &mut self, oid: ObjectIdentifier, invalidated: bool, )

Mark or clear the reset requirement for Event Enrollment private state.

Source

pub fn enrollment_eval_source( &self, oid: &ObjectIdentifier, ) -> Option<EventEnrollmentMonitoredSource>

Return database-owned monitored-source state for a custom Event Enrollment object.

Source

pub fn set_enrollment_eval_source( &mut self, oid: ObjectIdentifier, source: Option<EventEnrollmentMonitoredSource>, )

Store database-owned monitored-source state for a custom Event Enrollment object.

Source

pub fn remove( &mut self, oid: &ObjectIdentifier, ) -> Result<Option<Box<dyn BACnetObject>>, Error>

Remove an object by identifier. Installed Audit membership protection returns an error before removing the object or changing indexes. Pulse Converters whose Input_Reference names a removed object have the reference judged again, as add does.

Source

pub fn list_objects(&self) -> Vec<ObjectIdentifier>

List all object identifiers in the database.

Source

pub fn find_by_type(&self, object_type: ObjectType) -> Vec<ObjectIdentifier>

Find all objects of a given type.

Returns a Vec of ObjectIdentifiers whose object type matches object_type. Useful for WhoHas, object enumeration, and similar queries.

Source

pub fn iter_objects( &self, ) -> impl Iterator<Item = (ObjectIdentifier, &dyn BACnetObject)>

Iterate over all (ObjectIdentifier, &dyn BACnetObject) pairs.

Avoids the double-lookup pattern of list_objects() followed by get().

Source

pub fn set_clock_reader(&mut self, clock: Option<Arc<dyn ClockReader>>)

Bind one clock reader to the database and every object it contains.

Devices added after this call receive the same reader in add.

Source

pub fn clock_frame(&self) -> Option<ClockFrame>

Read one coherent sample from the database’s shared clock.

Source

pub fn for_each_object_mut<F>(&mut self, f: F)
where F: FnMut(ObjectIdentifier, &mut dyn BACnetObject),

Visit every (ObjectIdentifier, &mut dyn BACnetObject) pair.

Mutable counterpart to iter_objects, used by the intrinsic-reporting tick task to advance pending transitions. A callback is used (rather than a returned impl Iterator) so the &mut dyn BACnetObject borrow is tied to this call’s lifetime.

Source

pub fn len(&self) -> usize

Number of objects in the database.

Source

pub fn is_empty(&self) -> bool

Whether the database is empty.

Source§

impl ObjectDatabase

Source

pub fn lighting_color( &self, lighting_output: &ObjectIdentifier, ) -> Option<LightingColor>

The colour the lighting output lighting_output shows now (#1527): the Tracking_Value of the colour object its Color_Reference names, or its Override_Color_Reference while Color_Override is TRUE, so a colour fade in progress shows as it runs.

None when the object has no Color_Reference, the reference in use names no object (instance 4194303), or this database holds no Color or Color Temperature object by that identifier. It reads the properties, so it follows any object that serves them.

Trait Implementations§

Source§

impl Default for ObjectDatabase

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more