73int main(
int argc,
char* argv[])
81 auto& dependency = omitted.
ethservice.configuration.data.mc.mc4plusfaps.pos;
82 std::memset(&dependency, 0xa5,
sizeof(dependency));
83 check(parse(parser,
"", omitted),
"Entire MC POS group can be omitted");
84 const auto& omittedData = omitted.
ethservice.configuration.data.mc.mc4plusfaps;
85 eOmn_serv_config_data_as_pos_t expected {};
86 expected.config.boardconfig[0].boardinfo.type = eobrd_cantype_none;
87 check(std::memcmp(&omittedData.pos, &expected,
sizeof(expected)) == 0,
88 "Absent POS dependency is explicitly marked and otherwise zero-initialized");
93 "Encoder placement, resolution and tolerance remain available");
96 for (
const std::string pos : {
"(POS)",
"(POS (location CAN1:1))",
97 "(POS (location invalid))",
"(POS (location CAN1:1 CAN1:2))",
98 "(POS (SENSORS (connector ignored)) (SETTINGS (acquisitionRate invalid)))"})
100 check(!parse(parser, pos, ignored),
"MC POS group is rejected");
102 auto basicMC = motionConfig(
"",
"",
"eomn_serv_MC_mc4plus");
103 check(parser.
parseService(basicMC, ignored),
"Plain mc4plus service remains supported");
104 auto basicMCWithPOS = motionConfig(
"(POS)",
"",
"eomn_serv_MC_mc4plus");
105 check(!parser.
parseService(basicMCWithPOS, ignored),
"Plain mc4plus also rejects a POS group");
106 auto genericMCWithPOS = motionConfig(
"(POS)",
"",
"eomn_serv_MC_generic");
107 check(!parser.
parseService(genericMCWithPOS, ignored),
"Generic MC cannot bypass POS group rejection");
108 auto noBoards = motionConfig(
"",
"");
109 check(!parser.
parseService(noBoards, ignored),
"MC still requires CANBOARDS when POS is omitted");
110 auto malformedBoards = motionConfig(
"",
"(CANBOARDS (type mtb4c))");
111 check(!parser.
parseService(malformedBoards, ignored),
"MC still validates CANBOARDS protocol and firmware fields");
112 yarp::os::Property noMapping;
113 noMapping.fromString(
"(SERVICE (type eomn_serv_MC_mc4plusfaps) (PROPERTIES "
114 "(ETHBOARD (type mc4plus)) " + canboards +
"))");
115 check(!parser.
parseService(noMapping, ignored),
"Omitting POS does not bypass mandatory JOINTMAPPING validation");
116 auto badEncoderText = motionConfig(
"").toString();
117 const auto port = badEncoderText.find(
"POS:hand_thumb_oc");
118 check(port != std::string::npos,
"Encoder test fixture contains the expected POS port");
119 badEncoderText.replace(port, std::string(
"POS:hand_thumb_oc").size(),
"POS:invalid");
120 yarp::os::Property badEncoder;
121 badEncoder.fromString(badEncoderText);
122 check(!parser.
parseService(badEncoder, ignored),
"Omitting POS does not bypass encoder port validation");
124 yarp::os::Property posConfig;
125 posConfig.fromString(
"(SERVICE (type eomn_serv_AS_pos) (PROPERTIES " + canboards + R
"(
126 (SENSORS (id thumb index middle ring) (sensorName hand hand hand hand)
127 (type eoas_pos_angle eoas_pos_angle eoas_pos_angle eoas_pos_angle)
128 (location CAN1:1 CAN1:1 CAN1:1 CAN1:1)
129 (port POS:hand_thumb_oc POS:hand_index_oc POS:hand_middle_oc POS:hand_ring_pinky_oc)
130 (boardType mtb4c mtb4c mtb4c mtb4c)
131 (connector CONN:J3_SDA3 CONN:J3_SDA0 CONN:J3_SDA1 CONN:J3_SDA2)))
132 (SETTINGS (acquisitionRate 50) (enabledSensors thumb index middle ring)))
135 check(parser.parseService(posConfig, standalone), "Standalone POS still parses");
136 const auto& sensors = standalone.
ethservice.configuration.data.as.pos.config.boardconfig[0].sensors;
138 sensors[0].connector == 3 && sensors[1].connector == 0 &&
139 sensors[2].connector == 1 && sensors[3].connector == 2,
140 "Standalone POS settings and physical connector mapping are preserved");
143 eOmn_serv_parameter_t prepared {};
145 "MC dependency cannot be resolved before standalone POS activation");
148 "An MC request cannot masquerade as standalone POS activation");
151 "Activated standalone POS resolves the MC dependency");
152 const auto& resolved = prepared.configuration.data.mc.mc4plusfaps;
153 check(std::memcmp(&resolved.pos, &standalone.
ethservice.configuration.data.as.pos,
154 sizeof(resolved.pos)) == 0,
155 "Full POS board, location and sensor configuration is inherited");
157 expectedRequest.configuration.data.mc.mc4plusfaps.pos = standalone.
ethservice.configuration.data.as.pos;
158 check(std::memcmp(&prepared, &expectedRequest,
sizeof(prepared)) == 0,
159 "Resolving POS preserves every other MC service field");
160 check(std::memcmp(&resolved.arrayofjomodescriptors, &omittedData.arrayofjomodescriptors,
161 sizeof(resolved.arrayofjomodescriptors)) == 0,
162 "Resolving POS preserves MC actuator and encoder descriptors");
163 check(omittedData.pos.config.boardconfig[0].boardinfo.type == eobrd_cantype_none,
164 "Resolving POS does not mutate the original parser output");
167 "POS configuration cannot leak into a different Ethernet resource");
169 embedded.configuration.data.mc.mc4plusfaps.pos.config.boardconfig[0].boardinfo.type = eobrd_cantype_mtb4c;
170 check(!otherBoard.
prepare(eomn_serv_category_mc, &embedded, prepared),
171 "Prepopulated embedded POS cannot bypass standalone POS activation");
172 check(cache.
prepare(eomn_serv_category_mc, &embedded, prepared) &&
173 std::memcmp(&prepared.configuration.data.mc.mc4plusfaps.pos,
174 &standalone.
ethservice.configuration.data.as.pos,
sizeof(resolved.pos)) == 0,
175 "Activated standalone POS always replaces embedded MC POS data");
177 alternative.configuration.data.as.pos.config.boardconfig[0].sensors[0].connector = 0;
180 prepared.configuration.data.mc.mc4plusfaps.pos.config.boardconfig[0].sensors[0].connector == 3,
181 "A second POS owner does not overwrite the first active mapping");
184 "Stopping MC preserves standalone POS configuration");
187 "Stopping POS invalidates its cached dependency");
190 prepared.configuration.data.mc.mc4plusfaps.pos.config.boardconfig[0].sensors[0].connector == 0,
191 "POS can be activated again with a new configuration");
194 "Stopping all services invalidates the POS configuration");
196 std::memcmp(&prepared, &standalone.
ethservice,
sizeof(prepared)) == 0,
197 "Standalone POS requests pass through unchanged");
198 check(cache.
prepare(eomn_serv_category_mc,
nullptr, prepared),
"Null requests retain existing handling");
202 "Fake resource enforces POS-first activation as the real resource does");
205 "Fake resource accepts MC after standalone POS activation");
208 "Fake resource does not reuse a stopped POS service");
210 "Fake resource rejects prepopulated MC POS without standalone activation");