libosdp/
events.rs

1//
2// Copyright (c) 2023-2024 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3//
4// SPDX-License-Identifier: Apache-2.0
5
6//! OSDP PDs have to send messages to it's controlling unit - CP to intimate it
7//! about various events that originate there (such as key press, card reads,
8//! etc.,). They do this by creating an "event" and sending it to the CP. This
9//! module is responsible to handling such events though [`OsdpEvent`].
10
11use crate::OsdpError;
12use alloc::vec::Vec;
13use serde::{Deserialize, Serialize};
14
15use super::ConvertEndian;
16
17type Result<T> = core::result::Result<T, OsdpError>;
18
19#[cfg(feature = "defmt-03")]
20use defmt::panic;
21
22/// Various card formats that a PD can support. This is sent to CP when a PD
23/// must report a card read
24#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
26pub enum OsdpCardFormats {
27    /// Card format is not specified
28    #[default]
29    Unspecified,
30
31    /// Wiegand format
32    Wiegand,
33}
34
35impl From<libosdp_sys::osdp_event_cardread_format_e> for OsdpCardFormats {
36    fn from(value: libosdp_sys::osdp_event_cardread_format_e) -> Self {
37        match value {
38            libosdp_sys::osdp_event_cardread_format_e_OSDP_CARD_FMT_RAW_UNSPECIFIED => {
39                OsdpCardFormats::Unspecified
40            }
41            libosdp_sys::osdp_event_cardread_format_e_OSDP_CARD_FMT_RAW_WIEGAND => {
42                OsdpCardFormats::Wiegand
43            }
44            _ => panic!("Unknown osdp card format"),
45        }
46    }
47}
48
49impl From<OsdpCardFormats> for libosdp_sys::osdp_event_cardread_format_e {
50    fn from(val: OsdpCardFormats) -> Self {
51        match val {
52            OsdpCardFormats::Unspecified => {
53                libosdp_sys::osdp_event_cardread_format_e_OSDP_CARD_FMT_RAW_UNSPECIFIED
54            }
55            OsdpCardFormats::Wiegand => {
56                libosdp_sys::osdp_event_cardread_format_e_OSDP_CARD_FMT_RAW_WIEGAND
57            }
58        }
59    }
60}
61
62/// Event that describes card read activity on the PD
63#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
64#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
65pub struct OsdpEventCardRead {
66    /// Reader (another device connected to this PD) which caused this event
67    ///
68    /// 0 - self
69    /// 1 - fist connected reader
70    /// 2 - second connected reader
71    /// ....
72    pub reader_no: i32,
73
74    /// Format of the card that was read
75    pub format: OsdpCardFormats,
76
77    /// The direction of the PD where the card read happened (some PDs have two
78    /// physical card readers to put on either side of a door).
79    ///
80    /// false - Forward
81    /// true - Backward
82    pub direction: bool,
83
84    /// Number of valid data bits in [`OsdpEventCardRead::data`] when the card
85    /// format is not [`OsdpCardFormats::Ascii`]. For [`OsdpCardFormats::Ascii`], this
86    /// field is set to zero.
87    pub nr_bits: usize,
88
89    /// Card data; bytes or bits depending on [`OsdpCardFormats`]
90    pub data: Vec<u8>,
91}
92
93impl OsdpEventCardRead {
94    /// Create an raw data card read event for self and direction set to forward
95    pub fn new_raw(data: Vec<u8>) -> Self {
96        Self {
97            reader_no: 0,
98            format: OsdpCardFormats::Unspecified,
99            direction: false,
100            nr_bits: data.len() * 8,
101            data,
102        }
103    }
104
105    /// Create a Wiegand card read event for self and direction set to forward
106    pub fn new_wiegand(nr_bits: usize, data: Vec<u8>) -> Result<Self> {
107        if nr_bits > data.len() * 8 {
108            return Err(OsdpError::Command);
109        }
110        Ok(Self {
111            reader_no: 0,
112            format: OsdpCardFormats::Wiegand,
113            direction: false,
114            nr_bits,
115            data,
116        })
117    }
118}
119
120impl From<libosdp_sys::osdp_event_cardread> for OsdpEventCardRead {
121    fn from(value: libosdp_sys::osdp_event_cardread) -> Self {
122        let direction = value.direction == 1;
123        let format = value.format.into();
124        let len = value.length as usize;
125        let (nr_bits, nr_bytes) = (len, len.div_ceil(8));
126        let data = value.data[0..nr_bytes].to_vec();
127        OsdpEventCardRead {
128            reader_no: value.reader_no,
129            format,
130            direction,
131            nr_bits,
132            data,
133        }
134    }
135}
136
137impl From<OsdpEventCardRead> for libosdp_sys::osdp_event_cardread {
138    fn from(value: OsdpEventCardRead) -> Self {
139        let mut data = [0; libosdp_sys::OSDP_EVENT_CARDREAD_MAX_DATALEN as usize];
140        let length = value.nr_bits as i32;
141        data[..value.data.len()].copy_from_slice(&value.data[..]);
142        libosdp_sys::osdp_event_cardread {
143            reader_no: value.reader_no,
144            format: value.format.into(),
145            direction: value.direction as i32,
146            length,
147            data,
148        }
149    }
150}
151
152/// Event to describe a key press activity on the PD
153#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
154#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
155pub struct OsdpEventKeyPress {
156    /// Reader (another device connected to this PD) which caused this event
157    ///
158    /// 0 - self
159    /// 1 - fist connected reader
160    /// 2 - second connected reader
161    /// ....
162    pub reader_no: i32,
163
164    /// Key data
165    pub data: Vec<u8>,
166}
167
168impl OsdpEventKeyPress {
169    /// Create key press event for the keys specified in `data`.
170    pub fn new(data: Vec<u8>) -> Self {
171        Self { reader_no: 0, data }
172    }
173}
174
175impl From<libosdp_sys::osdp_event_keypress> for OsdpEventKeyPress {
176    fn from(value: libosdp_sys::osdp_event_keypress) -> Self {
177        let n = value.length as usize;
178        let data = value.data[0..n].to_vec();
179        OsdpEventKeyPress {
180            reader_no: value.reader_no,
181            data,
182        }
183    }
184}
185
186impl From<OsdpEventKeyPress> for libosdp_sys::osdp_event_keypress {
187    fn from(value: OsdpEventKeyPress) -> Self {
188        let mut data = [0; libosdp_sys::OSDP_EVENT_KEYPRESS_MAX_DATALEN as usize];
189        data[..value.data.len()].copy_from_slice(&value.data[..]);
190        libosdp_sys::osdp_event_keypress {
191            reader_no: value.reader_no,
192            length: value.data.len() as i32,
193            data,
194        }
195    }
196}
197
198/// Event to transport a Manufacturer specific command's response.
199#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
200#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
201pub struct OsdpEventMfgReply {
202    /// 3-byte IEEE assigned OUI used as vendor code
203    pub vendor_code: (u8, u8, u8),
204
205    /// Reply data (if any)
206    pub data: Vec<u8>,
207}
208
209impl From<libosdp_sys::osdp_event_mfgrep> for OsdpEventMfgReply {
210    fn from(value: libosdp_sys::osdp_event_mfgrep) -> Self {
211        let n = value.length as usize;
212        let data = value.data[0..n].to_vec();
213        let bytes = value.vendor_code.to_le_bytes();
214        let vendor_code: (u8, u8, u8) = (bytes[0], bytes[1], bytes[2]);
215        OsdpEventMfgReply {
216            vendor_code,
217            data,
218        }
219    }
220}
221
222impl From<OsdpEventMfgReply> for libosdp_sys::osdp_event_mfgrep {
223    fn from(value: OsdpEventMfgReply) -> Self {
224        let mut data = [0; libosdp_sys::OSDP_EVENT_MFGREP_MAX_DATALEN as usize];
225        data[..value.data.len()].copy_from_slice(&value.data[..]);
226        libosdp_sys::osdp_event_mfgrep {
227            vendor_code: value.vendor_code.as_le(),
228            length: value.data.len() as u8,
229            data,
230        }
231    }
232}
233
234/// Status report type
235#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
236#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
237pub enum OsdpStatusReportType {
238    /// Input status report
239    Input,
240    /// Output status report
241    Output,
242    /// Remote status report
243    Remote,
244    /// Local status report
245    Local,
246}
247
248impl From<libosdp_sys::osdp_status_report_type> for OsdpStatusReportType {
249    fn from(value: libosdp_sys::osdp_status_report_type) -> Self {
250        match value {
251            libosdp_sys::osdp_status_report_type_OSDP_STATUS_REPORT_INPUT => {
252                OsdpStatusReportType::Input
253            }
254            libosdp_sys::osdp_status_report_type_OSDP_STATUS_REPORT_OUTPUT => {
255                OsdpStatusReportType::Output
256            }
257            libosdp_sys::osdp_status_report_type_OSDP_STATUS_REPORT_REMOTE => {
258                OsdpStatusReportType::Remote
259            }
260            libosdp_sys::osdp_status_report_type_OSDP_STATUS_REPORT_LOCAL => {
261                OsdpStatusReportType::Local
262            }
263            _ => panic!("Invalid enum entry"),
264        }
265    }
266}
267
268impl From<OsdpStatusReportType> for libosdp_sys::osdp_status_report_type {
269    fn from(value: OsdpStatusReportType) -> Self {
270        match value {
271            OsdpStatusReportType::Input => {
272                libosdp_sys::osdp_status_report_type_OSDP_STATUS_REPORT_INPUT
273            }
274            OsdpStatusReportType::Output => {
275                libosdp_sys::osdp_status_report_type_OSDP_STATUS_REPORT_OUTPUT
276            }
277            OsdpStatusReportType::Remote => {
278                libosdp_sys::osdp_status_report_type_OSDP_STATUS_REPORT_REMOTE
279            }
280            OsdpStatusReportType::Local => {
281                libosdp_sys::osdp_status_report_type_OSDP_STATUS_REPORT_LOCAL
282            }
283        }
284    }
285}
286
287/// Event to describe various status changes on PD
288///
289/// This event is used by the PD to indicate status such as input, output,
290/// tamper, etc.,. up to a maximum of 64 status bits can be reported. The values
291/// of the least significant N bit of status are considered, where N is the
292/// number of items as described in the corresponding capability codes,
293/// - PdCapability::OutputControl
294/// - PdCapability::ContactStatusMonitoring
295#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
296#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
297pub struct OsdpStatusReport {
298    /// The kind of event to report see `enum osdp_event_status_type_e`
299    pub type_: OsdpStatusReportType,
300    /// Number of valid entries in `report`
301    pub nr_entries: usize,
302    /// Status report
303    #[serde(with = "serde_arrays")]
304    pub report: [u8; 64],
305}
306
307impl From<libosdp_sys::osdp_status_report> for OsdpStatusReport {
308    fn from(value: libosdp_sys::osdp_status_report) -> Self {
309        OsdpStatusReport {
310            type_: value.type_.into(),
311            nr_entries: value.nr_entries as usize,
312            report: value.report,
313        }
314    }
315}
316
317impl From<OsdpStatusReport> for libosdp_sys::osdp_status_report {
318    fn from(value: OsdpStatusReport) -> Self {
319        libosdp_sys::osdp_status_report {
320            type_: value.type_.into(),
321            nr_entries: value.nr_entries as i32,
322            report: value.report,
323        }
324    }
325}
326
327/// CP to intimate it about various events that originate there (such as key
328/// press, card reads, etc.,). They do this by creating an “event” and sending
329/// it to the CP. This module is responsible to handling such events though
330/// OsdpEvent.
331#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
332#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
333pub enum OsdpEvent {
334    /// Event that describes card read activity on the PD
335    CardRead(OsdpEventCardRead),
336
337    /// Event to describe a key press activity on the PD
338    KeyPress(OsdpEventKeyPress),
339
340    /// Event to transport a Manufacturer specific command’s response
341    MfgReply(OsdpEventMfgReply),
342
343    /// Event to describe a input/output/tamper/power status change
344    Status(OsdpStatusReport),
345}
346
347impl From<OsdpEvent> for libosdp_sys::osdp_event {
348    fn from(value: OsdpEvent) -> Self {
349        match value {
350            OsdpEvent::CardRead(e) => libosdp_sys::osdp_event {
351                type_: libosdp_sys::osdp_event_type_OSDP_EVENT_CARDREAD,
352                flags: 0,
353                __bindgen_anon_1: libosdp_sys::osdp_event__bindgen_ty_1 {
354                    cardread: e.clone().into(),
355                },
356            },
357            OsdpEvent::KeyPress(e) => libosdp_sys::osdp_event {
358                type_: libosdp_sys::osdp_event_type_OSDP_EVENT_KEYPRESS,
359                flags: 0,
360                __bindgen_anon_1: libosdp_sys::osdp_event__bindgen_ty_1 {
361                    keypress: e.clone().into(),
362                },
363            },
364            OsdpEvent::MfgReply(e) => libosdp_sys::osdp_event {
365                type_: libosdp_sys::osdp_event_type_OSDP_EVENT_MFGREP,
366                flags: 0,
367                __bindgen_anon_1: libosdp_sys::osdp_event__bindgen_ty_1 {
368                    mfgrep: e.clone().into(),
369                },
370            },
371            OsdpEvent::Status(e) => libosdp_sys::osdp_event {
372                type_: libosdp_sys::osdp_event_type_OSDP_EVENT_STATUS,
373                flags: 0,
374                __bindgen_anon_1: libosdp_sys::osdp_event__bindgen_ty_1 { status: e.into() },
375            },
376        }
377    }
378}
379
380impl From<libosdp_sys::osdp_event> for OsdpEvent {
381    fn from(value: libosdp_sys::osdp_event) -> Self {
382        match value.type_ {
383            libosdp_sys::osdp_event_type_OSDP_EVENT_CARDREAD => {
384                OsdpEvent::CardRead(unsafe { value.__bindgen_anon_1.cardread.into() })
385            }
386            libosdp_sys::osdp_event_type_OSDP_EVENT_KEYPRESS => {
387                OsdpEvent::KeyPress(unsafe { value.__bindgen_anon_1.keypress.into() })
388            }
389            libosdp_sys::osdp_event_type_OSDP_EVENT_MFGREP => {
390                OsdpEvent::MfgReply(unsafe { value.__bindgen_anon_1.mfgrep.into() })
391            }
392            libosdp_sys::osdp_event_type_OSDP_EVENT_STATUS => {
393                OsdpEvent::Status(unsafe { value.__bindgen_anon_1.status.into() })
394            }
395            _ => panic!("Unknown event"),
396        }
397    }
398}
399
400#[cfg(test)]
401mod tests {
402    use super::OsdpEventCardRead;
403    use libosdp_sys::{
404        osdp_event_cardread, osdp_event_cardread_format_e_OSDP_CARD_FMT_RAW_UNSPECIFIED,
405        osdp_event_cardread_format_e_OSDP_CARD_FMT_RAW_WIEGAND,
406    };
407
408    #[test]
409    fn test_event_cardread() {
410        let event = OsdpEventCardRead::new_raw(vec![0x55, 0xAA]);
411        let event_struct: osdp_event_cardread = event.clone().into();
412
413        assert_eq!(event_struct.length, 2 * 8);
414        assert_eq!(event_struct.direction, 0);
415        assert_eq!(
416            event_struct.format,
417            osdp_event_cardread_format_e_OSDP_CARD_FMT_RAW_UNSPECIFIED
418        );
419        assert_eq!(event_struct.data[0], 0x55);
420        assert_eq!(event_struct.data[1], 0xAA);
421
422        assert_eq!(event, event_struct.into());
423
424        let event = OsdpEventCardRead::new_wiegand(15, vec![0x55, 0xAA]).unwrap();
425        let event_struct: osdp_event_cardread = event.clone().into();
426
427        assert_eq!(event_struct.length, 15);
428        assert_eq!(event_struct.direction, 0);
429        assert_eq!(
430            event_struct.format,
431            osdp_event_cardread_format_e_OSDP_CARD_FMT_RAW_WIEGAND
432        );
433        assert_eq!(event_struct.data[0], 0x55);
434        assert_eq!(event_struct.data[1], 0xAA);
435
436        assert_eq!(event, event_struct.into());
437    }
438}