mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-25 15:16:53 +01:00
libftdi support
This commit is contained in:
parent
acc3e588d8
commit
346c65fc9c
790
sw/deployer/Cargo.lock
generated
790
sw/deployer/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,21 +7,22 @@ description = "SummerCart64 loader and control software"
|
|||||||
documentation = "https://github.com/Polprzewodnikowy/SummerCart64"
|
documentation = "https://github.com/Polprzewodnikowy/SummerCart64"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.23"
|
chrono = "0.4.38"
|
||||||
clap = { version = "4.1.6", features = ["derive"] }
|
clap = { version = "4.5.7", features = ["derive"] }
|
||||||
clap-num = "1.0.2"
|
clap-num = "1.1.1"
|
||||||
colored = "2.0.0"
|
colored = "2.1.0"
|
||||||
crc32fast = "1.3.2"
|
crc32fast = "1.4.2"
|
||||||
ctrlc = "3.2.5"
|
ctrlc = "3.4.4"
|
||||||
encoding_rs = "0.8.32"
|
encoding_rs = "0.8.34"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
image = "0.24.5"
|
image = "0.25.1"
|
||||||
include-flate = { version = "0.2.0", features = ["stable"] }
|
include-flate = { version = "0.2.0", features = ["stable"] }
|
||||||
|
libftdi1-sys = "1.1.3"
|
||||||
md5 = "0.7.0"
|
md5 = "0.7.0"
|
||||||
panic-message = "0.3.0"
|
panic-message = "0.3.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rust-ini = "0.18.0"
|
rust-ini = "0.18.0"
|
||||||
serial2 = "0.2.20"
|
serial2 = "0.2.26"
|
||||||
serialport = "4.3.0"
|
serialport = "4.3.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
@ -350,7 +350,13 @@ fn handle_list_command() -> Result<(), sc64::Error> {
|
|||||||
|
|
||||||
println!("{}", "Found devices:".bold());
|
println!("{}", "Found devices:".bold());
|
||||||
for (i, d) in devices.iter().enumerate() {
|
for (i, d) in devices.iter().enumerate() {
|
||||||
println!(" {i}: [{}] at port [{}]", d.serial_number, d.port);
|
let index = i + 1;
|
||||||
|
println!(
|
||||||
|
" {index}: [{}] at port [{}] (using \"{}\" backend)",
|
||||||
|
d.serial.bold(),
|
||||||
|
d.port.bold(),
|
||||||
|
d.backend.to_string().bold()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::ftdi::FtdiError;
|
||||||
use std::fmt::{Display, Formatter, Result};
|
use std::fmt::{Display, Formatter, Result};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -32,3 +33,9 @@ impl From<serialport::Error> for Error {
|
|||||||
Error::new(format!("SerialPort error: {}", value.description).as_str())
|
Error::new(format!("SerialPort error: {}", value.description).as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<FtdiError> for Error {
|
||||||
|
fn from(value: FtdiError) -> Self {
|
||||||
|
Error::new(format!("libftdi error: {}", value.description).as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
227
sw/deployer/src/sc64/ftdi.rs
Normal file
227
sw/deployer/src/sc64/ftdi.rs
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
pub struct FtdiError {
|
||||||
|
pub description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FtdiError {
|
||||||
|
fn malloc() -> FtdiError {
|
||||||
|
FtdiError {
|
||||||
|
description: format!("Couldn't allocate memory for the context"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn libftdi(context: *mut libftdi1_sys::ftdi_context) -> FtdiError {
|
||||||
|
let raw = unsafe { std::ffi::CStr::from_ptr(libftdi1_sys::ftdi_get_error_string(context)) };
|
||||||
|
FtdiError {
|
||||||
|
description: format!("{}", raw.to_str().unwrap_or("Unknown error")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FtdiError> for std::io::Error {
|
||||||
|
fn from(value: FtdiError) -> Self {
|
||||||
|
return Self::new(std::io::ErrorKind::Other, value.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
context: *mut libftdi1_sys::ftdi_context,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
fn new() -> Result<Context, FtdiError> {
|
||||||
|
let ctx = unsafe { libftdi1_sys::ftdi_new() };
|
||||||
|
if ctx.is_null() {
|
||||||
|
return Err(FtdiError::malloc());
|
||||||
|
}
|
||||||
|
let context = Context { context: ctx };
|
||||||
|
let result = unsafe {
|
||||||
|
libftdi1_sys::ftdi_set_interface(
|
||||||
|
context.get(),
|
||||||
|
libftdi1_sys::ftdi_interface::INTERFACE_A,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
context.check_result(result)?;
|
||||||
|
Ok(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> *mut libftdi1_sys::ftdi_context {
|
||||||
|
return self.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_result(&self, result: i32) -> Result<(), FtdiError> {
|
||||||
|
if result < 0 {
|
||||||
|
return Err(FtdiError::libftdi(self.get()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Context {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { libftdi1_sys::ftdi_free(self.get()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FtdiDevice {
|
||||||
|
context: Context,
|
||||||
|
read_timeout: std::time::Duration,
|
||||||
|
write_timeout: std::time::Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FtdiDevice {
|
||||||
|
pub fn open(
|
||||||
|
port: &str,
|
||||||
|
read_timeout: std::time::Duration,
|
||||||
|
write_timeout: std::time::Duration,
|
||||||
|
) -> Result<FtdiDevice, FtdiError> {
|
||||||
|
let context = Context::new()?;
|
||||||
|
unsafe {
|
||||||
|
let mode = libftdi1_sys::ftdi_module_detach_mode::AUTO_DETACH_REATACH_SIO_MODULE;
|
||||||
|
(*context.get()).module_detach_mode = mode;
|
||||||
|
}
|
||||||
|
let description = std::ffi::CString::new(port).unwrap_or_default().into_raw();
|
||||||
|
let result = unsafe { libftdi1_sys::ftdi_usb_open_string(context.get(), description) };
|
||||||
|
context.check_result(result)?;
|
||||||
|
let result = unsafe { libftdi1_sys::ftdi_set_latency_timer(context.get(), 1) };
|
||||||
|
context.check_result(result)?;
|
||||||
|
Ok(FtdiDevice {
|
||||||
|
context,
|
||||||
|
read_timeout,
|
||||||
|
write_timeout,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_dtr(&self, value: bool) -> Result<(), FtdiError> {
|
||||||
|
let state = if value { 1 } else { 0 };
|
||||||
|
let result = unsafe { libftdi1_sys::ftdi_setdtr(self.context.get(), state) };
|
||||||
|
self.context.check_result(result)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_dsr(&self) -> Result<bool, FtdiError> {
|
||||||
|
const DSR_BIT: u16 = 1 << 5;
|
||||||
|
let mut status: u16 = 0;
|
||||||
|
let result = unsafe {
|
||||||
|
libftdi1_sys::ftdi_poll_modem_status(
|
||||||
|
self.context.get(),
|
||||||
|
std::slice::from_mut(&mut status).as_mut_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
self.context.check_result(result)?;
|
||||||
|
Ok((status & DSR_BIT) != 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self, data: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
let timeout = std::time::Instant::now();
|
||||||
|
loop {
|
||||||
|
let result = unsafe {
|
||||||
|
libftdi1_sys::ftdi_read_data(
|
||||||
|
self.context.get(),
|
||||||
|
data.as_mut_ptr(),
|
||||||
|
data.len() as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
self.context.check_result(result)?;
|
||||||
|
if result > 0 {
|
||||||
|
return Ok(result as usize);
|
||||||
|
}
|
||||||
|
if timeout.elapsed() > self.read_timeout {
|
||||||
|
return Err(std::io::ErrorKind::TimedOut.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, data: &[u8]) -> std::io::Result<usize> {
|
||||||
|
let timeout = std::time::Instant::now();
|
||||||
|
loop {
|
||||||
|
let result = unsafe {
|
||||||
|
libftdi1_sys::ftdi_write_data(self.context.get(), data.as_ptr(), data.len() as i32)
|
||||||
|
};
|
||||||
|
self.context.check_result(result)?;
|
||||||
|
if result > 0 {
|
||||||
|
return Ok(result as usize);
|
||||||
|
}
|
||||||
|
if timeout.elapsed() > self.write_timeout {
|
||||||
|
return Err(std::io::ErrorKind::TimedOut.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_all(&self, data: &[u8]) -> std::io::Result<()> {
|
||||||
|
let mut data = data;
|
||||||
|
while !data.is_empty() {
|
||||||
|
let written = self.write(data)?;
|
||||||
|
data = &data[written..];
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn discard_buffers(&self) -> std::io::Result<()> {
|
||||||
|
let result = unsafe { libftdi1_sys::ftdi_tcioflush(self.context.get()) };
|
||||||
|
self.context.check_result(result)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for FtdiDevice {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { libftdi1_sys::ftdi_usb_close(self.context.get()) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FtdiDeviceInfo {
|
||||||
|
pub port: String,
|
||||||
|
pub serial: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_ftdi_devices(vendor: u16, product: u16) -> Result<Vec<FtdiDeviceInfo>, FtdiError> {
|
||||||
|
let context = Context::new()?;
|
||||||
|
|
||||||
|
let mut device_list: *mut libftdi1_sys::ftdi_device_list = std::ptr::null_mut();
|
||||||
|
let result = unsafe {
|
||||||
|
libftdi1_sys::ftdi_usb_find_all(
|
||||||
|
context.get(),
|
||||||
|
&mut device_list,
|
||||||
|
vendor as i32,
|
||||||
|
product as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
context.check_result(result)?;
|
||||||
|
|
||||||
|
let mut list: Vec<FtdiDeviceInfo> = vec![];
|
||||||
|
|
||||||
|
let mut serial = [0i8; 128];
|
||||||
|
|
||||||
|
let mut device = device_list;
|
||||||
|
let mut index = 0;
|
||||||
|
while !device.is_null() {
|
||||||
|
let result = unsafe {
|
||||||
|
libftdi1_sys::ftdi_usb_get_strings(
|
||||||
|
context.get(),
|
||||||
|
(*device).dev,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
serial.as_mut_ptr(),
|
||||||
|
serial.len() as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(()) = context.check_result(result) {
|
||||||
|
list.push(FtdiDeviceInfo {
|
||||||
|
port: format!("i:0x{vendor:04X}:0x{product:04X}:{index}"),
|
||||||
|
serial: unsafe { std::ffi::CStr::from_ptr(serial.as_ptr()) }
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
device = unsafe { (*device).next };
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { libftdi1_sys::ftdi_list_free(&mut device_list) }
|
||||||
|
|
||||||
|
Ok(list)
|
||||||
|
}
|
@ -1,10 +1,13 @@
|
|||||||
use super::error::Error;
|
use super::{
|
||||||
|
error::Error,
|
||||||
|
ftdi::{list_ftdi_devices, FtdiDevice, FtdiError},
|
||||||
|
};
|
||||||
use serial2::SerialPort;
|
use serial2::SerialPort;
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
fmt::Display,
|
||||||
io::{BufReader, BufWriter, ErrorKind, Read, Write},
|
io::{BufReader, BufWriter, ErrorKind, Read, Write},
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
thread,
|
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,74 +59,54 @@ pub struct Packet {
|
|||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Serial {
|
const SERIAL_PREFIX: &str = "serial://";
|
||||||
serial: SerialPort,
|
const FTDI_PREFIX: &str = "ftdi://";
|
||||||
}
|
|
||||||
|
|
||||||
impl Serial {
|
const RESET_TIMEOUT: Duration = Duration::from_secs(1);
|
||||||
fn reset(&self) -> Result<(), Error> {
|
const POLL_TIMEOUT: Duration = Duration::from_millis(1);
|
||||||
const RESET_WAIT_DURATION: Duration = Duration::from_millis(10);
|
const READ_TIMEOUT: Duration = Duration::from_secs(5);
|
||||||
const RESET_RETRY_COUNT: i32 = 100;
|
const WRITE_TIMEOUT: Duration = Duration::from_secs(5);
|
||||||
const FLUSH_TIMEOUT: Duration = Duration::from_secs(1);
|
|
||||||
|
|
||||||
self.serial.set_dtr(true)?;
|
pub trait Backend {
|
||||||
for n in 0..=RESET_RETRY_COUNT {
|
fn reset(&mut self) -> Result<(), Error>;
|
||||||
self.serial.discard_buffers()?;
|
|
||||||
thread::sleep(RESET_WAIT_DURATION);
|
|
||||||
if self.serial.read_dsr()? {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if n == RESET_RETRY_COUNT {
|
|
||||||
return Err(Error::new("Couldn't reset SC64 device (on)"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let flush_timeout = Instant::now();
|
fn close(&self);
|
||||||
|
|
||||||
|
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize>;
|
||||||
|
|
||||||
|
fn write(&mut self, buffer: &[u8]) -> std::io::Result<()>;
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()>;
|
||||||
|
|
||||||
|
fn purge_incoming_data(&mut self) -> std::io::Result<()> {
|
||||||
|
let timeout = Instant::now();
|
||||||
loop {
|
loop {
|
||||||
match self.serial.read(&mut vec![0; 1]) {
|
match self.read(&mut vec![0; 1]) {
|
||||||
Ok(length) => match length {
|
Ok(length) => match length {
|
||||||
0 => break,
|
0 => return Ok(()),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Err(error) => match error.kind() {
|
Err(error) => match error.kind() {
|
||||||
ErrorKind::TimedOut => break,
|
ErrorKind::TimedOut => return Ok(()),
|
||||||
_ => {
|
_ => return Err(error),
|
||||||
return Err(Error::new(
|
|
||||||
format!("Couldn't flush SC64 serial buffer: {error}").as_str(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if flush_timeout.elapsed() >= FLUSH_TIMEOUT {
|
if timeout.elapsed() >= RESET_TIMEOUT {
|
||||||
return Err(Error::new("SC64 serial buffer flush took too long"));
|
return Err(std::io::Error::new(
|
||||||
|
ErrorKind::TimedOut,
|
||||||
|
"SC64 read buffer flush took too long",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.serial.set_dtr(false)?;
|
|
||||||
for n in 0..=RESET_RETRY_COUNT {
|
|
||||||
thread::sleep(RESET_WAIT_DURATION);
|
|
||||||
if !self.serial.read_dsr()? {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if n == RESET_RETRY_COUNT {
|
|
||||||
return Err(Error::new("Couldn't reset SC64 device (off)"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_data(&self, buffer: &mut [u8], block: bool) -> Result<Option<()>, Error> {
|
fn try_read(&mut self, buffer: &mut [u8], block: bool) -> Result<Option<()>, Error> {
|
||||||
let timeout = Instant::now();
|
|
||||||
let mut position = 0;
|
let mut position = 0;
|
||||||
let length = buffer.len();
|
let length = buffer.len();
|
||||||
|
let timeout = Instant::now();
|
||||||
while position < length {
|
while position < length {
|
||||||
if timeout.elapsed() > Duration::from_secs(10) {
|
match self.read(&mut buffer[position..length]) {
|
||||||
return Err(Error::new("Serial read timeout"));
|
Ok(0) => return Err(Error::new("Unexpected end of stream data")),
|
||||||
}
|
|
||||||
match self.serial.read(&mut buffer[position..length]) {
|
|
||||||
Ok(0) => return Err(Error::new("Unexpected end of serial data")),
|
|
||||||
Ok(bytes) => position += bytes,
|
Ok(bytes) => position += bytes,
|
||||||
Err(error) => match error.kind() {
|
Err(error) => match error.kind() {
|
||||||
ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {
|
ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {
|
||||||
@ -134,42 +117,47 @@ impl Serial {
|
|||||||
_ => return Err(error.into()),
|
_ => return Err(error.into()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if timeout.elapsed() > READ_TIMEOUT {
|
||||||
|
return Err(Error::new("Read timeout"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(()))
|
Ok(Some(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_exact(&self, buffer: &mut [u8]) -> Result<(), Error> {
|
fn try_read_header(&mut self, block: bool) -> Result<Option<[u8; 4]>, Error> {
|
||||||
match self.read_data(buffer, true)? {
|
let mut header = [0u8; 4];
|
||||||
|
Ok(self.try_read(&mut header, block)?.map(|_| header))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_exact(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
match self.try_read(buffer, true)? {
|
||||||
Some(()) => Ok(()),
|
Some(()) => Ok(()),
|
||||||
None => Err(Error::new("Unexpected end of serial data")),
|
None => Err(Error::new("Unexpected end of data")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_header(&self, block: bool) -> Result<Option<[u8; 4]>, Error> {
|
fn send_command(&mut self, command: &Command) -> Result<(), Error> {
|
||||||
let mut header = [0u8; 4];
|
self.write(b"CMD")?;
|
||||||
Ok(self.read_data(&mut header, block)?.map(|_| header))
|
self.write(&command.id.to_be_bytes())?;
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_command(&self, command: &Command) -> Result<(), Error> {
|
self.write(&command.args[0].to_be_bytes())?;
|
||||||
self.serial.write_all(b"CMD")?;
|
self.write(&command.args[1].to_be_bytes())?;
|
||||||
self.serial.write_all(&command.id.to_be_bytes())?;
|
|
||||||
self.serial.write_all(&command.args[0].to_be_bytes())?;
|
|
||||||
self.serial.write_all(&command.args[1].to_be_bytes())?;
|
|
||||||
|
|
||||||
self.serial.write_all(&command.data)?;
|
self.write(&command.data)?;
|
||||||
|
|
||||||
self.serial.flush()?;
|
self.flush()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_incoming_data(
|
fn process_incoming_data(
|
||||||
&self,
|
&mut self,
|
||||||
data_type: DataType,
|
data_type: DataType,
|
||||||
packets: &mut VecDeque<Packet>,
|
packets: &mut VecDeque<Packet>,
|
||||||
) -> Result<Option<Response>, Error> {
|
) -> Result<Option<Response>, Error> {
|
||||||
let block = matches!(data_type, DataType::Response);
|
let block = matches!(data_type, DataType::Response);
|
||||||
while let Some(header) = self.read_header(block)? {
|
|
||||||
|
while let Some(header) = self.try_read_header(block)? {
|
||||||
let (packet_token, error) = (match &header[0..3] {
|
let (packet_token, error) = (match &header[0..3] {
|
||||||
b"CMP" => Ok((false, false)),
|
b"CMP" => Ok((false, false)),
|
||||||
b"PKT" => Ok((true, false)),
|
b"PKT" => Ok((true, false)),
|
||||||
@ -200,48 +188,115 @@ impl Serial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_serial(port: &str) -> Result<Serial, Error> {
|
pub struct SerialBackend {
|
||||||
let mut serial = SerialPort::open(port, 115_200)?;
|
device: SerialPort,
|
||||||
serial.set_write_timeout(Duration::from_secs(10))?;
|
|
||||||
serial.set_read_timeout(Duration::from_millis(10))?;
|
|
||||||
let backend = Serial { serial };
|
|
||||||
backend.reset()?;
|
|
||||||
Ok(backend)
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Backend {
|
|
||||||
fn send_command(&mut self, command: &Command) -> Result<(), Error>;
|
|
||||||
fn process_incoming_data(
|
|
||||||
&mut self,
|
|
||||||
data_type: DataType,
|
|
||||||
packets: &mut VecDeque<Packet>,
|
|
||||||
) -> Result<Option<Response>, Error>;
|
|
||||||
fn close(&self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SerialBackend {
|
|
||||||
inner: Serial,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend for SerialBackend {
|
impl Backend for SerialBackend {
|
||||||
fn send_command(&mut self, command: &Command) -> Result<(), Error> {
|
fn reset(&mut self) -> Result<(), Error> {
|
||||||
self.inner.send_command(command)
|
self.device.set_dtr(true)?;
|
||||||
|
let timeout = Instant::now();
|
||||||
|
loop {
|
||||||
|
self.device.discard_buffers()?;
|
||||||
|
if self.device.read_dsr()? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if timeout.elapsed() > RESET_TIMEOUT {
|
||||||
|
return Err(Error::new("Couldn't reset SC64 device (on)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.purge_incoming_data()?;
|
||||||
|
|
||||||
|
self.device.set_dtr(false)?;
|
||||||
|
let timeout = Instant::now();
|
||||||
|
loop {
|
||||||
|
if !self.device.read_dsr()? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if timeout.elapsed() > RESET_TIMEOUT {
|
||||||
|
return Err(Error::new("Couldn't reset SC64 device (off)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_incoming_data(
|
fn close(&self) {}
|
||||||
&mut self,
|
|
||||||
data_type: DataType,
|
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
|
||||||
packets: &mut VecDeque<Packet>,
|
self.device.read(buffer)
|
||||||
) -> Result<Option<Response>, Error> {
|
}
|
||||||
self.inner.process_incoming_data(data_type, packets)
|
|
||||||
|
fn write(&mut self, buffer: &[u8]) -> std::io::Result<()> {
|
||||||
|
self.device.write_all(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
self.device.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_serial_backend(port: &str) -> Result<SerialBackend, Error> {
|
fn new_serial_backend(port: &str) -> std::io::Result<SerialBackend> {
|
||||||
let backend = SerialBackend {
|
let mut serial = SerialPort::open(port, 115_200)?;
|
||||||
inner: new_serial(port)?,
|
serial.set_read_timeout(POLL_TIMEOUT)?;
|
||||||
};
|
serial.set_write_timeout(WRITE_TIMEOUT)?;
|
||||||
Ok(backend)
|
Ok(SerialBackend { device: serial })
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FtdiBackend {
|
||||||
|
device: FtdiDevice,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Backend for FtdiBackend {
|
||||||
|
fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
self.device.set_dtr(true)?;
|
||||||
|
let timeout = Instant::now();
|
||||||
|
loop {
|
||||||
|
self.device.discard_buffers()?;
|
||||||
|
if self.device.read_dsr()? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if timeout.elapsed() > RESET_TIMEOUT {
|
||||||
|
return Err(Error::new("Couldn't reset SC64 device (on)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.purge_incoming_data()?;
|
||||||
|
|
||||||
|
self.device.set_dtr(false)?;
|
||||||
|
let timeout = Instant::now();
|
||||||
|
loop {
|
||||||
|
if !self.device.read_dsr()? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if timeout.elapsed() > RESET_TIMEOUT {
|
||||||
|
return Err(Error::new("Couldn't reset SC64 device (off)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&self) {}
|
||||||
|
|
||||||
|
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
self.device.read(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, buffer: &[u8]) -> std::io::Result<()> {
|
||||||
|
self.device.write_all(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_ftdi_backend(port: &str) -> Result<FtdiBackend, FtdiError> {
|
||||||
|
Ok(FtdiBackend {
|
||||||
|
device: FtdiDevice::open(port, POLL_TIMEOUT, WRITE_TIMEOUT)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TcpBackend {
|
struct TcpBackend {
|
||||||
@ -250,58 +305,40 @@ struct TcpBackend {
|
|||||||
writer: BufWriter<TcpStream>,
|
writer: BufWriter<TcpStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TcpBackend {
|
|
||||||
fn read_data(&mut self, buffer: &mut [u8], block: bool) -> Result<Option<()>, Error> {
|
|
||||||
let timeout = Instant::now();
|
|
||||||
let mut position = 0;
|
|
||||||
let length = buffer.len();
|
|
||||||
while position < length {
|
|
||||||
if timeout.elapsed() > Duration::from_secs(10) {
|
|
||||||
return Err(Error::new("Stream read timeout"));
|
|
||||||
}
|
|
||||||
match self.reader.read(&mut buffer[position..length]) {
|
|
||||||
Ok(0) => return Err(Error::new("Unexpected end of stream data")),
|
|
||||||
Ok(bytes) => position += bytes,
|
|
||||||
Err(error) => match error.kind() {
|
|
||||||
ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {
|
|
||||||
if !block && position == 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Err(error.into()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_exact(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
|
||||||
match self.read_data(buffer, true)? {
|
|
||||||
Some(()) => Ok(()),
|
|
||||||
None => Err(Error::new("Unexpected end of stream data")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_header(&mut self, block: bool) -> Result<Option<[u8; 4]>, Error> {
|
|
||||||
let mut header = [0u8; 4];
|
|
||||||
Ok(self.read_data(&mut header, block)?.map(|_| header))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Backend for TcpBackend {
|
impl Backend for TcpBackend {
|
||||||
|
fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&self) {
|
||||||
|
self.stream.shutdown(std::net::Shutdown::Both).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
self.reader.read(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, buffer: &[u8]) -> std::io::Result<()> {
|
||||||
|
self.writer.write_all(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
self.writer.flush()
|
||||||
|
}
|
||||||
|
|
||||||
fn send_command(&mut self, command: &Command) -> Result<(), Error> {
|
fn send_command(&mut self, command: &Command) -> Result<(), Error> {
|
||||||
let payload_data_type: u32 = DataType::Command.into();
|
let payload_data_type: u32 = DataType::Command.into();
|
||||||
self.writer.write_all(&payload_data_type.to_be_bytes())?;
|
self.write(&payload_data_type.to_be_bytes())?;
|
||||||
|
|
||||||
self.writer.write_all(&command.id.to_be_bytes())?;
|
self.write(&command.id.to_be_bytes())?;
|
||||||
self.writer.write_all(&command.args[0].to_be_bytes())?;
|
self.write(&command.args[0].to_be_bytes())?;
|
||||||
self.writer.write_all(&command.args[1].to_be_bytes())?;
|
self.write(&command.args[1].to_be_bytes())?;
|
||||||
|
|
||||||
let command_data_length = command.data.len() as u32;
|
let command_data_length = command.data.len() as u32;
|
||||||
self.writer.write_all(&command_data_length.to_be_bytes())?;
|
self.write(&command_data_length.to_be_bytes())?;
|
||||||
self.writer.write_all(&command.data)?;
|
self.write(&command.data)?;
|
||||||
|
|
||||||
self.writer.flush()?;
|
self.flush()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -312,7 +349,7 @@ impl Backend for TcpBackend {
|
|||||||
packets: &mut VecDeque<Packet>,
|
packets: &mut VecDeque<Packet>,
|
||||||
) -> Result<Option<Response>, Error> {
|
) -> Result<Option<Response>, Error> {
|
||||||
let block = matches!(data_type, DataType::Response);
|
let block = matches!(data_type, DataType::Response);
|
||||||
while let Some(header) = self.read_header(block)? {
|
while let Some(header) = self.try_read_header(block)? {
|
||||||
let payload_data_type: DataType = u32::from_be_bytes(header).try_into()?;
|
let payload_data_type: DataType = u32::from_be_bytes(header).try_into()?;
|
||||||
let mut buffer = [0u8; 4];
|
let mut buffer = [0u8; 4];
|
||||||
match payload_data_type {
|
match payload_data_type {
|
||||||
@ -357,17 +394,13 @@ impl Backend for TcpBackend {
|
|||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&self) {
|
|
||||||
self.stream.shutdown(std::net::Shutdown::Both).ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_tcp_backend(address: &str) -> Result<TcpBackend, Error> {
|
fn new_tcp_backend(address: &str) -> Result<TcpBackend, Error> {
|
||||||
let stream = match TcpStream::connect(address) {
|
let stream = match TcpStream::connect(address) {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
stream.set_write_timeout(Some(Duration::from_secs(10)))?;
|
stream.set_write_timeout(Some(WRITE_TIMEOUT))?;
|
||||||
stream.set_read_timeout(Some(Duration::from_millis(10)))?;
|
stream.set_read_timeout(Some(POLL_TIMEOUT))?;
|
||||||
stream
|
stream
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@ -385,8 +418,28 @@ fn new_tcp_backend(address: &str) -> Result<TcpBackend, Error> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_local_backend(port: &str) -> Result<Box<dyn Backend>, Error> {
|
||||||
|
let mut backend: Box<dyn Backend> = if port.starts_with(SERIAL_PREFIX) {
|
||||||
|
Box::new(new_serial_backend(
|
||||||
|
port.strip_prefix(SERIAL_PREFIX).unwrap_or_default(),
|
||||||
|
)?)
|
||||||
|
} else if port.starts_with(FTDI_PREFIX) {
|
||||||
|
Box::new(new_ftdi_backend(
|
||||||
|
port.strip_prefix(FTDI_PREFIX).unwrap_or_default(),
|
||||||
|
)?)
|
||||||
|
} else {
|
||||||
|
return Err(Error::new("Invalid port prefix provided"));
|
||||||
|
};
|
||||||
|
backend.reset()?;
|
||||||
|
Ok(backend)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_remote_backend(address: &str) -> Result<Box<dyn Backend>, Error> {
|
||||||
|
Ok(Box::new(new_tcp_backend(address)?))
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Link {
|
pub struct Link {
|
||||||
backend: Box<dyn Backend>,
|
pub backend: Box<dyn Backend>,
|
||||||
packets: VecDeque<Packet>,
|
packets: VecDeque<Packet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,45 +504,76 @@ impl Drop for Link {
|
|||||||
|
|
||||||
pub fn new_local(port: &str) -> Result<Link, Error> {
|
pub fn new_local(port: &str) -> Result<Link, Error> {
|
||||||
Ok(Link {
|
Ok(Link {
|
||||||
backend: Box::new(new_serial_backend(port)?),
|
backend: new_local_backend(port)?,
|
||||||
packets: VecDeque::new(),
|
packets: VecDeque::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_remote(address: &str) -> Result<Link, Error> {
|
pub fn new_remote(address: &str) -> Result<Link, Error> {
|
||||||
Ok(Link {
|
Ok(Link {
|
||||||
backend: Box::new(new_tcp_backend(address)?),
|
backend: new_remote_backend(address)?,
|
||||||
packets: VecDeque::new(),
|
packets: VecDeque::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LocalDevice {
|
pub enum BackendType {
|
||||||
pub port: String,
|
Serial,
|
||||||
pub serial_number: String,
|
Ftdi,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_local_devices() -> Result<Vec<LocalDevice>, Error> {
|
impl Display for BackendType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(match self {
|
||||||
|
Self::Serial => "serial",
|
||||||
|
Self::Ftdi => "libftdi",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DeviceInfo {
|
||||||
|
pub backend: BackendType,
|
||||||
|
pub port: String,
|
||||||
|
pub serial: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_local_devices() -> Result<Vec<DeviceInfo>, Error> {
|
||||||
const SC64_VID: u16 = 0x0403;
|
const SC64_VID: u16 = 0x0403;
|
||||||
const SC64_PID: u16 = 0x6014;
|
const SC64_PID: u16 = 0x6014;
|
||||||
const SC64_SID: &str = "SC64";
|
const SC64_SID: &str = "SC64";
|
||||||
|
|
||||||
let mut serial_devices: Vec<LocalDevice> = Vec::new();
|
let mut devices: Vec<DeviceInfo> = Vec::new();
|
||||||
|
|
||||||
for device in serialport::available_ports()?.into_iter() {
|
if let Ok(list) = list_ftdi_devices(SC64_VID, SC64_PID) {
|
||||||
if let serialport::SerialPortType::UsbPort(info) = device.port_type {
|
for device in list.into_iter() {
|
||||||
let serial_number = info.serial_number.unwrap_or("".to_string());
|
if device.serial.starts_with(SC64_SID) {
|
||||||
if info.vid == SC64_VID && info.pid == SC64_PID && serial_number.starts_with(SC64_SID) {
|
devices.push(DeviceInfo {
|
||||||
serial_devices.push(LocalDevice {
|
backend: BackendType::Ftdi,
|
||||||
port: device.port_name,
|
port: format!("{FTDI_PREFIX}{}", device.port),
|
||||||
serial_number,
|
serial: device.serial,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if serial_devices.len() == 0 {
|
if let Ok(list) = serialport::available_ports() {
|
||||||
|
for device in list.into_iter() {
|
||||||
|
if let serialport::SerialPortType::UsbPort(i) = device.port_type {
|
||||||
|
if let Some(serial) = i.serial_number {
|
||||||
|
if i.vid == SC64_VID && i.pid == SC64_PID && serial.starts_with(SC64_SID) {
|
||||||
|
devices.push(DeviceInfo {
|
||||||
|
backend: BackendType::Serial,
|
||||||
|
port: format!("{SERIAL_PREFIX}{}", device.port_name),
|
||||||
|
serial,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if devices.len() == 0 {
|
||||||
return Err(Error::new("No SC64 devices found"));
|
return Err(Error::new("No SC64 devices found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(serial_devices);
|
return Ok(devices);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
mod cic;
|
mod cic;
|
||||||
mod error;
|
mod error;
|
||||||
pub mod firmware;
|
pub mod firmware;
|
||||||
|
mod ftdi;
|
||||||
mod link;
|
mod link;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
mod time;
|
mod time;
|
||||||
|
@ -1,19 +1,5 @@
|
|||||||
use super::{
|
use super::{error::Error, link::list_local_devices};
|
||||||
error::Error,
|
use std::net::{TcpListener, TcpStream};
|
||||||
link::{list_local_devices, new_serial, Command, DataType, Packet, Response, Serial},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::VecDeque,
|
|
||||||
io::{BufReader, BufWriter, ErrorKind, Read, Write},
|
|
||||||
net::{TcpListener, TcpStream},
|
|
||||||
sync::{
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
mpsc::{channel, Receiver, Sender},
|
|
||||||
Arc,
|
|
||||||
},
|
|
||||||
thread,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub enum ServerEvent {
|
pub enum ServerEvent {
|
||||||
Listening(String),
|
Listening(String),
|
||||||
@ -55,204 +41,208 @@ pub fn run(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Event {
|
// enum Event {
|
||||||
Command(Command),
|
// Command(Command),
|
||||||
Response(Response),
|
// Response(Response),
|
||||||
Packet(Packet),
|
// Packet(Packet),
|
||||||
KeepAlive,
|
// KeepAlive,
|
||||||
Closed(Option<Error>),
|
// Closed(Option<Error>),
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn server_accept_connection(port: String, stream: &mut TcpStream) -> Result<(), Error> {
|
fn server_accept_connection(_port: String, _stream: &mut TcpStream) -> Result<(), Error> {
|
||||||
let (event_sender, event_receiver) = channel::<Event>();
|
// let (event_sender, event_receiver) = channel::<Event>();
|
||||||
let exit_flag = Arc::new(AtomicBool::new(false));
|
// let exit_flag = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
let mut stream_writer = BufWriter::new(stream.try_clone()?);
|
// let mut stream_writer = BufWriter::new(stream.try_clone()?);
|
||||||
let mut stream_reader = stream.try_clone()?;
|
// let mut stream_reader = stream.try_clone()?;
|
||||||
|
|
||||||
let serial = Arc::new(new_serial(&port)?);
|
// let serial = Arc::new(new_local(&port)?);
|
||||||
let serial_writer = serial.clone();
|
// let serial_writer = serial.clone();
|
||||||
let serial_reader = serial.clone();
|
// let serial_reader = serial.clone();
|
||||||
|
|
||||||
let stream_event_sender = event_sender.clone();
|
// let stream_event_sender = event_sender.clone();
|
||||||
let stream_exit_flag = exit_flag.clone();
|
// let stream_exit_flag = exit_flag.clone();
|
||||||
let stream_thread = thread::spawn(move || {
|
// let stream_thread = thread::spawn(move || {
|
||||||
let closed_sender = stream_event_sender.clone();
|
// let closed_sender = stream_event_sender.clone();
|
||||||
match server_stream_thread(&mut stream_reader, stream_event_sender, stream_exit_flag) {
|
// match server_stream_thread(&mut stream_reader, stream_event_sender, stream_exit_flag) {
|
||||||
Ok(()) => closed_sender.send(Event::Closed(None)),
|
// Ok(()) => closed_sender.send(Event::Closed(None)),
|
||||||
Err(error) => closed_sender.send(Event::Closed(Some(error))),
|
// Err(error) => closed_sender.send(Event::Closed(Some(error))),
|
||||||
}
|
// }
|
||||||
.ok();
|
// .ok();
|
||||||
});
|
// });
|
||||||
|
|
||||||
let serial_event_sender = event_sender.clone();
|
// let serial_event_sender = event_sender.clone();
|
||||||
let serial_exit_flag = exit_flag.clone();
|
// let serial_exit_flag = exit_flag.clone();
|
||||||
let serial_thread = thread::spawn(move || {
|
// let serial_thread = thread::spawn(move || {
|
||||||
let closed_sender = serial_event_sender.clone();
|
// let closed_sender = serial_event_sender.clone();
|
||||||
match server_serial_thread(serial_reader, serial_event_sender, serial_exit_flag) {
|
// match server_serial_thread(serial_reader, serial_event_sender, serial_exit_flag) {
|
||||||
Ok(()) => closed_sender.send(Event::Closed(None)),
|
// Ok(()) => closed_sender.send(Event::Closed(None)),
|
||||||
Err(error) => closed_sender.send(Event::Closed(Some(error))),
|
// Err(error) => closed_sender.send(Event::Closed(Some(error))),
|
||||||
}
|
// }
|
||||||
.ok();
|
// .ok();
|
||||||
});
|
// });
|
||||||
|
|
||||||
let keepalive_event_sender = event_sender.clone();
|
// let keepalive_event_sender = event_sender.clone();
|
||||||
let keepalive_exit_flag = exit_flag.clone();
|
// let keepalive_exit_flag = exit_flag.clone();
|
||||||
let keepalive_thread = thread::spawn(move || {
|
// let keepalive_thread = thread::spawn(move || {
|
||||||
server_keepalive_thread(keepalive_event_sender, keepalive_exit_flag);
|
// server_keepalive_thread(keepalive_event_sender, keepalive_exit_flag);
|
||||||
});
|
// });
|
||||||
|
|
||||||
let result = server_process_events(&mut stream_writer, serial_writer, event_receiver);
|
// let result = server_process_events(&mut stream_writer, serial_writer, event_receiver);
|
||||||
|
|
||||||
exit_flag.store(true, Ordering::Relaxed);
|
// exit_flag.store(true, Ordering::Relaxed);
|
||||||
stream_thread.join().ok();
|
// stream_thread.join().ok();
|
||||||
serial_thread.join().ok();
|
// serial_thread.join().ok();
|
||||||
keepalive_thread.join().ok();
|
// keepalive_thread.join().ok();
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn server_process_events(
|
|
||||||
stream_writer: &mut BufWriter<TcpStream>,
|
|
||||||
serial_writer: Arc<Serial>,
|
|
||||||
event_receiver: Receiver<Event>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
for event in event_receiver.into_iter() {
|
|
||||||
match event {
|
|
||||||
Event::Command(command) => {
|
|
||||||
serial_writer.send_command(&command)?;
|
|
||||||
}
|
|
||||||
Event::Response(response) => {
|
|
||||||
stream_writer.write_all(&u32::to_be_bytes(DataType::Response.into()))?;
|
|
||||||
stream_writer.write_all(&[response.id])?;
|
|
||||||
stream_writer.write_all(&[response.error as u8])?;
|
|
||||||
stream_writer.write_all(&(response.data.len() as u32).to_be_bytes())?;
|
|
||||||
stream_writer.write_all(&response.data)?;
|
|
||||||
stream_writer.flush()?;
|
|
||||||
}
|
|
||||||
Event::Packet(packet) => {
|
|
||||||
stream_writer.write_all(&u32::to_be_bytes(DataType::Packet.into()))?;
|
|
||||||
stream_writer.write_all(&[packet.id])?;
|
|
||||||
stream_writer.write_all(&(packet.data.len() as u32).to_be_bytes())?;
|
|
||||||
stream_writer.write_all(&packet.data)?;
|
|
||||||
stream_writer.flush()?;
|
|
||||||
}
|
|
||||||
Event::KeepAlive => {
|
|
||||||
stream_writer.write_all(&u32::to_be_bytes(DataType::KeepAlive.into()))?;
|
|
||||||
stream_writer.flush()?;
|
|
||||||
}
|
|
||||||
Event::Closed(result) => match result {
|
|
||||||
Some(error) => return Err(error),
|
|
||||||
None => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// result
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn server_stream_thread(
|
// fn server_process_events(
|
||||||
stream: &mut TcpStream,
|
// stream_writer: &mut BufWriter<TcpStream>,
|
||||||
event_sender: Sender<Event>,
|
// link: &mut Link,
|
||||||
exit_flag: Arc<AtomicBool>,
|
// event_receiver: Receiver<Event>,
|
||||||
) -> Result<(), Error> {
|
// ) -> Result<(), Error> {
|
||||||
let mut stream_reader = BufReader::new(stream.try_clone()?);
|
// for event in event_receiver.into_iter() {
|
||||||
|
// match event {
|
||||||
|
// Event::Command(command) => {
|
||||||
|
// // serial_writer.send_command(&command)?;
|
||||||
|
// // serial_writer.
|
||||||
|
// // link.execute_command(&command)?;
|
||||||
|
// link.execute_command(&command)?;
|
||||||
|
// }
|
||||||
|
// Event::Response(response) => {
|
||||||
|
// stream_writer.write_all(&u32::to_be_bytes(DataType::Response.into()))?;
|
||||||
|
// stream_writer.write_all(&[response.id])?;
|
||||||
|
// stream_writer.write_all(&[response.error as u8])?;
|
||||||
|
// stream_writer.write_all(&(response.data.len() as u32).to_be_bytes())?;
|
||||||
|
// stream_writer.write_all(&response.data)?;
|
||||||
|
// stream_writer.flush()?;
|
||||||
|
// }
|
||||||
|
// Event::Packet(packet) => {
|
||||||
|
// stream_writer.write_all(&u32::to_be_bytes(DataType::Packet.into()))?;
|
||||||
|
// stream_writer.write_all(&[packet.id])?;
|
||||||
|
// stream_writer.write_all(&(packet.data.len() as u32).to_be_bytes())?;
|
||||||
|
// stream_writer.write_all(&packet.data)?;
|
||||||
|
// stream_writer.flush()?;
|
||||||
|
// }
|
||||||
|
// Event::KeepAlive => {
|
||||||
|
// stream_writer.write_all(&u32::to_be_bytes(DataType::KeepAlive.into()))?;
|
||||||
|
// stream_writer.flush()?;
|
||||||
|
// }
|
||||||
|
// Event::Closed(result) => match result {
|
||||||
|
// Some(error) => return Err(error),
|
||||||
|
// None => {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
let mut header = [0u8; 4];
|
// Ok(())
|
||||||
let header_length = header.len();
|
// }
|
||||||
|
|
||||||
loop {
|
// fn server_stream_thread(
|
||||||
let mut header_position = 0;
|
// stream: &mut TcpStream,
|
||||||
|
// event_sender: Sender<Event>,
|
||||||
|
// exit_flag: Arc<AtomicBool>,
|
||||||
|
// ) -> Result<(), Error> {
|
||||||
|
// let mut stream_reader = BufReader::new(stream.try_clone()?);
|
||||||
|
|
||||||
let timeout = stream.read_timeout()?;
|
// let mut header = [0u8; 4];
|
||||||
stream.set_read_timeout(Some(Duration::from_millis(10)))?;
|
// let header_length = header.len();
|
||||||
while header_position < header_length {
|
|
||||||
if exit_flag.load(Ordering::Relaxed) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
match stream_reader.read(&mut header[header_position..header_length]) {
|
|
||||||
Ok(0) => return Ok(()),
|
|
||||||
Ok(bytes) => header_position += bytes,
|
|
||||||
Err(error) => match error.kind() {
|
|
||||||
ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {}
|
|
||||||
_ => return Err(error.into()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream.set_read_timeout(timeout)?;
|
|
||||||
|
|
||||||
let data_type: DataType = u32::from_be_bytes(header).try_into()?;
|
// loop {
|
||||||
if !matches!(data_type, DataType::Command) {
|
// let mut header_position = 0;
|
||||||
return Err(Error::new("Received data type was not a command data type"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buffer = [0u8; 4];
|
// let timeout = stream.read_timeout()?;
|
||||||
let mut id_buffer = [0u8; 1];
|
// stream.set_read_timeout(Some(Duration::from_millis(10)))?;
|
||||||
let mut args = [0u32; 2];
|
// while header_position < header_length {
|
||||||
|
// if exit_flag.load(Ordering::Relaxed) {
|
||||||
|
// return Ok(());
|
||||||
|
// }
|
||||||
|
// match stream_reader.read(&mut header[header_position..header_length]) {
|
||||||
|
// Ok(0) => return Ok(()),
|
||||||
|
// Ok(bytes) => header_position += bytes,
|
||||||
|
// Err(error) => match error.kind() {
|
||||||
|
// ErrorKind::Interrupted | ErrorKind::TimedOut | ErrorKind::WouldBlock => {}
|
||||||
|
// _ => return Err(error.into()),
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// stream.set_read_timeout(timeout)?;
|
||||||
|
|
||||||
stream_reader.read_exact(&mut id_buffer)?;
|
// let data_type: DataType = u32::from_be_bytes(header).try_into()?;
|
||||||
let id = id_buffer[0];
|
// if !matches!(data_type, DataType::Command) {
|
||||||
|
// return Err(Error::new("Received data type was not a command data type"));
|
||||||
|
// }
|
||||||
|
|
||||||
stream_reader.read_exact(&mut buffer)?;
|
// let mut buffer = [0u8; 4];
|
||||||
args[0] = u32::from_be_bytes(buffer);
|
// let mut id_buffer = [0u8; 1];
|
||||||
stream_reader.read_exact(&mut buffer)?;
|
// let mut args = [0u32; 2];
|
||||||
args[1] = u32::from_be_bytes(buffer);
|
|
||||||
|
|
||||||
stream_reader.read_exact(&mut buffer)?;
|
// stream_reader.read_exact(&mut id_buffer)?;
|
||||||
let command_data_length = u32::from_be_bytes(buffer) as usize;
|
// let id = id_buffer[0];
|
||||||
let mut data = vec![0u8; command_data_length];
|
|
||||||
stream_reader.read_exact(&mut data)?;
|
|
||||||
|
|
||||||
if event_sender
|
// stream_reader.read_exact(&mut buffer)?;
|
||||||
.send(Event::Command(Command { id, args, data }))
|
// args[0] = u32::from_be_bytes(buffer);
|
||||||
.is_err()
|
// stream_reader.read_exact(&mut buffer)?;
|
||||||
{
|
// args[1] = u32::from_be_bytes(buffer);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
// stream_reader.read_exact(&mut buffer)?;
|
||||||
}
|
// let command_data_length = u32::from_be_bytes(buffer) as usize;
|
||||||
|
// let mut data = vec![0u8; command_data_length];
|
||||||
|
// stream_reader.read_exact(&mut data)?;
|
||||||
|
|
||||||
fn server_serial_thread(
|
// if event_sender
|
||||||
serial_reader: Arc<Serial>,
|
// .send(Event::Command(Command { id, args, data }))
|
||||||
event_sender: Sender<Event>,
|
// .is_err()
|
||||||
exit_flag: Arc<AtomicBool>,
|
// {
|
||||||
) -> Result<(), Error> {
|
// break;
|
||||||
let mut packets: VecDeque<Packet> = VecDeque::new();
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
while !exit_flag.load(Ordering::Relaxed) {
|
// Ok(())
|
||||||
let response = serial_reader.process_incoming_data(DataType::Packet, &mut packets)?;
|
// }
|
||||||
|
|
||||||
if let Some(response) = response {
|
// fn server_serial_thread(
|
||||||
if event_sender.send(Event::Response(response)).is_err() {
|
// link: &mut Link,
|
||||||
break;
|
// event_sender: Sender<Event>,
|
||||||
}
|
// exit_flag: Arc<AtomicBool>,
|
||||||
}
|
// ) -> Result<(), Error> {
|
||||||
|
// let mut packets: VecDeque<Packet> = VecDeque::new();
|
||||||
|
|
||||||
if let Some(packet) = packets.pop_front() {
|
// while !exit_flag.load(Ordering::Relaxed) {
|
||||||
if event_sender.send(Event::Packet(packet)).is_err() {
|
// let response = link.backend.process_incoming_data(DataType::Packet, &mut packets)?;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
// if let Some(response) = response {
|
||||||
}
|
// if event_sender.send(Event::Response(response)).is_err() {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
fn server_keepalive_thread(event_sender: Sender<Event>, exit_flag: Arc<AtomicBool>) {
|
// if let Some(packet) = packets.pop_front() {
|
||||||
let mut keepalive = Instant::now();
|
// if event_sender.send(Event::Packet(packet)).is_err() {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
while !exit_flag.load(Ordering::Relaxed) {
|
// Ok(())
|
||||||
if keepalive.elapsed() >= Duration::from_secs(5) {
|
// }
|
||||||
keepalive = Instant::now();
|
|
||||||
if event_sender.send(Event::KeepAlive).is_err() {
|
// fn server_keepalive_thread(event_sender: Sender<Event>, exit_flag: Arc<AtomicBool>) {
|
||||||
break;
|
// let mut keepalive = Instant::now();
|
||||||
}
|
|
||||||
} else {
|
// while !exit_flag.load(Ordering::Relaxed) {
|
||||||
thread::sleep(Duration::from_millis(10));
|
// if keepalive.elapsed() >= Duration::from_secs(5) {
|
||||||
}
|
// keepalive = Instant::now();
|
||||||
}
|
// if event_sender.send(Event::KeepAlive).is_err() {
|
||||||
}
|
// break;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// thread::sleep(Duration::from_millis(100));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
Loading…
Reference in New Issue
Block a user