refactor(host/usb): bump hsmc to 0.5 and collapse WaitingForFirstFrame into Connected
hsmc 0.5 makes default(...) optional on every state. Drop the WaitingForFirstFrame substate that existed only because 0.4 required Connected to declare a default child — Connected itself is now the resting "DTR up, no traffic yet" state, and on(FrameReceived) => Active descends into the inactivity-timer state on the first byte. Behavior is identical (entry/exit chain math unchanged); the chart has one fewer state and one fewer default(). Display and Radio charts left as-is — their defaults are all classic descents serving real grouping/lifecycle purposes (Splash grouping, Bright sub-state isolation, RX/TX session lifecycle), not 0.4 artifacts. No journal-replay tests in this repo, so the 0.5 journal-format change does not apply.
This commit is contained in:
3 files changed
+32
-35
No files matched your search
Generated
+4
-4
@@ -1550,9 +1550,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hsmc"
|
name = "hsmc"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adbdeff427849daf4d03395027c8538dcdd02a0a26c6e59745f8647f5e657527"
|
checksum = "a3951a3170ce7aa0f0bf4627ac4cc650857f15449c98e97647fa29fcd19c6d33"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"defmt 0.3.100",
|
"defmt 0.3.100",
|
||||||
"embassy-futures",
|
"embassy-futures",
|
||||||
@@ -1564,9 +1564,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hsmc-macros"
|
name = "hsmc-macros"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66ae4d525e3047234d69597f616c89b9ad49ac6a851e4522322a9d74fc2cc1bf"
|
checksum = "4b6d97dd1d31fba9e2f96c65c0ce38d7bc1ff59abe775a0ae18e14dfae01d1d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ embassy-usb-driver = "0.2"
|
|||||||
ucobs = { version = "0.3", features = ["legacy-msrv"] }
|
ucobs = { version = "0.3", features = ["legacy-msrv"] }
|
||||||
heapless = "0.9"
|
heapless = "0.9"
|
||||||
donglora-protocol = "1"
|
donglora-protocol = "1"
|
||||||
hsmc = { version = "0.4", default-features = false, features = ["embassy", "trace-defmt"] }
|
hsmc = { version = "0.5", default-features = false, features = ["embassy", "trace-defmt"] }
|
||||||
# Local path while iterating on CAD IRQ-clear / standby-after-CAD fixes.
|
# Local path while iterating on CAD IRQ-clear / standby-after-CAD fixes.
|
||||||
# Switch back to git fork (then crates.io) once changes are upstreamed.
|
# Switch back to git fork (then crates.io) once changes are upstreamed.
|
||||||
lora-phy = { path = "../../lora-rs/lora-phy", features = ["defmt-03"] }
|
lora-phy = { path = "../../lora-rs/lora-phy", features = ["defmt-03"] }
|
||||||
|
|||||||
+27
-30
@@ -3,11 +3,11 @@
|
|||||||
//! ```text
|
//! ```text
|
||||||
//! Host
|
//! Host
|
||||||
//! ├── Disconnected (default) no DTR
|
//! ├── Disconnected (default) no DTR
|
||||||
//! └── Connected host attached
|
//! └── Connected host attached, DTR up
|
||||||
//! ├── WaitingForFirstFrame (default) DTR up, no traffic yet
|
//! │ resting here means no traffic yet —
|
||||||
//! │ NO inactivity timer (boot quiet
|
//! │ NO inactivity timer (boot quiet is
|
||||||
//! │ is normal — chart structurally
|
//! │ normal; chart structurally blocks the
|
||||||
//! │ blocks the spurious 1s fire).
|
//! │ spurious 1s fire).
|
||||||
//! └── Active ≥1 frame seen since connect
|
//! └── Active ≥1 frame seen since connect
|
||||||
//! inactivity timer (1s) armed and
|
//! inactivity timer (1s) armed and
|
||||||
//! restarted by every frame via
|
//! restarted by every frame via
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
//! 1. **No spurious inactivity at boot.** The 1-second timer only exists
|
//! 1. **No spurious inactivity at boot.** The 1-second timer only exists
|
||||||
//! in `Active`, which requires a `FrameReceived` event to enter.
|
//! in `Active`, which requires a `FrameReceived` event to enter.
|
||||||
//! A freshly-connected host that holds DTR high but sends nothing
|
//! A freshly-connected host that holds DTR high but sends nothing
|
||||||
//! sits in `WaitingForFirstFrame` indefinitely — no timer, no warn,
|
//! rests at `Connected` indefinitely (hsmc 0.5 optional-default
|
||||||
//! no Reset cascade.
|
//! "microwave Standby" pattern) — no timer, no warn, no Reset cascade.
|
||||||
//! 2. **Chart-owned timer scope.** Per hsmc README rule §7 (timers
|
//! 2. **Chart-owned timer scope.** Per hsmc README rule §7 (timers
|
||||||
//! armed by state lifecycle), the inactivity timer is cancelled on
|
//! armed by state lifecycle), the inactivity timer is cancelled on
|
||||||
//! every exit of `Active`, restarted on every entry. No global
|
//! every exit of `Active`, restarted on every entry. No global
|
||||||
@@ -58,8 +58,8 @@ const POLL_INTERVAL_MS: u64 = 100;
|
|||||||
pub enum HostInput {
|
pub enum HostInput {
|
||||||
DtrRaised,
|
DtrRaised,
|
||||||
DtrDropped,
|
DtrDropped,
|
||||||
/// A USB read returned ≥1 byte. Drives `WaitingForFirstFrame →
|
/// A USB read returned ≥1 byte. Drives `Connected → Active` on
|
||||||
/// Active` (first frame after connect) and the `Active → Active`
|
/// the first frame after DTR up, and the `Active → Active`
|
||||||
/// self-transition that restarts the inactivity timer per
|
/// self-transition that restarts the inactivity timer per
|
||||||
/// hsmc README rule §4 (self-transition exits + re-enters target,
|
/// hsmc README rule §4 (self-transition exits + re-enters target,
|
||||||
/// re-arming its timer).
|
/// re-arming its timer).
|
||||||
@@ -122,24 +122,21 @@ statechart! {
|
|||||||
entry: wake_display;
|
entry: wake_display;
|
||||||
exit: reset_radio, reset_display;
|
exit: reset_radio, reset_display;
|
||||||
on(DtrDropped) => Disconnected;
|
on(DtrDropped) => Disconnected;
|
||||||
default(WaitingForFirstFrame);
|
// hsmc 0.5 microwave-Standby pattern: Connected itself is the
|
||||||
|
// resting "DTR up, no traffic yet" state. No inactivity timer
|
||||||
// Default child of Connected. No inactivity timer yet —
|
// here — boot quiet is normal (mux-rs holds DTR high before
|
||||||
// a freshly-connected host that hasn't sent its first byte
|
// any client attaches), so the chart structurally cannot fire
|
||||||
// is normal (mux-rs holds DTR high before any client
|
// a 1 s warn before the first byte. The first FrameReceived
|
||||||
// attaches). Stays here indefinitely until the first
|
// descends into Active, which owns the inactivity timer.
|
||||||
// frame arrives.
|
|
||||||
state WaitingForFirstFrame {
|
|
||||||
on(FrameReceived) => Active;
|
on(FrameReceived) => Active;
|
||||||
}
|
|
||||||
|
|
||||||
// Inactivity timer is chart-scoped: armed on every entry to
|
// Inactivity timer is chart-scoped: armed on every entry to
|
||||||
// Active (whether from WaitingForFirstFrame or self-loop
|
// Active (from Connected on the first frame, or from the
|
||||||
// after a frame), cancelled on every exit (per hsmc README
|
// self-transition below on each subsequent frame), cancelled
|
||||||
// rule §7). The self-transition `on(FrameReceived) =>
|
// on every exit (per hsmc README rule §7). The self-transition
|
||||||
// Active` re-enters Active per rule §4, re-arming the
|
// re-enters Active per rule §4, re-arming the timer —
|
||||||
// timer — equivalent to the "reset last_frame_at on each
|
// equivalent to the "reset last_frame_at on each byte" pattern
|
||||||
// byte" pattern but expressed in chart shape.
|
// but expressed in chart shape.
|
||||||
//
|
//
|
||||||
// hsmc's `after` triggers want `hsmc::Duration` (re-export
|
// hsmc's `after` triggers want `hsmc::Duration` (re-export
|
||||||
// of `core::time::Duration`), not `embassy_time::Duration`.
|
// of `core::time::Duration`), not `embassy_time::Duration`.
|
||||||
@@ -275,9 +272,9 @@ async fn protocol_loop<'d, D: embassy_usb_driver::Driver<'d>>(
|
|||||||
n,
|
n,
|
||||||
receiver.dtr(),
|
receiver.dtr(),
|
||||||
);
|
);
|
||||||
// Drives WaitingForFirstFrame → Active on first byte
|
// Drives Connected → Active on first byte and
|
||||||
// and Active → Active (timer-restart) on every byte
|
// Active → Active (timer-restart) on every byte after.
|
||||||
// after. The chart owns inactivity from here.
|
// The chart owns inactivity from here.
|
||||||
let _ = machine.dispatch(HostInput::FrameReceived).await;
|
let _ = machine.dispatch(HostInput::FrameReceived).await;
|
||||||
dispatch_bytes(&mut decoder, &read_buf[..n], commands, outbound).await;
|
dispatch_bytes(&mut decoder, &read_buf[..n], commands, outbound).await;
|
||||||
}
|
}
|
||||||
@@ -385,9 +382,9 @@ async fn protocol_loop<'d, D: embassy_usb_driver::Driver<'d>>(
|
|||||||
|
|
||||||
// DTR edge detection — drives Connected / Disconnected
|
// DTR edge detection — drives Connected / Disconnected
|
||||||
// transitions. The chart's `current_state()` may be
|
// transitions. The chart's `current_state()` may be
|
||||||
// Disconnected, Connected/WaitingForFirstFrame, or
|
// Disconnected, Connected (resting), or Connected/Active.
|
||||||
// Connected/Active per the new hierarchy. We're connected if
|
// We're connected if current state is anywhere inside
|
||||||
// current state is anywhere inside Connected.
|
// Connected.
|
||||||
let dtr_high = receiver.dtr();
|
let dtr_high = receiver.dtr();
|
||||||
let was_connected = !matches!(
|
let was_connected = !matches!(
|
||||||
machine.current_state(),
|
machine.current_state(),
|
||||||
|
|||||||
Reference in new issue
Block a user