Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .cargo/audit.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,4 @@

[advisories]
# Advisories to ignore (transitive dependencies we can't fix)
ignore = [
# rustls-webpki 0.102.x CRL issue - no patch in 0.102.x line
# Transitive via rumqttc, waiting for upstream fix
# Low impact: requires compromised CA to exploit
"RUSTSEC-2026-0049",
]
ignore = []
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **M17 — centralized Embassy connector spine: one audited home for the single-core `unsafe` ([Design 033](docs/design/033-M17-unify-connectors-drop-send.md)).** New `aimdb-embassy-adapter::connectors` module (features `connectors` / `connector-io`) collects the force-`Send` plumbing every Embassy connector used to hand-roll: session transports get `EmbassySessionClient`/`EmbassySessionServer`, `OneShotDialer`/`OneShotListener`/`OneShotCell`, and the framed `EmbassyConnection` + `Framer`; data-plane transports get the `EmbassySink`/`EmbassySource` bridges (over `EmbassySinkRaw`/`EmbassySourceRaw`) that ride core's existing `pump_sink`/`pump_source`, plus `into_box_future` for protocol tasks. The serial Embassy half is now thin sugar (just a COBS `Framer`) with **zero `unsafe`** (down from a 407-line hand-roll with 7 `unsafe impl`s); the MQTT and KNX Embassy halves dropped their hand-rolled publisher/router loops and `SendFutureWrapper` use to ride core's pumps (KNX inbound telegrams now flow through `pump_source`). All connector-crate `unsafe`/`SendFutureWrapper` is gone — confined to the adapter. The std/Tokio side, `aimdb-client`, the WebSocket server, examples, and tests are unchanged. (Chosen over Design 033's original "drop `Send` from the contract", which would have pushed `!Send` onto the std side; see the doc's Implementation Decision.) ([aimdb-embassy-adapter](aimdb-embassy-adapter/CHANGELOG.md), [aimdb-serial-connector](aimdb-serial-connector/CHANGELOG.md), [aimdb-mqtt-connector](aimdb-mqtt-connector/CHANGELOG.md), [aimdb-knx-connector](aimdb-knx-connector/CHANGELOG.md))

