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 {
24        class: ErrorClass::PROPERTY,
25        code: ErrorCode::WRITE_ACCESS_DENIED,
26    };
27    let owner_device = db.local_device().identifier().ok_or_else(denied)?;
28    let initiating_object = match source {
29        LocalCommandSource::ServerDevice => None,
30        LocalCommandSource::Object(oid) => {
31            if db.get(&oid).is_none() {
32                return Err(denied());
33            }
34            Some(oid)
35        }
36    };
37    let origin = CommandOrigin::Local {
38        owner_device,
39        initiating_object,
40    };
41    origin.validate()?;
42    Ok(origin)
43}
44
45/// On absent/invalid local identity, context-free dispatch preserves unrelated
46/// property writes while first-party tracked commands fail closed in the object.
47pub(crate) fn write_target(
48    object: &mut dyn bacnet_objects::traits::BACnetObject,
49    property: bacnet_types::enums::PropertyIdentifier,
50    index: Option<u32>,
51    value: bacnet_types::primitives::PropertyValue,
52    priority: Option<u8>,
53    origin: Option<&CommandOrigin>,
54) -> Result<(), Error> {
55    match origin {
56        Some(origin) => object.write_property_from(property, index, value, priority, origin),
57        None => object.write_property(property, index, value, priority),
58    }
59}
60
61#[cfg(test)]
62pub(crate) fn test_origin() -> bacnet_objects::command_source::CommandOrigin {
63    bacnet_objects::command_source::CommandOrigin::Local {
64        owner_device: bacnet_types::primitives::ObjectIdentifier::new(
65            bacnet_types::enums::ObjectType::DEVICE,
66            1,
67        )
68        .unwrap(),
69        initiating_object: None,
70    }
71}