use std::sync::mpsc::{self, Sender};
use eframe::egui;
use nord_usb::transport::{usb, UsbTransport};
use super::worker::{self, Emit, Flow};
use super::{DeviceCard, DeviceCmd, DeviceEvent};
pub struct Link {
ctx: egui::Context,
events: Sender<DeviceEvent>,
commands: Option<Sender<DeviceCmd>>,
}
impl Link {
pub fn new(ctx: egui::Context, events: Sender<DeviceEvent>) -> Link {
Link {
ctx,
events,
commands: None,
}
}
pub fn connect(&mut self) {
let (tx, rx) = mpsc::channel::<DeviceCmd>();
self.commands = Some(tx);
let emit = Emit::new(self.events.clone(), self.ctx.clone());
std::thread::spawn(move || {
let mut transport = match open() {
Ok((card, transport)) => {
emit.send(DeviceEvent::Connected(card));
transport
}
Err(why) => {
emit.send(DeviceEvent::ConnectFailed(why));
return;
}
};
let mut flow = Flow::Released;
while let Ok(cmd) = rx.recv() {
flow = nord_usb::block_on(worker::run(&mut transport, cmd, &emit));
if flow != Flow::Continue {
break;
}
}
drop(transport);
emit.send(DeviceEvent::Disconnected {
lost: flow == Flow::Lost,
});
});
}
pub fn disconnect(&mut self) {
if let Some(tx) = self.commands.take() {
let _ = tx.send(DeviceCmd::Disconnect);
}
}
pub fn send(&mut self, cmd: DeviceCmd) {
if let Some(tx) = &self.commands {
let _ = tx.send(cmd);
}
}
}
fn open() -> Result<(DeviceCard, UsbTransport), String> {
let devices = usb::list().map_err(|e| e.to_string())?;
let info = devices
.into_iter()
.next()
.ok_or("no Clavia device found — is the instrument awake and on a data cable?")?;
let Some(interface) = info.interfaces().find(|i| i.class() == 0xff) else {
return Err(format!(
"{} exposes no vendor interface; this tool cannot drive it",
info.product_string().unwrap_or("the attached device"),
));
};
let interface = interface.interface_number();
let transport = UsbTransport::open(&info).map_err(|e| e.to_string())?;
let identity = transport.identity().ok();
let card = DeviceCard {
build: identity.map(|id| id.build),
firmware: identity.map(|id| id.firmware),
interface: Some(interface),
kind: identity.map(|id| id.kind),
manufacturer: info.manufacturer_string().map(str::to_string),
max_transfer: identity.map(|id| id.max_transfer),
product: info
.product_string()
.unwrap_or("unnamed device")
.to_string(),
product_id: info.product_id(),
serial: info.serial_number().map(str::to_string),
vendor_id: info.vendor_id(),
};
Ok((card, transport))
}