new-sonar/firmware/src/command_interface.rs
2023-06-16 16:30:49 -06:00

113 lines
3.4 KiB
Rust

use smoltcp::socket::tcp::{Socket, State};
use crate::proto::{
serialize_response_error, serialize_response_value, ErrorCodes, PacketParser, ResponsePacket,
Settings,
};
pub struct CommandInterface {
parser: PacketParser,
}
const COMMAND_PORT: u16 = 2000;
impl CommandInterface {
pub fn new() -> Self {
Self {
parser: PacketParser::new(),
}
}
/// Run the command interface
pub fn run(&mut self, sock: &mut Socket) {
// Note that we don't attempt to check status of the connection. All of
// the smoltcp functions handle the error cases gracefully. Recv
// provides a buffer of length 0 when there is nothing to receive, so we
// are good there. We just reset on any smoltcp errors.
if !sock.is_open() {
defmt::debug!("Socket has been closed");
// Socket has been closed, some error has occured
sock.listen(COMMAND_PORT);
return;
}
if !sock.can_recv() {
return;
}
// Try and parse a packet
let res = sock.recv(|rx_buf| {
let (res, processed_bytes) = self.parser.try_parse(rx_buf);
(processed_bytes, res.ok())
});
defmt::debug!("Received data");
// Check for socket errors
let packet = match res {
// We got a packet! unwrap it
Ok(Some(packet)) => packet,
// No packet to process, move on
Ok(None) => return,
Err(_) => {
defmt::debug!("rx err");
sock.abort();
return;
}
};
defmt::debug!("Packet rx");
// Check that setting is valid
let setting = match Settings::try_from(packet.setting) {
Ok(v) => v,
Err(()) => {
// Write out an error packet, we'll just ignore errors
let _ = sock.send(|tx_buf| {
if tx_buf.len() < 8 {
// Just drop the packet if we don't have buffer available
return (0, ());
}
let response =
serialize_response_error(packet.setting, ErrorCodes::InvalidSetting);
&tx_buf[0..8].copy_from_slice(&response);
return (8, ());
});
return;
}
};
defmt::debug!(
"Valid packet: {:?}, is_write: {}, value: {}",
packet.setting,
packet.is_write,
packet.value
);
// TODO validate setting values
// TODO handle actually changing/getting settings in all the ways
// For temp testing, return values sent
let res = sock.send(|tx_buf| {
if tx_buf.len() < 8 {
// Since this is a low-BW configuration socket, we assume we have space, and drop
// the response otherwise. If this is an issue, may re-think this and queue commands but
// for now this is fine.
return (0, ());
}
let response = serialize_response_value(setting, packet.value);
&tx_buf[0..8].copy_from_slice(&response);
return (8, ());
});
if res.is_err() {
defmt::debug!("tx err");
sock.abort();
}
}
}