otsdaq  v2_04_02
GatewaySupervisor.cc
1 #include "otsdaq/GatewaySupervisor/GatewaySupervisor.h"
2 #include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
3 #include "otsdaq/Macros/CoutMacros.h"
4 #include "otsdaq/MessageFacility/MessageFacility.h"
5 #include "otsdaq/SOAPUtilities/SOAPCommand.h"
6 #include "otsdaq/SOAPUtilities/SOAPUtilities.h"
7 #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
8 
9 #include "otsdaq/ConfigurationInterface/ConfigurationManager.h"
10 #include "otsdaq/ConfigurationInterface/ConfigurationManagerRW.h"
11 #include "otsdaq/TablePlugins/DesktopIconTable.h"
12 #include "otsdaq/TablePlugins/XDAQContextTable.h"
13 #include "otsdaq/WorkLoopManager/WorkLoopManager.h"
14 
15 #include "otsdaq/NetworkUtilities/TransceiverSocket.h" // for UDP state changer
16 
17 #include <cgicc/HTMLClasses.h>
18 #include <cgicc/HTMLDoctype.h>
19 #include <cgicc/HTTPCookie.h>
20 #include <cgicc/HTTPHeader.h>
21 #include <xgi/Utils.h>
22 
23 #include <toolbox/fsm/FailedEvent.h>
24 #include <toolbox/task/WorkLoopFactory.h>
25 #include <xdaq/NamespaceURI.h>
26 #include <xoap/Method.h>
27 
28 #include <sys/stat.h> // for mkdir
29 #include <chrono> // std::chrono::seconds
30 #include <fstream>
31 #include <thread> // std::this_thread::sleep_for
32 
33 using namespace ots;
34 
35 #define RUN_NUMBER_PATH std::string(__ENV__("SERVICE_DATA_PATH")) + "/RunNumber/"
36 #define RUN_NUMBER_FILE_NAME "NextRunNumber.txt"
37 #define FSM_LAST_GROUP_ALIAS_PATH \
38  std::string(__ENV__("SERVICE_DATA_PATH")) + "/RunControlData/"
39 #define FSM_LAST_GROUP_ALIAS_FILE_START std::string("FSMLastGroupAlias-")
40 #define FSM_USERS_PREFERENCES_FILETYPE "pref"
41 
42 #undef __MF_SUBJECT__
43 #define __MF_SUBJECT__ "GatewaySupervisor"
44 
45 XDAQ_INSTANTIATOR_IMPL(GatewaySupervisor)
46 
47 //========================================================================================================================
48 GatewaySupervisor::GatewaySupervisor(xdaq::ApplicationStub* s)
49  : xdaq::Application(s)
50  , SOAPMessenger(this)
53  , stateMachineWorkLoopManager_(toolbox::task::bind(
54  this, &GatewaySupervisor::stateMachineThread, "StateMachine"))
55  , stateMachineSemaphore_(toolbox::BSem::FULL)
56  , infoRequestWorkLoopManager_(
57  toolbox::task::bind(this, &GatewaySupervisor::infoRequestThread, "InfoRequest"))
58  , infoRequestSemaphore_(toolbox::BSem::FULL)
59  , activeStateMachineName_("")
60  , theIterator_(this)
61  , broadcastCommandMessageIndex_(0)
62  , broadcastIterationBreakpoint_(
63  -1) // for standard transitions, ignore the breakpoint
64  , counterTest_(0)
65 {
66  INIT_MF("GatewaySupervisor");
67  __COUT__ << __E__;
68 
69  // attempt to make directory structure (just in case)
70  mkdir((std::string(__ENV__("SERVICE_DATA_PATH"))).c_str(), 0755);
71  mkdir((FSM_LAST_GROUP_ALIAS_PATH).c_str(), 0755);
72  mkdir((RUN_NUMBER_PATH).c_str(), 0755);
73 
74  securityType_ = theWebUsers_.getSecurity();
75 
76  __COUT__ << "Security: " << securityType_ << __E__;
77 
78  xgi::bind(this, &GatewaySupervisor::Default, "Default");
79  xgi::bind(this, &GatewaySupervisor::loginRequest, "LoginRequest");
80  xgi::bind(this, &GatewaySupervisor::request, "Request");
81  xgi::bind(this, &GatewaySupervisor::stateMachineXgiHandler, "StateMachineXgiHandler");
82  xgi::bind(this,
83  &GatewaySupervisor::stateMachineIterationBreakpoint,
84  "StateMachineIterationBreakpoint");
85 
86  xgi::bind(this, &GatewaySupervisor::statusRequest, "StatusRequest");
87  xgi::bind(this, &GatewaySupervisor::tooltipRequest, "TooltipRequest");
88 
89  xoap::bind(this,
90  &GatewaySupervisor::supervisorCookieCheck,
91  "SupervisorCookieCheck",
92  XDAQ_NS_URI);
93  xoap::bind(this,
94  &GatewaySupervisor::supervisorGetActiveUsers,
95  "SupervisorGetActiveUsers",
96  XDAQ_NS_URI);
97  xoap::bind(this,
98  &GatewaySupervisor::supervisorSystemMessage,
99  "SupervisorSystemMessage",
100  XDAQ_NS_URI);
101  xoap::bind(this,
102  &GatewaySupervisor::supervisorSystemLogbookEntry,
103  "SupervisorSystemLogbookEntry",
104  XDAQ_NS_URI);
105  xoap::bind(this,
106  &GatewaySupervisor::supervisorLastConfigGroupRequest,
107  "SupervisorLastConfigGroupRequest",
108  XDAQ_NS_URI);
109 
110  init();
111 
112  // exit(1); //keep for valid syntax to exit ots
113 
114 } // end constructor
115 
116 //========================================================================================================================
117 // TODO: Lore needs to detect program quit through killall or ctrl+c so that Logbook
118 // entry is made when ots is halted
119 GatewaySupervisor::~GatewaySupervisor(void)
120 {
121  delete CorePropertySupervisorBase::theConfigurationManager_;
122  makeSystemLogbookEntry("ots halted.");
123 } // end destructor
124 
125 //========================================================================================================================
126 void GatewaySupervisor::indicateOtsAlive(const CorePropertySupervisorBase* properties)
127 {
128  CorePropertySupervisorBase::indicateOtsAlive(properties);
129 }
130 
131 //========================================================================================================================
132 void GatewaySupervisor::init(void)
133 {
134  supervisorGuiHasBeenLoaded_ = false;
135 
136  // const XDAQContextTable* contextTable =
137  // CorePropertySupervisorBase::theConfigurationManager_->__GET_CONFIG__(XDAQContextTable);
138  //
139  // CorePropertySupervisorBase::supervisorContextUID_ =
140  // contextTable->getContextUID(
141  // getApplicationContext()->getContextDescriptor()->getURL()
142  // );
143  // __COUT__ << "Context UID:" << supervisorContextUID_ << __E__;
144  //
145  // CorePropertySupervisorBase::supervisorApplicationUID_ =
146  // contextTable->getApplicationUID(
147  // getApplicationContext()->getContextDescriptor()->getURL(),
148  // getApplicationDescriptor()->getLocalId()
149  // );
150  //
151  // __COUT__ << "Application UID:" << supervisorApplicationUID_ << __E__;
152  //
153  // ConfigurationTree configLinkNode =
154  // CorePropertySupervisorBase::getSupervisorTreeNode();
155  //
156  // std::string supervisorUID;
157  // if (!configLinkNode.isDisconnected())
158  // supervisorUID = configLinkNode.getValue();
159  // else
160  // supervisorUID = TableViewColumnInfo::DATATYPE_LINK_DEFAULT;
161  //
162  //__COUT__ << "GatewaySupervisor UID:" << supervisorUID << __E__;
163 
164  // setting up thread for UDP thread to drive state machine
165  {
166  bool enableStateChanges = false;
167  try
168  {
169  enableStateChanges = CorePropertySupervisorBase::getSupervisorTableNode()
170  .getNode("EnableStateChangesOverUDP")
171  .getValue<bool>();
172  }
173  catch(...)
174  {
175  ;
176  } // ignore errors
177 
178  if(enableStateChanges)
179  {
180  __COUT__ << "Enabling state changes over UDP..." << __E__;
181  // start state changer UDP listener thread
182  std::thread(
183  [](GatewaySupervisor* s) { GatewaySupervisor::StateChangerWorkLoop(s); },
184  this)
185  .detach();
186  }
187  else
188  __COUT__ << "State changes over UDP are disabled." << __E__;
189  } // end setting up thread for UDP drive of state machine
190 
191  // setting up checking of App Status
192  {
193  bool checkAppStatus = false;
194  try
195  {
196  checkAppStatus = CorePropertySupervisorBase::getSupervisorTableNode()
197  .getNode("EnableApplicationStatusMonitoring")
198  .getValue<bool>();
199  }
200  catch(...)
201  {
202  ;
203  } // ignore errors
204 
205  if(checkAppStatus)
206  {
207  __COUT__ << "Enabling App Status checking..." << __E__;
208  //
209  std::thread(
210  [](GatewaySupervisor* s) { GatewaySupervisor::AppStatusWorkLoop(s); },
211  this)
212  .detach();
213  }
214  else
215  __COUT__ << "App Status checking is disabled." << __E__;
216  } // end checking of Application Status
217 
218  // try
219  // {
220  // auto artdaqStateChangeEnabled =
221  // CorePropertySupervisorBase::getSupervisorTableNode()
222  // .getNode("EnableARTDAQCommanderPlugin")
223  // .getValue<bool>();
224  // if(artdaqStateChangeEnabled)
225  // {
226  // auto artdaqStateChangePort =
227  // CorePropertySupervisorBase::getSupervisorTableNode()
228  // .getNode("ARTDAQCommanderID")
229  // .getValue<int>();
230  // auto artdaqStateChangePluginType =
231  // CorePropertySupervisorBase::getSupervisorTableNode()
232  // .getNode("ARTDAQCommanderType")
233  // .getValue<std::string>();
234  // theArtdaqCommandable_.init(artdaqStateChangePort,
235  // artdaqStateChangePluginType);
236  // }
237  // }
238  // catch(...)
239  // {
240  // ;
241  // } // ignore errors
242 } // end init()
243 
244 //========================================================================================================================
245 // AppStatusWorkLoop
246 // child thread
247 void GatewaySupervisor::AppStatusWorkLoop(GatewaySupervisor* theSupervisor)
248 {
249  sleep(5);
250  std::string status, progress, appName;
251  int progressInteger;
252  while(1)
253  {
254  // workloop procedure
255  // Loop through all Apps and request status
256  // sleep
257  // __COUT__ << "Just debugging App status checking" << __E__;
258  for(const auto& it : theSupervisor->allSupervisorInfo_.getAllSupervisorInfo())
259  {
260  auto appInfo = it.second;
261  appName = appInfo.getName();
262  // __COUT__ << "Getting Status "
263  // << " Supervisor instance = '" << appInfo.getName()
264  // << "' [LID=" << appInfo.getId() << "] in Context '"
265  // << appInfo.getContextName() << "' [URL=" <<
266  // appInfo.getURL()
267  // << "].\n\n";
268  // if the application is the gateway supervisor, we do not send a SOAP message
269  if(appName == "Supervisor")
270  {
271  // send back status and progress parameters
272  status = theSupervisor->theStateMachine_.getCurrentStateName();
273  progress = theSupervisor->theProgressBar_.readPercentageString();
274 
275  if(theSupervisor->theStateMachine_.isInTransition())
276  {
277  // return the ProvenanceStateName
278  status = theSupervisor->theStateMachine_.getProvenanceStateName();
279  }
280  else
281  {
282  status = theSupervisor->theStateMachine_.getCurrentStateName();
283  }
284  }
285  else
286  {
287  // pass the application as a parameter to tempMessage
288  SOAPParameters appPointer;
289  appPointer.addParameter("ApplicationPointer");
290 
291  xoap::MessageReference tempMessage =
292  SOAPUtilities::makeSOAPMessageReference("ApplicationStatusRequest");
293  // print tempMessage
294  // __COUT__ << "tempMessage... " <<
295  // SOAPUtilities::translate(tempMessage)
296  // << std::endl;
297  //
298  try
299  {
300  xoap::MessageReference statusMessage =
301  theSupervisor->sendWithSOAPReply(appInfo.getDescriptor(),
302  tempMessage);
303 
304  // __COUT__ << "statusMessage... "
305  // <<
306  //SOAPUtilities::translate(statusMessage)
307  //<< std::endl;
308  //
309  SOAPParameters parameters;
310  parameters.addParameter("Status");
311  parameters.addParameter("Progress");
312  SOAPUtilities::receive(statusMessage, parameters);
313 
314  status = parameters.getValue("Status");
315  if(status.empty())
316  {
317  status = "Unknown";
318  }
319 
320  progress = parameters.getValue("Progress");
321  if(progress.empty())
322  {
323  progress = "0";
324  }
325  // __COUTV__(status);
326  // __COUTV__(progress);
327  }
328  catch(const xdaq::exception::Exception& e)
329  {
330  __COUT__ << "Failed to send SOAP Message... " << std::endl;
331  }
332  }
333 
334  // store status and progress in some struct..
335  // in the APP STATUS structure
336  // set status and progress
337  theSupervisor->allSupervisorInfo_.setSupervisorStatus(appInfo, status);
338  // convert the progress string into an integer in order to call
339  // appInfo.setProgress() function
340  std::istringstream ssProgress(progress);
341  ssProgress >> progressInteger;
342  theSupervisor->allSupervisorInfo_.setSupervisorProgress(appInfo,
343  progressInteger);
344 
345  } // end of for loop
346 
347  sleep(5);
348  }
349 } // end AppStatusWorkLoop
350 
351 //========================================================================================================================
352 // StateChangerWorkLoop
353 // child thread
354 void GatewaySupervisor::StateChangerWorkLoop(GatewaySupervisor* theSupervisor)
355 {
356  ConfigurationTree configLinkNode =
357  theSupervisor->CorePropertySupervisorBase::getSupervisorTableNode();
358 
359  std::string ipAddressForStateChangesOverUDP =
360  configLinkNode.getNode("IPAddressForStateChangesOverUDP").getValue<std::string>();
361  int portForStateChangesOverUDP =
362  configLinkNode.getNode("PortForStateChangesOverUDP").getValue<int>();
363  bool acknowledgementEnabled =
364  configLinkNode.getNode("EnableAckForStateChangesOverUDP").getValue<bool>();
365 
366  //__COUT__ << "IPAddressForStateChangesOverUDP = " << ipAddressForStateChangesOverUDP
367  //<< __E__;
368  //__COUT__ << "PortForStateChangesOverUDP = " << portForStateChangesOverUDP <<
369  //__E__;
370  //__COUT__ << "acknowledgmentEnabled = " << acknowledgmentEnabled << __E__;
371 
372  TransceiverSocket sock(ipAddressForStateChangesOverUDP,
373  portForStateChangesOverUDP); // Take Port from Table
374  try
375  {
376  sock.initialize();
377  }
378  catch(...)
379  {
380  // generate special message to indicate failed socket
381  __SS__ << "FATAL Console error. Could not initialize socket at ip '"
382  << ipAddressForStateChangesOverUDP << "' and port "
383  << portForStateChangesOverUDP
384  << ". Perhaps it is already in use? Exiting State Changer "
385  "SOAPUtilities::receive loop."
386  << __E__;
387  __COUT__ << ss.str();
388  __SS_THROW__;
389  return;
390  }
391 
392  std::size_t commaPosition;
393  unsigned int commaCounter = 0;
394  std::size_t begin = 0;
395  std::string buffer;
396  std::string errorStr;
397  std::string fsmName;
398  std::string command;
399  std::vector<std::string> parameters;
400  while(1)
401  {
402  // workloop procedure
403  // if SOAPUtilities::receive a UDP command
404  // execute command
405  // else
406  // sleep
407 
408  if(sock.receive(
409  buffer, 0 /*timeoutSeconds*/, 1 /*timeoutUSeconds*/, false /*verbose*/) !=
410  -1)
411  {
412  __COUT__ << "UDP State Changer packet received of size = " << buffer.size()
413  << __E__;
414 
415  size_t nCommas = std::count(buffer.begin(), buffer.end(), ',');
416  if(nCommas == 0)
417  {
418  __SS__ << "Unrecognized State Machine command :-" << buffer
419  << "-. Format is FiniteStateMachineName,Command,Parameter(s). "
420  "Where Parameter(s) is/are optional."
421  << __E__;
422  __COUT_INFO__ << ss.str();
423  __MOUT_INFO__ << ss.str();
424  }
425  begin = 0;
426  commaCounter = 0;
427  parameters.clear();
428  while((commaPosition = buffer.find(',', begin)) != std::string::npos ||
429  commaCounter == nCommas)
430  {
431  if(commaCounter == nCommas)
432  commaPosition = buffer.size();
433  if(commaCounter == 0)
434  fsmName = buffer.substr(begin, commaPosition - begin);
435  else if(commaCounter == 1)
436  command = buffer.substr(begin, commaPosition - begin);
437  else
438  parameters.push_back(buffer.substr(begin, commaPosition - begin));
439  __COUT__ << "Word: " << buffer.substr(begin, commaPosition - begin)
440  << __E__;
441 
442  begin = commaPosition + 1;
443  ++commaCounter;
444  }
445 
446  // set scope of mutext
447  {
448  // should be mutually exclusive with GatewaySupervisor main thread state
449  // machine accesses lockout the messages array for the remainder of the
450  // scope this guarantees the reading thread can safely access the
451  // messages
452  if(theSupervisor->VERBOSE_MUTEX)
453  __COUT__ << "Waiting for FSM access" << __E__;
454  std::lock_guard<std::mutex> lock(theSupervisor->stateMachineAccessMutex_);
455  if(theSupervisor->VERBOSE_MUTEX)
456  __COUT__ << "Have FSM access" << __E__;
457 
458  errorStr = theSupervisor->attemptStateMachineTransition(
459  0,
460  0,
461  command,
462  fsmName,
463  WebUsers::DEFAULT_STATECHANGER_USERNAME /*fsmWindowName*/,
464  WebUsers::DEFAULT_STATECHANGER_USERNAME,
465  parameters);
466  }
467 
468  if(errorStr != "")
469  {
470  __SS__ << "UDP State Changer failed to execute command because of the "
471  "following error: "
472  << errorStr;
473  __COUT_ERR__ << ss.str();
474  __MOUT_ERR__ << ss.str();
475  if(acknowledgementEnabled)
476  sock.acknowledge(errorStr, true /*verbose*/);
477  }
478  else
479  {
480  __SS__ << "Successfully executed state change command '" << command
481  << ".'" << __E__;
482  __COUT_INFO__ << ss.str();
483  __MOUT_INFO__ << ss.str();
484  if(acknowledgementEnabled)
485  sock.acknowledge("Done", true /*verbose*/);
486  }
487  }
488  else
489  sleep(1);
490  }
491 } // end StateChangerWorkLoop
492 
493 //========================================================================================================================
494 // makeSystemLogbookEntry
495 // makes a logbook entry into all Logbook supervisors
496 // and specifically the current active experiments within the logbook
497 // escape entryText to make it html/xml safe!!
499 void GatewaySupervisor::makeSystemLogbookEntry(std::string entryText)
500 {
501  __COUT__ << "Making System Logbook Entry: " << entryText << __E__;
502 
503  SupervisorInfoMap logbookInfoMap =
504  allSupervisorInfo_.getAllLogbookTypeSupervisorInfo();
505 
506  if(logbookInfoMap.size() == 0)
507  {
508  __COUT__ << "No logbooks found! Here is entry: " << entryText << __E__;
509  __MOUT__ << "No logbooks found! Here is entry: " << entryText << __E__;
510  return;
511  }
512  else
513  {
514  __COUT__ << "Making logbook entry: " << entryText << __E__;
515  __MOUT__ << "Making logbook entry: " << entryText << __E__;
516  }
517 
518  //__COUT__ << "before: " << entryText << __E__;
519  { // input entryText
520  std::string replace[] = {"\"", "'", "&", "<", ">", "\n", " "};
521  std::string with[] = {"%22", "%27", "%26", "%3C", "%3E", "%0A%0D", "%20%20"};
522 
523  int numOfKeys = 7;
524 
525  size_t f;
526  for(int i = 0; i < numOfKeys; ++i)
527  {
528  while((f = entryText.find(replace[i])) != std::string::npos)
529  {
530  entryText = entryText.substr(0, f) + with[i] +
531  entryText.substr(f + replace[i].length());
532  //__COUT__ << "found " << " " << entryText << __E__;
533  }
534  }
535  }
536  //__COUT__ << "after: " << entryText << __E__;
537 
538  SOAPParameters parameters("EntryText", entryText);
539  // SOAPParametersV parameters(1);
540  // parameters[0].setName("EntryText"); parameters[0].setValue(entryText);
541 
542  for(auto& logbookInfo : logbookInfoMap)
543  {
544  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
545  logbookInfo.second.getDescriptor(), "MakeSystemLogbookEntry", parameters);
546 
547  SOAPParameters retParameters("Status");
548  // SOAPParametersV retParameters(1);
549  // retParameters[0].setName("Status");
550  SOAPUtilities::receive(retMsg, retParameters);
551 
552  __COUT__ << "Returned Status: " << retParameters.getValue("Status")
553  << __E__; // retParameters[0].getValue() << __E__ << __E__;
554  }
555 }
556 
557 //========================================================================================================================
558 void GatewaySupervisor::Default(xgi::Input* in, xgi::Output* out)
559 
560 {
561  //
562  if(!supervisorGuiHasBeenLoaded_ &&
563  (supervisorGuiHasBeenLoaded_ =
564  true)) // make system logbook entry that ots has been started
565  makeSystemLogbookEntry("ots started.");
566 
567  *out << "<!DOCTYPE HTML><html lang='en'><head><title>ots</title>" <<
568  // show ots icon
569  // from http://www.favicon-generator.org/
570  "<link rel='apple-touch-icon' sizes='57x57' href='/WebPath/images/otsdaqIcons/apple-icon-57x57.png'>\
571  <link rel='apple-touch-icon' sizes='60x60' href='/WebPath/images/otsdaqIcons/apple-icon-60x60.png'>\
572  <link rel='apple-touch-icon' sizes='72x72' href='/WebPath/images/otsdaqIcons/apple-icon-72x72.png'>\
573  <link rel='apple-touch-icon' sizes='76x76' href='/WebPath/images/otsdaqIcons/apple-icon-76x76.png'>\
574  <link rel='apple-touch-icon' sizes='114x114' href='/WebPath/images/otsdaqIcons/apple-icon-114x114.png'>\
575  <link rel='apple-touch-icon' sizes='120x120' href='/WebPath/images/otsdaqIcons/apple-icon-120x120.png'>\
576  <link rel='apple-touch-icon' sizes='144x144' href='/WebPath/images/otsdaqIcons/apple-icon-144x144.png'>\
577  <link rel='apple-touch-icon' sizes='152x152' href='/WebPath/images/otsdaqIcons/apple-icon-152x152.png'>\
578  <link rel='apple-touch-icon' sizes='180x180' href='/WebPath/images/otsdaqIcons/apple-icon-180x180.png'>\
579  <link rel='icon' type='image/png' sizes='192x192' href='/WebPath/images/otsdaqIcons/android-icon-192x192.png'>\
580  <link rel='icon' type='image/png' sizes='32x32' href='/WebPath/images/otsdaqIcons/favicon-32x32.png'>\
581  <link rel='icon' type='image/png' sizes='96x96' href='/WebPath/images/otsdaqIcons/favicon-96x96.png'>\
582  <link rel='icon' type='image/png' sizes='16x16' href='/WebPath/images/otsdaqIcons/favicon-16x16.png'>\
583  <link rel='manifest' href='/WebPath/images/otsdaqIcons/manifest.json'>\
584  <meta name='msapplication-TileColor' content='#ffffff'>\
585  <meta name='msapplication-TileImage' content='/ms-icon-144x144.png'>\
586  <meta name='theme-color' content='#ffffff'>"
587  <<
588  // end show ots icon
589  "</head>"
590  << "<frameset col='100%' row='100%'>"
591  << "<frame src='/WebPath/html/Desktop.html?urn="
592  << this->getApplicationDescriptor()->getLocalId()
593  << "&securityType=" << securityType_ << "'></frameset></html>";
594 }
595 
596 //========================================================================================================================
597 // stateMachineIterationBreakpoint
598 // get/set the state machine iteration breakpoint
599 // If the iteration index >= breakpoint, then pause.
600 void GatewaySupervisor::stateMachineIterationBreakpoint(xgi::Input* in,
601  xgi::Output* out) try
602 {
603  cgicc::Cgicc cgiIn(in);
604 
605  std::string requestType = CgiDataUtilities::getData(cgiIn, "Request");
606 
607  HttpXmlDocument xmlOut;
608  WebUsers::RequestUserInfo userInfo(requestType,
609  CgiDataUtilities::postData(cgiIn, "CookieCode"));
610 
611  CorePropertySupervisorBase::getRequestUserInfo(userInfo);
612 
613  if(!theWebUsers_.xmlRequestOnGateway(cgiIn, out, &xmlOut, userInfo))
614  return; // access failed
615 
616  __COUTV__(requestType);
617 
618  try
619  {
620  if(requestType == "get")
621  {
622  std::stringstream v;
623  { // start mutex scope
624  std::lock_guard<std::mutex> lock(broadcastIterationBreakpointMutex_);
625  v << broadcastIterationBreakpoint_;
626  } // end mutex scope
627 
628  xmlOut.addTextElementToData("iterationBreakpoint", v.str());
629  }
630  else if(requestType == "set")
631  {
632  unsigned int breakpointSetValue =
633  CgiDataUtilities::getDataAsInt(cgiIn, "breakpointSetValue");
634  __COUTV__(breakpointSetValue);
635 
636  { // start mutex scope
637  std::lock_guard<std::mutex> lock(broadcastIterationBreakpointMutex_);
638  broadcastIterationBreakpoint_ = breakpointSetValue;
639  } // end mutex scope
640 
641  // return the value that was set
642  std::stringstream v;
643  v << breakpointSetValue;
644  xmlOut.addTextElementToData("iterationBreakpoint", v.str());
645  }
646  else
647  {
648  __SS__ << "Unknown iteration breakpoint request type = " << requestType
649  << __E__;
650  __SS_THROW__;
651  }
652  }
653  catch(const std::runtime_error& e)
654  {
655  __SS__ << "Error caught handling iteration breakpoint command: " << e.what()
656  << __E__;
657  __COUT_ERR__ << ss.str();
658  xmlOut.addTextElementToData("Error", ss.str());
659  }
660  catch(...)
661  {
662  __SS__ << "Unknown error caught handling iteration breakpoint command." << __E__;
663  __COUT_ERR__ << ss.str();
664  xmlOut.addTextElementToData("Error", ss.str());
665  } // end stateMachineIterationBreakpoint() catch
666 
667  xmlOut.outputXmlDocument((std::ostringstream*)out, false, true);
668 
669 } // end stateMachineIterationBreakpoint()
670 catch(const std::runtime_error& e)
671 {
672  __SS__ << "Error caught handling iteration breakpoint command: " << e.what() << __E__;
673  __COUT_ERR__ << ss.str();
674 }
675 catch(...)
676 {
677  __SS__ << "Unknown error caught handling iteration breakpoint command." << __E__;
678  __COUT_ERR__ << ss.str();
679 } // end stateMachineIterationBreakpoint() catch
680 
681 //========================================================================================================================
682 void GatewaySupervisor::stateMachineXgiHandler(xgi::Input* in, xgi::Output* out)
683 {
684  // for simplicity assume all commands should be mutually exclusive with iterator
685  // thread state machine accesses (really should just be careful with
686  // RunControlStateMachine access)
687  if(VERBOSE_MUTEX)
688  __COUT__ << "Waiting for FSM access" << __E__;
689  std::lock_guard<std::mutex> lock(stateMachineAccessMutex_);
690  if(VERBOSE_MUTEX)
691  __COUT__ << "Have FSM access" << __E__;
692 
693  cgicc::Cgicc cgiIn(in);
694 
695  std::string command = CgiDataUtilities::getData(cgiIn, "StateMachine");
696  std::string requestType =
697  "StateMachine" + command; // prepend StateMachine to request type
698 
699  HttpXmlDocument xmlOut;
700  WebUsers::RequestUserInfo userInfo(requestType,
701  CgiDataUtilities::postData(cgiIn, "CookieCode"));
702 
703  CorePropertySupervisorBase::getRequestUserInfo(userInfo);
704 
705  if(!theWebUsers_.xmlRequestOnGateway(cgiIn, out, &xmlOut, userInfo))
706  return; // access failed
707 
708  std::string fsmName = CgiDataUtilities::getData(cgiIn, "fsmName");
709  std::string fsmWindowName = CgiDataUtilities::getData(cgiIn, "fsmWindowName");
710  fsmWindowName = StringMacros::decodeURIComponent(fsmWindowName);
711  std::string currentState = theStateMachine_.getCurrentStateName();
712 
713  __COUT__ << "Check for Handled by theIterator_" << __E__;
714 
715  // check if Iterator should handle
716  if((activeStateMachineWindowName_ == "" ||
717  activeStateMachineWindowName_ == "iterator") &&
718  theIterator_.handleCommandRequest(xmlOut, command, fsmWindowName))
719  {
720  __COUT__ << "Handled by theIterator_" << __E__;
721  xmlOut.outputXmlDocument((std::ostringstream*)out, false);
722  return;
723  }
724 
725  // Do not allow transition while in transition
726  if(theStateMachine_.isInTransition())
727  {
728  __SS__ << "Error - Can not accept request because the State Machine is already "
729  "in transition!"
730  << __E__;
731  __COUT_ERR__ << "\n" << ss.str();
732 
733  xmlOut.addTextElementToData("state_tranisition_attempted",
734  "0"); // indicate to GUI transition NOT attempted
735  xmlOut.addTextElementToData(
736  "state_tranisition_attempted_err",
737  ss.str()); // indicate to GUI transition NOT attempted
738  xmlOut.outputXmlDocument((std::ostringstream*)out, false, true);
739  return;
740  }
741 
743  // Validate FSM name
744  // if fsm name != active fsm name
745  // only allow, if current state is halted or init
746  // take active fsm name when configured
747  // else, allow
748  if(activeStateMachineName_ != "" && activeStateMachineName_ != fsmName)
749  {
750  __COUT__ << "currentState = " << currentState << __E__;
751  if(currentState != "Halted" && currentState != "Initial")
752  {
753  // illegal for this FSM name to attempt transition
754 
755  __SS__ << "Error - Can not accept request because the State Machine "
756  << "with window name '" << activeStateMachineWindowName_
757  << "' (UID: " << activeStateMachineName_
758  << ") "
759  "is currently "
760  << "in control of State Machine progress. ";
761  ss << "\n\nIn order for this State Machine with window name '"
762  << fsmWindowName << "' (UID: " << fsmName
763  << ") "
764  "to control progress, please transition to Halted using the active "
765  << "State Machine '" << activeStateMachineWindowName_ << ".'" << __E__;
766  __COUT_ERR__ << "\n" << ss.str();
767 
768  xmlOut.addTextElementToData("state_tranisition_attempted",
769  "0"); // indicate to GUI transition NOT attempted
770  xmlOut.addTextElementToData(
771  "state_tranisition_attempted_err",
772  ss.str()); // indicate to GUI transition NOT attempted
773  xmlOut.outputXmlDocument((std::ostringstream*)out, false, true);
774  return;
775  }
776  else // clear active state machine
777  {
778  activeStateMachineName_ = "";
779  activeStateMachineWindowName_ = "";
780  }
781  }
782 
783  // At this point, attempting transition!
784 
785  std::vector<std::string> parameters;
786 
787  if(command == "Configure")
788  parameters.push_back(CgiDataUtilities::postData(cgiIn, "ConfigurationAlias"));
789 
790  attemptStateMachineTransition(
791  &xmlOut, out, command, fsmName, fsmWindowName, userInfo.username_, parameters);
792 
793 } // end stateMachineXgiHandler()
794 
795 //========================================================================================================================
796 std::string GatewaySupervisor::attemptStateMachineTransition(
797  HttpXmlDocument* xmldoc,
798  std::ostringstream* out,
799  const std::string& command,
800  const std::string& fsmName,
801  const std::string& fsmWindowName,
802  const std::string& username,
803  const std::vector<std::string>& commandParameters)
804 {
805  std::string errorStr = "";
806 
807  std::string currentState = theStateMachine_.getCurrentStateName();
808  __COUT__ << "State Machine command = " << command << __E__;
809  __COUT__ << "fsmName = " << fsmName << __E__;
810  __COUT__ << "fsmWindowName = " << fsmWindowName << __E__;
811  __COUT__ << "activeStateMachineName_ = " << activeStateMachineName_ << __E__;
812  __COUT__ << "command = " << command << __E__;
813  __COUT__ << "commandParameters.size = " << commandParameters.size() << __E__;
814 
815  SOAPParameters parameters;
816  if(command == "Configure")
817  {
818  if(currentState != "Halted") // check if out of sync command
819  {
820  __SS__ << "Error - Can only transition to Configured if the current "
821  << "state is Halted. Perhaps your state machine is out of sync."
822  << __E__;
823  __COUT_ERR__ << "\n" << ss.str();
824  errorStr = ss.str();
825 
826  if(xmldoc)
827  xmldoc->addTextElementToData(
828  "state_tranisition_attempted",
829  "0"); // indicate to GUI transition NOT attempted
830  if(xmldoc)
831  xmldoc->addTextElementToData(
832  "state_tranisition_attempted_err",
833  ss.str()); // indicate to GUI transition NOT attempted
834  if(out)
835  xmldoc->outputXmlDocument((std::ostringstream*)out,
836  false /*dispStdOut*/,
837  true /*allowWhiteSpace*/);
838 
839  return errorStr;
840  }
841 
842  // NOTE Original name of the configuration key
843  // parameters.addParameter("RUN_KEY",CgiDataUtilities::postData(cgi,"ConfigurationAlias"));
844  if(commandParameters.size() == 0)
845  {
846  __SS__ << "Error - Can only transition to Configured if a Configuration "
847  "Alias parameter is provided."
848  << __E__;
849  __COUT_ERR__ << "\n" << ss.str();
850  errorStr = ss.str();
851 
852  if(xmldoc)
853  xmldoc->addTextElementToData(
854  "state_tranisition_attempted",
855  "0"); // indicate to GUI transition NOT attempted
856  if(xmldoc)
857  xmldoc->addTextElementToData(
858  "state_tranisition_attempted_err",
859  ss.str()); // indicate to GUI transition NOT attempted
860  if(out)
861  xmldoc->outputXmlDocument((std::ostringstream*)out,
862  false /*dispStdOut*/,
863  true /*allowWhiteSpace*/);
864 
865  return errorStr;
866  }
867 
868  parameters.addParameter("ConfigurationAlias", commandParameters[0]);
869 
870  std::string configurationAlias = parameters.getValue("ConfigurationAlias");
871  __COUT__ << "Configure --> Name: ConfigurationAlias Value: " << configurationAlias
872  << __E__;
873 
874  // save last used config alias
875  std::string fn = FSM_LAST_GROUP_ALIAS_PATH + FSM_LAST_GROUP_ALIAS_FILE_START +
876  username + "." + FSM_USERS_PREFERENCES_FILETYPE;
877 
878  __COUT__ << "Save FSM preferences: " << fn << __E__;
879  FILE* fp = fopen(fn.c_str(), "w");
880  if(!fp)
881  {
882  __SS__ << ("Could not open file: " + fn) << __E__;
883  __COUT_ERR__ << ss.str();
884  __SS_THROW__;
885  }
886  fprintf(fp, "FSM_last_configuration_alias %s", configurationAlias.c_str());
887  fclose(fp);
888 
889  activeStateMachineName_ = fsmName;
890  activeStateMachineWindowName_ = fsmWindowName;
891  }
892  else if(command == "Start")
893  {
894  if(currentState != "Configured") // check if out of sync command
895  {
896  __SS__
897  << "Error - Can only transition to Configured if the current "
898  << "state is Halted. Perhaps your state machine is out of sync. "
899  << "(Likely the server was restarted or another user changed the state)"
900  << __E__;
901  __COUT_ERR__ << "\n" << ss.str();
902  errorStr = ss.str();
903 
904  if(xmldoc)
905  xmldoc->addTextElementToData(
906  "state_tranisition_attempted",
907  "0"); // indicate to GUI transition NOT attempted
908  if(xmldoc)
909  xmldoc->addTextElementToData(
910  "state_tranisition_attempted_err",
911  ss.str()); // indicate to GUI transition NOT attempted
912  if(out)
913  xmldoc->outputXmlDocument((std::ostringstream*)out,
914  false /*dispStdOut*/,
915  true /*allowWhiteSpace*/);
916 
917  return errorStr;
918  }
919  unsigned int runNumber;
920  if(commandParameters.size() == 0)
921  {
922  runNumber = getNextRunNumber();
923  setNextRunNumber(runNumber + 1);
924  }
925  else
926  {
927  runNumber = std::atoi(commandParameters[0].c_str());
928  }
929  parameters.addParameter("RunNumber", runNumber);
930  }
931 
932  xoap::MessageReference message =
933  SOAPUtilities::makeSOAPMessageReference(command, parameters);
934  // Maybe we return an acknowledgment that the message has been received and processed
935  xoap::MessageReference reply = stateMachineXoapHandler(message);
936  // stateMachineWorkLoopManager_.removeProcessedRequests();
937  // stateMachineWorkLoopManager_.processRequest(message);
938 
939  if(xmldoc)
940  xmldoc->addTextElementToData("state_tranisition_attempted",
941  "1"); // indicate to GUI transition attempted
942  if(out)
943  xmldoc->outputXmlDocument((std::ostringstream*)out, false);
944  __COUT__ << "FSM state transition launched!" << __E__;
945 
946  stateMachineLastCommandInput_ = command;
947  return errorStr;
948 } // end attemptStateMachineTransition()
949 
952 // void GatewaySupervisor::stateMachineResultXgiHandler(xgi::Input* in, xgi::Output* out)
953 // throw (xgi::exception::Exception)
954 //{
955 // cgicc::Cgicc cgiIn(in);
956 // __COUT__ << "Xgi Request!" << __E__;
957 //
958 // uint8_t userPermissions; // uint64_t uid;
959 // std::string cookieCode = CgiDataUtilities::postData(cgi, "CookieCode");
960 // if (!theWebUsers_.cookieCodeIsActiveForRequest(cookieCode,
961 // &userPermissions))
962 // {
963 // *out << cookieCode;
964 // return;
965 // }
966 //
967 // HttpXmlDocument xmldoc(cookieCode);
968 //
969 // std::string command = CgiDataUtilities::getData(cgi, "StateMachine");
970 //
971 // SOAPParameters parameters;
972 // /*
973 // //FIXME I don't think that there should be this if statement and I guess it has been
974 // copied from the stateMachineXgiHandler method if (command == "Configure")
975 //
976 //{
977 // parameters.addParameter("RUN_KEY", CgiDataUtilities::postData(cgi,
978 //"ConfigurationAlias"));
979 // __COUT__ << "Configure --> Name: RUN_KEY Value: " << parameters.getValue("RUN_KEY")
980 //<< __E__;
981 // }
982 // else if (command == "Start")
983 //
984 //{
985 // unsigned int runNumber = getNextRunNumber();
986 // std::stringstream runNumberStream;
987 // runNumberStream << runNumber;
988 // parameters.addParameter("RUN_NUMBER", runNumberStream.str().c_str());
989 // setNextRunNumber(++runNumber);
990 // }
991 // */
992 // xoap::MessageReference message = SOAPUtilities::makeSOAPMessageReference(
993 // CgiDataUtilities::getData(cgi, "StateMachine"), parameters);
994 // //Maybe we return an aknowledgment that the message has been received and processed
995 // xoap::MessageReference reply = stateMachineResultXoapHandler(message);
996 // //stateMachineWorkLoopManager_.removeProcessedRequests();
997 // //stateMachineWorkLoopManager_.processRequest(message);
998 // //xmldoc.outputXmlDocument((ostringstream*)out,false);
999 // __COUT__ << "Done - Xgi Request!" << __E__;
1000 //}
1001 
1002 //========================================================================================================================
1003 xoap::MessageReference GatewaySupervisor::stateMachineXoapHandler(
1004  xoap::MessageReference message)
1005 
1006 {
1007  __COUT__ << "Soap Handler!" << __E__;
1008  stateMachineWorkLoopManager_.removeProcessedRequests();
1009  stateMachineWorkLoopManager_.processRequest(message);
1010  __COUT__ << "Done - Soap Handler!" << __E__;
1011  return message;
1012 }
1013 
1015 // xoap::MessageReference GatewaySupervisor::stateMachineResultXoapHandler(
1016 // xoap::MessageReference message)
1017 //
1018 //{
1019 // __COUT__ << "Soap Handler!" << __E__;
1020 // // stateMachineWorkLoopManager_.removeProcessedRequests();
1021 // // stateMachineWorkLoopManager_.processRequest(message);
1022 // __COUT__ << "Done - Soap Handler!" << __E__;
1023 // return message;
1024 //}
1025 
1026 //========================================================================================================================
1027 // stateMachineThread
1028 // This asynchronously sends the xoap message to its own RunControlStateMachine
1029 // (that the Gateway inherits from), which then calls the Gateway
1030 // transition functions and eventually the broadcast to transition the global
1031 // state machine.
1032 bool GatewaySupervisor::stateMachineThread(toolbox::task::WorkLoop* workLoop)
1033 {
1034  stateMachineSemaphore_.take();
1035  std::string command =
1036  SOAPUtilities::translate(stateMachineWorkLoopManager_.getMessage(workLoop))
1037  .getCommand();
1038 
1039  __COUT__ << "Propagating command '" << command << "'..." << __E__;
1040 
1041  std::string reply = send(allSupervisorInfo_.getGatewayDescriptor(),
1042  stateMachineWorkLoopManager_.getMessage(workLoop));
1043  stateMachineWorkLoopManager_.report(workLoop, reply, 100, true);
1044 
1045  __COUT__ << "Done with command '" << command << ".' Reply = " << reply << __E__;
1046  stateMachineSemaphore_.give();
1047 
1048  if(reply == "Fault")
1049  {
1050  __SS__ << "Failure to send Workloop transition command '" << command
1051  << "!' An error response '" << reply << "' was received." << __E__;
1052  __COUT_ERR__ << ss.str();
1053  __MOUT_ERR__ << ss.str();
1054  }
1055  return false; // execute once and automatically remove the workloop so in
1056  // WorkLoopManager the try workLoop->remove(job_) could be commented
1057  // out return true;//go on and then you must do the
1058  // workLoop->remove(job_) in WorkLoopManager
1059 } // end stateMachineThread()
1060 
1061 //========================================================================================================================
1062 // statusRequest ~~
1063 // Call from JS GUI
1064 // parameter:
1065 //
1066 void GatewaySupervisor::statusRequest(xgi::Input* in, xgi::Output* out)
1067 
1068 {
1069  __COUT__ << "Starting to Request!" << __E__;
1070 
1071  cgicc::Cgicc cgiIn(in);
1072  std::string requestType = "statusRequest"; // force request type to statusRequest
1073 
1074  HttpXmlDocument xmlOut;
1075  WebUsers::RequestUserInfo userInfo(requestType,
1076  CgiDataUtilities::postData(cgiIn, "CookieCode"));
1077 
1078  CorePropertySupervisorBase::getRequestUserInfo(userInfo);
1079 
1080  if(!theWebUsers_.xmlRequestOnGateway(cgiIn, out, &xmlOut, userInfo))
1081  return; // access failed
1082 
1083  __COUT__ << "Starting to Request!" << __E__;
1084 
1085  // If the thread is canceled when you are still in this method that activated it, then
1086  // everything will freeze. The way the workloop manager now works is safe since it
1087  // cancel the thread only when the infoRequestResultHandler is called and that method
1088  // can be called ONLY when I am already out of here!
1089 
1090  // HttpXmlDocument tmpDoc = infoRequestWorkLoopManager_.processRequest(cgiIn);
1091 
1092  // xmlOut.copyDataChildren(tmpDoc);
1093 
1094  xmlOut.outputXmlDocument((std::ostringstream*)out, false);
1095 }
1096 
1097 //========================================================================================================================
1098 void GatewaySupervisor::infoRequestResultHandler(xgi::Input* in, xgi::Output* out)
1099 
1100 {
1101  __COUT__ << "Starting ask!" << __E__;
1102  cgicc::Cgicc cgi(in);
1103 
1104  cgicc::Cgicc cgiIn(in);
1105  std::string requestType =
1106  "infoRequestResultHandler"; // force request type to infoRequestResultHandler
1107 
1108  HttpXmlDocument xmlOut;
1109  WebUsers::RequestUserInfo userInfo(requestType,
1110  CgiDataUtilities::postData(cgiIn, "CookieCode"));
1111 
1112  CorePropertySupervisorBase::getRequestUserInfo(userInfo);
1113 
1114  if(!theWebUsers_.xmlRequestOnGateway(cgiIn, out, &xmlOut, userInfo))
1115  return; // access failed
1116 
1117  // //**** start LOGIN GATEWAY CODE ***//
1118  // //If TRUE, cookie code is good, and refreshed code is in cookieCode, also pointers
1119  // optionally for uint8_t userPermissions, uint64_t uid
1120  // //Else, error message is returned in cookieCode
1121  // std::string cookieCode = CgiDataUtilities::postData(cgi, "CookieCode");
1122  // uint8_t userPermissions;// uint64_t uid;
1123  // if (!theWebUsers_.cookieCodeIsActiveForRequest(cookieCode, &userPermissions))
1124  // {
1125  // *out << cookieCode;
1126  // return;
1127  // }
1128  // //**** end LOGIN GATEWAY CODE ***//
1129  //
1130  // HttpXmlDocument xmldoc(cookieCode);
1131 
1132  infoRequestWorkLoopManager_.getRequestResult(cgiIn, xmlOut);
1133 
1134  // return xml doc holding server response
1135  xmlOut.outputXmlDocument((std::ostringstream*)out, false);
1136 
1137  __COUT__ << "Done asking!" << __E__;
1138 }
1139 
1140 //========================================================================================================================
1141 bool GatewaySupervisor::infoRequestThread(toolbox::task::WorkLoop* workLoop)
1142 {
1143  // std::string workLoopName = workLoop->getName();
1144  // __COUT__ << " Starting WorkLoop: " << workLoopName << __E__;
1145  // __COUT__ << " Ready to lock" << __E__;
1146  infoRequestSemaphore_.take();
1147  // __COUT__ << " Locked" << __E__;
1148  vectorTest_.clear();
1149 
1150  for(unsigned long long i = 0; i < 100000000; i++)
1151  {
1152  counterTest_ += 2;
1153  vectorTest_.push_back(counterTest_);
1154  }
1155 
1156  infoRequestWorkLoopManager_.report(
1157  workLoop, "RESULT: This is the best result ever", 50, false);
1158  std::string workLoopName = workLoop->getName();
1159  __COUT__ << workLoopName << " test: " << counterTest_
1160  << " vector size: " << vectorTest_.size() << __E__;
1161  wait(400, "InfoRequestThread ----- locked");
1162  infoRequestSemaphore_.give();
1163  // __COUT__ << " Lock released" << __E__;
1164  wait(200, "InfoRequestThread");
1165  // __COUT__ << " Ready to lock again" << __E__;
1166  infoRequestSemaphore_.take();
1167  // __COUT__ << " Locked again" << __E__;
1168  vectorTest_.clear();
1169 
1170  for(unsigned long long i = 0; i < 100000000; i++)
1171  {
1172  counterTest_ += 2;
1173  vectorTest_.push_back(counterTest_);
1174  }
1175 
1176  wait(400, "InfoRequestThread ----- locked");
1177  __COUT__ << workLoopName << " test: " << counterTest_
1178  << " vector size: " << vectorTest_.size() << __E__;
1179  infoRequestSemaphore_.give();
1180  // __COUT__ << " Lock released again" << __E__;
1181  // infoRequestWorkLoopManager_->report(workLoop,"RESULT: This is the best result
1182  // ever");
1183  infoRequestWorkLoopManager_.report(
1184  workLoop, theStateMachine_.getCurrentStateName(), 100, true);
1185  // __COUT__ << " Done with WorkLoop: " << workLoopName << __E__;
1186  return false; // execute once and automatically remove the workloop so in
1187  // WorkLoopManager the try workLoop->remove(job_) could be commented
1188  // out return true;//go on and then you must do the
1189  // workLoop->remove(job_) in WorkLoopManager
1190 }
1191 
1192 //========================================================================================================================
1193 void GatewaySupervisor::stateInitial(toolbox::fsm::FiniteStateMachine& fsm)
1194 
1195 {
1196  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1197  // diagService_->reportError("--- GatewaySupervisor is in its Initial state
1198  // ---",DIAGINFO); diagService_->reportError("GatewaySupervisor::stateInitial:
1199  // workloop active: "+stringF(calibWorkloop_->isActive())+", workloop type:
1200  // "+calibWorkloop_->getType(),DIAGINFO);
1201 }
1202 
1203 //========================================================================================================================
1204 void GatewaySupervisor::statePaused(toolbox::fsm::FiniteStateMachine& fsm)
1205 
1206 {
1207  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1208  /*
1209  try
1210 {
1211  rcmsStateNotifier_.stateChanged("Paused", "");
1212  }
1213  catch(xcept::Exception &e)
1214 {
1215  diagService_->reportError("Failed to notify state change : "+
1216 xcept::stdformat_exception_history(e),DIAGERROR);
1217  }
1218 
1219  diagService_->reportError("GatewaySupervisor::statePaused: workloop active:
1220 "+stringF(calibWorkloop_->isActive())+", workloop type:
1221 "+calibWorkloop_->getType(),DIAGINFO);
1222  */
1223 }
1224 
1225 //========================================================================================================================
1226 void GatewaySupervisor::stateRunning(toolbox::fsm::FiniteStateMachine& fsm)
1227 
1228 {
1229  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1230  /*
1231  try
1232 {
1233  rcmsStateNotifier_.stateChanged("Running", "");
1234  }
1235  catch(xcept::Exception &e)
1236 {
1237  diagService_->reportError("Failed to notify state change : "+
1238 xcept::stdformat_exception_history(e),DIAGERROR);
1239  }
1240 
1241  diagService_->reportError("GatewaySupervisor::stateRunning: workloop active:
1242 "+stringF(calibWorkloop_->isActive())+", workloop type:
1243 "+calibWorkloop_->getType(),DIAGINFO);
1244  */
1245 }
1246 
1247 //========================================================================================================================
1248 void GatewaySupervisor::stateHalted(toolbox::fsm::FiniteStateMachine& fsm)
1249 
1250 {
1251  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1252  __COUT__ << "Fsm is in transition? "
1253  << (theStateMachine_.isInTransition() ? "yes" : "no") << __E__;
1254 
1255  /*
1256  percentageConfigured_=0.0;
1257  aliasesAndKeys_=PixelConfigInterface::getAliases();
1258  diagService_->reportError("GatewaySupervisor::stateHalted: aliases and keys
1259 reloaded",DIAGINFO); previousState_ = theStateMachine_.getCurrentStateName();
1260  diagService_->reportError("GatewaySupervisor::stateHalted: workloop active:
1261 "+stringF(calibWorkloop_->isActive())+", workloop type:
1262 "+calibWorkloop_->getType(),DIAGINFO);
1263 
1264  //(hopefully) make RCMS aware of successful Recovery
1265  //need to test that: (a) this works and (b) this does not break anything (regular
1266 Halt transition)
1267  //update -- condition (b) seems to be satisfied. not yet sure about condition (a)
1268  try
1269 {
1270  rcmsStateNotifier_.stateChanged("Halted", "");
1271  }
1272  catch(xcept::Exception &e)
1273 
1274 {
1275  diagService_->reportError("Failed to notify state change : "+
1276 xcept::stdformat_exception_history(e),DIAGERROR);
1277  }
1278  */
1279 }
1280 
1281 //========================================================================================================================
1282 void GatewaySupervisor::stateConfigured(toolbox::fsm::FiniteStateMachine& fsm)
1283 
1284 {
1285  /*
1286  // Notify RCMS of having entered the Configured state
1287  try
1288 
1289 {
1290  //rcmsStateNotifier_.stateChanged(previousState_.toString(), "");
1291  rcmsStateNotifier_.stateChanged("Configured", std::stringF(theGlobalKey_->key()) );
1292  }
1293  catch(xcept::Exception &e)
1294 
1295 {
1296  diagService_->reportError("Failed to notify state change : "+
1297 xcept::stdformat_exception_history(e),DIAGERROR);
1298  }
1299 
1300  PixelTimer debugTimer;
1301  if (extratimers_)
1302 {
1303  debugTimer.setName("GatewaySupervisor::stateConfigured");
1304  debugTimer.printTime("RCMS notified of Configured state");
1305  }
1306  if (configurationTimer_.started() )
1307 {
1308  configurationTimer_.stop();
1309 
1310  std::string confsource(__ENV__("PIXELCONFIGURATIONBASE"));
1311  if (confsource != "DB") confsource = "files";
1312 
1313  diagService_->reportError("Total configuration time ["+confsource+"] =
1314 "+stringF(configurationTimer_.tottime()),DIAGUSERINFO); configurationTimer_.reset();
1315  }
1316 
1317  diagService_->reportError( "GatewaySupervisor::stateConfigured: workloop active:
1318 "+stringF(calibWorkloop_->isActive())+", workloop type:
1319 "+calibWorkloop_->getType(),DIAGDEBUG);
1320  */
1321 }
1322 
1323 // void GatewaySupervisor::stateTTSTestMode (toolbox::fsm::FiniteStateMachine & fsm)
1324 //{
1325 // previousState_ = theStateMachine_.getCurrentStateName();
1326 //}
1327 
1328 // void GatewaySupervisor::inError (toolbox::fsm::FiniteStateMachine & fsm)
1329 //{
1330 // previousState_ = theStateMachine_.getCurrentStateName();
1331 // rcmsStateNotifier_.stateChanged("Error", "");
1332 //}
1333 
1334 /*
1335  xoap::MessageReference GatewaySupervisor::reset (xoap::MessageReference message)
1336 
1337 {
1338  //diagService_->reportError("New state before reset is: " +
1339 theStateMachine_.getCurrentStateName(),DIAGINFO);
1340 
1341  theStateMachine_.reset();
1342 
1343  xoap::MessageReference reply = xoap::createMessage();
1344  xoap::SOAPEnvelope envelope = reply->getSOAPPart().getEnvelope();
1345  xoap::SOAPName responseName = envelope.createName("ResetResponse", "xdaq", XDAQ_NS_URI);
1346  (void) envelope.getBody().addBodyElement ( responseName );
1347 
1348  diagService_->reportError("New state after reset is: " +
1349 theStateMachine_.getCurrentStateName(),DIAGINFO);
1350 
1351  return reply;
1352  }
1353  */
1354 
1355 //========================================================================================================================
1356 void GatewaySupervisor::inError(toolbox::fsm::FiniteStateMachine& fsm)
1357 
1358 {
1359  __COUT__
1360  << "Fsm current state: "
1361  << "Failed"
1362  // theStateMachine_.getCurrentStateName() //There may be a race condition here
1363  // when async errors occur (e.g. immediately in running)
1364  << __E__;
1365  // rcmsStateNotifier_.stateChanged("Error", "");
1366 }
1367 
1368 //========================================================================================================================
1369 void GatewaySupervisor::enteringError(toolbox::Event::Reference e)
1370 {
1371  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1372 
1373  // extract error message and save for user interface access
1374  toolbox::fsm::FailedEvent& failedEvent = dynamic_cast<toolbox::fsm::FailedEvent&>(*e);
1375 
1376  __SS__;
1377 
1378  // handle async error message differently
1379  if(RunControlStateMachine::asyncFailureReceived_)
1380  {
1381  ss << "\nAn asynchronous failure was encountered."
1382  << ".\n\nException:\n"
1383  << failedEvent.getException().what() << __E__;
1384  RunControlStateMachine::asyncFailureReceived_ = false; // clear async error
1385  }
1386  else
1387  {
1388  ss << "\nFailure performing transition from " << failedEvent.getFromState() << "-"
1389  << theStateMachine_.getStateName(failedEvent.getFromState()) << " to "
1390  << failedEvent.getToState() << "-"
1391  << theStateMachine_.getStateName(failedEvent.getToState())
1392  << ".\n\nException:\n"
1393  << failedEvent.getException().what() << __E__;
1394  }
1395 
1396  __COUT_ERR__ << "\n" << ss.str();
1397  theStateMachine_.setErrorMessage(ss.str());
1398 
1399  // move everything else to Error!
1400  broadcastMessage(SOAPUtilities::makeSOAPMessageReference("Error"));
1401 } // end enteringError()
1402 
1403 //========================================================================================================================
1404 void GatewaySupervisor::checkForAsyncError()
1405 {
1406  if(RunControlStateMachine::asyncFailureReceived_)
1407  {
1408  __COUTV__(RunControlStateMachine::asyncFailureReceived_);
1409 
1410  XCEPT_RAISE(toolbox::fsm::exception::Exception,
1411  RunControlStateMachine::getErrorMessage());
1412  return;
1413  }
1414 } // end checkForAsyncError()
1415 
1419 
1420 //========================================================================================================================
1421 void GatewaySupervisor::transitionConfiguring(toolbox::Event::Reference e)
1422 {
1423  checkForAsyncError();
1424 
1425  RunControlStateMachine::theProgressBar_.step();
1426 
1427  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1428 
1429  std::string systemAlias =
1430  SOAPUtilities::translate(theStateMachine_.getCurrentMessage())
1431  .getParameters()
1432  .getValue("ConfigurationAlias");
1433 
1434  __COUT__ << "Transition parameter: " << systemAlias << __E__;
1435 
1436  RunControlStateMachine::theProgressBar_.step();
1437 
1438  try
1439  {
1440  CorePropertySupervisorBase::theConfigurationManager_
1441  ->init(); // completely reset to re-align with any changes
1442  }
1443  catch(...)
1444  {
1445  __SS__ << "\nTransition to Configuring interrupted! "
1446  << "The Configuration Manager could not be initialized." << __E__;
1447 
1448  __COUT_ERR__ << "\n" << ss.str();
1449  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1450  return;
1451  }
1452 
1453  RunControlStateMachine::theProgressBar_.step();
1454 
1455  // Translate the system alias to a group name/key
1456  try
1457  {
1458  theConfigurationTableGroup_ =
1459  CorePropertySupervisorBase::theConfigurationManager_->getTableGroupFromAlias(
1460  systemAlias);
1461  }
1462  catch(...)
1463  {
1464  __COUT_INFO__ << "Exception occurred" << __E__;
1465  }
1466 
1467  RunControlStateMachine::theProgressBar_.step();
1468 
1469  if(theConfigurationTableGroup_.second.isInvalid())
1470  {
1471  __SS__ << "\nTransition to Configuring interrupted! System Alias " << systemAlias
1472  << " could not be translated to a group name and key." << __E__;
1473 
1474  __COUT_ERR__ << "\n" << ss.str();
1475  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1476  return;
1477  }
1478 
1479  RunControlStateMachine::theProgressBar_.step();
1480 
1481  __COUT__ << "Configuration table group name: " << theConfigurationTableGroup_.first
1482  << " key: " << theConfigurationTableGroup_.second << __E__;
1483 
1484  // make logbook entry
1485  {
1486  std::stringstream ss;
1487  ss << "Configuring '" << systemAlias << "' which translates to "
1488  << theConfigurationTableGroup_.first << " ("
1489  << theConfigurationTableGroup_.second << ").";
1490  makeSystemLogbookEntry(ss.str());
1491  }
1492 
1493  RunControlStateMachine::theProgressBar_.step();
1494 
1495  // load and activate
1496  try
1497  {
1498  CorePropertySupervisorBase::theConfigurationManager_->loadTableGroup(
1499  theConfigurationTableGroup_.first,
1500  theConfigurationTableGroup_.second,
1501  true /*doActivate*/);
1502 
1503  __COUT__ << "Done loading Configuration Alias." << __E__;
1504 
1505  // When configured, set the translated System Alias to be persistently active
1506  ConfigurationManagerRW tmpCfgMgr("TheGatewaySupervisor");
1507  tmpCfgMgr.activateTableGroup(theConfigurationTableGroup_.first,
1508  theConfigurationTableGroup_.second);
1509 
1510  __COUT__ << "Done activating Configuration Alias." << __E__;
1511  }
1512  catch(...)
1513  {
1514  __SS__ << "\nTransition to Configuring interrupted! System Alias " << systemAlias
1515  << " was translated to " << theConfigurationTableGroup_.first << " ("
1516  << theConfigurationTableGroup_.second
1517  << ") but could not be loaded and initialized." << __E__;
1518  ss << "\n\nTo debug this problem, try activating this group in the Configuration "
1519  "GUI "
1520  << " and detailed errors will be shown." << __E__;
1521  __COUT_ERR__ << "\n" << ss.str();
1522  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1523  return;
1524  }
1525 
1526  // check if configuration dump is enabled on configure transition
1527  {
1528  try
1529  {
1530  CorePropertySupervisorBase::theConfigurationManager_
1531  ->dumpMacroMakerModeFhicl();
1532  }
1533  catch(...) // ignore error for now
1534  {
1535  __COUT_ERR__ << "Failed to dump MacroMaker mode fhicl." << __E__;
1536  }
1537 
1538  ConfigurationTree configLinkNode =
1539  CorePropertySupervisorBase::theConfigurationManager_->getSupervisorTableNode(
1540  supervisorContextUID_, supervisorApplicationUID_);
1541  if(!configLinkNode.isDisconnected())
1542  {
1543  try // errors in dump are not tolerated
1544  {
1545  bool dumpConfiguration = true;
1546  std::string dumpFilePath, dumpFileRadix, dumpFormat;
1547  try // for backwards compatibility
1548  {
1549  ConfigurationTree fsmLinkNode =
1550  configLinkNode.getNode("LinkToStateMachineTable")
1551  .getNode(activeStateMachineName_);
1552  dumpConfiguration =
1553  fsmLinkNode
1554  .getNode("EnableConfigurationDumpOnConfigureTransition")
1555  .getValue<bool>();
1556  dumpFilePath =
1557  fsmLinkNode.getNode("ConfigurationDumpOnConfigureFilePath")
1558  .getValue<std::string>();
1559  dumpFileRadix =
1560  fsmLinkNode.getNode("ConfigurationDumpOnConfigureFileRadix")
1561  .getValue<std::string>();
1562  dumpFormat = fsmLinkNode.getNode("ConfigurationDumpOnConfigureFormat")
1563  .getValue<std::string>();
1564  }
1565  catch(std::runtime_error& e)
1566  {
1567  __COUT_INFO__ << "FSM configuration dump Link disconnected." << __E__;
1568  dumpConfiguration = false;
1569  }
1570 
1571  if(dumpConfiguration)
1572  {
1573  // dump configuration
1574  CorePropertySupervisorBase::theConfigurationManager_
1575  ->dumpActiveConfiguration(dumpFilePath + "/" + dumpFileRadix +
1576  "_" + std::to_string(time(0)) +
1577  ".dump",
1578  dumpFormat);
1579 
1580  CorePropertySupervisorBase::theConfigurationManager_
1581  ->dumpMacroMakerModeFhicl();
1582  }
1583  }
1584  catch(std::runtime_error& e)
1585  {
1586  __SS__ << "\nTransition to Configuring interrupted! There was an error "
1587  "identified "
1588  << "during the configuration dump attempt:\n\n " << e.what()
1589  << __E__;
1590  __COUT_ERR__ << "\n" << ss.str();
1591  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1592  return;
1593  }
1594  catch(...)
1595  {
1596  __SS__ << "\nTransition to Configuring interrupted! There was an error "
1597  "identified "
1598  << "during the configuration dump attempt.\n\n " << __E__;
1599  __COUT_ERR__ << "\n" << ss.str();
1600  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1601  return;
1602  }
1603  }
1604  }
1605 
1606  RunControlStateMachine::theProgressBar_.step();
1607  SOAPParameters parameters;
1608  parameters.addParameter("ConfigurationTableGroupName",
1609  theConfigurationTableGroup_.first);
1610  parameters.addParameter("ConfigurationTableGroupKey",
1611  theConfigurationTableGroup_.second.toString());
1612 
1613  // update Macro Maker front end list
1614  {
1615  __COUT__ << "Initializing Macro Maker." << __E__;
1616  xoap::MessageReference message =
1617  SOAPUtilities::makeSOAPMessageReference("FECommunication");
1618 
1619  SOAPParameters parameters;
1620  parameters.addParameter("type", "initFElist");
1621  parameters.addParameter("groupName", theConfigurationTableGroup_.first);
1622  parameters.addParameter("groupKey",
1623  theConfigurationTableGroup_.second.toString());
1624  SOAPUtilities::addParameters(message, parameters);
1625 
1626  __COUT__ << "Sending FE communication: " << SOAPUtilities::translate(message)
1627  << __E__;
1628 
1629  std::string reply =
1630  SOAPMessenger::send(CorePropertySupervisorBase::allSupervisorInfo_
1631  .getAllMacroMakerTypeSupervisorInfo()
1632  .begin()
1633  ->second.getDescriptor(),
1634  message);
1635 
1636  __COUT__ << "Macro Maker init reply: " << reply << __E__;
1637  if(reply == "Error")
1638  {
1639  __SS__ << "\nTransition to Configuring interrupted! There was an error "
1640  "identified initializing Macro Maker.\n\n "
1641  << __E__;
1642  __COUT_ERR__ << "\n" << ss.str();
1643  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1644  return;
1645  }
1646  }
1647 
1648  // xoap::MessageReference message =
1649  // SOAPUtilities::makeSOAPMessageReference(SOAPUtilities::translate(theStateMachine_.getCurrentMessage()).getCommand(),
1650  // parameters);
1651  xoap::MessageReference message = theStateMachine_.getCurrentMessage();
1652  SOAPUtilities::addParameters(message, parameters);
1653  broadcastMessage(message);
1654  RunControlStateMachine::theProgressBar_.step();
1655  // Advertise the exiting of this method
1656  // diagService_->reportError("GatewaySupervisor::stateConfiguring: Exiting",DIAGINFO);
1657 
1658  // save last configured group name/key
1659  saveGroupNameAndKey(theConfigurationTableGroup_,
1660  FSM_LAST_CONFIGURED_GROUP_ALIAS_FILE);
1661 
1662  __COUT__ << "Done configuring." << __E__;
1663  RunControlStateMachine::theProgressBar_.complete();
1664 } // end transitionConfiguring()
1665 
1666 //========================================================================================================================
1667 void GatewaySupervisor::transitionHalting(toolbox::Event::Reference e)
1668 {
1669  checkForAsyncError();
1670 
1671  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1672 
1673  makeSystemLogbookEntry("Run halting.");
1674 
1675  broadcastMessage(theStateMachine_.getCurrentMessage());
1676 } // end transitionHalting()
1677 
1678 //========================================================================================================================
1679 void GatewaySupervisor::transitionShuttingDown(toolbox::Event::Reference e)
1680 
1681 {
1682  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1683 
1684  RunControlStateMachine::theProgressBar_.step();
1685  makeSystemLogbookEntry("System shutting down.");
1686  RunControlStateMachine::theProgressBar_.step();
1687 
1688  // kill all non-gateway contexts
1689  GatewaySupervisor::launchStartOTSCommand(
1690  "OTS_APP_SHUTDOWN", CorePropertySupervisorBase::theConfigurationManager_);
1691  RunControlStateMachine::theProgressBar_.step();
1692 
1693  // important to give time for StartOTS script to recognize command (before user does
1694  // Startup again)
1695  for(int i = 0; i < 5; ++i)
1696  {
1697  sleep(1);
1698  RunControlStateMachine::theProgressBar_.step();
1699  }
1700 }
1701 
1702 //========================================================================================================================
1703 void GatewaySupervisor::transitionStartingUp(toolbox::Event::Reference e)
1704 
1705 {
1706  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1707 
1708  RunControlStateMachine::theProgressBar_.step();
1709  makeSystemLogbookEntry("System starting up.");
1710  RunControlStateMachine::theProgressBar_.step();
1711 
1712  // start all non-gateway contexts
1713  GatewaySupervisor::launchStartOTSCommand(
1714  "OTS_APP_STARTUP", CorePropertySupervisorBase::theConfigurationManager_);
1715  RunControlStateMachine::theProgressBar_.step();
1716 
1717  // important to give time for StartOTS script to recognize command and for apps to
1718  // instantiate things (before user does Initialize)
1719  for(int i = 0; i < 10; ++i)
1720  {
1721  sleep(1);
1722  RunControlStateMachine::theProgressBar_.step();
1723  }
1724 }
1725 
1726 //========================================================================================================================
1727 void GatewaySupervisor::transitionInitializing(toolbox::Event::Reference e)
1728 
1729 {
1730  __COUT__ << theStateMachine_.getCurrentStateName() << __E__;
1731 
1732  broadcastMessage(theStateMachine_.getCurrentMessage());
1733 
1734  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1735  __COUT__ << "Fsm current transition: "
1736  << theStateMachine_.getCurrentTransitionName(e->type()) << __E__;
1737  __COUT__ << "Fsm final state: "
1738  << theStateMachine_.getTransitionFinalStateName(e->type()) << __E__;
1739 }
1740 
1741 //========================================================================================================================
1742 void GatewaySupervisor::transitionPausing(toolbox::Event::Reference e)
1743 {
1744  checkForAsyncError();
1745 
1746  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1747 
1748  makeSystemLogbookEntry("Run pausing.");
1749 
1750  // the current message is not for Pause if its due to async failure
1751  if(RunControlStateMachine::asyncSoftFailureReceived_)
1752  {
1753  __COUT_ERR__ << "Broadcasting pause for async SOFT error!" << __E__;
1754  broadcastMessage(SOAPUtilities::makeSOAPMessageReference("Pause"));
1755  }
1756  else
1757  broadcastMessage(theStateMachine_.getCurrentMessage());
1758 }
1759 
1760 //========================================================================================================================
1761 void GatewaySupervisor::transitionResuming(toolbox::Event::Reference e)
1762 {
1763  if(RunControlStateMachine::asyncSoftFailureReceived_)
1764  {
1765  // clear async soft error
1766  __COUT_INFO__ << "Clearing async SOFT error!" << __E__;
1767  RunControlStateMachine::asyncSoftFailureReceived_ = false;
1768  }
1769 
1770  checkForAsyncError();
1771 
1772  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1773 
1774  makeSystemLogbookEntry("Run resuming.");
1775 
1776  broadcastMessage(theStateMachine_.getCurrentMessage());
1777 } // end transitionResuming()
1778 
1779 //========================================================================================================================
1780 void GatewaySupervisor::transitionStarting(toolbox::Event::Reference e)
1781 {
1782  if(RunControlStateMachine::asyncSoftFailureReceived_)
1783  {
1784  // clear async soft error
1785  __COUT_INFO__ << "Clearing async SOFT error!" << __E__;
1786  RunControlStateMachine::asyncSoftFailureReceived_ = false;
1787  }
1788 
1789  checkForAsyncError();
1790 
1791  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1792 
1793  SOAPParameters parameters("RunNumber");
1794  SOAPUtilities::receive(theStateMachine_.getCurrentMessage(), parameters);
1795 
1796  std::string runNumber = parameters.getValue("RunNumber");
1797  __COUTV__(runNumber);
1798 
1799  // check if configuration dump is enabled on configure transition
1800  {
1801  ConfigurationTree configLinkNode =
1802  CorePropertySupervisorBase::theConfigurationManager_->getSupervisorTableNode(
1803  supervisorContextUID_, supervisorApplicationUID_);
1804  if(!configLinkNode.isDisconnected())
1805  {
1806  try // errors in dump are not tolerated
1807  {
1808  bool dumpConfiguration = true;
1809  std::string dumpFilePath, dumpFileRadix, dumpFormat;
1810  try // for backwards compatibility
1811  {
1812  ConfigurationTree fsmLinkNode =
1813  configLinkNode.getNode("LinkToStateMachineTable")
1814  .getNode(activeStateMachineName_);
1815  dumpConfiguration =
1816  fsmLinkNode.getNode("EnableConfigurationDumpOnRunTransition")
1817  .getValue<bool>();
1818  dumpFilePath = fsmLinkNode.getNode("ConfigurationDumpOnRunFilePath")
1819  .getValue<std::string>();
1820  dumpFileRadix = fsmLinkNode.getNode("ConfigurationDumpOnRunFileRadix")
1821  .getValue<std::string>();
1822  dumpFormat = fsmLinkNode.getNode("ConfigurationDumpOnRunFormat")
1823  .getValue<std::string>();
1824  }
1825  catch(std::runtime_error& e)
1826  {
1827  __COUT_INFO__ << "FSM configuration dump Link disconnected." << __E__;
1828  dumpConfiguration = false;
1829  }
1830 
1831  if(dumpConfiguration)
1832  {
1833  // dump configuration
1834  CorePropertySupervisorBase::theConfigurationManager_
1835  ->dumpActiveConfiguration(dumpFilePath + "/" + dumpFileRadix +
1836  "_Run" + runNumber + "_" +
1837  std::to_string(time(0)) + ".dump",
1838  dumpFormat);
1839  }
1840  }
1841  catch(std::runtime_error& e)
1842  {
1843  __SS__ << "\nTransition to Running interrupted! There was an error "
1844  "identified "
1845  << "during the configuration dump attempt:\n\n " << e.what()
1846  << __E__;
1847  __COUT_ERR__ << "\n" << ss.str();
1848  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1849  return;
1850  }
1851  catch(...)
1852  {
1853  __SS__ << "\nTransition to Running interrupted! There was an error "
1854  "identified "
1855  << "during the configuration dump attempt.\n\n " << __E__;
1856  __COUT_ERR__ << "\n" << ss.str();
1857  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1858  return;
1859  }
1860  }
1861  }
1862 
1863  makeSystemLogbookEntry("Run " + runNumber + " starting.");
1864 
1865  broadcastMessage(theStateMachine_.getCurrentMessage());
1866 
1867  // save last started group name/key
1868  saveGroupNameAndKey(theConfigurationTableGroup_, FSM_LAST_STARTED_GROUP_ALIAS_FILE);
1869 }
1870 
1871 //========================================================================================================================
1872 void GatewaySupervisor::transitionStopping(toolbox::Event::Reference e)
1873 {
1874  checkForAsyncError();
1875 
1876  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1877 
1878  makeSystemLogbookEntry("Run stopping.");
1879 
1880  broadcastMessage(theStateMachine_.getCurrentMessage());
1881 }
1882 
1886 
1887 //========================================================================================================================
1888 // handleBroadcastMessageTarget
1889 // Sends message and gets reply
1890 // Handles sub-iterations at same target
1891 // if failure, THROW state machine exception
1892 // returns true if iterations are done, else false
1893 bool GatewaySupervisor::handleBroadcastMessageTarget(const SupervisorInfo& appInfo,
1894  xoap::MessageReference message,
1895  const std::string& command,
1896  const unsigned int& iteration,
1897  std::string& reply,
1898  unsigned int threadIndex)
1899 {
1900  unsigned int subIteration = 0; // reset for next subIteration loop
1901  bool subIterationsDone = false;
1902  bool iterationsDone = true;
1903 
1904  while(!subIterationsDone) // start subIteration handling loop
1905  {
1906  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1907  << "Supervisor instance = '" << appInfo.getName()
1908  << "' [LID=" << appInfo.getId() << "] in Context '"
1909  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
1910  << "] Command = " << command << __E__;
1911 
1912  checkForAsyncError();
1913 
1914  subIterationsDone = true;
1915  RunControlStateMachine::theProgressBar_.step();
1916 
1917  // add subIteration index to message
1918  if(subIteration)
1919  {
1920  SOAPParameters parameters;
1921  parameters.addParameter("subIterationIndex", subIteration);
1922  SOAPUtilities::addParameters(message, parameters);
1923  }
1924 
1925  if(iteration || subIteration)
1926  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1927  << "Adding iteration parameters " << iteration << "." << subIteration
1928  << __E__;
1929 
1930  RunControlStateMachine::theProgressBar_.step();
1931 
1932  if(iteration == 0 && subIteration == 0)
1933  {
1934  for(unsigned int j = 0; j < 4; ++j)
1935  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1936  << "Sending message to Supervisor " << appInfo.getName()
1937  << " [LID=" << appInfo.getId() << "]: " << command << __E__;
1938  }
1939  else // else this not the first time through the supervisors
1940  {
1941  if(subIteration == 0)
1942  {
1943  for(unsigned int j = 0; j < 4; ++j)
1944  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1945  << "Sending message to Supervisor " << appInfo.getName()
1946  << " [LID=" << appInfo.getId() << "]: " << command
1947  << " (iteration: " << iteration << ")" << __E__;
1948  }
1949  else
1950  {
1951  for(unsigned int j = 0; j < 4; ++j)
1952  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1953  << "Sending message to Supervisor " << appInfo.getName()
1954  << " [LID=" << appInfo.getId() << "]: " << command
1955  << " (iteration: " << iteration
1956  << ", sub-iteration: " << subIteration << ")" << __E__;
1957  }
1958  }
1959 
1960  {
1961  // add the message index
1962  SOAPParameters parameters;
1963  { // mutex scope
1964  std::lock_guard<std::mutex> lock(broadcastCommandMessageIndexMutex_);
1965  parameters.addParameter("commandId", broadcastCommandMessageIndex_++);
1966  } // end mutex scope
1967  SOAPUtilities::addParameters(message, parameters);
1968  }
1969 
1970  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1971  << "Sending... \t" << SOAPUtilities::translate(message) << std::endl;
1972 
1973  try // attempt transmit of transition command
1974  {
1975  reply = send(appInfo.getDescriptor(), message);
1976  }
1977  catch(const xdaq::exception::Exception& e) // due to xoap send failure
1978  {
1979  // do not kill whole system if xdaq xoap failure
1980  __SS__ << "Error! Gateway Supervisor can NOT " << command
1981  << " Supervisor instance = '" << appInfo.getName()
1982  << "' [LID=" << appInfo.getId() << "] in Context '"
1983  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
1984  << "].\n\n"
1985  << "Xoap message failure. Did the target Supervisor crash? Try "
1986  "re-initializing or restarting otsdaq."
1987  << __E__;
1988  __COUT_ERR__ << ss.str();
1989  __MOUT_ERR__ << ss.str();
1990 
1991  try
1992  {
1993  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1994  << "Try again.." << __E__;
1995 
1996  {
1997  // add a second try parameter flag
1998  SOAPParameters parameters;
1999  parameters.addParameter("retransmission", "1");
2000  SOAPUtilities::addParameters(message, parameters);
2001  }
2002 
2003  {
2004  // add the message index
2005  SOAPParameters parameters;
2006  { // mutex scope
2007  std::lock_guard<std::mutex> lock(
2008  broadcastCommandMessageIndexMutex_);
2009  parameters.addParameter("commandId",
2010  broadcastCommandMessageIndex_++);
2011  } // end mutex scope
2012  SOAPUtilities::addParameters(message, parameters);
2013  }
2014 
2015  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2016  << "Re-Sending... " << SOAPUtilities::translate(message)
2017  << std::endl;
2018 
2019  reply = send(appInfo.getDescriptor(), message);
2020  }
2021  catch(const xdaq::exception::Exception& e) // due to xoap send failure
2022  {
2023  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2024  << "Second try failed.." << __E__;
2025  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
2026  }
2027  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2028  << "2nd try passed.." << __E__;
2029  } // end send catch
2030 
2031  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2032  << "Reply received = " << reply << __E__;
2033 
2034  if((reply != command + "Done") && (reply != command + "Response") &&
2035  (reply != command + "Iterate") && (reply != command + "SubIterate"))
2036  {
2037  __SS__ << "Error! Gateway Supervisor can NOT " << command
2038  << " Supervisor instance = '" << appInfo.getName()
2039  << "' [LID=" << appInfo.getId() << "] in Context '"
2040  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
2041  << "].\n\n"
2042  << reply;
2043  __COUT_ERR__ << ss.str() << __E__;
2044  __MOUT_ERR__ << ss.str() << __E__;
2045 
2046  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2047  << "Getting error message..." << __E__;
2048  try
2049  {
2050  xoap::MessageReference errorMessage =
2051  sendWithSOAPReply(appInfo.getDescriptor(),
2052  SOAPUtilities::makeSOAPMessageReference(
2053  "StateMachineErrorMessageRequest"));
2054  SOAPParameters parameters;
2055  parameters.addParameter("ErrorMessage");
2056  SOAPUtilities::receive(errorMessage, parameters);
2057 
2058  std::string error = parameters.getValue("ErrorMessage");
2059  if(error == "")
2060  {
2061  std::stringstream err;
2062  err << "Unknown error from Supervisor instance = '"
2063  << appInfo.getName() << "' [LID=" << appInfo.getId()
2064  << "] in Context '" << appInfo.getContextName()
2065  << "' [URL=" << appInfo.getURL()
2066  << "]. If the problem persists or is repeatable, please notify "
2067  "admins.\n\n";
2068  error = err.str();
2069  }
2070 
2071  __SS__ << "Received error from Supervisor instance = '"
2072  << appInfo.getName() << "' [LID=" << appInfo.getId()
2073  << "] in Context '" << appInfo.getContextName()
2074  << "' [URL=" << appInfo.getURL()
2075  << "].\n\n Error Message = " << error << __E__;
2076 
2077  __COUT_ERR__ << ss.str() << __E__;
2078  __MOUT_ERR__ << ss.str() << __E__;
2079 
2080  if(command == "Error")
2081  return true; // do not throw exception and exit loop if informing all
2082  // apps about error
2083  // else throw exception and go into Error
2084  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
2085  }
2086  catch(const xdaq::exception::Exception& e) // due to xoap send failure
2087  {
2088  // do not kill whole system if xdaq xoap failure
2089  __SS__ << "Error! Gateway Supervisor failed to read error message from "
2090  "Supervisor instance = '"
2091  << appInfo.getName() << "' [LID=" << appInfo.getId()
2092  << "] in Context '" << appInfo.getContextName()
2093  << "' [URL=" << appInfo.getURL() << "].\n\n"
2094  << "Xoap message failure. Did the target Supervisor crash? Try "
2095  "re-initializing or restarting otsdaq."
2096  << __E__;
2097  __COUT_ERR__ << ss.str();
2098  __MOUT_ERR__ << ss.str();
2099  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
2100  }
2101  } // end error response handling
2102  else if(reply == command + "Iterate")
2103  {
2104  // when 'Working' this front-end is expecting
2105  // to get the same command again with an incremented iteration index
2106  // after all other front-ends see the same iteration index, and all
2107  // front-ends with higher priority see the incremented iteration index.
2108 
2109  iterationsDone = false;
2110  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2111  << "Supervisor instance = '" << appInfo.getName()
2112  << "' [LID=" << appInfo.getId() << "] in Context '"
2113  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
2114  << "] flagged for another iteration to " << command
2115  << "... (iteration: " << iteration << ")" << __E__;
2116 
2117  } // end still working response handling
2118  else if(reply == command + "SubIterate")
2119  {
2120  // when 'Working' this front-end is expecting
2121  // to get the same command again with an incremented sub-iteration index
2122  // without any other front-ends taking actions or seeing the sub-iteration
2123  // index.
2124 
2125  subIterationsDone = false;
2126  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2127  << "Supervisor instance = '" << appInfo.getName()
2128  << "' [LID=" << appInfo.getId() << "] in Context '"
2129  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
2130  << "] flagged for another sub-iteration to " << command
2131  << "... (iteration: " << iteration
2132  << ", sub-iteration: " << subIteration << ")" << __E__;
2133  }
2134  else // else success response
2135  {
2136  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2137  << "Supervisor instance = '" << appInfo.getName()
2138  << "' [LID=" << appInfo.getId() << "] in Context '"
2139  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
2140  << "] was " << command << "'d correctly!" << __E__;
2141  }
2142 
2143  if(subIteration)
2144  __COUT__ << "Broadcast thread " << threadIndex << "\t"
2145  << "Completed sub-iteration: " << subIteration << __E__;
2146  ++subIteration;
2147 
2148  } // end subIteration handling loop
2149 
2150  return iterationsDone;
2151 
2152 } // end handleBroadcastMessageTarget()
2153 
2154 //========================================================================================================================
2155 // broadcastMessageThread
2156 // Sends transition command message and gets reply
2157 // if failure, THROW
2158 void GatewaySupervisor::broadcastMessageThread(
2159  GatewaySupervisor* supervisorPtr,
2160  GatewaySupervisor::BroadcastThreadStruct* threadStruct)
2161 {
2162  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
2163  << "starting..." << __E__;
2164 
2165  while(!threadStruct->exitThread_)
2166  {
2167  // sleep to give time to main thread to dole out work
2168  usleep(1000 /* 1ms */);
2169 
2170  // take lock for remainder of scope
2171  std::lock_guard<std::mutex> lock(threadStruct->threadMutex);
2172  if(threadStruct->workToDo_)
2173  {
2174  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
2175  << "starting work..." << __E__;
2176 
2177  try
2178  {
2179  if(supervisorPtr->handleBroadcastMessageTarget(
2180  threadStruct->getAppInfo(),
2181  threadStruct->getMessage(),
2182  threadStruct->getCommand(),
2183  threadStruct->getIteration(),
2184  threadStruct->getReply(),
2185  threadStruct->threadIndex_))
2186  threadStruct->getIterationsDone() = true;
2187  }
2188  catch(toolbox::fsm::exception::Exception const& e)
2189  {
2190  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
2191  << "going into error: " << e.what() << __E__;
2192 
2193  threadStruct->getReply() = e.what();
2194  threadStruct->error_ = true;
2195  threadStruct->workToDo_ = false;
2196  threadStruct->working_ = false; // indicate exiting
2197  return;
2198  }
2199 
2200  if(!threadStruct->getIterationsDone())
2201  {
2202  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
2203  << "flagged for another iteration." << __E__;
2204 
2205  // set global iterationsDone
2206  std::lock_guard<std::mutex> lock(
2207  supervisorPtr->broadcastIterationsDoneMutex_);
2208  supervisorPtr->broadcastIterationsDone_ = false;
2209  }
2210 
2211  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
2212  << "done with work." << __E__;
2213 
2214  threadStruct->workToDo_ = false;
2215  } // end work
2216 
2217  } // end primary while loop
2218 
2219  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
2220  << "exited." << __E__;
2221  threadStruct->working_ = false; // indicate exiting
2222 } // end broadcastMessageThread()
2223 
2224 //========================================================================================================================
2225 // broadcastMessage
2226 // Broadcast state transition to all xdaq Supervisors.
2227 // - Transition in order of priority as given by AllSupervisorInfo
2228 // Update Supervisor Info based on result of transition.
2229 void GatewaySupervisor::broadcastMessage(xoap::MessageReference message)
2230 {
2231  RunControlStateMachine::theProgressBar_.step();
2232 
2233  // transition of Gateway Supervisor is assumed successful so update status
2234  allSupervisorInfo_.setSupervisorStatus(this, theStateMachine_.getCurrentStateName());
2235 
2236  std::string command = SOAPUtilities::translate(message).getCommand();
2237 
2238  std::string reply;
2239  broadcastIterationsDone_ = false;
2240  bool assignedJob;
2241 
2242  std::vector<std::vector<const SupervisorInfo*>> orderedSupervisors;
2243 
2244  try
2245  {
2246  orderedSupervisors = allSupervisorInfo_.getOrderedSupervisorDescriptors(command);
2247  }
2248  catch(const std::runtime_error& e)
2249  {
2250  __SS__ << "Error getting supervisor priority. Was there a change in the context?"
2251  << " Remember, if the context was changed, it is safest to relaunch "
2252  "StartOTS.sh. "
2253  << e.what() << __E__;
2254  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
2255  }
2256 
2257  RunControlStateMachine::theProgressBar_.step();
2258 
2259  // std::vector<std::vector<uint8_t/*bool*/>> supervisorIterationsDone; //Note: can not
2260  // use bool because std::vector does not allow access by reference of type bool
2261  GatewaySupervisor::BroadcastMessageIterationsDoneStruct supervisorIterationsDone;
2262 
2263  // initialize to false (not done)
2264  for(const auto& vectorAtPriority : orderedSupervisors)
2265  supervisorIterationsDone.push(
2266  vectorAtPriority.size()); // push_back(
2267  // std::vector<uint8_t>(vectorAtPriority.size(),
2268  // false /*initial value*/));
2269 
2270  unsigned int iteration = 0;
2271  unsigned int subIteration;
2272  unsigned int iterationBreakpoint;
2273 
2274  // send command to all supervisors (for multiple iterations) until all are done
2275 
2276  // make a copy of the message to use as starting point for iterations
2277  xoap::MessageReference originalMessage =
2278  SOAPUtilities::makeSOAPMessageReference(SOAPUtilities::translate(message));
2279 
2280  __COUT__ << "=========> Broadcasting state machine command = " << command << __E__;
2281 
2282  unsigned int numberOfThreads = 1;
2283 
2284  try
2285  {
2286  numberOfThreads = CorePropertySupervisorBase::getSupervisorTableNode()
2287  .getNode("NumberOfStateMachineBroadcastThreads")
2288  .getValue<unsigned int>();
2289  }
2290  catch(...)
2291  {
2292  // ignore error for backwards compatibility
2293  __COUT__ << "Number of threads not in configuration, so defaulting to "
2294  << numberOfThreads << __E__;
2295  }
2296 
2297  // Note: if 1 thread, then create no threads
2298  // i.e. only create threads if 2 or more.
2299  if(numberOfThreads == 1)
2300  numberOfThreads = 0;
2301 
2302  __COUTV__(numberOfThreads);
2303 
2304  std::vector<GatewaySupervisor::BroadcastThreadStruct> broadcastThreadStructs(
2305  numberOfThreads);
2306 
2307  // only launch threads if more than 1
2308  // if 1, just use main thread
2309  for(unsigned int i = 0; i < numberOfThreads; ++i)
2310  {
2311  broadcastThreadStructs[i].threadIndex_ = i;
2312 
2313  std::thread(
2314  [](GatewaySupervisor* supervisorPtr,
2315  GatewaySupervisor::BroadcastThreadStruct* threadStruct) {
2316  GatewaySupervisor::broadcastMessageThread(supervisorPtr, threadStruct);
2317  },
2318  this,
2319  &broadcastThreadStructs[i])
2320  .detach();
2321  } // end broadcast thread creation loop
2322 
2323  RunControlStateMachine::theProgressBar_.step();
2324 
2325  try
2326  {
2327  //:::::::::::::::::::::::::::::::::::::::::::::::::::::
2328  // Send a SOAP message to every Supervisor in order by priority
2329  do // while !iterationsDone
2330  {
2331  broadcastIterationsDone_ = true;
2332 
2333  { // start mutex scope
2334  std::lock_guard<std::mutex> lock(broadcastIterationBreakpointMutex_);
2335  iterationBreakpoint = broadcastIterationBreakpoint_; // get breakpoint
2336  } // end mutex scope
2337 
2338  if(iterationBreakpoint < (unsigned int)-1)
2339  __COUT__ << "Iteration breakpoint currently is " << iterationBreakpoint
2340  << __E__;
2341  if(iteration >= iterationBreakpoint)
2342  {
2343  broadcastIterationsDone_ = false;
2344  __COUT__ << "Waiting at transition breakpoint - iteration = " << iteration
2345  << __E__;
2346  usleep(5 * 1000 * 1000 /*5 s*/);
2347  continue; // wait until breakpoint moved
2348  }
2349 
2350  if(iteration)
2351  __COUT__ << "Starting iteration: " << iteration << __E__;
2352 
2353  for(unsigned int i = 0; i < supervisorIterationsDone.size(); ++i)
2354  {
2355  for(unsigned int j = 0; j < supervisorIterationsDone.size(i); ++j)
2356  {
2357  checkForAsyncError();
2358 
2359  if(supervisorIterationsDone[i][j])
2360  continue; // skip if supervisor is already done
2361 
2362  const SupervisorInfo& appInfo = *(orderedSupervisors[i][j]);
2363 
2364  // re-acquire original message
2365  message = SOAPUtilities::makeSOAPMessageReference(
2366  SOAPUtilities::translate(originalMessage));
2367 
2368  // add iteration index to message
2369  if(iteration)
2370  {
2371  // add the iteration index as a parameter to message
2372  SOAPParameters parameters;
2373  parameters.addParameter("iterationIndex", iteration);
2374  SOAPUtilities::addParameters(message, parameters);
2375  }
2376 
2377  if(numberOfThreads)
2378  {
2379  // schedule message to first open thread
2380  assignedJob = false;
2381  do
2382  {
2383  for(unsigned int k = 0; k < numberOfThreads; ++k)
2384  {
2385  if(!broadcastThreadStructs[k].workToDo_)
2386  {
2387  // found our thread!
2388  assignedJob = true;
2389  __COUT__ << "Giving work to thread " << k << __E__;
2390 
2391  std::lock_guard<std::mutex> lock(
2392  broadcastThreadStructs[k].threadMutex);
2393  broadcastThreadStructs[k].setMessage(
2394  appInfo,
2395  message,
2396  command,
2397  iteration,
2398  supervisorIterationsDone[i][j]);
2399 
2400  break;
2401  }
2402  } // end thread assigning search
2403 
2404  if(!assignedJob)
2405  {
2406  __COUT__ << "No free broadcast threads, "
2407  << "waiting for an available thread..." << __E__;
2408  usleep(100 * 1000 /*100 ms*/);
2409  }
2410  } while(!assignedJob);
2411  }
2412  else // no thread
2413  {
2414  if(handleBroadcastMessageTarget(
2415  appInfo, message, command, iteration, reply))
2416  supervisorIterationsDone[i][j] = true;
2417  else
2418  broadcastIterationsDone_ = false;
2419  }
2420 
2421  } // end supervisors at same priority broadcast loop
2422 
2423  // before proceeding to next priority,
2424  // make sure all threads have completed
2425  if(numberOfThreads)
2426  {
2427  __COUT__
2428  << "Done with priority level. Waiting for threads to finish..."
2429  << __E__;
2430  bool done;
2431  do
2432  {
2433  done = true;
2434  for(unsigned int i = 0; i < numberOfThreads; ++i)
2435  if(broadcastThreadStructs[i].workToDo_)
2436  {
2437  done = false;
2438  __COUT__ << "Still waiting on thread " << i << "..."
2439  << __E__;
2440  usleep(100 * 1000 /*100ms*/);
2441  break;
2442  }
2443  else if(broadcastThreadStructs[i].error_)
2444  {
2445  __COUT__ << "Found thread in error! Throwing state "
2446  "machine error: "
2447  << broadcastThreadStructs[i].getReply() << __E__;
2448  XCEPT_RAISE(toolbox::fsm::exception::Exception,
2449  broadcastThreadStructs[i].getReply());
2450  }
2451  } while(!done);
2452  __COUT__ << "All threads done with priority level work." << __E__;
2453  } // end thread complete verification
2454 
2455  } // end supervisor broadcast loop for each priority
2456 
2457  // if (!proceed)
2458  // {
2459  // __COUT__ << "Breaking out of primary loop." << __E__;
2460  // break;
2461  // }
2462 
2463  if(iteration || !broadcastIterationsDone_)
2464  __COUT__ << "Completed iteration: " << iteration << __E__;
2465  ++iteration;
2466 
2467  } while(!broadcastIterationsDone_);
2468 
2469  RunControlStateMachine::theProgressBar_.step();
2470  } // end main transition broadcast try
2471  catch(...)
2472  {
2473  __COUT__ << "Exception caught, exiting broadcast threads..." << __E__;
2474 
2475  // attempt to exit threads
2476  // The threads should already be done with all work.
2477  // If broadcastMessage scope ends, then the
2478  // thread struct will be destructed, and the thread will
2479  // crash on next access attempt (thought we probably do not care).
2480  for(unsigned int i = 0; i < numberOfThreads; ++i)
2481  broadcastThreadStructs[i].exitThread_ = true;
2482  usleep(100 * 1000 /*100ms*/); // sleep for exit time
2483 
2484  throw; // re-throw
2485  }
2486 
2487  if(numberOfThreads)
2488  {
2489  __COUT__ << "All transitions completed. Wrapping up, exiting broadcast threads..."
2490  << __E__;
2491 
2492  // attempt to exit threads
2493  // The threads should already be done with all work.
2494  // If broadcastMessage scope ends, then the
2495  // thread struct will be destructed, and the thread will
2496  // crash on next access attempt (when the thread crashes, the whole context
2497  // crashes).
2498  for(unsigned int i = 0; i < numberOfThreads; ++i)
2499  broadcastThreadStructs[i].exitThread_ = true;
2500  usleep(100 * 1000 /*100ms*/); // sleep for exit time
2501  }
2502 
2503  __COUT__ << "Broadcast complete." << __E__;
2504 } // end broadcastMessage()
2505 
2506 //========================================================================================================================
2507 void GatewaySupervisor::wait(int milliseconds, std::string who) const
2508 {
2509  for(int s = 1; s <= milliseconds; s++)
2510  {
2511  usleep(1000);
2512 
2513  if(s % 100 == 0)
2514  __COUT__ << s << " msecs " << who << __E__;
2515  }
2516 }
2517 
2518 //========================================================================================================================
2519 // LoginRequest
2520 // handles all users login/logout actions from web GUI.
2521 // NOTE: there are two ways for a user to be logged out: timeout or manual logout
2522 // System logbook messages are generated for login and logout
2523 void GatewaySupervisor::loginRequest(xgi::Input* in, xgi::Output* out)
2524 {
2525  __COUT__ << "Start" << __E__;
2526  cgicc::Cgicc cgi(in);
2527  std::string Command = CgiDataUtilities::getData(cgi, "RequestType");
2528  __COUT__ << "*** Login RequestType = " << Command << __E__;
2529 
2530  // RequestType Commands:
2531  // login
2532  // sessionId
2533  // checkCookie
2534  // logout
2535 
2536  // always cleanup expired entries and get a vector std::string of logged out users
2537  std::vector<std::string> loggedOutUsernames;
2538  theWebUsers_.cleanupExpiredEntries(&loggedOutUsernames);
2539  for(unsigned int i = 0; i < loggedOutUsernames.size();
2540  ++i) // Log logout for logged out users
2541  makeSystemLogbookEntry(loggedOutUsernames[i] + " login timed out.");
2542 
2543  if(Command == "sessionId")
2544  {
2545  // When client loads page, client submits unique user id and receives random
2546  // sessionId from server Whenever client submits user name and password it is
2547  // jumbled by sessionId when sent to server and sent along with UUID. Server uses
2548  // sessionId to unjumble.
2549  //
2550  // Server maintains list of active sessionId by UUID
2551  // sessionId expires after set time if no login attempt (e.g. 5 minutes)
2552  std::string uuid = CgiDataUtilities::postData(cgi, "uuid");
2553 
2554  std::string sid = theWebUsers_.createNewLoginSession(
2555  uuid, cgi.getEnvironment().getRemoteAddr() /* ip */);
2556 
2557  // __COUT__ << "uuid = " << uuid << __E__;
2558  // __COUT__ << "SessionId = " << sid.substr(0, 10) << __E__;
2559  *out << sid;
2560  }
2561  else if(Command == "checkCookie")
2562  {
2563  uint64_t uid;
2564  std::string uuid;
2565  std::string jumbledUser;
2566  std::string cookieCode;
2567 
2568  // If client has a cookie, client submits cookie and username, jumbled, to see if
2569  // cookie and user are still active if active, valid cookie code is returned
2570  // and name to display, in XML
2571  // if not, return 0
2572  // params:
2573  // uuid - unique user id, to look up sessionId
2574  // ju - jumbled user name
2575  // CookieCode - cookie code to check
2576 
2577  uuid = CgiDataUtilities::postData(cgi, "uuid");
2578  jumbledUser = CgiDataUtilities::postData(cgi, "ju");
2579  cookieCode = CgiDataUtilities::postData(cgi, "cc");
2580 
2581  // __COUT__ << "uuid = " << uuid << __E__;
2582  // __COUT__ << "Cookie Code = " << cookieCode.substr(0, 10) << __E__;
2583  // __COUT__ << "jumbledUser = " << jumbledUser.substr(0, 10) << __E__;
2584 
2585  // If cookie code is good, then refresh and return with display name, else return
2586  // 0 as CookieCode value
2587  uid = theWebUsers_.isCookieCodeActiveForLogin(
2588  uuid,
2589  cookieCode,
2590  jumbledUser); // after call jumbledUser holds displayName on success
2591 
2592  if(uid == theWebUsers_.NOT_FOUND_IN_DATABASE)
2593  {
2594  __COUT__ << "cookieCode invalid" << __E__;
2595  jumbledUser = ""; // clear display name if failure
2596  cookieCode = "0"; // clear cookie code if failure
2597  }
2598  else
2599  __COUT__ << "cookieCode is good." << __E__;
2600 
2601  // return xml holding cookie code and display name
2602  HttpXmlDocument xmldoc(cookieCode, jumbledUser);
2603 
2604  theWebUsers_.insertSettingsForUser(uid, &xmldoc); // insert settings
2605 
2606  xmldoc.outputXmlDocument((std::ostringstream*)out);
2607  }
2608  else if(Command == "login")
2609  {
2610  // If login attempt or create account, jumbled user and pw are submitted
2611  // if successful, valid cookie code and display name returned.
2612  // if not, return 0
2613  // params:
2614  // uuid - unique user id, to look up sessionId
2615  // nac - new account code for first time logins
2616  // ju - jumbled user name
2617  // jp - jumbled password
2618 
2619  std::string uuid = CgiDataUtilities::postData(cgi, "uuid");
2620  std::string newAccountCode = CgiDataUtilities::postData(cgi, "nac");
2621  std::string jumbledUser = CgiDataUtilities::postData(cgi, "ju");
2622  std::string jumbledPw = CgiDataUtilities::postData(cgi, "jp");
2623 
2624  // __COUT__ << "jumbledUser = " << jumbledUser.substr(0, 10) << __E__;
2625  // __COUT__ << "jumbledPw = " << jumbledPw.substr(0, 10) << __E__;
2626  // __COUT__ << "uuid = " << uuid << __E__;
2627  // __COUT__ << "nac =-" << newAccountCode << "-" << __E__;
2628 
2629  uint64_t uid = theWebUsers_.attemptActiveSession(
2630  uuid,
2631  jumbledUser,
2632  jumbledPw,
2633  newAccountCode,
2634  cgi.getEnvironment()
2635  .getRemoteAddr()); // after call jumbledUser holds displayName on success
2636 
2637  if(uid == theWebUsers_.NOT_FOUND_IN_DATABASE)
2638  {
2639  __COUT__ << "Login invalid." << __E__;
2640  jumbledUser = ""; // clear display name if failure
2641  if(newAccountCode != "1") // indicates uuid not found
2642  newAccountCode = "0"; // clear cookie code if failure
2643  }
2644  else // Log login in logbook for active experiment
2645  makeSystemLogbookEntry(theWebUsers_.getUsersUsername(uid) + " logged in.");
2646 
2647  //__COUT__ << "new cookieCode = " << newAccountCode.substr(0, 10) << __E__;
2648 
2649  HttpXmlDocument xmldoc(newAccountCode, jumbledUser);
2650 
2651  theWebUsers_.insertSettingsForUser(uid, &xmldoc); // insert settings
2652 
2653  // insert active session count for user
2654 
2655  if(uid != theWebUsers_.NOT_FOUND_IN_DATABASE)
2656  {
2657  uint64_t asCnt = theWebUsers_.getActiveSessionCountForUser(uid) -
2658  1; // subtract 1 to remove just started session from count
2659  char asStr[20];
2660  sprintf(asStr, "%lu", asCnt);
2661  xmldoc.addTextElementToData("user_active_session_count", asStr);
2662  }
2663 
2664  xmldoc.outputXmlDocument((std::ostringstream*)out);
2665  }
2666  else if(Command == "cert")
2667  {
2668  // If login attempt or create account, jumbled user and pw are submitted
2669  // if successful, valid cookie code and display name returned.
2670  // if not, return 0
2671  // params:
2672  // uuid - unique user id, to look up sessionId
2673  // nac - new account code for first time logins
2674  // ju - jumbled user name
2675  // jp - jumbled password
2676 
2677  std::string uuid = CgiDataUtilities::postData(cgi, "uuid");
2678  std::string jumbledEmail =
2679  cgicc::form_urldecode(CgiDataUtilities::getData(cgi, "httpsUser"));
2680  std::string username = "";
2681  std::string cookieCode = "";
2682 
2683  // __COUT__ << "CERTIFICATE LOGIN REUEST RECEVIED!!!" << __E__;
2684  // __COUT__ << "jumbledEmail = " << jumbledEmail << __E__;
2685  // __COUT__ << "uuid = " << uuid << __E__;
2686 
2687  uint64_t uid = theWebUsers_.attemptActiveSessionWithCert(
2688  uuid,
2689  jumbledEmail,
2690  cookieCode,
2691  username,
2692  cgi.getEnvironment()
2693  .getRemoteAddr()); // after call jumbledUser holds displayName on success
2694 
2695  if(uid == theWebUsers_.NOT_FOUND_IN_DATABASE)
2696  {
2697  __COUT__ << "cookieCode invalid" << __E__;
2698  jumbledEmail = ""; // clear display name if failure
2699  if(cookieCode != "1") // indicates uuid not found
2700  cookieCode = "0"; // clear cookie code if failure
2701  }
2702  else // Log login in logbook for active experiment
2703  makeSystemLogbookEntry(theWebUsers_.getUsersUsername(uid) + " logged in.");
2704 
2705  //__COUT__ << "new cookieCode = " << cookieCode.substr(0, 10) << __E__;
2706 
2707  HttpXmlDocument xmldoc(cookieCode, jumbledEmail);
2708 
2709  theWebUsers_.insertSettingsForUser(uid, &xmldoc); // insert settings
2710 
2711  // insert active session count for user
2712 
2713  if(uid != theWebUsers_.NOT_FOUND_IN_DATABASE)
2714  {
2715  uint64_t asCnt = theWebUsers_.getActiveSessionCountForUser(uid) -
2716  1; // subtract 1 to remove just started session from count
2717  char asStr[20];
2718  sprintf(asStr, "%lu", asCnt);
2719  xmldoc.addTextElementToData("user_active_session_count", asStr);
2720  }
2721 
2722  xmldoc.outputXmlDocument((std::ostringstream*)out);
2723  }
2724  else if(Command == "logout")
2725  {
2726  std::string cookieCode = CgiDataUtilities::postData(cgi, "CookieCode");
2727  std::string logoutOthers = CgiDataUtilities::postData(cgi, "LogoutOthers");
2728 
2729  // __COUT__ << "Cookie Code = " << cookieCode.substr(0, 10) << __E__;
2730  // __COUT__ << "logoutOthers = " << logoutOthers << __E__;
2731 
2732  uint64_t uid; // get uid for possible system logbook message
2733  if(theWebUsers_.cookieCodeLogout(cookieCode,
2734  logoutOthers == "1",
2735  &uid,
2736  cgi.getEnvironment().getRemoteAddr()) !=
2737  theWebUsers_.NOT_FOUND_IN_DATABASE) // user logout
2738  {
2739  // if did some logging out, check if completely logged out
2740  // if so, system logbook message should be made.
2741  if(!theWebUsers_.isUserIdActive(uid))
2742  makeSystemLogbookEntry(theWebUsers_.getUsersUsername(uid) +
2743  " logged out.");
2744  }
2745  }
2746  else
2747  {
2748  __COUT__ << "Invalid Command" << __E__;
2749  *out << "0";
2750  }
2751 
2752  __COUT__ << "Done" << __E__;
2753 } // end loginRequest()
2754 
2755 //========================================================================================================================
2756 void GatewaySupervisor::tooltipRequest(xgi::Input* in, xgi::Output* out)
2757 {
2758  __COUT__ << "Start" << __E__;
2759  cgicc::Cgicc cgi(in);
2760 
2761  std::string Command = CgiDataUtilities::getData(cgi, "RequestType");
2762  __COUT__ << "Tooltip RequestType = " << Command << __E__;
2763 
2764  //**** start LOGIN GATEWAY CODE ***//
2765  // If TRUE, cookie code is good, and refreshed code is in cookieCode, also pointers
2766  // optionally for uint8_t userPermissions, uint64_t uid Else, error message is
2767  // returned in cookieCode Notes: cookie code not refreshed if RequestType =
2768  // getSystemMessages
2769  std::string cookieCode = CgiDataUtilities::postData(cgi, "CookieCode");
2770  uint64_t uid;
2771 
2772  if(!theWebUsers_.cookieCodeIsActiveForRequest(
2773  cookieCode, 0 /*userPermissions*/, &uid, "0" /*dummy ip*/, false /*refresh*/))
2774  {
2775  *out << cookieCode;
2776  return;
2777  }
2778 
2779  //**** end LOGIN GATEWAY CODE ***//
2780 
2781  HttpXmlDocument xmldoc(cookieCode);
2782 
2783  if(Command == "check")
2784  {
2785  WebUsers::tooltipCheckForUsername(theWebUsers_.getUsersUsername(uid),
2786  &xmldoc,
2787  CgiDataUtilities::getData(cgi, "srcFile"),
2788  CgiDataUtilities::getData(cgi, "srcFunc"),
2789  CgiDataUtilities::getData(cgi, "srcId"));
2790  }
2791  else if(Command == "setNeverShow")
2792  {
2793  WebUsers::tooltipSetNeverShowForUsername(
2794  theWebUsers_.getUsersUsername(uid),
2795  &xmldoc,
2796  CgiDataUtilities::getData(cgi, "srcFile"),
2797  CgiDataUtilities::getData(cgi, "srcFunc"),
2798  CgiDataUtilities::getData(cgi, "srcId"),
2799  CgiDataUtilities::getData(cgi, "doNeverShow") == "1" ? true : false,
2800  CgiDataUtilities::getData(cgi, "temporarySilence") == "1" ? true : false);
2801  }
2802  else
2803  __COUT__ << "Command Request, " << Command << ", not recognized." << __E__;
2804 
2805  xmldoc.outputXmlDocument((std::ostringstream*)out, false, true);
2806 
2807  __COUT__ << "Done" << __E__;
2808 } // end tooltipRequest
2809 
2810 //========================================================================================================================
2811 // setSupervisorPropertyDefaults
2812 // override to set defaults for supervisor property values (before user settings
2813 // override)
2814 void GatewaySupervisor::setSupervisorPropertyDefaults()
2815 {
2816  CorePropertySupervisorBase::setSupervisorProperty(
2817  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
2818  std::string() + "*=1 | gatewayLaunchOTS=-1 | gatewayLaunchWiz=-1");
2819 }
2820 
2821 //========================================================================================================================
2822 // forceSupervisorPropertyValues
2823 // override to force supervisor property values (and ignore user settings)
2824 void GatewaySupervisor::forceSupervisorPropertyValues()
2825 {
2826  // note used by these handlers:
2827  // request()
2828  // stateMachineXgiHandler() -- prepend StateMachine to request type
2829 
2830  CorePropertySupervisorBase::setSupervisorProperty(
2831  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
2832  "getSystemMessages | getCurrentState | getIterationPlanStatus");
2833  CorePropertySupervisorBase::setSupervisorProperty(
2834  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes,
2835  "gatewayLaunchOTS | gatewayLaunchWiz");
2836  // CorePropertySupervisorBase::setSupervisorProperty(CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NeedUsernameRequestTypes,
2837  // "StateMachine*"); //for all stateMachineXgiHandler requests
2838 }
2839 
2840 //========================================================================================================================
2841 void GatewaySupervisor::request(xgi::Input* in, xgi::Output* out)
2842 {
2843  //__COUT__ << "Start" << __E__;
2844 
2845  // for simplicity assume all commands should be mutually exclusive with iterator
2846  // thread state machine accesses (really should just be careful with
2847  // RunControlStateMachine access)
2848  if(VERBOSE_MUTEX)
2849  __COUT__ << "Waiting for FSM access" << __E__;
2850  std::lock_guard<std::mutex> lock(stateMachineAccessMutex_);
2851  if(VERBOSE_MUTEX)
2852  __COUT__ << "Have FSM access" << __E__;
2853 
2854  cgicc::Cgicc cgiIn(in);
2855 
2856  std::string requestType = CgiDataUtilities::getData(cgiIn, "RequestType");
2857 
2858  HttpXmlDocument xmlOut;
2859  WebUsers::RequestUserInfo userInfo(requestType,
2860  CgiDataUtilities::postData(cgiIn, "CookieCode"));
2861 
2862  CorePropertySupervisorBase::getRequestUserInfo(userInfo);
2863 
2864  // std::string cookieCode = CgiDataUtilities::postData(cgiIn, "CookieCode");
2865  // uint64_t uid;
2866 
2867  // if(!theWebUsers_.cookieCodeIsActiveForRequest(
2868  // cookieCode, 0 /*userPermissions*/, &uid, "0" /*dummy ip*/, false
2869  // /*refresh*/))
2870  //{
2871  // *out << cookieCode;
2872  // return;
2873  //}
2874 
2875  if(!theWebUsers_.xmlRequestOnGateway(cgiIn, out, &xmlOut, userInfo))
2876  return; // access failed
2877 
2878  // RequestType Commands:
2879  // getSettings
2880  // setSettings
2881  // accountSettings
2882  // getAliasList
2883  // getFecList
2884  // getAppStatus
2885  // getContextMemberNames
2886  // getSystemMessages
2887  // setUserWithLock
2888  // getStateMachine
2889  // stateMatchinePreferences
2890  // getStateMachineNames
2891  // getCurrentState
2892  // cancelStateMachineTransition
2893  // getIterationPlanStatus
2894  // getErrorInStateMatchine
2895 
2896  // getDesktopIcons
2897  // addDesktopIcon
2898 
2899  // resetUserTooltips
2900  // silenceAllUserTooltips
2901 
2902  // gatewayLaunchOTS
2903  // gatewayLaunchWiz
2904 
2905  try
2906  {
2907  if(requestType == "getSettings")
2908  {
2909  std::string accounts = CgiDataUtilities::getData(cgiIn, "accounts");
2910 
2911  __COUT__ << "Get Settings Request" << __E__;
2912  __COUT__ << "accounts = " << accounts << __E__;
2913  theWebUsers_.insertSettingsForUser(userInfo.uid_, &xmlOut, accounts == "1");
2914  }
2915  else if(requestType == "viewCodeContent")
2916  {
2917  }
2918  else if(requestType == "setSettings")
2919  {
2920  std::string bgcolor = CgiDataUtilities::postData(cgiIn, "bgcolor");
2921  std::string dbcolor = CgiDataUtilities::postData(cgiIn, "dbcolor");
2922  std::string wincolor = CgiDataUtilities::postData(cgiIn, "wincolor");
2923  std::string layout = CgiDataUtilities::postData(cgiIn, "layout");
2924  std::string syslayout = CgiDataUtilities::postData(cgiIn, "syslayout");
2925 
2926  __COUT__ << "Set Settings Request" << __E__;
2927  __COUT__ << "bgcolor = " << bgcolor << __E__;
2928  __COUT__ << "dbcolor = " << dbcolor << __E__;
2929  __COUT__ << "wincolor = " << wincolor << __E__;
2930  __COUT__ << "layout = " << layout << __E__;
2931  __COUT__ << "syslayout = " << syslayout << __E__;
2932 
2933  theWebUsers_.changeSettingsForUser(
2934  userInfo.uid_, bgcolor, dbcolor, wincolor, layout, syslayout);
2935  theWebUsers_.insertSettingsForUser(
2936  userInfo.uid_, &xmlOut, true); // include user accounts
2937  }
2938  else if(requestType == "accountSettings")
2939  {
2940  std::string type = CgiDataUtilities::postData(
2941  cgiIn, "type"); // updateAccount, createAccount, deleteAccount
2942  int type_int = -1;
2943 
2944  if(type == "updateAccount")
2945  type_int = 0;
2946  else if(type == "createAccount")
2947  type_int = 1;
2948  else if(type == "deleteAccount")
2949  type_int = 2;
2950 
2951  std::string username = CgiDataUtilities::postData(cgiIn, "username");
2952  std::string displayname = CgiDataUtilities::postData(cgiIn, "displayname");
2953  std::string email = CgiDataUtilities::postData(cgiIn, "useremail");
2954  std::string permissions = CgiDataUtilities::postData(cgiIn, "permissions");
2955  std::string accounts = CgiDataUtilities::getData(cgiIn, "accounts");
2956 
2957  __COUT__ << "accountSettings Request" << __E__;
2958  __COUT__ << "type = " << type << " - " << type_int << __E__;
2959  __COUT__ << "username = " << username << __E__;
2960  __COUT__ << "useremail = " << email << __E__;
2961  __COUT__ << "displayname = " << displayname << __E__;
2962  __COUT__ << "permissions = " << permissions << __E__;
2963 
2964  theWebUsers_.modifyAccountSettings(
2965  userInfo.uid_, type_int, username, displayname, email, permissions);
2966 
2967  __COUT__ << "accounts = " << accounts << __E__;
2968 
2969  theWebUsers_.insertSettingsForUser(userInfo.uid_, &xmlOut, accounts == "1");
2970  }
2971  else if(requestType == "stateMatchinePreferences")
2972  {
2973  std::string set = CgiDataUtilities::getData(cgiIn, "set");
2974  const std::string DEFAULT_FSM_VIEW = "Default_FSM_View";
2975  if(set == "1")
2976  theWebUsers_.setGenericPreference(
2977  userInfo.uid_,
2978  DEFAULT_FSM_VIEW,
2979  CgiDataUtilities::getData(cgiIn, DEFAULT_FSM_VIEW));
2980  else
2981  theWebUsers_.getGenericPreference(
2982  userInfo.uid_, DEFAULT_FSM_VIEW, &xmlOut);
2983  }
2984  else if(requestType == "getAliasList")
2985  {
2986  std::string username = theWebUsers_.getUsersUsername(userInfo.uid_);
2987  std::string fsmName = CgiDataUtilities::getData(cgiIn, "fsmName");
2988  __COUT__ << "fsmName = " << fsmName << __E__;
2989 
2990  std::string stateMachineAliasFilter = "*"; // default to all
2991 
2992  std::map<std::string /*alias*/,
2993  std::pair<std::string /*group name*/, TableGroupKey>>
2994  aliasMap = CorePropertySupervisorBase::theConfigurationManager_
2995  ->getActiveGroupAliases();
2996 
2997  // get stateMachineAliasFilter if possible
2998  ConfigurationTree configLinkNode =
2999  CorePropertySupervisorBase::theConfigurationManager_
3000  ->getSupervisorTableNode(supervisorContextUID_,
3001  supervisorApplicationUID_);
3002 
3003  if(!configLinkNode.isDisconnected())
3004  {
3005  try // for backwards compatibility
3006  {
3007  ConfigurationTree fsmLinkNode =
3008  configLinkNode.getNode("LinkToStateMachineTable");
3009  if(!fsmLinkNode.isDisconnected())
3010  stateMachineAliasFilter =
3011  fsmLinkNode.getNode(fsmName + "/SystemAliasFilter")
3012  .getValue<std::string>();
3013  else
3014  __COUT_INFO__ << "FSM Link disconnected." << __E__;
3015  }
3016  catch(std::runtime_error& e)
3017  {
3018  __COUT_INFO__ << e.what() << __E__;
3019  }
3020  catch(...)
3021  {
3022  __COUT_ERR__ << "Unknown error. Should never happen." << __E__;
3023  }
3024  }
3025  else
3026  __COUT_INFO__ << "FSM Link disconnected." << __E__;
3027 
3028  __COUT__ << "stateMachineAliasFilter = " << stateMachineAliasFilter << __E__;
3029 
3030  // filter list of aliases based on stateMachineAliasFilter
3031  // ! as first character means choose those that do NOT match filter
3032  // * can be used as wild card.
3033  {
3034  bool invertFilter =
3035  stateMachineAliasFilter.size() && stateMachineAliasFilter[0] == '!';
3036  std::vector<std::string> filterArr;
3037 
3038  size_t i = 0;
3039  if(invertFilter)
3040  ++i;
3041  size_t f;
3042  std::string tmp;
3043  while((f = stateMachineAliasFilter.find('*', i)) != std::string::npos)
3044  {
3045  tmp = stateMachineAliasFilter.substr(i, f - i);
3046  i = f + 1;
3047  filterArr.push_back(tmp);
3048  //__COUT__ << filterArr[filterArr.size()-1] << " " << i <<
3049  // " of " << stateMachineAliasFilter.size() << __E__;
3050  }
3051  if(i <= stateMachineAliasFilter.size())
3052  {
3053  tmp = stateMachineAliasFilter.substr(i);
3054  filterArr.push_back(tmp);
3055  //__COUT__ << filterArr[filterArr.size()-1] << " last." << __E__;
3056  }
3057 
3058  bool filterMatch;
3059 
3060  for(auto& aliasMapPair : aliasMap)
3061  {
3062  //__COUT__ << "aliasMapPair.first: " << aliasMapPair.first << __E__;
3063 
3064  filterMatch = true;
3065 
3066  if(filterArr.size() == 1)
3067  {
3068  if(filterArr[0] != "" && filterArr[0] != "*" &&
3069  aliasMapPair.first != filterArr[0])
3070  filterMatch = false;
3071  }
3072  else
3073  {
3074  i = -1;
3075  for(f = 0; f < filterArr.size(); ++f)
3076  {
3077  if(!filterArr[f].size())
3078  continue; // skip empty filters
3079 
3080  if(f == 0) // must start with this filter
3081  {
3082  if((i = aliasMapPair.first.find(filterArr[f])) != 0)
3083  {
3084  filterMatch = false;
3085  break;
3086  }
3087  }
3088  else if(f ==
3089  filterArr.size() - 1) // must end with this filter
3090  {
3091  if(aliasMapPair.first.rfind(filterArr[f]) !=
3092  aliasMapPair.first.size() - filterArr[f].size())
3093  {
3094  filterMatch = false;
3095  break;
3096  }
3097  }
3098  else if((i = aliasMapPair.first.find(filterArr[f])) ==
3099  std::string::npos)
3100  {
3101  filterMatch = false;
3102  break;
3103  }
3104  }
3105  }
3106 
3107  if(invertFilter)
3108  filterMatch = !filterMatch;
3109 
3110  //__COUT__ << "filterMatch=" << filterMatch << __E__;
3111 
3112  if(!filterMatch)
3113  continue;
3114 
3115  xmlOut.addTextElementToData("config_alias", aliasMapPair.first);
3116  xmlOut.addTextElementToData(
3117  "config_key",
3118  TableGroupKey::getFullGroupString(aliasMapPair.second.first,
3119  aliasMapPair.second.second)
3120  .c_str());
3121 
3122  std::string groupComment, groupAuthor, groupCreationTime;
3123  try
3124  {
3125  CorePropertySupervisorBase::theConfigurationManager_
3126  ->loadTableGroup(aliasMapPair.second.first,
3127  aliasMapPair.second.second,
3128  false,
3129  0,
3130  0,
3131  0,
3132  &groupComment,
3133  &groupAuthor,
3134  &groupCreationTime,
3135  false /*false to not load member map*/);
3136 
3137  xmlOut.addTextElementToData("config_comment", groupComment);
3138  xmlOut.addTextElementToData("config_author", groupAuthor);
3139  xmlOut.addTextElementToData("config_create_time",
3140  groupCreationTime);
3141  }
3142  catch(...)
3143  {
3144  __COUT_WARN__ << "Failed to load group metadata." << __E__;
3145  }
3146  }
3147  }
3148 
3149  // return last group alias
3150  std::string fn = FSM_LAST_GROUP_ALIAS_PATH + FSM_LAST_GROUP_ALIAS_FILE_START +
3151  username + "." + FSM_USERS_PREFERENCES_FILETYPE;
3152  __COUT__ << "Load preferences: " << fn << __E__;
3153  FILE* fp = fopen(fn.c_str(), "r");
3154  if(fp)
3155  {
3156  char tmpLastAlias[500];
3157  fscanf(fp, "%*s %s", tmpLastAlias);
3158  __COUT__ << "tmpLastAlias: " << tmpLastAlias << __E__;
3159 
3160  xmlOut.addTextElementToData("UserLastConfigAlias", tmpLastAlias);
3161  fclose(fp);
3162  }
3163  }
3164  else if(requestType == "getAppStatus")
3165  {
3166  for(auto it : allSupervisorInfo_.getAllSupervisorInfo())
3167  {
3168  // bool pass = true;
3169 
3170  auto appInfo = it.second;
3171 
3172  xmlOut.addTextElementToData("name",
3173  appInfo.getName()); // get application name
3174  xmlOut.addTextElementToData(
3175  "id", std::to_string(appInfo.getId())); // get application id
3176  xmlOut.addTextElementToData("status", appInfo.getStatus()); // get status
3177  xmlOut.addTextElementToData(
3178  "time",
3179  StringMacros::getTimestampString(
3180  appInfo.getLastStatusTime())); // get time stamp
3181  xmlOut.addTextElementToData(
3182  "progress", std::to_string(appInfo.getProgress())); // get progress
3183  xmlOut.addTextElementToData("class",
3184  appInfo.getClass()); // get application class
3185  xmlOut.addTextElementToData("url",
3186  appInfo.getURL()); // get application url
3187  xmlOut.addTextElementToData("context",
3188  appInfo.getContextName()); // get context
3189  }
3190  }
3191 
3192  else if(requestType == "getContextMemberNames")
3193  {
3194  const XDAQContextTable* contextTable =
3195  CorePropertySupervisorBase::theConfigurationManager_->__GET_CONFIG__(
3197 
3198  auto contexts = contextTable->getContexts();
3199  for(const auto& context : contexts)
3200  {
3201  xmlOut.addTextElementToData(
3202  "ContextMember", context.contextUID_); // get context member name
3203  }
3204  }
3205 
3206  else if(requestType == "getSystemMessages")
3207  {
3208  xmlOut.addTextElementToData(
3209  "systemMessages",
3210  theSystemMessenger_.getSystemMessage(
3211  theWebUsers_.getUsersDisplayName(userInfo.uid_)));
3212 
3213  xmlOut.addTextElementToData(
3214  "username_with_lock",
3215  theWebUsers_.getUserWithLock()); // always give system lock update
3216 
3217  //__COUT__ << "userWithLock " << theWebUsers_.getUserWithLock() << __E__;
3218  }
3219  else if(requestType == "setUserWithLock")
3220  {
3221  std::string username = CgiDataUtilities::postData(cgiIn, "username");
3222  std::string lock = CgiDataUtilities::postData(cgiIn, "lock");
3223  std::string accounts = CgiDataUtilities::getData(cgiIn, "accounts");
3224 
3225  __COUTV__(username);
3226  __COUTV__(lock);
3227  __COUTV__(accounts);
3228  __COUTV__(userInfo.uid_);
3229 
3230  std::string tmpUserWithLock = theWebUsers_.getUserWithLock();
3231  if(!theWebUsers_.setUserWithLock(userInfo.uid_, lock == "1", username))
3232  xmlOut.addTextElementToData(
3233  "server_alert",
3234  std::string("Set user lock action failed. You must have valid "
3235  "permissions and ") +
3236  "locking user must be currently logged in.");
3237 
3238  theWebUsers_.insertSettingsForUser(userInfo.uid_, &xmlOut, accounts == "1");
3239 
3240  if(tmpUserWithLock !=
3241  theWebUsers_
3242  .getUserWithLock()) // if there was a change, broadcast system message
3243  theSystemMessenger_.addSystemMessage(
3244  "*",
3245  theWebUsers_.getUserWithLock() == ""
3246  ? tmpUserWithLock + " has unlocked ots."
3247  : theWebUsers_.getUserWithLock() + " has locked ots.");
3248  }
3249  else if(requestType == "getStateMachine")
3250  {
3251  // __COUT__ << "Getting state machine" << __E__;
3252  std::vector<toolbox::fsm::State> states;
3253  states = theStateMachine_.getStates();
3254  char stateStr[2];
3255  stateStr[1] = '\0';
3256  std::string transName;
3257  std::string transParameter;
3258 
3259  // bool addRun, addCfg;
3260  for(unsigned int i = 0; i < states.size(); ++i) // get all states
3261  {
3262  stateStr[0] = states[i];
3263  DOMElement* stateParent = xmlOut.addTextElementToData("state", stateStr);
3264 
3265  xmlOut.addTextElementToParent(
3266  "state_name", theStateMachine_.getStateName(states[i]), stateParent);
3267 
3268  //__COUT__ << "state: " << states[i] << " - " <<
3269  // theStateMachine_.getStateName(states[i]) << __E__;
3270 
3271  // get all transition final states, transitionNames and actionNames from
3272  // state
3273  std::map<std::string, toolbox::fsm::State, std::less<std::string>> trans =
3274  theStateMachine_.getTransitions(states[i]);
3275  std::set<std::string> actionNames = theStateMachine_.getInputs(states[i]);
3276 
3277  std::map<std::string, toolbox::fsm::State, std::less<std::string>>::
3278  iterator it = trans.begin();
3279  std::set<std::string>::iterator ait = actionNames.begin();
3280 
3281  // addRun = false;
3282  // addCfg = false;
3283 
3284  // handle hacky way to keep "forward" moving states on right of FSM
3285  // display must be first!
3286 
3287  for(; it != trans.end() && ait != actionNames.end(); ++it, ++ait)
3288  {
3289  stateStr[0] = it->second;
3290 
3291  if(stateStr[0] == 'R')
3292  {
3293  // addRun = true;
3294  xmlOut.addTextElementToParent(
3295  "state_transition", stateStr, stateParent);
3296 
3297  //__COUT__ << states[i] << " => " << *ait << __E__;
3298 
3299  xmlOut.addTextElementToParent(
3300  "state_transition_action", *ait, stateParent);
3301 
3302  transName = theStateMachine_.getTransitionName(states[i], *ait);
3303  //__COUT__ << states[i] << " => " << transName << __E__;
3304 
3305  xmlOut.addTextElementToParent(
3306  "state_transition_name", transName, stateParent);
3307  transParameter =
3308  theStateMachine_.getTransitionParameter(states[i], *ait);
3309  //__COUT__ << states[i] << " => " << transParameter<< __E__;
3310 
3311  xmlOut.addTextElementToParent(
3312  "state_transition_parameter", transParameter, stateParent);
3313  break;
3314  }
3315  else if(stateStr[0] == 'C')
3316  {
3317  // addCfg = true;
3318  xmlOut.addTextElementToParent(
3319  "state_transition", stateStr, stateParent);
3320 
3321  //__COUT__ << states[i] << " => " << *ait << __E__;
3322 
3323  xmlOut.addTextElementToParent(
3324  "state_transition_action", *ait, stateParent);
3325 
3326  transName = theStateMachine_.getTransitionName(states[i], *ait);
3327  //__COUT__ << states[i] << " => " << transName << __E__;
3328 
3329  xmlOut.addTextElementToParent(
3330  "state_transition_name", transName, stateParent);
3331  transParameter =
3332  theStateMachine_.getTransitionParameter(states[i], *ait);
3333  //__COUT__ << states[i] << " => " << transParameter<< __E__;
3334 
3335  xmlOut.addTextElementToParent(
3336  "state_transition_parameter", transParameter, stateParent);
3337  break;
3338  }
3339  }
3340 
3341  // reset for 2nd pass
3342  it = trans.begin();
3343  ait = actionNames.begin();
3344 
3345  // other states
3346  for(; it != trans.end() && ait != actionNames.end(); ++it, ++ait)
3347  {
3348  //__COUT__ << states[i] << " => " << it->second << __E__;
3349 
3350  stateStr[0] = it->second;
3351 
3352  if(stateStr[0] == 'R')
3353  continue;
3354  else if(stateStr[0] == 'C')
3355  continue;
3356 
3357  xmlOut.addTextElementToParent(
3358  "state_transition", stateStr, stateParent);
3359 
3360  //__COUT__ << states[i] << " => " << *ait << __E__;
3361 
3362  xmlOut.addTextElementToParent(
3363  "state_transition_action", *ait, stateParent);
3364 
3365  transName = theStateMachine_.getTransitionName(states[i], *ait);
3366  //__COUT__ << states[i] << " => " << transName << __E__;
3367 
3368  xmlOut.addTextElementToParent(
3369  "state_transition_name", transName, stateParent);
3370  transParameter =
3371  theStateMachine_.getTransitionParameter(states[i], *ait);
3372  //__COUT__ << states[i] << " => " << transParameter<< __E__;
3373 
3374  xmlOut.addTextElementToParent(
3375  "state_transition_parameter", transParameter, stateParent);
3376  }
3377  }
3378  }
3379  else if(requestType == "getStateMachineNames")
3380  {
3381  // get stateMachineAliasFilter if possible
3382  ConfigurationTree configLinkNode =
3383  CorePropertySupervisorBase::theConfigurationManager_
3384  ->getSupervisorTableNode(supervisorContextUID_,
3385  supervisorApplicationUID_);
3386 
3387  try
3388  {
3389  auto fsmNodes =
3390  configLinkNode.getNode("LinkToStateMachineTable").getChildren();
3391  for(const auto& fsmNode : fsmNodes)
3392  xmlOut.addTextElementToData("stateMachineName", fsmNode.first);
3393  }
3394  catch(...) // else empty set of state machines.. can always choose ""
3395  {
3396  __COUT__ << "Caught exception, assuming no valid FSM names." << __E__;
3397  xmlOut.addTextElementToData("stateMachineName", "");
3398  }
3399  }
3400  else if(requestType == "getIterationPlanStatus")
3401  {
3402  //__COUT__ << "checking it status" << __E__;
3403  theIterator_.handleCommandRequest(xmlOut, requestType, "");
3404  }
3405  else if(requestType == "getCurrentState")
3406  {
3407  xmlOut.addTextElementToData("current_state",
3408  theStateMachine_.getCurrentStateName());
3409  xmlOut.addTextElementToData("in_transition",
3410  theStateMachine_.isInTransition() ? "1" : "0");
3411  if(theStateMachine_.isInTransition())
3412  xmlOut.addTextElementToData(
3413  "transition_progress",
3414  RunControlStateMachine::theProgressBar_.readPercentageString());
3415  else
3416  xmlOut.addTextElementToData("transition_progress", "100");
3417 
3418  char tmp[20];
3419  sprintf(tmp, "%lu", theStateMachine_.getTimeInState());
3420  xmlOut.addTextElementToData("time_in_state", tmp);
3421 
3422  //__COUT__ << "current state: " << theStateMachine_.getCurrentStateName() <<
3423  //__E__;
3424 
3426 
3427  std::string fsmName = CgiDataUtilities::getData(cgiIn, "fsmName");
3428  // __COUT__ << "fsmName = " << fsmName << __E__;
3429  // __COUT__ << "activeStateMachineName_ = " << activeStateMachineName_ <<
3430  //__E__;
3431  // __COUT__ << "theStateMachine_.getProvenanceStateName() = " <<
3432  // theStateMachine_.getProvenanceStateName() << __E__;
3433  // __COUT__ << "theStateMachine_.getCurrentStateName() = " <<
3434  // theStateMachine_.getCurrentStateName() << __E__;
3435 
3436  if(!theStateMachine_.isInTransition())
3437  {
3438  std::string stateMachineRunAlias = "Run"; // default to "Run"
3439 
3440  // get stateMachineAliasFilter if possible
3441  ConfigurationTree configLinkNode =
3442  CorePropertySupervisorBase::theConfigurationManager_
3443  ->getSupervisorTableNode(supervisorContextUID_,
3444  supervisorApplicationUID_);
3445 
3446  if(!configLinkNode.isDisconnected())
3447  {
3448  try // for backwards compatibility
3449  {
3450  ConfigurationTree fsmLinkNode =
3451  configLinkNode.getNode("LinkToStateMachineTable");
3452  if(!fsmLinkNode.isDisconnected())
3453  stateMachineRunAlias =
3454  fsmLinkNode.getNode(fsmName + "/RunDisplayAlias")
3455  .getValue<std::string>();
3456  // else
3457  // __COUT_INFO__ << "FSM Link disconnected." << __E__;
3458  }
3459  catch(std::runtime_error& e)
3460  {
3461  //__COUT_INFO__ << e.what() << __E__;
3462  //__COUT_INFO__ << "No state machine Run alias. Ignoring and
3463  // assuming alias of '" << stateMachineRunAlias << ".'" <<
3464  //__E__;
3465  }
3466  catch(...)
3467  {
3468  __COUT_ERR__ << "Unknown error. Should never happen." << __E__;
3469 
3470  __COUT_INFO__ << "No state machine Run alias. Ignoring and "
3471  "assuming alias of '"
3472  << stateMachineRunAlias << ".'" << __E__;
3473  }
3474  }
3475  // else
3476  // __COUT_INFO__ << "FSM Link disconnected." << __E__;
3477 
3478  //__COUT__ << "stateMachineRunAlias = " << stateMachineRunAlias <<
3479  //__E__;
3480 
3481  xmlOut.addTextElementToData("stateMachineRunAlias", stateMachineRunAlias);
3482 
3484 
3485  if(theStateMachine_.getCurrentStateName() == "Running" ||
3486  theStateMachine_.getCurrentStateName() == "Paused")
3487  {
3488  sprintf(tmp,
3489  "Current %s Number: %u",
3490  stateMachineRunAlias.c_str(),
3491  getNextRunNumber(activeStateMachineName_) - 1);
3492 
3493  if(RunControlStateMachine::asyncSoftFailureReceived_)
3494  {
3495  //__COUTV__(RunControlStateMachine::asyncSoftFailureReceived_);
3496  //__COUTV__(RunControlStateMachine::getErrorMessage());
3497  xmlOut.addTextElementToData(
3498  "soft_error", RunControlStateMachine::getErrorMessage());
3499  }
3500  }
3501  else
3502  sprintf(tmp,
3503  "Next %s Number: %u",
3504  stateMachineRunAlias.c_str(),
3505  getNextRunNumber(fsmName));
3506  xmlOut.addTextElementToData("run_number", tmp);
3507  }
3508  }
3509  else if(requestType == "cancelStateMachineTransition")
3510  {
3511  __SS__ << "State transition was cancelled by user!" << __E__;
3512  __MCOUT__(ss.str());
3513  RunControlStateMachine::theStateMachine_.setErrorMessage(ss.str());
3514  RunControlStateMachine::asyncFailureReceived_ = true;
3515  }
3516  else if(requestType == "getErrorInStateMatchine")
3517  {
3518  xmlOut.addTextElementToData("FSM_Error", theStateMachine_.getErrorMessage());
3519  }
3520  else if(requestType == "getDesktopIcons")
3521  {
3522  // get icons and create comma-separated string based on user permissions
3523  // note: each icon has own permission threshold, so each user can have
3524  // a unique desktop icon experience.
3525 
3526  const DesktopIconTable* iconTable =
3527  CorePropertySupervisorBase::theConfigurationManager_->__GET_CONFIG__(
3529  std::vector<DesktopIconTable::DesktopIcon> icons =
3530  iconTable->getAllDesktopIcons();
3531 
3532  std::string iconString = ""; // comma-separated icon string, 7 fields:
3533  // 0 - caption = text below icon
3534  // 1 - altText = text icon if no image given
3535  // 2 - uniqueWin = if true, only one window is allowed,
3536  // else multiple instances of window 3 - permissions =
3537  // security level needed to see icon 4 - picfn =
3538  // icon image filename 5 - linkurl = url of the window to
3539  // open 6 - folderPath = folder and subfolder location
3540  // '/'
3541  // separated for example: State
3542  // Machine,FSM,1,200,icon-Physics.gif,/WebPath/html/StateMachine.html?fsm_name=OtherRuns0,,Chat,CHAT,1,1,icon-Chat.png,/urn:xdaq-application:lid=250,,Visualizer,VIS,0,10,icon-Visualizer.png,/WebPath/html/Visualization.html?urn=270,,Configure,CFG,0,10,icon-Configure.png,/urn:xdaq-application:lid=281,,Front-ends,CFG,0,15,icon-Configure.png,/WebPath/html/ConfigurationGUI_subset.html?urn=281&subsetBasePath=FEInterfaceTable&groupingFieldList=Status%2CFEInterfacePluginName&recordAlias=Front%2Dends&editableFieldList=%21%2ACommentDescription%2C%21SlowControls%2A,Config
3543  // Subsets
3544 
3545  //__COUTV__((unsigned int)userInfo.permissionLevel_);
3546 
3547  std::map<std::string, WebUsers::permissionLevel_t> userPermissionLevelsMap =
3548  theWebUsers_.getPermissionsForUser(userInfo.uid_);
3549  std::map<std::string, WebUsers::permissionLevel_t>
3550  iconPermissionThresholdsMap;
3551 
3552  bool firstIcon = true;
3553  for(const auto& icon : icons)
3554  {
3555  __COUTV__(icon.caption_);
3556  __COUTV__(icon.permissionThresholdString_);
3557 
3558  CorePropertySupervisorBase::extractPermissionsMapFromString(
3559  icon.permissionThresholdString_, iconPermissionThresholdsMap);
3560 
3561  if(!CorePropertySupervisorBase::doPermissionsGrantAccess(
3562  userPermissionLevelsMap, iconPermissionThresholdsMap))
3563  continue; // skip icon if no access
3564 
3565  //__COUTV__(icon.caption_);
3566 
3567  // have icon access, so add to CSV string
3568  if(firstIcon)
3569  firstIcon = false;
3570  else
3571  iconString += ",";
3572 
3573  iconString += icon.caption_;
3574  iconString += "," + icon.alternateText_;
3575  iconString +=
3576  "," + std::string(icon.enforceOneWindowInstance_ ? "1" : "0");
3577  iconString +=
3578  "," + std::string("1"); // set permission to 1 so the
3579  // desktop shows every icon that the
3580  // server allows (i.e., trust server
3581  // security, ignore client security)
3582  iconString += "," + icon.imageURL_;
3583  iconString += "," + icon.windowContentURL_;
3584  iconString += "," + icon.folderPath_;
3585  }
3586  __COUTV__(iconString);
3587 
3588  xmlOut.addTextElementToData("iconList", iconString);
3589  }
3590  else if(requestType == "addDesktopIcon")
3591  {
3592  GatewaySupervisor::handleAddDesktopIconRequest(
3593  theWebUsers_.getUsersUsername(userInfo.uid_), cgiIn, xmlOut);
3594  }
3595  else if(requestType == "gatewayLaunchOTS" || requestType == "gatewayLaunchWiz")
3596  {
3597  // NOTE: similar to ConfigurationGUI version but DOES keep active login
3598  // sessions
3599 
3600  __COUT_WARN__ << requestType << " requestType received! " << __E__;
3601  __MOUT_WARN__ << requestType << " requestType received! " << __E__;
3602 
3603  // gateway launch is different, in that it saves user sessions
3604  theWebUsers_.saveActiveSessions();
3605 
3606  // now launch
3607 
3608  if(requestType == "gatewayLaunchOTS")
3609  GatewaySupervisor::launchStartOTSCommand(
3610  "LAUNCH_OTS", CorePropertySupervisorBase::theConfigurationManager_);
3611  else if(requestType == "gatewayLaunchWiz")
3612  GatewaySupervisor::launchStartOTSCommand(
3613  "LAUNCH_WIZ", CorePropertySupervisorBase::theConfigurationManager_);
3614  }
3615  else if(requestType == "resetUserTooltips")
3616  {
3617  WebUsers::resetAllUserTooltips(theWebUsers_.getUsersUsername(userInfo.uid_));
3618  }
3619  else if(requestType == "silenceUserTooltips")
3620  {
3621  WebUsers::silenceAllUserTooltips(
3622  theWebUsers_.getUsersUsername(userInfo.uid_));
3623  }
3624  else
3625  {
3626  __SS__ << "requestType Request, " << requestType << ", not recognized."
3627  << __E__;
3628  __SS_THROW__;
3629  }
3630  }
3631  catch(const std::runtime_error& e)
3632  {
3633  __SS__ << "An error was encountered handling requestType '" << requestType
3634  << "':" << e.what() << __E__;
3635  __COUT__ << "\n" << ss.str();
3636  xmlOut.addTextElementToData("Error", ss.str());
3637  }
3638  catch(...)
3639  {
3640  __SS__ << "An unknown error was encountered handling requestType '" << requestType
3641  << ".' "
3642  << "Please check the printouts to debug." << __E__;
3643  __COUT__ << "\n" << ss.str();
3644  xmlOut.addTextElementToData("Error", ss.str());
3645  }
3646 
3647  // return xml doc holding server response
3648  xmlOut.outputXmlDocument(
3649  (std::ostringstream*)out,
3650  false /*dispStdOut*/,
3651  true /*allowWhiteSpace*/); // Note: allow white space need for error response
3652 
3653  //__COUT__ << "Done" << __E__;
3654 } // end request()
3655 
3656 //========================================================================================================================
3657 // launchStartOTSCommand
3658 // static function (so WizardSupervisor can use it)
3659 // throws exception if command fails to start
3660 void GatewaySupervisor::launchStartOTSCommand(const std::string& command,
3661  ConfigurationManager* cfgMgr)
3662 {
3663  __COUT__ << "launch StartOTS Command = " << command << __E__;
3664  __COUT__ << "Extracting target context hostnames... " << __E__;
3665 
3666  std::vector<std::string> hostnames;
3667  try
3668  {
3669  cfgMgr->init(); // completely reset to re-align with any changes
3670 
3671  const XDAQContextTable* contextTable = cfgMgr->__GET_CONFIG__(XDAQContextTable);
3672 
3673  auto contexts = contextTable->getContexts();
3674  unsigned int i, j;
3675  for(const auto& context : contexts)
3676  {
3677  if(!context.status_)
3678  continue;
3679 
3680  // find last slash
3681  j = 0; // default to whole string
3682  for(i = 0; i < context.address_.size(); ++i)
3683  if(context.address_[i] == '/')
3684  j = i + 1;
3685  hostnames.push_back(context.address_.substr(j));
3686  __COUT__ << "StartOTS.sh hostname = " << hostnames.back() << __E__;
3687  }
3688  }
3689  catch(...)
3690  {
3691  __SS__ << "\nRelaunch of otsdaq interrupted! "
3692  << "The Configuration Manager could not be initialized." << __E__;
3693 
3694  __SS_THROW__;
3695  }
3696 
3697  for(const auto& hostname : hostnames)
3698  {
3699  std::string fn = (std::string(__ENV__("SERVICE_DATA_PATH")) +
3700  "/StartOTS_action_" + hostname + ".cmd");
3701  FILE* fp = fopen(fn.c_str(), "w");
3702  if(fp)
3703  {
3704  fprintf(fp, command.c_str());
3705  fclose(fp);
3706  }
3707  else
3708  {
3709  __SS__ << "Unable to open command file: " << fn << __E__;
3710  __SS_THROW__;
3711  }
3712  }
3713 
3714  sleep(2 /*seconds*/); // then verify that the commands were read
3715  // note: StartOTS.sh has a sleep of 1 second
3716 
3717  for(const auto& hostname : hostnames)
3718  {
3719  std::string fn = (std::string(__ENV__("SERVICE_DATA_PATH")) +
3720  "/StartOTS_action_" + hostname + ".cmd");
3721  FILE* fp = fopen(fn.c_str(), "r");
3722  if(fp)
3723  {
3724  char line[100];
3725  fgets(line, 100, fp);
3726  fclose(fp);
3727 
3728  if(strcmp(line, command.c_str()) == 0)
3729  {
3730  __SS__ << "The command looks to have been ignored by " << hostname
3731  << ". Is StartOTS.sh still running on that node?" << __E__;
3732  __SS_THROW__;
3733  }
3734  __COUTV__(line);
3735  }
3736  else
3737  {
3738  __SS__ << "Unable to open command file for verification: " << fn << __E__;
3739  __SS_THROW__;
3740  }
3741  }
3742 } // end launchStartOTSCommand
3743 
3744 //========================================================================================================================
3745 // xoap::supervisorCookieCheck
3746 // verify cookie
3747 xoap::MessageReference GatewaySupervisor::supervisorCookieCheck(
3748  xoap::MessageReference message)
3749 
3750 {
3751  //__COUT__ << __E__;
3752 
3753  // SOAPUtilities::receive request parameters
3754  SOAPParameters parameters;
3755  parameters.addParameter("CookieCode");
3756  parameters.addParameter("RefreshOption");
3757  parameters.addParameter("IPAddress");
3758  SOAPUtilities::receive(message, parameters);
3759  std::string cookieCode = parameters.getValue("CookieCode");
3760  std::string refreshOption =
3761  parameters.getValue("RefreshOption"); // give external supervisors option to
3762  // refresh cookie or not, "1" to refresh
3763  std::string ipAddress =
3764  parameters.getValue("IPAddress"); // give external supervisors option to refresh
3765  // cookie or not, "1" to refresh
3766 
3767  // If TRUE, cookie code is good, and refreshed code is in cookieCode, also pointers
3768  // optionally for uint8_t userPermissions, uint64_t uid Else, error message is
3769  // returned in cookieCode
3770  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>
3771  userGroupPermissionsMap;
3772  std::string userWithLock = "";
3773  uint64_t activeSessionIndex, uid;
3774  theWebUsers_.cookieCodeIsActiveForRequest(cookieCode,
3775  &userGroupPermissionsMap,
3776  &uid /*uid is not given to remote users*/,
3777  ipAddress,
3778  refreshOption == "1",
3779  &userWithLock,
3780  &activeSessionIndex);
3781 
3782  //__COUT__ << "userWithLock " << userWithLock << __E__;
3783 
3784  // fill return parameters
3785  SOAPParameters retParameters;
3786  retParameters.addParameter("CookieCode", cookieCode);
3787  retParameters.addParameter(
3788  "Permissions", StringMacros::mapToString(userGroupPermissionsMap).c_str());
3789  retParameters.addParameter("UserWithLock", userWithLock);
3790  retParameters.addParameter("Username", theWebUsers_.getUsersUsername(uid));
3791  retParameters.addParameter("DisplayName", theWebUsers_.getUsersDisplayName(uid));
3792  sprintf(tmpStringForConversions_, "%lu", activeSessionIndex);
3793  retParameters.addParameter("ActiveSessionIndex", tmpStringForConversions_);
3794 
3795  //__COUT__ << __E__;
3796 
3797  return SOAPUtilities::makeSOAPMessageReference("CookieResponse", retParameters);
3798 }
3799 
3800 //========================================================================================================================
3801 // xoap::supervisorGetActiveUsers
3802 // get display names for all active users
3803 xoap::MessageReference GatewaySupervisor::supervisorGetActiveUsers(
3804  xoap::MessageReference message)
3805 
3806 {
3807  __COUT__ << __E__;
3808 
3809  SOAPParameters parameters("UserList", theWebUsers_.getActiveUsersString());
3810  return SOAPUtilities::makeSOAPMessageReference("ActiveUserResponse", parameters);
3811 }
3812 
3813 //========================================================================================================================
3814 // xoap::supervisorSystemMessage
3815 // SOAPUtilities::receive a new system Message from a supervisor
3816 // ToUser wild card * is to all users
3817 xoap::MessageReference GatewaySupervisor::supervisorSystemMessage(
3818  xoap::MessageReference message)
3819 
3820 {
3821  SOAPParameters parameters;
3822  parameters.addParameter("ToUser");
3823  parameters.addParameter("Message");
3824  SOAPUtilities::receive(message, parameters);
3825 
3826  __COUT__ << "toUser: " << parameters.getValue("ToUser").substr(0, 10)
3827  << ", message: " << parameters.getValue("Message").substr(0, 10) << __E__;
3828 
3829  theSystemMessenger_.addSystemMessage(parameters.getValue("ToUser"),
3830  parameters.getValue("Message"));
3831  return SOAPUtilities::makeSOAPMessageReference("SystemMessageResponse");
3832 }
3833 
3834 //===================================================================================================================
3835 // xoap::supervisorSystemLogbookEntry
3836 // SOAPUtilities::receive a new system Message from a supervisor
3837 // ToUser wild card * is to all users
3838 xoap::MessageReference GatewaySupervisor::supervisorSystemLogbookEntry(
3839  xoap::MessageReference message)
3840 
3841 {
3842  SOAPParameters parameters;
3843  parameters.addParameter("EntryText");
3844  SOAPUtilities::receive(message, parameters);
3845 
3846  __COUT__ << "EntryText: " << parameters.getValue("EntryText").substr(0, 10) << __E__;
3847 
3848  makeSystemLogbookEntry(parameters.getValue("EntryText"));
3849 
3850  return SOAPUtilities::makeSOAPMessageReference("SystemLogbookResponse");
3851 }
3852 
3853 //===================================================================================================================
3854 // supervisorLastConfigGroupRequest
3855 // return the group name and key for the last state machine activity
3856 //
3857 // Note: same as OtsConfigurationWizardSupervisor::supervisorLastConfigGroupRequest
3858 xoap::MessageReference GatewaySupervisor::supervisorLastConfigGroupRequest(
3859  xoap::MessageReference message)
3860 
3861 {
3862  SOAPParameters parameters;
3863  parameters.addParameter("ActionOfLastGroup");
3864  SOAPUtilities::receive(message, parameters);
3865 
3866  return GatewaySupervisor::lastConfigGroupRequestHandler(parameters);
3867 }
3868 
3869 //===================================================================================================================
3870 // xoap::lastConfigGroupRequestHandler
3871 // handles last config group request.
3872 // called by both:
3873 // GatewaySupervisor::supervisorLastConfigGroupRequest
3874 // OtsConfigurationWizardSupervisor::supervisorLastConfigGroupRequest
3875 xoap::MessageReference GatewaySupervisor::lastConfigGroupRequestHandler(
3876  const SOAPParameters& parameters)
3877 {
3878  std::string action = parameters.getValue("ActionOfLastGroup");
3879  __COUT__ << "ActionOfLastGroup: " << action.substr(0, 10) << __E__;
3880 
3881  std::string fileName = "";
3882  if(action == "Configured")
3883  fileName = FSM_LAST_CONFIGURED_GROUP_ALIAS_FILE;
3884  else if(action == "Started")
3885  fileName = FSM_LAST_STARTED_GROUP_ALIAS_FILE;
3886  else
3887  {
3888  __COUT_ERR__ << "Invalid last group action requested." << __E__;
3889  return SOAPUtilities::makeSOAPMessageReference("LastConfigGroupResponseFailure");
3890  }
3891  std::string timeString;
3892  std::pair<std::string /*group name*/, TableGroupKey> theGroup =
3893  loadGroupNameAndKey(fileName, timeString);
3894 
3895  // fill return parameters
3896  SOAPParameters retParameters;
3897  retParameters.addParameter("GroupName", theGroup.first);
3898  retParameters.addParameter("GroupKey", theGroup.second.toString());
3899  retParameters.addParameter("GroupAction", action);
3900  retParameters.addParameter("GroupActionTime", timeString);
3901 
3902  return SOAPUtilities::makeSOAPMessageReference("LastConfigGroupResponse",
3903  retParameters);
3904 }
3905 
3906 //========================================================================================================================
3907 // getNextRunNumber
3908 //
3909 // If fsmName is passed, then get next run number for that FSM name
3910 // Else get next run number for the active FSM name, activeStateMachineName_
3911 //
3912 // Note: the FSM name is sanitized of special characters and used in the filename.
3913 unsigned int GatewaySupervisor::getNextRunNumber(const std::string& fsmNameIn)
3914 {
3915  std::string runNumberFileName = RUN_NUMBER_PATH + "/";
3916  std::string fsmName = fsmNameIn == "" ? activeStateMachineName_ : fsmNameIn;
3917  // prepend sanitized FSM name
3918  for(unsigned int i = 0; i < fsmName.size(); ++i)
3919  if((fsmName[i] >= 'a' && fsmName[i] <= 'z') ||
3920  (fsmName[i] >= 'A' && fsmName[i] <= 'Z') ||
3921  (fsmName[i] >= '0' && fsmName[i] <= '9'))
3922  runNumberFileName += fsmName[i];
3923  runNumberFileName += RUN_NUMBER_FILE_NAME;
3924  //__COUT__ << "runNumberFileName: " << runNumberFileName << __E__;
3925 
3926  std::ifstream runNumberFile(runNumberFileName.c_str());
3927  if(!runNumberFile.is_open())
3928  {
3929  __COUT__ << "Can't open file: " << runNumberFileName << __E__;
3930 
3931  __COUT__ << "Creating file and setting Run Number to 1: " << runNumberFileName
3932  << __E__;
3933  FILE* fp = fopen(runNumberFileName.c_str(), "w");
3934  fprintf(fp, "1");
3935  fclose(fp);
3936 
3937  runNumberFile.open(runNumberFileName.c_str());
3938  if(!runNumberFile.is_open())
3939  {
3940  __SS__ << "Error. Can't create file: " << runNumberFileName << __E__;
3941  __COUT_ERR__ << ss.str();
3942  __SS_THROW__;
3943  }
3944  }
3945  std::string runNumberString;
3946  runNumberFile >> runNumberString;
3947  runNumberFile.close();
3948  return atoi(runNumberString.c_str());
3949 }
3950 
3951 //========================================================================================================================
3952 bool GatewaySupervisor::setNextRunNumber(unsigned int runNumber,
3953  const std::string& fsmNameIn)
3954 {
3955  std::string runNumberFileName = RUN_NUMBER_PATH + "/";
3956  std::string fsmName = fsmNameIn == "" ? activeStateMachineName_ : fsmNameIn;
3957  // prepend sanitized FSM name
3958  for(unsigned int i = 0; i < fsmName.size(); ++i)
3959  if((fsmName[i] >= 'a' && fsmName[i] <= 'z') ||
3960  (fsmName[i] >= 'A' && fsmName[i] <= 'Z') ||
3961  (fsmName[i] >= '0' && fsmName[i] <= '9'))
3962  runNumberFileName += fsmName[i];
3963  runNumberFileName += RUN_NUMBER_FILE_NAME;
3964  __COUT__ << "runNumberFileName: " << runNumberFileName << __E__;
3965 
3966  std::ofstream runNumberFile(runNumberFileName.c_str());
3967  if(!runNumberFile.is_open())
3968  {
3969  __SS__ << "Can't open file: " << runNumberFileName << __E__;
3970  __COUT__ << ss.str();
3971  __SS_THROW__;
3972  }
3973  std::stringstream runNumberStream;
3974  runNumberStream << runNumber;
3975  runNumberFile << runNumberStream.str().c_str();
3976  runNumberFile.close();
3977  return true;
3978 }
3979 
3980 //========================================================================================================================
3981 // loadGroupNameAndKey
3982 // loads group name and key (and time) from specified file
3983 // returns time string in returnedTimeString
3984 //
3985 // Note: this is static so the OtsConfigurationWizardSupervisor can call it
3986 std::pair<std::string /*group name*/, TableGroupKey>
3987 GatewaySupervisor::loadGroupNameAndKey(const std::string& fileName,
3988  std::string& returnedTimeString)
3989 {
3990  std::string fullPath = FSM_LAST_GROUP_ALIAS_PATH + "/" + fileName;
3991 
3992  FILE* groupFile = fopen(fullPath.c_str(), "r");
3993  if(!groupFile)
3994  {
3995  __COUT__ << "Can't open file: " << fullPath << __E__;
3996 
3997  __COUT__ << "Returning empty groupName and key -1" << __E__;
3998 
3999  return std::pair<std::string /*group name*/, TableGroupKey>("", TableGroupKey());
4000  }
4001 
4002  char line[500]; // assuming no group names longer than 500 chars
4003  // name and then key
4004  std::pair<std::string /*group name*/, TableGroupKey> theGroup;
4005 
4006  fgets(line, 500, groupFile); // name
4007  theGroup.first = line;
4008 
4009  fgets(line, 500, groupFile); // key
4010  int key;
4011  sscanf(line, "%d", &key);
4012  theGroup.second = key;
4013 
4014  fgets(line, 500, groupFile); // time
4015  time_t timestamp;
4016  sscanf(line, "%ld", &timestamp); // type long int
4017  // struct tm tmstruct;
4018  // ::localtime_r(&timestamp, &tmstruct);
4019  // ::strftime(line, 30, "%c %Z", &tmstruct);
4020  returnedTimeString = StringMacros::getTimestampString(timestamp); // line;
4021  fclose(groupFile);
4022 
4023  __COUT__ << "theGroup.first= " << theGroup.first
4024  << " theGroup.second= " << theGroup.second << __E__;
4025 
4026  return theGroup;
4027 } // end loadGroupNameAndKey()
4028 
4029 //========================================================================================================================
4030 void GatewaySupervisor::saveGroupNameAndKey(
4031  const std::pair<std::string /*group name*/, TableGroupKey>& theGroup,
4032  const std::string& fileName)
4033 {
4034  std::string fullPath = FSM_LAST_GROUP_ALIAS_PATH + "/" + fileName;
4035 
4036  std::ofstream groupFile(fullPath.c_str());
4037  if(!groupFile.is_open())
4038  {
4039  __SS__ << "Error. Can't open file: " << fullPath << __E__;
4040  __COUT_ERR__ << "\n" << ss.str();
4041  __SS_THROW__;
4042  }
4043  std::stringstream outss;
4044  outss << theGroup.first << "\n" << theGroup.second << "\n" << time(0);
4045  groupFile << outss.str().c_str();
4046  groupFile.close();
4047 } // end saveGroupNameAndKey()
4048 
4049 //========================================================================================================================
4050 void GatewaySupervisor::handleAddDesktopIconRequest(const std::string& author,
4051  cgicc::Cgicc& cgiIn,
4052  HttpXmlDocument& xmlOut)
4053 {
4054  std::string iconCaption =
4055  CgiDataUtilities::getData(cgiIn, "iconCaption"); // from GET
4056  std::string iconAltText =
4057  CgiDataUtilities::getData(cgiIn, "iconAltText"); // from GET
4058  std::string iconFolderPath =
4059  CgiDataUtilities::getData(cgiIn, "iconFolderPath"); // from GET
4060  std::string iconImageURL =
4061  CgiDataUtilities::getData(cgiIn, "iconImageURL"); // from GET
4062  std::string iconWindowURL =
4063  CgiDataUtilities::getData(cgiIn, "iconWindowURL"); // from GET
4064  std::string iconPermissions =
4065  CgiDataUtilities::getData(cgiIn, "iconPermissions"); // from GET
4066  std::string windowLinkedApp =
4067  CgiDataUtilities::getData(cgiIn, "iconLinkedApp"); // from GET
4068  unsigned int windowLinkedAppLID =
4069  CgiDataUtilities::getDataAsInt(cgiIn, "iconLinkedAppLID"); // from GET
4070  bool enforceOneWindowInstance =
4071  CgiDataUtilities::getData(cgiIn, "iconEnforceOneWindowInstance") == "1"
4072  ? true
4073  : false; // from GET
4074 
4075  std::string windowParameters = StringMacros::decodeURIComponent(
4076  CgiDataUtilities::postData(cgiIn, "iconParameters")); // from POST
4077 
4078  __COUTV__(author);
4079  __COUTV__(iconCaption);
4080  __COUTV__(iconAltText);
4081  __COUTV__(iconFolderPath);
4082  __COUTV__(iconImageURL);
4083  __COUTV__(iconWindowURL);
4084  __COUTV__(iconPermissions);
4085  __COUTV__(windowLinkedApp);
4086  __COUTV__(windowLinkedAppLID);
4087  __COUTV__(enforceOneWindowInstance);
4088 
4089  __COUTV__(windowParameters); // map: CSV list
4090 
4091  ConfigurationManagerRW tmpCfgMgr(author);
4092 
4093  ConfigurationSupervisorBase::handleAddDesktopIconXML(
4094  xmlOut,
4095  &tmpCfgMgr,
4096  iconCaption,
4097  iconAltText,
4098  iconFolderPath,
4099  iconImageURL,
4100  iconWindowURL,
4101  iconPermissions,
4102  windowLinkedApp /*= ""*/,
4103  windowLinkedAppLID /*= 0*/,
4104  enforceOneWindowInstance /*= false*/,
4105  windowParameters /*= ""*/);
4106 
4107 } // end handleAddDesktopIconRequest()