|
STAR
Cal STAR — firmware, DAQ server, and engine design
|
Sends UDP actuator commands based on state transitions. More...
#include <ActuatorCommander.hpp>
Public Member Functions | |
| bool | load (const std::string &config_content, const std::string &csv_path, const std::string &delay_csv_path="") |
| Load actuator roles from config content and state→actuator map from CSV. | |
| void | setDefaultBindAddress (const std::string &address) |
| Pin outgoing actuator commands to a NIC, unless [actuator_service].bind_address already named one explicitly. | |
| void | applyForState (State state, bool is_transition=false) |
| Send actuator commands for the given state (one shot). | |
| void | startContinuousLoop (State state, bool allow_delays=true, bool defer_first_pass=false) |
| Start the 1 Hz continuous re-send loop for the given state. | |
| void | stopContinuousLoop () |
| Stop the continuous re-send loop (blocks until the thread exits). | |
| bool | sendSingleActuator (const std::string &name, int pos) |
| Send one UDP packet for a single role (debug manual command). | |
| std::map< std::string, int > | positionsForState (const std::string &state_name) const |
| The logical positions the CSV declares for state_name (role -> 0 CLOSED / 1 OPEN). | |
| double | delayForRole (const std::string &state_name, const std::string &role) const |
| The staged delay, in seconds, that role waits before moving on entry to state_name. | |
| void | setManualOverride (const std::string &name, int pos) |
| Debug: hold this logical position for the role until cleared or state transition. | |
| void | clearAllManualOverrides () |
| Clear all manual overrides (e.g. | |
| void | setScriptPosition (const std::string &name, int pos) |
| A running script's position for one valve — the script layering on top of its state's CSV column. | |
| void | clearScriptPositions () |
| Drop every script position. | |
| void | setFireState (State s) |
| Set the Elodin client for publishing commanded state [0x32, ch] to the DB. | |
| void | setElodinClient (fsw::elodin::ElodinClient *client) |
| void | publishInitialState () |
| Publish initial de-energized state for all actuators to Elodin DB [0x32]. | |
| bool | isLoaded () const |
Private Member Functions | |
| std::map< std::string, std::map< std::string, int > >::const_iterator | findStateActuators (const std::string &state_name) const |
| bool | sendBatch (const std::map< std::string, std::vector< std::pair< uint8_t, uint8_t > > > &by_board) |
| Send to every board with the retransmits interleaved, so boards stay in step. | |
| bool | sendUDP (const std::string &board_ip, const std::vector< std::pair< uint8_t, uint8_t > > &id_state_pairs) |
| void | publishCommandedState (uint8_t table_hi, uint8_t global_channel, uint8_t logical_pos) |
Static Private Member Functions | |
| static void | logOverrideShadowingScript (const std::string &role) |
| Warn once per role that a manual override is fighting a running script for a valve. | |
Private Attributes | |
| std::map< std::string, ActuatorRole > | roles_ |
| std::map< std::string, std::map< std::string, double > > | state_actuator_delays_ |
| state -> actuator -> delay in seconds, from state_machine_actuator_delays.csv. | |
| State | fire_state_ {State::FIRE} |
| Bumped on every transition so an in-flight delayed stage from the previous state aborts instead of landing after we have already moved on. | |
| std::atomic< uint64_t > | schedule_gen_ {0} |
| std::set< std::string > | pending_roles_ |
| Roles whose delayed stage has not fired yet. | |
| std::mutex | pending_roles_mutex_ |
| std::map< std::string, std::map< std::string, int > > | state_actuators_ |
| std::map< std::string, int > | manual_overrides_ |
| std::mutex | overrides_mutex_ |
| std::map< std::string, int > | script_positions_ |
| Valves a running script has taken over, role -> logical position. | |
| std::mutex | script_positions_mutex_ |
| std::thread | loop_thread_ |
| std::atomic< bool > | loop_running_ {false} |
| State | loop_state_ {State::IDLE} |
| std::string | bind_addr_ {"0.0.0.0"} |
| bool | bind_addr_explicit_ {false} |
| uint16_t | actuator_port_ {5005} |
| bool | loaded_ {false} |
| fsw::elodin::ElodinClient * | elodin_ {nullptr} |
Sends UDP actuator commands based on state transitions.
Manages the continuous re-send loop (~10 Hz). Debug mode: manual overrides apply until cleared (Sequencer clears them on every state transition so the CSV wins when changing state).
Thread-safe: applyForState, setManualOverride, clearAllManualOverrides, stopContinuousLoop.
Definition at line 44 of file ActuatorCommander.hpp.
| void sequencer::ActuatorCommander::applyForState | ( | State | state, |
| bool | is_transition = false ) |
Send actuator commands for the given state (one shot).
Groups commands by board IP, sends one UDP packet per board. PWM actuators are skipped in FIRE state. Command every actuator for state.
is_transition selects between the two things this does. On an actual state change it runs the CSV's per-actuator delays as a staged schedule; on the 1 Hz republish it sends the settled (final) positions with no delay. Without that split the republish would re-arm every pending delay once a second and the schedule would never finish.
Definition at line 395 of file ActuatorCommander.cpp.
References sequencer::actuator_elodin_low_byte(), sequencer::ActuatorRole::board_id, sequencer::ActuatorRole::board_ip, ch(), sequencer::ActuatorRole::channel, sequencer::ActuatorRole::cmd_table_hi, emit(), findStateActuators(), fire_state_, sequencer::ActuatorRole::is_no, sequencer::ActuatorRole::is_pwm, lk, lock, logOverrideShadowingScript(), manual_overrides_, sequencer::StateMachine::name(), overrides_mutex_, pending_roles_, pending_roles_mutex_, publishCommandedState(), r, roles_, schedule_gen_, script_positions_, script_positions_mutex_, sendBatch(), state, state_actuator_delays_, and state_actuators_.
Referenced by main(), and startContinuousLoop().
| void sequencer::ActuatorCommander::clearAllManualOverrides | ( | ) |
Clear all manual overrides (e.g.
leaving debug mode or transitioning state).
Definition at line 789 of file ActuatorCommander.cpp.
References lock, manual_overrides_, and overrides_mutex_.
| void sequencer::ActuatorCommander::clearScriptPositions | ( | ) |
Drop every script position.
Called on any transition, beside clearAllManualOverrides.
Definition at line 784 of file ActuatorCommander.cpp.
References lk, script_positions_, and script_positions_mutex_.
Referenced by main().
| double sequencer::ActuatorCommander::delayForRole | ( | const std::string & | state_name, |
| const std::string & | role ) const |
The staged delay, in seconds, that role waits before moving on entry to state_name.
0 when the state, the role, or the delays file has no entry.
Exposed so a timed hold can be measured from when the valve it cares about actually opens rather than from the transition: a staggered state opens its valves at different times, so "the state was held for N" and "that valve was open for N" are different claims.
Definition at line 671 of file ActuatorCommander.cpp.
References state_actuator_delays_.
|
private |
Definition at line 263 of file ActuatorCommander.cpp.
References state_actuators_, and sequencer::toLower().
Referenced by applyForState(), and positionsForState().
|
inline |
Definition at line 178 of file ActuatorCommander.hpp.
References loaded_.
| bool sequencer::ActuatorCommander::load | ( | const std::string & | config_content, |
| const std::string & | csv_path, | ||
| const std::string & | delay_csv_path = "" ) |
Load actuator roles from config content and state→actuator map from CSV.
| config_content | Raw config.toml file contents (already read). |
| csv_path | Path to state_machine_actuators.csv (with fallbacks). |
| delay_csv_path | Where the per-actuator staged delays live, from [state_machine].actuator_delay_csv. Empty falls back to deriving it from csv_path. |
The derivation used to be the ONLY way this was found: the delays filename was built by swapping the "state_machine_actuators.csv" suffix on the positions path. That key was parsed into the config and then read by nothing, so pointing it anywhere had no effect — and if the positions file was named anything else the swap did not fire, the POSITIONS file was opened as the delays file, every cell parsed as non-numeric, and the stand silently ran with no staggering at all. Losing a fire-column stagger with no error is not an acceptable failure mode for a missing filename.
Definition at line 66 of file ActuatorCommander.cpp.
References actuator_port_, b, bind_addr_, bind_addr_explicit_, sequencer::ActuatorRole::board_id, sequencer::ActuatorRole::board_ip, sequencer::ActuatorRole::channel, sequencer::ActuatorRole::cmd_table_hi, sequencer::ActuatorRole::is_no, sequencer::ActuatorRole::is_pwm, fsw::elodin::kBallValveCmdHi, fsw::config::load_from_string(), loaded_, r, roles_, state_actuator_delays_, state_actuators_, and sequencer::trimVal().
Referenced by engine.layerx.measurements.MeasurementStore::get(), and main().
|
staticprivate |
Warn once per role that a manual override is fighting a running script for a valve.
Definition at line 738 of file ActuatorCommander.cpp.
References lk.
Referenced by applyForState().
| std::map< std::string, int > sequencer::ActuatorCommander::positionsForState | ( | const std::string & | state_name | ) | const |
The logical positions the CSV declares for state_name (role -> 0 CLOSED / 1 OPEN).
Empty when the state has no column.
Exposed so a timed hold can be checked against the state it expires into: the hold closes its valves only because the return state's column says CLOSE, and if it does not, the timer expires, the state changes, and the valve stays open.
Definition at line 681 of file ActuatorCommander.cpp.
References findStateActuators(), and state_actuators_.
|
private |
Definition at line 797 of file ActuatorCommander.cpp.
References elodin_.
Referenced by applyForState().
| void sequencer::ActuatorCommander::publishInitialState | ( | ) |
Publish initial de-energized state for all actuators to Elodin DB [0x32].
Does NOT send UDP commands — only populates the DB so the frontend has data on startup.
Definition at line 814 of file ActuatorCommander.cpp.
References sequencer::actuator_elodin_low_byte(), elodin_, ActuatorCommander::publishCommandedState(), and roles_.
|
private |
Send to every board with the retransmits interleaved, so boards stay in step.
Send one command batch to every board, interleaving the retransmits.
Each packet still goes out 3x ~1 ms apart so it lands in the board's UDP receive window whatever its loop() rate. What changed is the ordering: round 1 to ALL boards, then round 2, then round 3, instead of finishing all three rounds for board A before starting board B. The old order meant every extra board added ~2 ms of skew to a state change – with two actuator boards, board 2's first packet left ~2 ms after board 1's, on every transition. Now the boards are within microseconds of each other and the batch takes ~2 ms total instead of ~2 ms per board.
One socket for the whole batch rather than one per board, since we now revisit each board 3x.
Definition at line 296 of file ActuatorCommander.cpp.
References actuator_port_, bind_addr_, daq::create_actuator_command_packet(), and o.
Referenced by applyForState(), and sendUDP().
| bool sequencer::ActuatorCommander::sendSingleActuator | ( | const std::string & | name, |
| int | pos ) |
Send one UDP packet for a single role (debug manual command).
Definition at line 688 of file ActuatorCommander.cpp.
References sequencer::ActuatorRole::board_ip, sequencer::ActuatorRole::is_no, ok, roles_, sendUDP(), and sequencer::toLower().
Referenced by main().
|
private |
Definition at line 279 of file ActuatorCommander.cpp.
References sendBatch().
Referenced by sendSingleActuator().
| void sequencer::ActuatorCommander::setDefaultBindAddress | ( | const std::string & | address | ) |
Pin outgoing actuator commands to a NIC, unless [actuator_service].bind_address already named one explicitly.
Call after load().
bind_address defaulted to 0.0.0.0 in every shipped profile, so the bind() at the bottom of sendBatch() constrained nothing and the kernel chose the egress interface. That was harmless while the DAQ owned its machine and is not now that it shares the apps box.
Definition at line 60 of file ActuatorCommander.cpp.
References bind_addr_, and bind_addr_explicit_.
|
inline |
|
inline |
Set the Elodin client for publishing commanded state [0x32, ch] to the DB.
Which state hands PWM actuators to controller_service. During it this commander stops commanding PWM roles entirely, so exactly one thing drives them during a burn. Was the State::FIRE enumerator; now follows [fire] state like every other fire decision.
Definition at line 166 of file ActuatorCommander.hpp.
References fire_state_.
Referenced by main().
| void sequencer::ActuatorCommander::setManualOverride | ( | const std::string & | name, |
| int | pos ) |
Debug: hold this logical position for the role until cleared or state transition.
Definition at line 721 of file ActuatorCommander.cpp.
References lock, manual_overrides_, overrides_mutex_, roles_, and sequencer::toLower().
Referenced by main().
| void sequencer::ActuatorCommander::setScriptPosition | ( | const std::string & | name, |
| int | pos ) |
A running script's position for one valve — the script layering on top of its state's CSV column.
Deliberately NOT manual_overrides_. That map is cleared on every transition AND whenever debug mode is switched off (SequencerService::doSetDebugMode), so a script writing there would have its valve intent silently wiped by an operator leaving debug mode while the script kept running.
Setting a position also takes OWNERSHIP of the valve: any staged command still in flight for it is cancelled, and it stops being skipped by the republish's pending-delay filter. Without that, the state's own delays column could move the valve seconds after the script already commanded it, landing last and silently winning.
Like setManualOverride, this is not debug-gated here — the gate belongs to the caller.
Definition at line 750 of file ActuatorCommander.cpp.
References a, b, lk, pending_roles_, pending_roles_mutex_, r, roles_, script_positions_, and script_positions_mutex_.
Referenced by main().
| void sequencer::ActuatorCommander::startContinuousLoop | ( | State | state, |
| bool | allow_delays = true, | ||
| bool | defer_first_pass = false ) |
Start the 1 Hz continuous re-send loop for the given state.
Stops any previously running loop first. allow_delays false makes the entry apply immediately — used for abort states, which must not sit behind a delay.
| defer_first_pass | skip the immediate pass and start republishing one period later. For a dynamic state: the caller has already applied the column, so that pass is redundant, and running it races the script that is about to start commanding valves — a race the script loses, because the pass resolves positions and only then spends a couple of milliseconds on retransmits, landing a stale command on top of the script's. |
Definition at line 616 of file ActuatorCommander.cpp.
References applyForState(), loop_running_, loop_state_, loop_thread_, sequencer::StateMachine::name(), state, and stopContinuousLoop().
Referenced by main().
| void sequencer::ActuatorCommander::stopContinuousLoop | ( | ) |
Stop the continuous re-send loop (blocks until the thread exits).
Definition at line 662 of file ActuatorCommander.cpp.
References loop_running_, and loop_thread_.
Referenced by main(), and startContinuousLoop().
|
private |
Definition at line 217 of file ActuatorCommander.hpp.
Referenced by load(), and sendBatch().
|
private |
Definition at line 215 of file ActuatorCommander.hpp.
Referenced by load(), sendBatch(), and setDefaultBindAddress().
|
private |
Definition at line 216 of file ActuatorCommander.hpp.
Referenced by load(), and setDefaultBindAddress().
|
private |
Definition at line 219 of file ActuatorCommander.hpp.
Referenced by publishCommandedState(), publishInitialState(), and setElodinClient().
|
private |
Bumped on every transition so an in-flight delayed stage from the previous state aborts instead of landing after we have already moved on.
Definition at line 188 of file ActuatorCommander.hpp.
Referenced by applyForState(), and setFireState().
|
private |
Definition at line 218 of file ActuatorCommander.hpp.
Referenced by isLoaded(), and load().
|
private |
Definition at line 212 of file ActuatorCommander.hpp.
Referenced by startContinuousLoop(), and stopContinuousLoop().
|
private |
Definition at line 213 of file ActuatorCommander.hpp.
Referenced by startContinuousLoop().
|
private |
Definition at line 211 of file ActuatorCommander.hpp.
Referenced by startContinuousLoop(), and stopContinuousLoop().
|
private |
Definition at line 199 of file ActuatorCommander.hpp.
Referenced by applyForState(), clearAllManualOverrides(), and setManualOverride().
|
private |
Definition at line 200 of file ActuatorCommander.hpp.
Referenced by applyForState(), clearAllManualOverrides(), and setManualOverride().
|
private |
Roles whose delayed stage has not fired yet.
The 1 Hz republish skips these: the board holds its last commanded position, which is exactly the pre-transition value we want it to keep until the delay elapses. Without this the republish would send the settled position first and any delay longer than the republish period could never be observed.
Definition at line 194 of file ActuatorCommander.hpp.
Referenced by applyForState(), and setScriptPosition().
|
private |
Definition at line 195 of file ActuatorCommander.hpp.
Referenced by applyForState(), and setScriptPosition().
|
private |
Definition at line 183 of file ActuatorCommander.hpp.
Referenced by applyForState(), load(), publishInitialState(), sendSingleActuator(), setManualOverride(), and setScriptPosition().
|
private |
Definition at line 189 of file ActuatorCommander.hpp.
Referenced by applyForState().
|
private |
Valves a running script has taken over, role -> logical position.
Its own mutex, and never held at the same time as overrides_mutex_ — applyForState snapshots this first and then takes the other, so no call path holds both and there is no lock order to get wrong.
Definition at line 205 of file ActuatorCommander.hpp.
Referenced by applyForState(), clearScriptPositions(), and setScriptPosition().
|
private |
Definition at line 206 of file ActuatorCommander.hpp.
Referenced by applyForState(), clearScriptPositions(), and setScriptPosition().
|
private |
state -> actuator -> delay in seconds, from state_machine_actuator_delays.csv.
Definition at line 185 of file ActuatorCommander.hpp.
Referenced by applyForState(), delayForRole(), and load().
|
private |
Definition at line 197 of file ActuatorCommander.hpp.
Referenced by applyForState(), findStateActuators(), load(), and positionsForState().