otsdaq-utilities  3.10.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 (rate-limited to once per 5 minutes)
586  constexpr time_t TRIGGER_SYSTEM_MESSAGE_COOLDOWN_S = 5 * 60;
587  bool sendSystemMessage =
588  (triggeredAction.action != "Count Only") &&
589  (time(0) - lastTriggeredSystemMessageTime_ >= TRIGGER_SYSTEM_MESSAGE_COOLDOWN_S);
590 
591  if(triggeredAction.action != "Count Only" && !sendSystemMessage)
592  __SUP_COUT_INFO__ << "Suppressing repeated system message for triggered action '"
593  << triggeredAction.action << "' (cooldown "
594  << TRIGGER_SYSTEM_MESSAGE_COOLDOWN_S << "s)" << __E__;
595 
596  if(sendSystemMessage)
597  {
598  lastTriggeredSystemMessageTime_ = time(0);
599  theRemoteWebUsers_.sendSystemMessage(
600  "*" /* to all users*/,
601  "In the Console Supervisor, a custom count fired the action '" +
602  triggeredAction.action + "' on the search string '" +
603  StringMacros::vectorToString(triggeredAction.needleSubstrings, {'*'}) +
604  "'");
605  }
606 
607  if(triggeredAction.action == "Halt")
608  {
609  try
610  {
612  "Console-triggered FSM Halt", 0, 0);
613  }
614  catch(...)
615  {
616  theRemoteWebUsers_.sendSystemMessage(
617  "*" /* to all users*/,
618  "FSM Halt from Console Supervisor Triggered Action has failed!");
619  }
620  __SUP_COUTV__("FSM Halt triggered from console");
621  }
622  else if(triggeredAction.action == "Pause")
623  {
624  try
625  {
627  "Console-triggered FSM Pause", 1, 0);
628  }
629  catch(...)
630  {
631  theRemoteWebUsers_.sendSystemMessage(
632  "*" /* to all users*/,
633  "FSM Pause from Console Supervisor Triggered Action has failed!");
634  }
635  __SUP_COUTV__("FSM Pause triggered from console");
636  }
637  else if(triggeredAction.action == "Stop")
638  {
639  try
640  {
642  "Console-triggered FSM Stop", 0, 1);
643  }
644  catch(const std::exception& e)
645  {
646  theRemoteWebUsers_.sendSystemMessage(
647  "*" /* to all users*/,
648  "FSM Stop from Console Supervisor Triggered Action has failed!");
649  }
650  __SUP_COUTV__("FSM Stop triggered from console");
651  }
652  else if(triggeredAction.action == "Run Script")
653  {
654  std::string triggerScriptPath = "";
655  std::string scriptResult = "";
656  try
657  {
658  triggerScriptPath = __ENV__("OTS_CUSTOM_TRIGGER_SCRIPT");
659  triggerScriptPath = "source " + triggerScriptPath;
660  scriptResult = StringMacros::exec(triggerScriptPath.c_str());
661  __COUT_INFO__ << "The Script " << triggerScriptPath
662  << " Was launched, here is the result " << scriptResult;
663  }
664  catch(...)
665  {
666  __SS__ << "Trigger script path not defined! Please use environment variable "
667  "'OTS_CUSTOM_TRIGGER_SCRIPT' or contact admins."
668  << __E__;
669  __SS_THROW__;
670  }
671  }
672 
673 } // end doTriggeredAction()
674 
675 //==============================================================================
677 void ConsoleSupervisor::addCustomTriggeredAction(const std::string& triggerNeedle,
678  const std::string& triggerAction,
679  uint32_t priority, /* = -1 */
680  uint32_t triggerOnCount,
681  bool doLoop,
682  bool isArmed)
683 {
684  __SUP_COUTV__("Adding custom triggered action");
685  __SUP_COUTV__(triggerNeedle);
686  __SUP_COUTV__(triggerAction);
687  __SUP_COUTV__(priority);
688  __SUP_COUTV__(triggerOnCount);
689  __SUP_COUTV__(doLoop);
690  __SUP_COUTV__(isArmed);
691 
692  bool allAsterisks = true;
693  for(const auto& c : triggerNeedle)
694  if(c != '*')
695  {
696  allAsterisks = false;
697  break;
698  }
699  if(allAsterisks)
700  {
701  __SUP_SS__ << "Illegal empty Search String value for the new Custom Count and "
702  "Action! Please enter a valid Search String (* wildcards are "
703  "allowed, e.g. \"value = * seconds\")."
704  << __E__;
705  __SUP_SS_THROW__;
706  }
707 
708  //check if triggerNeedle already exists
709  uint32_t currentPriority = -1;
710  for(const auto& customTrigger : priorityCustomTriggerList_)
711  {
712  ++currentPriority; //inc first to get to 0
713  if(StringMacros::vectorToString(customTrigger.needleSubstrings, {'*'}) ==
714  triggerNeedle)
715  {
716  __SUP_SS__ << "Failure! Can not add Custom Count Search String that already "
717  "exists. Found '"
718  << triggerNeedle
719  << "' already existing at priority = " << currentPriority << __E__;
720  __SUP_SS_THROW__;
721  }
722  } //end check if already exists
723 
724  if(priority >= priorityCustomTriggerList_.size())
725  priority = priorityCustomTriggerList_.size(); //place at end
726  if(priority == 0 && triggerNeedle != CONSOLE_MISSED_NEEDLE)
727  {
728  __SUP_SS__ << "Illegal priority position of '" << priority
729  << "' requested. Please enter a priority value greater than 0. "
730  "Position 0 is reserved for identifying missing messages at the "
731  "Console Supervisor. Note: the action for missing messages, at "
732  "priority 0, may be customized by the user."
733  << __E__;
734  __SUP_SS_THROW__;
735  }
736  __SUP_COUTV__(priority);
737 
738  //valid actions:
739  // Halt
740  // Stop
741  // Pause
742  // Soft Error
743  // Hard Error
744  // System Message
745  // Run Script
746 
747  if(CUSTOM_TRIGGER_ACTIONS.find(triggerAction) == CUSTOM_TRIGGER_ACTIONS.end())
748  {
749  __SUP_SS__ << "Unrecognized triggered action '" << triggerAction
750  << ",' valid actions are "
752  __SUP_SS_THROW__;
753  }
754 
755  //insert new custom count at priority position
756  priorityCustomTriggerList_.insert(priorityCustomTriggerList_.begin() + priority,
757  CustomTriggeredAction_t());
758  // priorityCustomTriggerList_.push_back(CustomTriggeredAction_t());
759  // priorityCustomTriggerList_.back()
760 
761  //break up on substring
763  triggerNeedle,
764  priorityCustomTriggerList_[priority].needleSubstrings,
765  {'*'} /* delimiter */,
766  {} /* do not ignore whitespace */);
767  priorityCustomTriggerList_[priority].action = triggerAction;
768  priorityCustomTriggerList_[priority].triggerOnCount = triggerOnCount;
769  priorityCustomTriggerList_[priority].doLoop = doLoop;
770  priorityCustomTriggerList_[priority].isArmed = isArmed;
771 
772  __SUP_COUT__ << "Added custom count: '"
774  priorityCustomTriggerList_[priority].needleSubstrings, "*")
775  << "' at priority: " << priority
776  << " triggered every: " << triggerOnCount << " occurrences";
777  if(doLoop)
778  __SUP_COUT__ << " and will loop.";
779  else
780  __SUP_COUT__ << " and will not loop.";
781  __SUP_COUT__ << __E__;
782 
783 } // end addCustomTriggeredAction()
784 
785 //==============================================================================
789 uint32_t ConsoleSupervisor::modifyCustomTriggeredAction(const std::string& currentNeedle,
790  const std::string& modifyType,
791  const std::string& setNeedle,
792  const std::string& setAction,
793  uint32_t setPriority,
794  uint32_t setTriggerOnCount,
795  bool setDoLoop,
796  bool setIsArmed)
797 {
798  __SUP_COUTV__(currentNeedle);
799  __SUP_COUTV__(modifyType);
800  __SUP_COUTV__(setNeedle);
801  __SUP_COUTV__(setAction);
802  __SUP_COUTV__(setPriority);
803  __SUP_COUTV__(setTriggerOnCount);
804  __SUP_COUTV__(setDoLoop);
805  __SUP_COUTV__(setIsArmed);
806 
807  //find current priority position of currentNeedle
808  uint32_t currentPriority = -1;
809  bool found = false;
810  for(const auto& customTrigger : priorityCustomTriggerList_)
811  {
812  ++currentPriority; //inc first to get to 0, -1 indicates not found
813  if(StringMacros::vectorToString(customTrigger.needleSubstrings, {'*'}) ==
814  currentNeedle)
815  {
816  found = true;
817  break; //found
818  }
819  }
820 
821  __SUP_COUTV__(currentPriority);
822  if(!found)
823  {
824  __SUP_SS__ << "Attempt to modify Custom Count Search String failed. Could not "
825  "find specified Search String '"
826  << currentNeedle << "' in prioritized list." << __E__;
827  __SUP_SS_THROW__;
828  }
829 
830  if(modifyType == "Deletion")
831  {
832  if(currentPriority == 0)
833  {
834  __SUP_SS__ << "Illegal deletion requested of priority position 0. Position 0 "
835  "is reserved for identifying missing messages at the Console "
836  "Supervisor. Note: the action of priority 0 may be customized "
837  "by the user, but it can not be deleted."
838  << __E__;
839  __SUP_SS_THROW__;
840  }
841 
842  __SUP_COUT__ << "Deleting custom count: "
844  priorityCustomTriggerList_[currentPriority].needleSubstrings,
845  {'*'})
846  << " w/action: "
847  << priorityCustomTriggerList_[currentPriority].action
848  << " and priority: " << currentPriority << __E__;
849  priorityCustomTriggerList_.erase(priorityCustomTriggerList_.begin() +
850  currentPriority);
851  return -1;
852  }
853 
854  if(modifyType == "Priority" || modifyType == "All")
855  {
856  if(setPriority >= priorityCustomTriggerList_.size())
857  setPriority = priorityCustomTriggerList_.size(); //place at end
858  if(setPriority == 0 && setNeedle != CONSOLE_MISSED_NEEDLE)
859  {
860  __SUP_SS__ << "Illegal priority position of '" << setPriority
861  << "' requested. Position 0 is reserved for identifying missing "
862  "messages at the Console Supervisor. Note: the action of "
863  "priority 0 may be customized by the user."
864  << __E__;
865  __SUP_SS_THROW__;
866  }
867  }
868  else //keep existing
869  setPriority = currentPriority;
870 
871  if(modifyType == "Action" || modifyType == "All")
872  {
873  //valid actions:
874  // Halt
875  // Stop
876  // Pause
877  // Soft Error
878  // Hard Error
879  // System Message
880 
881  if(CUSTOM_TRIGGER_ACTIONS.find(setAction) == CUSTOM_TRIGGER_ACTIONS.end())
882  {
883  __SUP_SS__ << "Unrecognized custom count action '" << setAction
884  << ",' valid actions are "
886  __SUP_SS_THROW__;
887  }
888  //modify existing action
889  priorityCustomTriggerList_[currentPriority].action = setAction;
890  }
891 
892  if(modifyType == "Search String" || modifyType == "All")
893  {
894  //modify existing needle
895  priorityCustomTriggerList_[currentPriority].needleSubstrings.clear();
897  setNeedle,
898  priorityCustomTriggerList_[currentPriority].needleSubstrings,
899  {'*'} /* delimiter */,
900  {} /* do not ignore whitespace */);
901  }
902 
903  if(modifyType == "Trigger on Count" || modifyType == "All")
904  {
905  //modify existing action
906  priorityCustomTriggerList_[currentPriority].triggerOnCount = setTriggerOnCount;
907  }
908  if(modifyType == "Do Loop" || modifyType == "All")
909  {
910  //modify existing action
911  priorityCustomTriggerList_[currentPriority].doLoop = setDoLoop;
912  }
913  if(modifyType == "Arm Trigger" || modifyType == "All")
914  {
915  //modify existing action
916  priorityCustomTriggerList_[currentPriority].isArmed = setIsArmed;
917  }
918 
919  if(currentPriority != setPriority) //then need to copy
920  {
921  //insert new custom count at priority position
922  priorityCustomTriggerList_.insert(
923  priorityCustomTriggerList_.begin() + setPriority,
924  priorityCustomTriggerList_[currentPriority]);
925 
926  //delete from old position
927  if(currentPriority >= setPriority) //then increment after insert
928  ++currentPriority;
929 
930  priorityCustomTriggerList_.erase(priorityCustomTriggerList_.begin() +
931  currentPriority);
932 
933  if(currentPriority < setPriority) //then decrement after delete
934  --setPriority;
935  }
936 
937  __SUP_COUT__ << "Modified '" << modifyType << "' custom count: "
939  priorityCustomTriggerList_[setPriority].needleSubstrings, {'*'})
940  << " now w/action: " << priorityCustomTriggerList_[setPriority].action
941  << " and at priority: " << setPriority << __E__;
942 
943  return setPriority;
944 } // end modifyCustomTriggeredAction()
945 
946 //==============================================================================
947 void ConsoleSupervisor::loadCustomCountList()
948 {
949  // TODO: Migrate to a more robust read/write method
950  __SUP_COUT__ << "loadCustomCountList() from "
951  << USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME << __E__;
952 
953  FILE* fp = fopen((USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME).c_str(), "r");
954  if(!fp)
955  {
956  __SUP_COUT__ << "Ignoring missing Custom Count list file at path: "
957  << (USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME) << __E__;
958  return;
959  }
960  priorityCustomTriggerList_.clear();
961 
962  char line[1000]; //do not allow larger than 1000 chars!
963  uint32_t i = 0;
964  std::string needle;
965  std::string action;
966  size_t priority = 0;
967  size_t triggerOnCount = 0;
968  bool doLoop = false;
969  bool isArmed = false;
970  while(fgets(line, 1000, fp))
971  {
972  ++i;
973 
974  //ignore new line
975  if(strlen(line))
976  line[strlen(line) - 1] = '\0';
977 
978  __SUP_COUTV__(i);
979  __SUP_COUTV__(line);
980 
981  if(i == 1) //needle
982  needle = line;
983  else if(i == 2)
984  action = line;
985  else if(i == 3)
986  priority = std::stoi(line);
987  else if(i == 4)
988  triggerOnCount = std::stoi(line);
989  else if(i == 5)
990  doLoop = std::stoi(line) > 0 ? true : false;
991  else if(i == 6) // last line
992  {
993  isArmed = std::stoi(line) > 0 ? true : false;
994  __SUP_COUTTV__(needle);
995  __SUP_COUTTV__(priority);
996  __SUP_COUTTV__(action);
997  __SUP_COUTTV__(triggerOnCount);
998  __SUP_COUTTV__(doLoop);
999  __SUP_COUTTV__(isArmed);
1000  if(i == 1 &&
1001  needle !=
1002  CONSOLE_MISSED_NEEDLE) //then force missed Console message as priority 0
1003  addCustomTriggeredAction(
1004  CONSOLE_MISSED_NEEDLE, "System Message", 0, 1, false, true);
1005  addCustomTriggeredAction(
1006  needle, action, priority, triggerOnCount, doLoop, isArmed);
1007  i = 0; //reset for next entry
1008  }
1009  }
1010  fclose(fp);
1011 
1012 } // end loadCustomCountList()
1013 
1014 //==============================================================================
1015 void ConsoleSupervisor::saveCustomCountList()
1016 {
1017  __SUP_COUT__ << "saveCustomCountList()" << __E__;
1018 
1019  FILE* fp = fopen((USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME).c_str(), "w");
1020  if(!fp)
1021  {
1022  __SUP_SS__ << "Failed to create Custom Count list file at path: "
1023  << (USER_CONSOLE_PREF_PATH + CUSTOM_COUNT_LIST_FILENAME) << __E__;
1024  __SUP_SS_THROW__;
1025  }
1026  unsigned int priority = 0;
1027  for(auto& customCount : priorityCustomTriggerList_)
1028  {
1029  fprintf(fp,
1030  (StringMacros::vectorToString(customCount.needleSubstrings, {'*'}) + "\n")
1031  .c_str());
1032  fprintf(fp, "%s\n", customCount.action.c_str());
1033  fprintf(fp, "%d\n", priority);
1034  fprintf(fp, "%zu\n", customCount.triggerOnCount);
1035  fprintf(fp, "%s\n", customCount.doLoop ? "1" : "0");
1036  fprintf(fp, "%s\n", customCount.isArmed ? "1" : "0");
1037  ++priority;
1038  }
1039  fclose(fp);
1040 } // end saveCustomCountList()
1041 
1042 //==============================================================================
1043 void ConsoleSupervisor::defaultPage(xgi::Input* /*in*/, xgi::Output* out)
1044 {
1045  // __SUP_COUT__ << "ApplicationDescriptor LID="
1046  // << getApplicationDescriptor()->getLocalId() << __E__;
1047  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
1048  "src='/WebPath/html/Console.html?urn="
1049  << getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
1050 } // end defaultPage()
1051 
1052 //==============================================================================
1056 {
1057  CorePropertySupervisorBase::setSupervisorProperty(
1058  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
1059  "GetConsoleMsgs");
1060 } // end forceSupervisorPropertyValues()
1061 
1062 //==============================================================================
1066 void ConsoleSupervisor::request(const std::string& requestType,
1067  cgicc::Cgicc& cgiIn,
1068  HttpXmlDocument& xmlOut,
1069  const WebUsers::RequestUserInfo& userInfo)
1070 {
1071  //__SUP_COUT__ << "requestType " << requestType << __E__;
1072 
1073  // Commands:
1074  // GetConsoleMsgs
1075  // PrependHistoricMessages
1076  // SaveUserPreferences
1077  // LoadUserPreferences
1078  // GetTraceLevels
1079  // SetTraceLevels
1080  // GetTriggerStatus
1081  // SetTriggerEnable
1082  // ResetTRACE
1083  // EnableTRACE
1084  // GetTraceSnapshot
1085  // GetCustomCountsAndActions
1086  // AddCustomCountsAndAction
1087  // ModifyCustomCountsAndAction
1088 
1089  // Note: to report to logbook admin status use
1090  // xmlOut.addTextElementToData(XML_ADMIN_STATUS,refreshTempStr_);
1091 
1092  if(requestType == "GetConsoleMsgs")
1093  {
1094  // lindex of -1 means first time and user just gets update lcount and lindex
1095  std::string lastUpdateCountStr = CgiDataUtilities::postData(cgiIn, "lcount");
1096 
1097  if(lastUpdateCountStr == "")
1098  {
1099  __SUP_COUT_ERR__ << "Invalid Parameters! lastUpdateCount="
1100  << lastUpdateCountStr << __E__;
1101  xmlOut.addTextElementToData("Error",
1102  "Error - Invalid parameters for GetConsoleMsgs.");
1103  return;
1104  }
1105 
1106  size_t lastUpdateCount = std::stoull(lastUpdateCountStr);
1107 
1108  // __SUP_COUT__ << "lastUpdateCount=" << lastUpdateCount << __E__;
1109 
1110  insertMessageRefresh(&xmlOut, lastUpdateCount);
1111  }
1112  else if(requestType == "PrependHistoricMessages")
1113  {
1114  size_t earliestOnhandMessageCount =
1115  CgiDataUtilities::postDataAsInt(cgiIn, "earlyCount");
1116  __SUP_COUTV__(earliestOnhandMessageCount);
1117  prependHistoricMessages(&xmlOut, earliestOnhandMessageCount);
1118  }
1119  else if(requestType == "SaveUserPreferences")
1120  {
1121  int colorIndex = CgiDataUtilities::postDataAsInt(cgiIn, "colorIndex");
1122  int showSideBar = CgiDataUtilities::postDataAsInt(cgiIn, "showSideBar");
1123  int noWrap = CgiDataUtilities::postDataAsInt(cgiIn, "noWrap");
1124  int messageOnly = CgiDataUtilities::postDataAsInt(cgiIn, "messageOnly");
1125  int hideLineNumers = CgiDataUtilities::postDataAsInt(cgiIn, "hideLineNumers");
1126  int noHost = CgiDataUtilities::postDataAsInt(cgiIn, "noHost");
1127 
1128  // __SUP_COUT__ << "requestType " << requestType << __E__;
1129  // __SUP_COUT__ << "colorIndex: " << colorIndex << __E__;
1130  // __SUP_COUT__ << "showSideBar: " << showSideBar << __E__;
1131  // __SUP_COUT__ << "noWrap: " << noWrap << __E__;
1132  // __SUP_COUT__ << "messageOnly: " << messageOnly << __E__;
1133  // __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << __E__;
1134  // __SUP_COUT__ << "noHost: " << noHost << __E__;
1135 
1136  if(userInfo.username_ == "") // should never happen?
1137  {
1138  __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_
1139  << __E__;
1140  xmlOut.addTextElementToData("Error",
1141  "Error - InvauserInfo.username_user found.");
1142  return;
1143  }
1144 
1145  std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." +
1146  (std::string)USERS_PREFERENCES_FILETYPE;
1147 
1148  // __SUP_COUT__ << "Save preferences: " << fn << __E__;
1149  FILE* fp = fopen(fn.c_str(), "w");
1150  if(!fp)
1151  {
1152  __SS__;
1153  __THROW__(ss.str() + "Could not open file: " + fn);
1154  }
1155  fprintf(fp, "colorIndex %d\n", colorIndex);
1156  fprintf(fp, "showSideBar %d\n", showSideBar);
1157  fprintf(fp, "noWrap %d\n", noWrap);
1158  fprintf(fp, "messageOnly %d\n", messageOnly);
1159  fprintf(fp, "hideLineNumers %d\n", hideLineNumers);
1160  fprintf(fp, "noHost %d\n", noHost);
1161  fclose(fp);
1162  }
1163  else if(requestType == "LoadUserPreferences")
1164  {
1165  // __SUP_COUT__ << "requestType " << requestType << __E__;
1166 
1167  unsigned int colorIndex, showSideBar, noWrap, messageOnly, hideLineNumers, noHost;
1168 
1169  if(userInfo.username_ == "") // should never happen?
1170  {
1171  __SUP_COUT_ERR__ << "Invalid user found! user=" << userInfo.username_
1172  << __E__;
1173  xmlOut.addTextElementToData("Error", "Error - Invalid user found.");
1174  return;
1175  }
1176 
1177  std::string fn = (std::string)USER_CONSOLE_PREF_PATH + userInfo.username_ + "." +
1178  (std::string)USERS_PREFERENCES_FILETYPE;
1179 
1180  // __SUP_COUT__ << "Load preferences: " << fn << __E__;
1181 
1182  FILE* fp = fopen(fn.c_str(), "r");
1183  if(!fp)
1184  {
1185  // return defaults
1186  __SUP_COUT__ << "Returning defaults." << __E__;
1187  xmlOut.addTextElementToData("colorIndex", "0");
1188  xmlOut.addTextElementToData("showSideBar", "0");
1189  xmlOut.addTextElementToData("noWrap", "1");
1190  xmlOut.addTextElementToData("messageOnly", "0");
1191  xmlOut.addTextElementToData("hideLineNumers", "1");
1192  xmlOut.addTextElementToData("noHost", "1");
1193  return;
1194  }
1195  fscanf(fp, "%*s %u", &colorIndex);
1196  fscanf(fp, "%*s %u", &showSideBar);
1197  fscanf(fp, "%*s %u", &noWrap);
1198  fscanf(fp, "%*s %u", &messageOnly);
1199  fscanf(fp, "%*s %u", &hideLineNumers);
1200  if(fscanf(fp, "%*s %u", &noHost) != 1)
1201  noHost = 1; // default: hidden (backward compat with old pref files)
1202  fclose(fp);
1203  // __SUP_COUT__ << "colorIndex: " << colorIndex << __E__;
1204  // __SUP_COUT__ << "showSideBar: " << showSideBar << __E__;
1205  // __SUP_COUT__ << "noWrap: " << noWrap << __E__;
1206  // __SUP_COUT__ << "messageOnly: " << messageOnly << __E__;
1207  // __SUP_COUT__ << "hideLineNumers: " << hideLineNumers << __E__;
1208 
1209  char tmpStr[20];
1210  sprintf(tmpStr, "%u", colorIndex);
1211  xmlOut.addTextElementToData("colorIndex", tmpStr);
1212  sprintf(tmpStr, "%u", showSideBar);
1213  xmlOut.addTextElementToData("showSideBar", tmpStr);
1214  sprintf(tmpStr, "%u", noWrap);
1215  xmlOut.addTextElementToData("noWrap", tmpStr);
1216  sprintf(tmpStr, "%u", messageOnly);
1217  xmlOut.addTextElementToData("messageOnly", tmpStr);
1218  sprintf(tmpStr, "%u", hideLineNumers);
1219  xmlOut.addTextElementToData("hideLineNumers", tmpStr);
1220  sprintf(tmpStr, "%u", noHost);
1221  xmlOut.addTextElementToData("noHost", tmpStr);
1222  }
1223  else if(requestType == "GetTraceLevels")
1224  {
1225  __SUP_COUT__ << "requestType " << requestType << __E__;
1226 
1227  SOAPParameters txParameters; // params for xoap to send
1228  txParameters.addParameter("Request", "GetTraceLevels");
1229 
1230  SOAPParameters rxParameters; // params for xoap to recv
1231  rxParameters.addParameter("Command");
1232  rxParameters.addParameter("Error");
1233  rxParameters.addParameter("TRACEHostnameList");
1234  rxParameters.addParameter("TRACEList");
1235 
1236  traceMapToXDAQHostname_.clear(); // reset
1237 
1238  std::string traceList = "";
1239  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1240  __SUP_COUT__ << "Querying " << allTraceApps.size()
1241  << " TRACE controller supervisor(s) for TRACE levels. Hosts: ";
1242  {
1243  std::stringstream appHosts;
1244  for(const auto& appInfo : allTraceApps)
1245  appHosts << appInfo.first << "(" << appInfo.second.getClass() << ") ";
1246  __SUP_COUT__ << appHosts.str() << __E__;
1247  }
1248  for(const auto& appInfo : allTraceApps)
1249  {
1250  __SUP_COUT__ << "Supervisor hostname = " << appInfo.first << "/"
1251  << appInfo.second.getId()
1252  << " name = " << appInfo.second.getName()
1253  << " class = " << appInfo.second.getClass()
1254  << " hostname = " << appInfo.second.getHostname() << __E__;
1255  try
1256  {
1257  xoap::MessageReference retMsg =
1258  SOAPMessenger::sendWithSOAPReply(appInfo.second.getDescriptor(),
1259  "TRACESupervisorRequest",
1260  txParameters);
1261  SOAPUtilities::receive(retMsg, rxParameters);
1262  __SUP_COUT__ << "Received TRACE response: "
1263  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1264  << SOAPUtilities::translate(retMsg) << __E__;
1265 
1266  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1267  {
1268  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1269  "hostname = "
1270  << appInfo.first << "/" << appInfo.second.getId()
1271  << " name = " << appInfo.second.getName()
1272  << " class = " << appInfo.second.getClass()
1273  << " hostname = " << appInfo.second.getHostname() << __E__;
1274  __SUP_SS_THROW__;
1275  }
1276  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1277  {
1278  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1279  << __E__;
1280  __SUP_SS_THROW__;
1281  }
1282  }
1283  catch(const xdaq::exception::Exception& e)
1284  {
1285  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1286  << appInfo.second.getId()
1287  << " name = " << appInfo.second.getName() << ". \n\n"
1288  << e.what() << __E__;
1289  __SUP_COUT_ERR__ << ss.str();
1290  continue; //skip bad Supervisor
1291  }
1292 
1293  std::vector<std::string> traceHostnameArr;
1294  __COUTTV__(rxParameters.getValue("TRACEHostnameList"));
1296  rxParameters.getValue("TRACEHostnameList"), traceHostnameArr, {';'});
1297  for(const auto& traceHostname : traceHostnameArr)
1298  {
1299  if(traceHostname == "")
1300  continue; //skip blanks
1301  traceMapToXDAQHostname_[traceHostname] = appInfo.first;
1302  }
1303 
1304  // traceList += ";" + appInfo.first; //insert xdaq context version of
1305  // name
1306  // //FIXME and create mapp from user's typed in xdaq
1307  // context name to TRACE hostname resolution
1308 
1309  __COUTTV__(rxParameters.getValue("TRACEList"));
1310  traceList += rxParameters.getValue("TRACEList");
1311 
1312  } // end app get TRACE loop
1313  __SUP_COUT__ << "TRACE hostname map received: \n"
1314  << StringMacros::mapToString(traceMapToXDAQHostname_) << __E__;
1315  __SUP_COUT__ << "TRACE List received: \n" << traceList << __E__;
1316  xmlOut.addTextElementToData("traceList", traceList);
1317  } // end GetTraceLevels
1318  else if(requestType == "SetTraceLevels")
1319  {
1320  __SUP_COUT__ << "requestType " << requestType << __E__;
1321 
1322  std::string individualValues =
1323  CgiDataUtilities::postData(cgiIn, "individualValues");
1324  std::string hostLabelMap = CgiDataUtilities::postData(cgiIn, "hostLabelMap");
1325  std::string setMode = CgiDataUtilities::postData(cgiIn, "setMode");
1326  std::string setValueMSB = CgiDataUtilities::postData(cgiIn, "setValueMSB");
1327  std::string setValueLSB = CgiDataUtilities::postData(cgiIn, "setValueLSB");
1328 
1329  __SUP_COUTV__(individualValues);
1330  __SUP_COUTV__(setMode);
1331  // set modes: SLOW, FAST, TRIGGER
1332  __SUP_COUTV__(setValueMSB);
1333  __SUP_COUTV__(setValueLSB);
1334 
1335  std::map<std::string /*host*/, std::string /*labelArr*/> hostToLabelMap;
1336 
1337  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1338 
1339  SOAPParameters rxParameters; // params for xoap to recv
1340  rxParameters.addParameter("Command");
1341  rxParameters.addParameter("Error");
1342  rxParameters.addParameter("TRACEList");
1343 
1344  std::string modifiedTraceList = "";
1345  std::string xdaqHostname;
1346  StringMacros::getMapFromString(hostLabelMap, hostToLabelMap, {';'}, {':'});
1347  for(auto& hostLabelsPair : hostToLabelMap)
1348  {
1349  // identify artdaq hosts to go through ARTDAQ supervisor
1350  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1351  __SUP_COUTV__(hostLabelsPair.first);
1352  __SUP_COUTV__(hostLabelsPair.second);
1353 
1354  // use map to convert to xdaq host
1355  try
1356  {
1357  xdaqHostname = traceMapToXDAQHostname_.at(hostLabelsPair.first);
1358  }
1359  catch(...)
1360  {
1361  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1362  << hostLabelsPair.first << "' to xdaq Context hostname."
1363  << __E__;
1364  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1365  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1366  << __E__;
1367  __SUP_SS_THROW__;
1368  }
1369 
1370  __SUP_COUTV__(xdaqHostname);
1371 
1372  auto& appInfo = allTraceApps.at(xdaqHostname);
1373  __SUP_COUT__ << "Supervisor hostname = " << hostLabelsPair.first << "/"
1374  << xdaqHostname << ":" << appInfo.getId()
1375  << " name = " << appInfo.getName()
1376  << " class = " << appInfo.getClass()
1377  << " hostname = " << appInfo.getHostname() << __E__;
1378  try
1379  {
1380  SOAPParameters txParameters; // params for xoap to send
1381  txParameters.addParameter("Request", "SetTraceLevels");
1382  txParameters.addParameter("IndividualValues", individualValues);
1383  txParameters.addParameter("Host", hostLabelsPair.first);
1384  txParameters.addParameter("SetMode", setMode);
1385  txParameters.addParameter("Labels", hostLabelsPair.second);
1386  txParameters.addParameter("SetValueMSB", setValueMSB);
1387  txParameters.addParameter("SetValueLSB", setValueLSB);
1388 
1389  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1390  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1391  SOAPUtilities::receive(retMsg, rxParameters);
1392  __SUP_COUT__ << "Received TRACE response: "
1393  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1394  << SOAPUtilities::translate(retMsg) << __E__;
1395 
1396  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1397  {
1398  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1399  "hostname = "
1400  << hostLabelsPair.first << "/" << appInfo.getId()
1401  << " name = " << appInfo.getName()
1402  << " class = " << appInfo.getClass()
1403  << " hostname = " << appInfo.getHostname() << __E__;
1404  __SUP_SS_THROW__;
1405  }
1406  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1407  {
1408  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1409  << __E__;
1410  __SUP_SS_THROW__;
1411  }
1412  }
1413  catch(const xdaq::exception::Exception& e)
1414  {
1415  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1416  << appInfo.getId() << " name = " << appInfo.getName()
1417  << ". \n\n"
1418  << e.what() << __E__;
1419  __SUP_SS_THROW__;
1420  }
1421 
1422  modifiedTraceList +=
1423  ";" + hostLabelsPair.first; // insert xdaq context version of name
1424  // FIXME and create mapp from user's typed in xdaq
1425  // context name to TRACE hostname resolution
1426 
1427  modifiedTraceList += rxParameters.getValue("TRACEList");
1428 
1429  } // end host set TRACE loop
1430 
1431  __SUP_COUT__ << "mod'd TRACE List received: \n" << modifiedTraceList << __E__;
1432  xmlOut.addTextElementToData("modTraceList", modifiedTraceList);
1433  } // end SetTraceLevels
1434  else if(requestType == "GetTriggerStatus")
1435  {
1436  __SUP_COUT__ << "requestType " << requestType << __E__;
1437  SOAPParameters txParameters; // params for xoap to send
1438  txParameters.addParameter("Request", "GetTriggerStatus");
1439 
1440  SOAPParameters rxParameters; // params for xoap to recv
1441  rxParameters.addParameter("Command");
1442  rxParameters.addParameter("Error");
1443  rxParameters.addParameter("TRACETriggerStatus");
1444 
1445  std::string traceTriggerStatus = "";
1446  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1447  for(const auto& appInfo : allTraceApps)
1448  {
1449  __SUP_COUT__ << "Supervisor hostname = " << appInfo.first << "/"
1450  << appInfo.second.getId()
1451  << " name = " << appInfo.second.getName()
1452  << " class = " << appInfo.second.getClass()
1453  << " hostname = " << appInfo.second.getHostname() << __E__;
1454  try
1455  {
1456  xoap::MessageReference retMsg =
1457  SOAPMessenger::sendWithSOAPReply(appInfo.second.getDescriptor(),
1458  "TRACESupervisorRequest",
1459  txParameters);
1460  SOAPUtilities::receive(retMsg, rxParameters);
1461  __SUP_COUT__ << "Received TRACE response: "
1462  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1463  << SOAPUtilities::translate(retMsg) << __E__;
1464 
1465  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1466  {
1467  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1468  "hostname = "
1469  << appInfo.first << "/" << appInfo.second.getId()
1470  << " name = " << appInfo.second.getName()
1471  << " class = " << appInfo.second.getClass()
1472  << " hostname = " << appInfo.second.getHostname() << __E__;
1473  __SUP_SS_THROW__;
1474  }
1475  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1476  {
1477  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1478  << __E__;
1479  __SUP_SS_THROW__;
1480  }
1481  }
1482  catch(const xdaq::exception::Exception& e)
1483  {
1484  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1485  << appInfo.second.getId()
1486  << " name = " << appInfo.second.getName() << ". \n\n"
1487  << e.what() << __E__;
1488  __SUP_SS_THROW__;
1489  }
1490 
1491  traceTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1492 
1493  } // end app get TRACE loop
1494  __SUP_COUT__ << "TRACE Trigger Status received: \n"
1495  << traceTriggerStatus << __E__;
1496  xmlOut.addTextElementToData("traceTriggerStatus", traceTriggerStatus);
1497  } // end GetTriggerStatus
1498  else if(requestType == "SetTriggerEnable")
1499  {
1500  __SUP_COUT__ << "requestType " << requestType << __E__;
1501 
1502  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1503 
1504  __SUP_COUTV__(hostList);
1505 
1506  std::vector<std::string /*host*/> hosts;
1507 
1508  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1509 
1510  SOAPParameters rxParameters; // params for xoap to recv
1511  rxParameters.addParameter("Command");
1512  rxParameters.addParameter("Error");
1513  rxParameters.addParameter("TRACETriggerStatus");
1514 
1515  std::string modifiedTriggerStatus = "";
1516  std::string xdaqHostname;
1517  StringMacros::getVectorFromString(hostList, hosts, {';'});
1518  for(auto& host : hosts)
1519  {
1520  // identify artdaq hosts to go through ARTDAQ supervisor
1521  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1522  __SUP_COUTV__(host);
1523  if(host.size() < 3)
1524  continue; // skip bad hostnames
1525 
1526  // use map to convert to xdaq host
1527  try
1528  {
1529  xdaqHostname = traceMapToXDAQHostname_.at(host);
1530  }
1531  catch(...)
1532  {
1533  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1534  << host << "' to xdaq Context hostname." << __E__;
1535  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1536  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1537  << __E__;
1538  __SUP_SS_THROW__;
1539  }
1540 
1541  __SUP_COUTV__(xdaqHostname);
1542 
1543  auto& appInfo = allTraceApps.at(xdaqHostname);
1544  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1545  << appInfo.getId() << " name = " << appInfo.getName()
1546  << " class = " << appInfo.getClass()
1547  << " hostname = " << appInfo.getHostname() << __E__;
1548  try
1549  {
1550  SOAPParameters txParameters; // params for xoap to send
1551  txParameters.addParameter("Request", "SetTriggerEnable");
1552  txParameters.addParameter("Host", host);
1553 
1554  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1555  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1556  SOAPUtilities::receive(retMsg, rxParameters);
1557  __SUP_COUT__ << "Received TRACE response: "
1558  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1559  << SOAPUtilities::translate(retMsg) << __E__;
1560 
1561  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1562  {
1563  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1564  "hostname = "
1565  << host << "/" << appInfo.getId()
1566  << " name = " << appInfo.getName()
1567  << " class = " << appInfo.getClass()
1568  << " hostname = " << appInfo.getHostname() << __E__;
1569  __SUP_SS_THROW__;
1570  }
1571  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1572  {
1573  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1574  << __E__;
1575  __SUP_SS_THROW__;
1576  }
1577  }
1578  catch(const xdaq::exception::Exception& e)
1579  {
1580  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1581  << appInfo.getId() << " name = " << appInfo.getName()
1582  << ". \n\n"
1583  << e.what() << __E__;
1584  __SUP_SS_THROW__;
1585  }
1586 
1587  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1588  } // end host set TRACE loop
1589 
1590  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1591  << modifiedTriggerStatus << __E__;
1592  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1593  } // end SetTriggerEnable
1594  else if(requestType == "ResetTRACE")
1595  {
1596  __SUP_COUT__ << "requestType " << requestType << __E__;
1597 
1598  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1599 
1600  __SUP_COUTV__(hostList);
1601 
1602  std::vector<std::string /*host*/> hosts;
1603 
1604  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1605 
1606  SOAPParameters rxParameters; // params for xoap to recv
1607  rxParameters.addParameter("Command");
1608  rxParameters.addParameter("Error");
1609  rxParameters.addParameter("TRACETriggerStatus");
1610 
1611  std::string modifiedTriggerStatus = "";
1612  std::string xdaqHostname;
1613  StringMacros::getVectorFromString(hostList, hosts, {';'});
1614  for(auto& host : hosts)
1615  {
1616  // identify artdaq hosts to go through ARTDAQ supervisor
1617  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1618  __SUP_COUTV__(host);
1619  if(host.size() < 3)
1620  continue; // skip bad hostnames
1621 
1622  // use map to convert to xdaq host
1623  try
1624  {
1625  xdaqHostname = traceMapToXDAQHostname_.at(host);
1626  }
1627  catch(...)
1628  {
1629  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1630  << host << "' to xdaq Context hostname." << __E__;
1631  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1632  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1633  << __E__;
1634  __SUP_SS_THROW__;
1635  }
1636 
1637  __SUP_COUTV__(xdaqHostname);
1638 
1639  auto& appInfo = allTraceApps.at(xdaqHostname);
1640  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1641  << appInfo.getId() << " name = " << appInfo.getName()
1642  << " class = " << appInfo.getClass()
1643  << " hostname = " << appInfo.getHostname() << __E__;
1644  try
1645  {
1646  SOAPParameters txParameters; // params for xoap to send
1647  txParameters.addParameter("Request", "ResetTRACE");
1648  txParameters.addParameter("Host", host);
1649 
1650  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1651  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1652  SOAPUtilities::receive(retMsg, rxParameters);
1653  __SUP_COUT__ << "Received TRACE response: "
1654  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1655  << SOAPUtilities::translate(retMsg) << __E__;
1656 
1657  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1658  {
1659  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1660  "hostname = "
1661  << host << "/" << appInfo.getId()
1662  << " name = " << appInfo.getName()
1663  << " class = " << appInfo.getClass()
1664  << " hostname = " << appInfo.getHostname() << __E__;
1665  __SUP_SS_THROW__;
1666  }
1667  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1668  {
1669  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1670  << __E__;
1671  __SUP_SS_THROW__;
1672  }
1673  }
1674  catch(const xdaq::exception::Exception& e)
1675  {
1676  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1677  << appInfo.getId() << " name = " << appInfo.getName()
1678  << ". \n\n"
1679  << e.what() << __E__;
1680  __SUP_SS_THROW__;
1681  }
1682 
1683  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1684  } // end host set TRACE loop
1685 
1686  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1687  << modifiedTriggerStatus << __E__;
1688  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1689  } // end ResetTRACE
1690  else if(requestType == "EnableTRACE")
1691  {
1692  __SUP_COUT__ << "requestType " << requestType << __E__;
1693 
1694  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1695  std::string enable = CgiDataUtilities::postData(cgiIn, "enable");
1696 
1697  __SUP_COUTV__(hostList);
1698  __SUP_COUTV__(enable);
1699 
1700  std::vector<std::string /*host*/> hosts;
1701 
1702  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1703 
1704  SOAPParameters rxParameters; // params for xoap to recv
1705  rxParameters.addParameter("Command");
1706  rxParameters.addParameter("Error");
1707  rxParameters.addParameter("TRACETriggerStatus");
1708 
1709  std::string modifiedTriggerStatus = "";
1710  std::string xdaqHostname;
1711  StringMacros::getVectorFromString(hostList, hosts, {';'});
1712  for(auto& host : hosts)
1713  {
1714  // identify artdaq hosts to go through ARTDAQ supervisor
1715  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1716  __SUP_COUTV__(host);
1717  if(host.size() < 3)
1718  continue; // skip bad hostnames
1719 
1720  // use map to convert to xdaq host
1721  try
1722  {
1723  xdaqHostname = traceMapToXDAQHostname_.at(host);
1724  }
1725  catch(...)
1726  {
1727  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1728  << host << "' to xdaq Context hostname." << __E__;
1729  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1730  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1731  << __E__;
1732  __SUP_SS_THROW__;
1733  }
1734 
1735  __SUP_COUTV__(xdaqHostname);
1736 
1737  auto& appInfo = allTraceApps.at(xdaqHostname);
1738  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1739  << appInfo.getId() << " name = " << appInfo.getName()
1740  << " class = " << appInfo.getClass()
1741  << " hostname = " << appInfo.getHostname() << __E__;
1742  try
1743  {
1744  SOAPParameters txParameters; // params for xoap to send
1745  txParameters.addParameter("Request", "EnableTRACE");
1746  txParameters.addParameter("Host", host);
1747  txParameters.addParameter("SetEnable", enable);
1748 
1749  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1750  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1751  SOAPUtilities::receive(retMsg, rxParameters);
1752  __SUP_COUT__ << "Received TRACE response: "
1753  << SOAPUtilities::translate(retMsg).getCommand() << " ==> "
1754  << SOAPUtilities::translate(retMsg) << __E__;
1755 
1756  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1757  {
1758  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1759  "hostname = "
1760  << host << "/" << appInfo.getId()
1761  << " name = " << appInfo.getName()
1762  << " class = " << appInfo.getClass()
1763  << " hostname = " << appInfo.getHostname() << __E__;
1764  __SUP_SS_THROW__;
1765  }
1766  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1767  {
1768  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1769  << __E__;
1770  __SUP_SS_THROW__;
1771  }
1772  }
1773  catch(const xdaq::exception::Exception& e)
1774  {
1775  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1776  << appInfo.getId() << " name = " << appInfo.getName()
1777  << ". \n\n"
1778  << e.what() << __E__;
1779  __SUP_SS_THROW__;
1780  }
1781 
1782  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1783  } // end host set TRACE loop
1784 
1785  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1786  << modifiedTriggerStatus << __E__;
1787  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1788  } // end EnableTRACE
1789  else if(requestType == "GetTraceSnapshot")
1790  {
1791  __SUP_COUT__ << "requestType " << requestType << __E__;
1792 
1793  std::string hostList = CgiDataUtilities::postData(cgiIn, "hostList");
1794  std::string filterFor = CgiDataUtilities::postData(cgiIn, "filterFor");
1795  std::string filterOut = CgiDataUtilities::postData(cgiIn, "filterOut");
1796 
1797  __SUP_COUTV__(hostList);
1798  __SUP_COUTV__(filterFor);
1799  __SUP_COUTV__(filterOut);
1800 
1801  std::vector<std::string /*host*/> hosts;
1802 
1803  auto& allTraceApps = allSupervisorInfo_.getAllTraceControllerSupervisorInfo();
1804 
1805  SOAPParameters rxParameters; // params for xoap to recv
1806  rxParameters.addParameter("Command");
1807  rxParameters.addParameter("Error");
1808  rxParameters.addParameter("TRACETriggerStatus");
1809  rxParameters.addParameter("TRACESnapshot");
1810 
1811  std::string modifiedTriggerStatus = "";
1812  std::string xdaqHostname;
1813  StringMacros::getVectorFromString(hostList, hosts, {';'});
1814  for(auto& host : hosts)
1815  {
1816  // identify artdaq hosts to go through ARTDAQ supervisor
1817  // by adding "artdaq.." to hostname artdaq..correlator2.fnal.gov
1818  __SUP_COUTV__(host);
1819  if(host.size() < 3)
1820  continue; // skip bad hostnames
1821 
1822  // use map to convert to xdaq host
1823  try
1824  {
1825  xdaqHostname = traceMapToXDAQHostname_.at(host);
1826  }
1827  catch(...)
1828  {
1829  __SUP_SS__ << "Could not find the translation from TRACE hostname '"
1830  << host << "' to xdaq Context hostname." << __E__;
1831  ss << "Here is the existing map (size=" << traceMapToXDAQHostname_.size()
1832  << "): " << StringMacros::mapToString(traceMapToXDAQHostname_)
1833  << __E__;
1834  __SUP_SS_THROW__;
1835  }
1836 
1837  __SUP_COUTV__(xdaqHostname);
1838 
1839  auto& appInfo = allTraceApps.at(xdaqHostname);
1840  __SUP_COUT__ << "Supervisor hostname = " << host << "/" << xdaqHostname << ":"
1841  << appInfo.getId() << " name = " << appInfo.getName()
1842  << " class = " << appInfo.getClass()
1843  << " hostname = " << appInfo.getHostname() << __E__;
1844  try
1845  {
1846  SOAPParameters txParameters; // params for xoap to send
1847  txParameters.addParameter("Request", "GetSnapshot");
1848  txParameters.addParameter("Host", host);
1849  txParameters.addParameter("FilterForCSV", filterFor);
1850  txParameters.addParameter("FilterOutCSV", filterOut);
1851 
1852  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1853  appInfo.getDescriptor(), "TRACESupervisorRequest", txParameters);
1854  SOAPUtilities::receive(retMsg, rxParameters);
1855  __SUP_COUT__ << "Received TRACE response: "
1856  << SOAPUtilities::translate(retMsg).getCommand() << __E__;
1857  //<< " ==> Bytes " << SOAPUtilities::translate(retMsg) << __E__;
1858 
1859  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1860  {
1861  __SUP_SS__ << "Unrecognized command at destination TRACE Supervisor "
1862  "hostname = "
1863  << host << "/" << appInfo.getId()
1864  << " name = " << appInfo.getName()
1865  << " class = " << appInfo.getClass()
1866  << " hostname = " << appInfo.getHostname() << __E__;
1867  __SUP_SS_THROW__;
1868  }
1869  else if(SOAPUtilities::translate(retMsg).getCommand() == "TRACEFault")
1870  {
1871  __SUP_SS__ << "Error received: " << rxParameters.getValue("Error")
1872  << __E__;
1873  __SUP_SS_THROW__;
1874  }
1875  }
1876  catch(const xdaq::exception::Exception& e)
1877  {
1878  __SUP_SS__ << "Error transmitting request to TRACE Supervisor LID = "
1879  << appInfo.getId() << " name = " << appInfo.getName()
1880  << ". \n\n"
1881  << e.what() << __E__;
1882  __SUP_SS_THROW__;
1883  }
1884 
1885  modifiedTriggerStatus += rxParameters.getValue("TRACETriggerStatus");
1886  xmlOut.addTextElementToData("host", host);
1887  std::string snapshot = rxParameters.getValue("TRACESnapshot");
1888  // if(snapshot.size() > 100000)
1889  // {
1890  // __SUP_COUT__ << "Truncating snapshot" << __E__;
1891  // snapshot.resize(100000);
1892  // }
1893  // xmlOut.addTextElementToData("hostSnapshot", snapshot);
1894 
1895  {
1896  std::string filename =
1897  USER_CONSOLE_SNAPSHOT_PATH + "snapshot_" + host + ".txt";
1898  __SUP_COUTV__(filename);
1899  FILE* fp = fopen(filename.c_str(), "w");
1900  if(!fp)
1901  {
1902  __SUP_SS__ << "Failed to create snapshot file: " << filename << __E__;
1903  __SUP_SS_THROW__;
1904  }
1905  fprintf(fp,
1906  "TRACE Snapshot taken at %s\n",
1908 
1909  if(snapshot.size() > 5 && snapshot[2] != 'i')
1910  {
1911  // add header lines
1912  fprintf(
1913  fp,
1914  " idx us_tod delta pid tid cpu "
1915  " trcname lvl r msg \n");
1916  fprintf(fp,
1917  "----- ---------------- ----------- ------ ------ --- "
1918  "-------------------------------------- --- - "
1919  "--------------------------\n");
1920  }
1921  fprintf(fp, "%s", snapshot.c_str());
1922  fclose(fp);
1923  }
1924  } // end host set TRACE loop
1925 
1926  __SUP_COUT__ << "mod'd TRACE Trigger Status received: \n"
1927  << modifiedTriggerStatus << __E__;
1928  xmlOut.addTextElementToData("modTriggerStatus", modifiedTriggerStatus);
1929  } // end getTraceSnapshot
1930  else if(requestType == "GetCustomCountsAndActions" ||
1931  requestType == "AddCustomCountsAndAction" ||
1932  requestType == "ModifyCustomCountsAndAction")
1933  {
1934  __SUP_COUT__ << "requestType " << requestType
1935  << " size=" << priorityCustomTriggerList_.size() << __E__;
1936 
1937  //mutex scope:
1938  // lockout the messages array for the remainder of the scope
1939  // this guarantees can safely access the action queue
1940  std::lock_guard<std::mutex> lock(messageMutex_);
1941 
1942  if(requestType == "AddCustomCountsAndAction" ||
1943  requestType == "ModifyCustomCountsAndAction")
1944  {
1945  std::string needle = StringMacros::decodeURIComponent(
1946  CgiDataUtilities::postData(cgiIn, "needle"));
1947  uint32_t priority = CgiDataUtilities::postDataAsInt(cgiIn, "priority");
1948  std::string action = StringMacros::decodeURIComponent(
1949  CgiDataUtilities::postData(cgiIn, "action"));
1950  uint32_t triggerOnCount =
1951  CgiDataUtilities::postDataAsInt(cgiIn, "triggerOnCount");
1952  bool doLoop =
1953  CgiDataUtilities::postDataAsInt(cgiIn, "doLoop") > 0 ? true : false;
1954  bool isArmed =
1955  CgiDataUtilities::postDataAsInt(cgiIn, "isArmed") > 0 ? true : false;
1956 
1957  __SUP_COUTV__(needle);
1958  __SUP_COUTV__(priority);
1959  __SUP_COUTV__(action);
1960  __SUP_COUTV__(triggerOnCount);
1961  __SUP_COUTV__(doLoop);
1962  __SUP_COUTV__(isArmed);
1963 
1964  if(requestType == "ModifyCustomCountsAndAction")
1965  {
1966  std::string buttonDo = StringMacros::decodeURIComponent(
1967  CgiDataUtilities::postData(cgiIn, "buttonDo"));
1968  std::string currentNeedle =
1969  CgiDataUtilities::postData(cgiIn, "currentNeedle");
1970 
1971  //treat needle as CSV list and do in reverse order to maintain priority of group
1972  std::vector<std::string> csvNeedles =
1973  StringMacros::getVectorFromString(currentNeedle, {','});
1974  for(size_t i = csvNeedles.size() - 1; i < csvNeedles.size(); --i)
1975  {
1976  if(csvNeedles[i].size() == 0)
1977  continue; //skip empty entries
1978  //change the priority to the last placed priority entry to keep group order
1979  priority = modifyCustomTriggeredAction(
1980  StringMacros::decodeURIComponent(csvNeedles[i]),
1981  buttonDo,
1982  needle,
1983  action,
1984  priority,
1985  triggerOnCount,
1986  doLoop,
1987  isArmed);
1988  } //end csv needle list handling
1989  }
1990  else
1991  addCustomTriggeredAction(
1992  needle, action, priority, triggerOnCount, doLoop, isArmed);
1993 
1994  saveCustomCountList();
1995  } // end AddCustomCountsAndAction
1996 
1997  //always calculate untriggered count
1998  size_t untriggeredCount =
1999  messageCount_; //copy and then decrement "unique" incrementing ID for messages
2000 
2001  for(const auto& customCount : priorityCustomTriggerList_)
2002  {
2003  xercesc::DOMElement* customCountParent =
2004  xmlOut.addTextElementToData("customCount", "");
2005 
2006  if(customCount.occurrences < untriggeredCount)
2007  untriggeredCount -= customCount.occurrences;
2008  else
2009  {
2010  __SUP_SS__ << "Impossible custom count; notify admins! "
2011  << customCount.occurrences << " > " << untriggeredCount
2012  << " for "
2013  << StringMacros::vectorToString(customCount.needleSubstrings,
2014  {'*'})
2015  << __E__;
2016  __SUP_SS_THROW__;
2017  }
2018  xmlOut.addTextElementToParent(
2019  "needle",
2020  StringMacros::vectorToString(customCount.needleSubstrings, {'*'}),
2021  customCountParent);
2022  xmlOut.addTextElementToParent(
2023  "count", std::to_string(customCount.occurrences), customCountParent);
2024  xmlOut.addTextElementToParent(
2025  "action", customCount.action, customCountParent);
2026  xmlOut.addTextElementToParent("triggerOnCount",
2027  std::to_string(customCount.triggerOnCount),
2028  customCountParent);
2029  xmlOut.addTextElementToParent(
2030  "doLoop", std::to_string(customCount.doLoop), customCountParent);
2031  xmlOut.addTextElementToParent(
2032  "isArmed", std::to_string(customCount.isArmed), customCountParent);
2033  } //end adding custom counts to response xml loop
2034 
2035  //add untriggered always last
2036  xercesc::DOMElement* customCountParent =
2037  xmlOut.addTextElementToData("customCount", "");
2038  xmlOut.addTextElementToParent("needle", "< Untriggered >", customCountParent);
2039  xmlOut.addTextElementToParent(
2040  "count", std::to_string(untriggeredCount), customCountParent);
2041  xmlOut.addTextElementToParent("action", "Count Only", customCountParent);
2042  xmlOut.addTextElementToParent("triggerOnCount", "1", customCountParent);
2043  xmlOut.addTextElementToParent("doLoop", "1", customCountParent);
2044 
2045  } // end GetCustomCountsAndActions or AddCustomCountsAndAction
2046  else
2047  {
2048  __SUP_SS__ << "requestType Request, " << requestType
2049  << ", not recognized by the Console Supervisor (was it intended for "
2050  "another Supervisor?)."
2051  << __E__;
2052  __SUP_SS_THROW__;
2053  }
2054 } // end request()
2055 
2056 //==============================================================================
2060 {
2061  //Console Supervisor status detatil format is:
2062  // uptime, Err count, Warn count, Last Error msg, Last Warn msg
2063 
2064  //return uptime detail
2065  std::stringstream ss;
2066  ss << "Uptime: "
2067  << StringMacros::encodeURIComponent(StringMacros::getTimeDurationString(
2068  CorePropertySupervisorBase::getSupervisorUptime()));
2069 
2070  //return Err count, Warn count, Last Error msg, Last Warn msg, Last Info msg, Info count
2071 
2072  // size_t errorCount_ = 0, warnCount_ = 0;
2073  // std::string lastErrorMessage_, lastWarnMessage_;
2074  // time_t lastErrorMessageTime_ = 0, lastWarnMessageTime_ = 0;
2075 
2076  ss << ", Errors: " << errorCount_;
2077  ss << ", Warns: " << warnCount_;
2078  ss << ", Last Error ("
2079  << (lastErrorMessageTime_ ? StringMacros::getTimestampString(lastErrorMessageTime_)
2080  : "0")
2081  << "): "
2082  << (lastErrorMessageTime_ ? StringMacros::encodeURIComponent(lastErrorMessage_)
2083  : "");
2084  ss << ", Last Warn ("
2085  << (lastWarnMessageTime_ ? StringMacros::getTimestampString(lastWarnMessageTime_)
2086  : "0")
2087  << "): "
2088  << (lastWarnMessageTime_ ? StringMacros::encodeURIComponent(lastWarnMessage_)
2089  : "");
2090  ss << ", Last Info ("
2091  << (lastInfoMessageTime_ ? StringMacros::getTimestampString(lastInfoMessageTime_)
2092  : "0")
2093  << "): "
2094  << (lastInfoMessageTime_ ? StringMacros::encodeURIComponent(lastInfoMessage_)
2095  : "");
2096  ss << ", Info #: " << infoCount_;
2097  ss << ", First Error ("
2098  << (firstErrorMessageTime_
2099  ? StringMacros::getTimestampString(firstErrorMessageTime_)
2100  : "0")
2101  << "): "
2102  << (firstErrorMessageTime_ ? StringMacros::encodeURIComponent(firstErrorMessage_)
2103  : "");
2104  ss << ", First Warn ("
2105  << (firstWarnMessageTime_ ? StringMacros::getTimestampString(firstWarnMessageTime_)
2106  : "0")
2107  << "): "
2108  << (firstWarnMessageTime_ ? StringMacros::encodeURIComponent(firstWarnMessage_)
2109  : "");
2110  ss << ", First Info ("
2111  << (firstInfoMessageTime_ ? StringMacros::getTimestampString(firstInfoMessageTime_)
2112  : "0")
2113  << "): "
2114  << (firstInfoMessageTime_ ? StringMacros::encodeURIComponent(firstInfoMessage_)
2115  : "");
2116 
2117  return ss.str();
2118 } // end getStatusProgressDetail()
2119 
2120 //==============================================================================
2143 void ConsoleSupervisor::insertMessageRefresh(HttpXmlDocument* xmlOut,
2144  const size_t lastUpdateCount)
2145 {
2146  //__SUP_COUT__ << __E__;
2147 
2148  if(messages_.size() == 0)
2149  return;
2150 
2151  // validate lastUpdateCount
2152  if(lastUpdateCount > messages_.back().getCount() && lastUpdateCount != (size_t)-1)
2153  {
2154  __SS__ << "Invalid lastUpdateCount: " << lastUpdateCount
2155  << " messagesArray size = " << messages_.back().getCount() << __E__;
2156  __SS_THROW__;
2157  }
2158 
2159  // lockout the messages array for the remainder of the scope
2160  // this guarantees the reading thread can safely access the messages
2161  std::lock_guard<std::mutex> lock(messageMutex_);
2162 
2163  xmlOut->addTextElementToData("last_update_count",
2164  std::to_string(messages_.back().getCount()));
2165 
2166  refreshParent_ = xmlOut->addTextElementToData("messages", "");
2167 
2168  bool requestOutOfSync = false;
2169  std::string requestOutOfSyncMsg;
2170 
2171  size_t refreshReadPointer = 0;
2172  if(lastUpdateCount != (size_t)-1)
2173  {
2174  while(refreshReadPointer < messages_.size() &&
2175  messages_[refreshReadPointer].getCount() <= lastUpdateCount)
2176  {
2177  ++refreshReadPointer;
2178  }
2179  }
2180 
2181  if(refreshReadPointer >= messages_.size())
2182  return;
2183 
2184  // limit number of catch-up messages
2185  if(messages_.size() - refreshReadPointer > maxClientMessageRequest_)
2186  {
2187  // __SUP_COUT__ << "Only sending latest " << maxClientMessageRequest_ << "
2188  // messages!";
2189 
2190  // auto oldrrp = refreshReadPointer;
2191  refreshReadPointer = messages_.size() - maxClientMessageRequest_;
2192 
2193  // __SS__ << "Skipping " << (refreshReadPointer - oldrrp)
2194  // << " messages because the web console has fallen behind!" << __E__;
2195  // __COUT__ << ss.str();
2196  // ConsoleMessageStruct msg(CONSOLE_SPECIAL_WARNING + ss.str(), lastUpdateCount);
2197  // auto it = messages_.begin();
2198  // std::advance(it, refreshReadPointer + 1);
2199  // messages_.insert(it, msg);
2200  }
2201 
2202  //return first_update_count, so that older messages could be retrieved later if desired by user
2203  xmlOut->addTextElementToData(
2204  "earliest_update_count",
2205  std::to_string(messages_[refreshReadPointer].getCount()));
2206 
2207  std::string messagesJson = "[";
2208 
2209  // output oldest to new
2210  for(; refreshReadPointer < messages_.size(); ++refreshReadPointer)
2211  {
2212  auto msg = messages_[refreshReadPointer];
2213  if(msg.getCount() < lastUpdateCount)
2214  {
2215  if(!requestOutOfSync) // record out of sync message once only
2216  {
2217  requestOutOfSync = true;
2218  __SS__ << "Request is out of sync! Message count should be more recent! "
2219  << msg.getCount() << " < " << lastUpdateCount << __E__;
2220  requestOutOfSyncMsg = ss.str();
2221  }
2222  // assume these messages are new (due to a system restart)
2223  // continue;
2224  }
2225 
2226  // addMessageToResponse(xmlOut, msg);
2227  addMessageToResponse(messagesJson, msg);
2228  __COUTTV__(messagesJson.size());
2229 
2230  } //end main message add loop
2231 
2232  messagesJson += "]";
2233 
2234  xmlOut->addTextElementToParent("message_json", messagesJson, refreshParent_);
2235 
2236  if(requestOutOfSync) // if request was out of sync, show message
2237  __SUP_COUT__ << requestOutOfSyncMsg;
2238 } // end insertMessageRefresh()
2239 
2240 //==============================================================================
2261 void ConsoleSupervisor::prependHistoricMessages(HttpXmlDocument* xmlOut,
2262  const size_t earliestOnhandMessageCount)
2263 {
2264  //__SUP_COUT__ << __E__;
2265 
2266  if(messages_.size() == 0)
2267  return;
2268 
2269  // validate earliestOnhandMessageCount
2270  if(earliestOnhandMessageCount >= messages_.back().getCount())
2271  {
2272  __SS__
2273  << "Invalid claim from user request of earliest onhand message sequence ID = "
2274  << earliestOnhandMessageCount
2275  << ". Latest existing sequence ID = " << messages_.back().getCount()
2276  << ". Was the Console Supervisor restarted?" << __E__;
2277  __SS_THROW__;
2278  }
2279 
2280  // lockout the messages array for the remainder of the scope
2281  // this guarantees the reading thread can safely access the messages
2282  std::lock_guard<std::mutex> lock(messageMutex_);
2283 
2284  refreshParent_ = xmlOut->addTextElementToData("messages", "");
2285 
2286  size_t refreshReadPointer = 0;
2287  size_t readCountStart = earliestOnhandMessageCount - maxClientMessageRequest_;
2288  if(readCountStart >= messages_.back().getCount()) //then wrapped around, so set to 0
2289  readCountStart = 0;
2290 
2291  //find starting read pointer
2292  while(refreshReadPointer < messages_.size() &&
2293  messages_[refreshReadPointer].getCount() < readCountStart)
2294  {
2295  ++refreshReadPointer;
2296  }
2297 
2298  if(refreshReadPointer >= messages_.size())
2299  return;
2300 
2301  xmlOut->addTextElementToData("earliest_update_count", //return new early onhand count
2302  std::to_string(readCountStart));
2303 
2304  std::string messagesJson = "[";
2305 
2306  //messages returned will be from readCountStart to earliestOnhandMessageCount-1
2307  // output oldest to new
2308  for(; refreshReadPointer < messages_.size(); ++refreshReadPointer)
2309  {
2310  auto msg = messages_[refreshReadPointer];
2311  if(messages_[refreshReadPointer].getCount() >= earliestOnhandMessageCount)
2312  break; //found last message
2313 
2314  addMessageToResponse(messagesJson, msg);
2315 
2316  } //end main message add loop
2317 
2318  messagesJson += "]";
2319 
2320  xmlOut->addTextElementToParent("message_json", messagesJson, refreshParent_);
2321 
2322 } // end prependHistoricMessages()
2323 
2324 //==============================================================================
2325 void ConsoleSupervisor::addMessageToResponse(HttpXmlDocument* xmlOut,
2327 {
2328  // for all fields, give value
2329  for(auto& field : msg.fields)
2330  {
2331  if(field.first == ConsoleMessageStruct::FieldType::SOURCEID)
2332  continue; // skip, not useful
2333  if(field.first == ConsoleMessageStruct::FieldType::SEQID)
2334  continue; // skip, not useful
2335  if(field.first == ConsoleMessageStruct::FieldType::TIMESTAMP) //use Time instead
2336  continue; // skip, not useful
2337  if(field.first ==
2338  ConsoleMessageStruct::FieldType::LEVEL) //use modified getLevel instead
2339  continue; // skip, not useful
2340 
2341  xmlOut->addTextElementToParent(
2342  "message_" + ConsoleMessageStruct::fieldNames.at(field.first),
2343  field.second,
2344  refreshParent_);
2345  } //end msg field loop
2346 
2347  // give modified level also
2348  xmlOut->addTextElementToParent("message_Level", msg.getLevel(), refreshParent_);
2349 
2350  // give timestamp also
2351  xmlOut->addTextElementToParent("message_Time", msg.getTime(), refreshParent_);
2352 
2353  // give global count index also
2354  xmlOut->addTextElementToParent(
2355  "message_Count", std::to_string(msg.getCount()), refreshParent_);
2356 
2357  //give Custom count label also (i.e., which search string this message matches, or blank "" for no match)
2358  xmlOut->addTextElementToParent(
2359  "message_Custom",
2360  StringMacros::vectorToString(msg.getCustomTriggerMatch().needleSubstrings, {'*'}),
2361  refreshParent_);
2362 
2363 } // end addMessageToResponse()
2364 
2365 //==============================================================================
2367 void ConsoleSupervisor::addMessageToResponse(std::string& xmlValue,
2369 {
2370  if(xmlValue.size() &&
2371  xmlValue[xmlValue.size() - 1] != '[') //if not first entry, add comma
2372  xmlValue += ",";
2373 
2374  xmlValue += "{";
2375  // for all fields, give value
2376  for(auto& field : msg.fields)
2377  {
2378  if(field.first == ConsoleMessageStruct::FieldType::SOURCEID)
2379  continue; // skip, not useful
2380  if(field.first == ConsoleMessageStruct::FieldType::SEQID)
2381  continue; // skip, not useful
2382  if(field.first == ConsoleMessageStruct::FieldType::TIMESTAMP) //use Time instead
2383  continue; // skip, not useful
2384  if(field.first ==
2385  ConsoleMessageStruct::FieldType::LEVEL) //use modified getLevel instead
2386  continue; // skip, not useful
2387 
2388  if(field.first == ConsoleMessageStruct::FieldType::MSG ||
2389  field.first == ConsoleMessageStruct::FieldType::SOURCE)
2390  xmlValue += "\"" + ConsoleMessageStruct::fieldNames.at(field.first) +
2391  "\":\"" + StringMacros::escapeString(field.second) + "\",";
2392  else
2393  xmlValue += "\"" + ConsoleMessageStruct::fieldNames.at(field.first) +
2394  "\":\"" + field.second + "\",";
2395  } //end msg field loop
2396 
2397  // give modified level also
2398  xmlValue += "\"Level\":\"" + msg.getLevel() + "\",";
2399  // xmlOut->addTextElementToParent("message_Level", msg.getLevel(), refreshParent_);
2400 
2401  // give timestamp also
2402  xmlValue += "\"Time\":\"" + msg.getTime() + "\",";
2403  // xmlOut->addTextElementToParent("message_Time", msg.getTime(), refreshParent_);
2404 
2405  // give global count index also
2406  xmlValue += "\"Count\":\"" + std::to_string(msg.getCount()) + "\",";
2407  //xmlOut->addTextElementToParent("message_Count", std::to_string(msg.getCount()), refreshParent_);
2408 
2409  //give Custom count label also (i.e., which search string this message matches, or blank "" for no match)
2410  xmlValue += "\"Custom\":\"" +
2412  msg.getCustomTriggerMatch().needleSubstrings, {'*'})) +
2413  "\"";
2414  // xmlOut->addTextElementToParent(
2415  // "message_Custom",
2416  // StringMacros::vectorToString(msg.getCustomTriggerMatch().needleSubstrings, {'*'}),
2417  // refreshParent_);
2418 
2419  xmlValue += "}";
2420 } // 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)