soyal_client/
response.rs

1use crate::common::*;
2use crate::enums::*;
3use crate::structs::*;
4
5use chrono::{DateTime, Local, TimeZone};
6use either::Either;
7use enum_primitive::FromPrimitive;
8use log::*;
9use semver::Version;
10use serde::{Deserialize, Serialize};
11use std::ops::BitXorAssign;
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub struct EchoResponse<'a> {
15    pub destination_id: u8, // 0x00 == Host (PC)
16    pub command: EchoCode,
17    pub data: &'a [u8],
18}
19
20pub trait Response<T> {
21    fn decode(raw: &[u8]) -> Result<T>;
22
23    fn get_message_part(raw: &[u8]) -> Result<&[u8]> {
24        if raw.is_empty() {
25            return Err(ProtocolError::NoResponse.into());
26        }
27
28        let (raw_msg, expected_msg_length, msg_length) = match raw[0] {
29            0x7E => {
30                let non_header = &raw[1..];
31                let expected_msg_length = u16::from(non_header[0]) as usize;
32                let msg_length = non_header.len() - 1;
33
34                // ignore the first length byte
35                Ok((&non_header[1..], expected_msg_length, msg_length))
36            },
37            0xFF => match raw[0..4] == EXTENDED_HEADER {
38                true => {
39                    let non_header = &raw[4..];
40                    let expected_msg_length = u16::from_be_bytes([non_header[0], non_header[1]]) as usize;
41                    let msg_length = non_header.len() - 2;
42
43                    // ignore the first 2 length bytes
44                    Ok((&non_header[2..], expected_msg_length, msg_length))
45                },
46                false => Err(ProtocolError::UnexpectedHeaderValue),
47            },
48            other => Err(ProtocolError::UnexpectedFirstHeaderByte(other)),
49        }?;
50
51        if expected_msg_length != msg_length {
52            error!("Message length mismatch, expected: {} but got: {}", expected_msg_length, msg_length);
53            debug!("Raw response: {:?}", raw);
54            return Err(ProtocolError::MessageLengthMismatch.into());
55        };
56
57        // get and test XOR and SUM values
58        let sum = raw_msg.get(msg_length - 1).expect("Missing sum value");
59        let xor = raw_msg.get(msg_length - 2).expect("Missing xor value");
60
61        //trace!("Received XOR: {:#X?}, SUM {:#X?}", xor, sum);
62
63        let mut xor_res: u8 = 0xFF;
64        for d in &raw_msg[..msg_length - 2] {
65            xor_res.bitxor_assign(d);
66        }
67
68        //trace!("Calculated XOR: {:#X?}", xor_res);
69
70        if xor_res != *xor {
71            return Err(ProtocolError::BadXorValue.into());
72        }
73
74        let mut sum_res: u8 = 0;
75        for d in &raw_msg[..msg_length - 1] {
76            sum_res = sum_res.wrapping_add(*d);
77        }
78
79        //trace!("Calculated SUM: {:#X?}", sum_res);
80
81        if sum_res != *sum {
82            return Err(ProtocolError::BadChecksumValue.into());
83        }
84
85        // ignore the last two XOR/SUM bytes
86        Ok(&raw_msg[0..msg_length - 2])
87    }
88
89    fn get_data_parts(raw: &[u8], expected_command: Option<EchoCode>) -> Result<EchoResponse> {
90        let msg = Self::get_message_part(raw)?;
91        let destination_id = msg[0];
92        let command: EchoCode = EchoCode::from_u8(msg[1]).ok_or(ProtocolError::UnknownCommandCode(msg[1]))?;
93
94        if let Some(exp) = expected_command {
95            if exp != command {
96                return Err(ProtocolError::UnexpectedCommandCode.into());
97            }
98        }
99
100        let data = &msg[2..];
101        Ok(EchoResponse { destination_id, command, data })
102    }
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct PollResponse {
107    pub destination_id: u8,
108    pub function_code: u8,
109    pub source: u8,
110    pub event_type: u8,
111    pub data: ControllerStatus,
112}
113
114impl Response<PollResponse> for PollResponse {
115    fn decode(raw: &[u8]) -> Result<PollResponse> {
116        let msg = Self::get_message_part(raw)?;
117
118        if msg.len() < 5 {
119            return Err(ProtocolError::MessageTooShort.into());
120        }
121
122        let destination_id = msg[0];
123        let function_code = msg[1];
124        let source = msg[2];
125        let event_type = msg[3];
126        let data = ControllerStatus::decode(event_type, &msg[4..])?;
127
128        Ok(PollResponse {
129            destination_id,
130            function_code,
131            source,
132            event_type,
133            data,
134        })
135    }
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct SerialNumberResponse {
140    pub destination_id: u8,
141    pub command: EchoCode,
142    pub source: u8,
143    //pub flash_size_code: ???
144    pub serial: Vec<u8>,
145}
146
147impl Response<SerialNumberResponse> for SerialNumberResponse {
148    fn decode(raw: &[u8]) -> Result<SerialNumberResponse> {
149        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
150        let data = parts.data;
151
152        if data.len() < 15 {
153            return Err(ProtocolError::MessageTooShort.into());
154        }
155
156        let source = data[0];
157        let serial = data[3..15].to_vec();
158
159        Ok(SerialNumberResponse {
160            destination_id: parts.destination_id,
161            command: parts.command,
162            source,
163            serial,
164        })
165    }
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct RelayDelayResponse {
170    pub destination_id: u8,
171    pub command: EchoCode,
172    pub source: u8,
173    pub main_port_door_relay_time: u16,    // 10ms
174    pub wiegand_port_door_relay_time: u16, // 10ms
175    pub alarm_relay_time: u16,             // 10ms
176    pub lift_controller_time: u16,         // 10ms
177}
178
179impl Response<RelayDelayResponse> for RelayDelayResponse {
180    fn decode(raw: &[u8]) -> Result<RelayDelayResponse> {
181        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
182        let data = parts.data;
183
184        if data.len() < 9 {
185            return Err(ProtocolError::MessageTooShort.into());
186        }
187
188        let source = data[0];
189        let main_port_door_relay_time = u16::from_be_bytes([data[1], data[2]]);
190        let wiegand_port_door_relay_time = u16::from_be_bytes([data[3], data[4]]);
191        let alarm_relay_time = u16::from_be_bytes([data[5], data[6]]);
192        let lift_controller_time = u16::from_be_bytes([data[7], data[8]]);
193
194        Ok(RelayDelayResponse {
195            destination_id: parts.destination_id,
196            command: parts.command,
197            source,
198            main_port_door_relay_time,
199            wiegand_port_door_relay_time,
200            alarm_relay_time,
201            lift_controller_time,
202        })
203    }
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct EditPasswordResponse {
208    pub destination_id: u8,
209    pub command: EchoCode,
210    pub source: u8,
211    pub password: u32,
212}
213
214impl Response<EditPasswordResponse> for EditPasswordResponse {
215    fn decode(raw: &[u8]) -> Result<EditPasswordResponse> {
216        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
217        let data = parts.data;
218
219        if data.len() < 5 {
220            return Err(ProtocolError::MessageTooShort.into());
221        }
222
223        let source = data[0];
224        let password = u32::from_be_bytes([data[1], data[2], data[3], data[4]]);
225
226        Ok(EditPasswordResponse {
227            destination_id: parts.destination_id,
228            command: parts.command,
229            source,
230            password,
231        })
232    }
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ControllerOptionsResponse {
237    pub destination_id: u8,
238    pub command: EchoCode,
239    pub source: u8,
240    pub controller_type: ControllerType,
241    pub controller_options: ControllerOptions,
242}
243
244impl Response<ControllerOptionsResponse> for ControllerOptionsResponse {
245    fn decode(raw: &[u8]) -> Result<ControllerOptionsResponse> {
246        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
247        let data = parts.data;
248
249        if data.len() < 43 {
250            return Err(ProtocolError::MessageTooShort.into());
251        }
252
253        let source = data[0];
254        let controller_type = ControllerType::from_u8(data[1]).ok_or(ProtocolError::UnknownControllerType(data[1]))?;
255        let controller_options = ControllerOptions::decode(data, Version::new(4, 3, 0))?;
256
257        Ok(ControllerOptionsResponse {
258            destination_id: parts.destination_id,
259            command: parts.command,
260            source,
261            controller_type,
262            controller_options,
263        })
264    }
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct IpAndMacAddressResponse {
269    pub destination_id: u8,
270    pub command: EchoCode,
271    pub source: u8,
272    pub address_data: IpAndMacAddress,
273}
274
275impl Response<IpAndMacAddressResponse> for IpAndMacAddressResponse {
276    fn decode(raw: &[u8]) -> Result<IpAndMacAddressResponse> {
277        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
278        let data = parts.data;
279
280        if data.len() < 32 {
281            return Err(ProtocolError::MessageTooShort.into());
282        }
283
284        let source = data[0];
285        // data[1] ??? what is this? documentation doesn't have it
286
287        let address_data = IpAndMacAddress::decode(&data[2..]);
288
289        Ok(IpAndMacAddressResponse {
290            destination_id: parts.destination_id,
291            command: parts.command,
292            source,
293            address_data,
294        })
295    }
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct RemoteTCPServerParamsResponse {
300    pub destination_id: u8,
301    pub command: EchoCode,
302    pub source: u8,
303    pub remote_server_params: RemoteTCPServerParams,
304}
305
306impl Response<RemoteTCPServerParamsResponse> for RemoteTCPServerParamsResponse {
307    fn decode(raw: &[u8]) -> Result<RemoteTCPServerParamsResponse> {
308        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
309        let data = parts.data;
310
311        if data.len() < 13 {
312            return Err(ProtocolError::MessageTooShort.into());
313        }
314
315        let source = data[0];
316        let remote_server_params = RemoteTCPServerParams::decode(&data[1..]);
317
318        Ok(RemoteTCPServerParamsResponse {
319            destination_id: parts.destination_id,
320            command: parts.command,
321            source,
322            remote_server_params,
323        })
324    }
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct EventLogStatusResponse {
329    pub destination_id: u8,
330    pub command: EchoCode,
331    pub event_log_counter: u8,
332    pub queue_input_point: u8,
333    pub queue_output_point: u8,
334}
335
336impl Response<EventLogStatusResponse> for EventLogStatusResponse {
337    fn decode(raw: &[u8]) -> Result<EventLogStatusResponse> {
338        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
339        let data = parts.data;
340
341        if data.len() < 3 {
342            return Err(ProtocolError::MessageTooShort.into());
343        }
344
345        Ok(EventLogStatusResponse {
346            destination_id: parts.destination_id,
347            command: parts.command,
348            event_log_counter: data[0],
349            queue_input_point: data[1],
350            queue_output_point: data[2],
351        })
352    }
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct AckResponse {
357    pub destination_id: u8,
358    pub source: u8,
359    // controller specific data
360}
361
362impl Response<AckResponse> for AckResponse {
363    fn decode(raw: &[u8]) -> Result<AckResponse> {
364        let parts = Self::get_data_parts(raw, Some(EchoCode::CommandAcknowledged))?;
365        let data = parts.data;
366
367        if data.is_empty() {
368            return Err(ProtocolError::MessageTooShort.into());
369        }
370
371        let source = data[0];
372
373        Ok(AckResponse {
374            destination_id: parts.destination_id,
375            source,
376        })
377    }
378}
379
380#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct NackResponse {
382    pub destination_id: u8,
383    pub source: u8,
384    // controller specific data
385}
386
387impl Response<NackResponse> for NackResponse {
388    fn decode(raw: &[u8]) -> Result<NackResponse> {
389        let parts = Self::get_data_parts(raw, Some(EchoCode::CommandUnacknowledged))?;
390        let data = parts.data;
391
392        if data.is_empty() {
393            return Err(ProtocolError::MessageTooShort.into());
394        }
395
396        let source = data[0];
397
398        Ok(NackResponse {
399            destination_id: parts.destination_id,
400            source,
401        })
402    }
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct AckOrNack {
407    res: Either<NackResponse, AckResponse>,
408}
409
410impl AckOrNack {
411    pub fn should_ack(&self) -> Result<AckResponse> {
412        match self.res.as_ref() {
413            Either::Left(_) => Err(ProtocolError::CommandNotAcknowledged.into()),
414            Either::Right(r) => Ok(r.to_owned()),
415        }
416    }
417
418    pub fn handle(raw: Vec<u8>) -> Result<AckOrNack> {
419        match AckResponse::decode(&raw) {
420            Ok(x) => Ok(Either::Right(x)),
421            Err(_) => NackResponse::decode(&raw).map(Either::Left),
422        }
423        .map(|res| AckOrNack { res })
424    }
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct EventLogResponse {
429    pub destination_id: u8,
430    pub function_code: EventFunctionCode,
431    pub source: u8,
432    pub timestamp: DateTime<Local>,
433    pub port_number: EventPortNumber,
434    pub user_address_or_tag_id: u16, // Normal Access: User ID - Other: last 2 bytes of the Card UID // TODO make this an enum?
435    pub tag_id: TagId32,
436    // Sub Code
437    // Sub Func. // function code AlarmEvent
438    // Ext Code
439    // User level
440    pub door_number: u8,
441    pub sor_deduction_amount: u16,
442    pub sor_balance: u16,                // or 8 byte UID???
443    pub user_inputted_code: Option<u32>, // only available for function code InvalidUserPIN
444}
445
446impl Response<EventLogResponse> for EventLogResponse {
447    fn decode(raw: &[u8]) -> Result<EventLogResponse> {
448        let msg = Self::get_message_part(raw)?;
449
450        let destination_id = msg[0];
451        let function_code = EventFunctionCode::from_u8(msg[1]).ok_or(ProtocolError::UnknownEventFunctionCode(msg[1]))?;
452        let data = &msg[2..];
453
454        if data.len() < 25 {
455            return Err(ProtocolError::MessageTooShort.into());
456        }
457
458        let source = data[0];
459
460        let year = 2000 + data[7] as i32;
461        let month = data[6] as u32;
462        let day = data[5] as u32;
463        let hour = data[3] as u32;
464        let minute = data[2] as u32;
465        let second = data[1] as u32;
466        let timestamp = Local
467            .with_ymd_and_hms(year, month, day, hour, minute, second)
468            .latest()
469            .ok_or(ProtocolError::InvalidDateTime)?;
470
471        let port_number = EventPortNumber::from_u8(data[8]).ok_or(ProtocolError::UnknownPortNumber(data[8]))?;
472
473        let user_address_or_tag_id = u16::from_be_bytes([data[9], data[10]]);
474        let tag_id = TagId32::decode(&[data[15], data[16], data[19], data[20]])?;
475
476        let door_number = data[17];
477
478        let sor_deduction_amount = u16::from_be_bytes([data[21], data[22]]);
479        let sor_balance = u16::from_be_bytes([data[23], data[24]]);
480
481        let user_inputted_code = if function_code == EventFunctionCode::InvalidUserPIN {
482            Some(u32::from_be_bytes([data[25], data[26], data[27], data[28]]))
483        } else {
484            None
485        };
486
487        Ok(EventLogResponse {
488            destination_id,
489            function_code,
490            source,
491            timestamp,
492            port_number,
493            user_address_or_tag_id,
494            tag_id,
495            door_number,
496            sor_deduction_amount,
497            sor_balance,
498            user_inputted_code,
499        })
500    }
501}
502
503#[derive(Debug, Clone, Serialize, Deserialize)]
504pub struct UserParametersResponse {
505    pub destination_id: u8,
506    pub command: EchoCode,
507    pub source: u8,
508    pub user_parameters: UserParameters,
509}
510
511impl Response<UserParametersResponse> for UserParametersResponse {
512    fn decode(raw: &[u8]) -> Result<UserParametersResponse> {
513        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
514        let data = parts.data;
515
516        //trace!("User data: {:?}, len: {}", data, data.len());
517
518        if data.len() < 25 {
519            return Err(ProtocolError::MessageTooShort.into());
520        }
521
522        let source = data[0];
523        let user_parameters = UserParameters::decode(&data[1..25])?;
524
525        Ok(UserParametersResponse {
526            destination_id: parts.destination_id,
527            command: parts.command,
528            source,
529            user_parameters,
530        })
531    }
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize)]
535pub struct RelayStatusResponse {
536    pub destination_id: u8,
537    pub command: EchoCode,
538    pub source: u8,
539    pub firmware_version: semver::Version,
540    pub di_port: DIPortStatus,
541    pub relay_port: RelayPortStatus,
542    pub main_port_options: ControllerPortOptions,
543    pub wiegand_port_options: ControllerPortOptions,
544    pub main_port_arming: bool,
545    pub wiegand_port_arming: bool,
546}
547
548impl Response<RelayStatusResponse> for RelayStatusResponse {
549    fn decode(raw: &[u8]) -> Result<RelayStatusResponse> {
550        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
551        let data = parts.data;
552
553        if data.len() < 9 {
554            return Err(ProtocolError::MessageTooShort.into());
555        }
556
557        let source = data[0];
558        let firmware_major = (data[1] & 0xF0) >> 4;
559        let firmware_minor = data[1] & 0x0F;
560        let firmware_version = semver::Version::new(firmware_major as u64, firmware_minor as u64, 0);
561        let di_port = DIPortStatus::decode(data[2]);
562        let relay_port = RelayPortStatus::decode(data[3]);
563        let main_port_options = ControllerPortOptions::decode(data[4]);
564        let wiegand_port_options = ControllerPortOptions::decode(data[5]);
565        // data[6] reserved
566        let main_port_arming = data[7] & 0b00000001 != 0;
567        let wiegand_port_arming = data[7] & 0b00000010 != 0;
568        // data[8] reserved
569
570        Ok(RelayStatusResponse {
571            destination_id: parts.destination_id,
572            command: parts.command,
573            source,
574            firmware_version,
575            di_port,
576            relay_port,
577            main_port_options,
578            wiegand_port_options,
579            main_port_arming,
580            wiegand_port_arming,
581        })
582    }
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
586pub struct RealTimeClockResponse {
587    pub destination_id: u8,
588    pub command: EchoCode,
589    pub source: u8,
590    pub clock: ClockData,
591}
592
593impl Response<RealTimeClockResponse> for RealTimeClockResponse {
594    fn decode(raw: &[u8]) -> Result<RealTimeClockResponse> {
595        let parts = Self::get_data_parts(raw, Some(EchoCode::RequestedData))?;
596        let data = parts.data;
597
598        let source = data[0];
599        let clock = ClockData::decode(&data[1..])?;
600
601        Ok(RealTimeClockResponse {
602            destination_id: parts.destination_id,
603            command: parts.command,
604            source,
605            clock,
606        })
607    }
608}
609
610#[cfg(test)]
611mod tests {
612    use super::*;
613
614    #[test]
615    fn decode_status_io_response() {
616        let raw = vec![255, 0, 90, 165, 0, 10, 0, 9, 1, 0, 1, 0, 16, 0, 230, 1];
617        let d = PollResponse::decode(&raw);
618        assert!(d.is_ok());
619        if let Ok(echo) = d {
620            assert_eq!(echo.destination_id, 0);
621            assert_eq!(echo.function_code, 9);
622            assert_eq!(echo.source, 1);
623            assert_eq!(echo.event_type, 0);
624            assert_eq!(
625                echo.data,
626                ControllerStatus::IoStatus(IoStatusData {
627                    status_data: StatusData {
628                        keypad_locked: false,
629                        door_release_output: false,
630                        alarm_output: false,
631                        arming: false,
632                        controller_alarm: false,
633                        egress_released: false,
634                        door_open: true
635                    },
636                    alarm_type: None,
637                    controller_options: ControllerPortOptions {
638                        anti_pass_back_enabled: false,
639                        anti_pass_back_in: false,
640                        force_open_alarm: false,
641                        egress_button: true,
642                        skip_pin_check: false,
643                        auto_open_zone: false,
644                        auto_lock_door: false,
645                        time_attendance_disabled: false
646                    },
647                })
648            );
649        }
650    }
651
652    #[test]
653    fn decode_status_all_keys_response() {
654        let raw = vec![255, 0, 90, 165, 0, 21, 0, 9, 1, 1, 139, 4, 210, 0, 16, 1, 0, 5, 0, 3, 4, 0, 1, 11, 0, 178, 71];
655        let d = PollResponse::decode(&raw);
656        assert!(d.is_ok());
657        if let Ok(echo) = d {
658            assert_eq!(echo.destination_id, 0);
659            assert_eq!(echo.function_code, 9);
660            assert_eq!(echo.source, 1);
661            assert_eq!(echo.event_type, 1);
662            assert_eq!(
663                echo.data,
664                ControllerStatus::AllKeysPressed(AllKeysPressedData {
665                    fifth_key_data: None,
666                    input_value: 1234,
667                    device_params: ControllerPortOptions {
668                        anti_pass_back_enabled: false,
669                        anti_pass_back_in: false,
670                        force_open_alarm: false,
671                        egress_button: true,
672                        skip_pin_check: false,
673                        auto_open_zone: false,
674                        auto_lock_door: false,
675                        time_attendance_disabled: false
676                    },
677                })
678            );
679        }
680    }
681
682    #[test]
683    fn decode_status_new_card_response() {
684        let raw = vec![
685            255, 0, 90, 165, 0, 23, 0, 9, 1, 2, 11, 18, 221, 0, 0, 186, 139, 0, 16, 0, 138, 0, 0, 0, 0, 0, 0, 154, 127,
686        ];
687        let d = PollResponse::decode(&raw);
688        assert!(d.is_ok());
689        if let Ok(echo) = d {
690            assert_eq!(echo.destination_id, 0);
691            assert_eq!(echo.function_code, 9);
692            assert_eq!(echo.source, 1);
693            assert_eq!(echo.event_type, 2);
694            assert_eq!(
695                echo.data,
696                ControllerStatus::NewCardPresent(NewCardPresentData {
697                    card_id: TagId32::new(4829, 47755),
698                    input_value: 0,
699                    id_em4001: 0,
700                    device_params: ControllerPortOptions {
701                        anti_pass_back_enabled: false,
702                        anti_pass_back_in: false,
703                        force_open_alarm: false,
704                        egress_button: true,
705                        skip_pin_check: false,
706                        auto_open_zone: false,
707                        auto_lock_door: false,
708                        time_attendance_disabled: false
709                    },
710                    from_wiegand: false,
711                    setting_forced_open_alarm: false
712                })
713            );
714        }
715    }
716
717    #[test]
718    fn decode_controller_options_response() {
719        let raw = vec![
720            255, 0, 90, 165, 0, 57, 0, 3, 1, 193, 1, 2, 0, 1, 226, 64, 0, 0, 0, 0, 0, 0, 0, 0, 4, 210, 0, 0, 0, 0, 1, 4, 0, 100, 2, 188, 2, 188, 5, 220, 48,
721            16, 1, 17, 15, 15, 0, 8, 0, 1, 1, 65, 24, 9, 5, 0, 0, 0, 3, 3, 0, 159, 13,
722        ];
723        let d = ControllerOptionsResponse::decode(&raw);
724        assert!(d.is_ok());
725        if let Ok(o) = d {
726            assert_eq!(o.destination_id, 0);
727            assert_eq!(o.command, EchoCode::RequestedData);
728            assert_eq!(o.source, 1);
729            assert_eq!(o.controller_type, ControllerType::AR725Ev2);
730            assert_eq!(o.controller_options.main_port_door_number, 1);
731            assert_eq!(o.controller_options.wiegand_port_door_number, 2);
732            assert_eq!(o.controller_options.edit_password, 123456);
733            assert_eq!(o.controller_options.master_user_range_start, 0);
734            assert_eq!(o.controller_options.master_user_range_end, 0);
735            assert_eq!(o.controller_options.general_password, 1234);
736            assert_eq!(o.controller_options.duress_code, 0);
737            assert_eq!(o.controller_options.tag_hold_time, 100);
738            assert_eq!(o.controller_options.main_port_door_relay_time, 700);
739            assert_eq!(o.controller_options.wiegand_port_door_relay_time, 700);
740            assert_eq!(o.controller_options.alarm_relay_time, 1500);
741            assert_eq!(
742                o.controller_options.main_port_options,
743                ControllerPortOptions {
744                    anti_pass_back_enabled: false,
745                    anti_pass_back_in: false,
746                    force_open_alarm: true,
747                    egress_button: true,
748                    skip_pin_check: false,
749                    auto_open_zone: false,
750                    auto_lock_door: false,
751                    time_attendance_disabled: false
752                }
753            );
754            assert_eq!(
755                o.controller_options.wiegand_port_options,
756                ControllerPortOptions {
757                    anti_pass_back_enabled: false,
758                    anti_pass_back_in: false,
759                    force_open_alarm: false,
760                    egress_button: true,
761                    skip_pin_check: false,
762                    auto_open_zone: false,
763                    auto_lock_door: false,
764                    time_attendance_disabled: false
765                }
766            );
767            assert_eq!(
768                o.controller_options.main_port_extended_options,
769                ExtendedControllerOptions {
770                    door_relay_active_in_auto_open_time_zone: false,
771                    stop_alarm_at_door_closed: false,
772                    free_tag_access_mode: false,
773                    use_main_door_relay_for_wiegand_port: false,
774                    auto_disarmed_time_zone: false,
775                    key_pad_inhibited: false,
776                    fingerprint_only_enabled: false,
777                    egress_button_sound: true
778                }
779            );
780            assert_eq!(
781                o.controller_options.wiegand_port_extended_options,
782                ExtendedControllerOptions {
783                    door_relay_active_in_auto_open_time_zone: false,
784                    stop_alarm_at_door_closed: false,
785                    free_tag_access_mode: false,
786                    use_main_door_relay_for_wiegand_port: true,
787                    auto_disarmed_time_zone: false,
788                    key_pad_inhibited: false,
789                    fingerprint_only_enabled: false,
790                    egress_button_sound: true
791                }
792            );
793            assert_eq!(o.controller_options.main_port_door_close_time, 15);
794            assert_eq!(o.controller_options.wiegand_port_door_close_time, 15);
795            assert_eq!(o.controller_options.main_port_arming, false);
796            assert_eq!(o.controller_options.wiegand_port_arming, false);
797            assert_eq!(o.controller_options.access_mode, ControllerAccessMode::PINOnly);
798            assert_eq!(o.controller_options.armed_output_pulse_width, 0);
799            assert_eq!(o.controller_options.arming_delay, 1);
800            assert_eq!(o.controller_options.alarm_delay, 1);
801        }
802    }
803
804    #[test]
805    fn decode_ack_extended() {
806        let raw = vec![255, 0, 90, 165, 0, 15, 0, 4, 1, 193, 67, 15, 145, 16, 16, 0, 0, 0, 0, 230, 175];
807        let d = AckResponse::decode(&raw);
808        println!("Result: {:?}", d);
809        assert!(d.is_ok());
810    }
811
812    #[test]
813    fn decode_ack_simple() {
814        let raw = vec![126, 15, 0, 4, 1, 193, 67, 15, 145, 16, 16, 0, 0, 0, 0, 230, 175];
815        let d = AckResponse::decode(&raw);
816        println!("Result: {:?}", d);
817        assert!(d.is_ok());
818    }
819}