lego_powered_up/
notifications.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Parser and data structure for hub notification messages
6
7use crate::consts::*;
8use crate::error::{Error, OptionContext, Result};
9use log::{debug, trace};
10use lpu_macros::Parse;
11use num_derive::FromPrimitive;
12use num_traits::FromPrimitive;
13use std::collections::HashMap;
14use std::fmt::{self, Debug, Display};
15
16pub use self::message::NotificationMessage;
17pub mod message;
18
19#[macro_use]
20pub mod macros;
21
22pub const MAX_NAME_SIZE: usize = 14;
23
24/// The two modes by which Hub LED colours may be set
25#[repr(u8)]
26#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27pub enum HubLedMode {
28    /// Colour may be set to one of a number of specific named colours
29    Colour = 0x0,
30    /// Colour may be set to any 12-bit RGB value
31    Rgb = 0x01,
32}
33
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct HubProperty {
36    pub property: HubPropertyValue,
37    pub operation: HubPropertyOperation,
38    pub reference: HubPropertyRef,
39}
40
41impl HubProperty {
42    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
43        let property_int = next!(msg);
44        let operation = ok!(HubPropertyOperation::from_u8(next!(msg)));
45        let property = HubPropertyValue::parse(property_int, &mut msg)?;
46        let reference = match property {
47            HubPropertyValue::AdvertisingName(_) => {
48                HubPropertyRef::AdvertisingName
49            }
50            HubPropertyValue::Button(_) => HubPropertyRef::Button,
51            HubPropertyValue::FwVersion(_) => HubPropertyRef::FwVersion,
52            HubPropertyValue::HwVersion(_) => HubPropertyRef::HwVersion,
53            HubPropertyValue::Rssi(_) => HubPropertyRef::Rssi,
54            HubPropertyValue::BatteryVoltage(_) => {
55                HubPropertyRef::BatteryVoltage
56            }
57            HubPropertyValue::BatteryType(_) => HubPropertyRef::BatteryType,
58            HubPropertyValue::ManufacturerName(_) => {
59                HubPropertyRef::ManufacturerName
60            }
61            HubPropertyValue::RadioFirmwareVersion(_) => {
62                HubPropertyRef::RadioFirmwareVersion
63            }
64            HubPropertyValue::LegoWirelessProtocolVersion(_) => {
65                HubPropertyRef::LegoWirelessProtocolVersion
66            }
67            HubPropertyValue::SystemTypeId(_) => HubPropertyRef::SystemTypeId,
68            HubPropertyValue::HwNetworkId(_) => HubPropertyRef::HwNetworkId,
69            HubPropertyValue::PrimaryMacAddress(_) => {
70                HubPropertyRef::PrimaryMacAddress
71            }
72            HubPropertyValue::SecondaryMacAddress => {
73                HubPropertyRef::SecondaryMacAddress
74            }
75            HubPropertyValue::HardwareNetworkFamily(_) => {
76                HubPropertyRef::HardwareNetworkFamily
77            }
78        };
79
80        Ok(Self {
81            reference,
82            operation,
83            property,
84        })
85    }
86    pub fn serialise(&self) -> Vec<u8> {
87        let mut msg = Vec::with_capacity(10);
88        msg.extend_from_slice(&[
89            0,
90            0,
91            MessageType::HubProperties as u8,
92            // prop_ref,
93            self.reference as u8,
94            self.operation as u8,
95        ]);
96
97        msg
98    }
99}
100
101#[derive(Clone, Debug, PartialEq, Eq)]
102pub enum HubPropertyValue {
103    AdvertisingName(Vec<u8>),
104    Button(u8),
105    FwVersion(i32),
106    HwVersion(i32),
107    Rssi(i8),
108    BatteryVoltage(u8),
109    BatteryType(HubBatteryType),
110    ManufacturerName(Vec<u8>),
111    RadioFirmwareVersion(Vec<u8>),
112    LegoWirelessProtocolVersion(u16),
113    SystemTypeId(u8),
114    HwNetworkId(u8),
115    PrimaryMacAddress([u8; 6]),
116    SecondaryMacAddress,
117    HardwareNetworkFamily(u8),
118}
119
120impl HubPropertyValue {
121    pub fn parse<'a>(
122        prop_type: u8,
123        mut msg: impl Iterator<Item = &'a u8>,
124    ) -> Result<Self> {
125        use HubPropertyValue::*;
126        let prop_type = ok!(HubPropertyRef::from_u8(prop_type));
127
128        Ok(match prop_type {
129            HubPropertyRef::AdvertisingName => {
130                // name is the rest of the data
131                let name = msg.copied().collect();
132
133                AdvertisingName(name)
134            }
135            HubPropertyRef::Button => Button(next!(msg)),
136            HubPropertyRef::FwVersion => {
137                let vers = next_i32!(msg);
138
139                FwVersion(vers)
140            }
141            HubPropertyRef::HwVersion => {
142                let vers = next_i32!(msg);
143
144                HwVersion(vers)
145            }
146            HubPropertyRef::Rssi => {
147                let bytes = [next!(msg)];
148                let rssi = i8::from_le_bytes(bytes);
149
150                Rssi(rssi)
151            }
152            HubPropertyRef::BatteryVoltage => BatteryVoltage(next!(msg)),
153            HubPropertyRef::BatteryType => {
154                BatteryType(ok!(HubBatteryType::parse(&mut msg)))
155            }
156            HubPropertyRef::ManufacturerName => {
157                let name = msg.copied().collect();
158
159                ManufacturerName(name)
160            }
161            HubPropertyRef::RadioFirmwareVersion => {
162                let vers = msg.copied().collect();
163
164                RadioFirmwareVersion(vers)
165            }
166            HubPropertyRef::LegoWirelessProtocolVersion => {
167                let vers = next_u16!(msg);
168
169                LegoWirelessProtocolVersion(vers)
170            }
171            HubPropertyRef::SystemTypeId => SystemTypeId(next!(msg)),
172            HubPropertyRef::HwNetworkId => HwNetworkId(next!(msg)),
173            HubPropertyRef::PrimaryMacAddress => {
174                let mac = [
175                    next!(msg),
176                    next!(msg),
177                    next!(msg),
178                    next!(msg),
179                    next!(msg),
180                    next!(msg),
181                ];
182                PrimaryMacAddress(mac)
183            }
184            HubPropertyRef::SecondaryMacAddress => SecondaryMacAddress,
185            HubPropertyRef::HardwareNetworkFamily => {
186                HardwareNetworkFamily(next!(msg))
187            }
188        })
189    }
190}
191
192#[repr(u8)]
193#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
194pub enum HubBatteryType {
195    Normal = 0x00,
196    Rechargeable = 0x01,
197}
198
199#[repr(u8)]
200#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
201pub enum HubAction {
202    SwitchOffHub = 0x01,
203    Disconnect = 0x02,
204    VccPortControlOn = 0x03,
205    VccPortControlOff = 0x04,
206    ActivateBusyIndication = 0x05,
207    ResetBusyIndication = 0x06,
208    Shutdown = 0x2F,
209    HubWillSwitchOff = 0x30,
210    HubWillDisconnect = 0x31,
211    HubWillGoIntoBootMode = 0x32,
212}
213
214#[derive(Copy, Clone, Debug, PartialEq, Eq)]
215pub struct HubActionRequest {
216    pub(crate) action_type: HubAction,
217}
218
219impl HubActionRequest {
220    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
221        let action_type = HubAction::parse(&mut msg)?;
222        Ok(HubActionRequest { action_type })
223    }
224    pub fn serialise(&self) -> Vec<u8> {
225        let mut msg = Vec::with_capacity(10);
226        msg.extend_from_slice(&[
227            0,
228            0,
229            MessageType::HubActions as u8,
230            self.action_type as u8,
231        ]);
232        msg
233    }
234}
235
236#[repr(u8)]
237#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
238pub enum AlertType {
239    LowVoltage = 0x01,
240    HighCurrent = 0x02,
241    LowSignalStrength = 0x03,
242    OverPowerCondition = 0x04,
243}
244impl Display for AlertType {
245    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246        match self {
247            AlertType::HighCurrent => write!(f, "High current"),
248            AlertType::LowSignalStrength => write!(f, "Low signal strength"),
249            AlertType::LowVoltage => write!(f, "Low voltage"),
250            AlertType::OverPowerCondition => write!(f, "Over power condition"),
251        }
252    }
253}
254
255#[repr(u8)]
256#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
257pub enum AlertOperation {
258    EnableUpdates = 0x01,
259    DisableUpdates = 0x02,
260    RequestUpdate = 0x03,
261    Update = 0x04,
262}
263
264#[repr(u8)]
265#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
266pub enum AlertPayload {
267    StatusOk = 0x00,
268    Alert = 0xFF,
269}
270
271#[derive(Copy, Clone, Debug, PartialEq, Eq)]
272pub struct HubAlert {
273    pub alert_type: AlertType,
274    pub operation: AlertOperation,
275    pub payload: AlertPayload,
276}
277
278impl HubAlert {
279    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
280        let alert_type = AlertType::parse(&mut msg)?;
281        let operation = AlertOperation::parse(&mut msg)?;
282        let payload = AlertPayload::parse(&mut msg)?;
283        Ok(HubAlert {
284            alert_type,
285            operation,
286            payload,
287        })
288    }
289    pub fn serialise(&self) -> Vec<u8> {
290        let mut msg = Vec::with_capacity(10);
291        msg.extend_from_slice(&[
292            0,
293            0,
294            MessageType::HubAlerts as u8,
295            self.alert_type as u8,
296            self.operation as u8,
297        ]);
298        msg
299    }
300}
301
302#[derive(Copy, Clone, Debug, PartialEq, Eq)]
303pub struct AttachedIo {
304    pub port: u8,
305    pub event: IoAttachEvent,
306}
307
308impl AttachedIo {
309    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
310        let port = next!(msg);
311        let event = IoAttachEvent::parse(&mut msg)?;
312        Ok(Self { port, event })
313    }
314}
315
316#[repr(u8)]
317#[derive(Copy, Clone, Debug, PartialEq, Eq)]
318pub enum IoAttachEvent {
319    DetachedIo {
320        // io_type_id: IoTypeId,        //Not included in detached event
321    },
322    AttachedIo {
323        io_type_id: IoTypeId,
324        hw_rev: VersionNumber,
325        fw_rev: VersionNumber,
326    },
327    AttachedVirtualIo {
328        io_type_id: IoTypeId,
329        port_a: u8,
330        port_b: u8,
331    },
332}
333
334impl IoAttachEvent {
335    // Note: Returns "NoneError("Cannot convert 'None'")"
336    // if incoming IoTypeId-value is not in enum IoTypeId.
337    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
338        let event_type = ok!(Event::from_u8(next!(msg)));
339
340        Ok(match event_type {
341            Event::DetachedIo => {
342                // let io_type_id = ok!(IoTypeId::from_u16(next_u16!(msg)));
343                IoAttachEvent::DetachedIo {}
344            }
345
346            Event::AttachedIo => {
347                let io_type_id = ok!(IoTypeId::from_u16(next_u16!(msg)));
348                let hw_rev = VersionNumber::parse(&mut msg)?;
349                let fw_rev = VersionNumber::parse(&mut msg)?;
350                IoAttachEvent::AttachedIo {
351                    io_type_id,
352                    hw_rev,
353                    fw_rev,
354                }
355            }
356            Event::AttachedVirtualIo => {
357                let io_type_id = ok!(IoTypeId::from_u16(next_u16!(msg)));
358                let port_a = next!(msg);
359                let port_b = next!(msg);
360                IoAttachEvent::AttachedVirtualIo {
361                    io_type_id,
362                    port_a,
363                    port_b,
364                }
365            }
366        })
367    }
368}
369
370/// One observed version number (for a large motor) is 0x1000002f,
371/// for which the build number component is decidedly *not* valid BCD,
372/// so instead for the build number we just take the two bytes and
373/// store them unconverted. As long as the build is printed as hex every
374/// time then no one will notice
375#[derive(Copy, Clone, PartialEq, Eq)]
376pub struct VersionNumber {
377    pub major: u8,
378    pub minor: u8,
379    pub bugfix: u8,
380    pub build: u16,
381}
382
383impl VersionNumber {
384    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
385        //let byte0 = next!(msg);
386        //let byte1 = next!(msg);
387
388        let build = next_u16!(msg);
389        let byte2 = next!(msg);
390        let byte3 = next!(msg);
391        //trace!("Bytes: {:02x?}", [byte3, byte2, byte1, byte0]);
392
393        let major = (byte3 & 0x70) >> 4;
394        let minor = byte3 & 0x0f;
395        // BCD
396        let bugfix = (byte2 >> 4) * 10 + (byte2 & 0x0f);
397        /*
398        let build = (byte1 >> 4) as u16 * 1000
399            + (byte1 & 0x0f) as u16 * 100
400            + (byte0 >> 4) as u16 * 10
401            + (byte0 & 0x0f) as u16;
402            */
403
404        Ok(Self {
405            major,
406            minor,
407            bugfix,
408            build,
409        })
410    }
411
412    pub fn serialise(&self) -> Vec<u8> {
413        let byte3 = (self.major << 4) | self.minor;
414        let byte2 = ((self.bugfix / 10) << 4) | (self.bugfix % 10);
415        /*
416        let mut digits = [0_u8; 4];
417        let mut build = self.build;
418        for digit in digits.iter_mut() {
419            *digit = (build % 10) as u8;
420            build /= 10;
421        }
422        trace!("digits: {:02x?}", digits);
423
424        let byte1 = (digits[3] << 4) | digits[2];
425        let byte0 = (digits[1] << 4) | digits[0];
426        */
427        let byte1 = (self.build >> 8) as u8;
428        let byte0 = self.build as u8;
429
430        vec![byte0, byte1, byte2, byte3]
431    }
432}
433
434impl Display for VersionNumber {
435    fn fmt(
436        &self,
437        fmt: &mut fmt::Formatter,
438    ) -> std::result::Result<(), fmt::Error> {
439        write!(
440            fmt,
441            "{}.{}.{}.{:x}",
442            self.major, self.minor, self.bugfix, self.build
443        )
444    }
445}
446
447impl Debug for VersionNumber {
448    fn fmt(
449        &self,
450        fmt: &mut fmt::Formatter,
451    ) -> std::result::Result<(), fmt::Error> {
452        write!(fmt, "{}", self)
453    }
454}
455
456#[derive(Copy, Clone, Debug, PartialEq, Eq)]
457pub struct ErrorMessageFormat {
458    command_type: u8,
459    error_code: ErrorCode,
460}
461
462impl ErrorMessageFormat {
463    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
464        trace!("ErrorMessageFormat");
465        let command_type = next!(msg);
466        let error_code = ErrorCode::parse(&mut msg)?;
467        Ok(Self {
468            command_type,
469            error_code,
470        })
471    }
472}
473
474#[repr(u8)]
475#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
476pub enum ErrorCode {
477    Ack = 0x01,
478    Mack = 0x02,
479    BufferOverflow = 0x03,
480    Timeout = 0x04,
481    CommandNotRecognized = 0x05,
482    InvalidUse = 0x06,
483    Overcurrent = 0x07,
484    InternalError = 0x08,
485}
486
487/// @typedef HWNetWorkCommandType
488/// @param {number} CONNECTION_REQUEST 0x02
489/// @param {number} FAMILY_REQUEST 0x03
490/// @param {number} FAMILY_SET 0x04
491/// @param {number} JOIN_DENIED 0x05
492/// @param {number} GET_FAMILY 0x06
493/// @param {number} FAMILY 0x07
494/// @param {number} GET_SUBFAMILY 0x08
495/// @param {number} SUBFAMILY 0x09
496/// @param {number} SUBFAMILY_SET 0x0A
497/// @param {number} GET_EXTENDED_FAMILY 0x0B
498/// @param {number} EXTENDED_FAMILY 0x0C
499/// @param {number} EXTENDED_FAMILY_SET 0x0D
500/// @param {number} RESET_LONG_PRESS_TIMING 0x0E
501/// @description <https://lego.github.io/lego-ble-wireless-protocol-docs/index.html#h-w-network-command-type>
502#[repr(u8)]
503#[derive(Copy, Clone, Debug, PartialEq, Eq)]
504pub enum NetworkCommand {
505    ConnectionRequest(ButtonState),
506    FamilyRequest,
507    FamilySet(NetworkFamily),
508    JoinDenied(),
509    GetFamily(),
510    Family(NetworkFamily),
511    GetSubfamily(),
512    Subfamily(NetworkSubFamily),
513    SubfamilySet(NetworkSubFamily),
514    GetExtendedFamily(),
515    ExtendedFamily {
516        family: NetworkFamily,
517        subfamily: NetworkSubFamily,
518    },
519    ExtendedFamilySet {
520        family: NetworkFamily,
521        subfamily: NetworkSubFamily,
522    },
523    ResetLongPressTiming(),
524}
525
526impl NetworkCommand {
527    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
528        use NetworkCommand::*;
529        let command_type = ok!(HwNetworkCommandType::from_u8(next!(msg)));
530
531        Ok(match command_type {
532            HwNetworkCommandType::ConnectionRequest => {
533                let button = ButtonState::parse(&mut msg)?;
534                ConnectionRequest(button)
535            }
536            HwNetworkCommandType::FamilyRequest => FamilyRequest,
537            HwNetworkCommandType::FamilySet => {
538                let fam = NetworkFamily::parse(&mut msg)?;
539                FamilySet(fam)
540            }
541            HwNetworkCommandType::JoinDenied => {
542                todo!()
543            }
544            HwNetworkCommandType::GetFamily => {
545                todo!()
546            }
547            HwNetworkCommandType::Family => {
548                let fam = NetworkFamily::parse(&mut msg)?;
549                Family(fam)
550            }
551            HwNetworkCommandType::GetSubfamily => {
552                todo!()
553            }
554            HwNetworkCommandType::Subfamily => {
555                let fam = NetworkSubFamily::parse(&mut msg)?;
556                Subfamily(fam)
557            }
558            HwNetworkCommandType::SubfamilySet => {
559                let fam = NetworkSubFamily::parse(&mut msg)?;
560                SubfamilySet(fam)
561            }
562            HwNetworkCommandType::GetExtendedFamily => {
563                todo!()
564            }
565            HwNetworkCommandType::ExtendedFamily => {
566                // Bit 7 | sss | ffff
567                let byte = next!(msg);
568                let fam_byte = byte & 0x0f;
569                let sub_bytes = (byte >> 4) & 0x7;
570                let family = ok!(NetworkFamily::from_u8(fam_byte));
571                let subfamily = ok!(NetworkSubFamily::from_u8(sub_bytes));
572                ExtendedFamily { family, subfamily }
573            }
574            HwNetworkCommandType::ExtendedFamilySet => {
575                // Bit 7 | sss | ffff
576                let byte = next!(msg);
577                let fam_byte = byte & 0x0f;
578                let sub_bytes = (byte >> 4) & 0x7;
579                let family = ok!(NetworkFamily::from_u8(fam_byte));
580                let subfamily = ok!(NetworkSubFamily::from_u8(sub_bytes));
581                ExtendedFamilySet { family, subfamily }
582            }
583            HwNetworkCommandType::ResetLongPressTiming => {
584                todo!()
585            }
586        })
587    }
588}
589
590#[repr(u8)]
591#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
592pub enum ButtonState {
593    Pressed = 2,
594    Released = 0,
595    Up = 1,
596    Down = 255,
597    Stop = 127,
598}
599
600#[repr(u8)]
601#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
602pub enum NetworkFamily {
603    Green = 0x01,
604    Yellow = 0x02,
605    Red = 0x03,
606    Blue = 0x04,
607    Purple = 0x05,
608    LightBlue = 0x06,
609    Teal = 0x07,
610    Pink = 0x08,
611    White = 0x00,
612}
613
614#[repr(u8)]
615#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
616pub enum NetworkSubFamily {
617    OneFlash = 0x01,
618    TwoFlashes = 0x02,
619    ThreeFlashes = 0x03,
620    FourFlashes = 0x04,
621    FiveFlashes = 0x05,
622    SixFlashes = 0x06,
623    SevenFlashes = 0x07,
624}
625
626#[repr(u8)]
627#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
628pub enum LockStatus {
629    Ok = 0x00,
630    NotLocked = 0xff,
631}
632
633#[derive(Copy, Clone, Debug, PartialEq, Eq)]
634pub struct InformationRequest {
635    pub(crate) port_id: u8,
636    pub(crate) information_type: InformationType,
637}
638
639impl InformationRequest {
640    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
641        let port_id = next!(msg);
642        let information_type = InformationType::parse(&mut msg)?;
643        Ok(InformationRequest {
644            port_id,
645            information_type,
646        })
647    }
648    pub fn serialise(&self) -> Vec<u8> {
649        let mut msg = Vec::with_capacity(10);
650        msg.extend_from_slice(&[
651            0,
652            0,
653            MessageType::PortInformationRequest as u8,
654            self.port_id,
655            self.information_type as u8,
656        ]);
657        msg
658    }
659}
660
661#[repr(u8)]
662#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
663pub enum InformationType {
664    PortValue = 0x00,
665    ModeInfo = 0x01,
666    PossibleModeCombinations = 0x02,
667}
668
669#[derive(Copy, Clone, Debug, PartialEq, Eq)]
670pub struct ModeInformationRequest {
671    pub(crate) port_id: u8,
672    pub(crate) mode: u8,
673    pub(crate) information_type: ModeInformationType,
674}
675
676impl ModeInformationRequest {
677    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
678        let port_id = next!(msg);
679        let mode = next!(msg);
680        let information_type = ModeInformationType::parse(&mut msg)?;
681        Ok(Self {
682            port_id,
683            mode,
684            information_type,
685        })
686    }
687    pub fn serialise(&self) -> Vec<u8> {
688        let mut msg = Vec::with_capacity(10);
689        msg.extend_from_slice(&[
690            0,
691            0,
692            MessageType::PortModeInformationRequest as u8,
693            self.port_id,
694            self.mode,
695            self.information_type as u8,
696        ]);
697        msg
698    }
699}
700
701#[repr(u8)]
702#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
703pub enum ModeInformationType {
704    Name = 0x00,
705    Raw = 0x01,
706    Pct = 0x02,
707    Si = 0x03,
708    Symbol = 0x04,
709    Mapping = 0x05,
710    UsedInternally = 0x06,
711    MotorBias = 0x07,
712    CapabilityBits = 0x08,
713    ValueFormat = 0x80,
714}
715
716#[derive(Copy, Clone, Debug, PartialEq, Eq)]
717pub struct InputSetupSingle {
718    pub(crate) port_id: u8,
719    pub(crate) mode: u8,
720    pub(crate) delta: u32,
721    pub(crate) notification_enabled: bool,
722}
723
724impl InputSetupSingle {
725    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
726        let port_id = next!(msg);
727        let mode = next!(msg);
728        let delta = next_u32!(msg);
729        let notif_byte = next!(msg);
730        let notification_enabled = match notif_byte {
731            0x00 => false,
732            0x01 => true,
733            b => {
734                return Err(Error::ParseError(format!(
735                    "Invalid notification enabled state {:x}",
736                    b
737                )))
738            }
739        };
740        Ok(Self {
741            port_id,
742            mode,
743            delta,
744            notification_enabled,
745        })
746    }
747
748    pub fn serialise(&self) -> Vec<u8> {
749        let mut msg = Vec::with_capacity(10);
750        msg.extend_from_slice(&[
751            0,
752            0,
753            MessageType::PortInputFormatSetupSingle as u8,
754            self.port_id,
755            self.mode,
756        ]);
757        msg.extend_from_slice(&self.delta.to_le_bytes());
758        msg.push(self.notification_enabled as u8);
759        msg
760    }
761}
762
763#[derive(Copy, Clone, Debug, PartialEq, Eq)]
764pub struct InputSetupCombined {
765    pub(crate) port_id: u8,
766    pub(crate) subcommand: InputSetupCombinedSubcommand,
767}
768
769impl InputSetupCombined {
770    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
771        let port_id = next!(msg);
772        let subcommand = InputSetupCombinedSubcommand::parse(&mut msg)?;
773        Ok(InputSetupCombined {
774            port_id,
775            subcommand,
776        })
777    }
778    pub fn serialise(&self) -> Vec<u8> {
779        use InputSetupCombinedSubcommand::*;
780        match &self.subcommand {
781            SetModeanddatasetCombinations {
782                combination_index,
783                mode_dataset,
784            } => {
785                let mut bytes = vec![
786                    // Header
787                    0, // len
788                    0, // hub id - always set to 0
789                    MessageType::PortInputFormatSetupCombined as u8,
790                    // Command
791                    self.port_id,
792                    InputSetupCombinedSubcommandValue::SetModeanddatasetCombinations as u8,
793                    // Subcommand payload
794                    *combination_index,
795                ];
796                // Not sure why mode_dataset needs to be a [u8; 8] but changing it necessitates reworking
797                // the parse function and possibly more. Workaround for now is to set unneeded values to
798                // all 1's as a marker. Should be ok since no device probably has 128 modes and 128 datasets.
799                let md = mode_dataset.as_slice();
800                for val in md.iter() {
801                    if *val == 255 {
802                        break;
803                    } else {
804                        bytes.push(*val);
805                    }
806                }
807                // bytes.extend_from_slice(mode_dataset.as_slice());
808                bytes
809            }
810            LockLpf2DeviceForSetup {} => {
811                vec![
812                    // Header
813                    0, // len
814                    0, // hub id - always set to 0
815                    MessageType::PortInputFormatSetupCombined as u8,
816                    // Command
817                    self.port_id,
818                    InputSetupCombinedSubcommandValue::LockLpf2DeviceForSetup
819                        as u8,
820                ]
821            }
822            UnlockAndStartMultiEnabled {} => {
823                vec![
824                    // Header
825                    0, // len
826                    0, // hub id - always set to 0
827                    MessageType::PortInputFormatSetupCombined as u8,
828                    // Command
829                    self.port_id,
830                    InputSetupCombinedSubcommandValue::UnlockAndStartMultiEnabled as u8,
831                ]
832            }
833            UnlockAndStartMultiDisabled {} => {
834                vec![
835                    // Header
836                    0, // len
837                    0, // hub id - always set to 0
838                    MessageType::PortInputFormatSetupCombined as u8,
839                    // Command
840                    self.port_id,
841                    InputSetupCombinedSubcommandValue::UnlockAndStartMultiDisabled as u8,
842                ]
843            }
844            NotUsed {} => {
845                vec![
846                    // Header
847                    0, // len
848                    0, // hub id - always set to 0
849                    MessageType::PortInputFormatSetupCombined as u8,
850                    // Command
851                    self.port_id,
852                    InputSetupCombinedSubcommandValue::NotUsed as u8,
853                ]
854            }
855            ResetSensor {} => {
856                vec![
857                    // Header
858                    0, // len
859                    0, // hub id - always set to 0
860                    MessageType::PortInputFormatSetupCombined as u8,
861                    // Command
862                    self.port_id,
863                    InputSetupCombinedSubcommandValue::ResetSensor as u8,
864                ]
865            }
866        }
867    }
868}
869
870#[derive(Copy, Clone, Debug, PartialEq, Eq)]
871pub enum InputSetupCombinedSubcommand {
872    SetModeanddatasetCombinations {
873        combination_index: u8,
874        mode_dataset: [u8; 8],
875    },
876    LockLpf2DeviceForSetup,
877    UnlockAndStartMultiEnabled,
878    UnlockAndStartMultiDisabled,
879    NotUsed,
880    ResetSensor,
881}
882
883impl InputSetupCombinedSubcommand {
884    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
885        use InputSetupCombinedSubcommand::*;
886
887        let comm = ok!(PortInputFormatSetupSubCommand::from_u8(next!(msg)));
888        Ok(match comm {
889            PortInputFormatSetupSubCommand::SetModeanddatasetCombinations => {
890                let combination_index = next!(msg);
891                let mut mode_dataset = [0_u8; 8];
892                for ele in mode_dataset.iter_mut() {
893                    *ele = next!(msg);
894                }
895                SetModeanddatasetCombinations {
896                    combination_index,
897                    mode_dataset,
898                }
899            }
900            PortInputFormatSetupSubCommand::LockLpf2DeviceForSetup => {
901                LockLpf2DeviceForSetup
902            }
903            PortInputFormatSetupSubCommand::UnlockAndStartMultiEnabled => {
904                UnlockAndStartMultiEnabled
905            }
906
907            PortInputFormatSetupSubCommand::UnlockAndStartMultiDisabled => {
908                UnlockAndStartMultiDisabled
909            }
910            PortInputFormatSetupSubCommand::NotUsed => NotUsed,
911            PortInputFormatSetupSubCommand::ResetSensor => ResetSensor,
912        })
913    }
914}
915
916#[derive(Clone, Debug, PartialEq, Eq)]
917pub struct PortInformationValue {
918    pub port_id: u8,
919    pub information_type: PortInformationType,
920}
921
922impl PortInformationValue {
923    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
924        let port_id = next!(msg);
925        let information_type = PortInformationType::parse(&mut msg)?;
926        Ok(Self {
927            port_id,
928            information_type,
929        })
930    }
931}
932
933#[derive(Clone, Debug, PartialEq, Eq)]
934pub enum PortInformationType {
935    ModeInfo {
936        capabilities: PortCapabilities,
937        mode_count: u8,
938        input_modes: u16,
939        output_modes: u16,
940    },
941    PossibleModeCombinations(Vec<u8>),
942}
943
944impl PortInformationType {
945    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
946        use PortInformationType::*;
947
948        let mode = next!(msg);
949        match mode {
950            1 => {
951                // Mode info
952                let capabilities = PortCapabilities(next!(msg));
953                let mode_count = next!(msg);
954                let input_modes = next_u16!(msg);
955                let output_modes = next_u16!(msg);
956                Ok(ModeInfo {
957                    capabilities,
958                    mode_count,
959                    input_modes,
960                    output_modes,
961                })
962            }
963            2 => {
964                // possible mode combinations
965                let combinations = msg.cloned().collect();
966                Ok(PossibleModeCombinations(combinations))
967            }
968            m => Err(Error::ParseError(format!(
969                "Invalid port information type {}",
970                m
971            ))),
972        }
973    }
974}
975
976#[derive(Clone, Debug, PartialEq, Eq)]
977pub struct PortCapabilities(pub u8);
978impl PortCapabilities {
979    pub const LOGICAL_SYNCHRONIZABLE: u8 = 0b1000;
980    pub const LOGICAL_COMBINABLE: u8 = 0b0100;
981    pub const INPUT: u8 = 0b0010;
982    pub const OUTPUT: u8 = 0b0001;
983}
984
985#[derive(Clone, Debug, PartialEq)]
986pub struct PortModeInformationValue {
987    pub port_id: u8,
988    pub mode: u8,
989    pub information_type: PortModeInformationType,
990}
991
992impl PortModeInformationValue {
993    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
994        let port_id = next!(msg);
995        let mode = next!(msg);
996        let information_type = PortModeInformationType::parse(&mut msg)?;
997        Ok(Self {
998            port_id,
999            mode,
1000            information_type,
1001        })
1002    }
1003}
1004
1005#[repr(u8)]
1006#[derive(Clone, Debug, PartialEq)]
1007pub enum PortModeInformationType {
1008    Name(Vec<u8>),
1009    RawRange {
1010        min: f32,
1011        max: f32,
1012    },
1013    PctRange {
1014        min: f32,
1015        max: f32,
1016    },
1017    SiRange {
1018        min: f32,
1019        max: f32,
1020    },
1021    Symbol(Vec<u8>),
1022    Mapping {
1023        input: MappingValue,
1024        output: MappingValue,
1025    },
1026    MotorBias(u8),
1027    CapabilityBits([u8; 6]),
1028    ValueFormat(ValueFormatType),
1029}
1030
1031impl PortModeInformationType {
1032    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1033        use PortModeInformationType::*;
1034
1035        let info_type = next!(msg);
1036        Ok(match info_type {
1037            0 => {
1038                // name is the remainder of the message
1039                let name = msg.cloned().collect();
1040                Name(name)
1041            }
1042            1 => {
1043                // raw is two 32-bit floats
1044                let min = next_f32!(msg);
1045                let max = next_f32!(msg);
1046                RawRange { min, max }
1047            }
1048            2 => {
1049                // pct is two 32-bit floats
1050                let min = next_f32!(msg);
1051                let max = next_f32!(msg);
1052                PctRange { min, max }
1053            }
1054            3 => {
1055                // si is two 32-bit floats
1056                let min = next_f32!(msg);
1057                let max = next_f32!(msg);
1058                SiRange { min, max }
1059            }
1060            4 => {
1061                // symbol is rest of message
1062                let sym = msg.cloned().collect();
1063                Symbol(sym)
1064            }
1065            5 => {
1066                // mapping is officially a u16 but actually looks like
1067                // two u8 values (one for input bitflags and one for
1068                // output bitflags)
1069                let input = MappingValue(next!(msg));
1070                let output = MappingValue(next!(msg));
1071                Mapping { input, output }
1072            }
1073            7 => {
1074                // motor bias is a byte
1075                MotorBias(next!(msg))
1076            }
1077            8 => {
1078                // capability bits is a 48-wide bitfield that might be
1079                // documented Somewhere™
1080                let mut bits = [0_u8; 6];
1081                for ele in bits.iter_mut() {
1082                    *ele = next!(msg);
1083                }
1084                CapabilityBits(bits)
1085            }
1086            128 => {
1087                // value format is the struct format
1088                let number_of_datasets = next!(msg);
1089                let dataset_type = DatasetType::parse(&mut msg)?;
1090                let total_figures = next!(msg);
1091                let decimals = next!(msg);
1092                ValueFormat(ValueFormatType {
1093                    number_of_datasets,
1094                    dataset_type,
1095                    total_figures,
1096                    decimals,
1097                })
1098            }
1099            t => {
1100                return Err(Error::ParseError(format!(
1101                    "Invalid information type {}",
1102                    t
1103                )))
1104            }
1105        })
1106    }
1107}
1108
1109#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
1110pub struct ValueFormatType {
1111    pub number_of_datasets: u8,
1112    pub dataset_type: DatasetType,
1113    pub total_figures: u8,
1114    pub decimals: u8,
1115}
1116impl fmt::Display for ValueFormatType {
1117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1118        write!(
1119            f,
1120            "{:>4} value(s) of {:<8}   Figures: {:<3} Decimals: {:<3}",
1121            self.number_of_datasets,
1122            self.dataset_type,
1123            self.total_figures,
1124            self.decimals
1125        )
1126    }
1127}
1128
1129#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1130pub struct MappingValue(pub u8);
1131impl MappingValue {
1132    pub const SUPPORTS_NULL: u8 = 0b1000_0000;
1133    pub const SUPPORTS_FUNCTIONAL2: u8 = 0b0100_0000;
1134    pub const ABS: u8 = 0b0001_0000;
1135    pub const REL: u8 = 0b0000_1000;
1136    pub const DIS: u8 = 0b0000_0100;
1137}
1138
1139#[repr(u8)]
1140#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse, Default)]
1141pub enum DatasetType {
1142    #[default]
1143    Bits8 = 0b00,
1144    Bits16 = 0b01,
1145    Bits32 = 0b10,
1146    Float = 0b11,
1147}
1148impl fmt::Display for DatasetType {
1149    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1150        match *self {
1151            DatasetType::Bits8 => {
1152                write!(f, " 8 bit")
1153            }
1154            DatasetType::Bits16 => {
1155                write!(f, "16 bit")
1156            }
1157            DatasetType::Bits32 => {
1158                write!(f, "32 bit")
1159            }
1160            DatasetType::Float => {
1161                write!(f, "float ")
1162            }
1163        }
1164    }
1165}
1166
1167#[derive(Copy, Clone, Debug, PartialEq)]
1168pub enum TypedValue {
1169    Bits8(u8),
1170    Bits16(u16),
1171    Bits32(u32),
1172    Float(f32),
1173}
1174
1175/// The PortValueSingleFormat is a list of port id & value pairs, except
1176/// that the values may be different lengths (u8, u16, u32, f32) depending
1177/// on the port configuration. For now we just save the payload and then
1178/// later on will provide a method to split it out into port-value pairs
1179/// based on a separate port type mapping.
1180///
1181/// Notes on Value Format
1182/// 1) The valuetypes are signed, i.e. the variants are i8, i16, i32, f32. This:
1183///    https://lego.github.io/lego-ble-wireless-protocol-docs/index.html#port-value-single
1184///    says that the values are unsigned, but this is seemingly incorrect. Many sensors can
1185///    report negative values, as can be seen by requesting Port Mode Information::Raw range.
1186/// 2) The values are not a single value but an array, the length of which is given
1187///    by the "number_of_datasets"-member of Value Format.
1188///    ("Single" in PortValueSingle refers to single sensor mode, but single sensors can)  
1189///     and do provide provide array data, ex. color RGB or accelerometer XYZ-data.)
1190/// 3) There are some inconsistencies looking at port mode information:
1191///         HubLeds in RBG reports taking 8 bit values in the range 0-255, though this
1192///         doesn't concern the parser of incoming values. As regards sensors;
1193///         TechnicHubTiltSensor mode CFG, as well as MoveHubInternalTilt modes IM_CF
1194///         and CALIB: These all report that they will provide 8 bit values in
1195///         range 0-255.
1196///     But these are the only ones I've been able to find. On the whole it seems better
1197///     to correctly support the multitude of sensors and modes.
1198#[derive(Clone, Debug, PartialEq, Eq)]
1199pub struct PortValueSingleFormat {
1200    pub port_id: u8,
1201    pub data: Vec<i8>,
1202}
1203impl PortValueSingleFormat {
1204    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1205        let port_id = next!(msg);
1206        let data = msg.cloned().map(|x| x as i8).collect();
1207        Ok(Self { port_id, data })
1208    }
1209
1210    pub fn process(&self, _type_mapping: ()) -> HashMap<u8, TypedValue> {
1211        unimplemented!()
1212    }
1213}
1214
1215/// The PortValueCombinedFormat is some horrific set of pointers to
1216/// values we should already have cached elsewhere. For now we save the
1217/// raw data and leave parsing it for later.
1218#[derive(Clone, Debug, PartialEq, Eq)]
1219pub struct PortValueCombinedFormat {
1220    pub port_id: u8,
1221    pub data: Vec<u8>,
1222}
1223
1224impl PortValueCombinedFormat {
1225    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1226        let port_id = next!(msg);
1227        let data = msg.cloned().collect();
1228        Ok(Self { port_id, data })
1229    }
1230}
1231
1232#[derive(Clone, Debug, PartialEq, Eq)]
1233pub struct PortInputFormatSingleFormat {
1234    pub port_id: u8,
1235    pub mode: u8,
1236    pub delta: u32,
1237    pub notification_enabled: bool,
1238}
1239
1240impl PortInputFormatSingleFormat {
1241    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1242        let port_id = next!(msg);
1243        let mode = next!(msg);
1244        let delta = next_u32!(msg);
1245        let notification_enabled = match next!(msg) {
1246            0 => Ok(false),
1247            1 => Ok(true),
1248            v => Err(Error::ParseError(format!(
1249                "Invalid notification enabled status {}",
1250                v
1251            ))),
1252        }?;
1253        Ok(Self {
1254            port_id,
1255            mode,
1256            delta,
1257            notification_enabled,
1258        })
1259    }
1260}
1261
1262#[derive(Clone, Debug, PartialEq, Eq)]
1263pub struct PortInputFormatCombinedFormat {
1264    port_id: u8,
1265    control: u8,
1266    combination_index: u8,
1267    multi_update: bool,
1268    mode_dataset_combination_pointer: u16,
1269}
1270
1271impl PortInputFormatCombinedFormat {
1272    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1273        let port_id = next!(msg);
1274        let control = next!(msg);
1275
1276        // let combination_index = next!(msg);  // combination index is part of control byte, not separate byte.
1277        // This caused function to fail with "NoneError: Insufficient length"
1278        let combination_index: u8 = 0; // Set to 0 for now, figure out how to get from control byte later
1279
1280        let multi_update = (control >> 7) != 0;
1281        let mode_dataset_combination_pointer = next_u16!(msg);
1282
1283        Ok(Self {
1284            port_id,
1285            control,
1286            combination_index,
1287            multi_update,
1288            mode_dataset_combination_pointer,
1289        })
1290    }
1291}
1292
1293#[derive(Clone, Debug, PartialEq, Eq)]
1294pub enum VirtualPortSetupFormat {
1295    Disconnect { port_id: u8 },
1296    Connect { port_a: u8, port_b: u8 },
1297}
1298
1299impl VirtualPortSetupFormat {
1300    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1301        use VirtualPortSetupFormat::*;
1302        match next!(msg) {
1303            0 => {
1304                // Disconnected
1305                let port_id = next!(msg);
1306                Ok(Disconnect { port_id })
1307            }
1308            1 => {
1309                // Connected
1310                let port_a = next!(msg);
1311                let port_b = next!(msg);
1312                Ok(Connect { port_a, port_b })
1313            }
1314            c => Err(Error::ParseError(format!(
1315                "Invalid virtual port subcommand {}",
1316                c
1317            ))),
1318        }
1319    }
1320}
1321
1322#[derive(Clone, Debug, PartialEq, Eq)]
1323pub struct PortOutputCommandFormat {
1324    pub port_id: u8,
1325    pub startup_info: StartupInfo,
1326    pub completion_info: CompletionInfo,
1327    pub subcommand: PortOutputSubcommand,
1328}
1329
1330impl PortOutputCommandFormat {
1331    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1332        let port_id = next!(msg);
1333        let startup_and_completion_byte = next!(msg);
1334        let startup_info = ok!(StartupInfo::from_u8(
1335            (startup_and_completion_byte & 0xf0) >> 4
1336        ));
1337        let completion_info =
1338            ok!(CompletionInfo::from_u8(startup_and_completion_byte & 0x0f));
1339        let subcommand = PortOutputSubcommand::parse(&mut msg)?;
1340
1341        Ok(Self {
1342            port_id,
1343            startup_info,
1344            completion_info,
1345            subcommand,
1346        })
1347    }
1348
1349    pub fn serialise(&self) -> Vec<u8> {
1350        use PortOutputSubcommand::*;
1351        let startup_and_completion_byte =
1352            ((self.startup_info as u8) << 4) + self.completion_info as u8;
1353        match &self.subcommand {
1354            WriteDirectModeData(data) => data.serialise(self),
1355            StartSpeed {
1356                speed,
1357                max_power,
1358                use_acc_profile,
1359                use_dec_profile,
1360            } => {
1361                let profile =
1362                    ((*use_acc_profile as u8) << 1) | (*use_dec_profile as u8);
1363                let speed = speed.to_le_bytes()[0];
1364                let max_power = max_power.to_le_bytes()[0];
1365                vec![
1366                    // Header
1367                    0, // len
1368                    0, // hub id - always set to 0
1369                    MessageType::PortOutputCommand as u8,
1370                    // Command
1371                    self.port_id,
1372                    // 0x11, // 0001 Execute immediately, 0001 Command feedback
1373                    startup_and_completion_byte,
1374                    PortOutputSubCommandValue::StartSpeed as u8,
1375                    // Subcommand payload
1376                    speed,
1377                    max_power,
1378                    profile,
1379                ]
1380            }
1381            StartSpeedForDegrees {
1382                degrees,
1383                speed,
1384                max_power,
1385                end_state,
1386                use_acc_profile,
1387                use_dec_profile,
1388            } => {
1389                let profile =
1390                    ((*use_acc_profile as u8) << 1) | (*use_dec_profile as u8);
1391                let speed = speed.to_le_bytes()[0];
1392                let max_power = max_power.to_le_bytes()[0];
1393                let degrees = degrees.to_le_bytes();
1394                let mut bytes = vec![
1395                    // Header
1396                    0, // len
1397                    0, // hub id - always set to 0
1398                    MessageType::PortOutputCommand as u8,
1399                    // Command
1400                    self.port_id,
1401                    startup_and_completion_byte,
1402                    PortOutputSubCommandValue::StartSpeedForDegrees as u8,
1403                ];
1404                // Subcommand payload
1405                bytes.extend_from_slice(&degrees);
1406                bytes.push(speed);
1407                bytes.push(max_power);
1408                bytes.push(end_state.to_u8());
1409                bytes.push(profile);
1410
1411                bytes
1412            }
1413            GotoAbsolutePosition {
1414                abs_pos,
1415                speed,
1416                max_power,
1417                end_state,
1418                use_acc_profile,
1419                use_dec_profile,
1420            } => {
1421                let profile =
1422                    ((*use_acc_profile as u8) << 1) | (*use_dec_profile as u8);
1423                let speed = speed.to_le_bytes()[0];
1424                let max_power = max_power.to_le_bytes()[0];
1425                let abs_pos = abs_pos.to_le_bytes();
1426                let mut bytes = vec![
1427                    // Header
1428                    0, // len
1429                    0, // hub id - always set to 0
1430                    MessageType::PortOutputCommand as u8,
1431                    // Command
1432                    self.port_id,
1433                    startup_and_completion_byte,
1434                    PortOutputSubCommandValue::GotoAbsolutePosition as u8,
1435                ];
1436                // Subcommand payload
1437                bytes.extend_from_slice(&abs_pos);
1438                bytes.push(speed);
1439                bytes.push(max_power);
1440                bytes.push(end_state.to_u8());
1441                bytes.push(profile);
1442
1443                bytes
1444            }
1445            StartSpeedForTime {
1446                time,
1447                speed,
1448                max_power,
1449                end_state,
1450                use_acc_profile,
1451                use_dec_profile,
1452            } => {
1453                let profile =
1454                    ((*use_acc_profile as u8) << 1) | (*use_dec_profile as u8);
1455                let speed = speed.to_le_bytes()[0];
1456                let max_power = max_power.to_le_bytes()[0];
1457                dbg!(time);
1458                let time = time.to_le_bytes();
1459                dbg!(time);
1460                let mut bytes = vec![
1461                    // Header
1462                    0, // len
1463                    0, // hub id - always set to 0
1464                    MessageType::PortOutputCommand as u8,
1465                    // Command
1466                    self.port_id,
1467                    startup_and_completion_byte,
1468                    PortOutputSubCommandValue::StartSpeedForTime as u8,
1469                ];
1470                // Subcommand payload
1471                bytes.extend_from_slice(&time);
1472                bytes.push(speed);
1473                bytes.push(max_power);
1474                bytes.push(end_state.to_u8());
1475                bytes.push(profile);
1476
1477                bytes
1478            }
1479            SetAccTime {
1480                time,
1481                profile_number,
1482            } => {
1483                let time = time.to_le_bytes();
1484                let profile_number = profile_number.to_le_bytes();
1485                let mut bytes = vec![
1486                    // Header
1487                    0, // len
1488                    0, // hub id - always set to 0
1489                    MessageType::PortOutputCommand as u8,
1490                    // Command
1491                    self.port_id,
1492                    startup_and_completion_byte,
1493                    PortOutputSubCommandValue::SetAccTime as u8,
1494                ];
1495                // Subcommand payload
1496                bytes.extend_from_slice(&time);
1497                bytes.extend_from_slice(&profile_number);
1498
1499                bytes
1500            }
1501            SetDecTime {
1502                time,
1503                profile_number,
1504            } => {
1505                let time = time.to_le_bytes();
1506                let profile_number = profile_number.to_le_bytes();
1507                let mut bytes = vec![
1508                    // Header
1509                    0, // len
1510                    0, // hub id - always set to 0
1511                    MessageType::PortOutputCommand as u8,
1512                    // Command
1513                    self.port_id,
1514                    startup_and_completion_byte,
1515                    PortOutputSubCommandValue::SetDecTime as u8,
1516                ];
1517                // Subcommand payload
1518                bytes.extend_from_slice(&time);
1519                bytes.extend_from_slice(&profile_number);
1520
1521                bytes
1522            }
1523            _ => todo!(),
1524        }
1525    }
1526}
1527
1528#[repr(u8)]
1529#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
1530pub enum StartupInfo {
1531    BufferIfNecessary = 0b0000,
1532    ExecuteImmediately = 0b0001,
1533}
1534
1535#[repr(u8)]
1536#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
1537pub enum CompletionInfo {
1538    NoAction = 0b0000,
1539    CommandFeedback = 0b0001,
1540}
1541
1542impl StartupInfo {
1543    pub fn serialise(&self, completion: &CompletionInfo) -> u8 {
1544        ((*self as u8) << 4) | (*completion as u8)
1545    }
1546}
1547
1548#[derive(Clone, Debug, PartialEq, Eq)]
1549pub enum PortOutputSubcommand {
1550    /// This has a subcommand number and also a "writedirectmodedata"
1551    /// annotation so I have no idea where this really lives
1552    ///
1553    /// According to (*) it does live here
1554    /// (*) <https://github.com/LEGO/lego-ble-wireless-protocol-docs/issues/15>
1555    StartPower2 {
1556        power1: Power,
1557        power2: Power,
1558    },
1559    SetAccTime {
1560        time: i16,
1561        profile_number: i8,
1562    },
1563    SetDecTime {
1564        time: i16,
1565        profile_number: i8,
1566    },
1567    StartSpeed {
1568        speed: i8,
1569        max_power: u8,
1570        use_acc_profile: bool,
1571        use_dec_profile: bool,
1572    },
1573    StartSpeedNoPower {
1574        speed: i8,
1575        max_power: u8,
1576        use_acc_profile: bool,
1577        use_dec_profile: bool,
1578    },
1579    StartSpeed2 {
1580        speed1: i8,
1581        speed2: i8,
1582        max_power: u8,
1583        use_acc_profile: bool,
1584        use_dec_profile: bool,
1585    },
1586    StartSpeedForTime {
1587        time: i16,
1588        speed: i8,
1589        max_power: u8,
1590        end_state: EndState,
1591        use_acc_profile: bool,
1592        use_dec_profile: bool,
1593    },
1594    StartSpeedForTime2 {
1595        time: i16,
1596        speed_l: i8,
1597        speed_r: i8,
1598        max_power: u8,
1599        end_state: EndState,
1600        use_acc_profile: bool,
1601        use_dec_profile: bool,
1602    },
1603    StartSpeedForDegrees {
1604        degrees: i32,
1605        speed: i8,
1606        max_power: u8,
1607        end_state: EndState,
1608        use_acc_profile: bool,
1609        use_dec_profile: bool,
1610    },
1611    StartSpeedForDegrees2 {
1612        degrees: i32,
1613        speed_l: i8,
1614        speed_r: i8,
1615        max_power: u8,
1616        end_state: EndState,
1617        use_acc_profile: bool,
1618        use_dec_profile: bool,
1619    },
1620    GotoAbsolutePosition {
1621        abs_pos: i32,
1622        speed: i8,
1623        max_power: u8,
1624        end_state: EndState,
1625        use_acc_profile: bool,
1626        use_dec_profile: bool,
1627    },
1628    GotoAbsolutePosition2 {
1629        abs_pos1: i32,
1630        abs_pos2: i32,
1631        speed: i8,
1632        max_power: u8,
1633        end_state: EndState,
1634        use_acc_profile: bool,
1635        use_dec_profile: bool,
1636    },
1637    PresetEncoder2 {
1638        left_position: i32,
1639        right_position: i32,
1640    },
1641    WriteDirect(WriteDirectPayload),
1642    WriteDirectModeData(WriteDirectModeDataPayload),
1643}
1644
1645impl PortOutputSubcommand {
1646    pub const POWER_FLOAT: i8 = 0;
1647    pub const POWER_BRAKE: i8 = 127;
1648
1649    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1650        use PortOutputSubcommand::*;
1651
1652        let subcomm = next!(msg);
1653        trace!("Port output subcommand: {:x}", subcomm);
1654        Ok(match subcomm {
1655            0x02 => {
1656                // StartPower(Power1, Power2)
1657                let power1 = Power::parse(&mut msg)?;
1658                let power2 = Power::parse(&mut msg)?;
1659                StartPower2 { power1, power2 }
1660            }
1661            0x05 => {
1662                // SetAccTime(Time, ProfileNo)
1663                let time = next_i16!(msg);
1664                let profile_number = next_i8!(msg);
1665                SetAccTime {
1666                    time,
1667                    profile_number,
1668                }
1669            }
1670            0x06 => {
1671                // SetDecTime(Time, ProfileNo)
1672                let time = next_i16!(msg);
1673                let profile_number = next_i8!(msg);
1674                SetDecTime {
1675                    time,
1676                    profile_number,
1677                }
1678            }
1679            0x07 => {
1680                // StartSpeed(Speed, MaxPower, UseProfile)
1681                let speed = next_i8!(msg);
1682                let max_power = next!(msg);
1683                let use_prof = next!(msg);
1684                let use_acc_profile = (use_prof & 0x01) != 0;
1685                let use_dec_profile = (use_prof & 0x02) != 0;
1686                StartSpeed {
1687                    speed,
1688                    max_power,
1689                    use_acc_profile,
1690                    use_dec_profile,
1691                }
1692            }
1693            0x08 => {
1694                // StartSpeed(Speed1, Speed2, MaxPower, UseProfile)
1695                let speed1 = next_i8!(msg);
1696                let speed2 = next_i8!(msg);
1697                let max_power = next!(msg);
1698                let use_prof = next!(msg);
1699                let use_acc_profile = (use_prof & 0x01) != 0;
1700                let use_dec_profile = (use_prof & 0x02) != 0;
1701                StartSpeed2 {
1702                    speed1,
1703                    speed2,
1704                    max_power,
1705                    use_acc_profile,
1706                    use_dec_profile,
1707                }
1708            }
1709            0x09 => {
1710                // StartSpeedForTime (Time, Speed, MaxPower, EndState, UseProfile)
1711                let time = next_i16!(msg);
1712                let speed = next_i8!(msg);
1713                let max_power = next!(msg);
1714                let end_state = EndState::parse(&mut msg)?;
1715                let use_prof = next!(msg);
1716                let use_acc_profile = (use_prof & 0x01) != 0;
1717                let use_dec_profile = (use_prof & 0x02) != 0;
1718                StartSpeedForTime {
1719                    time,
1720                    speed,
1721                    max_power,
1722                    end_state,
1723                    use_acc_profile,
1724                    use_dec_profile,
1725                }
1726            }
1727            0x0a => {
1728                // StartSpeedForTime(Time, SpeedL, SpeedR, MaxPower, EndState,
1729                // UseProfile)
1730                let time = next_i16!(msg);
1731                let speed_l = next_i8!(msg);
1732                let speed_r = next_i8!(msg);
1733                let max_power = next!(msg);
1734                let end_state = EndState::parse(&mut msg)?;
1735                let use_prof = next!(msg);
1736                let use_acc_profile = (use_prof & 0x01) != 0;
1737                let use_dec_profile = (use_prof & 0x02) != 0;
1738                StartSpeedForTime2 {
1739                    time,
1740                    speed_l,
1741                    speed_r,
1742                    max_power,
1743                    end_state,
1744                    use_acc_profile,
1745                    use_dec_profile,
1746                }
1747            }
1748            0x0b => {
1749                // StartSpeedForDegrees (Degrees, Speed, MaxPower, EndState,
1750                // UseProfile)
1751                let degrees = next_i32!(msg);
1752                let speed = next_i8!(msg);
1753                let max_power = next!(msg);
1754                let end_state = EndState::parse(&mut msg)?;
1755                let use_prof = next!(msg);
1756                let use_acc_profile = (use_prof & 0x01) != 0;
1757                let use_dec_profile = (use_prof & 0x02) != 0;
1758                StartSpeedForDegrees {
1759                    degrees,
1760                    speed,
1761                    max_power,
1762                    end_state,
1763                    use_acc_profile,
1764                    use_dec_profile,
1765                }
1766            }
1767            0x0c => {
1768                // StartSpeedForDegrees2 (Degrees, SpeedL, SpeedR, MaxPower,
1769                // EndState, UseProfile)
1770                let degrees = next_i32!(msg);
1771                let speed_l = next_i8!(msg);
1772                let speed_r = next_i8!(msg);
1773                let max_power = next!(msg);
1774                let end_state = EndState::parse(&mut msg)?;
1775                let use_prof = next!(msg);
1776                let use_acc_profile = (use_prof & 0x01) != 0;
1777                let use_dec_profile = (use_prof & 0x02) != 0;
1778                StartSpeedForDegrees2 {
1779                    degrees,
1780                    speed_l,
1781                    speed_r,
1782                    max_power,
1783                    end_state,
1784                    use_acc_profile,
1785                    use_dec_profile,
1786                }
1787            }
1788            0x0d => {
1789                // GotoAbsolutePosition(AbsPos, Speed, MaxPower, EndState,
1790                // UseProfile)
1791                let abs_pos = next_i32!(msg);
1792                let speed = next_i8!(msg);
1793                let max_power = next!(msg);
1794                let end_state = EndState::parse(&mut msg)?;
1795                let use_prof = next!(msg);
1796                let use_acc_profile = (use_prof & 0x01) != 0;
1797                let use_dec_profile = (use_prof & 0x02) != 0;
1798                GotoAbsolutePosition {
1799                    abs_pos,
1800                    speed,
1801                    max_power,
1802                    end_state,
1803                    use_acc_profile,
1804                    use_dec_profile,
1805                }
1806            }
1807            0x0e => {
1808                // GotoAbsolutePosition(AbsPos1, AbsPos2, Speed, MaxPower,
1809                // EndState, UseProfile)
1810                let abs_pos1 = next_i32!(msg);
1811                let abs_pos2 = next_i32!(msg);
1812                let speed = next_i8!(msg);
1813                let max_power = next!(msg);
1814                let end_state = EndState::parse(&mut msg)?;
1815                let use_prof = next!(msg);
1816                let use_acc_profile = (use_prof & 0x01) != 0;
1817                let use_dec_profile = (use_prof & 0x02) != 0;
1818                GotoAbsolutePosition2 {
1819                    abs_pos1,
1820                    abs_pos2,
1821                    speed,
1822                    max_power,
1823                    end_state,
1824                    use_acc_profile,
1825                    use_dec_profile,
1826                }
1827            }
1828            0x14 => {
1829                // PresetEncoder(LeftPosition, RightPosition)
1830                let left_position = next_i32!(msg);
1831                let right_position = next_i32!(msg);
1832                PresetEncoder2 {
1833                    left_position,
1834                    right_position,
1835                }
1836            }
1837            0x50 => {
1838                // WriteDirect(Byte[0],Byte[0 + n])
1839                let data = WriteDirectPayload::parse(&mut msg)?;
1840                WriteDirect(data)
1841            }
1842            0x51 => {
1843                // WriteDirectModeData(Mode, PayLoad[0] PayLoad [0 + n]
1844                let data = WriteDirectModeDataPayload::parse(&mut msg)?;
1845                WriteDirectModeData(data)
1846            }
1847            c => {
1848                return Err(Error::ParseError(format!(
1849                    "Invalid port output subcommand {}",
1850                    c
1851                )))
1852            }
1853        })
1854    }
1855}
1856
1857#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1858pub enum Speed {
1859    Cw(u8),
1860    Ccw(u8),
1861    Hold,
1862}
1863
1864impl Speed {
1865    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1866        let val = next_i8!(msg);
1867        Speed::from_i8(val)
1868    }
1869
1870    pub fn to_u8(&self) -> u8 {
1871        use Speed::*;
1872        let integer: i8 = match self {
1873            Hold => 0,
1874            Cw(p) => *p as i8,
1875            Ccw(p) => -(*p as i8),
1876        };
1877        integer.to_le_bytes()[0]
1878    }
1879
1880    pub fn from_i8(val: i8) -> Result<Self> {
1881        use Speed::*;
1882        match val {
1883            0 => Ok(Hold),
1884            p if (1..=100).contains(&p) => Ok(Cw(p as u8)),
1885            p if (-100..=-1).contains(&p) => Ok(Ccw((-p) as u8)),
1886            p => Err(Error::ParseError(format!(
1887                "Invalid value for Speed: {}",
1888                p
1889            ))),
1890        }
1891    }
1892}
1893
1894#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1895pub enum Power {
1896    Cw(u8),
1897    Ccw(u8),
1898    Float,
1899    Brake,
1900}
1901
1902impl Power {
1903    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1904        let val = next_i8!(msg);
1905        Power::from_i8(val)
1906    }
1907
1908    pub fn to_u8(&self) -> u8 {
1909        use Power::*;
1910        let integer: i8 = match self {
1911            Float => 0,
1912            Brake => 127,
1913            Cw(p) => *p as i8,
1914            Ccw(p) => -(*p as i8),
1915        };
1916        integer.to_le_bytes()[0]
1917    }
1918
1919    pub fn from_i8(val: i8) -> Result<Self> {
1920        use Power::*;
1921        match val {
1922            0 => Ok(Float),
1923            127 => Ok(Brake),
1924            p if (1..=100).contains(&p) => Ok(Cw(p as u8)),
1925            p if (-100..=-1).contains(&p) => Ok(Ccw((-p) as u8)),
1926            p => Err(Error::ParseError(format!(
1927                "Invalid value for power: {}",
1928                p
1929            ))),
1930        }
1931    }
1932}
1933
1934#[derive(Clone, Debug, PartialEq, Eq)]
1935pub enum WriteDirectPayload {
1936    TiltFactoryCalibration {
1937        orientation: CalibrationOrientation,
1938        pass_code: String,
1939    },
1940    HardwareReset,
1941}
1942
1943impl WriteDirectPayload {
1944    pub fn parse<'a>(_msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1945        todo!()
1946    }
1947}
1948
1949#[repr(i8)]
1950#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
1951pub enum CalibrationOrientation {
1952    LayingFlat = 1,
1953    Standing = 2,
1954}
1955
1956impl CalibrationOrientation {
1957    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
1958        Ok(ok!(Self::from_i8(next_i8!(msg))))
1959    }
1960}
1961
1962#[repr(u8)]
1963#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, Parse)]
1964pub enum EndState {
1965    Float = 0,
1966    Hold = 126,
1967    Brake = 127,
1968}
1969impl EndState {
1970    pub fn to_u8(&self) -> u8 {
1971        use EndState::*;
1972        let integer: u8 = match self {
1973            Float => 0,
1974            Hold => 126,
1975            Brake => 127,
1976        };
1977        integer.to_le_bytes()[0]
1978    }
1979}
1980
1981#[derive(Clone, Debug, PartialEq, Eq)]
1982pub enum WriteDirectModeDataPayload {
1983    StartPower(Power),
1984    // StartPower2 has the "Encoded through WriteDirectModeData"-tag, but it also has a subcommand-id (0x02)
1985    // and is not listed among the WriteDirectModeData-commands. I think the tag is a doc error, so:
1986    // StartPower2{
1987    // power1: Power,
1988    // power2: Power
1989    // },
1990    // i32 as four bytes
1991    PresetEncoder(i32),
1992    TiltImpactPreset(i32),
1993    TiltConfigOrientation(Orientation),
1994    TiltConfigImpact {
1995        impact_threshold: i8,
1996        bump_holdoff: i8,
1997    },
1998    TiltFactoryCalibration(i8),
1999    SetHubColor(i8),
2000    SetHubRgb {
2001        red: u8,
2002        green: u8,
2003        blue: u8,
2004    },
2005    SetVisionSensorColor(i8),
2006}
2007
2008impl WriteDirectModeDataPayload {
2009    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
2010        use WriteDirectModeDataPayload::*;
2011
2012        let mode = next!(msg);
2013        Ok(match mode {
2014            0x01 => {
2015                //Should be 0x00 according to docs? Seems to work, I may be misreading.
2016                // StartPower(Power)
2017                let power = Power::parse(&mut msg)?;
2018                StartPower(power)
2019            }
2020            0x02 => {
2021                // PresetEncoder(Position)
2022                let position = next_i32!(msg);
2023                PresetEncoder(position)
2024            }
2025            0x03 => {
2026                // TiltImpactPreset(PresetValue)
2027                // "Mode 3 impact counts"
2028                let preset_value = next_i32!(msg);
2029                TiltImpactPreset(preset_value)
2030            }
2031            0x05 => {
2032                // TiltConfigOrientation(Orientation)
2033                // "Mode 5"
2034                let orientation = Orientation::parse(&mut msg)?;
2035                TiltConfigOrientation(orientation)
2036            }
2037            0x06 => {
2038                // TiltConfigImpact(ImpactThreshold, BumpHoldoff)
2039                // "Mode 6"
2040                let impact_threshold = next_i8!(msg);
2041                let bump_holdoff = next_i8!(msg);
2042                TiltConfigImpact {
2043                    impact_threshold,
2044                    bump_holdoff,
2045                }
2046            }
2047            0x07 => {
2048                // TiltFactoryCalibration(Orientation, CalibrationPassCode)  Passcode is 12 chars: Calib-Sensor
2049                // "Mode 7"
2050                let orientation = next_i8!(msg);
2051                // let passcode = next_i8!(msg);
2052                TiltFactoryCalibration(orientation)
2053            }
2054            0x08 => {
2055                // SetHubColor(ColorNo)
2056                let col = next_i8!(msg);
2057                SetHubColor(col)
2058            }
2059            0x09 => {
2060                // SetHubRgb(RedColor, GreenColor, BlueColor)
2061                let red = next!(msg);
2062                let green = next!(msg);
2063                let blue = next!(msg);
2064                SetHubRgb { red, green, blue }
2065            }
2066            m => {
2067                return Err(Error::ParseError(format!(
2068                    "Invalid write direct mode {}",
2069                    m
2070                )))
2071            }
2072        })
2073    }
2074
2075    pub fn serialise(&self, meta: &PortOutputCommandFormat) -> Vec<u8> {
2076        use WriteDirectModeDataPayload::*;
2077        match self {
2078            SetHubRgb { red, green, blue } => {
2079                let startup_and_completion =
2080                    meta.startup_info.serialise(&meta.completion_info);
2081                vec![
2082                    0,
2083                    0, // hub id
2084                    MessageType::PortOutputCommand as u8,
2085                    meta.port_id,
2086                    startup_and_completion,
2087                    0x51, // WriteDirect
2088                    // Docs says to insert an 0x00 and then an extra 0x51 here, but works without it
2089                    crate::iodevice::modes::HubLed::RGB_O,
2090                    *red,
2091                    *green,
2092                    *blue,
2093                ]
2094            }
2095            SetHubColor(c) => {
2096                let startup_and_completion =
2097                    meta.startup_info.serialise(&meta.completion_info);
2098                vec![
2099                    0,
2100                    0, // hub id
2101                    MessageType::PortOutputCommand as u8,
2102                    meta.port_id,
2103                    startup_and_completion,
2104                    0x51, // WriteDirect
2105                    crate::iodevice::modes::HubLed::COL_O,
2106                    *c as u8,
2107                ]
2108            }
2109            StartPower(p) => {
2110                let startup_and_completion =
2111                    meta.startup_info.serialise(&meta.completion_info);
2112                let power = p.to_u8();
2113                vec![
2114                    0,
2115                    0, // hub id
2116                    MessageType::PortOutputCommand as u8,
2117                    meta.port_id,
2118                    startup_and_completion,
2119                    0x51, // WriteDirect
2120                    crate::iodevice::modes::InternalMotorTacho::POWER,
2121                    power,
2122                ]
2123            }
2124            PresetEncoder(position) => {
2125                let startup_and_completion =
2126                    meta.startup_info.serialise(&meta.completion_info);
2127                let pos_bytes: [u8; 4] = position.to_le_bytes(); // i32 sent as 4 bytes
2128                vec![
2129                    0,
2130                    0, // hub id
2131                    MessageType::PortOutputCommand as u8,
2132                    meta.port_id,
2133                    startup_and_completion,
2134                    0x51, // WriteDirect
2135                    crate::iodevice::modes::InternalMotorTacho::POS,
2136                    pos_bytes[0],
2137                    pos_bytes[1],
2138                    pos_bytes[2],
2139                    pos_bytes[3],
2140                ]
2141            }
2142            // Set the Tilt into TiltImpactCount mode (0x03) and change (preset) the value to PresetValue.
2143            TiltImpactPreset(preset_value) => {
2144                let startup_and_completion =
2145                    meta.startup_info.serialise(&meta.completion_info);
2146                let val_bytes: [u8; 4] = preset_value.to_le_bytes(); // i32 sent as 4 bytes
2147                vec![
2148                    0,
2149                    0, // hub id
2150                    MessageType::PortOutputCommand as u8,
2151                    meta.port_id,
2152                    startup_and_completion,
2153                    0x51, // WriteDirect
2154                    crate::iodevice::modes::InternalTilt::IMPCT,
2155                    val_bytes[0],
2156                    val_bytes[1],
2157                    val_bytes[2],
2158                    val_bytes[3],
2159                ]
2160            }
2161            // Set the Tilt into TiltOrientation mode (0x05) and set the Orientation value to Orientation
2162            TiltConfigOrientation(orientation) => {
2163                let startup_and_completion =
2164                    meta.startup_info.serialise(&meta.completion_info);
2165                vec![
2166                    0,
2167                    0, // hub id
2168                    MessageType::PortOutputCommand as u8,
2169                    meta.port_id,
2170                    startup_and_completion,
2171                    0x51, // WriteDirect
2172                    crate::iodevice::modes::InternalTilt::OR_CF,
2173                    *orientation as u8,
2174                ]
2175            }
2176            // Setup Tilt ImpactThreshold and BumpHoldoff by entering mode 6 and use the payload ImpactThreshold and BumpHoldoff.
2177            TiltConfigImpact {
2178                impact_threshold,
2179                bump_holdoff,
2180            } => {
2181                let startup_and_completion =
2182                    meta.startup_info.serialise(&meta.completion_info);
2183                vec![
2184                    0,
2185                    0, // hub id
2186                    MessageType::PortOutputCommand as u8,
2187                    meta.port_id,
2188                    startup_and_completion,
2189                    0x51, // WriteDirect
2190                    crate::iodevice::modes::InternalTilt::IM_CF,
2191                    *impact_threshold as u8,
2192                    *bump_holdoff as u8,
2193                ]
2194            }
2195            //  Sets the actual orientation in the montage automat.  0: XY (laying flat) 1: Z (standing long direction)
2196            TiltFactoryCalibration(orientation) => {
2197                let startup_and_completion =
2198                    meta.startup_info.serialise(&meta.completion_info);
2199                vec![
2200                    0,
2201                    0, // hub id
2202                    MessageType::PortOutputCommand as u8,
2203                    meta.port_id,
2204                    startup_and_completion,
2205                    0x51, // WriteDirect
2206                    crate::iodevice::modes::InternalTilt::CALIB,
2207                    *orientation as u8,
2208                    b'C',
2209                    b'a',
2210                    b'l',
2211                    b'i',
2212                    b'b',
2213                    b'-',
2214                    b'S',
2215                    b'e',
2216                    b'n',
2217                    b's',
2218                    b'o',
2219                    b'r',
2220                ]
2221            }
2222            SetVisionSensorColor(c) => {
2223                let startup_and_completion =
2224                    meta.startup_info.serialise(&meta.completion_info);
2225                vec![
2226                    0,
2227                    0, // hub id
2228                    MessageType::PortOutputCommand as u8,
2229                    meta.port_id,
2230                    startup_and_completion,
2231                    0x51, // WriteDirect
2232                    crate::iodevice::modes::VisionSensor::COL_O,
2233                    *c as u8,
2234                ]
2235            }
2236        }
2237    }
2238}
2239
2240#[repr(i8)]
2241#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
2242pub enum Orientation {
2243    Bottom = 0,
2244    Front = 1,
2245    Back = 2,
2246    Left = 3,
2247    Right = 4,
2248    Top = 5,
2249    UseActualAsBottomReference = 6,
2250}
2251
2252impl Orientation {
2253    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
2254        Ok(ok!(Self::from_i8(next_i8!(msg))))
2255    }
2256}
2257
2258#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2259pub struct PortOutputCommandFeedbackFormat {
2260    pub msg1: FeedbackMessage,
2261    pub msg2: Option<FeedbackMessage>,
2262    pub msg3: Option<FeedbackMessage>,
2263}
2264
2265impl PortOutputCommandFeedbackFormat {
2266    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
2267        let msg1 = FeedbackMessage::parse(&mut msg)?;
2268        let msg2 = FeedbackMessage::parse(&mut msg).ok();
2269        let msg3 = FeedbackMessage::parse(&mut msg).ok();
2270        Ok(PortOutputCommandFeedbackFormat { msg1, msg2, msg3 })
2271    }
2272}
2273
2274#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2275pub struct FeedbackMessage {
2276    pub port_id: u8,
2277    pub empty_cmd_in_progress: bool, // Command in progress, queue empty
2278    pub empty_cmd_completed: bool,   // Command in progress, queue full
2279    pub discarded: bool,             // Command discarded
2280    pub idle: bool, // Nothing in progress, buffer empty. (“Idle”)
2281    pub busy_full: bool, // Command in progress, buffer full (“Busy/Full”)
2282}
2283
2284impl FeedbackMessage {
2285    pub fn parse<'a>(mut msg: impl Iterator<Item = &'a u8>) -> Result<Self> {
2286        let port_id = next!(msg);
2287        let bitfields = next!(msg);
2288        let empty_cmd_in_progress = (bitfields & 0x01) != 0;
2289        let empty_cmd_completed = (bitfields & 0x02) != 0;
2290        let discarded = (bitfields & 0x04) != 0;
2291        let idle = (bitfields & 0x08) != 0;
2292        let busy_full = (bitfields & 0x10) != 0;
2293        Ok(FeedbackMessage {
2294            port_id,
2295            empty_cmd_in_progress,
2296            empty_cmd_completed,
2297            discarded,
2298            idle,
2299            busy_full,
2300        })
2301    }
2302}