pub use mio_serial::{DataBits, FlowControl, Parity, StopBits};
use bevy::app::{App, Plugin, PostUpdate, PreUpdate};
use bevy::ecs::message::{Message, MessageReader, MessageWriter};
use bevy::ecs::resource::Resource;
use bevy::ecs::system::{In, IntoSystem, Res, ResMut};
use mio::{Events, Interest, Poll, Token};
use mio_serial::SerialStream;
use once_cell::sync::OnceCell;
use std::collections::HashMap;
use std::io::{ErrorKind, Read, Write};
use std::sync::{Arc, Mutex};
use std::time::Duration;
type ResultHandler = Arc<dyn Fn(&str, &Result<usize, std::io::Error>) + Send + Sync + 'static>;
#[derive(Clone)]
pub struct SerialConfig {
pub label: Option<String>,
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,
pub read_buffer_len: usize,
pub read_result_handler: Option<ResultHandler>,
pub write_result_handler: Option<ResultHandler>,
}
impl std::fmt::Debug for SerialConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SerialConfig")
.field("label", &self.label)
.field("port_name", &self.port_name)
.field("baud_rate", &self.baud_rate)
.field("data_bits", &self.data_bits)
.field("flow_control", &self.flow_control)
.field("parity", &self.parity)
.field("stop_bits", &self.stop_bits)
.field("timeout", &self.timeout)
.field("read_buffer_len", &self.read_buffer_len)
.finish()
}
}
impl Default for SerialConfig {
fn default() -> Self {
Self {
label: None,
port_name: "".to_string(),
baud_rate: 115200,
data_bits: DataBits::Eight,
flow_control: FlowControl::None,
parity: Parity::None,
stop_bits: StopBits::One,
timeout: Duration::from_millis(0),
read_buffer_len: 2048,
read_result_handler: None,
write_result_handler: None,
}
}
}
#[derive(Message)]
pub struct SerialReadMessage(pub String, pub Vec<u8>);
#[derive(Message)]
pub struct SerialWriteMessage(pub String, pub Vec<u8>);
#[derive(Debug)]
struct SerialStreamLabeled {
stream: SerialStream,
label: String,
read_buffer_len: usize,
}
#[derive(Resource)]
struct MioContext {
poll: Poll,
events: Events,
}
impl MioContext {
fn poll(&mut self) {
self.poll
.poll(&mut self.events, Some(Duration::from_micros(1)))
.unwrap_or_else(|e| {
panic!("Failed to poll events: {e:?}");
});
}
}
#[derive(Resource)]
struct Indices(HashMap<String, usize>);
type ReadWriteResults = HashMap<String, Result<usize, std::io::Error>>;
static SERIALS: OnceCell<Vec<Mutex<SerialStreamLabeled>>> = OnceCell::new();
static READ_RESULT_HANDLERS: OnceCell<HashMap<String, Option<ResultHandler>>> = OnceCell::new();
static WRITE_RESULT_HANDLERS: OnceCell<HashMap<String, Option<ResultHandler>>> = OnceCell::new();
#[derive(Clone)]
pub struct SerialPlugin {
pub config: Vec<SerialConfig>,
}
impl SerialPlugin {
pub fn new(port_name: &str, baud_rate: u32) -> Self {
Self {
config: vec![SerialConfig {
port_name: port_name.to_string(),
baud_rate,
..Default::default()
}],
}
}
pub fn new_with_config(config: Vec<SerialConfig>) -> Self {
Self { config }
}
}
impl Plugin for SerialPlugin {
fn build(&self, app: &mut App) {
let poll = Poll::new().unwrap();
let events = Events::with_capacity(self.config.len());
let mio_ctx = MioContext { poll, events };
let mut serials: Vec<Mutex<SerialStreamLabeled>> = vec![];
let mut read_result_handlers: HashMap<String, Option<ResultHandler>> = HashMap::new();
let mut write_result_handlers: HashMap<String, Option<ResultHandler>> = HashMap::new();
let mut indices = Indices(HashMap::new());
for (i, config) in self.config.iter().enumerate() {
let port_builder = serialport::new(&config.port_name, config.baud_rate)
.data_bits(config.data_bits)
.flow_control(config.flow_control)
.parity(config.parity)
.stop_bits(config.stop_bits)
.timeout(config.timeout);
let mut stream = SerialStream::open(&port_builder).unwrap_or_else(|e| {
panic!("Failed to open serial port {}: {:?}", config.port_name, e);
});
mio_ctx
.poll
.registry()
.register(&mut stream, Token(i), Interest::READABLE)
.unwrap_or_else(|e| {
panic!("Failed to register stream to poll : {e:?}");
});
let label = if let Some(label) = &config.label {
label.clone()
} else {
config.port_name.clone()
};
indices.0.insert(label.clone(), i);
read_result_handlers.insert(label.clone(), config.read_result_handler.clone());
write_result_handlers.insert(label.clone(), config.write_result_handler.clone());
serials.push(Mutex::new(SerialStreamLabeled {
stream,
label,
read_buffer_len: config.read_buffer_len,
}));
}
SERIALS.set(serials).unwrap_or_else(|e| {
panic!("Failed to set SerialStream to global variable: {e:?}");
});
READ_RESULT_HANDLERS
.set(read_result_handlers)
.unwrap_or_else(|_| {
panic!("Failed to set read result handler to global variable");
});
WRITE_RESULT_HANDLERS
.set(write_result_handlers)
.unwrap_or_else(|_| {
panic!("Failed to set write result handler to global variable");
});
app.insert_resource(mio_ctx)
.insert_resource(indices)
.add_message::<SerialReadMessage>()
.add_message::<SerialWriteMessage>()
.add_systems(PreUpdate, read_serial.pipe(read_serial_result_handler))
.add_systems(PostUpdate, write_serial.pipe(write_serial_result_handler));
}
}
fn read_serial(
mut writer: MessageWriter<SerialReadMessage>,
mut mio_ctx: ResMut<MioContext>,
indices: Res<Indices>,
) -> ReadWriteResults {
if indices.0.is_empty() {
return ReadWriteResults::new();
}
mio_ctx.poll();
let mut read_results = ReadWriteResults::new();
for event in mio_ctx.events.iter() {
if !event.is_readable() {
continue;
}
let serials = SERIALS.get().expect("SERIALS are not initialized");
let serial_mtx = serials
.get(event.token().0) .unwrap_or_else(|| panic!("SERIALS index {} not found", event.token().0));
if let Ok(mut serial) = serial_mtx.lock() {
let mut buffer = vec![0_u8; serial.read_buffer_len];
let mut bytes_read = 0;
loop {
match serial.stream.read(&mut buffer[bytes_read..]) {
Ok(0) => {
eprintln!("{} connection maybe closed", serial.label);
read_results.insert(
serial.label.clone(),
Err(std::io::Error::new(
ErrorKind::NotConnected,
"Maybe connection closed",
)),
);
break;
}
Ok(n) => {
bytes_read += n;
if bytes_read == buffer.len() {
buffer.resize(buffer.len() + serial.read_buffer_len, 0);
}
continue;
}
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {
let label = serial.label.clone();
let buffer = buffer.drain(..bytes_read).collect();
writer.write(SerialReadMessage(label, buffer));
read_results.insert(serial.label.clone(), Ok(bytes_read));
break;
}
Err(ref e) if e.kind() == ErrorKind::Interrupted => {
continue;
}
Err(e) => {
eprintln!("Failed to read serial port {}: {}", serial.label, e);
read_results.insert(serial.label.clone(), Err(e));
break;
}
}
}
}
}
read_results
}
fn read_serial_result_handler(In(result): In<ReadWriteResults>) {
let read_result_handlers = READ_RESULT_HANDLERS
.get()
.expect("READ_RESULT_HANDLERS are not initialized");
for (label, result) in result.iter() {
if let Some(Some(handler)) = read_result_handlers.get(label) {
handler(label, result);
}
}
}
fn write_serial(
mut reader: MessageReader<SerialWriteMessage>,
indices: Res<Indices>,
) -> ReadWriteResults {
if indices.0.is_empty() {
return ReadWriteResults::new();
}
let mut rw_results = ReadWriteResults::new();
for SerialWriteMessage(label, buffer) in reader.read() {
let &serial_index = indices.0.get(label).unwrap_or_else(|| {
panic!("{} is not exist", label.as_str());
});
let serials = SERIALS.get().expect("SERIALS are not initialized");
let serial_mtx = serials
.get(serial_index)
.unwrap_or_else(|| panic!("SERIALS index {serial_index} not found"));
if let Ok(mut serial) = serial_mtx.lock() {
let mut bytes_wrote = 0;
loop {
match serial.stream.write(&buffer[bytes_wrote..]) {
Ok(n) if n < buffer.len() => {
bytes_wrote += n;
}
Ok(_) => {
bytes_wrote += buffer.len();
}
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => {
eprintln!("Failed to write serial port {}: {}", serial.label, e);
rw_results.insert(serial.label.clone(), Err(e));
break;
}
}
if bytes_wrote == buffer.len() {
rw_results.insert(serial.label.clone(), Ok(bytes_wrote));
break;
} else {
continue;
}
}
}
}
rw_results
}
fn write_serial_result_handler(In(result): In<ReadWriteResults>) {
let write_result_handlers = WRITE_RESULT_HANDLERS
.get()
.expect("WRITE_RESULT_HANDLERS are not initialized");
for (label, result) in result.iter() {
if let Some(Some(handler)) = write_result_handlers.get(label) {
handler(label, result);
}
}
}