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