use crate::error::Error;
use crate::state::{ReadData, SerialportInfo, SerialportState};
use serde::{Deserialize, Serialize};
use serialport5::{
DataBits, FlowControl, Parity, SerialPort, SerialPortType, StopBits,
UsbPortInfo,
};
use std::io::{Read, Write};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
use std::thread;
use std::time::Duration;
use tauri::{command, AppHandle, Runtime, State, Window};
fn get_serialport<T, F: FnOnce(&mut SerialportInfo) -> Result<T, Error>>(
state: State<'_, SerialportState>,
path: String,
f: F,
) -> Result<T, Error> {
match state.serialports.lock() {
Ok(mut map) => match map.get_mut(&path) {
Some(serialport_info) => f(serialport_info),
None => Err(Error::String(format!("{} not found!", &path))),
},
Err(error) => Err(Error::String(format!("{} ", error))),
}
}
fn get_data_bits(value: Option<usize>) -> DataBits {
match value {
Some(value) => match value {
5 => DataBits::Five,
6 => DataBits::Six,
7 => DataBits::Seven,
8 => DataBits::Eight,
_ => DataBits::Eight,
},
None => DataBits::Eight,
}
}
fn get_flow_control(value: Option<String>) -> FlowControl {
match value {
Some(value) => match value.as_str() {
"Software" => FlowControl::Software,
"Hardware" => FlowControl::Hardware,
_ => FlowControl::None,
},
None => FlowControl::None,
}
}
fn get_parity(value: Option<String>) -> Parity {
match value {
Some(value) => match value.as_str() {
"Odd" => Parity::Odd,
"Even" => Parity::Even,
_ => Parity::None,
},
None => Parity::None,
}
}
fn get_stop_bits(value: Option<usize>) -> StopBits {
match value {
Some(value) => match value {
1 => StopBits::One,
2 => StopBits::Two,
_ => StopBits::Two,
},
None => StopBits::Two,
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(remote = "UsbPortInfo")]
#[serde(rename_all = "camelCase")]
struct UsbPortInfoDef {
pub vid: u16,
pub pid: u16,
pub serial_number: Option<String>,
pub manufacturer: Option<String>,
pub product: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(remote = "SerialPortType", untagged)]
enum SerialPortTypeDef {
#[serde(with = "UsbPortInfoDef")]
UsbPort(UsbPortInfo),
PciPort,
BluetoothPort,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SerialPortInfo {
pub path: String,
#[serde(with = "SerialPortTypeDef")]
pub info: SerialPortType,
}
#[command]
pub fn available_ports() -> Vec<SerialPortInfo> {
let mut list = match serialport5::available_ports() {
Ok(list) => list,
Err(_) => vec![],
};
list.sort_by(|a, b| a.port_name.cmp(&b.port_name));
let mut name_list: Vec<String> = vec![];
for i in &list {
name_list.push(i.port_name.clone());
}
println!("串口列表: {:#?}", &list);
let list: Vec<SerialPortInfo> = list
.into_iter()
.map(|spi| SerialPortInfo {
path: spi.port_name,
info: spi.port_type,
})
.collect();
list
}
#[command]
pub async fn cancel_read<R: Runtime>(
_app: AppHandle<R>,
_window: Window<R>,
state: State<'_, SerialportState>,
path: String,
) -> Result<(), Error> {
get_serialport(state, path.clone(), |serialport_info| {
match &serialport_info.sender {
Some(sender) => match sender.send(1) {
Ok(_) => {}
Err(error) => {
return Err(Error::String(format!("{}", error)));
}
},
None => {}
}
serialport_info.sender = None;
Ok(())
})
}
#[command]
pub fn close<R: Runtime>(
_app: AppHandle<R>,
_window: Window<R>,
state: State<'_, SerialportState>,
path: String,
) -> Result<(), Error> {
match state.serialports.lock() {
Ok(mut serialports) => {
if serialports.remove(&path).is_some() {
Ok(())
} else {
Err(Error::String(format!("串口 {} 未打开!", &path)))
}
}
Err(error) => Err(Error::String(format!("获取锁失败: {}", error))),
}
}
#[command]
pub fn close_all<R: Runtime>(
_app: AppHandle<R>,
_window: Window<R>,
state: State<'_, SerialportState>,
) -> Result<(), Error> {
match state.serialports.lock() {
Ok(mut map) => {
for serialport_info in map.values() {
if let Some(sender) = &serialport_info.sender {
match sender.send(1) {
Ok(_) => {}
Err(error) => {
return Err(Error::String(format!("{}", error)));
}
}
}
}
map.clear();
Ok(())
}
Err(error) => Err(Error::String(format!("{}", error))),
}
}
#[command]
pub fn force_close<R: Runtime>(
_app: AppHandle<R>,
_window: Window<R>,
state: State<'_, SerialportState>,
path: String,
) -> Result<(), Error> {
match state.serialports.lock() {
Ok(mut map) => {
if let Some(serial) = map.get_mut(&path) {
if let Some(sender) = &serial.sender {
match sender.send(1) {
Ok(_) => {}
Err(error) => {
return Err(Error::String(format!("{}", error)));
}
}
}
map.remove(&path);
Ok(())
} else {
Ok(())
}
}
Err(error) => Err(Error::String(format!("{}", error))),
}
}
#[command]
pub fn open<R: Runtime>(
_app: AppHandle<R>,
state: State<'_, SerialportState>,
_window: Window<R>,
path: String,
baud_rate: u32,
data_bits: Option<usize>,
flow_control: Option<String>,
parity: Option<String>,
stop_bits: Option<usize>,
timeout: Option<u64>,
) -> Result<(), Error> {
match state.serialports.lock() {
Ok(mut serialports) => {
if serialports.contains_key(&path) {
return Err(Error::String(format!("{} is opened!", &path)));
}
match SerialPort::builder()
.baud_rate(baud_rate)
.data_bits(get_data_bits(data_bits))
.flow_control(get_flow_control(flow_control))
.parity(get_parity(parity))
.stop_bits(get_stop_bits(stop_bits))
.read_timeout(Some(Duration::from_millis(timeout.unwrap_or(200))))
.write_timeout(Some(Duration::from_millis(timeout.unwrap_or(200))))
.open(&path)
{
Ok(serial) => {
let data = SerialportInfo {
serialport: serial,
sender: None,
};
serialports.insert(path, data);
Ok(())
}
Err(error) => Err(Error::String(format!(
"open {} error: {}",
path, error.description
))),
}
}
Err(error) => Err(Error::String(format!("{}", error))),
}
}
#[command]
pub fn read<R: Runtime>(
_app: AppHandle<R>,
window: Window<R>,
state: State<'_, SerialportState>,
path: String,
timeout: Option<u64>,
size: Option<usize>,
) -> Result<(), Error> {
get_serialport(state.clone(), path.clone(), |serialport_info| {
if serialport_info.sender.is_some() {
Ok(())
} else {
match serialport_info.serialport.try_clone() {
Ok(mut serial) => {
let read_event =
format!("plugin-serialport-read-{}", &path.replace(".", "__dot__"));
let (tx, rx): (Sender<usize>, Receiver<usize>) = mpsc::channel();
serialport_info.sender = Some(tx);
thread::spawn(move || loop {
match rx.try_recv() {
Ok(_) => {
break;
}
Err(error) => match error {
TryRecvError::Disconnected => {
break;
}
TryRecvError::Empty => {}
},
}
let mut serial_buf: Vec<u8> = vec![0; size.unwrap_or(1024)];
match serial.read(serial_buf.as_mut_slice()) {
Ok(size) => {
match window.emit(
&read_event,
ReadData {
data: &serial_buf[..size],
size,
},
) {
Ok(_) => {}
Err(_error) => {
}
}
}
Err(_err) => {}
}
thread::sleep(Duration::from_millis(timeout.unwrap_or(200)));
});
}
Err(error) => {
return Err(Error::String(format!("read {} error: {}", &path, error)));
}
}
Ok(())
}
})
}
#[command]
pub fn write<R: Runtime>(
_app: AppHandle<R>,
_window: Window<R>,
state: State<'_, SerialportState>,
path: String,
value: String,
) -> Result<usize, Error> {
get_serialport(state, path.clone(), |serialport_info| match serialport_info
.serialport
.write(value.as_bytes())
{
Ok(size) => Ok(size),
Err(error) => Err(Error::String(format!("write {} error: {}", &path, error))),
})
}
#[command]
pub fn write_binary<R: Runtime>(
_app: AppHandle<R>,
_window: Window<R>,
state: State<'_, SerialportState>,
path: String,
value: Vec<u8>,
) -> Result<usize, Error> {
get_serialport(state, path.clone(), |serialport_info| match serialport_info
.serialport
.write(&value)
{
Ok(size) => Ok(size),
Err(error) => Err(Error::String(format!("write {} error: {}", &path, error))),
})
}