Skip to main content

drs_0x01/
reader.rs

1#![allow(dead_code)]
2#![allow(unused_imports)]
3
4use arrayvec::ArrayVec;
5
6use addr::EEPReadData;
7use addr::RamReadData;
8use addr::WritableEEPAddr;
9use addr::WritableRamAddr;
10
11/// The size of the internal buffer of `ACKReader` where `ACKPacket` are stored when parsing data.
12pub const TRAME_READER_INTERNAL_BUFFER_SIZE: usize = 64;
13
14/// An `ACKPacket` is a message sent by the servomotor and received by an `AckReader`.
15#[derive(Debug, PartialEq, Eq)]
16pub struct ACKPacket {
17    /// The ID of the servomotor who sent this packet
18    pub pid: u8,
19    /// The command of the packet
20    pub cmd: Command,
21    /// Status Error register content
22    pub error: StatusError,
23    /// Status Error register detail
24    pub detail: StatusDetail,
25}
26
27impl From<RawACKPacket> for ACKPacket {
28    fn from(packet: RawACKPacket) -> ACKPacket {
29        ACKPacket {
30            pid: packet.pid,
31            cmd: packet.cmd,
32            error: packet.error,
33            detail: packet.detail,
34        }
35    }
36}
37
38#[derive(Debug, PartialEq, Eq)]
39struct RawACKPacket {
40    /// The size of the packet
41    pub psize: u8,
42    /// The ID of the servomotor who sent this packet
43    pub pid: u8,
44    /// The command of the packet
45    pub cmd: Command,
46    /// The checksum1 of the packet
47    pub chk1: u8,
48    /// The checksum2 of the packet
49    pub chk2: u8,
50    /// Status Error register content
51    pub error: StatusError,
52    /// Status Error register detail
53    pub detail: StatusDetail,
54}
55
56impl RawACKPacket {
57    pub fn is_valid(&self) -> bool {
58        use addr::ReadableEEPAddr;
59        use addr::ReadableRamAddr;
60        use reader::Command::*;
61
62        // Construction de chk1
63        let mut chk1 = self.psize;
64        chk1 ^= self.pid;
65        chk1 ^= u8::from(self.cmd);
66
67        match self.cmd {
68            Command::EEPRead { data } => {
69                let a: u8 = data.addr.into();
70                chk1 ^= a;
71                chk1 ^= data.data_len;
72                for i in &data.data[0..data.data_len as usize] {
73                    chk1 ^= i;
74                }
75            }
76            Command::RamRead { data } => {
77                let a: u8 = data.addr.into();
78                chk1 ^= a;
79                chk1 ^= data.data_len;
80                for i in &data.data[0..data.data_len as usize] {
81                    chk1 ^= i;
82                }
83            }
84            _ => (),
85        };
86        chk1 &= 0xFE;
87        self.chk1 == chk1 && self.chk2 == !chk1 & 0xFE
88    }
89}
90
91impl Into<Command> for RawACKPacket {
92    fn into(self) -> Command {
93        self.cmd
94    }
95}
96
97/// The kind of command the servomotor is answering to.
98#[derive(Copy, Clone, Debug, PartialEq, Eq)]
99pub enum Command {
100    /// EEPWrite command
101    EEPWrite,
102    /// EEPRead command
103    EEPRead {
104        /// The data read
105        data: EEPReadData,
106    },
107    /// RamWrite command
108    RamWrite,
109    /// RamRead command
110    RamRead {
111        /// The data read
112        data: RamReadData,
113    },
114    /// IJog command
115    IJog,
116    /// Sjog command
117    SJog,
118    /// Stat command
119    Stat,
120    /// Rollback command
121    Rollback,
122    /// Reboot command
123    Reboot,
124}
125
126#[derive(Copy, Clone, Debug, PartialEq)]
127enum InternalCommand {
128    EEPWrite,
129    EEPRead,
130    RamWrite,
131    RamRead,
132    IJog,
133    SJog,
134    Stat,
135    Rollback,
136    Reboot,
137}
138
139impl InternalCommand {
140    fn inject_payload(self, payload: AssociatedData) -> Command {
141        use self::Command::*;
142        match (self, payload) {
143            (InternalCommand::EEPWrite, AssociatedData::Nothing) => EEPWrite,
144            (InternalCommand::RamWrite, AssociatedData::Nothing) => RamWrite,
145            (InternalCommand::IJog, AssociatedData::Nothing) => IJog,
146            (InternalCommand::SJog, AssociatedData::Nothing) => SJog,
147            (InternalCommand::Stat, AssociatedData::Nothing) => Stat,
148            (InternalCommand::Rollback, AssociatedData::Nothing) => Rollback,
149            (InternalCommand::Reboot, AssociatedData::Nothing) => Reboot,
150            (InternalCommand::EEPRead, AssociatedData::EEP(data)) => EEPRead { data },
151            (InternalCommand::RamRead, AssociatedData::Ram(data)) => RamRead { data },
152            _ => unreachable!(),
153        }
154    }
155}
156
157impl From<Command> for u8 {
158    fn from(cmd: Command) -> Self {
159        use reader::Command::*;
160        match cmd {
161            EEPWrite => 0x41,
162            EEPRead { .. } => 0x42,
163            RamWrite => 0x43,
164            RamRead { .. } => 0x44,
165            IJog => 0x45,
166            SJog => 0x46,
167            Stat => 0x47,
168            Rollback => 0x48,
169            Reboot => 0x49,
170        }
171    }
172}
173
174/// The values of the status error register
175#[allow(missing_docs)]
176#[derive(Copy, Clone, Debug, PartialEq, Eq)]
177pub enum StatusError {
178    ExceedInputVoltageLimit,
179    ExceedAllowedPOTLimit,
180    ExceedTemperatureLimit,
181    InvalidPacket,
182    OverloadDetected,
183    DriverFaultDetected,
184    EEPREGDistorded,
185    NoError,
186}
187
188/// The values of the status detail error register
189#[allow(missing_docs)]
190#[derive(Copy, Clone, Debug, PartialEq, Eq)]
191pub enum StatusDetail {
192    MovingFlag,
193    ImpositionFlag,
194    ChecksumError,
195    UnknownCommand,
196    ExceedREGRange,
197    GarbageDetected,
198    MotorOnFlag,
199    NoDetail,
200}
201
202#[derive(Copy, Clone, Debug)]
203enum AssociatedData {
204    EEP(EEPReadData),
205    Ram(RamReadData),
206    Nothing,
207}
208
209/// This is a state machine that take in some bytes and outputs `[AckPacket]`.
210/// Please note that this structure will allocate roughly 1 kiB of stack.
211pub struct ACKReader {
212    state: ReaderState,
213    buffer: ArrayVec<[ACKPacket; TRAME_READER_INTERNAL_BUFFER_SIZE]>,
214}
215
216impl Default for ACKReader {
217    fn default() -> ACKReader {
218        ACKReader {
219            state: ReaderState::H1,
220            buffer: ArrayVec::new(),
221        }
222    }
223}
224
225// Structure permettant de gérer la machine à états
226#[derive(Debug, Clone, Copy)]
227enum ReaderState {
228    H1,
229    H2,
230    Psize,
231    Pid {
232        size: u8,
233    },
234    Cmd {
235        size: u8,
236        pid: u8,
237    },
238    Checksum1 {
239        size: u8,
240        pid: u8,
241        cmd: InternalCommand,
242    },
243    Checksum2 {
244        size: u8,
245        pid: u8,
246        cmd: InternalCommand,
247        chk1: u8,
248    },
249    DataAddr {
250        size: u8,
251        pid: u8,
252        cmd: InternalCommand,
253        chk1: u8,
254        chk2: u8,
255    },
256    DataLenEEP {
257        size: u8,
258        pid: u8,
259        cmd: InternalCommand,
260        chk1: u8,
261        chk2: u8,
262        data: EEPReadData,
263    },
264    Data1EEP {
265        size: u8,
266        pid: u8,
267        cmd: InternalCommand,
268        chk1: u8,
269        chk2: u8,
270        data: EEPReadData,
271    },
272    Data2EEP {
273        size: u8,
274        pid: u8,
275        cmd: InternalCommand,
276        chk1: u8,
277        chk2: u8,
278        data: EEPReadData,
279    },
280    DataLenRAM {
281        size: u8,
282        pid: u8,
283        cmd: InternalCommand,
284        chk1: u8,
285        chk2: u8,
286        data: RamReadData,
287    },
288    Data1RAM {
289        size: u8,
290        pid: u8,
291        cmd: InternalCommand,
292        chk1: u8,
293        chk2: u8,
294        data: RamReadData,
295    },
296    Data2RAM {
297        size: u8,
298        pid: u8,
299        cmd: InternalCommand,
300        chk1: u8,
301        chk2: u8,
302        data: RamReadData,
303    },
304    Error {
305        size: u8,
306        pid: u8,
307        cmd: InternalCommand,
308        chk1: u8,
309        chk2: u8,
310        payload: AssociatedData,
311    },
312    Detail {
313        size: u8,
314        pid: u8,
315        cmd: InternalCommand,
316        chk1: u8,
317        chk2: u8,
318        payload: AssociatedData,
319        status_error: StatusError,
320    },
321}
322
323impl ReaderState {
324    fn step(&mut self, byte: u8) -> Option<RawACKPacket> {
325        use addr::EEPReadData;
326        use addr::RamReadData;
327        use addr::ReadableEEPAddr;
328        use addr::ReadableRamAddr;
329        use addr::WritableEEPAddr::*;
330        use addr::WritableRamAddr::*;
331        use reader::AssociatedData::*;
332        use reader::InternalCommand::*;
333        use reader::ReaderState::*;
334        use reader::StatusDetail::*;
335        use reader::StatusError::*;
336        use try_from::TryFrom;
337
338        let mut result: Option<RawACKPacket> = None;
339        match *self {
340            H1 => *self = H2,
341            H2 => *self = Psize,
342            Psize => *self = Pid { size: byte },
343            Pid { size } => *self = Cmd { size, pid: byte },
344            Cmd { size, pid } => {
345                let mut command: Option<InternalCommand> = None;
346                match byte {
347                    0x41 => command = Some(EEPWrite),
348                    0x42 => command = Some(EEPRead),
349                    0x43 => command = Some(RamWrite),
350                    0x44 => command = Some(RamRead),
351                    0x45 => command = Some(IJog),
352                    0x46 => command = Some(SJog),
353                    0x47 => command = Some(Stat),
354                    0x48 => command = Some(Rollback),
355                    0x49 => command = Some(Reboot),
356                    _ => *self = H1,
357                }
358                if let Some(command) = command {
359                    *self = Checksum1 {
360                        size,
361                        pid,
362                        cmd: command,
363                    }
364                }
365            }
366            Checksum1 { size, pid, cmd } => {
367                *self = Checksum2 {
368                    size,
369                    pid,
370                    cmd,
371                    chk1: byte,
372                }
373            }
374            Checksum2 {
375                size,
376                pid,
377                cmd,
378                chk1,
379            }
380                if (cmd == EEPRead || cmd == RamRead) =>
381            {
382                *self = DataAddr {
383                    size,
384                    pid,
385                    cmd,
386                    chk1,
387                    chk2: byte,
388                }
389            }
390            Checksum2 {
391                size,
392                pid,
393                cmd,
394                chk1,
395            } => {
396                *self = Error {
397                    size,
398                    pid,
399                    cmd,
400                    chk1,
401                    chk2: byte,
402                    payload: Nothing,
403                }
404            }
405            DataAddr {
406                size,
407                pid,
408                cmd,
409                chk1,
410                chk2,
411            } => match cmd {
412                EEPRead => {
413                    *self = match TryFrom::try_from(byte) {
414                        Ok(data_addr) => DataLenEEP {
415                            size,
416                            pid,
417                            cmd,
418                            chk1,
419                            chk2,
420                            data: EEPReadData {
421                                addr: data_addr,
422                                data_len: 0,
423                                data: [0, 0],
424                            },
425                        },
426                        Err(_) => H1,
427                    }
428                }
429                RamRead => {
430                    *self = match TryFrom::try_from(byte) {
431                        Ok(data_addr) => DataLenRAM {
432                            size,
433                            pid,
434                            cmd,
435                            chk1,
436                            chk2,
437                            data: RamReadData {
438                                addr: data_addr,
439                                data_len: 0,
440                                data: [0, 0],
441                            },
442                        },
443                        Err(_) => H1,
444                    }
445                }
446                _ => unreachable!(),
447            },
448            DataLenEEP {
449                size,
450                pid,
451                cmd,
452                chk1,
453                chk2,
454                data,
455            } => {
456                let new_data = EEPReadData {
457                    addr: data.addr,
458                    data_len: byte,
459                    data: [0, 0],
460                };
461                *self = Data1EEP {
462                    size,
463                    pid,
464                    cmd,
465                    chk1,
466                    chk2,
467                    data: new_data,
468                };
469            }
470            DataLenRAM {
471                size,
472                pid,
473                cmd,
474                chk1,
475                chk2,
476                data,
477            } => {
478                let new_data = RamReadData {
479                    addr: data.addr,
480                    data_len: byte,
481                    data: [0, 0],
482                };
483                *self = Data1RAM {
484                    size,
485                    pid,
486                    cmd,
487                    chk1,
488                    chk2,
489                    data: new_data,
490                }
491            }
492            Data1EEP {
493                size,
494                pid,
495                chk1,
496                chk2,
497                data,
498                ..
499            } => {
500                let new_data = EEPReadData {
501                    addr: data.addr,
502                    data_len: data.data_len,
503                    data: [byte, 0],
504                };
505                if data.data_len == 2 {
506                    *self = Data2EEP {
507                        size,
508                        pid,
509                        cmd: InternalCommand::EEPRead,
510                        chk1,
511                        chk2,
512                        data: new_data,
513                    }
514                } else {
515                    *self = Error {
516                        size,
517                        pid,
518                        cmd: InternalCommand::EEPRead,
519                        chk1,
520                        chk2,
521                        payload: AssociatedData::EEP(new_data),
522                    }
523                }
524            }
525            Data2EEP {
526                size,
527                pid,
528                cmd,
529                chk1,
530                chk2,
531                data,
532            } => {
533                let new_data = EEPReadData {
534                    addr: data.addr,
535                    data_len: data.data_len,
536                    data: [data.data[0], byte],
537                };
538                *self = Error {
539                    size,
540                    pid,
541                    cmd,
542                    chk1,
543                    chk2,
544                    payload: AssociatedData::EEP(new_data),
545                }
546            }
547            Data1RAM {
548                size,
549                pid,
550                chk1,
551                chk2,
552                data,
553                ..
554            } => {
555                let new_data = RamReadData {
556                    addr: data.addr,
557                    data_len: data.data_len,
558                    data: [byte, 0],
559                };
560                if data.data_len == 2 {
561                    *self = Data2RAM {
562                        size,
563                        pid,
564                        cmd: InternalCommand::RamRead,
565                        chk1,
566                        chk2,
567                        data: new_data,
568                    }
569                } else {
570                    *self = Error {
571                        size,
572                        pid,
573                        cmd: InternalCommand::RamRead,
574                        chk1,
575                        chk2,
576                        payload: AssociatedData::Ram(new_data),
577                    }
578                }
579            }
580            Data2RAM {
581                size,
582                pid,
583                cmd,
584                chk1,
585                chk2,
586                data,
587            } => {
588                let new_data = RamReadData {
589                    addr: data.addr,
590                    data_len: data.data_len,
591                    data: [data.data[0], byte],
592                };
593                *self = Error {
594                    size,
595                    pid,
596                    cmd,
597                    chk1,
598                    chk2,
599                    payload: AssociatedData::Ram(new_data),
600                }
601            }
602            Error {
603                size,
604                pid,
605                cmd,
606                chk1,
607                chk2,
608                payload,
609            } => {
610                let status_error = match byte {
611                    0x00 => Some(NoError),
612                    0x01 => Some(ExceedInputVoltageLimit),
613                    0x02 => Some(ExceedAllowedPOTLimit),
614                    0x04 => Some(ExceedTemperatureLimit),
615                    0x08 => Some(InvalidPacket),
616                    0x10 => Some(OverloadDetected),
617                    0x20 => Some(DriverFaultDetected),
618                    0x40 => Some(EEPREGDistorded),
619                    _ => None,
620                };
621                if let Some(valid_error) = status_error {
622                    *self = Detail {
623                        size,
624                        pid,
625                        cmd,
626                        chk1,
627                        chk2,
628                        payload,
629                        status_error: valid_error,
630                    };
631                } else {
632                    *self = H1;
633                }
634            }
635            Detail {
636                size,
637                pid,
638                cmd,
639                chk1,
640                chk2,
641                payload,
642                status_error,
643            } => {
644                let mut status_detail = None;
645                match byte {
646                    0x00 => status_detail = Some(NoDetail),
647                    0x01 => status_detail = Some(MovingFlag),
648                    0x02 => status_detail = Some(ImpositionFlag),
649                    0x04 => status_detail = Some(ChecksumError),
650                    0x08 => status_detail = Some(UnknownCommand),
651                    0x10 => status_detail = Some(ExceedREGRange),
652                    0x20 => status_detail = Some(GarbageDetected),
653                    0x40 => status_detail = Some(MotorOnFlag),
654                    _ => (),
655                };
656                if let Some(status_detail) = status_detail {
657                    result = self.make_packet(
658                        size,
659                        pid,
660                        cmd,
661                        chk1,
662                        chk2,
663                        payload,
664                        status_error,
665                        status_detail,
666                    );
667                }
668                *self = H1;
669            }
670        };
671        result
672    }
673
674    fn make_packet(
675        &mut self,
676        size: u8,
677        pid: u8,
678        cmd: InternalCommand,
679        chk1: u8,
680        chk2: u8,
681        payload: AssociatedData,
682        status_error: StatusError,
683        status_detail: StatusDetail,
684    ) -> Option<RawACKPacket> {
685        let cmd = cmd.inject_payload(payload);
686        let packet = RawACKPacket {
687            psize: size,
688            pid,
689            cmd,
690            chk1,
691            chk2,
692            error: status_error,
693            detail: status_detail,
694        };
695        if packet.is_valid() {
696            Some(packet)
697        } else {
698            None
699        }
700    }
701}
702
703impl ACKReader {
704    /// Creates a new state machine to read incoming Herkulex messages
705    pub fn new() -> ACKReader {
706        ACKReader {
707            state: ReaderState::H1,
708            buffer: ArrayVec::new(),
709        }
710    }
711
712    /// Return the oldest [ACKPacket] read
713    pub fn pop_ack_packet(&mut self) -> Option<ACKPacket> {
714        self.buffer.pop()
715    }
716
717    /// Get the number of available messages in the internal buffer
718    pub fn available_messages(&mut self) -> usize {
719        self.buffer.len()
720    }
721
722    /// Parse a buffer of bytes, adding sucessfully decoded  messages to the internal buffer
723    pub fn parse(&mut self, buf: &[u8]) {
724        for byte in buf {
725            if let Some(trame) = self.state.step(*byte) {
726                self.buffer.push(ACKPacket::from(trame));
727            }
728        }
729    }
730}
731
732#[cfg(test)]
733mod test {
734    use addr::*;
735    use reader::{
736        ACKPacket, ACKReader, AssociatedData, Command, RawACKPacket, StatusDetail, StatusError,
737    };
738
739    //#[test]
740    fn test_eepread() {
741        let mut reader = ACKReader::new();
742
743        // Test de EEPRead
744        // [H1][H2][psize][pid][cmd][chk1][chk2][data_addr][data_len][data][data][status_error][status_detail]
745        let packet_eepread = [
746            0xFF, 0xFF, 0x0F, 0xFD, 0x42, 0x14, 0xEA, 0x1E, 0x02, 0xB8, 0x01, 0x08, 0x20,
747        ];
748
749        reader.parse(&packet_eepread);
750
751        let data_eepread = EEPReadData {
752            addr: ReadableEEPAddr::PositionKp,
753            data_len: 2,
754            data: [0xB8, 0x01],
755        };
756
757        assert_eq!(
758            reader.pop_ack_packet().unwrap(),
759            ACKPacket {
760                pid: 0xFD,
761                cmd: Command::EEPRead { data: data_eepread },
762                error: StatusError::InvalidPacket,
763                detail: StatusDetail::GarbageDetected,
764            }
765        );
766    }
767
768    //#[test]
769    fn test_ramread() {
770        let mut reader = ACKReader::new();
771
772        // Test de RamRead
773        // [H1][H2][psize][pid][cmd][chk1][chk2][data_addr][data_len][data][status_error][status_detail]
774        let packet_ramread = [
775            0xFF, 0xFF, 0x0C, 0xFD, 0x44, 0xA0, 0x5E, 0x14, 0x01, 0x01, 0x10, 0x40,
776        ];
777
778        reader.parse(&packet_ramread);
779
780        let data_ramread = RamReadData {
781            addr: ReadableRamAddr::MinPosition, // 20 (0x14)
782            data_len: 1,
783            data: [0x01, 0x00],
784        };
785
786        assert_eq!(
787            reader.pop_ack_packet().unwrap(),
788            ACKPacket {
789                pid: 0xFD,
790                cmd: Command::RamRead { data: data_ramread },
791                error: StatusError::OverloadDetected,
792                detail: StatusDetail::MotorOnFlag,
793            }
794        );
795    }
796
797    #[test]
798    fn test_sjog() {
799        let mut reader = ACKReader::new();
800
801        // Test de SJOG
802        // [H1][H2][psize][pid][cmd][chk1][chk2][status_error][status_detail]
803        let packet_sjog = [
804            0xFF, 0xFF, 0x09, 0xFD, 0x46, /*0xF2*/ 0xB2, /*0x0C*/ 0x4C, 0x08, 0x08,
805        ];
806
807        reader.parse(&packet_sjog);
808
809        assert_eq!(
810            reader.pop_ack_packet().unwrap(),
811            ACKPacket {
812                pid: 0xFD,
813                cmd: Command::SJog,
814                error: StatusError::InvalidPacket,
815                detail: StatusDetail::UnknownCommand,
816            }
817        );
818    }
819}