otsdaq-utilities  3.03.02
ConsoleSupervisor.cc
1 #include "otsdaq-utilities/Console/ConsoleSupervisor.h"
2 #include <xdaq/NamespaceURI.h>
3 #include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
4 #include "otsdaq/Macros/CoutMacros.h"
5 #include "otsdaq/MessageFacility/MessageFacility.h"
6 #include "otsdaq/NetworkUtilities/ReceiverSocket.h"
7 #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
8 
9 #include <dirent.h> //for DIR
10 #include <sys/stat.h> //for mkdir
11 #include <fstream>
12 #include <iostream>
13 #include <string>
14 #include <thread> //for std::thread
15 
16 using namespace ots;
17 
22 XDAQ_INSTANTIATOR_IMPL(ConsoleSupervisor)
23 
24 #define USER_CONSOLE_PREF_PATH \
25  std::string(__ENV__("SERVICE_DATA_PATH")) + "/ConsolePreferences/"
26 #define USER_CONSOLE_SNAPSHOT_PATH \
27  std::string(__ENV__("SERVICE_DATA_PATH")) + "/ConsoleSnapshots/"
28 #define USERS_PREFERENCES_FILETYPE "pref"
29 #define CUSTOM_COUNT_LIST_FILENAME std::string("CustomCountList.dat")
30 
31 #define QUIET_CFG_FILE \
32  std::string(__ENV__("USER_DATA")) + \
33  "/MessageFacilityConfigurations/" \
34  "QuietForwarder.cfg"
35 
36 #define CONSOLE_SPECIAL_ERROR \
37  std::string("|30-Aug-2019 15:30:17 CDT|0|||Error|Console||-1||ConsoleSupervisor|") + \
38  std::string(__FILE__) + std::string("|") + std::to_string(__LINE__) + \
39  std::string("|")
40 #define CONSOLE_SPECIAL_WARNING \
41  std::string( \
42  "|30-Aug-2019 15:30:17 CDT|0|||Warning|Console||-1||ConsoleSupervisor|") + \
43  std::string(__FILE__) + std::string("|") + std::to_string(__LINE__) + \
44  std::string("|")
45 
46 #undef __MF_SUBJECT__
47 #define __MF_SUBJECT__ "Console"
48 
49 #define CONSOLE_MISSED_NEEDLE "Console missed * packet(s)"
50 
52 const std::set<std::string> ConsoleSupervisor::CUSTOM_TRIGGER_ACTIONS({"Count Only",
53  "System Message",
54  "Halt",
55  "Stop",
56  "Pause",
57  "Soft Error",
58  "Hard Error",
59  "Run Script"});
60 
61 const std::string ConsoleSupervisor::ConsoleMessageStruct::LABEL_TRACE = "TRACE";
62 const std::string ConsoleSupervisor::ConsoleMessageStruct::LABEL_TRACE_PLUS = "TRACE+";
64  std::string /* fieldNames */>
65  ConsoleSupervisor::ConsoleMessageStruct::fieldNames({
66  {FieldType::TIMESTAMP, "Timestamp"},
67  {FieldType::SEQID, "SequenceID"},
68  {FieldType::LEVEL, "Level"},
69  {FieldType::LABEL, "Label"},
70  {FieldType::SOURCEID, "SourceID"},
71  {FieldType::HOSTNAME, "Hostname"},
72  {FieldType::SOURCE, "Source"},
73  {FieldType::FILE, "File"},
74  {FieldType::LINE, "Line"},
75  {FieldType::MSG, "Msg"},
76  });
77 
78 //==============================================================================
79 ConsoleSupervisor::ConsoleSupervisor(xdaq::ApplicationStub* stub)
80  : CoreSupervisorBase(stub)
81  , messageCount_(0)
82  , maxMessageCount_(100000)
83  , maxClientMessageRequest_(500)
84 {
85  __SUP_COUT__ << "Constructor started." << __E__;
86 
87  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
88 
89  // attempt to make directory structure (just in case)
90  mkdir(((std::string)USER_CONSOLE_PREF_PATH).c_str(), 0755);
91  mkdir(((std::string)USER_CONSOLE_SNAPSHOT_PATH).c_str(), 0755);
92 
93  xoap::bind(
94  this, &ConsoleSupervisor::resetConsoleCounts, "ResetConsoleCounts", XDAQ_NS_URI);
95 
96  init();
97 
98  //test custom count list and always force the first one to be the "Console missed" !
99  //Note: to avoid recursive triggers, Label='Console' can not trigger, so this 1st one is the only Console source trigger!
100  // Custom Trigger Philosophy
101  // - only one global custom count priority list (too complicated to manage by user, would have to timeout logins, etc..)
102  // - the admin users can modify priority of items in prority list
103  // - actions can infer others ('Count' is always inferred, in addition 'System Message' is inferred for FSM actions)
104  // - the admin users can modify actions
105  // -- including to the 1st Console-missing-message custom count
106  loadCustomCountList();
107  if(priorityCustomTriggerList_.size() ==
108  0) //then create default starting list example for user
109  {
110  addCustomTriggeredAction(CONSOLE_MISSED_NEEDLE, "System Message");
111  // addCustomTriggeredAction("runtime*4",
112  // "Halt");
113  // addCustomTriggeredAction("runtime",
114  // "System Message");
115  } //end test and force custom count list
116 
117  __SUP_COUT__ << "Constructor complete." << __E__;
118 
119 } // end constructor()
120 
121 //==============================================================================
122 ConsoleSupervisor::~ConsoleSupervisor(void) { destroy(); }
123 //==============================================================================
124 void ConsoleSupervisor::init(void)
125 {
126  // start mf msg listener
127  std::thread(
128  [](ConsoleSupervisor* cs) {
129  ConsoleSupervisor::messageFacilityReceiverWorkLoop(cs);
130  },
131  this)
132  .detach();
133 } // end init()
134 
135 //==============================================================================
136 void ConsoleSupervisor::destroy(void)
137 {
138  // called by destructor
139 } // end destroy()
140 
141 //==============================================================================
142 xoap::MessageReference ConsoleSupervisor::resetConsoleCounts(
143  xoap::MessageReference /*message*/)
144 {
145  __COUT_INFO__ << "Resetting Console Error/Warn/Info counts and preparing for new "
146  "first messages."
147  << __E__;
148 
149  // lockout the messages array for the remainder of the scope
150  // this guarantees the reading thread can safely access the messages
151  std::lock_guard<std::mutex> lock(messageMutex_);
152  errorCount_ = 0;
153  firstErrorMessageTime_ = 0;
154  lastErrorMessageTime_ = 0;
155  warnCount_ = 0;
156  firstWarnMessageTime_ = 0;
157  lastWarnMessageTime_ = 0;
158  infoCount_ = 0;
159  firstInfoMessageTime_ = 0;
160  lastInfoMessageTime_ = 0;
161 
162  return SOAPUtilities::makeSOAPMessageReference("Done");
163 } // end resetConsoleCounts()
164 
165 //==============================================================================
169 void ConsoleSupervisor::messageFacilityReceiverWorkLoop(ConsoleSupervisor* cs)
170 try
171 {
172  __COUT__ << "Starting workloop based on config file: " << QUIET_CFG_FILE << __E__;
173 
174  std::string configFile = QUIET_CFG_FILE;
175  FILE* fp = fopen(configFile.c_str(), "r");
176  if(!fp)
177  {
178  __SS__ << "File with port info could not be loaded: " << QUIET_CFG_FILE << __E__;
179  __COUT__ << "\n" << ss.str();
180  __SS_THROW__;
181  }
182  char tmp[100];
183  fgets(tmp, 100, fp); // receive port (ignore)
184  fgets(tmp, 100, fp); // destination port *** used here ***
185  int myport;
186  sscanf(tmp, "%*s %d", &myport);
187 
188  fgets(tmp, 100, fp); // destination ip *** used here ***
189  char myip[100];
190  sscanf(tmp, "%*s %s", myip);
191  fclose(fp);
192 
193  ReceiverSocket rsock(myip, myport); // Take Port from Configuration
194  try
195  {
196  rsock.initialize(0x1400000 /*socketReceiveBufferSize*/);
197  }
198  catch(...)
199  {
200  // lockout the messages array for the remainder of the scope
201  // this guarantees the reading thread can safely access the messages
202  std::lock_guard<std::mutex> lock(cs->messageMutex_);
203 
204  // NOTE: if we do not want this to be fatal, do not throw here, just print out
205 
206  if(1) // generate special message and throw for failed socket
207  {
208  __SS__ << "FATAL Console error. Could not initialize socket on port "
209  << myport
210  << ". Perhaps the port is already in use? Check for multiple stale "
211  "instances of otsdaq processes, or notify admins."
212  << " Multiple instances of otsdaq on the same node should be "
213  "possible, but port numbers must be unique."
214  << __E__;
215  __SS_THROW__;
216  }
217 
218  // generate special message to indicate failed socket
219  __SS__ << "FATAL Console error. Could not initialize socket on port " << myport
220  << ". Perhaps it is already in use? Exiting Console receive loop."
221  << __E__;
222  __COUT__ << ss.str();
223 
224  cs->messages_.emplace_back(CONSOLE_SPECIAL_ERROR + ss.str(),
225  cs->messageCount_++,
226  cs->priorityCustomTriggerList_);
227  //force time to now for auto-generated message
228  cs->messages_.back().setTime(time(0));
229  if(cs->messages_.size() > cs->maxMessageCount_)
230  {
231  cs->messages_.erase(cs->messages_.begin());
232  }
233 
234  return;
235  }
236 
237  std::string buffer;
238  int i = 0;
239  int heartbeatCount = 0;
240  int selfGeneratedMessageCount = 0;
241 
242  std::map<unsigned int, unsigned int> sourceLastSequenceID; // map from sourceID to
243  // lastSequenceID to
244  // identify missed messages
245  long long newSourceId;
246  uint32_t newSequenceId;
247  unsigned int c;
248 
249  // force a starting message
250  __COUT__ << "DEBUG messages look like this." << __E__;
251 
252  while(1)
253  {
254  // if receive succeeds display message
255 
256  //__COUTV__(i);
257 
258  if(rsock.receive(
259  buffer, 1 /*timeoutSeconds*/, 0 /*timeoutUSeconds*/, false /*verbose*/) !=
260  -1)
261  {
262  // use 1-byte "ping" to keep socket alive
263  if(buffer.size() == 1)
264  {
265  // std::cout << "Ping!" << __E__;
266  continue;
267  }
268 
269  if(i != 200)
270  {
271  __COUT__ << "Console has first message." << __E__;
272  i = 200; // mark so things are good for all time. (this indicates things
273  // are configured to be sent here)
274 
275  __COUT_INFO__ << "INFO messages look like this and mean 'something "
276  "occurred of which you should be aware.'"
277  << __E__;
278  __COUT_WARN__ << "WARNING messages look like this and mean 'something "
279  "suboptimal occurred and could be fixed.'"
280  << __E__;
281  __COUT_ERR__ << "ERROR messages look like this and mean 'something wrong "
282  "occurred and should be fixed.'"
283  << __E__;
284 
285  // //to debug special packets
286  // __SS__ << "???";
287  // cs->messages_[cs->writePointer_].set(CONSOLE_SPECIAL_ERROR
288  //+ ss.str(),
289  // cs->messageCount_++);
290  //
291  // if(++cs->writePointer_ == cs->messages_.size()) //handle
292  // wrap-around cs->writePointer_ = 0;
293  }
294 
295  if(selfGeneratedMessageCount)
296  --selfGeneratedMessageCount; // decrement internal message count
297  else // reset heartbeat if external messages are coming through
298  heartbeatCount = 0;
299 
300  //__COUT__ << buffer << __E__;
301 
302  // lockout the messages array for the remainder of the scope
303  // this guarantees the reading thread can safely access the messages
304  std::lock_guard<std::mutex> lock(cs->messageMutex_);
305 
306  // handle message stacking in packet
307  c = 0;
308  while(c < buffer.size())
309  {
310  // std::cout << "CONSOLE " << c << " sz=" << buffer.size() << " len=" <<
311  // strlen(&(buffer.c_str()[c])) << __E__;
312  cs->messages_.emplace_back(&(buffer.c_str()[c]),
313  cs->messageCount_++,
314  cs->priorityCustomTriggerList_);
315  if(cs->messages_.back().hasCustomTriggerMatchAction())
316  cs->customTriggerActionQueue_.push(
317  cs->messages_.back().getCustomTriggerMatch());
318 
319  //update system status
320  if(cs->messages_.back().getLevel() == "Error")
321  {
322  if(cs->errorCount_ == 0)
323  {
324  cs->firstErrorMessageTime_ = time(0);
325  cs->firstErrorMessage_ = cs->messages_.back().getMsg();
326  }
327 
328  cs->lastErrorMessageTime_ = time(0);
329  ++cs->errorCount_;
330  cs->lastErrorMessage_ = cs->messages_.back().getMsg();
331  }
332  else if(cs->messages_.back().getLevel() == "Warning")
333  {
334  if(cs->warnCount_ == 0)
335  {
336  cs->firstWarnMessageTime_ = time(0);
337  cs->firstWarnMessage_ = cs->messages_.back().getMsg();
338  }
339  cs->lastWarnMessageTime_ = time(0);
340  ++cs->warnCount_;
341  cs->lastWarnMessage_ = cs->messages_.back().getMsg();
342  }
343  else if(cs->messages_.back().getLevel() == "Info")
344  {
345  if(cs->infoCount_ == 0)
346  {
347  cs->firstInfoMessageTime_ = time(0);
348  cs->firstInfoMessage_ = cs->messages_.back().getMsg();
349  }
350  cs->lastInfoMessageTime_ = time(0);
351  ++cs->infoCount_;
352  cs->lastInfoMessage_ = cs->messages_.back().getMsg();
353  }
354 
355  // check if sequence ID is out of order
356  newSourceId = cs->messages_.back().getSourceIDAsNumber();
357  newSequenceId = cs->messages_.back().getSequenceIDAsNumber();
358 
359  // std::cout << rsock.getLastIncomingIPAddress() << ":" <<
360  // rsock.getLastIncomingPort() << ":newSourceId: " << newSourceId <<
361  // ":" << newSequenceId << __E__;
362 
363  if( // newSequenceId%1000 == 0 || //for debugging missed!
364  (newSourceId != -1 &&
365  sourceLastSequenceID.find(newSourceId) !=
366  sourceLastSequenceID
367  .end() && // ensure not first packet received
368  ((newSequenceId == 0 && sourceLastSequenceID[newSourceId] !=
369  (uint32_t)-1) || // wrap around case
370  newSequenceId !=
371  sourceLastSequenceID[newSourceId] + 1)) // normal sequence case
372  )
373  {
374  // missed some messages!
375  std::stringstream missedSs;
376  missedSs << "Console missed "
377  << (newSequenceId - 1) -
378  (sourceLastSequenceID[newSourceId] + 1) + 1
379  << " packet(s) [" << sourceLastSequenceID[newSourceId]
380  << " -> " << newSequenceId << "] from "
381  << cs->messages_.back().getSource() << ":" << newSourceId
382  << "! (uptime: " << cs->getSupervisorUptime() << " s)"
383  << __E__;
384  __SS__ << missedSs.str();
385  std::cout << ss.str();
386 
387  // seems like some messages at startup ALWAYs come out of order (i.e. 3, 4, 1)
388  // after startup grace period, generate special message to indicate missed packets
389  if(cs->getSupervisorUptime() > 20 /* seconds */ &&
390  sourceLastSequenceID[newSourceId] !=
391  1 && //veto missed error if sequenceID was 1 (this likely means a trace source was restarted)
392  newSequenceId !=
393  1) //veto missed error if sequenceID is 1 (this likely means a trace source was restarted)
394  {
395  cs->messages_.emplace_back(
396  CONSOLE_SPECIAL_WARNING + missedSs.str(),
397  cs->messageCount_++,
398  cs->priorityCustomTriggerList_);
399  //force time to now for auto-generated message
400  cs->messages_.back().setTime(time(0));
401  //Force a custom count because Console Label are ignored! if(cs->messages_.back().hasCustomTriggerMatchAction())
402  if(cs->priorityCustomTriggerList_.size())
403  {
404  cs->customTriggerActionQueue_.push(
405  cs->priorityCustomTriggerList_
406  [0]); //push newest action to back
407  cs->priorityCustomTriggerList_[0]
408  .occurrences++; //increment occurrences
409  cs->customTriggerActionQueue_.back()
410  .triggeredMessageCountIndex =
411  cs->messages_.back().getCount();
412  cs->messages_.back().setCustomTriggerMatch(
413  cs->customTriggerActionQueue_.back());
414  }
415  }
416  }
417 
418  // save the new last sequence ID
419  sourceLastSequenceID[newSourceId] = newSequenceId;
420 
421  size_t i = 0; //to skip over important messages
422  while(cs->messages_.size() > 0 &&
423  cs->messages_.size() > cs->maxMessageCount_)
424  {
425  if(i < cs->maxMessageCount_ /
426  2 && //in first half, keep important messages
427  (cs->messages_[i].getLevel() == "Error" ||
428  cs->messages_[i].getLevel() == "Warning" ||
429  cs->messages_[i].getLevel() == "Info"))
430  {
431  ++i;
432  continue;
433  }
434  //else erase expendable message
435  cs->messages_.erase(cs->messages_.begin() + i);
436  } //end loop to maintain max message count
437 
438  c += strlen(&(buffer.c_str()[c])) + 1;
439  } // end handle message stacking in packet
440  } // end received packet handling
441  else // idle network handling
442  {
443  if(i < 120) // if nothing received for 120 seconds, then something is wrong
444  // with Console configuration
445  ++i;
446 
447  sleep(1); // sleep one second, if timeout
448 
449  // every 60 heartbeatCount (2 seconds each = 1 sleep and 1 timeout) print a
450  // heartbeat message
451  if(i != 200 || // show first message, if not already a message
452  (heartbeatCount < 60 * 5 &&
453  heartbeatCount % 60 == 59)) // every ~2 min for first 5 messages
454  {
455  ++selfGeneratedMessageCount; // increment internal message count
456  __COUT__ << "Console is alive and waiting... (if no messages, next "
457  "heartbeat is in two minutes)"
458  << __E__;
459  }
460  else if(heartbeatCount % (60 * 30) == 59) // approx every hour
461  {
462  ++selfGeneratedMessageCount; // increment internal message count
463  __COUT__ << "Console is alive and waiting a long time... (if no "
464  "messages, next heartbeat is in one hour)"
465  << __E__;
466  }
467 
468  ++heartbeatCount;
469  } // end idle network handling
470 
471  // if nothing received for 2 minutes (120 seconds), then something is wrong with Console
472  // configuration after 5 seconds there is a self-send. Which will at least
473  // confirm configuration. OR if 5 generated messages and never cleared.. then
474  // the forwarding is not working.
475  if(i == 120 || selfGeneratedMessageCount == 5)
476  {
477  __COUTV__(i);
478  __COUTV__(selfGeneratedMessageCount);
479  __COUT__ << "No messages received at Console Supervisor. Exiting Console "
480  "messageFacilityReceiverWorkLoop"
481  << __E__;
482  break; // assume something wrong, and break loop
483  }
484 
485  if(cs->customTriggerActionQueue_.size() && !cs->customTriggerActionThreadExists_)
486  {
487  cs->customTriggerActionThreadExists_ = true;
488 
489  std::thread(
490  [](ConsoleSupervisor* c) {
491  ConsoleSupervisor::customTriggerActionThread(c);
492  },
493  cs)
494  .detach();
495  }
496 
497  } // end infinite loop
498 
499 } // end messageFacilityReceiverWorkLoop()
500 catch(const std::runtime_error& e)
501 {
502  __COUT_ERR__ << "Error caught at Console Supervisor thread: " << e.what() << __E__;
503 }
504 catch(...)
505 {
506  __COUT_ERR__ << "Unknown error caught at Console Supervisor thread." << __E__;
507 } // end messageFacilityReceiverWorkLoop() exception handling
508 
509 //==============================================================================
512 void ConsoleSupervisor::customTriggerActionThread(ConsoleSupervisor* cs)
513 try
514 {
515  __COUT__ << "Starting customTriggerActionThread" << __E__;
516  CustomTriggeredAction_t triggeredAction;
517  while(1) //infinite workloop
518  {
519  { //mutex scope:
520  // lockout the messages array for the remainder of the scope
521  // this guarantees the reading thread can safely access the action queue
522  std::lock_guard<std::mutex> lock(cs->messageMutex_);
523  if(cs->customTriggerActionQueue_.size())
524  {
525  triggeredAction = cs->customTriggerActionQueue_.front();
526  cs->customTriggerActionQueue_.pop(); //pop first/oldest element
527  }
528  } //end mutex scope
529 
530  if(triggeredAction.action.size())
531  {
532  __COUTS__(2) << "Handling action '" << triggeredAction.action
533  << "' on custom count search string: "
534  << StringMacros::vectorToString(triggeredAction.needleSubstrings,
535  {'*'})
536  << __E__;
537  cs->doTriggeredAction(triggeredAction);
538  }
539 
540  triggeredAction.action = ""; //clear action for next in queue
541  triggeredAction.triggeredMessageCountIndex =
542  -1; //clear triggered message ID for next in queue
543  sleep(2); //mostly sleep
544 
545  } //end infinite workloop
546 
547 } // end customTriggerActionThread()
548 catch(const std::runtime_error& e)
549 {
550  __COUT_ERR__ << "Error caught at Console Supervisor Action thread: " << e.what()
551  << __E__;
552 }
553 catch(...)
554 {
555  __COUT_ERR__ << "Unknown error caught at Console Supervisor Action thread." << __E__;
556 } // end customTriggerActionThread() exception handling
557 
558 //==============================================================================
559 void ConsoleSupervisor::doTriggeredAction(const CustomTriggeredAction_t& triggeredAction)
560 {
561  __SUP_COUT_INFO__ << "Launching Triggered Action '" << triggeredAction.action
562  << "' fired on custom count search string: "
563  << StringMacros::vectorToString(triggeredAction.needleSubstrings,
564  {'*'})
565  << __E__;
566 
567  //valid actions:
568  // Halt
569  // Stop
570  // Pause
571  // Soft Error
572  // Hard Error
573  // System Message
574  // Run Script
575 
576  if(CUSTOM_TRIGGER_ACTIONS.find(triggeredAction.action) ==
578  {
579  __SUP_SS__ << "Unrecognized triggered action '" << triggeredAction.action
580  << ",' valid actions are "
582  __SUP_SS_THROW__;
583  }
584 
585  //all FSM commands include a system message
586  if(triggeredAction.action != "Count Only")
587  theRemoteWebUsers_.sendSystemMessage(
588  "*" /* to all users*/,
589  "In the Console Supervisor, a custom count fired the action '" +
590  triggeredAction.action + "' on the search string '" +
591  StringMacros::vectorToString(triggeredAction.needleSubstrings, {'*'}) +
592  "'");
593 
594  if(triggeredAction.action == "Halt")
595  {
596  try
597  {
599  "Console-triggered FSM Halt", 0, 0);
600  }
601  catch(...)
602  {
603  theRemoteWebUsers_.sendSystemMessage(
604  "*" /* to all users*/,
605  "FSM Halt from Console Supervisor Triggered Action has failed!");
606  }
607  __SUP_COUTV__("FSM Halt triggered from console");
608  }
609  else if(triggeredAction.action == "Pause")
610  {
611  try
612  {
614  "Console-triggered FSM Pause", 1, 0);
615  }
616  catch(...)
617  {
618  theRemoteWebUsers_.sendSystemMessage(
619  "*" /* to all users*/,
620  "FSM Pause from Console Supervisor Triggered Action has failed!");
621  }
622  __SUP_COUTV__("FSM Pause triggered from console");
623  }
624  else if(triggeredAction.action == "Stop")
625  {
626  try
627  {
629  "Console-triggered FSM Stop", 0, 1);
630  }
631  catch(const std::exception& e)
632  {
633  theRemoteWebUsers_.sendSystemMessage(
634  "*" /* to all users*/,
635  "FSM Stop from Console Supervisor Triggered Action has failed!");
636  }
637  __SUP_COUTV__("FSM Stop triggered from console");
638  }
639  else if(triggeredAction.action == "Run Script")
640  {
641  std::string triggerScriptPath = "";
642  std::string scriptResult = "";
643  try
644  {
645  triggerScriptPath = __ENV__("OTS_CUSTOM_TRIGGER_SCRIPT");
646  triggerScriptPath = "source " + triggerScriptPath;
647  scriptResult = StringMacros::exec(triggerScriptPath.c_str());
648  __COUT_INFO__ << "The Script " << triggerScriptPath
649  << " Was launched, here is the result " << scriptResult;
650  }
651  catch(...)
652  {
653  __SS__ << "Trigger script path not defined! Please use environment variable "
654  "'OTS_CUSTOM_TRIGGER_SCRIPT' or contact admins."
655  << __E__;
656  __SS_THROW__;
657  }
658  }
659 
660 } // end doTriggeredAction()
661 
662 //==============================================================================
664 void ConsoleSupervisor::addCustomTriggeredAction(const std::string& triggerNeedle,
665  const std::string& triggerAction,
666  uint32_t priority, /* = -1 */
667  uint32_t triggerOnCount,
668  bool doLoop,
669  bool isArmed)
670 {
671  __SUP_COUTV__("Adding custom triggered action");
672  __SUP_COUTV__(triggerNeedle);
673  __SUP_COUTV__(triggerAction);
674  __SUP_COUTV__(priority);
675  __SUP_COUTV__(triggerOnCount);
676  __SUP_COUTV__(doLoop);
677  __SUP_COUTV__(isArmed);
678 
679  bool allAsterisks = true;
680  for(const auto& c : triggerNeedle)
681  if(c != '*')
682  {
683  allAsterisks = false;
684  break;
685  }
686  if(allAsterisks)
687  {
688  __SUP_SS__ << "Illegal empty Search String value for the new Custom Count and "
689  "Action! Please enter a valid Search String (* wildcards are "
690  "allowed, e.g. \"value = * seconds\")."
691  << __E__;
692  __SUP_SS_THROW__;
693  }
694 
695  //check if triggerNeedle already exists
696  uint32_t currentPriority = -1;
697  for(const auto& customTrigger : priorityCustomTriggerList_)
698  {
699  ++currentPriority; //inc first to get to 0
700  if(StringMacros::vectorToString(customTrigger.needleSubstrings, {'*'}) ==
701  triggerNeedle)
702  {
703  __SUP_SS__ << "Failure! Can not add Custom Count Search String that already "
704  "exists. Found '"
705  << triggerNeedle
706  << "' already existing at priority = " << currentPriority << __E__;
707  __SUP_SS_THROW__;
708  }
709  } //end check if already exists
710 
711  if(priority >= priorityCustomTriggerList_.size())
712  priority = priorityCustomTriggerList_.size(); //place at end
713  if(priority == 0 && triggerNeedle != CONSOLE_MISSED_NEEDLE)
714  {
715  __SUP_SS__ << "Illegal priority position of '" << priority
716  << "' requested. Please enter a priority value greater than 0. "
717  "Position 0 is reserved for identifying missing messages at the "
718  "Console Supervisor. Note: the action for missing messages, at "
719  "priority 0, may be customized by the user."
720  << __E__;
721  __SUP_SS_THROW__;
722  }
723  __SUP_COUTV__(priority);
724 
725  //valid actions:
726  // Halt
727  // Stop
728  // Pause
729  // Soft Error
730  // Hard Error
731  // System Message
732  // Run Script
733 
734  if(CUSTOM_TRIGGER_ACTIONS.find(triggerAction) == CUSTOM_TRIGGER_ACTIONS.end())
735  {
736  __SUP_SS__ << "Unrecognized triggered action '" << triggerAction
737  << ",' valid actions are "
739  __SUP_SS_THROW__;
740  }
741 
742  //insert new custom count at priority position
743  priorityCustomTriggerList_.insert(priorityCustomTriggerList_.begin() + priority,
744  CustomTriggeredAction_t());
745  // priorityCustomTriggerList_.push_back(CustomTriggeredAction_t());
746  // priorityCustomTriggerList_.back()
747 
748  //break up on substring
750  triggerNeedle,
751  priorityCustomTriggerList_[priority].needleSubstrings,
752  {'*'} /* delimiter */,
753  {} /* do not ignore whitespace */);
754  priorityCustomTriggerList_[priority].action = triggerAction;
755  priorityCustomTriggerList_[priority].triggerOnCount = triggerOnCount;
756  priorityCustomTriggerList_[priority].doLoop = doLoop;
757  priorityCustomTriggerList_[priority].isArmed = isArmed;
758 
759  __SUP_COUT__ << "Added custom count: '"
761  priorityCustomTriggerList_[priority].needleSubstrings, "*")
762  << "' at priority: " << priority
763  << " triggered every: " << triggerOnCount << " occurrences";
764  if(doLoop)
765  __SUP_COUT__ << " and will loop.";
766  else
767  __SUP_COUT__ << " and will not loop.";
768  __SUP_COUT__ << __E__;
769 
770 } // end addCustomTriggeredAction()
771 
772 //==============================================================================
776 uint32_t ConsoleSupervisor::modifyCustomTriggeredAction(const std::string& currentNeedle,
777  const std::string& modifyType,
778  const std::string& setNeedle,
779  const std::string& setAction,
780  uint32_t setPriority,
781  uint32_t setTriggerOnCount,
782  bool setDoLoop,
783  bool setIsArmed)
784 {
785  __SUP_COUTV__(currentNeedle);
786  __SUP_COUTV__(modifyType);
787  __SUP_COUTV__(setNeedle);
788  __SUP_COUTV__(setAction);
789  __SUP_COUTV__(setPriority);
790  __SUP_COUTV__(setTriggerOnCount);
791  __SUP_COUTV__(setDoLoop);
792  __SUP_COUTV__(setIsArmed);
793 
794  //find current priority position of currentNeedle
795  uint32_t currentPriority = -1;
796  bool found = false;
797  for(const auto& customTrigger : priorityCustomTriggerList_)
798  {
799  ++currentPriority; //inc first to get to 0, -1 indicates not found
800  if(StringMacros::vectorToString(customTrigger.needleSubstrings, {'*'}) ==
801  currentNeedle)
802  {
803  found = true;
804  break; //found
805  }
806  }
807 
808  __SUP_COUTV__(currentPriority);
809  if(!found)
810  {
811  __SUP_SS__ << "Attempt to modify Custom Count Search String failed. Could not "
812  "find specified Search String '"
813  << currentNeedle << "' in prioritized list." << __E__;
814  __SUP_SS_THROW__;
815  }
816 
817  if(modifyType == "Deletion")
818  {
819  if(currentPriority == 0)
820  {
821  __SUP_SS__ << "Illegal deletion requested of priority position 0. Position 0 "
822  "is reserved for identifying missing messages at the Console "
823  "Supervisor. Note: the action of priority 0 may be customized "
824  "by the user, but it can not be deleted."
825  << __E__;
826  __SUP_SS_THROW__;
827  }
828 
829  __SUP_COUT__ << "Deleting custom count: "
831  priorityCustomTriggerList_[currentPriority].needleSubstrings,
832  {'*'})
833  << " w/action: "
834  << priorityCustomTriggerList_[currentPriority].action
835  << " and priority: " << currentPriority << __E__;
836  priorityCustomTriggerList_.erase(priorityCustomTriggerList_.begin() +
837  currentPriority);
838  return -1;
839  }
840 
841  if(modifyType == "Priority" || modifyType == "All")
842  {
843  if(setPriority >= priorityCustomTriggerList_.size())
844  setPriority = priorityCustomTriggerList_.size(); //place at end
845  if(setPriority == 0 && setNeedle != CONSOLE_MISSED_NEEDLE)
846  {
847  __SUP_SS__ << "Illegal priority position of '" << setPriority
848  << "' requested. Position 0 is reserved for identifying missing "
849  "messages at the Console Supervisor. Note: the action of "
850  "priority 0 may be customized by the user."
851  << __E__;
852  __SUP_SS_THROW__;
853  }
854  }
855  else //keep existing
856  setPriority = currentPriority;
857 
858  if(modifyType == "Action" || modifyType == "All")
859  {
860  //valid actions:
861  // Halt
862  // Stop
863  // Pause
864  // Soft Error
865  // Hard Error
866  // System Message
867 
868  if(CUSTOM_TRIGGER_ACTIONS.find(setAction) == CUSTOM_TRIGGER_ACTIONS.end())
869  {
870  __SUP_SS__ << "Unrecognized custom count action '" << setAction
871  << ",' valid actions are "
873  __SUP_SS_THROW__;
874  }
875  //modify existing action
876  priorityCustomTriggerList_[currentPriority].action = setAction;
877  }
878 
879  if(modifyType == "Search String" || modifyType == "All")
880  {
881  //modify existing needle
882  priorityCustomTriggerList_[currentPriority].needleSubstrings.clear();
884  setNeedle,
885  priorityCustomTriggerList_[currentPriority].needleSubstrings,
886  {'*'} /* delimiter */,
887  {} /* do not ignore whitespace */);
888  }
889 
890  if(modifyType == "Trigger on Count" || modifyType == "All")
891  {
892  //modify existing action
893  priorityCustomTriggerList_[currentPriority].triggerOnCount = setTriggerOnCount;
894  }
895  if(modifyType == "Do Loop" || modifyType == "All")
896  {
897  //modify existing action
898  priorityCustomTriggerList_[currentPriority].doLoop = setDoLoop;
899  }
900  if(modifyType == "Arm Trigger" || modifyType == "All")
901  {
902  //modify existing action
903  priorityCustomTriggerList_[currentPriority].isArmed = setIsArmed;
904  }
905 
906  if(currentPriority != setPriority) //then need to copy
907  {
908  //insert new custom count at priority position
909  priorityCustomTriggerList_.insert(
910  priorityCustomTriggerList_.begin() + setPriority,
911  priorityCustomTriggerList_[currentPriority]);
912 
913  //delete from old position
914  if(currentPriority >= setPriority) //then increment after insert
915  ++currentPriority;
916 
917  priorityCustomTriggerList_.erase(priorityCustomTriggerList_.begin() +
918  currentPriority);
919 
920  if(currentPriority < setPriority) //then decrement after delete
921  --setPriority;
922  }
923 
924  __SUP_COUT__ << "Modified '" << modifyType << "' custom count: "
926  priorityCustomTriggerList_[setPriority].needleSubstrings, {'*'})
927  << " now w/action: " << priorityCustomTriggerList_[setPriority].action
928  << " and at priority: " << setPriority << __E__;
929 
930  return setPriority;
931 } // end modifyCustomTriggeredAction()
932 
933 //==============================================================================
934 void ConsoleSupervisor::loadCustomCountList()
935 {
936  // TODO: Migrate to a more robust read/write method
937  __SUP_COUT__ << "loadCustomCountList() from "
938  << USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME << __E__;
939 
940  FILE* fp = fopen((USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME).c_str(), "r");
941  if(!fp)
942  {
943  __SUP_COUT__ << "Ignoring missing Custom Count list file at path: "
944  << (USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME) << __E__;
945  return;
946  }
947  priorityCustomTriggerList_.clear();
948 
949  char line[1000]; //do not allow larger than 1000 chars!
950  uint32_t i = 0;
951  std::string needle;
952  std::string action;
953  size_t priority = 0;
954  size_t triggerOnCount = 0;
955  bool doLoop = false;
956  bool isArmed = false;
957  while(fgets(line, 1000, fp))
958  {
959  ++i;
960 
961  //ignore new line
962  if(strlen(line))
963  line[strlen(line) - 1] = '\0';
964 
965  __SUP_COUTV__(i);
966  __SUP_COUTV__(line);
967 
968  if(i == 1) //needle
969  needle = line;
970  else if(i == 2)
971  action = line;
972  else if(i == 3)
973  priority = std::stoi(line);
974  else if(i == 4)
975  triggerOnCount = std::stoi(line);
976  else if(i == 5)
977  doLoop = std::stoi(line) > 0 ? true : false;
978  else if(i == 6) // last line
979  {
980  isArmed = std::stoi(line) > 0 ? true : false;
981  __SUP_COUTTV__(needle);
982  __SUP_COUTTV__(priority);
983  __SUP_COUTTV__(action);
984  __SUP_COUTTV__(triggerOnCount);
985  __SUP_COUTTV__(doLoop);
986  __SUP_COUTTV__(isArmed);
987  if(i == 1 &&
988  needle !=
989  CONSOLE_MISSED_NEEDLE) //then force missed Console message as priority 0
990  addCustomTriggeredAction(
991  CONSOLE_MISSED_NEEDLE, "System Message", 0, 1, false, true);
992  addCustomTriggeredAction(
993  needle, action, priority, triggerOnCount, doLoop, isArmed);
994  i = 0; //reset for next entry
995  }
996  }
997  fclose(fp);
998 
999 } // end loadCustomCountList()
1000 
1001 //==============================================================================
1002 void ConsoleSupervisor::saveCustomCountList()
1003 {
1004  __SUP_COUT__ << "saveCustomCountList()" << __E__;
1005 
1006  FILE* fp = fopen((USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME).c_str(), "w");
1007  if(!fp)
1008  {
1009  __SUP_SS__ << "Failed to create Custom Count list file at path: "
1010  << (USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME) << __E__;
1011  __SUP_SS_THROW__;
1012  }
1013  unsigned int priority = 0;
1014  for(auto& customCount : priorityCustomTriggerList_)
1015  {
1016  fprintf(fp,
1017  (StringMacros::vectorToString(customCount.needleSubstrings, {'*'}) + "\n")
1018  .c_str());
1019  fprintf(fp, "%s\n", customCount.action.c_str());
1020  fprintf(fp, "%d\n", priority);
1021  fprintf(fp, "%zu\n", customCount.triggerOnCount);
1022  fprintf(fp, "%s\n", customCount.doLoop ? "1" : "0");
1023  fprintf(fp, "%s\n", customCount.isArmed ? "1" : "0");
1024  ++priority;
1025  }
1026  fclose(fp);
1027 } // end saveCustomCountList()
1028 
1029 //==============================================================================
1030 void ConsoleSupervisor::defaultPage(xgi::Input* /*in*/, xgi::Output* out)
1031 {
1032  // __SUP_COUT__ << "ApplicationDescriptor LID="
1033  // << getApplicationDescriptor()->getLocalId() << __E__;
1034  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
1035  "src='/WebPath/html/Console.html?urn="
1036  << getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
1037 } // end defaultPage()
1038 
1039 //==============================================================================
1043 {
1044  CorePropertySupervisorBase::setSupervisorProperty(
1045  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
1046  "GetConsoleMsgs");
1047 } // end forceSupervisorPropertyValues()
1048 
1049 //==============================================================================
1053 void ConsoleSupervisor::request(const std::string& requestType,
1054  cgicc::Cgicc& cgiIn,
1055  HttpXmlDocument& xmlOut,
1056  const WebUsers::RequestUserInfo& userInfo)
1057 {
1058  //__SUP_COUT__ << "requestType " << requestType << __E__;
1059 
1060  // Commands:
1061  // GetConsoleMsgs
1062  // PrependHistoricMessages
1063  // SaveUserPreferences
1064  // LoadUserPreferences
1065  // GetTraceLevels
1066  // SetTraceLevels
1067  // GetTriggerStatus
1068  // SetTriggerEnable
1069  // ResetTRACE
1070  // EnableTRACE
1071  // GetTraceSnapshot
1072  // GetCustomCountsAndActions
1073  // AddCustomCountsAndAction
1074  // ModifyCustomCountsAndAction
1075 
1076  // Note: to report to logbook admin status use
1077  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,refreshTempStr_);
1078 
1079  if(requestType == "GetConsoleMsgs")
1080  {
1081  // lindex of -1 means first time and user just gets update lcount and lindex
1082  std::string lastUpdateCountStr = CgiDataUtilities::postData(cgiIn, "lcount");
1083 
1084  if(lastUpdateCountStr == "")
1085  {
1086  __SUP_COUT_ERR__ << "Invalid Parameters! lastUpdateCount="
1087  << lastUpdateCountStr << __E__;
1088  xmlOut.addTextElementToData("Error",
1089  "Error - Invalid parameters for GetConsoleMsgs.");
1090  return;
1091  }
1092 
1093  size_t lastUpdateCount = std::stoull(lastUpdateCountStr);
1094 
1095  // __SUP_COUT__ << "lastUpdateCount=" << lastUpdateCount << __E__;
1096 
1097  insertMessageRefresh(&xmlOut, lastUpdateCount);
1098  }
1099  else if(requestType == "PrependHistoricMessages")
1100  {
1101  size_t earliestOnhandMessageCount =
1102  CgiDataUtilities::postDataAsInt(cgiIn, "earlyCount");
1103  __SUP_COUTV__(earliestOnhandMessageCount);
1104  prependHistoricMessages(&xmlOut, earliestOnhandMessageCount);
1105  }
1106  else if(requestType == "SaveUserPreferences")
1107  {
1108  int colorIndex = CgiDataUtilities::postDataAsInt(cgiIn, "colorIndex");
1109  int showSideBar = CgiDataUtilities::postDataAsInt(cgiIn, "showSideBar");
1110  int noWrap = CgiDataUtilities::postDataAsInt(cgiIn, "noWrap");
1111  int messageOnly = CgiDataUtilities::postDataAsInt(cgiIn, "messageOnly");
1112  int hideLineNumers = CgiDataUtilities::postDataAsInt(cgiIn, "hideLineNumers");
1113 
1114  // __SUP_COUT__ << "requestType " << requestType << __E__;
1115  // __SUP_COUT__ << "colorIndex: " << colorIndex << __E__;
1116  // __SUP_COUT__ << "showSideBar: " << showSideBar << __E__;
1117  // __SUP_COUT__ << "noWrap: " << noWrap << __E__;
1118  // __SUP_COUT__ << "messageOnly: " << messageOnly << __E__;
1119  // __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << __E__;
1120 
1121  if(userInfo.username_ == "") // should never happen?
1122  {
1123  __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_
1124  << __E__;
1125  xmlOut.addTextElementToData("Error",
1126  "Error - InvauserInfo.username_user found.");
1127  return;
1128  }
1129 
1130  std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." +
1131  (std::string)USERS_PREFERENCES_FILETYPE;
1132 
1133  // __SUP_COUT__ << "Save preferences: " << fn << __E__;
1134  FILE* fp = fopen(fn.c_str(), "w");
1135  if(!fp)
1136  {
1137  __SS__;
1138  __THROW__(ss.str() + "Could not open file: " + fn);
1139  }
1140  fprintf(fp, "colorIndex %d\n", colorIndex);
1141  fprintf(fp, "showSideBar %d\n", showSideBar);
1142  fprintf(fp, "noWrap %d\n", noWrap);
1143  fprintf(fp, "messageOnly %d\n", messageOnly);
1144  fprintf(fp, "hideLineNumers %d\n", hideLineNumers);
1145  fclose(fp);
1146  }
1147  else if(requestType == "LoadUserPreferences")
1148  {
1149  // __SUP_COUT__ << "requestType " << requestType << __E__;
1150 
1151  unsigned int colorIndex, showSideBar, noWrap, messageOnly, hideLineNumers;
1152 
1153  if(userInfo.username_ == "") // should never happen?
1154  {
1155  __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_
1156  << __E__;
1157  xmlOut.addTextElementToData("Error", "Error - Invalid user found.");
1158  return;
1159  }
1160 
1161  std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." +
1162  (std::string)USERS_PREFERENCES_FILETYPE;
1163 
1164  // __SUP_COUT__ << "Load preferences: " << fn << __E__;
1165 
1166  FILE* fp = fopen(fn.c_str(), "r");
1167  if(!fp)
1168  {
1169  // return defaults
1170  __SUP_COUT__ << "Returning defaults." << __E__;
1171  xmlOut.addTextElementToData("colorIndex", "0");
1172  xmlOut.addTextElementToData("showSideBar", "0");
1173  xmlOut.addTextElementToData("noWrap", "1");
1174  xmlOut.addTextElementToData("messageOnly", "0");
1175  xmlOut.addTextElementToData("hideLineNumers", "1");
1176  return;
1177  }
1178  fscanf(fp, "%*s %u", &colorIndex);
1179  fscanf(fp, "%*s %u", &showSideBar);
1180  fscanf(fp, "%*s %u", &noWrap);
1181  fscanf(fp, "%*s %u", &messageOnly);
1182  fscanf(fp, "%*s %u", &hideLineNumers);
1183  fclose(fp);
1184  // __SUP_COUT__ << "colorIndex: " << colorIndex << __E__;
1185  // __SUP_COUT__ << "showSideBar: " << showSideBar << __E__;
1186  // __SUP_COUT__ << "noWrap: " << noWrap << __E__;
1187  // __SUP_COUT__ << "messageOnly: " << messageOnly << __E__;
1188  // __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << __E__;
1189 
1190  char tmpStr[20];
1191  sprintf(tmpStr, "%u", colorIndex);
1192  xmlOut.addTextElementToData("colorIndex", tmpStr);
1193  sprintf(tmpStr, "%u", showSideBar);
1194  xmlOut.addTextElementToData("showSideBar", tmpStr);
1195  sprintf(tmpStr, "%u", noWrap);
1196  xmlOut.addTextElementToData("noWrap", tmpStr);
1197  sprintf(tmpStr, "%u", messageOnly);
1198  xmlOut.addTextElementToData("messageOnly", tmpStr);
1199  sprintf(tmpStr, "%u", hideLineNumers);
1200  xmlOut.addTextElementToData("hideLineNumers", tmpStr);
1201  }
1202  else if(requestType == "GetTraceLevels")
1203  {
1204  __SUP_COUT__ << "requestType " << requestType << __E__;
1205 
1206  SOAPParameters txParameters; // params for xoap to send
1207  txParameters.addParameter("Request", "GetTraceLevels");
1208 
1209  SOAPParameters rxParameters; // params for xoap to recv
1210  rxParameters.addParameter("Command");
1211  rxParameters.addParameter("Error");
1212  rxParameters.addParameter("TRACEHostnameList");
1213  rxParameters.addParameter("TRACEList");
1214 
1215  traceMapToXDAQHostname_.clear(); // reset
1216 
1217  std::string traceList = "";
1218  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1219  for(const auto& appInfo : allTraceApps)
1220  {
1221  __SUP_COUT__ << "Supervisor hostname = " << appInfo.first << "/"
1222  << appInfo.second.getId()
1223  << " name = " << appInfo.second.getName()
1224  << " class = " << appInfo.second.getClass()
1225  << " hostname = " << appInfo.second.getHostname() << __E__;
1226  try
1227  {
1228  xoap::MessageReference retMsg =
1229  SOAPMessenger::sendWithSOAPReply(appInfo.second.getDescriptor(),
1230  "TRACESupervisorRequest",
1231  txParameters);
1232  SOAPUtilities::receive(retMsg, rxParameters);
1233  __SUP_COUT__ << "Received TRACE response: "
1234  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1235  << SOAPUtilities::translate(retMsg) << __E__;
1236 
1237  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1238  {
1239  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1240  "hostname = "
1241  << appInfo.first << "/" << appInfo.second.getId()
1242  << " name = " << appInfo.second.getName()
1243  << " class = " << appInfo.second.getClass()
1244  << " hostname = " << appInfo.second.getHostname() << __E__;
1245  __SUP_SS_THROW__;
1246  }
1247  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1248  {
1249  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1250  << __E__;
1251  __SUP_SS_THROW__;
1252  }
1253  }
1254  catch(const xdaq::exception::Exception& e)
1255  {
1256  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1257  << appInfo.second.getId()
1258  << " name = " << appInfo.second.getName() << ". \n\n"
1259  << e.what() << __E__;
1260  //do not throw exception, because unable to set levels when some Supervisors are down
1261  //__SUP_SS_THROW__;
1262  __SUP_COUT_ERR__ << ss.str();
1263  continue; //skip bad Supervisor
1264  }
1265 
1266  std::vector<std::string> traceHostnameArr;
1267  __COUTTV__(rxParameters.getValue("TRACEHostnameList"));
1269  rxParameters.getValue("TRACEHostnameList"), traceHostnameArr, {';'});
1270  for(const auto& traceHostname : traceHostnameArr)
1271  {
1272  if(traceHostname == "")
1273  continue; //skip blanks
1274  traceMapToXDAQHostname_[traceHostname] = appInfo.first;
1275  }
1276 
1277  // traceList += ";" + appInfo.first; //insert xdaq context version of
1278  // name
1279  // //FIXME and create mapp from user's typed in xdaq
1280  // context name to TRACE hostname resolution
1281 
1282  __COUTTV__(rxParameters.getValue("TRACEList"));
1283  traceList += rxParameters.getValue("TRACEList");
1284 
1285  } // end app get TRACE loop
1286  __SUP_COUT__ << "TRACE hostname map received: \n"
1287  << StringMacros::mapToString(traceMapToXDAQHostname_) << __E__;
1288  __SUP_COUT__ << "TRACE List received: \n" << traceList << __E__;
1289  xmlOut.addTextElementToData("traceList", traceList);
1290  } // end GetTraceLevels
1291  else if(requestType == "SetTraceLevels")
1292  {
1293  __SUP_COUT__ << "requestType " << requestType << __E__;
1294 
1295  std::string individualValues =
1296  CgiDataUtilities::postData(cgiIn, "individualValues");
1297  std::string hostLabelMap = CgiDataUtilities::postData(cgiIn, "hostLabelMap");
1298  std::string setMode = CgiDataUtilities::postData(cgiIn, "setMode");
1299  std::string setValueMSB = CgiDataUtilities::postData(cgiIn, "setValueMSB");
1300  std::string setValueLSB = CgiDataUtilities::postData(cgiIn, "setValueLSB");
1301 
1302  __SUP_COUTV__(individualValues);
1303  __SUP_COUTV__(setMode);
1304  // set modes: SLOW, FAST, TRIGGER
1305  __SUP_COUTV__(setValueMSB);
1306  __SUP_COUTV__(setValueLSB);
1307 
1308  std::map<std::string /*host*/, std::string /*labelArr*/> hostToLabelMap;
1309 
1310  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1311 
1312  SOAPParameters rxParameters; // params for xoap to recv
1313  rxParameters.addParameter("Command");
1314  rxParameters.addParameter("Error");
1315  rxParameters.addParameter("TRACEList");
1316 
1317  std::string modifiedTraceList = "";
1318  std::string xdaqHostname;
1319  StringMacros::getMapFromString(hostLabelMap, hostToLabelMap, {';'}, {':'});
1320  for(auto& hostLabelsPair : hostToLabelMap)
1321  {
1322  // identify artdaq hosts to go through ARTDAQ supervisor
1323  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1324  __SUP_COUTV__(hostLabelsPair.first);
1325  __SUP_COUTV__(hostLabelsPair.second);
1326 
1327  // use map to convert to xdaq host
1328  try
1329  {
1330  xdaqHostname = traceMapToXDAQHostname_.at(hostLabelsPair.first);
1331  }
1332  catch(...)
1333  {
1334  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1335  << hostLabelsPair.first << "' to xdaq Context hostname."
1336  << __E__;
1337  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1338  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1339  << __E__;
1340  __SUP_SS_THROW__;
1341  }
1342 
1343  __SUP_COUTV__(xdaqHostname);
1344 
1345  auto& appInfo = allTraceApps.at(xdaqHostname);
1346  __SUP_COUT__ << "Supervisor hostname = " << hostLabelsPair.first << "/"
1347  << xdaqHostname << ":" << appInfo.getId()
1348  << " name = " << appInfo.getName()
1349  << " class = " << appInfo.getClass()
1350  << " hostname = " << appInfo.getHostname() << __E__;
1351  try
1352  {
1353  SOAPParameters txParameters; // params for xoap to send
1354  txParameters.addParameter("Request", "SetTraceLevels");
1355  txParameters.addParameter("IndividualValues", individualValues);
1356  txParameters.addParameter("Host", hostLabelsPair.first);
1357  txParameters.addParameter("SetMode", setMode);
1358  txParameters.addParameter("Labels", hostLabelsPair.second);
1359  txParameters.addParameter("SetValueMSB", setValueMSB);
1360  txParameters.addParameter("SetValueLSB", setValueLSB);
1361 
1362  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1363  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1364  SOAPUtilities::receive(retMsg, rxParameters);
1365  __SUP_COUT__ << "Received TRACE response: "
1366  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1367  << SOAPUtilities::translate(retMsg) << __E__;
1368 
1369  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1370  {
1371  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1372  "hostname = "
1373  << hostLabelsPair.first << "/" << appInfo.getId()
1374  << " name = " << appInfo.getName()
1375  << " class = " << appInfo.getClass()
1376  << " hostname = " << appInfo.getHostname() << __E__;
1377  __SUP_SS_THROW__;
1378  }
1379  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1380  {
1381  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1382  << __E__;
1383  __SUP_SS_THROW__;
1384  }
1385  }
1386  catch(const xdaq::exception::Exception& e)
1387  {
1388  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1389  << appInfo.getId() << " name = " << appInfo.getName()
1390  << ". \n\n"
1391  << e.what() << __E__;
1392  __SUP_SS_THROW__;
1393  }
1394 
1395  modifiedTraceList +=
1396  ";" + hostLabelsPair.first; // insert xdaq context version of name
1397  // FIXME and create mapp from user's typed in xdaq
1398  // context name to TRACE hostname resolution
1399 
1400  modifiedTraceList += rxParameters.getValue("TRACEList");
1401 
1402  } // end host set TRACE loop
1403 
1404  __SUP_COUT__ << "mod'd TRACE List received: \n" << modifiedTraceList << __E__;
1405  xmlOut.addTextElementToData("modTraceList", modifiedTraceList);
1406  } // end SetTraceLevels
1407  else if(requestType == "GetTriggerStatus")
1408  {
1409  __SUP_COUT__ << "requestType " << requestType << __E__;
1410  SOAPParameters txParameters; // params for xoap to send
1411  txParameters.addParameter("Request", "GetTriggerStatus");
1412 
1413  SOAPParameters rxParameters; // params for xoap to recv
1414  rxParameters.addParameter("Command");
1415  rxParameters.addParameter("Error");
1416  rxParameters.addParameter("TRACETriggerStatus");
1417 
1418  std::string traceTriggerStatus = "";
1419  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1420  for(const auto& appInfo : allTraceApps)
1421  {
1422  __SUP_COUT__ << "Supervisor hostname = " << appInfo.first << "/"
1423  << appInfo.second.getId()
1424  << " name = " << appInfo.second.getName()
1425  << " class = " << appInfo.second.getClass()
1426  << " hostname = " << appInfo.second.getHostname() << __E__;
1427  try
1428  {
1429  xoap::MessageReference retMsg =
1430  SOAPMessenger::sendWithSOAPReply(appInfo.second.getDescriptor(),
1431  "TRACESupervisorRequest",
1432  txParameters);
1433  SOAPUtilities::receive(retMsg, rxParameters);
1434  __SUP_COUT__ << "Received TRACE response: "
1435  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1436  << SOAPUtilities::translate(retMsg) << __E__;
1437 
1438  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1439  {
1440  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1441  "hostname = "
1442  << appInfo.first << "/" << appInfo.second.getId()
1443  << " name = " << appInfo.second.getName()
1444  << " class = " << appInfo.second.getClass()
1445  << " hostname = " << appInfo.second.getHostname() << __E__;
1446  __SUP_SS_THROW__;
1447  }
1448  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1449  {
1450  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1451  << __E__;
1452  __SUP_SS_THROW__;
1453  }
1454  }
1455  catch(const xdaq::exception::Exception& e)
1456  {
1457  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1458  << appInfo.second.getId()
1459  << " name = " << appInfo.second.getName() << ". \n\n"
1460  << e.what() << __E__;
1461  __SUP_SS_THROW__;
1462  }
1463 
1464  traceTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1465 
1466  } // end app get TRACE loop
1467  __SUP_COUT__ << "TRACE Trigger Status received: \n"
1468  << traceTriggerStatus << __E__;
1469  xmlOut.addTextElementToData("traceTriggerStatus", traceTriggerStatus);
1470  } // end GetTriggerStatus
1471  else if(requestType == "SetTriggerEnable")
1472  {
1473  __SUP_COUT__ << "requestType " << requestType << __E__;
1474 
1475  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1476 
1477  __SUP_COUTV__(hostList);
1478 
1479  std::vector<std::string /*host*/> hosts;
1480 
1481  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1482 
1483  SOAPParameters rxParameters; // params for xoap to recv
1484  rxParameters.addParameter("Command");
1485  rxParameters.addParameter("Error");
1486  rxParameters.addParameter("TRACETriggerStatus");
1487 
1488  std::string modifiedTriggerStatus = "";
1489  std::string xdaqHostname;
1490  StringMacros::getVectorFromString(hostList, hosts, {';'});
1491  for(auto& host : hosts)
1492  {
1493  // identify artdaq hosts to go through ARTDAQ supervisor
1494  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1495  __SUP_COUTV__(host);
1496  if(host.size() < 3)
1497  continue; // skip bad hostnames
1498 
1499  // use map to convert to xdaq host
1500  try
1501  {
1502  xdaqHostname = traceMapToXDAQHostname_.at(host);
1503  }
1504  catch(...)
1505  {
1506  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1507  << host << "' to xdaq Context hostname." << __E__;
1508  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1509  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1510  << __E__;
1511  __SUP_SS_THROW__;
1512  }
1513 
1514  __SUP_COUTV__(xdaqHostname);
1515 
1516  auto& appInfo = allTraceApps.at(xdaqHostname);
1517  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1518  << appInfo.getId() << " name = " << appInfo.getName()
1519  << " class = " << appInfo.getClass()
1520  << " hostname = " << appInfo.getHostname() << __E__;
1521  try
1522  {
1523  SOAPParameters txParameters; // params for xoap to send
1524  txParameters.addParameter("Request", "SetTriggerEnable");
1525  txParameters.addParameter("Host", host);
1526 
1527  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1528  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1529  SOAPUtilities::receive(retMsg, rxParameters);
1530  __SUP_COUT__ << "Received TRACE response: "
1531  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1532  << SOAPUtilities::translate(retMsg) << __E__;
1533 
1534  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1535  {
1536  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1537  "hostname = "
1538  << host << "/" << appInfo.getId()
1539  << " name = " << appInfo.getName()
1540  << " class = " << appInfo.getClass()
1541  << " hostname = " << appInfo.getHostname() << __E__;
1542  __SUP_SS_THROW__;
1543  }
1544  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1545  {
1546  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1547  << __E__;
1548  __SUP_SS_THROW__;
1549  }
1550  }
1551  catch(const xdaq::exception::Exception& e)
1552  {
1553  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1554  << appInfo.getId() << " name = " << appInfo.getName()
1555  << ". \n\n"
1556  << e.what() << __E__;
1557  __SUP_SS_THROW__;
1558  }
1559 
1560  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1561  } // end host set TRACE loop
1562 
1563  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1564  << modifiedTriggerStatus << __E__;
1565  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1566  } // end SetTriggerEnable
1567  else if(requestType == "ResetTRACE")
1568  {
1569  __SUP_COUT__ << "requestType " << requestType << __E__;
1570 
1571  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1572 
1573  __SUP_COUTV__(hostList);
1574 
1575  std::vector<std::string /*host*/> hosts;
1576 
1577  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1578 
1579  SOAPParameters rxParameters; // params for xoap to recv
1580  rxParameters.addParameter("Command");
1581  rxParameters.addParameter("Error");
1582  rxParameters.addParameter("TRACETriggerStatus");
1583 
1584  std::string modifiedTriggerStatus = "";
1585  std::string xdaqHostname;
1586  StringMacros::getVectorFromString(hostList, hosts, {';'});
1587  for(auto& host : hosts)
1588  {
1589  // identify artdaq hosts to go through ARTDAQ supervisor
1590  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1591  __SUP_COUTV__(host);
1592  if(host.size() < 3)
1593  continue; // skip bad hostnames
1594 
1595  // use map to convert to xdaq host
1596  try
1597  {
1598  xdaqHostname = traceMapToXDAQHostname_.at(host);
1599  }
1600  catch(...)
1601  {
1602  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1603  << host << "' to xdaq Context hostname." << __E__;
1604  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1605  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1606  << __E__;
1607  __SUP_SS_THROW__;
1608  }
1609 
1610  __SUP_COUTV__(xdaqHostname);
1611 
1612  auto& appInfo = allTraceApps.at(xdaqHostname);
1613  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1614  << appInfo.getId() << " name = " << appInfo.getName()
1615  << " class = " << appInfo.getClass()
1616  << " hostname = " << appInfo.getHostname() << __E__;
1617  try
1618  {
1619  SOAPParameters txParameters; // params for xoap to send
1620  txParameters.addParameter("Request", "ResetTRACE");
1621  txParameters.addParameter("Host", host);
1622 
1623  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1624  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1625  SOAPUtilities::receive(retMsg, rxParameters);
1626  __SUP_COUT__ << "Received TRACE response: "
1627  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1628  << SOAPUtilities::translate(retMsg) << __E__;
1629 
1630  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1631  {
1632  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1633  "hostname = "
1634  << host << "/" << appInfo.getId()
1635  << " name = " << appInfo.getName()
1636  << " class = " << appInfo.getClass()
1637  << " hostname = " << appInfo.getHostname() << __E__;
1638  __SUP_SS_THROW__;
1639  }
1640  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1641  {
1642  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1643  << __E__;
1644  __SUP_SS_THROW__;
1645  }
1646  }
1647  catch(const xdaq::exception::Exception& e)
1648  {
1649  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1650  << appInfo.getId() << " name = " << appInfo.getName()
1651  << ". \n\n"
1652  << e.what() << __E__;
1653  __SUP_SS_THROW__;
1654  }
1655 
1656  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1657  } // end host set TRACE loop
1658 
1659  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1660  << modifiedTriggerStatus << __E__;
1661  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1662  } // end ResetTRACE
1663  else if(requestType == "EnableTRACE")
1664  {
1665  __SUP_COUT__ << "requestType " << requestType << __E__;
1666 
1667  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1668  std::string enable = CgiDataUtilities::postData(cgiIn, "enable");
1669 
1670  __SUP_COUTV__(hostList);
1671  __SUP_COUTV__(enable);
1672 
1673  std::vector<std::string /*host*/> hosts;
1674 
1675  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1676 
1677  SOAPParameters rxParameters; // params for xoap to recv
1678  rxParameters.addParameter("Command");
1679  rxParameters.addParameter("Error");
1680  rxParameters.addParameter("TRACETriggerStatus");
1681 
1682  std::string modifiedTriggerStatus = "";
1683  std::string xdaqHostname;
1684  StringMacros::getVectorFromString(hostList, hosts, {';'});
1685  for(auto& host : hosts)
1686  {
1687  // identify artdaq hosts to go through ARTDAQ supervisor
1688  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1689  __SUP_COUTV__(host);
1690  if(host.size() < 3)
1691  continue; // skip bad hostnames
1692 
1693  // use map to convert to xdaq host
1694  try
1695  {
1696  xdaqHostname = traceMapToXDAQHostname_.at(host);
1697  }
1698  catch(...)
1699  {
1700  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1701  << host << "' to xdaq Context hostname." << __E__;
1702  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1703  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1704  << __E__;
1705  __SUP_SS_THROW__;
1706  }
1707 
1708  __SUP_COUTV__(xdaqHostname);
1709 
1710  auto& appInfo = allTraceApps.at(xdaqHostname);
1711  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1712  << appInfo.getId() << " name = " << appInfo.getName()
1713  << " class = " << appInfo.getClass()
1714  << " hostname = " << appInfo.getHostname() << __E__;
1715  try
1716  {
1717  SOAPParameters txParameters; // params for xoap to send
1718  txParameters.addParameter("Request", "EnableTRACE");
1719  txParameters.addParameter("Host", host);
1720  txParameters.addParameter("SetEnable", enable);
1721 
1722  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1723  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1724  SOAPUtilities::receive(retMsg, rxParameters);
1725  __SUP_COUT__ << "Received TRACE response: "
1726  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1727  << SOAPUtilities::translate(retMsg) << __E__;
1728 
1729  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1730  {
1731  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1732  "hostname = "
1733  << host << "/" << appInfo.getId()
1734  << " name = " << appInfo.getName()
1735  << " class = " << appInfo.getClass()
1736  << " hostname = " << appInfo.getHostname() << __E__;
1737  __SUP_SS_THROW__;
1738  }
1739  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1740  {
1741  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1742  << __E__;
1743  __SUP_SS_THROW__;
1744  }
1745  }
1746  catch(const xdaq::exception::Exception& e)
1747  {
1748  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1749  << appInfo.getId() << " name = " << appInfo.getName()
1750  << ". \n\n"
1751  << e.what() << __E__;
1752  __SUP_SS_THROW__;
1753  }
1754 
1755  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1756  } // end host set TRACE loop
1757 
1758  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1759  << modifiedTriggerStatus << __E__;
1760  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1761  } // end EnableTRACE
1762  else if(requestType == "GetTraceSnapshot")
1763  {
1764  __SUP_COUT__ << "requestType " << requestType << __E__;
1765 
1766  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1767  std::string filterFor = CgiDataUtilities::postData(cgiIn, "filterFor");
1768  std::string filterOut = CgiDataUtilities::postData(cgiIn, "filterOut");
1769 
1770  __SUP_COUTV__(hostList);
1771  __SUP_COUTV__(filterFor);
1772  __SUP_COUTV__(filterOut);
1773 
1774  std::vector<std::string /*host*/> hosts;
1775 
1776  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1777 
1778  SOAPParameters rxParameters; // params for xoap to recv
1779  rxParameters.addParameter("Command");
1780  rxParameters.addParameter("Error");
1781  rxParameters.addParameter("TRACETriggerStatus");
1782  rxParameters.addParameter("TRACESnapshot");
1783 
1784  std::string modifiedTriggerStatus = "";
1785  std::string xdaqHostname;
1786  StringMacros::getVectorFromString(hostList, hosts, {';'});
1787  for(auto& host : hosts)
1788  {
1789  // identify artdaq hosts to go through ARTDAQ supervisor
1790  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1791  __SUP_COUTV__(host);
1792  if(host.size() < 3)
1793  continue; // skip bad hostnames
1794 
1795  // use map to convert to xdaq host
1796  try
1797  {
1798  xdaqHostname = traceMapToXDAQHostname_.at(host);
1799  }
1800  catch(...)
1801  {
1802  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1803  << host << "' to xdaq Context hostname." << __E__;
1804  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1805  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1806  << __E__;
1807  __SUP_SS_THROW__;
1808  }
1809 
1810  __SUP_COUTV__(xdaqHostname);
1811 
1812  auto& appInfo = allTraceApps.at(xdaqHostname);
1813  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1814  << appInfo.getId() << " name = " << appInfo.getName()
1815  << " class = " << appInfo.getClass()
1816  << " hostname = " << appInfo.getHostname() << __E__;
1817  try
1818  {
1819  SOAPParameters txParameters; // params for xoap to send
1820  txParameters.addParameter("Request", "GetSnapshot");
1821  txParameters.addParameter("Host", host);
1822  txParameters.addParameter("FilterForCSV", filterFor);
1823  txParameters.addParameter("FilterOutCSV", filterOut);
1824 
1825  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1826  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1827  SOAPUtilities::receive(retMsg, rxParameters);
1828  __SUP_COUT__ << "Received TRACE response: "
1829  << SOAPUtilities::translate(retMsg).getCommand() << __E__;
1830  //<< " ==> Bytes " << SOAPUtilities::translate(retMsg) << __E__;
1831 
1832  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1833  {
1834  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1835  "hostname = "
1836  << host << "/" << appInfo.getId()
1837  << " name = " << appInfo.getName()
1838  << " class = " << appInfo.getClass()
1839  << " hostname = " << appInfo.getHostname() << __E__;
1840  __SUP_SS_THROW__;
1841  }
1842  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1843  {
1844  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1845  << __E__;
1846  __SUP_SS_THROW__;
1847  }
1848  }
1849  catch(const xdaq::exception::Exception& e)
1850  {
1851  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1852  << appInfo.getId() << " name = " << appInfo.getName()
1853  << ". \n\n"
1854  << e.what() << __E__;
1855  __SUP_SS_THROW__;
1856  }
1857 
1858  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1859  xmlOut.addTextElementToData("host", host);
1860  std::string snapshot = rxParameters.getValue("TRACESnapshot");
1861  // if(snapshot.size() > 100000)
1862  // {
1863  // __SUP_COUT__ << "Truncating snapshot" << __E__;
1864  // snapshot.resize(100000);
1865  // }
1866  // xmlOut.addTextElementToData("hostSnapshot", snapshot);
1867 
1868  {
1869  std::string filename =
1870  USER_CONSOLE_SNAPSHOT_PATH + "snapshot_" + host + ".txt";
1871  __SUP_COUTV__(filename);
1872  FILE* fp = fopen(filename.c_str(), "w");
1873  if(!fp)
1874  {
1875  __SUP_SS__ << "Failed to create snapshot file: " << filename << __E__;
1876  __SUP_SS_THROW__;
1877  }
1878  fprintf(fp,
1879  "TRACE Snapshot taken at %s\n",
1881 
1882  if(snapshot.size() > 5 && snapshot[2] != 'i')
1883  {
1884  // add header lines
1885  fprintf(
1886  fp,
1887  " idx us_tod delta pid tid cpu "
1888  " trcname lvl r msg \n");
1889  fprintf(fp,
1890  "----- ---------------- ----------- ------ ------ --- "
1891  "-------------------------------------- --- - "
1892  "--------------------------\n");
1893  }
1894  fprintf(fp, "%s", snapshot.c_str());
1895  fclose(fp);
1896  }
1897  } // end host set TRACE loop
1898 
1899  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1900  << modifiedTriggerStatus << __E__;
1901  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1902  } // end getTraceSnapshot
1903  else if(requestType == "GetCustomCountsAndActions" ||
1904  requestType == "AddCustomCountsAndAction" ||
1905  requestType == "ModifyCustomCountsAndAction")
1906  {
1907  __SUP_COUT__ << "requestType " << requestType
1908  << " size=" << priorityCustomTriggerList_.size() << __E__;
1909 
1910  //mutex scope:
1911  // lockout the messages array for the remainder of the scope
1912  // this guarantees can safely access the action queue
1913  std::lock_guard<std::mutex> lock(messageMutex_);
1914 
1915  if(requestType == "AddCustomCountsAndAction" ||
1916  requestType == "ModifyCustomCountsAndAction")
1917  {
1918  std::string needle = StringMacros::decodeURIComponent(
1919  CgiDataUtilities::postData(cgiIn, "needle"));
1920  uint32_t priority = CgiDataUtilities::postDataAsInt(cgiIn, "priority");
1921  std::string action = StringMacros::decodeURIComponent(
1922  CgiDataUtilities::postData(cgiIn, "action"));
1923  uint32_t triggerOnCount =
1924  CgiDataUtilities::postDataAsInt(cgiIn, "triggerOnCount");
1925  bool doLoop =
1926  CgiDataUtilities::postDataAsInt(cgiIn, "doLoop") > 0 ? true : false;
1927  bool isArmed =
1928  CgiDataUtilities::postDataAsInt(cgiIn, "isArmed") > 0 ? true : false;
1929 
1930  __SUP_COUTV__(needle);
1931  __SUP_COUTV__(priority);
1932  __SUP_COUTV__(action);
1933  __SUP_COUTV__(triggerOnCount);
1934  __SUP_COUTV__(doLoop);
1935  __SUP_COUTV__(isArmed);
1936 
1937  if(requestType == "ModifyCustomCountsAndAction")
1938  {
1939  std::string buttonDo = StringMacros::decodeURIComponent(
1940  CgiDataUtilities::postData(cgiIn, "buttonDo"));
1941  std::string currentNeedle =
1942  CgiDataUtilities::postData(cgiIn, "currentNeedle");
1943 
1944  //treat needle as CSV list and do in reverse order to maintain priority of group
1945  std::vector<std::string> csvNeedles =
1946  StringMacros::getVectorFromString(currentNeedle, {','});
1947  for(size_t i = csvNeedles.size() - 1; i < csvNeedles.size(); --i)
1948  {
1949  if(csvNeedles[i].size() == 0)
1950  continue; //skip empty entries
1951  //change the priority to the last placed priority entry to keep group order
1952  priority = modifyCustomTriggeredAction(
1953  StringMacros::decodeURIComponent(csvNeedles[i]),
1954  buttonDo,
1955  needle,
1956  action,
1957  priority,
1958  triggerOnCount,
1959  doLoop,
1960  isArmed);
1961  } //end csv needle list handling
1962  }
1963  else
1964  addCustomTriggeredAction(
1965  needle, action, priority, triggerOnCount, doLoop, isArmed);
1966 
1967  saveCustomCountList();
1968  } // end AddCustomCountsAndAction
1969 
1970  //always calculate untriggered count
1971  size_t untriggeredCount =
1972  messageCount_; //copy and then decrement "unique" incrementing ID for messages
1973 
1974  for(const auto& customCount : priorityCustomTriggerList_)
1975  {
1976  xercesc::DOMElement* customCountParent =
1977  xmlOut.addTextElementToData("customCount", "");
1978 
1979  if(customCount.occurrences < untriggeredCount)
1980  untriggeredCount -= customCount.occurrences;
1981  else
1982  {
1983  __SUP_SS__ << "Impossible custom count; notify admins! "
1984  << customCount.occurrences << " > " << untriggeredCount
1985  << " for "
1986  << StringMacros::vectorToString(customCount.needleSubstrings,
1987  {'*'})
1988  << __E__;
1989  __SUP_SS_THROW__;
1990  }
1991  xmlOut.addTextElementToParent(
1992  "needle",
1993  StringMacros::vectorToString(customCount.needleSubstrings, {'*'}),
1994  customCountParent);
1995  xmlOut.addTextElementToParent(
1996  "count", std::to_string(customCount.occurrences), customCountParent);
1997  xmlOut.addTextElementToParent(
1998  "action", customCount.action, customCountParent);
1999  xmlOut.addTextElementToParent("triggerOnCount",
2000  std::to_string(customCount.triggerOnCount),
2001  customCountParent);
2002  xmlOut.addTextElementToParent(
2003  "doLoop", std::to_string(customCount.doLoop), customCountParent);
2004  xmlOut.addTextElementToParent(
2005  "isArmed", std::to_string(customCount.isArmed), customCountParent);
2006  } //end adding custom counts to response xml loop
2007 
2008  //add untriggered always last
2009  xercesc::DOMElement* customCountParent =
2010  xmlOut.addTextElementToData("customCount", "");
2011  xmlOut.addTextElementToParent("needle", "< Untriggered >", customCountParent);
2012  xmlOut.addTextElementToParent(
2013  "count", std::to_string(untriggeredCount), customCountParent);
2014  xmlOut.addTextElementToParent("action", "Count Only", customCountParent);
2015  xmlOut.addTextElementToParent("triggerOnCount", "1", customCountParent);
2016  xmlOut.addTextElementToParent("doLoop", "1", customCountParent);
2017 
2018  } // end GetCustomCountsAndActions or AddCustomCountsAndAction
2019  else
2020  {
2021  __SUP_SS__ << "requestType Request, " << requestType
2022  << ", not recognized by the Console Supervisor (was it intended for "
2023  "another Supervisor?)."
2024  << __E__;
2025  __SUP_SS_THROW__;
2026  }
2027 } // end request()
2028 
2029 //==============================================================================
2033 {
2034  //Console Supervisor status detatil format is:
2035  // uptime, Err count, Warn count, Last Error msg, Last Warn msg
2036 
2037  //return uptime detail
2038  std::stringstream ss;
2039  ss << "Uptime: "
2040  << StringMacros::encodeURIComponent(StringMacros::getTimeDurationString(
2041  CorePropertySupervisorBase::getSupervisorUptime()));
2042 
2043  //return Err count, Warn count, Last Error msg, Last Warn msg, Last Info msg, Info count
2044 
2045  // size_t errorCount_ = 0, warnCount_ = 0;
2046  // std::string lastErrorMessage_, lastWarnMessage_;
2047  // time_t lastErrorMessageTime_ = 0, lastWarnMessageTime_ = 0;
2048 
2049  ss << ", Errors: " << errorCount_;
2050  ss << ", Warns: " << warnCount_;
2051  ss << ", Last Error ("
2052  << (lastErrorMessageTime_ ? StringMacros::getTimestampString(lastErrorMessageTime_)
2053  : "0")
2054  << "): "
2055  << (lastErrorMessageTime_ ? StringMacros::encodeURIComponent(lastErrorMessage_)
2056  : "");
2057  ss << ", Last Warn ("
2058  << (lastWarnMessageTime_ ? StringMacros::getTimestampString(lastWarnMessageTime_)
2059  : "0")
2060  << "): "
2061  << (lastWarnMessageTime_ ? StringMacros::encodeURIComponent(lastWarnMessage_)
2062  : "");
2063  ss << ", Last Info ("
2064  << (lastInfoMessageTime_ ? StringMacros::getTimestampString(lastInfoMessageTime_)
2065  : "0")
2066  << "): "
2067  << (lastInfoMessageTime_ ? StringMacros::encodeURIComponent(lastInfoMessage_)
2068  : "");
2069  ss << ", Info #: " << infoCount_;
2070  ss << ", First Error ("
2071  << (firstErrorMessageTime_
2072  ? StringMacros::getTimestampString(firstErrorMessageTime_)
2073  : "0")
2074  << "): "
2075  << (firstErrorMessageTime_ ? StringMacros::encodeURIComponent(firstErrorMessage_)
2076  : "");
2077  ss << ", First Warn ("
2078  << (firstWarnMessageTime_ ? StringMacros::getTimestampString(firstWarnMessageTime_)
2079  : "0")
2080  << "): "
2081  << (firstWarnMessageTime_ ? StringMacros::encodeURIComponent(firstWarnMessage_)
2082  : "");
2083  ss << ", First Info ("
2084  << (firstInfoMessageTime_ ? StringMacros::getTimestampString(firstInfoMessageTime_)
2085  : "0")
2086  << "): "
2087  << (firstInfoMessageTime_ ? StringMacros::encodeURIComponent(firstInfoMessage_)
2088  : "");
2089 
2090  return ss.str();
2091 } // end getStatusProgressDetail()
2092 
2093 //==============================================================================
2116 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument* xmlOut,
2117  const size_t lastUpdateCount)
2118 {
2119  //__SUP_COUT__ << __E__;
2120 
2121  if(messages_.size() == 0)
2122  return;
2123 
2124  // validate lastUpdateCount
2125  if(lastUpdateCount > messages_.back().getCount() && lastUpdateCount != (size_t)-1)
2126  {
2127  __SS__ << "Invalid lastUpdateCount: " << lastUpdateCount
2128  << " messagesArray size = " << messages_.back().getCount() << __E__;
2129  __SS_THROW__;
2130  }
2131 
2132  // lockout the messages array for the remainder of the scope
2133  // this guarantees the reading thread can safely access the messages
2134  std::lock_guard<std::mutex> lock(messageMutex_);
2135 
2136  xmlOut->addTextElementToData("last_update_count",
2137  std::to_string(messages_.back().getCount()));
2138 
2139  refreshParent_ = xmlOut->addTextElementToData("messages", "");
2140 
2141  bool requestOutOfSync = false;
2142  std::string requestOutOfSyncMsg;
2143 
2144  size_t refreshReadPointer = 0;
2145  if(lastUpdateCount != (size_t)-1)
2146  {
2147  while(refreshReadPointer < messages_.size() &&
2148  messages_[refreshReadPointer].getCount() <= lastUpdateCount)
2149  {
2150  ++refreshReadPointer;
2151  }
2152  }
2153 
2154  if(refreshReadPointer >= messages_.size())
2155  return;
2156 
2157  // limit number of catch-up messages
2158  if(messages_.size() - refreshReadPointer > maxClientMessageRequest_)
2159  {
2160  // __SUP_COUT__ << "Only sending latest " << maxClientMessageRequest_ << "
2161  // messages!";
2162 
2163  // auto oldrrp = refreshReadPointer;
2164  refreshReadPointer = messages_.size() - maxClientMessageRequest_;
2165 
2166  // __SS__ << "Skipping " << (refreshReadPointer - oldrrp)
2167  // << " messages because the web console has fallen behind!" << __E__;
2168  // __COUT__ << ss.str();
2169  // ConsoleMessageStruct msg(CONSOLE_SPECIAL_WARNING + ss.str(), lastUpdateCount);
2170  // auto it = messages_.begin();
2171  // std::advance(it, refreshReadPointer + 1);
2172  // messages_.insert(it, msg);
2173  }
2174 
2175  //return first_update_count, so that older messages could be retrieved later if desired by user
2176  xmlOut->addTextElementToData(
2177  "earliest_update_count",
2178  std::to_string(messages_[refreshReadPointer].getCount()));
2179 
2180  std::string messagesJson = "[";
2181 
2182  // output oldest to new
2183  for(; refreshReadPointer < messages_.size(); ++refreshReadPointer)
2184  {
2185  auto msg = messages_[refreshReadPointer];
2186  if(msg.getCount() < lastUpdateCount)
2187  {
2188  if(!requestOutOfSync) // record out of sync message once only
2189  {
2190  requestOutOfSync = true;
2191  __SS__ << "Request is out of sync! Message count should be more recent! "
2192  << msg.getCount() << " < " << lastUpdateCount << __E__;
2193  requestOutOfSyncMsg = ss.str();
2194  }
2195  // assume these messages are new (due to a system restart)
2196  // continue;
2197  }
2198 
2199  // addMessageToResponse(xmlOut, msg);
2200  addMessageToResponse(messagesJson, msg);
2201  __COUTTV__(messagesJson.size());
2202 
2203  } //end main message add loop
2204 
2205  messagesJson += "]";
2206 
2207  xmlOut->addTextElementToParent("message_json", messagesJson, refreshParent_);
2208 
2209  if(requestOutOfSync) // if request was out of sync, show message
2210  __SUP_COUT__ << requestOutOfSyncMsg;
2211 } // end insertMessageRefresh()
2212 
2213 //==============================================================================
2234 void ConsoleSupervisor::prependHistoricMessages(HttpXmlDocument* xmlOut,
2235  const size_t earliestOnhandMessageCount)
2236 {
2237  //__SUP_COUT__ << __E__;
2238 
2239  if(messages_.size() == 0)
2240  return;
2241 
2242  // validate earliestOnhandMessageCount
2243  if(earliestOnhandMessageCount >= messages_.back().getCount())
2244  {
2245  __SS__
2246  << "Invalid claim from user request of earliest onhand message sequence ID = "
2247  << earliestOnhandMessageCount
2248  << ". Latest existing sequence ID = " << messages_.back().getCount()
2249  << ". Was the Console Supervisor restarted?" << __E__;
2250  __SS_THROW__;
2251  }
2252 
2253  // lockout the messages array for the remainder of the scope
2254  // this guarantees the reading thread can safely access the messages
2255  std::lock_guard<std::mutex> lock(messageMutex_);
2256 
2257  refreshParent_ = xmlOut->addTextElementToData("messages", "");
2258 
2259  size_t refreshReadPointer = 0;
2260  size_t readCountStart = earliestOnhandMessageCount - maxClientMessageRequest_;
2261  if(readCountStart >= messages_.back().getCount()) //then wrapped around, so set to 0
2262  readCountStart = 0;
2263 
2264  //find starting read pointer
2265  while(refreshReadPointer < messages_.size() &&
2266  messages_[refreshReadPointer].getCount() < readCountStart)
2267  {
2268  ++refreshReadPointer;
2269  }
2270 
2271  if(refreshReadPointer >= messages_.size())
2272  return;
2273 
2274  xmlOut->addTextElementToData("earliest_update_count", //return new early onhand count
2275  std::to_string(readCountStart));
2276 
2277  std::string messagesJson = "[";
2278 
2279  //messages returned will be from readCountStart to earliestOnhandMessageCount-1
2280  // output oldest to new
2281  for(; refreshReadPointer < messages_.size(); ++refreshReadPointer)
2282  {
2283  auto msg = messages_[refreshReadPointer];
2284  if(messages_[refreshReadPointer].getCount() >= earliestOnhandMessageCount)
2285  break; //found last message
2286 
2287  addMessageToResponse(messagesJson, msg);
2288 
2289  } //end main message add loop
2290 
2291  messagesJson += "]";
2292 
2293  xmlOut->addTextElementToParent("message_json", messagesJson, refreshParent_);
2294 
2295 } // end prependHistoricMessages()
2296 
2297 //==============================================================================
2298 void ConsoleSupervisor::addMessageToResponse(HttpXmlDocument* xmlOut,
2300 {
2301  // for all fields, give value
2302  for(auto& field : msg.fields)
2303  {
2304  if(field.first == ConsoleMessageStruct::FieldType::SOURCE)
2305  continue; // skip, not userful
2306  if(field.first == ConsoleMessageStruct::FieldType::SOURCEID)
2307  continue; // skip, not userful
2308  if(field.first == ConsoleMessageStruct::FieldType::SEQID)
2309  continue; // skip, not userful
2310  if(field.first == ConsoleMessageStruct::FieldType::TIMESTAMP) //use Time instead
2311  continue; // skip, not userful
2312  if(field.first ==
2313  ConsoleMessageStruct::FieldType::LEVEL) //use modified getLevel instead
2314  continue; // skip, not userful
2315 
2316  xmlOut->addTextElementToParent(
2317  "message_" + ConsoleMessageStruct::fieldNames.at(field.first),
2318  field.second,
2319  refreshParent_);
2320  } //end msg field loop
2321 
2322  // give modified level also
2323  xmlOut->addTextElementToParent("message_Level", msg.getLevel(), refreshParent_);
2324 
2325  // give timestamp also
2326  xmlOut->addTextElementToParent("message_Time", msg.getTime(), refreshParent_);
2327 
2328  // give global count index also
2329  xmlOut->addTextElementToParent(
2330  "message_Count", std::to_string(msg.getCount()), refreshParent_);
2331 
2332  //give Custom count label also (i.e., which search string this message matches, or blank "" for no match)
2333  xmlOut->addTextElementToParent(
2334  "message_Custom",
2335  StringMacros::vectorToString(msg.getCustomTriggerMatch().needleSubstrings, {'*'}),
2336  refreshParent_);
2337 
2338 } // end addMessageToResponse()
2339 
2340 //==============================================================================
2342 void ConsoleSupervisor::addMessageToResponse(std::string& xmlValue,
2344 {
2345  if(xmlValue.size() &&
2346  xmlValue[xmlValue.size() - 1] != '[') //if not first entry, add comma
2347  xmlValue += ",";
2348 
2349  xmlValue += "{";
2350  // for all fields, give value
2351  for(auto& field : msg.fields)
2352  {
2353  if(field.first == ConsoleMessageStruct::FieldType::SOURCE)
2354  continue; // skip, not useful
2355  if(field.first == ConsoleMessageStruct::FieldType::SOURCEID)
2356  continue; // skip, not useful
2357  if(field.first == ConsoleMessageStruct::FieldType::SEQID)
2358  continue; // skip, not useful
2359  if(field.first == ConsoleMessageStruct::FieldType::TIMESTAMP) //use Time instead
2360  continue; // skip, not useful
2361  if(field.first ==
2362  ConsoleMessageStruct::FieldType::LEVEL) //use modified getLevel instead
2363  continue; // skip, not useful
2364 
2365  if(field.first ==
2366  ConsoleMessageStruct::FieldType::MSG) //only need to escape message field
2367  xmlValue += "\"" + ConsoleMessageStruct::fieldNames.at(field.first) +
2368  "\":\"" + StringMacros::escapeString(field.second) + "\",";
2369  else
2370  xmlValue += "\"" + ConsoleMessageStruct::fieldNames.at(field.first) +
2371  "\":\"" + field.second + "\",";
2372 
2373  // xmlOut->addTextElementToParent(
2374  // "message_" + ConsoleMessageStruct::fieldNames.at(field.first),
2375  // field.second,
2376  // refreshParent_);
2377  } //end msg field loop
2378 
2379  // give modified level also
2380  xmlValue += "\"Level\":\"" + msg.getLevel() + "\",";
2381  // xmlOut->addTextElementToParent("message_Level", msg.getLevel(), refreshParent_);
2382 
2383  // give timestamp also
2384  xmlValue += "\"Time\":\"" + msg.getTime() + "\",";
2385  // xmlOut->addTextElementToParent("message_Time", msg.getTime(), refreshParent_);
2386 
2387  // give global count index also
2388  xmlValue += "\"Count\":\"" + std::to_string(msg.getCount()) + "\",";
2389  //xmlOut->addTextElementToParent("message_Count", std::to_string(msg.getCount()), refreshParent_);
2390 
2391  //give Custom count label also (i.e., which search string this message matches, or blank "" for no match)
2392  xmlValue += "\"Custom\":\"" +
2394  msg.getCustomTriggerMatch().needleSubstrings, {'*'})) +
2395  "\"";
2396  // xmlOut->addTextElementToParent(
2397  // "message_Custom",
2398  // StringMacros::vectorToString(msg.getCustomTriggerMatch().needleSubstrings, {'*'}),
2399  // refreshParent_);
2400 
2401  xmlValue += "}";
2402 } // end addMessageToResponse()
static std::string postData(cgicc::Cgicc &cgi, const std::string &needle)
virtual void forceSupervisorPropertyValues(void) override
virtual void request(const std::string &requestType, cgicc::Cgicc &cgiIn, HttpXmlDocument &xmlOut, const WebUsers::RequestUserInfo &userInfo) override
static const std::set< std::string > CUSTOM_TRIGGER_ACTIONS
Count always happens, and System Message always happens for FSM commands.
virtual std::string getStatusProgressDetail(void) override
void sendAsyncExceptionToGateway(const std::string &errMsg, bool isPauseException, bool isStopException)
void addParameter(const std::string name, const std::string value)
xercesc::DOMElement * addTextElementToParent(const std::string &childName, const std::string &childText, xercesc::DOMElement *parent)
void INIT_MF(const char *name)
@ SEQID
sequence ID is incrementing number independent from each source
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string exec(const char *cmd)
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
static std::string escapeString(std::string inString, bool allowWhiteSpace=false)
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
static std::string getTimeDurationString(const time_t durationInSeconds=time(0))
static std::string decodeURIComponent(const std::string &data)