use crate::command::{Command, Response};
use crate::error::Error;
use crate::framing::{encode_into, Parser};
use crate::transport::Transport;
pub struct Session<T> {
transport: T,
parser: Parser,
}
impl<T: Transport> Session<T> {
pub fn new(transport: T) -> Self {
Self {
transport,
parser: Parser::new(),
}
}
pub fn into_transport(self) -> T {
self.transport
}
pub fn send(&mut self, cmd: Command) -> Result<Response, Error<T::Error>> {
let mut frame_buf = [0u8; 32];
let len = encode_into(&cmd, &mut frame_buf)
.map_err(Error::InvalidParam)?;
self.transport
.write(&frame_buf[..len])
.map_err(Error::Transport)?;
let mut byte = [0u8; 1];
let mut rx_frame = [0u8; 32];
let mut rx_len = 0usize;
loop {
let n = self
.transport
.read(&mut byte)
.map_err(Error::Transport)?;
if n == 0 {
return Err(Error::Timeout);
}
let b = byte[0];
if b == b'\r' || b == b'\n' {
if let Some(result) = self.parser.feed(b) {
let sent = &frame_buf[..len.saturating_sub(1)]; if rx_frame[..rx_len] == *sent {
rx_len = 0;
continue;
}
return result.map_err(Error::Parse);
}
rx_len = 0;
} else {
if rx_len < rx_frame.len() {
rx_frame[rx_len] = b;
rx_len += 1;
}
self.parser.feed(b);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::transport::MockTransport;
#[test]
fn session_query_position() {
let mut mock = MockTransport::new();
mock.enqueue_response(b"AZ=180 EL=090\r".to_vec());
let mut session = Session::new(mock);
let resp = session.send(Command::QueryPosition).unwrap();
assert_eq!(resp, Response::Position { az: 180, el: 90 });
}
#[test]
fn session_send_written_bytes() {
let mut mock = MockTransport::new();
mock.enqueue_response(b"+\r".to_vec());
let mut session = Session::new(mock);
session.send(Command::Azimuth(90)).unwrap();
let transport = session.into_transport();
assert_eq!(&transport.written, b"A090\r");
}
#[test]
fn session_device_error_response() {
let mut mock = MockTransport::new();
mock.enqueue_response(b"?\r".to_vec());
let mut session = Session::new(mock);
let resp = session.send(Command::Stop).unwrap();
assert_eq!(resp, Response::Error);
}
#[test]
fn session_echo_suppression() {
let mut mock = MockTransport::new();
mock.enqueue_response(b"C\rAZ=045 EL=010\r".to_vec());
let mut session = Session::new(mock);
let resp = session.send(Command::QueryPosition).unwrap();
assert_eq!(resp, Response::Position { az: 45, el: 10 });
}
#[test]
fn session_keep_alive() {
let mut mock = MockTransport::new();
mock.enqueue_response(b"+\r".to_vec());
let mut session = Session::new(mock);
let resp = session.send(Command::KeepAlive).unwrap();
assert_eq!(resp, Response::Ack);
let t = session.into_transport();
assert_eq!(&t.written, b"?\r");
}
#[test]
fn session_timeout_on_empty_read() {
let mock = MockTransport::new(); let mut session = Session::new(mock);
let err = session.send(Command::QueryPosition).unwrap_err();
assert!(matches!(err, Error::Timeout));
}
#[test]
fn session_query_azimuth() {
let mut mock = MockTransport::new();
mock.enqueue_response(b"AZ270.0\n".to_vec());
let mut session = Session::new(mock);
let resp = session.send(Command::QueryAzimuth).unwrap();
assert_eq!(resp, Response::AzimuthPosition(270));
let t = session.into_transport();
assert_eq!(&t.written, b"AZ\r");
}
#[test]
fn session_query_elevation() {
let mut mock = MockTransport::new();
mock.enqueue_response(b"EL045.0\n".to_vec());
let mut session = Session::new(mock);
let resp = session.send(Command::QueryElevation).unwrap();
assert_eq!(resp, Response::ElevationPosition(45));
let t = session.into_transport();
assert_eq!(&t.written, b"EL\r");
}
#[test]
fn session_query_azimuth_echo_suppression() {
let mut mock = MockTransport::new();
mock.enqueue_response(b"AZ\rAZ270.0\n".to_vec());
let mut session = Session::new(mock);
let resp = session.send(Command::QueryAzimuth).unwrap();
assert_eq!(resp, Response::AzimuthPosition(270));
}
}