19 static const std::map<std::string, State> map = {
57 std::map<State, std::string>
names;
75 for (
const auto& s : cfg.states) {
77 if (s.id < 0 || s.id > 255 || s.name.empty())
79 const State st =
static_cast<State>(
static_cast<uint8_t
>(s.id));
80 fresh.names[st] = s.name;
81 fresh.by_name[s.name] = st;
83 fresh.aborts.insert(st);
84 if (s.is_boot && !fresh.boot_set) {
86 fresh.boot_set =
true;
90 fresh.loaded = !fresh.names.empty();
91 configStates() = fresh;
93 std::cout <<
"[StateMachine] Loaded " << fresh.
names.size() <<
" state(s) from config"
94 << (fresh.aborts.empty() ?
""
95 :
" (" + std::to_string(fresh.aborts.size()) +
" abort)")
100 const auto& cs = configStates();
105 return cs.aborts.count(s) > 0;
110 const auto& cs = configStates();
111 if (cs.loaded && cs.boot_set)
113 if (cs.loaded && !cs.names.empty()) {
117 const State first = cs.names.begin()->first;
118 std::cerr <<
"[StateMachine] No is_boot state in config — booting into lowest-id state \""
119 << cs.names.begin()->second <<
"\"" << std::endl;
130 const auto& cs = configStates();
131 auto it = cs.names.find(s);
132 if (it != cs.names.end())
147 return "Press Standby";
149 return "GN2 Low Press";
151 return "GN2 Low Vent";
161 return "GN2 High Press";
163 return "GN2 High Vent";
173 return "Engine Abort";
177 return "Emergency Abort";
186 const auto& cs = configStates();
187 auto it = cs.by_name.find(
name);
188 if (it != cs.by_name.end())
197 auto it = map.find(
name);
202 std::string lower =
name;
203 std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
204 for (
const auto& [k, v] : map) {
206 std::transform(kl.begin(), kl.end(), kl.begin(), ::tolower);
217 size_t a = s.find_first_not_of(
" \t\r\n\"");
218 size_t b = s.find_last_not_of(
" \t\r\n\"");
219 return (
a == std::string::npos) ?
"" : s.substr(
a,
b -
a + 1);
223 const char* fallbacks[] = {
224 "config/state_transitions.csv",
225 "../config/state_transitions.csv",
226 "../../config/state_transitions.csv",
229 "config/profiles/default/state_transitions.csv",
230 "../config/profiles/default/state_transitions.csv",
231 "../../config/profiles/default/state_transitions.csv",
234 std::ifstream f(csv_path);
235 std::string used_path = csv_path;
237 for (
const char* fb : fallbacks) {
238 if (std::string(fb) == csv_path)
248 std::cerr <<
"[StateMachine] Cannot open state_transitions.csv (tried: " << csv_path;
249 for (
const char* fb : fallbacks) {
250 if (std::string(fb) != csv_path)
251 std::cerr <<
", " << fb;
253 std::cerr <<
")" << std::endl;
258 if (!std::getline(f, line))
262 std::vector<State> column_states;
264 std::istringstream iss(line);
267 while (std::getline(iss, cell,
',')) {
273 column_states.push_back(s);
279 while (std::getline(f, line)) {
280 std::vector<std::string> cells;
281 std::istringstream iss(line);
283 while (std::getline(iss, cell,
','))
285 if (cells.empty() || cells[0].empty())
292 for (
size_t col = 1; col < cells.size() && col - 1 < column_states.size(); ++col) {
293 if (cells[col] ==
"1") {
294 State to = column_states[col - 1];
303 std::cout <<
"[StateMachine] Loaded " << rows <<
" states from " << used_path << std::endl;
312 return it->second.count(to) > 0;
316 std::vector<State>
result;
319 for (
State s : it->second)
329 for (
State s : it->second) {
330 uint8_t val =
static_cast<uint8_t
>(s);
bool load(const std::string &csv_path)
Load from a state_transitions.csv file.
std::vector< State > allowedFrom(State state) const
Returns the list of states reachable from the given state.
static State fromName(const std::string &name)
Translate a CSV name (e.g.
static std::string name(State s)
Human-readable name for a State (e.g.
static const std::map< std::string, State > & csvStateMap()
uint32_t allowedBitmask(State state) const
Returns a bitmask where bit N is set if state N is reachable from the given state.
bool isAllowed(State from, State to) const
Returns true if transitioning from→to is allowed.
static State bootState()
The state to boot into (config is_boot, else Idle).
static void loadStatesFromConfig(const std::string &config_content)
Adopt the [[states]] entries from a config as the source of truth for state names and semantics.
static bool isAbort(State s)
True if the state carries the abort semantics (config is_abort, else the built-in trio).
std::map< State, std::set< State > > transitions_
static ActuatorControllerState state
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
*brief create JSON pointer *sa reference_tokens const string_t & a
Config load_from_string(const std::string &text)
Parse config from an in-memory TOML string (SequencerService / ActuatorCommander hold it so).
ConfigStates & configStates()
static std::string trimCell(const std::string &s)
State
System state IDs — must match the TypeScript SystemState enum in web-gui/shared/types....
The whole parsed config.toml, restricted to what C++ consumes.
std::map< std::string, State > by_name
std::map< State, std::string > names