bacnet_types/constructed/property_states.rs
1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3
4use crate::error::Error;
5
6const EXTENDED_VALUE_FACTOR: u32 = 100_000;
7
8/// Property-state value for a choice tag greater than 254.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct BACnetExtendedPropertyState {
11 tag: u32,
12 value: u32,
13}
14
15impl BACnetExtendedPropertyState {
16 /// Build the tag-63 representation for a choice tag greater than 254.
17 pub fn new(tag: u32, value: u32) -> Result<Self, Error> {
18 if tag <= 254 {
19 return Err(Error::decoding(
20 0,
21 "extended property-state tag must exceed 254",
22 ));
23 }
24 if value >= EXTENDED_VALUE_FACTOR {
25 return Err(Error::decoding(
26 0,
27 "extended property-state value must be below 100000",
28 ));
29 }
30 tag.checked_mul(EXTENDED_VALUE_FACTOR)
31 .and_then(|base| base.checked_add(value))
32 .ok_or_else(|| Error::decoding(0, "extended property-state value exceeds u32"))?;
33 Ok(Self { tag, value })
34 }
35
36 /// Decode the Unsigned32 carried by context tag 63.
37 pub fn from_encoded(encoded: u32) -> Result<Self, Error> {
38 Self::new(
39 encoded / EXTENDED_VALUE_FACTOR,
40 encoded % EXTENDED_VALUE_FACTOR,
41 )
42 }
43
44 /// Return the choice tag represented by tag 63.
45 pub const fn tag(self) -> u32 {
46 self.tag
47 }
48
49 /// Return the vendor enumeration value.
50 pub const fn value(self) -> u32 {
51 self.value
52 }
53
54 /// Return the Unsigned32 encoded under context tag 63.
55 pub fn encoded(self) -> u32 {
56 self.tag * EXTENDED_VALUE_FACTOR + self.value
57 }
58}
59
60/// Vendor-defined property state using a context tag from 64 through 254.
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct BACnetProprietaryPropertyState {
63 tag: u8,
64 data: Vec<u8>,
65 constructed: bool,
66}
67
68impl BACnetProprietaryPropertyState {
69 fn new(tag: u8, data: Vec<u8>, constructed: bool) -> Result<Self, Error> {
70 if !(64..=254).contains(&tag) {
71 return Err(Error::decoding(
72 0,
73 "proprietary property-state tag must be in 64..=254",
74 ));
75 }
76 Ok(Self {
77 tag,
78 data,
79 constructed,
80 })
81 }
82
83 /// Preserve a primitive vendor-defined alternative.
84 pub fn primitive(tag: u8, data: Vec<u8>) -> Result<Self, Error> {
85 Self::new(tag, data, false)
86 }
87
88 /// Preserve the body of a constructed vendor-defined alternative.
89 ///
90 /// The wire encoder rejects bodies that are not BACnet TLV sequences.
91 pub fn constructed(tag: u8, data: Vec<u8>) -> Result<Self, Error> {
92 Self::new(tag, data, true)
93 }
94
95 /// Return the proprietary context tag.
96 pub const fn tag(&self) -> u8 {
97 self.tag
98 }
99
100 /// Return the encoded primitive contents or constructed body.
101 pub fn data(&self) -> &[u8] {
102 &self.data
103 }
104
105 /// Return whether the value uses opening and closing tags.
106 pub const fn is_constructed(&self) -> bool {
107 self.constructed
108 }
109}
110
111/// Discrete or enumerated property value used by event and fault parameters.
112///
113/// Each variant is one alternative of the `BACnetPropertyStates` choice from
114/// Standard 135-2020 Clause 21. The standard alternatives are primitive values
115/// under their own context tag, listed below. Tags 26, 29, 35, 61 and 62 have
116/// no alternative and the decoder rejects them. Decoders use `Other` for
117/// proprietary context tags 64 through 254.
118///
119/// | Tag | Alternative | Type |
120/// |---|---|---|
121/// | `[0]` | `boolean-value` | BOOLEAN |
122/// | `[1]` | `binary-value` | `BACnetBinaryPV` |
123/// | `[2]` | `event-type` | `BACnetEventType` |
124/// | `[3]` | `polarity` | `BACnetPolarity` |
125/// | `[4]` | `program-change` | `BACnetProgramRequest` |
126/// | `[5]` | `program-state` | `BACnetProgramState` |
127/// | `[6]` | `reason-for-halt` | `BACnetProgramError` |
128/// | `[7]` | `reliability` | `BACnetReliability` |
129/// | `[8]` | `state` | `BACnetEventState` |
130/// | `[9]` | `system-status` | `BACnetDeviceStatus` |
131/// | `[10]` | `units` | `BACnetEngineeringUnits` |
132/// | `[11]` | `unsigned-value` | Unsigned |
133/// | `[12]` | `life-safety-mode` | `BACnetLifeSafetyMode` |
134/// | `[13]` | `life-safety-state` | `BACnetLifeSafetyState` |
135/// | `[14]` | `restart-reason` | `BACnetRestartReason` |
136/// | `[15]` | `door-alarm-state` | `BACnetDoorAlarmState` |
137/// | `[16]` | `action` | `BACnetAction` |
138/// | `[17]` | `door-secured-status` | `BACnetDoorSecuredStatus` |
139/// | `[18]` | `door-status` | `BACnetDoorStatus` |
140/// | `[19]` | `door-value` | `BACnetDoorValue` |
141/// | `[20]` | `file-access-method` | `BACnetFileAccessMethod` |
142/// | `[21]` | `lock-status` | `BACnetLockStatus` |
143/// | `[22]` | `life-safety-operation` | `BACnetLifeSafetyOperation` |
144/// | `[23]` | `maintenance` | `BACnetMaintenance` |
145/// | `[24]` | `node-type` | `BACnetNodeType` |
146/// | `[25]` | `notify-type` | `BACnetNotifyType` |
147/// | `[27]` | `shed-state` | `BACnetShedState` |
148/// | `[28]` | `silenced-state` | `BACnetSilencedState` |
149/// | `[30]` | `access-event` | `BACnetAccessEvent` |
150/// | `[31]` | `zone-occupancy-state` | `BACnetAccessZoneOccupancyState` |
151/// | `[32]` | `access-credential-disable-reason` | `BACnetAccessCredentialDisableReason` |
152/// | `[33]` | `access-credential-disable` | `BACnetAccessCredentialDisable` |
153/// | `[34]` | `authentication-status` | `BACnetAuthenticationStatus` |
154/// | `[36]` | `backup-state` | `BACnetBackupState` |
155/// | `[37]` | `write-status` | `BACnetWriteStatus` |
156/// | `[38]` | `lighting-in-progress` | `BACnetLightingInProgress` |
157/// | `[39]` | `lighting-operation` | `BACnetLightingOperation` |
158/// | `[40]` | `lighting-transition` | `BACnetLightingTransition` |
159/// | `[41]` | `integer-value` | INTEGER (signed) |
160/// | `[42]` | `binary-lighting-value` | `BACnetBinaryLightingPV` |
161/// | `[43]` | `timer-state` | `BACnetTimerState` |
162/// | `[44]` | `timer-transition` | `BACnetTimerTransition` |
163/// | `[45]` | `bacnet-ip-mode` | `BACnetIPMode` |
164/// | `[46]` | `network-port-command` | `BACnetNetworkPortCommand` |
165/// | `[47]` | `network-type` | `BACnetNetworkType` |
166/// | `[48]` | `network-number-quality` | `BACnetNetworkNumberQuality` |
167/// | `[49]` | `escalator-operation-direction` | `BACnetEscalatorOperationDirection` |
168/// | `[50]` | `escalator-fault` | `BACnetEscalatorFault` |
169/// | `[51]` | `escalator-mode` | `BACnetEscalatorMode` |
170/// | `[52]` | `lift-car-direction` | `BACnetLiftCarDirection` |
171/// | `[53]` | `lift-car-door-command` | `BACnetLiftCarDoorCommand` |
172/// | `[54]` | `lift-car-drive-status` | `BACnetLiftCarDriveStatus` |
173/// | `[55]` | `lift-car-mode` | `BACnetLiftCarMode` |
174/// | `[56]` | `lift-group-mode` | `BACnetLiftGroupMode` |
175/// | `[57]` | `lift-fault` | `BACnetLiftFault` |
176/// | `[58]` | `protocol-level` | `BACnetProtocolLevel` |
177/// | `[59]` | `audit-level` | `BACnetAuditLevel` |
178/// | `[60]` | `audit-operation` | `BACnetAuditOperation` |
179/// | `[63]` | `extended-value` | Unsigned32, packed as described below |
180/// | `[64]` to `[254]` | vendor-defined | primitive or constructed, kept as raw contents |
181///
182/// `extended-value` carries a choice tag above 254 packed into one Unsigned32
183/// as tag × 100000 + value.
184#[derive(Debug, Clone, PartialEq)]
185pub enum BACnetPropertyStates {
186 /// Tag 0: a BOOLEAN.
187 BooleanValue(bool),
188 /// Tag 1: `BACnetBinaryPV`.
189 BinaryValue(u32),
190 /// Tag 2: `BACnetEventType`.
191 EventType(u32),
192 /// Tag 3: `BACnetPolarity`.
193 Polarity(u32),
194 /// Tag 4: `BACnetProgramRequest`.
195 ProgramChange(u32),
196 /// Tag 5: `BACnetProgramState`.
197 ProgramState(u32),
198 /// Tag 6: `BACnetProgramError`.
199 ReasonForHalt(u32),
200 /// Tag 7: `BACnetReliability`.
201 Reliability(u32),
202 /// Tag 8: `BACnetEventState`.
203 State(u32),
204 /// Tag 9: `BACnetDeviceStatus`.
205 SystemStatus(u32),
206 /// Tag 10: `BACnetEngineeringUnits`.
207 Units(u32),
208 /// Tag 11: an Unsigned.
209 UnsignedValue(u32),
210 /// Tag 12: `BACnetLifeSafetyMode`.
211 LifeSafetyMode(u32),
212 /// Tag 13: `BACnetLifeSafetyState`.
213 LifeSafetyState(u32),
214 /// Tag 14: `BACnetRestartReason`.
215 RestartReason(u32),
216 /// Tag 15: `BACnetDoorAlarmState`.
217 DoorAlarmState(u32),
218 /// Tag 16: `BACnetAction`.
219 Action(u32),
220 /// Tag 17: `BACnetDoorSecuredStatus`.
221 DoorSecuredStatus(u32),
222 /// Tag 18: `BACnetDoorStatus`.
223 DoorStatus(u32),
224 /// Tag 19: `BACnetDoorValue`.
225 DoorValue(u32),
226 /// Tag 20: `BACnetFileAccessMethod`.
227 FileAccessMethod(u32),
228 /// Tag 21: `BACnetLockStatus`.
229 LockStatus(u32),
230 /// Tag 22: `BACnetLifeSafetyOperation`.
231 LifeSafetyOperation(u32),
232 /// Tag 23: `BACnetMaintenance`.
233 Maintenance(u32),
234 /// Tag 24: `BACnetNodeType`.
235 NodeType(u32),
236 /// Tag 25: `BACnetNotifyType`.
237 NotifyType(u32),
238 /// Tag 27: `BACnetShedState`.
239 ShedState(u32),
240 /// Tag 28: `BACnetSilencedState`.
241 SilencedState(u32),
242 /// Tag 30: `BACnetAccessEvent`.
243 AccessEvent(u32),
244 /// Tag 31: `BACnetAccessZoneOccupancyState`.
245 ZoneOccupancyState(u32),
246 /// Tag 32: `BACnetAccessCredentialDisableReason`.
247 AccessCredentialDisableReason(u32),
248 /// Tag 33: `BACnetAccessCredentialDisable`.
249 AccessCredentialDisable(u32),
250 /// Tag 34: `BACnetAuthenticationStatus`.
251 AuthenticationStatus(u32),
252 /// Tag 36: `BACnetBackupState`.
253 BackupState(u32),
254 /// Tag 37: `BACnetWriteStatus`.
255 WriteStatus(u32),
256 /// Tag 38: `BACnetLightingInProgress`.
257 LightingInProgress(u32),
258 /// Tag 39: `BACnetLightingOperation`.
259 LightingOperation(u32),
260 /// Tag 40: `BACnetLightingTransition`.
261 LightingTransition(u32),
262 /// Tag 41: a signed INTEGER.
263 IntegerValue(i32),
264 /// Tag 42: `BACnetBinaryLightingPV`.
265 BinaryLightingValue(u32),
266 /// Tag 43: `BACnetTimerState`.
267 TimerState(u32),
268 /// Tag 44: `BACnetTimerTransition`.
269 TimerTransition(u32),
270 /// Tag 45: `BACnetIPMode`.
271 BacnetIpMode(u32),
272 /// Tag 46: `BACnetNetworkPortCommand`.
273 NetworkPortCommand(u32),
274 /// Tag 47: `BACnetNetworkType`.
275 NetworkType(u32),
276 /// Tag 48: `BACnetNetworkNumberQuality`.
277 NetworkNumberQuality(u32),
278 /// Tag 49: `BACnetEscalatorOperationDirection`.
279 EscalatorOperationDirection(u32),
280 /// Tag 50: `BACnetEscalatorFault`.
281 EscalatorFault(u32),
282 /// Tag 51: `BACnetEscalatorMode`.
283 EscalatorMode(u32),
284 /// Tag 52: `BACnetLiftCarDirection`.
285 LiftCarDirection(u32),
286 /// Tag 53: `BACnetLiftCarDoorCommand`.
287 LiftCarDoorCommand(u32),
288 /// Tag 54: `BACnetLiftCarDriveStatus`.
289 LiftCarDriveStatus(u32),
290 /// Tag 55: `BACnetLiftCarMode`.
291 LiftCarMode(u32),
292 /// Tag 56: `BACnetLiftGroupMode`.
293 LiftGroupMode(u32),
294 /// Tag 57: `BACnetLiftFault`.
295 LiftFault(u32),
296 /// Tag 58: `BACnetProtocolLevel`.
297 ProtocolLevel(u32),
298 /// Tag 59: `BACnetAuditLevel`.
299 AuditLevel(u32),
300 /// Tag 60: `BACnetAuditOperation`.
301 AuditOperation(u32),
302 /// Tag 63: a choice tag above 254 and its value, unpacked from one Unsigned32.
303 ExtendedValue(BACnetExtendedPropertyState),
304 /// Vendor-defined context tag 64 through 254.
305 Other(BACnetProprietaryPropertyState),
306}
307
308impl BACnetPropertyStates {
309 /// Return the semantic scalar used for enum-like event comparisons.
310 pub fn as_u32(&self) -> Option<u32> {
311 use BACnetPropertyStates as S;
312
313 match self {
314 S::BooleanValue(value) => Some(u32::from(*value)),
315 S::BinaryValue(value)
316 | S::EventType(value)
317 | S::Polarity(value)
318 | S::ProgramChange(value)
319 | S::ProgramState(value)
320 | S::ReasonForHalt(value)
321 | S::Reliability(value)
322 | S::State(value)
323 | S::SystemStatus(value)
324 | S::Units(value)
325 | S::UnsignedValue(value)
326 | S::LifeSafetyMode(value)
327 | S::LifeSafetyState(value)
328 | S::RestartReason(value)
329 | S::DoorAlarmState(value)
330 | S::Action(value)
331 | S::DoorSecuredStatus(value)
332 | S::DoorStatus(value)
333 | S::DoorValue(value)
334 | S::FileAccessMethod(value)
335 | S::LockStatus(value)
336 | S::LifeSafetyOperation(value)
337 | S::Maintenance(value)
338 | S::NodeType(value)
339 | S::NotifyType(value)
340 | S::ShedState(value)
341 | S::SilencedState(value)
342 | S::AccessEvent(value)
343 | S::ZoneOccupancyState(value)
344 | S::AccessCredentialDisableReason(value)
345 | S::AccessCredentialDisable(value)
346 | S::AuthenticationStatus(value)
347 | S::BackupState(value)
348 | S::WriteStatus(value)
349 | S::LightingInProgress(value)
350 | S::LightingOperation(value)
351 | S::LightingTransition(value)
352 | S::BinaryLightingValue(value)
353 | S::TimerState(value)
354 | S::TimerTransition(value)
355 | S::BacnetIpMode(value)
356 | S::NetworkPortCommand(value)
357 | S::NetworkType(value)
358 | S::NetworkNumberQuality(value)
359 | S::EscalatorOperationDirection(value)
360 | S::EscalatorFault(value)
361 | S::EscalatorMode(value)
362 | S::LiftCarDirection(value)
363 | S::LiftCarDoorCommand(value)
364 | S::LiftCarDriveStatus(value)
365 | S::LiftCarMode(value)
366 | S::LiftGroupMode(value)
367 | S::LiftFault(value)
368 | S::ProtocolLevel(value)
369 | S::AuditLevel(value)
370 | S::AuditOperation(value) => Some(*value),
371 S::ExtendedValue(value) => Some(value.value()),
372 S::IntegerValue(value) => u32::try_from(*value).ok(),
373 S::Other(value) if !value.is_constructed() && !value.data().is_empty() => {
374 value.data().iter().try_fold(0u32, |acc, byte| {
375 acc.checked_mul(256)?.checked_add(*byte as u32)
376 })
377 }
378 S::Other(_) => None,
379 }
380 }
381}