STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
sequencer::SequencerService Class Reference

Top-level sequencer service. More...

#include <SequencerService.hpp>

Collaboration diagram for sequencer::SequencerService:
[legend]

Classes

struct  Command
struct  HoldRule
 One state's timed hold: how long, where it lands, and whether a client may set the length. More...

Public Member Functions

 SequencerService ()=default
 ~SequencerService ()
bool init (const std::string &config_path="config/config.toml")
 Load config, CSVs, connect to Elodin.
bool transitionTo (const std::string &state_name)
 Request a state transition by name (e.g.
bool transitionTo (const std::string &state_name, uint32_t requested_hold_ms, std::string *refusal_reason=nullptr)
bool transitionTo (State to)
 Resolve-free overload.
bool transitionTo (State to, uint32_t requested_hold_ms, std::string *refusal_reason=nullptr)
 Transition, optionally overriding the target state's hold with a client-supplied window.
bool setDebugMode (bool enabled)
 Enable or disable debug mode.
bool manualActuator (const std::string &name, int pos, std::string *refusal_reason=nullptr)
 Debug mode only: manual OPEN/CLOSE for one role.
bool extendFire ()
 Extend the FIRE window (only valid while in FIRE state).
std::string scriptStatusReport () const
 What this process made of its dynamic states at startup, as newline-terminated text.
State currentState () const
bool isDebugMode () const

Private Member Functions

void commandLoop ()
bool enqueueAndWait (std::function< bool()> fn)
 Post work and block until the worker has run it.
void enqueueDetached (std::function< bool()> fn)
 Post work and return immediately.
bool doTransitionTo (State to, uint32_t requested_hold_ms=0, std::string *refusal_reason=nullptr)
bool doSetDebugMode (bool enabled)
bool doManualActuator (const std::string &name, int pos, std::string *refusal_reason=nullptr)
bool doExtendFire ()
ScriptEnv makeScriptEnv (const DynamicState &ds)
 Build the callbacks a script is allowed to reach the world through.
std::string configDir () const
 Directory containing config_path_, or "." — where scripts/ is resolved against.
void publishState ()
void publishStateTransition (State from, State to)
bool tryConnectElodin ()
 Connect to Elodin and do everything that depends on the connection: register the ACT_CMD VTables, hand the client to the commander, publish initial state.
void startElodinRetry ()
void stopElodinRetry ()
bool loadConfig (const std::string &path)
bool applyConfig (const fsw::config::Config &cfg)
 Everything a config load has to apply, in the one order that works.
void notifyControllerFire (bool active)
 Send FIRE_START / FIRE_STOP to controller_service.
bool notifyControllerFireOnce (bool active)
 One attempt: connect, send, wait for the controller's "OK".

Static Private Member Functions

static bool isAbortState (State s)

Private Attributes

std::deque< Command > cmd_queue_
std::mutex cmd_mutex_
std::condition_variable cmd_cv_
std::thread cmd_thread_
std::atomic< bool > cmd_stop_ {false}
StateMachine state_machine_
ActuatorCommander actuator_commander_
AbortBroadcaster abort_broadcaster_
HoldTimer hold_timer_
ScriptRunner script_runner_
PressureFeed pressure_feed_
 Live calibrated pressures for pressure(), on its own Elodin connection.
fsw::elodin::ElodinClient elodin_
std::atomic< State > current_state_ {State::IDLE}
std::atomic< bool > debug_mode_ {false}
std::map< State, HoldRule > hold_rules_
 Holdable states, by target.
std::map< State, DynamicState > dynamic_states_
 Dynamic states whose scripts loaded and validated.
std::map< State, std::string > refused_states_
 States that declare a script which did NOT hold up, with the reason.
std::mutex config_mutex_
std::string config_path_
std::string config_content_
std::string daq_bind_address_ {"0.0.0.0"}
 Local address board-facing UDP leaves from, resolved once in init().
std::vector< fsw::elodin::BoardChannels > actuator_boards_
std::vector< fsw::elodin::BoardChannels > ball_valve_boards_
std::string elodin_host_ = "127.0.0.1"
uint16_t elodin_port_ = 2240
std::thread elodin_retry_thread_
std::atomic< bool > elodin_retry_stop_ {false}
State fire_state_ {State::UNKNOWN}
State fire_expiry_state_ {State::UNKNOWN}
uint32_t fire_duration_ms_ {6000}
uint32_t fire_extended_ms_ {10000}
std::string controller_host_ {"127.0.0.1"}
uint16_t controller_port_ {8000}
bool sequencer_owns_valves_ {false}
 [controller_service].sequencer_owns_valves.

Static Private Attributes

static constexpr int kCommandTimeoutSeconds = 10
 How long a caller waits for its command to run before giving up and reporting failure.
static constexpr uint32_t kAbortDoneDelayMs = 3000
 Delay between ABORT and ABORT_DONE.
static constexpr int kElodinRetrySeconds = 5

Detailed Description

Top-level sequencer service.

Owns and wires together:

All external commands arrive via the TCP server in sequencer_main.cpp and are dispatched to the methods below.

── Threading contract ────────────────────────────────────────────────────── Every public command method is a thin wrapper that puts work on cmd_queue_ and waits for the single worker thread (commandLoop) to run it. Exactly one thread is ever inside the state machine. Callers may come from anywhere: the TCP command path, and the FireManager timer thread when a burn expires.

This replaced a design where nothing was serialized at all. Individual fields were atomic, but a transition is a seven-step sequence built out of them — stop the republish loop, apply actuators, restart the loop, assign the state, arm the burn — and making each step atomic does nothing for the sequence. Two threads interleaving it could move-assign onto a still-joinable std::thread, which is a std::terminate: the sequencer died outright if an operator aborted at the moment the fire timer expired.

A mutex was the obvious fix and does not work here. An operator thread holding it calls fire_manager_.stop(), which joins the timer thread — while the timer thread is inside its expiry callback waiting for that same mutex. A recursive_mutex does not help; the re-entry is cross-thread. A queue sidesteps it entirely: the timer thread never enters the state machine, it posts a request and returns immediately (see kDetached below).

Definition at line 69 of file SequencerService.hpp.

Constructor & Destructor Documentation

◆ SequencerService()

sequencer::SequencerService::SequencerService ( )
default

◆ ~SequencerService()

sequencer::SequencerService::~SequencerService ( )

Member Function Documentation

◆ applyConfig()

bool sequencer::SequencerService::applyConfig ( const fsw::config::Config & cfg)
private

◆ commandLoop()

void sequencer::SequencerService::commandLoop ( )
private

◆ configDir()

std::string sequencer::SequencerService::configDir ( ) const
private

Directory containing config_path_, or "." — where scripts/ is resolved against.

Definition at line 179 of file SequencerService.cpp.

References config_path_.

Referenced by applyConfig().

◆ currentState()

State sequencer::SequencerService::currentState ( ) const
inline

Definition at line 150 of file SequencerService.hpp.

References current_state_.

Referenced by attemptEntry(), main(), waitForState(), and waitForState().

◆ doExtendFire()

bool sequencer::SequencerService::doExtendFire ( )
private

Definition at line 987 of file SequencerService.cpp.

References current_state_, fire_state_, and hold_timer_.

Referenced by extendFire().

◆ doManualActuator()

bool sequencer::SequencerService::doManualActuator ( const std::string & name,
int pos,
std::string * refusal_reason = nullptr )
private

Definition at line 964 of file SequencerService.cpp.

References actuator_commander_, and debug_mode_.

Referenced by manualActuator().

◆ doSetDebugMode()

bool sequencer::SequencerService::doSetDebugMode ( bool enabled)
private

Definition at line 947 of file SequencerService.cpp.

References actuator_commander_, debug_mode_, and publishState().

Referenced by setDebugMode().

◆ doTransitionTo()

◆ enqueueAndWait()

bool sequencer::SequencerService::enqueueAndWait ( std::function< bool()> fn)
private

Post work and block until the worker has run it.

Returns false if the service is shutting down or the worker did not get to it within kCommandTimeoutSeconds.

Definition at line 109 of file SequencerService.cpp.

References cmd_cv_, cmd_mutex_, cmd_queue_, cmd_stop_, kCommandTimeoutSeconds, lk, and result.

Referenced by extendFire(), manualActuator(), setDebugMode(), transitionTo(), and transitionTo().

◆ enqueueDetached()

void sequencer::SequencerService::enqueueDetached ( std::function< bool()> fn)
private

Post work and return immediately.

Used by the fire-expiry callback: waiting there would reintroduce the join deadlock described above.

Definition at line 129 of file SequencerService.cpp.

References cmd_cv_, cmd_mutex_, cmd_queue_, cmd_stop_, and lk.

Referenced by doTransitionTo(), and makeScriptEnv().

◆ extendFire()

bool sequencer::SequencerService::extendFire ( )

Extend the FIRE window (only valid while in FIRE state).

Definition at line 981 of file SequencerService.cpp.

References doExtendFire(), and enqueueAndWait().

Referenced by anonymous_namespace{sequencer_main.cpp}::handleCommandLine().

◆ init()

bool sequencer::SequencerService::init ( const std::string & config_path = "config/config.toml")

◆ isAbortState()

bool sequencer::SequencerService::isAbortState ( State s)
staticprivate

Definition at line 626 of file SequencerService.cpp.

References sequencer::StateMachine::isAbort().

Referenced by doTransitionTo().

◆ isDebugMode()

bool sequencer::SequencerService::isDebugMode ( ) const
inline

Definition at line 153 of file SequencerService.hpp.

References debug_mode_.

◆ loadConfig()

bool sequencer::SequencerService::loadConfig ( const std::string & path)
private

Definition at line 149 of file SequencerService.cpp.

References config_content_, config_path_, and sequencer::readFile().

Referenced by init().

◆ makeScriptEnv()

◆ manualActuator()

bool sequencer::SequencerService::manualActuator ( const std::string & name,
int pos,
std::string * refusal_reason = nullptr )

Debug mode only: manual OPEN/CLOSE for one role.

Overrides persist until a state transition (which clears overrides and applies the new state's CSV).

Allowed in a dynamic state too, where it outranks the running script's position for that valve. The script cannot notice, and the two will fight over it — but the operator with their hand on the panel is the last line, and taking that away from them to protect a script is the wrong trade.

Parameters
refusal_reasonoptional; on a false return, set to a short phrase for a protocol reply. One validation, one wording — the TCP layer does not get its own copy of these rules to drift out of step with.

Definition at line 957 of file SequencerService.cpp.

References doManualActuator(), and enqueueAndWait().

Referenced by anonymous_namespace{sequencer_main.cpp}::handleCommandLine(), and main().

◆ notifyControllerFire()

void sequencer::SequencerService::notifyControllerFire ( bool active)
private

Send FIRE_START / FIRE_STOP to controller_service.

Tell controller_service the burn gate changed.

The single place anything tells the controller the burn gate changed. Retries once if the first attempt is not acknowledged.

One TCP connection per message, 1 s send timeout.

This lives here rather than in the hold timer so exactly one component talks to the controller — previously the timer opened its own socket AND the backend independently saw the FIRE edge and sent the same messages, so a safety-critical gate had two writers in two processes.

Definition at line 1005 of file SequencerService.cpp.

References active, controller_host_, controller_port_, and notifyControllerFireOnce().

Referenced by doTransitionTo().

◆ notifyControllerFireOnce()

bool sequencer::SequencerService::notifyControllerFireOnce ( bool active)
private

One attempt: connect, send, wait for the controller's "OK".

Returns whether it was acknowledged — a successful send() is not evidence of delivery, since the kernel buffers for an application that may never read.

Definition at line 1023 of file SequencerService.cpp.

References active, controller_host_, controller_port_, sequencer::kControllerConnectTimeoutMs, r, reply(), and send().

Referenced by notifyControllerFire().

◆ publishState()

◆ publishStateTransition()

void sequencer::SequencerService::publishStateTransition ( State from,
State to )
private

Definition at line 1245 of file SequencerService.cpp.

References elodin_, sequencer::now_ns(), and sequencer::VTABLE_STATE_TRANSITION.

Referenced by doTransitionTo().

◆ scriptStatusReport()

std::string sequencer::SequencerService::scriptStatusReport ( ) const

What this process made of its dynamic states at startup, as newline-terminated text.

One line per state that declares a script: SCRIPT:<id>:<name>:OK:<statements>:<timeout_ms> SCRIPT:<id>:<name>:REFUSED:<reason> terminated by "END\n".

Read-only and answerable from any thread — the answer was decided once, at config load, and is fixed for the life of the run. It exists so the panel can show a refused state as dead with a reason on hover rather than as a button that errors when pressed.

Definition at line 1206 of file SequencerService.cpp.

References config_mutex_, dynamic_states_, sequencer::PressureFeed::explain(), lk, sequencer::StateMachine::name(), sequencer::PressureFeed::Ok, pressure_feed_, and refused_states_.

Referenced by anonymous_namespace{sequencer_main.cpp}::handleCommandLine(), and main().

◆ setDebugMode()

bool sequencer::SequencerService::setDebugMode ( bool enabled)

Enable or disable debug mode.

Debug mode: all state transitions allowed, manual actuator commands accepted.

Definition at line 941 of file SequencerService.cpp.

References doSetDebugMode(), and enqueueAndWait().

Referenced by anonymous_namespace{sequencer_main.cpp}::handleCommandLine(), and main().

◆ startElodinRetry()

void sequencer::SequencerService::startElodinRetry ( )
private

◆ stopElodinRetry()

void sequencer::SequencerService::stopElodinRetry ( )
private

Definition at line 1289 of file SequencerService.cpp.

References elodin_retry_stop_, and elodin_retry_thread_.

Referenced by ~SequencerService().

◆ transitionTo() [1/4]

bool sequencer::SequencerService::transitionTo ( const std::string & state_name)

Request a state transition by name (e.g.

"Fuel Fill"). Validates via StateMachine unless debug mode is on. Applies actuators, fires abort/FIRE lifecycle handlers, publishes to Elodin.

Returns
true if transition was accepted and executed.

Definition at line 634 of file SequencerService.cpp.

References transitionTo().

Referenced by attemptEntry(), anonymous_namespace{sequencer_main.cpp}::handleCommandLine(), main(), transitionTo(), and transitionTo().

◆ transitionTo() [2/4]

bool sequencer::SequencerService::transitionTo ( const std::string & state_name,
uint32_t requested_hold_ms,
std::string * refusal_reason = nullptr )

◆ transitionTo() [3/4]

bool sequencer::SequencerService::transitionTo ( State to)

Resolve-free overload.

Internal callers use this so a state never round-trips through its own name, which is what let a rename refuse the fire timer's expiry transition.

Definition at line 655 of file SequencerService.cpp.

References transitionTo().

◆ transitionTo() [4/4]

bool sequencer::SequencerService::transitionTo ( State to,
uint32_t requested_hold_ms,
std::string * refusal_reason = nullptr )

Transition, optionally overriding the target state's hold with a client-supplied window.

requested_hold_ms of 0 means "none supplied" — the state's configured default is used, and this is the form every internal caller uses. A non-zero value is only honoured for a state whose hold rule is marked gui_settable, which the burn state never is; anything else is REFUSED WITHOUT TRANSITIONING. That last part is the point: a client that queues a command, disconnects and has it replayed on reconnect must end up where it started, not in a hold it cannot be talked out of.

Parameters
refusal_reasonoptional; on a false return, set to a short phrase suitable for a protocol reply. One validation, one wording — the TCP layer does not get its own copy of these rules to drift out of step with.

Definition at line 659 of file SequencerService.cpp.

References doTransitionTo(), and enqueueAndWait().

◆ tryConnectElodin()

bool sequencer::SequencerService::tryConnectElodin ( )
private

Connect to Elodin and do everything that depends on the connection: register the ACT_CMD VTables, hand the client to the commander, publish initial state.

Safe to call repeatedly — a reconnect must re-register, since the VTables live in the db process.

Definition at line 1253 of file SequencerService.cpp.

References actuator_boards_, actuator_commander_, ball_valve_boards_, elodin_, elodin_host_, elodin_port_, fsw::elodin::DatabaseConfig::register_ball_valve_cmd_tables(), and fsw::elodin::DatabaseConfig::register_non_sensor_tables().

Referenced by init(), and startElodinRetry().

Member Data Documentation

◆ abort_broadcaster_

AbortBroadcaster sequencer::SequencerService::abort_broadcaster_
private

Definition at line 225 of file SequencerService.hpp.

Referenced by applyConfig(), and doTransitionTo().

◆ actuator_boards_

std::vector<fsw::elodin::BoardChannels> sequencer::SequencerService::actuator_boards_
private

Definition at line 273 of file SequencerService.hpp.

Referenced by init(), and tryConnectElodin().

◆ actuator_commander_

ActuatorCommander sequencer::SequencerService::actuator_commander_
private

◆ ball_valve_boards_

std::vector<fsw::elodin::BoardChannels> sequencer::SequencerService::ball_valve_boards_
private

Definition at line 274 of file SequencerService.hpp.

Referenced by init(), and tryConnectElodin().

◆ cmd_cv_

std::condition_variable sequencer::SequencerService::cmd_cv_
private

◆ cmd_mutex_

std::mutex sequencer::SequencerService::cmd_mutex_
private

◆ cmd_queue_

std::deque<Command> sequencer::SequencerService::cmd_queue_
private

Definition at line 166 of file SequencerService.hpp.

Referenced by commandLoop(), enqueueAndWait(), and enqueueDetached().

◆ cmd_stop_

std::atomic<bool> sequencer::SequencerService::cmd_stop_ {false}
private

◆ cmd_thread_

std::thread sequencer::SequencerService::cmd_thread_
private

Definition at line 169 of file SequencerService.hpp.

Referenced by init(), and ~SequencerService().

◆ config_content_

std::string sequencer::SequencerService::config_content_
private

Definition at line 258 of file SequencerService.hpp.

Referenced by applyConfig(), init(), and loadConfig().

◆ config_mutex_

std::mutex sequencer::SequencerService::config_mutex_
mutableprivate

◆ config_path_

std::string sequencer::SequencerService::config_path_
private

Definition at line 257 of file SequencerService.hpp.

Referenced by configDir(), init(), and loadConfig().

◆ controller_host_

std::string sequencer::SequencerService::controller_host_ {"127.0.0.1"}
private

◆ controller_port_

uint16_t sequencer::SequencerService::controller_port_ {8000}
private

◆ current_state_

std::atomic<State> sequencer::SequencerService::current_state_ {State::IDLE}
private

Definition at line 235 of file SequencerService.hpp.

Referenced by currentState(), doExtendFire(), doTransitionTo(), init(), and publishState().

◆ daq_bind_address_

std::string sequencer::SequencerService::daq_bind_address_ {"0.0.0.0"}
private

Local address board-facing UDP leaves from, resolved once in init().

"0.0.0.0" means the host has no NIC on the board subnet (a dev box) and traffic stays unpinned.

Definition at line 265 of file SequencerService.hpp.

Referenced by applyConfig().

◆ debug_mode_

std::atomic<bool> sequencer::SequencerService::debug_mode_ {false}
private

◆ dynamic_states_

std::map<State, DynamicState> sequencer::SequencerService::dynamic_states_
private

Dynamic states whose scripts loaded and validated.

Guarded by config_mutex_.

Definition at line 244 of file SequencerService.hpp.

Referenced by applyConfig(), doTransitionTo(), publishState(), and scriptStatusReport().

◆ elodin_

fsw::elodin::ElodinClient sequencer::SequencerService::elodin_
private

◆ elodin_host_

std::string sequencer::SequencerService::elodin_host_ = "127.0.0.1"
private

Definition at line 280 of file SequencerService.hpp.

Referenced by applyConfig(), init(), and tryConnectElodin().

◆ elodin_port_

uint16_t sequencer::SequencerService::elodin_port_ = 2240
private

Definition at line 281 of file SequencerService.hpp.

Referenced by init(), and tryConnectElodin().

◆ elodin_retry_stop_

std::atomic<bool> sequencer::SequencerService::elodin_retry_stop_ {false}
private

Definition at line 283 of file SequencerService.hpp.

Referenced by startElodinRetry(), and stopElodinRetry().

◆ elodin_retry_thread_

std::thread sequencer::SequencerService::elodin_retry_thread_
private

Definition at line 282 of file SequencerService.hpp.

Referenced by startElodinRetry(), and stopElodinRetry().

◆ fire_duration_ms_

uint32_t sequencer::SequencerService::fire_duration_ms_ {6000}
private

Definition at line 333 of file SequencerService.hpp.

Referenced by applyConfig().

◆ fire_expiry_state_

State sequencer::SequencerService::fire_expiry_state_ {State::UNKNOWN}
private

Definition at line 329 of file SequencerService.hpp.

Referenced by applyConfig().

◆ fire_extended_ms_

uint32_t sequencer::SequencerService::fire_extended_ms_ {10000}
private

Definition at line 334 of file SequencerService.hpp.

Referenced by applyConfig().

◆ fire_state_

State sequencer::SequencerService::fire_state_ {State::UNKNOWN}
private

Definition at line 328 of file SequencerService.hpp.

Referenced by applyConfig(), doExtendFire(), and doTransitionTo().

◆ hold_rules_

std::map<State, HoldRule> sequencer::SequencerService::hold_rules_
private

Holdable states, by target.

Rebuilt on every config load; read on the transition path. RELOAD_CONFIG runs on a TCP handler thread while a transition may be running on another, hence the mutex.

Definition at line 241 of file SequencerService.hpp.

Referenced by applyConfig(), and doTransitionTo().

◆ hold_timer_

HoldTimer sequencer::SequencerService::hold_timer_
private

Definition at line 226 of file SequencerService.hpp.

Referenced by doExtendFire(), doTransitionTo(), and ~SequencerService().

◆ kAbortDoneDelayMs

uint32_t sequencer::SequencerService::kAbortDoneDelayMs = 3000
staticconstexprprivate

Delay between ABORT and ABORT_DONE.

Was an AbortBroadcaster ctor default; kept here beside the other fire/abort timings until a config key is wanted for it.

Definition at line 269 of file SequencerService.hpp.

Referenced by applyConfig().

◆ kCommandTimeoutSeconds

int sequencer::SequencerService::kCommandTimeoutSeconds = 10
staticconstexprprivate

How long a caller waits for its command to run before giving up and reporting failure.

Generous: the point is that a wedged worker cannot pin every TCP thread forever, not to impose a deadline on normal work (a transition is milliseconds).

Definition at line 175 of file SequencerService.hpp.

Referenced by enqueueAndWait().

◆ kElodinRetrySeconds

int sequencer::SequencerService::kElodinRetrySeconds = 5
staticconstexprprivate

Definition at line 279 of file SequencerService.hpp.

Referenced by init(), and startElodinRetry().

◆ pressure_feed_

PressureFeed sequencer::SequencerService::pressure_feed_
private

Live calibrated pressures for pressure(), on its own Elodin connection.

Definition at line 229 of file SequencerService.hpp.

Referenced by applyConfig(), doTransitionTo(), makeScriptEnv(), publishState(), and scriptStatusReport().

◆ refused_states_

std::map<State, std::string> sequencer::SequencerService::refused_states_
private

States that declare a script which did NOT hold up, with the reason.

These are not enterable. doTransitionTo refuses them in its validation block, before the abort broadcast and before any valve moves, and allowedBitmask() omits them so nothing ever advertises one as reachable in the first place. Guarded by config_mutex_.

Definition at line 253 of file SequencerService.hpp.

Referenced by applyConfig(), doTransitionTo(), publishState(), and scriptStatusReport().

◆ script_runner_

ScriptRunner sequencer::SequencerService::script_runner_
private

Definition at line 227 of file SequencerService.hpp.

Referenced by doTransitionTo().

◆ sequencer_owns_valves_

bool sequencer::SequencerService::sequencer_owns_valves_ {false}
private

[controller_service].sequencer_owns_valves.

When true the fire hold is given no notifier at all, so there is no FIRE_START to suppress and no flag to forget to check.

Definition at line 339 of file SequencerService.hpp.

Referenced by applyConfig(), and doTransitionTo().

◆ state_machine_

StateMachine sequencer::SequencerService::state_machine_
private

Definition at line 223 of file SequencerService.hpp.

Referenced by applyConfig(), doTransitionTo(), and publishState().


The documentation for this class was generated from the following files: