~ruther/map-led-strip

8ae60ccfb8f93b1bc11585503f27fc2a501e6ed9 — František Boháček 1 year, 9 months ago d2155d9
feat: add command handling support
A src/commands.rs => src/commands.rs +6 -0
@@ 0,0 1,6 @@
pub mod hello_world_command;
pub mod command_handler;
pub mod command;
pub mod command_argument;
pub mod set_command;
pub mod reset_command;
\ No newline at end of file

A src/commands/command.rs => src/commands/command.rs +7 -0
@@ 0,0 1,7 @@
use crate::commands::command_argument::CommandArgument;

pub struct Command<'d>
{
    pub full: &'d [char],
    pub parsed_arguments: &'d [CommandArgument<'d>],
}
\ No newline at end of file

A src/commands/command_argument.rs => src/commands/command_argument.rs +62 -0
@@ 0,0 1,62 @@
use esp_println::{print, println};

#[derive(Clone, Copy, Eq, PartialEq)]
pub struct CommandArgument<'d>
{
    pub data: &'d [char],
}

impl<'d> CommandArgument<'d>
{
    pub fn new(data: &'d [char]) -> Self
    {
        Self {
            data
        }
    }

    pub fn chars(&self) -> &'d [char]
    {
        self.data
    }

    pub fn try_to_integer(&self) -> Option<u32>
    {
        let length = self.data.len();
        let mut multiplier = 1u32;
        for _ in 0..length - 1 {
            multiplier *= 10;
        }

        let mut result = 0u32;

        for c in self.chars().iter() {
            let num = (*c as u32) - (b'0' as u32);
            if num > 9 {
                return None;
            }

            result += num * multiplier;
            multiplier /= 10;
        }

        Some(result)
    }

    pub fn compare(&self, to: &str) -> bool {
        if self.data.len() != to.len() {
            return false;
        }

        let to = to.as_bytes();
        for (i, c) in self.data.iter().enumerate() {
            let compare_against = to[i];

            if compare_against != (*c) as u8 {
                return false;
            }
        }

        true
    }
}
\ No newline at end of file

A src/commands/command_handler.rs => src/commands/command_handler.rs +230 -0
@@ 0,0 1,230 @@
use core::slice::IterMut;
use embedded_hal::serial::{Read, Write};
use esp_println::{print, println};
use nb::block;
use nb::Error::{Other, WouldBlock};
use crate::command_handler::CommandHandleError::{CommandNotRead, NotFound};
use crate::command_handler::CommandReadError::{BufferOverflowed, CommandLoadedAlready, UnexpectedEndOfLine};
use crate::commands::command::Command;
use crate::commands::command_argument::CommandArgument;
use crate::map::Map;

pub trait SpecificCommandHandler {
    fn handle(&self, command: &mut CommandData) -> Result<(), CommandHandleError>;
    fn help(&self) -> &'static str;
}

pub struct CommandData<'d, 'a> {
    pub command: Command<'d>,
    pub map: &'d mut Map<'a>,
}

pub struct CommandHandler<'d, const BUFFER_SIZE: usize, const HANDLERS_COUNT: usize> {
    buffer_position: usize,
    command_loaded: bool,
    buffer: [char; BUFFER_SIZE],
    handlers: [(&'d str, &'d dyn SpecificCommandHandler); HANDLERS_COUNT],
    handling_special: u8
}

#[derive(Debug, Eq, PartialEq)]
pub enum CommandReadError {
    UnexpectedEndOfLine,
    BufferOverflowed,
    CommandLoadedAlready,
}

#[derive(Debug, Eq, PartialEq)]
pub enum CommandHandleError {
    NotFound,
    WrongArguments,
    CommandNotRead,
}

impl<'d, const BUFFER_SIZE: usize, const HANDLERS_COUNT: usize> CommandHandler<'d, BUFFER_SIZE, HANDLERS_COUNT> {
    pub fn new(handlers: [(&'d str, &'d dyn SpecificCommandHandler); HANDLERS_COUNT], buffer: [char; BUFFER_SIZE]) -> Self
    {
        Self {
            command_loaded: false,
            handling_special: 0,
            buffer_position: 0,
            buffer,
            handlers,
        }
    }

    pub fn reset(&mut self) -> ()
    {
        self.buffer_position = 0;
        self.command_loaded = false;
        self.handling_special = 0;
    }

    fn handle_special<Serial>(&mut self, serial: &mut Serial, data: u8) -> bool
        where Serial: Read<u8> + Write<u8>
    {
        if self.handling_special > 0 { // some special characters have char length 2.
            self.handling_special -= 1;
            return false;
        }

        match data {
            b'\x08' => { // backspace
                if self.buffer_position > 0 {
                    self.buffer_position -= 1;
                    block!(serial.write(b'\x08')).ok().unwrap();
                    block!(serial.write(b' ')).ok().unwrap();
                    block!(serial.write(b'\x08')).ok().unwrap();
                }
                false
            },
            b'\x1b' => { // other special characters
                self.handling_special = 2;
                false
            },
            _ => true
        }
    }

    pub fn read_command<Serial>(&mut self, serial: &mut Serial) -> nb::Result<(), CommandReadError>
        where Serial: Read<u8> + Write<u8>
    {
        if self.command_loaded {
            return Err(Other(CommandLoadedAlready));
        }

        let read = serial.read();

        if self.buffer_position >= BUFFER_SIZE {
            return Err(Other(BufferOverflowed));
        }

        match read {
            Ok(data) => {
                if self.handle_special(serial, data) {
                    self.buffer[self.buffer_position] = data as char
                } else { // special character handled
                    return Err(WouldBlock);
                }
            }
            Err(_) => return Err(WouldBlock)
        };

        let read = self.buffer[self.buffer_position];
        self.buffer_position += 1;

        block!(serial.write(read as u8)).ok().unwrap();

        if read != '\r' {
            return Err(WouldBlock);
        }

        block!(serial.write(b'\n')).ok().unwrap();

        if self.buffer_position <= 1 {
            self.reset();
            return Err(Other(UnexpectedEndOfLine));
        }

        self.command_loaded = true;
        Ok(())
    }

