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