90c177b7ce
GEM300 layer: SEMI E40-0705 Process Job and E94-0705 Control Job state machines, plus the E30 §6.1 communication-state machine that sits between HSMS SELECT and full GEM communication. Data-driven via data/process_job_state.yaml and data/control_job_state.yaml, mirroring the existing control_state.yaml pattern. Wire coverage: S14F9/F10 CreateObject (CJ) host -> equipment S14F11/F12 DeleteObject (CJ) host -> equipment S16F5/F6 PRJobCommand host -> equipment S16F9 PRJobAlert equipment -> host S16F11/F12 PRJobCreate (simplified body) host -> equipment S16F13/F14 PRJobDequeue host -> equipment S16F27/F28 CJobCommand host -> equipment Process Job FSM exposes 8 states matching PRJOBSTATE bytes (E40 §10.3.2); HOQ is reorder-aware (move-to-head against an insertion-order vector); Stop/Abort on a Queued PJ routes through ABORTING so the host observes PRJOBSTATE=7 on the wire (§6.3); alert_enabled is settable per-PJ for PRALERT control; FSM dispatches through ProcessJobStore::on_change_ dynamically so a late set_state_change_handler() reaches existing PJs. Hardening: loader rejects NoState (sentinel) as initial/from/to and rejects `on: created` rows; static_asserts pin enum values to wire bytes; ProcessJobStore is non-movable to keep the per-PJ this-capture safe. Server simulator cascades the full CJ -> PJ lifecycle on CJSTART so the wire trace exercises every legal state. CEIDs 400/401 fire on CJ state changes via the existing event-report pipeline. Tests: 60+ new assertions across test_process_jobs, test_control_jobs, test_communication_state, test_hsms_connection, plus loader and messages round-trip coverage. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
112 lines
3.6 KiB
C++
112 lines
3.6 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "secsgem/gem/store/host_commands.hpp" // HostCmdAck
|
|
|
|
namespace secsgem::gem {
|
|
|
|
// E94 §6 Control Job state model.
|
|
//
|
|
// E94 governs the *batch* of work — a CJ owns an ordered list of PRJOBIDs
|
|
// (process jobs) and a processing policy. States below match E94-0705 §6
|
|
// (the values are this project's own opaque encoding — E94 does not pin a
|
|
// PRJOBSTATE-style wire enum for CJ; we surface state via S6F11 CEIDs in
|
|
// the demo).
|
|
enum class ControlJobState : uint8_t {
|
|
Queued = 0,
|
|
Selected = 1,
|
|
WaitingForStart = 2,
|
|
Executing = 3,
|
|
Paused = 4,
|
|
Completed = 5, // terminal: all PJs done, awaiting deletion
|
|
Stopping = 6,
|
|
Aborting = 7,
|
|
NoState = 255,
|
|
};
|
|
|
|
const char* control_job_state_name(ControlJobState s);
|
|
std::optional<ControlJobState> parse_control_job_state(const std::string& s);
|
|
|
|
enum class ControlJobEvent {
|
|
Created, // -> Queued
|
|
Select, // Queued -> Selected
|
|
SetupComplete, // Selected -> WaitingForStart
|
|
Start, // host: CJSTART -> Executing
|
|
Pause, // host: CJPAUSE -> Paused
|
|
Resume, // host: CJRESUME -> Executing
|
|
Stop, // host: CJSTOP -> Stopping
|
|
Abort, // host: CJABORT -> Aborting
|
|
AllJobsComplete, // internal: Executing/Stopping -> Completed
|
|
AbortComplete, // internal: Aborting -> Completed
|
|
};
|
|
|
|
const char* control_job_event_name(ControlJobEvent e);
|
|
std::optional<ControlJobEvent> parse_control_job_event(const std::string& s);
|
|
|
|
// Wire-name -> event mapping for the CTLJOBCMD string on S16F27.
|
|
std::optional<ControlJobEvent> ctl_cmd_to_event(const std::string& cmd);
|
|
|
|
struct ControlJobTransition {
|
|
ControlJobState from;
|
|
ControlJobEvent on;
|
|
std::optional<ControlJobState> to;
|
|
std::optional<uint8_t> ack_code; // HostCmdAck when applicable
|
|
};
|
|
|
|
class ControlJobTransitionTable {
|
|
public:
|
|
void add(ControlJobTransition row);
|
|
const ControlJobTransition* find(ControlJobState from,
|
|
ControlJobEvent on) const;
|
|
std::size_t size() const { return rows_.size(); }
|
|
const std::vector<ControlJobTransition>& rows() const { return rows_; }
|
|
|
|
static ControlJobTransitionTable default_table();
|
|
|
|
private:
|
|
std::vector<ControlJobTransition> rows_;
|
|
};
|
|
|
|
class ControlJobStateMachine {
|
|
public:
|
|
using StateChangeHandler =
|
|
std::function<void(ControlJobState from, ControlJobState to,
|
|
ControlJobEvent trigger)>;
|
|
|
|
ControlJobStateMachine();
|
|
explicit ControlJobStateMachine(ControlJobTransitionTable table,
|
|
ControlJobState initial = ControlJobState::Queued);
|
|
|
|
ControlJobState state() const { return state_; }
|
|
void set_state_change_handler(StateChangeHandler h) { on_change_ = std::move(h); }
|
|
|
|
HostCmdAck on_host_command(ControlJobEvent event);
|
|
bool on_internal(ControlJobEvent event);
|
|
|
|
private:
|
|
const ControlJobTransition* fire(ControlJobEvent on);
|
|
void transition(ControlJobState next, ControlJobEvent trigger);
|
|
|
|
ControlJobTransitionTable table_;
|
|
ControlJobState state_;
|
|
StateChangeHandler on_change_;
|
|
};
|
|
|
|
// S14F10 / F12 OBJACK — generic ObjectService ack. Used by the
|
|
// CreateControlJob / DeleteControlJob handlers.
|
|
enum class ObjectAck : uint8_t {
|
|
Success = 0,
|
|
Error = 1,
|
|
Denied_UnknownObject = 2,
|
|
Denied_AlreadyExists = 3,
|
|
Denied_InvalidAttribute = 4,
|
|
Denied_BadState = 5,
|
|
};
|
|
|
|
} // namespace secsgem::gem
|