use std::time::Duration;
use log::*;
use serialport::{self, TTYPort};
use secop_core::prelude::*;
use secop_derive::ModuleBase;
use crate::support::comm::{CommClient, CommThread, HasComm};
#[derive(ModuleBase)]
#[param(name="status", doc="status", datatype="StatusType", readonly=true)]
#[command(name="communicate", doc="communicate (write/read cycle)",
argtype="Str(1024)", restype="Str(1024)")]
#[command(name="readline", doc="read a message",
argtype="Null", restype="Str(1024)")]
#[command(name="writeline", doc="write a message",
argtype="Str(1024)", restype="Null")]
#[command(name="read", doc="read input buffer",
argtype="Null", restype="Str(1024)")]
#[command(name="write", doc="write raw string",
argtype="Str(1024)", restype="Null")]
#[command(name="multi_communicate", doc="do multiple communicate cycles",
argtype="ArrayOf(1, 16, Tuple2(Str(1024), Double))",
restype="ArrayOf(1, 16, Str(1024))")]
#[param(name="sol", doc="start-of-line", datatype="Str(8)", readonly=true,
default="\"\".into()", swonly=true, visibility="none")]
#[param(name="eol", doc="end-of-line", datatype="Str(8)", readonly=true,
default="\"\\n\".into()", swonly=true, visibility="none")]
#[param(name="timeout", doc="comm timeout", datatype="DoubleFrom(0.)", readonly=true,
default="2.0", swonly=true, visibility="none")]
#[param(name="devfile", doc="device file name", datatype="Str(128)", readonly=true,
mandatory=true, swonly=true, visibility="none")]
#[param(name="baudrate", doc="baud rate", datatype="Int(1200, 230400)", readonly=true,
default="9600", swonly=true, visibility="none")]
pub struct SerialComm {
internals: ModInternals,
cache: SerialCommParamCache,
comm: Option<CommClient<TTYPort>>,
}
impl Module for SerialComm {
fn create(internals: ModInternals) -> Result<Self> {
Ok(SerialComm { internals, cache: Default::default(), comm: None })
}
fn setup(&mut self) -> Result<()> {
let devfile = self.cache.devfile.clone();
if devfile.is_empty() {
return Err(Error::config("need a devfile configured"));
}
let timeout = Duration::from_millis((*self.cache.timeout * 1000.) as u64);
let baudrate = *self.cache.baudrate as u32;
let connect = move || -> Result<(TTYPort, TTYPort)> {
info!("opening {}...", devfile);
let port = serialport::new(&devfile, baudrate)
.timeout(Duration::from_secs(1_000_000_000))
.open_native()
.map_err(|e| Error::comm_failed(e.to_string()))?;
let rport = port.try_clone_native().map_err(|e| Error::comm_failed(e.to_string()))?;
Ok((rport, port))
};
self.comm = Some(CommThread::spawn(
Box::new(connect),
self.cache.sol.as_bytes(),
self.cache.eol.as_bytes(),
timeout,
)?);
Ok(())
}
fn teardown(&mut self) {
self.comm.take();
}
}
impl HasComm for SerialComm {
type IO = TTYPort;
fn get_comm(&self) -> Result<&CommClient<Self::IO>> {
self.comm.as_ref().ok_or_else(|| Error::comm_failed("connection not open"))
}
}