nxtusb 0.2.0

USB driver for communicating with the NXT brick
Documentation
//! Types and functionality related to system functions

use crate::{DISPLAY_DATA_LEN, DISPLAY_HEIGHT, DISPLAY_WIDTH};

/// Handle identifying an open file on the NXT brick
#[derive(Debug)]
pub struct FileHandle {
    /// Handle identifier
    pub(crate) handle: u8,
    /// Length available for reading/writing
    pub len: u32,
}

/// Handle identifying a file search session
#[derive(Debug)]
pub struct FindFileHandle {
    /// Handle identifier
    pub(crate) handle: u8,
    /// Name of the current discovered file
    pub name: String,
    /// Length of the current discovered file
    pub len: u32,
}

/// Version information from the NXT brick
#[derive(Debug)]
pub struct FwVersion {
    /// Protocol version?
    pub prot: (u8, u8),
    /// Firmware version
    pub fw: (u8, u8),
}

/// Handle identifying an open module iomap
#[derive(Debug)]
pub struct ModuleHandle {
    /// Handle identifier
    pub(crate) handle: u8,
    /// Name of the opened module
    pub name: String,
    /// ID of the opened module
    pub id: u32,
    /// Length of the opened module?
    pub len: u32,
    /// Length of the iomap
    pub iomap_len: u16,
}

/// Information returned by the `GetDeviceInfo` API
#[derive(Debug)]
pub struct DeviceInfo {
    /// Name of the NXT brick
    pub name: String,
    /// Address of the Bluetooth interface
    pub bt_addr: [u8; 6],
    /// Link quality of the 4 possible connected devices
    pub signal_strength: (u8, u8, u8, u8),
    /// Available flash memory for user storage, in bytes
    pub flash: u32,
}

/// Types of buffer that can be read
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum BufType {
    /// USB
    Usb = 0,
    /// High speed UART
    HighSpeed = 1,
}

/// Type alias for the display raster
pub type DisplayRaster = [[u8; DISPLAY_WIDTH]; DISPLAY_HEIGHT];

/// Function to map the display iomap data onto a rectangular array
#[must_use]
pub fn display_data_to_raster(data: &[u8; DISPLAY_DATA_LEN]) -> DisplayRaster {
    // Display data is in a column-major format, one bit per pixel.
    // Target output is row-major format, one byte per pixel

    let mut out = [[0u8; DISPLAY_WIDTH]; DISPLAY_HEIGHT];

    // Each byte encodes a column of 8 pixels. The byte-wide chunks of
    // pixels are arranged left-to-right in strips across the image

    for (idx, chunk) in data.iter().enumerate() {
        let col = idx % DISPLAY_WIDTH;
        let row_base = (idx / DISPLAY_WIDTH) * 8;
        // pull the bits out of the chunk
        for shift in 0..8 {
            let row = row_base + shift;
            let bit = (chunk >> shift) & 0x01;

            out[row][col] = bit;
        }
    }

    out
}

/// Render the display raster into a string for printing to the terminal
#[must_use]
pub fn raster_to_string(raster: &DisplayRaster) -> String {
    raster
        .iter()
        .map(|row| {
            row.iter()
                .map(|&pixel| if pixel == 0 { '.' } else { '#' })
                .chain(std::iter::once('\n'))
                .collect::<String>()
        })
        .collect::<String>()
}

#[cfg(test)]
mod test {
    use super::*;

