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