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