use std::path::PathBuf;
use std::time::Duration;
use serde::{Deserialize, Serialize};
use crate::controllers::*;
use crate::defaults::{default_command_retries, default_retry_delay};
use crate::drivers::InstrumentError;
use crate::logging_utils::device_info;
use crate::model::SCADADevice;
use crate::state::DeviceState;
type Result<T> = std::result::Result<T, InstrumentError>;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Connection {
pub port: PathBuf,
pub baudrate: usize,
pub timeout: u64,
#[serde(default)]
pub addr: u8,
pub controller_addr: u8,
pub controller: Controller,
}
impl Connection {
pub fn port(&self) -> String {
self.port.as_path().to_str().unwrap().to_string()
}
pub fn addr(&self) -> u8 {
self.addr
}
pub fn controller_addr(&self) -> u8 {
self.controller_addr
}
pub fn controller(&self) -> &Controller {
&self.controller
}
pub fn baudrate(&self) -> &usize {
&self.baudrate
}
pub fn timeout(&self) -> Duration {
Duration::from_millis(self.timeout)
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Device {
pub id: String,
pub name: String,
#[serde(default = "default_command_retries")]
pub command_retries: u8,
#[serde(default = "default_retry_delay")]
pub retry_delay: u64,
pub conn: Connection,
#[serde(default)]
pub state: DeviceState,
}
impl Device {
pub async fn update(&mut self) -> Result<()> {
let total_attempts = self.command_retries + 1;
for i in 1..=total_attempts {
device_info!(
&self,
&format!("updating (attempt {i} of {})", total_attempts)
);
let result = match self.conn.controller {
Controller::STR1 => STR1::update(self).await,
Controller::CN7500 => CN7500::update(self).await,
Controller::Waveshare => Waveshare::update(self).await,
Controller::WaveshareV2 => WaveshareV2::update(self).await,
};
match result {
Ok(_) => return Ok(()),
Err(e) => {
if i == total_attempts {
return Err(e);
}
device_info!(&self, &format!("updating failed, but attempts remain. Waiting for retry_delay = {} ms before trying again.", self.retry_delay));
std::thread::sleep(Duration::from_millis(self.retry_delay));
}
}
}
panic!("Reached some code that shouldn't be reachable. Ran through all iterations of a device update loop without Ok() or Err()");
}
pub async fn enact(&mut self) -> Result<()> {
let total_attempts = self.command_retries + 1;
for i in 1..=total_attempts {
device_info!(
&self,
&format!("enacting (attempt {i} of {})", total_attempts)
);
let result = match self.conn.controller {
Controller::STR1 => STR1::enact(self).await,
Controller::CN7500 => CN7500::enact(self).await,
Controller::Waveshare => Waveshare::enact(self).await,
Controller::WaveshareV2 => WaveshareV2::enact(self).await,
};
match result {
Ok(_) => return Ok(()),
Err(e) => {
if i == total_attempts {
return Err(e);
}
device_info!(&self, &format!("enacting failed, but attempts remain. Waiting for retry_delay = {} ms before trying again.", self.retry_delay));
std::thread::sleep(Duration::from_millis(self.retry_delay));
}
}
}
panic!("Reached some code that shouldn't be reachable. Ran through all iterations of a device enact loop without Ok() or Err()");
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_connection_port() {
let conn = Connection {
port: PathBuf::from("/dev/ttyUSB0"),
baudrate: 19200,
timeout: 200,
controller: Controller::CN7500,
addr: 0,
controller_addr: 22,
};
assert_eq!("/dev/ttyUSB0", conn.port());
assert_ne!(r#""/dev/ttyUSB0""#, conn.port());
}
}