soyal_client/
request.rs

1use serde::{Deserialize, Serialize};
2use std::ops::BitXorAssign;
3
4use crate::common::*;
5
6enum_from_primitive! {
7#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
8pub enum Command {
9    PromptAcceptedMessage            = 0x04,
10    PromptInvalidMessage             = 0x05,
11    PromptKeyingInPassword           = 0x09,
12    GetControllerParams              = 0x12,
13    HostingPolling                   = 0x18,
14    SetControllerParams              = 0x20,
15    RelayOnOffControl                = 0x21,
16    SetRealTimeClock                 = 0x23,
17    GetRealTimeClock                 = 0x24,
18    GetOldestEventLog                = 0x25,
19    BuzzerSounds                     = 0x26,
20    SendTextToLCD                    = 0x28,
21    DailyTimeZone                    = 0x2A,
22    ReadWriteBeginDay                = 0x2B,
23    AnnualHolidaySetting             = 0x2C,
24    EmptyEventLog                    = 0x2D,
25    ReadWriteUserAlias               = 0x2E,
26    ReadWriteUserFloor               = 0x2F,
27    SerialSpecificFormat             = 0x30,
28    RemoveOldestEventLog             = 0x37,
29    SetUserParamsWithAntiPassBack    = 0x83,
30    SetUserParamsWithoutAntiPassBack = 0x84,
31    EraseUserData                    = 0x85,
32    InitialAntiPassback              = 0x86,
33    GetUserParams                    = 0x87,
34    AntiPassbackDB                   = 0x8A,
35    FingerprintOrVein                = 0x8F,
36    BlackUIDManagement               = 0x90,
37}
38}
39
40enum_from_primitive! {
41#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
42pub enum ControllerParamSubCommand {
43    ControllerOptionParams = 0x00,
44    AutoOpenTimeZone       = 0x01,
45    DailyAlarmTable        = 0x02,
46    AutoDutyShiftTimeTable = 0x03,
47    MasterCardUID          = 0x04,
48    RS485SubNodeDoorNumber = 0x05,
49    CustomData             = 0x06,
50    UIDBlockParams         = 0x08,
51    RemoteTCPServerParams  = 0x0A,
52    DutyText               = 0x0B,
53    DESFireFieldAssignment = 0x12,
54    HostingFlag            = 0x13,
55    IpAndMacAddress        = 0x14,
56    RelayDelayTime         = 0x16,
57    ExtraFlag              = 0x17,
58    ControllerEditPassword = 0x18,
59    ControllerAccessMode   = 0x19,
60    RS485ReaderStatus      = 0x81, // read-only
61    ContorllerSerialNumber = 0xFE, // read-only
62}
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct ExtendedMessage<'a> {
67    pub destination_id: u8, // 0x00: bus master, 0xFF: broadcast
68    pub command: Command,
69    pub data: &'a [u8],
70}
71
72impl<'a> ExtendedMessage<'a> {
73    pub fn encode(&self) -> Vec<u8> {
74        let length: u16 = self.data.len() as u16 + 4; // 4 extra bytes: destination_id, command_code, xor, sum
75
76        assert!(length < 250, "Extended message data part too long!");
77
78        let full_length: u16 = length + 4 + 2; // 4 header + 2 length bytes
79
80        let command_code = self.command as u8;
81
82        let mut buffer = Vec::<u8>::with_capacity(full_length as usize);
83        buffer.extend_from_slice(&EXTENDED_HEADER);
84        buffer.extend_from_slice(&length.to_be_bytes());
85        buffer.push(self.destination_id);
86        buffer.push(command_code);
87        buffer.extend_from_slice(self.data);
88
89        let mut xor_res: u8 = 0xFF;
90        xor_res.bitxor_assign(self.destination_id);
91        xor_res.bitxor_assign(command_code);
92        for d in self.data {
93            xor_res.bitxor_assign(d);
94        }
95        buffer.push(xor_res);
96
97        let mut sum_res: u8 = 0;
98        sum_res = sum_res.wrapping_add(self.destination_id);
99        sum_res = sum_res.wrapping_add(command_code);
100        for d in self.data {
101            sum_res = sum_res.wrapping_add(*d);
102        }
103        sum_res = sum_res.wrapping_add(xor_res);
104        buffer.push(sum_res);
105
106        buffer
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn encode_no_data() {
116        let d = ExtendedMessage {
117            destination_id: 1,
118            command: Command::HostingPolling,
119            data: &[],
120        };
121        assert_eq!(d.encode(), vec!(0xFF, 0x00, 0x5A, 0xA5, 0x00, 0x04, 0x01, 0x18, 0xE6, 0xFF))
122    }
123
124    #[test]
125    fn encode_with_data() {
126        let d = ExtendedMessage {
127            destination_id: 1,
128            command: Command::HostingPolling,
129            data: &[0x01, 0x02],
130        };
131        assert_eq!(d.encode(), vec!(0xFF, 0x00, 0x5A, 0xA5, 0x00, 0x06, 0x01, 0x18, 0x01, 0x02, 0xE5, 0x01))
132    }
133}