- **Remote access via connectors — Phases 0–6: converge four hand-rolled networking stacks onto two shared engines (Issue #39, [design doc](docs/design/remote-access-via-connectors.md)).** AimX remote access (and any future transport) now rides the connector layer instead of a bespoke I/O abstraction. New, runtime-neutral `aimdb-core::session` module (feature `connector-session`, `no_std + alloc`): the three-layer substrate (`Connection`/`Listener`/`Dialer` + `EnvelopeCodec` + `Dispatch`/`Session`), the reactive **server** engine (`serve`/`run_session`) and proactive **client** engine (`run_client`/`pump_client`), the `pump_sink`/`pump_source` data-plane toolkit, and the transport-agnostic `SessionClientConnector`/`SessionServerConnector`. The AimX-v2 NDJSON protocol (`session::aimx`: `AimxCodec` + `AimxDispatch`) and the WebSocket connector are ports onto this substrate, so the AimX server/client and WS server/client stacks collapse onto the two engines. New **`aimdb-uds-connector`** crate carries the UDS transport (`UdsClient`/`UdsServer`). ([aimdb-core](aimdb-core/CHANGELOG.md), [aimdb-uds-connector](aimdb-uds-connector/CHANGELOG.md), [aimdb-websocket-connector](aimdb-websocket-connector/CHANGELOG.md), [aimdb-client](aimdb-client/CHANGELOG.md), [aimdb-mqtt-connector](aimdb-mqtt-connector/CHANGELOG.md), [aimdb-knx-connector](aimdb-knx-connector/CHANGELOG.md), [aimdb-embassy-adapter](aimdb-embassy-adapter/CHANGELOG.md))
- **M16 — JSON codec extracted behind the `json-serialize` feature; `RecordValue::as_json()` now works on `no_std + alloc`, not just `std` ([Design 032](docs/design/032-M16-aimx-json-codec.md)).** New `aimdb-core::codec` module: `RemoteSerialize` (blanket-impl'd for every `serde` `Serialize + DeserializeOwned` type), the object-safe `JsonCodec<T>`, and the zero-sized `SerdeJsonCodec`. `serde_json` runs on `alloc`, so embedded targets can opt in; `std` enables the feature transitively, so std builds are unaffected. ([aimdb-core](aimdb-core/CHANGELOG.md))
- **Embassy buffer + join-queue tests now run in CI (Issue #85).** The join-queue tests previously sat behind `embassy-runtime`, which pulls `embassy-executor`'s cortex-m assembly and can't compile under `cargo test` on x86_64 — so ordering / backpressure / clone-routing regressions were never caught. The `join_queue` module is now gated on `embassy-sync`, and `make test` runs the embassy adapter's unit tests + doctests on the host (no executor). Also adds `EmbassyBuffer::peek()` and fixes a stale `EmbassyBuffer` doc example. ([aimdb-embassy-adapter](aimdb-embassy-adapter/CHANGELOG.md))
Expand Down
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ test:
cargo test --package aimdb-tokio-adapter --features "tokio-runtime,tracing,metrics"
@printf "$(YELLOW) → Testing tokio adapter (with profiling)$(NC)\n"
cargo test --package aimdb-tokio-adapter --features "tokio-runtime,tracing,profiling"
@printf "$(YELLOW) → Testing embassy adapter (host, no executor: buffers, join-queue, doctests)$(NC)\n"
cargo test --package aimdb-embassy-adapter --no-default-features --features "alloc,embassy-sync,embassy-time"
@printf "$(YELLOW) → Testing embassy adapter (host, no executor: buffers, join-queue, connector spine, doctests)$(NC)\n"
cargo test --package aimdb-embassy-adapter --no-default-features --features "alloc,embassy-sync,embassy-time,connectors"
@printf "$(YELLOW) → Testing sync wrapper$(NC)\n"
cargo test --package aimdb-sync
@printf "$(YELLOW) → Testing codegen library$(NC)\n"
Expand Down Expand Up @@ -338,6 +338,8 @@ test-embedded:
cargo check --package aimdb-embassy-adapter --target thumbv7em-none-eabihf --target-dir $(EMBEDDED_CHECK_TARGET_DIR) --no-default-features --features "embassy-runtime,profiling"
@printf "$(YELLOW) → Checking aimdb-embassy-adapter with metrics on thumbv7em-none-eabihf target$(NC)\n"
cargo check --package aimdb-embassy-adapter --target thumbv7em-none-eabihf --target-dir $(EMBEDDED_CHECK_TARGET_DIR) --no-default-features --features "embassy-runtime,metrics"
@printf "$(YELLOW) → Checking aimdb-embassy-adapter connector spine (connector-io) on thumbv7em-none-eabihf target$(NC)\n"
cargo check --package aimdb-embassy-adapter --target thumbv7em-none-eabihf --target-dir $(EMBEDDED_CHECK_TARGET_DIR) --no-default-features --features "embassy-runtime,connector-io"
@printf "$(YELLOW) → Checking aimdb-mqtt-connector (Embassy) on thumbv7em-none-eabihf target$(NC)\n"
cargo check --package aimdb-mqtt-connector --target thumbv7em-none-eabihf --target-dir $(EMBEDDED_CHECK_TARGET_DIR) --no-default-features --features "embassy-runtime"
@printf "$(YELLOW) → Checking aimdb-mqtt-connector (Embassy + defmt) on thumbv7em-none-eabihf target$(NC)\n"
Expand Down
5 changes: 5 additions & 0 deletions aimdb-embassy-adapter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **M17 — centralized Embassy connector spines: the one audited home for the single-core `unsafe` ([Design 033](../docs/design/033-M17-unify-connectors-drop-send.md)).** New `connectors` module (features `connectors` / `connector-io`) collecting the force-`Send` plumbing every Embassy connector used to hand-roll, so a connector crate carries **no `unsafe` and no `SendFutureWrapper`**:
- **Session spine** — `EmbassySessionClient` / `EmbassySessionServer` (the Embassy duals of core's `SessionClientConnector` / `SessionServerConnector`), the one-shot `OneShotDialer` / `OneShotListener` over a moved-in peripheral connection (the listener parks forever after the first accept — point-to-point), and the force-`Send + Sync` `OneShotCell` for builders holding a moved-in value. `EmbassySessionClient::new` defaults to `reconnect: false` (unlike `ClientConfig::default`): a one-shot dialer can't redial, so the engine would otherwise spin on `TransportError::Io` forever; a re-dialable transport opts back in via `with_config`.
- **Framed connection** (feature `connector-io`) — `EmbassyConnection<Rd, Wr, F>` over `embedded-io-async` `Read`/`Write` halves with a pluggable `Framer` (COBS, length-prefix, …), chunking writes for atomic-or-error HAL `BufferedUart`s and skip-and-resync on undecodable runs.
- **Data-plane bridges** — `EmbassySinkRaw` / `EmbassySourceRaw` (the `!Send` duals of core's `Connector` / `Source`) with the force-`Send` `EmbassySink` / `EmbassySource` wrappers so an Embassy connector rides core's `pump_sink` / `pump_source` unchanged, plus `into_box_future` for long-lived `!Send` protocol tasks.
- Every `unsafe impl` cites one module-level invariant (single-core, cooperative Embassy executor — no preemption, no thread migration); the module is gated to `no_std` builds so the force-`Send` types can't leak into std/Tokio code. Host smoke tests (`tests/connectors_smoke.rs`) cover the one-shot cell/listener semantics and `serve` over the one-shot listener.
- **Wall-clock anchor — `EmbassyAdapter::set_unix_time(now_unix_secs)` + `TimeOps::unix_time()` (Issue #120).** Embassy has no real-time clock, so `unix_time()` returns `None` until the device learns the real time (NTP / GPS / host handshake) and calls `set_unix_time` once (Unix **seconds**); thereafter it derives absolute `(secs, nanos)` from Embassy's monotonic uptime plus the anchor. The anchor is a process-global `u32` (Unix seconds at boot) — a natively-atomic word on Cortex-M, no `portable-atomic` / critical-section needed — with sub-second precision taken from uptime. Gated on `embassy-time`.
- **`SendFutureWrapper` — shared force-`Send` wrapper for Embassy data-plane connectors (Issue #39).** New `pub` type (`no_std` only): asserts `Send` for a future driven exclusively by a single-core, cooperative Embassy executor, so an Embassy connector's `!Send` data-plane futures (over `NoopRawMutex` channels) satisfy the connector spine's `Send` `BoxFuture` bound. Consolidates the identical wrappers that the KNX and MQTT Embassy clients previously each hand-rolled.
- **Session-engine smoke test on the Embassy clock (Issue #39, Phase 5, [design doc](../docs/design/remote-access-via-connectors.md)).** New `tests/session_smoke.rs` drives `aimdb-core`'s runtime-neutral `run_client` engine using the `EmbassyAdapter`'s `TimeOps` clock for reconnect backoff / keepalive — proving the shared session engines run on Embassy, not just Tokio. Dev-only: pulls in `aimdb-core` with the `connector-session` feature, so the normal `no_std` lib build and the `thumbv7em` cross-checks stay `alloc`-only.
Expand Down
6 changes: 6 additions & 0 deletions aimdb-embassy-adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ alloc = []
embassy-runtime = ["embassy-executor", "embassy-time", "embassy-sync"]
embassy-net-support = ["embassy-net"] # Network stack support for connectors

connectors = ["aimdb-core/connector-session"]
connector-io = ["connectors", "dep:embedded-io-async"]

# Observability features (no_std compatible)
tracing = ["aimdb-core/tracing", "dep:tracing"]

Expand Down Expand Up @@ -51,6 +54,9 @@ aimdb-core = { version = "1.1.0", path = "../aimdb-core", default-features = fal
# Stream trait for bidirectional connectors (minimal, no_std compatible)
futures-core = { version = "0.3", default-features = false }

# Generic framed `Connection` over async UART halves (feature `connector-io`).
embedded-io-async = { workspace = true, optional = true }

# Embassy ecosystem for embedded async
embassy-executor = { workspace = true, optional = true }
embassy-time = { workspace = true, optional = true }
Expand Down
Loading