32namespace fs = std::filesystem;
40static void check(
bool ok,
const std::string& what) {
41 std::cout << (
ok ?
" ok " :
" FAIL ") << what << std::endl;
49 std::ofstream act(dir /
"state_machine_actuators.csv");
50 act <<
",Idle,Armed\n";
51 act <<
"Vent Valve,CLOSE,CLOSE\n";
52 act <<
"Staged Valve,CLOSE,OPEN\n";
55 std::ofstream del(dir /
"state_machine_actuator_delays.csv");
56 del <<
",Idle,Armed\n";
57 del <<
"Vent Valve,0,0\n";
58 del <<
"Staged Valve,0," << staged_delay_s <<
"\n";
60 return "[network]\nactuator_cmd_port = " + std::to_string(
port) +
61 "\n\n[actuator_service]\nbind_address = \"0.0.0.0\"\n\n"
62 "[boards.act_board]\ntype = \"ACTUATOR\"\nip = \"127.0.0.1\"\nboard_id = 12\n"
63 "enabled = true\nnum_actuators = 10\n\n"
64 "[actuator_roles]\n\"Vent Valve\" = [\"NC\", 1, 12]\n"
65 "\"Staged Valve\" = [\"NC\", 2, 12]\n";
69static int countCommands(
const std::vector<daqtest::Burst>& bursts, uint8_t
ch, uint8_t hw) {
71 for (
const auto&
b : bursts) {
72 auto it =
b.state.find(
ch);
73 if (it !=
b.state.end() && it->second == hw)
80 std::cout <<
"=== Script positions vs the republish ===" << std::endl;
82 const uint16_t
port = 45918;
83 const double kStagedDelay = 2.0;
84 fs::path dir = fs::temp_directory_path() /
"daq_script_positions_test";
86 fs::create_directories(dir);
88 const std::string csv = (dir /
"state_machine_actuators.csv").
string();
93 if (!listener.
start()) {
94 std::cerr <<
"could not bind UDP " <<
port << std::endl;
98 if (!ac.
load(cfg, csv)) {
99 std::cerr <<
"load failed" << std::endl;
106 std::this_thread::sleep_for(std::chrono::milliseconds(2600));
110 const auto b = listener.
bursts();
113 check(opens >= 3,
"the script's OPEN is re-asserted by every republish (saw " +
114 std::to_string(opens) +
")");
115 check(closes == 0,
"the republish never reverts it to the column's CLOSE");
127 std::this_thread::sleep_for(std::chrono::milliseconds(2600));
131 const auto b = listener.
bursts();
133 check(staged_open >= 1700 && staged_open <= 2400,
134 "an unnamed valve still honours its 2 s stagger (opened at " +
135 std::to_string(staged_open) +
" ms)");
148 std::this_thread::sleep_for(std::chrono::milliseconds(50));
152 std::this_thread::sleep_for(std::chrono::milliseconds(2600));
156 const auto b = listener.
bursts();
158 check(reopened < 0,
"the stale +2 s stage never re-opens the valve the script closed");
160 "and the republish keeps re-asserting the script's CLOSE");
172 std::this_thread::sleep_for(std::chrono::milliseconds(1200));
176 const auto b = listener.
bursts();
179 "the operator's override wins over the script's position");
190 std::this_thread::sleep_for(std::chrono::milliseconds(1200));
192 std::this_thread::sleep_for(std::chrono::milliseconds(1300));
196 const auto b = listener.
bursts();
199 "and back to the column's CLOSE once the script's positions were cleared");
203 std::cout << (
g_failures == 0 ?
"\nAll script-position checks passed.\n"
204 :
"\nFAILURES: " + std::to_string(
g_failures) +
"\n");
Collects UDP actuator packets, folding the 3x retransmit of one batch into a single burst.
std::vector< Burst > bursts() const
Sends UDP actuator commands based on state transitions.
void stopContinuousLoop()
Stop the continuous re-send loop (blocks until the thread exits).
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.
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 setManualOverride(const std::string &name, int pos)
Debug: hold this logical position for the role until cleared or state transition.
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.
bool sendSingleActuator(const std::string &name, int pos)
Send one UDP packet for a single role (debug manual command).
void clearScriptPositions()
Drop every script position.
static const emmc_port_t port
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
long long firstCommand(const std::vector< Burst > &bursts, uint8_t ch, uint8_t hw, long long after_ms=-1)
When ch was first commanded to hw at or after after_ms.
*brief user defined string literal for JSON values *sa std::size_t n
State
System state IDs — must match the TypeScript SystemState enum in web-gui/shared/types....
static void check(bool ok, const std::string &what)
long long firstCommand(const std::vector< Burst > &bursts, uint8_t ch, uint8_t hw, long long after_ms=-1)
When ch was first commanded to hw at or after after_ms.
static std::string writeFixture(const fs::path &dir, uint16_t port, double staged_delay_s)
Channel 1 = Vent Valve, channel 2 = Staged Valve (which the Armed column staggers).
static int countCommands(const std::vector< daqtest::Burst > &bursts, uint8_t ch, uint8_t hw)
How many distinct bursts commanded ch to hw.
static std::string ch(int ain, const std::string &kind, const std::string &extra="", const std::string &board="lj")