Skip to main content

bacnet_objects/access_control/
credential_data_input.rs

1use super::*;
2
3// CredentialDataInputObject (type 37)
4// ---------------------------------------------------------------------------
5
6/// BACnet Credential Data Input object (type 37).
7///
8/// Represents a credential reader device (card reader, biometric scanner, etc.).
9/// Its Present_Value is a BACnetAuthenticationFactor *structure* (Clause
10/// 12.36), not an enumerated status: Authentication_Status — the
11/// BACnetAuthenticationStatus property whose values run 0=not-ready,
12/// 1=ready, … — belongs to the Access Point object (Clause 12.31,
13/// Table 12-36).
14pub struct CredentialDataInputObject {
15    oid: ObjectIdentifier,
16    name: String,
17    description: String,
18    present_value: u32, // BACnetAuthenticationFactor structure (12.36), stored raw
19    update_time: ([u8; 4], [u8; 4]), // (Date, Time) as raw bytes
20    supported_formats: Vec<u64>,
21    supported_format_classes: Vec<u64>,
22    status_flags: StatusFlags,
23    out_of_service: bool,
24    reliability: u32,
25}
26
27impl CredentialDataInputObject {
28    /// Create a new Credential Data Input object.
29    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
30        let oid = ObjectIdentifier::new(ObjectType::CREDENTIAL_DATA_INPUT, instance)?;
31        Ok(Self {
32            oid,
33            name: name.into(),
34            description: String::new(),
35            present_value: 0, // empty factor placeholder (see field note)
36            update_time: ([0xFF, 0xFF, 0xFF, 0xFF], [0xFF, 0xFF, 0xFF, 0xFF]),
37            supported_formats: Vec::new(),
38            supported_format_classes: Vec::new(),
39            status_flags: StatusFlags::empty(),
40            out_of_service: false,
41            reliability: 0,
42        })
43    }
44}
45
46impl BACnetObject for CredentialDataInputObject {
47    fn object_identifier(&self) -> ObjectIdentifier {
48        self.oid
49    }
50
51    fn object_name(&self) -> &str {
52        &self.name
53    }
54
55    fn read_property(
56        &self,
57        property: PropertyIdentifier,
58        array_index: Option<u32>,
59    ) -> Result<PropertyValue, Error> {
60        if let Some(result) = read_common_properties!(self, property, array_index) {
61            return result;
62        }
63        match property {
64            p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
65                ObjectType::CREDENTIAL_DATA_INPUT.to_raw(),
66            )),
67            p if p == PropertyIdentifier::PRESENT_VALUE => {
68                Ok(PropertyValue::Enumerated(self.present_value))
69            }
70            p if p == PropertyIdentifier::UPDATE_TIME => {
71                let (d, t) = &self.update_time;
72                Ok(PropertyValue::List(vec![
73                    PropertyValue::Date(Date {
74                        year: d[0],
75                        month: d[1],
76                        day: d[2],
77                        day_of_week: d[3],
78                    }),
79                    PropertyValue::Time(Time {
80                        hour: t[0],
81                        minute: t[1],
82                        second: t[2],
83                        hundredths: t[3],
84                    }),
85                ]))
86            }
87            p if p == PropertyIdentifier::SUPPORTED_FORMATS => Ok(PropertyValue::List(
88                self.supported_formats
89                    .iter()
90                    .map(|v| PropertyValue::Unsigned(*v))
91                    .collect(),
92            )),
93            p if p == PropertyIdentifier::SUPPORTED_FORMAT_CLASSES => Ok(PropertyValue::List(
94                self.supported_format_classes
95                    .iter()
96                    .map(|v| PropertyValue::Unsigned(*v))
97                    .collect(),
98            )),
99            _ => Err(common::unknown_property_error()),
100        }
101    }
102
103    fn write_property(
104        &mut self,
105        property: PropertyIdentifier,
106        _array_index: Option<u32>,
107        value: PropertyValue,
108        _priority: Option<u8>,
109    ) -> Result<(), Error> {
110        if let Some(result) =
111            common::write_out_of_service(&mut self.out_of_service, property, &value)
112        {
113            return result;
114        }
115        if let Some(result) = common::write_description(&mut self.description, property, &value) {
116            return result;
117        }
118        // CredentialDataInput is primarily read-only (driven by hardware)
119        Err(common::write_access_denied_error())
120    }
121
122    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
123        static PROPS: &[PropertyIdentifier] = &[
124            PropertyIdentifier::OBJECT_IDENTIFIER,
125            PropertyIdentifier::OBJECT_NAME,
126            PropertyIdentifier::DESCRIPTION,
127            PropertyIdentifier::OBJECT_TYPE,
128            PropertyIdentifier::PRESENT_VALUE,
129            PropertyIdentifier::UPDATE_TIME,
130            PropertyIdentifier::SUPPORTED_FORMATS,
131            PropertyIdentifier::SUPPORTED_FORMAT_CLASSES,
132            PropertyIdentifier::STATUS_FLAGS,
133            PropertyIdentifier::OUT_OF_SERVICE,
134            PropertyIdentifier::RELIABILITY,
135        ];
136        Cow::Borrowed(PROPS)
137    }
138}
139
140// ---------------------------------------------------------------------------
141// Tests
142// ---------------------------------------------------------------------------