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:
@@ -146,3 +146,65 @@ TEST_CASE("Equipment gRPC service over an in-process channel") {
|
||||
|
||||
server->Shutdown();
|
||||
}
|
||||
|
||||
// GetVariables needs a live io thread (read_sync posts onto it), so this case
|
||||
// runs the engine in run_async() — the daemon's PRODUCTION threading mode —
|
||||
// with real concurrency between the gRPC handler thread and the io thread.
|
||||
TEST_CASE("GetVariables round-trip under run_async (production threading mode)") {
|
||||
gem::EquipmentRuntime rt(test_config());
|
||||
dmn::EquipmentService svc(rt); // snapshot BEFORE the io thread starts
|
||||
rt.run_async();
|
||||
|
||||
grpc::ServerBuilder builder;
|
||||
builder.RegisterService(&svc);
|
||||
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
|
||||
REQUIRE(server);
|
||||
auto stub = pb::Equipment::NewStub(server->InProcessChannel(grpc::ChannelArguments{}));
|
||||
|
||||
// Write through the API, then read back through the API: exercises BOTH
|
||||
// conversions (Value->Item with declared formats, Item->Value) end to end.
|
||||
{
|
||||
grpc::ClientContext ctx;
|
||||
pb::VariableUpdate req;
|
||||
pb::Ack resp;
|
||||
(*req.mutable_values())["ChamberPressure"].set_real(2.5);
|
||||
(*req.mutable_values())["WaferCounter"].set_integer(7);
|
||||
REQUIRE(stub->SetVariables(&ctx, req, &resp).ok());
|
||||
REQUIRE(resp.code() == pb::Ack::ACCEPT);
|
||||
}
|
||||
{
|
||||
grpc::ClientContext ctx;
|
||||
pb::VariableQuery req;
|
||||
pb::VariableSnapshot resp;
|
||||
req.add_names("ChamberPressure");
|
||||
req.add_names("WaferCounter");
|
||||
auto st = stub->GetVariables(&ctx, req, &resp);
|
||||
REQUIRE(st.ok());
|
||||
REQUIRE(resp.values().count("ChamberPressure") == 1);
|
||||
REQUIRE(resp.values().count("WaferCounter") == 1);
|
||||
CHECK(resp.values().at("ChamberPressure").real() == doctest::Approx(2.5));
|
||||
CHECK(resp.values().at("WaferCounter").integer() == 7);
|
||||
}
|
||||
|
||||
SUBCASE("empty query returns every configured variable") {
|
||||
grpc::ClientContext ctx;
|
||||
pb::VariableQuery req;
|
||||
pb::VariableSnapshot resp;
|
||||
REQUIRE(stub->GetVariables(&ctx, req, &resp).ok());
|
||||
const auto expected = rt.model().svids.size() + rt.model().dvids.all().size();
|
||||
CHECK(resp.values().size() == expected);
|
||||
}
|
||||
|
||||
SUBCASE("unknown name is INVALID_ARGUMENT, naming the offender") {
|
||||
grpc::ClientContext ctx;
|
||||
pb::VariableQuery req;
|
||||
pb::VariableSnapshot resp;
|
||||
req.add_names("definitely_not_a_var");
|
||||
auto st = stub->GetVariables(&ctx, req, &resp);
|
||||
CHECK(st.error_code() == grpc::StatusCode::INVALID_ARGUMENT);
|
||||
CHECK(st.error_message().find("definitely_not_a_var") != std::string::npos);
|
||||
}
|
||||
|
||||
server->Shutdown();
|
||||
rt.stop();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user