Connection guide
find opens the first match. With several boxes, find_medius lists every match as a PortInfo (path, vid, pid) without opening any. Then open the chosen one.
use medius::{Device, find_medius};
let ports = find_medius();
for port in &ports {
println!("{} (vid={:#06x} pid={:#06x})", port.path, port.vid, port.pid);
}
// open one (here, the first):
let port = ports.first().ok_or(medius::Error::NotFound)?;
let dev = Device::open(&port.path)?;Threading and Clone Share one connection across threads
Device is Send + Sync and clones cheaply (an Arc inside). One connection serves any number of threads, and every clone shares the open port.
use std::thread;
let worker = device.clone();
let handle = thread::spawn(move || {
worker.move_rel(10, 0)
});
handle.join().unwrap()?;Concurrent queries join and cloning across tasks
AsyncDevice clones the same way across tasks. futures::future::join awaits queries together.
let (v, h) = futures::future::join(
device.query_version(),
device.query_health(),
).await;
let v = v?;
let h = h?;
println!("{v}, link_up={}", h.link_up);Only the queries need .await. Mutators stay synchronous on either handle.
Keepalive and holds Holds past the silence window
If no frame arrives for the silence window, the box clears every injected input and pending move.
A Device keeps overrides past the window. While anything is held, its background thread (medius-keepalive) sends QUERY(STATS) every DEFAULT_KEEPALIVE_CADENCE (500 ms). Each tick also re-sends held catch subscriptions, rewrite rules and transforms.
| State | Behaviour |
|---|---|
| Override held | Keepalive thread runs. The reply's session counter shows whether the box released the session. |
| Clip held | Keepalive thread runs while a clip is loaded (from append to clear), a clip setting is off its default, or a trigger of either kind is bound. |
| Idle | No QUERY(STATS) ticks. A device-chip restart still starts a recovery: QUERY(CAPS) every 50 ms (every 500 ms after the first 5 s) until the box has a clone, then one QUERY(STATS) and one QUERY(CLIP). |
device.press(Button::LEFT)?;
// The keepalive thread sends QUERY(STATS), so the hold outlasts
// the 1000 ms silence window.
std::thread::sleep(std::time::Duration::from_secs(5));
device.reset()?;An override is an input the box holds down or up, set by press or force_release. The library keeps a copy. After a dropped link, reapply and reconnect restore it. After a device-chip restart or a released session, the library restores it itself.
Releasing the device Drop the handle; no close() call
Dropping the last Arc-backed handle runs its Drop and tears the connection down.
| Step | Effect |
|---|---|
| Signal | Sets the stop flag the reader and keepalive threads watch. |
| Join | Waits for both threads to exit. |
| Close | Releases the serial port as the transport drops. |
To return the box to passthrough at once, call reset before drop.
device.reset()?; // passthrough now, not after the silence window
drop(device); // tears down threads and closes the port