otsdaq  v2_05_02_indev
FEVInterface.cc
1 #include "otsdaq/FECore/FEVInterface.h"
2 #include "otsdaq/CoreSupervisors/CoreSupervisorBase.h"
3 #include "otsdaq/FECore/FEVInterfacesManager.h"
4 #include "otsdaq/NetworkUtilities/UDPDataStreamerBase.h"
5 
6 #include <iostream>
7 #include <sstream>
8 #include <thread> //for std::thread
9 
10 using namespace ots;
11 
12 //==============================================================================
13 FEVInterface::FEVInterface(const std::string& interfaceUID, const ConfigurationTree& theXDAQContextConfigTree, const std::string& configurationPath)
14  : WorkLoop(interfaceUID)
15  , Configurable(theXDAQContextConfigTree, configurationPath)
16  , VStateMachine(interfaceUID)
17  , slowControlsWorkLoop_(interfaceUID + "-SlowControls", this)
18  , interfaceUID_(interfaceUID)
19  , mfSubject_(interfaceUID)
20 //, interfaceType_
21 //(theXDAQContextConfigTree_.getBackNode(theConfigurationPath_).getNode("FEInterfacePluginName").getValue<std::string>())
22 //, daqHardwareType_ ("NOT SET")
23 //, firmwareType_ ("NOT SET")
24 {
25  // NOTE!! be careful to not decorate with __FE_COUT__ because in the constructor the
26  // base class versions of function (e.g. getInterfaceType) are called because the
27  // derived class has not been instantiate yet!
28  // Instead use __GEN_COUT__ which decorates using mfSubject_
29  __GEN_COUT__ << "Constructed." << __E__;
30 } // end constructor()
31 
32 //==============================================================================
33 FEVInterface::~FEVInterface(void)
34 {
35  // NOTE:: be careful not to call __FE_COUT__ decoration because it might use the tree
36  // depending on child class overrides, and it may already be destructed partially.
37  // Instead use __GEN_COUT__ which decorates using mfSubject_
38  __GEN_COUT__ << "Destructed." << __E__;
39 } // end destructor()
40 
41 //==============================================================================
42 void FEVInterface::configureSlowControls(void)
43 {
44  // Start artdaq metric manager here, if possible
45  if(metricMan && !metricMan->Running() && metricMan->Initialized())
46  {
47  __GEN_COUT__ << "Metric manager starting..." << __E__;
48  metricMan->do_start();
49  __GEN_COUT__ << "Metric manager started." << __E__;
50  }
51  else if(!metricMan || !metricMan->Initialized())
52  __GEN_COUT__ << "Metric manager could not be started! metricMan: " << metricMan << " Initialized()= " << metricMan->Initialized() << __E__;
53  else
54  __GEN_COUT__ << "Metric manager already started." << __E__;
55 
56  mapOfSlowControlsChannels_.clear(); // reset
57 
58  addSlowControlsChannels(theXDAQContextConfigTree_.getBackNode(theConfigurationPath_).getNode("LinkToSlowControlsChannelTable"),
59  "" /*subInterfaceID*/,
60  &mapOfSlowControlsChannels_);
61 
62 } // end configureSlowControls()
63 
64 //==============================================================================
65 // addSlowControlsChannels
66 // Usually subInterfaceID = "" and mapOfSlowControlsChannels =
67 //&mapOfSlowControlsChannels_
68 void FEVInterface::addSlowControlsChannels(ConfigurationTree slowControlsGroupLink,
69  const std::string& subInterfaceID,
70  std::map<std::string /* ROC UID*/, FESlowControlsChannel>* mapOfSlowControlsChannels)
71 {
72  if(slowControlsGroupLink.isDisconnected())
73  {
74  __FE_COUT__ << "slowControlsGroupLink is disconnected, so done configuring slow controls." << __E__;
75  return;
76  }
77  __FE_COUT__ << "slowControlsGroupLink is valid! Adding slow controls channels..." << __E__;
78 
79  std::vector<std::pair<std::string, ConfigurationTree> > groupLinkChildren = slowControlsGroupLink.getChildren();
80  for(auto& groupLinkChild : groupLinkChildren)
81  {
82  // skip channels that are off
83  if(!(groupLinkChild.second.getNode(TableViewColumnInfo::COL_NAME_STATUS).getValue<bool>()))
84  continue;
85 
86  __FE_COUT__ << "Channel:" << getInterfaceUID() << subInterfaceID << "/" << groupLinkChild.first
87  << "\t Type:" << groupLinkChild.second.getNode("ChannelDataType") << __E__;
88 
89  mapOfSlowControlsChannels->insert(std::pair<std::string, FESlowControlsChannel>(
90  groupLinkChild.first,
91  FESlowControlsChannel(getInterfaceUID() + subInterfaceID,
92  groupLinkChild.first,
93  groupLinkChild.second.getNode("ChannelDataType").getValue<std::string>(),
94  universalDataSize_,
95  universalAddressSize_,
96  groupLinkChild.second.getNode("UniversalInterfaceAddress").getValue<std::string>(),
97  groupLinkChild.second.getNode("UniversalDataBitOffset").getValue<unsigned int>(),
98  groupLinkChild.second.getNode("ReadAccess").getValue<bool>(),
99  groupLinkChild.second.getNode("WriteAccess").getValue<bool>(),
100  groupLinkChild.second.getNode("MonitoringEnabled").getValue<bool>(),
101  groupLinkChild.second.getNode("RecordChangesOnly").getValue<bool>(),
102  groupLinkChild.second.getNode("DelayBetweenSamplesInSeconds").getValue<time_t>(),
103  groupLinkChild.second.getNode("LocalSavingEnabled").getValue<bool>(),
104  groupLinkChild.second.getNode("LocalFilePath").getValue<std::string>(),
105  groupLinkChild.second.getNode("RadixFileName").getValue<std::string>(),
106  groupLinkChild.second.getNode("SaveBinaryFile").getValue<bool>(),
107  groupLinkChild.second.getNode("AlarmsEnabled").getValue<bool>(),
108  groupLinkChild.second.getNode("LatchAlarms").getValue<bool>(),
109  groupLinkChild.second.getNode("LowLowThreshold").getValue<std::string>(),
110  groupLinkChild.second.getNode("LowThreshold").getValue<std::string>(),
111  groupLinkChild.second.getNode("HighThreshold").getValue<std::string>(),
112  groupLinkChild.second.getNode("HighHighThreshold").getValue<std::string>())));
113  }
114 } // end addSlowControlsChannels()
115 
116 //==============================================================================
117 // virtual in case channels are handled in multiple maps, for example
118 void FEVInterface::resetSlowControlsChannelIterator(void)
119 {
120  slowControlsChannelsIterator_ = mapOfSlowControlsChannels_.begin();
121 } // end resetSlowControlsChannelIterator()
122 
123 //==============================================================================
124 // virtual in case channels are handled in multiple maps, for example
125 FESlowControlsChannel* FEVInterface::getNextSlowControlsChannel(void)
126 {
127  if(slowControlsChannelsIterator_ == mapOfSlowControlsChannels_.end())
128  return nullptr;
129 
130  return &((slowControlsChannelsIterator_++)->second); // return iterator, then increment
131 } // end getNextSlowControlsChannel()
132 
133 //==============================================================================
134 // virtual in case read should be different than universalread
135 void FEVInterface::getSlowControlsValue(FESlowControlsChannel& channel, std::string& readValue)
136 {
137  readValue.resize(universalDataSize_);
138  universalRead(channel.getUniversalAddress(), &readValue[0]);
139 } // end getNextSlowControlsChannel()
140 
141 //==============================================================================
142 // virtual in case channels are handled in multiple maps, for example
143 unsigned int FEVInterface::getSlowControlsChannelCount(void) { return mapOfSlowControlsChannels_.size(); } // end getSlowControlsChannelCount()
144 
145 //==============================================================================
146 bool FEVInterface::slowControlsRunning(void) try
147 {
148  __FE_COUT__ << "slowControlsRunning" << __E__;
149 
150  if(getSlowControlsChannelCount() == 0)
151  {
152  __FE_COUT__ << "No slow controls channels to monitor, exiting slow controls workloop." << __E__;
153  return false;
154  }
155  std::string readVal;
156  readVal.resize(universalDataSize_); // size to data in advance
157 
158  FESlowControlsChannel* channel;
159 
160  const unsigned int txBufferSz = 1500;
161  const unsigned int txBufferFullThreshold = 750;
162  std::string txBuffer;
163  txBuffer.reserve(txBufferSz);
164 
165  ConfigurationTree FEInterfaceNode = theXDAQContextConfigTree_.getBackNode(theConfigurationPath_);
166 
167  // attempt to make Slow Controls transfer socket
168  std::unique_ptr<UDPDataStreamerBase> slowContrlolsTxSocket;
169  std::string slowControlsSupervisorIPAddress = "", slowControlsSelfIPAddress = "";
170  int slowControlsSupervisorPort = 0, slowControlsSelfPort = 0;
171  try
172  {
173  ConfigurationTree slowControlsInterfaceLink = FEInterfaceNode.getNode("LinkToSlowControlsSupervisorTable");
174 
175  if(slowControlsInterfaceLink.isDisconnected())
176  {
177  __FE_SS__ << "slowControlsInterfaceLink is disconnected, so no socket made." << __E__;
178  __FE_SS_THROW__;
179  }
180 
181  slowControlsSelfIPAddress = FEInterfaceNode.getNode("SlowControlsTxSocketIPAddress").getValue<std::string>();
182  slowControlsSelfPort = FEInterfaceNode.getNode("SlowControlsTxSocketPort").getValue<int>();
183  slowControlsSupervisorIPAddress = slowControlsInterfaceLink.getNode("IPAddress").getValue<std::string>();
184  slowControlsSupervisorPort = slowControlsInterfaceLink.getNode("Port").getValue<int>();
185  }
186  catch(...)
187  {
188  __FE_COUT__ << "Link to slow controls supervisor is missing, so no socket made." << __E__;
189  }
190 
191  if(slowControlsSupervisorPort && slowControlsSelfPort && slowControlsSupervisorIPAddress != "" && slowControlsSelfIPAddress != "")
192  {
193  __FE_COUT__ << "slowControlsInterfaceLink is valid! Create tx socket..." << __E__;
194  slowContrlolsTxSocket.reset(
195  new UDPDataStreamerBase(slowControlsSelfIPAddress, slowControlsSelfPort, slowControlsSupervisorIPAddress, slowControlsSupervisorPort));
196  }
197  else
198  {
199  __FE_COUT__ << "Invalid Slow Controls socket parameters, so no socket made." << __E__;
200  }
201 
202  // check if aggregate saving
203 
204  FILE* fp = 0;
205  bool aggregateFileIsBinaryFormat = false;
206  try
207  {
208  if(FEInterfaceNode.getNode("SlowControlsLocalAggregateSavingEnabled").getValue<bool>())
209  {
210  aggregateFileIsBinaryFormat = FEInterfaceNode.getNode("SlowControlsSaveBinaryFile").getValue<bool>();
211 
212  __FE_COUT_INFO__ << "Slow Controls Aggregate Saving turned On BinaryFormat=" << aggregateFileIsBinaryFormat << __E__;
213 
214  std::string saveFullFileName = FEInterfaceNode.getNode("SlowControlsLocalFilePath").getValue<std::string>() + "/" +
215  FEInterfaceNode.getNode("SlowControlsRadixFileName").getValue<std::string>() + "-" +
216  FESlowControlsChannel::underscoreString(getInterfaceUID()) + "-" + std::to_string(time(0)) +
217  (aggregateFileIsBinaryFormat ? ".dat" : ".txt");
218 
219  fp = fopen(saveFullFileName.c_str(), aggregateFileIsBinaryFormat ? "ab" : "a");
220  if(!fp)
221  {
222  __FE_COUT_ERR__ << "Failed to open slow controls channel file: " << saveFullFileName << __E__;
223  // continue on, just nothing will be saved
224  }
225  else
226  __FE_COUT_INFO__ << "Slow controls aggregate file opened: " << saveFullFileName << __E__;
227  }
228  }
229  catch(...)
230  {
231  } // do nothing
232 
233  if(!aggregateFileIsBinaryFormat)
234  __FE_COUT_INFO__ << "Slow Controls Aggregate Saving turned off." << __E__;
235 
236  time_t timeCounter = 0;
237 
238  unsigned int numOfReadAccessChannels = 0;
239  bool firstTime = true;
240 
241  while(slowControlsWorkLoop_.getContinueWorkLoop())
242  {
243  __FE_COUT__ << "..." << __E__;
244 
245  sleep(1); // seconds
246  ++timeCounter;
247 
248  if(txBuffer.size())
249  __FE_COUT__ << "txBuffer sz=" << txBuffer.size() << __E__;
250 
251  txBuffer.resize(0); // clear buffer a la txBuffer = "";
252 
253  //__FE_COUT__ << "timeCounter=" << timeCounter << __E__;
254  //__FE_COUT__ << "txBuffer sz=" << txBuffer.size() << __E__;
255 
256  resetSlowControlsChannelIterator();
257  while((channel = getNextSlowControlsChannel()) != nullptr)
258  {
259  // skip if no read access
260  if(!channel->readAccess_)
261  continue;
262 
263  if(firstTime)
264  ++numOfReadAccessChannels;
265 
266  // skip if not a sampling moment in time for channel
267  if(timeCounter % channel->delayBetweenSamples_)
268  continue;
269 
270  __FE_COUT__ << "Reading Channel:" << channel->fullChannelName_ << __E__;
271 
272  getSlowControlsValue(*channel, readVal);
273 
274  // { //print
275  // __FE_SS__ << "0x ";
276  // for(int i=(int)universalAddressSize_-1;i>=0;--i)
277  // ss << std::hex << (int)((readVal[i]>>4)&0xF) <<
278  // (int)((readVal[i])&0xF) << " " << std::dec;
279  // ss << __E__;
280  // __FE_COUT__ << "Sampled.\n" << ss.str();
281  // }
282 
283  // have sample
284  channel->handleSample(readVal, txBuffer, fp, aggregateFileIsBinaryFormat);
285  if(txBuffer.size())
286  __FE_COUT__ << "txBuffer sz=" << txBuffer.size() << __E__;
287 
288  // Use artdaq Metric Manager if available,
289  if(channel->monitoringEnabled_ && metricMan && metricMan->Running() &&
290  universalAddressSize_ <= 8)
291  {
292  uint64_t val = 0; // 64 bits!
293  for(size_t ii = 0; ii < universalAddressSize_; ++ii)
294  val += (uint8_t)readVal[ii] << (ii * 4);
295 
296  __FE_COUT__ << "Sending sample to Metric Manager..." << __E__;
297  metricMan->sendMetric(channel->fullChannelName_, val, "", 3, artdaq::MetricMode::LastPoint);
298  }
299  else
300  __FE_COUT__ << "Skipping sample to Metric Manager: " <<
301  " channel->monitoringEnabled_=" << channel->monitoringEnabled_ <<
302  " metricMan=" << metricMan <<
303  " metricMan->Running()=" << (metricMan && metricMan->Running()) << __E__;
304 
305 
306  // make sure buffer hasn't exploded somehow
307  if(txBuffer.size() > txBufferSz)
308  {
309  __FE_SS__ << "This should never happen hopefully!" << __E__;
310  __FE_SS_THROW__;
311  }
312 
313  // send early if threshold reached
314  if(slowContrlolsTxSocket && txBuffer.size() > txBufferFullThreshold)
315  {
316  __FE_COUT__ << "Sending now! txBufferFullThreshold=" << txBufferFullThreshold << __E__;
317  slowContrlolsTxSocket->send(txBuffer);
318  txBuffer.resize(0); // clear buffer a la txBuffer = "";
319  }
320  }
321 
322  if(txBuffer.size())
323  __FE_COUT__ << "txBuffer sz=" << txBuffer.size() << __E__;
324 
325  // send anything left
326  if(slowContrlolsTxSocket && txBuffer.size())
327  {
328  __FE_COUT__ << "Sending now!" << __E__;
329  slowContrlolsTxSocket->send(txBuffer);
330  }
331 
332  if(fp)
333  fflush(fp); // flush anything in aggregate file for reading ease
334 
335  if(firstTime)
336  {
337  if(numOfReadAccessChannels == 0)
338  {
339  __MCOUT_WARN__("There are no slow controls channels with read access!" << __E__);
340  break;
341  }
342  else
343  __COUT__ << "There are " << getSlowControlsChannelCount() << " slow controls channels total. " << numOfReadAccessChannels
344  << " with read access enabled." << __E__;
345  }
346  firstTime = false;
347  } // end main slow controls loop
348 
349  if(fp)
350  fclose(fp);
351 
352  __FE_COUT__ << "Slow controls workloop done." << __E__;
353 
354  return false;
355 } // end slowControlsRunning()
356 catch(...) //
357 {
358  // catch all, then rethrow with local variables needed
359  __FE_SS__;
360 
361  bool isSoftError = false;
362 
363  try
364  {
365  throw;
366  }
367  catch(const __OTS_SOFT_EXCEPTION__& e)
368  {
369  ss << "SOFT Error was caught during slow controls running thread: " << e.what() << std::endl;
370  isSoftError = true;
371  }
372  catch(const std::runtime_error& e)
373  {
374  ss << "Caught an error during slow controls running thread of FE Interface '" << Configurable::theConfigurationRecordName_ << "': " << e.what()
375  << __E__;
376  }
377  catch(...)
378  {
379  ss << "Caught an unknown error during slow controls running thread." << __E__;
380  }
381 
382  // At this point, an asynchronous error has occurred
383  // during front-end running...
384  // Send async error to Gateway
385  // Do this as thread so that workloop can end.
386 
387  __FE_COUT_ERR__ << ss.str();
388 
389  std::thread(
390  [](FEVInterface* fe, const std::string errorMessage, bool isSoftError) { FEVInterface::sendAsyncErrorToGateway(fe, errorMessage, isSoftError); },
391  // pass the values
392  this /*fe*/,
393  ss.str() /*errorMessage*/,
394  isSoftError)
395  .detach();
396 
397  return false;
398 } // end slowControlsRunning()
399 
400 //==============================================================================
401 // SendAsyncErrorToGateway
402 // Static -- thread
403 // Send async error or soft error to gateway
404 // Call this as thread so that parent calling function (workloop) can end.
405 //
406 // Note: be careful not to access fe pointer after HALT
407 // has potentially propagated.. because the pointer might be destructed!
408 void FEVInterface::sendAsyncErrorToGateway(FEVInterface* fe, const std::string& errorMessage, bool isSoftError) try
409 {
410  std::stringstream feHeader;
411  feHeader << ":FE:" << fe->getInterfaceType() << ":" << fe->getInterfaceUID() << ":" << fe->theConfigurationRecordName_ << "\t";
412 
413  if(isSoftError)
414  {
415  __COUT_ERR__ << feHeader.str() << "Sending FE Async SOFT Running Error... \n" << errorMessage << __E__;
416  fe->VStateMachine::parentSupervisor_->setAsyncSoftErrorMessage(errorMessage);
417  }
418  else
419  __COUT_ERR__ << feHeader.str() << "Sending FE Async Running Error... \n" << errorMessage << __E__;
420 
421  XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisor = fe->VStateMachine::parentSupervisor_->allSupervisorInfo_.getGatewayInfo().getDescriptor();
422 
423  SOAPParameters parameters;
424  parameters.addParameter("ErrorMessage", errorMessage);
425 
426  xoap::MessageReference replyMessage =
427  fe->VStateMachine::parentSupervisor_->SOAPMessenger::sendWithSOAPReply(gatewaySupervisor, isSoftError ? "AsyncSoftError" : "AsyncError", parameters);
428 
429  std::stringstream replyMessageSStream;
430  replyMessageSStream << SOAPUtilities::translate(replyMessage);
431  __COUT__ << feHeader.str() << "Received... " << replyMessageSStream.str() << std::endl;
432 
433  if(replyMessageSStream.str().find("Fault") != std::string::npos)
434  {
435  __COUT_ERR__ << feHeader.str() << "Failure to indicate fault to Gateway..." << __E__;
436  throw;
437  }
438 }
439 catch(const xdaq::exception::Exception& e)
440 {
441  if(isSoftError)
442  __COUT__ << "SOAP message failure indicating front-end asynchronous running SOFT "
443  "error back to Gateway: "
444  << e.what() << __E__;
445  else
446  __COUT__ << "SOAP message failure indicating front-end asynchronous running "
447  "error back to Gateway: "
448  << e.what() << __E__;
449  throw; // rethrow and hope error is noticed
450 }
451 catch(...)
452 {
453  if(isSoftError)
454  __COUT__ << "Unknown error encounter indicating front-end asynchronous running "
455  "SOFT error back to Gateway."
456  << __E__;
457  else
458  __COUT__ << "Unknown error encounter indicating front-end asynchronous running "
459  "error back to Gateway."
460  << __E__;
461  throw; // rethrow and hope error is noticed
462 } // end SendAsyncErrorToGateway()
463 
464 //==============================================================================
465 // override WorkLoop::workLoopThread
466 // return false to stop the workloop from calling the thread again
467 bool FEVInterface::workLoopThread(toolbox::task::WorkLoop* /*workLoop*/)
468 {
469  try
470  {
471  continueWorkLoop_ = running(); /* in case users return false, without using continueWorkLoop_*/
472  }
473  catch(...) //
474  {
475  // catch all, then rethrow with local variables needed
476  __FE_SS__;
477 
478  bool isSoftError = false;
479 
480  try
481  {
482  throw;
483  }
484  catch(const __OTS_SOFT_EXCEPTION__& e)
485  {
486  ss << "SOFT Error was caught while running: " << e.what() << std::endl;
487  isSoftError = true;
488  }
489  catch(const std::runtime_error& e)
490  {
491  ss << "Caught an error during running at FE Interface '" << Configurable::theConfigurationRecordName_ << "': " << e.what() << __E__;
492  }
493  catch(...)
494  {
495  ss << "Caught an unknown error during running." << __E__;
496  }
497 
498  // At this point, an asynchronous error has occurred
499  // during front-end running...
500  // Send async error to Gateway
501  // Do this as thread so that workloop can end.
502 
503  __FE_COUT_ERR__ << ss.str();
504 
505  std::thread(
506  [](FEVInterface* fe, const std::string errorMessage, bool isSoftError) { FEVInterface::sendAsyncErrorToGateway(fe, errorMessage, isSoftError); },
507  // pass the values
508  this /*fe*/,
509  ss.str() /*errorMessage*/,
510  isSoftError)
511  .detach();
512 
513  return false;
514  }
515 
516  return continueWorkLoop_;
517 } // end workLoopThread()
518 
519 //==============================================================================
520 // registerFEMacroFunction
521 // used by user-defined front-end interface implementations of this
522 // virtual interface class to register their macro functions.
523 //
524 // Front-end Macro Functions are then made accessible through the ots Control System
525 // web interfaces. The menu consisting of all enabled FEs macros is assembled
526 // by the FE Supervisor (and its FE Interface Manager).
527 void FEVInterface::registerFEMacroFunction(const std::string& feMacroName,
528  frontEndMacroFunction_t feMacroFunction,
529  const std::vector<std::string>& namesOfInputArgs,
530  const std::vector<std::string>& namesOfOutputArgs,
531  uint8_t requiredUserPermissions,
532  const std::string& allowedCallingFEs)
533 {
534  if(mapOfFEMacroFunctions_.find(feMacroName) != mapOfFEMacroFunctions_.end())
535  {
536  __FE_SS__ << "feMacroName '" << feMacroName << "' already exists! Not allowed." << __E__;
537  __FE_COUT_ERR__ << "\n" << ss.str();
538  __FE_SS_THROW__;
539  }
540 
541  mapOfFEMacroFunctions_.insert(std::pair<std::string, frontEndMacroStruct_t>(
542  feMacroName, frontEndMacroStruct_t(feMacroName, feMacroFunction, namesOfInputArgs, namesOfOutputArgs, requiredUserPermissions, allowedCallingFEs)));
543 }
544 
545 //==============================================================================
546 // getFEMacroConstArgument
547 // helper function for getting the value of an argument
548 //
549 // Note: static function
550 const std::string& FEVInterface::getFEMacroConstArgument(frontEndMacroConstArgs_t& args, const std::string& argName)
551 {
552  for(const frontEndMacroArg_t& pair : args)
553  {
554  if(pair.first == argName)
555  {
556  __COUT__ << "argName : " << pair.second << __E__;
557  return pair.second;
558  }
559  }
560  __SS__ << "Requested input argument not found with name '" << argName << "'" << __E__;
561  __SS_THROW__;
562 }
563 
564 //==============================================================================
565 // getFEMacroConstArgumentValue
566 // helper function for getting the copy of the value of an argument
567 template<>
568 std::string ots::getFEMacroConstArgumentValue<std::string>(FEVInterface::frontEndMacroConstArgs_t& args, const std::string& argName)
569 {
570  return FEVInterface::getFEMacroConstArgument(args, argName);
571 }
572 
573 //==============================================================================
574 // getFEMacroArgumentValue
575 // helper function for getting the copy of the value of an argument
576 template<>
577 std::string ots::getFEMacroArgumentValue<std::string>(FEVInterface::frontEndMacroArgs_t& args, const std::string& argName)
578 {
579  return FEVInterface::getFEMacroArgument(args, argName);
580 }
581 
582 //==============================================================================
583 // getFEMacroOutputArgument
584 // helper function for getting the value of an argument
585 //
586 // Note: static function
587 std::string& FEVInterface::getFEMacroArgument(frontEndMacroArgs_t& args, const std::string& argName)
588 {
589  for(std::pair<const std::string /* output arg name */, std::string /* arg output value */>& pair : args)
590  {
591  if(pair.first == argName)
592  return pair.second;
593  }
594  __SS__ << "Requested argument not found with name '" << argName << "'" << __E__;
595  __SS_THROW__;
596 }
597 
598 //==============================================================================
599 // runSequenceOfCommands
600 // runs a sequence of write commands from a linked section of the configuration tree
601 // based on these fields:
602 // - WriteAddress, WriteValue, StartingBitPosition, BitFieldSize
603 void FEVInterface::runSequenceOfCommands(const std::string& treeLinkName)
604 {
605  std::map<uint64_t, uint64_t> writeHistory;
606  uint64_t writeAddress, writeValue, bitMask;
607  uint8_t bitPosition;
608 
609  std::string writeBuffer;
610  std::string readBuffer;
611  char msg[1000];
612  bool ignoreError = true;
613 
614  // ignore errors getting sequence of commands through tree (since it is optional)
615  try
616  {
617  ConfigurationTree configSeqLink = theXDAQContextConfigTree_.getNode(theConfigurationPath_).getNode(treeLinkName);
618 
619  // but throw errors if problems executing the sequence of commands
620  try
621  {
622  if(configSeqLink.isDisconnected())
623  __FE_COUT__ << "Disconnected configure sequence" << __E__;
624  else
625  {
626  __FE_COUT__ << "Handling configure sequence." << __E__;
627  auto childrenMap = configSeqLink.getChildrenMap();
628  for(const auto& child : childrenMap)
629  {
630  // WriteAddress and WriteValue fields
631 
632  writeAddress = child.second.getNode("WriteAddress").getValue<uint64_t>();
633  writeValue = child.second.getNode("WriteValue").getValue<uint64_t>();
634  bitPosition = child.second.getNode("StartingBitPosition").getValue<uint8_t>();
635  bitMask = (1 << child.second.getNode("BitFieldSize").getValue<uint8_t>()) - 1;
636 
637  writeValue &= bitMask;
638  writeValue <<= bitPosition;
639  bitMask = ~(bitMask << bitPosition);
640 
641  // place into write history
642  if(writeHistory.find(writeAddress) == writeHistory.end())
643  writeHistory[writeAddress] = 0; // init to 0
644 
645  writeHistory[writeAddress] &= bitMask; // clear incoming bits
646  writeHistory[writeAddress] |= writeValue; // add incoming bits
647 
648  sprintf(msg,
649  "\t Writing %s: \t %ld(0x%lX) \t %ld(0x%lX)",
650  child.first.c_str(),
651  writeAddress,
652  writeAddress,
653  writeHistory[writeAddress],
654  writeHistory[writeAddress]);
655 
656  __FE_COUT__ << msg << __E__;
657 
658  universalWrite((char*)&writeAddress, (char*)&(writeHistory[writeAddress]));
659  }
660  }
661  }
662  catch(...)
663  {
664  ignoreError = false;
665  throw;
666  }
667  }
668  catch(...)
669  {
670  if(!ignoreError)
671  throw;
672  // else ignoring error
673  __FE_COUT__ << "Unable to access sequence of commands through configuration tree. "
674  << "Assuming no sequence. " << __E__;
675  }
676 } // end runSequenceOfCommands()
677 
678 //==============================================================================
679 // runFrontEndMacro
680 // Helper function to run this FEInterface's own front-end macro
681 // and gets the output arguments back.
682 //
683 // Very similar to FEVInterfacesManager::runFEMacro()
684 //
685 // Note: that argsOut are populated for caller, can just pass empty vector.
686 void FEVInterface::runSelfFrontEndMacro(const std::string& feMacroName,
687  // not equivalent to __ARGS__
688  // left non-const value so caller can modify inputArgs as they are being created
689  const std::vector<FEVInterface::frontEndMacroArg_t>& argsIn,
690  std::vector<FEVInterface::frontEndMacroArg_t>& argsOut)
691 {
692  // have pointer to virtual FEInterface, find Macro structure
693  auto FEMacroIt = this->getMapOfFEMacroFunctions().find(feMacroName);
694  if(FEMacroIt == this->getMapOfFEMacroFunctions().end())
695  {
696  __CFG_SS__ << "FE Macro '" << feMacroName << "' of interfaceID '" << getInterfaceUID() << "' was not found!" << __E__;
697  __CFG_COUT_ERR__ << "\n" << ss.str();
698  __CFG_SS_THROW__;
699  }
700  const FEVInterface::frontEndMacroStruct_t& feMacro = FEMacroIt->second;
701 
702  // check for input arg name match
703  for(unsigned int i = 0; i < argsIn.size(); ++i)
704  if(argsIn[i].first != feMacro.namesOfInputArguments_[i])
705  {
706  __CFG_SS__ << "FE Macro '" << feMacro.feMacroName_ << "' of interfaceID '" << getInterfaceUID() << "' was attempted with a mismatch in"
707  << " a name of an input argument. " << argsIn[i].first << " was given. " << feMacro.namesOfInputArguments_[i] << " expected." << __E__;
708  __CFG_COUT_ERR__ << "\n" << ss.str();
709  __CFG_SS_THROW__;
710  }
711 
712  // check namesOfInputArguments_
713  if(feMacro.namesOfInputArguments_.size() != argsIn.size())
714  {
715  __CFG_SS__ << "FE Macro '" << feMacro.feMacroName_ << "' of interfaceID '" << getInterfaceUID() << "' was attempted with a mismatch in"
716  << " number of input arguments. " << argsIn.size() << " were given. " << feMacro.namesOfInputArguments_.size() << " expected." << __E__;
717  __CFG_COUT_ERR__ << "\n" << ss.str();
718  __CFG_SS_THROW__;
719  }
720 
721  __CFG_COUT__ << "# of input args = " << argsIn.size() << __E__;
722  for(auto& argIn : argsIn)
723  __CFG_COUT__ << argIn.first << ": " << argIn.second << __E__;
724 
725  __CFG_COUT__ << "Launching FE Macro '" << feMacro.feMacroName_ << "' ..." << __E__;
726 
727  argsOut.clear();
728  for(unsigned int i = 0; i < feMacro.namesOfOutputArguments_.size(); ++i)
729  argsOut.push_back(FEVInterface::frontEndMacroArg_t(feMacro.namesOfOutputArguments_[i], "DEFAULT"));
730 
731  // run it!
732  (this->*(feMacro.macroFunction_))(feMacro, argsIn, argsOut);
733 
734  __CFG_COUT__ << "FE Macro complete!" << __E__;
735 
736  __CFG_COUT__ << "# of output args = " << argsOut.size() << __E__;
737  for(const auto& arg : argsOut)
738  __CFG_COUT__ << arg.first << ": " << arg.second << __E__;
739 
740 } // end runSelfFrontEndMacro()
741 
742 //==============================================================================
743 // runFrontEndMacro
744 // run a front-end macro in the target interface plug-in and gets the output arguments
745 // back
746 void FEVInterface::runFrontEndMacro(const std::string& targetInterfaceID,
747  const std::string& feMacroName,
748  const std::vector<FEVInterface::frontEndMacroArg_t>& inputArgs,
749  std::vector<FEVInterface::frontEndMacroArg_t>& outputArgs) const
750 {
751  __FE_COUTV__(targetInterfaceID);
752  __FE_COUTV__(VStateMachine::parentSupervisor_);
753 
754  std::string inputArgsStr = StringMacros::vectorToString(inputArgs, ";" /*primaryDelimeter*/, "," /*secondaryDelimeter*/);
755 
756  __FE_COUTV__(inputArgsStr);
757 
758  xoap::MessageReference message = SOAPUtilities::makeSOAPMessageReference("FECommunication");
759 
760  SOAPParameters parameters;
761  parameters.addParameter("type", "feMacro");
762  parameters.addParameter("requester", FEVInterface::interfaceUID_);
763  parameters.addParameter("targetInterfaceID", targetInterfaceID);
764  parameters.addParameter("feMacroName", feMacroName);
765  parameters.addParameter("inputArgs", inputArgsStr);
766  SOAPUtilities::addParameters(message, parameters);
767 
768  __FE_COUT__ << "Sending FE communication: " << SOAPUtilities::translate(message) << __E__;
769 
770  xoap::MessageReference replyMessage = VStateMachine::parentSupervisor_->SOAPMessenger::sendWithSOAPReply(
771  VStateMachine::parentSupervisor_->allSupervisorInfo_.getAllMacroMakerTypeSupervisorInfo().begin()->second.getDescriptor(), message);
772 
773  __FE_COUT__ << "Response received: " << SOAPUtilities::translate(replyMessage) << __E__;
774 
775  SOAPParameters rxParameters;
776  rxParameters.addParameter("Error");
777  SOAPUtilities::receive(replyMessage, rxParameters);
778 
779  std::string error = rxParameters.getValue("Error");
780 
781  if(error != "")
782  {
783  // error occurred!
784  __FE_SS__ << "Error transmitting request to target interface '" << targetInterfaceID << "' from '" << FEVInterface::interfaceUID_ << ".' " << error
785  << __E__;
786  __FE_SS_THROW__;
787  }
788 
789  // extract output args
790  SOAPParameters argsOutParameter;
791  argsOutParameter.addParameter("outputArgs");
792  SOAPUtilities::receive(replyMessage, argsOutParameter);
793 
794  std::string outputArgsStr = argsOutParameter.getValue("outputArgs");
795  std::set<char> pairDelimiter({';'}), nameValueDelimiter({','});
796 
797  std::map<std::string, std::string> mapToReturn;
798  StringMacros::getMapFromString(outputArgsStr, mapToReturn, pairDelimiter, nameValueDelimiter);
799 
800  outputArgs.clear();
801  for(auto& mapPair : mapToReturn)
802  outputArgs.push_back(mapPair);
803 
804 } // end runFrontEndMacro()
805 
806 //==============================================================================
807 // receiveFromFrontEnd
808 // specialized template function for T=std::string
809 //
810 // Note: requester can be a wildcard string as defined in StringMacros
811 void FEVInterface::receiveFromFrontEnd(const std::string& requester, std::string& retValue, unsigned int timeoutInSeconds) const
812 {
813  __FE_COUTV__(requester);
814  __FE_COUTV__(parentSupervisor_);
815 
816  std::string data = "0";
817  //bool found = false;
818  while(1)
819  {
820  // mutex scope
821  {
822  std::lock_guard<std::mutex> lock(parentInterfaceManager_->frontEndCommunicationReceiveMutex_);
823 
824  auto receiveBuffersForTargetIt = parentInterfaceManager_->frontEndCommunicationReceiveBuffer_.find(FEVInterface::interfaceUID_);
825  if(receiveBuffersForTargetIt != parentInterfaceManager_->frontEndCommunicationReceiveBuffer_.end())
826  {
827  __FE_COUT__ << "Number of source buffers found for front-end '" << FEVInterface::interfaceUID_
828  << "': " << receiveBuffersForTargetIt->second.size() << __E__;
829 
830  for(auto& buffPair : receiveBuffersForTargetIt->second)
831  __FE_COUTV__(buffPair.first);
832 
833  // match requester to map of buffers
834  std::string sourceBufferId = "";
835  std::queue<std::string /*value*/>& sourceBuffer =
836  StringMacros::getWildCardMatchFromMap(requester, receiveBuffersForTargetIt->second, &sourceBufferId);
837 
838  __FE_COUT__ << "Found source buffer '" << sourceBufferId << "' with size " << sourceBuffer.size() << __E__;
839 
840  if(sourceBuffer.size())
841  {
842  __FE_COUT__ << "Found a value in queue of size " << sourceBuffer.size() << __E__;
843 
844  // remove from receive buffer
845  retValue = sourceBuffer.front();
846  sourceBuffer.pop();
847  return;
848  }
849  else
850  __FE_COUT__ << "Source buffer empty for '" << requester << "'" << __E__;
851  }
852 
853  // else, not found...
854 
855  // if out of time, throw error
856  if(!timeoutInSeconds)
857  {
858  __FE_SS__ << "Timeout (" << timeoutInSeconds << " s) waiting for front-end communication from " << requester << "." << __E__;
859  __FE_SS_THROW__;
860  }
861  // else, there is still hope
862 
863  } // end mutex scope
864 
865  // else try again in a sec
866  __FE_COUT__ << "Waiting for front-end communication from " << requester << " for " << timeoutInSeconds << " more seconds..." << __E__;
867 
868  --timeoutInSeconds;
869  sleep(1); // wait a sec
870  } // end timeout loop
871 
872  // should never get here
873 } // end receiveFromFrontEnd()
874 
875 //==============================================================================
876 // receiveFromFrontEnd
877 // specialized template function for T=std::string
878 // Note: if called without template <T> syntax, necessary because types of
879 // std::basic_string<char> cause compiler problems if no string specific function
880 std::string FEVInterface::receiveFromFrontEnd(const std::string& requester, unsigned int timeoutInSeconds) const
881 {
882  std::string retValue;
883  FEVInterface::receiveFromFrontEnd(requester, retValue, timeoutInSeconds);
884  return retValue;
885 } // end receiveFromFrontEnd()
886 
887 //==============================================================================
888 // macroStruct_t constructor
889 FEVInterface::macroStruct_t::macroStruct_t(const std::string& macroString)
890 {
891  __COUTV__(macroString);
892 
893  // example macro string:
894  // {"name":"testPublic","sequence":"0:w:1001:writeVal,1:r:1001:","time":"Sat Feb 0
895  // 9 2019 10:42:03 GMT-0600 (Central Standard Time)","notes":"","LSBF":"false"}@
896 
897  std::vector<std::string> extractVec;
898  StringMacros::getVectorFromString(macroString, extractVec, {'"'});
899 
900  __COUTV__(StringMacros::vectorToString(extractVec, " ||| "));
901 
902  enum
903  {
904  MACRONAME_NAME_INDEX = 1,
905  MACRONAME_VALUE_INDEX = 3,
906  SEQUENCE_NAME_INDEX = 5,
907  SEQUENCE_VALUE_INDEX = 7,
908  LSBF_NAME_INDEX = 17,
909  LSFBF_VALUE_INDEX = 19,
910  };
911 
912  // verify fields in sequence (for sanity)
913  if(MACRONAME_NAME_INDEX >= extractVec.size() || extractVec[MACRONAME_NAME_INDEX] != "name")
914  {
915  __SS__ << "Invalid sequence, 'name' expected in position " << MACRONAME_NAME_INDEX << __E__;
916  __SS_THROW__;
917  }
918  if(SEQUENCE_NAME_INDEX >= extractVec.size() || extractVec[SEQUENCE_NAME_INDEX] != "sequence")
919  {
920  __SS__ << "Invalid sequence, 'sequence' expected in position " << SEQUENCE_NAME_INDEX << __E__;
921  __SS_THROW__;
922  }
923  if(LSBF_NAME_INDEX >= extractVec.size() || extractVec[LSBF_NAME_INDEX] != "LSBF")
924  {
925  __SS__ << "Invalid sequence, 'LSBF' expected in position " << LSBF_NAME_INDEX << __E__;
926  __SS_THROW__;
927  }
928  macroName_ = extractVec[MACRONAME_VALUE_INDEX];
929  __COUTV__(macroName_);
930  lsbf_ = extractVec[LSFBF_VALUE_INDEX] == "false" ? false : true;
931  __COUTV__(lsbf_);
932  std::string& sequence = extractVec[SEQUENCE_VALUE_INDEX];
933  __COUTV__(sequence);
934 
935  std::vector<std::string> sequenceCommands;
936  StringMacros::getVectorFromString(sequence, sequenceCommands, {','});
937 
938  __COUTV__(StringMacros::vectorToString(sequenceCommands, " ### "));
939 
940  for(auto& command : sequenceCommands)
941  {
942  __COUTV__(command);
943 
944  // Note: the only way to distinguish between variable and data
945  // is hex characters or not (lower and upper case allowed for hex)
946 
947  std::vector<std::string> commandPieces;
948  StringMacros::getVectorFromString(command, commandPieces, {':'});
949 
950  __COUTV__(StringMacros::vectorToString(commandPieces, " ### "));
951 
952  __COUTV__(commandPieces.size());
953 
954  // command format
955  // index | type | address/sleep[ms] | data
956  // d is delay (1+2)
957  // w is write (1+3)
958  // r is read (1+2/3 with arg)
959 
960  // extract input arguments, as the variables in address/data fields
961  // extract output arguments, as the variables in read data fields
962 
963  if(commandPieces.size() < 3 || commandPieces.size() > 4 || commandPieces[1].size() != 1)
964  {
965  __SS__ << "Invalid command type specified in command string: " << command << __E__;
966  __SS_THROW__;
967  }
968 
969  //==========================
970  // Use lambda to identify variable name in field
971  std::function<bool(const std::string& /*field value*/
972  )>
973  localIsVariable = [/*capture variable*/](const std::string& fieldValue) {
974  // create local message facility subject
975  std::string mfSubject_ = "isVar";
976  __GEN_COUTV__(fieldValue);
977 
978  // return false if all hex characters found
979  for(const auto& c : fieldValue)
980  if(!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
981  return true; // is variable name!
982  return false; // else is a valid hex string, so not variable name
983 
984  }; //end local lambda localIsVariable()
985 
986  if(commandPieces[1][0] == 'r' && commandPieces.size() == 4) // read type
987  {
988  __COUT__ << "Read type found." << __E__;
989  // 2: address or optional variable name
990  // 3: optional variable name
991 
992  operations_.push_back(std::make_pair(macroStruct_t::OP_TYPE_READ, readOps_.size()));
993 
994  readOps_.push_back(macroStruct_t::readOp_t());
995  readOps_.back().addressIsVar_ = localIsVariable(commandPieces[2]);
996  readOps_.back().dataIsVar_ = localIsVariable(commandPieces[3]);
997 
998  if(!readOps_.back().addressIsVar_)
999  {
1000  if(lsbf_) // flip byte order
1001  {
1002  std::string lsbfData = "";
1003 
1004  // always add leading 0 to guarantee do not miss data
1005  commandPieces[2] = "0" + commandPieces[2];
1006  for(unsigned int i = 0; i < commandPieces[2].size() / 2; ++i)
1007  {
1008  __COUTV__(commandPieces[2].size() - 2 * (i + 1));
1009  // add one byte at a time, backwards
1010  lsbfData += commandPieces[2][commandPieces[2].size() - 2 * (i + 1)];
1011  lsbfData += commandPieces[2][commandPieces[2].size() - 2 * (i + 1) + 1];
1012  __COUTV__(lsbfData);
1013  }
1014  __COUTV__(lsbfData);
1015  StringMacros::getNumber("0x" + lsbfData, readOps_.back().address_);
1016  }
1017  else
1018  StringMacros::getNumber("0x" + commandPieces[2], readOps_.back().address_);
1019  }
1020  else
1021  {
1022  readOps_.back().addressVarName_ = commandPieces[2];
1023  __COUTV__(readOps_.back().addressVarName_);
1024 
1025  namesOfInputArguments_.emplace(readOps_.back().addressVarName_);
1026  }
1027 
1028  if(readOps_.back().dataIsVar_)
1029  {
1030  readOps_.back().dataVarName_ = commandPieces[3];
1031  __COUTV__(readOps_.back().dataVarName_);
1032 
1033  namesOfOutputArguments_.emplace(readOps_.back().dataVarName_);
1034  }
1035  }
1036  else if(commandPieces[1][0] == 'w' && commandPieces.size() == 4) // write type
1037  {
1038  __COUT__ << "Write type found." << __E__;
1039  // 2: address or optional variable name
1040  // 3: data or optional variable name
1041 
1042  operations_.push_back(std::make_pair(macroStruct_t::OP_TYPE_WRITE, writeOps_.size()));
1043 
1044  writeOps_.push_back(macroStruct_t::writeOp_t());
1045  writeOps_.back().addressIsVar_ = localIsVariable(commandPieces[2]);
1046  writeOps_.back().dataIsVar_ = localIsVariable(commandPieces[3]);
1047 
1048  if(!writeOps_.back().addressIsVar_)
1049  {
1050  if(lsbf_) // flip byte order
1051  {
1052  std::string lsbfData = "";
1053 
1054  // always add leading 0 to guarantee do not miss data
1055  commandPieces[2] = "0" + commandPieces[2];
1056  for(unsigned int i = 0; i < commandPieces[2].size() / 2; ++i)
1057  {
1058  __COUTV__(commandPieces[2].size() - 2 * (i + 1));
1059  // add one byte at a time, backwards
1060  lsbfData += commandPieces[2][commandPieces[2].size() - 2 * (i + 1)];
1061  lsbfData += commandPieces[2][commandPieces[2].size() - 2 * (i + 1) + 1];
1062  __COUTV__(lsbfData);
1063  }
1064  __COUTV__(lsbfData);
1065  StringMacros::getNumber("0x" + lsbfData, writeOps_.back().address_);
1066  }
1067  else
1068  StringMacros::getNumber("0x" + commandPieces[2], writeOps_.back().address_);
1069  }
1070  else
1071  {
1072  writeOps_.back().addressVarName_ = commandPieces[2];
1073  __COUTV__(writeOps_.back().addressVarName_);
1074 
1075  namesOfInputArguments_.emplace(writeOps_.back().addressVarName_);
1076  }
1077 
1078  if(!writeOps_.back().dataIsVar_)
1079  {
1080  if(lsbf_) // flip byte order
1081  {
1082  std::string lsbfData = "";
1083 
1084  // always add leading 0 to guarantee do not miss data
1085  commandPieces[2] = "0" + commandPieces[3];
1086  for(unsigned int i = 0; i < commandPieces[3].size() / 2; ++i)
1087  {
1088  __COUTV__(commandPieces[3].size() - 2 * (i + 1));
1089  // add one byte at a time, backwards
1090  lsbfData += commandPieces[3][commandPieces[3].size() - 2 * (i + 1)];
1091  lsbfData += commandPieces[3][commandPieces[3].size() - 2 * (i + 1) + 1];
1092  __COUTV__(lsbfData);
1093  }
1094  __COUTV__(lsbfData);
1095  StringMacros::getNumber("0x" + lsbfData, writeOps_.back().data_);
1096  }
1097  else
1098  StringMacros::getNumber("0x" + commandPieces[3], writeOps_.back().data_);
1099  }
1100  else
1101  {
1102  writeOps_.back().dataVarName_ = commandPieces[3];
1103  __COUTV__(writeOps_.back().dataVarName_);
1104 
1105  namesOfInputArguments_.emplace(writeOps_.back().dataVarName_);
1106  }
1107  }
1108  else if(commandPieces[1][0] == 'd' && commandPieces.size() == 3) // delay type
1109  {
1110  __COUT__ << "Delay type found." << __E__;
1111  // 2: delay[ms] or optional variable name
1112 
1113  operations_.push_back(std::make_pair(macroStruct_t::OP_TYPE_DELAY, delayOps_.size()));
1114 
1115  delayOps_.push_back(macroStruct_t::delayOp_t());
1116  delayOps_.back().delayIsVar_ = localIsVariable(commandPieces[2]);
1117 
1118  if(!delayOps_.back().delayIsVar_)
1119  StringMacros::getNumber("0x" + commandPieces[2], delayOps_.back().delay_);
1120  else
1121  {
1122  delayOps_.back().delayVarName_ = commandPieces[2];
1123  __COUTV__(delayOps_.back().delayVarName_);
1124 
1125  namesOfInputArguments_.emplace(delayOps_.back().delayVarName_);
1126  }
1127  }
1128  else // invalid type
1129  {
1130  __SS__ << "Invalid command type '" << commandPieces[1][0] << "' specified with " << commandPieces.size() << " components." << __E__;
1131  __SS_THROW__;
1132  }
1133 
1134  } // end sequence commands extraction loop
1135 
1136  __COUT__ << operations_.size() << " operations extracted: \n\t" << readOps_.size() << " reads \n\t" << writeOps_.size() << " writes \n\t"
1137  << delayOps_.size() << " delays" << __E__;
1138 
1139  __COUT__ << "Input arguments: " << __E__;
1140  for(const auto& inputArg : namesOfInputArguments_)
1141  __COUT__ << "\t" << inputArg << __E__;
1142 
1143  __COUT__ << "Output arguments: " << __E__;
1144  for(const auto& outputArg : namesOfOutputArguments_)
1145  __COUT__ << "\t" << outputArg << __E__;
1146 
1147 } // end macroStruct_t constructor
1148 
1149 //==============================================================================
1150 // runMacro
1151 void FEVInterface::runMacro(FEVInterface::macroStruct_t& macro, std::map<std::string /*name*/, uint64_t /*value*/>& variableMap)
1152 {
1153  // Similar to FEVInterface::runSequenceOfCommands()
1154 
1155  __FE_COUT__ << "Running Macro '" << macro.macroName_ << "' of " << macro.operations_.size() << " operations." << __E__;
1156 
1157  for(auto& op : macro.operations_)
1158  {
1159  if(op.first == macroStruct_t::OP_TYPE_READ)
1160  {
1161  __FE_COUT__ << "Doing read op..." << __E__;
1162  macroStruct_t::readOp_t& readOp = macro.readOps_[op.second];
1163  if(readOp.addressIsVar_)
1164  {
1165  __FE_COUTV__(readOp.addressVarName_);
1166  readOp.address_ = variableMap.at(readOp.addressVarName_);
1167  }
1168 
1169  uint64_t dataValue = 0;
1170 
1171  __FE_COUT__ << std::hex << "Read address: \t 0x" << readOp.address_ << __E__ << std::dec;
1172 
1173  universalRead((char*)&readOp.address_, (char*)&dataValue);
1174 
1175  __FE_COUT__ << std::hex << "Read data: \t 0x" << dataValue << __E__ << std::dec;
1176 
1177  if(readOp.dataIsVar_)
1178  {
1179  __FE_COUTV__(readOp.dataVarName_);
1180  variableMap.at(readOp.dataVarName_) = dataValue;
1181  }
1182 
1183  } // end read op
1184  else if(op.first == macroStruct_t::OP_TYPE_WRITE)
1185  {
1186  __FE_COUT__ << "Doing write op..." << __E__;
1187  macroStruct_t::writeOp_t& writeOp = macro.writeOps_[op.second];
1188  if(writeOp.addressIsVar_)
1189  {
1190  __FE_COUTV__(writeOp.addressVarName_);
1191  writeOp.address_ = variableMap.at(writeOp.addressVarName_);
1192  }
1193  if(writeOp.dataIsVar_)
1194  {
1195  __FE_COUTV__(writeOp.dataVarName_);
1196  writeOp.data_ = variableMap.at(writeOp.dataVarName_);
1197  }
1198 
1199  __FE_COUT__ << std::hex << "Write address: \t 0x" << writeOp.address_ << __E__ << std::dec;
1200  __FE_COUT__ << std::hex << "Write data: \t 0x" << writeOp.data_ << __E__ << std::dec;
1201 
1202  universalWrite((char*)&writeOp.address_, (char*)&writeOp.data_);
1203 
1204  } // end write op
1205  else if(op.first == macroStruct_t::OP_TYPE_DELAY)
1206  {
1207  __FE_COUT__ << "Doing delay op..." << __E__;
1208 
1209  macroStruct_t::delayOp_t& delayOp = macro.delayOps_[op.second];
1210  if(delayOp.delayIsVar_)
1211  {
1212  __FE_COUTV__(delayOp.delayVarName_);
1213  delayOp.delay_ = variableMap.at(delayOp.delayVarName_);
1214  }
1215 
1216  __FE_COUT__ << std::dec << "Delay ms: \t " << delayOp.delay_ << __E__;
1217 
1218  usleep(delayOp.delay_ /*ms*/ * 1000);
1219 
1220  } // end delay op
1221  else // invalid type
1222  {
1223  __FE_SS__ << "Invalid command type '" << op.first << "!'" << __E__;
1224  __FE_SS_THROW__;
1225  }
1226 
1227  } // end operations loop
1228 
1229 } // end runMacro