Line data Source code
1 :
2 : #include "test/ots_mm_udp_interface.h"
3 :
4 : #include <limits>
5 :
6 : ///==============================================================================
7 : /// To access Macro Maker with this tool
8 : /// first export...
9 : /// export OTS_MACROMAKER_UDP_PORT=<YOUR_PORT>
10 : /// export OTS_MACROMAKER_UDP_IP=127.0.0.1 #or the IP of the machine running Macro Maker
11 : /// then run... Macro Maker app
12 0 : int main(int argc, char* argv[])
13 : try
14 : {
15 0 : if(argc < 3)
16 : {
17 0 : fprintf(stderr,
18 : "usage: ots_mm_udp_test <mm IP> <mm Port> <Front-end UID> <FEMacro "
19 : "Name> <Macro Input args...> \n");
20 0 : fprintf(stderr,
21 : " ots_mm_udp_test <mm IP> <mm Port> <Sequence Name> <iterations> "
22 : "[[varName,value,step,max] ...]\n\n");
23 0 : fprintf(stderr,
24 : "\t If <Front-end UID> not specified, then the list of existing FE UIDs "
25 : "and MoM Sequences will be output \n");
26 0 : fprintf(stderr,
27 : "\t If <FEMacro Name> not specified, then the list of existing FE Macro "
28 : "Names will be output associated with the specified FE UID\n");
29 0 : fprintf(stderr,
30 : "\t If only a <Sequence Name> is given (no iterations), lists the "
31 : "sequence's commands and Input Variables with their saved defaults.\n");
32 0 : fprintf(stderr,
33 : "\t Variable overrides use bracket syntax, e.g. [num_of_packets,200], "
34 : "and are named. Step and max are optional (default 0).\n");
35 0 : fprintf(stderr, "\t The $ prefix on variable names is optional.\n");
36 0 : exit(1);
37 : }
38 :
39 0 : std::string target_mm_ip = argv[1];
40 0 : int target_mm_port = atoi(argv[2]);
41 :
42 : __COUTV__(target_mm_ip);
43 : __COUTV__(target_mm_port);
44 :
45 : __COUT__ << "instantiating..." << __E__;
46 0 : ots_mm_udp_interface mm(target_mm_ip.c_str(), target_mm_port);
47 :
48 0 : std::string feStr = mm.getFrontendList();
49 : __COUTV__(feStr);
50 0 : std::vector<std::string> fes = getVectorFromString(feStr, {';'});
51 0 : if(fes.size() == 0 || (fes.size() == 1 && fes[0] == ""))
52 : {
53 0 : __COUT_ERR__
54 : << "No Front-ends found in FE list! Check that a MacroMaker Supervisor is "
55 : << "configured with UDP Remote Control enabled and accessible at "
56 0 : << target_mm_ip << ":" << target_mm_port << "." << __E__;
57 0 : return 1;
58 : }
59 :
60 0 : uint32_t fe = -1;
61 0 : uint32_t cmd = -1;
62 :
63 0 : if(argc >= 4)
64 : {
65 : //look for FE
66 0 : std::string target_fe = argv[3];
67 0 : for(fe = 0; fe < fes.size(); ++fe)
68 0 : if(fes[fe] == target_fe)
69 0 : break;
70 :
71 0 : if(fe >= fes.size())
72 : {
73 : // not a FE UID — check if it is a MoM sequence name
74 0 : std::string sequenceListStr = mm.getSequenceList();
75 : std::vector<std::string> sequenceNames =
76 0 : getVectorFromString(sequenceListStr, {';'});
77 :
78 0 : std::string strippedTargetName = target_fe;
79 0 : if(strippedTargetName.find("public/") == 0)
80 : strippedTargetName =
81 0 : strippedTargetName.substr(std::string("public/").size());
82 :
83 0 : bool isSequenceMatch = false;
84 0 : for(size_t sequenceIndex = 0; sequenceIndex < sequenceNames.size();
85 : ++sequenceIndex)
86 : {
87 0 : std::string candidateName = sequenceNames[sequenceIndex];
88 0 : if(candidateName.find("public/") == 0)
89 0 : candidateName = candidateName.substr(std::string("public/").size());
90 0 : if(candidateName == strippedTargetName)
91 : {
92 0 : isSequenceMatch = true;
93 0 : break;
94 : }
95 0 : }
96 :
97 0 : if(isSequenceMatch)
98 : {
99 : // ======== Sequence mode ========
100 : // keep the user's exact name (including any public/ qualifier) so the
101 : // server resolves the intended directory
102 0 : std::string sequenceName = target_fe;
103 :
104 0 : if(argc == 4)
105 : {
106 : // Discovery mode — print commands and variables, then exit
107 0 : __COUT_INFO__ << "Loading sequence '" << sequenceName << "'..."
108 0 : << __E__;
109 :
110 0 : std::string sequenceJSON = mm.getSequenceInfo(sequenceName);
111 0 : std::string sequenceDescription = mm.describeSequence(sequenceJSON);
112 :
113 0 : __COUT_INFO__ << "\nSequence '" << sequenceName << "':\n"
114 0 : << sequenceDescription << __E__;
115 :
116 0 : __COUT_INFO__ << "To run: ots_mm_udp_test " << target_mm_ip << " "
117 : << target_mm_port << " \"" << sequenceName
118 0 : << "\" <iterations> [varName,value] ..." << __E__;
119 0 : __COUT_INFO__ << " e.g.: ots_mm_udp_test " << target_mm_ip << " "
120 : << target_mm_port << " \"" << sequenceName
121 0 : << "\" 1 [var1,0]" << __E__;
122 0 : return 0;
123 0 : }
124 :
125 : // Run mode — argv[4] is iteration count, argv[5..] are variable overrides
126 0 : unsigned int iterationCount = 0;
127 0 : if(!ots_mm_udp_interface::parseIterationCount(argv[4], iterationCount))
128 : {
129 0 : __COUT_ERR__
130 0 : << "Invalid iteration count: '" << argv[4]
131 0 : << "'. Must be a positive integer (digits only) no larger than "
132 0 : << std::numeric_limits<unsigned int>::max() << "." << __E__;
133 0 : return 1;
134 : }
135 :
136 : // Parse variable overrides from [value,step,max] arguments
137 : // First, load the sequence to get variable names in order
138 0 : std::string sequenceJSON = mm.getSequenceInfo(sequenceName);
139 :
140 : // Extract variable names in order from __variables__ block
141 : // (Same approach as discovery mode but just collecting names)
142 0 : std::vector<std::string> variableNamesInOrder;
143 : {
144 0 : size_t variablesKeyPosition = sequenceJSON.find("\"__variables__\"");
145 0 : if(variablesKeyPosition != std::string::npos)
146 : {
147 : size_t variablesObjectStart =
148 0 : sequenceJSON.find('{', variablesKeyPosition);
149 0 : if(variablesObjectStart != std::string::npos)
150 : {
151 0 : size_t scanPosition = variablesObjectStart + 1;
152 0 : int depth = 1;
153 0 : while(scanPosition < sequenceJSON.size() && depth > 0)
154 : {
155 0 : size_t nameStart = sequenceJSON.find('"', scanPosition);
156 0 : if(nameStart == std::string::npos)
157 0 : break;
158 :
159 : // check if we've left the __variables__ block
160 0 : bool insideQuote = false;
161 0 : bool leftBlock = false;
162 0 : for(size_t checkPos = variablesObjectStart + 1;
163 0 : checkPos < nameStart;
164 : ++checkPos)
165 : {
166 0 : if(sequenceJSON[checkPos] == '\\' && insideQuote)
167 : {
168 0 : ++checkPos;
169 0 : continue;
170 : }
171 0 : if(sequenceJSON[checkPos] == '"')
172 0 : insideQuote = !insideQuote;
173 0 : if(!insideQuote)
174 : {
175 0 : if(sequenceJSON[checkPos] == '{')
176 0 : ++depth;
177 0 : else if(sequenceJSON[checkPos] == '}')
178 : {
179 0 : --depth;
180 0 : if(depth <= 0)
181 : {
182 0 : leftBlock = true;
183 0 : break;
184 : }
185 : }
186 : }
187 : }
188 0 : if(leftBlock)
189 0 : break;
190 :
191 0 : size_t nameEnd = std::string::npos;
192 0 : for(size_t i = nameStart + 1; i < sequenceJSON.size();
193 : ++i)
194 : {
195 0 : if(sequenceJSON[i] == '\\')
196 : {
197 0 : ++i;
198 0 : continue;
199 : }
200 0 : if(sequenceJSON[i] == '"')
201 : {
202 0 : nameEnd = i;
203 0 : break;
204 : }
205 : }
206 0 : if(nameEnd == std::string::npos)
207 0 : break;
208 :
209 : std::string variableName = sequenceJSON.substr(
210 0 : nameStart + 1, nameEnd - nameStart - 1);
211 :
212 : // skip past the value object {value, step, max}
213 0 : size_t objectStart = sequenceJSON.find('{', nameEnd + 1);
214 0 : if(objectStart == std::string::npos)
215 0 : break;
216 0 : int objectDepth = 1;
217 0 : bool objectQuote = false;
218 0 : size_t objectEnd = std::string::npos;
219 0 : for(size_t i = objectStart + 1; i < sequenceJSON.size();
220 : ++i)
221 : {
222 0 : if(sequenceJSON[i] == '\\' && objectQuote)
223 : {
224 0 : ++i;
225 0 : continue;
226 : }
227 0 : if(sequenceJSON[i] == '"')
228 0 : objectQuote = !objectQuote;
229 0 : if(!objectQuote)
230 : {
231 0 : if(sequenceJSON[i] == '{')
232 0 : ++objectDepth;
233 0 : else if(sequenceJSON[i] == '}')
234 : {
235 0 : --objectDepth;
236 0 : if(objectDepth == 0)
237 : {
238 0 : objectEnd = i;
239 0 : break;
240 : }
241 : }
242 : }
243 : }
244 0 : if(objectEnd == std::string::npos)
245 0 : break;
246 :
247 0 : variableNamesInOrder.push_back(variableName);
248 0 : scanPosition = objectEnd + 1;
249 0 : }
250 : }
251 : }
252 : }
253 :
254 : // Parse [$varName,value,step,max] arguments from argv[5..argc-1]
255 0 : unsigned int numberOfOverrides = argc - 5;
256 :
257 : // Build variablesJSON from overrides
258 0 : std::string variablesJSON;
259 0 : if(numberOfOverrides > 0)
260 : {
261 0 : variablesJSON = "{";
262 0 : unsigned int validOverrideCount = 0;
263 0 : for(unsigned int overrideIndex = 0; overrideIndex < numberOfOverrides;
264 : ++overrideIndex)
265 : {
266 0 : std::string overrideArg = argv[5 + overrideIndex];
267 :
268 : // Strip brackets
269 0 : if(overrideArg.size() >= 2 && overrideArg[0] == '[' &&
270 0 : overrideArg[overrideArg.size() - 1] == ']')
271 0 : overrideArg = overrideArg.substr(1, overrideArg.size() - 2);
272 :
273 : std::vector<std::string> overrideParts =
274 0 : getVectorFromString(overrideArg, {','}, {} /*whitespace*/);
275 :
276 0 : if(overrideParts.size() < 2)
277 : {
278 0 : __COUT_ERR__ << "Invalid variable override: '"
279 0 : << argv[5 + overrideIndex]
280 0 : << "'. Expected [varName,value,step,max]."
281 0 : << __E__;
282 0 : return 1;
283 : }
284 :
285 0 : std::string overrideName = overrideParts[0];
286 0 : if(!overrideName.empty() && overrideName[0] != '$')
287 0 : overrideName = "$" + overrideName;
288 : std::string overrideValue =
289 0 : overrideParts.size() > 1 ? overrideParts[1] : "0";
290 : std::string overrideStep =
291 0 : overrideParts.size() > 2 ? overrideParts[2] : "0";
292 : std::string overrideMax =
293 0 : overrideParts.size() > 3 ? overrideParts[3] : "0";
294 :
295 : // Verify the variable name exists in the sequence
296 0 : bool foundVariable = false;
297 0 : for(size_t variableIndex = 0;
298 0 : variableIndex < variableNamesInOrder.size();
299 : ++variableIndex)
300 : {
301 0 : if(variableNamesInOrder[variableIndex] == overrideName)
302 : {
303 0 : foundVariable = true;
304 0 : break;
305 : }
306 : }
307 0 : if(!foundVariable)
308 : {
309 0 : __COUT_ERR__ << "Unknown variable '" << overrideName
310 0 : << "'. Available variables:" << __E__;
311 0 : for(size_t variableIndex = 0;
312 0 : variableIndex < variableNamesInOrder.size();
313 : ++variableIndex)
314 0 : __COUT_ERR__ << "\t"
315 0 : << variableNamesInOrder[variableIndex]
316 0 : << __E__;
317 0 : return 1;
318 : }
319 :
320 0 : if(validOverrideCount > 0)
321 0 : variablesJSON += ",";
322 : variablesJSON +=
323 0 : "\"" + ots_mm_udp_interface::escapeJsonString(overrideName) +
324 0 : "\":{\"value\":\"" +
325 0 : ots_mm_udp_interface::escapeJsonString(overrideValue) +
326 0 : "\",\"step\":\"" +
327 0 : ots_mm_udp_interface::escapeJsonString(overrideStep) +
328 0 : "\",\"max\":\"" +
329 0 : ots_mm_udp_interface::escapeJsonString(overrideMax) + "\"}";
330 0 : ++validOverrideCount;
331 0 : }
332 0 : variablesJSON += "}";
333 : }
334 :
335 0 : __COUT_INFO__ << "Running sequence '" << sequenceName << "' for "
336 0 : << iterationCount << " iteration(s)" << __E__;
337 :
338 0 : if(numberOfOverrides > 0)
339 : {
340 0 : __COUT_INFO__ << "Variable overrides:" << __E__;
341 0 : for(unsigned int overrideIndex = 0; overrideIndex < numberOfOverrides;
342 : ++overrideIndex)
343 0 : __COUT_INFO__ << "\t" << argv[5 + overrideIndex] << __E__;
344 : }
345 :
346 0 : mm.runSequence(sequenceName, variablesJSON, iterationCount);
347 :
348 0 : __COUT_INFO__ << "Done." << __E__;
349 0 : return 0;
350 0 : }
351 :
352 : // neither FE UID nor sequence name
353 0 : __COUT_ERR__ << "'" << target_fe
354 0 : << "' not found as a front-end UID or MoM sequence name."
355 0 : << __E__;
356 0 : __COUT_INFO__ << "\nAvailable Front-end UIDs:" << __E__;
357 0 : for(size_t feIndex = 0; feIndex < fes.size(); ++feIndex)
358 0 : __COUT_INFO__ << "\t\t" << fes[feIndex] << __E__;
359 :
360 0 : __COUT_INFO__ << "\nAvailable MoM Sequences:" << __E__;
361 0 : for(size_t sequenceIndex = 0; sequenceIndex < sequenceNames.size();
362 : ++sequenceIndex)
363 : {
364 0 : if(sequenceNames[sequenceIndex].empty() ||
365 0 : sequenceNames[sequenceIndex] == "." ||
366 0 : sequenceNames[sequenceIndex] == "..")
367 0 : continue;
368 0 : __COUT_INFO__ << "\t\t" << sequenceNames[sequenceIndex] << __E__;
369 : }
370 0 : return 1;
371 0 : }
372 0 : }
373 : else
374 : {
375 0 : __COUT_INFO__
376 0 : << "No front-end UID specified, here is the list of existing Front-end UIDs:"
377 0 : << __E__;
378 0 : for(fe = 0; fe < fes.size(); ++fe)
379 0 : __COUT_INFO__ << "\t\t" << fes[fe] << __E__;
380 :
381 : // Also list available sequences
382 : try
383 : {
384 0 : std::string sequenceListStr = mm.getSequenceList();
385 : std::vector<std::string> sequenceNames =
386 0 : getVectorFromString(sequenceListStr, {';'});
387 :
388 0 : bool hasSequences = false;
389 0 : for(size_t sequenceIndex = 0; sequenceIndex < sequenceNames.size();
390 : ++sequenceIndex)
391 : {
392 0 : if(sequenceNames[sequenceIndex].empty() ||
393 0 : sequenceNames[sequenceIndex] == "." ||
394 0 : sequenceNames[sequenceIndex] == "..")
395 0 : continue;
396 0 : if(!hasSequences)
397 : {
398 0 : __COUT_INFO__ << "\nAvailable MoM Sequences:" << __E__;
399 0 : hasSequences = true;
400 : }
401 0 : __COUT_INFO__ << "\t\t" << sequenceNames[sequenceIndex] << __E__;
402 : }
403 0 : }
404 0 : catch(const std::runtime_error& sequenceListError)
405 : {
406 0 : __COUT_WARN__ << "Could not retrieve sequence list: "
407 0 : << sequenceListError.what() << __E__;
408 0 : }
409 :
410 0 : return 0;
411 : }
412 :
413 0 : if(fe >= fes.size())
414 : {
415 0 : __COUT_ERR__ << "Illegal front-end index " << fe << " vs " << fes.size()
416 0 : << " count." << __E__;
417 0 : return 1;
418 : }
419 :
420 : __COUTV__(fes[fe]);
421 0 : std::string commandStr = mm.getCommandList(fes[fe]);
422 : __COUTV__(commandStr);
423 :
424 0 : std::vector<std::string> commands = getVectorFromString(commandStr, {';'});
425 :
426 0 : if(argc >= 5)
427 : {
428 : //look for command
429 0 : std::string target_command = argv[4];
430 0 : for(cmd = 0; cmd < commands.size(); ++cmd)
431 0 : if(commands[cmd] == target_command)
432 0 : break;
433 0 : if(cmd >= commands.size())
434 : {
435 0 : __COUT_ERR__ << "Illegal Front-end Macro Command name '" << target_command
436 0 : << "' not found in '" << fes[fe]
437 0 : << "' Command list: " << vectorToString(commands, {';'}) << "."
438 0 : << __E__;
439 0 : return 1;
440 : }
441 0 : }
442 : else
443 : {
444 0 : __COUT_INFO__ << "No FE Macro Name specified, here is the list of existing FE "
445 : "Macro Names corresponding to the specified Front-end UID '"
446 0 : << argv[3] << "':" << __E__;
447 0 : for(cmd = 0; cmd < commands.size(); ++cmd)
448 0 : __COUT_INFO__ << "\t\t" << commands[cmd] << __E__;
449 :
450 0 : return 0;
451 : }
452 :
453 0 : if(cmd >= commands.size())
454 : {
455 0 : __COUT_ERR__ << "Illegal command index " << cmd << " vs " << commands.size()
456 0 : << " count." << __E__;
457 0 : return 1;
458 : }
459 :
460 : __COUTV__(commands[cmd]);
461 :
462 0 : uint32_t numberOfInputs = mm.getCommandInputCount(fes[fe], commands[cmd]);
463 : __COUTV__(numberOfInputs);
464 :
465 0 : uint32_t numberOfOutputs = mm.getCommandOutputCount(fes[fe], commands[cmd]);
466 : __COUTV__(numberOfOutputs);
467 :
468 0 : std::vector<std::string> inputs;
469 0 : if(argc != int(5 + numberOfInputs))
470 : {
471 0 : __COUT_WARN__
472 : << "Incorrect number of inputs provided for Front-end Macro Command name '"
473 0 : << commands[cmd] << "' on FE UID '" << fes[fe] << "': " << argc - 5 << " vs "
474 0 : << numberOfInputs << " expected." << __E__;
475 0 : __COUT_INFO__ << "Will not run command." << __E__;
476 :
477 0 : for(uint32_t i = 0; i < numberOfInputs; ++i)
478 : {
479 0 : std::string inputName = mm.getCommandInputName(fes[fe], commands[cmd], i);
480 0 : __COUT_INFO__ << "\tExpected input #" << i + 1 << ": " << inputName << __E__;
481 0 : }
482 0 : return 0;
483 : }
484 :
485 : __COUTV__(vectorToString(inputs, {';'}));
486 0 : for(uint32_t i = 0; i < numberOfInputs; ++i)
487 : {
488 0 : inputs.push_back(ots_mm_udp_interface::encodeURIComponent(argv[5 + i]));
489 0 : std::string inputName = mm.getCommandInputName(fes[fe], commands[cmd], i);
490 0 : __COUT_INFO__ << "\tInput #" << i << ": " << inputName << " = "
491 0 : << ots_mm_udp_interface::decodeURIComponent(inputs.back()) << __E__;
492 0 : }
493 :
494 0 : __COUT_INFO__ << "Running command with these expected outputs..." << __E__;
495 0 : for(uint32_t i = 0; i < numberOfOutputs; ++i)
496 : {
497 0 : std::string outputName = mm.getCommandOutputName(fes[fe], commands[cmd], i);
498 0 : __COUT_INFO__ << "\tOutput #" << i << ": " << outputName << __E__;
499 0 : }
500 :
501 : std::string commandResult =
502 0 : mm.runCommand(fes[fe], commands[cmd], vectorToString(inputs, {';'}));
503 :
504 0 : std::vector<std::string> outputs = getVectorFromString(commandResult, {';'});
505 :
506 0 : if(numberOfOutputs != outputs.size())
507 : {
508 : __COUTV__(commandResult);
509 0 : __COUT_ERR__ << "Illegal response, mismatch in output arguments returned: "
510 0 : << outputs.size() << " vs " << numberOfOutputs << " expected."
511 0 : << __E__;
512 0 : return 1;
513 : }
514 :
515 : __COUTV__(vectorToString(outputs, {';'}));
516 0 : for(uint32_t i = 0; i < numberOfOutputs; ++i)
517 : {
518 0 : std::string outputName = mm.getCommandOutputName(fes[fe], commands[cmd], i);
519 : __COUT__ << "\tOutput #" << i << ": " << outputName << " = "
520 : << mm.decodeURIComponent(outputs[i]) << __E__;
521 0 : }
522 0 : __COUT_INFO__ << "Done." << __E__;
523 0 : return 0;
524 0 : }
525 0 : catch(const std::runtime_error& e)
526 : {
527 0 : __COUT_ERR__ << "Error caught during test execution: \n" << e.what() << __E__;
528 0 : return 1;
529 0 : }
|