otsdaq  v2_03_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(getenv("SERVICE_DATA_PATH")) + "/RunNumber/"
37 #define RUN_NUMBER_FILE_NAME "NextRunNumber.txt"
38 #define FSM_LAST_GROUP_ALIAS_PATH \
39  std::string(getenv("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(getenv("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(getenv("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, theConfigurationTableGroup_.second, true);
1276 
1277  // When configured, set the translated System Alias to be persistently active
1278  ConfigurationManagerRW tmpCfgMgr("TheSupervisor");
1279  tmpCfgMgr.activateTableGroup(theConfigurationTableGroup_.first,
1280  theConfigurationTableGroup_.second);
1281  }
1282  catch(...)
1283  {
1284  __SS__ << "\nTransition to Configuring interrupted! System Alias " << systemAlias
1285  << " was translated to " << theConfigurationTableGroup_.first << " ("
1286  << theConfigurationTableGroup_.second
1287  << ") but could not be loaded and initialized." << __E__;
1288  ss << "\n\nTo debug this problem, try activating this group in the Configuration "
1289  "GUI "
1290  << " and detailed errors will be shown." << __E__;
1291  __COUT_ERR__ << "\n" << ss.str();
1292  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1293  return;
1294  }
1295 
1296  // check if configuration dump is enabled on configure transition
1297  {
1298  ConfigurationTree configLinkNode =
1299  CorePropertySupervisorBase::theConfigurationManager_->getSupervisorTableNode(
1300  supervisorContextUID_, supervisorApplicationUID_);
1301  if(!configLinkNode.isDisconnected())
1302  {
1303  try // errors in dump are not tolerated
1304  {
1305  bool dumpConfiguration = true;
1306  std::string dumpFilePath, dumpFileRadix, dumpFormat;
1307  try // for backwards compatibility
1308  {
1309  ConfigurationTree fsmLinkNode =
1310  configLinkNode.getNode("LinkToStateMachineTable")
1311  .getNode(activeStateMachineName_);
1312  dumpConfiguration =
1313  fsmLinkNode
1314  .getNode("EnableConfigurationDumpOnConfigureTransition")
1315  .getValue<bool>();
1316  dumpFilePath =
1317  fsmLinkNode.getNode("ConfigurationDumpOnConfigureFilePath")
1318  .getValue<std::string>();
1319  dumpFileRadix =
1320  fsmLinkNode.getNode("ConfigurationDumpOnConfigureFileRadix")
1321  .getValue<std::string>();
1322  dumpFormat = fsmLinkNode.getNode("ConfigurationDumpOnConfigureFormat")
1323  .getValue<std::string>();
1324  }
1325  catch(std::runtime_error& e)
1326  {
1327  __COUT_INFO__ << "FSM configuration dump Link disconnected." << __E__;
1328  dumpConfiguration = false;
1329  }
1330 
1331  if(dumpConfiguration)
1332  {
1333  // dump configuration
1334  CorePropertySupervisorBase::theConfigurationManager_
1335  ->dumpActiveConfiguration(dumpFilePath + "/" + dumpFileRadix +
1336  "_" + std::to_string(time(0)) +
1337  ".dump",
1338  dumpFormat);
1339  }
1340  }
1341  catch(std::runtime_error& e)
1342  {
1343  __SS__ << "\nTransition to Configuring interrupted! There was an error "
1344  "identified "
1345  << "during the configuration dump attempt:\n\n " << e.what()
1346  << __E__;
1347  __COUT_ERR__ << "\n" << ss.str();
1348  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1349  return;
1350  }
1351  catch(...)
1352  {
1353  __SS__ << "\nTransition to Configuring interrupted! There was an error "
1354  "identified "
1355  << "during the configuration dump attempt.\n\n " << __E__;
1356  __COUT_ERR__ << "\n" << ss.str();
1357  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1358  return;
1359  }
1360  }
1361  }
1362 
1363  RunControlStateMachine::theProgressBar_.step();
1364  SOAPParameters parameters;
1365  parameters.addParameter("ConfigurationTableGroupName",
1366  theConfigurationTableGroup_.first);
1367  parameters.addParameter("ConfigurationTableGroupKey",
1368  theConfigurationTableGroup_.second.toString());
1369 
1370  // update Macro Maker front end list
1371  {
1372  __COUT__ << "Initializing Macro Maker." << __E__;
1373  xoap::MessageReference message =
1374  SOAPUtilities::makeSOAPMessageReference("FECommunication");
1375 
1376  SOAPParameters parameters;
1377  parameters.addParameter("type", "initFElist");
1378  parameters.addParameter("groupName", theConfigurationTableGroup_.first);
1379  parameters.addParameter("groupKey",
1380  theConfigurationTableGroup_.second.toString());
1381  SOAPUtilities::addParameters(message, parameters);
1382 
1383  __COUT__ << "Sending FE communication: " << SOAPUtilities::translate(message)
1384  << __E__;
1385 
1386  std::string reply =
1387  SOAPMessenger::send(CorePropertySupervisorBase::allSupervisorInfo_
1388  .getAllMacroMakerTypeSupervisorInfo()
1389  .begin()
1390  ->second.getDescriptor(),
1391  message);
1392 
1393  __COUT__ << "Macro Maker init reply: " << reply << __E__;
1394  if(reply == "Error")
1395  {
1396  __SS__ << "\nTransition to Configuring interrupted! There was an error "
1397  "identified initializing Macro Maker.\n\n "
1398  << __E__;
1399  __COUT_ERR__ << "\n" << ss.str();
1400  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1401  return;
1402  }
1403  }
1404 
1405  // xoap::MessageReference message =
1406  // SOAPUtilities::makeSOAPMessageReference(SOAPUtilities::translate(theStateMachine_.getCurrentMessage()).getCommand(),
1407  // parameters);
1408  xoap::MessageReference message = theStateMachine_.getCurrentMessage();
1409  SOAPUtilities::addParameters(message, parameters);
1410  broadcastMessage(message);
1411  RunControlStateMachine::theProgressBar_.step();
1412  // Advertise the exiting of this method
1413  // diagService_->reportError("GatewaySupervisor::stateConfiguring: Exiting",DIAGINFO);
1414 
1415  // save last configured group name/key
1416  saveGroupNameAndKey(theConfigurationTableGroup_,
1417  FSM_LAST_CONFIGURED_GROUP_ALIAS_FILE);
1418 
1419  __COUT__ << "Done configuring." << __E__;
1420  RunControlStateMachine::theProgressBar_.complete();
1421 } // end transitionConfiguring()
1422 
1423 //========================================================================================================================
1424 void GatewaySupervisor::transitionHalting(toolbox::Event::Reference e)
1425 {
1426  checkForAsyncError();
1427 
1428  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1429 
1430  makeSystemLogbookEntry("Run halting.");
1431 
1432  broadcastMessage(theStateMachine_.getCurrentMessage());
1433 } // end transitionHalting()
1434 
1435 //========================================================================================================================
1436 void GatewaySupervisor::transitionShuttingDown(toolbox::Event::Reference e)
1437 
1438 {
1439  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1440 
1441  RunControlStateMachine::theProgressBar_.step();
1442  makeSystemLogbookEntry("System shutting down.");
1443  RunControlStateMachine::theProgressBar_.step();
1444 
1445  // kill all non-gateway contexts
1446  GatewaySupervisor::launchStartOTSCommand(
1447  "OTS_APP_SHUTDOWN", CorePropertySupervisorBase::theConfigurationManager_);
1448  RunControlStateMachine::theProgressBar_.step();
1449 
1450  // important to give time for StartOTS script to recognize command (before user does
1451  // Startup again)
1452  for(int i = 0; i < 5; ++i)
1453  {
1454  sleep(1);
1455  RunControlStateMachine::theProgressBar_.step();
1456  }
1457 }
1458 
1459 //========================================================================================================================
1460 void GatewaySupervisor::transitionStartingUp(toolbox::Event::Reference e)
1461 
1462 {
1463  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1464 
1465  RunControlStateMachine::theProgressBar_.step();
1466  makeSystemLogbookEntry("System starting up.");
1467  RunControlStateMachine::theProgressBar_.step();
1468 
1469  // start all non-gateway contexts
1470  GatewaySupervisor::launchStartOTSCommand(
1471  "OTS_APP_STARTUP", CorePropertySupervisorBase::theConfigurationManager_);
1472  RunControlStateMachine::theProgressBar_.step();
1473 
1474  // important to give time for StartOTS script to recognize command and for apps to
1475  // instantiate things (before user does Initialize)
1476  for(int i = 0; i < 10; ++i)
1477  {
1478  sleep(1);
1479  RunControlStateMachine::theProgressBar_.step();
1480  }
1481 }
1482 
1483 //========================================================================================================================
1484 void GatewaySupervisor::transitionInitializing(toolbox::Event::Reference e)
1485 
1486 {
1487  __COUT__ << theStateMachine_.getCurrentStateName() << __E__;
1488 
1489  broadcastMessage(theStateMachine_.getCurrentMessage());
1490 
1491  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1492  __COUT__ << "Fsm current transition: "
1493  << theStateMachine_.getCurrentTransitionName(e->type()) << __E__;
1494  __COUT__ << "Fsm final state: "
1495  << theStateMachine_.getTransitionFinalStateName(e->type()) << __E__;
1496 }
1497 
1498 //========================================================================================================================
1499 void GatewaySupervisor::transitionPausing(toolbox::Event::Reference e)
1500 {
1501  checkForAsyncError();
1502 
1503  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1504 
1505  makeSystemLogbookEntry("Run pausing.");
1506 
1507  // the current message is not for Pause if its due to async failure
1508  if(RunControlStateMachine::asyncSoftFailureReceived_)
1509  {
1510  __COUT_ERR__ << "Broadcasting pause for async SOFT error!" << __E__;
1511  broadcastMessage(SOAPUtilities::makeSOAPMessageReference("Pause"));
1512  }
1513  else
1514  broadcastMessage(theStateMachine_.getCurrentMessage());
1515 }
1516 
1517 //========================================================================================================================
1518 void GatewaySupervisor::transitionResuming(toolbox::Event::Reference e)
1519 {
1520  if(RunControlStateMachine::asyncSoftFailureReceived_)
1521  {
1522  // clear async soft error
1523  __COUT_INFO__ << "Clearing async SOFT error!" << __E__;
1524  RunControlStateMachine::asyncSoftFailureReceived_ = false;
1525  }
1526 
1527  checkForAsyncError();
1528 
1529  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1530 
1531  makeSystemLogbookEntry("Run resuming.");
1532 
1533  broadcastMessage(theStateMachine_.getCurrentMessage());
1534 } // end transitionResuming()
1535 
1536 //========================================================================================================================
1537 void GatewaySupervisor::transitionStarting(toolbox::Event::Reference e)
1538 {
1539  if(RunControlStateMachine::asyncSoftFailureReceived_)
1540  {
1541  // clear async soft error
1542  __COUT_INFO__ << "Clearing async SOFT error!" << __E__;
1543  RunControlStateMachine::asyncSoftFailureReceived_ = false;
1544  }
1545 
1546  checkForAsyncError();
1547 
1548  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1549 
1550  SOAPParameters parameters("RunNumber");
1551  SOAPUtilities::receive(theStateMachine_.getCurrentMessage(), parameters);
1552 
1553  std::string runNumber = parameters.getValue("RunNumber");
1554  __COUTV__(runNumber);
1555 
1556  // check if configuration dump is enabled on configure transition
1557  {
1558  ConfigurationTree configLinkNode =
1559  CorePropertySupervisorBase::theConfigurationManager_->getSupervisorTableNode(
1560  supervisorContextUID_, supervisorApplicationUID_);
1561  if(!configLinkNode.isDisconnected())
1562  {
1563  try // errors in dump are not tolerated
1564  {
1565  bool dumpConfiguration = true;
1566  std::string dumpFilePath, dumpFileRadix, dumpFormat;
1567  try // for backwards compatibility
1568  {
1569  ConfigurationTree fsmLinkNode =
1570  configLinkNode.getNode("LinkToStateMachineTable")
1571  .getNode(activeStateMachineName_);
1572  dumpConfiguration =
1573  fsmLinkNode.getNode("EnableConfigurationDumpOnRunTransition")
1574  .getValue<bool>();
1575  dumpFilePath = fsmLinkNode.getNode("ConfigurationDumpOnRunFilePath")
1576  .getValue<std::string>();
1577  dumpFileRadix = fsmLinkNode.getNode("ConfigurationDumpOnRunFileRadix")
1578  .getValue<std::string>();
1579  dumpFormat = fsmLinkNode.getNode("ConfigurationDumpOnRunFormat")
1580  .getValue<std::string>();
1581  }
1582  catch(std::runtime_error& e)
1583  {
1584  __COUT_INFO__ << "FSM configuration dump Link disconnected." << __E__;
1585  dumpConfiguration = false;
1586  }
1587 
1588  if(dumpConfiguration)
1589  {
1590  // dump configuration
1591  CorePropertySupervisorBase::theConfigurationManager_
1592  ->dumpActiveConfiguration(dumpFilePath + "/" + dumpFileRadix +
1593  "_Run" + runNumber + "_" +
1594  std::to_string(time(0)) + ".dump",
1595  dumpFormat);
1596  }
1597  }
1598  catch(std::runtime_error& e)
1599  {
1600  __SS__ << "\nTransition to Running interrupted! There was an error "
1601  "identified "
1602  << "during the configuration dump attempt:\n\n " << e.what()
1603  << __E__;
1604  __COUT_ERR__ << "\n" << ss.str();
1605  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1606  return;
1607  }
1608  catch(...)
1609  {
1610  __SS__ << "\nTransition to Running interrupted! There was an error "
1611  "identified "
1612  << "during the configuration dump attempt.\n\n " << __E__;
1613  __COUT_ERR__ << "\n" << ss.str();
1614  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1615  return;
1616  }
1617  }
1618  }
1619 
1620  makeSystemLogbookEntry("Run " + runNumber + " starting.");
1621 
1622  broadcastMessage(theStateMachine_.getCurrentMessage());
1623 
1624  // save last started group name/key
1625  saveGroupNameAndKey(theConfigurationTableGroup_, FSM_LAST_STARTED_GROUP_ALIAS_FILE);
1626 }
1627 
1628 //========================================================================================================================
1629 void GatewaySupervisor::transitionStopping(toolbox::Event::Reference e)
1630 {
1631  checkForAsyncError();
1632 
1633  __COUT__ << "Fsm current state: " << theStateMachine_.getCurrentStateName() << __E__;
1634 
1635  makeSystemLogbookEntry("Run stopping.");
1636 
1637  broadcastMessage(theStateMachine_.getCurrentMessage());
1638 }
1639 
1643 
1644 //========================================================================================================================
1645 // handleBroadcastMessageTarget
1646 // Sends message and gets reply
1647 // Handles sub-iterations at same target
1648 // if failure, THROW state machine exception
1649 // returns true if iterations are done, else false
1650 bool GatewaySupervisor::handleBroadcastMessageTarget(const SupervisorInfo& appInfo,
1651  xoap::MessageReference message,
1652  const std::string& command,
1653  const unsigned int& iteration,
1654  std::string& reply,
1655  unsigned int threadIndex)
1656 {
1657  unsigned int subIteration = 0; // reset for next subIteration loop
1658  bool subIterationsDone = false;
1659  bool iterationsDone = true;
1660 
1661  while(!subIterationsDone) // start subIteration handling loop
1662  {
1663  subIterationsDone = true;
1664  RunControlStateMachine::theProgressBar_.step();
1665 
1666  // add subIteration index to message
1667  if(subIteration)
1668  {
1669  SOAPParameters parameters;
1670  parameters.addParameter("subIterationIndex", subIteration);
1671  SOAPUtilities::addParameters(message, parameters);
1672  }
1673 
1674  if(iteration || subIteration)
1675  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1676  << "Adding iteration parameters " << iteration << "." << subIteration
1677  << __E__;
1678 
1679  RunControlStateMachine::theProgressBar_.step();
1680 
1681  if(iteration == 0 && subIteration == 0)
1682  {
1683  for(unsigned int j = 0; j < 4; ++j)
1684  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1685  << "Sending message to Supervisor " << appInfo.getName()
1686  << " [LID=" << appInfo.getId() << "]: " << command << __E__;
1687  }
1688  else // else this not the first time through the supervisors
1689  {
1690  if(subIteration == 0)
1691  {
1692  for(unsigned int j = 0; j < 4; ++j)
1693  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1694  << "Sending message to Supervisor " << appInfo.getName()
1695  << " [LID=" << appInfo.getId() << "]: " << command
1696  << " (iteration: " << iteration << ")" << __E__;
1697  }
1698  else
1699  {
1700  for(unsigned int j = 0; j < 4; ++j)
1701  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1702  << "Sending message to Supervisor " << appInfo.getName()
1703  << " [LID=" << appInfo.getId() << "]: " << command
1704  << " (iteration: " << iteration
1705  << ", sub-iteration: " << subIteration << ")" << __E__;
1706  }
1707  }
1708 
1709  {
1710  // add the message index
1711  SOAPParameters parameters;
1712  { // mutex scope
1713  std::lock_guard<std::mutex> lock(broadcastCommandMessageIndexMutex_);
1714  parameters.addParameter("commandId", broadcastCommandMessageIndex_++);
1715  } // end mutex scope
1716  SOAPUtilities::addParameters(message, parameters);
1717  }
1718 
1719  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1720  << "Sending... \t" << SOAPUtilities::translate(message) << std::endl;
1721 
1722  try // attempt transmit of transition command
1723  {
1724  reply = send(appInfo.getDescriptor(), message);
1725  }
1726  catch(const xdaq::exception::Exception& e) // due to xoap send failure
1727  {
1728  // do not kill whole system if xdaq xoap failure
1729  __SS__ << "Error! Gateway Supervisor can NOT " << command
1730  << " Supervisor instance = '" << appInfo.getName()
1731  << "' [LID=" << appInfo.getId() << "] in Context '"
1732  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
1733  << "].\n\n"
1734  << "Xoap message failure. Did the target Supervisor crash? Try "
1735  "re-initializing or restarting otsdaq."
1736  << __E__;
1737  __COUT_ERR__ << ss.str();
1738  __MOUT_ERR__ << ss.str();
1739 
1740  try
1741  {
1742  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1743  << "Try again.." << __E__;
1744 
1745  {
1746  // add a second try parameter flag
1747  SOAPParameters parameters;
1748  parameters.addParameter("retransmission", "1");
1749  SOAPUtilities::addParameters(message, parameters);
1750  }
1751 
1752  {
1753  // add the message index
1754  SOAPParameters parameters;
1755  { // mutex scope
1756  std::lock_guard<std::mutex> lock(
1757  broadcastCommandMessageIndexMutex_);
1758  parameters.addParameter("commandId",
1759  broadcastCommandMessageIndex_++);
1760  } // end mutex scope
1761  SOAPUtilities::addParameters(message, parameters);
1762  }
1763 
1764  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1765  << "Re-Sending... " << SOAPUtilities::translate(message)
1766  << std::endl;
1767 
1768  reply = send(appInfo.getDescriptor(), message);
1769  }
1770  catch(const xdaq::exception::Exception& e) // due to xoap send failure
1771  {
1772  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1773  << "Second try failed.." << __E__;
1774  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1775  }
1776  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1777  << "2nd try passed.." << __E__;
1778  } // end send catch
1779 
1780  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1781  << "Reply received = " << reply << __E__;
1782 
1783  if((reply != command + "Done") && (reply != command + "Response") &&
1784  (reply != command + "Iterate") && (reply != command + "SubIterate"))
1785  {
1786  __SS__ << "Error! Gateway Supervisor can NOT " << command
1787  << " Supervisor instance = '" << appInfo.getName()
1788  << "' [LID=" << appInfo.getId() << "] in Context '"
1789  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
1790  << "].\n\n"
1791  << reply;
1792  __COUT_ERR__ << ss.str() << __E__;
1793  __MOUT_ERR__ << ss.str() << __E__;
1794 
1795  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1796  << "Getting error message..." << __E__;
1797  try
1798  {
1799  xoap::MessageReference errorMessage =
1800  sendWithSOAPReply(appInfo.getDescriptor(),
1801  SOAPUtilities::makeSOAPMessageReference(
1802  "StateMachineErrorMessageRequest"));
1803  SOAPParameters parameters;
1804  parameters.addParameter("ErrorMessage");
1805  SOAPUtilities::receive(errorMessage, parameters);
1806 
1807  std::string error = parameters.getValue("ErrorMessage");
1808  if(error == "")
1809  {
1810  std::stringstream err;
1811  err << "Unknown error from Supervisor instance = '"
1812  << appInfo.getName() << "' [LID=" << appInfo.getId()
1813  << "] in Context '" << appInfo.getContextName()
1814  << "' [URL=" << appInfo.getURL()
1815  << "]. If the problem persists or is repeatable, please notify "
1816  "admins.\n\n";
1817  error = err.str();
1818  }
1819 
1820  __SS__ << "Received error from Supervisor instance = '"
1821  << appInfo.getName() << "' [LID=" << appInfo.getId()
1822  << "] in Context '" << appInfo.getContextName()
1823  << "' [URL=" << appInfo.getURL()
1824  << "].\n\n Error Message = " << error << __E__;
1825 
1826  __COUT_ERR__ << ss.str() << __E__;
1827  __MOUT_ERR__ << ss.str() << __E__;
1828 
1829  if(command == "Error")
1830  return true; // do not throw exception and exit loop if informing all
1831  // apps about error
1832  // else throw exception and go into Error
1833  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1834  }
1835  catch(const xdaq::exception::Exception& e) // due to xoap send failure
1836  {
1837  // do not kill whole system if xdaq xoap failure
1838  __SS__ << "Error! Gateway Supervisor failed to read error message from "
1839  "Supervisor instance = '"
1840  << appInfo.getName() << "' [LID=" << appInfo.getId()
1841  << "] in Context '" << appInfo.getContextName()
1842  << "' [URL=" << appInfo.getURL() << "].\n\n"
1843  << "Xoap message failure. Did the target Supervisor crash? Try "
1844  "re-initializing or restarting otsdaq."
1845  << __E__;
1846  __COUT_ERR__ << ss.str();
1847  __MOUT_ERR__ << ss.str();
1848  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
1849  }
1850  } // end error response handling
1851  else if(reply == command + "Iterate")
1852  {
1853  // when 'Working' this front-end is expecting
1854  // to get the same command again with an incremented iteration index
1855  // after all other front-ends see the same iteration index, and all
1856  // front-ends with higher priority see the incremented iteration index.
1857 
1858  iterationsDone = false;
1859  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1860  << "Supervisor instance = '" << appInfo.getName()
1861  << "' [LID=" << appInfo.getId() << "] in Context '"
1862  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
1863  << "] flagged for another iteration to " << command
1864  << "... (iteration: " << iteration << ")" << __E__;
1865 
1866  } // end still working response handling
1867  else if(reply == command + "SubIterate")
1868  {
1869  // when 'Working' this front-end is expecting
1870  // to get the same command again with an incremented sub-iteration index
1871  // without any other front-ends taking actions or seeing the sub-iteration
1872  // index.
1873 
1874  subIterationsDone = false;
1875  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1876  << "Supervisor instance = '" << appInfo.getName()
1877  << "' [LID=" << appInfo.getId() << "] in Context '"
1878  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
1879  << "] flagged for another sub-iteration to " << command
1880  << "... (iteration: " << iteration
1881  << ", sub-iteration: " << subIteration << ")" << __E__;
1882  }
1883  else // else success response
1884  {
1885  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1886  << "Supervisor instance = '" << appInfo.getName()
1887  << "' [LID=" << appInfo.getId() << "] in Context '"
1888  << appInfo.getContextName() << "' [URL=" << appInfo.getURL()
1889  << "] was " << command << "'d correctly!" << __E__;
1890  }
1891 
1892  if(subIteration)
1893  __COUT__ << "Broadcast thread " << threadIndex << "\t"
1894  << "Completed sub-iteration: " << subIteration << __E__;
1895  ++subIteration;
1896 
1897  } // end subIteration handling loop
1898 
1899  return iterationsDone;
1900 
1901 } // end handleBroadcastMessageTarget()
1902 
1903 //========================================================================================================================
1904 // broadcastMessageThread
1905 // Sends message and gets reply
1906 // if failure, reply THROW
1907 void GatewaySupervisor::broadcastMessageThread(
1908  GatewaySupervisor* supervisorPtr,
1909  GatewaySupervisor::BroadcastThreadStruct* threadStruct)
1910 {
1911  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
1912  << "starting..." << __E__;
1913 
1914  while(!threadStruct->exitThread_)
1915  {
1916  // sleep to give time to main thread to dole out work
1917  usleep(1000 /* 1ms */);
1918 
1919  // take lock for remainder of scope
1920  std::lock_guard<std::mutex> lock(threadStruct->threadMutex);
1921  if(threadStruct->workToDo_)
1922  {
1923  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
1924  << "starting work..." << __E__;
1925 
1926  try
1927  {
1928  if(supervisorPtr->handleBroadcastMessageTarget(
1929  threadStruct->getAppInfo(),
1930  threadStruct->getMessage(),
1931  threadStruct->getCommand(),
1932  threadStruct->getIteration(),
1933  threadStruct->getReply(),
1934  threadStruct->threadIndex_))
1935  threadStruct->getIterationsDone() = true;
1936  }
1937  catch(toolbox::fsm::exception::Exception e)
1938  {
1939  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
1940  << "going into error: " << e.what() << __E__;
1941 
1942  threadStruct->getReply() = e.what();
1943  threadStruct->error_ = true;
1944  threadStruct->workToDo_ = false;
1945  threadStruct->working_ = false; // indicate exiting
1946  return;
1947  }
1948 
1949  if(!threadStruct->getIterationsDone())
1950  {
1951  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
1952  << "flagged for another iteration." << __E__;
1953 
1954  // set global iterationsDone
1955  std::lock_guard<std::mutex> lock(
1956  supervisorPtr->broadcastIterationsDoneMutex_);
1957  supervisorPtr->broadcastIterationsDone_ = false;
1958  }
1959 
1960  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
1961  << "done with work." << __E__;
1962 
1963  threadStruct->workToDo_ = false;
1964  } // end work
1965 
1966  } // end primary while loop
1967 
1968  __COUT__ << "Broadcast thread " << threadStruct->threadIndex_ << "\t"
1969  << "exited." << __E__;
1970  threadStruct->working_ = false; // indicate exiting
1971 } // end broadcastMessageThread()
1972 
1973 //========================================================================================================================
1974 // broadcastMessage
1975 // Broadcast state transition to all xdaq Supervisors.
1976 // - Transition in order of priority as given by AllSupervisorInfo
1977 // Update Supervisor Info based on result of transition.
1978 void GatewaySupervisor::broadcastMessage(xoap::MessageReference message)
1979 {
1980  RunControlStateMachine::theProgressBar_.step();
1981 
1982  // transition of Gateway Supervisor is assumed successful so update status
1983  allSupervisorInfo_.setSupervisorStatus(this, theStateMachine_.getCurrentStateName());
1984 
1985  std::string command = SOAPUtilities::translate(message).getCommand();
1986 
1987  std::string reply;
1988  broadcastIterationsDone_ = false;
1989  bool assignedJob;
1990 
1991  std::vector<std::vector<const SupervisorInfo*>> orderedSupervisors;
1992 
1993  try
1994  {
1995  orderedSupervisors = allSupervisorInfo_.getOrderedSupervisorDescriptors(command);
1996  }
1997  catch(const std::runtime_error& e)
1998  {
1999  __SS__ << "Error getting supervisor priority. Was there a change in the context?"
2000  << " Remember, if the context was changed, it is safest to relaunch "
2001  "StartOTS.sh. "
2002  << e.what() << __E__;
2003  XCEPT_RAISE(toolbox::fsm::exception::Exception, ss.str());
2004  }
2005 
2006  RunControlStateMachine::theProgressBar_.step();
2007 
2008  // std::vector<std::vector<uint8_t/*bool*/>> supervisorIterationsDone; //Note: can not
2009  // use bool because std::vector does not allow access by reference of type bool
2010  GatewaySupervisor::BroadcastMessageIterationsDoneStruct supervisorIterationsDone;
2011 
2012  // initialize to false (not done)
2013  for(const auto& vectorAtPriority : orderedSupervisors)
2014  supervisorIterationsDone.push(
2015  vectorAtPriority.size()); // push_back(
2016  // std::vector<uint8_t>(vectorAtPriority.size(),
2017  // false /*initial value*/));
2018 
2019  unsigned int iteration = 0;
2020  unsigned int subIteration;
2021 
2022  // send command to all supervisors (for multiple iterations) until all are done
2023 
2024  // make a copy of the message to use as starting point for iterations
2025  xoap::MessageReference originalMessage =
2026  SOAPUtilities::makeSOAPMessageReference(SOAPUtilities::translate(message));
2027 
2028  __COUT__ << "=========> Broadcasting state machine command = " << command << __E__;
2029 
2030  unsigned int numberOfThreads = 1;
2031 
2032  try
2033  {
2034  numberOfThreads = CorePropertySupervisorBase::getSupervisorTableNode()
2035  .getNode("NumberOfStateMachineBroadcastThreads")
2036  .getValue<unsigned int>();
2037  }
2038  catch(...)
2039  {
2040  // ignore error for backwards compatibility
2041  __COUT__ << "Number of threads not in configuration, so defaulting to "
2042  << numberOfThreads << __E__;
2043  }
2044 
2045  // Note: if 1 thread, then create no threads
2046  // i.e. only create threads if 2 or more.
2047  if(numberOfThreads == 1)
2048  numberOfThreads = 0;
2049 
2050  __COUTV__(numberOfThreads);
2051 
2052  std::vector<GatewaySupervisor::BroadcastThreadStruct> broadcastThreadStructs(
2053  numberOfThreads);
2054 
2055  // only launch threads if more than 1
2056  // if 1, just use main thread
2057  for(unsigned int i = 0; i < numberOfThreads; ++i)
2058  {
2059  broadcastThreadStructs[i].threadIndex_ = i;
2060 
2061  std::thread(
2062  [](GatewaySupervisor* supervisorPtr,
2063  GatewaySupervisor::BroadcastThreadStruct* threadStruct) {
2064  GatewaySupervisor::broadcastMessageThread(supervisorPtr, threadStruct);
2065  },
2066  this,
2067  &broadcastThreadStructs[i])
2068  .detach();
2069  } // end broadcast thread creation loop
2070 
2071  RunControlStateMachine::theProgressBar_.step();
2072 
2073  try
2074  {
2075  //:::::::::::::::::::::::::::::::::::::::::::::::::::::
2076  // Send a SOAP message to every Supervisor in order by priority
2077  do // while !iterationsDone
2078  {
2079  broadcastIterationsDone_ = true;
2080 
2081  if(iteration)
2082  __COUT__ << "Starting iteration: " << iteration << __E__;
2083 
2084  for(unsigned int i = 0; i < supervisorIterationsDone.size(); ++i)
2085  {
2086  for(unsigned int j = 0; j < supervisorIterationsDone.size(i); ++j)
2087  {
2088  checkForAsyncError();
2089 
2090  if(supervisorIterationsDone[i][j])
2091  continue; // skip if supervisor is already done
2092 
2093  const SupervisorInfo& appInfo = *(orderedSupervisors[i][j]);
2094 
2095  // re-acquire original message
2096  message = SOAPUtilities::makeSOAPMessageReference(
2097  SOAPUtilities::translate(originalMessage));
2098 
2099  // add iteration index to message
2100  if(iteration)
2101  {
2102  // add the iteration index as a parameter to message
2103  SOAPParameters parameters;
2104  parameters.addParameter("iterationIndex", iteration);
2105  SOAPUtilities::addParameters(message, parameters);
2106  }
2107 
2108  if(numberOfThreads)
2109  {
2110  // schedule message to first open thread
2111  assignedJob = false;
2112  do
2113  {
2114  for(unsigned int k = 0; k < numberOfThreads; ++k)
2115  {
2116  if(!broadcastThreadStructs[k].workToDo_)
2117  {
2118  // found our thread!
2119  assignedJob = true;
2120  __COUT__ << "Giving work to thread " << k << __E__;
2121 
2122  std::lock_guard<std::mutex> lock(
2123  broadcastThreadStructs[k].threadMutex);
2124  broadcastThreadStructs[k].setMessage(
2125  appInfo,
2126  message,
2127  command,
2128  iteration,
2129  supervisorIterationsDone[i][j]);
2130 
2131  break;
2132  }
2133  } // end thread assigning search
2134 
2135  if(!assignedJob)
2136  {
2137  __COUT__ << "No free broadcast threads, "
2138  << "waiting for an available thread..." << __E__;
2139  usleep(100 * 1000 /*100 ms*/);
2140  }
2141  } while(!assignedJob);
2142  }
2143  else // no thread
2144  {
2145  if(handleBroadcastMessageTarget(
2146  appInfo, message, command, iteration, reply))
2147  supervisorIterationsDone[i][j] = true;
2148  else
2149  broadcastIterationsDone_ = false;
2150  }
2151 
2152  } // end supervisors at same priority broadcast loop
2153 
2154  // before proceeding to next priority,
2155  // make sure all threads have completed
2156  if(numberOfThreads)
2157  {
2158  __COUT__
2159  << "Done with priority level. Waiting for threads to finish..."
2160  << __E__;
2161  bool done;
2162  do
2163  {
2164  done = true;
2165  for(unsigned int i = 0; i < numberOfThreads; ++i)
2166  if(broadcastThreadStructs[i].workToDo_)
2167  {
2168  done = false;
2169  __COUT__ << "Still waiting on thread " << i << "..."
2170  << __E__;
2171  usleep(100 * 1000 /*100ms*/);
2172  break;
2173  }
2174  else if(broadcastThreadStructs[i].error_)
2175  {
2176  __COUT__ << "Found thread in error! Throwing state "
2177  "machine error: "
2178  << broadcastThreadStructs[i].getReply() << __E__;
2179  XCEPT_RAISE(toolbox::fsm::exception::Exception,
2180  broadcastThreadStructs[i].getReply());
2181  }
2182  } while(!done);
2183  __COUT__ << "All threads done with priority level work." << __E__;
2184  } // end thread complete verification
2185 
2186  } // end supervisor broadcast loop for each priority
2187 
2188  // if (!proceed)
2189  // {
2190  // __COUT__ << "Breaking out of primary loop." << __E__;
2191  // break;
2192  // }
2193 
2194  if(iteration || !broadcastIterationsDone_)
2195  __COUT__ << "Completed iteration: " << iteration << __E__;
2196  ++iteration;
2197 
2198  } while(!broadcastIterationsDone_);
2199 
2200  RunControlStateMachine::theProgressBar_.step();
2201  } // end main transition broadcast try
2202  catch(...)
2203  {
2204  __COUT__ << "Exception caught, exiting broadcast threads..." << __E__;
2205 
2206  // attempt to exit threads
2207  // The threads should already be done with all work.
2208  // If broadcastMessage scope ends, then the
2209  // thread struct will be destructed, and the thread will
2210  // crash on next access attempt (thought we probably do not care).
2211  for(unsigned int i = 0; i < numberOfThreads; ++i)
2212  broadcastThreadStructs[i].exitThread_ = true;
2213  usleep(100 * 1000 /*100ms*/); // sleep for exit time
2214 
2215  throw; // re-throw
2216  }
2217 
2218  if(numberOfThreads)
2219  {
2220  __COUT__ << "All transitions completed. Wrapping up, exiting broadcast threads..."
2221  << __E__;
2222 
2223  // attempt to exit threads
2224  // The threads should already be done with all work.
2225  // If broadcastMessage scope ends, then the
2226  // thread struct will be destructed, and the thread will
2227  // crash on next access attempt (when the thread crashes, the whole context
2228  // crashes).
2229  for(unsigned int i = 0; i < numberOfThreads; ++i)
2230  broadcastThreadStructs[i].exitThread_ = true;
2231  usleep(100 * 1000 /*100ms*/); // sleep for exit time
2232  }
2233 
2234  __COUT__ << "Broadcast complete." << __E__;
2235 } // end broadcastMessage()
2236 
2237 //========================================================================================================================
2238 void GatewaySupervisor::wait(int milliseconds, std::string who) const
2239 {
2240  for(int s = 1; s <= milliseconds; s++)
2241  {
2242  usleep(1000);
2243 
2244  if(s % 100 == 0)
2245  __COUT__ << s << " msecs " << who << __E__;
2246  }
2247 }
2248 
2249 //========================================================================================================================
2250 // LoginRequest
2251 // handles all users login/logout actions from web GUI.
2252 // NOTE: there are two ways for a user to be logged out: timeout or manual logout
2253 // System logbook messages are generated for login and logout
2254 void GatewaySupervisor::loginRequest(xgi::Input* in, xgi::Output* out)
2255 {
2256  __COUT__ << "Start" << __E__;
2257  cgicc::Cgicc cgi(in);
2258  std::string Command = CgiDataUtilities::getData(cgi, "RequestType");
2259  __COUT__ << "*** Login RequestType = " << Command << __E__;
2260 
2261  // RequestType Commands:
2262  // login
2263  // sessionId
2264  // checkCookie
2265  // logout
2266 
2267  // always cleanup expired entries and get a vector std::string of logged out users
2268  std::vector<std::string> loggedOutUsernames;
2269  theWebUsers_.cleanupExpiredEntries(&loggedOutUsernames);
2270  for(unsigned int i = 0; i < loggedOutUsernames.size();
2271  ++i) // Log logout for logged out users
2272  makeSystemLogbookEntry(loggedOutUsernames[i] + " login timed out.");
2273 
2274  if(Command == "sessionId")
2275  {
2276  // When client loads page, client submits unique user id and receives random
2277  // sessionId from server Whenever client submits user name and password it is
2278  // jumbled by sessionId when sent to server and sent along with UUID. Server uses
2279  // sessionId to unjumble.
2280  //
2281  // Server maintains list of active sessionId by UUID
2282  // sessionId expires after set time if no login attempt (e.g. 5 minutes)
2283  std::string uuid = CgiDataUtilities::postData(cgi, "uuid");
2284 
2285  std::string sid = theWebUsers_.createNewLoginSession(
2286  uuid, cgi.getEnvironment().getRemoteAddr() /* ip */);
2287 
2288  // __COUT__ << "uuid = " << uuid << __E__;
2289  // __COUT__ << "SessionId = " << sid.substr(0, 10) << __E__;
2290  *out << sid;
2291  }
2292  else if(Command == "checkCookie")
2293  {
2294  uint64_t uid;
2295  std::string uuid;
2296  std::string jumbledUser;
2297  std::string cookieCode;
2298 
2299  // If client has a cookie, client submits cookie and username, jumbled, to see if
2300  // cookie and user are still active if active, valid cookie code is returned
2301  // and name to display, in XML
2302  // if not, return 0
2303  // params:
2304  // uuid - unique user id, to look up sessionId
2305  // ju - jumbled user name
2306  // CookieCode - cookie code to check
2307 
2308  uuid = CgiDataUtilities::postData(cgi, "uuid");
2309  jumbledUser = CgiDataUtilities::postData(cgi, "ju");
2310  cookieCode = CgiDataUtilities::postData(cgi, "cc");
2311 
2312  // __COUT__ << "uuid = " << uuid << __E__;
2313  // __COUT__ << "Cookie Code = " << cookieCode.substr(0, 10) << __E__;
2314  // __COUT__ << "jumbledUser = " << jumbledUser.substr(0, 10) << __E__;
2315 
2316  // If cookie code is good, then refresh and return with display name, else return
2317  // 0 as CookieCode value
2318  uid = theWebUsers_.isCookieCodeActiveForLogin(
2319  uuid,
2320  cookieCode,
2321  jumbledUser); // after call jumbledUser holds displayName on success
2322 
2323  if(uid == theWebUsers_.NOT_FOUND_IN_DATABASE)
2324  {
2325  __COUT__ << "cookieCode invalid" << __E__;
2326  jumbledUser = ""; // clear display name if failure
2327  cookieCode = "0"; // clear cookie code if failure
2328  }
2329  else
2330  __COUT__ << "cookieCode is good." << __E__;
2331 
2332  // return xml holding cookie code and display name
2333  HttpXmlDocument xmldoc(cookieCode, jumbledUser);
2334 
2335  theWebUsers_.insertSettingsForUser(uid, &xmldoc); // insert settings
2336 
2337  xmldoc.outputXmlDocument((std::ostringstream*)out);
2338  }
2339  else if(Command == "login")
2340  {
2341  // If login attempt or create account, jumbled user and pw are submitted
2342  // if successful, valid cookie code and display name returned.
2343  // if not, return 0
2344  // params:
2345  // uuid - unique user id, to look up sessionId
2346  // nac - new account code for first time logins
2347  // ju - jumbled user name
2348  // jp - jumbled password
2349 
2350  std::string uuid = CgiDataUtilities::postData(cgi, "uuid");
2351  std::string newAccountCode = CgiDataUtilities::postData(cgi, "nac");
2352  std::string jumbledUser = CgiDataUtilities::postData(cgi, "ju");
2353  std::string jumbledPw = CgiDataUtilities::postData(cgi, "jp");
2354 
2355  // __COUT__ << "jumbledUser = " << jumbledUser.substr(0, 10) << __E__;
2356  // __COUT__ << "jumbledPw = " << jumbledPw.substr(0, 10) << __E__;
2357  // __COUT__ << "uuid = " << uuid << __E__;
2358  // __COUT__ << "nac =-" << newAccountCode << "-" << __E__;
2359 
2360  uint64_t uid = theWebUsers_.attemptActiveSession(
2361  uuid,
2362  jumbledUser,
2363  jumbledPw,
2364  newAccountCode,
2365  cgi.getEnvironment()
2366  .getRemoteAddr()); // after call jumbledUser holds displayName on success
2367 
2368  if(uid == theWebUsers_.NOT_FOUND_IN_DATABASE)
2369  {
2370  __COUT__ << "Login invalid." << __E__;
2371  jumbledUser = ""; // clear display name if failure
2372  if(newAccountCode != "1") // indicates uuid not found
2373  newAccountCode = "0"; // clear cookie code if failure
2374  }
2375  else // Log login in logbook for active experiment
2376  makeSystemLogbookEntry(theWebUsers_.getUsersUsername(uid) + " logged in.");
2377 
2378  //__COUT__ << "new cookieCode = " << newAccountCode.substr(0, 10) << __E__;
2379 
2380  HttpXmlDocument xmldoc(newAccountCode, jumbledUser);
2381 
2382  theWebUsers_.insertSettingsForUser(uid, &xmldoc); // insert settings
2383 
2384  // insert active session count for user
2385 
2386  if(uid != theWebUsers_.NOT_FOUND_IN_DATABASE)
2387  {
2388  uint64_t asCnt = theWebUsers_.getActiveSessionCountForUser(uid) -
2389  1; // subtract 1 to remove just started session from count
2390  char asStr[20];
2391  sprintf(asStr, "%lu", asCnt);
2392  xmldoc.addTextElementToData("user_active_session_count", asStr);
2393  }
2394 
2395  xmldoc.outputXmlDocument((std::ostringstream*)out);
2396  }
2397  else if(Command == "cert")
2398  {
2399  // If login attempt or create account, jumbled user and pw are submitted
2400  // if successful, valid cookie code and display name returned.
2401  // if not, return 0
2402  // params:
2403  // uuid - unique user id, to look up sessionId
2404  // nac - new account code for first time logins
2405  // ju - jumbled user name
2406  // jp - jumbled password
2407 
2408  std::string uuid = CgiDataUtilities::postData(cgi, "uuid");
2409  std::string jumbledEmail =
2410  cgicc::form_urldecode(CgiDataUtilities::getData(cgi, "httpsUser"));
2411  std::string username = "";
2412  std::string cookieCode = "";
2413 
2414  // __COUT__ << "CERTIFICATE LOGIN REUEST RECEVIED!!!" << __E__;
2415  // __COUT__ << "jumbledEmail = " << jumbledEmail << __E__;
2416  // __COUT__ << "uuid = " << uuid << __E__;
2417 
2418  uint64_t uid = theWebUsers_.attemptActiveSessionWithCert(
2419  uuid,
2420  jumbledEmail,
2421  cookieCode,
2422  username,
2423  cgi.getEnvironment()
2424  .getRemoteAddr()); // after call jumbledUser holds displayName on success
2425 
2426  if(uid == theWebUsers_.NOT_FOUND_IN_DATABASE)
2427  {
2428  __COUT__ << "cookieCode invalid" << __E__;
2429  jumbledEmail = ""; // clear display name if failure
2430  if(cookieCode != "1") // indicates uuid not found
2431  cookieCode = "0"; // clear cookie code if failure
2432  }
2433  else // Log login in logbook for active experiment
2434  makeSystemLogbookEntry(theWebUsers_.getUsersUsername(uid) + " logged in.");
2435 
2436  //__COUT__ << "new cookieCode = " << cookieCode.substr(0, 10) << __E__;
2437 
2438  HttpXmlDocument xmldoc(cookieCode, jumbledEmail);
2439 
2440  theWebUsers_.insertSettingsForUser(uid, &xmldoc); // insert settings
2441 
2442  // insert active session count for user
2443 
2444  if(uid != theWebUsers_.NOT_FOUND_IN_DATABASE)
2445  {
2446  uint64_t asCnt = theWebUsers_.getActiveSessionCountForUser(uid) -
2447  1; // subtract 1 to remove just started session from count
2448  char asStr[20];
2449  sprintf(asStr, "%lu", asCnt);
2450  xmldoc.addTextElementToData("user_active_session_count", asStr);
2451  }
2452 
2453  xmldoc.outputXmlDocument((std::ostringstream*)out);
2454  }
2455  else if(Command == "logout")
2456  {
2457  std::string cookieCode = CgiDataUtilities::postData(cgi, "CookieCode");
2458  std::string logoutOthers = CgiDataUtilities::postData(cgi, "LogoutOthers");
2459 
2460  // __COUT__ << "Cookie Code = " << cookieCode.substr(0, 10) << __E__;
2461  // __COUT__ << "logoutOthers = " << logoutOthers << __E__;
2462 
2463  uint64_t uid; // get uid for possible system logbook message
2464  if(theWebUsers_.cookieCodeLogout(cookieCode,
2465  logoutOthers == "1",
2466  &uid,
2467  cgi.getEnvironment().getRemoteAddr()) !=
2468  theWebUsers_.NOT_FOUND_IN_DATABASE) // user logout
2469  {
2470  // if did some logging out, check if completely logged out
2471  // if so, system logbook message should be made.
2472  if(!theWebUsers_.isUserIdActive(uid))
2473  makeSystemLogbookEntry(theWebUsers_.getUsersUsername(uid) +
2474  " logged out.");
2475  }
2476  }
2477  else
2478  {
2479  __COUT__ << "Invalid Command" << __E__;
2480  *out << "0";
2481  }
2482 
2483  __COUT__ << "Done" << __E__;
2484 } // end loginRequest()
2485 
2486 //========================================================================================================================
2487 void GatewaySupervisor::tooltipRequest(xgi::Input* in, xgi::Output* out)
2488 {
2489  __COUT__ << "Start" << __E__;
2490  cgicc::Cgicc cgi(in);
2491 
2492  std::string Command = CgiDataUtilities::getData(cgi, "RequestType");
2493  __COUT__ << "Tooltip RequestType = " << Command << __E__;
2494 
2495  //**** start LOGIN GATEWAY CODE ***//
2496  // If TRUE, cookie code is good, and refreshed code is in cookieCode, also pointers
2497  // optionally for uint8_t userPermissions, uint64_t uid Else, error message is
2498  // returned in cookieCode Notes: cookie code not refreshed if RequestType =
2499  // getSystemMessages
2500  std::string cookieCode = CgiDataUtilities::postData(cgi, "CookieCode");
2501  uint64_t uid;
2502 
2503  if(!theWebUsers_.cookieCodeIsActiveForRequest(
2504  cookieCode, 0 /*userPermissions*/, &uid, "0" /*dummy ip*/, false /*refresh*/))
2505  {
2506  *out << cookieCode;
2507  return;
2508  }
2509 
2510  //**** end LOGIN GATEWAY CODE ***//
2511 
2512  HttpXmlDocument xmldoc(cookieCode);
2513 
2514  if(Command == "check")
2515  {
2516  WebUsers::tooltipCheckForUsername(theWebUsers_.getUsersUsername(uid),
2517  &xmldoc,
2518  CgiDataUtilities::getData(cgi, "srcFile"),
2519  CgiDataUtilities::getData(cgi, "srcFunc"),
2520  CgiDataUtilities::getData(cgi, "srcId"));
2521  }
2522  else if(Command == "setNeverShow")
2523  {
2524  WebUsers::tooltipSetNeverShowForUsername(
2525  theWebUsers_.getUsersUsername(uid),
2526  &xmldoc,
2527  CgiDataUtilities::getData(cgi, "srcFile"),
2528  CgiDataUtilities::getData(cgi, "srcFunc"),
2529  CgiDataUtilities::getData(cgi, "srcId"),
2530  CgiDataUtilities::getData(cgi, "doNeverShow") == "1" ? true : false,
2531  CgiDataUtilities::getData(cgi, "temporarySilence") == "1" ? true : false);
2532  }
2533  else
2534  __COUT__ << "Command Request, " << Command << ", not recognized." << __E__;
2535 
2536  xmldoc.outputXmlDocument((std::ostringstream*)out, false, true);
2537 
2538  __COUT__ << "Done" << __E__;
2539 } // end tooltipRequest
2540 
2541 //========================================================================================================================
2542 // setSupervisorPropertyDefaults
2543 // override to set defaults for supervisor property values (before user settings
2544 // override)
2545 void GatewaySupervisor::setSupervisorPropertyDefaults()
2546 {
2547  CorePropertySupervisorBase::setSupervisorProperty(
2548  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
2549  std::string() + "*=1 | gatewayLaunchOTS=-1 | gatewayLaunchWiz=-1");
2550 }
2551 
2552 //========================================================================================================================
2553 // forceSupervisorPropertyValues
2554 // override to force supervisor property values (and ignore user settings)
2555 void GatewaySupervisor::forceSupervisorPropertyValues()
2556 {
2557  // note used by these handlers:
2558  // request()
2559  // stateMachineXgiHandler() -- prepend StateMachine to request type
2560 
2561  CorePropertySupervisorBase::setSupervisorProperty(
2562  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
2563  "getSystemMessages | getCurrentState | getIterationPlanStatus");
2564  CorePropertySupervisorBase::setSupervisorProperty(
2565  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes,
2566  "gatewayLaunchOTS | gatewayLaunchWiz");
2567  // CorePropertySupervisorBase::setSupervisorProperty(CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NeedUsernameRequestTypes,
2568  // "StateMachine*"); //for all stateMachineXgiHandler requests
2569 }
2570 
2571 //========================================================================================================================
2572 void GatewaySupervisor::request(xgi::Input* in, xgi::Output* out)
2573 {
2574  //__COUT__ << "Start" << __E__;
2575 
2576  // for simplicity assume all commands should be mutually exclusive with iterator
2577  // thread state machine accesses (really should just be careful with
2578  // RunControlStateMachine access)
2579  if(VERBOSE_MUTEX)
2580  __COUT__ << "Waiting for FSM access" << __E__;
2581  std::lock_guard<std::mutex> lock(stateMachineAccessMutex_);
2582  if(VERBOSE_MUTEX)
2583  __COUT__ << "Have FSM access" << __E__;
2584 
2585  cgicc::Cgicc cgiIn(in);
2586 
2587  std::string requestType = CgiDataUtilities::getData(cgiIn, "RequestType");
2588 
2589  HttpXmlDocument xmlOut;
2590  WebUsers::RequestUserInfo userInfo(requestType,
2591  CgiDataUtilities::postData(cgiIn, "CookieCode"));
2592 
2593  CorePropertySupervisorBase::getRequestUserInfo(userInfo);
2594 
2595  if(!theWebUsers_.xmlRequestOnGateway(cgiIn, out, &xmlOut, userInfo))
2596  return; // access failed
2597 
2598  // RequestType Commands:
2599  // getSettings
2600  // setSettings
2601  // accountSettings
2602  // getAliasList
2603  // getFecList
2604  // getSystemMessages
2605  // setUserWithLock
2606  // getStateMachine
2607  // stateMatchinePreferences
2608  // getStateMachineNames
2609  // getCurrentState
2610  // cancelStateMachineTransition
2611  // getIterationPlanStatus
2612  // getErrorInStateMatchine
2613  // getDesktopIcons
2614 
2615  // resetUserTooltips
2616 
2617  // gatewayLaunchOTS
2618  // gatewayLaunchWiz
2619 
2620  try
2621  {
2622  if(requestType == "getSettings")
2623  {
2624  std::string accounts = CgiDataUtilities::getData(cgiIn, "accounts");
2625 
2626  __COUT__ << "Get Settings Request" << __E__;
2627  __COUT__ << "accounts = " << accounts << __E__;
2628  theWebUsers_.insertSettingsForUser(userInfo.uid_, &xmlOut, accounts == "1");
2629  }
2630  else if(requestType == "setSettings")
2631  {
2632  std::string bgcolor = CgiDataUtilities::postData(cgiIn, "bgcolor");
2633  std::string dbcolor = CgiDataUtilities::postData(cgiIn, "dbcolor");
2634  std::string wincolor = CgiDataUtilities::postData(cgiIn, "wincolor");
2635  std::string layout = CgiDataUtilities::postData(cgiIn, "layout");
2636  std::string syslayout = CgiDataUtilities::postData(cgiIn, "syslayout");
2637 
2638  __COUT__ << "Set Settings Request" << __E__;
2639  __COUT__ << "bgcolor = " << bgcolor << __E__;
2640  __COUT__ << "dbcolor = " << dbcolor << __E__;
2641  __COUT__ << "wincolor = " << wincolor << __E__;
2642  __COUT__ << "layout = " << layout << __E__;
2643  __COUT__ << "syslayout = " << syslayout << __E__;
2644 
2645  theWebUsers_.changeSettingsForUser(
2646  userInfo.uid_, bgcolor, dbcolor, wincolor, layout, syslayout);
2647  theWebUsers_.insertSettingsForUser(
2648  userInfo.uid_, &xmlOut, true); // include user accounts
2649  }
2650  else if(requestType == "accountSettings")
2651  {
2652  std::string type = CgiDataUtilities::postData(
2653  cgiIn, "type"); // updateAccount, createAccount, deleteAccount
2654  int type_int = -1;
2655 
2656  if(type == "updateAccount")
2657  type_int = 0;
2658  else if(type == "createAccount")
2659  type_int = 1;
2660  else if(type == "deleteAccount")
2661  type_int = 2;
2662 
2663  std::string username = CgiDataUtilities::postData(cgiIn, "username");
2664  std::string displayname = CgiDataUtilities::postData(cgiIn, "displayname");
2665  std::string email = CgiDataUtilities::postData(cgiIn, "useremail");
2666  std::string permissions = CgiDataUtilities::postData(cgiIn, "permissions");
2667  std::string accounts = CgiDataUtilities::getData(cgiIn, "accounts");
2668 
2669  __COUT__ << "accountSettings Request" << __E__;
2670  __COUT__ << "type = " << type << " - " << type_int << __E__;
2671  __COUT__ << "username = " << username << __E__;
2672  __COUT__ << "useremail = " << email << __E__;
2673  __COUT__ << "displayname = " << displayname << __E__;
2674  __COUT__ << "permissions = " << permissions << __E__;
2675 
2676  theWebUsers_.modifyAccountSettings(
2677  userInfo.uid_, type_int, username, displayname, email, permissions);
2678 
2679  __COUT__ << "accounts = " << accounts << __E__;
2680 
2681  theWebUsers_.insertSettingsForUser(userInfo.uid_, &xmlOut, accounts == "1");
2682  }
2683  else if(requestType == "stateMatchinePreferences")
2684  {
2685  std::string set = CgiDataUtilities::getData(cgiIn, "set");
2686  const std::string DEFAULT_FSM_VIEW = "Default_FSM_View";
2687  if(set == "1")
2688  theWebUsers_.setGenericPreference(
2689  userInfo.uid_,
2690  DEFAULT_FSM_VIEW,
2691  CgiDataUtilities::getData(cgiIn, DEFAULT_FSM_VIEW));
2692  else
2693  theWebUsers_.getGenericPreference(
2694  userInfo.uid_, DEFAULT_FSM_VIEW, &xmlOut);
2695  }
2696  else if(requestType == "getAliasList")
2697  {
2698  std::string username = theWebUsers_.getUsersUsername(userInfo.uid_);
2699  std::string fsmName = CgiDataUtilities::getData(cgiIn, "fsmName");
2700  __COUT__ << "fsmName = " << fsmName << __E__;
2701 
2702  std::string stateMachineAliasFilter = "*"; // default to all
2703 
2704  std::map<std::string /*alias*/,
2705  std::pair<std::string /*group name*/, TableGroupKey>>
2706  aliasMap = CorePropertySupervisorBase::theConfigurationManager_
2707  ->getActiveGroupAliases();
2708 
2709  // get stateMachineAliasFilter if possible
2710  ConfigurationTree configLinkNode =
2711  CorePropertySupervisorBase::theConfigurationManager_
2712  ->getSupervisorTableNode(supervisorContextUID_,
2713  supervisorApplicationUID_);
2714 
2715  if(!configLinkNode.isDisconnected())
2716  {
2717  try // for backwards compatibility
2718  {
2719  ConfigurationTree fsmLinkNode =
2720  configLinkNode.getNode("LinkToStateMachineTable");
2721  if(!fsmLinkNode.isDisconnected())
2722  stateMachineAliasFilter =
2723  fsmLinkNode.getNode(fsmName + "/SystemAliasFilter")
2724  .getValue<std::string>();
2725  else
2726  __COUT_INFO__ << "FSM Link disconnected." << __E__;
2727  }
2728  catch(std::runtime_error& e)
2729  {
2730  __COUT_INFO__ << e.what() << __E__;
2731  }
2732  catch(...)
2733  {
2734  __COUT_ERR__ << "Unknown error. Should never happen." << __E__;
2735  }
2736  }
2737  else
2738  __COUT_INFO__ << "FSM Link disconnected." << __E__;
2739 
2740  __COUT__ << "stateMachineAliasFilter = " << stateMachineAliasFilter << __E__;
2741 
2742  // filter list of aliases based on stateMachineAliasFilter
2743  // ! as first character means choose those that do NOT match filter
2744  // * can be used as wild card.
2745  {
2746  bool invertFilter =
2747  stateMachineAliasFilter.size() && stateMachineAliasFilter[0] == '!';
2748  std::vector<std::string> filterArr;
2749 
2750  size_t i = 0;
2751  if(invertFilter)
2752  ++i;
2753  size_t f;
2754  std::string tmp;
2755  while((f = stateMachineAliasFilter.find('*', i)) != std::string::npos)
2756  {
2757  tmp = stateMachineAliasFilter.substr(i, f - i);
2758  i = f + 1;
2759  filterArr.push_back(tmp);
2760  //__COUT__ << filterArr[filterArr.size()-1] << " " << i <<
2761  // " of " << stateMachineAliasFilter.size() << __E__;
2762  }
2763  if(i <= stateMachineAliasFilter.size())
2764  {
2765  tmp = stateMachineAliasFilter.substr(i);
2766  filterArr.push_back(tmp);
2767  //__COUT__ << filterArr[filterArr.size()-1] << " last." << __E__;
2768  }
2769 
2770  bool filterMatch;
2771 
2772  for(auto& aliasMapPair : aliasMap)
2773  {
2774  //__COUT__ << "aliasMapPair.first: " << aliasMapPair.first << __E__;
2775 
2776  filterMatch = true;
2777 
2778  if(filterArr.size() == 1)
2779  {
2780  if(filterArr[0] != "" && filterArr[0] != "*" &&
2781  aliasMapPair.first != filterArr[0])
2782  filterMatch = false;
2783  }
2784  else
2785  {
2786  i = -1;
2787  for(f = 0; f < filterArr.size(); ++f)
2788  {
2789  if(!filterArr[f].size())
2790  continue; // skip empty filters
2791 
2792  if(f == 0) // must start with this filter
2793  {
2794  if((i = aliasMapPair.first.find(filterArr[f])) != 0)
2795  {
2796  filterMatch = false;
2797  break;
2798  }
2799  }
2800  else if(f ==
2801  filterArr.size() - 1) // must end with this filter
2802  {
2803  if(aliasMapPair.first.rfind(filterArr[f]) !=
2804  aliasMapPair.first.size() - filterArr[f].size())
2805  {
2806  filterMatch = false;
2807  break;
2808  }
2809  }
2810  else if((i = aliasMapPair.first.find(filterArr[f])) ==
2811  std::string::npos)
2812  {
2813  filterMatch = false;
2814  break;
2815  }
2816  }
2817  }
2818 
2819  if(invertFilter)
2820  filterMatch = !filterMatch;
2821 
2822  //__COUT__ << "filterMatch=" << filterMatch << __E__;
2823 
2824  if(!filterMatch)
2825  continue;
2826 
2827  xmlOut.addTextElementToData("config_alias", aliasMapPair.first);
2828  xmlOut.addTextElementToData(
2829  "config_key",
2830  TableGroupKey::getFullGroupString(aliasMapPair.second.first,
2831  aliasMapPair.second.second)
2832  .c_str());
2833 
2834  std::string groupComment, groupAuthor, groupCreationTime;
2835  try
2836  {
2837  CorePropertySupervisorBase::theConfigurationManager_
2838  ->loadTableGroup(aliasMapPair.second.first,
2839  aliasMapPair.second.second,
2840  false,
2841  0,
2842  0,
2843  0,
2844  &groupComment,
2845  &groupAuthor,
2846  &groupCreationTime,
2847  false /*false to not load member map*/);
2848 
2849  xmlOut.addTextElementToData("config_comment", groupComment);
2850  xmlOut.addTextElementToData("config_author", groupAuthor);
2851  xmlOut.addTextElementToData("config_create_time",
2852  groupCreationTime);
2853  }
2854  catch(...)
2855  {
2856  __COUT_WARN__ << "Failed to load group metadata." << __E__;
2857  }
2858  }
2859  }
2860 
2861  // return last group alias
2862  std::string fn = FSM_LAST_GROUP_ALIAS_PATH + FSM_LAST_GROUP_ALIAS_FILE_START +
2863  username + "." + FSM_USERS_PREFERENCES_FILETYPE;
2864  __COUT__ << "Load preferences: " << fn << __E__;
2865  FILE* fp = fopen(fn.c_str(), "r");
2866  if(fp)
2867  {
2868  char tmpLastAlias[500];
2869  fscanf(fp, "%*s %s", tmpLastAlias);
2870  __COUT__ << "tmpLastAlias: " << tmpLastAlias << __E__;
2871 
2872  xmlOut.addTextElementToData("UserLastConfigAlias", tmpLastAlias);
2873  fclose(fp);
2874  }
2875  }
2876  else if(requestType == "getFecList")
2877  {
2878  xmlOut.addTextElementToData("fec_list", "");
2879 
2880  for(auto it : allSupervisorInfo_.getAllFETypeSupervisorInfo())
2881  {
2882  xmlOut.addTextElementToParent("fec_url", it.second.getURL(), "fec_list");
2883  xmlOut.addTextElementToParent(
2884  "fec_urn", std::to_string(it.second.getId()), "fec_list");
2885  }
2886  }
2887  else if(requestType == "getSystemMessages")
2888  {
2889  xmlOut.addTextElementToData(
2890  "systemMessages",
2891  theSystemMessenger_.getSystemMessage(
2892  theWebUsers_.getUsersDisplayName(userInfo.uid_)));
2893 
2894  xmlOut.addTextElementToData(
2895  "username_with_lock",
2896  theWebUsers_.getUserWithLock()); // always give system lock update
2897 
2898  //__COUT__ << "userWithLock " << theWebUsers_.getUserWithLock() << __E__;
2899  }
2900  else if(requestType == "setUserWithLock")
2901  {
2902  std::string username = CgiDataUtilities::postData(cgiIn, "username");
2903  std::string lock = CgiDataUtilities::postData(cgiIn, "lock");
2904  std::string accounts = CgiDataUtilities::getData(cgiIn, "accounts");
2905 
2906  __COUT__ << requestType << __E__;
2907  __COUT__ << "username " << username << __E__;
2908  __COUT__ << "lock " << lock << __E__;
2909  __COUT__ << "accounts " << accounts << __E__;
2910  __COUT__ << "userInfo.uid_ " << userInfo.uid_ << __E__;
2911 
2912  std::string tmpUserWithLock = theWebUsers_.getUserWithLock();
2913  if(!theWebUsers_.setUserWithLock(userInfo.uid_, lock == "1", username))
2914  xmlOut.addTextElementToData(
2915  "server_alert",
2916  std::string("Set user lock action failed. You must have valid "
2917  "permissions and ") +
2918  "locking user must be currently logged in.");
2919 
2920  theWebUsers_.insertSettingsForUser(userInfo.uid_, &xmlOut, accounts == "1");
2921 
2922  if(tmpUserWithLock !=
2923  theWebUsers_
2924  .getUserWithLock()) // if there was a change, broadcast system message
2925  theSystemMessenger_.addSystemMessage(
2926  "*",
2927  theWebUsers_.getUserWithLock() == ""
2928  ? tmpUserWithLock + " has unlocked ots."
2929  : theWebUsers_.getUserWithLock() + " has locked ots.");
2930  }
2931  else if(requestType == "getStateMachine")
2932  {
2933  // __COUT__ << "Getting state machine" << __E__;
2934  std::vector<toolbox::fsm::State> states;
2935  states = theStateMachine_.getStates();
2936  char stateStr[2];
2937  stateStr[1] = '\0';
2938  std::string transName;
2939  std::string transParameter;
2940 
2941  // bool addRun, addCfg;
2942  for(unsigned int i = 0; i < states.size(); ++i) // get all states
2943  {
2944  stateStr[0] = states[i];
2945  DOMElement* stateParent = xmlOut.addTextElementToData("state", stateStr);
2946 
2947  xmlOut.addTextElementToParent(
2948  "state_name", theStateMachine_.getStateName(states[i]), stateParent);
2949 
2950  //__COUT__ << "state: " << states[i] << " - " <<
2951  // theStateMachine_.getStateName(states[i]) << __E__;
2952 
2953  // get all transition final states, transitionNames and actionNames from
2954  // state
2955  std::map<std::string, toolbox::fsm::State, std::less<std::string>> trans =
2956  theStateMachine_.getTransitions(states[i]);
2957  std::set<std::string> actionNames = theStateMachine_.getInputs(states[i]);
2958 
2959  std::map<std::string, toolbox::fsm::State, std::less<std::string>>::
2960  iterator it = trans.begin();
2961  std::set<std::string>::iterator ait = actionNames.begin();
2962 
2963  // addRun = false;
2964  // addCfg = false;
2965 
2966  // handle hacky way to keep "forward" moving states on right of FSM
2967  // display must be first!
2968 
2969  for(; it != trans.end() && ait != actionNames.end(); ++it, ++ait)
2970  {
2971  stateStr[0] = it->second;
2972 
2973  if(stateStr[0] == 'R')
2974  {
2975  // addRun = true;
2976  xmlOut.addTextElementToParent(
2977  "state_transition", stateStr, stateParent);
2978 
2979  //__COUT__ << states[i] << " => " << *ait << __E__;
2980 
2981  xmlOut.addTextElementToParent(
2982  "state_transition_action", *ait, stateParent);
2983 
2984  transName = theStateMachine_.getTransitionName(states[i], *ait);
2985  //__COUT__ << states[i] << " => " << transName << __E__;
2986 
2987  xmlOut.addTextElementToParent(
2988  "state_transition_name", transName, stateParent);
2989  transParameter =
2990  theStateMachine_.getTransitionParameter(states[i], *ait);
2991  //__COUT__ << states[i] << " => " << transParameter<< __E__;
2992 
2993  xmlOut.addTextElementToParent(
2994  "state_transition_parameter", transParameter, stateParent);
2995  break;
2996  }
2997  else if(stateStr[0] == 'C')
2998  {
2999  // addCfg = true;
3000  xmlOut.addTextElementToParent(
3001  "state_transition", stateStr, stateParent);
3002 
3003  //__COUT__ << states[i] << " => " << *ait << __E__;
3004 
3005  xmlOut.addTextElementToParent(
3006  "state_transition_action", *ait, stateParent);
3007 
3008  transName = theStateMachine_.getTransitionName(states[i], *ait);
3009  //__COUT__ << states[i] << " => " << transName << __E__;
3010 
3011  xmlOut.addTextElementToParent(
3012  "state_transition_name", transName, stateParent);
3013  transParameter =
3014  theStateMachine_.getTransitionParameter(states[i], *ait);
3015  //__COUT__ << states[i] << " => " << transParameter<< __E__;
3016 
3017  xmlOut.addTextElementToParent(
3018  "state_transition_parameter", transParameter, stateParent);
3019  break;
3020  }
3021  }
3022 
3023  // reset for 2nd pass
3024  it = trans.begin();
3025  ait = actionNames.begin();
3026 
3027  // other states
3028  for(; it != trans.end() && ait != actionNames.end(); ++it, ++ait)
3029  {
3030  //__COUT__ << states[i] << " => " << it->second << __E__;
3031 
3032  stateStr[0] = it->second;
3033 
3034  if(stateStr[0] == 'R')
3035  continue;
3036  else if(stateStr[0] == 'C')
3037  continue;
3038 
3039  xmlOut.addTextElementToParent(
3040  "state_transition", stateStr, stateParent);
3041 
3042  //__COUT__ << states[i] << " => " << *ait << __E__;
3043 
3044  xmlOut.addTextElementToParent(
3045  "state_transition_action", *ait, stateParent);
3046 
3047  transName = theStateMachine_.getTransitionName(states[i], *ait);
3048  //__COUT__ << states[i] << " => " << transName << __E__;
3049 
3050  xmlOut.addTextElementToParent(
3051  "state_transition_name", transName, stateParent);
3052  transParameter =
3053  theStateMachine_.getTransitionParameter(states[i], *ait);
3054  //__COUT__ << states[i] << " => " << transParameter<< __E__;
3055 
3056  xmlOut.addTextElementToParent(
3057  "state_transition_parameter", transParameter, stateParent);
3058  }
3059  }
3060  }
3061  else if(requestType == "getStateMachineNames")
3062  {
3063  // get stateMachineAliasFilter if possible
3064  ConfigurationTree configLinkNode =
3065  CorePropertySupervisorBase::theConfigurationManager_
3066  ->getSupervisorTableNode(supervisorContextUID_,
3067  supervisorApplicationUID_);
3068 
3069  try
3070  {
3071  auto fsmNodes =
3072  configLinkNode.getNode("LinkToStateMachineTable").getChildren();
3073  for(const auto& fsmNode : fsmNodes)
3074  xmlOut.addTextElementToData("stateMachineName", fsmNode.first);
3075  }
3076  catch(...) // else empty set of state machines.. can always choose ""
3077  {
3078  __COUT__ << "Caught exception, assuming no valid FSM names." << __E__;
3079  xmlOut.addTextElementToData("stateMachineName", "");
3080  }
3081  }
3082  else if(requestType == "getIterationPlanStatus")
3083  {
3084  //__COUT__ << "checking it status" << __E__;
3085  theIterator_.handleCommandRequest(xmlOut, requestType, "");
3086  }
3087  else if(requestType == "getCurrentState")
3088  {
3089  xmlOut.addTextElementToData("current_state",
3090  theStateMachine_.getCurrentStateName());
3091  xmlOut.addTextElementToData("in_transition",
3092  theStateMachine_.isInTransition() ? "1" : "0");
3093  if(theStateMachine_.isInTransition())
3094  xmlOut.addTextElementToData(
3095  "transition_progress",
3096  RunControlStateMachine::theProgressBar_.readPercentageString());
3097  else
3098  xmlOut.addTextElementToData("transition_progress", "100");
3099 
3100  char tmp[20];
3101  sprintf(tmp, "%lu", theStateMachine_.getTimeInState());
3102  xmlOut.addTextElementToData("time_in_state", tmp);
3103 
3104  //__COUT__ << "current state: " << theStateMachine_.getCurrentStateName() <<
3105  //__E__;
3106 
3108 
3109  std::string fsmName = CgiDataUtilities::getData(cgiIn, "fsmName");
3110  // __COUT__ << "fsmName = " << fsmName << __E__;
3111  // __COUT__ << "activeStateMachineName_ = " << activeStateMachineName_ <<
3112  //__E__;
3113  // __COUT__ << "theStateMachine_.getProvenanceStateName() = " <<
3114  // theStateMachine_.getProvenanceStateName() << __E__;
3115  // __COUT__ << "theStateMachine_.getCurrentStateName() = " <<
3116  // theStateMachine_.getCurrentStateName() << __E__;
3117 
3118  if(!theStateMachine_.isInTransition())
3119  {
3120  std::string stateMachineRunAlias = "Run"; // default to "Run"
3121 
3122  // get stateMachineAliasFilter if possible
3123  ConfigurationTree configLinkNode =
3124  CorePropertySupervisorBase::theConfigurationManager_
3125  ->getSupervisorTableNode(supervisorContextUID_,
3126  supervisorApplicationUID_);
3127 
3128  if(!configLinkNode.isDisconnected())
3129  {
3130  try // for backwards compatibility
3131  {
3132  ConfigurationTree fsmLinkNode =
3133  configLinkNode.getNode("LinkToStateMachineTable");
3134  if(!fsmLinkNode.isDisconnected())
3135  stateMachineRunAlias =
3136  fsmLinkNode.getNode(fsmName + "/RunDisplayAlias")
3137  .getValue<std::string>();
3138  // else
3139  // __COUT_INFO__ << "FSM Link disconnected." << __E__;
3140  }
3141  catch(std::runtime_error& e)
3142  {
3143  //__COUT_INFO__ << e.what() << __E__;
3144  //__COUT_INFO__ << "No state machine Run alias. Ignoring and
3145  // assuming alias of '" << stateMachineRunAlias << ".'" <<
3146  //__E__;
3147  }
3148  catch(...)
3149  {
3150  __COUT_ERR__ << "Unknown error. Should never happen." << __E__;
3151 
3152  __COUT_INFO__ << "No state machine Run alias. Ignoring and "
3153  "assuming alias of '"
3154  << stateMachineRunAlias << ".'" << __E__;
3155  }
3156  }
3157  // else
3158  // __COUT_INFO__ << "FSM Link disconnected." << __E__;
3159 
3160  //__COUT__ << "stateMachineRunAlias = " << stateMachineRunAlias <<
3161  //__E__;
3162 
3163  xmlOut.addTextElementToData("stateMachineRunAlias", stateMachineRunAlias);
3164 
3166 
3167  if(theStateMachine_.getCurrentStateName() == "Running" ||
3168  theStateMachine_.getCurrentStateName() == "Paused")
3169  {
3170  sprintf(tmp,
3171  "Current %s Number: %u",
3172  stateMachineRunAlias.c_str(),
3173  getNextRunNumber(activeStateMachineName_) - 1);
3174 
3175  if(RunControlStateMachine::asyncSoftFailureReceived_)
3176  {
3177  //__COUTV__(RunControlStateMachine::asyncSoftFailureReceived_);
3178  //__COUTV__(RunControlStateMachine::getErrorMessage());
3179  xmlOut.addTextElementToData(
3180  "soft_error", RunControlStateMachine::getErrorMessage());
3181  }
3182  }
3183  else
3184  sprintf(tmp,
3185  "Next %s Number: %u",
3186  stateMachineRunAlias.c_str(),
3187  getNextRunNumber(fsmName));
3188  xmlOut.addTextElementToData("run_number", tmp);
3189  }
3190  }
3191  else if(requestType == "cancelStateMachineTransition")
3192  {
3193  __SS__ << "State transition was cancelled by user!" << __E__;
3194  __MCOUT__(ss.str());
3195  RunControlStateMachine::theStateMachine_.setErrorMessage(ss.str());
3196  RunControlStateMachine::asyncFailureReceived_ = true;
3197  }
3198  else if(requestType == "getErrorInStateMatchine")
3199  {
3200  xmlOut.addTextElementToData("FSM_Error", theStateMachine_.getErrorMessage());
3201  }
3202  else if(requestType == "getDesktopIcons")
3203  {
3204  // get icons and create comma-separated string based on user permissions
3205  // note: each icon has own permission threshold, so each user can have
3206  // a unique desktop icon experience.
3207 
3208  const DesktopIconTable* iconTable =
3209  CorePropertySupervisorBase::theConfigurationManager_->__GET_CONFIG__(
3211  std::vector<DesktopIconTable::DesktopIcon> icons =
3212  iconTable->getAllDesktopIcons();
3213 
3214  std::string iconString = ""; // comma-separated icon string, 7 fields:
3215  // 0 - caption = text below icon
3216  // 1 - altText = text icon if no image given
3217  // 2 - uniqueWin = if true, only one window is allowed,
3218  // else multiple instances of window 3 - permissions =
3219  // security level needed to see icon 4 - picfn =
3220  // icon image filename 5 - linkurl = url of the window to
3221  // open 6 - folderPath = folder and subfolder location
3222  // '/'
3223  // separated for example: State
3224  // 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
3225  // Subsets
3226 
3227  //__COUTV__((unsigned int)userInfo.permissionLevel_);
3228 
3229  std::map<std::string, WebUsers::permissionLevel_t> userPermissionLevelsMap =
3230  theWebUsers_.getPermissionsForUser(userInfo.uid_);
3231  std::map<std::string, WebUsers::permissionLevel_t>
3232  iconPermissionThresholdsMap;
3233 
3234  bool firstIcon = true;
3235  for(const auto& icon : icons)
3236  {
3237  __COUTV__(icon.caption_);
3238  __COUTV__(icon.permissionThresholdString_);
3239 
3240  CorePropertySupervisorBase::extractPermissionsMapFromString(
3241  icon.permissionThresholdString_, iconPermissionThresholdsMap);
3242 
3243  if(!CorePropertySupervisorBase::doPermissionsGrantAccess(
3244  userPermissionLevelsMap, iconPermissionThresholdsMap))
3245  continue; // skip icon if no access
3246 
3247  //__COUTV__(icon.caption_);
3248 
3249  // have icon access, so add to CSV string
3250  if(firstIcon)
3251  firstIcon = false;
3252  else
3253  iconString += ",";
3254 
3255  iconString += icon.caption_;
3256  iconString += "," + icon.alternateText_;
3257  iconString +=
3258  "," + std::string(icon.enforceOneWindowInstance_ ? "1" : "0");
3259  iconString +=
3260  "," + std::string("1"); // set permission to 1 so the
3261  // desktop shows every icon that the
3262  // server allows (i.e., trust server
3263  // security, ignore client security)
3264  iconString += "," + icon.imageURL_;
3265  iconString += "," + icon.windowContentURL_;
3266  iconString += "," + icon.folderPath_;
3267  }
3268  __COUTV__(iconString);
3269 
3270  xmlOut.addTextElementToData("iconList", iconString);
3271  }
3272  else if(requestType == "gatewayLaunchOTS" || requestType == "gatewayLaunchWiz")
3273  {
3274  // NOTE: similar to ConfigurationGUI version but DOES keep active sessions
3275 
3276  __COUT_WARN__ << requestType << " requestType received! " << __E__;
3277  __MOUT_WARN__ << requestType << " requestType received! " << __E__;
3278 
3279  // gateway launch is different, in that it saves user sessions
3280  theWebUsers_.saveActiveSessions();
3281 
3282  // now launch
3283 
3284  if(requestType == "gatewayLaunchOTS")
3285  GatewaySupervisor::launchStartOTSCommand(
3286  "LAUNCH_OTS", CorePropertySupervisorBase::theConfigurationManager_);
3287  else if(requestType == "gatewayLaunchWiz")
3288  GatewaySupervisor::launchStartOTSCommand(
3289  "LAUNCH_WIZ", CorePropertySupervisorBase::theConfigurationManager_);
3290  }
3291  else if(requestType == "resetUserTooltips")
3292  {
3293  WebUsers::resetAllUserTooltips(theWebUsers_.getUsersUsername(userInfo.uid_));
3294  }
3295  else
3296  {
3297  __SS__ << "requestType Request, " << requestType << ", not recognized."
3298  << __E__;
3299  __SS_THROW__;
3300  }
3301  }
3302  catch(const std::runtime_error& e)
3303  {
3304  __SS__ << "An error was encountered handling requestType '" << requestType
3305  << "':" << e.what() << __E__;
3306  __COUT__ << "\n" << ss.str();
3307  xmlOut.addTextElementToData("Error", ss.str());
3308  }
3309  catch(...)
3310  {
3311  __SS__ << "An unknown error was encountered handling requestType '" << requestType
3312  << ".' "
3313  << "Please check the printouts to debug." << __E__;
3314  __COUT__ << "\n" << ss.str();
3315  xmlOut.addTextElementToData("Error", ss.str());
3316  }
3317 
3318  // return xml doc holding server response
3319  xmlOut.outputXmlDocument(
3320  (std::ostringstream*)out,
3321  false /*dispStdOut*/,
3322  true /*allowWhiteSpace*/); // Note: allow white space need for error response
3323 
3324  //__COUT__ << "Done" << __E__;
3325 } // end request()
3326 
3327 //========================================================================================================================
3328 // launchStartOTSCommand
3329 // static function (so WizardSupervisor can use it)
3330 // throws exception if command fails to start
3331 void GatewaySupervisor::launchStartOTSCommand(const std::string& command,
3332  ConfigurationManager* cfgMgr)
3333 {
3334  __COUT__ << "launch StartOTS Command = " << command << __E__;
3335  __COUT__ << "Extracting target context hostnames... " << __E__;
3336 
3337  std::vector<std::string> hostnames;
3338  try
3339  {
3340  cfgMgr->init(); // completely reset to re-align with any changes
3341 
3342  const XDAQContextTable* contextTable = cfgMgr->__GET_CONFIG__(XDAQContextTable);
3343 
3344  auto contexts = contextTable->getContexts();
3345  unsigned int i, j;
3346  for(const auto& context : contexts)
3347  {
3348  if(!context.status_)
3349  continue;
3350 
3351  // find last slash
3352  j = 0; // default to whole string
3353  for(i = 0; i < context.address_.size(); ++i)
3354  if(context.address_[i] == '/')
3355  j = i + 1;
3356  hostnames.push_back(context.address_.substr(j));
3357  __COUT__ << "StartOTS.sh hostname = " << hostnames.back() << __E__;
3358  }
3359  }
3360  catch(...)
3361  {
3362  __SS__ << "\nRelaunch of otsdaq interrupted! "
3363  << "The Configuration Manager could not be initialized." << __E__;
3364 
3365  __SS_THROW__;
3366  }
3367 
3368  for(const auto& hostname : hostnames)
3369  {
3370  std::string fn = (std::string(getenv("SERVICE_DATA_PATH")) + "/StartOTS_action_" +
3371  hostname + ".cmd");
3372  FILE* fp = fopen(fn.c_str(), "w");
3373  if(fp)
3374  {
3375  fprintf(fp, command.c_str());
3376  fclose(fp);
3377  }
3378  else
3379  {
3380  __SS__ << "Unable to open command file: " << fn << __E__;
3381  __SS_THROW__;
3382  }
3383  }
3384 
3385  sleep(2 /*seconds*/); // then verify that the commands were read
3386  // note: StartOTS.sh has a sleep of 1 second
3387 
3388  for(const auto& hostname : hostnames)
3389  {
3390  std::string fn = (std::string(getenv("SERVICE_DATA_PATH")) + "/StartOTS_action_" +
3391  hostname + ".cmd");
3392  FILE* fp = fopen(fn.c_str(), "r");
3393  if(fp)
3394  {
3395  char line[100];
3396  fgets(line, 100, fp);
3397  fclose(fp);
3398 
3399  if(strcmp(line, command.c_str()) == 0)
3400  {
3401  __SS__ << "The command looks to have been ignored by " << hostname
3402  << ". Is StartOTS.sh still running on that node?" << __E__;
3403  __SS_THROW__;
3404  }
3405  __COUTV__(line);
3406  }
3407  else
3408  {
3409  __SS__ << "Unable to open command file for verification: " << fn << __E__;
3410  __SS_THROW__;
3411  }
3412  }
3413 } // end launchStartOTSCommand
3414 
3415 //========================================================================================================================
3416 // xoap::supervisorCookieCheck
3417 // verify cookie
3418 xoap::MessageReference GatewaySupervisor::supervisorCookieCheck(
3419  xoap::MessageReference message)
3420 
3421 {
3422  //__COUT__ << __E__;
3423 
3424  // SOAPUtilities::receive request parameters
3425  SOAPParameters parameters;
3426  parameters.addParameter("CookieCode");
3427  parameters.addParameter("RefreshOption");
3428  parameters.addParameter("IPAddress");
3429  SOAPUtilities::receive(message, parameters);
3430  std::string cookieCode = parameters.getValue("CookieCode");
3431  std::string refreshOption =
3432  parameters.getValue("RefreshOption"); // give external supervisors option to
3433  // refresh cookie or not, "1" to refresh
3434  std::string ipAddress =
3435  parameters.getValue("IPAddress"); // give external supervisors option to refresh
3436  // cookie or not, "1" to refresh
3437 
3438  // If TRUE, cookie code is good, and refreshed code is in cookieCode, also pointers
3439  // optionally for uint8_t userPermissions, uint64_t uid Else, error message is
3440  // returned in cookieCode
3441  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t>
3442  userGroupPermissionsMap;
3443  std::string userWithLock = "";
3444  uint64_t activeSessionIndex, uid;
3445  theWebUsers_.cookieCodeIsActiveForRequest(cookieCode,
3446  &userGroupPermissionsMap,
3447  &uid /*uid is not given to remote users*/,
3448  ipAddress,
3449  refreshOption == "1",
3450  &userWithLock,
3451  &activeSessionIndex);
3452 
3453  //__COUT__ << "userWithLock " << userWithLock << __E__;
3454 
3455  // fill return parameters
3456  SOAPParameters retParameters;
3457  retParameters.addParameter("CookieCode", cookieCode);
3458  retParameters.addParameter(
3459  "Permissions", StringMacros::mapToString(userGroupPermissionsMap).c_str());
3460  retParameters.addParameter("UserWithLock", userWithLock);
3461  retParameters.addParameter("Username", theWebUsers_.getUsersUsername(uid));
3462  retParameters.addParameter("DisplayName", theWebUsers_.getUsersDisplayName(uid));
3463  sprintf(tmpStringForConversions_, "%lu", activeSessionIndex);
3464  retParameters.addParameter("ActiveSessionIndex", tmpStringForConversions_);
3465 
3466  //__COUT__ << __E__;
3467 
3468  return SOAPUtilities::makeSOAPMessageReference("CookieResponse", retParameters);
3469 }
3470 
3471 //========================================================================================================================
3472 // xoap::supervisorGetActiveUsers
3473 // get display names for all active users
3474 xoap::MessageReference GatewaySupervisor::supervisorGetActiveUsers(
3475  xoap::MessageReference message)
3476 
3477 {
3478  __COUT__ << __E__;
3479 
3480  SOAPParameters parameters("UserList", theWebUsers_.getActiveUsersString());
3481  return SOAPUtilities::makeSOAPMessageReference("ActiveUserResponse", parameters);
3482 }
3483 
3484 //========================================================================================================================
3485 // xoap::supervisorSystemMessage
3486 // SOAPUtilities::receive a new system Message from a supervisor
3487 // ToUser wild card * is to all users
3488 xoap::MessageReference GatewaySupervisor::supervisorSystemMessage(
3489  xoap::MessageReference message)
3490 
3491 {
3492  SOAPParameters parameters;
3493  parameters.addParameter("ToUser");
3494  parameters.addParameter("Message");
3495  SOAPUtilities::receive(message, parameters);
3496 
3497  __COUT__ << "toUser: " << parameters.getValue("ToUser").substr(0, 10)
3498  << ", message: " << parameters.getValue("Message").substr(0, 10) << __E__;
3499 
3500  theSystemMessenger_.addSystemMessage(parameters.getValue("ToUser"),
3501  parameters.getValue("Message"));
3502  return SOAPUtilities::makeSOAPMessageReference("SystemMessageResponse");
3503 }
3504 
3505 //===================================================================================================================
3506 // xoap::supervisorSystemLogbookEntry
3507 // SOAPUtilities::receive a new system Message from a supervisor
3508 // ToUser wild card * is to all users
3509 xoap::MessageReference GatewaySupervisor::supervisorSystemLogbookEntry(
3510  xoap::MessageReference message)
3511 
3512 {
3513  SOAPParameters parameters;
3514  parameters.addParameter("EntryText");
3515  SOAPUtilities::receive(message, parameters);
3516 
3517  __COUT__ << "EntryText: " << parameters.getValue("EntryText").substr(0, 10) << __E__;
3518 
3519  makeSystemLogbookEntry(parameters.getValue("EntryText"));
3520 
3521  return SOAPUtilities::makeSOAPMessageReference("SystemLogbookResponse");
3522 }
3523 
3524 //===================================================================================================================
3525 // supervisorLastConfigGroupRequest
3526 // return the group name and key for the last state machine activity
3527 //
3528 // Note: same as OtsConfigurationWizardSupervisor::supervisorLastConfigGroupRequest
3529 xoap::MessageReference GatewaySupervisor::supervisorLastConfigGroupRequest(
3530  xoap::MessageReference message)
3531 
3532 {
3533  SOAPParameters parameters;
3534  parameters.addParameter("ActionOfLastGroup");
3535  SOAPUtilities::receive(message, parameters);
3536 
3537  return GatewaySupervisor::lastConfigGroupRequestHandler(parameters);
3538 }
3539 
3540 //===================================================================================================================
3541 // xoap::lastConfigGroupRequestHandler
3542 // handles last config group request.
3543 // called by both:
3544 // GatewaySupervisor::supervisorLastConfigGroupRequest
3545 // OtsConfigurationWizardSupervisor::supervisorLastConfigGroupRequest
3546 xoap::MessageReference GatewaySupervisor::lastConfigGroupRequestHandler(
3547  const SOAPParameters& parameters)
3548 {
3549  std::string action = parameters.getValue("ActionOfLastGroup");
3550  __COUT__ << "ActionOfLastGroup: " << action.substr(0, 10) << __E__;
3551 
3552  std::string fileName = "";
3553  if(action == "Configured")
3554  fileName = FSM_LAST_CONFIGURED_GROUP_ALIAS_FILE;
3555  else if(action == "Started")
3556  fileName = FSM_LAST_STARTED_GROUP_ALIAS_FILE;
3557  else
3558  {
3559  __COUT_ERR__ << "Invalid last group action requested." << __E__;
3560  return SOAPUtilities::makeSOAPMessageReference("LastConfigGroupResponseFailure");
3561  }
3562  std::string timeString;
3563  std::pair<std::string /*group name*/, TableGroupKey> theGroup =
3564  loadGroupNameAndKey(fileName, timeString);
3565 
3566  // fill return parameters
3567  SOAPParameters retParameters;
3568  retParameters.addParameter("GroupName", theGroup.first);
3569  retParameters.addParameter("GroupKey", theGroup.second.toString());
3570  retParameters.addParameter("GroupAction", action);
3571  retParameters.addParameter("GroupActionTime", timeString);
3572 
3573  return SOAPUtilities::makeSOAPMessageReference("LastConfigGroupResponse",
3574  retParameters);
3575 }
3576 
3577 //========================================================================================================================
3578 // getNextRunNumber
3579 //
3580 // If fsmName is passed, then get next run number for that FSM name
3581 // Else get next run number for the active FSM name, activeStateMachineName_
3582 //
3583 // Note: the FSM name is sanitized of special characters and used in the filename.
3584 unsigned int GatewaySupervisor::getNextRunNumber(const std::string& fsmNameIn)
3585 {
3586  std::string runNumberFileName = RUN_NUMBER_PATH + "/";
3587  std::string fsmName = fsmNameIn == "" ? activeStateMachineName_ : fsmNameIn;
3588  // prepend sanitized FSM name
3589  for(unsigned int i = 0; i < fsmName.size(); ++i)
3590  if((fsmName[i] >= 'a' && fsmName[i] <= 'z') ||
3591  (fsmName[i] >= 'A' && fsmName[i] <= 'Z') ||
3592  (fsmName[i] >= '0' && fsmName[i] <= '9'))
3593  runNumberFileName += fsmName[i];
3594  runNumberFileName += RUN_NUMBER_FILE_NAME;
3595  //__COUT__ << "runNumberFileName: " << runNumberFileName << __E__;
3596 
3597  std::ifstream runNumberFile(runNumberFileName.c_str());
3598  if(!runNumberFile.is_open())
3599  {
3600  __COUT__ << "Can't open file: " << runNumberFileName << __E__;
3601 
3602  __COUT__ << "Creating file and setting Run Number to 1: " << runNumberFileName
3603  << __E__;
3604  FILE* fp = fopen(runNumberFileName.c_str(), "w");
3605  fprintf(fp, "1");
3606  fclose(fp);
3607 
3608  runNumberFile.open(runNumberFileName.c_str());
3609  if(!runNumberFile.is_open())
3610  {
3611  __SS__ << "Error. Can't create file: " << runNumberFileName << __E__;
3612  __COUT_ERR__ << ss.str();
3613  __SS_THROW__;
3614  }
3615  }
3616  std::string runNumberString;
3617  runNumberFile >> runNumberString;
3618  runNumberFile.close();
3619  return atoi(runNumberString.c_str());
3620 }
3621 
3622 //========================================================================================================================
3623 bool GatewaySupervisor::setNextRunNumber(unsigned int runNumber,
3624  const std::string& fsmNameIn)
3625 {
3626  std::string runNumberFileName = RUN_NUMBER_PATH + "/";
3627  std::string fsmName = fsmNameIn == "" ? activeStateMachineName_ : fsmNameIn;
3628  // prepend sanitized FSM name
3629  for(unsigned int i = 0; i < fsmName.size(); ++i)
3630  if((fsmName[i] >= 'a' && fsmName[i] <= 'z') ||
3631  (fsmName[i] >= 'A' && fsmName[i] <= 'Z') ||
3632  (fsmName[i] >= '0' && fsmName[i] <= '9'))
3633  runNumberFileName += fsmName[i];
3634  runNumberFileName += RUN_NUMBER_FILE_NAME;
3635  __COUT__ << "runNumberFileName: " << runNumberFileName << __E__;
3636 
3637  std::ofstream runNumberFile(runNumberFileName.c_str());
3638  if(!runNumberFile.is_open())
3639  {
3640  __SS__ << "Can't open file: " << runNumberFileName << __E__;
3641  __COUT__ << ss.str();
3642  __SS_THROW__;
3643  }
3644  std::stringstream runNumberStream;
3645  runNumberStream << runNumber;
3646  runNumberFile << runNumberStream.str().c_str();
3647  runNumberFile.close();
3648  return true;
3649 }
3650 
3651 //========================================================================================================================
3652 // loadGroupNameAndKey
3653 // loads group name and key (and time) from specified file
3654 // returns time string in returnedTimeString
3655 //
3656 // Note: this is static so the OtsConfigurationWizardSupervisor can call it
3657 std::pair<std::string /*group name*/, TableGroupKey>
3658 GatewaySupervisor::loadGroupNameAndKey(const std::string& fileName,
3659  std::string& returnedTimeString)
3660 {
3661  std::string fullPath = FSM_LAST_GROUP_ALIAS_PATH + "/" + fileName;
3662 
3663  FILE* groupFile = fopen(fullPath.c_str(), "r");
3664  if(!groupFile)
3665  {
3666  __COUT__ << "Can't open file: " << fullPath << __E__;
3667 
3668  __COUT__ << "Returning empty groupName and key -1" << __E__;
3669 
3670  return std::pair<std::string /*group name*/, TableGroupKey>("", TableGroupKey());
3671  }
3672 
3673  char line[500]; // assuming no group names longer than 500 chars
3674  // name and then key
3675  std::pair<std::string /*group name*/, TableGroupKey> theGroup;
3676 
3677  fgets(line, 500, groupFile); // name
3678  theGroup.first = line;
3679 
3680  fgets(line, 500, groupFile); // key
3681  int key;
3682  sscanf(line, "%d", &key);
3683  theGroup.second = key;
3684 
3685  fgets(line, 500, groupFile); // time
3686  time_t timestamp;
3687  sscanf(line, "%ld", &timestamp); // type long int
3688  // struct tm tmstruct;
3689  // ::localtime_r(&timestamp, &tmstruct);
3690  // ::strftime(line, 30, "%c %Z", &tmstruct);
3691  returnedTimeString = StringMacros::getTimestampString(timestamp); // line;
3692  fclose(groupFile);
3693 
3694  __COUT__ << "theGroup.first= " << theGroup.first
3695  << " theGroup.second= " << theGroup.second << __E__;
3696 
3697  return theGroup;
3698 } // end loadGroupNameAndKey()
3699 
3700 //========================================================================================================================
3701 void GatewaySupervisor::saveGroupNameAndKey(
3702  const std::pair<std::string /*group name*/, TableGroupKey>& theGroup,
3703  const std::string& fileName)
3704 {
3705  std::string fullPath = FSM_LAST_GROUP_ALIAS_PATH + "/" + fileName;
3706 
3707  std::ofstream groupFile(fullPath.c_str());
3708  if(!groupFile.is_open())
3709  {
3710  __SS__ << "Error. Can't open file: " << fullPath << __E__;
3711  __COUT_ERR__ << "\n" << ss.str();
3712  __SS_THROW__;
3713  }
3714  std::stringstream outss;
3715  outss << theGroup.first << "\n" << theGroup.second << "\n" << time(0);
3716  groupFile << outss.str().c_str();
3717  groupFile.close();
3718 } // end saveGroupNameAndKey()
3719 
3721 
3724 // void GatewaySupervisor::infoRequest(xgi::Input * in, xgi::Output * out )
3725 //{
3726 // __COUT__ << __LINE__ << __E__ << __E__;
3727 // cgicc::Cgicc cgi(in);
3728 // std::string Command=cgi.getElement("RequestType")->getValue();
3729 //
3730 // if (Command=="FEWList")
3731 // {
3732 // *out << "<FEWList>" << __E__;
3733 // for (std::set<xdaq::ApplicationDescriptor*>::iterator
3734 // pxFESupervisorsIt=pxFESupervisors_.begin();
3735 // pxFESupervisorsIt!=pxFESupervisors_.end(); pxFESupervisorsIt++)
3736 // {
3737 // *out << "<FEWInterface id='1' alias='FPIX Disk 1' type='OtsUDPHardware'>" <<
3738 // __E__
3739 // << "<svg xmlns='http://www.w3.org/2000/svg' version='1.1'>" << __E__
3740 // << "<circle cx='100' cy='50' r='40' stroke='black' stroke-width='2'
3741 // fill='red' />" << __E__
3742 // << "</svg>" << __E__
3743 // << "</FEWInterface>" << __E__;
3744 // *out << "<FEWInterface id='2' alias='FPIX Disk 2' type='OtsUDPHardware'>" <<
3745 // __E__
3746 // << "<svg xmlns='http://www.w3.org/2000/svg' version='1.1'>" << __E__
3747 // << "<circle cx='100' cy='50' r='40' stroke='black' stroke-width='2'
3748 // fill='red' />" << __E__
3749 // << "</svg>" << __E__
3750 // << "</FEWInterface>" << __E__;
3751 // }
3752 // *out << "</FEWList>" << __E__;
3753 // }
3754 //}
3755 //========================================================================================================================