    fn parse_command<'a>(&self, buffer: &'a [char], args: &'a mut [CommandArgument<'a>]) -> Command<'a>
    {
        let mut last_arg_index = 0;
        let mut length = 0;
        let mut args_iter = args.iter_mut();

        for i in 0..buffer.len() {
            let argument_length = i - last_arg_index;

            if buffer[i] == ' ' {
                if argument_length > 0 {
                    *args_iter.next().unwrap() = CommandArgument::new(&buffer[last_arg_index..i]);
                    length += 1;
                }

                last_arg_index = i + 1;
            }

            if buffer[i] == '\r' || buffer[i] == '\n' {
                if argument_length > 0 {
                    *args_iter.next().unwrap() = CommandArgument::new(&buffer[last_arg_index..i]);
                    length += 1;
                }

                break;
            }
        }

        Command {
            full: buffer,
            parsed_arguments: &args[0..length],
        }
    }

    fn handle_help(&self) -> Result<(), CommandHandleError>
    {
        println!("Available commands:\r");
        for (cmd, handler) in self.handlers {
            println!("  {0} {1}\r", cmd, handler.help());
        }

        Ok(())
    }

    pub fn handle_command(&mut self, map: &mut Map) -> Result<(), CommandHandleError>
    {
        if !self.command_loaded {
            return Err(CommandNotRead);
        }

        let buffer = &self.buffer[0..self.buffer_position];
        let mut args = [CommandArgument::new(buffer); BUFFER_SIZE];

        let command = self.parse_command(buffer, &mut args);

        if command.parsed_arguments.len() == 0 {
            self.reset();
            return Err(NotFound);
        }

        let first_argument = command.parsed_arguments[0];

        if first_argument.compare("HELP") {
            let help_handled = self.handle_help();
            self.reset();
            return help_handled;
        }

        for (handler_command, handler) in self.handlers {
            if !first_argument.compare(handler_command) {
                continue;
            }

            let mut command_data = CommandData {
                command,
                map,
            };

            let handled = handler.handle(&mut command_data);
            self.reset();
            return handled;
        }

        self.reset();
        Err(NotFound)
    }
}

// HELP
// DEFAULT <R> <G> <B>
// SET <X:id or name> <R> <G> <B>
// ALL <R> <G> <B>
// RESET
// ITER [X] [Y] (X start, Y end)
// SNAKE [X] [Y] (X start, Y end)
// RAINBOW -- show rainbow

// WIFI <SSID> <PASS>

M src/main.rs => src/main.rs +61 -0
@@ 3,6 3,7 @@

mod strip;
mod map;
mod commands;

use embedded_hal::serial::{Read, Write};
use esp_backtrace as _;


@@ 13,8 14,14 @@ use hal::uart::TxRxPins;
use nb::block;
use nb::Error::{WouldBlock, Other};
use smart_leds::{RGB8, SmartLedsWrite};
use crate::commands::command_handler::{CommandHandler};
use crate::commands::command_handler;
use crate::commands::hello_world_command::HelloWorldCommand;
use crate::commands::reset_command::ResetCommand;
use crate::commands::set_command::SetCommand;
use crate::map::Map;
use crate::strip::StripTiming;

const LEDS_COUNT: usize = 72;
const COMMAND_BUFFER: usize = 200;



@@ 84,7 91,61 @@ fn main() -> ! {

    let mut rgb_data: [RGB8; 72] = [RGB8 { r: 0, g: 0, b: 0 }; 72];
    let mut map = map::Map::new(&map::INDEX_MAP, &mut rgb_data);
    let mut handler = CommandHandler::new(
        [
        ],
        ['\0'; COMMAND_BUFFER],
    );

    block!(serial.write(b'>')).ok().unwrap();
    block!(serial.write(b' ')).ok().unwrap();
    loop {
        let handled = match handler.read_command(&mut serial) {
            Ok(()) => {
                println!("\r");
                let result = handler.handle_command(&mut map);

                match result {
                    Ok(()) => Ok(true),
                    Err(err) => Err(err)
                }
            }
            Err(err) => {
                match err {
                    Other(error) => {
                        match error {
                            command_handler::CommandReadError::BufferOverflowed => println!("Command is too long.\r"),
                            command_handler::CommandReadError::UnexpectedEndOfLine => (),
                            command_handler::CommandReadError::CommandLoadedAlready => println!("FATAL: Previous command not processed correctly.\r")
                        };
                        Ok(true)
                    }
                    _ => Ok(false)
                }
            }
        };

        let new_command = match handled {
            Ok(handled) => {
                handled
            }
            Err(err) => {
                match err {
                    command_handler::CommandHandleError::NotFound => println!("Command not found.\r"),
                    command_handler::CommandHandleError::WrongArguments => println!("Wrong arguments.\r"),
                    command_handler::CommandHandleError::CommandNotRead => println!("FATAL: Command is not prepared.\r")
                }
                true
            }
        };

        if new_command {
            println!("\r");
            block!(serial.write(b'>')).ok().unwrap();
            block!(serial.write(b' ')).ok().unwrap();
        }

        strip.write(map.get_map().cloned()).unwrap();
        delay.delay_us(500u32);
    }
}
\ No newline at end of file

Do not follow this link