Skip to main content

DeviceHandle

Struct DeviceHandle 

Source
pub struct DeviceHandle {
Show 19 fields pub id: String, pub mac: Option<String>, pub port_path: String, pub baud_rate: u32, pub native_usb: bool, pub serial_connected: AtomicBool, pub collection_running: AtomicBool, pub firmware_verified: AtomicBool, pub cmd_tx: Sender<String>, pub csi_tx: Sender<Vec<u8>>, pub output_mode_tx: Sender<OutputMode>, pub session_file_tx: Sender<Option<String>>, pub info_request_tx: Sender<InfoResponder>, pub config: Mutex<DeviceConfig>, pub device_info: Mutex<Option<DeviceInfo>>, pub fault: Mutex<Option<String>>, pub recovery_cycles: AtomicU32, pub shutdown: CancellationToken, pub profile: Arc<dyn CsiProfile>,
}
Expand description

All per-device runtime state for one attached ESP32.

Wrapped in an Arc and shared between the device’s serial task and every route handler that resolves it from the registry. The inner atomics, mutexes, and watch senders are plain (not individually Arc-wrapped): the single Arc<DeviceHandle> provides the sharing.

Fields§

§id: String

Stable identifier used in URL paths. Derived from the board’s MAC (D0-CF-13-E2-90-E8) when known, so it survives a ttyACMx renumbering; falls back to a CLI alias or the sanitized port basename.

§mac: Option<String>

Stable hardware identity: the board’s MAC as reported by its USB iSerialNumber descriptor (AA:BB:CC:DD:EE:FF), read at scan time without opening the port. None for adapters that expose no serial number (most CP210x/CH340 UART bridges). This is what lets the supervisor follow a board across a re-enumeration that changes its /dev/ttyACMx number — see crate::supervisor::run_supervisor. The firmware also echoes it in the info block (mac=) as confirmation.

§port_path: String

USB serial port path used to reach the ESP32 (e.g. /dev/ttyUSB0). Pinned for the lifetime of the per-device task; if the board re-enumerates under a different node the supervisor tears this task down and respawns it (keyed by Self::mac) at the new path.

§baud_rate: u32

Baud rate negotiated at startup. The serial task and the RTS-reset handler both read this so a single source of truth governs the link.

§native_usb: bool

True when this port is an Espressif native USB-Serial-JTAG endpoint (VID 0x303A). Such chips re-enumerate their USB device when reset, so the serial task must NOT pulse RTS/DTR on connect — doing so drops the /dev/ttyACMx node (often returning under a different number) and the pinned-path reconnect loop can then never re-verify the device.

§serial_connected: AtomicBool

Whether the serial task currently has an open and healthy ESP32 link.

§collection_running: AtomicBool

Best-effort flag: true after successful start, false after reset/disconnect.

§firmware_verified: AtomicBool

true once the serial task (or an explicit /api/.../info call) has observed a valid ESP-CSI-CLI/<version> magic block from the device. Cleared on disconnect, on reset (until the post-reset re-verification completes), and on a failed verification. Command endpoints refuse to send while this is false.

§cmd_tx: Sender<String>

Send CLI command strings to the serial background task.

§csi_tx: Sender<Vec<u8>>

Broadcast raw CSI frame bytes (COBS-framed postcard) to this device’s WebSocket clients.

§output_mode_tx: Sender<OutputMode>

Notify the serial task of output-mode changes (stream / dump / both).

§session_file_tx: Sender<Option<String>>

Signal the serial task of the current session’s dump file path. Some(path) → open/reuse that file; None → session ended, close file.

§info_request_tx: Sender<InfoResponder>

Issue an info command on the device and capture the magic block. The serial task synchronously consumes the responder.

§config: Mutex<DeviceConfig>

Cached view of this device’s configuration.

§device_info: Mutex<Option<DeviceInfo>>

Last successfully parsed firmware identification block. None until the first verification succeeds; cleared alongside firmware_verified.

§fault: Mutex<Option<String>>

Detected chip fault (e.g. the ESP32-C5/C6 USB-JTAG reset-loop wedge or a ROM boot loop), with the recovery action, set by the serial task when firmware verification keeps failing with a recognizable boot signature. Cleared on successful verification. Surfaced via GET /api/devices.

§recovery_cycles: AtomicU32

Completed recovery attempts (the firmware restart rung, whose reboot re-enumerates native USB and thus restarts the per-connection escalation ladder). Carried across handle respawns (re-enumeration replaces the handle!) via RecoveryCarryOver so an unrecoverable device is restarted at most once and then declared faulted, instead of being bounced through reboots forever. Cleared on successful verification.

§shutdown: CancellationToken

Cancelled by the supervisor when the device is unplugged, signalling the serial task to tear down cleanly.

§profile: Arc<dyn CsiProfile>

Optional capability extensions (extra protocols/presets/format labels). Inherited from the owning DeviceRegistry; the per-device serial task consults it when labelling Parquet data_format.

Implementations§

Source§

impl DeviceHandle

Source

pub fn require_firmware(&self) -> Option<(StatusCode, Json<ApiResponse>)>

Returns an early-return tuple suitable for handlers when the firmware has not yet been verified as esp-csi-cli-rs. Use this to short-circuit any endpoint that issues a CLI command — sending commands to an unverified device may interact with whatever bootloader/firmware is listening in unintended ways.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more