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