Added rotctl protocol parser
This commit is contained in:
commit
b38014686b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
32
Cargo.lock
generated
Normal file
32
Cargo.lock
generated
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radomctld"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "radomctld"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nom = "7.1.3"
|
5
src/main.rs
Normal file
5
src/main.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
mod rotctl;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
278
src/rotctl.rs
Normal file
278
src/rotctl.rs
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
use nom::{
|
||||||
|
branch::alt,
|
||||||
|
bytes::complete::tag,
|
||||||
|
character::complete::{alphanumeric1, i8, multispace1, space1},
|
||||||
|
combinator::{all_consuming, map, rest},
|
||||||
|
error::{context, convert_error, VerboseError},
|
||||||
|
number::complete::float,
|
||||||
|
sequence::{preceded, separated_pair, terminated},
|
||||||
|
IResult, Parser,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum Command {
|
||||||
|
Exit,
|
||||||
|
SetPos(f32, f32),
|
||||||
|
GetPos,
|
||||||
|
Move(Direction, i8),
|
||||||
|
Stop,
|
||||||
|
Park,
|
||||||
|
SetConf(String, String),
|
||||||
|
Reset,
|
||||||
|
GetInfo,
|
||||||
|
DumpState,
|
||||||
|
DumpCaps,
|
||||||
|
SendCmd(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum Direction {
|
||||||
|
UP,
|
||||||
|
DOWN,
|
||||||
|
CW,
|
||||||
|
CCW,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
all_consuming(map(alt((tag("q"), tag("Q"))), |_| Command::Exit)).parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn float_pair(input: &str) -> IResult<&str, (f32, f32), VerboseError<&str>> {
|
||||||
|
context("float_pair", separated_pair(float, multispace1, float)).parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_pos(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
context(
|
||||||
|
"set_pos",
|
||||||
|
all_consuming(map(
|
||||||
|
preceded(
|
||||||
|
terminated(alt((tag("set_pos"), tag("P"))), multispace1),
|
||||||
|
float_pair,
|
||||||
|
),
|
||||||
|
|pair: (f32, f32)| Command::SetPos(pair.0, pair.1),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pos(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
all_consuming(map(alt((tag("get_pos"), tag("p"))), |_| Command::GetPos)).parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn direction(input: &str) -> IResult<&str, Direction, VerboseError<&str>> {
|
||||||
|
context(
|
||||||
|
"direction",
|
||||||
|
alt((
|
||||||
|
map(alt((tag("UP"), tag("2"))), |_| Direction::UP),
|
||||||
|
map(alt((tag("DOWN"), tag("4"))), |_| Direction::DOWN),
|
||||||
|
map(alt((tag("CCW"), tag("LEFT"), tag("8"))), |_| Direction::CCW),
|
||||||
|
map(alt((tag("CW"), tag("RIGHT"), tag("16"))), |_| Direction::CW),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_parameters(input: &str) -> IResult<&str, (Direction, i8), VerboseError<&str>> {
|
||||||
|
context(
|
||||||
|
"move_parameters",
|
||||||
|
separated_pair(direction, multispace1, i8),
|
||||||
|
)
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_command(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
context(
|
||||||
|
"move",
|
||||||
|
all_consuming(map(
|
||||||
|
preceded(
|
||||||
|
terminated(alt((tag("move"), tag("M"))), multispace1),
|
||||||
|
move_parameters,
|
||||||
|
),
|
||||||
|
|pair: (Direction, i8)| Command::Move(pair.0, pair.1),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
all_consuming(map(alt((tag("stop"), tag("S"))), |_| Command::Stop)).parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn park(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
all_consuming(map(alt((tag("park"), tag("K"))), |_| Command::Park)).parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn string_pair(input: &str) -> IResult<&str, (&str, &str), VerboseError<&str>> {
|
||||||
|
context(
|
||||||
|
"string_pair",
|
||||||
|
separated_pair(alphanumeric1, multispace1, alphanumeric1),
|
||||||
|
)
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_conf(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
context(
|
||||||
|
"set_comf",
|
||||||
|
all_consuming(map(
|
||||||
|
preceded(
|
||||||
|
terminated(alt((tag("set_conf"), tag("C"))), multispace1),
|
||||||
|
string_pair,
|
||||||
|
),
|
||||||
|
|pair: (&str, &str)| Command::SetConf(pair.0.to_owned(), pair.1.to_owned()),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
all_consuming(map(alt((tag("reset"), tag("R"))), |_| Command::Reset)).parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_info(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
all_consuming(map(alt((tag("get_info"), tag("_"))), |_| Command::GetInfo)).parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_state(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
all_consuming(map(tag("dump_state"), |_| Command::DumpState)).parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_caps(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
all_consuming(map(alt((tag("dump_caps"), tag("1"))), |_| {
|
||||||
|
Command::DumpCaps
|
||||||
|
}))
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_cmd(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
context(
|
||||||
|
"set_comf",
|
||||||
|
all_consuming(map(
|
||||||
|
preceded(terminated(alt((tag("send_cmd"), tag("w"))), space1), rest),
|
||||||
|
|rest: &str| Command::SendCmd(rest.to_owned()),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command(input: &str) -> IResult<&str, Command, VerboseError<&str>> {
|
||||||
|
context(
|
||||||
|
"command",
|
||||||
|
alt((
|
||||||
|
exit,
|
||||||
|
set_pos,
|
||||||
|
get_pos,
|
||||||
|
move_command,
|
||||||
|
stop,
|
||||||
|
park,
|
||||||
|
set_conf,
|
||||||
|
reset,
|
||||||
|
get_info,
|
||||||
|
dump_state,
|
||||||
|
dump_caps,
|
||||||
|
send_cmd,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use nom::Err as NomErr;
|
||||||
|
|
||||||
|
fn assert_command(input: &str, expected: Command) {
|
||||||
|
let result = command(input);
|
||||||
|
match result {
|
||||||
|
Ok(("", cmd)) => assert_eq!(cmd, expected),
|
||||||
|
Err(err) => match err {
|
||||||
|
NomErr::Incomplete(_) => panic!("Command was ncomplete"),
|
||||||
|
NomErr::Error(err) | NomErr::Failure(err) => {
|
||||||
|
panic!("{}", convert_error(input, err))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_exit() {
|
||||||
|
assert_command("Q", Command::Exit);
|
||||||
|
assert_command("q", Command::Exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_pos() {
|
||||||
|
assert_command("set_pos 180.0 10.0", Command::SetPos(180.0, 10.0));
|
||||||
|
assert_command("P 180.0 10.0", Command::SetPos(180.0, 10.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_pos() {
|
||||||
|
assert_command("get_pos", Command::GetPos);
|
||||||
|
assert_command("p", Command::GetPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_move() {
|
||||||
|
assert_command("M UP 100", Command::Move(Direction::UP, 100));
|
||||||
|
assert_command("M DOWN -1", Command::Move(Direction::DOWN, -1));
|
||||||
|
assert_command("M CCW 42", Command::Move(Direction::CCW, 42));
|
||||||
|
assert_command("M LEFT 42", Command::Move(Direction::CCW, 42));
|
||||||
|
assert_command("M CW 42", Command::Move(Direction::CW, 42));
|
||||||
|
assert_command("M RIGHT 42", Command::Move(Direction::CW, 42));
|
||||||
|
assert_command("move UP 100", Command::Move(Direction::UP, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stop() {
|
||||||
|
assert_command("stop", Command::Stop);
|
||||||
|
assert_command("S", Command::Stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_park() {
|
||||||
|
assert_command("park", Command::Park);
|
||||||
|
assert_command("K", Command::Park);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reset() {
|
||||||
|
assert_command("reset", Command::Reset);
|
||||||
|
assert_command("R", Command::Reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_info() {
|
||||||
|
assert_command("get_info", Command::GetInfo);
|
||||||
|
assert_command("_", Command::GetInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dump_state() {
|
||||||
|
assert_command("dump_state", Command::DumpState);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dump_caps() {
|
||||||
|
assert_command("dump_caps", Command::DumpCaps);
|
||||||
|
assert_command("1", Command::DumpCaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_conf() {
|
||||||
|
assert_command(
|
||||||
|
"set_conf foo bar",
|
||||||
|
Command::SetConf("foo".to_owned(), "bar".to_owned()),
|
||||||
|
);
|
||||||
|
assert_command(
|
||||||
|
"C foo bar",
|
||||||
|
Command::SetConf("foo".to_owned(), "bar".to_owned()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_send_cmd() {
|
||||||
|
assert_command("send_cmd foo bar", Command::SendCmd("foo bar".to_owned()));
|
||||||
|
assert_command("w foo bar", Command::SendCmd("foo bar".to_owned()));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue