Skip to main content

bacnet_types/enums/
misc.rs

1// ===========================================================================
2// Miscellaneous enums
3// ===========================================================================
4
5use super::{ConfirmedServiceChoice, UnconfirmedServiceChoice};
6
7bacnet_enum! {
8    /// BACnet load control shed state (Clause 12.28).
9    pub struct ShedState(u32);
10
11    const SHED_INACTIVE = 0;
12    const SHED_REQUEST_PENDING = 1;
13    const SHED_COMPLIANT = 2;
14    const SHED_NON_COMPLIANT = 3;
15}
16
17bacnet_enum! {
18    /// BACnet node type for Structured View (Clause 12.29).
19    pub struct NodeType(u32);
20
21    const UNKNOWN = 0;
22    const SYSTEM = 1;
23    const NETWORK = 2;
24    const DEVICE = 3;
25    const ORGANIZATIONAL = 4;
26    const AREA = 5;
27    const EQUIPMENT = 6;
28    const POINT = 7;
29    const COLLECTION = 8;
30    const PROPERTY = 9;
31    const FUNCTIONAL = 10;
32    const OTHER = 11;
33    const SUBSYSTEM = 12;
34    const BUILDING = 13;
35    const FLOOR = 14;
36    const SECTION = 15;
37    const MODULE = 16;
38    const TREE = 17;
39    const MEMBER = 18;
40    const PROTOCOL = 19;
41    const ROOM = 20;
42    const ZONE = 21;
43}
44
45bacnet_enum! {
46    /// BACnet maintenance type (Clause 21); used by life safety point/zone
47    /// and access door Maintenance_Required.
48    pub struct Maintenance(u32);
49
50    const NONE = 0;
51    const PERIODIC_TEST = 1;
52    const NEED_SERVICE_OPERATIONAL = 2;
53    const NEED_SERVICE_INOPERATIVE = 3;
54}
55
56bacnet_enum! {
57    /// BACnet subordinate/parent relationship for Structured View
58    /// (Clause 21).
59    ///
60    /// Values above `DEFAULT` come in even/odd forward/reverse pairs
61    /// (e.g. `CONTAINS`(2) / `CONTAINED_BY`(3)); proprietary extensions
62    /// follow the same pairing so the opposite of a relationship `n > 1` is
63    /// always `n ^ 1`.
64    pub struct Relationship(u32);
65
66    const UNKNOWN = 0;
67    const DEFAULT = 1;
68    const CONTAINS = 2;
69    const CONTAINED_BY = 3;
70    const USES = 4;
71    const USED_BY = 5;
72    const COMMANDS = 6;
73    const COMMANDED_BY = 7;
74    const ADJUSTS = 8;
75    const ADJUSTED_BY = 9;
76    const INGRESS = 10;
77    const EGRESS = 11;
78    const SUPPLIES_AIR = 12;
79    const RECEIVES_AIR = 13;
80    const SUPPLIES_HOT_AIR = 14;
81    const RECEIVES_HOT_AIR = 15;
82    const SUPPLIES_COOL_AIR = 16;
83    const RECEIVES_COOL_AIR = 17;
84    const SUPPLIES_POWER = 18;
85    const RECEIVES_POWER = 19;
86    const SUPPLIES_GAS = 20;
87    const RECEIVES_GAS = 21;
88    const SUPPLIES_WATER = 22;
89    const RECEIVES_WATER = 23;
90    const SUPPLIES_HOT_WATER = 24;
91    const RECEIVES_HOT_WATER = 25;
92    const SUPPLIES_COOL_WATER = 26;
93    const RECEIVES_COOL_WATER = 27;
94    const SUPPLIES_STEAM = 28;
95    const RECEIVES_STEAM = 29;
96}
97
98bacnet_enum! {
99    /// BACnet acknowledgment filter for GetEnrollmentSummary (Clause 13.11.1.1).
100    pub struct AcknowledgmentFilter(u32);
101
102    const ALL = 0;
103    const ACKED = 1;
104    const NOT_ACKED = 2;
105}
106
107bacnet_enum! {
108    /// Event-state filter for GetEnrollmentSummary (Clause 13.11.1.1).
109    pub struct EnrollmentSummaryEventStateFilter(u32);
110
111    const OFFNORMAL = 0;
112    const FAULT = 1;
113    const NORMAL = 2;
114    const ALL = 3;
115    const ACTIVE = 4;
116}
117
118bacnet_enum! {
119    /// Bit positions within a `BACnetServicesSupported` bit string (Clause 21).
120    pub struct ServiceSupported(u8);
121
122    const ACKNOWLEDGE_ALARM = 0;
123    const CONFIRMED_COV_NOTIFICATION = 1;
124    const CONFIRMED_EVENT_NOTIFICATION = 2;
125    const GET_ALARM_SUMMARY = 3;
126    const GET_ENROLLMENT_SUMMARY = 4;
127    const SUBSCRIBE_COV = 5;
128    const ATOMIC_READ_FILE = 6;
129    const ATOMIC_WRITE_FILE = 7;
130    const ADD_LIST_ELEMENT = 8;
131    const REMOVE_LIST_ELEMENT = 9;
132    const CREATE_OBJECT = 10;
133    const DELETE_OBJECT = 11;
134    const READ_PROPERTY = 12;
135    // 13: readPropertyConditional (removed)
136    const READ_PROPERTY_MULTIPLE = 14;
137    const WRITE_PROPERTY = 15;
138    const WRITE_PROPERTY_MULTIPLE = 16;
139    const DEVICE_COMMUNICATION_CONTROL = 17;
140    const CONFIRMED_PRIVATE_TRANSFER = 18;
141    const CONFIRMED_TEXT_MESSAGE = 19;
142    const REINITIALIZE_DEVICE = 20;
143    const VT_OPEN = 21;
144    const VT_CLOSE = 22;
145    const VT_DATA = 23;
146    // 24: authenticate (removed), 25: requestKey (removed)
147    const I_AM = 26;
148    const I_HAVE = 27;
149    const UNCONFIRMED_COV_NOTIFICATION = 28;
150    const UNCONFIRMED_EVENT_NOTIFICATION = 29;
151    const UNCONFIRMED_PRIVATE_TRANSFER = 30;
152    const UNCONFIRMED_TEXT_MESSAGE = 31;
153    const TIME_SYNCHRONIZATION = 32;
154    const WHO_HAS = 33;
155    const WHO_IS = 34;
156    const READ_RANGE = 35;
157    const UTC_TIME_SYNCHRONIZATION = 36;
158    const LIFE_SAFETY_OPERATION = 37;
159    const SUBSCRIBE_COV_PROPERTY = 38;
160    const GET_EVENT_INFORMATION = 39;
161    const WRITE_GROUP = 40;
162    const SUBSCRIBE_COV_PROPERTY_MULTIPLE = 41;
163    const CONFIRMED_COV_NOTIFICATION_MULTIPLE = 42;
164    const UNCONFIRMED_COV_NOTIFICATION_MULTIPLE = 43;
165    const CONFIRMED_AUDIT_NOTIFICATION = 44;
166    const AUDIT_LOG_QUERY = 45;
167    const UNCONFIRMED_AUDIT_NOTIFICATION = 46;
168    const WHO_AM_I = 47;
169    const YOU_ARE = 48;
170}
171
172impl ServiceSupported {
173    /// The `BACnetServicesSupported` bit for a confirmed service choice.
174    ///
175    /// The choice and bit numberings diverge for every service added after
176    /// the original standard (read-range is choice 26 but bit 35,
177    /// get-event-information choice 29 but bit 39, …) — always map through
178    /// here, never by reusing the choice number as a bit. `None` for choices
179    /// with no defined bit (reserved/unassigned values).
180    pub fn from_confirmed_choice(choice: ConfirmedServiceChoice) -> Option<Self> {
181        Some(match choice.to_raw() {
182            0 => Self::ACKNOWLEDGE_ALARM,
183            1 => Self::CONFIRMED_COV_NOTIFICATION,
184            2 => Self::CONFIRMED_EVENT_NOTIFICATION,
185            3 => Self::GET_ALARM_SUMMARY,
186            4 => Self::GET_ENROLLMENT_SUMMARY,
187            5 => Self::SUBSCRIBE_COV,
188            6 => Self::ATOMIC_READ_FILE,
189            7 => Self::ATOMIC_WRITE_FILE,
190            8 => Self::ADD_LIST_ELEMENT,
191            9 => Self::REMOVE_LIST_ELEMENT,
192            10 => Self::CREATE_OBJECT,
193            11 => Self::DELETE_OBJECT,
194            12 => Self::READ_PROPERTY,
195            14 => Self::READ_PROPERTY_MULTIPLE,
196            15 => Self::WRITE_PROPERTY,
197            16 => Self::WRITE_PROPERTY_MULTIPLE,
198            17 => Self::DEVICE_COMMUNICATION_CONTROL,
199            18 => Self::CONFIRMED_PRIVATE_TRANSFER,
200            19 => Self::CONFIRMED_TEXT_MESSAGE,
201            20 => Self::REINITIALIZE_DEVICE,
202            21 => Self::VT_OPEN,
203            22 => Self::VT_CLOSE,
204            23 => Self::VT_DATA,
205            26 => Self::READ_RANGE,
206            27 => Self::LIFE_SAFETY_OPERATION,
207            28 => Self::SUBSCRIBE_COV_PROPERTY,
208            29 => Self::GET_EVENT_INFORMATION,
209            30 => Self::SUBSCRIBE_COV_PROPERTY_MULTIPLE,
210            31 => Self::CONFIRMED_COV_NOTIFICATION_MULTIPLE,
211            32 => Self::CONFIRMED_AUDIT_NOTIFICATION,
212            33 => Self::AUDIT_LOG_QUERY,
213            _ => return None,
214        })
215    }
216
217    /// The `BACnetServicesSupported` bit for an unconfirmed service choice.
218    ///
219    /// Every unconfirmed choice diverges from its bit (who-is is choice 8 but
220    /// bit 34). `None` for choices with no defined bit.
221    pub fn from_unconfirmed_choice(choice: UnconfirmedServiceChoice) -> Option<Self> {
222        Some(match choice.to_raw() {
223            0 => Self::I_AM,
224            1 => Self::I_HAVE,
225            2 => Self::UNCONFIRMED_COV_NOTIFICATION,
226            3 => Self::UNCONFIRMED_EVENT_NOTIFICATION,
227            4 => Self::UNCONFIRMED_PRIVATE_TRANSFER,
228            5 => Self::UNCONFIRMED_TEXT_MESSAGE,
229            6 => Self::TIME_SYNCHRONIZATION,
230            7 => Self::WHO_HAS,
231            8 => Self::WHO_IS,
232            9 => Self::UTC_TIME_SYNCHRONIZATION,
233            10 => Self::WRITE_GROUP,
234            11 => Self::UNCONFIRMED_COV_NOTIFICATION_MULTIPLE,
235            12 => Self::UNCONFIRMED_AUDIT_NOTIFICATION,
236            13 => Self::WHO_AM_I,
237            14 => Self::YOU_ARE,
238            _ => return None,
239        })
240    }
241}
242
243bacnet_enum! {
244    /// BACnet message priority for TextMessage services (Clause 16.5).
245    pub struct MessagePriority(u32);
246
247    const NORMAL = 0;
248    const URGENT = 1;
249}
250
251bacnet_enum! {
252    /// BACnet virtual terminal class, the VT-Open class parameter (Clause 17.2).
253    pub struct VTClass(u32);
254
255    const DEFAULT_TERMINAL = 0;
256    const ANSI_X3_64 = 1;
257    const DEC_VT52 = 2;
258    const DEC_VT100 = 3;
259    const DEC_VT220 = 4;
260    const HP_700_94 = 5;
261    const IBM_3130 = 6;
262}
263
264#[cfg(test)]
265mod tests {
266    use super::*;
267
268    #[test]
269    fn service_choice_to_bit_divergent_pairs() {
270        // The pairs where choice != bit — the exact confusions that produced
271        // the old hardcoded Protocol_Services_Supported constant (#192).
272        let confirmed = [
273            (26, ServiceSupported::READ_RANGE, 35),
274            (27, ServiceSupported::LIFE_SAFETY_OPERATION, 37),
275            (28, ServiceSupported::SUBSCRIBE_COV_PROPERTY, 38),
276            (29, ServiceSupported::GET_EVENT_INFORMATION, 39),
277            (30, ServiceSupported::SUBSCRIBE_COV_PROPERTY_MULTIPLE, 41),
278            (
279                31,
280                ServiceSupported::CONFIRMED_COV_NOTIFICATION_MULTIPLE,
281                42,
282            ),
283            (32, ServiceSupported::CONFIRMED_AUDIT_NOTIFICATION, 44),
284            (33, ServiceSupported::AUDIT_LOG_QUERY, 45),
285        ];
286        for (choice, expected, bit) in confirmed {
287            let got =
288                ServiceSupported::from_confirmed_choice(ConfirmedServiceChoice::from_raw(choice))
289                    .unwrap();
290            assert_eq!(got, expected);
291            assert_eq!(got.to_raw(), bit);
292        }
293
294        let unconfirmed = [
295            (0, ServiceSupported::I_AM, 26),
296            (5, ServiceSupported::UNCONFIRMED_TEXT_MESSAGE, 31),
297            (6, ServiceSupported::TIME_SYNCHRONIZATION, 32),
298            (7, ServiceSupported::WHO_HAS, 33),
299            (8, ServiceSupported::WHO_IS, 34),
300            (9, ServiceSupported::UTC_TIME_SYNCHRONIZATION, 36),
301            (10, ServiceSupported::WRITE_GROUP, 40),
302            (14, ServiceSupported::YOU_ARE, 48),
303        ];
304        for (choice, expected, bit) in unconfirmed {
305            let got = ServiceSupported::from_unconfirmed_choice(
306                UnconfirmedServiceChoice::from_raw(choice),
307            )
308            .unwrap();
309            assert_eq!(got, expected);
310            assert_eq!(got.to_raw(), bit);
311        }
312    }
313
314    #[test]
315    fn service_choice_identity_range_and_reserved() {
316        // Choices 0..=23 (minus reserved 13) map bit == choice.
317        for c in (0..=23u8).filter(|c| *c != 13) {
318            let bit = ServiceSupported::from_confirmed_choice(ConfirmedServiceChoice::from_raw(c))
319                .unwrap()
320                .to_raw();
321            assert_eq!(bit, c, "choice {c} should be the identity mapping");
322        }
323        // Reserved/unassigned choices have no bit.
324        assert!(
325            ServiceSupported::from_confirmed_choice(ConfirmedServiceChoice::from_raw(13)).is_none()
326        );
327        assert!(
328            ServiceSupported::from_confirmed_choice(ConfirmedServiceChoice::from_raw(34)).is_none()
329        );
330        assert!(
331            ServiceSupported::from_unconfirmed_choice(UnconfirmedServiceChoice::from_raw(15))
332                .is_none()
333        );
334    }
335}