use bevy_log::error;
use bytes::Bytes;
use futures::{
stream::{SplitSink, SplitStream, StreamExt},
SinkExt,
};
use parking_lot::Mutex;
use serialport::{DataBits, FlowControl, Parity, StopBits};
use std::{sync::Arc, time::Duration};
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
use tokio_serial::{SerialPortBuilderExt, SerialStream};
use tokio_util::codec::{Decoder, Framed};
use crate::{codec::RawCodec, error::SerialError, ArcRuntime, RecvQueue};
type FramedSerial = Framed<SerialStream, RawCodec>;
type SerialSender = SplitSink<FramedSerial, Bytes>;
type SerialReceiver = SplitStream<FramedSerial>;
type SerialSplitResult = Result<(SerialSender, SerialReceiver), SerialError>;
#[derive(Debug, Clone)]
pub struct SerialPortSetting {
pub port_name: String,
pub baud_rate: u32,
pub data_bits: DataBits,
pub flow_control: FlowControl,
pub parity: Parity,
pub stop_bits: StopBits,
pub timeout: Duration,
}
impl Default for SerialPortSetting {
fn default() -> Self {
Self {
port_name: String::new(),
baud_rate: 115_200,
data_bits: DataBits::Eight,
flow_control: FlowControl::None,
parity: Parity::None,
stop_bits: StopBits::One,
timeout: Duration::from_millis(0),
}
}
}
impl SerialPortSetting {
pub fn new(port_name: impl Into<String>, baud_rate: u32) -> Self {
Self {
port_name: port_name.into(),
baud_rate,
..Default::default()
}
}
pub fn with_data_bits(mut self, data_bits: DataBits) -> Self {
self.data_bits = data_bits;
self
}
pub fn with_flow_control(mut self, flow_control: FlowControl) -> Self {
self.flow_control = flow_control;
self
}
pub fn with_parity(mut self, parity: Parity) -> Self {
self.parity = parity;
self
}
pub fn with_stop_bits(mut self, stop_bits: StopBits) -> Self {
self.stop_bits = stop_bits;
self
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn validate(&self) -> Result<(), SerialError> {
if self.port_name.is_empty() {
return Err(SerialError::InvalidConfiguration {
reason: "Port name cannot be empty".to_string(),
});
}
if self.baud_rate == 0 {
return Err(SerialError::InvalidConfiguration {
reason: "Baud rate must be greater than 0".to_string(),
});
}
Ok(())
}
}
pub struct SerialPortWrap {
pub msg_sender: Arc<Mutex<UnboundedSender<Bytes>>>,
pub recv_queue: RecvQueue,
}
impl SerialPortWrap {
pub fn new(task_pool: ArcRuntime, setting: SerialPortSetting) -> Result<Self, SerialError> {
let recv_queue = Arc::new(Mutex::new(Vec::new()));
let (message_sender, message_receiver) = unbounded_channel::<Bytes>();
let (sender, reader) = Self::initialize_serial_port(&task_pool, &setting)?;
Self::spawn_handlers(
task_pool,
sender,
reader,
message_receiver,
recv_queue.clone(),
);
Ok(Self {
msg_sender: Arc::new(Mutex::new(message_sender)),
recv_queue,
})
}
fn initialize_serial_port(
task_pool: &ArcRuntime,
setting: &SerialPortSetting,
) -> SerialSplitResult {
task_pool.block_on(async {
let serial_port = tokio_serial::new(&setting.port_name, setting.baud_rate)
.data_bits(setting.data_bits)
.flow_control(setting.flow_control)
.parity(setting.parity)
.stop_bits(setting.stop_bits)
.open_native_async()
.map_err(|e| SerialError::SerialPortError {
port: setting.port_name.clone(),
source: e,
})?;
let stream = RawCodec.framed(serial_port);
Ok(stream.split())
})
}
fn spawn_handlers(
task_pool: ArcRuntime,
mut sender: SerialSender,
mut reader: SerialReceiver,
mut message_receiver: tokio::sync::mpsc::UnboundedReceiver<Bytes>,
recv_queue: RecvQueue,
) {
task_pool.spawn(async move {
while let Some(message) = message_receiver.recv().await {
if let Err(err) = sender.send(message).await {
error!("Failed to send message: {:?}", err);
break;
}
}
});
task_pool.spawn(async move {
while let Some(result) = reader.next().await {
match result {
Ok(message) => {
recv_queue.lock().push(message);
}
Err(err) => {
error!("Failed to receive message: {:?}", err);
break;
}
}
}
});
}
pub fn get_messages(&mut self) -> Vec<Bytes> {
self.recv_queue.lock().drain(..).collect()
}
}