C1: ExceptionStateMachine FSM + ExceptionStore

Per-EXID exception lifecycle for E5 §9.  States mirror the wire flow:

  Posted          equipment sent S5F9, awaiting host or autonomous clear
  Recovering      host's S5F13 accepted; equipment running recovery
  RecoverFailed   S5F15 reported a failed result; host may retry
  Cleared         terminal — store removes the row

Events:
  Created          synthetic NoState->Posted observer signal
  Recover          host's S5F13 (Posted/RecoverFailed -> Recovering)
  RecoveryComplete equipment internal (Recovering -> Cleared)
  RecoveryFailed   equipment internal (Recovering -> RecoverFailed)
  RecoveryAbort    host's S5F17 (Recovering -> Posted)
  Clear            equipment internal (Posted/RecoverFailed -> Cleared)

ExceptionStore mirrors ProcessJobStore: per-EXID FSMs heap-allocated via
unique_ptr, non-movable to keep `this`-captures safe, synthetic Created
fires after the row lands so observers can decide whether to emit S5F9
out of band.  on_recover validates EXRECVRA against the candidates the
post advertised.

The store joins EquipmentDataModel alongside process_jobs / control_jobs.
S5F9-F18 server-side dispatch lands in C2.

Tests (12 cases) cover FSM transitions including retry, abort, and
autonomous clear, plus store-level duplicate-rejection, EXRECVRA
validation, and Cleared-removes-the-row semantics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 23:41:51 +02:00
parent 4fec4297e2
commit a1f7da4a7d
6 changed files with 465 additions and 0 deletions
+2
View File
@@ -53,6 +53,7 @@ add_library(secsgem
src/gem/communication_state.cpp
src/gem/process_job_state.cpp
src/gem/control_job_state.cpp
src/gem/exception_state.cpp
src/gem/host_handler.cpp
src/config/loader.cpp
src/endpoint.cpp
@@ -96,6 +97,7 @@ add_executable(secsgem_tests
tests/test_loader.cpp
tests/test_process_jobs.cpp
tests/test_control_jobs.cpp
tests/test_exceptions.cpp
)
target_link_libraries(secsgem_tests PRIVATE secsgem doctest::doctest)
target_compile_definitions(secsgem_tests PRIVATE