35 std::cerr <<
"usage: state_script_check --config <config.toml> --state <name> "
36 "[--transitions <state_transitions.csv>] <script-file>\n";
61 std::ifstream f(
path, std::ios::binary);
64 std::ostringstream ss;
72int main(
int argc,
char** argv) {
73 std::string config_path, state_name, transitions_path, script_path;
75 for (
int i = 1; i < argc; i++) {
76 const std::string
a = argv[i];
77 auto next = [&](std::string& dst) {
83 if (
a ==
"--config") {
84 if (!next(config_path))
86 }
else if (
a ==
"--state") {
87 if (!next(state_name))
89 }
else if (
a ==
"--transitions") {
90 if (!next(transitions_path))
92 }
else if (
a ==
"-h" ||
a ==
"--help") {
94 }
else if (!
a.empty() &&
a[0] ==
'-') {
95 std::cerr <<
"unknown option " <<
a <<
"\n";
101 if (config_path.empty() || state_name.empty() || script_path.empty())
104 std::string config_text;
105 if (!
readFile(config_path, config_text)) {
106 std::cerr <<
"cannot read config: " << config_path <<
"\n";
109 std::string script_text;
110 if (!
readFile(script_path, script_text)) {
111 std::cerr <<
"cannot read script: " << script_path <<
"\n";
116 if (cfg.states.empty()) {
120 std::cerr <<
"config declares no [[states]] (or failed to parse): " << config_path <<
"\n";
125 for (
const auto&
b : build.bad)
126 std::cerr <<
"config: " <<
b.where <<
" \"" <<
b.canonical
127 <<
"\" has no usable slug (got \"" <<
b.slug <<
"\") — a script cannot name it\n";
128 for (
const auto& c : build.collisions)
129 std::cerr <<
"config: " << c.where <<
"s \"" << c.first <<
"\" and \"" << c.second
130 <<
"\" both slug to " << c.slug <<
" — ambiguous\n";
137 const std::string csv =
138 transitions_path.empty() ? cfg.state_machine.transitions_csv : transitions_path;
143 csv_ok = sm.
load(csv);
146 std::cerr <<
"cannot read state_transitions.csv: " << csv
147 <<
" (pass --transitions to point at it)\n";
152 std::cerr <<
"config declares no state named \"" << state_name <<
"\"\n";
155 for (
const auto& s : cfg.states) {
164 std::vector<fsw::script::Diagnostic> issues = parsed.
diagnostics;
168 for (
const auto& d : issues)
170 << d.message <<
"\n";
172 return issues.empty() ? 0 : 1;
Parses state_transitions.csv and validates state transitions.
bool load(const std::string &csv_path)
Load from a state_transitions.csv file.
static State fromName(const std::string &name)
Translate a CSV name (e.g.
bool isAllowed(State from, State to) const
Returns true if transitioning from→to is allowed.
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.
*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
bool readFile(const std::string &path, std::string &out)
Config load_from_string(const std::string &text)
Parse config from an in-memory TOML string (SequencerService / ActuatorCommander hold it so).
const char * diag_name(Diag d)
std::string slugify(const std::string &name)
Canonical name -> slug: trim, uppercase, collapse internal whitespace runs to a single '_'.
ParseResult parse(const std::string &source)
Lex and parse, applying every check that does not need config.
TableBuild build_slug_tables(const fsw::config::Config &cfg)
Build the actuator / sensor / state slug tables from a loaded config.
std::vector< Diagnostic > validate(const ScriptProgram &program, const SlugTables &tables)
Config-dependent checks.
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::vector< Diagnostic > diagnostics