pub mod canboat_csv;
pub mod ikonvert;
pub mod line_gateway;
pub mod maretron;
pub mod ngt1;
pub mod socketcan;
pub mod supervisor;
pub use supervisor::{DeviceFactory, Supervisor};
use std::io::{self, Read, Write};
use std::sync::mpsc;
use std::thread::{self, JoinHandle};
use std::time::{Duration, Instant};
use crate::engine::RawFrame;
#[derive(Debug)]
pub enum DeviceEvent {
Frame(RawFrame),
SendBytes(Vec<u8>),
Error(String),
}
pub trait DeviceDecoder: Send + 'static {
fn decode(&mut self, bytes: &[u8], events: &mut Vec<DeviceEvent>);
}
pub trait DeviceEncoder: Send + Sync + 'static {
fn init_bytes(&self) -> Vec<u8> {
Vec::new()
}
fn keepalive(&self) -> Option<(Duration, Vec<u8>)> {
None
}
fn encode_frame(&self, frame: &RawFrame) -> Option<Vec<u8>>;
}
pub(crate) enum WriterCmd {
Bytes(Vec<u8>),
Frame(RawFrame),
}
pub struct DeviceHandle {
pub frames_rx: mpsc::Receiver<RawFrame>,
cmd_tx: mpsc::Sender<WriterCmd>,
joins: Vec<JoinHandle<()>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DeviceWriterGone;
impl std::fmt::Display for DeviceWriterGone {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("device writer is no longer accepting frames")
}
}
impl std::error::Error for DeviceWriterGone {}
impl DeviceHandle {
pub fn send_frame(&self, frame: RawFrame) -> Result<(), DeviceWriterGone> {
self.cmd_tx
.send(WriterCmd::Frame(frame))
.map_err(|_| DeviceWriterGone)
}
pub fn frame_sender(&self) -> FrameSender {
FrameSender {
cmd_tx: self.cmd_tx.clone(),
}
}
pub fn join(self) {
drop(self.cmd_tx);
for j in self.joins {
let _ = j.join();
}
}
}
#[derive(Clone)]
pub struct FrameSender {
cmd_tx: mpsc::Sender<WriterCmd>,
}
impl FrameSender {
pub fn send_frame(&self, frame: RawFrame) -> Result<(), DeviceWriterGone> {
self.cmd_tx
.send(WriterCmd::Frame(frame))
.map_err(|_| DeviceWriterGone)
}
pub(crate) fn from_cmd_tx(cmd_tx: mpsc::Sender<WriterCmd>) -> Self {
Self { cmd_tx }
}
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub(crate) fn from_parts(
frames_rx: mpsc::Receiver<RawFrame>,
cmd_tx: mpsc::Sender<WriterCmd>,
joins: Vec<JoinHandle<()>>,
) -> DeviceHandle {
DeviceHandle {
frames_rx,
cmd_tx,
joins,
}
}
pub fn run<D: DeviceDecoder, E: DeviceEncoder>(
decoder: D,
encoder: E,
mut reader: Box<dyn Read + Send>,
mut writer: Box<dyn Write + Send>,
) -> DeviceHandle {
let (frames_tx, frames_rx) = mpsc::channel::<RawFrame>();
let (cmd_tx, cmd_rx) = mpsc::channel::<WriterCmd>();
let cmd_tx_for_reader = cmd_tx.clone();
let init = encoder.init_bytes();
let keepalive = encoder.keepalive();
let writer_join = thread::Builder::new()
.name("device-writer".into())
.spawn(move || writer_thread(&mut *writer, encoder, init, keepalive, cmd_rx))
.expect("spawn device writer");
let reader_join = thread::Builder::new()
.name("device-reader".into())
.spawn(move || reader_thread(&mut *reader, decoder, frames_tx, cmd_tx_for_reader))
.expect("spawn device reader");
DeviceHandle {
frames_rx,
cmd_tx,
joins: vec![reader_join, writer_join],
}
}
fn reader_thread<D: DeviceDecoder>(
reader: &mut dyn Read,
mut decoder: D,
frames_tx: mpsc::Sender<RawFrame>,
cmd_tx: mpsc::Sender<WriterCmd>,
) {
let mut buf = [0u8; 4096];
let mut events: Vec<DeviceEvent> = Vec::with_capacity(8);
loop {
match reader.read(&mut buf) {
Ok(0) => return,
Ok(n) => {
events.clear();
decoder.decode(&buf[..n], &mut events);
for ev in events.drain(..) {
match ev {
DeviceEvent::Frame(f) => {
if frames_tx.send(f).is_err() {
return;
}
}
DeviceEvent::SendBytes(b) => {
if cmd_tx.send(WriterCmd::Bytes(b)).is_err() {
return;
}
}
DeviceEvent::Error(e) => {
log::warn!("device decode error: {e}");
}
}
}
}
Err(e) if e.kind() == io::ErrorKind::TimedOut => continue,
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => {
log::error!("device read error: {e}");
return;
}
}
}
}
fn writer_thread<E: DeviceEncoder>(
writer: &mut dyn Write,
encoder: E,
init: Vec<u8>,
keepalive: Option<(Duration, Vec<u8>)>,
rx: mpsc::Receiver<WriterCmd>,
) {
if !init.is_empty() {
if let Err(e) = writer.write_all(&init) {
log::error!("device init write failed: {e}");
return;
}
let _ = writer.flush();
}
let mut next_keepalive = keepalive.as_ref().map(|(d, _)| Instant::now() + *d);
loop {
let now = Instant::now();
let cmd = match (next_keepalive, &keepalive) {
(Some(deadline), Some((interval, payload))) => {
let timeout = deadline.saturating_duration_since(now);
match rx.recv_timeout(timeout) {
Ok(cmd) => cmd,
Err(mpsc::RecvTimeoutError::Timeout) => {
if let Err(e) = writer.write_all(payload) {
log::warn!("device keepalive failed: {e}");
return;
}
let _ = writer.flush();
next_keepalive = Some(Instant::now() + *interval);
continue;
}
Err(mpsc::RecvTimeoutError::Disconnected) => return,
}
}
_ => match rx.recv() {
Ok(cmd) => cmd,
Err(_) => return,
},
};
let bytes = match cmd {
WriterCmd::Bytes(b) => b,
WriterCmd::Frame(f) => match encoder.encode_frame(&f) {
Some(b) => b,
None => continue,
},
};
if let Err(e) = writer.write_all(&bytes) {
log::error!("device write failed: {e}");
return;
}
let _ = writer.flush();
if let Some((interval, _)) = keepalive.as_ref() {
next_keepalive = Some(Instant::now() + *interval);
}
}
}