cf230d4119
Item 8a — ConfigValidator warns on non-identifier variable/event/alarm/ command names ([A-Za-z_][A-Za-z0-9_]*): language bindings expose names as kwargs/attributes, so 'Chamber Pressure' would be unusable in the planned Python client. Warning not error — the wire doesn't care. Tested (4 warning sites + good-name negative). Item 4 tail — golden frames for S5F1 (Binary ALCD / U4 ALID / ASCII ALTX) and a composed S6F11 (the production-critical report shape), bytes hand- computed from E5 encoding rules: external pins on message composition. Item 7 — equipment_service.hpp moved to include/secsgem/daemon/ (apps/ include-path hack removed) and a TSan daemon lane added locally + in CI. tools/tsan.supp suppresses races whose accesses sit entirely inside the UNinstrumented system libgrpc/libgpr/libabsl (epoll wakeups, absl Mutex GraphCycles bookkeeping); our frames stay fully checked. The lane earned its keep on first run: it caught a REAL threading-contract violation — a daemon test reading model stores from the test thread while the io thread serviced posted writes — fixed to use read_sync, exactly per the documented contract. Now TSan-clean under halt_on_error=1 in the full production threading shape. Suites: core 470/3068, daemon Release+TSan 125/125 each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78 lines
3.0 KiB
C++
78 lines
3.0 KiB
C++
// secs_gemd — the SECS/GEM daemon.
|
|
//
|
|
// Runs the GEM engine (EquipmentRuntime + the default handlers) on a background
|
|
// thread, owning the HSMS link to the host, and exposes a small name-based gRPC
|
|
// API (proto/secsgem/v1/equipment.proto) so a tool's software — in any language
|
|
// — can drive the equipment without linking C++ or knowing SEMI.
|
|
//
|
|
// Increment 1: the universal "report state to the host" RPCs (SetVariables,
|
|
// FireEvent) plus control-state visibility. Alarms, GetVariables, and the
|
|
// host->tool Subscribe command stream follow (see docs/DAEMON_ROADMAP.md).
|
|
|
|
#include <grpcpp/grpcpp.h>
|
|
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "secsgem/daemon/equipment_service.hpp"
|
|
#include "secsgem/gem/default_handlers.hpp"
|
|
#include "secsgem/gem/runtime.hpp"
|
|
|
|
namespace gem = secsgem::gem;
|
|
|
|
namespace {
|
|
std::string arg(int argc, char** argv, const std::string& key, const std::string& def) {
|
|
for (int i = 1; i + 1 < argc; ++i)
|
|
if (key == argv[i]) return argv[i + 1];
|
|
return def;
|
|
}
|
|
} // namespace
|
|
|
|
int main(int argc, char** argv) {
|
|
const std::string hsms_port = arg(argc, argv, "--port", "5000");
|
|
const std::string grpc_addr = arg(argc, argv, "--grpc", "0.0.0.0:50051");
|
|
const std::string cfgdir = arg(argc, argv, "--config-dir", "data");
|
|
|
|
gem::EquipmentRuntime::Config cfg;
|
|
cfg.equipment_yaml = cfgdir + "/equipment.yaml";
|
|
cfg.control_state_yaml = cfgdir + "/control_state.yaml";
|
|
cfg.process_job_yaml = cfgdir + "/process_job_state.yaml";
|
|
cfg.control_job_yaml = cfgdir + "/control_job_state.yaml";
|
|
cfg.port = static_cast<uint16_t>(std::stoi(hsms_port));
|
|
cfg.log = [](const std::string& m) { std::cout << "[gemd] " << m << std::endl; };
|
|
|
|
std::unique_ptr<gem::EquipmentRuntime> rt;
|
|
try {
|
|
rt = std::make_unique<gem::EquipmentRuntime>(cfg);
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "[gemd] config error: " << e.what() << std::endl;
|
|
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
|
|
|
|
grpc::ServerBuilder builder;
|
|
builder.AddListeningPort(grpc_addr, grpc::InsecureServerCredentials());
|
|
builder.RegisterService(&service);
|
|
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
|
|
if (!server) {
|
|
std::cerr << "[gemd] failed to start gRPC server on " << grpc_addr << std::endl;
|
|
return 1;
|
|
}
|
|
std::cout << "[gemd] gRPC on " << grpc_addr
|
|
<< "; HSMS equipment on :" << hsms_port << std::endl;
|
|
// TODO(daemon): graceful shutdown — handle SIGTERM/SIGINT by calling
|
|
// server->Shutdown() and rt->stop() so in-flight RPCs drain and the spool
|
|
// journal flushes, instead of dying mid-write when supervised (systemd/
|
|
// docker stop). See DAEMON_ROADMAP Phase E.
|
|
server->Wait();
|
|
return 0;
|
|
}
|