rcx 0.1.3

Communicate with LEGO RCX bricks
Documentation
#![allow(clippy::tabs_in_doc_comments)]

use crate::Error;
use std::ops::BitOr;

/// This section describes the available sources and arguments.
///
/// Sources are like addressing modes. They specify where and how to get certain operand values.
///
/// There are 16 sources available, of which 13 apply to the RCX
#[repr(u8)]
pub enum SourceType {
    /// Returns value of specified variable.
    Variable = 0,
    /// Returns value of specified timer, in 1/100ths of a second.
    Timer = 1,
    /// Returns specified immediate value.
    Immediate = 2,
    /// Returns state of specified motor. See below.
    MotorState = 3,
    /// Returns random value, 0..max.
    Random = 4,
    // 5,6,7 reserved for Cybermaster
    /// Returns current program number.
    CurrentProgram = 8,
    /// Returns value of specified sensor.
    SensorValue = 9,
    /// Returns type of specified sensor.
    SensorType = 10,
    /// Returns mode of specified sensor.
    SensorMode = 11,
    /// Returns raw value of specified sensor, 0..1023.
    RawSensorValue = 12,
    /// Returns boolean value of specified sensor, 0..1.
    BooleanSensorValue = 13,
    /// Returns minutes since power on.
    Clock = 14,
    /// Returns value of message buffer.
    Message = 15,
}

/// Motor state is encoded as a single byte. Bits 0-2 contain the motor
/// power, 0..7. The remaining bits are used as follows:
/// ```text
/// Bit	Description	Notes
/// 0x08	Forward flag	0 if forward, 1 if reverse.
/// 0x40	Off flag	1 if off.
/// 0x80	On flag	1 if on.
///```
/// If both bit 0x40 and bit 0x80 are 0, the specified motor is set to
/// float.
pub struct MotorState {
    pub power: u8,
    pub direction: MotorDirection,
    pub state: MotorPowerState,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MotorDirection {
    Forward,
    Reverse,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MotorPowerState {
    On,
    Off,
    Float,
}

impl TryFrom<u8> for MotorState {
    type Error = Error;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        const DIRECTION_BIT: u8 = 0x08;
        const OFF_FLAG: u8 = 0x40;
        const ON_FLAG: u8 = 0x80;

        let power = value & 0b0111;

        let direction = if value & DIRECTION_BIT == 0 {
            MotorDirection::Forward
        } else {
            MotorDirection::Reverse
        };

        let state = match (value & OFF_FLAG, value & ON_FLAG) {
            (0, 0) => MotorPowerState::Float,
            (_, 0) => MotorPowerState::Off,
            (0, _) => MotorPowerState::On,
            _ => panic!("Invalid motor power state"),
        };

        Ok(Self {
            power,
            direction,
            state,
        })
    }
}

/**
There are six avaiable sound types:
```text
    Index	Description
    0	Blip
    1	Beep beep
    2	Downward tones
    3	Upward tones
    4	Low buzz
    5	Fast upward tones
```
*/
#[repr(u8)]
pub enum Sound {
    Blip = 0,
    BeepBeep = 1,
    DownwardTones = 2,
    UpwardTones = 3,
    LowBuzz = 4,
    FastUpwardTones = 5,
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct MotorSelection {
    pub(crate) bitfield: u8,
}

impl MotorSelection {
    pub const A: Self = Self { bitfield: 0x01 };
    pub const B: Self = Self { bitfield: 0x02 };
    pub const C: Self = Self { bitfield: 0x04 };
}

impl BitOr for MotorSelection {
    type Output = Self;
    fn bitor(self, rhs: Self) -> Self::Output {
        Self {
            bitfield: self.bitfield | rhs.bitfield,
        }
    }
}

/**
    Set the slope and mode of sensor number sensor to the value specified by mode, and clear that sensor's value. The bits of mode are split into two portions. Bits 0-4 contain a slope value in 0..31, while bits 5-7 contain the mode, 0..7. The eight modes, which control the value returned by the sensor, are:
    ```text
        Mode	Name	Description
        0	Raw	Value in 0..1023.
        1	Boolean	Either 0 or 1.
        2	Edge count	Number of boolean transitions.
        3	Pulse count	Number of boolean transitions divided by two.
        4	Percentage	Raw value scaled to 0..100.
        5	Temperature in °C	1/10ths of a degree, -19.8..69.5.
        6	Temperature in °F	1/10ths of a degree, -3.6..157.1.
        7	Angle	1/16ths of a rotation, represented as a signed short.
    ```

    The slope value controls 0/1 detection for the three boolean modes. A slope of 0 causes raw sensor values greater than 562 to cause a transition to 0 and raw sensor values less than 460 to cause a transition to 1. The hysteresis prevents bouncing between 0 and 1 near the transition point. A slope value in 1..31, inclusive, causes a transition to 0 or to 1 whenever the difference between consecutive raw sensor values exceeds the slope. Increases larger than the slope result in 0 transitions, while decreases larger than the slope result in 1 transitions. Note the inversions: high raw values correspond to a boolean 0, while low raw values correspond to a boolean 1.
*/
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum SensorMode {
    Raw = 0,
    Boolean,
    EdgeCount,
    PulseCount,
    Percentage,
    TemperatureC,
    TemperatureF,
    Angle,
}

/**
    ```text
        Type	Description	Default Mode
        0	Raw	Raw
        1	Touch	Boolean
        2	Temperature	Temperature in °C
        3	Light	Percentage
        4	Rotation	Angle
    ```
*/
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum SensorType {
    Raw = 0,
    Touch,
    Temperature,
    Light,
    Rotation,
}

/// Set the transmitter range. 0 indicates short range, 1 indicates long
/// range. Other values are ignored.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum TransmitterRange {
    Short = 0,
    Long = 1,
}