fix(daemon): honour declared SECS-II formats + make service thread-safe

Audit fixes for two real bugs in the gRPC service:

1. Format compliance: to_item() wrote F8/I8 regardless of the variable's
   declared wire format, so values contradicted the S1F11/S1F21 namelists
   (ChamberPressure is F4, WaferCounter U4; the interop trace showed <F8 2.5>
   on the wire). Conversion now targets the declared format — verified
   end-to-end: secsgem-py now receives <F4 2.5> in S6F11.

2. Thread safety: gRPC handler threads called resolve_variable/resolve_event,
   copying live store entries (including Item values) while the io thread
   mutates them. The service now snapshots the immutable name->id/format maps
   at construction (before run_async, per the documented ordering); all writes
   already post to the io thread. Remaining known narrow race (GetControlState
   enum read) documented in DAEMON_ROADMAP.

Also: drop a stale tools/run_interop.sh reference from docker-compose.yml.
Tests: daemon in-process 16/16 (new F4/U4 format assertions), core 459/459,
secsgem-py interop green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 18:35:53 +02:00
parent 92afbd2a37
commit 99bfa794fc
4 changed files with 85 additions and 24 deletions
+5 -1
View File
@@ -51,9 +51,13 @@ int main(int argc, char** argv) {
return 1;
}
gem::register_default_handlers(*rt);
// Construct the service before starting the io thread: its constructor
// snapshots the name->id/format maps from the model (see the service's
// threading note).
secsgem::daemon::EquipmentService service(*rt);
rt->run_async(); // engine + HSMS link on a background thread
secsgem::daemon::EquipmentService service(*rt);
grpc::ServerBuilder builder;
builder.AddListeningPort(grpc_addr, grpc::InsecureServerCredentials());
builder.RegisterService(&service);