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