#pragma once #include #include #include #include #include #include #include #include "secsgem/secs2/item.hpp" namespace secsgem::gem { namespace s2 = secsgem::secs2; enum class HostCmdAck : uint8_t { Accept = 0, InvalidCommand = 1, CannotDoNow = 2, ParameterInvalid = 3, AcceptedWillFinishLater = 4, Rejected = 5, InvalidObject = 6, }; // One entry on S2F41. The messages catalog declares this // struct external_struct: true so the codegen references it rather than // redefining it. struct CommandParameter { std::string name; s2::Item value; }; class HostCommandRegistry { public: // Declarative effect, loaded from YAML. struct Spec { HostCmdAck ack = HostCmdAck::Accept; std::optional emit_ceid; std::optional set_alarm; std::optional force_spool; // toggles the SpoolStore's flag }; struct Result { HostCmdAck ack = HostCmdAck::InvalidCommand; std::optional emit_ceid; std::optional set_alarm; std::optional force_spool; }; // Behaviour hook: vendor code that actually *does* something when the host // sends a command (run a recipe, move hardware, validate the parameters). // The handler receives the live parameter list and returns // the HCACK to send back; returning anything other than Accept suppresses // the Spec's declarative side effects, since the router only applies // emit_ceid / set_alarm / force_spool on Accept. Without a handler a // command stays purely declarative (the YAML Spec's static ack), which is // the pre-existing behaviour. using Handler = std::function& params)>; void register_command(std::string rcmd, Spec spec) { by_rcmd_.insert_or_assign(std::move(rcmd), std::move(spec)); } // Attach behaviour to an already-registered command. Independent of // register_command so the data dictionary (YAML) and the behaviour // (application code) can be wired separately. void set_handler(std::string rcmd, Handler h) { handlers_.insert_or_assign(std::move(rcmd), std::move(h)); } bool has(const std::string& rcmd) const { return by_rcmd_.count(rcmd) > 0; } bool has_handler(const std::string& rcmd) const { return handlers_.count(rcmd) > 0; } Result dispatch(const std::string& rcmd, const std::vector& params) const { auto it = by_rcmd_.find(rcmd); if (it == by_rcmd_.end()) return {HostCmdAck::InvalidCommand, std::nullopt, std::nullopt, std::nullopt}; HostCmdAck ack = it->second.ack; if (auto hit = handlers_.find(rcmd); hit != handlers_.end() && hit->second) ack = hit->second(rcmd, params); // vendor behaviour decides the outcome return {ack, it->second.emit_ceid, it->second.set_alarm, it->second.force_spool}; } private: std::map by_rcmd_; std::map handlers_; }; } // namespace secsgem::gem