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, ContorllerSerialNumber = 0xFE, }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct ExtendedMessage<'a> {
67 pub destination_id: u8, 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; assert!(length < 250, "Extended message data part too long!");
77
78 let full_length: u16 = length + 4 + 2; 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}