Skip to main content

bacnet_server/
command_source.rs

1//! Explicit source choice for trusted local property writes.
2use bacnet_objects::{command_source::CommandOrigin, database::ObjectDatabase};
3use bacnet_types::{
4    enums::{ErrorClass, ErrorCode},
5    error::Error,
6    primitives::ObjectIdentifier,
7};
8
9/// Initiator asserted by a trusted local caller. The selected local Device owns
10/// the command in either case; an object changes only its published source.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum LocalCommandSource {
13    /// The selected concrete local Device initiated the command.
14    ServerDevice,
15    /// An existing concrete local object initiated the command.
16    Object(ObjectIdentifier),
17}
18
19pub(crate) fn resolve_local(
20    db: &ObjectDatabase,
21    source: LocalCommandSource,
22) -> Result<CommandOrigin, Error> {
23    let denied = || Error::protocol(ErrorClass::PROPERTY, ErrorCode::WRITE_ACCESS_DENIED);
24    let owner_device = db.local_device().identifier().ok_or_else(denied)?;
25    let initiating_object = match source {
26        LocalCommandSource::ServerDevice => None,
27        LocalCommandSource::Object(oid) => {
28            if db.get(&oid).is_none() {
29                return Err(denied());
30            }
31            Some(oid)
32        }
33    };
34    let origin = CommandOrigin::Local {
35        owner_device,
36        initiating_object,
37    };
38    origin.validate()?;
39    Ok(origin)
40}
41
42/// On absent/invalid local identity, context-free dispatch preserves unrelated
43/// property writes while first-party tracked commands fail closed in the object.
44pub(crate) fn write_target(
45    object: &mut dyn bacnet_objects::traits::BACnetObject,
46    property: bacnet_types::enums::PropertyIdentifier,
47    index: Option<u32>,
48    value: bacnet_types::primitives::PropertyValue,
49    priority: Option<u8>,
50    origin: Option<&CommandOrigin>,
51) -> Result<(), Error> {
52    match origin {
53        Some(origin) => object.write_property_from(property, index, value, priority, origin),
54        None => object.write_property(property, index, value, priority),
55    }
56}
57
58#[cfg(test)]
59pub(crate) fn test_origin() -> bacnet_objects::command_source::CommandOrigin {
60    bacnet_objects::command_source::CommandOrigin::Local {
61        owner_device: bacnet_types::primitives::ObjectIdentifier::new(
62            bacnet_types::enums::ObjectType::DEVICE,
63            1,
64        )
65        .unwrap(),
66        initiating_object: None,
67    }
68}