bacnet_objects/
command_source.rs1use bacnet_types::constructed::{BACnetAddress, BACnetDeviceObjectReference, BACnetValueSource};
7use bacnet_types::enums::ObjectType;
8use bacnet_types::error::Error;
9use bacnet_types::primitives::ObjectIdentifier;
10
11mod tracking;
12pub(crate) use tracking::write_sourced_priority;
13pub(crate) use tracking::ValueSourceTracking;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum CommandDeviceBinding {
18 Unknown,
20 Unique(ObjectIdentifier),
22 Ambiguous,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum CommandOrigin {
29 Remote {
31 actual_address: BACnetAddress,
33 binding: CommandDeviceBinding,
35 },
36 Local {
38 owner_device: ObjectIdentifier,
40 initiating_object: Option<ObjectIdentifier>,
42 },
43}
44
45impl CommandOrigin {
46 pub fn validate(&self) -> Result<(), Error> {
51 let concrete =
52 |oid: ObjectIdentifier| oid.instance_number() != ObjectIdentifier::WILDCARD_INSTANCE;
53 let device =
54 |oid: ObjectIdentifier| concrete(oid) && oid.object_type() == ObjectType::DEVICE;
55 let valid = match self {
56 Self::Local {
57 owner_device,
58 initiating_object,
59 } => device(*owner_device) && initiating_object.is_none_or(concrete),
60 Self::Remote {
61 actual_address,
62 binding,
63 } => {
64 actual_address.network_number != u16::MAX
65 && !actual_address.mac_address.is_empty()
66 && actual_address.mac_address.len() <= BACnetAddress::MAX_MAC_LEN
67 && match binding {
68 CommandDeviceBinding::Unique(oid) => device(*oid),
69 _ => true,
70 }
71 }
72 };
73 if valid {
74 Ok(())
75 } else {
76 Err(crate::common::write_access_denied_error())
77 }
78 }
79
80 pub(crate) fn published_source(&self) -> BACnetValueSource {
81 match self {
82 Self::Local {
83 owner_device,
84 initiating_object,
85 } => BACnetValueSource::Object(BACnetDeviceObjectReference {
86 device_identifier: None,
87 object_identifier: initiating_object.unwrap_or(*owner_device),
88 }),
89 Self::Remote {
90 binding: CommandDeviceBinding::Unique(device),
91 ..
92 } => BACnetValueSource::Object(BACnetDeviceObjectReference {
93 device_identifier: None,
94 object_identifier: *device,
95 }),
96 Self::Remote { actual_address, .. } => {
97 BACnetValueSource::Address(actual_address.clone())
98 }
99 }
100 }
101
102 pub(crate) fn permits_correction_by(&self, incoming: &Self) -> bool {
103 match (self, incoming) {
104 (
105 Self::Local {
106 owner_device: old, ..
107 },
108 Self::Local {
109 owner_device: new, ..
110 },
111 ) => old == new,
112 (
113 Self::Remote {
114 actual_address: old_address,
115 binding: old,
116 },
117 Self::Remote {
118 actual_address: new_address,
119 binding: new,
120 },
121 ) => {
122 use CommandDeviceBinding::*;
123 match (old, new) {
124 (Ambiguous, _) | (_, Ambiguous) => false,
125 (Unique(a), Unique(b)) => a == b,
126 _ => old_address == new_address,
127 }
128 }
129 _ => false,
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests;
136
137#[cfg(test)]
138pub(crate) fn test_origin() -> crate::command_source::CommandOrigin {
139 crate::command_source::CommandOrigin::Local {
140 owner_device: bacnet_types::primitives::ObjectIdentifier::new(
141 bacnet_types::enums::ObjectType::DEVICE,
142 1,
143 )
144 .unwrap(),
145 initiating_object: None,
146 }
147}