feat(daemon): GetVariables + read_sync — the standard mutable-read pattern
EquipmentRuntime::read_sync establishes THE pattern for reading mutable engine state from gRPC/binding threads (Phase 0 item 6): post the read onto the io thread (the model's single owner), wait on a future with a deadline, nullopt => UNAVAILABLE at the RPC edge. Always truthful, no cache to invalidate; milliseconds are irrelevant at SECS rates. GetVariables: name resolution against the service snapshot (empty query = all; unknown name => INVALID_ARGUMENT naming the offender), values read via read_sync, converted by the new from_item reverse conversion (single-element numeric arrays => scalars, multi-element => List; Boolean/Binary/text per format; C2-as-integer and U8>2^63 wrap documented as TODOs). Tests run the engine in run_async — the daemon's PRODUCTION threading mode, previously untested — and round-trip through both conversions: SetVariables (declared-format write) then GetVariables (read) over a real in-process channel. Daemon suite 41 -> 61 assertions. daemon_interop.py gains a live GetVariables round-trip check vs the running daemon (verified green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -113,9 +113,9 @@ debts tax every later phase, and the most valuable tests aren't automated.
|
||||
(SVIDs 1/2 `refresh()`, CEIDs 400/401) with YAML role bindings
|
||||
(`control_state_svid:`, `cj_executing_ceid:` …). Gradual; aligns with the
|
||||
capability structure GEM itself defines (S1F19) and enables vendor subsetting.
|
||||
6. ⬜ **Standardize the mutable-read pattern** for daemon RPCs: post-to-io +
|
||||
future with deadline (always truthful; latency irrelevant at SECS rates).
|
||||
First consumer: `GetVariables` (Phase A1) — set the precedent there.
|
||||
6. ✅ **Standardize the mutable-read pattern** — `EquipmentRuntime::read_sync`
|
||||
(post-to-io + future with deadline; nullopt => UNAVAILABLE at the RPC edge).
|
||||
Precedent set by `GetVariables`; every future mutable read copies it.
|
||||
7. ⬜ Move `apps/equipment_service.hpp` into the library tree
|
||||
(`include/secsgem/daemon/`) once Phase B grows it; add a TSan-built
|
||||
`run_async` + concurrent-RPC daemon test (today's daemon tests only poll()).
|
||||
@@ -126,9 +126,12 @@ debts tax every later phase, and the most valuable tests aren't automated.
|
||||
RPC edge (was silently writing ASCII "").
|
||||
|
||||
### Phase A — finish the universal daemon surface (small, unblock vendors)
|
||||
1. ⬜ `GetVariables` — needs the reverse `Item → proto Value` conversion
|
||||
(read via post-to-io + future, or serve from a daemon-side cache of last
|
||||
set values; decide and document).
|
||||
1. ✅ `GetVariables` — `from_item` reverse conversion (scalar for 1-element
|
||||
arrays, List otherwise; C2-as-text and U8>2^63 noted as TODOs) + reads via
|
||||
`read_sync`. Tested under **run_async (production threading)** — write
|
||||
through the API, read back through the API — plus empty-query-returns-all,
|
||||
INVALID_ARGUMENT on unknown names, and a live round-trip check in
|
||||
`daemon_interop.py`.
|
||||
2. ⬜ Alarm `name:` config field + `SetAlarm`/`ClearAlarm` RPCs + tests.
|
||||
3. ⬜ `RequestControlState` (operator online/offline) + control-state atomic
|
||||
mirror (fixes the known race) + `WatchHealth` stream (link state from the
|
||||
|
||||
Reference in New Issue
Block a user