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