Skip to main content

bacnet_objects/access_control/
door.rs

1use super::*;
2
3// AccessDoorObject (type 30)
4// ---------------------------------------------------------------------------
5
6/// BACnet Access Door object (type 30).
7///
8/// Represents a physical door or barrier in an access control system.
9/// Present value indicates the door command status (DoorStatus enumeration).
10pub struct AccessDoorObject {
11    oid: ObjectIdentifier,
12    name: String,
13    description: String,
14    present_value: u32,    // DoorStatus: 0=closed, 1=opened, 2=unknown
15    door_status: u32,      // DoorStatus enumeration
16    lock_status: u32,      // LockStatus enumeration
17    secured_status: u32,   // DoorSecuredStatus enumeration
18    door_alarm_state: u32, // DoorAlarmState enumeration
19    door_members: Vec<ObjectIdentifier>,
20    status_flags: StatusFlags,
21    /// Event_State: 0 = NORMAL.
22    event_state: u32,
23    out_of_service: bool,
24    reliability: u32,
25    /// 16-level priority array for commandable Present_Value.
26    priority_array: [Option<u32>; 16],
27    relinquish_default: u32,
28}
29
30impl AccessDoorObject {
31    /// Create a new Access Door object.
32    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
33        let oid = ObjectIdentifier::new(ObjectType::ACCESS_DOOR, instance)?;
34        Ok(Self {
35            oid,
36            name: name.into(),
37            description: String::new(),
38            present_value: 0, // closed
39            door_status: 0,   // closed
40            lock_status: 0,
41            secured_status: 0,
42            door_alarm_state: 0,
43            door_members: Vec::new(),
44            status_flags: StatusFlags::empty(),
45            event_state: 0, // NORMAL
46            out_of_service: false,
47            reliability: 0,
48            priority_array: Default::default(),
49            relinquish_default: 0, // closed
50        })
51    }
52}
53
54impl BACnetObject for AccessDoorObject {
55    fn object_identifier(&self) -> ObjectIdentifier {
56        self.oid
57    }
58
59    fn object_name(&self) -> &str {
60        &self.name
61    }
62
63    fn read_property(
64        &self,
65        property: PropertyIdentifier,
66        array_index: Option<u32>,
67    ) -> Result<PropertyValue, Error> {
68        if let Some(result) = read_common_properties!(self, property, array_index) {
69            return result;
70        }
71        match property {
72            p if p == PropertyIdentifier::OBJECT_TYPE => {
73                Ok(PropertyValue::Enumerated(ObjectType::ACCESS_DOOR.to_raw()))
74            }
75            p if p == PropertyIdentifier::PRESENT_VALUE => {
76                Ok(PropertyValue::Enumerated(self.present_value))
77            }
78            p if p == PropertyIdentifier::DOOR_STATUS => {
79                Ok(PropertyValue::Enumerated(self.door_status))
80            }
81            p if p == PropertyIdentifier::LOCK_STATUS => {
82                Ok(PropertyValue::Enumerated(self.lock_status))
83            }
84            p if p == PropertyIdentifier::SECURED_STATUS => {
85                Ok(PropertyValue::Enumerated(self.secured_status))
86            }
87            p if p == PropertyIdentifier::DOOR_ALARM_STATE => {
88                Ok(PropertyValue::Enumerated(self.door_alarm_state))
89            }
90            p if p == PropertyIdentifier::DOOR_MEMBERS => Ok(PropertyValue::List(
91                self.door_members
92                    .iter()
93                    .map(|oid| PropertyValue::ObjectIdentifier(*oid))
94                    .collect(),
95            )),
96            p if p == PropertyIdentifier::EVENT_STATE => {
97                Ok(PropertyValue::Enumerated(self.event_state))
98            }
99            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
100                common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
101            }
102            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
103                Ok(PropertyValue::Enumerated(self.relinquish_default))
104            }
105            _ => Err(common::unknown_property_error()),
106        }
107    }
108
109    fn write_property(
110        &mut self,
111        property: PropertyIdentifier,
112        _array_index: Option<u32>,
113        value: PropertyValue,
114        priority: Option<u8>,
115    ) -> Result<(), Error> {
116        if let Some(result) =
117            common::write_out_of_service(&mut self.out_of_service, property, &value)
118        {
119            return result;
120        }
121        if let Some(result) = common::write_description(&mut self.description, property, &value) {
122            return result;
123        }
124        match property {
125            p if p == PropertyIdentifier::PRESENT_VALUE => {
126                let slot = priority.unwrap_or(16).clamp(1, 16) as usize - 1;
127                if let PropertyValue::Null = value {
128                    // Relinquish command at this priority
129                    self.priority_array[slot] = None;
130                } else if let PropertyValue::Enumerated(v) = value {
131                    self.priority_array[slot] = Some(v);
132                } else if self.out_of_service {
133                    // When OOS, accept direct writes without priority
134                    if let PropertyValue::Enumerated(v) = value {
135                        self.present_value = v;
136                        return Ok(());
137                    }
138                    return Err(common::invalid_data_type_error());
139                } else {
140                    return Err(common::invalid_data_type_error());
141                }
142                // Recalculate PV from priority array
143                self.present_value = self
144                    .priority_array
145                    .iter()
146                    .flatten()
147                    .next()
148                    .copied()
149                    .unwrap_or(self.relinquish_default);
150                Ok(())
151            }
152            _ => Err(common::write_access_denied_error()),
153        }
154    }
155
156    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
157        static PROPS: &[PropertyIdentifier] = &[
158            PropertyIdentifier::OBJECT_IDENTIFIER,
159            PropertyIdentifier::OBJECT_NAME,
160            PropertyIdentifier::DESCRIPTION,
161            PropertyIdentifier::OBJECT_TYPE,
162            PropertyIdentifier::PRESENT_VALUE,
163            PropertyIdentifier::DOOR_STATUS,
164            PropertyIdentifier::LOCK_STATUS,
165            PropertyIdentifier::SECURED_STATUS,
166            PropertyIdentifier::DOOR_ALARM_STATE,
167            PropertyIdentifier::DOOR_MEMBERS,
168            PropertyIdentifier::STATUS_FLAGS,
169            PropertyIdentifier::OUT_OF_SERVICE,
170            PropertyIdentifier::RELIABILITY,
171        ];
172        Cow::Borrowed(PROPS)
173    }
174
175    fn supports_cov(&self) -> bool {
176        true
177    }
178}
179
180// ---------------------------------------------------------------------------