    // first column is a bit like:
    // 00000000 00000000 00111110 00100000
    const DISPLAY_DATA: [u8; crate::DISPLAY_DATA_LEN] = [
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 32, 62, 0, 46, 42, 58, 0, 62,
        42, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 62, 68, 36, 0,
        0, 56, 84, 84, 84, 8, 0, 8, 84, 84, 84, 32, 0, 4, 62, 68, 36, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 129, 129, 153, 153,
        129, 129, 126, 0, 0, 0, 0, 24, 36, 36, 36, 36, 36, 60, 60, 0, 0, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 2, 4, 2, 127, 0, 156, 160, 96,
        60, 0, 0, 0, 0, 0, 0, 0, 0, 127, 9, 9, 9, 1, 0, 0, 0, 125, 64, 0, 0, 0,
        0, 127, 64, 0, 0, 56, 84, 84, 84, 8, 0, 8, 84, 84, 84, 32, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 224, 160, 160, 160, 160,
        160, 160, 224, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 2,
        250, 10, 250, 74, 202, 74, 74, 74, 74, 74, 74, 74, 122, 74, 250, 66,
        194, 2, 2, 2, 2, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 32, 32, 32,
        224, 128, 128, 224, 32, 32, 32, 224, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 1, 0, 0, 24,
        24, 0, 0, 0, 0, 24, 24, 0, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        255, 0, 255, 0, 255, 0, 255, 2, 254, 2, 2, 2, 2, 2, 2, 2, 3, 2, 255, 2,
        254, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 2, 2, 3, 0, 0, 3,
        2, 130, 202, 251, 120, 120, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16, 32, 32, 32, 34, 36, 36, 36,
        36, 34, 32, 32, 32, 16, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0,
        1, 7, 8, 63, 64, 128, 129, 249, 137, 233, 137, 137, 137, 249, 129, 128,
        128, 127, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 68, 68,
        68, 124, 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ];

    const DISPLAY_RENDERED:&str = "...............................................................................######...............
............#.#.###.###................#.................#...........................#..............
............#.#.#...#.#...............####...###...###..####.........................#.....#######..
............#.#.###.###................#....#...#.#......#....................#..##..#....#.....##..
............#.#...#.#.#................#....####...###...#....................#..##..#....#.....##..
............###.###.###................#.#..#.........#..#.#..................#......#.....#######..
........................................#....###...###....#...................#......#..............
...............................................................................######...............
....................................................................................................
####################################################################################################
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
..........................#...#.............#####...#.....#.........................................
..........................##.##.............#.............#.........................................
..........................#.#.#.#..#........#.......#.....#....###...###............................
..........................#...#.#..#........####....#.....#...#...#.#...............................
..........................#...#.#..#........#.......#.....#...####...###............................
..........................#...#..###........#.......#.....#...#.........#...........................
..........................#...#...#.........#.......##....##...###...###............................
................................##..................................................................
....................................................................................................
......................................########################......................................
......................................#......................#......................................
......................................#.###############......#......................................
......................................#.#.#.........#.#......#......................................
...............########...............#.#.#.........#.#......#..........#####..#####................
...............#......#...............#.#.###############....#..........#...#..#...#................
.............############.............#.#.#.#.........#.#....#..........#...####...###..............
............#............#............#.#.#.#.........#.#....#..........#...#..#...#................
...........#..............#...........#.#.#.###############..#..........#####..#####................
...........#..............#...........#.#.#.#.#.........#.#..#......................................
...........#...##....##...#...........#.#.#.#.#.........#.#..#....................#####.............
...........#...##....##...#...........#.#.#.#.#.........#.#..#.....................####.............
...........#..............#...........#.#.#.#.#.........#.#..#.....................####.............
...........#..............#...........#.#.#.#.#.........#.#..#....................#####.............
...........#..............#...........#.#.#.#.#.........#.#..#...................###..#.............
...........#..............#...........#..##.#..#########..#..#..................###.................
...........#....#....#....#...........#...#.#.............#..#..................##..................
...........#.....####.....#...........#...#.#.............#..#............#####.....................
...........#..............#...........#....##...#######...#..#............#...#.....................
............#............#............#.....#...#.....#...#..#............#...#.....................
.............############.............#.....#...#.#...#...#..#............#...#.....................
......................................#......#..#.#...#...#..#............#####.....................
......................................#.......############...#......................................
";

    #[test]
    fn display_raster() {
        let raster = display_data_to_raster(&DISPLAY_DATA);
        let rendered = raster_to_string(&raster);
        println!("{rendered}");
        assert_eq!(rendered, DISPLAY_RENDERED);
    }
}