LCOV - code coverage report
Current view: top level - otsdaq-utilities/test - ots_mm_udp_interface.cpp (source / functions) Coverage Total Hit
Test: mu2edaq.info.cleaned Lines: 0.0 % 503 0
Test Date: 2026-07-30 01:56:44 Functions: 0.0 % 23 0

            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 :             {"&lt;", "&gt;", "&amp;", "&quot;", "&apos;", "&#160;", "&#013;"});
     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()
        

Generated by: LCOV version 2.0-1