F1: EptStateMachine (E116 Equipment Performance Tracking)
Adds the six E116-0712 §6.2 buckets for classifying equipment time: NonScheduledTime (0) not scheduled to operate ScheduledDowntime (1) planned maintenance window UnscheduledDowntime (2) faults / unplanned stoppage Engineering (3) engineering / qualification time Standby (4) idle but available Productive (5) actively producing Wire-byte values pinned via static_assert to E116 §10.3. The FSM is a classifier rather than a strict lifecycle — every (state, event) pair is legal — but it remains data-driven through the shared CarrierTransitionTable template so the default cross-product is expressible declaratively. The state-change handler also surfaces dwell time (how long the previous state was held) computed off std::chrono::steady_clock, so accounting code can compute MTBF / availability / utilization from a single source without maintaining a parallel timestamp log. 4 test cases cover the initial state, every event firing, dwell-time reporting, and the no-op same-state event (no handler call). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "secsgem/gem/carrier_state.hpp" // shared CarrierTransition template
|
||||
|
||||
// E116 §6 Equipment Performance Tracking — top-level equipment state
|
||||
// model that classifies operating time into productive / non-productive
|
||||
// categories. Each transition is timestamped so the application can
|
||||
// compute MTBF / availability / utilization from the same source.
|
||||
//
|
||||
// The states correspond directly to the SEMI-defined buckets in E116-
|
||||
// 0712 §6.2; the wire byte values match the order the spec presents.
|
||||
namespace secsgem::gem {
|
||||
|
||||
enum class EptState : uint8_t {
|
||||
NonScheduledTime = 0, // equipment not scheduled to operate
|
||||
ScheduledDowntime = 1, // scheduled maintenance
|
||||
UnscheduledDowntime = 2, // unscheduled downtime (faults)
|
||||
Engineering = 3, // engineering / qualification time
|
||||
Standby = 4, // idle, available but not producing
|
||||
Productive = 5, // actively producing
|
||||
NoState = 255,
|
||||
};
|
||||
|
||||
const char* ept_state_name(EptState s);
|
||||
std::optional<EptState> parse_ept_state(const std::string& s);
|
||||
|
||||
// Events correspond to transitions the equipment can declare; events
|
||||
// are intentionally state-neutral (any -> X) since EPT is a "what kind
|
||||
// of time is this" classifier rather than a strict lifecycle.
|
||||
enum class EptEvent {
|
||||
EnterNonScheduled,
|
||||
EnterScheduledDown,
|
||||
EnterUnscheduledDown,
|
||||
EnterEngineering,
|
||||
EnterStandby,
|
||||
EnterProductive,
|
||||
};
|
||||
|
||||
const char* ept_event_name(EptEvent e);
|
||||
|
||||
using EptTable = CarrierTransitionTable<EptState, EptEvent>;
|
||||
EptTable default_ept_table();
|
||||
|
||||
class EptStateMachine {
|
||||
public:
|
||||
using StateChangeHandler =
|
||||
std::function<void(EptState from, EptState to, EptEvent,
|
||||
std::chrono::milliseconds dwell_before_change)>;
|
||||
|
||||
EptStateMachine();
|
||||
|
||||
EptState state() const { return state_; }
|
||||
void set_state_change_handler(StateChangeHandler h) { on_change_ = std::move(h); }
|
||||
|
||||
// Fire a transition. Returns true if a row matched. The dwell time
|
||||
// (how long we sat in the previous state) is computed off the
|
||||
// monotonic clock and surfaced to the handler so accounting code
|
||||
// doesn't need a parallel timestamp tracker.
|
||||
bool on_event(EptEvent e);
|
||||
|
||||
private:
|
||||
EptTable table_;
|
||||
EptState state_ = EptState::NonScheduledTime;
|
||||
std::chrono::steady_clock::time_point entered_;
|
||||
StateChangeHandler on_change_;
|
||||
};
|
||||
|
||||
} // namespace secsgem::gem
|
||||
Reference in New Issue
Block a user