mod communicator;
mod serial_port;
mod status;
use std::fmt::{Display, Formatter};
use std::hash::Hash;
use std::io;
use std::ops::Deref;
use communicator::Communicator;
use log;
use nusb::DeviceInfo;
use smol::lock::RwLock;
use status::Status;
use crate::devices::{abort, abort_device, get_device, remove_device};
use crate::error::{cmd, sn};
use crate::messages::{Dispatcher, Metadata, Provenance};
#[derive(Debug)]
pub(crate) struct UsbPrimitive<const CHANNELS: usize> {
serial_number: String,
device_info: DeviceInfo,
status: RwLock<Status<CHANNELS>>,
}
impl<const CHANNELS: usize> UsbPrimitive<CHANNELS> {
pub(super) fn new(
serial_number: &String,
ids: &[Metadata<CHANNELS>],
) -> Result<Self, sn::Error> {
log::debug!("USB Primitive {serial_number} NEW (requested)");
let device_info = get_device(serial_number)?;
log::debug!("USB Primitive {serial_number} NEW (found)");
let device = Self {
serial_number: serial_number.clone(),
device_info,
status: RwLock::new(Status::Closed(Dispatcher::new(ids, serial_number))),
};
log::debug!("USB Primitive {serial_number} NEW (success)");
Ok(device)
}
pub(crate) fn serial_number(&self) -> &str {
&self.serial_number
}
pub(super) async fn is_open(&self) -> bool {
match *self.status.read().await {
Status::Open(_) => true,
Status::Closed(_) => false,
}
}
pub(super) async fn open(&self) -> Result<(), io::Error> {
log::debug!("{self} OPEN (requested)");
let mut guard = self.status.write().await;
if let Status::Closed(dsp) = guard.deref() {
log::debug!("{self} OPEN (is closed)");
let interface = self.device_info.open()?.detach_and_claim_interface(0)?;
let dispatcher = dsp.clone(); let communicator = Communicator::new(interface, dispatcher).await;
*guard = Status::Open(communicator);
}
log::debug!("{self} OPEN (success)");
Ok(())
}
pub(super) async fn close(&self) -> Result<(), io::Error> {
log::debug!("{self} CLOSE (requested)");
let mut guard = self.status.write().await;
if let Status::Open(communicator) = guard.deref() {
log::debug!("{self} CLOSE (is open)");
let dispatcher = communicator.get_dispatcher();
*guard = Status::Closed(dispatcher);
}
log::debug!("{self} CLOSE (success)");
Ok(())
}
async fn abort(&self) {
log::warn!("{self} ABORT (requested)");
abort_device(self.serial_number());
log::warn!("{self} ABORT (success)");
}
pub(crate) async fn receiver(&self, id: &[u8], channel: usize) -> Provenance {
log::debug!("{self} CHANNEL {channel} RECEIVER {id:02X?} (requested)");
self.status
.read()
.await
.dispatcher()
.receiver(id, channel)
.await
}
pub(crate) async fn new_receiver(&self, id: &[u8], channel: usize) -> Provenance {
log::debug!("{self} CHANNEL {channel} NEW_RECEIVER (requested)");
self.status
.read()
.await
.dispatcher()
.new_receiver(id, channel)
.await
}
pub(crate) async fn send(&self, command: Vec<u8>) {
log::debug!("{self} SEND (requested)");
self.try_send(command)
.await
.unwrap_or_else(|e| abort(format!("{self} SEND (failed) {e}")));
}
pub(crate) async fn try_send(&self, command: Vec<u8>) -> Result<(), cmd::Error> {
let guard = self.status.read().await;
match &*guard {
Status::Open(communicator) => {
communicator.send(command).await;
Ok(())
}
Status::Closed(_) => Err(cmd::Error::DeviceClosed),
}
}
}
impl<const CHANNELS: usize> PartialEq<UsbPrimitive<CHANNELS>> for UsbPrimitive<CHANNELS> {
fn eq(&self, other: &Self) -> bool {
self.device_info.vendor_id() == other.device_info.vendor_id()
&& self.device_info.product_id() == other.device_info.product_id()
&& self.device_info.serial_number().unwrap_or("")
== other.device_info.serial_number().unwrap_or("")
}
}
impl<const CH: usize> Eq for UsbPrimitive<CH> {}
impl<const CH: usize> Hash for UsbPrimitive<CH> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.device_info.vendor_id().hash(state);
self.device_info.product_id().hash(state);
self.device_info.serial_number().unwrap_or("").hash(state);
}
}
impl<const CH: usize> Display for UsbPrimitive<CH> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "USB Primitive {}", self.serial_number)
}
}
impl<const CH: usize> Drop for UsbPrimitive<CH> {
fn drop(&mut self) {
smol::block_on(async {
remove_device(self.serial_number());
});
}
}