29static void check(
bool ok,
const std::string& what) {
30 std::cout << (
ok ?
" ok " :
" FAIL ") << what << std::endl;
37 bool declare_board_14 =
true) {
39 toml +=
"[boards.act]\ntype = \"ACTUATOR\"\nip = \"192.0.2.12\"\nboard_id = 12\n";
40 toml +=
"enabled = true\n\n";
41 if (declare_board_14) {
42 toml +=
"[boards.act2]\ntype = \"ACTUATOR\"\nip = \"192.0.2.14\"\nboard_id = 14\n";
43 toml +=
"enabled = true\n\n";
45 auto role = [&](
const char* name,
const char* kind,
int ch,
int board) {
46 const auto it = assign.find(name);
47 std::string line = std::string(
"\"") + name +
"\" = [\"" + kind +
"\", " +
48 std::to_string(
ch) +
", " + std::to_string(
board);
49 if (it != assign.end())
50 line +=
", \"" + it->second +
"\"";
53 toml +=
"[actuator_roles]\n";
54 toml += role(
"Fuel Press",
"NC", 3, 12);
55 toml += role(
"LOX Press",
"NO", 8, 12);
56 toml += role(
"Far Side Press",
"NC", 2, 14);
61 std::cout <<
"=== controller PWM actuator resolution ===" << std::endl;
66 makeConfig({{
"Fuel Press",
"pwm_fuel"}, {
"LOX Press",
"pwm_ox"}}));
67 check(
r.ok(),
"both outputs resolve with no issues");
68 check(
r.fuel.resolved() &&
r.fuel.channel == 3 &&
r.fuel.board_ip ==
"192.0.2.12",
69 "fuel resolves to the channel and board its actuator declares");
70 check(
r.ox.resolved() &&
r.ox.channel == 8 &&
r.ox.board_ip ==
"192.0.2.12",
71 "ox resolves to the channel and board its actuator declares");
72 check(
r.fuel.actuator_name ==
"Fuel Press",
73 "the target carries the actuator name, so logs can say which valve");
80 makeConfig({{
"Far Side Press",
"pwm_fuel"}, {
"LOX Press",
"pwm_ox"}}));
81 check(
r.ok(),
"any actuator can serve an output, whatever it is called");
82 check(
r.fuel.channel == 2 &&
r.fuel.board_ip ==
"192.0.2.14",
83 "fuel follows the assignment to that actuator's own board and channel");
89 check(!
r.ok(),
"an unassigned fuel output is reported");
90 check(!
r.fuel.resolved(),
"an unassigned fuel output resolves to nothing (no CH3 default)");
91 check(
r.fuel.channel == 0,
"unresolved target keeps channel 0 so it cannot be addressed");
92 check(
r.ox.resolved(),
"the other output still resolves independently");
93 check(
r.issues.size() == 1 &&
r.issues[0].find(
"pwm_fuel") != std::string::npos,
94 "the reported issue names the output that has no actuator");
102 {{
"Fuel Press",
"pwm_fuel"}, {
"Far Side Press",
"pwm_fuel"}, {
"LOX Press",
"pwm_ox"}}));
103 check(!
r.ok(),
"a duplicate assignment is reported");
105 "a duplicated output resolves to nothing rather than picking one");
106 check(
r.issues.size() == 1 &&
r.issues[0].find(
"Fuel Press") != std::string::npos &&
107 r.issues[0].find(
"Far Side Press") != std::string::npos,
108 "the reported issue names both claimants");
116 makeConfig({{
"Fuel Press",
"pwm_fuel"}, {
"Far Side Press",
"pwm_ox"}},
118 check(!
r.ok(),
"an actuator on an undeclared board is reported");
119 check(!
r.ox.resolved(),
"no IP is synthesized from the board_id");
120 check(
r.issues.size() == 1 &&
r.issues[0].find(
"board_id 14") != std::string::npos,
121 "the reported issue names the board that has no ip");
130 check(
r.issues.size() == 2,
"both unassigned outputs are reported together");
131 check(!
r.fuel.resolved() && !
r.ox.resolved(),
"neither target is invented");
139 makeConfig({{
"Fuel Press",
"pwm_fuel"}, {
"Far Side Press",
"pwm_ox"}}));
140 check(
r.ok(),
"actuators on different boards are not an error");
141 check(
r.fuel.board_ip ==
"192.0.2.12" &&
r.ox.board_ip ==
"192.0.2.14",
142 "each target keeps its own board IP");
148 "[actuator_roles]\n\"Fuel Press\" = [\"NC\", 3, 12, \"pwm_fuel\"]\n"
149 "\"LOX Press\" = [\"NO\", 8, 12, \"pwm_ox\"]\n");
151 check(!
r.ok(),
"no declared boards is reported, not filled in from a default table");
152 check(!
r.fuel.resolved() && !
r.ox.resolved(),
"neither target is invented");
159 const auto cfg =
makeConfig({{
"Fuel Press",
"pwm_fuel"}, {
"LOX Press",
"pwm_ox"}});
160 check(cfg.actuator_roles.at(
"LOX Press").is_no,
161 "an assigned actuator keeps its NO polarity");
162 check(cfg.actuator_roles.at(
"LOX Press").controller_role ==
"pwm_ox",
163 "...and its controller assignment at the same time");
164 check(cfg.actuator_roles.at(
"Far Side Press").controller_role.empty(),
165 "an unassigned actuator has no controller role");
Resolve which hardware the controller PWMs, from config alone.
Config load_from_string(const std::string &text)
Parse config from an in-memory TOML string (SequencerService / ActuatorCommander hold it so).
PWMResolution resolvePWMTargets(const fsw::config::Config &cfg)
The whole parsed config.toml, restricted to what C++ consumes.
static void check(bool ok, const std::string &what)
static fsw::config::Config makeConfig(const std::map< std::string, std::string > &assign, bool declare_board_14=true)
assign maps an actuator name to its 4th element; a name absent from it is unassigned.
std::string board(const std::string &name, const std::string &id, bool enabled=true)
static std::string ch(int ain, const std::string &kind, const std::string &extra="", const std::string &board="lj")