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