use embedded_hal::serial;
use crate::cmd::{Message, Response, Value};
use crate::errors::{
try_code_from_bytes, DriverError, Mks979bError, ResponseError,
};
use crate::scinumber::SciNumber979b;
use crate::*;
use core::fmt::Write;
use core::str::from_utf8;
use heapless::{String, Vec};
use numtoa::NumToA;
#[derive(PartialEq, Debug)]
pub enum State {
Idle,
SendingMessage(Vec<u8, MAX_STR_LEN>),
Flush,
ReadFirst,
ReadID(usize),
ReadResult(Vec<u8, MAX_STR_LEN>),
ReadTermination(usize, Vec<u8, MAX_STR_LEN>),
Failure,
}
#[derive(Debug)]
pub struct Mks979b<S>
where
S: Serial,
{
port: S,
addr: [u8; ADDR_LEN],
addrnum: usize,
state: State,
last_message: Message,
}
pub type NBResult<T, S> = nb::Result<
T,
DriverError<
<S as serial::Read<u8>>::Error,
<S as serial::Write<u8>>::Error,
>,
>;
pub struct ResponseData {
query: Message,
response: Option<Vec<u8, MAX_STR_LEN>>,
}
fn read<S: Serial>(serial: &mut S) -> NBResult<u8, S> {
serial
.read()
.map_err(|error| error.map(DriverError::SerialRead))
}
fn write<S: Serial>(serial: &mut S, byte: u8) -> NBResult<(), S> {
serial
.write(byte)
.map_err(|error| error.map(DriverError::SerialWrite))
}
fn flush<S: Serial>(serial: &mut S) -> NBResult<(), S> {
serial
.flush()
.map_err(|error| error.map(DriverError::SerialWrite))
}
impl<S> Mks979b<S>
where
S: Serial,
{
pub fn new(port: S, address: u8) -> Mks979b<S> {
let mut addr = [0u8; ADDR_LEN];
address.numtoa(10, &mut addr);
Mks979b {
port,
addr,
addrnum: address as usize,
last_message: Message::None,
state: State::Idle,
}
}
pub fn take_port(self) -> S {
self.port
}
pub fn reconfig_port(&mut self, config: fn(p: &mut S) -> ()) {
config(&mut self.port);
}
pub fn poll(&mut self) -> NBResult<Option<ResponseData>, S> {
let response: Option<ResponseData> = loop {
match &mut self.state {
State::ReadFirst => {
if read(&mut self.port)? == b'@' {
self.state = State::ReadID(0);
} else {
self.state = State::Failure;
}
}
State::ReadID(i) => {
if read(&mut self.port)? == self.addr[*i] {
self.state = if *i < ADDR_LEN - 1 {
State::ReadID(*i + 1)
} else {
State::ReadResult(Vec::new())
};
} else {
self.state = State::Failure;
}
}
State::ReadResult(buffer) => {
let byte = read(&mut self.port)?;
match byte {
b';' => {
self.state = State::ReadTermination(
0,
core::mem::take(buffer),
);
}
ch => {
if buffer.push(ch).is_err() {
self.state = State::Failure
};
}
}
}
State::ReadTermination(i, buffer) => {
match read(&mut self.port)? {
b'F' => {
if *i < 1 {
*i += 1;
} else {
break Some(ResponseData {
query: self.last_message.clone(),
response: Some(core::mem::take(buffer)),
});
}
}
_ => self.state = State::Failure,
}
}
State::Failure => {
while read(&mut self.port).is_ok() {}
self.state = State::Idle;
break Some(ResponseData {
query: self.last_message.clone(),
response: None,
});
}
State::Idle | State::SendingMessage(_) | State::Flush => {
let _ = read(&mut self.port)?;
break None;
}
}
};
self.state = State::Idle;
Ok(response)
}
pub fn schedule_message(&mut self, msg: Message) -> NBResult<(), S> {
match self.state {
State::Idle => {
let mut buffer = Vec::<u8, MAX_STR_LEN>::new();
self.last_message = msg;
write!(
&mut buffer,
"@{}{};FF",
self.addrnum, self.last_message
)
.map_err(|_| DriverError::BadMessage)?;
buffer.reverse();
self.state = State::SendingMessage(buffer);
Ok(())
}
_ => Err(nb::Error::WouldBlock),
}
}
pub fn reschedule_last_message(&mut self) -> NBResult<(), S> {
self.schedule_message(self.last_message.clone())
}
pub fn send_message(&mut self) -> NBResult<(), S> {
loop {
match &mut self.state {
State::SendingMessage(buffer) => {
if let Some(byte) = buffer.last() {
write(&mut self.port, *byte)?;
buffer.pop();
} else {
self.state = State::Flush;
}
}
State::Flush => {
flush(&mut self.port)?;
self.state = State::ReadFirst
}
_ => break,
}
}
Ok(())
}
}
pub fn parse_response(data: &ResponseData) -> Result<Response, ResponseError> {
if let Some(response) = &data.response {
match &response[0..ACKNAK_LEN] {
b"ACK" => match data.query.clone() {
Message::Value(Value::RSDelayON)
| Message::Value(Value::TestLedON)
| Message::Value(Value::ActiveFilamentPowerON)
| Message::Value(Value::SetPointEnabled(_))
| Message::Value(Value::ActiveFilamentON)
| Message::Value(Value::DegasON)
| Message::Setting(cmd::Setting::RSDelayON(_))
| Message::Setting(cmd::Setting::TestLedON(_))
| Message::Setting(cmd::Setting::ActiveFilamentPowerON(_))
| Message::Setting(cmd::Setting::DegasON(_))
| Message::Setting(cmd::Setting::EnableSetPoint(_, _))=> Ok(match &response[ACKNAK_LEN..response.len()] {
b"ON" => Response::Boolean(true),
_ => Response::Boolean(false),
}),
Message::Value(Value::DeviceStatus) => Ok(match &response[ACKNAK_LEN..response.len()] {
b"F" => {
Response::String(String::from("Filament fault, filament cannot turn on"))
}
b"G" => Response::String(String::from("Hot cathode on ")),
b"P" => Response::String(String::from(
"Pressure fault, system pressure above protect pressure",
)),
b"W" => Response::String(String::from(
"Hot Cathode is turning on; pressure reading not valid",
)),
b"D" => Response::String(String::from("Degas ON")),
_ => Response::String(String::from("OK, no errors to report")),
}),
Message::Value(Value::OnChipSensorTemp)
| Message::Value(Value::HotCathodeTemp)
| Message::Value(Value::PressureReadingMicroPirani)
| Message::Value(Value::PressureReadingHotCathode)
| Message::Value(Value::PressureReadingCombined)
| Message::Value(Value::SetPointValue(_))
| Message::Value(Value::SetPointHysteresis(_))
| Message::Setting(cmd::Setting::SetPointValue(_, _))
| Message::Setting(cmd::Setting::SetPointHysteresis(_, _))
| Message::Setting(cmd::Setting::AtmosphericPressure(_))
| Message::Setting(cmd::Setting::VacuumReadoutZero) => {
if let Ok(res) = SciNumber979b::from_ascii(&response[ACKNAK_LEN..response.len()]){
Ok(Response::SciNumber(res))
}else{
Err(ResponseError::FailedToParseResponse)
}
},
_ => {
if let Ok(string) = from_utf8(&response[ACKNAK_LEN..response.len()]) {
Ok(Response::String(String::from(string)))
} else {
Err(ResponseError::FailedToParseResponse)
}
}
},
b"NAK" => {
if let Ok(code) = try_code_from_bytes(&response[ACKNAK_LEN..response.len()]) {
Err(ResponseError::Mks979bError(Mks979bError::get_error(code)))
} else {
Err(ResponseError::FailedToParseResponse)
}
}
_ =>Err(ResponseError::FailedToParseResponse) }
} else {
Err(ResponseError::FailedToParseResponse)
}
}