Skip to main content

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}