Line data Source code
1 :
2 : #include "test/ots_mm_udp_interface.h"
3 :
4 : #include <chrono>
5 :
6 : #define MAXBUFLEN 5000
7 :
8 : //==============================================================================
9 0 : bool hasCompleteXmlRoot(const std::string& xml)
10 : {
11 0 : return xml.size() >= 10 &&
12 0 : xml.substr(xml.size() - 10).find("</ROOT>") != std::string::npos;
13 : } //end hasCompleteXmlRoot()
14 :
15 : //==============================================================================
16 : /// get sockaddr, IPv4 or IPv6:
17 0 : void* get_in_addr(struct sockaddr* sa)
18 : {
19 0 : if(sa->sa_family == AF_INET)
20 : {
21 0 : return &(((struct sockaddr_in*)sa)->sin_addr);
22 : }
23 :
24 0 : return &(((struct sockaddr_in6*)sa)->sin6_addr);
25 : }
26 :
27 : //==============================================================================
28 0 : int makeSocket(const char* ip, int port, struct sockaddr_in& mm_ai_addr)
29 : {
30 : int sockfd;
31 : struct addrinfo hints, *servinfo, *p;
32 : int rv;
33 : // int numbytes;
34 : // struct sockaddr_storage their_addr;
35 : // socklen_t addr_len;
36 : // char s[INET6_ADDRSTRLEN];
37 :
38 0 : memset(&hints, 0, sizeof hints);
39 0 : hints.ai_family = AF_UNSPEC;
40 0 : hints.ai_socktype = SOCK_DGRAM;
41 :
42 0 : if((rv = getaddrinfo(ip, std::to_string(port).c_str(), &hints, &servinfo)) != 0)
43 : {
44 0 : __SS__ << "getaddrinfo: " << gai_strerror(rv) << __E__;
45 0 : __SS_THROW__;
46 0 : }
47 :
48 : // loop through all the results and make a socket
49 0 : for(p = servinfo; p != NULL; p = p->ai_next)
50 : {
51 0 : if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
52 : {
53 0 : __COUT_WARN__ << "sw: socket failed, trying other address..." << __E__;
54 0 : continue;
55 : }
56 :
57 0 : break;
58 : }
59 :
60 0 : if(p == NULL)
61 : {
62 0 : __SS__ << "sw: failed to create socket" << __E__;
63 0 : __SS_THROW__;
64 0 : }
65 :
66 : //copy ai_addr, which is needed for sendto
67 0 : memcpy(&mm_ai_addr, p->ai_addr, sizeof(mm_ai_addr));
68 :
69 0 : freeaddrinfo(servinfo);
70 :
71 : // increase socket buffer size
72 0 : unsigned int socketReceiveBufferSize = 0x1400000;
73 0 : if(setsockopt(sockfd,
74 : SOL_SOCKET,
75 : SO_RCVBUF,
76 : (char*)&socketReceiveBufferSize,
77 0 : sizeof(socketReceiveBufferSize)) < 0)
78 0 : __COUT_ERR__ << "Failed to set socket receive size to 0x" << std::hex
79 0 : << socketReceiveBufferSize << std::dec << "." << std::endl;
80 : else
81 : __COUT__ << "set socket receive size to 0x" << std::hex << socketReceiveBufferSize
82 : << std::dec << "." << __E__;
83 :
84 0 : return sockfd;
85 : } //end makeSocket()
86 :
87 : //==============================================================================
88 : /// receive ~~
89 : /// returns numberOfBytes on success, -1 on failure
90 : fd_set fileDescriptor_;
91 : struct timeval timeout_;
92 : struct sockaddr_in fromAddress_;
93 : socklen_t addressLength_ = sizeof(fromAddress_);
94 0 : int receive(int sockfd,
95 : std::string& buffer,
96 : unsigned int timeoutSeconds,
97 : unsigned int timeoutUSeconds,
98 : bool verbose)
99 : {
100 : unsigned long fromIPAddress;
101 : unsigned short fromPort;
102 :
103 : // set timeout period for select()
104 0 : timeout_.tv_sec = timeoutSeconds;
105 0 : timeout_.tv_usec = timeoutUSeconds;
106 :
107 0 : FD_ZERO(&fileDescriptor_);
108 0 : FD_SET(sockfd, &fileDescriptor_);
109 0 : select(sockfd + 1, &fileDescriptor_, 0, 0, &timeout_);
110 :
111 : int numberOfBytes;
112 0 : if(FD_ISSET(sockfd, &fileDescriptor_))
113 : {
114 0 : buffer.resize(MAXBUFLEN); // NOTE: this is inexpensive according to
115 : // Lorenzo/documentation in C++11 (only increases
116 : // size once and doesn't decrease size)
117 0 : if((numberOfBytes = recvfrom(sockfd,
118 0 : &buffer[0],
119 : MAXBUFLEN,
120 : 0,
121 : (struct sockaddr*)&fromAddress_,
122 0 : &addressLength_)) == -1)
123 : {
124 0 : __SS__ << "Error reading buffer from\tIP:\t";
125 0 : std::string fromIP = inet_ntoa(fromAddress_.sin_addr);
126 0 : fromIPAddress = fromAddress_.sin_addr.s_addr;
127 0 : fromPort = fromAddress_.sin_port;
128 :
129 0 : for(int i = 0; i < 4; i++)
130 : {
131 0 : ss << ((fromIPAddress << (i * 8)) & 0xff);
132 0 : if(i < 3)
133 0 : ss << ".";
134 : }
135 0 : ss << "\tPort\t" << ntohs(fromPort) << " IP " << fromIP << std::endl;
136 : __COUT__ << "\n" << ss.str();
137 0 : return -1;
138 0 : }
139 : // char address[INET_ADDRSTRLEN];
140 : // inet_ntop(AF_INET, &(fromAddress.sin_addr), address, INET_ADDRSTRLEN);
141 0 : fromIPAddress = fromAddress_.sin_addr.s_addr;
142 0 : fromPort = fromAddress_.sin_port;
143 :
144 : //__COUT__ << __PRETTY_FUNCTION__ << "IP: " << std::hex << fromIPAddress <<
145 : // std::dec << " port: " << fromPort << std::endl;
146 : //__COUT__ << "Socket Number: " << socketNumber_ << " number of bytes: " <<
147 : // nOfBytes << std::endl; gettimeofday(&tvend,NULL);
148 : //__COUT__ << "started at" << tvbegin.tv_sec << ":" <<tvbegin.tv_usec <<
149 : // std::endl;
150 : //__COUT__ << "ended at" << tvend.tv_sec << ":" <<tvend.tv_usec << std::endl;
151 :
152 : // NOTE: this is inexpensive according to Lorenzo/documentation in C++11 (only
153 : // increases size once and doesn't decrease size)
154 0 : buffer.resize(numberOfBytes);
155 :
156 0 : if(verbose) // debug
157 : {
158 0 : std::string fromIP = inet_ntoa(fromAddress_.sin_addr);
159 :
160 : __COUT__ << "Receiving from: " << fromIP << ":" << ntohs(fromPort)
161 : << " size: " << buffer.size() << std::endl;
162 :
163 : // std::stringstream ss;
164 : // ss << "\tRx";
165 : // uint32_t begin = 0;
166 : // for(uint32_t i=begin; i<buffer.size(); i++)
167 : // {
168 : // if(i==begin+2) ss << ":::";
169 : // else if(i==begin+10) ss << ":::";
170 : // ss << std::setfill('0') << std::setw(2) << std::hex <<
171 : //(((int16_t) buffer[i]) &0xFF) << "-" << std::dec;
172 : // }
173 : // ss << std::endl;
174 : // std::cout << ss.str();
175 0 : }
176 : }
177 : else
178 : {
179 : if(verbose)
180 : __COUT__ << "No new messages for "
181 : << timeoutSeconds + timeoutUSeconds / 1000000.
182 : << "s. Read request timed out." << std::endl;
183 0 : return -1;
184 : }
185 :
186 0 : return 0;
187 : } //end receive()
188 :
189 : //==============================================================================
190 0 : ots_mm_udp_interface::ots_mm_udp_interface(const char* mm_ip, int mm_port) : mm_sock_(-1)
191 : /// , mm_p_(nullptr)
192 : /// , mm_servinfo_(nullptr)
193 : {
194 : __COUT__ << "Constructor" << __E__;
195 :
196 0 : mm_sock_ = makeSocket(mm_ip, mm_port, mm_ai_addr); // mm_p_);
197 :
198 0 : selfIPandPort_ = mm_ip;
199 0 : selfIPandPort_ += ":";
200 0 : selfIPandPort_ += std::to_string(mm_port);
201 0 : } //end constructor()
202 :
203 : //==============================================================================
204 0 : ots_mm_udp_interface::~ots_mm_udp_interface()
205 : {
206 : __COUT__ << "Destructor" << __E__;
207 :
208 0 : if(mm_sock_ != -1)
209 0 : close(mm_sock_);
210 :
211 : // if(mm_servinfo_)
212 : // freeaddrinfo(mm_servinfo_);
213 0 : } //end destructor()
214 :
215 : //==============================================================================
216 0 : void ots_mm_udp_interface::receiveXmlResponse(std::string& response,
217 : const std::string& waitDescription,
218 : int inactivityTimeoutSeconds)
219 : {
220 0 : response.clear();
221 0 : auto startTime = std::chrono::steady_clock::now();
222 0 : auto lastUpdateTime = std::chrono::steady_clock::now();
223 0 : int64_t lastWarnElapsedTime = 0;
224 :
225 : while(true)
226 : {
227 0 : if(receive(mm_sock_,
228 0 : buffer_,
229 : 0 /*timeoutSeconds*/,
230 : 200000 /*timeoutUSeconds*/,
231 0 : false /*verbose*/) == 0)
232 : {
233 0 : lastUpdateTime = std::chrono::steady_clock::now();
234 0 : if(buffer_.find("<progress>") == 0)
235 : {
236 0 : __COUT_INFO__ << "Progress update: " << buffer_ << __E__;
237 0 : continue;
238 : }
239 :
240 0 : response += buffer_;
241 : // __COUT_INFO__ << "Received: " << buffer_ << __E__;
242 0 : if(hasCompleteXmlRoot(response))
243 0 : break;
244 : }
245 :
246 0 : auto currentTime = std::chrono::steady_clock::now();
247 : auto elapsed =
248 0 : std::chrono::duration_cast<std::chrono::seconds>(currentTime - startTime);
249 0 : if(lastWarnElapsedTime != elapsed.count() && elapsed.count() > 2 &&
250 0 : elapsed.count() % 3 == 0)
251 : {
252 0 : __COUT_WARN__ << "Still waiting for " << waitDescription << " after "
253 0 : << elapsed.count() << " seconds... " << response.size()
254 0 : << " bytes received so far." << __E__;
255 0 : lastWarnElapsedTime = elapsed.count();
256 : }
257 :
258 0 : elapsed = std::chrono::duration_cast<std::chrono::seconds>(currentTime -
259 0 : lastUpdateTime);
260 0 : if(elapsed.count() > inactivityTimeoutSeconds)
261 0 : break;
262 0 : }
263 0 : } //end receiveXmlResponse()
264 :
265 : //=========================================================================
266 : ///extract value for field from xml looking forwards from after
267 : /// occurence = 0 is first occurence
268 0 : std::string extractXmlField(const std::string& xml,
269 : const std::string& field,
270 : uint32_t occurrence,
271 : size_t after,
272 : size_t* returnAfter)
273 : {
274 0 : size_t lo = after, hi;
275 0 : for(uint32_t i = 0; i <= occurrence; ++i)
276 0 : if((lo = xml.find("<" + field + " ", lo)) == std::string::npos)
277 0 : return "";
278 0 : if((hi = xml.find("'/>", lo)) == std::string::npos)
279 0 : return "";
280 :
281 0 : lo = xml.find("value='", lo) + 7;
282 :
283 0 : if(returnAfter)
284 0 : *returnAfter = hi + 3;
285 :
286 0 : return xml.substr(lo, hi - lo);
287 : } //end extractXmlField()
288 :
289 : //=========================================================================
290 : ///extract value for field from xml looking backwards from before
291 : /// occurence = 0 is first occurence
292 0 : std::string rextractXmlField(const std::string& xml,
293 : const std::string& field,
294 : uint32_t occurrence,
295 : size_t before)
296 : {
297 0 : size_t lo = 0, hi = before;
298 0 : for(uint32_t i = 0; i <= occurrence; ++i)
299 0 : if((lo = xml.rfind("<" + field + " ", hi)) == std::string::npos)
300 0 : return "";
301 0 : if((hi = xml.rfind("'/>", hi)) == std::string::npos)
302 0 : return "";
303 :
304 0 : lo = xml.find("value='", lo) + 7;
305 :
306 0 : return xml.substr(lo, hi - lo);
307 : } //end rextractXmlField()
308 :
309 : //==============================================================================
310 : /// getVectorFromString
311 : /// extracts the list of elements from string that uses a delimiter
312 : /// ignoring whitespace
313 : /// optionally returns the list of delimiters encountered, which may be useful
314 : /// for extracting which operator was used.
315 : ///
316 : ///
317 : /// Note: lists are returned as vectors
318 : /// Note: the size() of delimiters will be one less than the size() of the returned values
319 : /// unless there is a leading delimiter, in which case vectors will have the same
320 : /// size.
321 0 : void getVectorFromString(const std::string& inputString,
322 : std::vector<std::string>& listToReturn,
323 : const std::set<char>& delimiter,
324 : const std::set<char>& whitespace,
325 : std::vector<char>* listOfDelimiters,
326 : bool decodeURIComponents)
327 : {
328 0 : unsigned int i = 0;
329 0 : unsigned int j = 0;
330 0 : unsigned int c = 0;
331 0 : std::set<char>::iterator delimeterSearchIt;
332 0 : char lastDelimiter = 0;
333 : bool isDelimiter;
334 : // bool foundLeadingDelimiter = false;
335 :
336 : //__COUT__ << inputString << __E__;
337 : //__COUTV__(inputString.length());
338 :
339 : // go through the full string extracting elements
340 : // add each found element to set
341 0 : for(; c < inputString.size(); ++c)
342 : {
343 : //__COUT__ << (char)inputString[c] << __E__;
344 :
345 0 : delimeterSearchIt = delimiter.find(inputString[c]);
346 0 : isDelimiter = delimeterSearchIt != delimiter.end();
347 :
348 : //__COUT__ << (char)inputString[c] << " " << isDelimiter <<
349 : //__E__;//char)lastDelimiter << __E__;
350 :
351 0 : if(whitespace.find(inputString[c]) !=
352 0 : whitespace.end() // ignore leading white space
353 0 : && i == j)
354 : {
355 0 : ++i;
356 0 : ++j;
357 : // if(isDelimiter)
358 : // foundLeadingDelimiter = true;
359 : }
360 0 : else if(whitespace.find(inputString[c]) != whitespace.end() &&
361 : i != j) // trailing white space, assume possible end of element
362 : {
363 : // do not change j or i
364 : }
365 0 : else if(isDelimiter) // delimiter is end of element
366 : {
367 : // __COUT__ << "Set element found: " << i << "-" << j << " of " << inputString.size() << __E__;
368 : // inputString.substr(i,j-i) << std::endl;
369 :
370 0 : if(listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
371 : // //accept leading delimiter
372 : // (especially for case of
373 : // leading negative in math
374 : // parsing)
375 : {
376 : //__COUTV__(lastDelimiter);
377 0 : listOfDelimiters->push_back(lastDelimiter);
378 : }
379 :
380 0 : listToReturn.push_back(decodeURIComponents
381 0 : ? ots_mm_udp_interface::decodeURIComponent(
382 0 : inputString.substr(i, j - i))
383 0 : : inputString.substr(i, j - i));
384 :
385 : // setup i and j for next find
386 0 : i = c + 1;
387 0 : j = c + 1;
388 : }
389 : else // part of element, so move j, not i
390 0 : j = c + 1;
391 :
392 0 : if(isDelimiter)
393 0 : lastDelimiter = *delimeterSearchIt;
394 : //__COUTV__(lastDelimiter);
395 : }
396 :
397 : if(1) // i != j) //last element check (for case when no concluding ' ' or delimiter)
398 : {
399 : //__COUT__ << "Last element found: " <<
400 : // inputString.substr(i,j-i) << std::endl;
401 :
402 0 : if(listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
403 : // //accept leading delimiter
404 : // (especially for case of leading
405 : // negative in math parsing)
406 : {
407 : //__COUTV__(lastDelimiter);
408 0 : listOfDelimiters->push_back(lastDelimiter);
409 : }
410 0 : listToReturn.push_back(
411 : decodeURIComponents
412 0 : ? ots_mm_udp_interface::decodeURIComponent(inputString.substr(i, j - i))
413 0 : : inputString.substr(i, j - i));
414 : }
415 :
416 : // assert that there is one less delimiter than values
417 0 : if(listOfDelimiters && listToReturn.size() - 1 != listOfDelimiters->size() &&
418 0 : listToReturn.size() != listOfDelimiters->size())
419 : {
420 0 : __SS__
421 : << "There is a mismatch in delimiters to entries (should be equal or one "
422 0 : "less delimiter): "
423 0 : << listOfDelimiters->size() << " vs " << listToReturn.size()
424 0 : << __E__; // << "Entries: " << StringMacros::vectorToString(listToReturn) << __E__
425 : //<< "Delimiters: " << StringMacros::vectorToString(*listOfDelimiters) << __E__;
426 0 : __SS_THROW__;
427 0 : }
428 :
429 0 : } // end getVectorFromString()
430 :
431 : //==============================================================================
432 : /// getVectorFromString
433 : /// extracts the list of elements from string that uses a delimiter
434 : /// ignoring whitespace
435 : /// optionally returns the list of delimiters encountered, which may be useful
436 : /// for extracting which operator was used.
437 : ///
438 : ///
439 : /// Note: lists are returned as vectors
440 : /// Note: the size() of delimiters will be one less than the size() of the returned values
441 : /// unless there is a leading delimiter, in which case vectors will have the same
442 : /// size.
443 0 : std::vector<std::string> getVectorFromString(const std::string& inputString,
444 : const std::set<char>& delimiter,
445 : const std::set<char>& whitespace,
446 : std::vector<char>* listOfDelimiters,
447 : bool decodeURIComponents)
448 : {
449 0 : std::vector<std::string> listToReturn;
450 :
451 0 : getVectorFromString(inputString,
452 : listToReturn,
453 : delimiter,
454 : whitespace,
455 : listOfDelimiters,
456 : decodeURIComponents);
457 0 : return listToReturn;
458 0 : } // end getVectorFromString()
459 :
460 : //==============================================================================
461 0 : std::string vectorToString(const std::vector<std::string>& setToReturn,
462 : const std::string& delimeter /*= ", "*/)
463 : {
464 0 : std::stringstream ss;
465 0 : bool first = true;
466 0 : for(auto& setValue : setToReturn)
467 : {
468 0 : if(first)
469 0 : first = false;
470 : else
471 0 : ss << delimeter;
472 0 : ss << setValue;
473 : }
474 0 : return ss.str();
475 0 : } // end vectorToString()
476 :
477 : //==============================================================================
478 : /// decodeURIComponent
479 : /// converts all %## to the ascii character
480 0 : std::string ots_mm_udp_interface::decodeURIComponent(const std::string& data)
481 : {
482 0 : std::string decodeURIString(data.size(), 0); // init to same size
483 0 : unsigned int j = 0;
484 0 : for(unsigned int i = 0; i < data.size(); ++i, ++j)
485 : {
486 0 : if(data[i] == '%')
487 : {
488 : // high order hex nibble digit
489 0 : if(data[i + 1] > '9') // then ABCDEF
490 0 : decodeURIString[j] += (data[i + 1] - 55) * 16;
491 : else
492 0 : decodeURIString[j] += (data[i + 1] - 48) * 16;
493 :
494 : // low order hex nibble digit
495 0 : if(data[i + 2] > '9') // then ABCDEF
496 0 : decodeURIString[j] += (data[i + 2] - 55);
497 : else
498 0 : decodeURIString[j] += (data[i + 2] - 48);
499 :
500 0 : i += 2; // skip to next char
501 : }
502 : else
503 0 : decodeURIString[j] = data[i];
504 : }
505 0 : decodeURIString.resize(j);
506 0 : return decodeURIString;
507 0 : } // end decodeURIComponent()
508 :
509 : //==============================================================================
510 0 : std::string ots_mm_udp_interface::encodeURIComponent(const std::string& sourceStr)
511 : {
512 0 : std::string retStr = "";
513 : char encodeStr[4];
514 0 : for(const auto& c : sourceStr)
515 0 : if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
516 0 : retStr += c;
517 : else
518 : {
519 0 : sprintf(encodeStr, "%%%2.2X", (uint8_t)c);
520 0 : retStr += encodeStr;
521 : }
522 0 : return retStr;
523 0 : } // end encodeURIComponent()
524 :
525 : //==============================================================================
526 0 : std::string ots_mm_udp_interface::decodeHTMLEntities(const std::string& sourceStr)
527 : {
528 0 : std::string retStr = sourceStr;
529 : std::vector<std::string> htmlSubstrings(
530 0 : {"<", ">", "&", """, "'", " ", "
"});
531 0 : std::vector<std::string> htmlReplaces({"<", ">", "&", "\"", "'", " ", "\n"});
532 0 : for(size_t i = 0; i < htmlSubstrings.size(); ++i)
533 : {
534 0 : size_t index = 0;
535 0 : while((index = retStr.find(htmlSubstrings[i], index)) != std::string::npos)
536 : {
537 0 : retStr.replace(index, htmlSubstrings[i].size(), htmlReplaces[i]);
538 0 : index += htmlReplaces[i].size(); //advance search location
539 : }
540 : }
541 0 : return retStr;
542 0 : } // end decodeHTMLEntities()
543 :
544 : //==============================================================================
545 : ///returns CSV list of Front-end interface UIDs
546 0 : const std::string& ots_mm_udp_interface::getFrontendMacroInfo()
547 : {
548 : __COUT__ << "getFrontendMacroInfo()" << __E__;
549 0 : if(fullXML_.size())
550 0 : return fullXML_;
551 :
552 : int numbytes;
553 :
554 0 : std::string sendMessage = "GetFrontendMacroInfo";
555 :
556 0 : if((numbytes = sendto(mm_sock_,
557 0 : &(sendMessage.c_str()[0]),
558 : sendMessage.size(),
559 : 0,
560 0 : (struct sockaddr*)&mm_ai_addr,
561 0 : sizeof(mm_ai_addr))) == -1)
562 : {
563 0 : __SS__ << "Error on getFrontendMacroInfo() sendto!" << __E__;
564 0 : __SS_THROW__;
565 0 : }
566 :
567 : // __COUTV__(numbytes);
568 :
569 : // read response ///////////////////////////////////////////////////////////
570 0 : receiveXmlResponse(fullXML_, "FE Macro Info response", 5);
571 :
572 0 : if(fullXML_.size() == 0)
573 : {
574 0 : __SS__ << "FE Macro Info receive failed! Check that a MacroMaker Supervisor is "
575 : "configured with UDP Remote Control enabled and accessible at "
576 0 : << selfIPandPort_ << "." << __E__;
577 0 : __SS_THROW__;
578 0 : }
579 :
580 : // __COUTV__(fullXML_);
581 :
582 : //Format: each line:
583 : // <parent supervisor name>;<parent supervisor lid>;<interface type>;<interface UID>
584 : // ;<macro name>;<macro permissions req>;<macro num of inputs>;...<input names ;
585 : // separated>...
586 : // ;<macro num of outputs>;...<output names ; separated>...
587 : // do not use :-separator because of the : in user permissions strings
588 :
589 0 : return fullXML_;
590 0 : } //end getFrontendMacroInfo()
591 :
592 : //==============================================================================
593 : ///returns CSV list of Front-end interface UIDs
594 0 : std::string ots_mm_udp_interface::getFrontendList()
595 : {
596 : __COUT__ << "getFrontendList()" << __E__;
597 0 : getFrontendMacroInfo(); //init fullXML_
598 :
599 : //Format: each line:
600 : // <parent supervisor name>;<parent supervisor lid>;<interface type>;<interface UID>
601 : // ;<macro name>;<macro permissions req>;<macro tooltip>;<macro num of inputs>;...<input names ;
602 : // separated>...
603 : // ;<macro num of outputs>;...<output names ; separated>...
604 : // do not use :-separator because of the : in user permissions strings
605 :
606 0 : std::string value;
607 0 : size_t after = 0;
608 :
609 0 : std::string retStr;
610 0 : while((value = extractXmlField(fullXML_, "FEMacros", 0, after, &after)) != "")
611 : {
612 : __COUTV__(fullXML_.size());
613 : __COUTV__(after);
614 : // __COUTV__(value);
615 :
616 0 : std::vector<std::string> fields = getVectorFromString(value, {';'});
617 :
618 : // __COUTV__(vectorToString(fields));
619 0 : if(fields.size() < 6)
620 0 : continue;
621 :
622 : //0 = supervisor name
623 : //1 = supervisor lid
624 : //2 = type
625 : //3 = FE UID
626 :
627 0 : if(retStr.size())
628 0 : retStr += ';';
629 0 : retStr += fields[3]; //append FE UID
630 : // __COUTV__(retStr);
631 0 : } //end primary loop
632 :
633 0 : return retStr;
634 0 : } //end getFrontendList()
635 :
636 : //==============================================================================
637 0 : std::string ots_mm_udp_interface::getCommandList(const std::string& targetFE)
638 : {
639 : __COUT__ << "getCommandList()" << __E__;
640 0 : getFrontendMacroInfo(); //init fullXML_
641 :
642 : //Format: each line:
643 : // <parent supervisor name>;<parent supervisor lid>;<interface type>;<interface UID>
644 : // ;<macro name>;<macro permissions req>;<macro tooltip>;<macro num of inputs>;...<input names ;
645 : // separated>...
646 : // ;<macro num of outputs>;...<output names ; separated>...
647 : // do not use :-separator because of the : in user permissions strings
648 :
649 0 : std::string value;
650 0 : size_t after = 0;
651 0 : std::string retStr;
652 0 : while((value = extractXmlField(fullXML_, "FEMacros", 0, after, &after)) != "")
653 : {
654 : // __COUTV__(after);
655 : // __COUTV__(value);
656 0 : std::vector<std::string> fields = getVectorFromString(value, {';'});
657 :
658 0 : if(fields.size() < 6)
659 0 : continue;
660 :
661 0 : uint32_t i = 4; //start at first Macro Name, then...
662 : //i+0 = Macro Name
663 : //i+1 = Macro Permissions
664 : //i+2 = Macro Tooltip
665 : //i+3 = number of inputs N
666 : //i+4+N = number of outputs M
667 : //i+4+N+M = Macro Name...
668 :
669 0 : if(fields[3] == targetFE)
670 : {
671 : // __COUTV__(vectorToString(fields));
672 :
673 : //scan through all Macro Names
674 :
675 0 : while(fields.size() >
676 0 : i + 5) //need type, uid, input count, output count (at least)
677 : {
678 0 : if(retStr.size())
679 0 : retStr += ';';
680 0 : retStr += fields[i + 0]; //append FE Macro Name
681 :
682 : // __COUTV__(fields[i+0]);
683 : // __COUTV__(fields[i+3]);
684 :
685 : //now navigate to next FE UID, by jumping input and output counts
686 0 : i += 3 + 1 + atoi(fields[i + 3].c_str());
687 :
688 0 : if(fields.size() < i)
689 : {
690 0 : __SS__ << "Illegal FE Macro info! " << i << " vs " << fields.size()
691 0 : << __E__;
692 0 : __SS_THROW__;
693 0 : }
694 : // __COUTV__(fields[i]);
695 0 : i += 1 + atoi(fields[i].c_str());
696 : } //primary FE UID search with fields from one FE Supervisor
697 :
698 0 : break; //found targetFE, so end search
699 : }
700 :
701 0 : } //end FE search loop
702 :
703 0 : return retStr;
704 0 : } //end getCommandList()
705 :
706 : //==============================================================================
707 0 : int ots_mm_udp_interface::getCommandInputCount(const std::string& targetFE,
708 : const std::string& command)
709 : {
710 : __COUT__ << "getCommandInputCount()" << __E__;
711 0 : getFrontendMacroInfo(); //init fullXML_
712 :
713 : //Format: each line:
714 : // <parent supervisor name>;<parent supervisor lid>;<interface type>;<interface UID>
715 : // ;<macro name>;<macro permissions req>;<macro tooltip>;<macro num of inputs>;...<input names ;
716 : // separated>...
717 : // ;<macro num of outputs>;...<output names ; separated>...
718 : // do not use :-separator because of the : in user permissions strings
719 :
720 0 : std::string value;
721 0 : size_t after = 0;
722 0 : std::string retStr;
723 0 : while((value = extractXmlField(fullXML_, "FEMacros", 0, after, &after)) != "")
724 : {
725 : // __COUTV__(after);
726 : // __COUTV__(value);
727 0 : std::vector<std::string> fields = getVectorFromString(value, {';'});
728 :
729 0 : if(fields.size() < 6)
730 0 : continue;
731 :
732 0 : uint32_t i = 4; //start at first Macro Name, then...
733 : //i+0 = Macro Name
734 : //i+1 = Macro Permissions
735 : //i+2 = Macro Tooltip
736 : //i+3 = number of inputs N
737 : //i+4+N = number of outputs M
738 : //i+4+N+M = Macro Name...
739 :
740 0 : if(fields[3] == targetFE)
741 : {
742 : // __COUTV__(vectorToString(fields));
743 :
744 : //scan through all Macro Names
745 :
746 0 : while(fields.size() >
747 0 : i + 5) //need type, uid, input count, output count (at least)
748 : {
749 0 : if(fields[i + 0] == command) //if found uid/command pair, return
750 : {
751 0 : return atoi(fields[i + 3].c_str()); //return input count
752 : }
753 : //else keep looking for macro name
754 :
755 : //now navigate to next FE UID, by jumping input and output counts
756 0 : i += 3 + 1 + atoi(fields[i + 3].c_str());
757 :
758 0 : if(fields.size() < i)
759 : {
760 0 : __SS__ << "Illegal FE Macro info! " << i << " vs " << fields.size()
761 0 : << __E__;
762 0 : __SS_THROW__;
763 0 : }
764 : // __COUTV__(fields[i]);
765 0 : i += 1 + atoi(fields[i].c_str());
766 : } //primary FE UID search with fields from one FE Supervisor
767 :
768 0 : break; //found targetFE, so end search
769 : }
770 0 : } //end FE search loop
771 :
772 0 : __SS__ << "Count not find FE '" << targetFE << "' Command '" << command
773 0 : << "' in FE Macro Info! Check UID and Macro name." << __E__;
774 0 : __SS_THROW__;
775 0 : } //end getCommandInputCount()
776 :
777 : //==============================================================================
778 0 : int ots_mm_udp_interface::getCommandOutputCount(const std::string& targetFE,
779 : const std::string& command)
780 : {
781 : __COUT__ << "getCommandOutputCount()" << __E__;
782 0 : getFrontendMacroInfo(); //init fullXML_
783 :
784 : //Format: each line:
785 : // <parent supervisor name>;<parent supervisor lid>;<interface type>;<interface UID>
786 : // ;<macro name>;<macro permissions req>;<macro tooltip>;<macro num of inputs>;...<input names ;
787 : // separated>...
788 : // ;<macro num of outputs>;...<output names ; separated>...
789 : // do not use :-separator because of the : in user permissions strings
790 :
791 0 : std::string value;
792 0 : size_t after = 0;
793 0 : std::string retStr;
794 0 : while((value = extractXmlField(fullXML_, "FEMacros", 0, after, &after)) != "")
795 : {
796 : // __COUTV__(after);
797 : // __COUTV__(value);
798 0 : std::vector<std::string> fields = getVectorFromString(value, {';'});
799 :
800 0 : if(fields.size() < 6)
801 0 : continue;
802 :
803 0 : uint32_t i = 4; //start at first Macro Name, then...
804 : //i+0 = Macro Name
805 : //i+1 = Macro Permissions
806 : //i+2 = Macro Tooltip
807 : //i+3 = number of inputs N
808 : //i+4+N = number of outputs M
809 : //i+4+N+M = Macro Name...
810 :
811 0 : if(fields[3] == targetFE)
812 : {
813 : // __COUTV__(vectorToString(fields));
814 :
815 : //scan through all Macro Names
816 :
817 0 : while(fields.size() >
818 0 : i + 5) //need type, uid, input count, output count (at least)
819 : {
820 0 : bool found = false;
821 0 : if(fields[i + 0] == command) //if found uid/command pair, return
822 : {
823 0 : found = true;
824 : }
825 : //else keep looking for macro name
826 :
827 : //now navigate to next FE UID, by jumping input and output counts
828 0 : i += 3 + 1 + atoi(fields[i + 3].c_str());
829 :
830 0 : if(fields.size() < i)
831 : {
832 0 : __SS__ << "Illegal FE Macro info! " << i << " vs " << fields.size()
833 0 : << __E__;
834 0 : __SS_THROW__;
835 0 : }
836 : // __COUTV__(fields[i]);
837 :
838 0 : if(found)
839 : {
840 0 : return atoi(fields[i].c_str()); //return output count
841 : }
842 : //else keep looking for macro name
843 0 : i += 1 + atoi(fields[i].c_str());
844 : } //primary FE UID search with fields from one FE Supervisor
845 :
846 0 : break; //found targetFE, so end search
847 : }
848 0 : } //end FE search loop
849 :
850 0 : __SS__ << "Count not find FE '" << targetFE << "' Command '" << command
851 0 : << "' in FE Macro Info! Check UID and Macro name." << __E__;
852 0 : __SS_THROW__;
853 0 : } //end getCommandOutputCount()
854 :
855 : //==============================================================================
856 0 : std::string ots_mm_udp_interface::getCommandInputName(const std::string& targetFE,
857 : const std::string& command,
858 : int inputIndex)
859 : {
860 : // __COUT__ << "getCommandInputName()" << __E__;
861 0 : getFrontendMacroInfo(); //init fullXML_
862 :
863 : //Format: each line:
864 : // <parent supervisor name>;<parent supervisor lid>;<interface type>;<interface UID>
865 : // ;<macro name>;<macro permissions req>;<macro tooltip>;<macro num of inputs>;...<input names ;
866 : // separated>...
867 : // ;<macro num of outputs>;...<output names ; separated>...
868 : // do not use :-separator because of the : in user permissions strings
869 :
870 0 : std::string value;
871 0 : size_t after = 0;
872 0 : std::string retStr;
873 0 : while((value = extractXmlField(fullXML_, "FEMacros", 0, after, &after)) != "")
874 : {
875 : // __COUTV__(after);
876 : // __COUTV__(value);
877 0 : std::vector<std::string> fields = getVectorFromString(value, {';'});
878 :
879 0 : if(fields.size() < 6)
880 0 : continue;
881 :
882 0 : uint32_t i = 4; //start at first Macro Name, then...
883 : //i+0 = Macro Name
884 : //i+1 = Macro Permissions
885 : //i+2 = Macro Tooltip
886 : //i+3 = number of inputs N
887 : //i+4+N = number of outputs M
888 : //i+4+N+M = Macro Name...
889 :
890 0 : if(fields[3] == targetFE)
891 : {
892 : // __COUTV__(vectorToString(fields));
893 :
894 : //scan through all Macro Names
895 :
896 0 : while(fields.size() >
897 0 : i + 5) //need type, uid, input count, output count (at least)
898 : {
899 0 : if(fields[i + 0] == command) //if found uid/command pair, return
900 : {
901 0 : if(i + 3 + 1 + inputIndex >= fields.size() ||
902 0 : inputIndex >= atoi(fields[i + 3].c_str()))
903 : {
904 0 : __SS__ << "Illegal input arg index " << inputIndex << " vs "
905 0 : << fields[i + 3] << " count" << __E__;
906 0 : __SS_THROW__;
907 0 : }
908 : return decodeURIComponent(
909 0 : fields[i + 3 + 1 + inputIndex]); //return input arg name
910 : }
911 : //else keep looking for macro name
912 :
913 : //now navigate to next FE UID, by jumping input and output counts
914 0 : i += 3 + 1 + atoi(fields[i + 3].c_str());
915 :
916 0 : if(fields.size() < i)
917 : {
918 0 : __SS__ << "Illegal FE Macro info! " << i << " vs " << fields.size()
919 0 : << __E__;
920 0 : __SS_THROW__;
921 0 : }
922 : // __COUTV__(fields[i]);
923 0 : i += 1 + atoi(fields[i].c_str());
924 : } //primary FE UID search with fields from one FE Supervisor
925 :
926 0 : break; //found targetFE, so end search
927 : }
928 0 : } //end FE search loop
929 :
930 0 : __SS__ << "Count not find FE '" << targetFE << "' Command '" << command
931 0 : << "' in FE Macro Info! Check UID and Macro name." << __E__;
932 0 : __SS_THROW__;
933 0 : } //end getCommandInputName()
934 :
935 : //==============================================================================
936 : ///Note: if std::map does not complicate interface too much for ROOT/pyton, could make this const std::string& and leverage cache solution
937 0 : std::string ots_mm_udp_interface::getCommandOutputName(const std::string& targetFE,
938 : const std::string& command,
939 : int outputIndex)
940 : {
941 : // // __COUT__ << "getCommandOutputName()" << __E__;
942 : // if(feCache_.find(targetFE + "|" + command) != feCache_.end())
943 : // {
944 : // if(feCache_.at(targetFE + "|" + command).find("outputNames-" + std::to_string(outputIndex)) != feCache_.at(targetFE + "|" + command).end())
945 : // {
946 : // __COUT__ << "Found direct cache" << __E__;
947 : // return feCache_.at(targetFE + "|" + command).at("outputNames-" + std::to_string(outputIndex));
948 : // }
949 : // else if(feCache_.at(targetFE + "|" + command).find("outputNames") != feCache_.at(targetFE + "|" + command).end())
950 : // {
951 : // __COUT__ << "Found cache" << __E__;
952 : // std::vector<std::string> outputs = getVectorFromString(
953 : // feCache_.at(targetFE + "|" + command).at("outputNames"), {';'});
954 : // return outputs[outputIndex];
955 : // }
956 : // }
957 0 : getFrontendMacroInfo(); //init fullXML_
958 :
959 : //Format: each line:
960 : // <parent supervisor name>;<parent supervisor lid>;<interface type>;<interface UID>
961 : // ;<macro name>;<macro permissions req>;<macro tooltip>;<macro num of inputs>;...<input names ;
962 : // separated>...
963 : // ;<macro num of outputs>;...<output names ; separated>...
964 : // do not use :-separator because of the : in user permissions strings
965 :
966 0 : std::string value;
967 0 : size_t after = 0;
968 0 : std::string retStr;
969 0 : while((value = extractXmlField(fullXML_, "FEMacros", 0, after, &after)) != "")
970 : {
971 : // __COUTV__(after);
972 : // __COUTV__(value);
973 0 : std::vector<std::string> fields = getVectorFromString(value, {';'});
974 :
975 0 : if(fields.size() < 6)
976 0 : continue;
977 :
978 0 : uint32_t i = 4; //start at first Macro Name, then...
979 : //i+0 = Macro Name
980 : //i+1 = Macro Permissions
981 : //i+2 = Macro Tooltip
982 : //i+3 = number of inputs N
983 : //i+4+N = number of outputs M
984 : //i+4+N+M = Macro Name...
985 :
986 0 : if(fields[3] == targetFE)
987 : {
988 : // __COUTV__(vectorToString(fields));
989 :
990 : //scan through all Macro Names
991 :
992 0 : while(fields.size() >
993 0 : i + 5) //need type, uid, input count, output count (at least)
994 : {
995 0 : bool found = false;
996 0 : if(fields[i + 0] == command) //if found uid/command pair, return
997 : {
998 0 : found = true;
999 : }
1000 : //else keep looking for macro name
1001 :
1002 : //now navigate to next FE UID, by jumping input and output counts
1003 0 : i += 3 + 1 + atoi(fields[i + 3].c_str());
1004 :
1005 0 : if(fields.size() < i)
1006 : {
1007 0 : __SS__ << "Illegal FE Macro info! " << i << " vs " << fields.size()
1008 0 : << __E__;
1009 0 : __SS_THROW__;
1010 0 : }
1011 : // __COUTV__(fields[i]);
1012 :
1013 0 : if(found)
1014 : {
1015 0 : if(i + 1 + outputIndex >= fields.size() ||
1016 0 : outputIndex >= atoi(fields[i].c_str()))
1017 : {
1018 0 : __SS__ << "Illegal output arg index " << outputIndex << " vs "
1019 0 : << fields[i] << " count" << __E__;
1020 0 : __SS_THROW__;
1021 0 : }
1022 :
1023 : // feCache_[targetFE + "|" + command]["outputNames-" + std::to_string(outputIndex)] = decodeURIComponent(fields[i+1+outputIndex]);
1024 : // return feCache_.at(targetFE + "|" + command).at("outputNames-" + std::to_string(outputIndex)); //return output arg name
1025 0 : return decodeURIComponent(fields[i + 1 + outputIndex]);
1026 : }
1027 : //else keep looking for macro name
1028 0 : i += 1 + atoi(fields[i].c_str());
1029 : } //primary FE UID search with fields from one FE Supervisor
1030 :
1031 0 : break; //found targetFE, so end search
1032 : }
1033 0 : } //end FE search loop
1034 :
1035 0 : __SS__ << "Count not find FE '" << targetFE << "' Command '" << command
1036 0 : << "' in FE Macro Info! Check UID and Macro name." << __E__;
1037 0 : __SS_THROW__;
1038 0 : } //end getCommandOutputName()
1039 :
1040 : //==============================================================================
1041 : /// inputs should be ;-separated and URI encoded (to avoid commas and semicolons)
1042 : /// outputs will be ;-separated and URI encoded
1043 0 : std::string ots_mm_udp_interface::runCommand(const std::string& targetFE,
1044 : const std::string& command,
1045 : const std::string& inputs)
1046 : {
1047 : __COUT__ << "On '" << targetFE << "' runCommand: " << command << __E__;
1048 :
1049 : __COUTV__(inputs);
1050 :
1051 : //extract FE Type from FE Macro info...
1052 0 : getFrontendMacroInfo(); //init fullXML_
1053 0 : std::string feType;
1054 :
1055 : //Format: each line:
1056 : // <parent supervisor name>;<parent supervisor lid>;<interface type>;<interface UID>
1057 : // ;<macro name>;<macro permissions req>;<macro tooltip>;<macro num of inputs>;...<input names ;
1058 : // separated>...
1059 : // ;<macro num of outputs>;...<output names ; separated>...
1060 : // do not use :-separator because of the : in user permissions strings
1061 :
1062 0 : std::string value;
1063 0 : size_t after = 0;
1064 :
1065 0 : std::string retStr;
1066 0 : while((value = extractXmlField(fullXML_, "FEMacros", 0, after, &after)) != "")
1067 : {
1068 : __COUTV__(fullXML_.size());
1069 : __COUTV__(after);
1070 : // __COUTV__(value);
1071 :
1072 0 : std::vector<std::string> fields = getVectorFromString(value, {';'});
1073 :
1074 : // __COUTV__(vectorToString(fields));
1075 0 : if(fields.size() < 6)
1076 0 : continue;
1077 :
1078 : //0 = supervisor name
1079 : //1 = supervisor lid
1080 : //2 = type
1081 : //3 = FE UID
1082 :
1083 0 : if(fields[3] == targetFE)
1084 : {
1085 0 : feType = fields[2];
1086 : __COUTV__(feType);
1087 0 : break;
1088 : }
1089 0 : } //end primary loop
1090 :
1091 0 : if(feType.size() == 0)
1092 : {
1093 0 : __SS__ << "Could not find front-end type for FE UID '" << targetFE << __E__;
1094 0 : __SS_THROW__;
1095 0 : }
1096 :
1097 : int numbytes;
1098 :
1099 0 : std::string sendMessage = "RunFrontendMacro";
1100 : //create ;-separated arguments to satisfy this:
1101 : // std::string feClassSelected = bufferFields[1];
1102 : // std::string feUIDSelected = bufferFields[2]; // allow CSV multi-selection
1103 : // std::string macroType = bufferFields[3]; // "fe", "public", "private"
1104 : // std::string macroName = bufferFields[4];
1105 : // std::string inputArgs = StringMacros::decodeURIComponent(bufferFields[5]); //two level ;- and ,- separated
1106 : // std::string outputArgs = StringMacros::decodeURIComponent(bufferFields[6]); // ,- separated
1107 : // bool saveOutputs = bufferFields[7] == "1";
1108 0 : sendMessage += ";" + feType; // std::string feClassSelected = bufferFields[1];
1109 : sendMessage +=
1110 0 : ";" +
1111 0 : targetFE; // std::string feUIDSelected = bufferFields[2]; // allow CSV multi-selection
1112 : sendMessage +=
1113 0 : ";" +
1114 0 : std::string(
1115 0 : "fe"); // std::string macroType = bufferFields[3]; // "fe", "public", "private"
1116 : sendMessage +=
1117 0 : ";" + encodeURIComponent(command); // std::string macroName = bufferFields[4];
1118 :
1119 : //handle inputs
1120 : {
1121 0 : std::string inputStr;
1122 0 : uint32_t numberOfInputs = getCommandInputCount(targetFE, command);
1123 :
1124 0 : std::vector<std::string> inputVec = getVectorFromString(inputs, {';'});
1125 0 : uint32_t countOfNonEmptyInputs = 0;
1126 0 : for(uint32_t i = 0; i < inputVec.size(); ++i)
1127 : {
1128 0 : if(inputVec[i] == "")
1129 0 : continue; //skip empty inputs
1130 0 : if(countOfNonEmptyInputs)
1131 0 : inputStr += ";";
1132 0 : inputStr += encodeURIComponent(getCommandInputName(
1133 0 : targetFE, command, countOfNonEmptyInputs)) +
1134 0 : "," + inputVec[i]; //already encoded (hopefully)
1135 :
1136 0 : ++countOfNonEmptyInputs;
1137 : }
1138 0 : if(numberOfInputs != countOfNonEmptyInputs)
1139 : {
1140 0 : __SS__ << "Input argument mismatch: " << countOfNonEmptyInputs << " vs "
1141 0 : << numberOfInputs << " expected." << __E__;
1142 0 : __SS_THROW__;
1143 0 : }
1144 : __COUTV__(inputStr);
1145 0 : sendMessage += ";" + encodeURIComponent(inputStr); //double encoded
1146 0 : }
1147 :
1148 : //handle outputs, which are ,- separated
1149 0 : uint32_t numberOfOutputs = getCommandOutputCount(targetFE, command);
1150 : {
1151 0 : std::string outputStr;
1152 :
1153 0 : for(uint32_t i = 0; i < numberOfOutputs; ++i)
1154 : {
1155 0 : if(i)
1156 0 : outputStr += ",";
1157 0 : outputStr += encodeURIComponent(getCommandOutputName(targetFE, command, i));
1158 : }
1159 :
1160 0 : sendMessage += ";" + encodeURIComponent(outputStr); //double encoded
1161 0 : }
1162 :
1163 : sendMessage +=
1164 0 : ";" + std::string("0"); // bool saveOutputs = bufferFields[7] == "1";
1165 :
1166 : __COUTV__(sendMessage.size());
1167 : __COUTV__(sendMessage);
1168 :
1169 0 : if((numbytes = sendto(mm_sock_,
1170 0 : &(sendMessage.c_str()[0]),
1171 : sendMessage.size(),
1172 : 0,
1173 0 : (struct sockaddr*)&mm_ai_addr,
1174 0 : sizeof(mm_ai_addr))) == -1)
1175 : {
1176 0 : __SS__ << "Error on runCommand() sendto! Error: " << strerror(errno) << __E__;
1177 :
1178 : { //check for more error info
1179 0 : int error = 0;
1180 0 : socklen_t len = sizeof(error);
1181 0 : int retval = getsockopt(mm_sock_, SOL_SOCKET, SO_ERROR, &error, &len);
1182 0 : if(retval != 0 || error != 0)
1183 : {
1184 0 : ss << "Socket is closed or in error state: " << strerror(error)
1185 0 : << std::endl;
1186 : }
1187 : }
1188 :
1189 0 : close(mm_sock_);
1190 0 : mm_sock_ = -1;
1191 :
1192 0 : __SS_THROW__;
1193 0 : }
1194 :
1195 : // read response ///////////////////////////////////////////////////////////
1196 0 : std::string runXML;
1197 0 : receiveXmlResponse(runXML, "command run response", 10);
1198 :
1199 : // At this point, runXML should contain the complete response assembled by the current receive logic.
1200 0 : if(runXML.size() == 0 || runXML.find("Error") == 0 || !hasCompleteXmlRoot(runXML))
1201 : {
1202 0 : __SS__ << "Error running the command. Received error or incomplete buffer: "
1203 0 : << (runXML.size() == 0 ? "<empty>" : runXML) << __E__;
1204 0 : __SS_THROW__;
1205 0 : }
1206 :
1207 : //example response:
1208 : // <ROOT>
1209 : // <DATA>
1210 : // <feMacroExec value='Check Firefly Loss-of-Light'>
1211 : // <exec_time value='Fri Aug 16 18:59:15 2024 CDT'/>
1212 : // <fe_uid value='CFO0'/>
1213 : // <fe_type value='CFOFrontEndInterface'/>
1214 : // <fe_context value='ots::FESupervisor'/>
1215 : // <fe_supervisor value='LID-280'/>
1216 : // <fe_hostname value='mu2e-calo-03.fnal.gov'/>
1217 : // <outputArgs_name value='Link Status'/>
1218 : // <outputArgs_value value='%7B0%3ADEAD%2C%201%3A%20DEAD%2C%202%3ADEAD%2C%203%3ADEAD%2C%204%3A%20DEAD%2C%205%3A%20DEAD%2C%206%2FCFO%3A%20OK%2C%207%2FEVB%3A%20DEAD%7D'/>
1219 : // </feMacroExec>
1220 : // </DATA>
1221 : // </ROOT>
1222 :
1223 : //verify outputs
1224 : {
1225 0 : std::string outputStr;
1226 :
1227 0 : size_t after = 0;
1228 0 : std::string error = extractXmlField(runXML, "Error", 0, after, &after);
1229 :
1230 0 : if(error != "")
1231 : {
1232 : //error has html entities in it
1233 0 : error = decodeHTMLEntities(error);
1234 0 : __SS__ << "Error message received after command execution attempt: \n"
1235 0 : << error << __E__;
1236 0 : __SS_THROW__;
1237 0 : }
1238 :
1239 0 : for(uint32_t i = 0; i < numberOfOutputs; ++i)
1240 : {
1241 0 : std::string expectedOutputName = getCommandOutputName(targetFE, command, i);
1242 :
1243 : std::string outputName =
1244 0 : extractXmlField(runXML, "outputArgs_name", 0, after, &after);
1245 : std::string outputValue =
1246 0 : extractXmlField(runXML, "outputArgs_value", 0, after, &after);
1247 0 : __COUT_INFO__ << "Command Result output #" << i << ": " << outputName << " = "
1248 0 : << decodeURIComponent(outputValue) << __E__;
1249 :
1250 0 : if(i)
1251 0 : outputStr += ';';
1252 0 : outputStr += outputValue;
1253 0 : }
1254 :
1255 0 : return outputStr;
1256 0 : }
1257 :
1258 0 : } //end runCommand()
|