otsdaq-utilities  3.06.00
MacroMakerSupervisor.cc
1 #include "otsdaq-utilities/MacroMaker/MacroMakerSupervisor.h"
2 
3 #include "otsdaq/CodeEditor/CodeEditor.h"
4 #include "otsdaq/ConfigurationInterface/ConfigurationManager.h"
5 #include "otsdaq/FECore/FEVInterface.h"
6 
7 #include "otsdaq/NetworkUtilities/TransceiverSocket.h" // for UDP remote control
8 
9 #include <dirent.h> //for DIR
10 #include <stdio.h> //for file rename
11 #include <sys/stat.h> //for mkdir
12 #include <chrono>
13 #include <cstdio>
14 #include <filesystem> //for std::filesytem
15 #include <fstream>
16 #include <future> //to track active worker completions
17 #include <thread> //for std::thread
18 #include "otsdaq/TableCore/TableGroupKey.h"
19 
20 #define MACROS_DB_PATH std::string(__ENV__("SERVICE_DATA_PATH")) + "/MacroData/"
21 #define MACROS_HIST_PATH std::string(__ENV__("SERVICE_DATA_PATH")) + "/MacroHistory/"
22 #define MACROS_SEQUENCE_PATH std::string(__ENV__("SERVICE_DATA_PATH")) + "/MacroSequence/"
23 #define MACROS_EXPORT_PATH std::string("/MacroExport/")
24 
25 #define SEQUENCE_FILE_NAME \
26  std::string(__ENV__("SERVICE_DATA_PATH")) + "/OtsWizardData/sequence.dat"
27 #define SEQUENCE_OUT_FILE_NAME \
28  std::string(__ENV__("SERVICE_DATA_PATH")) + "/OtsWizardData/sequence.out"
29 
30 using namespace ots;
31 
32 #undef __MF_SUBJECT__
33 #define __MF_SUBJECT__ "MacroMaker"
34 
35 XDAQ_INSTANTIATOR_IMPL(MacroMakerSupervisor)
36 
37 //==============================================================================
38 MacroMakerSupervisor::MacroMakerSupervisor(xdaq::ApplicationStub* stub)
39  : CoreSupervisorBase(stub)
40 {
41  __SUP_COUT__ << "Constructing..." << __E__;
42 
43  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
44 
45  // make macro directories in case they don't exist
46  mkdir(((std::string)MACROS_DB_PATH).c_str(), 0755);
47  mkdir(((std::string)MACROS_HIST_PATH).c_str(), 0755);
48  mkdir(((std::string)MACROS_SEQUENCE_PATH).c_str(), 0755);
49  mkdir((__ENV__("SERVICE_DATA_PATH") + MACROS_EXPORT_PATH).c_str(), 0755);
50 
51  xoap::bind(this,
52  &MacroMakerSupervisor::frontEndCommunicationRequest,
53  "FECommunication",
54  XDAQ_NS_URI);
55 
56  // start requests for MacroMaker only mode
57  if(CorePropertySupervisorBase::allSupervisorInfo_.isMacroMakerMode())
58  {
59  __SUP_COUT__ << "Starting constructor for Macro Maker mode." << __E__;
60 
61  xgi::bind(this, &MacroMakerSupervisor::requestIcons, "requestIcons");
62  xgi::bind(this, &MacroMakerSupervisor::verification, "Verify");
63  xgi::bind(this, &MacroMakerSupervisor::tooltipRequest, "TooltipRequest");
64  xgi::bind(this, &MacroMakerSupervisor::requestWrapper, "Request");
65  xoap::bind(this,
66  &MacroMakerSupervisor::supervisorSequenceCheck,
67  "SupervisorSequenceCheck",
68  XDAQ_NS_URI);
69  generateURL();
70  __SUP_COUT__ << "Completed constructor for Macro Maker mode." << __E__;
71  }
72  else
73  __SUP_COUT__ << "Not Macro Maker only mode." << __E__;
74  // end requests for MacroMaker only mode
75 
76  init();
77 
78  // initFElist for Macro Maker mode
79  if(CorePropertySupervisorBase::allSupervisorInfo_.isMacroMakerMode())
80  {
81  // const SupervisorInfoMap& feTypeSupervisors =
82  // CorePropertySupervisorBase::allSupervisorInfo_.getAllFETypeSupervisorInfo();
83 
84  ConfigurationTree appsNode = theConfigurationManager_->getNode(
85  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME);
86 
87  // __SUP_COUT__ << "Number of FE Supervisors found = " << feTypeSupervisors.size()
88  // << __E__;
89 
90  FEPluginTypetoFEsMap_.clear(); // reset
91  FEtoSupervisorMap_.clear(); // reset
92  FEtoPluginTypeMap_.clear(); // reset
93  // for(auto& feApp : feTypeSupervisors)
94  // {
95  __SUP_COUT__ << "FEs for app MacroMakerFESupervisor"
96  << __E__; // << feApp.first << ":" << feApp.second.getName()
97  // << __E__;
98 
99  auto feChildren =
100  appsNode
101  .getNode("MacroMakerFESupervisor") // feApp.second.getName())
102  .getNode("LinkToSupervisorTable")
103  .getNode("LinkToFEInterfaceTable")
104  .getChildren();
105 
106  for(auto& fe : feChildren)
107  {
108  if(!fe.second.status())
109  continue; // skip disabled FEs
110 
111  __SUP_COUTV__(fe.first);
112  FEtoSupervisorMap_[fe.first] =
113  atoi(__ENV__("FE_SUPERVISOR_ID")); // feApp.first;
114 
115  std::string pluginType =
116  fe.second.getNode("FEInterfacePluginName").getValue();
117  FEPluginTypetoFEsMap_[pluginType].emplace(fe.first);
118  FEtoPluginTypeMap_[fe.first] = pluginType;
119  }
120  // }
121 
122  __SUP_COUTV__(StringMacros::mapToString(FEtoSupervisorMap_));
123  __SUP_COUTV__(StringMacros::mapToString(FEPluginTypetoFEsMap_));
124  __SUP_COUTV__(StringMacros::mapToString(FEtoPluginTypeMap_));
125  }
126 
127  //setup UDP interface thread if env variable set for port
128  {
129  bool enableRemoteControl = false;
130  try
131  {
132  __ENV__("OTS_MACROMAKER_UDP_PORT");
133  __ENV__("OTS_MACROMAKER_UDP_IP");
134  enableRemoteControl = true;
135  }
136  catch(const std::runtime_error& e)
137  {
138  __SUP_COUT__ << "Ignoring MacroMaker server env var error: " << e.what()
139  << __E__;
140  }
141  catch(...)
142  {
143  __SUP_COUT__ << "Ignoring unknown error reading OTS_MACROMAKER_UDP_PORT/"
144  "OTS_MACROMAKER_UDP_IP env vars."
145  << __E__;
146  } // ignore errors
147 
148  if(enableRemoteControl)
149  {
150  __SUP_COUT_INFO__ << "Enabling remote control over UDP..." << __E__;
151  // start state changer UDP listener thread
152  std::thread(
153  [](MacroMakerSupervisor* s) {
154  MacroMakerSupervisor::RemoteControlWorkLoop(s);
155  },
156  this)
157  .detach();
158  }
159  else
160  __SUP_COUT_INFO__ << "Remote control over UDP is disabled." << __E__;
161  } // end setting up thread for UDP drive of state machine
162 
163  __SUP_COUT__ << "Constructed." << __E__;
164 } // end constructor
165 
166 //==============================================================================
167 MacroMakerSupervisor::~MacroMakerSupervisor(void) { destroy(); }
168 
169 //==============================================================================
170 void MacroMakerSupervisor::init(void)
171 {
172  // called by constructor
173 
174  // MacroMaker should consider all FE compatible types..
175  allFESupervisorInfo_ =
176  SupervisorInfoMap(allSupervisorInfo_.getAllFETypeSupervisorInfo());
177 
178 } // end init()
179 
180 //==============================================================================
181 void MacroMakerSupervisor::destroy(void)
182 {
183  // called by destructor
184 }
185 
186 //==============================================================================
190 } // end forceSupervisorPropertyValues()
191 
192 //==============================================================================
193 void MacroMakerSupervisor::tooltipRequest(xgi::Input* in, xgi::Output* out)
194 {
195  cgicc::Cgicc cgi(in);
196 
197  std::string Command = CgiDataUtilities::getData(cgi, "RequestType");
198  //__COUT__ << "Command = " << Command << __E__;
199 
200  std::string submittedSequence = CgiDataUtilities::postData(cgi, "sequence");
201 
202  // SECURITY CHECK START ****
203  if(securityCode_.compare(submittedSequence) != 0)
204  {
205  __COUT__ << "Unauthorized Request made, security sequence doesn't match!"
206  << __E__;
207  return;
208  }
209  // else
210  // {
211  // __COUT__ << "***Successfully authenticated security sequence." << __E__;
212  // }
213  // SECURITY CHECK END ****
214 
215  HttpXmlDocument xmldoc;
216 
217  if(Command == "check")
218  {
219  WebUsers::tooltipCheckForUsername(WebUsers::DEFAULT_ADMIN_USERNAME,
220  &xmldoc,
221  CgiDataUtilities::getData(cgi, "srcFile"),
222  CgiDataUtilities::getData(cgi, "srcFunc"),
223  CgiDataUtilities::getData(cgi, "srcId"));
224  }
225  else if(Command == "setNeverShow")
226  {
228  WebUsers::DEFAULT_ADMIN_USERNAME,
229  &xmldoc,
230  CgiDataUtilities::getData(cgi, "srcFile"),
231  CgiDataUtilities::getData(cgi, "srcFunc"),
232  CgiDataUtilities::getData(cgi, "srcId"),
233  CgiDataUtilities::getData(cgi, "doNeverShow") == "1" ? true : false,
234  CgiDataUtilities::getData(cgi, "temporarySilence") == "1" ? true : false);
235  }
236  else
237  __COUT__ << "Command Request, " << Command << ", not recognized." << __E__;
238 
239  xmldoc.outputXmlDocument((std::ostringstream*)out, false, true);
240 } // end tooltipRequest()
241 
242 //==============================================================================
243 void MacroMakerSupervisor::verification(xgi::Input* in, xgi::Output* out)
244 {
245  cgicc::Cgicc cgi(in);
246  std::string submittedSequence = CgiDataUtilities::getData(cgi, "code");
247  __COUT__ << "submittedSequence=" << submittedSequence << " " << time(0) << __E__;
248 
249  std::string securityWarning = "";
250 
251  if(securityCode_.compare(submittedSequence) != 0)
252  {
253  __COUT__ << "Unauthorized Request made, security sequence doesn't match!"
254  << __E__;
255  *out << "Invalid code.";
256  return;
257  }
258  else
259  {
260  // defaultSequence_ = false;
261  __COUT__ << "*** Successfully authenticated security sequence "
262  << "@ " << time(0) << __E__;
263 
264  if(defaultSequence_)
265  {
266  //__COUT__ << " UNSECURE!!!" << __E__;
267  securityWarning = "&secure=False";
268  }
269  }
270 
271  *out << "<!DOCTYPE HTML><html lang='en'><head><title>ots MacroMaker mode</title>" <<
272  // show ots icon
273  // from http://www.favicon-generator.org/
274  "<link rel='apple-touch-icon' sizes='57x57' href='/WebPath/images/otsdaqIcons/apple-icon-57x57.png'>\
275  <link rel='apple-touch-icon' sizes='60x60' href='/WebPath/images/otsdaqIcons/apple-icon-60x60.png'>\
276  <link rel='apple-touch-icon' sizes='72x72' href='/WebPath/images/otsdaqIcons/apple-icon-72x72.png'>\
277  <link rel='apple-touch-icon' sizes='76x76' href='/WebPath/images/otsdaqIcons/apple-icon-76x76.png'>\
278  <link rel='apple-touch-icon' sizes='114x114' href='/WebPath/images/otsdaqIcons/apple-icon-114x114.png'>\
279  <link rel='apple-touch-icon' sizes='120x120' href='/WebPath/images/otsdaqIcons/apple-icon-120x120.png'>\
280  <link rel='apple-touch-icon' sizes='144x144' href='/WebPath/images/otsdaqIcons/apple-icon-144x144.png'>\
281  <link rel='apple-touch-icon' sizes='152x152' href='/WebPath/images/otsdaqIcons/apple-icon-152x152.png'>\
282  <link rel='apple-touch-icon' sizes='180x180' href='/WebPath/images/otsdaqIcons/apple-icon-180x180.png'>\
283  <link rel='icon' type='image/png' sizes='192x192' href='/WebPath/images/otsdaqIcons/android-icon-192x192.png'>\
284  <link rel='icon' type='image/png' sizes='32x32' href='/WebPath/images/otsdaqIcons/favicon-32x32.png'>\
285  <link rel='icon' type='image/png' sizes='96x96' href='/WebPath/images/otsdaqIcons/favicon-96x96.png'>\
286  <link rel='icon' type='image/png' sizes='16x16' href='/WebPath/images/otsdaqIcons/favicon-16x16.png'>\
287  <link rel='manifest' href='/WebPath/images/otsdaqIcons/manifest.json'>\
288  <meta name='msapplication-TileColor' content='#ffffff'>\
289  <meta name='msapplication-TileImage' content='/ms-icon-144x144.png'>\
290  <meta name='theme-color' content='#ffffff'>"
291  <<
292  // end show ots icon
293  "</head>"
294  << "<frameset col='100%' row='100%'><frame "
295  "src='/WebPath/html/MacroMakerSupervisor.html?urn="
296  << this->getApplicationDescriptor()->getLocalId() << securityWarning
297  << "'></frameset></html>";
298 } // end verification()
299 
300 //==============================================================================
301 void MacroMakerSupervisor::generateURL()
302 {
303  defaultSequence_ = true;
304 
305  int length = 4;
306  FILE* fp = fopen((SEQUENCE_FILE_NAME).c_str(), "r");
307  if(fp)
308  {
309  __SUP_COUT_INFO__ << "Sequence length file found: " << SEQUENCE_FILE_NAME
310  << __E__;
311  char line[100];
312  fgets(line, 100, fp);
313  sscanf(line, "%d", &length);
314  fclose(fp);
315  if(length < 4)
316  length = 4; // don't allow shorter than 4
317  else
318  defaultSequence_ = false;
319  srand(time(0)); // randomize differently each "time"
320  }
321  else
322  {
323  __SUP_COUT_INFO__
324  << "(Reverting to default wiz security) Sequence length file NOT found: "
325  << SEQUENCE_FILE_NAME << __E__;
326  srand(0); // use same seed for convenience if file not found
327  }
328 
329  __SUP_COUT__ << "Sequence length = " << length << __E__;
330 
331  securityCode_ = "";
332 
333  const char alphanum[] =
334  "0123456789"
335  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
336  "abcdefghijklmnopqrstuvwxyz";
337 
338  for(int i = 0; i < length; ++i)
339  {
340  securityCode_ += alphanum[rand() % (sizeof(alphanum) - 1)];
341  }
342 
343  __SUP_COUT__ << __ENV__("HOSTNAME") << ":" << __ENV__("PORT")
344  << "/urn:xdaq-application:lid="
345  << this->getApplicationDescriptor()->getLocalId()
346  << "/Verify?code=" << securityCode_ << __E__;
347 
348  // Note: print out handled by start ots script now
349  // std::thread([&](WizardSupervisor *ptr, std::string securityCode)
350  // {printURL(ptr,securityCode);},this,securityCode_).detach();
351 
352  fp = fopen((SEQUENCE_OUT_FILE_NAME).c_str(), "w");
353  if(fp)
354  {
355  fprintf(fp, "%s", securityCode_.c_str());
356  fclose(fp);
357  }
358  else
359  __SUP_COUT_ERR__ << "Sequence output file NOT found: " << SEQUENCE_OUT_FILE_NAME
360  << __E__;
361 
362  return;
363 } // end generateURL()
364 
365 //==============================================================================
366 void MacroMakerSupervisor::requestIcons(xgi::Input* in, xgi::Output* out)
367 {
368  cgicc::Cgicc cgi(in);
369 
370  std::string submittedSequence = CgiDataUtilities::postData(cgi, "sequence");
371 
372  // SECURITY CHECK START ****
373  if(securityCode_.compare(submittedSequence) != 0)
374  {
375  __COUT__ << "Unauthorized Request made, security sequence doesn't match! "
376  << time(0) << __E__;
377  return;
378  }
379  else
380  {
381  __COUT__ << "***Successfully authenticated security sequence. " << time(0)
382  << __E__;
383  }
384  // SECURITY CHECK END ****
385 
386  // an icon is 7 fields.. give comma-separated
387  // 0 - subtext = text below icon
388  // 1 - altText = text for icon if image set to 0
389  // 2 - uniqueWin = if true, only one window is allowed, else multiple instances of
390  // window 3 - permissions = security level needed to see icon 4 - picfn = icon image
391  // filename, 0 for no image 5 - linkurl = url of the window to open 6 - folderPath =
392  // folder and subfolder location
393 
394  *out << "Macro Maker "
395  ",MM,0,1,icon-MacroMaker.png,/WebPath/html/"
396  "MacroMaker.html?urn=290,/"
397  ",FE Macros"
398  ",CFG,0,1,icon-Configure.png,/WebPath/html/"
399  "FEMacroTest.html?urn=290,/"
400  // << ",Console,C,1,1,icon-Console.png,/urn:xdaq-application:lid=260/,/"
401  << ",Code Editor,CODE,0,1,icon-CodeEditor.png,/urn:xdaq-application:lid=240/,/"
402  << "";
403 
404  // if there is a file of more icons, add to end of output
405  std::string iconFile = std::string(__ENV__("USER_DATA")) + "/MacroMakerModeIcons.dat";
406  __COUT__ << "Macro Maker mode user icons file: " << iconFile << __E__;
407  FILE* fp = fopen(iconFile.c_str(), "r");
408  if(fp)
409  {
410  __COUT__ << "Macro Maker mode user icons loading from " << iconFile << __E__;
411  fseek(fp, 0, SEEK_END);
412  const unsigned long fileSize = ftell(fp);
413  std::string fileString(fileSize, 0);
414  rewind(fp);
415  if(fread(&fileString[0], 1, fileSize, fp) != fileSize)
416  {
417  __COUT_ERR__ << "Unable to read proper size string from icons file!" << __E__;
418  return;
419  }
420 
421  fclose(fp);
422  __COUTV__(fileString);
423  *out << fileString;
424  }
425  else
426  __COUT__ << "Macro Maker mode user icons file not found: " << iconFile << __E__;
427  return;
428 } // end requestIcons()
429 
430 //==============================================================================
433 xoap::MessageReference MacroMakerSupervisor::supervisorSequenceCheck(
434  xoap::MessageReference message)
435 {
436  // SOAPUtilities::receive request parameters
437  SOAPParameters parameters;
438  parameters.addParameter("sequence");
439  SOAPUtilities::receive(message, parameters);
440 
441  std::string submittedSequence = parameters.getValue("sequence");
442 
443  // If submittedSequence matches securityCode_ then return full permissions (255)
444  // else, return permissions 0
445  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> permissionMap;
446 
447  if(securityCode_ == submittedSequence)
448  permissionMap.emplace(
449  std::pair<std::string /*groupName*/, WebUsers::permissionLevel_t>(
450  WebUsers::DEFAULT_USER_GROUP, WebUsers::PERMISSION_LEVEL_ADMIN));
451  else
452  {
453  __COUT__ << "Unauthorized Request made, security sequence doesn't match!"
454  << std::endl;
455 
456  permissionMap.emplace(
457  std::pair<std::string /*groupName*/, WebUsers::permissionLevel_t>(
458  WebUsers::DEFAULT_USER_GROUP, WebUsers::PERMISSION_LEVEL_INACTIVE));
459  }
460 
461  // fill return parameters
462  SOAPParameters retParameters;
463  retParameters.addParameter("Permissions", StringMacros::mapToString(permissionMap));
464 
465  return SOAPUtilities::makeSOAPMessageReference("SequenceResponse", retParameters);
466 } //end supervisorSequenceCheck()
467 
468 //==============================================================================
471 void MacroMakerSupervisor::RemoteControlWorkLoop(MacroMakerSupervisor* theSupervisor)
472 {
473  // ConfigurationTree configLinkNode = theSupervisor->CorePropertySupervisorBase::getSupervisorTableNode();
474 
475  std::string ipAddressForRemoteControlOverUDP = __ENV__(
476  "OTS_MACROMAKER_UDP_IP"); //configLinkNode.getNode("IPAddressForStateChangesOverUDP").getValue<std::string>();
477  int portForRemoteControlOverUDP = atoi(__ENV__(
478  "OTS_MACROMAKER_UDP_PORT")); //configLinkNode.getNode("PortForStateChangesOverUDP").getValue<int>();
479  bool acknowledgementEnabled =
480  true; //configLinkNode.getNode("EnableAckForStateChangesOverUDP").getValue<bool>();
481 
482  __COUTV__(ipAddressForRemoteControlOverUDP);
483  __COUTV__(portForRemoteControlOverUDP);
484  __COUTV__(acknowledgementEnabled);
485 
486  TransceiverSocket sock(ipAddressForRemoteControlOverUDP,
487  portForRemoteControlOverUDP); // Take Port from Table
488  try
489  {
490  sock.initialize();
491  }
492  catch(...)
493  {
494  // generate special message to indicate failed socket
495  __SS__ << "FATAL Console error. Could not initialize socket at ip '"
496  << ipAddressForRemoteControlOverUDP << "' and port "
497  << portForRemoteControlOverUDP
498  << ". Perhaps it is already in use? Exiting Remote Control "
499  "SOAPUtilities::receive loop."
500  << __E__;
501  __SS_THROW__;
502  return;
503  }
504 
505  std::string buffer;
506  __COUT__ << "UDP Remote Control workloop starting..." << __E__;
507  while(1)
508  {
509  // workloop procedure
510  // if SOAPUtilities::receive a UDP command
511  // execute command
512  // else
513  // sleep
514 
515  if(sock.receive(
516  buffer, 0 /*timeoutSeconds*/, 1 /*timeoutUSeconds*/, false /*verbose*/) !=
517  -1)
518  {
519  __COUT__ << "UDP Remote Control packet received of size = " << buffer.size()
520  << __E__;
521  __COUTV__(buffer);
522 
523  try
524  {
525  if(buffer == "GetFrontendMacroInfo")
526  {
527  std::string macroPath = (std::string)MACROS_DB_PATH + "NO-USER" + "/";
528  mkdir(macroPath.c_str(), 0755);
529  std::string histPath =
530  (std::string)MACROS_HIST_PATH + "NO-USER" + "/";
531  mkdir(histPath.c_str(), 0755);
532 
533  HttpXmlDocument xmldoc;
534  theSupervisor->getFEMacroList(xmldoc, "NO-USER");
535 
536  std::stringstream out;
537  xmldoc.outputXmlDocument((std::ostringstream*)&out,
538  false /*dispStdOut*/,
539  false /*allowWhiteSpace*/);
540  __COUT__ << "out: " << out.str();
541  sock.acknowledge(out.str(),
542  true /* verbose */,
543  1500 /* max chunk size*/,
544  1000 /* inter-chunk delay us */);
545  }
546  else if(buffer.find("RunFrontendMacro") == 0)
547  {
548  HttpXmlDocument xmldoc;
549  __COUTV__(buffer);
550  std::vector<std::string> bufferFields =
551  StringMacros::getVectorFromString(buffer, {';'});
552  if(bufferFields.size() < 8)
553  {
554  __SS__ << "Missing input arguments for running FE Macro: "
555  << bufferFields.size() << " vs 8 expected" << __E__;
556  __SS_THROW__;
557  }
558 
559  std::string feClassSelected = bufferFields[1];
560  std::string feUIDSelected =
561  bufferFields[2]; // allow CSV multi-selection
562  std::string macroType = bufferFields[3]; // "fe", "public", "private"
563  std::string macroName =
564  StringMacros::decodeURIComponent(bufferFields[4]);
565  std::string inputArgs = StringMacros::decodeURIComponent(
566  bufferFields[5]); //two level ;- and ,- separated
567  std::string outputArgs =
568  StringMacros::decodeURIComponent(bufferFields[6]); //,- separated
569  bool saveOutputs = bufferFields[7] == "1";
570  std::string username = "NO-USER";
571  std::string userGroupPermission = "allUsers: 255";
572 
573  // Build the same per-UID group execution model used by CGI runFEMacro
574  std::set<std::string> feUIDs;
575  {
576  std::string expandUID =
577  feUIDSelected.empty() ? "*" : feUIDSelected;
578  if(expandUID != "*")
579  StringMacros::getSetFromString(expandUID, feUIDs);
580  else
581  {
582  for(auto& feTypePair : theSupervisor->FEPluginTypetoFEsMap_)
583  {
584  if(feClassSelected.empty() || feClassSelected == "*" ||
585  feClassSelected == feTypePair.first)
586  for(auto& uid : feTypePair.second)
587  feUIDs.emplace(uid);
588  }
589  }
590  if(feUIDs.empty())
591  feUIDs.emplace(
592  feUIDSelected); // fallback to existing error behavior
593  }
594 
595  auto group = std::make_shared<runFEMacroGroupStruct>();
596  group->historyFeClassSelected_ =
597  feClassSelected.empty() ? "*" : feClassSelected;
598  group->historyFeUIDSelected_ =
599  feUIDSelected.empty() ? "*" : feUIDSelected;
600  group->historyMacroType_ = macroType;
601  group->historyMacroName_ = macroName;
602  group->historyInputArgs_ = inputArgs;
603  group->historyOutputArgs_ = outputArgs;
604  group->historySaveOutputs_ = saveOutputs;
605  group->historyUsername_ = username;
606 
607  for(const std::string& uid : feUIDs)
608  group->tasks_.push_back(
609  std::make_shared<runFEMacroStruct>(xmldoc,
610  feClassSelected,
611  uid,
612  macroType,
613  macroName,
614  inputArgs,
615  outputArgs,
616  saveOutputs,
617  username,
618  userGroupPermission));
619 
620  {
621  std::lock_guard<std::mutex> lock(
622  theSupervisor->feMacroRunThreadStructMutex_);
623  group->groupID_ = ++theSupervisor->feMacroRunGroupIDCounter_;
624  if(theSupervisor->feMacroRunGroupIDCounter_ == 0)
625  group->groupID_ = ++theSupervisor->feMacroRunGroupIDCounter_;
626 
627  for(auto& task : group->tasks_)
628  {
629  task->bar_ = std::make_unique<ProgressBar>();
630  task->bar_->reset(macroName,
631  task->parameters_.feUIDSelected_);
632  }
633  theSupervisor->feMacroRunThreadStruct_.emplace_back(group);
634  }
635 
636  std::thread([group, theSupervisor]() {
637  MacroMakerSupervisor::runFEMacroGroupSchedulerThread(
638  group, theSupervisor);
639  }).detach();
640 
641  auto lastProgressSend = std::chrono::steady_clock::now();
642  while(!group->allDone())
643  {
644  usleep(100 * 1000); // poll at 100 ms
645 
646  auto now = std::chrono::steady_clock::now();
647  if(std::chrono::duration_cast<std::chrono::seconds>(
648  now - lastProgressSend)
649  .count() >= 2)
650  {
651  lastProgressSend = now;
652 
653  int totalProgress = 0;
654  int taskCount = 0;
655  {
656  std::lock_guard<std::mutex> lock(
657  theSupervisor->feMacroRunThreadStructMutex_);
658  for(auto& task : group->tasks_)
659  {
660  ++taskCount;
661  if(task->feMacroRunDone_)
662  totalProgress += 100;
663  else if(task->bar_)
664  totalProgress += task->bar_->read();
665  }
666  }
667 
668  int percent =
669  (taskCount > 0) ? (totalProgress / taskCount) : 0;
670  if(percent >= 100)
671  percent = 99;
672 
673  __COUTV__(percent);
674  sock.acknowledge(std::string("<progress>") +
675  std::to_string(percent) + "</progress>",
676  true /* verbose */);
677  }
678  }
679 
680  // Collect any task error before cleanup so group is always removed
681  std::string taskError;
682  for(auto& task : group->tasks_)
683  {
684  if(task->parameters_.feMacroRunError_ != "")
685  {
686  taskError = task->parameters_.feMacroRunError_;
687  break;
688  }
689  xmldoc.copyDataChildren(task->parameters_.xmldoc_);
690  }
691 
692  {
693  std::lock_guard<std::mutex> lock(
694  theSupervisor->feMacroRunThreadStructMutex_);
695  for(size_t i = 0;
696  i < theSupervisor->feMacroRunThreadStruct_.size();
697  ++i)
698  if(theSupervisor->feMacroRunThreadStruct_[i].get() ==
699  group.get())
700  {
701  theSupervisor->feMacroRunThreadStruct_.erase(
702  theSupervisor->feMacroRunThreadStruct_.begin() + i);
703  break;
704  }
705  }
706 
707  if(!taskError.empty())
708  {
709  __SS__ << taskError;
710  __SS_THROW__;
711  }
712 
713  std::stringstream out;
714  xmldoc.outputXmlDocument((std::ostringstream*)&out,
715  false /*dispStdOut*/,
716  true /*allowWhiteSpace*/);
717  __COUT__ << "out: " << out.str();
718  sock.acknowledge(out.str(),
719  true /* verbose */,
720  1500 /* max chunk size*/,
721  1000 /* inter-chunk delay us */);
722  }
723  else
724  {
725  __SS__ << "Unrecognized UDP command received: " << buffer << __E__;
726  __SS_THROW__;
727  }
728  }
729  catch(const std::runtime_error& e)
730  {
731  __COUT_ERR__ << "Error during UDP command handling: " << e.what()
732  << __E__;
733  sock.acknowledge(std::string("Error: ") + e.what(), true /* verbose */);
734  }
735  catch(...)
736  {
737  __COUT_ERR__ << "Unknown error caught during UDP command handling - "
738  "check the logs."
739  << __E__;
740  sock.acknowledge(std::string("Error: ") + "unknown error caught",
741  true /* verbose */);
742  }
743 
744  __COUT__ << "Done handling command '" << buffer << "'" << __E__;
745  }
746  else
747  usleep(1000);
748  }
749 } // end RemoteControlWorkLoop()
750 
751 //==============================================================================
754 void MacroMakerSupervisor::requestWrapper(xgi::Input* in, xgi::Output* out)
755 {
756  // use default wrapper if not Macro Maker mode
757  if(!CorePropertySupervisorBase::allSupervisorInfo_.isMacroMakerMode())
758  {
759  __SUP_COUTT__ << "Default request wrapper" << __E__;
760  return CoreSupervisorBase::requestWrapper(in, out);
761  }
762  // else very specialized Macro Maker mode!
763 
764  __SUP_COUTT__ << "MacroMaker mode request handler!" << __E__;
765 
766  // checkSupervisorPropertySetup();
767 
768  cgicc::Cgicc cgiIn(in);
769 
770  std::string submittedSequence = CgiDataUtilities::postData(cgiIn, "sequence");
771 
772  // SECURITY CHECK START ****
773  if(securityCode_.compare(submittedSequence) != 0)
774  {
775  __COUT__ << "Unauthorized Request made, security sequence doesn't match! "
776  << time(0) << __E__;
777  *out << WebUsers::REQ_NO_PERMISSION_RESPONSE.c_str();
778  return;
779  }
780  else
781  {
782  __SUP_COUTT__ << "***Successfully authenticated security sequence. " << time(0)
783  << __E__;
784  }
785  // SECURITY CHECK END ****
786 
787  std::string requestType = CgiDataUtilities::getData(cgiIn, "RequestType");
788 
789  __SUP_COUT_TYPE__(TLVL_DEBUG + 10) << __COUT_HDR__ << "requestType " << requestType
790  << " files: " << cgiIn.getFiles().size() << __E__;
791 
792  HttpXmlDocument xmlOut;
793  WebUsers::RequestUserInfo userInfo(
794  requestType, CgiDataUtilities::getOrPostData(cgiIn, "CookieCode"));
795 
797 
798  // copied from WebUsers::checkRequestAccess
799  userInfo.username_ = "admin";
800  userInfo.displayName_ = "Admin";
801  userInfo.usernameWithLock_ = "admin";
802  userInfo.userSessionIndex_ = 0;
803  std::map<std::string /*groupName*/, WebUsers::permissionLevel_t> initPermissions = {
804  {WebUsers::DEFAULT_USER_GROUP, WebUsers::PERMISSION_LEVEL_ADMIN}};
805  userInfo.setGroupPermissionLevels(StringMacros::mapToString(initPermissions));
806 
807  if(TTEST(1))
808  __SUP_COUTT__ << "requestType: " << requestType << __E__;
809  else if(!userInfo.automatedCommand_)
810  __SUP_COUT__ << "requestType: " << requestType << __E__;
811 
812  if(userInfo.NonXMLRequestType_)
813  {
814  try
815  {
816  nonXmlRequest(requestType, cgiIn, *out, userInfo);
817  }
818  catch(const std::runtime_error& e)
819  {
820  __SUP_SS__ << "An error was encountered handling requestType '" << requestType
821  << "':" << e.what() << __E__;
822  __SUP_COUT_ERR__ << "\n" << ss.str();
823  }
824  catch(...)
825  {
826  __SUP_SS__ << "An unknown error was encountered handling requestType '"
827  << requestType << ".' "
828  << "Please check the printouts to debug." << __E__;
829  try
830  {
831  throw;
832  } //one more try to printout extra info
833  catch(const std::exception& e)
834  {
835  ss << "Exception message: " << e.what();
836  }
837  catch(...)
838  {
839  }
840  __SUP_COUT_ERR__ << "\n" << ss.str();
841  }
842  return;
843  }
844  // else xml request type
845 
846  try
847  {
848  // call derived class' request()
849  request(requestType, cgiIn, xmlOut, userInfo);
850  __SUP_COUTT__ << "Request '" << requestType << "' complete." << __E__;
851  }
852  catch(const std::runtime_error& e)
853  {
854  __SUP_SS__ << "An error was encountered handling requestType '" << requestType
855  << "':" << e.what() << __E__;
856  __SUP_COUT_ERR__ << "\n" << ss.str();
857  xmlOut.addTextElementToData("Error", ss.str());
858  }
859  catch(...)
860  {
861  __SUP_SS__ << "An unknown error was encountered handling requestType '"
862  << requestType << ".' "
863  << "Please check the printouts to debug." << __E__;
864  try
865  {
866  throw;
867  } //one more try to printout extra info
868  catch(const std::exception& e)
869  {
870  ss << "Exception message: " << e.what();
871  }
872  catch(...)
873  {
874  }
875  __SUP_COUT_ERR__ << "\n" << ss.str();
876  xmlOut.addTextElementToData("Error", ss.str());
877  }
878 
879  // report any errors encountered
880  {
881  unsigned int occurance = 0;
882  std::string err = xmlOut.getMatchingValue("Error", occurance++);
883  while(err != "")
884  {
885  __SUP_COUT_ERR__ << "'" << requestType << "' ERROR encountered: " << err
886  << __E__;
887  err = xmlOut.getMatchingValue("Error", occurance++);
888  }
889  }
890 
891  __SUP_COUTVS__(10, userInfo.NoXmlWhiteSpace_);
892 
893  // return xml doc holding server response
894  xmlOut.outputXmlDocument((std::ostringstream*)out,
895  false /*print to cout*/,
896  !userInfo.NoXmlWhiteSpace_ /*allow whitespace*/);
897 } // end requestWrapper()
898 
899 //==============================================================================
900 void MacroMakerSupervisor::request(const std::string& requestType,
901  cgicc::Cgicc& cgiIn,
902  HttpXmlDocument& xmlOut,
903  const WebUsers::RequestUserInfo& userInfo)
904 try
905 {
906  std::chrono::steady_clock::time_point requestStart = std::chrono::steady_clock::now();
907  time_t requestStartTime = time(0);
908 
909  // sanitize username
910  std::string username = "";
911  for(unsigned int i = 0; i < userInfo.username_.size(); ++i)
912  if((userInfo.username_[i] >= 'a' && userInfo.username_[i] <= 'z') ||
913  (userInfo.username_[i] >= 'A' && userInfo.username_[i] <= 'Z') ||
914  (userInfo.username_[i] >= '0' && userInfo.username_[i] <= '9') ||
915  userInfo.username_[i] >= '-' || userInfo.username_[i] <= '_')
916  username += userInfo.username_[i];
917 
918  if(username.size() < 2)
919  {
920  __SUP_SS__ << "Illegal username '" << userInfo.username_ << "' received."
921  << __E__;
922  __SUP_SS_THROW__;
923  }
924 
925  __SUP_COUT__ << "User name is " << userInfo.username_ << "." << __E__;
926  __SUP_COUT__ << "User permission level for request '" << requestType << "' is "
927  << unsigned(userInfo.permissionLevel_) << "." << __E__;
928 
929  // handle request per requestType
930 
931  if(requestType == "loadFEHistory")
932  {
933  std::string histPath = (std::string)MACROS_HIST_PATH + userInfo.username_ + "/";
934  mkdir(histPath.c_str(), 0755);
935  }
936 
937  if(requestType == "loadFEMacroSequences")
938  {
939  std::string seqPath =
940  (std::string)MACROS_SEQUENCE_PATH + userInfo.username_ + "/";
941  mkdir(seqPath.c_str(), 0755);
942  }
943 
944  if(requestType == "getPermission")
945  {
946  xmlOut.addTextElementToData("Permission",
947  std::to_string(unsigned(userInfo.permissionLevel_)));
948  // create macro maker folders for the user (the first time a user authenticates
949  // with macro maker)
950  std::string publicPath = (std::string)MACROS_DB_PATH + "publicMacros/";
951  mkdir(publicPath.c_str(), 0755);
952  std::string exportPath =
953  __ENV__("SERVICE_DATA_PATH") + MACROS_EXPORT_PATH + userInfo.username_ + "/";
954  mkdir(exportPath.c_str(), 0755);
955  }
956  else
957  handleRequest(requestType, xmlOut, cgiIn, userInfo);
958 
959  __SUP_COUTT__ << "Total MacroMaker request time: "
960  << artdaq::TimeUtils::GetElapsedTime(requestStart) << " = "
961  << time(0) - requestStartTime << " seconds" << __E__;
962 } // end request()
963 catch(const std::runtime_error& e)
964 {
965  __SS__ << "Error occurred handling request '" << requestType << "': " << e.what()
966  << __E__;
967  __SUP_COUT__ << ss.str();
968  xmlOut.addTextElementToData("Error", ss.str());
969 }
970 catch(...)
971 {
972  __SS__ << "Unknown error occurred handling request '" << requestType << "!'" << __E__;
973  try
974  {
975  throw;
976  } //one more try to printout extra info
977  catch(const std::exception& e)
978  {
979  ss << "Exception message: " << e.what();
980  }
981  catch(...)
982  {
983  }
984  __SUP_COUT__ << ss.str();
985  xmlOut.addTextElementToData("Error", ss.str());
986 } // end request() error handling
987 
988 //==============================================================================
989 
990 void MacroMakerSupervisor::handleRequest(const std::string Command,
991  HttpXmlDocument& xmldoc,
992  cgicc::Cgicc& cgi,
993  const WebUsers::RequestUserInfo& userInfo)
994 {
995  if(Command == "FElist") // called by MacroMaker GUI
996  {
997  //make user directories if needed
998  std::string macroPath = (std::string)MACROS_DB_PATH + userInfo.username_ + "/";
999  mkdir(macroPath.c_str(), 0755);
1000  std::string histPath = (std::string)MACROS_HIST_PATH + userInfo.username_ + "/";
1001  mkdir(histPath.c_str(), 0755);
1002 
1003  getFElist(xmldoc);
1004  }
1005  else if(Command == "writeData") // called by MacroMaker GUI
1006  writeData(xmldoc, cgi, userInfo.username_);
1007  else if(Command == "readData") // called by MacroMaker GUI
1008  readData(xmldoc, cgi, userInfo.username_);
1009  else if(Command == "createMacro") // called by MacroMaker GUI
1010  createMacro(xmldoc, cgi, userInfo.username_);
1011  else if(Command == "loadMacros") // called by MacroMaker GUI
1012  loadMacros(xmldoc, userInfo.username_);
1013  else if(Command == "loadHistory") // called by MacroMaker GUI
1014  loadHistory(xmldoc, userInfo.username_);
1015  else if(Command == "deleteMacro") // called by MacroMaker GUI
1016  deleteMacro(xmldoc, cgi, userInfo.username_);
1017  else if(Command == "editMacro") // called by MacroMaker GUI
1018  editMacro(xmldoc, cgi, userInfo.username_);
1019  else if(Command == "clearHistory") // called by MacroMaker GUI
1020  clearHistory(userInfo.username_);
1021  else if(Command == "exportMacro") // called by MacroMaker GUI
1022  exportMacro(xmldoc, cgi, userInfo.username_);
1023  else if(Command == "exportFEMacro") // called by MacroMaker GUI
1024  exportFEMacro(xmldoc, cgi, userInfo.username_);
1025  else if(Command == "getFEMacroList") // called by FE Macro Test and returns FE Macros
1026  // and Macro Maker Macros
1027  {
1028  //make user directories if needed
1029  std::string macroPath = (std::string)MACROS_DB_PATH + userInfo.username_ + "/";
1030  mkdir(macroPath.c_str(), 0755);
1031  std::string histPath = (std::string)MACROS_HIST_PATH + userInfo.username_ + "/";
1032  mkdir(histPath.c_str(), 0755);
1033 
1034  getFEMacroList(xmldoc, userInfo.username_);
1035  }
1036  else if(Command == "runFEMacro") // called by FE Macro Test returns FE Macros and
1037  // Macro Maker Macros
1038  runFEMacro(xmldoc, cgi, userInfo);
1039  else if(Command == "loadFEHistory") // called by FE Macro Test and returns FE Macros
1040  // and Macro Maker Macros
1041  loadFEHistory(xmldoc, userInfo.username_);
1042  else if(Command == "clearFEHistory") // called by FE Macro Test returns FE Macros and
1043  // Macro Maker Macros
1044  clearFEHistory(userInfo.username_);
1045  else if(Command == "loadFEMacroSequences")
1046  loadFEMacroSequences(xmldoc, userInfo.username_);
1047  else if(Command == "saveFEMacroSequence")
1048  saveFEMacroSequence(cgi, userInfo.username_);
1049  else if(Command == "getFEMacroSequence")
1050  getFEMacroSequence(xmldoc, cgi, userInfo.username_);
1051  else if(Command == "deleteFEMacroSequence")
1052  deleteFEMacroSequence(cgi, userInfo.username_);
1053  else if(Command == "makeSequencePublic")
1054  makeSequencePublic(cgi, userInfo.username_);
1055  else
1056  xmldoc.addTextElementToData("Error",
1057  "Command '" + Command +
1058  "' not recognized by the Macro Maker Supervisor "
1059  "(was it intended for another Supervisor?).");
1060 } // end handleRequest()
1061 
1062 //==============================================================================
1063 xoap::MessageReference MacroMakerSupervisor::frontEndCommunicationRequest(
1064  xoap::MessageReference message)
1065 try
1066 {
1067  __COUTT__; //mark for debugging
1068  __SUP_COUT__ << "FE Request received: " << SOAPUtilities::translate(message) << __E__;
1069 
1070  SOAPParameters typeParameter, rxParameters; // params for xoap to recv
1071  typeParameter.addParameter("type");
1072  SOAPUtilities::receive(message, typeParameter);
1073 
1074  std::string type = typeParameter.getValue("type");
1075 
1076  std::string error = "";
1077 
1078  if(type == "initFElist") // gateway initializes during configure
1079  {
1080  __SUP_COUTV__(type);
1081 
1082  rxParameters.addParameter("groupName");
1083  rxParameters.addParameter("groupKey");
1084  rxParameters.addParameter("SubsystemCommonList");
1085  rxParameters.addParameter("SubsystemCommonOverrideList");
1086  SOAPUtilities::receive(message, rxParameters);
1087 
1088  std::string groupName = rxParameters.getValue("groupName");
1089  std::string groupKey = rxParameters.getValue("groupKey");
1090  std::string subsystemCommonList = rxParameters.getValue("SubsystemCommonList");
1091  std::string subsystemCommonOverrideList =
1092  rxParameters.getValue("SubsystemCommonOverrideList");
1093 
1094  __SUP_COUTV__(groupName);
1095  __SUP_COUTV__(groupKey);
1096  __SUP_COUTV__(subsystemCommonList);
1097  __SUP_COUTV__(subsystemCommonOverrideList);
1098 
1099  // Assemble Subsystem Common Table List ----------------
1100  std::map<std::string /* tableName */, TableVersion> mergeInTables, overrideTables;
1101  {
1102  { //handle common merge-in list
1103  if(!subsystemCommonList.empty())
1104  {
1105  subsystemCommonList =
1106  StringMacros::decodeURIComponent(subsystemCommonList);
1107  __COUT__ << "Transition parameter SubsystemCommonList: "
1108  << subsystemCommonList << __E__;
1109  StringMacros::getMapFromString(subsystemCommonList, mergeInTables);
1110  __COUTV__(StringMacros::mapToString(mergeInTables));
1111  }
1112  } //end handle common merge-in list
1113 
1114  { //handle common override list
1115  if(!subsystemCommonOverrideList.empty())
1116  {
1117  subsystemCommonOverrideList =
1118  StringMacros::decodeURIComponent(subsystemCommonOverrideList);
1119  __COUT__ << "Transition parameter SubsystemCommonOverrideList: "
1120  << subsystemCommonOverrideList << __E__;
1121  StringMacros::getMapFromString(subsystemCommonOverrideList,
1122  overrideTables);
1123  __COUTV__(StringMacros::mapToString(overrideTables));
1124  }
1125  } //end handle common override list
1126  } // end Assemble Subsystem Common Table List ----------------
1127 
1128  ConfigurationManager cfgMgr;
1129  cfgMgr.loadTableGroup(groupName,
1130  TableGroupKey(groupKey),
1131  true,
1132 
1133  0 /*groupMembers */,
1134  0 /*progressBar */,
1135  0 /*accumulateWarnings*/,
1136  0 /*groupComment */,
1137  0 /*groupAuthor */,
1138  0 /*groupCreateTime */,
1139  false /*doNotLoadMember */,
1140  0 /*groupTypeString */,
1141  0 /*groupAliases */,
1142  ConfigurationManager::LoadGroupType::ALL_TYPES,
1143  true /*ignoreVersionTracking*/,
1144  mergeInTables /* mergeInTables */,
1145  overrideTables /* overrideTables */
1146  );
1147 
1148  // for each FESupervisor
1149  // get all front end children
1150 
1151  const SupervisorInfoMap& feTypeSupervisors =
1152  CorePropertySupervisorBase::allSupervisorInfo_.getAllFETypeSupervisorInfo();
1153 
1154  ConfigurationTree appsNode =
1155  cfgMgr.getNode(ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME);
1156 
1157  __SUP_COUT__ << "Number of FE Supervisors found = " << feTypeSupervisors.size()
1158  << __E__;
1159 
1160  FEPluginTypetoFEsMap_.clear(); // reset
1161  FEtoSupervisorMap_.clear(); // reset
1162  FEtoPluginTypeMap_.clear(); // reset
1163  for(auto& feApp : feTypeSupervisors)
1164  {
1165  __SUP_COUT__ << "FEs for app " << feApp.first << ":" << feApp.second.getName()
1166  << __E__;
1167 
1168  auto feChildren = appsNode.getNode(feApp.second.getName())
1169  .getNode("LinkToSupervisorTable")
1170  .getNode("LinkToFEInterfaceTable")
1171  .getChildren();
1172 
1173  for(auto& fe : feChildren)
1174  {
1175  if(!fe.second.status())
1176  continue; // skip disabled FEs
1177 
1178  __SUP_COUTV__(fe.first);
1179  FEtoSupervisorMap_[fe.first] = feApp.first;
1180 
1181  std::string pluginType =
1182  fe.second.getNode("FEInterfacePluginName").getValue();
1183  FEPluginTypetoFEsMap_[pluginType].emplace(fe.first);
1184  FEtoPluginTypeMap_[fe.first] = pluginType;
1185  }
1186  }
1187 
1188  __SUP_COUTV__(StringMacros::mapToString(FEtoSupervisorMap_));
1189  __SUP_COUTV__(StringMacros::mapToString(FEPluginTypetoFEsMap_));
1190  __SUP_COUTV__(StringMacros::mapToString(FEtoPluginTypeMap_));
1191  }
1192  else if(type == "feSend" || // from front-ends
1193  type == "feMacro" || // from front-ends
1194  type == "feMacroMultiDimensionalStart" || // from iterator
1195  type == "feMacroMultiDimensionalCheck" || // from iterator
1196  type == "macroMultiDimensionalStart" || // from iterator
1197  type == "macroMultiDimensionalCheck") // from iterator
1198  {
1199  __SUP_COUTV__(type);
1200 
1201  rxParameters.addParameter("targetInterfaceID");
1202  SOAPUtilities::receive(message, rxParameters);
1203 
1204  std::string targetInterfaceID = rxParameters.getValue("targetInterfaceID");
1205 
1206  __SUP_COUTV__(targetInterfaceID);
1207 
1208  auto feIt = FEtoSupervisorMap_.find(targetInterfaceID);
1209  if(feIt == FEtoSupervisorMap_.end())
1210  {
1211  __SUP_SS__ << "Destination front end interface ID '" << targetInterfaceID
1212  << "' was not found in the list of front ends." << __E__;
1213  __SUP_SS_THROW__;
1214  }
1215 
1216  unsigned int FESupervisorIndex = feIt->second;
1217  __SUP_COUT__ << "Found supervisor index: " << FESupervisorIndex << __E__;
1218 
1219  SupervisorInfoMap::iterator it = allFESupervisorInfo_.find(FESupervisorIndex);
1220  if(it == allFESupervisorInfo_.end())
1221  {
1222  __SUP_SS__ << "Error transmitting request to FE Supervisor '"
1223  << targetInterfaceID << ":" << FESupervisorIndex << ".' \n\n"
1224  << "The FE Supervisor Index does not exist. Have you configured "
1225  "the state machine properly?"
1226  << __E__;
1227  __SUP_SS_THROW__;
1228  }
1229 
1230  if(type == "macroMultiDimensionalStart")
1231  {
1232  // add Macro sequence (and check macro exists)
1233 
1234  SOAPParameters rxParameters;
1235  rxParameters.addParameter("macroName");
1236  SOAPUtilities::receive(message, rxParameters);
1237  std::string macroName = rxParameters.getValue("macroName");
1238  __SUP_COUTV__(macroName);
1239 
1240  std::string macroString;
1241  loadMacro(macroName, macroString);
1242 
1243  SOAPParameters parameters;
1244  parameters.addParameter("macroString", macroString);
1245  SOAPUtilities::addParameters(message, parameters);
1246  }
1247 
1248  try
1249  {
1250  __SUP_COUT__ << "Forwarding request: " << SOAPUtilities::translate(message)
1251  << __E__;
1252 
1253  xoap::MessageReference replyMessage =
1254  SOAPMessenger::sendWithSOAPReply(it->second.getDescriptor(), message);
1255 
1256  if(type != "feSend")
1257  {
1258  __SUP_COUT__ << "Forwarding FE Macro response: "
1259  << SOAPUtilities::translate(replyMessage) << __E__;
1260 
1261  return replyMessage;
1262  }
1263  }
1264  catch(const xdaq::exception::Exception& e)
1265  {
1266  __SUP_SS__ << "Error forwarding FE Communication request to FE Supervisor '"
1267  << targetInterfaceID << ":" << FESupervisorIndex << ".' "
1268  << "Have you configured the state machine properly?\n\n"
1269  << e.what() << __E__;
1270  __SUP_SS_THROW__;
1271  }
1272  }
1273  else
1274  {
1275  __SUP_SS__ << "Unrecognized FE Communication type: " << type << __E__;
1276  __SUP_SS_THROW__;
1277  }
1278 
1279  return SOAPUtilities::makeSOAPMessageReference("Received");
1280 } // end frontEndCommunicationRequest()
1281 catch(const std::runtime_error& e)
1282 {
1283  __SUP_SS__ << "Error processing FE communication request: " << e.what() << __E__;
1284  __SUP_COUT_ERR__ << ss.str();
1285 
1286  xoap::MessageReference returnMessage =
1287  SOAPUtilities::makeSOAPMessageReference("Error");
1288 
1289  SOAPParameters parameters;
1290  parameters.addParameter("Error", ss.str());
1291  SOAPUtilities::addParameters(returnMessage, parameters);
1292  return returnMessage;
1293 }
1294 catch(...)
1295 {
1296  xoap::MessageReference returnMessage =
1297  SOAPUtilities::makeSOAPMessageReference("Error");
1298 
1299  __SUP_SS__ << "Unknown error processing FE communication request." << __E__;
1300  try
1301  {
1302  throw;
1303  } //one more try to printout extra info
1304  catch(const std::exception& e)
1305  {
1306  ss << "Exception message: " << e.what();
1307  }
1308  catch(...)
1309  {
1310  }
1311  __SUP_COUT_ERR__ << ss.str();
1312 
1313  SOAPParameters parameters;
1314  parameters.addParameter("Error", ss.str());
1315  SOAPUtilities::addParameters(returnMessage, parameters);
1316  return returnMessage;
1317 } // end frontEndCommunicationRequest() catch
1318 
1319 //==============================================================================
1320 void MacroMakerSupervisor::getFElist(HttpXmlDocument& xmldoc)
1321 {
1322  __SUP_COUT__ << "Getting FE list!!!!!!!!!" << __E__;
1323 
1324  SOAPParameters txParameters; // params for xoap to send
1325  txParameters.addParameter("Request", "GetInterfaces");
1326 
1327  SOAPParameters rxParameters; // params for xoap to recv
1328  rxParameters.addParameter("Command");
1329  rxParameters.addParameter("FEList");
1330  rxParameters.addParameter("frontEndError"); // if there were errors recorded (during
1331  // configuration, e.g. in Macro Maker
1332  // only mode)
1333 
1334  SupervisorInfoMap::const_iterator it;
1335  std::string oneInterface;
1336  std::string rxFEList;
1337  std::string rxFrontEndError;
1338 
1339  size_t lastColonIndex;
1340 
1341  // for each list of FE Supervisors,
1342  // loop through each FE Supervisors and get FE interfaces list
1343  for(auto& appInfo : allFESupervisorInfo_)
1344  {
1345  // __SUP_COUT__ << "Number of " << listPair.first << " = " <<
1346  // listPair.second.size() << __E__;
1347  //
1348  // for (it = listPair.second.begin(); it != listPair.second.end(); it++)
1349  // {
1350 
1351  __SUP_COUT__ << "FESupervisor LID = " << appInfo.second.getId()
1352  << " name = " << appInfo.second.getName() << __E__;
1353 
1354  try
1355  {
1356  xoap::MessageReference retMsg =
1357  SOAPMessenger::sendWithSOAPReply(appInfo.second.getDescriptor(),
1358  "MacroMakerSupervisorRequest",
1359  txParameters);
1360  SOAPUtilities::receive(retMsg, rxParameters);
1361 
1362  __SUP_COUT__ << "Received MacroMaker response: "
1363  << SOAPUtilities::translate(retMsg).getCommand() << "==>"
1364  << SOAPUtilities::translate(retMsg) << __E__;
1365 
1366  if(SOAPUtilities::translate(retMsg).getCommand() == "Fault")
1367  {
1368  __SUP_SS__ << "Unrecognized command received!" << __E__;
1369  __SUP_SS_THROW__;
1370  }
1371  }
1372  catch(const xdaq::exception::Exception& e)
1373  {
1374  __SUP_SS__ << "Error transmitting request to FE Supervisor LID = "
1375  << appInfo.second.getId() << " name = " << appInfo.second.getName()
1376  << ". \n\n"
1377  << e.what() << __E__;
1378  __SUP_SS_THROW__;
1379  }
1380 
1381  rxFEList = rxParameters.getValue("FEList");
1382  rxFrontEndError = rxParameters.getValue("frontEndError");
1383 
1384  __SUP_COUT__ << "FE List received: \n" << rxFEList << __E__;
1385 
1386  if(rxFrontEndError != "")
1387  {
1388  __SUP_SS__ << "FE Errors received: \n" << rxFrontEndError << __E__;
1389  __SUP_SS_THROW__;
1390  }
1391 
1392  std::istringstream allInterfaces(rxFEList);
1393  while(std::getline(allInterfaces, oneInterface))
1394  {
1395  __SUP_COUTV__(oneInterface);
1396  xmldoc.addTextElementToData("FE", oneInterface);
1397 
1398  lastColonIndex = oneInterface.rfind(':');
1399  if(lastColonIndex == std::string::npos)
1400  {
1401  __SUP_SS__ << "Last colon could not be found in " << oneInterface
1402  << __E__;
1403  __SUP_SS_THROW__;
1404  }
1405  oneInterface = oneInterface.substr(lastColonIndex);
1406 
1407  __SUP_COUTV__(oneInterface);
1408  } // end FE extract loop
1409 
1410  } // end ask Supervisors for their FE list loop
1411 
1412 } // end getFEList()
1413 
1414 //==============================================================================
1415 void MacroMakerSupervisor::writeData(HttpXmlDocument& /*xmldoc*/,
1416  cgicc::Cgicc& cgi,
1417  const std::string& username)
1418 {
1419  __SUP_COUT__ << "MacroMaker writing..." << __E__;
1420 
1421  std::string Address = CgiDataUtilities::getData(cgi, "Address");
1422  std::string Data = CgiDataUtilities::getData(cgi, "Data");
1423  std::string interfaceIndexArray = CgiDataUtilities::getData(cgi, "interfaceIndex");
1424  std::string supervisorIndexArray = CgiDataUtilities::getData(cgi, "supervisorIndex");
1425  std::string time =
1427  std::string addressFormatStr = CgiDataUtilities::getData(cgi, "addressFormatStr");
1428  std::string dataFormatStr = CgiDataUtilities::getData(cgi, "dataFormatStr");
1429 
1430  std::string interfaces = CgiDataUtilities::postData(cgi, "interfaces");
1431 
1432  __SUP_COUT__ << "Write Address: " << Address << " Data: " << Data << __E__;
1433  __SUP_COUTV__(interfaces);
1434 
1435  std::string command = "w:" + Address + ":" + Data;
1436  std::string format = addressFormatStr + ":" + dataFormatStr;
1437  appendCommandToHistory(command, format, time, interfaces, username);
1438 
1439  SOAPParameters txParameters; // params for xoap to send
1440  txParameters.addParameter("Request", "UniversalWrite");
1441  txParameters.addParameter("Address", Address);
1442  txParameters.addParameter("Data", Data);
1443 
1444  __SUP_COUT__ << "Here comes the array from multiselect box for WRITE, behold: \n"
1445  << supervisorIndexArray << "\n"
1446  << interfaceIndexArray << __E__;
1447 
1450  std::vector<std::string> interfaceIndices;
1451  std::istringstream f(interfaceIndexArray);
1452  std::string s;
1453  while(getline(f, s, ','))
1454  interfaceIndices.push_back(s);
1455  std::vector<int> supervisorIndices;
1456  std::istringstream g(supervisorIndexArray);
1457  std::string t;
1458  while(getline(g, t, ','))
1459  supervisorIndices.push_back(std::stoi(t));
1460 
1461  for(unsigned int i = 0; i < supervisorIndices.size(); i++)
1462  {
1463  unsigned int FESupervisorIndex = supervisorIndices[i];
1464  std::string interfaceIndex = interfaceIndices[i];
1465 
1466  txParameters.addParameter("InterfaceID", interfaceIndex);
1467 
1468  __SUP_COUT__ << "The index of the supervisor instance is: " << FESupervisorIndex
1469  << __E__;
1470  __SUP_COUT__ << "...and the interface ID is: " << interfaceIndex << __E__;
1471 
1472  SupervisorInfoMap::iterator it = allFESupervisorInfo_.find(FESupervisorIndex);
1473  if(it == allFESupervisorInfo_.end())
1474  {
1475  __SUP_SS__ << "Error transmitting request to FE Supervisor '"
1476  << interfaceIndex << ":" << FESupervisorIndex << ".' \n\n"
1477  << "The FE Index doesn't exist. Have you configured the state "
1478  "machine properly?"
1479  << __E__;
1480  __SUP_SS_THROW__;
1481  }
1482 
1483  try
1484  {
1485  xoap::MessageReference replyMessage = SOAPMessenger::sendWithSOAPReply(
1486  it->second.getDescriptor(), "MacroMakerSupervisorRequest", txParameters);
1487 
1488  __SUP_COUT__ << "Response received: "
1489  << SOAPUtilities::translate(replyMessage) << __E__;
1490 
1491  SOAPParameters rxParameters;
1492  rxParameters.addParameter("Error");
1493  SOAPUtilities::receive(replyMessage, rxParameters);
1494 
1495  std::string error = rxParameters.getValue("Error");
1496  __SUP_COUTV__(error);
1497 
1498  if(error != "")
1499  {
1500  // error occurred!
1501  __SUP_SS__ << "Error transmitting request to FE Supervisor '"
1502  << interfaceIndex << ":" << FESupervisorIndex << ".' "
1503  << "Have you configured the state machine properly?\n\n"
1504  << error << __E__;
1505  __SUP_SS_THROW__;
1506  }
1507  }
1508  catch(const xdaq::exception::Exception& e)
1509  {
1510  __SUP_SS__ << "Error transmitting request to FE Supervisor '"
1511  << interfaceIndex << ":" << FESupervisorIndex << ".' "
1512  << "Have you configured the state machine properly?\n\n"
1513  << e.what() << __E__;
1514  __SUP_SS_THROW__;
1515  }
1516 
1517  } // end FE Supervisor loop
1518 } // end writeData()
1519 
1520 //==============================================================================
1521 void MacroMakerSupervisor::readData(HttpXmlDocument& xmldoc,
1522  cgicc::Cgicc& cgi,
1523  const std::string& username)
1524 {
1525  __SUP_COUT__ << "@@@@@@@ MacroMaker wants to read data @@@@@@@@" << __E__;
1526  std::string Address = CgiDataUtilities::getData(cgi, "Address");
1527  std::string interfaceIndexArray = CgiDataUtilities::getData(cgi, "interfaceIndex");
1528  std::string supervisorIndexArray = CgiDataUtilities::getData(cgi, "supervisorIndex");
1529  std::string time =
1531  std::string addressFormatStr = CgiDataUtilities::getData(cgi, "addressFormatStr");
1532  std::string dataFormatStr = CgiDataUtilities::getData(cgi, "dataFormatStr");
1533 
1534  std::string interfaces = CgiDataUtilities::postData(cgi, "interfaces");
1535 
1536  __SUP_COUT__ << "Read Address: " << Address << __E__;
1537  __SUP_COUTV__(interfaces);
1538 
1539  SOAPParameters txParameters; // params for xoap to send
1540  txParameters.addParameter("Request", "UniversalRead");
1541  txParameters.addParameter("Address", Address);
1542 
1543  SOAPParameters rxParameters;
1544  rxParameters.addParameter("dataResult");
1545  rxParameters.addParameter("Error");
1546  __SUP_COUT__ << "Here comes the array from multiselect box for READ, behold: "
1547  << supervisorIndexArray << "," << interfaceIndexArray << __E__;
1548 
1551  std::vector<std::string> interfaceIndices;
1552  std::istringstream f(interfaceIndexArray);
1553  std::string s;
1554  while(getline(f, s, ','))
1555  interfaceIndices.push_back(s);
1556  std::vector<int> supervisorIndices;
1557  std::istringstream g(supervisorIndexArray);
1558  std::string t;
1559  while(getline(g, t, ','))
1560  supervisorIndices.push_back(std::stoi(t));
1561 
1562  for(unsigned int i = 0; i < supervisorIndices.size(); i++)
1563  {
1564  unsigned int FESupervisorIndex = supervisorIndices[i];
1565  std::string interfaceIndex = interfaceIndices[i];
1566 
1567  txParameters.addParameter("InterfaceID", interfaceIndex);
1568 
1569  __SUP_COUT__ << "The index of the supervisor instance is: " << FESupervisorIndex
1570  << __E__;
1571  __SUP_COUT__ << "...and the interface ID is: " << interfaceIndex << __E__;
1572 
1573  SupervisorInfoMap::iterator it = allFESupervisorInfo_.find(FESupervisorIndex);
1574  if(it == allFESupervisorInfo_.end())
1575  {
1576  __SUP_SS__ << "Error transmitting request to FE Supervisor '"
1577  << interfaceIndex << ":" << FESupervisorIndex << ".' \n\n"
1578  << "The FE Index doesn't exist. Have you configured the state "
1579  "machine properly?"
1580  << __E__;
1581  __SUP_SS_THROW__;
1582  }
1583 
1584  try
1585  {
1586  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
1587  it->second.getDescriptor(), "MacroMakerSupervisorRequest", txParameters);
1588 
1589  __SUP_COUT__ << "Response received: " << SOAPUtilities::translate(retMsg)
1590  << __E__;
1591 
1592  // SOAPParameters rxParameters;
1593  // rxParameters.addParameter("Error");
1594  SOAPUtilities::receive(retMsg, rxParameters);
1595 
1596  std::string error = rxParameters.getValue("Error");
1597  __SUP_COUTV__(error);
1598 
1599  if(error != "")
1600  {
1601  // error occurred!
1602  __SUP_SS__ << "Error transmitting request to FE Supervisor '"
1603  << interfaceIndex << ":" << FESupervisorIndex << ".' "
1604  << "Have you configured the state machine properly?\n\n"
1605  << error << __E__;
1606  __SUP_SS_THROW__;
1607  }
1608  }
1609  catch(const xdaq::exception::Exception& e)
1610  {
1611  __SUP_SS__ << "Error transmitting request to FE Supervisor '"
1612  << interfaceIndex << ":" << FESupervisorIndex << ".' "
1613  << "Have you configured the state machine properly?\n\n"
1614  << e.what() << __E__;
1615  __SUP_SS_THROW__;
1616  }
1617 
1618  std::string dataReadResult = rxParameters.getValue("dataResult");
1619  __SUP_COUT__ << "Data reading result received: " << dataReadResult << __E__;
1620  xmldoc.addTextElementToData("readData", dataReadResult);
1621  std::string command = "r:" + Address + ":" + dataReadResult;
1622  std::string format = addressFormatStr + ":" + dataFormatStr;
1623  appendCommandToHistory(command, format, time, interfaces, username);
1624  }
1625 } //end readData()
1626 
1627 //==============================================================================
1628 void MacroMakerSupervisor::createMacro(HttpXmlDocument& /*xmldoc*/,
1629  cgicc::Cgicc& cgi,
1630  const std::string& username)
1631 {
1632  __SUP_COUT__ << "MacroMaker wants to create a macro!!!!!!!!!" << __E__;
1633  std::string Name = CgiDataUtilities::postData(cgi, "Name");
1634  std::string Sequence = CgiDataUtilities::postData(cgi, "Sequence");
1635  std::string Time = CgiDataUtilities::postData(cgi, "Time");
1636  std::string Notes =
1638  std::string isMacroPublic = CgiDataUtilities::getData(cgi, "isPublic");
1639  std::string isMacroLSBF = CgiDataUtilities::getData(cgi, "isLSBF");
1640 
1641  __SUP_COUTV__(Name);
1642  __SUP_COUTV__(Sequence);
1643  __SUP_COUTV__(Notes);
1644  __SUP_COUTV__(Time);
1645  __SUP_COUTV__(isMacroPublic);
1646  __SUP_COUTV__(isMacroLSBF);
1647 
1648  __SUP_COUTV__(MACROS_DB_PATH);
1649 
1650  std::string fileName = Name + ".dat";
1651  std::string fullPath;
1652  if(isMacroPublic == "true")
1653  fullPath = (std::string)MACROS_DB_PATH + "publicMacros/" + fileName;
1654  else
1655  fullPath = (std::string)MACROS_DB_PATH + username + "/" + fileName;
1656 
1657  __SUP_COUTV__(fullPath);
1658 
1659  std::ofstream macrofile(fullPath.c_str());
1660  if(macrofile.is_open())
1661  {
1662  macrofile << "{\n";
1663  macrofile << "\"name\":\"" << Name << "\",\n";
1664  macrofile << "\"sequence\":\"" << Sequence << "\",\n";
1665  macrofile << "\"time\":\"" << Time << "\",\n";
1666  macrofile << "\"notes\":\"" << Notes << "\",\n";
1667  macrofile << "\"LSBF\":\"" << isMacroLSBF << "\"\n";
1668  macrofile << "}@" << __E__;
1669  macrofile.close();
1670  }
1671  else
1672  {
1673  __SUP_SS__ << "Unable to open file" << __E__;
1674  __SUP_SS_THROW__;
1675  }
1676 } // end createMacro()
1677 
1678 //==============================================================================
1686 void MacroMakerSupervisor::loadMacro(const std::string& macroName,
1687  std::string& macroString,
1688  const std::string& username /*=""*/)
1689 {
1690  __SUP_COUTV__(macroName);
1691 
1692  // first check public folder, then user
1693  std::string fullPath, line;
1694  macroString = "";
1695  for(unsigned int i = 0; i < 2; ++i)
1696  {
1697  if(i == 1)
1698  fullPath = (std::string)MACROS_DB_PATH + username + "/";
1699  else
1700  fullPath = (std::string)MACROS_DB_PATH + "publicMacros/";
1701 
1702  fullPath += macroName;
1703  if(macroName.find(".dat") != macroName.size() - 4)
1704  fullPath += ".dat";
1705  __SUP_COUTV__(fullPath);
1706 
1707  std::ifstream read(fullPath.c_str()); // reading a file
1708  if(read.is_open())
1709  {
1710  while(!read.eof())
1711  {
1712  getline(read, line);
1713  macroString += line;
1714  }
1715 
1716  read.close();
1717  }
1718  else // file does not exist
1719  {
1720  __SUP_COUT__ << "Unable to open file: " << fullPath << __E__;
1721  continue;
1722  }
1723 
1724  if(macroString != "")
1725  break; // macro has been found!
1726  } // end load from path loop
1727 
1728  if(macroString == "")
1729  {
1730  __SUP_SS__ << "Unable to locate file for macro '" << macroName
1731  << "'... does it exist?" << __E__;
1732  if(username != "")
1733  ss << " Attempted username was '" << username << ".'" << __E__;
1734  __SUP_SS_THROW__;
1735  }
1736 
1737  __SUP_COUTV__(macroString);
1738 } // end loadMacro()
1739 
1740 //==============================================================================
1741 void MacroMakerSupervisor::loadMacroNames(
1742  const std::string& username,
1743  std::pair<std::vector<std::string> /*public macros*/,
1744  std::vector<std::string> /*private macros*/>& returnMacroNames)
1745 {
1746  DIR* dir;
1747  struct dirent* ent;
1748  std::string fullPath = (std::string)MACROS_DB_PATH + username + "/";
1749  if((dir = opendir(fullPath.c_str())) != NULL)
1750  {
1751  /* print all the files and directories within directory */
1752  while((ent = readdir(dir)) != NULL)
1753  {
1754  /* File name validation check */
1755  if((unsigned)strlen(ent->d_name) > 4)
1756  {
1757  std::string line;
1758  std::ifstream read(
1759  ((fullPath + (std::string)ent->d_name)).c_str()); // reading a file
1760  if(read.is_open())
1761  {
1762  read.close();
1763  // private macro found
1764  returnMacroNames.second.push_back(ent->d_name);
1765  }
1766  else
1767  __SUP_COUT__ << "Unable to open file" << __E__;
1768  }
1769  }
1770  closedir(dir);
1771  }
1772  else
1773  {
1774  __SUP_COUT__ << "Looping through privateMacros folder failed! Wrong directory"
1775  << __E__;
1776  }
1777  fullPath = (std::string)MACROS_DB_PATH + "publicMacros/";
1778  if((dir = opendir(fullPath.c_str())) != NULL)
1779  {
1780  /* print all the files and directories within directory */
1781  while((ent = readdir(dir)) != NULL)
1782  {
1783  /* File name validation check */
1784  if((unsigned)strlen(ent->d_name) > 4)
1785  {
1786  std::string line;
1787  std::ifstream read(
1788  ((fullPath + (std::string)ent->d_name)).c_str()); // reading a file
1789  if(read.is_open())
1790  {
1791  // public macro found
1792  returnMacroNames.first.push_back(ent->d_name);
1793  read.close();
1794  }
1795  else
1796  __SUP_COUT__ << "Unable to open file" << __E__;
1797  }
1798  }
1799  closedir(dir);
1800  }
1801  else
1802  {
1803  __SUP_COUT__ << fullPath << __E__;
1804  __SUP_COUT__ << "Looping through MacroData folder failed! Wrong directory"
1805  << __E__;
1806  }
1807 
1808 } // end loadMacroNames
1809 
1810 //==============================================================================
1811 void MacroMakerSupervisor::loadMacros(HttpXmlDocument& xmldoc,
1812  const std::string& username)
1813 {
1814  DIR* dir;
1815  struct dirent* ent;
1816  std::string returnStr = "";
1817  std::string fullPath = (std::string)MACROS_DB_PATH + username + "/";
1818  if((dir = opendir(fullPath.c_str())) != NULL)
1819  {
1820  /* print all the files and directories within directory */
1821  while((ent = readdir(dir)) != NULL)
1822  {
1823  /* File name validation check */
1824  if((unsigned)strlen(ent->d_name) > 4)
1825  {
1826  std::string line;
1827  std::ifstream read(
1828  ((fullPath + (std::string)ent->d_name)).c_str()); // reading a file
1829  if(read.is_open())
1830  {
1831  std::stringstream buffer;
1832  while(!read.eof())
1833  {
1834  getline(read, line);
1835  buffer << line;
1836  //__SUP_COUT__ << line << __E__;
1837  }
1838  returnStr += buffer.str();
1839 
1840  read.close();
1841  }
1842  else
1843  __SUP_COUT__ << "Unable to open file" << __E__;
1844  }
1845  }
1846  std::string returnMacroStr = returnStr.substr(0, returnStr.size() - 1);
1847 
1848  __SUP_COUT__ << "Loading existing macros! " << returnMacroStr << __E__;
1849 
1850  closedir(dir);
1851  xmldoc.addTextElementToData("returnMacroStr", returnMacroStr);
1852  }
1853  else
1854  {
1855  __SUP_COUT__ << "Looping through privateMacros folder failed! Wrong directory"
1856  << __E__;
1857  }
1858  fullPath = (std::string)MACROS_DB_PATH + "publicMacros/";
1859  returnStr = "";
1860  if((dir = opendir(fullPath.c_str())) != NULL)
1861  {
1862  /* print all the files and directories within directory */
1863  while((ent = readdir(dir)) != NULL)
1864  {
1865  /* File name validation check */
1866  if((unsigned)strlen(ent->d_name) > 4)
1867  {
1868  std::string line;
1869  std::ifstream read(
1870  ((fullPath + (std::string)ent->d_name)).c_str()); // reading a file
1871  if(read.is_open())
1872  {
1873  std::stringstream buffer;
1874  while(!read.eof())
1875  {
1876  getline(read, line);
1877  buffer << line;
1878  //__SUP_COUT__ << line << __E__;
1879  }
1880  returnStr += buffer.str();
1881  read.close();
1882  }
1883  else
1884  __SUP_COUT__ << "Unable to open file" << __E__;
1885  }
1886  }
1887  std::string returnPublicStr = returnStr.substr(0, returnStr.size() - 1);
1888  __SUP_COUT__ << "Loading existing public macros: " << returnPublicStr << __E__;
1889  closedir(dir);
1890  xmldoc.addTextElementToData("returnPublicStr", returnPublicStr);
1891  }
1892  else
1893  {
1894  __SUP_COUT__ << fullPath << __E__;
1895  __SUP_COUT__ << "Looping through MacroData folder failed! Wrong directory"
1896  << __E__;
1897  }
1898 } // end loadMacros()
1899 
1900 //==============================================================================
1901 void MacroMakerSupervisor::appendCommandToHistory(std::string Command,
1902  std::string Format,
1903  std::string Time,
1904  std::string Interfaces,
1905  const std::string& username)
1906 {
1907  std::string fileName = "history.hist";
1908  std::string fullPath = (std::string)MACROS_HIST_PATH + username + "/" + fileName;
1909  __SUP_COUT__ << fullPath << __E__;
1910  std::ofstream histfile(fullPath.c_str(), std::ios::app);
1911  if(histfile.is_open())
1912  {
1913  histfile << "{\n";
1914  histfile << "\"Command\":\"" << Command << "\",\n";
1915  histfile << "\"Format\":\"" << Format << "\",\n";
1916  histfile << "\"Time\":\"" << Time << "\",\n";
1917  histfile << "\"Interfaces\":\"" << Interfaces << "\"\n";
1918  histfile << "}#" << __E__;
1919  histfile.close();
1920  }
1921  else
1922  {
1923  __SUP_SS__ << "Unable to open history.hist at " << fullPath << __E__;
1924  __SUP_SS_THROW__;
1925  }
1926 } //end appendCommandToHistory()
1927 
1928 //==============================================================================
1929 void MacroMakerSupervisor::appendCommandToHistory(std::string feClass,
1930  std::string feUID,
1931  std::string macroType,
1932  std::string macroName,
1933  std::string inputArgs,
1934  std::string outputArgs,
1935  bool saveOutputs,
1936  const std::string& username,
1937  time_t launchTime,
1938  time_t completeTime)
1939 {
1940  if(launchTime == 0)
1941  launchTime = time(0);
1942  if(completeTime == 0)
1943  completeTime = launchTime;
1944 
1945  //prevent repeats to FE command history (otherwise live view can overwhelm history)
1946  auto feHistoryIt = lastFeCommandToHistory_.find(username);
1947  if(feHistoryIt != lastFeCommandToHistory_.end() && feHistoryIt->second.size() == 7 &&
1948  feHistoryIt->second[0] == feClass && feHistoryIt->second[1] == feUID &&
1949  feHistoryIt->second[2] == macroType && feHistoryIt->second[3] == macroName &&
1950  feHistoryIt->second[4] == inputArgs && feHistoryIt->second[5] == outputArgs &&
1951  feHistoryIt->second[6] == (saveOutputs ? "1" : "0"))
1952  {
1953  __SUP_COUTT__ << "Not saving repeat command to history from user " << username
1954  << __E__;
1955  return;
1956  }
1957 
1958  std::string fileName = "FEhistory.hist";
1959  std::string fullPath = (std::string)MACROS_HIST_PATH + username + "/" + fileName;
1960  __SUP_COUT__ << fullPath << __E__;
1961  std::ofstream histfile(fullPath.c_str(), std::ios::app);
1962  if(histfile.is_open())
1963  {
1964  histfile << "{\n";
1965  histfile << "\"feClass\":\"" << feClass << "\",\n";
1966  histfile << "\"feUID\":\"" << feUID << "\",\n";
1967  histfile << "\"macroType\":\"" << macroType << "\",\n";
1968  histfile << "\"macroName\":\"" << macroName << "\",\n";
1969  histfile << "\"inputArgs\":\"" << inputArgs << "\",\n";
1970  histfile << "\"outputArgs\":\"" << outputArgs << "\",\n";
1971  histfile << "\"launchTime\":\"" << launchTime << "\",\n";
1972  histfile << "\"completeTime\":\"" << completeTime << "\",\n";
1973  if(saveOutputs)
1974  histfile << "\"saveOutputs\":\"" << 1 << "\"\n";
1975  else
1976  histfile << "\"saveOutputs\":\"" << 0 << "\"\n";
1977  histfile << "}#" << __E__;
1978  histfile.close();
1979 
1980  lastFeCommandToHistory_[username].clear(); //create instance and/or clear
1981  feHistoryIt = lastFeCommandToHistory_.find(username);
1982  feHistoryIt->second.push_back(feClass);
1983  feHistoryIt->second.push_back(feUID);
1984  feHistoryIt->second.push_back(macroType);
1985  feHistoryIt->second.push_back(macroName);
1986  feHistoryIt->second.push_back(inputArgs);
1987  feHistoryIt->second.push_back(outputArgs);
1988  feHistoryIt->second.push_back((saveOutputs ? "1" : "0"));
1989  }
1990  else
1991  {
1992  __SUP_SS__ << "Unable to open FEhistory.hist at " << fullPath << __E__;
1993  __SUP_SS_THROW__;
1994  }
1995 
1996 } //end appendCommandToHistory()
1997 
1998 //==============================================================================
1999 void MacroMakerSupervisor::loadFEMacroSequences(HttpXmlDocument& xmldoc,
2000  const std::string& username)
2001 {
2002  __SUP_COUT__ << "loadFEMacroSequences for " << username << __E__;
2003  DIR* dir;
2004  struct dirent* ent;
2005  std::string fullPath = (std::string)MACROS_SEQUENCE_PATH + username + "/";
2006  std::string sequences = "";
2007  __SUP_COUTV__(fullPath);
2008  if((dir = opendir(fullPath.c_str())) != NULL)
2009  {
2010  /* print all the files and directories within directory */
2011  while((ent = readdir(dir)) != NULL)
2012  {
2013  std::string line;
2014  std::ifstream read(
2015  ((fullPath + (std::string)ent->d_name)).c_str()); // reading a file
2016  if(read.is_open())
2017  {
2018  read.close();
2019  sequences += ent->d_name + std::string(";");
2020  }
2021  else
2022  __SUP_COUT__ << "Unable to open file" << __E__;
2023  }
2024  closedir(dir);
2025  }
2026  else
2027  {
2028  __SUP_COUT__ << "Looping through MacroSequence/" + username +
2029  " folder failed! Invalid directory."
2030  << __E__;
2031  }
2032 
2033  if(username == WebUsers::DEFAULT_ADMIN_USERNAME)
2034  {
2035  // already have admin list, return the list of sequences
2036  xmldoc.addTextElementToData("FEsequences", sequences);
2037  return;
2038  }
2039 
2040  //always add admin sequences (as "public")
2041  fullPath = (std::string)MACROS_SEQUENCE_PATH + WebUsers::DEFAULT_ADMIN_USERNAME + "/";
2042 
2043  if((dir = opendir(fullPath.c_str())) != NULL)
2044  {
2045  /* print all the files and directories within directory */
2046  while((ent = readdir(dir)) != NULL)
2047  {
2048  std::string line;
2049  std::ifstream read(
2050  ((fullPath + (std::string)ent->d_name)).c_str()); // reading a file
2051  if(read.is_open())
2052  {
2053  read.close();
2054  sequences += std::string("public/") + ent->d_name + std::string(";");
2055  }
2056  else
2057  __SUP_COUT__ << "Unable to open file" << __E__;
2058  }
2059  closedir(dir);
2060  }
2061  else
2062  {
2063  __SUP_COUT__ << "Looping through MacroSequence/" +
2064  WebUsers::DEFAULT_ADMIN_USERNAME +
2065  " folder failed! Invalid directory."
2066  << __E__;
2067  }
2068 
2069  // return the list of sequences
2070  xmldoc.addTextElementToData("FEsequences", sequences);
2071 } //end loadFEMacroSequences()
2072 
2073 //==============================================================================
2074 void MacroMakerSupervisor::saveFEMacroSequence(cgicc::Cgicc& cgi,
2075  const std::string& username)
2076 {
2077  // get data from the http request
2078  std::string name =
2080  std::string FEsequence =
2082  bool overwrite = CgiDataUtilities::getDataAsInt(cgi, "overwrite");
2083 
2084  __SUP_COUTV__(overwrite);
2085  __SUP_COUTV__(name);
2086  __SUP_COUTV__(FEsequence);
2087 
2088  //reject illegal characters (only alphanumeric, spaces, dash, underscores)
2089  std::string fixedName = "";
2090  for(size_t i = 0; i < name.size(); ++i)
2091  if(!(name[i] == ' ' || name[i] == '-' || name[i] == '_' ||
2092  (name[i] >= '0' && name[i] <= '9') || (name[i] >= 'A' && name[i] <= 'Z') ||
2093  (name[i] >= 'a' && name[i] <= 'z')))
2094  {
2095  __SUP_SS__
2096  << "Illegal character in Sequence name (position " << i
2097  << ") - only alphanumeric, spaces, dashes, and underscores allowed!"
2098  << __E__;
2099  __SUP_SS_THROW__;
2100  }
2101  else
2102  fixedName += name[i];
2103  __SUP_COUTV__(fixedName);
2104 
2105  std::string fullPath =
2106  (std::string)MACROS_SEQUENCE_PATH + username + "/" + fixedName + ".dat";
2107  __SUP_COUTV__(fullPath);
2108 
2109  //do not allow overwrite
2110  if(!overwrite && std::filesystem::exists(fullPath))
2111  {
2112  __SUP_SS__ << "Please choose another Sequence name! A sequence with the same "
2113  "resulting filename already exists at "
2114  << fullPath << __E__;
2115  __SUP_SS_THROW__;
2116  }
2117 
2118  std::ofstream seqfile(fullPath.c_str());
2119  if(seqfile.is_open())
2120  {
2121  seqfile << FEsequence << __E__;
2122  seqfile.close();
2123  }
2124  else
2125  {
2126  __SUP_SS__ << "Unable to open file to save FE Macro Sequence at " << fullPath
2127  << __E__;
2128  __SUP_SS_THROW__;
2129  }
2130 } //end saveFEMacroSequence()
2131 
2132 //==============================================================================
2133 void MacroMakerSupervisor::getFEMacroSequence(HttpXmlDocument& xmldoc,
2134  cgicc::Cgicc& cgi,
2135  const std::string& username)
2136 {
2137  std::string name =
2139  __SUP_COUTV__(name);
2140 
2141  bool isPublic = (name.find("public/") == 0 ? true : false);
2142  __SUP_COUTV__(isPublic);
2143 
2144  //reject illegal characters (only alphanumeric, spaces, dash, underscores)
2145  std::string fixedName = "";
2146  for(size_t i = (isPublic ? std::string("public/").size() : 0); i < name.size(); ++i)
2147  if(!(name[i] == ' ' || name[i] == '-' || name[i] == '_' ||
2148  (name[i] >= '0' && name[i] <= '9') || (name[i] >= 'A' && name[i] <= 'Z') ||
2149  (name[i] >= 'a' && name[i] <= 'z')))
2150  {
2151  __COUT__ << "Illegal character in Sequence name (position " << i
2152  << ") - only alphanumeric, spaces, dashes, and underscores allowed!"
2153  << __E__;
2154  }
2155  else
2156  fixedName += name[i];
2157  __SUP_COUTV__(fixedName);
2158 
2159  // access to the file
2160  std::string fullPath =
2161  (std::string)MACROS_SEQUENCE_PATH + username + "/" + fixedName + ".dat";
2162  __SUP_COUT__ << fullPath << __E__;
2163 
2164  std::ifstream read(fullPath.c_str()); // reading the file
2165  char* response;
2166  unsigned long long fileSize;
2167 
2168  if(!isPublic && read.is_open())
2169  {
2170  read.seekg(0, std::ios::end);
2171  fileSize = read.tellg();
2172  response = new char[fileSize + 1];
2173  response[fileSize] = '\0';
2174  read.seekg(0, std::ios::beg);
2175 
2176  // read data as a block:
2177  read.read(response, fileSize);
2178  read.close();
2179 
2180  xmldoc.addTextElementToData("FEsequence", &response[0]);
2181 
2182  delete[] response;
2183  }
2184  else
2185  {
2186  if(!isPublic)
2187  __SUP_COUT__ << "Unable to open " << fullPath << "! Trying public area..."
2188  << __E__;
2189 
2190  //attempt to load from admin "public" area
2191  std::string publicFullPath = (std::string)MACROS_SEQUENCE_PATH +
2192  WebUsers::DEFAULT_ADMIN_USERNAME + "/" + fixedName +
2193  ".dat";
2194  __SUP_COUT__ << publicFullPath << __E__;
2195 
2196  std::ifstream read(publicFullPath.c_str()); // reading the file
2197  char* response;
2198  unsigned long long fileSize;
2199 
2200  if(read.is_open())
2201  {
2202  read.seekg(0, std::ios::end);
2203  fileSize = read.tellg();
2204  response = new char[fileSize + 1];
2205  response[fileSize] = '\0';
2206  read.seekg(0, std::ios::beg);
2207 
2208  // read data as a block:
2209  read.read(response, fileSize);
2210  read.close();
2211 
2212  xmldoc.addTextElementToData("FEsequence", &response[0]);
2213 
2214  delete[] response;
2215  }
2216  else
2217  {
2218  __SUP_SS__ << "Unable to open FE Macro Sequence at " << fullPath << " or "
2219  << publicFullPath << __E__;
2220  __SUP_SS_THROW__;
2221  }
2222  }
2223 } //end getFEMacroSequence()
2224 
2225 //==============================================================================
2226 void MacroMakerSupervisor::deleteFEMacroSequence(cgicc::Cgicc& cgi,
2227  const std::string& username)
2228 {
2229  std::string name =
2231  __SUP_COUTV__(name);
2232 
2233  bool isPublic = (name.find("public/") == 0 ? true : false);
2234  __SUP_COUTV__(isPublic);
2235 
2236  //reject illegal characters (only alphanumeric, spaces, dash, underscores)
2237  std::string fixedName = "";
2238  for(size_t i = (isPublic ? std::string("public/").size() : 0); i < name.size(); ++i)
2239  if(!(name[i] == ' ' || name[i] == '-' || name[i] == '_' ||
2240  (name[i] >= '0' && name[i] <= '9') || (name[i] >= 'A' && name[i] <= 'Z') ||
2241  (name[i] >= 'a' && name[i] <= 'z')))
2242  {
2243  __COUT__ << "Illegal character in Sequence name (position " << i
2244  << ") - only alphanumeric, spaces, dashes, and underscores allowed!"
2245  << __E__;
2246  }
2247  else
2248  fixedName += name[i];
2249  __SUP_COUTV__(fixedName);
2250 
2251  // access to the file
2252  std::string fullPath =
2253  (std::string)MACROS_SEQUENCE_PATH + username + "/" + fixedName + ".dat";
2254  if(isPublic)
2255  fullPath = (std::string)MACROS_SEQUENCE_PATH + WebUsers::DEFAULT_ADMIN_USERNAME +
2256  "/" + fixedName + ".dat";
2257  __SUP_COUT__ << fullPath << __E__;
2258 
2259  //do not allow overwrite
2260  if(!std::filesystem::exists(fullPath))
2261  {
2262  __SUP_SS__
2263  << "The specified Sequence name does not exist! Looking for sequence file at "
2264  << fullPath << __E__;
2265  __SUP_SS_THROW__;
2266  }
2267 
2268  std::remove(fullPath.c_str());
2269  __SUP_COUT__ << "Successfully deleted " << fullPath << __E__;
2270 } //end deleteFEMacroSequence()
2271 
2272 //==============================================================================
2273 void MacroMakerSupervisor::makeSequencePublic(cgicc::Cgicc& cgi,
2274  const std::string& username)
2275 {
2276  std::string name =
2278  __SUP_COUTV__(name);
2279 
2280  bool isPublic = (name.find("public/") == 0 ? true : false);
2281  __SUP_COUTV__(isPublic);
2282  if(isPublic)
2283  {
2284  __SUP_SS__ << "The specified Sequence name is already designated as public."
2285  << __E__;
2286  __SUP_SS_THROW__;
2287  }
2288 
2289  //reject illegal characters (only alphanumeric, spaces, dash, underscores)
2290  std::string fixedName = "";
2291  for(size_t i = 0; i < name.size(); ++i)
2292  if(!(name[i] == ' ' || name[i] == '-' || name[i] == '_' ||
2293  (name[i] >= '0' && name[i] <= '9') || (name[i] >= 'A' && name[i] <= 'Z') ||
2294  (name[i] >= 'a' && name[i] <= 'z')))
2295  {
2296  __COUT__ << "Illegal character in Sequence name (position " << i
2297  << ") - only alphanumeric, spaces, dashes, and underscores allowed!"
2298  << __E__;
2299  }
2300  else
2301  fixedName += name[i];
2302  __SUP_COUTV__(fixedName);
2303 
2304  // access to the file
2305  std::string source =
2306  (std::string)MACROS_SEQUENCE_PATH + username + "/" + fixedName + ".dat";
2307  __SUP_COUT__ << source << __E__;
2308  std::string destination = (std::string)MACROS_SEQUENCE_PATH +
2309  WebUsers::DEFAULT_ADMIN_USERNAME + "/" + fixedName + ".dat";
2310  __SUP_COUT__ << destination << __E__;
2311 
2312  if(std::filesystem::exists(destination))
2313  {
2314  __SUP_SS__ << "The sequence name '" << fixedName
2315  << "' already exists in the admin/public location: " << destination
2316  << __E__;
2317  __SUP_SS_THROW__;
2318  }
2319 
2320  //copy if file does not already exist
2321  std::filesystem::copy_file(
2322  source, destination, std::filesystem::copy_options::skip_existing);
2323  __SUP_COUT__ << "Successfully made " << fixedName
2324  << " public at path: " << destination << __E__;
2325 } //end deleteFEMacroSequence()
2326 
2327 //==============================================================================
2328 void MacroMakerSupervisor::loadHistory(HttpXmlDocument& xmldoc,
2329  const std::string& username)
2330 {
2331  std::string fileName = MACROS_HIST_PATH + username + "/" + "history.hist";
2332 
2333  std::ifstream read(fileName.c_str()); // reading a file
2334  __SUP_COUT__ << fileName << __E__;
2335 
2336  if(read.is_open())
2337  {
2338  std::string line;
2339  char* returnStr;
2340  unsigned long long fileSz, i = 0, MAX_HISTORY_SIZE = 100000;
2341 
2342  // get length of file to reserve the string size
2343  // and to cap history size
2344  read.seekg(0, std::ios::end);
2345  fileSz = read.tellg();
2346  returnStr = new char[fileSz + 1];
2347  returnStr[fileSz] = '\0';
2348  read.seekg(0, std::ios::beg);
2349 
2350  // read data as a block:
2351  read.read(returnStr, fileSz);
2352  read.close();
2353 
2354  // find i such that new string size is less than
2355  if(fileSz > MAX_HISTORY_SIZE)
2356  {
2357  i = fileSz - MAX_HISTORY_SIZE;
2358  for(; i < fileSz; ++i)
2359  if(returnStr[i] == '#')
2360  {
2361  i += 2;
2362  break; // skip new line character also to get to next record
2363  }
2364  if(i > fileSz)
2365  i = fileSz;
2366 
2367  // write back to file truncated history
2368  FILE* fp = fopen(fileName.c_str(), "w");
2369  if(!fp)
2370  {
2371  delete[] returnStr;
2372  __SS__ << "Big problem with macromaker history file: " << fileName
2373  << __E__;
2374  __SS_THROW__;
2375  }
2376  fwrite(&returnStr[i], fileSz - i, 1, fp);
2377  fclose(fp);
2378  }
2379 
2380  __SUP_COUT__ << "Loading user history! " << __E__;
2381 
2382  if(fileSz > 1)
2383  returnStr[fileSz - 2] = '\0'; // remove final newline and last #
2384 
2385  xmldoc.addTextElementToData("returnHistStr", &returnStr[i]);
2386 
2387  delete[] returnStr;
2388  }
2389  else
2390  __SUP_COUT__ << "Unable to open history.hist" << __E__;
2391 
2392 } //end loadHistory()
2393 
2394 //==============================================================================
2395 void MacroMakerSupervisor::loadFEHistory(HttpXmlDocument& xmldoc,
2396  const std::string& username)
2397 {
2398  std::string fileName = MACROS_HIST_PATH + username + "/" + "FEhistory.hist";
2399 
2400  std::ifstream read(fileName.c_str());
2401  __SUP_COUT__ << fileName << __E__;
2402 
2403  if(!read.is_open() && username != WebUsers::DEFAULT_ADMIN_USERNAME)
2404  {
2405  __SUP_COUT__ << "Unable to open FE history.hist.. Defaulting to admin's FE "
2406  "history as starting point."
2407  << __E__;
2408 
2409  fileName =
2410  MACROS_HIST_PATH + WebUsers::DEFAULT_ADMIN_USERNAME + "/" + "FEhistory.hist";
2411  read.open(fileName.c_str());
2412  }
2413 
2414  if(read.is_open())
2415  {
2416  std::string line;
2417  char* returnStr;
2418  unsigned long long fileSize;
2419  unsigned long long i = 0;
2420  unsigned long long MAX_HISTORY_SIZE = 100000;
2421 
2422  // get the length of the file
2423  read.seekg(0, std::ios::end);
2424  fileSize = read.tellg();
2425  returnStr = new char[fileSize + 1];
2426  returnStr[fileSize] = '\0';
2427  read.seekg(0, std::ios::beg);
2428 
2429  // read data as block
2430  read.read(returnStr, fileSize);
2431  read.close();
2432 
2433  // find i such that new string size is less than
2434  if(fileSize > MAX_HISTORY_SIZE)
2435  {
2436  i = fileSize - MAX_HISTORY_SIZE;
2437  for(; i < fileSize; ++i)
2438  {
2439  if(returnStr[i] == '#') // skip the new line char
2440  {
2441  i += 2;
2442  break;
2443  }
2444  }
2445  if(i > fileSize)
2446  i = fileSize;
2447 
2448  // write back to file truncated history
2449  FILE* fp = fopen(fileName.c_str(), "w");
2450  if(!fp)
2451  {
2452  delete[] returnStr;
2453  __SS__ << "Big problem with FE history file: " << fileName << __E__;
2454  __SS_THROW__;
2455  }
2456  fwrite(&returnStr[i], fileSize - i, 1, fp);
2457  fclose(fp);
2458  }
2459 
2460  __SUP_COUT__ << "Loading user history! " << __E__;
2461 
2462  if(fileSize > 1)
2463  returnStr[fileSize - 2] = '\0'; // remove final newline and last #
2464 
2465  xmldoc.addTextElementToData("returnHistStr", &returnStr[i]);
2466 
2467  delete[] returnStr;
2468  }
2469  else
2470  __SUP_COUT__ << "Unable to open FE history.hist" << __E__;
2471 
2472 } //end loadFEHistory()
2473 
2474 //==============================================================================
2475 void MacroMakerSupervisor::deleteMacro(HttpXmlDocument& xmldoc,
2476  cgicc::Cgicc& cgi,
2477  const std::string& username)
2478 {
2479  std::string MacroName = CgiDataUtilities::getData(cgi, "MacroName");
2480  std::string isMacroPublic = CgiDataUtilities::getData(cgi, "isPublic");
2481 
2482  std::string fileName = MacroName + ".dat";
2483  std::string fullPath;
2484  if(isMacroPublic == "true")
2485  fullPath = (std::string)MACROS_DB_PATH + "publicMacros/" + fileName;
2486  else
2487  fullPath = (std::string)MACROS_DB_PATH + username + "/" + fileName;
2488 
2489  __SUP_COUT__ << fullPath << __E__;
2490 
2491  std::remove(fullPath.c_str());
2492  __SUP_COUT__ << "Successfully deleted " << MacroName;
2493  xmldoc.addTextElementToData("deletedMacroName", MacroName);
2494 } //end deleteMacro()
2495 
2496 //==============================================================================
2497 void MacroMakerSupervisor::editMacro(HttpXmlDocument& xmldoc,
2498  cgicc::Cgicc& cgi,
2499  const std::string& username)
2500 {
2501  std::string oldMacroName = CgiDataUtilities::postData(cgi, "oldMacroName");
2502  std::string newMacroName = CgiDataUtilities::postData(cgi, "newMacroName");
2503  std::string FESequence = CgiDataUtilities::postData(cgi, "FEsequence");
2504  std::string Time = CgiDataUtilities::postData(cgi, "Time");
2505  std::string Notes =
2507 
2508  std::string isMacroPublic = CgiDataUtilities::getData(cgi, "isPublic");
2509  std::string isMacroLSBF = CgiDataUtilities::getData(cgi, "isLSBF");
2510 
2511  __SUP_COUTV__(oldMacroName);
2512  __SUP_COUTV__(newMacroName);
2513  __SUP_COUTV__(FESequence);
2514  __SUP_COUTV__(Notes);
2515  __SUP_COUTV__(Time);
2516  __SUP_COUTV__(isMacroPublic);
2517  __SUP_COUTV__(isMacroLSBF);
2518 
2519  __SUP_COUTV__(MACROS_DB_PATH);
2520 
2521  std::string fileName = oldMacroName + ".dat";
2522  std::string fullPath;
2523  if(isMacroPublic == "true")
2524  fullPath = (std::string)MACROS_DB_PATH + "publicMacros/" + fileName;
2525  else
2526  fullPath = (std::string)MACROS_DB_PATH + username + "/" + fileName;
2527 
2528  __SUP_COUTV__(fullPath);
2529 
2530  std::ofstream macrofile(fullPath.c_str());
2531  if(macrofile.is_open())
2532  {
2533  macrofile << "{\n";
2534  macrofile << "\"name\":\"" << newMacroName << "\",\n";
2535  macrofile << "\"FEsequence\":\"" << FESequence << "\",\n";
2536  macrofile << "\"time\":\"" << Time << "\",\n";
2537  macrofile << "\"notes\":\"" << Notes << "\",\n";
2538  macrofile << "\"LSBF\":\"" << isMacroLSBF << "\"\n";
2539  macrofile << "}@" << __E__;
2540  macrofile.close();
2541  }
2542  else
2543  __SUP_COUT__ << "Unable to open file" << __E__;
2544 
2545  if(oldMacroName != newMacroName) // renaming macro
2546  {
2547  int result;
2548  result =
2549  rename((MACROS_DB_PATH + username + "/" + oldMacroName + ".dat").c_str(),
2550  (MACROS_DB_PATH + username + "/" + newMacroName + ".dat").c_str());
2551  if(result == 0)
2552  xmldoc.addTextElementToData("newMacroName", newMacroName);
2553  else
2554  xmldoc.addTextElementToData("newMacroName", "ERROR");
2555  }
2556 } //end editMacro()
2557 
2558 //==============================================================================
2559 void MacroMakerSupervisor::clearHistory(const std::string& username)
2560 {
2561  std::string fileName = "history.hist";
2562  std::string fullPath = (std::string)MACROS_HIST_PATH + username + "/" + fileName;
2563 
2564  std::remove(fullPath.c_str());
2565  __SUP_COUT__ << "Successfully deleted " << fullPath;
2566 } //end clearHistory()
2567 
2568 //==============================================================================
2569 void MacroMakerSupervisor::clearFEHistory(const std::string& username)
2570 {
2571  std::string fileName = "FEhistory.hist";
2572  std::string fullPath = (std::string)MACROS_HIST_PATH + username + "/" + fileName;
2573 
2574  std::remove(fullPath.c_str());
2575  __SUP_COUT__ << "Successfully deleted " << fullPath;
2576 } //end clearFEHistory()
2577 
2578 //==============================================================================
2579 void MacroMakerSupervisor::exportFEMacro(HttpXmlDocument& xmldoc,
2580  cgicc::Cgicc& cgi,
2581  const std::string& username)
2582 {
2583  std::string macroName = CgiDataUtilities::getData(cgi, "MacroName");
2584  std::string pluginName = CgiDataUtilities::getData(cgi, "PluginName");
2585  std::string macroSequence = CgiDataUtilities::postData(cgi, "MacroSequence");
2586  std::string macroNotes =
2588 
2589  __SUP_COUTV__(pluginName);
2590  __SUP_COUTV__(macroName);
2591  __SUP_COUTV__(macroSequence);
2592 
2593  // replace all special characters with white space
2594  for(unsigned int i = 0; i < macroNotes.length(); ++i)
2595  if(macroNotes[i] == '\r' || macroNotes[i] == '\n')
2596  macroNotes[i] = ' ';
2597  __SUP_COUTV__(macroNotes);
2598 
2599  std::stringstream ss(macroSequence);
2600  std::string command;
2601  std::vector<std::string> commands;
2602 
2603  while(getline(ss, command, ','))
2604  commands.push_back(command);
2605 
2606  __SUP_COUTV__(StringMacros::vectorToString(commands));
2607 
2608  std::map<std::string /*special type*/, std::set<std::string> /*special file paths*/>
2609  specialsCodeMap = CodeEditor::getSpecialsMap();
2610 
2611  //__SUP_COUTV__(StringMacros::mapToString(specialsCodeMap));
2612  auto specialsCodeMapIt = specialsCodeMap.find(CodeEditor::SPECIAL_TYPE_FEInterface);
2613  if(specialsCodeMapIt == specialsCodeMap.end())
2614  {
2615  __SS__
2616  << "Could not find any FE Interface plugins in source code. Does MacroMaker "
2617  << "have access to the source code? Check that the Supervisor context places "
2618  "MacroMaker in a "
2619  << "location with access to the source code." << __E__;
2620  __SS_THROW__;
2621  }
2622 
2623  // find first .h and .cc with the plugin name
2624  std::string headerFile = pluginName + ".h";
2625  std::string sourceFile = pluginName + "_interface.cc";
2626  bool foundHeaderFile = false;
2627  bool foundSourceFile = false;
2628  for(const auto& filePath : specialsCodeMapIt->second)
2629  {
2630  if(!foundHeaderFile && filePath.find(headerFile) != std::string::npos)
2631  {
2632  foundHeaderFile = true;
2633  headerFile = filePath;
2634  __SUP_COUT__ << "found headerFile=" << filePath << __E__;
2635  }
2636  if(!foundSourceFile && filePath.find(sourceFile) != std::string::npos)
2637  {
2638  foundSourceFile = true;
2639  sourceFile = filePath;
2640  __SUP_COUT__ << "found sourceFile=" << filePath << __E__;
2641  }
2642 
2643  if(foundSourceFile && foundHeaderFile)
2644  break;
2645  } // end file search loop
2646 
2647  if(!foundHeaderFile)
2648  {
2649  __SS__ << "Could not find the header file for the FE Interface plugins at '"
2650  << headerFile << ".' Does MacroMaker "
2651  << "have access to the source code? Check that the Supervisor context "
2652  "places MacroMaker in a "
2653  << "location with access to the source code." << __E__;
2654  __SS_THROW__;
2655  }
2656  if(!foundSourceFile)
2657  {
2658  __SS__ << "Could not find the source file for the FE Interface plugins at '"
2659  << sourceFile << ".' Does MacroMaker "
2660  << "have access to the source code? Check that the Supervisor context "
2661  "places MacroMaker in a "
2662  << "location with access to the source code." << __E__;
2663  __SS_THROW__;
2664  }
2665 
2666  // at this point have header and source file, now add FE Macro
2667  // Steps for each file:
2668  // - read current file
2669  // - find insert point
2670  // - open file for writing
2671  // - write original file up to insert point
2672  // - insert new code
2673  // - write remaining original file
2674 
2675  char timeBuffer[100];
2676  { // get time string
2677  time_t rawtime;
2678  struct tm* timeinfo;
2679 
2680  time(&rawtime);
2681  timeinfo = localtime(&rawtime);
2682 
2683  strftime(timeBuffer, 100, "%b-%d-%Y %I:%M:%S", timeinfo);
2684  }
2685 
2686  std::string contents;
2687  std::string insert;
2688 
2690  // handle source file modifications
2691  CodeEditor::readFile(CodeEditor::SOURCE_BASE_PATH, sourceFile, contents);
2692  //__SUP_COUTV__(contents);
2693 
2694  // return file locations, for the user to inspect on error
2695  xmldoc.addTextElementToData("sourceFile", sourceFile);
2696  xmldoc.addTextElementToData("headerFile", headerFile);
2697 
2698  // check for duplicate functions
2699  if(contents.find(pluginName + "::" + macroName) != std::string::npos)
2700  {
2701  __SS__ << "The function definition '" << (pluginName + "::" + macroName)
2702  << "(...)' already exists in the source file '" << sourceFile
2703  << ".' Duplicate functions are not allowed - please rename the macro or "
2704  "modify the source file."
2705  << __E__;
2706  __SS_THROW__;
2707  }
2708 
2709  std::stringstream codess;
2710  std::set<std::string> inArgNames, outArgNames;
2711  createCode(codess,
2712  commands,
2713  "\t" /*tabOffset*/,
2714  true /*forFeMacro*/,
2715  &inArgNames,
2716  &outArgNames);
2717  __SUP_COUTV__(StringMacros::setToString(inArgNames));
2718  __SUP_COUTV__(StringMacros::setToString(outArgNames));
2719 
2720  // find start of constructor and register macro
2721  {
2722  auto insertPos = contents.find(pluginName + "::" + pluginName);
2723  if(insertPos == std::string::npos)
2724  {
2725  __SS__ << "Could not find the code insert position in the source file '"
2726  << sourceFile << ".' The FE plugin class constructor must be '"
2727  << pluginName << ":" << pluginName << "' - is this the case?" << __E__;
2728  __SS_THROW__;
2729  }
2730  __SUP_COUTV__(insertPos);
2731  // find opening bracket after constructor name
2732  insertPos = contents.find("{", insertPos);
2733  if(insertPos == std::string::npos)
2734  {
2735  __SS__ << "Could not find the code insert position in the source file '"
2736  << sourceFile
2737  << ".' The FE plugin class constructor must begin with '{"
2738  << "' - is this the case?" << __E__;
2739  __SS_THROW__;
2740  }
2741  ++insertPos; // go past {
2742  __SUP_COUTV__(insertPos);
2743 
2744  insert = "\n\t//registration of FEMacro '" + macroName + "' generated, " +
2745  timeBuffer + ", by '" + username + "' using MacroMaker.\n\t" +
2746  "FEVInterface::registerFEMacroFunction(\"" + macroName +
2747  "\",//feMacroName \n\t\t" +
2748  "static_cast<FEVInterface::frontEndMacroFunction_t>(&" + pluginName +
2749  "::" + macroName + "), //feMacroFunction \n\t\t" +
2750  "std::vector<std::string>{";
2751  { // insert input argument names
2752  bool first = true;
2753  for(const auto& inArg : inArgNames)
2754  {
2755  if(first)
2756  first = false;
2757  else
2758  insert += ",";
2759  insert += "\"" + inArg + "\"";
2760  }
2761  }
2762  insert += "}, //namesOfInputArgs \n\t\t";
2763  insert += "std::vector<std::string>{";
2764  { // insert output argument names
2765  bool first = true;
2766  for(const auto& outArg : outArgNames)
2767  {
2768  if(first)
2769  first = false;
2770  else
2771  insert += ",";
2772  insert += "\"" + outArg + "\"";
2773  }
2774  }
2775  insert += "}, //namesOfOutputArgs \n\t\t";
2776  insert += "1); //requiredUserPermissions \n\n";
2777 
2778  __SUP_COUTV__(insert);
2779  contents = contents.substr(0, insertPos) + insert + contents.substr(insertPos);
2780  }
2781 
2782  // find end of source to append FE Macro function
2783  {
2784  auto insertPos = contents.rfind("DEFINE_OTS_INTERFACE");
2785  if(insertPos == std::string::npos)
2786  {
2787  __SS__ << "Could not find the code insert position in the source file '"
2788  << sourceFile
2789  << ".' The FE plugin class must end with a 'DEFINE_OTS_INTERFACE("
2790  << pluginName << ")' - is this the case?" << __E__;
2791  __SS_THROW__;
2792  }
2793  __SUP_COUTV__(insertPos);
2794 
2795  insert =
2796  "\n//"
2797  "============================================================================"
2798  "============================================\n//" +
2799  macroName + "\n" + "//\tFEMacro '" + macroName + "' generated, " +
2800  timeBuffer + ", by '" + username + "' using MacroMaker.\n" +
2801  "//\tMacro Notes: " + macroNotes + "\n" + "void " + pluginName +
2802  "::" + macroName + "(__ARGS__)\n{\n\t" +
2803  "__CFG_COUT__ << \"# of input args = \" << argsIn.size() << __E__; \n\t" +
2804  "__CFG_COUT__ << \"# of output args = \" << argsOut.size() << __E__; \n\t" +
2805  "for(auto &argIn:argsIn) \n\t\t" +
2806  "__CFG_COUT__ << argIn.first << \": \" << argIn.second << __E__; \n\n\t" +
2807  "//macro commands section \n" + codess.str() + "\n\n\t" +
2808  "for(auto &argOut:argsOut) \n\t\t" +
2809  "__CFG_COUT__ << argOut.first << \": \" << argOut.second << __E__; \n\n" +
2810  "} //end " + macroName + "()\n\n";
2811 
2812  //__SUP_COUTV__(insert);
2813  CodeEditor::writeFile(CodeEditor::SOURCE_BASE_PATH,
2814  sourceFile,
2815  contents,
2816  "MacroMaker-" + username,
2817  insertPos,
2818  insert);
2819  }
2820 
2822  // handle include file insertions
2823  CodeEditor::readFile(CodeEditor::SOURCE_BASE_PATH, headerFile, contents);
2824  //__SUP_COUTV__(contents);
2825 
2826  // find end of class by looking for last };
2827  {
2828  auto insertPos = contents.rfind("};");
2829  if(insertPos == std::string::npos)
2830  {
2831  __SS__ << "Could not find the code insert position in the header file '"
2832  << headerFile
2833  << ".' The FE plugin class must end with a '};' - is this the case?"
2834  << __E__;
2835  __SS_THROW__;
2836  }
2837 
2838  __SUP_COUTV__(insertPos);
2839 
2840  insert = "\npublic: // FEMacro '" + macroName + "' generated, " + timeBuffer +
2841  ", by '" + username + "' using MacroMaker.\n\t" + "void " + macroName +
2842  "\t(__ARGS__);\n";
2843 
2844  __SUP_COUTV__(insert);
2845  CodeEditor::writeFile(CodeEditor::SOURCE_BASE_PATH,
2846  headerFile,
2847  contents,
2848  "MacroMaker-" + username,
2849  insertPos,
2850  insert);
2851  }
2852 
2853 } // end exportFEMacro()
2854 
2855 //==============================================================================
2856 void MacroMakerSupervisor::exportMacro(HttpXmlDocument& xmldoc,
2857  cgicc::Cgicc& cgi,
2858  const std::string& username)
2859 {
2860  std::string macroName = CgiDataUtilities::getData(cgi, "MacroName");
2861  std::string macroSequence = CgiDataUtilities::postData(cgi, "MacroSequence");
2862  std::string macroNotes =
2864 
2865  __SUP_COUTV__(macroName);
2866  __SUP_COUTV__(macroSequence);
2867 
2868  // replace all special characters with white space
2869  for(unsigned int i = 0; i < macroNotes.length(); ++i)
2870  if(macroNotes[i] == '\r' || macroNotes[i] == '\n')
2871  macroNotes[i] = ' ';
2872  __SUP_COUTV__(macroNotes);
2873 
2874  std::stringstream ss(macroSequence);
2875  std::string command;
2876  std::vector<std::string> commands;
2877 
2878  while(getline(ss, command, ','))
2879  commands.push_back(command);
2880 
2881  std::string fileName = macroName + ".cc";
2882 
2883  std::string fullPath =
2884  __ENV__("SERVICE_DATA_PATH") + MACROS_EXPORT_PATH + username + "/" + fileName;
2885  __SUP_COUT__ << fullPath << __E__;
2886  std::ofstream exportFile(fullPath.c_str(), std::ios::trunc);
2887  if(exportFile.is_open())
2888  {
2889  exportFile << "//Generated Macro Name:\t" << macroName << "\n";
2890  exportFile << "//Macro Notes: " << macroNotes << "\n";
2891 
2892  {
2893  time_t rawtime;
2894  struct tm* timeinfo;
2895  char buffer[100];
2896 
2897  time(&rawtime);
2898  timeinfo = localtime(&rawtime);
2899 
2900  strftime(buffer, 100, "%b-%d-%Y %I:%M:%S", timeinfo);
2901  exportFile << "//Generated Time: \t\t" << buffer << "\n";
2902  }
2903 
2904  exportFile << "//Paste this whole file into an interface to transfer Macro "
2905  "functionality.\n";
2906 
2907  createCode(exportFile, commands);
2908 
2909  exportFile.close();
2910 
2911  xmldoc.addTextElementToData(
2912  "ExportFile",
2913  "$USER_DATA/ServiceData/" + MACROS_EXPORT_PATH + username + "/" + fileName);
2914  }
2915  else
2916  __SUP_COUT__ << "Unable to open file" << __E__;
2917 } // end exportMacro()
2918 
2919 //==============================================================================
2921 void MacroMakerSupervisor::createCode(std::ostream& out,
2922  const std::vector<std::string>& commands,
2923  const std::string& tabOffset,
2924  bool forFeMacro,
2925  std::set<std::string>* inArgNames,
2926  std::set<std::string>* outArgNames)
2927 {
2928  // int numOfHexBytes;
2929  std::set<std::string /*argInName*/> argInHasBeenInitializedSet;
2930  bool addressIsVariable, dataIsVariable;
2931 
2932  out << tabOffset << "{";
2933 
2934  out << "\n"
2935  << tabOffset << "\t"
2936  << "char *address \t= new char[universalAddressSize_]{0}; //create address "
2937  "buffer of interface size and init to all 0";
2938  out << "\n"
2939  << tabOffset << "\t"
2940  << "char *data \t\t= new char[universalDataSize_]{0}; //create data buffer "
2941  "of interface size and init to all 0";
2942 
2943  out << "\n"
2944  << tabOffset << "\t"
2945  << "uint64_t macroAddress; //create macro address buffer (size 8 bytes)";
2946  out << "\n"
2947  << tabOffset << "\t"
2948  << "uint64_t macroData; //create macro address buffer (size 8 bytes)";
2949 
2950  out << "\n"
2951  << tabOffset << "\t"
2952  << "std::map<std::string /*arg name*/,uint64_t /*arg val*/> macroArgs; //create "
2953  "map from arg name to 64-bit number";
2954 
2955  // loop through each macro command
2956  for(unsigned int i = 0; i < commands.size(); i++)
2957  {
2958  std::stringstream sst(commands[i]);
2959  std::string tokens;
2960  std::vector<std::string>
2961  oneCommand; // 4 fields: cmd index | cmd type | addr | data
2962  while(getline(sst, tokens, ':'))
2963  oneCommand.push_back(tokens);
2964  while(oneCommand.size() < 4)
2965  oneCommand.push_back(""); // fill out the 4 fields
2966 
2967  __SUP_COUTV__(StringMacros::vectorToString(oneCommand));
2968 
2969  // make this:
2970  // std::map<std::string,uint64_t> macroArgs;
2971  // {
2972  // uint64_t address = 0x1001; //create address buffer
2973  // uint64_t data = 0x100203; //create data buffer
2974  //
2975  // universalWrite(address,data);
2976  // universalRead(address,data);
2977  // }
2978  //
2979  // //if variable, first time init
2980  // {
2981  // address =
2982  // theXDAQContextConfigTree_.getNode(theConfigurationPath_).getNode("variableName").getValue<uint64_t>();
2983  // or
2984  // address = __GET_ARG_IN__("variableName",uint64_t);
2985  // }
2986  //
2987  // //if variable, second time use macroArgs
2988  // {
2989  // address = macroArgs["variableName"];
2990  // data = macroArgs["variableName"];
2991  // }
2992 
2993  addressIsVariable = isArgumentVariable(oneCommand[2]);
2994  dataIsVariable = isArgumentVariable(oneCommand[3]);
2995 
2996  __SUP_COUTV__(addressIsVariable);
2997  __SUP_COUTV__(dataIsVariable);
2998 
2999  out << "\n\n" << tabOffset << "\t// command-#" << i << ": ";
3000 
3001  if(oneCommand[1][0] == 'w' || oneCommand[1][0] == 'r')
3002  {
3003  if(oneCommand[1][0] == 'w')
3004  out << "Write(";
3005  else if(oneCommand[1][0] == 'r')
3006  out << "Read(";
3007 
3008  if(addressIsVariable)
3009  out << oneCommand[2];
3010  else // literal hex address
3011  out << "0x" << oneCommand[2];
3012  out << " /*address*/,";
3013 
3014  if(dataIsVariable) // read or write can have variable data, sink or source
3015  // respectively
3016  out << oneCommand[3] << " /*data*/";
3017  else if(oneCommand[1][0] == 'w') // literal hex data
3018  out << "0x" << oneCommand[3] << " /*data*/";
3019  else if(oneCommand[1][0] == 'r') // just reading to buffer
3020  out << "data";
3021  out << ");\n";
3022  }
3023  else if(oneCommand[1][0] == 'd')
3024  {
3025  out << "delay(" << oneCommand[2] << ");\n";
3026  out << tabOffset << "\t"
3027  << "__CFG_COUT__ << \"Sleeping for... \" << " << oneCommand[2]
3028  << " << \" milliseconds \" << __E__;\n";
3029  out << tabOffset << "\t"
3030  << "usleep(" << oneCommand[2] << "*1000 /* microseconds */);\n";
3031  continue;
3032  }
3033  else
3034  {
3035  __SS__ << "FATAL ERROR: Unknown command '" << oneCommand[1]
3036  << "'... command is not w, r or d" << __E__;
3037  __SS_THROW__;
3038  }
3039 
3041  // handle address
3042  if(addressIsVariable) // handle address as variable
3043  {
3044  if(argInHasBeenInitializedSet.find(oneCommand[2]) ==
3045  argInHasBeenInitializedSet.end()) // only initialize input argument once
3046  {
3047  argInHasBeenInitializedSet.emplace(oneCommand[2]);
3048 
3049  if(!forFeMacro)
3050  {
3051  // get address from configuration Tree
3052  out << tabOffset << "\t"
3053  << "macroArgs[\"" << oneCommand[2]
3054  << "\"] = "
3055  "theXDAQContextConfigTree_.getNode(theConfigurationPath_)."
3056  "getNode("
3057  << "\n"
3058  << tabOffset << "\t\t\"" << oneCommand[2]
3059  << "\").getValue<uint64_t>();";
3060  }
3061  else
3062  {
3063  if(inArgNames)
3064  inArgNames->emplace(oneCommand[2]);
3065 
3066  // get address from arguments
3067  out << tabOffset << "\t"
3068  << "macroArgs[\"" << oneCommand[2] << "\"] = __GET_ARG_IN__(\""
3069  << oneCommand[2] << "\", uint64_t);";
3070  }
3071  }
3072  out << "\t//get macro address argument";
3073  out << "\n"
3074  << tabOffset << "\tmemcpy(address,&macroArgs[\"" << oneCommand[2]
3075  << "\"],8); //copy macro address argument to buffer";
3076  }
3077  else // handle address as literal
3078  {
3079  out << tabOffset << "\t"
3080  << "macroAddress = 0x" << oneCommand[2]
3081  << "; memcpy(address,&macroAddress,8);"
3082  << "\t//copy macro address to buffer";
3083  }
3084 
3086  // handle data
3087  if(oneCommand[1] == "w") // if write, handle data too
3088  {
3089  if(dataIsVariable) // handle data as variable
3090  {
3091  if(argInHasBeenInitializedSet.find(oneCommand[3]) ==
3092  argInHasBeenInitializedSet
3093  .end()) // only initialize input argument once
3094  {
3095  argInHasBeenInitializedSet.emplace(oneCommand[3]);
3096 
3097  if(forFeMacro)
3098  {
3099  if(inArgNames)
3100  inArgNames->emplace(oneCommand[3]);
3101 
3102  // get data from arguments
3103  out << "\n"
3104  << tabOffset << "\t"
3105  << "macroArgs[\"" << oneCommand[3]
3106  << "\"] = __GET_ARG_IN__(\"" << oneCommand[3]
3107  << "\", uint64_t); //initialize from input arguments";
3108  }
3109  else
3110  {
3111  // get data from configuration Tree
3112  out << "\n"
3113  << tabOffset << "\t"
3114  << "macroArgs[\"" << oneCommand[3]
3115  << "\"] = "
3116  "theXDAQContextConfigTree_.getNode(theConfigurationPath_)."
3117  "getNode("
3118  << "\n"
3119  << tabOffset << "\t\t\"" << oneCommand[3]
3120  << "\").getValue<uint64_t>(); //initialize from "
3121  "configuration tree";
3122  }
3123  }
3124  out << "\t//get macro data argument";
3125  out << "\n"
3126  << tabOffset << "\tmemcpy(data,&macroArgs[\"" << oneCommand[3]
3127  << "\"],8); //copy macro data argument to buffer";
3128  }
3129  else // handle data as literal
3130  {
3131  out << "\n"
3132  << tabOffset << "\t"
3133  << "macroData = 0x" << oneCommand[3] << "; memcpy(data,&macroData,8);"
3134  << "\t//copy macro data to buffer";
3135  }
3136  out << "\n"
3137  << tabOffset << "\t"
3138  << "universalWrite(address,data);";
3139  }
3140  else
3141  {
3142  out << "\n"
3143  << tabOffset << "\t"
3144  << "universalRead(address,data);";
3145 
3146  std::string outputArgName;
3147 
3148  if(dataIsVariable) // handle data as variable
3149  outputArgName = oneCommand[3];
3150  else // give each read data a unique argument name
3151  {
3152  char str[20];
3153  sprintf(str, "outArg%d", i);
3154  outputArgName = str; // use command index for uniqueness
3155  }
3156  __SUP_COUTV__(outputArgName);
3157 
3158  out << tabOffset << "\t"
3159  << "memcpy(&macroArgs[\"" << outputArgName
3160  << "\"],data,8); //copy buffer to argument map";
3161 
3162  // copy read data to output args
3163  if(forFeMacro)
3164  out << "\n"
3165  << tabOffset << "\t"
3166  << "__SET_ARG_OUT__(\"" << outputArgName << "\",macroArgs[\""
3167  << outputArgName << "\"]); //update output argument result";
3168 
3169  if(outArgNames)
3170  outArgNames->emplace(outputArgName);
3171  argInHasBeenInitializedSet.emplace(
3172  outputArgName); // mark initialized since value has been read
3173  }
3174  } // end command loop
3175 
3176  out << "\n\n" << tabOffset << "\tdelete[] address; //free the memory";
3177  out << "\n" << tabOffset << "\tdelete[] data; //free the memory";
3178  out << "\n" << tabOffset << "}";
3179 
3180  __SUP_COUT__ << "Done with code generation." << __E__;
3181 } // end createCode()
3182 
3183 //==============================================================================
3186 bool MacroMakerSupervisor::isArgumentVariable(const std::string& argumentString)
3187 {
3188  for(unsigned int i = 0; i < argumentString.length(); ++i)
3189  {
3190  // detect non-hex
3191  if(!((argumentString[i] >= '0' && argumentString[i] <= '9') ||
3192  (argumentString[i] >= 'a' && argumentString[i] <= 'f') ||
3193  (argumentString[i] >= 'A' && argumentString[i] <= 'F')))
3194  return true;
3195  }
3196  return false;
3197 } // end isArgumentVariable()
3198 //==============================================================================
3208 std::string MacroMakerSupervisor::generateHexArray(const std::string& sourceHexString,
3209  int& numOfBytes)
3210 {
3211  std::stringstream retSs;
3212 
3213  std::string srcHexStr = sourceHexString;
3214  __SUP_COUT__ << "Translating: \n";
3215  __SUP_COUT__ << srcHexStr << __E__;
3216 
3217  if(srcHexStr.size() % 2) // if odd, make even
3218  srcHexStr = "0" + srcHexStr;
3219 
3220  numOfBytes = srcHexStr.size() / 2;
3221  retSs << "[" << numOfBytes << "] = {";
3222 
3223  for(int i = 0; i < numOfBytes * 2; i += 2)
3224  {
3225  // detect non-hex
3226  if(!((srcHexStr[i] >= '0' && srcHexStr[i] <= '9') ||
3227  (srcHexStr[i] >= 'a' && srcHexStr[i] <= 'f') ||
3228  (srcHexStr[i] >= 'A' && srcHexStr[i] <= 'F')) ||
3229  !((srcHexStr[i + 1] >= '0' && srcHexStr[i + 1] <= '9') ||
3230  (srcHexStr[i + 1] >= 'a' && srcHexStr[i + 1] <= 'f') ||
3231  (srcHexStr[i + 1] >= 'A' && srcHexStr[i + 1] <= 'F')))
3232  {
3233  numOfBytes = -1;
3234  return srcHexStr;
3235  }
3236 
3237  if(i != 0)
3238  retSs << ", ";
3239  retSs << "0x" << srcHexStr[srcHexStr.size() - 1 - i - 1]
3240  << srcHexStr[srcHexStr.size() - 1 - i];
3241  }
3242  retSs << "};";
3243 
3244  __SUP_COUT__ << retSs.str() << __E__;
3245 
3246  return retSs.str();
3247 } //end generateHexArray()
3248 
3249 //==============================================================================
3250 void MacroMakerSupervisor::runFEMacro(HttpXmlDocument& xmldoc,
3251  cgicc::Cgicc& cgi,
3252  const WebUsers::RequestUserInfo& userInfo)
3253 try
3254 {
3255  __SUP_COUTT__ << __E__;
3256 
3257  uint64_t NotDoneID = CgiDataUtilities::getDataAsUint64_t(cgi, "NotDoneID");
3258  if(NotDoneID)
3259  {
3260  std::lock_guard<std::mutex> lock(feMacroRunThreadStructMutex_);
3261 
3262  __SUP_COUT__ << "Checking if recent FE macro group has completed for NotDoneID = "
3263  << NotDoneID << __E__;
3264 
3265  for(const auto& g : feMacroRunThreadStruct_)
3266  __SUP_COUTT__ << "[] groupID_ = " << g->groupID_ << __E__;
3267 
3268  time_t now = time(0);
3269  size_t target_i = -1;
3270  for(size_t i = 0; i < feMacroRunThreadStruct_.size(); ++i)
3271  {
3272  auto& group = feMacroRunThreadStruct_[i];
3273  if(group->groupID_ == NotDoneID)
3274  {
3275  __SUP_COUTT__ << "Found group NotDoneID = " << NotDoneID << __E__;
3276  target_i = i;
3277  }
3278  else if(group->allDone())
3279  {
3280  // compute latest task doneTime to determine age
3281  time_t latestDone = 0;
3282  for(const auto& t : group->tasks_)
3283  if(t->parameters_.doneTime_ > latestDone)
3284  latestDone = t->parameters_.doneTime_;
3285  if(latestDone >= 0 && now - latestDone > 5 * 60 /* 5 minutes */)
3286  {
3287  __SUP_COUTT__ << "Cleaning up completed group " << group->groupID_
3288  << __E__;
3289  feMacroRunThreadStruct_.erase(feMacroRunThreadStruct_.begin() + i);
3290  --i; //rewind
3291  }
3292  }
3293  else if(now - group->startTime_ > 5 * 60 /* 5 minutes */)
3294  {
3295  std::string targets;
3296  std::string feMacroName;
3297  for(const auto& t : group->tasks_)
3298  {
3299  if(!targets.empty())
3300  targets += ", ";
3301  targets += t->parameters_.feUIDSelected_;
3302  if(feMacroName.empty())
3303  feMacroName = t->parameters_.macroName_;
3304  }
3305  __SUP_COUT_WARN__ << "Found old FE Macro group that has not completed"
3306  << " (groupID=" << group->groupID_ << ", targets=["
3307  << targets << "], FE macro name=" << feMacroName << ")"
3308  << __E__;
3309  continue;
3310  }
3311  }
3312 
3313  if(target_i >= feMacroRunThreadStruct_.size())
3314  {
3315  __SUP_SS__
3316  << "Attempted to check recent FE Macro run completion with invalid ID="
3317  << NotDoneID
3318  << ". Perhaps this FE Macro completed more than 5 minutes ago?" << __E__;
3319  __SUP_SS_THROW__;
3320  }
3321 
3322  auto& targetGroup = feMacroRunThreadStruct_[target_i];
3323  if(targetGroup->allDone())
3324  {
3325  __SUP_COUT__ << "Found all done for group NotDoneID = " << NotDoneID << __E__;
3326  for(auto& task : targetGroup->tasks_)
3327  if(task->bar_)
3328  task->bar_->complete();
3329 
3330  // check for any errors
3331  for(auto& task : targetGroup->tasks_)
3332  {
3333  if(task->parameters_.feMacroRunError_ != "")
3334  {
3335  __SUP_SS__ << task->parameters_.feMacroRunError_;
3336  __SUP_SS_THROW__;
3337  }
3338  }
3339  // aggregate results from all per-UID xmldocs
3340  if(TTEST(1))
3341  {
3342  std::ostringstream oss;
3343  for(auto& task : targetGroup->tasks_)
3344  task->parameters_.xmldoc_.outputXmlDocument(&oss);
3345  __SUP_COUTT__ << "xmldoc: " << oss.str() << __E__;
3346  }
3347  for(auto& task : targetGroup->tasks_)
3348  xmldoc.copyDataChildren(task->parameters_.xmldoc_);
3349  __SUP_COUT__ << "FE macro group complete." << __E__;
3350  }
3351  else
3352  {
3353  __SUP_COUT__ << "Found still going for group NotDoneID = " << NotDoneID
3354  << __E__;
3355  //return same NotDoneID to user for future check
3356  xmldoc.addNumberElementToData("NotDoneID", NotDoneID);
3357 
3358  //report per-UID progress
3359  for(auto& task : targetGroup->tasks_)
3360  {
3361  DOMElement* progParent = xmldoc.addTextElementToData(
3362  "feMacroProgress", task->parameters_.feUIDSelected_);
3363  if(task->feMacroRunDone_)
3364  {
3365  xmldoc.addTextElementToParent("progress", "100", progParent);
3366  }
3367  else
3368  {
3369  if(task->bar_)
3370  {
3371  xmldoc.addTextElementToParent(
3372  "progress", std::to_string(task->bar_->read()), progParent);
3373  }
3374  }
3375  } //end report per-UID progress
3376  }
3377 
3378  return;
3379  } //end done checking for done long duration FE Macro
3380 
3381  std::string feClassSelected = CgiDataUtilities::getData(cgi, "feClassSelected");
3382  std::string feUIDSelected =
3383  CgiDataUtilities::getData(cgi, "feUIDSelected"); // allow CSV multi-selection
3384  std::string macroType = CgiDataUtilities::getData(cgi, "macroType");
3385  std::string macroName =
3387  std::string inputArgs = CgiDataUtilities::postData(cgi, "inputArgs");
3388  std::string outputArgs = CgiDataUtilities::postData(cgi, "outputArgs");
3389  bool saveOutputs = CgiDataUtilities::getDataAsInt(cgi, "saveOutputs") == 1;
3390 
3391  __SUP_COUTTV__(feClassSelected);
3392  __SUP_COUTTV__(feUIDSelected);
3393  __SUP_COUTTV__(macroType);
3394  __SUP_COUTTV__(macroName);
3395  __SUP_COUTTV__(inputArgs);
3396  __SUP_COUTTV__(outputArgs);
3397  __SUP_COUTTV__(saveOutputs);
3398  __SUP_COUTTV__(userInfo.username_);
3399  __SUP_COUTTV__(StringMacros::mapToString(userInfo.getGroupPermissionLevels()));
3400 
3401  // Expand feUIDSelected CSV into individual per-UID tasks
3402  std::set<std::string> feUIDs;
3403  {
3404  std::string expandUID = feUIDSelected.empty() ? "*" : feUIDSelected;
3405  if(expandUID != "*")
3406  {
3407  StringMacros::getSetFromString(expandUID, feUIDs);
3408  }
3409  else
3410  {
3411  // wildcard: collect all UIDs for the selected class (or all classes)
3412  for(auto& feTypePair : FEPluginTypetoFEsMap_)
3413  {
3414  if(feClassSelected.empty() || feClassSelected == "*" ||
3415  feClassSelected == feTypePair.first)
3416  for(auto& uid : feTypePair.second)
3417  feUIDs.emplace(uid);
3418  }
3419  }
3420  if(feUIDs.empty())
3421  feUIDs.emplace(feUIDSelected); // fallback: let work overload handle error
3422  }
3423  __SUP_COUTV__(StringMacros::setToString(feUIDs));
3424 
3425  // Create one runFEMacroStruct per UID, grouped under a single runFEMacroGroupStruct
3426  auto group = std::make_shared<runFEMacroGroupStruct>();
3427  group->historyFeClassSelected_ = feClassSelected.empty() ? "*" : feClassSelected;
3428  group->historyFeUIDSelected_ = feUIDSelected.empty() ? "*" : feUIDSelected;
3429  group->historyMacroType_ = macroType;
3430  group->historyMacroName_ = macroName;
3431  group->historyInputArgs_ = inputArgs;
3432  group->historyOutputArgs_ = outputArgs;
3433  group->historySaveOutputs_ = saveOutputs;
3434  group->historyUsername_ = userInfo.username_;
3435  for(const std::string& uid : feUIDs)
3436  {
3437  group->tasks_.push_back(std::make_shared<runFEMacroStruct>(
3438  xmldoc,
3439  feClassSelected,
3440  uid,
3441  macroType,
3442  macroName,
3443  inputArgs,
3444  outputArgs,
3445  saveOutputs,
3446  userInfo.username_,
3447  StringMacros::mapToString(userInfo.getGroupPermissionLevels())));
3448  }
3449  {
3450  std::lock_guard<std::mutex> lock(feMacroRunThreadStructMutex_);
3451  group->groupID_ = ++feMacroRunGroupIDCounter_;
3452  if(feMacroRunGroupIDCounter_ == 0)
3453  group->groupID_ =
3454  ++feMacroRunGroupIDCounter_; // avoid 0 for better error detection
3455 
3456  for(auto& task : group->tasks_)
3457  {
3458  task->bar_ = std::make_unique<ProgressBar>();
3459  task->bar_->reset(macroName, task->parameters_.feUIDSelected_);
3460  }
3461  feMacroRunThreadStruct_.emplace_back(group);
3462  }
3463 
3464  std::thread([group, this]() {
3465  MacroMakerSupervisor::runFEMacroGroupSchedulerThread(group, this);
3466  }).detach();
3467 
3468  size_t sleepTime = 10 * 1000; //10ms
3469  usleep(sleepTime);
3470  //if not all done quickly, track in "not-done" queue
3471  for(int i = 0; i < 6; ++i)
3472  {
3473  if(group->allDone())
3474  {
3475  __SUP_COUTT__ << "All FE macro tasks marked done" << __E__;
3476  for(const auto& task : group->tasks_)
3477  if(task->parameters_.doneTime_ > group->completeTime_)
3478  group->completeTime_ = task->parameters_.doneTime_;
3479  break;
3480  }
3481  else
3482  {
3483  __SUP_COUTT__ << "FE macros not all done, sleeping..." << __E__;
3484  sleepTime *= 5; //50ms, 250ms, 1s
3485  if(sleepTime > 1000 * 1000 /* 1 second */)
3486  sleepTime = 1000 * 1000;
3487  usleep(sleepTime);
3488  }
3489  } //end wait loop
3490 
3491  if(!group->allDone()) //not all done - go async
3492  {
3493  xmldoc.addNumberElementToData("NotDoneID", group->groupID_);
3494 
3495  //report per-UID progress started
3496  {
3497  std::lock_guard<std::mutex> lock(feMacroRunThreadStructMutex_);
3498  for(auto& task : group->tasks_)
3499  {
3500  DOMElement* progParent = xmldoc.addTextElementToData(
3501  "feMacroProgress", task->parameters_.feUIDSelected_);
3502  if(task->feMacroRunDone_)
3503  {
3504  xmldoc.addTextElementToParent("progress", "100", progParent);
3505  }
3506  else
3507  {
3508  if(task->bar_)
3509  {
3510  xmldoc.addTextElementToParent(
3511  "progress", std::to_string(task->bar_->read()), progParent);
3512  }
3513  }
3514  }
3515  } //end report per-UID progress started
3516  }
3517  else //all done synchronously
3518  {
3519  for(auto& task : group->tasks_)
3520  {
3521  if(task->parameters_.feMacroRunError_ != "")
3522  {
3523  __SUP_SS__ << task->parameters_.feMacroRunError_;
3524  __SUP_SS_THROW__;
3525  }
3526  }
3527  //copy result back to user
3528  if(TTEST(1))
3529  {
3530  std::ostringstream oss;
3531  for(auto& task : group->tasks_)
3532  task->parameters_.xmldoc_.outputXmlDocument(
3533  &oss, false /* dispStdOut */, true /* allowWhiteSpace */);
3534  __SUP_COUTT__ << "xmldoc: " << oss.str() << __E__;
3535  }
3536  for(auto& task : group->tasks_)
3537  xmldoc.copyDataChildren(task->parameters_.xmldoc_);
3538 
3539  {
3540  std::lock_guard<std::mutex> lock(feMacroRunThreadStructMutex_);
3541  for(size_t i = 0; i < feMacroRunThreadStruct_.size(); ++i)
3542  {
3543  if(feMacroRunThreadStruct_[i].get() == group.get())
3544  {
3545  feMacroRunThreadStruct_.erase(feMacroRunThreadStruct_.begin() + i);
3546  break;
3547  }
3548  }
3549  }
3550  __SUP_COUT__ << "All FE macros complete." << __E__;
3551  }
3552  {
3553  std::lock_guard<std::mutex> lock(feMacroRunThreadStructMutex_);
3554  for(const auto& g : feMacroRunThreadStruct_)
3555  __SUP_COUTT__ << "[] groupID_ = " << g->groupID_ << __E__;
3556  }
3557 
3558 } //end runFEMacro()
3559 catch(const std::runtime_error& e)
3560 {
3561  __SUP_SS__ << "Error processing FE communication request: " << e.what() << __E__;
3562  __SUP_COUT_ERR__ << ss.str();
3563  xmldoc.addTextElementToData("Error", ss.str());
3564 }
3565 catch(...)
3566 {
3567  __SUP_SS__ << "Unknown error processing FE communication request." << __E__;
3568  try
3569  {
3570  throw;
3571  } //one more try to printout extra info
3572  catch(const std::exception& e)
3573  {
3574  ss << "Exception message: " << e.what();
3575  }
3576  catch(...)
3577  {
3578  }
3579  __SUP_COUT_ERR__ << ss.str();
3580 
3581  xmldoc.addTextElementToData("Error", ss.str());
3582 } // end runFEMacro() catch
3583 
3584 //==============================================================================
3586 void MacroMakerSupervisor::runFEMacroGroupSchedulerThread(
3587  std::shared_ptr<runFEMacroGroupStruct> group, MacroMakerSupervisor* mmSupervisor)
3588 try
3589 {
3590  if(!group || !mmSupervisor || group->tasks_.empty())
3591  return;
3592 
3593  __COUT__ << "FE macro group scheduler started. groupID=" << group->groupID_
3594  << " tasks=" << group->tasks_.size() << __E__;
3595 
3596  std::size_t maxThreads = std::thread::hardware_concurrency();
3597  if(maxThreads == 0)
3598  maxThreads = 4;
3599  if(maxThreads > group->tasks_.size())
3600  maxThreads = group->tasks_.size();
3601 
3602  std::vector<std::pair<std::shared_ptr<runFEMacroStruct>, std::future<void>>> active;
3603  active.reserve(maxThreads);
3604  std::size_t nextTaskIndex = 0;
3605 
3606  auto launchTask = [&](std::shared_ptr<runFEMacroStruct> task) {
3607  active.emplace_back(task, std::async(std::launch::async, [task, mmSupervisor]() {
3608  MacroMakerSupervisor::runFEMacroThread(task,
3609  mmSupervisor);
3610  }));
3611  };
3612 
3613  while(nextTaskIndex < group->tasks_.size() || !active.empty())
3614  {
3615  while(nextTaskIndex < group->tasks_.size() && active.size() < maxThreads)
3616  launchTask(group->tasks_[nextTaskIndex++]);
3617 
3618  {
3619  std::lock_guard<std::mutex> lock(mmSupervisor->feMacroRunThreadStructMutex_);
3620  for(const auto& activeTask : active)
3621  if(activeTask.first && !activeTask.first->feMacroRunDone_ &&
3622  activeTask.first->bar_)
3623  activeTask.first->bar_->step();
3624  }
3625 
3626  bool anyFinished = false;
3627  for(size_t i = 0; i < active.size();)
3628  {
3629  if(active[i].second.wait_for(std::chrono::milliseconds(0)) ==
3630  std::future_status::ready)
3631  {
3632  __COUTT__ << "FE macro group scheduler task complete. groupID="
3633  << group->groupID_
3634  << " uid=" << active[i].first->parameters_.feUIDSelected_
3635  << __E__;
3636  active[i].second.get();
3637  active.erase(active.begin() + i);
3638  anyFinished = true;
3639  }
3640  else
3641  ++i;
3642  }
3643 
3644  if(!anyFinished)
3645  usleep(10 * 1000); // 10ms poll interval to keep scheduler lightweight
3646  }
3647 
3648  for(const auto& task : group->tasks_)
3649  if(task->parameters_.doneTime_ > group->completeTime_)
3650  group->completeTime_ = task->parameters_.doneTime_;
3651 
3652  if(!group->historySaved_)
3653  {
3654  mmSupervisor->appendCommandToHistory(group->historyFeClassSelected_,
3655  group->historyFeUIDSelected_,
3656  group->historyMacroType_,
3657  group->historyMacroName_,
3658  group->historyInputArgs_,
3659  group->historyOutputArgs_,
3660  group->historySaveOutputs_,
3661  group->historyUsername_,
3662  group->startTime_,
3663  group->completeTime_);
3664  group->historySaved_ = true;
3665  }
3666 
3667  __COUT__ << "FE macro group scheduler ended. groupID=" << group->groupID_ << __E__;
3668 } //end runFEMacroGroupSchedulerThread()
3669 catch(const std::exception& e)
3670 {
3671  __SS__ << "Error during FE macro group scheduler thread: " << e.what() << __E__;
3672  __COUT_ERR__ << ss.str();
3673 }
3674 catch(...)
3675 {
3676  __COUT_ERR__ << "Unknown error during FE macro group scheduler thread." << __E__;
3677 } //end runFEMacroGroupSchedulerThread() catch
3678 
3679 //==============================================================================
3681 void MacroMakerSupervisor::runFEMacroThread(
3682  std::shared_ptr<runFEMacroStruct> feMacroRunThreadStruct,
3683  MacroMakerSupervisor* mmSupervisor)
3684 try
3685 {
3686  feMacroRunThreadStruct->parameters_.threadID_ =
3687  std::hash<std::thread::id>{}(std::this_thread::get_id());
3688 
3689  __COUT__ << "runFEMacro thread started... threadid = " << std::this_thread::get_id()
3690  << " " << mmSupervisor << " getpid()=" << getpid()
3691  << " gettid()=" << gettid() << __E__;
3692 
3693  mmSupervisor->runFEMacro(feMacroRunThreadStruct->parameters_.xmldoc_,
3694  feMacroRunThreadStruct->parameters_.feClassSelected_,
3695  feMacroRunThreadStruct->parameters_.feUIDSelected_,
3696  feMacroRunThreadStruct->parameters_.macroType_,
3697  feMacroRunThreadStruct->parameters_.macroName_,
3698  feMacroRunThreadStruct->parameters_.inputArgs_,
3699  feMacroRunThreadStruct->parameters_.outputArgs_,
3700  feMacroRunThreadStruct->parameters_.saveOutputs_,
3701  feMacroRunThreadStruct->parameters_.runningUsername_,
3702  feMacroRunThreadStruct->parameters_.userGroupPermissions_,
3703  false /* saveToHistory */);
3704 
3705  feMacroRunThreadStruct->parameters_.doneTime_ = time(0);
3706  feMacroRunThreadStruct->feMacroRunDone_ = true;
3707  __COUT__ << "runFEMacro thread done. threadid = " << std::this_thread::get_id()
3708  << __E__;
3709 
3710 } //end static runFEMacroThread()
3711 catch(const std::runtime_error& e)
3712 {
3713  __SS__ << "Error during runFEMacro thread: " << e.what() << __E__;
3714  __COUT_ERR__ << ss.str();
3715  feMacroRunThreadStruct->parameters_.feMacroRunError_ = ss.str();
3716  feMacroRunThreadStruct->parameters_.doneTime_ = time(0);
3717  feMacroRunThreadStruct->feMacroRunDone_ = true;
3718 }
3719 catch(...)
3720 {
3721  __SS__ << "Unknown error during runFEMacro thread." << __E__;
3722  try
3723  {
3724  throw;
3725  } //one more try to printout extra info
3726  catch(const std::exception& e)
3727  {
3728  ss << "Exception message: " << e.what();
3729  }
3730  catch(...)
3731  {
3732  }
3733  __COUT_ERR__ << ss.str();
3734  feMacroRunThreadStruct->parameters_.feMacroRunError_ = ss.str();
3735  feMacroRunThreadStruct->parameters_.doneTime_ = time(0);
3736  feMacroRunThreadStruct->feMacroRunDone_ = true;
3737 } // end static runFEMacroThread() catch
3738 
3739 //==============================================================================
3740 void MacroMakerSupervisor::runFEMacro(HttpXmlDocument& xmldoc,
3741  std::string feClassSelected,
3742  std::string feUIDSelected,
3743  const std::string& macroType,
3744  const std::string& macroName,
3745  const std::string& inputArgs,
3746  const std::string outputArgs,
3747  bool saveOutputs,
3748  const std::string& username,
3749  const std::string& userGroupPermissions,
3750  bool saveToHistory)
3751 {
3752  __SUP_COUTV__(feClassSelected);
3753  __SUP_COUTV__(feUIDSelected);
3754  __SUP_COUTV__(macroType);
3755  __SUP_COUTV__(macroName);
3756  __SUP_COUTV__(inputArgs);
3757  __SUP_COUTV__(outputArgs);
3758  __SUP_COUTV__(saveOutputs);
3759  __SUP_COUTV__(username);
3760  __SUP_COUTV__(userGroupPermissions);
3761 
3762  time_t launchTime = time(0);
3763  auto saveHistoryIfRequested = [&]() {
3764  if(!saveToHistory)
3765  return;
3766 
3767  appendCommandToHistory(feClassSelected,
3768  feUIDSelected,
3769  macroType,
3770  macroName,
3771  inputArgs,
3772  outputArgs,
3773  saveOutputs,
3774  username,
3775  launchTime,
3776  time(0));
3777  saveToHistory = false;
3778  };
3779 
3780  std::set<std::string /*feUID*/> feUIDs;
3781 
3782  if(feUIDSelected == "")
3783  feUIDSelected = "*"; // treat empty as all
3784  if(feClassSelected == "")
3785  feClassSelected = "*"; // treat empty as all
3786 
3787  if(feClassSelected == "" || feUIDSelected == "" || macroType == "" || macroName == "")
3788  {
3789  __SUP_SS__ << "Illegal empty front-end parameter." << __E__;
3790  __SUP_SS_THROW__;
3791  }
3792  else if(feUIDSelected != "*")
3793  {
3794  StringMacros::getSetFromString(feUIDSelected, feUIDs);
3795  }
3796  else // * all case
3797  {
3798  // add all FEs for type
3799  if(feClassSelected == "*")
3800  {
3801  for(auto& feTypePair : FEPluginTypetoFEsMap_)
3802  for(auto& feUID : feTypePair.second)
3803  feUIDs.emplace(feUID);
3804  }
3805  else
3806  {
3807  auto typeIt = FEPluginTypetoFEsMap_.find(feClassSelected);
3808  if(typeIt == FEPluginTypetoFEsMap_.end())
3809  {
3810  __SUP_SS__ << "Illegal front-end type parameter '" << feClassSelected
3811  << "' not in list of types." << __E__;
3812  __SUP_SS_THROW__;
3813  }
3814 
3815  for(auto& feUID : typeIt->second)
3816  feUIDs.emplace(feUID);
3817  }
3818  }
3819 
3820  __SUP_COUTV__(StringMacros::setToString(feUIDs));
3821 
3822  std::string macroString;
3823  if(macroType == "public")
3824  loadMacro(macroName, macroString);
3825  else if(macroType == "private")
3826  loadMacro(macroName, macroString, username);
3827 
3828  __SUP_COUTV__(macroString);
3829 
3830  FILE* fp = 0;
3831  try
3832  {
3833  if(saveOutputs)
3834  {
3835  std::string filename = "/macroOutput_" + std::to_string(time(0)) + "_" +
3836  std::to_string(clock()) + ".txt";
3837 
3838  __SUP_COUTV__(filename);
3839  fp = fopen((CodeEditor::OTSDAQ_DATA_PATH + filename).c_str(), "w");
3840  if(!fp)
3841  {
3842  __SUP_SS__ << "Failed to open file to save macro output '"
3843  << CodeEditor::OTSDAQ_DATA_PATH << filename << "'..." << __E__;
3844  __SUP_SS_THROW__;
3845  }
3846 
3847  fprintf(fp, "############################\n");
3848  fprintf(fp,
3849  "### Running '%s' at time %s\n",
3850  macroName.c_str(),
3852  fprintf(fp,
3853  "### \t Target front-ends (count=%lu): %s\n",
3854  feUIDs.size(),
3855  StringMacros::setToString(feUIDs).c_str());
3856  fprintf(fp, "### \t\t Inputs: %s\n", inputArgs.c_str());
3857  fprintf(fp, "############################\n\n\n");
3858 
3859  xmldoc.addTextElementToData("feMacroRunArgs_name", "Filename");
3860  xmldoc.addTextElementToData("feMacroRunArgs_value",
3861  "$OTSDAQ_DATA/" + filename);
3862  }
3863 
3864  // do for all target front-ends
3865  for(auto& feUID : feUIDs)
3866  {
3867  auto feIt = FEtoSupervisorMap_.find(feUID);
3868  if(feIt == FEtoSupervisorMap_.end())
3869  {
3870  __SUP_SS__ << "Destination front end interface ID '" << feUID
3871  << "' was not found in the list of front ends." << __E__;
3872  ss << "\n\nHere is the map:\n\n"
3873  << StringMacros::mapToString(FEtoSupervisorMap_) << __E__;
3874  __SUP_SS_THROW__;
3875  }
3876 
3877  unsigned int FESupervisorIndex = feIt->second;
3878  __SUP_COUT__ << "Found supervisor index: " << FESupervisorIndex << __E__;
3879 
3880  SupervisorInfoMap::iterator it = allFESupervisorInfo_.find(FESupervisorIndex);
3881  if(it == allFESupervisorInfo_.end())
3882  {
3883  __SUP_SS__
3884  << "Error transmitting request to FE Supervisor '" << feUID << ":"
3885  << FESupervisorIndex << ".' \n\n"
3886  << "The FE Supervisor Index does not exist. Have you configured "
3887  "the state machine properly?"
3888  << __E__;
3889  __SUP_SS_THROW__;
3890  }
3891 
3892  // send command to chosen FE and await response
3893  SOAPParameters txParameters; // params for xoap to send
3894  if(macroType == "fe")
3895  txParameters.addParameter("Request", "RunInterfaceMacro");
3896  else
3897  txParameters.addParameter("Request", "RunMacroMakerMacro");
3898  txParameters.addParameter("InterfaceID", feUID);
3899  if(macroType == "fe")
3900  txParameters.addParameter("feMacroName", macroName);
3901  else
3902  {
3903  txParameters.addParameter("macroName", macroName);
3904  txParameters.addParameter("macroString", macroString);
3905  }
3906  txParameters.addParameter("inputArgs", inputArgs);
3907  txParameters.addParameter("outputArgs", outputArgs);
3908  txParameters.addParameter("userPermissions", userGroupPermissions);
3909 
3910  SOAPParameters rxParameters; // params for xoap to recv
3911  // rxParameters.addParameter("success");
3912  rxParameters.addParameter("outputArgs");
3913  rxParameters.addParameter("Error");
3914 
3915  if(saveOutputs)
3916  {
3917  fprintf(fp,
3918  "Running '%s' at time %s\n",
3919  macroName.c_str(),
3921  fprintf(fp,
3922  "\t Target front-end: '%s::%s'\n",
3923  FEtoPluginTypeMap_[feUID].c_str(),
3924  feUID.c_str());
3925  fprintf(fp,
3926  "\t\t Inputs: %s\n",
3927  StringMacros::decodeURIComponent(inputArgs).c_str());
3928  }
3929 
3930  // have FE supervisor descriptor, so send
3931  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
3932  it->second.getDescriptor(), // supervisor descriptor
3933  "MacroMakerSupervisorRequest",
3934  txParameters);
3935 
3936  __SUP_COUT__ << "Received response message: "
3937  << SOAPUtilities::translate(retMsg) << __E__;
3938 
3939  SOAPUtilities::receive(retMsg, rxParameters);
3940 
3941  __SUP_COUT__ << "Received it " << __E__;
3942 
3943  // bool success = rxParameters.getValue("success") == "1";
3944  std::string outputResults = rxParameters.getValue("outputArgs");
3945  std::string error = rxParameters.getValue("Error");
3946 
3947  //__SUP_COUT__ << "rx success = " << success << __E__;
3948  __SUP_COUT__ << "outputArgs = " << outputResults << __E__;
3949 
3950  if(error != "")
3951  {
3952  __SS__ << "Attempted FE Macro Failed. Attempted target "
3953  << "was UID=" << feUID
3954  << " at feSupervisorID=" << FESupervisorIndex << "." << __E__;
3955  ss << "\n\n The error was:\n\n" << error << __E__;
3956  __SUP_COUT_ERR__ << "\n" << ss.str();
3957  xmldoc.addTextElementToData("Error", ss.str());
3958  saveHistoryIfRequested();
3959  return;
3960  }
3961 
3962  // build output arguments
3963  // parse args, colon-separated pairs, and then comma-separated
3964  {
3965  DOMElement* feMacroExecParent =
3966  xmldoc.addTextElementToData("feMacroExec", macroName);
3967 
3968  xmldoc.addTextElementToParent(
3969  "exec_time", StringMacros::getTimestampString(), feMacroExecParent);
3970  xmldoc.addTextElementToParent("fe_uid", feUID, feMacroExecParent);
3971  xmldoc.addTextElementToParent(
3972  "fe_type", FEtoPluginTypeMap_[feUID], feMacroExecParent);
3973  xmldoc.addTextElementToParent(
3974  "fe_context", it->second.getContextName(), feMacroExecParent);
3975  xmldoc.addTextElementToParent(
3976  "fe_supervisor", it->second.getName(), feMacroExecParent);
3977  xmldoc.addTextElementToParent(
3978  "fe_hostname", it->second.getHostname(), feMacroExecParent);
3979 
3980  std::istringstream inputStream(outputResults);
3981  std::string splitVal, argName, argValue;
3982  while(getline(inputStream, splitVal, ';'))
3983  {
3984  std::istringstream pairInputStream(splitVal);
3985  getline(pairInputStream, argName, ',');
3986  getline(pairInputStream, argValue, ',');
3987 
3988  if(saveOutputs)
3989  {
3990  fprintf(fp,
3991  "\t\t Output '%s' = %s\n",
3992  argName.c_str(),
3993  StringMacros::decodeURIComponent(argValue).c_str());
3994  }
3995  else
3996  {
3997  xmldoc.addTextElementToParent(
3998  "outputArgs_name", argName, feMacroExecParent);
3999  xmldoc.addTextElementToParent(
4000  "outputArgs_value", argValue, feMacroExecParent);
4001  }
4002  __SUP_COUT__ << argName << ": " << argValue << __E__;
4003  }
4004  }
4005 
4006  __SUP_COUTT__ << "runFEMacro() chk3." << __E__;
4007  } // end target front-end loop
4008  }
4009  catch(...) // handle file close on error
4010  {
4011  if(fp)
4012  fclose(fp);
4013  saveHistoryIfRequested();
4014  throw;
4015  }
4016 
4017  if(fp)
4018  fclose(fp);
4019 
4020  saveHistoryIfRequested();
4021 
4022  __SUP_COUT__ << "runFEMacro() done." << __E__;
4023  //to comment after progress bar test
4024  //sleep(20);
4025 } // end runFEMacro()
4026 
4027 //==============================================================================
4028 void MacroMakerSupervisor::getFEMacroList(HttpXmlDocument& xmldoc,
4029  const std::string& username)
4030 {
4031  __SUP_COUT__ << "Getting FE Macro list" << __E__;
4032 
4033  SOAPParameters txParameters; // params for xoap to send
4034  txParameters.addParameter("Request", "GetInterfaceMacros");
4035 
4036  SOAPParameters rxParameters; // params for xoap to recv
4037  rxParameters.addParameter("FEMacros");
4038 
4039  std::string oneInterface;
4040  std::string rxFEMacros;
4041 
4042  // for each list of FE Supervisors,
4043  // get all FE specific macros
4044  for(auto& appInfo : allFESupervisorInfo_)
4045  {
4046  __SUP_COUT__ << "FESupervisor LID = " << appInfo.second.getId()
4047  << " name = " << appInfo.second.getName() << __E__;
4048 
4049  xoap::MessageReference retMsg = SOAPMessenger::sendWithSOAPReply(
4050  appInfo.second.getDescriptor(), "MacroMakerSupervisorRequest", txParameters);
4051  SOAPUtilities::receive(retMsg, rxParameters);
4052 
4053  rxFEMacros = rxParameters.getValue("FEMacros");
4054 
4055  __SUP_COUT__ << "FE Macros received: \n" << rxFEMacros << __E__;
4056 
4057  std::istringstream allInterfaces(rxFEMacros);
4058  while(std::getline(allInterfaces, oneInterface))
4059  {
4060  //__SUP_COUT__ << oneInterface << __E__;
4061  //__SUP_COUT__ << appInfo.second.getId() << __E__;
4062  xmldoc.addTextElementToData("FEMacros", oneInterface);
4063  // xmldoc.outputXmlDocument(0,true);
4064  }
4065  }
4066 
4067  // add macros to response
4068  std::pair<std::vector<std::string> /*public macros*/,
4069  std::vector<std::string> /*private macros*/>
4070  macroNames;
4071  loadMacroNames(username, macroNames);
4072 
4073  __SUP_COUT__ << "Public macro count: " << macroNames.first.size() << __E__;
4074  __SUP_COUT__ << "Private macro count: " << macroNames.second.size() << __E__;
4075 
4076  std::string macroString;
4077  // make xml ':' separated fields:
4078  // macro name
4079  // permissions string
4080  // number of inputs
4081  // inputs separated by :
4082  // number of outputs
4083  // outputs separated by :
4084 
4085  for(int i = 0; i < 2; ++i) // first is public, then private
4086  for(auto& macroName : (i ? macroNames.second : macroNames.first))
4087  {
4088  // get macro string
4089  loadMacro(macroName, macroString, username);
4090 
4091  // extract macro object
4092  FEVInterface::macroStruct_t macro(macroString);
4093 
4094  std::stringstream xmlMacroStream;
4095  xmlMacroStream << macro.macroName_;
4096  xmlMacroStream << ":"
4097  << "1"; // permissions string
4098  xmlMacroStream << ":" << macro.namesOfInputArguments_.size();
4099  for(auto& inputArg : macro.namesOfInputArguments_)
4100  xmlMacroStream << ":" << inputArg;
4101  xmlMacroStream << ":" << macro.namesOfOutputArguments_.size();
4102  for(auto& inputArg : macro.namesOfOutputArguments_)
4103  xmlMacroStream << ":" << inputArg;
4104 
4105  xmldoc.addTextElementToData(i ? "PrivateMacro" : "PublicMacro",
4106  xmlMacroStream.str());
4107  }
4108 } //end getFEMacroList()
static std::string postData(cgicc::Cgicc &cgi, const std::string &needle)
static std::string getOrPostData(cgicc::Cgicc &cgi, const std::string &needle)
static std::string getData(cgicc::Cgicc &cgi, const std::string &needle)
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
void loadTableGroup(const std::string &tableGroupName, const TableGroupKey &tableGroupKey, bool doActivate=false, std::map< std::string, TableVersion > *groupMembers=0, ProgressBar *progressBar=0, std::string *accumulateWarnings=0, std::string *groupComment=0, std::string *groupAuthor=0, std::string *groupCreateTime=0, bool doNotLoadMember=false, std::string *groupTypeString=0, std::map< std::string, std::string > *groupAliases=0, ConfigurationManager::LoadGroupType groupTypeToLoad=ConfigurationManager::LoadGroupType::ALL_TYPES, bool ignoreVersionTracking=false, std::map< std::string, TableVersion > mergeInTables={}, std::map< std::string, TableVersion > overrideTables={})
ConfigurationTree getNode(const std::string &nodeName, bool doNotThrowOnBrokenUIDLinks=false) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
void getRequestUserInfo(WebUsers::RequestUserInfo &requestUserInfo)
friend friend class MacroMakerSupervisor
virtual void nonXmlRequest(const std::string &requestType, cgicc::Cgicc &cgiIn, std::ostream &out, const WebUsers::RequestUserInfo &userInfo)
void copyDataChildren(HttpXmlDocument &document)
std::string getMatchingValue(const std::string &field, const unsigned int occurance=0)
void outputXmlDocument(std::ostringstream *out, bool dispStdOut=false, bool allowWhiteSpace=false, bool printErrors=false)
virtual void forceSupervisorPropertyValues(void) override
override to force supervisor property values (and ignore user settings)
void addParameter(const std::string name, const std::string value)
static void tooltipSetNeverShowForUsername(const std::string &username, HttpXmlDocument *xmldoc, const std::string &srcFile, const std::string &srcFunc, const std::string &srcId, bool doNeverShow, bool temporarySilence)
static void tooltipCheckForUsername(const std::string &username, HttpXmlDocument *xmldoc, const std::string &srcFile, const std::string &srcFunc, const std::string &srcId)
xercesc::DOMElement * addTextElementToParent(const std::string &childName, const std::string &childText, xercesc::DOMElement *parent)
void INIT_MF(const char *name)
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static void getSetFromString(const std::string &inputString, std::set< std::string > &setToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
static std::string decodeURIComponent(const std::string &data)