// 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 #include #include #include #include #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(std::stoi(hsms_port)); cfg.log = [](const std::string& m) { std::cout << "[gemd] " << m << std::endl; }; std::unique_ptr rt; try { rt = std::make_unique(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 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; }