use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write};
type Result = std::io::Result<Vec<String>>;
#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
pub enum RegisteredPlotters {
AxiDrawV3,
AxiDrawV3A3,
}
pub trait Device {
fn execute(&mut self, cmd: &[u8], responses: usize) -> Result;
}
#[derive(Debug)]
pub struct Plotter<P>
where
P: Read + Write,
{
serial: P,
}
impl<P> Plotter<P>
where
P: Read + Write,
{
pub fn new(serial: P) -> Self {
Plotter { serial }
}
fn read_response(&mut self, responses: usize) -> Result {
let mut result = Vec::new();
let mut rdr = BufReader::new(&mut self.serial);
let mut idx = 0;
while idx < responses {
let mut buf = String::new();
let _ = rdr.read_line(&mut buf);
let cleaned = buf.trim().to_owned();
result.push(cleaned);
idx += 1;
}
Ok(result)
}
}
impl<P> Device for Plotter<P>
where
P: Read + Write,
{
fn execute(&mut self, cmd: &[u8], responses: usize) -> Result {
match self.serial.write(&cmd) {
Ok(bytes_written) => {
if bytes_written != cmd.len() {
let e = Error::from(ErrorKind::Other);
return Err(e);
}
}
Err(e) => return Err(e),
};
self.serial
.flush()
.expect("Failed to flush bytes to socket");
self.read_response(responses)
}
}
#[cfg(test)]
mod plotter_tests {
use super::*;
use std::io::Cursor;
struct Mocket {
read_buf: Vec<u8>,
write_buf: Vec<u8>,
}
impl Read for Mocket {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
buf.copy_from_slice(&self.read_buf);
return Ok(self.read_buf.len());
}
}
impl Write for Mocket {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.write_buf.copy_from_slice(&buf);
return Ok(self.write_buf.len());
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Mocket {
fn new() -> Self {
Mocket {
read_buf: Vec::new(),
write_buf: Vec::new(),
}
}
fn new_with_read_write(read_buf: Vec<u8>, write_buf: Vec<u8>) -> Self {
Mocket {
read_buf,
write_buf,
}
}
fn new_with_read(read_buf: Vec<u8>) -> Self {
Self::new_with_read_write(read_buf, Vec::new())
}
fn new_with_write(write_buf: Vec<u8>) -> Self {
Self::new_with_read_write(Vec::new(), write_buf)
}
}
fn make_test_plotter(curs: Cursor<&mut Vec<u8>>) -> Plotter<Cursor<&mut Vec<u8>>> {
Plotter::new(curs)
}
#[test]
fn test_correct_number_of_responses() {
let first = "first";
let second = "second";
let third = "third";
let first_message = format!("{}\n\r", first);
let second_message = format!("{}\n\r{}\n\r{}\n\r", first, second, third);
let first_responses = 1;
let second_responses = 3;
let mut first_data = first_message.into_bytes();
let curs = Cursor::new(&mut first_data);
let mut plotter = make_test_plotter(curs);
let first_expected = vec![first.to_owned()];
match plotter.read_response(first_responses) {
Ok(got) => {
assert_eq!(first_responses, got.len());
assert_eq!(first_expected, got);
}
Err(e) => panic!("Expected OK, got: {}", e),
}
let mut second_data = second_message.into_bytes();
let curs = Cursor::new(&mut second_data);
let mut plotter = make_test_plotter(curs);
let second_expected = vec![first.to_owned(), second.to_owned(), third.to_owned()];
match plotter.read_response(second_responses) {
Ok(got) => {
assert_eq!(second_responses, got.len());
assert_eq!(second_expected, got);
}
Err(e) => panic!("Expected OK, got: {}", e),
}
}
#[test]
fn test_delimiting_and_cleaning() {
let text_one = "hi";
let text_two = "lois";
let responses = 2;
let mut data = format!("{}\r\n\r{}\n\r", text_one, text_two).into_bytes();
let curs = Cursor::new(&mut data);
let mut plotter = make_test_plotter(curs);
let expected = vec![text_one.to_owned(), text_two.to_owned()];
match plotter.read_response(responses) {
Ok(got) => {
assert_eq!(responses, got.len());
assert_eq!(expected, got);
}
Err(e) => panic!("Expected OK, got: {}", e),
}
}
}
#[derive(Debug, Default)]
pub struct ReferenceDevice {
motor1: i32,
motor2: i32,
pen: i8,
}
impl ReferenceDevice {
fn increment_non_negative(&self, m: i32, v: i32) -> i32 {
if (m + v) <= 0 {
0
} else {
m + v
}
}
fn increment_motors(&mut self, im1: i32, im2: i32) {
self.motor1 = self.increment_non_negative(self.motor1, im1);
self.motor2 = self.increment_non_negative(self.motor2, im2);
}
fn toggle_pen(&mut self) {
if self.pen == 1 {
self.pen = 0;
} else {
self.pen = 1;
}
}
fn respond(&mut self, cmd: &str) -> Result {
let parts_vec: Vec<&str> = cmd.trim().split(",").collect();
let ok = String::from("OK");
match &parts_vec[..] {
["V"] => Ok(vec![String::from(
"EBBv13_and_above EB Firmware Version 2.4.2",
)]),
["XM", _duration, motor1, motor2] => match (motor1.parse(), motor2.parse()) {
(Ok(m1), Ok(m2)) => {
println!("incrementing by {} and {}", m1, m2);
self.increment_motors(m1, m2);
Ok(vec![ok])
}
_ => Err(Error::from(ErrorKind::InvalidInput)),
},
["QM"] => {
let resp_str = format!("QM,1,{},{},1", self.motor1, self.motor2);
let resp = vec![resp_str];
Ok(resp)
}
["TP"] => {
self.toggle_pen();
Ok(vec![ok])
}
_ => Err(Error::from(ErrorKind::InvalidData)),
}
}
}
impl Device for ReferenceDevice {
fn execute(&mut self, cmd: &[u8], _responses: usize) -> Result {
let cmd_str =
String::from_utf8(cmd.to_vec()).expect("Passed unparseable garbage! Exploding");
self.respond(&cmd_str)
}
}
#[cfg(test)]
mod reference_device_internal_api_tests {
use super::*;
#[test]
fn test_increment_non_negative() {
let dev = ReferenceDevice::default();
let motor = 5;
let step1 = 1;
let step2 = 5;
let step3 = -10;
let expected1 = 6;
let expected2 = 10;
let expected3 = 0;
assert_eq!(expected1, dev.increment_non_negative(motor, step1));
assert_eq!(expected2, dev.increment_non_negative(motor, step2));
assert_eq!(expected3, dev.increment_non_negative(motor, step3));
}
#[test]
fn test_increment_motors() {
let mut dev = ReferenceDevice::default();
dev.increment_motors(10, 10);
assert_eq!(10, dev.motor1);
assert_eq!(10, dev.motor2);
}
#[test]
fn test_toggle_pen() {
let mut dev = ReferenceDevice::default();
assert_eq!(0, dev.pen);
dev.toggle_pen();
assert_eq!(1, dev.pen);
}
}
#[cfg(test)]
mod reference_device_tests {
use super::*;
use crate::instructions::EBBInstruction;
#[test]
fn test_toggle_pen() {
let mut dev = ReferenceDevice::default();
let inst = EBBInstruction::TogglePen;
match dev.execute(&inst.as_bytes(), 1) {
Ok(resp) => assert_eq!(vec![String::from("OK")], resp),
Err(_) => panic!(),
}
}
#[test]
fn test_mix_axis_and_query_motors() {
let ok = vec![String::from("OK")];
let qm = EBBInstruction::QueryMotors;
let mut dev = ReferenceDevice::default();
let expected_starting_pos = vec![String::from("QM,1,0,0,1")];
let move_1 = EBBInstruction::MixedGeometryMove {
x: 5,
y: 0,
duration: None,
};
let move_2 = EBBInstruction::MixedGeometryMove {
x: 0,
y: 5,
duration: None,
};
let expected_after_mov_1 = vec![String::from("QM,1,5,0,1")];
let expected_after_mov_2 = vec![String::from("QM,1,5,5,1")];
match dev.execute(&qm.as_bytes(), 1) {
Ok(resp) => assert_eq!(expected_starting_pos, resp),
Err(e) => panic!(e),
};
match dev.execute(&move_1.as_bytes(), 1) {
Ok(resp) => assert_eq!(ok, resp),
_ => panic!(),
};
match dev.execute(&qm.as_bytes(), 1) {
Ok(resp) => assert_eq!(expected_after_mov_1, resp),
Err(e) => panic!(e),
}
match dev.execute(&move_2.as_bytes(), 1) {
Ok(resp) => assert_eq!(ok, resp),
_ => panic!(),
};
match dev.execute(&qm.as_bytes(), 1) {
Ok(resp) => assert_eq!(expected_after_mov_2, resp),
Err(e) => panic!(e),
}
}
}