otsdaq-utilities  3.08.00
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  int noHost = CgiDataUtilities::postDataAsInt(cgiIn, "noHost");
1114 
1115  // __SUP_COUT__ << "requestType " << requestType << __E__;
1116  // __SUP_COUT__ << "colorIndex: " << colorIndex << __E__;
1117  // __SUP_COUT__ << "showSideBar: " << showSideBar << __E__;
1118  // __SUP_COUT__ << "noWrap: " << noWrap << __E__;
1119  // __SUP_COUT__ << "messageOnly: " << messageOnly << __E__;
1120  // __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << __E__;
1121  // __SUP_COUT__ << "noHost: " << noHost << __E__;
1122 
1123  if(userInfo.username_ == "") // should never happen?
1124  {
1125  __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_
1126  << __E__;
1127  xmlOut.addTextElementToData("Error",
1128  "Error - InvauserInfo.username_user found.");
1129  return;
1130  }
1131 
1132  std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." +
1133  (std::string)USERS_PREFERENCES_FILETYPE;
1134 
1135  // __SUP_COUT__ << "Save preferences: " << fn << __E__;
1136  FILE* fp = fopen(fn.c_str(), "w");
1137  if(!fp)
1138  {
1139  __SS__;
1140  __THROW__(ss.str() + "Could not open file: " + fn);
1141  }
1142  fprintf(fp, "colorIndex %d\n", colorIndex);
1143  fprintf(fp, "showSideBar %d\n", showSideBar);
1144  fprintf(fp, "noWrap %d\n", noWrap);
1145  fprintf(fp, "messageOnly %d\n", messageOnly);
1146  fprintf(fp, "hideLineNumers %d\n", hideLineNumers);
1147  fprintf(fp, "noHost %d\n", noHost);
1148  fclose(fp);
1149  }
1150  else if(requestType == "LoadUserPreferences")
1151  {
1152  // __SUP_COUT__ << "requestType " << requestType << __E__;
1153 
1154  unsigned int colorIndex, showSideBar, noWrap, messageOnly, hideLineNumers, noHost;
1155 
1156  if(userInfo.username_ == "") // should never happen?
1157  {
1158  __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_
1159  << __E__;
1160  xmlOut.addTextElementToData("Error", "Error - Invalid user found.");
1161  return;
1162  }
1163 
1164  std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." +
1165  (std::string)USERS_PREFERENCES_FILETYPE;
1166 
1167  // __SUP_COUT__ << "Load preferences: " << fn << __E__;
1168 
1169  FILE* fp = fopen(fn.c_str(), "r");
1170  if(!fp)
1171  {
1172  // return defaults
1173  __SUP_COUT__ << "Returning defaults." << __E__;
1174  xmlOut.addTextElementToData("colorIndex", "0");
1175  xmlOut.addTextElementToData("showSideBar", "0");
1176  xmlOut.addTextElementToData("noWrap", "1");
1177  xmlOut.addTextElementToData("messageOnly", "0");
1178  xmlOut.addTextElementToData("hideLineNumers", "1");
1179  xmlOut.addTextElementToData("noHost", "1");
1180  return;
1181  }
1182  fscanf(fp, "%*s %u", &colorIndex);
1183  fscanf(fp, "%*s %u", &showSideBar);
1184  fscanf(fp, "%*s %u", &noWrap);
1185  fscanf(fp, "%*s %u", &messageOnly);
1186  fscanf(fp, "%*s %u", &hideLineNumers);
1187  if(fscanf(fp, "%*s %u", &noHost) != 1)
1188  noHost = 1; // default: hidden (backward compat with old pref files)
1189  fclose(fp);
1190  // __SUP_COUT__ << "colorIndex: " << colorIndex << __E__;
1191  // __SUP_COUT__ << "showSideBar: " << showSideBar << __E__;
1192  // __SUP_COUT__ << "noWrap: " << noWrap << __E__;
1193  // __SUP_COUT__ << "messageOnly: " << messageOnly << __E__;
1194  // __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << __E__;
1195 
1196  char tmpStr[20];
1197  sprintf(tmpStr, "%u", colorIndex);
1198  xmlOut.addTextElementToData("colorIndex", tmpStr);
1199  sprintf(tmpStr, "%u", showSideBar);
1200  xmlOut.addTextElementToData("showSideBar", tmpStr);
1201  sprintf(tmpStr, "%u", noWrap);
1202  xmlOut.addTextElementToData("noWrap", tmpStr);
1203  sprintf(tmpStr, "%u", messageOnly);
1204  xmlOut.addTextElementToData("messageOnly", tmpStr);
1205  sprintf(tmpStr, "%u", hideLineNumers);
1206  xmlOut.addTextElementToData("hideLineNumers", tmpStr);
1207  sprintf(tmpStr, "%u", noHost);
1208  xmlOut.addTextElementToData("noHost", tmpStr);
1209  }
1210  else if(requestType == "GetTraceLevels")
1211  {
1212  __SUP_COUT__ << "requestType " << requestType << __E__;
1213 
1214  SOAPParameters txParameters; // params for xoap to send
1215  txParameters.addParameter("Request", "GetTraceLevels");
1216 
1217  SOAPParameters rxParameters; // params for xoap to recv
1218  rxParameters.addParameter("Command");
1219  rxParameters.addParameter("Error");
1220  rxParameters.addParameter("TRACEHostnameList");
1221  rxParameters.addParameter("TRACEList");
1222 
1223  traceMapToXDAQHostname_.clear(); // reset
1224 
1225  std::string traceList = "";
1226  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1227  __SUP_COUT__ << "Querying " << allTraceApps.size()
1228  << " TRACE controller supervisor(s) for TRACE levels. Hosts: ";
1229  {
1230  std::stringstream appHosts;
1231  for(const auto& appInfo : allTraceApps)
1232  appHosts << appInfo.first << "(" << appInfo.second.getClass() << ") ";
1233  __SUP_COUT__ << appHosts.str() << __E__;
1234  }
1235  for(const auto& appInfo : allTraceApps)
1236  {
1237  __SUP_COUT__ << "Supervisor hostname = " << appInfo.first << "/"
1238  << appInfo.second.getId()
1239  << " name = " << appInfo.second.getName()
1240  << " class = " << appInfo.second.getClass()
1241  << " hostname = " << appInfo.second.getHostname() << __E__;
1242  try
1243  {
1244  xoap::MessageReference retMsg =
1245  SOAPMessenger::sendWithSOAPReply(appInfo.second.getDescriptor(),
1246  "TRACESupervisorRequest",
1247  txParameters);
1248  SOAPUtilities::receive(retMsg, rxParameters);
1249  __SUP_COUT__ << "Received TRACE response: "
1250  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1251  << SOAPUtilities::translate(retMsg) << __E__;
1252 
1253  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1254  {
1255  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1256  "hostname = "
1257  << appInfo.first << "/" << appInfo.second.getId()
1258  << " name = " << appInfo.second.getName()
1259  << " class = " << appInfo.second.getClass()
1260  << " hostname = " << appInfo.second.getHostname() << __E__;
1261  __SUP_SS_THROW__;
1262  }
1263  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1264  {
1265  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1266  << __E__;
1267  __SUP_SS_THROW__;
1268  }
1269  }
1270  catch(const xdaq::exception::Exception& e)
1271  {
1272  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1273  << appInfo.second.getId()
1274  << " name = " << appInfo.second.getName() << ". \n\n"
1275  << e.what() << __E__;
1276  __SUP_COUT_ERR__ << ss.str();
1277  continue; //skip bad Supervisor
1278  }
1279 
1280  std::vector<std::string> traceHostnameArr;
1281  __COUTTV__(rxParameters.getValue("TRACEHostnameList"));
1283  rxParameters.getValue("TRACEHostnameList"), traceHostnameArr, {';'});
1284  for(const auto& traceHostname : traceHostnameArr)
1285  {
1286  if(traceHostname == "")
1287  continue; //skip blanks
1288  traceMapToXDAQHostname_[traceHostname] = appInfo.first;
1289  }
1290 
1291  // traceList += ";" + appInfo.first; //insert xdaq context version of
1292  // name
1293  // //FIXME and create mapp from user's typed in xdaq
1294  // context name to TRACE hostname resolution
1295 
1296  __COUTTV__(rxParameters.getValue("TRACEList"));
1297  traceList += rxParameters.getValue("TRACEList");
1298 
1299  } // end app get TRACE loop
1300  __SUP_COUT__ << "TRACE hostname map received: \n"
1301  << StringMacros::mapToString(traceMapToXDAQHostname_) << __E__;
1302  __SUP_COUT__ << "TRACE List received: \n" << traceList << __E__;
1303  xmlOut.addTextElementToData("traceList", traceList);
1304  } // end GetTraceLevels
1305  else if(requestType == "SetTraceLevels")
1306  {
1307  __SUP_COUT__ << "requestType " << requestType << __E__;
1308 
1309  std::string individualValues =
1310  CgiDataUtilities::postData(cgiIn, "individualValues");
1311  std::string hostLabelMap = CgiDataUtilities::postData(cgiIn, "hostLabelMap");
1312  std::string setMode = CgiDataUtilities::postData(cgiIn, "setMode");
1313  std::string setValueMSB = CgiDataUtilities::postData(cgiIn, "setValueMSB");
1314  std::string setValueLSB = CgiDataUtilities::postData(cgiIn, "setValueLSB");
1315 
1316  __SUP_COUTV__(individualValues);
1317  __SUP_COUTV__(setMode);
1318  // set modes: SLOW, FAST, TRIGGER
1319  __SUP_COUTV__(setValueMSB);
1320  __SUP_COUTV__(setValueLSB);
1321 
1322  std::map<std::string /*host*/, std::string /*labelArr*/> hostToLabelMap;
1323 
1324  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1325 
1326  SOAPParameters rxParameters; // params for xoap to recv
1327  rxParameters.addParameter("Command");
1328  rxParameters.addParameter("Error");
1329  rxParameters.addParameter("TRACEList");
1330 
1331  std::string modifiedTraceList = "";
1332  std::string xdaqHostname;
1333  StringMacros::getMapFromString(hostLabelMap, hostToLabelMap, {';'}, {':'});
1334  for(auto& hostLabelsPair : hostToLabelMap)
1335  {
1336  // identify artdaq hosts to go through ARTDAQ supervisor
1337  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1338  __SUP_COUTV__(hostLabelsPair.first);
1339  __SUP_COUTV__(hostLabelsPair.second);
1340 
1341  // use map to convert to xdaq host
1342  try
1343  {
1344  xdaqHostname = traceMapToXDAQHostname_.at(hostLabelsPair.first);
1345  }
1346  catch(...)
1347  {
1348  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1349  << hostLabelsPair.first << "' to xdaq Context hostname."
1350  << __E__;
1351  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1352  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1353  << __E__;
1354  __SUP_SS_THROW__;
1355  }
1356 
1357  __SUP_COUTV__(xdaqHostname);
1358 
1359  auto& appInfo = allTraceApps.at(xdaqHostname);
1360  __SUP_COUT__ << "Supervisor hostname = " << hostLabelsPair.first << "/"
1361  << xdaqHostname << ":" << appInfo.getId()
1362  << " name = " << appInfo.getName()
1363  << " class = " << appInfo.getClass()
1364  << " hostname = " << appInfo.getHostname() << __E__;
1365  try
1366  {
1367  SOAPParameters txParameters; // params for xoap to send
1368  txParameters.addParameter("Request", "SetTraceLevels");
1369  txParameters.addParameter("IndividualValues", individualValues);
1370  txParameters.addParameter("Host", hostLabelsPair.first);
1371  txParameters.addParameter("SetMode", setMode);
1372  txParameters.addParameter("Labels", hostLabelsPair.second);
1373  txParameters.addParameter("SetValueMSB", setValueMSB);
1374  txParameters.addParameter("SetValueLSB", setValueLSB);
1375 
1376  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1377  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1378  SOAPUtilities::receive(retMsg, rxParameters);
1379  __SUP_COUT__ << "Received TRACE response: "
1380  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1381  << SOAPUtilities::translate(retMsg) << __E__;
1382 
1383  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1384  {
1385  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1386  "hostname = "
1387  << hostLabelsPair.first << "/" << appInfo.getId()
1388  << " name = " << appInfo.getName()
1389  << " class = " << appInfo.getClass()
1390  << " hostname = " << appInfo.getHostname() << __E__;
1391  __SUP_SS_THROW__;
1392  }
1393  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1394  {
1395  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1396  << __E__;
1397  __SUP_SS_THROW__;
1398  }
1399  }
1400  catch(const xdaq::exception::Exception& e)
1401  {
1402  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1403  << appInfo.getId() << " name = " << appInfo.getName()
1404  << ". \n\n"
1405  << e.what() << __E__;
1406  __SUP_SS_THROW__;
1407  }
1408 
1409  modifiedTraceList +=
1410  ";" + hostLabelsPair.first; // insert xdaq context version of name
1411  // FIXME and create mapp from user's typed in xdaq
1412  // context name to TRACE hostname resolution
1413 
1414  modifiedTraceList += rxParameters.getValue("TRACEList");
1415 
1416  } // end host set TRACE loop
1417 
1418  __SUP_COUT__ << "mod'd TRACE List received: \n" << modifiedTraceList << __E__;
1419  xmlOut.addTextElementToData("modTraceList", modifiedTraceList);
1420  } // end SetTraceLevels
1421  else if(requestType == "GetTriggerStatus")
1422  {
1423  __SUP_COUT__ << "requestType " << requestType << __E__;
1424  SOAPParameters txParameters; // params for xoap to send
1425  txParameters.addParameter("Request", "GetTriggerStatus");
1426 
1427  SOAPParameters rxParameters; // params for xoap to recv
1428  rxParameters.addParameter("Command");
1429  rxParameters.addParameter("Error");
1430  rxParameters.addParameter("TRACETriggerStatus");
1431 
1432  std::string traceTriggerStatus = "";
1433  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1434  for(const auto& appInfo : allTraceApps)
1435  {
1436  __SUP_COUT__ << "Supervisor hostname = " << appInfo.first << "/"
1437  << appInfo.second.getId()
1438  << " name = " << appInfo.second.getName()
1439  << " class = " << appInfo.second.getClass()
1440  << " hostname = " << appInfo.second.getHostname() << __E__;
1441  try
1442  {
1443  xoap::MessageReference retMsg =
1444  SOAPMessenger::sendWithSOAPReply(appInfo.second.getDescriptor(),
1445  "TRACESupervisorRequest",
1446  txParameters);
1447  SOAPUtilities::receive(retMsg, rxParameters);
1448  __SUP_COUT__ << "Received TRACE response: "
1449  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1450  << SOAPUtilities::translate(retMsg) << __E__;
1451 
1452  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1453  {
1454  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1455  "hostname = "
1456  << appInfo.first << "/" << appInfo.second.getId()
1457  << " name = " << appInfo.second.getName()
1458  << " class = " << appInfo.second.getClass()
1459  << " hostname = " << appInfo.second.getHostname() << __E__;
1460  __SUP_SS_THROW__;
1461  }
1462  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1463  {
1464  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1465  << __E__;
1466  __SUP_SS_THROW__;
1467  }
1468  }
1469  catch(const xdaq::exception::Exception& e)
1470  {
1471  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1472  << appInfo.second.getId()
1473  << " name = " << appInfo.second.getName() << ". \n\n"
1474  << e.what() << __E__;
1475  __SUP_SS_THROW__;
1476  }
1477 
1478  traceTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1479 
1480  } // end app get TRACE loop
1481  __SUP_COUT__ << "TRACE Trigger Status received: \n"
1482  << traceTriggerStatus << __E__;
1483  xmlOut.addTextElementToData("traceTriggerStatus", traceTriggerStatus);
1484  } // end GetTriggerStatus
1485  else if(requestType == "SetTriggerEnable")
1486  {
1487  __SUP_COUT__ << "requestType " << requestType << __E__;
1488 
1489  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1490 
1491  __SUP_COUTV__(hostList);
1492 
1493  std::vector<std::string /*host*/> hosts;
1494 
1495  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1496 
1497  SOAPParameters rxParameters; // params for xoap to recv
1498  rxParameters.addParameter("Command");
1499  rxParameters.addParameter("Error");
1500  rxParameters.addParameter("TRACETriggerStatus");
1501 
1502  std::string modifiedTriggerStatus = "";
1503  std::string xdaqHostname;
1504  StringMacros::getVectorFromString(hostList, hosts, {';'});
1505  for(auto& host : hosts)
1506  {
1507  // identify artdaq hosts to go through ARTDAQ supervisor
1508  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1509  __SUP_COUTV__(host);
1510  if(host.size() < 3)
1511  continue; // skip bad hostnames
1512 
1513  // use map to convert to xdaq host
1514  try
1515  {
1516  xdaqHostname = traceMapToXDAQHostname_.at(host);
1517  }
1518  catch(...)
1519  {
1520  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1521  << host << "' to xdaq Context hostname." << __E__;
1522  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1523  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1524  << __E__;
1525  __SUP_SS_THROW__;
1526  }
1527 
1528  __SUP_COUTV__(xdaqHostname);
1529 
1530  auto& appInfo = allTraceApps.at(xdaqHostname);
1531  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1532  << appInfo.getId() << " name = " << appInfo.getName()
1533  << " class = " << appInfo.getClass()
1534  << " hostname = " << appInfo.getHostname() << __E__;
1535  try
1536  {
1537  SOAPParameters txParameters; // params for xoap to send
1538  txParameters.addParameter("Request", "SetTriggerEnable");
1539  txParameters.addParameter("Host", host);
1540 
1541  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1542  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1543  SOAPUtilities::receive(retMsg, rxParameters);
1544  __SUP_COUT__ << "Received TRACE response: "
1545  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1546  << SOAPUtilities::translate(retMsg) << __E__;
1547 
1548  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1549  {
1550  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1551  "hostname = "
1552  << host << "/" << appInfo.getId()
1553  << " name = " << appInfo.getName()
1554  << " class = " << appInfo.getClass()
1555  << " hostname = " << appInfo.getHostname() << __E__;
1556  __SUP_SS_THROW__;
1557  }
1558  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1559  {
1560  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1561  << __E__;
1562  __SUP_SS_THROW__;
1563  }
1564  }
1565  catch(const xdaq::exception::Exception& e)
1566  {
1567  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1568  << appInfo.getId() << " name = " << appInfo.getName()
1569  << ". \n\n"
1570  << e.what() << __E__;
1571  __SUP_SS_THROW__;
1572  }
1573 
1574  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1575  } // end host set TRACE loop
1576 
1577  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1578  << modifiedTriggerStatus << __E__;
1579  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1580  } // end SetTriggerEnable
1581  else if(requestType == "ResetTRACE")
1582  {
1583  __SUP_COUT__ << "requestType " << requestType << __E__;
1584 
1585  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1586 
1587  __SUP_COUTV__(hostList);
1588 
1589  std::vector<std::string /*host*/> hosts;
1590 
1591  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1592 
1593  SOAPParameters rxParameters; // params for xoap to recv
1594  rxParameters.addParameter("Command");
1595  rxParameters.addParameter("Error");
1596  rxParameters.addParameter("TRACETriggerStatus");
1597 
1598  std::string modifiedTriggerStatus = "";
1599  std::string xdaqHostname;
1600  StringMacros::getVectorFromString(hostList, hosts, {';'});
1601  for(auto& host : hosts)
1602  {
1603  // identify artdaq hosts to go through ARTDAQ supervisor
1604  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1605  __SUP_COUTV__(host);
1606  if(host.size() < 3)
1607  continue; // skip bad hostnames
1608 
1609  // use map to convert to xdaq host
1610  try
1611  {
1612  xdaqHostname = traceMapToXDAQHostname_.at(host);
1613  }
1614  catch(...)
1615  {
1616  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1617  << host << "' to xdaq Context hostname." << __E__;
1618  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1619  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1620  << __E__;
1621  __SUP_SS_THROW__;
1622  }
1623 
1624  __SUP_COUTV__(xdaqHostname);
1625 
1626  auto& appInfo = allTraceApps.at(xdaqHostname);
1627  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1628  << appInfo.getId() << " name = " << appInfo.getName()
1629  << " class = " << appInfo.getClass()
1630  << " hostname = " << appInfo.getHostname() << __E__;
1631  try
1632  {
1633  SOAPParameters txParameters; // params for xoap to send
1634  txParameters.addParameter("Request", "ResetTRACE");
1635  txParameters.addParameter("Host", host);
1636 
1637  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1638  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1639  SOAPUtilities::receive(retMsg, rxParameters);
1640  __SUP_COUT__ << "Received TRACE response: "
1641  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1642  << SOAPUtilities::translate(retMsg) << __E__;
1643 
1644  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1645  {
1646  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1647  "hostname = "
1648  << host << "/" << appInfo.getId()
1649  << " name = " << appInfo.getName()
1650  << " class = " << appInfo.getClass()
1651  << " hostname = " << appInfo.getHostname() << __E__;
1652  __SUP_SS_THROW__;
1653  }
1654  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1655  {
1656  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1657  << __E__;
1658  __SUP_SS_THROW__;
1659  }
1660  }
1661  catch(const xdaq::exception::Exception& e)
1662  {
1663  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1664  << appInfo.getId() << " name = " << appInfo.getName()
1665  << ". \n\n"
1666  << e.what() << __E__;
1667  __SUP_SS_THROW__;
1668  }
1669 
1670  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1671  } // end host set TRACE loop
1672 
1673  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1674  << modifiedTriggerStatus << __E__;
1675  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1676  } // end ResetTRACE
1677  else if(requestType == "EnableTRACE")
1678  {
1679  __SUP_COUT__ << "requestType " << requestType << __E__;
1680 
1681  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1682  std::string enable = CgiDataUtilities::postData(cgiIn, "enable");
1683 
1684  __SUP_COUTV__(hostList);
1685  __SUP_COUTV__(enable);
1686 
1687  std::vector<std::string /*host*/> hosts;
1688 
1689  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1690 
1691  SOAPParameters rxParameters; // params for xoap to recv
1692  rxParameters.addParameter("Command");
1693  rxParameters.addParameter("Error");
1694  rxParameters.addParameter("TRACETriggerStatus");
1695 
1696  std::string modifiedTriggerStatus = "";
1697  std::string xdaqHostname;
1698  StringMacros::getVectorFromString(hostList, hosts, {';'});
1699  for(auto& host : hosts)
1700  {
1701  // identify artdaq hosts to go through ARTDAQ supervisor
1702  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1703  __SUP_COUTV__(host);
1704  if(host.size() < 3)
1705  continue; // skip bad hostnames
1706 
1707  // use map to convert to xdaq host
1708  try
1709  {
1710  xdaqHostname = traceMapToXDAQHostname_.at(host);
1711  }
1712  catch(...)
1713  {
1714  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1715  << host << "' to xdaq Context hostname." << __E__;
1716  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1717  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1718  << __E__;
1719  __SUP_SS_THROW__;
1720  }
1721 
1722  __SUP_COUTV__(xdaqHostname);
1723 
1724  auto& appInfo = allTraceApps.at(xdaqHostname);
1725  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1726  << appInfo.getId() << " name = " << appInfo.getName()
1727  << " class = " << appInfo.getClass()
1728  << " hostname = " << appInfo.getHostname() << __E__;
1729  try
1730  {
1731  SOAPParameters txParameters; // params for xoap to send
1732  txParameters.addParameter("Request", "EnableTRACE");
1733  txParameters.addParameter("Host", host);
1734  txParameters.addParameter("SetEnable", enable);
1735 
1736  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1737  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1738  SOAPUtilities::receive(retMsg, rxParameters);
1739  __SUP_COUT__ << "Received TRACE response: "
1740  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1741  << SOAPUtilities::translate(retMsg) << __E__;
1742 
1743  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1744  {
1745  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1746  "hostname = "
1747  << host << "/" << appInfo.getId()
1748  << " name = " << appInfo.getName()
1749  << " class = " << appInfo.getClass()
1750  << " hostname = " << appInfo.getHostname() << __E__;
1751  __SUP_SS_THROW__;
1752  }
1753  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1754  {
1755  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1756  << __E__;
1757  __SUP_SS_THROW__;
1758  }
1759  }
1760  catch(const xdaq::exception::Exception& e)
1761  {
1762  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1763  << appInfo.getId() << " name = " << appInfo.getName()
1764  << ". \n\n"
1765  << e.what() << __E__;
1766  __SUP_SS_THROW__;
1767  }
1768 
1769  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1770  } // end host set TRACE loop
1771 
1772  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1773  << modifiedTriggerStatus << __E__;
1774  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1775  } // end EnableTRACE
1776  else if(requestType == "GetTraceSnapshot")
1777  {
1778  __SUP_COUT__ << "requestType " << requestType << __E__;
1779 
1780  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1781  std::string filterFor = CgiDataUtilities::postData(cgiIn, "filterFor");
1782  std::string filterOut = CgiDataUtilities::postData(cgiIn, "filterOut");
1783 
1784  __SUP_COUTV__(hostList);
1785  __SUP_COUTV__(filterFor);
1786  __SUP_COUTV__(filterOut);
1787 
1788  std::vector<std::string /*host*/> hosts;
1789 
1790  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1791 
1792  SOAPParameters rxParameters; // params for xoap to recv
1793  rxParameters.addParameter("Command");
1794  rxParameters.addParameter("Error");
1795  rxParameters.addParameter("TRACETriggerStatus");
1796  rxParameters.addParameter("TRACESnapshot");
1797 
1798  std::string modifiedTriggerStatus = "";
1799  std::string xdaqHostname;
1800  StringMacros::getVectorFromString(hostList, hosts, {';'});
1801  for(auto& host : hosts)
1802  {
1803  // identify artdaq hosts to go through ARTDAQ supervisor
1804  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1805  __SUP_COUTV__(host);
1806  if(host.size() < 3)
1807  continue; // skip bad hostnames
1808 
1809  // use map to convert to xdaq host
1810  try
1811  {
1812  xdaqHostname = traceMapToXDAQHostname_.at(host);
1813  }
1814  catch(...)
1815  {
1816  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1817  << host << "' to xdaq Context hostname." << __E__;
1818  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1819  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1820  << __E__;
1821  __SUP_SS_THROW__;
1822  }
1823 
1824  __SUP_COUTV__(xdaqHostname);
1825 
1826  auto& appInfo = allTraceApps.at(xdaqHostname);
1827  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1828  << appInfo.getId() << " name = " << appInfo.getName()
1829  << " class = " << appInfo.getClass()
1830  << " hostname = " << appInfo.getHostname() << __E__;
1831  try
1832  {
1833  SOAPParameters txParameters; // params for xoap to send
1834  txParameters.addParameter("Request", "GetSnapshot");
1835  txParameters.addParameter("Host", host);
1836  txParameters.addParameter("FilterForCSV", filterFor);
1837  txParameters.addParameter("FilterOutCSV", filterOut);
1838 
1839  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1840  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1841  SOAPUtilities::receive(retMsg, rxParameters);
1842  __SUP_COUT__ << "Received TRACE response: "
1843  << SOAPUtilities::translate(retMsg).getCommand() << __E__;
1844  //<< " ==> Bytes " << SOAPUtilities::translate(retMsg) << __E__;
1845 
1846  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1847  {
1848  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1849  "hostname = "
1850  << host << "/" << appInfo.getId()
1851  << " name = " << appInfo.getName()
1852  << " class = " << appInfo.getClass()
1853  << " hostname = " << appInfo.getHostname() << __E__;
1854  __SUP_SS_THROW__;
1855  }
1856  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1857  {
1858  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1859  << __E__;
1860  __SUP_SS_THROW__;
1861  }
1862  }
1863  catch(const xdaq::exception::Exception& e)
1864  {
1865  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1866  << appInfo.getId() << " name = " << appInfo.getName()
1867  << ". \n\n"
1868  << e.what() << __E__;
1869  __SUP_SS_THROW__;
1870  }
1871 
1872  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1873  xmlOut.addTextElementToData("host", host);
1874  std::string snapshot = rxParameters.getValue("TRACESnapshot");
1875  // if(snapshot.size() > 100000)
1876  // {
1877  // __SUP_COUT__ << "Truncating snapshot" << __E__;
1878  // snapshot.resize(100000);
1879  // }
1880  // xmlOut.addTextElementToData("hostSnapshot", snapshot);
1881 
1882  {
1883  std::string filename =
1884  USER_CONSOLE_SNAPSHOT_PATH + "snapshot_" + host + ".txt";
1885  __SUP_COUTV__(filename);
1886  FILE* fp = fopen(filename.c_str(), "w");
1887  if(!fp)
1888  {
1889  __SUP_SS__ << "Failed to create snapshot file: " << filename << __E__;
1890  __SUP_SS_THROW__;
1891  }
1892  fprintf(fp,
1893  "TRACE Snapshot taken at %s\n",
1895 
1896  if(snapshot.size() > 5 && snapshot[2] != 'i')
1897  {
1898  // add header lines
1899  fprintf(
1900  fp,
1901  " idx us_tod delta pid tid cpu "
1902  " trcname lvl r msg \n");
1903  fprintf(fp,
1904  "----- ---------------- ----------- ------ ------ --- "
1905  "-------------------------------------- --- - "
1906  "--------------------------\n");
1907  }
1908  fprintf(fp, "%s", snapshot.c_str());
1909  fclose(fp);
1910  }
1911  } // end host set TRACE loop
1912 
1913  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1914  << modifiedTriggerStatus << __E__;
1915  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1916  } // end getTraceSnapshot
1917  else if(requestType == "GetCustomCountsAndActions" ||
1918  requestType == "AddCustomCountsAndAction" ||
1919  requestType == "ModifyCustomCountsAndAction")
1920  {
1921  __SUP_COUT__ << "requestType " << requestType
1922  << " size=" << priorityCustomTriggerList_.size() << __E__;
1923 
1924  //mutex scope:
1925  // lockout the messages array for the remainder of the scope
1926  // this guarantees can safely access the action queue
1927  std::lock_guard<std::mutex> lock(messageMutex_);
1928 
1929  if(requestType == "AddCustomCountsAndAction" ||
1930  requestType == "ModifyCustomCountsAndAction")
1931  {
1932  std::string needle = StringMacros::decodeURIComponent(
1933  CgiDataUtilities::postData(cgiIn, "needle"));
1934  uint32_t priority = CgiDataUtilities::postDataAsInt(cgiIn, "priority");
1935  std::string action = StringMacros::decodeURIComponent(
1936  CgiDataUtilities::postData(cgiIn, "action"));
1937  uint32_t triggerOnCount =
1938  CgiDataUtilities::postDataAsInt(cgiIn, "triggerOnCount");
1939  bool doLoop =
1940  CgiDataUtilities::postDataAsInt(cgiIn, "doLoop") > 0 ? true : false;
1941  bool isArmed =
1942  CgiDataUtilities::postDataAsInt(cgiIn, "isArmed") > 0 ? true : false;
1943 
1944  __SUP_COUTV__(needle);
1945  __SUP_COUTV__(priority);
1946  __SUP_COUTV__(action);
1947  __SUP_COUTV__(triggerOnCount);
1948  __SUP_COUTV__(doLoop);
1949  __SUP_COUTV__(isArmed);
1950 
1951  if(requestType == "ModifyCustomCountsAndAction")
1952  {
1953  std::string buttonDo = StringMacros::decodeURIComponent(
1954  CgiDataUtilities::postData(cgiIn, "buttonDo"));
1955  std::string currentNeedle =
1956  CgiDataUtilities::postData(cgiIn, "currentNeedle");
1957 
1958  //treat needle as CSV list and do in reverse order to maintain priority of group
1959  std::vector<std::string> csvNeedles =
1960  StringMacros::getVectorFromString(currentNeedle, {','});
1961  for(size_t i = csvNeedles.size() - 1; i < csvNeedles.size(); --i)
1962  {
1963  if(csvNeedles[i].size() == 0)
1964  continue; //skip empty entries
1965  //change the priority to the last placed priority entry to keep group order
1966  priority = modifyCustomTriggeredAction(
1967  StringMacros::decodeURIComponent(csvNeedles[i]),
1968  buttonDo,
1969  needle,
1970  action,
1971  priority,
1972  triggerOnCount,
1973  doLoop,
1974  isArmed);
1975  } //end csv needle list handling
1976  }
1977  else
1978  addCustomTriggeredAction(
1979  needle, action, priority, triggerOnCount, doLoop, isArmed);
1980 
1981  saveCustomCountList();
1982  } // end AddCustomCountsAndAction
1983 
1984  //always calculate untriggered count
1985  size_t untriggeredCount =
1986  messageCount_; //copy and then decrement "unique" incrementing ID for messages
1987 
1988  for(const auto& customCount : priorityCustomTriggerList_)
1989  {
1990  xercesc::DOMElement* customCountParent =
1991  xmlOut.addTextElementToData("customCount", "");
1992 
1993  if(customCount.occurrences < untriggeredCount)
1994  untriggeredCount -= customCount.occurrences;
1995  else
1996  {
1997  __SUP_SS__ << "Impossible custom count; notify admins! "
1998  << customCount.occurrences << " > " << untriggeredCount
1999  << " for "
2000  << StringMacros::vectorToString(customCount.needleSubstrings,
2001  {'*'})
2002  << __E__;
2003  __SUP_SS_THROW__;
2004  }
2005  xmlOut.addTextElementToParent(
2006  "needle",
2007  StringMacros::vectorToString(customCount.needleSubstrings, {'*'}),
2008  customCountParent);
2009  xmlOut.addTextElementToParent(
2010  "count", std::to_string(customCount.occurrences), customCountParent);
2011  xmlOut.addTextElementToParent(
2012  "action", customCount.action, customCountParent);
2013  xmlOut.addTextElementToParent("triggerOnCount",
2014  std::to_string(customCount.triggerOnCount),
2015  customCountParent);
2016  xmlOut.addTextElementToParent(
2017  "doLoop", std::to_string(customCount.doLoop), customCountParent);
2018  xmlOut.addTextElementToParent(
2019  "isArmed", std::to_string(customCount.isArmed), customCountParent);
2020  } //end adding custom counts to response xml loop
2021 
2022  //add untriggered always last
2023  xercesc::DOMElement* customCountParent =
2024  xmlOut.addTextElementToData("customCount", "");
2025  xmlOut.addTextElementToParent("needle", "< Untriggered >", customCountParent);
2026  xmlOut.addTextElementToParent(
2027  "count", std::to_string(untriggeredCount), customCountParent);
2028  xmlOut.addTextElementToParent("action", "Count Only", customCountParent);
2029  xmlOut.addTextElementToParent("triggerOnCount", "1", customCountParent);
2030  xmlOut.addTextElementToParent("doLoop", "1", customCountParent);
2031 
2032  } // end GetCustomCountsAndActions or AddCustomCountsAndAction
2033  else
2034  {
2035  __SUP_SS__ << "requestType Request, " << requestType
2036  << ", not recognized by the Console Supervisor (was it intended for "
2037  "another Supervisor?)."
2038  << __E__;
2039  __SUP_SS_THROW__;
2040  }
2041 } // end request()
2042 
2043 //==============================================================================
2047 {
2048  //Console Supervisor status detatil format is:
2049  // uptime, Err count, Warn count, Last Error msg, Last Warn msg
2050 
2051  //return uptime detail
2052  std::stringstream ss;
2053  ss << "Uptime: "
2054  << StringMacros::encodeURIComponent(StringMacros::getTimeDurationString(
2055  CorePropertySupervisorBase::getSupervisorUptime()));
2056 
2057  //return Err count, Warn count, Last Error msg, Last Warn msg, Last Info msg, Info count
2058 
2059  // size_t errorCount_ = 0, warnCount_ = 0;
2060  // std::string lastErrorMessage_, lastWarnMessage_;
2061  // time_t lastErrorMessageTime_ = 0, lastWarnMessageTime_ = 0;
2062 
2063  ss << ", Errors: " << errorCount_;
2064  ss << ", Warns: " << warnCount_;
2065  ss << ", Last Error ("
2066  << (lastErrorMessageTime_ ? StringMacros::getTimestampString(lastErrorMessageTime_)
2067  : "0")
2068  << "): "
2069  << (lastErrorMessageTime_ ? StringMacros::encodeURIComponent(lastErrorMessage_)
2070  : "");
2071  ss << ", Last Warn ("
2072  << (lastWarnMessageTime_ ? StringMacros::getTimestampString(lastWarnMessageTime_)
2073  : "0")
2074  << "): "
2075  << (lastWarnMessageTime_ ? StringMacros::encodeURIComponent(lastWarnMessage_)
2076  : "");
2077  ss << ", Last Info ("
2078  << (lastInfoMessageTime_ ? StringMacros::getTimestampString(lastInfoMessageTime_)
2079  : "0")
2080  << "): "
2081  << (lastInfoMessageTime_ ? StringMacros::encodeURIComponent(lastInfoMessage_)
2082  : "");
2083  ss << ", Info #: " << infoCount_;
2084  ss << ", First Error ("
2085  << (firstErrorMessageTime_
2086  ? StringMacros::getTimestampString(firstErrorMessageTime_)
2087  : "0")
2088  << "): "
2089  << (firstErrorMessageTime_ ? StringMacros::encodeURIComponent(firstErrorMessage_)
2090  : "");
2091  ss << ", First Warn ("
2092  << (firstWarnMessageTime_ ? StringMacros::getTimestampString(firstWarnMessageTime_)
2093  : "0")
2094  << "): "
2095  << (firstWarnMessageTime_ ? StringMacros::encodeURIComponent(firstWarnMessage_)
2096  : "");
2097  ss << ", First Info ("
2098  << (firstInfoMessageTime_ ? StringMacros::getTimestampString(firstInfoMessageTime_)
2099  : "0")
2100  << "): "
2101  << (firstInfoMessageTime_ ? StringMacros::encodeURIComponent(firstInfoMessage_)
2102  : "");
2103 
2104  return ss.str();
2105 } // end getStatusProgressDetail()
2106 
2107 //==============================================================================
2130 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument* xmlOut,
2131  const size_t lastUpdateCount)
2132 {
2133  //__SUP_COUT__ << __E__;
2134 
2135  if(messages_.size() == 0)
2136  return;
2137 
2138  // validate lastUpdateCount
2139  if(lastUpdateCount > messages_.back().getCount() && lastUpdateCount != (size_t)-1)
2140  {
2141  __SS__ << "Invalid lastUpdateCount: " << lastUpdateCount
2142  << " messagesArray size = " << messages_.back().getCount() << __E__;
2143  __SS_THROW__;
2144  }
2145 
2146  // lockout the messages array for the remainder of the scope
2147  // this guarantees the reading thread can safely access the messages
2148  std::lock_guard<std::mutex> lock(messageMutex_);
2149 
2150  xmlOut->addTextElementToData("last_update_count",
2151  std::to_string(messages_.back().getCount()));
2152 
2153  refreshParent_ = xmlOut->addTextElementToData("messages", "");
2154 
2155  bool requestOutOfSync = false;
2156  std::string requestOutOfSyncMsg;
2157 
2158  size_t refreshReadPointer = 0;
2159  if(lastUpdateCount != (size_t)-1)
2160  {
2161  while(refreshReadPointer < messages_.size() &&
2162  messages_[refreshReadPointer].getCount() <= lastUpdateCount)
2163  {
2164  ++refreshReadPointer;
2165  }
2166  }
2167 
2168  if(refreshReadPointer >= messages_.size())
2169  return;
2170 
2171  // limit number of catch-up messages
2172  if(messages_.size() - refreshReadPointer > maxClientMessageRequest_)
2173  {
2174  // __SUP_COUT__ << "Only sending latest " << maxClientMessageRequest_ << "
2175  // messages!";
2176 
2177  // auto oldrrp = refreshReadPointer;
2178  refreshReadPointer = messages_.size() - maxClientMessageRequest_;
2179 
2180  // __SS__ << "Skipping " << (refreshReadPointer - oldrrp)
2181  // << " messages because the web console has fallen behind!" << __E__;
2182  // __COUT__ << ss.str();
2183  // ConsoleMessageStruct msg(CONSOLE_SPECIAL_WARNING + ss.str(), lastUpdateCount);
2184  // auto it = messages_.begin();
2185  // std::advance(it, refreshReadPointer + 1);
2186  // messages_.insert(it, msg);
2187  }
2188 
2189  //return first_update_count, so that older messages could be retrieved later if desired by user
2190  xmlOut->addTextElementToData(
2191  "earliest_update_count",
2192  std::to_string(messages_[refreshReadPointer].getCount()));
2193 
2194  std::string messagesJson = "[";
2195 
2196  // output oldest to new
2197  for(; refreshReadPointer < messages_.size(); ++refreshReadPointer)
2198  {
2199  auto msg = messages_[refreshReadPointer];
2200  if(msg.getCount() < lastUpdateCount)
2201  {
2202  if(!requestOutOfSync) // record out of sync message once only
2203  {
2204  requestOutOfSync = true;
2205  __SS__ << "Request is out of sync! Message count should be more recent! "
2206  << msg.getCount() << " < " << lastUpdateCount << __E__;
2207  requestOutOfSyncMsg = ss.str();
2208  }
2209  // assume these messages are new (due to a system restart)
2210  // continue;
2211  }
2212 
2213  // addMessageToResponse(xmlOut, msg);
2214  addMessageToResponse(messagesJson, msg);
2215  __COUTTV__(messagesJson.size());
2216 
2217  } //end main message add loop
2218 
2219  messagesJson += "]";
2220 
2221  xmlOut->addTextElementToParent("message_json", messagesJson, refreshParent_);
2222 
2223  if(requestOutOfSync) // if request was out of sync, show message
2224  __SUP_COUT__ << requestOutOfSyncMsg;
2225 } // end insertMessageRefresh()
2226 
2227 //==============================================================================
2248 void ConsoleSupervisor::prependHistoricMessages(HttpXmlDocument* xmlOut,
2249  const size_t earliestOnhandMessageCount)
2250 {
2251  //__SUP_COUT__ << __E__;
2252 
2253  if(messages_.size() == 0)
2254  return;
2255 
2256  // validate earliestOnhandMessageCount
2257  if(earliestOnhandMessageCount >= messages_.back().getCount())
2258  {
2259  __SS__
2260  << "Invalid claim from user request of earliest onhand message sequence ID = "
2261  << earliestOnhandMessageCount
2262  << ". Latest existing sequence ID = " << messages_.back().getCount()
2263  << ". Was the Console Supervisor restarted?" << __E__;
2264  __SS_THROW__;
2265  }
2266 
2267  // lockout the messages array for the remainder of the scope
2268  // this guarantees the reading thread can safely access the messages
2269  std::lock_guard<std::mutex> lock(messageMutex_);
2270 
2271  refreshParent_ = xmlOut->addTextElementToData("messages", "");
2272 
2273  size_t refreshReadPointer = 0;
2274  size_t readCountStart = earliestOnhandMessageCount - maxClientMessageRequest_;
2275  if(readCountStart >= messages_.back().getCount()) //then wrapped around, so set to 0
2276  readCountStart = 0;
2277 
2278  //find starting read pointer
2279  while(refreshReadPointer < messages_.size() &&
2280  messages_[refreshReadPointer].getCount() < readCountStart)
2281  {
2282  ++refreshReadPointer;
2283  }
2284 
2285  if(refreshReadPointer >= messages_.size())
2286  return;
2287 
2288  xmlOut->addTextElementToData("earliest_update_count", //return new early onhand count
2289  std::to_string(readCountStart));
2290 
2291  std::string messagesJson = "[";
2292 
2293  //messages returned will be from readCountStart to earliestOnhandMessageCount-1
2294  // output oldest to new
2295  for(; refreshReadPointer < messages_.size(); ++refreshReadPointer)
2296  {
2297  auto msg = messages_[refreshReadPointer];
2298  if(messages_[refreshReadPointer].getCount() >= earliestOnhandMessageCount)
2299  break; //found last message
2300 
2301  addMessageToResponse(messagesJson, msg);
2302 
2303  } //end main message add loop
2304 
2305  messagesJson += "]";
2306 
2307  xmlOut->addTextElementToParent("message_json", messagesJson, refreshParent_);
2308 
2309 } // end prependHistoricMessages()
2310 
2311 //==============================================================================
2312 void ConsoleSupervisor::addMessageToResponse(HttpXmlDocument* xmlOut,
2314 {
2315  // for all fields, give value
2316  for(auto& field : msg.fields)
2317  {
2318  if(field.first == ConsoleMessageStruct::FieldType::SOURCE)
2319  continue; // skip, not userful
2320  if(field.first == ConsoleMessageStruct::FieldType::SOURCEID)
2321  continue; // skip, not userful
2322  if(field.first == ConsoleMessageStruct::FieldType::SEQID)
2323  continue; // skip, not userful
2324  if(field.first == ConsoleMessageStruct::FieldType::TIMESTAMP) //use Time instead
2325  continue; // skip, not userful
2326  if(field.first ==
2327  ConsoleMessageStruct::FieldType::LEVEL) //use modified getLevel instead
2328  continue; // skip, not userful
2329 
2330  xmlOut->addTextElementToParent(
2331  "message_" + ConsoleMessageStruct::fieldNames.at(field.first),
2332  field.second,
2333  refreshParent_);
2334  } //end msg field loop
2335 
2336  // give modified level also
2337  xmlOut->addTextElementToParent("message_Level", msg.getLevel(), refreshParent_);
2338 
2339  // give timestamp also
2340  xmlOut->addTextElementToParent("message_Time", msg.getTime(), refreshParent_);
2341 
2342  // give global count index also
2343  xmlOut->addTextElementToParent(
2344  "message_Count", std::to_string(msg.getCount()), refreshParent_);
2345 
2346  //give Custom count label also (i.e., which search string this message matches, or blank "" for no match)
2347  xmlOut->addTextElementToParent(
2348  "message_Custom",
2349  StringMacros::vectorToString(msg.getCustomTriggerMatch().needleSubstrings, {'*'}),
2350  refreshParent_);
2351 
2352 } // end addMessageToResponse()
2353 
2354 //==============================================================================
2356 void ConsoleSupervisor::addMessageToResponse(std::string& xmlValue,
2358 {
2359  if(xmlValue.size() &&
2360  xmlValue[xmlValue.size() - 1] != '[') //if not first entry, add comma
2361  xmlValue += ",";
2362 
2363  xmlValue += "{";
2364  // for all fields, give value
2365  for(auto& field : msg.fields)
2366  {
2367  if(field.first == ConsoleMessageStruct::FieldType::SOURCE)
2368  continue; // skip, not useful
2369  if(field.first == ConsoleMessageStruct::FieldType::SOURCEID)
2370  continue; // skip, not useful
2371  if(field.first == ConsoleMessageStruct::FieldType::SEQID)
2372  continue; // skip, not useful
2373  if(field.first == ConsoleMessageStruct::FieldType::TIMESTAMP) //use Time instead
2374  continue; // skip, not useful
2375  if(field.first ==
2376  ConsoleMessageStruct::FieldType::LEVEL) //use modified getLevel instead
2377  continue; // skip, not useful
2378 
2379  if(field.first ==
2380  ConsoleMessageStruct::FieldType::MSG) //only need to escape message field
2381  xmlValue += "\"" + ConsoleMessageStruct::fieldNames.at(field.first) +
2382  "\":\"" + StringMacros::escapeString(field.second) + "\",";
2383  else
2384  xmlValue += "\"" + ConsoleMessageStruct::fieldNames.at(field.first) +
2385  "\":\"" + field.second + "\",";
2386 
2387  // xmlOut->addTextElementToParent(
2388  // "message_" + ConsoleMessageStruct::fieldNames.at(field.first),
2389  // field.second,
2390  // refreshParent_);
2391  } //end msg field loop
2392 
2393  // give modified level also
2394  xmlValue += "\"Level\":\"" + msg.getLevel() + "\",";
2395  // xmlOut->addTextElementToParent("message_Level", msg.getLevel(), refreshParent_);
2396 
2397  // give timestamp also
2398  xmlValue += "\"Time\":\"" + msg.getTime() + "\",";
2399  // xmlOut->addTextElementToParent("message_Time", msg.getTime(), refreshParent_);
2400 
2401  // give global count index also
2402  xmlValue += "\"Count\":\"" + std::to_string(msg.getCount()) + "\",";
2403  //xmlOut->addTextElementToParent("message_Count", std::to_string(msg.getCount()), refreshParent_);
2404 
2405  //give Custom count label also (i.e., which search string this message matches, or blank "" for no match)
2406  xmlValue += "\"Custom\":\"" +
2408  msg.getCustomTriggerMatch().needleSubstrings, {'*'})) +
2409  "\"";
2410  // xmlOut->addTextElementToParent(
2411  // "message_Custom",
2412  // StringMacros::vectorToString(msg.getCustomTriggerMatch().needleSubstrings, {'*'}),
2413  // refreshParent_);
2414 
2415  xmlValue += "}";
2416 } // 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, bool forHtml=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)