otsdaq  3.10.00
FESupervisor.cc
1 #include "otsdaq/CoreSupervisors/FESupervisor.h"
2 #include "otsdaq/ConfigurationInterface/ConfigurationManager.h"
3 #include "otsdaq/FECore/FEVInterfacesManager.h"
4 #include "otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.h"
5 
6 #include "artdaq/DAQdata/Globals.hh" // instantiates artdaq::Globals::metricMan_
7 
8 #include <unistd.h> //DIAG: for gettid() in FE macro latency investigation
9 #include <cstring>
10 #include <iostream>
11 #include <string>
12 
13 #include "artdaq-core/Utilities/ExceptionHandler.hh" /*for artdaq::ExceptionHandler*/
14 
37 using namespace ots;
38 
39 XDAQ_INSTANTIATOR_IMPL(FESupervisor)
40 
41 //==============================================================================
42 FESupervisor::FESupervisor(xdaq::ApplicationStub* stub)
43  : CoreSupervisorBase(stub)
44  , dp_context_{1} // 1 I/O thread – sufficient for most tests
45  , dp_socket_{dp_context_, zmq::socket_type::pub}
46 {
47  __SUP_COUT__ << "Constructing..." << __E__;
48 
49  xoap::bind(this,
50  &FESupervisor::macroMakerSupervisorRequest,
51  "MacroMakerSupervisorRequest",
52  XDAQ_NS_URI);
53 
54  xoap::bind(
55  this, &FESupervisor::workLoopStatusRequest, "WorkLoopStatusRequest", XDAQ_NS_URI);
56 
57  xoap::bind(this,
58  &FESupervisor::frontEndCommunicationRequest,
59  "FECommunication",
60  XDAQ_NS_URI);
61 
62  std::string dataPublishingEndpoint;
63  try
64  {
65  dataPublishingEndpoint = getSupervisorProperty(
66  "data_publishing_endpoint"); //like "tcp://0.0.0.0:$((OTS_MAIN_PORT-2))"
67  }
68  catch(const std::runtime_error& e)
69  {
70  __SUP_COUT__ << "Data publishing property 'data_publishing_endpoint' not set. "
71  "Defaulting to data publishing disabled."
72  << __E__;
73 
74  try
75  {
76  dataPublishingEndpoint = __ENV__(
77  "DATA_PUBLISHING_ENDPOINT"); //like "tcp://0.0.0.0:$((OTS_MAIN_PORT-2))"
78  }
79  catch(const std::runtime_error& e)
80  {
81  __SUP_COUT__
82  << "Data publishing environment variable 'DATA_PUBLISHING_ENDPOINT' "
83  "not set. Defaulting to data publishing disabled."
84  << __E__;
85  }
86  }
87  __SUP_COUT_INFO__ << "dataPublishingEndpoint = " << dataPublishingEndpoint << __E__;
88 
89  if(dataPublishingEndpoint.size())
90  {
91  __SUP_COUT__ << "Initializing data publishing to endpoint '"
92  << dataPublishingEndpoint << "'..." << __E__;
93 
94  initDataPublishing(dataPublishingEndpoint,
95  getSupervisorProperty("data_publishing_topic", ""));
96  }
97 
98  try
99  {
100  CoreSupervisorBase::theStateMachineImplementation_.push_back(
102  CorePropertySupervisorBase::getContextTreeNode(),
103  CorePropertySupervisorBase::getSupervisorConfigurationPath()));
104  }
105  catch(...)
106  {
107  if(CorePropertySupervisorBase::allSupervisorInfo_.isMacroMakerMode())
108  {
109  __SUP_COUT_WARN__ << "Error caught constructing FE Interface Manager. In "
110  "Macro Maker mode, the input fhicl defines the "
111  "configuration tree, make sure you specified a valid "
112  "fcl file path."
113  << __E__;
114  try
115  {
116  throw;
117  }
118  catch(const std::runtime_error& e)
119  {
120  __SUP_COUT_WARN__ << e.what() << __E__;
121  throw;
122  }
123  }
124  throw;
125  }
126 
127  extractFEInterfacesManager();
128 
129  __SUP_COUT__ << "Constructed." << __E__;
130 
131  if(CorePropertySupervisorBase::allSupervisorInfo_.isMacroMakerMode())
132  {
133  __SUP_COUT_INFO__ << "Macro Maker mode, so configuring at startup!" << __E__;
134  if(!theFEInterfacesManager_)
135  {
136  __SUP_SS__ << "Missing FE Interface manager!" << __E__;
137  __SUP_SS_THROW__;
138  }
139 
140  // copied from CoreSupervisorBase::transitionConfiguring()
141 
142  // Now that the configuration manager has all the necessary configurations,
143  // create all objects that depend on the configuration (the first iteration)
144 
145  try
146  {
147  __SUP_COUT__ << "Configuring all state machine implementations..." << __E__;
148  __SUP_COUTV__(stateMachinesIterationDone_.size());
149  preStateMachineExecutionLoop();
150  __SUP_COUTV__(stateMachinesIterationDone_.size());
151  for(unsigned int i = 0; i < theStateMachineImplementation_.size(); ++i)
152  {
153  __SUP_COUT__ << "Configuring state machine i " << i << __E__;
154 
155  // if one state machine is doing a sub-iteration, then target that one
156  if(subIterationWorkStateMachineIndex_ != (unsigned int)-1 &&
157  i != subIterationWorkStateMachineIndex_)
158  continue; // skip those not in the sub-iteration
159 
160  if(stateMachinesIterationDone_[i])
161  continue; // skip state machines already done
162 
163  preStateMachineExecution(i);
164  theStateMachineImplementation_[i]->parentSupervisor_ =
165  this; // for backwards compatibility, kept out of configure
166  // parameters
167  theStateMachineImplementation_[i]->configure(); // e.g. for FESupervisor,
168  // this is configure of
169  // FEVInterfacesManager
170  postStateMachineExecution(i);
171  }
172  postStateMachineExecutionLoop();
173 
174  //ony indicate alive after configure
175  CorePropertySupervisorBase::indicateOtsAlive(0);
176  }
177  catch(const std::runtime_error& e)
178  {
179  __SUP_SS__ << "Error was caught while configuring: " << e.what() << __E__;
180  __SUP_COUT_ERR__ << "\n" << ss.str();
181  throw;
182  }
183  catch(...)
184  {
185  __SUP_SS__
186  << "Unknown error was caught while configuring. Please checked the logs."
187  << __E__;
188  try
189  {
190  throw;
191  } //one more try to printout extra info
192  catch(const std::exception& e)
193  {
194  ss << "Exception message: " << e.what();
195  }
196  catch(...)
197  {
198  }
199  __SUP_COUT_ERR__ << "\n" << ss.str();
200  throw;
201  }
202  } // end Macro Maker mode initial configure
203 } // end constructor
204 
205 //==============================================================================
206 FESupervisor::~FESupervisor(void)
207 {
208  __SUP_COUT__ << "Destroying..." << __E__;
209  // theStateMachineImplementation_ is reset and the object it points to deleted in
210  // ~CoreSupervisorBase()
211 
212  // Destructor must never throw, so we swallow any zmq_error.
213  try
214  {
216  }
217  catch(...)
218  {
219  // nothing – best‑effort cleanup.
220  }
221 
222  artdaq::Globals::CleanUpGlobals(); // destruct metricManager (among other things)
223 
224  __SUP_COUT__ << "Destructed." << __E__;
225 } // end destructor
226 
227 //==============================================================================
228 xoap::MessageReference FESupervisor::frontEndCommunicationRequest(
229  xoap::MessageReference message)
230 try
231 {
232  __SUP_COUT_INFO__ << "DIAG ms=" << StringMacros::nowEpochMs() << " tid=" << gettid()
233  << " frontEndCommunicationRequest received: "
234  << SOAPUtilities::translate(message) << __E__;
235 
236  if(!theFEInterfacesManager_)
237  {
238  __SUP_SS__ << "No FE Interface Manager!" << __E__;
239  __SUP_SS_THROW__;
240  }
241  SOAPParameters typeParameter, rxParameters; // params for xoap to recv
242  typeParameter.addParameter("type");
243  SOAPUtilities::receive(message, typeParameter);
244 
245  std::string type = typeParameter.getValue("type");
246 
247  // types
248  // feSend
249  // feMacro
250  // feMacroMultiDimensionalStart
251  // macroMultiDimensionalStart
252  // feMacroMultiDimensionalCheck
253  // macroMultiDimensionalCheck
254 
255  rxParameters.addParameter("requester");
256  rxParameters.addParameter("targetInterfaceID");
257 
258  if(type == "feSend")
259  {
260  __SUP_COUTV__(type);
261 
262  rxParameters.addParameter("value");
263  SOAPUtilities::receive(message, rxParameters);
264 
265  std::string requester = rxParameters.getValue("requester");
266  std::string targetInterfaceID = rxParameters.getValue("targetInterfaceID");
267  std::string value = rxParameters.getValue("value");
268 
269  __SUP_COUTV__(requester);
270  __SUP_COUTV__(targetInterfaceID);
271  __SUP_COUTV__(value);
272 
273  // test that the interface exists
274  theFEInterfacesManager_->getFEInterface(targetInterfaceID);
275 
276  // mutex scope
277  {
278  std::lock_guard<std::mutex> lock(
279  theFEInterfacesManager_->frontEndCommunicationReceiveMutex_);
280 
281  theFEInterfacesManager_
282  ->frontEndCommunicationReceiveBuffer_[targetInterfaceID][requester]
283  .emplace(value);
284 
285  __SUP_COUT__ << "Number of target interface ID '" << targetInterfaceID
286  << "' buffers: "
287  << theFEInterfacesManager_
288  ->frontEndCommunicationReceiveBuffer_[targetInterfaceID]
289  .size()
290  << __E__;
291  __SUP_COUT__
292  << "Number of source interface ID '" << requester << "' values received: "
293  << theFEInterfacesManager_
294  ->frontEndCommunicationReceiveBuffer_[targetInterfaceID][requester]
295  .size()
296  << __E__;
297  }
298  return SOAPUtilities::makeSOAPMessageReference("Received");
299  } // end type feSend
300  else if(type == "feMacro")
301  {
302  __SUP_COUTV__(type);
303 
304  rxParameters.addParameter("feMacroName");
305  rxParameters.addParameter("inputArgs");
306 
307  SOAPUtilities::receive(message, rxParameters);
308 
309  std::string requester = rxParameters.getValue("requester");
310  std::string targetInterfaceID = rxParameters.getValue("targetInterfaceID");
311  std::string feMacroName = rxParameters.getValue("feMacroName");
312  std::string inputArgs = rxParameters.getValue("inputArgs");
313 
314  __SUP_COUTV__(requester);
315  __SUP_COUTV__(targetInterfaceID);
316  __SUP_COUTV__(feMacroName);
317  __SUP_COUTV__(inputArgs);
318 
319  std::string outputArgs;
320  try
321  {
322  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
323  << " runFEMacroByFE starting, macro='" << feMacroName
324  << "' target='" << targetInterfaceID << "'" << __E__;
325  theFEInterfacesManager_->runFEMacroByFE(
326  requester, targetInterfaceID, feMacroName, inputArgs, outputArgs);
327  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
328  << " runFEMacroByFE done, macro='" << feMacroName << "'"
329  << __E__;
330  }
331  catch(std::runtime_error& e)
332  {
333  __SUP_SS__ << "In Supervisor with LID="
334  << getApplicationDescriptor()->getLocalId()
335  << " the FE Macro named '" << feMacroName << "' with target FE '"
336  << targetInterfaceID << "' failed. Here is the error:\n\n"
337  << e.what() << __E__;
338  __SUP_SS_THROW__;
339  }
340  catch(...)
341  {
342  __SUP_SS__ << "In Supervisor with LID="
343  << getApplicationDescriptor()->getLocalId()
344  << " the FE Macro named '" << feMacroName << "' with target FE '"
345  << targetInterfaceID << "' failed due to an unknown error."
346  << __E__;
347  try
348  {
349  throw;
350  } //one more try to printout extra info
351  catch(const std::exception& e)
352  {
353  ss << "Exception message: " << e.what();
354  }
355  catch(...)
356  {
357  }
358  __SUP_SS_THROW__;
359  }
360 
361  __SUP_COUTV__(outputArgs);
362 
363  xoap::MessageReference replyMessage =
364  SOAPUtilities::makeSOAPMessageReference("feMacrosResponse");
365  SOAPParameters txParameters;
366  txParameters.addParameter("requester", requester);
367  txParameters.addParameter("targetInterfaceID", targetInterfaceID);
368  txParameters.addParameter("feMacroName", feMacroName);
369  txParameters.addParameter("outputArgs", outputArgs);
370  SOAPUtilities::addParameters(replyMessage, txParameters);
371 
372  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
373  << " Sending FE macro result: "
374  << SOAPUtilities::translate(replyMessage) << __E__;
375 
376  return replyMessage;
377  } // end type feMacro
378  else if(type == "feMacroMultiDimensionalStart" || // from iterator
379  type == "macroMultiDimensionalStart") // from iterator
380  {
381  __SUP_COUTV__(type);
382 
383  if(type[0] == 'm')
384  {
385  rxParameters.addParameter("macroString");
386  rxParameters.addParameter("macroName");
387  }
388  else
389  rxParameters.addParameter("feMacroName");
390 
391  rxParameters.addParameter("enableSavingOutput");
392  rxParameters.addParameter("outputFilePath");
393  rxParameters.addParameter("outputFileRadix");
394  rxParameters.addParameter("inputArgs");
395 
396  SOAPUtilities::receive(message, rxParameters);
397 
398  std::string requester = rxParameters.getValue("requester");
399  std::string targetInterfaceID = rxParameters.getValue("targetInterfaceID");
400  std::string macroName, macroString;
401  if(type[0] == 'm')
402  {
403  macroName = rxParameters.getValue("macroName");
404  macroString = rxParameters.getValue("macroString");
405  __SUP_COUTV__(macroString);
406  }
407  else
408  macroName = rxParameters.getValue("feMacroName");
409  bool enableSavingOutput = rxParameters.getValue("enableSavingOutput") == "1";
410  std::string outputFilePath = rxParameters.getValue("outputFilePath");
411  std::string outputFileRadix = rxParameters.getValue("outputFileRadix");
412  std::string inputArgs = rxParameters.getValue("inputArgs");
413 
414  __SUP_COUTV__(requester);
415  __SUP_COUTV__(targetInterfaceID);
416  __SUP_COUTV__(macroName);
417  __SUP_COUTV__(enableSavingOutput);
418  __SUP_COUTV__(outputFilePath);
419  __SUP_COUTV__(outputFileRadix);
420  __SUP_COUTV__(inputArgs);
421 
422  if(type[0] == 'm') // start Macro
423  {
424  try
425  {
426  theFEInterfacesManager_->startMacroMultiDimensional(requester,
427  targetInterfaceID,
428  macroName,
429  macroString,
430  enableSavingOutput,
431  outputFilePath,
432  outputFileRadix,
433  inputArgs);
434  }
435  catch(std::runtime_error& e)
436  {
437  __SUP_SS__ << "In Supervisor with LID="
438  << getApplicationDescriptor()->getLocalId()
439  << " the Macro named '" << macroName << "' with target FE '"
440  << targetInterfaceID
441  << "' failed to start multi-dimensional launch. "
442  << "Here is the error:\n\n"
443  << e.what() << __E__;
444  __SUP_SS_THROW__;
445  }
446  catch(...)
447  {
448  __SUP_SS__ << "In Supervisor with LID="
449  << getApplicationDescriptor()->getLocalId()
450  << " the Macro named '" << macroName << "' with target FE '"
451  << targetInterfaceID
452  << "' failed to start multi-dimensional launch "
453  << "due to an unknown error." << __E__;
454  try
455  {
456  throw;
457  } //one more try to printout extra info
458  catch(const std::exception& e)
459  {
460  ss << "Exception message: " << e.what();
461  }
462  catch(...)
463  {
464  }
465  __SUP_SS_THROW__;
466  }
467  }
468  else // start FE Macro
469  {
470  try
471  {
472  theFEInterfacesManager_->startFEMacroMultiDimensional(requester,
473  targetInterfaceID,
474  macroName,
475  enableSavingOutput,
476  outputFilePath,
477  outputFileRadix,
478  inputArgs);
479  }
480  catch(std::runtime_error& e)
481  {
482  __SUP_SS__ << "In Supervisor with LID="
483  << getApplicationDescriptor()->getLocalId()
484  << " the FE Macro named '" << macroName << "' with target FE '"
485  << targetInterfaceID
486  << "' failed to start multi-dimensional launch. "
487  << "Here is the error:\n\n"
488  << e.what() << __E__;
489  __SUP_SS_THROW__;
490  }
491  catch(...)
492  {
493  __SUP_SS__ << "In Supervisor with LID="
494  << getApplicationDescriptor()->getLocalId()
495  << " the FE Macro named '" << macroName << "' with target FE '"
496  << targetInterfaceID
497  << "' failed to start multi-dimensional launch "
498  << "due to an unknown error." << __E__;
499  try
500  {
501  throw;
502  } //one more try to printout extra info
503  catch(const std::exception& e)
504  {
505  ss << "Exception message: " << e.what();
506  }
507  catch(...)
508  {
509  }
510  __SUP_SS_THROW__;
511  }
512  }
513 
514  xoap::MessageReference replyMessage =
515  SOAPUtilities::makeSOAPMessageReference(type + "Done");
516  SOAPParameters txParameters;
517  // txParameters.addParameter("started", "1");
518  SOAPUtilities::addParameters(replyMessage, txParameters);
519 
520  __SUP_COUT__ << "Sending FE macro result: "
521  << SOAPUtilities::translate(replyMessage) << __E__;
522 
523  return replyMessage;
524  } // end type (fe)MacroMultiDimensionalStart
525  else if(type == "feMacroMultiDimensionalCheck" || // from iterator
526  type == "macroMultiDimensionalCheck")
527  {
528  // LORE__SUP_COUTV__(type);
529  if(type[0] == 'm')
530  rxParameters.addParameter("macroName");
531  else
532  rxParameters.addParameter("feMacroName");
533  rxParameters.addParameter("targetInterfaceID");
534 
535  SOAPUtilities::receive(message, rxParameters);
536 
537  std::string targetInterfaceID = rxParameters.getValue("targetInterfaceID");
538  std::string macroName;
539  if(type[0] == 'm')
540  macroName = rxParameters.getValue("macroName");
541  else
542  macroName = rxParameters.getValue("feMacroName");
543 
544  // LORE__SUP_COUTV__(targetInterfaceID);
545  // LORE__SUP_COUTV__(macroName);
546 
547  bool done = false;
548  try
549  {
550  done = theFEInterfacesManager_->checkMacroMultiDimensional(targetInterfaceID,
551  macroName);
552  }
553  catch(std::runtime_error& e)
554  {
555  __SUP_SS__ << "In Supervisor with LID="
556  << getApplicationDescriptor()->getLocalId()
557  << " the FE Macro named '" << macroName << "' with target FE '"
558  << targetInterfaceID
559  << "' failed to check multi-dimensional launch. "
560  << "Here is the error:\n\n"
561  << e.what() << __E__;
562  __SUP_SS_THROW__;
563  }
564  catch(...)
565  {
566  __SUP_SS__ << "In Supervisor with LID="
567  << getApplicationDescriptor()->getLocalId()
568  << " the FE Macro named '" << macroName << "' with target FE '"
569  << targetInterfaceID
570  << "' failed to check multi-dimensional launch "
571  << "due to an unknown error." << __E__;
572  try
573  {
574  throw;
575  } //one more try to printout extra info
576  catch(const std::exception& e)
577  {
578  ss << "Exception message: " << e.what();
579  }
580  catch(...)
581  {
582  }
583  __SUP_SS_THROW__;
584  }
585 
586  xoap::MessageReference replyMessage =
587  SOAPUtilities::makeSOAPMessageReference(type + "Done");
588  SOAPParameters txParameters;
589  txParameters.addParameter("Done", done ? "1" : "0");
590  SOAPUtilities::addParameters(replyMessage, txParameters);
591 
592  // LORE__SUP_COUT__ << "Sending FE macro result: " << SOAPUtilities::translate(replyMessage) << __E__;
593 
594  return replyMessage;
595  } // end type (fe)MacroMultiDimensionalCheck
596  else
597  {
598  __SUP_SS__ << "Unrecognized FE Communication type: " << type << __E__;
599  __SUP_SS_THROW__;
600  }
601 }
602 catch(const std::runtime_error& e)
603 {
604  __SUP_SS__ << "Error encountered processing FE communication request: " << e.what()
605  << __E__;
606  __SUP_COUT_ERR__ << ss.str();
607 
608  SOAPParameters parameters;
609  parameters.addParameter("Error", ss.str());
610  return SOAPUtilities::makeSOAPMessageReference(
611  supervisorClassNoNamespace_ + "FailFECommunicationRequest", parameters);
612 }
613 catch(...)
614 {
615  __SUP_SS__ << "Unknown error encountered processing FE communication request."
616  << __E__;
617  try
618  {
619  throw;
620  } //one more try to printout extra info
621  catch(const std::exception& e)
622  {
623  ss << "Exception message: " << e.what();
624  }
625  catch(...)
626  {
627  }
628  __SUP_COUT_ERR__ << ss.str();
629 
630  SOAPParameters parameters;
631  parameters.addParameter("Error", ss.str());
632  return SOAPUtilities::makeSOAPMessageReference(
633  supervisorClassNoNamespace_ + "FailFECommunicationRequest", parameters);
634 } // end frontEndCommunicationRequest()
635 
636 //==============================================================================
644  xoap::MessageReference message)
645 {
646  __SUP_COUT__ << "$$$$$$$$$$$$$$$$$" << __E__;
647 
648  // receive request parameters
649  SOAPParameters parameters;
650  parameters.addParameter("Request");
651 
652  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs() << " tid=" << gettid()
653  << " Received Macro Maker message: " << SOAPUtilities::translate(message)
654  << __E__;
655 
656  SOAPUtilities::receive(message, parameters);
657  std::string request = parameters.getValue("Request");
658 
659  __SUP_COUT__ << "request: " << request << __E__;
660 
661  // request types:
662  // GetInterfaces
663  // UniversalWrite
664  // UniversalRead
665  // GetInterfaceMacros
666  // RunInterfaceMacro
667  // RunMacroMakerMacro
668 
669  SOAPParameters retParameters;
670 
671  try
672  {
673  if(request == "GetInterfaces")
674  {
675  if(theFEInterfacesManager_)
676  retParameters.addParameter(
677  "FEList",
678  theFEInterfacesManager_->getFEListString(
679  std::to_string(getApplicationDescriptor()->getLocalId())));
680  else // if no FE interfaces, return empty string
681  retParameters.addParameter("FEList", "");
682 
683  // if errors in state machine, send also
684  if(theStateMachine_.getErrorMessage() != "")
685  retParameters.addParameter("frontEndError",
686  theStateMachine_.getErrorMessage());
687 
688  return SOAPUtilities::makeSOAPMessageReference(
689  supervisorClassNoNamespace_ + "Response", retParameters);
690  }
691  else if(request == "UniversalWrite")
692  {
693  if(!theFEInterfacesManager_)
694  {
695  __SUP_SS__ << "No FE Interface Manager! Are you configured?" << __E__;
696  __SUP_SS_THROW__;
697  }
698  // params for running macros
699  SOAPParameters requestParameters;
700  requestParameters.addParameter("InterfaceID");
701  requestParameters.addParameter("Address");
702  requestParameters.addParameter("Data");
703  SOAPUtilities::receive(message, requestParameters);
704  std::string interfaceID = requestParameters.getValue("InterfaceID");
705  std::string addressStr = requestParameters.getValue("Address");
706  std::string dataStr = requestParameters.getValue("Data");
707 
708  __SUP_COUT__ << "Address: " << addressStr << " Data: " << dataStr
709  << " InterfaceID: " << interfaceID << __E__;
710 
711  // parameters interface index!
712  // unsigned int index = stoi(indexStr); // As long as the supervisor has only
713  // one interface, this index will remain 0?
714 
715  __SUP_COUT__
716  << "theFEInterfacesManager_->getInterfaceUniversalAddressSize(index) "
717  << theFEInterfacesManager_->getInterfaceUniversalAddressSize(interfaceID)
718  << __E__;
719  __SUP_COUT__
720  << "theFEInterfacesManager_->getInterfaceUniversalDataSize(index) "
721  << theFEInterfacesManager_->getInterfaceUniversalDataSize(interfaceID)
722  << __E__;
723 
724  // Converting std::string to char*
725  // char address
726 
727  char tmpHex[3]; // for use converting hex to binary
728  tmpHex[2] = '\0';
729 
730  __SUP_COUT__ << "Translating address: ";
731 
732  std::string addressTmp;
733  addressTmp.reserve(
734  theFEInterfacesManager_->getInterfaceUniversalAddressSize(interfaceID));
735  char* address = &addressTmp[0];
736 
737  if(addressStr.size() % 2) // if odd, make even
738  addressStr = "0" + addressStr;
739  unsigned int i = 0;
740  for(; i < addressStr.size() &&
741  i / 2 < theFEInterfacesManager_->getInterfaceUniversalAddressSize(
742  interfaceID);
743  i += 2)
744  {
745  tmpHex[0] = addressStr[addressStr.size() - 1 - i - 1];
746  tmpHex[1] = addressStr[addressStr.size() - 1 - i];
747  sscanf(tmpHex, "%hhX", (unsigned char*)&address[i / 2]);
748  printf("%2.2X", (unsigned char)address[i / 2]);
749  }
750  // finish and fill with 0s
751  for(; i / 2 <
752  theFEInterfacesManager_->getInterfaceUniversalAddressSize(interfaceID);
753  i += 2)
754  {
755  address[i / 2] = 0;
756  printf("%2.2X", (unsigned char)address[i / 2]);
757  }
758 
759  std::cout << __E__;
760 
761  __SUP_COUT__ << "Translating data: ";
762 
763  std::string dataTmp;
764  dataTmp.reserve(
765  theFEInterfacesManager_->getInterfaceUniversalDataSize(interfaceID));
766  char* data = &dataTmp[0];
767 
768  if(dataStr.size() % 2) // if odd, make even
769  dataStr = "0" + dataStr;
770 
771  i = 0;
772  for(; i < dataStr.size() &&
773  i / 2 <
774  theFEInterfacesManager_->getInterfaceUniversalDataSize(interfaceID);
775  i += 2)
776  {
777  tmpHex[0] = dataStr[dataStr.size() - 1 - i - 1];
778  tmpHex[1] = dataStr[dataStr.size() - 1 - i];
779  sscanf(tmpHex, "%hhX", (unsigned char*)&data[i / 2]);
780  printf("%2.2X", (unsigned char)data[i / 2]);
781  }
782  // finish and fill with 0s
783  for(; i / 2 <
784  theFEInterfacesManager_->getInterfaceUniversalDataSize(interfaceID);
785  i += 2)
786  {
787  data[i / 2] = 0;
788  printf("%2.2X", (unsigned char)data[i / 2]);
789  }
790 
791  std::cout << __E__;
792 
793  // char* address = new char[addressStr.size() + 1];
794  // std::copy(addressStr.begin(), addressStr.end(), address);
795  // address[addressStr.size()] = '\0';
796  // char* data = new char[dataStr.size() + 1];
797  // std::copy(dataStr.begin(), dataStr.end(), data);
798  // data[dataStr.size()] = '\0';
799 
800  theFEInterfacesManager_->universalWrite(interfaceID, address, data);
801 
802  // delete[] address;
803  // delete[] data;
804 
805  return SOAPUtilities::makeSOAPMessageReference(
806  supervisorClassNoNamespace_ + "DataWritten", retParameters);
807  }
808  else if(request == "UniversalRead")
809  {
810  if(!theFEInterfacesManager_)
811  {
812  __SUP_SS__ << "No FE Interface Manager! Are you configured?" << __E__;
813  __SUP_SS_THROW__;
814  }
815 
816  // params for running macros
817  SOAPParameters requestParameters;
818  requestParameters.addParameter("InterfaceID");
819  requestParameters.addParameter("Address");
820  SOAPUtilities::receive(message, requestParameters);
821  std::string interfaceID = requestParameters.getValue("InterfaceID");
822  std::string addressStr = requestParameters.getValue("Address");
823 
824  __SUP_COUT__ << "Address: " << addressStr << " InterfaceID: " << interfaceID
825  << __E__;
826 
827  // parameters interface index!
828  // parameter address and data
829  // unsigned int index = stoi(indexStr); // As long as the supervisor has only
830  // one interface, this index will remain 0?
831 
832  __SUP_COUT__
833  << "theFEInterfacesManager_->getInterfaceUniversalAddressSize(index) "
834  << theFEInterfacesManager_->getInterfaceUniversalAddressSize(interfaceID)
835  << __E__;
836  __SUP_COUT__
837  << "theFEInterfacesManager_->getInterfaceUniversalDataSize(index) "
838  << theFEInterfacesManager_->getInterfaceUniversalDataSize(interfaceID)
839  << __E__;
840 
841  char tmpHex[3]; // for use converting hex to binary
842  tmpHex[2] = '\0';
843 
844  __SUP_COUT__ << "Translating address: ";
845 
846  std::string addressTmp;
847  addressTmp.reserve(
848  theFEInterfacesManager_->getInterfaceUniversalAddressSize(interfaceID));
849  char* address = &addressTmp[0];
850 
851  if(addressStr.size() % 2) // if odd, make even
852  addressStr = "0" + addressStr;
853 
854  unsigned int i = 0;
855  for(; i < addressStr.size() &&
856  i / 2 < theFEInterfacesManager_->getInterfaceUniversalAddressSize(
857  interfaceID);
858  i += 2)
859  {
860  tmpHex[0] = addressStr[addressStr.size() - 1 - i - 1];
861  tmpHex[1] = addressStr[addressStr.size() - 1 - i];
862  sscanf(tmpHex, "%hhX", (unsigned char*)&address[i / 2]);
863  printf("%2.2X", (unsigned char)address[i / 2]);
864  }
865  // finish and fill with 0s
866  for(; i / 2 <
867  theFEInterfacesManager_->getInterfaceUniversalAddressSize(interfaceID);
868  i += 2)
869  {
870  address[i / 2] = 0;
871  printf("%2.2X", (unsigned char)address[i / 2]);
872  }
873 
874  std::cout << __E__;
875 
876  unsigned int dataSz =
877  theFEInterfacesManager_->getInterfaceUniversalDataSize(interfaceID);
878  std::string dataStr;
879  dataStr.resize(dataSz);
880  char* data = &dataStr[0];
881 
882  // std::string result =
883  // theFEInterfacesManager_->universalRead(index,address,data);
884  // __SUP_COUT__<< result << __E__ << __E__;
885 
886  try
887  {
888  theFEInterfacesManager_->universalRead(interfaceID, address, data);
889  }
890  catch(const std::runtime_error& e)
891  {
892  // do not allow read exception to crash everything when a macromaker
893  // command
894  __SUP_COUT_ERR__ << "Exception caught during read: " << e.what() << __E__;
895  retParameters.addParameter("dataResult", "Time Out Error");
896  return SOAPUtilities::makeSOAPMessageReference(
897  supervisorClassNoNamespace_ + "aa", retParameters);
898  }
899  catch(...)
900  {
901  // do not allow read exception to crash everything when a macromaker
902  // command
903  __SUP_COUT_ERR__ << "Exception caught during read." << __E__;
904  retParameters.addParameter("dataResult", "Time Out Error");
905  return SOAPUtilities::makeSOAPMessageReference(
906  supervisorClassNoNamespace_ + "aa", retParameters);
907  }
908 
909  // if dataSz is less than 8 show what the unsigned number would be
910  if(dataSz <= 8)
911  {
912  std::string str8(data);
913  str8.resize(8);
914  __SUP_COUT__ << "decResult[" << dataSz
915  << " bytes]: " << *((unsigned long long*)(&str8[0]))
916  << __E__;
917  }
918 
919  std::string hexResultStr;
920  hexResultStr.reserve(dataSz * 2 + 1);
921  char* hexResult = &hexResultStr[0];
922  // go through each byte and convert it to hex value (i.e. 2 0-F chars)
923  // go backwards through source data since should be provided in host order
924  // (i.e. a cast to unsigned long long should acquire expected value)
925  for(unsigned int i = 0; i < dataSz; ++i)
926  {
927  sprintf(&hexResult[i * 2], "%2.2X", (unsigned char)data[dataSz - 1 - i]);
928  }
929 
930  __SUP_COUT__ << "hexResult[" << strlen(hexResult)
931  << " nibbles]: " << std::string(hexResult) << __E__;
932 
933  retParameters.addParameter("dataResult", hexResult);
934  return SOAPUtilities::makeSOAPMessageReference(
935  supervisorClassNoNamespace_ + "aa", retParameters);
936  }
937  else if(request == "GetInterfaceMacros")
938  {
939  if(theFEInterfacesManager_)
940  {
941  __SUP_COUT__ << "Getting FE Macros from FE Interface Manager..." << __E__;
942  retParameters.addParameter(
943  "FEMacros",
944  theFEInterfacesManager_->getFEMacrosString(
945  CorePropertySupervisorBase::getSupervisorUID(),
946  std::to_string(CoreSupervisorBase::getSupervisorLID())));
947  }
948  else
949  {
950  __SUP_COUT__ << "No FE Macros because there is no FE Interface Manager..."
951  << __E__;
952  retParameters.addParameter("FEMacros", "");
953  }
954 
955  return SOAPUtilities::makeSOAPMessageReference(
956  supervisorClassNoNamespace_ + "Response", retParameters);
957  }
958  else if(request == "RunInterfaceMacro")
959  {
960  if(!theFEInterfacesManager_)
961  {
962  __SUP_SS__ << "Missing FE Interface Manager! Are you configured?"
963  << __E__;
964  __SUP_SS_THROW__;
965  }
966 
967  // params for running macros
968  SOAPParameters requestParameters;
969  requestParameters.addParameter("feMacroName");
970  requestParameters.addParameter("inputArgs");
971  requestParameters.addParameter("outputArgs");
972  requestParameters.addParameter("InterfaceID");
973  requestParameters.addParameter("userPermissions");
974  requestParameters.addParameter("AsyncSupported");
975  SOAPUtilities::receive(message, requestParameters);
976  std::string interfaceID = requestParameters.getValue("InterfaceID");
977  std::string feMacroName = requestParameters.getValue("feMacroName");
978  std::string inputArgs = requestParameters.getValue("inputArgs");
979  std::string outputArgs = requestParameters.getValue("outputArgs");
980  std::string userPermissions = requestParameters.getValue("userPermissions");
981  bool asyncSupported = requestParameters.getValue("AsyncSupported") == "1";
982 
983  // check user permission
984  // userPermissions = "allUsers:1";
985  // userPermissions = "allUsers:1 & TDAQ:255";
986  __COUTV__(userPermissions);
987  std::map<std::string, WebUsers::permissionLevel_t> userPermissionLevelsMap;
989  userPermissions, userPermissionLevelsMap);
990  __COUTV__(StringMacros::mapToString(userPermissionLevelsMap));
991 
992  // outputArgs must be filled with the proper argument names
993  // and then the response output values will be returned in the string.
994 
995  // check for interfaceID
996  FEVInterface* fe = theFEInterfacesManager_->getFEInterfaceP(interfaceID);
997 
998  // have pointer to virtual FEInterface, find Macro structure
999  auto FEMacroIt = fe->getMapOfFEMacroFunctions().find(feMacroName);
1000  if(FEMacroIt == fe->getMapOfFEMacroFunctions().end())
1001  {
1002  __SUP_SS__ << "FE Macro '" << feMacroName << "' of interfaceID '"
1003  << interfaceID << "' was not found!" << __E__;
1004  __SUP_SS_THROW__;
1005  }
1006  const FEVInterface::frontEndMacroStruct_t& FEMacro = FEMacroIt->second;
1007 
1008  __COUTV__(FEMacro.requiredUserPermissions_);
1009  std::map<std::string, WebUsers::permissionLevel_t>
1010  FERequiredUserPermissionsMap;
1012  FEMacro.requiredUserPermissions_, FERequiredUserPermissionsMap);
1013  __COUTV__(StringMacros::mapToString(FERequiredUserPermissionsMap));
1014 
1016  userPermissionLevelsMap, FERequiredUserPermissionsMap))
1017  {
1018  __SUP_SS__ << "Invalid user permission for FE Macro '" << feMacroName
1019  << "' of interfaceID '" << interfaceID << "'!\n\n"
1020  << "Must have access level of at least '"
1021  << StringMacros::mapToString(FERequiredUserPermissionsMap)
1022  << ".' Users permissions level is only '" << userPermissions
1023  << ".'" << __E__;
1024  __SUP_SS_THROW__;
1025  }; // skip icon if no access
1026 
1027  if(asyncSupported)
1028  {
1029  // Async path: launch macro in a thread, return NotDoneTaskID immediately
1030  auto task = std::make_shared<AsyncMacroTask>();
1031  {
1032  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1033  task->taskID = ++asyncMacroTaskIDCounter_;
1034  if(asyncMacroTaskIDCounter_ == 0)
1035  task->taskID = ++asyncMacroTaskIDCounter_;
1036  task->interfaceID = interfaceID;
1037  task->startTime = time(0);
1038  asyncMacroTasks_.push_back(task);
1039  }
1040 
1041  FEVInterface::frontEndMacroStruct_t localFEMacro = FEMacro;
1042  std::thread([this,
1043  task,
1044  interfaceID,
1045  localFEMacro,
1046  inputArgs,
1047  outputArgs]() mutable {
1048  {
1049  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1050  task->threadID = std::this_thread::get_id();
1051  }
1052  // Clear stale progress in case thread ID was reused
1053  try
1054  {
1055  theFEInterfacesManager_->getFEInterfaceP(interfaceID)
1056  ->clearFEMacroPercentDone(std::this_thread::get_id());
1057  }
1058  catch(...)
1059  {
1060  }
1061 
1062  try
1063  {
1064  __COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
1065  << " async FE Macro thread starting runFEMacro, taskID="
1066  << task->taskID << __E__;
1067  theFEInterfacesManager_->runFEMacro(
1068  interfaceID, localFEMacro, inputArgs, outputArgs);
1069  __COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
1070  << " async FE Macro thread finished runFEMacro, taskID="
1071  << task->taskID << __E__;
1072  try
1073  {
1074  theFEInterfacesManager_->getFEInterfaceP(interfaceID)
1075  ->clearFEMacroPercentDone(std::this_thread::get_id());
1076  }
1077  catch(...)
1078  {
1079  }
1080  {
1081  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1082  task->outputArgs = outputArgs;
1083  task->doneTime = time(0);
1084  task->done = true;
1085  }
1086  }
1087  catch(const std::exception& e)
1088  {
1089  try
1090  {
1091  theFEInterfacesManager_->getFEInterfaceP(interfaceID)
1092  ->clearFEMacroPercentDone(std::this_thread::get_id());
1093  }
1094  catch(...)
1095  {
1096  }
1097  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1098  task->error =
1099  "In Supervisor with LID=" +
1100  std::to_string(getApplicationDescriptor()->getLocalId()) +
1101  " the FE Macro named '" + interfaceID +
1102  "' failed. Here is the error:\n\n" + e.what();
1103  task->doneTime = time(0);
1104  task->done = true;
1105  }
1106  catch(...)
1107  {
1108  try
1109  {
1110  theFEInterfacesManager_->getFEInterfaceP(interfaceID)
1111  ->clearFEMacroPercentDone(std::this_thread::get_id());
1112  }
1113  catch(...)
1114  {
1115  }
1116  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1117  task->error =
1118  "In Supervisor with LID=" +
1119  std::to_string(getApplicationDescriptor()->getLocalId()) +
1120  " the FE Macro named '" + interfaceID +
1121  "' failed due to an unknown error.";
1122  task->doneTime = time(0);
1123  task->done = true;
1124  }
1125  }).detach();
1126 
1127  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
1128  << " Launched async FE Macro '" << feMacroName
1129  << "' for interfaceID '" << interfaceID
1130  << "' with taskID=" << task->taskID << __E__;
1131 
1132  // Wait briefly to see if macro finishes quickly (avoids unnecessary polling)
1133  {
1134  size_t sleepUs = 100;
1135  for(int i = 0; i < 10; ++i)
1136  {
1137  usleep(sleepUs);
1138  {
1139  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1140  if(task->done)
1141  {
1142  if(!task->error.empty())
1143  {
1144  std::string errorCopy = task->error;
1145  for(size_t j = 0; j < asyncMacroTasks_.size(); ++j)
1146  if(asyncMacroTasks_[j]->taskID == task->taskID)
1147  {
1148  asyncMacroTasks_.erase(
1149  asyncMacroTasks_.begin() + j);
1150  break;
1151  }
1152  __SUP_SS__ << errorCopy;
1153  __SUP_SS_THROW__;
1154  }
1155  __SUP_COUT__
1156  << "DIAG ms=" << StringMacros::nowEpochMs()
1157  << " async macro finished within quick-wait,"
1158  << " returning synchronously, taskID=" << task->taskID
1159  << __E__;
1160  retParameters.addParameter("outputArgs",
1161  task->outputArgs);
1162  for(size_t j = 0; j < asyncMacroTasks_.size(); ++j)
1163  if(asyncMacroTasks_[j]->taskID == task->taskID)
1164  {
1165  asyncMacroTasks_.erase(asyncMacroTasks_.begin() +
1166  j);
1167  break;
1168  }
1169  return SOAPUtilities::makeSOAPMessageReference(
1170  supervisorClassNoNamespace_ + "Response",
1171  retParameters);
1172  }
1173  }
1174  sleepUs *= 2;
1175  if(sleepUs > 50000)
1176  sleepUs = 50000;
1177  }
1178  }
1179 
1180  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
1181  << " async macro NOT done within quick-wait,"
1182  << " returning NotDoneTaskID=" << task->taskID << __E__;
1183  retParameters.addParameter("NotDoneTaskID", std::to_string(task->taskID));
1184  return SOAPUtilities::makeSOAPMessageReference(
1185  supervisorClassNoNamespace_ + "Response", retParameters);
1186  }
1187  else
1188  {
1189  // Synchronous path: run macro and return result (backward compatible)
1190  try
1191  {
1192  theFEInterfacesManager_->runFEMacro(
1193  interfaceID, FEMacro, inputArgs, outputArgs);
1194  }
1195  catch(std::runtime_error& e)
1196  {
1197  __SUP_SS__ << "In Supervisor with LID="
1198  << getApplicationDescriptor()->getLocalId()
1199  << " the FE Macro named '" << feMacroName
1200  << "' with target FE '" << interfaceID
1201  << "' failed. Here is the error:\n\n"
1202  << e.what() << __E__;
1203  __SUP_SS_THROW__;
1204  }
1205  catch(std::exception& e)
1206  {
1207  __SUP_SS__ << "In Supervisor with LID="
1208  << getApplicationDescriptor()->getLocalId()
1209  << " the FE Macro named '" << feMacroName
1210  << "' with target FE '" << interfaceID
1211  << "' failed. Here is the error:\n\n"
1212  << e.what() << __E__;
1213  __SUP_SS_THROW__;
1214  }
1215  catch(...)
1216  {
1217  __SUP_SS__ << "In Supervisor with LID="
1218  << getApplicationDescriptor()->getLocalId()
1219  << " the FE Macro named '" << feMacroName
1220  << "' with target FE '" << interfaceID
1221  << "' failed due to an unknown error." << __E__;
1222  try
1223  {
1224  throw;
1225  } //one more try to printout extra info
1226  catch(const std::exception& e)
1227  {
1228  ss << "Exception message: " << e.what();
1229  }
1230  catch(...)
1231  {
1232  }
1233  __SUP_SS_THROW__;
1234  }
1235 
1236  retParameters.addParameter("outputArgs", outputArgs);
1237  return SOAPUtilities::makeSOAPMessageReference(
1238  supervisorClassNoNamespace_ + "Response", retParameters);
1239  }
1240  }
1241  else if(request == "RunMacroMakerMacro")
1242  {
1243  if(!theFEInterfacesManager_)
1244  {
1245  __SUP_SS__ << "Missing FE Interface Manager! Are you configured?"
1246  << __E__;
1247  __SUP_SS_THROW__;
1248  }
1249 
1250  // params for running macros
1251  SOAPParameters requestParameters;
1252  requestParameters.addParameter("macroName");
1253  requestParameters.addParameter("macroString");
1254  requestParameters.addParameter("inputArgs");
1255  requestParameters.addParameter("outputArgs");
1256  requestParameters.addParameter("InterfaceID");
1257  requestParameters.addParameter("AsyncSupported");
1258  SOAPUtilities::receive(message, requestParameters);
1259  std::string interfaceID = requestParameters.getValue("InterfaceID");
1260  std::string macroName = requestParameters.getValue("macroName");
1261  std::string macroString = requestParameters.getValue("macroString");
1262  std::string inputArgs = requestParameters.getValue("inputArgs");
1263  std::string outputArgs = requestParameters.getValue("outputArgs");
1264  bool asyncSupported = requestParameters.getValue("AsyncSupported") == "1";
1265 
1266  if(asyncSupported)
1267  {
1268  // Async path: launch macro in a thread, return NotDoneTaskID immediately
1269  auto task = std::make_shared<AsyncMacroTask>();
1270  {
1271  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1272  task->taskID = ++asyncMacroTaskIDCounter_;
1273  if(asyncMacroTaskIDCounter_ == 0)
1274  task->taskID = ++asyncMacroTaskIDCounter_;
1275  task->interfaceID = interfaceID;
1276  task->startTime = time(0);
1277  asyncMacroTasks_.push_back(task);
1278  }
1279 
1280  std::thread([this,
1281  task,
1282  interfaceID,
1283  macroName,
1284  macroString,
1285  inputArgs,
1286  outputArgs]() mutable {
1287  {
1288  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1289  task->threadID = std::this_thread::get_id();
1290  }
1291  try
1292  {
1293  theFEInterfacesManager_->getFEInterfaceP(interfaceID)
1294  ->clearFEMacroPercentDone(std::this_thread::get_id());
1295  }
1296  catch(...)
1297  {
1298  }
1299 
1300  try
1301  {
1302  theFEInterfacesManager_->runMacro(
1303  interfaceID, macroString, inputArgs, outputArgs);
1304  try
1305  {
1306  theFEInterfacesManager_->getFEInterfaceP(interfaceID)
1307  ->clearFEMacroPercentDone(std::this_thread::get_id());
1308  }
1309  catch(...)
1310  {
1311  }
1312  {
1313  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1314  task->outputArgs = outputArgs;
1315  task->doneTime = time(0);
1316  task->done = true;
1317  }
1318  }
1319  catch(const std::exception& e)
1320  {
1321  try
1322  {
1323  theFEInterfacesManager_->getFEInterfaceP(interfaceID)
1324  ->clearFEMacroPercentDone(std::this_thread::get_id());
1325  }
1326  catch(...)
1327  {
1328  }
1329  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1330  task->error =
1331  "In Supervisor with LID=" +
1332  std::to_string(getApplicationDescriptor()->getLocalId()) +
1333  " the MacroMaker Macro named '" + macroName +
1334  "' with target FE '" + interfaceID +
1335  "' failed. Here is the error:\n\n" + e.what();
1336  task->doneTime = time(0);
1337  task->done = true;
1338  }
1339  catch(...)
1340  {
1341  try
1342  {
1343  theFEInterfacesManager_->getFEInterfaceP(interfaceID)
1344  ->clearFEMacroPercentDone(std::this_thread::get_id());
1345  }
1346  catch(...)
1347  {
1348  }
1349  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1350  task->error =
1351  "In Supervisor with LID=" +
1352  std::to_string(getApplicationDescriptor()->getLocalId()) +
1353  " the MacroMaker Macro named '" + macroName +
1354  "' with target FE '" + interfaceID +
1355  "' failed due to an unknown error.";
1356  task->doneTime = time(0);
1357  task->done = true;
1358  }
1359  }).detach();
1360 
1361  __SUP_COUT__ << "Launched async MacroMaker Macro '" << macroName
1362  << "' for interfaceID '" << interfaceID
1363  << "' with taskID=" << task->taskID << __E__;
1364 
1365  // Wait briefly to see if macro finishes quickly (avoids unnecessary polling)
1366  {
1367  size_t sleepUs = 100;
1368  for(int i = 0; i < 10; ++i)
1369  {
1370  usleep(sleepUs);
1371  {
1372  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1373  if(task->done)
1374  {
1375  if(!task->error.empty())
1376  {
1377  std::string errorCopy = task->error;
1378  for(size_t j = 0; j < asyncMacroTasks_.size(); ++j)
1379  if(asyncMacroTasks_[j]->taskID == task->taskID)
1380  {
1381  asyncMacroTasks_.erase(
1382  asyncMacroTasks_.begin() + j);
1383  break;
1384  }
1385  __SUP_SS__ << errorCopy;
1386  __SUP_SS_THROW__;
1387  }
1388  __SUP_COUT__
1389  << "DIAG ms=" << StringMacros::nowEpochMs()
1390  << " async macro finished within quick-wait,"
1391  << " returning synchronously, taskID=" << task->taskID
1392  << __E__;
1393  retParameters.addParameter("outputArgs",
1394  task->outputArgs);
1395  for(size_t j = 0; j < asyncMacroTasks_.size(); ++j)
1396  if(asyncMacroTasks_[j]->taskID == task->taskID)
1397  {
1398  asyncMacroTasks_.erase(asyncMacroTasks_.begin() +
1399  j);
1400  break;
1401  }
1402  return SOAPUtilities::makeSOAPMessageReference(
1403  supervisorClassNoNamespace_ + "Response",
1404  retParameters);
1405  }
1406  }
1407  sleepUs *= 2;
1408  if(sleepUs > 50000)
1409  sleepUs = 50000;
1410  }
1411  }
1412 
1413  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
1414  << " async macro NOT done within quick-wait,"
1415  << " returning NotDoneTaskID=" << task->taskID << __E__;
1416  retParameters.addParameter("NotDoneTaskID", std::to_string(task->taskID));
1417  return SOAPUtilities::makeSOAPMessageReference(
1418  supervisorClassNoNamespace_ + "Response", retParameters);
1419  }
1420  else
1421  {
1422  // Synchronous path: run macro and return result (backward compatible)
1423  try
1424  {
1425  theFEInterfacesManager_->runMacro(
1426  interfaceID, macroString, inputArgs, outputArgs);
1427  }
1428  catch(std::runtime_error& e)
1429  {
1430  __SUP_SS__ << "In Supervisor with LID="
1431  << getApplicationDescriptor()->getLocalId()
1432  << " the MacroMaker Macro named '" << macroName
1433  << "' with target FE '" << interfaceID
1434  << "' failed. Here is the error:\n\n"
1435  << e.what() << __E__;
1436  __SUP_SS_THROW__;
1437  }
1438  catch(...)
1439  {
1440  __SUP_SS__ << "In Supervisor with LID="
1441  << getApplicationDescriptor()->getLocalId()
1442  << " the MacroMaker Macro named '" << macroName
1443  << "' with target FE '" << interfaceID
1444  << "' failed due to an unknown error." << __E__;
1445  try
1446  {
1447  throw;
1448  } //one more try to printout extra info
1449  catch(const std::exception& e)
1450  {
1451  ss << "Exception message: " << e.what();
1452  }
1453  catch(...)
1454  {
1455  }
1456  __SUP_SS_THROW__;
1457  }
1458 
1459  retParameters.addParameter("outputArgs", outputArgs);
1460  return SOAPUtilities::makeSOAPMessageReference(
1461  supervisorClassNoNamespace_ + "Response", retParameters);
1462  }
1463  }
1464  else if(request == "CheckMacro")
1465  {
1466  SOAPParameters requestParameters;
1467  requestParameters.addParameter("TaskID");
1468  SOAPUtilities::receive(message, requestParameters);
1469  uint64_t taskID = std::stoull(requestParameters.getValue("TaskID"));
1470 
1471  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1472 
1473  // Cleanup old completed tasks (>5 minutes after completion)
1474  time_t now = time(0);
1475  for(size_t i = 0; i < asyncMacroTasks_.size();)
1476  {
1477  auto& t = asyncMacroTasks_[i];
1478  if(t->done && t->doneTime > 0 && (now - t->doneTime) > 5 * 60)
1479  asyncMacroTasks_.erase(asyncMacroTasks_.begin() + i);
1480  else
1481  ++i;
1482  }
1483 
1484  // Find the requested task
1485  std::shared_ptr<AsyncMacroTask> foundTask;
1486  for(auto& t : asyncMacroTasks_)
1487  {
1488  if(t->taskID == taskID)
1489  {
1490  foundTask = t;
1491  break;
1492  }
1493  }
1494 
1495  if(!foundTask)
1496  {
1497  __SUP_SS__ << "Async macro task with ID=" << taskID
1498  << " was not found. Perhaps it completed more than 5 "
1499  "minutes ago?"
1500  << __E__;
1501  __SUP_SS_THROW__;
1502  }
1503 
1504  if(foundTask->done)
1505  {
1506  if(!foundTask->error.empty())
1507  {
1508  std::string errorCopy = foundTask->error;
1509  // Remove completed task
1510  for(size_t i = 0; i < asyncMacroTasks_.size(); ++i)
1511  {
1512  if(asyncMacroTasks_[i]->taskID == taskID)
1513  {
1514  asyncMacroTasks_.erase(asyncMacroTasks_.begin() + i);
1515  break;
1516  }
1517  }
1518  __SUP_SS__ << errorCopy;
1519  __SUP_SS_THROW__;
1520  }
1521 
1522  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
1523  << " CheckMacro found task DONE (doneTime="
1524  << foundTask->doneTime << "), taskID=" << taskID << __E__;
1525  retParameters.addParameter("outputArgs", foundTask->outputArgs);
1526 
1527  // Remove completed task
1528  for(size_t i = 0; i < asyncMacroTasks_.size(); ++i)
1529  {
1530  if(asyncMacroTasks_[i]->taskID == taskID)
1531  {
1532  asyncMacroTasks_.erase(asyncMacroTasks_.begin() + i);
1533  break;
1534  }
1535  }
1536 
1537  return SOAPUtilities::makeSOAPMessageReference(
1538  supervisorClassNoNamespace_ + "Response", retParameters);
1539  }
1540  else
1541  {
1542  // Still running — check for real progress from the FE macro
1543  if(theFEInterfacesManager_ && foundTask->threadID != std::thread::id())
1544  {
1545  try
1546  {
1547  int progress = theFEInterfacesManager_
1548  ->getFEInterfaceP(foundTask->interfaceID)
1549  ->getFEMacroPercentDone(foundTask->threadID);
1550  if(progress >= 0)
1551  retParameters.addParameter("Progress",
1552  std::to_string(progress));
1553  }
1554  catch(...)
1555  {
1556  }
1557  }
1558  __SUP_COUT__ << "DIAG ms=" << StringMacros::nowEpochMs()
1559  << " CheckMacro found task still RUNNING, taskID=" << taskID
1560  << __E__;
1561  retParameters.addParameter("NotDoneTaskID", std::to_string(taskID));
1562  return SOAPUtilities::makeSOAPMessageReference(
1563  supervisorClassNoNamespace_ + "Response", retParameters);
1564  }
1565  }
1566  else
1567  {
1568  __SUP_SS__ << "Unrecognized request received! '" << request << "'" << __E__;
1569  __SUP_SS_THROW__;
1570  }
1571  }
1572  catch(const std::runtime_error& e)
1573  {
1574  __SUP_SS__ << "Error occurred handling request: " << e.what() << __E__;
1575  __SUP_COUT_ERR__ << ss.str();
1576  retParameters.addParameter("Error", ss.str());
1577  }
1578  catch(...)
1579  {
1580  __SUP_SS__ << "Error occurred handling request." << __E__;
1581  try
1582  {
1583  throw;
1584  } //one more try to printout extra info
1585  catch(const std::exception& e)
1586  {
1587  ss << "Exception message: " << e.what();
1588  }
1589  catch(...)
1590  {
1591  }
1592  __SUP_COUT_ERR__ << ss.str();
1593  retParameters.addParameter("Error", ss.str());
1594  }
1595 
1596  return SOAPUtilities::makeSOAPMessageReference(
1597  supervisorClassNoNamespace_ + "FailRequest", retParameters);
1598 
1599 } // end macroMakerSupervisorRequest()
1600 
1601 //==============================================================================
1602 xoap::MessageReference FESupervisor::workLoopStatusRequest(
1603  xoap::MessageReference /*message*/)
1604 {
1605  if(!theFEInterfacesManager_)
1606  {
1607  __SUP_SS__ << "Invalid request for front-end workloop status from Supervisor "
1608  "without a FEVInterfacesManager."
1609  << __E__;
1610  __SUP_SS_THROW__;
1611  }
1612 
1613  return SOAPUtilities::makeSOAPMessageReference(
1614  (theFEInterfacesManager_->allFEWorkloopsAreDone()
1615  ? CoreSupervisorBase::WORK_LOOP_DONE
1616  : CoreSupervisorBase::WORK_LOOP_WORKING));
1617 } // end workLoopStatusRequest()
1618 
1619 //==============================================================================
1627 FEVInterfacesManager* FESupervisor::extractFEInterfacesManager()
1628 {
1629  theFEInterfacesManager_ = 0;
1630 
1631  for(unsigned int i = 0; i < theStateMachineImplementation_.size(); ++i)
1632  {
1633  try
1634  {
1635  theFEInterfacesManager_ =
1636  dynamic_cast<FEVInterfacesManager*>(theStateMachineImplementation_[i]);
1637  if(!theFEInterfacesManager_)
1638  {
1639  // dynamic_cast returns null pointer on failure
1640  __SUP_SS__ << "Dynamic cast failure!" << __E__;
1641  __SUP_SS_THROW__;
1642  }
1643  __SUP_COUT__ << "State Machine " << i << " WAS of type FEVInterfacesManager"
1644  << __E__;
1645 
1646  break;
1647  }
1648  catch(...)
1649  {
1650  __SUP_COUT__ << "State Machine " << i
1651  << " was NOT of type FEVInterfacesManager" << __E__;
1652  }
1653  }
1654 
1655  __SUP_COUT__ << "theFEInterfacesManager pointer = " << theFEInterfacesManager_
1656  << __E__;
1657 
1658  return theFEInterfacesManager_;
1659 } // end extractFEInterfaceManager()
1660 
1661 //==============================================================================
1662 void FESupervisor::transitionConfiguring(toolbox::Event::Reference /*event*/)
1663 {
1664  __SUP_COUT__ << "transitionConfiguring" << __E__;
1665  CoreSupervisorBase::configureInit();
1666 
1667  // get pset from Board Reader metric manager table
1668  try
1669  {
1670  __COUTV__(CorePropertySupervisorBase::getSupervisorConfigurationPath());
1671 
1672  ConfigurationTree feSupervisorNode =
1673  CorePropertySupervisorBase::getSupervisorTableNode();
1674 
1675  std::string metric_string = "";
1676  bool metricStringSetup = true;
1677  try
1678  {
1679  std::ostringstream oss;
1680  std::string tabString = "";
1681  std::string commentsString = "";
1683  oss, tabString, commentsString, "" /* parentPath*/, feSupervisorNode);
1684  metric_string = oss.str();
1685  }
1686  catch(...)
1687  {
1688  metricStringSetup = false;
1689  metric_string = "";
1690  } // ignore error
1691 
1692  if(!metricMan)
1693  {
1694  __SUP_COUT__ << "Metric manager is not instantiated! Attempting to fix."
1695  << __E__;
1696  metricMan = std::make_unique<artdaq::MetricManager>();
1697  }
1698  std::string metricNamePreamble =
1699  feSupervisorNode.getNode("/SlowControlsMetricManagerChannelNamePreamble")
1700  .getValueWithDefault<std::string>("");
1701  __SUP_COUTV__(metricNamePreamble);
1702 
1703  // std::string metric_string = "epics: {metricPluginType:epics level:3 channel_name_prefix:Mu2e}";
1704  fhicl::ParameterSet metric_pset = fhicl::ParameterSet::make(metric_string);
1705 
1706  __SUP_COUTV__(metricNamePreamble);
1707  try
1708  {
1709  metricMan->initialize(metric_pset.get<fhicl::ParameterSet>("metrics"),
1710  metricNamePreamble);
1711  }
1712  catch(...)
1713  {
1714  if(metricStringSetup)
1715  throw;
1716  else
1717  __SUP_COUT__ << "Ignore metric manager initialize error because metric "
1718  "string is not setup."
1719  << __E__;
1720  }
1721  __SUP_COUT__ << "transitionConfiguring metric manager(" << metricMan
1722  << ") initialized = " << metricMan->Initialized() << __E__;
1723  }
1724  catch(const std::runtime_error& e)
1725  {
1726  __SS__ << "Error loading metrics in FESupervisor::transitionConfiguring(): "
1727  << e.what() << __E__;
1728  __SUP_COUT_ERR__ << ss.str();
1729  // ExceptionHandler(ExceptionHandlerRethrow::no, ss.str());
1730 
1731  //__SS_THROW_ONLY__;
1732  theStateMachine_.setErrorMessage(ss.str());
1733  throw toolbox::fsm::exception::Exception(
1734  "Transition Error" /*name*/,
1735  ss.str() /* message*/,
1736  "FESupervisor::transitionConfiguring" /*module*/,
1737  __LINE__ /*line*/,
1738  __FUNCTION__ /*function*/
1739  );
1740  }
1741  catch(...)
1742  {
1743  __SS__ << "Error loading metrics in FESupervisor::transitionConfiguring()"
1744  << __E__;
1745  try
1746  {
1747  throw;
1748  } //one more try to printout extra info
1749  catch(const std::exception& e)
1750  {
1751  ss << "Exception message: " << e.what();
1752  }
1753  catch(...)
1754  {
1755  }
1756  __SUP_COUT_ERR__ << ss.str();
1757  // ExceptionHandler(ExceptionHandlerRethrow::no, ss.str());
1758 
1759  //__SS_THROW_ONLY__;
1760  theStateMachine_.setErrorMessage(ss.str());
1761  throw toolbox::fsm::exception::Exception(
1762  "Transition Error" /*name*/,
1763  ss.str() /* message*/,
1764  "FESupervisor::transitionConfiguring" /*module*/,
1765  __LINE__ /*line*/,
1766  __FUNCTION__ /*function*/
1767  );
1768  }
1769 
1770  CoreSupervisorBase::transitionConfiguringFSMs();
1771 
1772  __SUP_COUT__ << "transitionConfiguring done." << __E__;
1773 } // end transitionConfiguring()
1774 
1775 //==============================================================================
1776 void FESupervisor::transitionHalting(toolbox::Event::Reference event)
1777 {
1778  __SUP_COUT__ << "transitionHalting" << __E__;
1779  TLOG_DEBUG(7) << "transitionHalting";
1780 
1781  // Wait for any in-flight async macro tasks before tearing down FE interfaces
1782  {
1783  const int timeoutSeconds = 30;
1784  const time_t deadline = time(0) + timeoutSeconds;
1785  bool hasOutstanding = true;
1786  while(hasOutstanding && time(0) < deadline)
1787  {
1788  {
1789  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1790  hasOutstanding = false;
1791  for(const auto& t : asyncMacroTasks_)
1792  {
1793  if(!t->done)
1794  {
1795  hasOutstanding = true;
1796  break;
1797  }
1798  }
1799  }
1800  if(hasOutstanding)
1801  {
1802  __SUP_COUT__ << "Waiting for outstanding async macro tasks to complete "
1803  "before halting..."
1804  << __E__;
1805  usleep(500 * 1000);
1806  }
1807  }
1808  if(hasOutstanding)
1809  __SUP_COUT_WARN__ << "Timed out waiting for async macro tasks; "
1810  "proceeding with halt."
1811  << __E__;
1812  std::lock_guard<std::mutex> lock(asyncMacroMutex_);
1813  asyncMacroTasks_.clear();
1814  }
1815 
1816  // shutdown workloops first, then shutdown metric manager
1818 
1819  try
1820  {
1821  if(metricMan && metricMan->Initialized())
1822  {
1823  TLOG_DEBUG(7) << "Metric manager(" << metricMan << ") shutting down..."
1824  << __E__;
1825  metricMan
1826  ->shutdown(); // will set initilized_ to false with mutex, which should prevent races
1827  TLOG_DEBUG(7) << "Metric manager shutdown." << __E__;
1828  }
1829  else
1830  __SUP_COUT__ << "Metric manager(" << metricMan << ") already shutdown."
1831  << __E__;
1832 
1833  metricMan.reset(nullptr);
1834  }
1835  catch(...)
1836  {
1837  __SS__ << "Error shutting down metrics in FESupervisor::transitionHalting()"
1838  << __E__;
1839  try
1840  {
1841  throw;
1842  } //one more try to printout extra info
1843  catch(const std::exception& e)
1844  {
1845  ss << "Exception message: " << e.what();
1846  }
1847  catch(...)
1848  {
1849  }
1850  __SUP_COUT_ERR__ << ss.str();
1851  // ExceptionHandler(ExceptionHandlerRethrow::no, ss.str());
1852 
1853  //__SS_THROW_ONLY__;
1854  theStateMachine_.setErrorMessage(ss.str());
1855  throw toolbox::fsm::exception::Exception(
1856  "Transition Error" /*name*/,
1857  ss.str() /* message*/,
1858  "FESupervisor::transitionHalting" /*module*/,
1859  __LINE__ /*line*/,
1860  __FUNCTION__ /*function*/
1861  );
1862  }
1863 
1864  __SUP_COUT__ << "transitionHalting done." << __E__;
1865 } // end transitionHalting()
1866 
1867 //==============================================================================
1868 void FESupervisor::initDataPublishing(const std::string& endpoint,
1869  const std::string& topic)
1870 {
1871  if(dp_isInitialized_)
1872  {
1873  // silently re‑initialise – close old socket first.
1874  closeDataPublishing(false);
1875  }
1876 
1877  // Store for later use.
1878  dp_endpoint_ = endpoint;
1879  dp_topic_ = topic;
1880 
1881  // Bind the PUB socket.
1882  try
1883  {
1884  dp_socket_.bind(dp_endpoint_);
1885  }
1886  catch(const zmq::error_t& e)
1887  {
1888  __SUP_SS__ << "initDataPublishing() - bind to zmq '" + dp_endpoint_ +
1889  "' failed: " + e.what()
1890  << __E__;
1891  __SUP_SS_THROW__;
1892  }
1893 
1894  dp_isInitialized_ = true;
1895 } //end initDataPublishing()
1896 
1897 //==============================================================================
1898 void FESupervisor::closeDataPublishing(bool alsoCloseContext /* = true */)
1899 {
1900  if(dp_isInitialized_)
1901  {
1902  try
1903  {
1904  dp_socket_.set(zmq::sockopt::linger, 0); // discard unsent messages on close
1905  dp_socket_.close(); // close the PUB socket
1906  }
1907  catch(const zmq::error_t&)
1908  {
1909  // ignore – we are cleaning up.
1910  }
1911  try
1912  {
1913  if(alsoCloseContext)
1914  dp_context_.close(); // close the ZMQ context
1915  }
1916  catch(const zmq::error_t&)
1917  {
1918  // ignore.
1919  }
1920  dp_isInitialized_ = false;
1921  }
1922 } //end closeDataPublishing()
1923 
1924 //==============================================================================
1932 void FESupervisor::publishData(const char* dataPtr, size_t dataSize)
1933 {
1934  if(!dp_isInitialized_)
1935  {
1936  __SUP_SS__ << "publishData() called before zmq init()" << __E__;
1937  __SUP_SS_THROW__;
1938  }
1939 
1940  // ---- Topic frame (must be sent with sndmore) ----
1941  // Using zmq::buffer prevents an extra copy – it just points at the data.
1942  auto rc_topic = dp_socket_.send(zmq::buffer(dp_topic_), zmq::send_flags::sndmore);
1943  if(!rc_topic)
1944  {
1945  __SUP_SS__ << "publishData() - failed to send zmq topic" << __E__;
1946  __SUP_SS_THROW__;
1947  }
1948 
1949  // ---- Payload frame (final frame) ----
1950  // `dataPtr` may be nullptr only if `dataSize == 0` – ZeroMQ accepts an empty frame.
1951  if(dataPtr == nullptr && dataSize > 0)
1952  {
1953  __SUP_SS__ << "FESupervisor::publishData() - dataPtr is nullptr but dataSize is "
1954  << dataSize << __E__;
1955  __SUP_SS_THROW__;
1956  }
1957  auto rc_payload =
1958  dp_socket_.send(zmq::buffer(dataPtr, dataSize), zmq::send_flags::none);
1959  if(!rc_payload)
1960  {
1961  __SUP_SS__ << "publishData() - failed to send zmq payload" << __E__;
1962  __SUP_SS_THROW__;
1963  }
1964 } //end publishData()
static void insertMetricsBlock(std::ostream &out, std::string &tabStr, std::string &commentStr, const std::string &parentPath, ConfigurationTree daqNode)
insertMetricsBlock
ConfigurationTree getNode(const std::string &nodeName, bool doNotThrowOnBrokenUIDLinks=false) const
navigating between nodes
T getValueWithDefault(const T &defaultValue) const
static void extractPermissionsMapFromString(const std::string &permissionsString, std::map< std::string, WebUsers::permissionLevel_t > &permissionsMap)
static bool doPermissionsGrantAccess(std::map< std::string, WebUsers::permissionLevel_t > &permissionLevelsMap, std::map< std::string, WebUsers::permissionLevel_t > &permissionThresholdsMap)
virtual void transitionHalting(toolbox::Event::Reference event)
virtual void request(const std::string &requestType, cgicc::Cgicc &cgiIn, HttpXmlDocument &xmlOut, const WebUsers::RequestUserInfo &userInfo)
void publishData(const char *dataPtr, size_t dataSize)
virtual void transitionHalting(toolbox::Event::Reference event) override
void initDataPublishing(const std::string &endpoint, const std::string &topic="test")
void closeDataPublishing(bool alsoCloseContext=true)
xoap::MessageReference macroMakerSupervisorRequest(xoap::MessageReference message)
const FEVInterface & getFEInterface(const std::string &interfaceID) const
getFEInterface
std::string getFEMacrosString(const std::string &supervisorName, const std::string &supervisorLid)
used by MacroMaker
FEVInterface * getFEInterfaceP(const std::string &interfaceID)
getFEInterfaceP
void runFEMacro(const std::string &interfaceID, const FEVInterface::frontEndMacroStruct_t &feMacro, const std::string &inputArgs, std::string &outputArgs)
used by MacroMaker and FE calling indirectly
unsigned int getInterfaceUniversalAddressSize(const std::string &interfaceID)
used by MacroMaker
void startFEMacroMultiDimensional(const std::string &requester, const std::string &interfaceID, const std::string &feMacroName, const bool enableSavingOutput, const std::string &outputFilePath, const std::string &outputFileRadix, const std::string &inputArgs)
used by iterator calling (i.e. FESupervisor)
void universalWrite(const std::string &interfaceID, char *address, char *writeValue)
used by MacroMaker
bool allFEWorkloopsAreDone(void)
used by Iterator, e.g.
void runFEMacroByFE(const std::string &callingInterfaceID, const std::string &interfaceID, const std::string &feMacroName, const std::string &inputArgs, std::string &outputArgs)
used by FE calling (i.e. FESupervisor)
unsigned int getInterfaceUniversalDataSize(const std::string &interfaceID)
used by MacroMaker
void universalRead(const std::string &interfaceID, char *address, char *returnValue)
used by MacroMaker
void startMacroMultiDimensional(const std::string &requester, const std::string &interfaceID, const std::string &macroName, const std::string &macroString, const bool enableSavingOutput, const std::string &outputFilePath, const std::string &outputFileRadix, const std::string &inputArgs)
used by iterator calling (i.e. FESupervisor)
bool checkMacroMultiDimensional(const std::string &interfaceID, const std::string &macroName)
used by iterator calling (i.e. FESupervisor)
std::mutex frontEndCommunicationReceiveMutex_
FE communication helpers.
void runMacro(const std::string &interfaceID, const std::string &macroObjectString, const std::string &inputArgs, std::string &outputArgs)
used by MacroMaker
std::string getFEListString(const std::string &supervisorLid)
used by MacroMaker
defines used also by OtsConfigurationWizardSupervisor
< members fully define a front-end macro function
Definition: FEVInterface.h:178
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")