#![warn(missing_docs)]
use serialport::{SerialPort, TTYPort};
use std::{
io::{self, BufRead, BufReader},
time::Duration,
};
use thiserror::Error;
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum KelError {
#[error("Serial port error: {0}")]
Serial(#[from] serialport::Error),
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Failed to parse float value: {0}")]
ParseFloat(#[from] std::num::ParseFloatError),
#[error("Received invalid UTF-8 data from device: {0}")]
Utf8(#[from] std::string::FromUtf8Error),
#[error("Value set incorrectly on the device: {0}")]
ValueError(String),
#[error("Device communication error: {0}")]
DeviceError(String),
#[error("Device is not a KEL103")]
DeviceModel(String),
}
type Result<T> = std::result::Result<T, KelError>;
pub struct Kel103 {
port_write: Box<dyn SerialPort>,
port_read: BufReader<TTYPort>,
}
impl Kel103 {
pub fn new(serial_port: &str, baud_rate: u32) -> Result<Self> {
let port = serialport::new(serial_port, baud_rate)
.timeout(Duration::from_secs(1))
.open_native()?;
let (port_write, port_read) = (port.try_clone()?, BufReader::new(port));
let mut this = Kel103 {
port_write,
port_read,
};
let info = this.device_info()?;
if !info.contains("KEL103") {
return Err(KelError::DeviceModel(info));
};
Ok(this)
}
pub fn device_info(&mut self) -> Result<String> {
self.send_recv(b"*IDN?")
}
pub fn measure_volt(&mut self) -> Result<f32> {
let s = self.send_recv(b":MEAS:VOLT?")?;
let val_str = s.trim_end_matches(['V', '\n', '\r'].as_ref()).trim();
val_str.parse::<f32>().map_err(KelError::from) }
pub fn measure_set_volt(&mut self) -> Result<f32> {
let s = self.send_recv(b":VOLT?")?;
let val_str = s.trim_end_matches(['V', '\n', '\r'].as_ref()).trim();
val_str.parse::<f32>().map_err(KelError::from)
}
pub fn set_volt(&mut self, voltage: f32) -> Result<()> {
let cmd = format!(":VOLT {:.3}V", voltage); self.send(cmd.as_bytes())?;
let set_v = self.measure_set_volt()?;
if (set_v - voltage).abs() > 1e-9 {
return Err(KelError::ValueError(format!(
"Voltage set incorrectly on the device. Expected {}, got {}",
voltage, set_v
)));
}
Ok(())
}
pub fn measure_power(&mut self) -> Result<f32> {
let s = self.send_recv(b":MEAS:POW?")?;
let val_str = s.trim_end_matches(['W', '\n', '\r'].as_ref()).trim();
val_str.parse::<f32>().map_err(KelError::from)
}
pub fn measure_set_power(&mut self) -> Result<f32> {
let s = self.send_recv(b":POW?")?;
let val_str = s.trim_end_matches(['W', '\n', '\r'].as_ref()).trim();
val_str.parse::<f32>().map_err(KelError::from)
}
pub fn set_power(&mut self, power: f32) -> Result<()> {
let cmd = format!(":POW {:.3}W", power);
self.send(cmd.as_bytes())?;
let set_p = self.measure_set_power()?;
if (set_p - power).abs() > 1e-9 {
return Err(KelError::ValueError(format!(
"Power set incorrectly on the device. Expected {}, got {}",
power, set_p
)));
}
Ok(())
}
pub fn measure_current(&mut self) -> Result<f32> {
let s = self.send_recv(b":MEAS:CURR?")?;
let val_str = s.trim_end_matches(['A', '\n', '\r'].as_ref()).trim();
val_str.parse::<f32>().map_err(KelError::from)
}
pub fn measure_set_current(&mut self) -> Result<f32> {
let s = self.send_recv(b":CURR?")?;
let val_str = s.trim_end_matches(['A', '\n', '\r'].as_ref()).trim();
val_str.parse::<f32>().map_err(KelError::from)
}
pub fn set_current(&mut self, current: f32) -> Result<()> {
let cmd = format!(":CURR {:.3}A", current);
self.send(cmd.as_bytes())?;
let set_c = self.measure_set_current()?;
if (set_c - current).abs() > 1e-9 {
return Err(KelError::ValueError(format!(
"Current set incorrectly on the device. Expected {}, got {}",
current, set_c
)));
}
Ok(())
}
pub fn check_output(&mut self) -> Result<bool> {
let s = self.send_recv(b":INP?")?;
if s.contains("OFF") {
Ok(false)
} else if s.contains("ON") {
Ok(true)
} else {
Err(KelError::DeviceError(format!(
"Unexpected response from :INP?: {}",
s
)))
}
}
pub fn set_output(&mut self, state: bool) -> Result<()> {
let cmd = if state { b":INP 1" } else { b":INP 0" };
self.send(cmd)?;
let actual_state = self.check_output()?;
if actual_state != state {
return Err(KelError::ValueError(format!(
"Caution: Output not set correctly. Expected {}, got {}",
state, actual_state
)));
}
Ok(())
}
pub fn set_constant_current(&mut self) -> Result<()> {
self.send(b":FUNC CC")
}
pub fn set_constant_power(&mut self) -> Result<()> {
self.send(b":FUNC CW")
}
pub fn set_constant_resistance(&mut self) -> Result<()> {
self.send(b":FUNC CR")
}
pub fn set_dynamic_mode_cv(
&mut self,
voltage1: f32,
voltage2: f32,
freq: f32,
dutycycle: f32,
) -> Result<()> {
let cmd = format!(
":DYN 1,{:.3}V,{:.3}V,{:.3}HZ,{:.3}%",
voltage1, voltage2, freq, dutycycle
);
self.send(cmd.as_bytes())
}
pub fn set_dynamic_mode_cc(
&mut self,
slope1: f32,
slope2: f32,
current1: f32,
current2: f32,
freq: f32,
dutycycle: f32,
) -> Result<()> {
let cmd = format!(
":DYN 2,{:.3}A/uS,{:.3}A/uS,{:.3}A,{:.3}A,{:.3}HZ,{:.3}%",
slope1, slope2, current1, current2, freq, dutycycle
);
self.send(cmd.as_bytes())
}
pub fn get_dynamic_mode(&mut self) -> Result<String> {
let s = self.send_recv(b":DYN?")?;
Ok(s.trim_end_matches('\n').to_string())
}
fn send_recv(&mut self, message: &[u8]) -> Result<String> {
self.send(message)?;
let mut response_bytes = Vec::new();
self.port_read.read_until(b'\n', &mut response_bytes)?;
let response_str = String::from_utf8(response_bytes)?;
Ok(response_str) }
fn send(&mut self, message: &[u8]) -> Result<()> {
self.port_write.write_all(message)?; self.port_write.write_all(b"\n")?;
self.port_write.flush()?;
Ok(())
}
}