1extern crate byteorder;
6extern crate crc;
7
8mod answers;
9mod capsuled_parser;
10mod checksum;
11mod cmds;
12mod errors;
13mod internals;
14mod prelude;
15mod protocol;
16pub mod rpos_drv;
17mod ultra_capsuled_parser;
18pub mod utils;
19
20pub use self::errors::*;
21pub use self::prelude::*;
22
23pub use self::answers::RplidarResponseDeviceInfo;
24
25use self::answers::*;
26use self::capsuled_parser::parse_capsuled;
27use self::checksum::Checksum;
28use self::cmds::*;
29use self::internals::*;
30pub use self::protocol::RplidarHostProtocol;
31use self::ultra_capsuled_parser::parse_ultra_capsuled;
32use byteorder::{ByteOrder, LittleEndian};
33use crc::crc32;
34pub use rpos_drv::{Channel, Message, Result};
35use std::collections::VecDeque;
36use std::io::{Read, Write};
37use std::mem::transmute_copy;
38use std::time::{Duration, Instant};
39
40const RPLIDAR_GET_LIDAR_CONF_START_VERSION: u16 = ((1 << 8) | (24)) as u16;
41
42#[derive(Debug)]
44pub struct RplidarDevice<T: ?Sized> {
45 channel: Channel<RplidarHostProtocol, T>,
46 cached_measurement_nodes: VecDeque<ScanPoint>,
47 cached_prev_capsule: CachedPrevCapsule,
48}
49
50macro_rules! parse_resp_data {
51 ($x:expr, $t:ty) => {{
52 const SIZE: usize = std::mem::size_of::<$t>();
53 if $x.len() != SIZE {
54 Err(Error::from(RposError::OperationFail {
55 description: "answer type mismatch".to_owned(),
56 }))
57 } else {
58 let mut slice = [0u8; SIZE];
59 slice.clone_from_slice(&$x[..]);
60 Ok(unsafe { transmute_copy::<[u8; SIZE], $t>(&slice) })
61 }
62 }};
63}
64
65macro_rules! parse_resp {
66 ($x:expr, $t:ty) => {
67 parse_resp_data!($x.data, $t)
68 };
69}
70
71macro_rules! handle_resp {
72 ($ans:expr, $x:expr, $t:ty) => {
73 if $x.cmd != $ans {
74 Err(RposError::OperationFail {
75 description: "answer type mismatch".to_owned(),
76 }
77 .into())
78 } else {
79 parse_resp!($x, $t)
80 }
81 };
82}
83
84impl From<RplidarResponseMeasurementNodeHq> for ScanPoint {
85 fn from(p: RplidarResponseMeasurementNodeHq) -> ScanPoint {
86 ScanPoint {
87 angle_z_q14: p.angle_z_q14,
88 dist_mm_q2: p.dist_mm_q2,
89 quality: p.quality,
90 flag: p.flag,
91 }
92 }
93}
94
95impl<T: ?Sized> RplidarDevice<T>
96where
97 T: Read + Write,
98{
99 pub fn new(channel: Channel<RplidarHostProtocol, T>) -> RplidarDevice<T> {
108 RplidarDevice {
109 channel,
110 cached_measurement_nodes: VecDeque::with_capacity(RPLIDAR_DEFAULT_CACHE_DEPTH),
111 cached_prev_capsule: CachedPrevCapsule::None,
112 }
113 }
114
115 pub fn with_stream(stream: Box<T>) -> RplidarDevice<T> {
123 RplidarDevice::<T>::new(rpos_drv::Channel::new(RplidarHostProtocol::new(), stream))
124 }
125
126 pub fn get_device_info(&mut self) -> Result<RplidarResponseDeviceInfo> {
128 self.get_device_info_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
129 }
130
131 pub fn get_device_info_with_timeout(
133 &mut self,
134 timeout: Duration,
135 ) -> Result<RplidarResponseDeviceInfo> {
136 if let Some(msg) = self
137 .channel
138 .invoke(&Message::new(RPLIDAR_CMD_GET_DEVICE_INFO), timeout)?
139 {
140 return handle_resp!(RPLIDAR_ANS_TYPE_DEVINFO, msg, RplidarResponseDeviceInfo);
141 }
142
143 Err(RposError::OperationTimeout.into())
144 }
145
146 pub fn stop(&mut self) -> Result<()> {
148 self.channel.write(&Message::new(RPLIDAR_CMD_STOP))?;
149 Ok(())
150 }
151
152 pub fn core_reset(&mut self) -> Result<()> {
154 self.channel.write(&Message::new(RPLIDAR_CMD_RESET))?;
155 Ok(())
156 }
157
158 pub fn set_motor_pwm(&mut self, pwm: u16) -> Result<()> {
160 let mut payload = [0; 2];
161 LittleEndian::write_u16(&mut payload, pwm);
162
163 self.channel
164 .write(&Message::with_data(RPLIDAR_CMD_SET_MOTOR_PWM, &payload))?;
165
166 Ok(())
167 }
168
169 pub fn stop_motor(&mut self) -> Result<()> {
171 self.set_motor_pwm(0)
172 }
173
174 pub fn start_motor(&mut self) -> Result<()> {
176 self.set_motor_pwm(RPLIDAR_DEFAULT_MOTOR_PWM)
177 }
178
179 fn get_lidar_conf_with_timeout(
193 &mut self,
194 config_type: u32,
195 timeout: Duration,
196 ) -> Result<Vec<u8>> {
197 self.get_lidar_conf_with_param_and_timeout(config_type, &[], timeout)
198 }
199
200 fn get_lidar_conf_with_param_and_timeout(
202 &mut self,
203 config_type: u32,
204 param: &[u8],
205 timeout: Duration,
206 ) -> Result<Vec<u8>> {
207 let mut msg = Message::with_data(RPLIDAR_CMD_GET_LIDAR_CONF, &[0; 4]);
208
209 LittleEndian::write_u32(&mut msg.data, config_type);
210 msg.data.extend_from_slice(param);
211
212 let response = self.channel.invoke(&msg, timeout)?;
213
214 if let Some(mut response_msg) = response {
215 if response_msg.cmd != RPLIDAR_ANS_TYPE_GET_LIDAR_CONF {
216 Err(RposError::OperationFail {
217 description: "answer type mismatch".to_owned(),
218 }
219 .into())
220 } else if response_msg.data.len() < 4
221 || LittleEndian::read_u32(&response_msg.data[0..4]) != config_type
222 {
223 return Err(RposError::OperationFail {
224 description: "answer config type mismatch".to_owned(),
225 }
226 .into());
227 } else {
228 return Ok(response_msg.data.split_off(4));
229 }
230 } else {
231 Err(RposError::OperationTimeout.into())
232 }
233 }
234
235 pub fn get_typical_scan_mode(&mut self) -> Result<u16> {
237 self.get_typical_scan_mode_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
238 }
239
240 pub fn get_typical_scan_mode_with_timeout(&mut self, timeout: Duration) -> Result<u16> {
242 let device_info = self.get_device_info_with_timeout(timeout)?;
243
244 if device_info.firmware_version < RPLIDAR_GET_LIDAR_CONF_START_VERSION {
245 return Ok(if device_info.model >= 0x20u8 {
246 1u16
247 } else {
248 0u16
249 });
250 }
251
252 let scan_mode_data =
253 self.get_lidar_conf_with_timeout(RPLIDAR_CONF_SCAN_MODE_TYPICAL, timeout)?;
254
255 parse_resp_data!(scan_mode_data, u16)
256 }
257
258 fn get_scan_mode_us_per_sample_with_timeout(
260 &mut self,
261 scan_mode: u16,
262 timeout: Duration,
263 ) -> Result<f32> {
264 let mut param = [0; 2];
265 LittleEndian::write_u16(&mut param, scan_mode);
266 let us_per_sample_data = self.get_lidar_conf_with_param_and_timeout(
267 RPLIDAR_CONF_SCAN_MODE_US_PER_SAMPLE,
268 ¶m,
269 timeout,
270 )?;
271 let us_per_sample = (parse_resp_data!(us_per_sample_data, u32)? as f32) / 256f32;
272 Ok(us_per_sample)
273 }
274
275 fn get_scan_mode_max_distance_with_timeout(
277 &mut self,
278 scan_mode: u16,
279 timeout: Duration,
280 ) -> Result<f32> {
281 let mut param = [0; 2];
282 LittleEndian::write_u16(&mut param, scan_mode);
283 let max_distance_data = self.get_lidar_conf_with_param_and_timeout(
284 RPLIDAR_CONF_SCAN_MODE_MAX_DISTANCE,
285 ¶m,
286 timeout,
287 )?;
288 let max_distance = (parse_resp_data!(max_distance_data, u32)? as f32) / 256f32;
289 Ok(max_distance)
290 }
291
292 fn get_scan_mode_ans_type_with_timeout(
294 &mut self,
295 scan_mode: u16,
296 timeout: Duration,
297 ) -> Result<u8> {
298 let mut param = [0; 2];
299 LittleEndian::write_u16(&mut param, scan_mode);
300 let ans_type_data = self.get_lidar_conf_with_param_and_timeout(
301 RPLIDAR_CONF_SCAN_MODE_ANS_TYPE,
302 ¶m,
303 timeout,
304 )?;
305 parse_resp_data!(ans_type_data, u8)
306 }
307
308 fn get_scan_mode_name_with_timeout(
310 &mut self,
311 scan_mode: u16,
312 timeout: Duration,
313 ) -> Result<String> {
314 let mut param = [0; 2];
315 LittleEndian::write_u16(&mut param, scan_mode);
316 let ans_type_data = self.get_lidar_conf_with_param_and_timeout(
317 RPLIDAR_CONF_SCAN_MODE_NAME,
318 ¶m,
319 timeout,
320 )?;
321
322 if let Ok(name) = std::str::from_utf8(&ans_type_data) {
323 Ok(name.to_owned().trim_matches('\0').to_owned())
324 } else {
325 Err(RposError::ProtocolError {
326 description: "invalid scan mode name".to_owned(),
327 }
328 .into())
329 }
330 }
331
332 fn get_scan_mode_count_with_timeout(&mut self, timeout: Duration) -> Result<u16> {
334 let scan_mode_count_data =
335 self.get_lidar_conf_with_timeout(RPLIDAR_CONF_SCAN_MODE_COUNT, timeout)?;
336 parse_resp_data!(scan_mode_count_data, u16)
337 }
338
339 fn get_scan_mode_with_timeout(
341 &mut self,
342 scan_mode: u16,
343 timeout: Duration,
344 ) -> Result<ScanMode> {
345 Ok(ScanMode {
346 id: scan_mode,
347 us_per_sample: self.get_scan_mode_us_per_sample_with_timeout(scan_mode, timeout)?,
348 max_distance: self.get_scan_mode_max_distance_with_timeout(scan_mode, timeout)?,
349 ans_type: self.get_scan_mode_ans_type_with_timeout(scan_mode, timeout)?,
350 name: self.get_scan_mode_name_with_timeout(scan_mode, timeout)?,
351 })
352 }
353
354 pub fn get_all_supported_scan_modes(&mut self) -> Result<Vec<ScanMode>> {
356 self.get_all_supported_scan_modes_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
357 }
358
359 pub fn get_all_supported_scan_modes_with_timeout(
361 &mut self,
362 timeout: Duration,
363 ) -> Result<Vec<ScanMode>> {
364 let device_info = self.get_device_info_with_timeout(timeout)?;
365
366 if device_info.firmware_version < RPLIDAR_GET_LIDAR_CONF_START_VERSION {
367 let mut output: Vec<ScanMode> = Vec::with_capacity(2);
368
369 output.push(ScanMode {
370 id: 0u16,
371 us_per_sample: 1000000f32 / 2000f32,
372 max_distance: 8000f32,
373 ans_type: RPLIDAR_ANS_TYPE_MEASUREMENT,
374 name: "Standard".to_owned(),
375 });
376
377 if device_info.model >= 0x20u8 {
378 output.push(ScanMode {
379 id: 1u16,
380 us_per_sample: 1000000f32 / 4000f32,
381 max_distance: 16000f32,
382 ans_type: RPLIDAR_ANS_TYPE_MEASUREMENT_CAPSULED,
383 name: "Express".to_owned(),
384 });
385 }
386
387 Ok(output)
388 } else {
389 let scan_mode_count = self.get_scan_mode_count_with_timeout(timeout)?;
390 let mut output: Vec<ScanMode> = Vec::with_capacity(scan_mode_count as usize);
391
392 for i in 0..scan_mode_count {
393 output.push(self.get_scan_mode_with_timeout(i, timeout)?);
394 }
395
396 Ok(output)
397 }
398 }
399
400 pub fn start_scan(&mut self) -> Result<ScanMode> {
402 self.start_scan_with_options(&ScanOptions::default())
403 }
404
405 pub fn start_scan_with_timeout(&mut self, timeout: Duration) -> Result<ScanMode> {
407 self.start_scan_with_options_and_timeout(&ScanOptions::default(), timeout)
408 }
409
410 pub fn start_scan_with_options(&mut self, options: &ScanOptions) -> Result<ScanMode> {
412 self.start_scan_with_options_and_timeout(options, RPLIDAR_DEFAULT_TIMEOUT)
413 }
414
415 pub fn start_scan_with_options_and_timeout(
417 &mut self,
418 options: &ScanOptions,
419 timeout: Duration,
420 ) -> Result<ScanMode> {
421 self.cached_prev_capsule = CachedPrevCapsule::None;
422
423 let scan_mode = match options.scan_mode {
424 Some(mode) => mode,
425 None => self.get_typical_scan_mode_with_timeout(timeout)?,
426 };
427
428 let scan_mode_info = self.get_scan_mode_with_timeout(scan_mode, timeout)?;
429
430 match scan_mode {
431 0 => self.legacy_start_scan(options.force_scan)?,
432 _ => {
433 let payload = RplidarPayloadExpressScan {
434 work_mode: scan_mode as u8,
435 work_flags: options.options as u16,
436 param: 0,
437 };
438 self.start_express_scan(&payload)?;
439 }
440 }
441
442 Ok(scan_mode_info)
443 }
444
445 fn legacy_start_scan(&mut self, force_scan: bool) -> Result<()> {
447 self.channel.write(&Message::new(if force_scan {
448 RPLIDAR_CMD_FORCE_SCAN
449 } else {
450 RPLIDAR_CMD_SCAN
451 }))?;
452 Ok(())
453 }
454
455 fn start_express_scan(&mut self, options: &RplidarPayloadExpressScan) -> Result<()> {
457 let data = unsafe {
458 transmute_copy::<
459 RplidarPayloadExpressScan,
460 [u8; std::mem::size_of::<RplidarPayloadExpressScan>()],
461 >(options)
462 };
463 self.channel
464 .write(&Message::with_data(RPLIDAR_CMD_EXPRESS_SCAN, &data))?;
465 Ok(())
466 }
467
468 fn on_measurement_node_hq(&mut self, node: RplidarResponseMeasurementNodeHq) {
470 self.cached_measurement_nodes
471 .push_back(ScanPoint::from(node));
472 }
473
474 fn on_measurement_node(&mut self, node: RplidarResponseMeasurementNode) {
476 self.on_measurement_node_hq(RplidarResponseMeasurementNodeHq {
477 angle_z_q14: ((((node.angle_q6_checkbit as u32)
478 >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT as u32)
479 << 8)
480 / 90) as u16,
481 dist_mm_q2: node.distance_q2 as u32,
482 flag: node.sync_quality & RPLIDAR_RESP_MEASUREMENT_SYNCBIT,
483 quality: (node.sync_quality >> RPLIDAR_RESP_MEASUREMENT_QUALITY_SHIFT as u8)
484 << RPLIDAR_RESP_MEASUREMENT_QUALITY_SHIFT as u8,
485 });
486 }
487
488 fn on_measurement_capsuled_msg(&mut self, msg: &Message) -> Result<()> {
490 check_sync_and_checksum(msg)?;
491 self.on_measurement_capsuled(parse_resp!(msg, RplidarResponseCapsuleMeasurementNodes)?);
492 Ok(())
493 }
494
495 fn on_measurement_capsuled(&mut self, nodes: RplidarResponseCapsuleMeasurementNodes) {
497 let (parsed_nodes, new_cached_capsuled) = parse_capsuled(&self.cached_prev_capsule, nodes);
498 self.cached_prev_capsule = new_cached_capsuled;
499
500 for node in parsed_nodes {
501 self.on_measurement_node_hq(node);
502 }
503 }
504
505 fn on_measurement_ultra_capsuled_msg(&mut self, msg: &Message) -> Result<()> {
507 check_sync_and_checksum(msg)?;
508 self.on_measurement_ultra_capsuled(parse_resp!(
509 msg,
510 RplidarResponseUltraCapsuleMeasurementNodes
511 )?);
512 Ok(())
513 }
514
515 fn on_measurement_ultra_capsuled(
517 &mut self,
518 nodes: RplidarResponseUltraCapsuleMeasurementNodes,
519 ) {
520 let (parsed_nodes, new_cached_capsuled) =
521 parse_ultra_capsuled(&self.cached_prev_capsule, nodes);
522 self.cached_prev_capsule = new_cached_capsuled;
523
524 for node in parsed_nodes {
525 self.on_measurement_node_hq(node);
526 }
527 }
528
529 fn on_measurement_hq_capsuled_msg(&mut self, msg: &Message) -> Result<()> {
531 check_sync_and_checksum_hq(msg)?;
532 self.on_measurement_hq_capsuled(parse_resp!(
533 msg,
534 RplidarResponseHqCapsuledMeasurementNodes
535 )?);
536 Ok(())
537 }
538
539 fn on_measurement_hq_capsuled(&mut self, nodes: RplidarResponseHqCapsuledMeasurementNodes) {
541 for node in nodes.nodes.iter() {
542 self.on_measurement_node_hq(*node);
543 }
544 }
545
546 fn wait_scan_data_with_timeout(&mut self, timeout: Duration) -> Result<()> {
548 let opt_msg = self.channel.read_until(timeout)?;
549
550 if let Some(msg) = opt_msg {
551 match msg.cmd {
552 RPLIDAR_ANS_TYPE_MEASUREMENT => {
553 self.on_measurement_node(parse_resp!(msg, RplidarResponseMeasurementNode)?)
554 }
555 RPLIDAR_ANS_TYPE_MEASUREMENT_CAPSULED => self.on_measurement_capsuled_msg(&msg)?,
556 RPLIDAR_ANS_TYPE_MEASUREMENT_CAPSULED_ULTRA => {
557 self.on_measurement_ultra_capsuled_msg(&msg)?
558 }
559 RPLIDAR_ANS_TYPE_MEASUREMENT_HQ => self.on_measurement_hq_capsuled_msg(&msg)?,
560 _ => {
561 return Err(RposError::ProtocolError {
562 description: "unexpected response".to_owned(),
563 }
564 .into());
565 }
566 }
567 Ok(())
568 } else {
569 Ok(())
570 }
571 }
572
573 pub fn grab_scan_point(&mut self) -> Result<ScanPoint> {
575 self.grab_scan_point_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
576 }
577
578 pub fn grab_scan_point_with_timeout(&mut self, timeout: Duration) -> Result<ScanPoint> {
580 if self.cached_measurement_nodes.is_empty() {
581 self.wait_scan_data_with_timeout(timeout)?;
582
583 if self.cached_measurement_nodes.is_empty() {
584 return Err(RposError::OperationTimeout.into());
585 }
586 }
587
588 Ok(self.cached_measurement_nodes.pop_front().unwrap())
589 }
590
591 pub fn grab_scan(&mut self) -> Result<Vec<ScanPoint>> {
593 self.grab_scan_with_timeout(RPLIDAR_DEFAULT_TIMEOUT * 5)
594 }
595
596 pub fn grab_scan_with_timeout(&mut self, timeout: Duration) -> Result<Vec<ScanPoint>> {
598 let deadline = Instant::now() + timeout;
599 let mut end = 1;
600
601 'outer_loop: loop {
602 if Instant::now() > deadline {
603 return Err(RposError::OperationTimeout.into());
604 }
605
606 if self.cached_measurement_nodes.len() <= end {
607 self.wait_scan_data_with_timeout(std::cmp::min(
608 deadline - Instant::now(),
609 RPLIDAR_DEFAULT_TIMEOUT,
610 ))?;
611 }
612
613 for i in end..self.cached_measurement_nodes.len() {
614 if self.cached_measurement_nodes[i].is_sync() {
615 end = i;
616 break 'outer_loop;
617 }
618 }
619
620 end = self.cached_measurement_nodes.len();
621 }
622
623 let mut out = Vec::<ScanPoint>::with_capacity(end);
624 for _ in 0..end {
625 if let Some(point) = self.cached_measurement_nodes.pop_front() {
626 out.push(point);
627 }
628 }
629
630 Ok(out)
631 }
632
633 pub fn get_device_health(&mut self) -> Result<Health> {
635 self.get_device_health_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
636 }
637
638 pub fn get_device_health_with_timeout(&mut self, timeout: Duration) -> Result<Health> {
640 if let Some(msg) = self
641 .channel
642 .invoke(&Message::new(RPLIDAR_CMD_GET_DEVICE_HEALTH), timeout)?
643 {
644 let resp = handle_resp!(RPLIDAR_ANS_TYPE_DEVHEALTH, msg, RplidarResponseDeviceHealth)?;
645
646 return Ok(match resp.status {
647 RPLIDAR_HEALTH_STATUS_OK => Health::Healthy,
648 RPLIDAR_HEALTH_STATUS_WARNING => Health::Warning(resp.error_code),
649 RPLIDAR_HEALTH_STATUS_ERROR => Health::Error(resp.error_code),
650 _ => Health::Healthy,
651 });
652 }
653
654 Err(RposError::OperationTimeout.into())
655 }
656
657 pub fn check_motor_ctrl_support(&mut self) -> Result<bool> {
659 self.check_motor_ctrl_support_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
660 }
661
662 pub fn check_motor_ctrl_support_with_timeout(&mut self, timeout: Duration) -> Result<bool> {
664 let mut data = [0u8; 4];
665 LittleEndian::write_u32(&mut data, 0u32);
666
667 let resp_msg = self.channel.invoke(
668 &Message::with_data(RPLIDAR_CMD_GET_ACC_BOARD_FLAG, &data),
669 timeout,
670 )?;
671
672 if let Some(msg) = resp_msg {
673 let support_flag = handle_resp!(RPLIDAR_ANS_TYPE_ACC_BOARD_FLAG, msg, u32)?;
674
675 Ok(
676 (support_flag & RPLIDAR_RESP_ACC_BOARD_FLAG_MOTOR_CTRL_SUPPORT_MASK)
677 == RPLIDAR_RESP_ACC_BOARD_FLAG_MOTOR_CTRL_SUPPORT_MASK,
678 )
679 } else {
680 Err(RposError::OperationTimeout.into())
681 }
682 }
683}
684
685fn check_sync_and_checksum(msg: &Message) -> Result<()> {
686 if msg.data.len() < 2 {
687 return Err(RposError::ProtocolError {
688 description: "data too short".to_owned(),
689 }
690 .into());
691 }
692
693 if (msg.data[0] >> 4) != RPLIDAR_RESP_MEASUREMENT_EXP_SYNC_1 {
694 return Err(RposError::ProtocolError {
695 description: "miss sync 1".to_owned(),
696 }
697 .into());
698 }
699
700 if (msg.data[1] >> 4) != RPLIDAR_RESP_MEASUREMENT_EXP_SYNC_2 {
701 return Err(RposError::ProtocolError {
702 description: "miss sync 2".to_owned(),
703 }
704 .into());
705 }
706
707 let recv_checksum = (msg.data[0] & 0xf) | (msg.data[1] << 4);
708 let mut checksum = Checksum::new();
709 checksum.push_slice(&msg.data[2..]);
710
711 if checksum.checksum() != recv_checksum {
712 Err(RposError::ProtocolError {
713 description: "checksum mismatch".to_owned(),
714 }
715 .into())
716 } else {
717 Ok(())
718 }
719}
720
721fn check_sync_and_checksum_hq(msg: &Message) -> Result<()> {
722 if msg.data.len() != std::mem::size_of::<RplidarResponseHqCapsuledMeasurementNodes>() {
723 return Err(RposError::ProtocolError {
724 description: "data length mismatch".to_owned(),
725 }
726 .into());
727 }
728
729 if msg.data[0] != RPLIDAR_RESP_MEASUREMENT_HQ_SYNC {
730 return Err(RposError::ProtocolError {
731 description: "sync mismatch".to_owned(),
732 }
733 .into());
734 }
735
736 let checksum = crc32::checksum_ieee(&msg.data[0..msg.data.len() - 4]);
737 let recv_checksum = LittleEndian::read_u32(&msg.data[msg.data.len() - 4..msg.data.len()]);
738
739 if checksum != recv_checksum {
740 Err(RposError::ProtocolError {
741 description: "checksum mismatch".to_owned(),
742 }
743 .into())
744 } else {
745 Ok(())
746 }
747}