Skip to main content

moteus_protocol/
multiplex.rs

1// Copyright 2026 mjbots Robotic Systems, LLC.  info@mjbots.com
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Multiplex protocol encoding and decoding.
16//!
17//! The moteus multiplex protocol is a register-based protocol that runs over
18//! CAN-FD. It supports reading and writing multiple registers in a single
19//! frame using efficient variable-length encoding.
20
21use crate::frame::CanFdFrame;
22use crate::resolution::Resolution;
23use crate::scaling::{self, saturate_i16, saturate_i32, saturate_i8, Scaling};
24
25/// Write Int8 values
26pub const WRITE_INT8: u8 = 0x00;
27/// Write Int16 values
28pub const WRITE_INT16: u8 = 0x04;
29/// Write Int32 values
30pub const WRITE_INT32: u8 = 0x08;
31/// Write Float values
32pub const WRITE_FLOAT: u8 = 0x0c;
33
34/// Read Int8 values
35pub const READ_INT8: u8 = 0x10;
36/// Read Int16 values
37pub const READ_INT16: u8 = 0x14;
38/// Read Int32 values
39pub const READ_INT32: u8 = 0x18;
40/// Read Float values
41pub const READ_FLOAT: u8 = 0x1c;
42
43/// Reply Int8 values
44pub const REPLY_INT8: u8 = 0x20;
45/// Reply Int16 values
46pub const REPLY_INT16: u8 = 0x24;
47/// Reply Int32 values
48pub const REPLY_INT32: u8 = 0x28;
49/// Reply Float values
50pub const REPLY_FLOAT: u8 = 0x2c;
51
52/// Write error
53pub const WRITE_ERROR: u8 = 0x30;
54/// Read error
55pub const READ_ERROR: u8 = 0x31;
56
57/// Tunneled stream: client to server
58pub const CLIENT_TO_SERVER: u8 = 0x40;
59/// Tunneled stream: server to client
60pub const SERVER_TO_CLIENT: u8 = 0x41;
61/// Tunneled stream: client poll server
62pub const CLIENT_POLL_SERVER: u8 = 0x42;
63
64/// No operation
65pub const NOP: u8 = 0x50;
66
67/// Writer for appending data to a CAN-FD frame.
68pub struct WriteCanData<'a> {
69    data: &'a mut [u8; 64],
70    size: &'a mut u8,
71}
72
73impl<'a> core::fmt::Debug for WriteCanData<'a> {
74    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75        f.debug_struct("WriteCanData")
76            .field("offset", &(*self.size as usize))
77            .field("remaining", &(64 - *self.size as usize))
78            .finish()
79    }
80}
81
82impl<'a> WriteCanData<'a> {
83    /// Creates a new writer from a frame.
84    pub fn new(frame: &'a mut CanFdFrame) -> Self {
85        WriteCanData {
86            data: &mut frame.data,
87            size: &mut frame.size,
88        }
89    }
90
91    /// Creates a writer from raw pointers (for compatibility).
92    pub fn from_parts(data: &'a mut [u8; 64], size: &'a mut u8) -> Self {
93        WriteCanData { data, size }
94    }
95
96    /// Returns the current size of written data.
97    pub fn size(&self) -> u8 {
98        *self.size
99    }
100
101    /// Returns the remaining capacity.
102    pub fn remaining(&self) -> usize {
103        64 - *self.size as usize
104    }
105
106    /// Writes a single byte.
107    pub fn write_u8(&mut self, value: u8) {
108        if (*self.size as usize) < 64 {
109            self.data[*self.size as usize] = value;
110            *self.size += 1;
111        }
112    }
113
114    /// Writes a signed byte.
115    pub fn write_i8(&mut self, value: i8) {
116        self.write_u8(value as u8);
117    }
118
119    /// Writes a little-endian i16.
120    pub fn write_i16(&mut self, value: i16) {
121        let bytes = value.to_le_bytes();
122        self.write_bytes(&bytes);
123    }
124
125    /// Writes a little-endian i32.
126    pub fn write_i32(&mut self, value: i32) {
127        let bytes = value.to_le_bytes();
128        self.write_bytes(&bytes);
129    }
130
131    /// Writes a little-endian f32.
132    pub fn write_f32(&mut self, value: f32) {
133        let bytes = value.to_le_bytes();
134        self.write_bytes(&bytes);
135    }
136
137    /// Writes raw bytes.
138    pub fn write_bytes(&mut self, bytes: &[u8]) {
139        let start = *self.size as usize;
140        let end = start + bytes.len();
141        if end <= 64 {
142            self.data[start..end].copy_from_slice(bytes);
143            *self.size = end as u8;
144        }
145    }
146
147    /// Writes a variable-length unsigned integer (varuint).
148    pub fn write_varuint(&mut self, mut value: u32) {
149        loop {
150            let mut this_byte = (value & 0x7f) as u8;
151            value >>= 7;
152            if value != 0 {
153                this_byte |= 0x80;
154            }
155            self.write_u8(this_byte);
156            if value == 0 {
157                break;
158            }
159        }
160    }
161
162    /// Writes an integer value with the specified resolution.
163    pub fn write_int(&mut self, value: i32, res: Resolution) {
164        match res {
165            Resolution::Int8 => {
166                let clamped = value.clamp(-127, 127) as i8;
167                self.write_i8(clamped);
168            }
169            Resolution::Int16 => {
170                let clamped = value.clamp(-32767, 32767) as i16;
171                self.write_i16(clamped);
172            }
173            Resolution::Int32 => {
174                self.write_i32(value);
175            }
176            Resolution::Float => {
177                self.write_f32(value as f32);
178            }
179            Resolution::Ignore => {}
180        }
181    }
182
183    /// Writes a scaled value with the specified resolution and scaling.
184    pub fn write_mapped(&mut self, value: f32, scaling: &Scaling, res: Resolution) {
185        match res {
186            Resolution::Int8 => {
187                self.write_i8(saturate_i8(value, scaling.int8));
188            }
189            Resolution::Int16 => {
190                self.write_i16(saturate_i16(value, scaling.int16));
191            }
192            Resolution::Int32 => {
193                self.write_i32(saturate_i32(value, scaling.int32));
194            }
195            Resolution::Float => {
196                self.write_f32(value);
197            }
198            Resolution::Ignore => {}
199        }
200    }
201
202    // === Convenience methods for common register types ===
203
204    /// Writes a position value (revolutions).
205    pub fn write_position(&mut self, value: f32, res: Resolution) {
206        self.write_mapped(value, &scaling::POSITION, res);
207    }
208
209    /// Writes a velocity value (revolutions/second).
210    pub fn write_velocity(&mut self, value: f32, res: Resolution) {
211        self.write_mapped(value, &scaling::VELOCITY, res);
212    }
213
214    /// Writes an acceleration value (revolutions/second^2).
215    pub fn write_accel(&mut self, value: f32, res: Resolution) {
216        self.write_mapped(value, &scaling::ACCELERATION, res);
217    }
218
219    /// Writes a torque value (Nm).
220    pub fn write_torque(&mut self, value: f32, res: Resolution) {
221        self.write_mapped(value, &scaling::TORQUE, res);
222    }
223
224    /// Writes a PWM/normalized value (0-1).
225    pub fn write_pwm(&mut self, value: f32, res: Resolution) {
226        self.write_mapped(value, &scaling::PWM, res);
227    }
228
229    /// Writes a voltage value (V).
230    pub fn write_voltage(&mut self, value: f32, res: Resolution) {
231        self.write_mapped(value, &scaling::VOLTAGE, res);
232    }
233
234    /// Writes a temperature value (C).
235    pub fn write_temperature(&mut self, value: f32, res: Resolution) {
236        self.write_mapped(value, &scaling::TEMPERATURE, res);
237    }
238
239    /// Writes a time value (seconds).
240    pub fn write_time(&mut self, value: f32, res: Resolution) {
241        self.write_mapped(value, &scaling::TIME, res);
242    }
243
244    /// Writes a current value (A).
245    pub fn write_current(&mut self, value: f32, res: Resolution) {
246        self.write_mapped(value, &scaling::CURRENT, res);
247    }
248}
249
250/// Combines consecutive register writes of the same resolution for efficiency.
251///
252/// This helper determines how to group registers when encoding them to minimize
253/// the required bytes in the frame. It tracks state and writes framing bytes
254/// when the resolution changes.
255pub struct WriteCombiner<'a> {
256    base_command: u8,
257    start_register: u16,
258    resolutions: &'a [Resolution],
259    current_resolution: Resolution,
260    offset: usize,
261    reply_size: u8,
262}
263
264impl<'a> core::fmt::Debug for WriteCombiner<'a> {
265    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
266        f.debug_struct("WriteCombiner")
267            .field("base_command", &self.base_command)
268            .field("start_register", &self.start_register)
269            .field("offset", &self.offset)
270            .field("current_resolution", &self.current_resolution)
271            .field("reply_size", &self.reply_size)
272            .finish()
273    }
274}
275
276impl<'a> WriteCombiner<'a> {
277    /// Creates a new WriteCombiner.
278    ///
279    /// # Arguments
280    /// * `base_command` - Base command (0x00 for write, 0x10 for read)
281    /// * `start_register` - First register address in the sequence
282    /// * `resolutions` - Resolution for each register in sequence
283    pub fn new(base_command: u8, start_register: u16, resolutions: &'a [Resolution]) -> Self {
284        WriteCombiner {
285            base_command,
286            start_register,
287            resolutions,
288            current_resolution: Resolution::Ignore,
289            offset: 0,
290            reply_size: 0,
291        }
292    }
293
294    /// Returns the expected reply size so far.
295    pub fn reply_size(&self) -> u8 {
296        self.reply_size
297    }
298
299    /// Advances to the next register and writes framing if needed.
300    ///
301    /// Returns `true` if the caller should write the register value.
302    /// Returns `false` if the register should be skipped (Ignore resolution).
303    pub fn maybe_write(&mut self, frame: &mut WriteCanData) -> bool {
304        let this_offset = self.offset;
305        self.offset += 1;
306
307        if this_offset >= self.resolutions.len() {
308            return false;
309        }
310
311        let new_resolution = self.resolutions[this_offset];
312
313        // Same resolution as before - no framing needed
314        if self.current_resolution == new_resolution {
315            return new_resolution != Resolution::Ignore;
316        }
317
318        // Update current resolution
319        self.current_resolution = new_resolution;
320
321        // Ignore means skip this register
322        if new_resolution == Resolution::Ignore {
323            return false;
324        }
325
326        // Count how many consecutive registers have this resolution
327        let mut count = 1i16;
328        for i in (this_offset + 1)..self.resolutions.len() {
329            if self.resolutions[i] == new_resolution {
330                count += 1;
331            } else {
332                break;
333            }
334        }
335
336        // Build the command byte
337        let write_command = self.base_command + new_resolution.type_code();
338
339        let start_size = frame.size();
340
341        if count <= 3 {
342            // Short form: command encodes count in lower 2 bits
343            frame.write_u8(write_command + count as u8);
344        } else {
345            // Long form: count follows command byte
346            frame.write_u8(write_command);
347            frame.write_u8(count as u8);
348        }
349
350        // Write register address
351        frame.write_varuint((self.start_register + this_offset as u16) as u32);
352
353        let end_size = frame.size();
354
355        // Update expected reply size
356        self.reply_size += end_size - start_size;
357        self.reply_size += (count as u8) * (new_resolution.size() as u8);
358
359        true
360    }
361}
362
363/// A decoded register value from the multiplex protocol.
364#[non_exhaustive]
365#[derive(Debug, Clone, Copy)]
366pub enum Value {
367    /// 8-bit signed integer
368    Int8(i8),
369    /// 16-bit signed integer
370    Int16(i16),
371    /// 32-bit signed integer
372    Int32(i32),
373    /// 32-bit IEEE 754 float
374    Float(f32),
375}
376
377impl Value {
378    /// Returns the raw integer value.
379    ///
380    /// Float values are cast to i32.  This should only be used for
381    /// registers which are intended to hold an integer type, and thus
382    /// can not store a non-finite value.
383    pub fn to_i32(&self) -> i32 {
384        match *self {
385            Value::Int8(v) => v as i32,
386            Value::Int16(v) => v as i32,
387            Value::Int32(v) => v,
388            Value::Float(v) => v as i32,
389        }
390    }
391
392    /// Returns the value as f32, applying NaN mapping and scaling.
393    ///
394    /// Integer minimum values (e.g., -128 for Int8) are mapped to NaN.
395    /// Integer values are multiplied by the appropriate scaling factor.
396    /// Float values are returned directly.
397    ///
398    /// # Examples
399    ///
400    /// ```
401    /// use moteus_protocol::Value;
402    /// use moteus_protocol::scaling;
403    ///
404    /// let raw = Value::Int16(5000);
405    /// let position = raw.to_f32(&scaling::POSITION);
406    /// assert!((position - 0.5).abs() < 0.001); // 5000 * 0.0001 = 0.5 rev
407    /// ```
408    pub fn to_f32(&self, scaling: &Scaling) -> f32 {
409        use crate::scaling::{nanify_i16, nanify_i32, nanify_i8};
410
411        match *self {
412            Value::Int8(v) => nanify_i8(v) * scaling.int8,
413            Value::Int16(v) => nanify_i16(v) * scaling.int16,
414            Value::Int32(v) => nanify_i32(v) * scaling.int32,
415            Value::Float(v) => v,
416        }
417    }
418}
419
420/// The type of a parsed subframe.
421#[non_exhaustive]
422#[derive(Debug, Clone, Copy, PartialEq, Eq)]
423pub enum SubframeType {
424    /// Write register values
425    Write,
426    /// Read register request
427    Read,
428    /// Response to a read request
429    Response,
430    /// Write error
431    WriteError,
432    /// Read error
433    ReadError,
434    /// Tunneled stream: client to server
435    StreamClientToServer,
436    /// Tunneled stream: server to client
437    StreamServerToClient,
438    /// Tunneled stream: client poll server
439    StreamClientPollServer,
440}
441
442/// A single parsed subframe from a multiplex protocol frame.
443#[non_exhaustive]
444#[derive(Debug, Clone)]
445pub enum Subframe<'a> {
446    /// A register read, write, or response subframe.
447    Register {
448        /// The type of operation
449        subframe_type: SubframeType,
450        /// The register address
451        register: u16,
452        /// The resolution of the value
453        resolution: Resolution,
454        /// The decoded value (None for Read requests)
455        value: Option<Value>,
456    },
457    /// An error subframe.
458    Error {
459        /// The type of error (WriteError or ReadError)
460        subframe_type: SubframeType,
461        /// The register that caused the error
462        register: u16,
463        /// The error code
464        error_code: u16,
465    },
466    /// A tunneled stream subframe.
467    Stream {
468        /// The stream direction
469        subframe_type: SubframeType,
470        /// The stream channel number
471        channel: u16,
472        /// The stream data (borrowed from the input)
473        data: &'a [u8],
474    },
475}
476
477/// Iterator over subframes in a multiplex protocol frame.
478///
479/// Handles all opcode families: WRITE (0x00-0x0f), READ (0x10-0x1f),
480/// REPLY (0x20-0x2f), ERROR (0x30-0x31), STREAM (0x40-0x42), NOP (0x50).
481///
482/// See [`parse_frame`] for a convenience wrapper.
483pub struct FrameParser<'a> {
484    data: &'a [u8],
485    offset: usize,
486    remaining: u16,
487    current_register: u16,
488    current_resolution: Resolution,
489    current_type: SubframeType,
490}
491
492impl<'a> core::fmt::Debug for FrameParser<'a> {
493    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
494        f.debug_struct("FrameParser")
495            .field("data_len", &self.data.len())
496            .field("offset", &self.offset)
497            .field("remaining", &self.remaining)
498            .field("current_register", &self.current_register)
499            .field("current_resolution", &self.current_resolution)
500            .field("current_type", &self.current_type)
501            .finish()
502    }
503}
504
505impl<'a> FrameParser<'a> {
506    /// Creates a parser from a byte slice.
507    pub fn new(data: &'a [u8]) -> Self {
508        FrameParser {
509            data,
510            offset: 0,
511            remaining: 0,
512            current_register: 0,
513            current_resolution: Resolution::Ignore,
514            current_type: SubframeType::Response,
515        }
516    }
517
518    /// Creates a parser from a frame.
519    pub fn from_frame(frame: &'a CanFdFrame) -> Self {
520        Self::new(&frame.data[..frame.size as usize])
521    }
522
523    /// Reads a varuint from the current position.
524    fn read_varuint(&mut self) -> u16 {
525        let mut result: u16 = 0;
526        let mut shift = 0;
527
528        for _ in 0..5 {
529            if self.offset >= self.data.len() {
530                return result;
531            }
532
533            let byte = self.data[self.offset];
534            self.offset += 1;
535
536            result |= ((byte & 0x7f) as u16) << shift;
537            shift += 7;
538
539            if byte & 0x80 == 0 {
540                return result;
541            }
542        }
543
544        result
545    }
546
547    /// Reads a value at the current offset with the given resolution.
548    fn read_value(&mut self, res: Resolution) -> Option<Value> {
549        match res {
550            Resolution::Int8 => {
551                if self.offset < self.data.len() {
552                    let v = self.data[self.offset] as i8;
553                    self.offset += 1;
554                    Some(Value::Int8(v))
555                } else {
556                    None
557                }
558            }
559            Resolution::Int16 => {
560                if self.offset + 2 <= self.data.len() {
561                    let bytes = [self.data[self.offset], self.data[self.offset + 1]];
562                    self.offset += 2;
563                    Some(Value::Int16(i16::from_le_bytes(bytes)))
564                } else {
565                    None
566                }
567            }
568            Resolution::Int32 => {
569                if self.offset + 4 <= self.data.len() {
570                    let bytes = [
571                        self.data[self.offset],
572                        self.data[self.offset + 1],
573                        self.data[self.offset + 2],
574                        self.data[self.offset + 3],
575                    ];
576                    self.offset += 4;
577                    Some(Value::Int32(i32::from_le_bytes(bytes)))
578                } else {
579                    None
580                }
581            }
582            Resolution::Float => {
583                if self.offset + 4 <= self.data.len() {
584                    let bytes = [
585                        self.data[self.offset],
586                        self.data[self.offset + 1],
587                        self.data[self.offset + 2],
588                        self.data[self.offset + 3],
589                    ];
590                    self.offset += 4;
591                    Some(Value::Float(f32::from_le_bytes(bytes)))
592                } else {
593                    None
594                }
595            }
596            Resolution::Ignore => None,
597        }
598    }
599
600    /// Yields the next register subframe from the current block.
601    fn next_from_block(&mut self) -> Option<Subframe<'a>> {
602        if self.remaining == 0 {
603            return None;
604        }
605
606        self.remaining -= 1;
607        let register = self.current_register;
608        self.current_register += 1;
609
610        // For Read requests, no value data
611        if self.current_type == SubframeType::Read {
612            return Some(Subframe::Register {
613                subframe_type: self.current_type,
614                register,
615                resolution: self.current_resolution,
616                value: None,
617            });
618        }
619
620        // For Write/Response, read the value
621        let value = self.read_value(self.current_resolution);
622        if value.is_none() {
623            // Not enough data, stop iteration
624            self.offset = self.data.len();
625            self.remaining = 0;
626            return None;
627        }
628
629        Some(Subframe::Register {
630            subframe_type: self.current_type,
631            register,
632            resolution: self.current_resolution,
633            value,
634        })
635    }
636
637    /// Parses a register block header (WRITE, READ, or REPLY).
638    fn parse_register_block(&mut self, cmd: u8) -> Option<Subframe<'a>> {
639        let family = cmd & 0xf0;
640        let subframe_type = match family {
641            0x00 => SubframeType::Write,
642            0x10 => SubframeType::Read,
643            0x20 => SubframeType::Response,
644            _ => return None,
645        };
646
647        let resolution = match (cmd >> 2) & 0x03 {
648            0 => Resolution::Int8,
649            1 => Resolution::Int16,
650            2 => Resolution::Int32,
651            3 => Resolution::Float,
652            _ => Resolution::Int8,
653        };
654
655        let mut count = (cmd & 0x03) as u16;
656        if count == 0 {
657            if self.offset >= self.data.len() {
658                return None;
659            }
660            count = self.data[self.offset] as u16;
661            self.offset += 1;
662        }
663
664        if count == 0 {
665            return None;
666        }
667
668        let register = self.read_varuint();
669
670        self.current_type = subframe_type;
671        self.current_resolution = resolution;
672        self.current_register = register + 1;
673        self.remaining = count - 1;
674
675        // For Read requests, no value data
676        if subframe_type == SubframeType::Read {
677            return Some(Subframe::Register {
678                subframe_type,
679                register,
680                resolution,
681                value: None,
682            });
683        }
684
685        // For Write/Response, read the value
686        let value = self.read_value(resolution);
687        if value.is_none() {
688            self.offset = self.data.len();
689            self.remaining = 0;
690            return None;
691        }
692
693        Some(Subframe::Register {
694            subframe_type,
695            register,
696            resolution,
697            value,
698        })
699    }
700
701    /// Parses an error subframe (0x30 or 0x31).
702    fn parse_error(&mut self, cmd: u8) -> Option<Subframe<'a>> {
703        let subframe_type = match cmd {
704            WRITE_ERROR => SubframeType::WriteError,
705            READ_ERROR => SubframeType::ReadError,
706            _ => return None,
707        };
708
709        let register = self.read_varuint();
710        let error_code = self.read_varuint();
711
712        Some(Subframe::Error {
713            subframe_type,
714            register,
715            error_code,
716        })
717    }
718
719    /// Parses a stream subframe (0x40-0x42).
720    fn parse_stream(&mut self, cmd: u8) -> Option<Subframe<'a>> {
721        let subframe_type = match cmd {
722            CLIENT_TO_SERVER => SubframeType::StreamClientToServer,
723            SERVER_TO_CLIENT => SubframeType::StreamServerToClient,
724            CLIENT_POLL_SERVER => SubframeType::StreamClientPollServer,
725            _ => return None,
726        };
727
728        let channel = self.read_varuint();
729        let size = self.read_varuint() as usize;
730
731        if self.offset + size > self.data.len() {
732            self.offset = self.data.len();
733            return None;
734        }
735
736        let data = &self.data[self.offset..self.offset + size];
737        self.offset += size;
738
739        Some(Subframe::Stream {
740            subframe_type,
741            channel,
742            data,
743        })
744    }
745}
746
747impl<'a> Iterator for FrameParser<'a> {
748    type Item = Subframe<'a>;
749
750    fn next(&mut self) -> Option<Subframe<'a>> {
751        // Continue yielding from current block
752        if self.remaining > 0 {
753            return self.next_from_block();
754        }
755
756        // Look for next command
757        while self.offset < self.data.len() {
758            let cmd = self.data[self.offset];
759            self.offset += 1;
760
761            // NOP: skip silently
762            if cmd == NOP {
763                continue;
764            }
765
766            match cmd & 0xf0 {
767                // WRITE (0x00-0x0f), READ (0x10-0x1f), REPLY (0x20-0x2f)
768                0x00 | 0x10 | 0x20 => {
769                    if let Some(subframe) = self.parse_register_block(cmd) {
770                        return Some(subframe);
771                    }
772                    // parse_register_block returned None (e.g., count=0), try next cmd
773                    continue;
774                }
775                // ERROR (0x30-0x31)
776                0x30 => {
777                    if let Some(subframe) = self.parse_error(cmd) {
778                        return Some(subframe);
779                    }
780                    continue;
781                }
782                // STREAM (0x40-0x42)
783                0x40 => {
784                    if let Some(subframe) = self.parse_stream(cmd) {
785                        return Some(subframe);
786                    }
787                    continue;
788                }
789                // Unknown: stop iteration
790                _ => {
791                    self.offset = self.data.len();
792                    return None;
793                }
794            }
795        }
796
797        None
798    }
799}
800
801/// Creates a parser that iterates over all subframes in a multiplex protocol frame.
802///
803/// # Examples
804///
805/// ```
806/// use moteus_protocol::{parse_frame, Subframe};
807///
808/// // A response frame with mode=10 (position) at register 0
809/// let data = [0x21, 0x00, 0x0a];
810/// for subframe in parse_frame(&data) {
811///     match subframe {
812///         Subframe::Register { register, value, .. } => {
813///             println!("Register 0x{:03x} = {:?}", register, value);
814///         }
815///         _ => {}
816///     }
817/// }
818/// ```
819pub fn parse_frame(data: &[u8]) -> FrameParser<'_> {
820    FrameParser::new(data)
821}
822
823#[cfg(test)]
824mod tests {
825    use super::*;
826
827    #[test]
828    fn test_write_varuint() {
829        // Test varuint 0
830        let mut frame = CanFdFrame::new();
831        {
832            let mut writer = WriteCanData::new(&mut frame);
833            writer.write_varuint(0);
834        }
835        assert_eq!(frame.data[0], 0x00);
836        assert_eq!(frame.size, 1);
837
838        // Test varuint 127
839        frame.size = 0;
840        {
841            let mut writer = WriteCanData::new(&mut frame);
842            writer.write_varuint(127);
843        }
844        assert_eq!(frame.data[0], 0x7f);
845
846        // Test varuint 128
847        frame.size = 0;
848        {
849            let mut writer = WriteCanData::new(&mut frame);
850            writer.write_varuint(128);
851        }
852        assert_eq!(frame.data[0], 0x80);
853        assert_eq!(frame.data[1], 0x01);
854        assert_eq!(frame.size, 2);
855    }
856
857    #[test]
858    fn test_write_combiner() {
859        let mut frame = CanFdFrame::new();
860        let mut writer = WriteCanData::new(&mut frame);
861
862        let resolutions = [Resolution::Float, Resolution::Float, Resolution::Ignore];
863        let mut combiner = WriteCombiner::new(0x00, 0x020, &resolutions);
864
865        // First register should trigger framing
866        assert!(combiner.maybe_write(&mut writer));
867        writer.write_f32(1.0);
868        // Second register same resolution - no framing
869        assert!(combiner.maybe_write(&mut writer));
870        writer.write_f32(2.0);
871        // Third register is Ignore
872        assert!(!combiner.maybe_write(&mut writer));
873
874        // Expected: [0x0e] [0x20] [1.0 as f32 LE] [2.0 as f32 LE]
875        //   0x0e = Write Float (0x0c) + count 2
876        //   0x20 = register 0x020 as varuint
877        assert_eq!(frame.size, 10); // 1 cmd + 1 reg + 4 float + 4 float
878        assert_eq!(frame.data[0], 0x0e);
879        assert_eq!(frame.data[1], 0x20);
880        assert_eq!(
881            f32::from_le_bytes(frame.data[2..6].try_into().unwrap()),
882            1.0
883        );
884        assert_eq!(
885            f32::from_le_bytes(frame.data[6..10].try_into().unwrap()),
886            2.0
887        );
888
889        // Verify reply_size: 2 framing bytes + 2 * 4 value bytes = 10
890        assert_eq!(combiner.reply_size(), 10);
891    }
892
893    #[test]
894    fn test_parser_basic() {
895        // Build a simple reply frame: mode=10 (position mode)
896        let data = [
897            0x21, // Reply Int8, count=1
898            0x00, // Register 0 (MODE)
899            0x0a, // Value 10 (Position mode)
900        ];
901
902        let mut iter = parse_frame(&data);
903
904        let subframe = iter.next().unwrap();
905        match subframe {
906            Subframe::Register {
907                subframe_type,
908                register,
909                resolution,
910                value,
911            } => {
912                assert_eq!(subframe_type, SubframeType::Response);
913                assert_eq!(register, 0);
914                assert_eq!(resolution, Resolution::Int8);
915                assert_eq!(value.unwrap().to_i32(), 10);
916            }
917            _ => panic!("Expected Register subframe"),
918        }
919
920        assert!(iter.next().is_none());
921    }
922
923    #[test]
924    fn test_parser_write_subframes() {
925        // Write Int16, count=2, register 0x20, values 100 and 200
926        let data = [
927            0x06, // Write Int16 (0x04), count=2
928            0x20, // Register 0x20
929            0x64, 0x00, // 100 as i16 LE
930            0xc8, 0x00, // 200 as i16 LE
931        ];
932
933        let mut iter = parse_frame(&data);
934
935        match iter.next().unwrap() {
936            Subframe::Register {
937                subframe_type,
938                register,
939                resolution,
940                value,
941            } => {
942                assert_eq!(subframe_type, SubframeType::Write);
943                assert_eq!(register, 0x20);
944                assert_eq!(resolution, Resolution::Int16);
945                assert_eq!(value.unwrap().to_i32(), 100);
946            }
947            _ => panic!("Expected Register subframe"),
948        }
949
950        match iter.next().unwrap() {
951            Subframe::Register {
952                subframe_type,
953                register,
954                value,
955                ..
956            } => {
957                assert_eq!(subframe_type, SubframeType::Write);
958                assert_eq!(register, 0x21);
959                assert_eq!(value.unwrap().to_i32(), 200);
960            }
961            _ => panic!("Expected Register subframe"),
962        }
963
964        assert!(iter.next().is_none());
965    }
966
967    #[test]
968    fn test_parser_read_subframes() {
969        // Read Float, count=1, register 5
970        let data = [
971            0x1d, // Read Float (0x1c), count=1
972            0x05, // Register 5
973        ];
974
975        let mut iter = parse_frame(&data);
976
977        match iter.next().unwrap() {
978            Subframe::Register {
979                subframe_type,
980                register,
981                resolution,
982                value,
983            } => {
984                assert_eq!(subframe_type, SubframeType::Read);
985                assert_eq!(register, 5);
986                assert_eq!(resolution, Resolution::Float);
987                assert!(value.is_none());
988            }
989            _ => panic!("Expected Register subframe"),
990        }
991
992        assert!(iter.next().is_none());
993    }
994
995    #[test]
996    fn test_parser_nop_skipped() {
997        let data = [
998            0x50, // NOP
999            0x21, // Reply Int8, count=1
1000            0x00, // Register 0
1001            0x05, // Value 5
1002        ];
1003
1004        let mut iter = parse_frame(&data);
1005
1006        match iter.next().unwrap() {
1007            Subframe::Register {
1008                register, value, ..
1009            } => {
1010                assert_eq!(register, 0);
1011                assert_eq!(value.unwrap().to_i32(), 5);
1012            }
1013            _ => panic!("Expected Register subframe"),
1014        }
1015
1016        assert!(iter.next().is_none());
1017    }
1018
1019    #[test]
1020    fn test_parser_multiple_blocks() {
1021        // Reply Int8 count=1 reg 0 value 10, then Reply Float count=1 reg 1 value 0.5
1022        let data = [0x21, 0x00, 0x0a, 0x2d, 0x01, 0x00, 0x00, 0x00, 0x3f];
1023
1024        let mut iter = parse_frame(&data);
1025
1026        match iter.next().unwrap() {
1027            Subframe::Register {
1028                register, value, ..
1029            } => {
1030                assert_eq!(register, 0);
1031                assert_eq!(value.unwrap().to_i32(), 10);
1032            }
1033            _ => panic!("Expected Register subframe"),
1034        }
1035
1036        match iter.next().unwrap() {
1037            Subframe::Register {
1038                register, value, ..
1039            } => {
1040                assert_eq!(register, 1);
1041                let val = match value.unwrap() {
1042                    Value::Float(f) => f,
1043                    _ => panic!("Expected Float"),
1044                };
1045                assert!((val - 0.5).abs() < 0.001);
1046            }
1047            _ => panic!("Expected Register subframe"),
1048        }
1049
1050        assert!(iter.next().is_none());
1051    }
1052
1053    #[test]
1054    fn test_value_to_f32() {
1055        // Int8 with position scaling: 50 * 0.01 = 0.5
1056        let v = Value::Int8(50);
1057        assert!((v.to_f32(&scaling::POSITION) - 0.5).abs() < 1e-5);
1058
1059        // Int8 min = NaN
1060        let v = Value::Int8(i8::MIN);
1061        assert!(v.to_f32(&scaling::POSITION).is_nan());
1062
1063        // Float passes through
1064        let v = Value::Float(1.5);
1065        assert!((v.to_f32(&scaling::POSITION) - 1.5).abs() < 1e-5);
1066    }
1067}