2 #include "test/ots_mm_udp_interface.h"
8 void* get_in_addr(
struct sockaddr* sa)
10 if(sa->sa_family == AF_INET)
12 return &(((
struct sockaddr_in*)sa)->sin_addr);
15 return &(((
struct sockaddr_in6*)sa)->sin6_addr);
19 int makeSocket(
const char* ip,
int port,
struct sockaddr_in& mm_ai_addr)
22 struct addrinfo hints, *servinfo, *p;
29 memset(&hints, 0,
sizeof hints);
30 hints.ai_family = AF_UNSPEC;
31 hints.ai_socktype = SOCK_DGRAM;
33 if((rv = getaddrinfo(ip, std::to_string(port).c_str(), &hints, &servinfo)) != 0)
35 __SS__ <<
"getaddrinfo: " << gai_strerror(rv) << __E__;
40 for(p = servinfo; p != NULL; p = p->ai_next)
42 if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
44 __COUT_WARN__ <<
"sw: socket failed, trying other address..." << __E__;
53 __SS__ <<
"sw: failed to create socket" << __E__;
58 memcpy(&mm_ai_addr, p->ai_addr,
sizeof(mm_ai_addr));
60 freeaddrinfo(servinfo);
67 fd_set fileDescriptor_;
68 struct timeval timeout_;
69 struct sockaddr_in fromAddress_;
70 socklen_t addressLength_ =
sizeof(fromAddress_);
71 int receive(
int sockfd,
73 unsigned int timeoutSeconds,
74 unsigned int timeoutUSeconds,
77 unsigned long fromIPAddress;
78 unsigned short fromPort;
81 timeout_.tv_sec = timeoutSeconds;
82 timeout_.tv_usec = timeoutUSeconds;
84 FD_ZERO(&fileDescriptor_);
85 FD_SET(sockfd, &fileDescriptor_);
86 select(sockfd + 1, &fileDescriptor_, 0, 0, &timeout_);
89 if(FD_ISSET(sockfd, &fileDescriptor_))
91 buffer.resize(MAXBUFLEN);
94 if((numberOfBytes = recvfrom(sockfd,
98 (
struct sockaddr*)&fromAddress_,
99 &addressLength_)) == -1)
101 __SS__ <<
"Error reading buffer from\tIP:\t";
102 std::string fromIP = inet_ntoa(fromAddress_.sin_addr);
103 fromIPAddress = fromAddress_.sin_addr.s_addr;
104 fromPort = fromAddress_.sin_port;
106 for(
int i = 0; i < 4; i++)
108 ss << ((fromIPAddress << (i * 8)) & 0xff);
112 ss <<
"\tPort\t" << ntohs(fromPort) <<
" IP " << fromIP << std::endl;
113 __COUT__ <<
"\n" << ss.str();
118 fromIPAddress = fromAddress_.sin_addr.s_addr;
119 fromPort = fromAddress_.sin_port;
131 buffer.resize(numberOfBytes);
135 std::string fromIP = inet_ntoa(fromAddress_.sin_addr);
137 __COUT__ <<
"Receiving from: " << fromIP <<
":" << ntohs(fromPort)
138 <<
" size: " << buffer.size() << std::endl;
157 __COUT__ <<
"No new messages for "
158 << timeoutSeconds + timeoutUSeconds / 1000000.
159 <<
"s. Read request timed out." << std::endl;
171 __COUT__ <<
"Constructor" << __E__;
173 mm_sock_ = makeSocket(mm_ip, mm_port, mm_ai_addr);
177 ots_mm_udp_interface::~ots_mm_udp_interface()
179 __COUT__ <<
"Destructor" << __E__;
191 std::string extractXmlField(
const std::string& xml,
192 const std::string& field,
197 size_t lo = after, hi;
198 for(uint32_t i = 0; i <= occurrence; ++i)
199 if((lo = xml.find(
"<" + field +
" ", lo)) == std::string::npos)
201 if((hi = xml.find(
"'/>", lo)) == std::string::npos)
204 lo = xml.find(
"value='", lo) + 7;
207 *returnAfter = hi + 3;
209 return xml.substr(lo, hi - lo);
215 std::string rextractXmlField(
const std::string& xml,
216 const std::string& field,
220 size_t lo = 0, hi = before;
221 for(uint32_t i = 0; i <= occurrence; ++i)
222 if((lo = xml.rfind(
"<" + field +
" ", hi)) == std::string::npos)
224 if((hi = xml.rfind(
"'/>", hi)) == std::string::npos)
227 lo = xml.find(
"value='", lo) + 7;
229 return xml.substr(lo, hi - lo);
244 void getVectorFromString(
const std::string& inputString,
245 std::vector<std::string>& listToReturn,
246 const std::set<char>& delimiter,
247 const std::set<char>& whitespace,
248 std::vector<char>* listOfDelimiters,
249 bool decodeURIComponents)
254 std::set<char>::iterator delimeterSearchIt;
255 char lastDelimiter = 0;
264 for(; c < inputString.size(); ++c)
268 delimeterSearchIt = delimiter.find(inputString[c]);
269 isDelimiter = delimeterSearchIt != delimiter.end();
274 if(whitespace.find(inputString[c]) !=
283 else if(whitespace.find(inputString[c]) != whitespace.end() &&
293 if(listOfDelimiters && listToReturn.size())
300 listOfDelimiters->push_back(lastDelimiter);
303 listToReturn.push_back(decodeURIComponents
305 inputString.substr(i, j - i))
306 : inputString.substr(i, j - i));
316 lastDelimiter = *delimeterSearchIt;
325 if(listOfDelimiters && listToReturn.size())
331 listOfDelimiters->push_back(lastDelimiter);
333 listToReturn.push_back(
336 : inputString.substr(i, j - i));
340 if(listOfDelimiters && listToReturn.size() - 1 != listOfDelimiters->size() &&
341 listToReturn.size() != listOfDelimiters->size())
344 <<
"There is a mismatch in delimiters to entries (should be equal or one "
346 << listOfDelimiters->size() <<
" vs " << listToReturn.size()
366 std::vector<std::string> getVectorFromString(
const std::string& inputString,
367 const std::set<char>& delimiter,
368 const std::set<char>& whitespace,
369 std::vector<char>* listOfDelimiters,
370 bool decodeURIComponents)
372 std::vector<std::string> listToReturn;
374 getVectorFromString(inputString,
379 decodeURIComponents);
384 std::string vectorToString(
const std::vector<std::string>& setToReturn,
385 const std::string& delimeter )
387 std::stringstream ss;
389 for(
auto& setValue : setToReturn)
405 std::string decodeURIString(data.size(), 0);
407 for(
unsigned int i = 0; i < data.size(); ++i, ++j)
412 if(data[i + 1] >
'9')
413 decodeURIString[j] += (data[i + 1] - 55) * 16;
415 decodeURIString[j] += (data[i + 1] - 48) * 16;
418 if(data[i + 2] >
'9')
419 decodeURIString[j] += (data[i + 2] - 55);
421 decodeURIString[j] += (data[i + 2] - 48);
426 decodeURIString[j] = data[i];
428 decodeURIString.resize(j);
429 return decodeURIString;
433 std::string ots_mm_udp_interface::encodeURIComponent(
const std::string& sourceStr)
435 std::string retStr =
"";
437 for(
const auto& c : sourceStr)
438 if((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9'))
442 sprintf(encodeStr,
"%%%2.2X", (uint8_t)c);
449 std::string ots_mm_udp_interface::decodeHTMLEntities(
const std::string& sourceStr)
451 std::string retStr = sourceStr;
452 std::vector<std::string> htmlSubstrings(
453 {
"<",
">",
"&",
""",
"'",
" ",
"
"});
454 std::vector<std::string> htmlReplaces({
"<",
">",
"&",
"\"",
"'",
" ",
"\n"});
455 for(
size_t i = 0; i < htmlSubstrings.size(); ++i)
458 while((index = retStr.find(htmlSubstrings[i], index)) != std::string::npos)
460 retStr.replace(index, htmlSubstrings[i].size(), htmlReplaces[i]);
461 index += htmlReplaces[i].size();
471 __COUT__ <<
"getFrontendMacroInfo()" << __E__;
477 std::string sendMessage =
"GetFrontendMacroInfo";
479 if((numbytes = sendto(mm_sock_,
480 &(sendMessage.c_str()[0]),
483 (
struct sockaddr*)&mm_ai_addr,
484 sizeof(mm_ai_addr))) == -1)
486 __SS__ <<
"Error on getFrontendMacroInfo() sendto!" << __E__;
494 while(receive(mm_sock_,
504 if(fullXML_.size() == 0)
506 __SS__ <<
"FE Macro Info receive failed! Check that a MacroMaker Supervisor is "
507 "running with UDP Remote Control enabled."
528 __COUT__ <<
"getFrontendList()" << __E__;
542 while((value = extractXmlField(fullXML_,
"FEMacros", 0, after, &after)) !=
"")
544 __COUTV__(fullXML_.size());
548 std::vector<std::string> fields = getVectorFromString(value, {
';'});
551 if(fields.size() < 6)
569 std::string ots_mm_udp_interface::getCommandList(
const std::string& targetFE)
571 __COUT__ <<
"getCommandList()" << __E__;
584 while((value = extractXmlField(fullXML_,
"FEMacros", 0, after, &after)) !=
"")
588 std::vector<std::string> fields = getVectorFromString(value, {
';'});
590 if(fields.size() < 6)
601 if(fields[3] == targetFE)
607 while(fields.size() >
612 retStr += fields[i + 0];
618 i += 3 + 1 + atoi(fields[i + 3].c_str());
620 if(fields.size() < i)
622 __SS__ <<
"Illegal FE Macro info! " << i <<
" vs " << fields.size()
627 i += 1 + atoi(fields[i].c_str());
639 int ots_mm_udp_interface::getCommandInputCount(
const std::string& targetFE,
640 const std::string& command)
642 __COUT__ <<
"getCommandInputCount()" << __E__;
655 while((value = extractXmlField(fullXML_,
"FEMacros", 0, after, &after)) !=
"")
659 std::vector<std::string> fields = getVectorFromString(value, {
';'});
661 if(fields.size() < 6)
672 if(fields[3] == targetFE)
678 while(fields.size() >
681 if(fields[i + 0] == command)
683 return atoi(fields[i + 3].c_str());
688 i += 3 + 1 + atoi(fields[i + 3].c_str());
690 if(fields.size() < i)
692 __SS__ <<
"Illegal FE Macro info! " << i <<
" vs " << fields.size()
697 i += 1 + atoi(fields[i].c_str());
704 __SS__ <<
"Count not find FE '" << targetFE <<
"' Command '" << command
705 <<
"' in FE Macro Info! Check UID and Macro name." << __E__;
710 int ots_mm_udp_interface::getCommandOutputCount(
const std::string& targetFE,
711 const std::string& command)
713 __COUT__ <<
"getCommandOutputCount()" << __E__;
726 while((value = extractXmlField(fullXML_,
"FEMacros", 0, after, &after)) !=
"")
730 std::vector<std::string> fields = getVectorFromString(value, {
';'});
732 if(fields.size() < 6)
743 if(fields[3] == targetFE)
749 while(fields.size() >
753 if(fields[i + 0] == command)
760 i += 3 + 1 + atoi(fields[i + 3].c_str());
762 if(fields.size() < i)
764 __SS__ <<
"Illegal FE Macro info! " << i <<
" vs " << fields.size()
772 return atoi(fields[i].c_str());
775 i += 1 + atoi(fields[i].c_str());
782 __SS__ <<
"Count not find FE '" << targetFE <<
"' Command '" << command
783 <<
"' in FE Macro Info! Check UID and Macro name." << __E__;
788 std::string ots_mm_udp_interface::getCommandInputName(
const std::string& targetFE,
789 const std::string& command,
805 while((value = extractXmlField(fullXML_,
"FEMacros", 0, after, &after)) !=
"")
809 std::vector<std::string> fields = getVectorFromString(value, {
';'});
811 if(fields.size() < 6)
822 if(fields[3] == targetFE)
828 while(fields.size() >
831 if(fields[i + 0] == command)
833 if(i + 3 + 1 + inputIndex >= fields.size() ||
834 inputIndex >= atoi(fields[i + 3].c_str()))
836 __SS__ <<
"Illegal input arg index " << inputIndex <<
" vs "
837 << fields[i + 3] <<
" count" << __E__;
841 fields[i + 3 + 1 + inputIndex]);
846 i += 3 + 1 + atoi(fields[i + 3].c_str());
848 if(fields.size() < i)
850 __SS__ <<
"Illegal FE Macro info! " << i <<
" vs " << fields.size()
855 i += 1 + atoi(fields[i].c_str());
862 __SS__ <<
"Count not find FE '" << targetFE <<
"' Command '" << command
863 <<
"' in FE Macro Info! Check UID and Macro name." << __E__;
870 const std::string& command,
901 while((value = extractXmlField(fullXML_,
"FEMacros", 0, after, &after)) !=
"")
905 std::vector<std::string> fields = getVectorFromString(value, {
';'});
907 if(fields.size() < 6)
918 if(fields[3] == targetFE)
924 while(fields.size() >
928 if(fields[i + 0] == command)
935 i += 3 + 1 + atoi(fields[i + 3].c_str());
937 if(fields.size() < i)
939 __SS__ <<
"Illegal FE Macro info! " << i <<
" vs " << fields.size()
947 if(i + 1 + outputIndex >= fields.size() ||
948 outputIndex >= atoi(fields[i].c_str()))
950 __SS__ <<
"Illegal output arg index " << outputIndex <<
" vs "
951 << fields[i] <<
" count" << __E__;
960 i += 1 + atoi(fields[i].c_str());
967 __SS__ <<
"Count not find FE '" << targetFE <<
"' Command '" << command
968 <<
"' in FE Macro Info! Check UID and Macro name." << __E__;
976 const std::string& command,
977 const std::string& inputs)
979 __COUT__ <<
"On '" << targetFE <<
"' runCommand: " << command << __E__;
998 while((value = extractXmlField(fullXML_,
"FEMacros", 0, after, &after)) !=
"")
1000 __COUTV__(fullXML_.size());
1004 std::vector<std::string> fields = getVectorFromString(value, {
';'});
1007 if(fields.size() < 6)
1015 if(fields[3] == targetFE)
1023 if(feType.size() == 0)
1025 __SS__ <<
"Could not find front-end type for FE UID '" << targetFE << __E__;
1031 std::string sendMessage =
"RunFrontendMacro";
1040 sendMessage +=
";" + feType;
1049 ";" + encodeURIComponent(command);
1053 std::string inputStr;
1054 uint32_t numberOfInputs = getCommandInputCount(targetFE, command);
1056 std::vector<std::string> inputVec = getVectorFromString(inputs, {
';'});
1057 uint32_t countOfNonEmptyInputs = 0;
1058 for(uint32_t i = 0; i < inputVec.size(); ++i)
1060 if(inputVec[i] ==
"")
1062 if(countOfNonEmptyInputs)
1064 inputStr += encodeURIComponent(getCommandInputName(
1065 targetFE, command, countOfNonEmptyInputs)) +
1068 ++countOfNonEmptyInputs;
1070 if(numberOfInputs != countOfNonEmptyInputs)
1072 __SS__ <<
"Input argument mismatch: " << countOfNonEmptyInputs <<
" vs "
1073 << numberOfInputs <<
" expected." << __E__;
1076 __COUTV__(inputStr);
1077 sendMessage +=
";" + encodeURIComponent(inputStr);
1081 uint32_t numberOfOutputs = getCommandOutputCount(targetFE, command);
1083 std::string outputStr;
1085 for(uint32_t i = 0; i < numberOfOutputs; ++i)
1092 sendMessage +=
";" + encodeURIComponent(outputStr);
1096 ";" + std::string(
"0");
1098 __COUTV__(sendMessage.size());
1099 __COUTV__(sendMessage);
1101 if((numbytes = sendto(mm_sock_,
1102 &(sendMessage.c_str()[0]),
1105 (
struct sockaddr*)&mm_ai_addr,
1106 sizeof(mm_ai_addr))) == -1)
1108 __SS__ <<
"Error on runCommand() sendto! Error: " << strerror(errno) << __E__;
1112 socklen_t len =
sizeof(error);
1113 int retval = getsockopt(mm_sock_, SOL_SOCKET, SO_ERROR, &error, &len);
1114 if(retval != 0 || error != 0)
1116 ss <<
"Socket is closed or in error state: " << strerror(error)
1128 std::string runXML =
"";
1130 if(receive(mm_sock_,
1138 while(receive(mm_sock_,
1148 if(runXML.size() == 0 || runXML.find(
"Error") == 0)
1150 __SS__ <<
"Error running the command. Received buffer: "
1151 << (runXML.size() == 0 ?
"<empty>" : runXML) << __E__;
1173 std::string outputStr;
1176 std::string error = extractXmlField(runXML,
"Error", 0, after, &after);
1181 error = decodeHTMLEntities(error);
1182 __SS__ <<
"Error message received after command execution attempt: \n"
1187 for(uint32_t i = 0; i < numberOfOutputs; ++i)
1191 std::string outputName =
1192 extractXmlField(runXML,
"outputArgs_name", 0, after, &after);
1193 std::string outputValue =
1194 extractXmlField(runXML,
"outputArgs_value", 0, after, &after);
1195 __COUT_INFO__ <<
"Command Result output #" << i <<
": " << outputName <<
" = "
1200 outputStr += outputValue;
std::string getFrontendList(void)
returns CSV list of Front-end interface UIDs
ots_mm_udp_interface(const char *mm_ip, int mm_port)
static std::string decodeURIComponent(const std::string &data)
std::string runCommand(const std::string &targetFE, const std::string &command, const std::string &inputs)
const std::string & getFrontendMacroInfo(void)
returns CSV list of Front-end interface UIDs
std::string getCommandOutputName(const std::string &targetFE, const std::string &command, int outputIndex)
Note: if std::map does not complicate interface too much for ROOT/pyton, could make this const std::s...