otsdaq  3.03.00
FEVInterface.cc
1 #include "otsdaq/FECore/FEVInterface.h"
2 #include "otsdaq/CoreSupervisors/CoreSupervisorBase.h"
3 #include "otsdaq/FECore/FEVInterfacesManager.h"
4 #include "otsdaq/Macros/BinaryStringMacros.h"
5 #include "otsdaq/NetworkUtilities/UDPDataStreamerBase.h"
6 
7 #include <TFormula.h>
8 
9 #define TRACE_NAME "FEVInterface"
10 #include <iostream>
11 #include <sstream>
12 #include <thread> //for std::thread
13 
14 using namespace ots;
15 
16 const std::string FEVInterface::UNKNOWN_TYPE = "UNKNOWN";
17 
18 //==============================================================================
19 FEVInterface::FEVInterface(const std::string& interfaceUID,
20  const ConfigurationTree& theXDAQContextConfigTree,
21  const std::string& configurationPath)
22  : WorkLoop(interfaceUID)
23  , Configurable(theXDAQContextConfigTree, configurationPath)
24  , VStateMachine(interfaceUID)
25  , slowControlsWorkLoop_(interfaceUID + "-SlowControls", this)
26  , interfaceUID_(interfaceUID)
27  , interfaceType_(FEVInterface::UNKNOWN_TYPE)
28  , mfSubject_(interfaceUID)
29 {
30  // NOTE!! be careful to not decorate with __FE_COUT__ because in the constructor the
31  // base class versions of function (e.g. getInterfaceType) are called because the
32  // derived class has not been instantiate yet!
33  // Instead use __GEN_COUT__ which decorates using mfSubject_
34 
35  try
36  {
37  interfaceType_ = theXDAQContextConfigTree_.getBackNode(theConfigurationPath_)
38  .getNode("FEInterfacePluginName")
39  .getValue<std::string>();
40  }
41  catch(...) //ignore exception, but give warning
42  {
43  __GEN_COUT__
44  << "FEInterface type could not be determined in base class from "
45  "configuration tree path; "
46  "the type may be defined subsequently by the inheriting class (e.g. to "
47  "take advantage of Slow Controls caching functionality, "
48  "the FEInterface type should be defined for all frontend interfaces)"
49  << __E__;
50  }
51 
52  //add generic FE Macros that exist for all FEVInterfaces
53  {
54  registerFEMacroFunction(
55  "Slow Controls Pause/Resume",
57  &FEVInterface::PauseResumeSlowControls),
58  std::vector<std::string>{"Pause Slow Controls (Default = false)"},
59  std::vector<std::string>{"Result"},
60  1, // requiredUserPermissions
61  "255",
62  "This FE Macro is used to Pause or Resume the Slow Controls workloop, which "
63  "could be valuable while debugging front-ends.");
64  } //end add generic FE Macros that exist for all FEVInterfaces
65 
66  __GEN_COUT__ << "Constructed." << __E__;
67 } // end constructor()
68 
69 //==============================================================================
70 FEVInterface::~FEVInterface(void)
71 {
72  // NOTE:: be careful not to call __FE_COUT__ decoration because it might use the tree
73  // depending on child class overrides, and it may already be destructed partially.
74  // Instead use __GEN_COUT__ which decorates using mfSubject_
75  __GEN_COUT__ << "Destructed." << __E__;
76 } // end destructor()
77 
78 //========================================================================
79 void FEVInterface::PauseResumeSlowControls(__ARGS__)
80 {
81  slowControlsWorkLoopShouldRun_ =
82  __GET_ARG_IN__("Pause Slow Controls (Default = false)", bool, false);
83  __FE_COUTV__(slowControlsWorkLoopShouldRun_);
84 
85  std::stringstream outss;
86  outss << "Slow Controls workloop is "
87  << (slowControlsWorkLoopShouldRun_ ? "paused." : "active.");
88 
89  __SET_ARG_OUT__("Result", outss.str());
90 } // end PauseResumeSlowControls()
91 
92 //==============================================================================
94 try
95 {
96  //do not use __GEN_COUT__ as mfSubject may not be setup
97  __COUT__ << "configureSlowControls path=" << theConfigurationPath_ << __E__;
98 
99  // Start artdaq metric manager here, if possible
100  if(metricMan && !metricMan->Running() && metricMan->Initialized())
101  {
102  __COUT__ << "Metric manager starting..." << __E__;
103  metricMan->do_start();
104  __COUT__ << "Metric manager started." << __E__;
105  }
106  else if(!metricMan || !metricMan->Initialized())
107  __COUT__ << "Metric manager could not be started! metricMan: " << metricMan
108  << " Initialized()= " << (metricMan ? metricMan->Initialized() : 0)
109  << __E__;
110  else
111  __COUT__ << "Metric manager already started." << __E__;
112 
113  mapOfSlowControlsChannels_.clear(); // reset
114 
115  //allow to possible position for link "LinkToSlowControlsChannelTable"
116  // 1. back 1 node (e.g. at generic FEInterface table)
117  // 2. at the config path (e.g. for special FE children cases)
118 
119  bool type1 = true;
120  std::string errMessage;
121  try
122  {
123  ConfigurationTree testNode =
124  theXDAQContextConfigTree_.getNode(theConfigurationPath_)
125  .getNode("LinkToSlowControlsChannelTable");
126  __COUTV__(testNode.isDisconnected());
127  type1 = false;
128  }
129  catch(...)
130  { /* ignore */
131  }
132 
133  if(type1)
135  theXDAQContextConfigTree_.getBackNode(theConfigurationPath_)
136  .getNode("LinkToSlowControlsChannelTable"),
138  else
139  {
140  try
141  {
143  theXDAQContextConfigTree_.getNode(theConfigurationPath_)
144  .getNode("LinkToSlowControlsChannelTable"),
146  }
147  catch(const std::runtime_error& e)
148  {
149  __SS__ << "Configuring slow controls channels encountered an error: "
150  << e.what();
151  __SS_THROW__;
152  }
153  }
154 
155 } // end configureSlowControls()
156 catch(const std::runtime_error& e)
157 {
158  __SS__ << "Error was caught while configuring slow controls: " << e.what() << __E__;
159  __SS_THROW__;
160 }
161 catch(...)
162 {
163  __SS__ << "Unknown error was caught while configuring slow controls." << __E__;
164  try
165  {
166  throw;
167  } //one more try to printout extra info
168  catch(const std::exception& e)
169  {
170  ss << "Exception message: " << e.what();
171  }
172  catch(...)
173  {
174  }
175  __SS_THROW__;
176 } // end configureSlowControls() catch
177 
178 //==============================================================================
183  ConfigurationTree slowControlsGroupLink,
184  std::map<std::string /* ROC UID*/, FESlowControlsChannel>* mapOfSlowControlsChannels)
185 {
186  if(slowControlsGroupLink.isDisconnected())
187  {
188  __FE_COUT__
189  << "slowControlsGroupLink is disconnected, so done configuring slow controls."
190  << __E__;
191  return;
192  }
193  __FE_COUT__ << "slowControlsGroupLink is valid! Adding slow controls channels..."
194  << __E__;
195 
196  std::vector<std::pair<std::string, ConfigurationTree> > groupLinkChildren =
197  slowControlsGroupLink.getChildren();
198  for(auto& groupLinkChild : groupLinkChildren)
199  {
200  // skip channels that are off
201  if(!(groupLinkChild.second.getNode(TableViewColumnInfo::COL_NAME_STATUS)
202  .getValue<bool>()))
203  continue;
204 
205  __FE_COUT__ << "Channel:" << slowControlsGroupLink.getTableName() << "/"
206  << getInterfaceUID() << "/" << groupLinkChild.first
207  << "\t Type:" << groupLinkChild.second.getNode("ChannelDataType")
208  << __E__;
209 
210  // Unit transforms
211  std::string transformation = "";
212  try
213  {
214  transformation =
215  groupLinkChild.second.getNode("Transformation").getValue<std::string>();
216  }
217  catch(...)
218  {
219  __FE_COUT__ << "Slow controls 'Transformation' setting not found." << __E__;
220  }
221 
222  mapOfSlowControlsChannels->insert(std::pair<std::string, FESlowControlsChannel>(
223  groupLinkChild.first,
225  this,
226  groupLinkChild.first,
227  groupLinkChild.second.getNode("ChannelDataType").getValue<std::string>(),
228  groupLinkChild.second.getNode("UniversalInterfaceAddress")
229  .getValue<std::string>(),
230  groupLinkChild.second.getNode("Transformation").getValue<std::string>(),
231  groupLinkChild.second.getNode("UniversalDataBitOffset")
232  .getValue<unsigned int>(),
233  groupLinkChild.second.getNode("ReadAccess").getValue<bool>(),
234  groupLinkChild.second.getNode("WriteAccess").getValue<bool>(),
235  groupLinkChild.second.getNode("MonitoringEnabled").getValue<bool>(),
236  groupLinkChild.second.getNode("RecordChangesOnly").getValue<bool>(),
237  groupLinkChild.second.getNode("DelayBetweenSamplesInSeconds")
238  .getValue<time_t>(),
239  groupLinkChild.second.getNode("LocalSavingEnabled").getValue<bool>(),
240  groupLinkChild.second.getNode("LocalFilePath").getValue<std::string>(),
241  groupLinkChild.second.getNode("RadixFileName").getValue<std::string>(),
242  groupLinkChild.second.getNode("SaveBinaryFile").getValue<bool>(),
243  groupLinkChild.second.getNode("AlarmsEnabled").getValue<bool>(),
244  groupLinkChild.second.getNode("LatchAlarms").getValue<bool>(),
245  groupLinkChild.second.getNode("LowLowThreshold").getValue<std::string>(),
246  groupLinkChild.second.getNode("LowThreshold").getValue<std::string>(),
247  groupLinkChild.second.getNode("HighThreshold").getValue<std::string>(),
248  groupLinkChild.second.getNode("HighHighThreshold")
249  .getValue<std::string>())));
250  }
251  __FE_COUT__ << "Added " << mapOfSlowControlsChannels->size()
252  << " slow controls channels." << __E__;
253 
254 } // end addSlowControlsChannels()
255 
256 //==============================================================================
259 {
260  slowControlsChannelsIterator_ = mapOfSlowControlsChannels_.begin();
261 } // end resetSlowControlsChannelIterator()
262 
263 //==============================================================================
266 {
267  if(slowControlsChannelsIterator_ == mapOfSlowControlsChannels_.end())
268  return nullptr;
269 
270  return &(
271  (slowControlsChannelsIterator_++)->second); // return iterator, then increment
272 } // end getNextSlowControlsChannel()
273 
274 //==============================================================================
277 {
278  return mapOfSlowControlsChannels_.size();
279 } // end getSlowControlsChannelCount()
280 
281 //==============================================================================
283 try
284 {
285  __FE_COUT__ << "slowControlsRunning" << __E__;
286 
287  if(getSlowControlsChannelCount() == 0)
288  {
289  __FE_COUT__
290  << "No slow controls channels to monitor, exiting slow controls workloop."
291  << __E__;
292  return false;
293  }
294 
295  FESlowControlsChannel* channel;
296 
297  const unsigned int txBufferSz = 1500;
298  const unsigned int txBufferFullThreshold = 750;
299  std::string txBuffer;
300  txBuffer.reserve(txBufferSz);
301 
302  ConfigurationTree FEInterfaceNode =
303  theXDAQContextConfigTree_.getBackNode(theConfigurationPath_);
304 
305  // attempt to make Slow Controls transfer socket
306  std::unique_ptr<UDPDataStreamerBase> slowContrlolsTxSocket;
307  std::string slowControlsSupervisorIPAddress = "", slowControlsSelfIPAddress = "";
308  int slowControlsSupervisorPort = 0, slowControlsSelfPort = 0;
309  try
310  {
311  ConfigurationTree slowControlsInterfaceLink =
312  FEInterfaceNode.getNode("LinkToSlowControlsSupervisorTable");
313 
314  if(slowControlsInterfaceLink.isDisconnected())
315  {
316  __FE_SS__ << "slowControlsInterfaceLink is disconnected, so no socket made."
317  << __E__;
318  __FE_SS_THROW__;
319  }
320 
321  slowControlsSelfIPAddress =
322  FEInterfaceNode.getNode("SlowControlsTxSocketIPAddress")
323  .getValue<std::string>();
324  slowControlsSelfPort =
325  FEInterfaceNode.getNode("SlowControlsTxSocketPort").getValue<int>();
326  slowControlsSupervisorIPAddress =
327  slowControlsInterfaceLink.getNode("IPAddress").getValue<std::string>();
328  slowControlsSupervisorPort =
329  slowControlsInterfaceLink.getNode("Port").getValue<int>();
330  }
331  catch(...)
332  {
333  __FE_COUT__ << "Link to slow controls supervisor is missing, so no socket made."
334  << __E__;
335  }
336 
337  bool txBufferUsed = false;
338  if(slowControlsSupervisorPort && slowControlsSelfPort &&
339  slowControlsSupervisorIPAddress != "" && slowControlsSelfIPAddress != "")
340  {
341  __FE_COUT__ << "slowControlsInterfaceLink is valid! Create tx socket..." << __E__;
342  slowContrlolsTxSocket.reset(
343  new UDPDataStreamerBase(slowControlsSelfIPAddress,
344  slowControlsSelfPort,
345  slowControlsSupervisorIPAddress,
346  slowControlsSupervisorPort));
347  txBufferUsed = true;
348  }
349  else
350  {
351  __FE_COUT__ << "Invalid Slow Controls socket parameters, so no socket made."
352  << __E__;
353  }
354 
355  // check if aggregate saving
356 
357  FILE* fp = 0;
358  bool aggregateFileIsBinaryFormat = false;
359  try
360  {
361  if(FEInterfaceNode.getNode("SlowControlsLocalAggregateSavingEnabled")
362  .getValue<bool>())
363  {
364  aggregateFileIsBinaryFormat =
365  FEInterfaceNode.getNode("SlowControlsSaveBinaryFile").getValue<bool>();
366 
367  __FE_COUT_INFO__ << "Slow Controls Aggregate Saving turned On BinaryFormat="
368  << aggregateFileIsBinaryFormat << __E__;
369 
370  std::string saveFullFileName =
371  FEInterfaceNode.getNode("SlowControlsLocalFilePath")
372  .getValue<std::string>() +
373  "/" +
374  FEInterfaceNode.getNode("SlowControlsRadixFileName")
375  .getValue<std::string>() +
376  "-" + FESlowControlsChannel::underscoreString(getInterfaceUID()) + "-" +
377  std::to_string(time(0)) + (aggregateFileIsBinaryFormat ? ".dat" : ".txt");
378 
379  fp =
380  fopen(saveFullFileName.c_str(), aggregateFileIsBinaryFormat ? "ab" : "a");
381  if(!fp)
382  {
383  __FE_COUT_ERR__
384  << "Failed to open slow controls channel file: " << saveFullFileName
385  << __E__;
386  // continue on, just nothing will be saved
387  }
388  else
389  __FE_COUT_INFO__
390  << "Slow controls aggregate file opened: " << saveFullFileName
391  << __E__;
392  }
393  }
394  catch(...)
395  {
396  } // do nothing
397 
398  if(!aggregateFileIsBinaryFormat)
399  __FE_COUT_INFO__ << "Slow Controls Aggregate Saving turned off." << __E__;
400 
401  time_t timeCounter = 0;
402 
403  unsigned int numOfReadAccessChannels = 0;
404  bool firstTime = true;
405  while(slowControlsWorkLoop_.getContinueWorkLoop())
406  {
407  // __FE_COUT__ << "..." << __E__;
408 
409  sleep(1); // seconds
410  ++timeCounter;
411  if(!slowControlsWorkLoopShouldRun_)
412  continue;
413 
414  if(txBuffer.size())
415  __FE_COUT__ << "txBuffer sz=" << txBuffer.size() << __E__;
416 
417  txBuffer.resize(0); // clear buffer a la txBuffer = "";
418 
419  //__FE_COUT__ << "timeCounter=" << timeCounter << __E__;
420  //__FE_COUT__ << "txBuffer sz=" << txBuffer.size() << __E__;
421 
423  while((channel = getNextSlowControlsChannel()) != nullptr)
424  {
425  // skip if no read access
426  if(!channel->readAccess_)
427  continue;
428 
429  if(firstTime)
430  ++numOfReadAccessChannels;
431 
432  // skip if not a sampling moment in time for channel
433  if(timeCounter % channel->delayBetweenSamples_)
434  continue;
435 
436  __FE_COUT__ << "Reading Channel:" << channel->fullChannelName
437  << " at t=" << time(0) << __E__;
438 
439  //check if can use buffered value
440  bool usingBufferedValue = false;
441  if(channel->getInterfaceType() != FEVInterface::UNKNOWN_TYPE)
442  {
444  FESlowControlsChannel* channelToCopy;
445  while((channelToCopy = getNextSlowControlsChannel()) != channel &&
446  channelToCopy != nullptr)
447  {
448  __FE_COUTT__ << "Looking for buffered value at "
449  << BinaryStringMacros::binaryNumberToHexString(
450  channelToCopy->getUniversalAddress(), "0x", " ")
451  << " " << channelToCopy->getReadSizeBytes() << " "
452  << time(0) - channelToCopy->getLastSampleTime() << __E__;
453 
454  __FE_COUTTV__(channel->getInterfaceUID());
455  __FE_COUTTV__(channelToCopy->getInterfaceUID());
456  __FE_COUTTV__(channel->getInterfaceType());
457  __FE_COUTTV__(channelToCopy->getInterfaceType());
458 
459  if(!usingBufferedValue &&
460  channelToCopy->getInterfaceUID() == channel->getInterfaceUID() &&
461  channelToCopy->getInterfaceType() == channel->getInterfaceType() &&
462  BinaryStringMacros::binaryNumberToHexString(
463  channelToCopy->getUniversalAddress(), "0x", " ") ==
464  BinaryStringMacros::binaryNumberToHexString(
465  channel->getUniversalAddress(), "0x", " ") &&
466  channelToCopy->getReadSizeBytes() == channel->getReadSizeBytes() &&
467  time(0) - channelToCopy->getLastSampleTime() <
468  2 /* within 2 seconds, then re-use buffer */)
469  {
470  usingBufferedValue = true;
471  __FE_COUT__
472  << "Using buffered " << channelToCopy->getReadSizeBytes()
473  << "-byte value at address:"
474  << BinaryStringMacros::binaryNumberToHexString(
475  channelToCopy->getUniversalAddress(), "0x", " ")
476  << __E__;
477 
478  __FE_COUT__
479  << "Copying: "
480  << BinaryStringMacros::binaryNumberToHexString(
481  channelToCopy->getLastSampleReadValue(), "0x", " ")
482  << " at t=" << time(0) << __E__;
483  channel->handleSample(channelToCopy->getLastSampleReadValue(),
484  txBuffer,
485  fp,
486  aggregateFileIsBinaryFormat,
487  txBufferUsed);
488  __FE_COUT__ << "Copied: "
489  << BinaryStringMacros::binaryNumberToHexString(
490  channel->getSample(), "0x", " ")
491  << " at t=" << time(0) << __E__;
492 
493  //can NOT break; from while loop... must take iterator back to starting point channel iterator
494  }
495  } //end while loop searching for buffered slow controls value
496  } //end buffered value check
497 
498  //get and handle sample if not already handled using buffered value
499  if(!usingBufferedValue)
500  {
501  std::string readValInst;
502  std::string& readVal = readValInst;
503  readVal.resize(universalDataSize_); // size to data in advance
504  channel->doRead(readVal);
505  channel->handleSample(
506  readVal, txBuffer, fp, aggregateFileIsBinaryFormat, txBufferUsed);
507  __FE_COUT__ << "Have: "
508  << BinaryStringMacros::binaryNumberToHexString(
509  channel->getSample(), "0x", " ")
510  << " at t=" << time(0) << __E__;
511  }
512 
513  if(txBuffer.size())
514  __FE_COUT__ << "txBuffer sz=" << txBuffer.size() << __E__;
515 
516  // Use artdaq Metric Manager if available,
517  if(channel->monitoringEnabled && metricMan && metricMan->Running() &&
518  universalAddressSize_ <= 8)
519  {
520  uint64_t val = 0; // 64 bits!
521  for(size_t ii = 0; ii < channel->getSample().size(); ++ii)
522  val += (uint8_t)channel->getSample()[ii] << (ii * 8);
523 
524  // Unit transforms
525  if((channel->transformation).size() >
526  1) // Execute transformation if a formula is present
527  {
528  __FE_COUT__ << "Transformation formula = " << channel->transformation
529  << __E__;
530 
531  TFormula transformationFormula("transformationFormula",
532  (channel->transformation).c_str());
533  double transformedVal = transformationFormula.Eval(val);
534 
535  if(!std::isnan(transformedVal))
536  {
537  __FE_COUT__ << "Transformed " << val << " into " << transformedVal
538  << __E__;
539  __FE_COUT__ << "Sending \"" << channel->fullChannelName
540  << "\" transformed sample to Metric Manager..."
541  << __E__;
542  metricMan->sendMetric(channel->fullChannelName,
543  transformedVal,
544  "",
545  3,
546  artdaq::MetricMode::LastPoint);
547  }
548  else
549  {
550  __FE_SS__ << "Transformed value is NaN!" << __E__;
551  __FE_SS_THROW__;
552  }
553  }
554  else
555  {
556  __FE_COUT__ << "Sending \"" << channel->fullChannelName
557  << "\" sample to Metric Manager..." << __E__;
558  metricMan->sendMetric(channel->fullChannelName,
559  val,
560  "",
561  3,
562  artdaq::MetricMode::LastPoint);
563  }
564  }
565  else
566  {
567  __FE_COUT__ << "Skipping \"" << channel->fullChannelName
568  << "\" sample to Metric Manager... "
569  << " channel->monitoringEnabled="
570  << channel->monitoringEnabled << " metricMan=" << metricMan
571  << " metricMan->Running()="
572  << (metricMan && metricMan->Running()) << __E__;
573  }
574 
575  // make sure buffer hasn't exploded somehow
576  if(txBuffer.size() > txBufferSz)
577  {
578  __FE_SS__ << "This should never happen hopefully!" << __E__;
579  __FE_SS_THROW__;
580  }
581  // if we don't have a socket, no need for txBuffer, should already be handled by
582  if(!slowContrlolsTxSocket && txBufferUsed)
583  txBuffer.resize(0);
584 
585  // send early if threshold reached
586  if(slowContrlolsTxSocket && txBuffer.size() > txBufferFullThreshold)
587  {
588  __FE_COUT__ << "Sending now! txBufferFullThreshold="
589  << txBufferFullThreshold << __E__;
590  slowContrlolsTxSocket->send(txBuffer);
591  txBuffer.resize(0); // clear buffer a la txBuffer = "";
592  }
593  }
594 
595  if(txBuffer.size())
596  __FE_COUT__ << "txBuffer sz=" << txBuffer.size() << __E__;
597 
598  // send anything left
599  if(slowContrlolsTxSocket && txBuffer.size())
600  {
601  __FE_COUT__ << "Sending now!" << __E__;
602  slowContrlolsTxSocket->send(txBuffer);
603  }
604 
605  if(fp)
606  fflush(fp); // flush anything in aggregate file for reading ease
607 
608  if(firstTime)
609  {
610  if(numOfReadAccessChannels == 0)
611  {
612  __FE_COUT_WARN__
613  << "There are no slow controls channels with read access!" << __E__;
614  break;
615  }
616  else
617  __FE_COUT__ << "There are " << getSlowControlsChannelCount()
618  << " slow controls channels total. "
619  << numOfReadAccessChannels << " with read access enabled."
620  << __E__;
621  }
622  firstTime = false;
623  } // end main slow controls loop
624 
625  if(fp)
626  fclose(fp);
627 
628  __FE_COUT__ << "Slow controls workloop done." << __E__;
629 
630  return false;
631 } // end slowControlsRunning()
632 catch(...) //
633 {
634  // catch all, then rethrow with local variables needed
635  __FE_SS__;
636 
637  bool isPauseException = false;
638  bool isStopException = false;
639 
640  try
641  {
642  throw;
643  }
644  catch(const __OTS_PAUSE_EXCEPTION__& e)
645  {
646  ss << "PAUSE Exception was caught during slow controls running thread: "
647  << e.what() << std::endl;
648  isPauseException = true;
649  }
650  catch(const __OTS_STOP_EXCEPTION__& e)
651  {
652  ss << "STOP Exception was caught during slow controls running thread: "
653  << e.what() << std::endl;
654  isStopException = true;
655  }
656  catch(const std::runtime_error& e)
657  {
658  ss << "Caught an error during slow controls running thread of FE Interface '"
659  << Configurable::theConfigurationRecordName_ << "': " << e.what() << __E__;
660  }
661  catch(...)
662  {
663  ss << "Caught an unknown error during slow controls running thread." << __E__;
664  try
665  {
666  throw;
667  } //one more try to printout extra info
668  catch(const std::exception& e)
669  {
670  ss << "Exception message: " << e.what();
671  }
672  catch(...)
673  {
674  }
675  }
676 
677  // At this point, an asynchronous error has occurred
678  // during front-end running...
679  // Send async error to Gateway
680  // Do this as thread so that workloop can end.
681 
682  __FE_COUT_ERR__ << ss.str();
683 
684  std::thread(
685  [](FEVInterface* fe,
686  const std::string errorMessage,
687  bool isPauseException,
688  bool isStopException) {
690  fe, errorMessage, isPauseException, isStopException);
691  },
692  // pass the values
693  this /*fe*/,
694  ss.str() /*errorMessage*/,
695  isPauseException,
696  isStopException)
697  .detach();
698 
699  return false;
700 } // end slowControlsRunning()
701 
702 //==============================================================================
711  const std::string& errorMessage,
712  bool isPauseException,
713  bool isStopException)
714 try
715 {
716  std::stringstream feHeader;
717  feHeader << ":FE:" << fe->getInterfaceType() << ":" << fe->getInterfaceUID() << ":"
718  << fe->theConfigurationRecordName_ << "\t";
719 
720  if(isStopException)
721  {
722  __COUT_ERR__ << feHeader.str() << "Sending FE Async STOP Running Exception... \n"
723  << errorMessage << __E__;
724  fe->VStateMachine::parentSupervisor_->setAsyncPauseExceptionMessage(errorMessage);
725  }
726  else if(isPauseException)
727  {
728  __COUT_ERR__ << feHeader.str() << "Sending FE Async PAUSE Running Exception... \n"
729  << errorMessage << __E__;
730  fe->VStateMachine::parentSupervisor_->setAsyncStopExceptionMessage(errorMessage);
731  }
732  else
733  __COUT_ERR__ << feHeader.str() << "Sending FE Async Running Error... \n"
734  << errorMessage << __E__;
735 
736  XDAQ_CONST_CALL xdaq::ApplicationDescriptor* gatewaySupervisor =
737  fe->VStateMachine::parentSupervisor_->allSupervisorInfo_.getGatewayInfo()
738  .getDescriptor();
739 
740  SOAPParameters parameters;
741  parameters.addParameter("ErrorMessage", errorMessage);
742 
743  xoap::MessageReference replyMessage =
744  fe->VStateMachine::parentSupervisor_->SOAPMessenger::sendWithSOAPReply(
745  gatewaySupervisor,
746  isPauseException ? "AsyncPauseException" : "AsyncError",
747  parameters);
748 
749  std::stringstream replyMessageSStream;
750  replyMessageSStream << SOAPUtilities::translate(replyMessage);
751  __COUT__ << feHeader.str() << "Received... " << replyMessageSStream.str()
752  << std::endl;
753 
754  if(replyMessageSStream.str().find("Fault") != std::string::npos)
755  {
756  __COUT_ERR__ << feHeader.str() << "Failure to indicate fault to Gateway..."
757  << __E__;
758  throw;
759  }
760 }
761 catch(const xdaq::exception::Exception& e)
762 {
763  if(isPauseException)
764  __COUT__ << "SOAP message failure indicating front-end asynchronous running SOFT "
765  "error back to Gateway: "
766  << e.what() << __E__;
767  else
768  __COUT__ << "SOAP message failure indicating front-end asynchronous running "
769  "error back to Gateway: "
770  << e.what() << __E__;
771  throw; // rethrow and hope error is noticed
772 }
773 catch(...)
774 {
775  if(isPauseException)
776  __COUT__ << "Unknown error encounter indicating front-end asynchronous running "
777  "SOFT error back to Gateway."
778  << __E__;
779  else
780  __COUT__ << "Unknown error encounter indicating front-end asynchronous running "
781  "error back to Gateway."
782  << __E__;
783  throw; // rethrow and hope error is noticed
784 } // end SendAsyncErrorToGateway()
785 
786 //==============================================================================
789 bool FEVInterface::workLoopThread(toolbox::task::WorkLoop* /*workLoop*/)
790 {
791  try
792  {
793  continueWorkLoop_ =
794  running(); /* in case users return false, without using continueWorkLoop_*/
795  }
796  catch(...) //
797  {
798  // catch all, then rethrow with local variables needed
799  __FE_SS__;
800 
801  bool isPauseException = false;
802  bool isStopException = false;
803 
804  try
805  {
806  throw;
807  }
808  catch(const __OTS_PAUSE_EXCEPTION__& e)
809  {
810  ss << "SOFT Exception was caught while running: " << e.what() << std::endl;
811  isPauseException = true;
812  }
813  catch(const __OTS_STOP_EXCEPTION__& e)
814  {
815  ss << "STOP Exception was caught while running: " << e.what() << std::endl;
816  isStopException = true;
817  }
818  catch(const std::runtime_error& e)
819  {
820  ss << "Caught an error during running at FE Interface '"
821  << Configurable::theConfigurationRecordName_ << "': " << e.what() << __E__;
822  }
823  catch(...)
824  {
825  ss << "Caught an unknown error during running." << __E__;
826  try
827  {
828  throw;
829  } //one more try to printout extra info
830  catch(const std::exception& e)
831  {
832  ss << "Exception message: " << e.what();
833  }
834  catch(...)
835  {
836  }
837  }
838 
839  // At this point, an asynchronous error has occurred
840  // during front-end running...
841  // Send async error to Gateway
842  // Do this as thread so that workloop can end.
843 
844  __FE_COUT_ERR__ << ss.str();
845 
846  std::thread(
847  [](FEVInterface* fe,
848  const std::string errorMessage,
849  bool isPauseException,
850  bool isStopException) {
852  fe, errorMessage, isPauseException, isStopException);
853  },
854  // pass the values
855  this /*fe*/,
856  ss.str() /*errorMessage*/,
857  isPauseException,
858  isStopException)
859  .detach();
860 
861  return false;
862  }
863 
864  return continueWorkLoop_;
865 } // end workLoopThread()
866 
867 //==============================================================================
876  const std::string& feMacroName,
877  frontEndMacroFunction_t feMacroFunction,
878  const std::vector<std::string>& namesOfInputArgs,
879  const std::vector<std::string>& namesOfOutputArgs,
880  uint8_t requiredUserPermissions,
881  const std::string& allowedCallingFEs,
882  const std::string& feMacroTooltip)
883 {
884  registerFEMacroFunction(feMacroName,
885  feMacroFunction,
886  namesOfInputArgs,
887  namesOfOutputArgs,
888  std::to_string(requiredUserPermissions),
889  allowedCallingFEs,
890  feMacroTooltip);
891 } // end registerFEMacroFunction()
893  const std::string& feMacroName,
894  frontEndMacroFunction_t feMacroFunction,
895  const std::vector<std::string>& namesOfInputArgs,
896  const std::vector<std::string>& namesOfOutputArgs,
897  const std::string& requiredUserPermissions,
898  const std::string& allowedCallingFEs,
899  const std::string& feMacroTooltip)
900 {
901  if(mapOfFEMacroFunctions_.find(feMacroName) != mapOfFEMacroFunctions_.end())
902  {
903  __FE_SS__ << "feMacroName '" << feMacroName << "' already exists! Not allowed."
904  << __E__;
905  __FE_COUT_ERR__ << "\n" << ss.str();
906  __FE_SS_THROW__;
907  }
908 
909  mapOfFEMacroFunctions_.insert(std::pair<std::string, frontEndMacroStruct_t>(
910  feMacroName,
911  frontEndMacroStruct_t(feMacroName,
912  feMacroFunction,
913  namesOfInputArgs,
914  namesOfOutputArgs,
915  requiredUserPermissions,
916  allowedCallingFEs,
917  feMacroTooltip)));
918 } // end registerFEMacroFunction()
919 
920 //==============================================================================
926 const std::string& FEVInterface::getFEMacroConstArgument(frontEndMacroConstArgs_t& args,
927  const std::string& argName)
928 {
929  for(const frontEndMacroArg_t& pair : args)
930  {
931  //check arg names and ignore after ( to allow Defaults/Notes to change over time, while not breaking user history
932  if(pair.first.substr(0, pair.first.find('(')) ==
933  argName.substr(0, argName.find('(')))
934  {
935  __COUT__ << argName << ": " << pair.second << __E__;
936  return pair.second;
937  }
938  }
939  __SS__ << "Requested input argument '" << argName
940  << "' not found in list of arguments." << __E__;
941  ss << "\nHere is the list of arguments: \n";
942  for(const frontEndMacroArg_t& pair : args)
943  ss << "\t - " << pair.first << "\n";
944  __SS_THROW__;
945 } //end getFEMacroConstArgument()
946 
947 //==============================================================================
950 template<>
951 std::string ots::getFEMacroConstArgumentValue<std::string>(
952  FEVInterface::frontEndMacroConstArgs_t& args,
953  const std::string& argName,
954  const std::string& defaultValue)
955 {
956  const std::string& data = FEVInterface::getFEMacroConstArgument(args, argName);
957 
958  // default value is used only if the user leave "Default"
959  if(data == "Default")
960  return defaultValue;
961 
962  return data;
963 } //end getFEMacroConstArgumentValue()
964 //==============================================================================
967 template<>
968 std::string ots::getFEMacroArgumentValue<std::string>(
969  FEVInterface::frontEndMacroArgs_t& args, const std::string& argName)
970 {
971  return FEVInterface::getFEMacroArgument(args, argName);
972 } //end getFEMacroArgumentValue()
973 
974 //==============================================================================
979 std::string& FEVInterface::getFEMacroArgument(frontEndMacroArgs_t& args,
980  const std::string& argName)
981 {
982  for(std::pair<const std::string /* output arg name */,
983  std::string /* arg output value */>& pair : args)
984  {
985  if(pair.first == argName)
986  return pair.second;
987  }
988  __SS__ << "Requested argument not found with name '" << argName << "'" << __E__;
989  __SS_THROW__;
990 } //end getFEMacroArgument()
991 
992 //==============================================================================
997 void FEVInterface::runSequenceOfCommands(const std::string& treeLinkName)
998 {
999  std::map<uint64_t, uint64_t> writeHistory;
1000  uint64_t writeAddress, writeValue, bitMask;
1001  uint8_t bitPosition;
1002 
1003  std::string writeBuffer;
1004  std::string readBuffer;
1005  char msg[1000];
1006  bool ignoreError = true;
1007 
1008  // ignore errors getting sequence of commands through tree (since it is optional)
1009  try
1010  {
1011  ConfigurationTree configSeqLink =
1012  theXDAQContextConfigTree_.getNode(theConfigurationPath_)
1013  .getNode(treeLinkName);
1014 
1015  // but throw errors if problems executing the sequence of commands
1016  try
1017  {
1018  if(configSeqLink.isDisconnected())
1019  __FE_COUT__ << "Disconnected configure sequence" << __E__;
1020  else
1021  {
1022  __FE_COUT__ << "Handling configure sequence." << __E__;
1023  auto childrenMap = configSeqLink.getChildrenMap();
1024  for(const auto& child : childrenMap)
1025  {
1026  // WriteAddress and WriteValue fields
1027 
1028  writeAddress =
1029  child.second.getNode("WriteAddress").getValue<uint64_t>();
1030  writeValue = child.second.getNode("WriteValue").getValue<uint64_t>();
1031  bitPosition =
1032  child.second.getNode("StartingBitPosition").getValue<uint8_t>();
1033  bitMask =
1034  (1 << child.second.getNode("BitFieldSize").getValue<uint8_t>()) -
1035  1;
1036 
1037  writeValue &= bitMask;
1038  writeValue <<= bitPosition;
1039  bitMask = ~(bitMask << bitPosition);
1040 
1041  // place into write history
1042  if(writeHistory.find(writeAddress) == writeHistory.end())
1043  writeHistory[writeAddress] = 0; // init to 0
1044 
1045  writeHistory[writeAddress] &= bitMask; // clear incoming bits
1046  writeHistory[writeAddress] |= writeValue; // add incoming bits
1047 
1048  sprintf(msg,
1049  "\t Writing %s: \t %ld(0x%lX) \t %ld(0x%lX)",
1050  child.first.c_str(),
1051  writeAddress,
1052  writeAddress,
1053  writeHistory[writeAddress],
1054  writeHistory[writeAddress]);
1055 
1056  __FE_COUT__ << msg << __E__;
1057 
1058  universalWrite((char*)&writeAddress,
1059  (char*)&(writeHistory[writeAddress]));
1060  }
1061  }
1062  }
1063  catch(...)
1064  {
1065  ignoreError = false;
1066  throw;
1067  }
1068  }
1069  catch(...)
1070  {
1071  if(!ignoreError)
1072  throw;
1073  // else ignoring error
1074  __FE_COUT__
1075  << "Unable to access sequence of commands through configuration tree. "
1076  << "Assuming no sequence. " << __E__;
1077  }
1078 } // end runSequenceOfCommands()
1079 
1080 //==============================================================================
1089  const std::string& feMacroName,
1090  // not equivalent to __ARGS__
1091  // left non-const value so caller can modify inputArgs as they are being created
1092  const std::vector<FEVInterface::frontEndMacroArg_t>& argsIn,
1093  std::vector<FEVInterface::frontEndMacroArg_t>& argsOut)
1094 {
1095  // have pointer to virtual FEInterface, find Macro structure
1096  auto FEMacroIt = this->getMapOfFEMacroFunctions().find(feMacroName);
1097  if(FEMacroIt == this->getMapOfFEMacroFunctions().end())
1098  {
1099  __CFG_SS__ << "FE Macro '" << feMacroName << "' of interfaceID '"
1100  << getInterfaceUID() << "' was not found!" << __E__;
1101  __CFG_COUT_ERR__ << "\n" << ss.str();
1102  __CFG_SS_THROW__;
1103  }
1104  const FEVInterface::frontEndMacroStruct_t& feMacro = FEMacroIt->second;
1105 
1106  // check for input arg name match
1107  for(unsigned int i = 0;
1108  i < argsIn.size() && i < feMacro.namesOfInputArguments_.size();
1109  ++i)
1110  if(argsIn[i].first != feMacro.namesOfInputArguments_[i])
1111  {
1112  __CFG_SS__ << "FE Macro '" << feMacro.feMacroName_ << "' of interfaceID '"
1113  << getInterfaceUID() << "' was attempted with a mismatch in"
1114  << " a name of an input argument. " << argsIn[i].first
1115  << " was given. " << feMacro.namesOfInputArguments_[i]
1116  << " expected." << __E__;
1117  __CFG_COUT_ERR__ << "\n" << ss.str();
1118  __CFG_SS_THROW__;
1119  }
1120 
1121  // check namesOfInputArguments_
1122  if(feMacro.namesOfInputArguments_.size() != argsIn.size())
1123  {
1124  __CFG_SS__ << "FE Macro '" << feMacro.feMacroName_ << "' of interfaceID '"
1125  << getInterfaceUID() << "' was attempted with a mismatch in"
1126  << " number of input arguments. " << argsIn.size() << " were given. "
1127  << feMacro.namesOfInputArguments_.size() << " expected." << __E__;
1128  __CFG_COUT_ERR__ << "\n" << ss.str();
1129  __CFG_SS_THROW__;
1130  }
1131 
1132  __CFG_COUT__ << "# of input args = " << argsIn.size() << __E__;
1133  for(auto& argIn : argsIn)
1134  __CFG_COUT__ << argIn.first << ": " << argIn.second << __E__;
1135 
1136  __CFG_COUT__ << "Launching FE Macro '" << feMacro.feMacroName_ << "' ..." << __E__;
1137 
1138  argsOut.clear();
1139  for(unsigned int i = 0; i < feMacro.namesOfOutputArguments_.size(); ++i)
1140  argsOut.push_back(FEVInterface::frontEndMacroArg_t(
1141  feMacro.namesOfOutputArguments_[i], "DEFAULT"));
1142 
1143  // run it!
1144  (this->*(feMacro.macroFunction_))(feMacro, argsIn, argsOut);
1145 
1146  __CFG_COUT__ << "FE Macro complete!" << __E__;
1147 
1148  __CFG_COUT__ << "# of output args = " << argsOut.size() << __E__;
1149  for(const auto& arg : argsOut)
1150  __CFG_COUT__ << arg.first << ": " << arg.second << __E__;
1151 
1152 } // end runSelfFrontEndMacro()
1153 
1154 //==============================================================================
1159  const std::string& targetInterfaceID,
1160  const std::string& feMacroName,
1161  const std::vector<FEVInterface::frontEndMacroArg_t>& inputArgs,
1162  std::vector<FEVInterface::frontEndMacroArg_t>& outputArgs) const
1163 {
1164  __FE_COUTV__(targetInterfaceID);
1165  __FE_COUTV__(VStateMachine::parentSupervisor_);
1166 
1167  std::string inputArgsStr = StringMacros::vectorToString(
1168  inputArgs, ";" /*primaryDelimeter*/, "," /*secondaryDelimeter*/);
1169 
1170  __FE_COUTV__(inputArgsStr);
1171 
1172  xoap::MessageReference message =
1173  SOAPUtilities::makeSOAPMessageReference("FECommunication");
1174 
1175  SOAPParameters parameters;
1176  parameters.addParameter("type", "feMacro");
1177  parameters.addParameter("requester", FEVInterface::interfaceUID_);
1178  parameters.addParameter("targetInterfaceID", targetInterfaceID);
1179  parameters.addParameter("feMacroName", feMacroName);
1180  parameters.addParameter("inputArgs", inputArgsStr);
1181  SOAPUtilities::addParameters(message, parameters);
1182 
1183  __FE_COUT__ << "Sending FE communication: " << SOAPUtilities::translate(message)
1184  << __E__;
1185 
1186  xoap::MessageReference replyMessage =
1187  VStateMachine::parentSupervisor_->SOAPMessenger::sendWithSOAPReply(
1188  VStateMachine::parentSupervisor_->allSupervisorInfo_
1189  .getAllMacroMakerTypeSupervisorInfo()
1190  .begin()
1191  ->second.getDescriptor(),
1192  message);
1193 
1194  __FE_COUT__ << "Response received: " << SOAPUtilities::translate(replyMessage)
1195  << __E__;
1196 
1197  SOAPParameters rxParameters;
1198  rxParameters.addParameter("Error");
1199  SOAPUtilities::receive(replyMessage, rxParameters);
1200 
1201  std::string error = rxParameters.getValue("Error");
1202 
1203  if(error != "")
1204  {
1205  // error occurred!
1206  __FE_SS__ << "Error transmitting request to target interface '"
1207  << targetInterfaceID << "' from '" << FEVInterface::interfaceUID_
1208  << ".' " << error << __E__;
1209  __FE_SS_THROW__;
1210  }
1211 
1212  // extract output args
1213  SOAPParameters argsOutParameter;
1214  argsOutParameter.addParameter("outputArgs");
1215  SOAPUtilities::receive(replyMessage, argsOutParameter);
1216 
1217  std::string outputArgsStr = argsOutParameter.getValue("outputArgs");
1218  std::set<char> pairDelimiter({';'}), nameValueDelimiter({','});
1219 
1220  std::map<std::string, std::string> mapToReturn;
1222  outputArgsStr, mapToReturn, pairDelimiter, nameValueDelimiter);
1223 
1224  outputArgs.clear();
1225  for(auto& mapPair : mapToReturn)
1226  outputArgs.push_back(mapPair);
1227 
1228 } // end runFrontEndMacro()
1229 
1230 //==============================================================================
1235 void FEVInterface::receiveFromFrontEnd(const std::string& requester,
1236  std::string& retValue,
1237  unsigned int timeoutInSeconds) const
1238 {
1239  __FE_COUTV__(requester);
1240  __FE_COUTV__(parentSupervisor_);
1241 
1242  std::string data = "0";
1243  // bool found = false;
1244  while(1)
1245  {
1246  // mutex scope
1247  {
1248  std::lock_guard<std::mutex> lock(
1249  parentInterfaceManager_->frontEndCommunicationReceiveMutex_);
1250 
1251  auto receiveBuffersForTargetIt =
1252  parentInterfaceManager_->frontEndCommunicationReceiveBuffer_.find(
1253  FEVInterface::interfaceUID_);
1254  if(receiveBuffersForTargetIt !=
1255  parentInterfaceManager_->frontEndCommunicationReceiveBuffer_.end())
1256  {
1257  __FE_COUT__ << "Number of source buffers found for front-end '"
1258  << FEVInterface::interfaceUID_
1259  << "': " << receiveBuffersForTargetIt->second.size() << __E__;
1260 
1261  for(auto& buffPair : receiveBuffersForTargetIt->second)
1262  __FE_COUTV__(buffPair.first);
1263 
1264  // match requester to map of buffers
1265  std::string sourceBufferId = "";
1266  std::queue<std::string /*value*/>& sourceBuffer =
1268  requester, receiveBuffersForTargetIt->second, &sourceBufferId);
1269 
1270  __FE_COUT__ << "Found source buffer '" << sourceBufferId << "' with size "
1271  << sourceBuffer.size() << __E__;
1272 
1273  if(sourceBuffer.size())
1274  {
1275  __FE_COUT__ << "Found a value in queue of size "
1276  << sourceBuffer.size() << __E__;
1277 
1278  // remove from receive buffer
1279  retValue = sourceBuffer.front();
1280  sourceBuffer.pop();
1281  return;
1282  }
1283  else
1284  __FE_COUT__ << "Source buffer empty for '" << requester << "'"
1285  << __E__;
1286  }
1287 
1288  // else, not found...
1289 
1290  // if out of time, throw error
1291  if(!timeoutInSeconds)
1292  {
1293  __FE_SS__ << "Timeout (" << timeoutInSeconds
1294  << " s) waiting for front-end communication from " << requester
1295  << "." << __E__;
1296  __FE_SS_THROW__;
1297  }
1298  // else, there is still hope
1299 
1300  } // end mutex scope
1301 
1302  // else try again in a sec
1303  __FE_COUT__ << "Waiting for front-end communication from " << requester << " for "
1304  << timeoutInSeconds << " more seconds..." << __E__;
1305 
1306  --timeoutInSeconds;
1307  sleep(1); // wait a sec
1308  } // end timeout loop
1309 
1310  // should never get here
1311 } // end receiveFromFrontEnd()
1312 
1313 //==============================================================================
1318 std::string FEVInterface::receiveFromFrontEnd(const std::string& requester,
1319  unsigned int timeoutInSeconds) const
1320 {
1321  std::string retValue;
1322  FEVInterface::receiveFromFrontEnd(requester, retValue, timeoutInSeconds);
1323  return retValue;
1324 } // end receiveFromFrontEnd()
1325 
1326 //==============================================================================
1328 FEVInterface::macroStruct_t::macroStruct_t(const std::string& macroString)
1329 {
1330  __COUTVS__(20, macroString);
1331 
1332  // example macro string:
1333  // {"name":"testPublic","sequence":"0:w:1001:writeVal,1:r:1001:","time":"Sat Feb 0
1334  // 9 2019 10:42:03 GMT-0600 (Central Standard Time)","notes":"","LSBF":"false"}@
1335 
1336  std::vector<std::string> extractVec;
1337  StringMacros::getVectorFromString(macroString, extractVec, {'"'});
1338 
1339  __COUTVS__(20, StringMacros::vectorToString(extractVec, " ||| "));
1340 
1341  enum
1342  {
1343  MACRONAME_NAME_INDEX = 1,
1344  MACRONAME_VALUE_INDEX = 3,
1345  SEQUENCE_NAME_INDEX = 5,
1346  SEQUENCE_VALUE_INDEX = 7,
1347  LSBF_NAME_INDEX = 17,
1348  LSFBF_VALUE_INDEX = 19,
1349  };
1350 
1351  // verify fields in sequence (for sanity)
1352  if(MACRONAME_NAME_INDEX >= extractVec.size() ||
1353  extractVec[MACRONAME_NAME_INDEX] != "name")
1354  {
1355  __SS__ << "Invalid sequence, 'name' expected in position " << MACRONAME_NAME_INDEX
1356  << __E__;
1357  __SS_THROW__;
1358  }
1359  if(SEQUENCE_NAME_INDEX >= extractVec.size() ||
1360  extractVec[SEQUENCE_NAME_INDEX] != "sequence")
1361  {
1362  __SS__ << "Invalid sequence, 'sequence' expected in position "
1363  << SEQUENCE_NAME_INDEX << __E__;
1364  __SS_THROW__;
1365  }
1366  if(LSBF_NAME_INDEX >= extractVec.size() || extractVec[LSBF_NAME_INDEX] != "LSBF")
1367  {
1368  __SS__ << "Invalid sequence, 'LSBF' expected in position " << LSBF_NAME_INDEX
1369  << __E__;
1370  __SS_THROW__;
1371  }
1372  macroName_ = extractVec[MACRONAME_VALUE_INDEX];
1373  __COUTVS__(20, macroName_);
1374  lsbf_ = extractVec[LSFBF_VALUE_INDEX] == "false" ? false : true;
1375  __COUTVS__(20, lsbf_);
1376  std::string& sequence = extractVec[SEQUENCE_VALUE_INDEX];
1377  __COUTVS__(20, sequence);
1378 
1379  std::vector<std::string> sequenceCommands;
1380  StringMacros::getVectorFromString(sequence, sequenceCommands, {','});
1381 
1382  __COUTVS__(20, StringMacros::vectorToString(sequenceCommands, " ### "));
1383 
1384  for(auto& command : sequenceCommands)
1385  {
1386  __COUTVS__(20, command);
1387 
1388  // Note: the only way to distinguish between variable and data
1389  // is hex characters or not (lower and upper case allowed for hex)
1390 
1391  std::vector<std::string> commandPieces;
1392  StringMacros::getVectorFromString(command, commandPieces, {':'});
1393 
1394  __COUTVS__(20, StringMacros::vectorToString(commandPieces, " ### "));
1395 
1396  __COUTVS__(20, commandPieces.size());
1397 
1398  // command format
1399  // index | type | address/sleep[ms] | data
1400  // d is delay (1+2)
1401  // w is write (1+3)
1402  // r is read (1+2/3 with arg)
1403 
1404  // extract input arguments, as the variables in address/data fields
1405  // extract output arguments, as the variables in read data fields
1406 
1407  if(commandPieces.size() < 3 || commandPieces.size() > 4 ||
1408  commandPieces[1].size() != 1)
1409  {
1410  __SS__ << "Invalid command type specified in command string: " << command
1411  << __E__;
1412  __SS_THROW__;
1413  }
1414 
1415  //==========================
1416  // Use lambda to identify variable name in field
1417  std::function<bool(const std::string& /*field value*/
1418  )>
1419  localIsVariable = [/*capture variable*/](const std::string& fieldValue) {
1420  // create local message facility subject
1421  std::string mfSubject_ = "isVar";
1422  __GEN_COUTV__(fieldValue);
1423 
1424  // return false if all hex characters found
1425  for(const auto& c : fieldValue)
1426  if(!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
1427  (c >= 'A' && c <= 'F')))
1428  return true; // is variable name!
1429  return false; // else is a valid hex string, so not variable name
1430  }; // end local lambda localIsVariable()
1431 
1432  if(commandPieces[1][0] == 'r' && commandPieces.size() == 4) // read type
1433  {
1434  TLOG_DEBUG(20) << __COUT_HDR__ << "Read type found." << __E__;
1435  // 2: address or optional variable name
1436  // 3: optional variable name
1437 
1438  operations_.push_back(
1439  std::make_pair(macroStruct_t::OP_TYPE_READ, readOps_.size()));
1440 
1441  readOps_.push_back(macroStruct_t::readOp_t());
1442  readOps_.back().addressIsVar_ = localIsVariable(commandPieces[2]);
1443  readOps_.back().dataIsVar_ = localIsVariable(commandPieces[3]);
1444 
1445  if(!readOps_.back().addressIsVar_)
1446  {
1447  if(lsbf_) // flip byte order
1448  {
1449  std::string lsbfData = "";
1450 
1451  // always add leading 0 to guarantee do not miss data
1452  commandPieces[2] = "0" + commandPieces[2];
1453  for(unsigned int i = 0; i < commandPieces[2].size() / 2; ++i)
1454  {
1455  __COUTVS__(20, commandPieces[2].size() - 2 * (i + 1));
1456  // add one byte at a time, backwards
1457  lsbfData +=
1458  commandPieces[2][commandPieces[2].size() - 2 * (i + 1)];
1459  lsbfData +=
1460  commandPieces[2][commandPieces[2].size() - 2 * (i + 1) + 1];
1461  __COUTV__(lsbfData);
1462  }
1463  __COUTVS__(20, lsbfData);
1464  StringMacros::getNumber("0x" + lsbfData, readOps_.back().address_);
1465  }
1466  else
1467  StringMacros::getNumber("0x" + commandPieces[2],
1468  readOps_.back().address_);
1469  }
1470  else
1471  {
1472  readOps_.back().addressVarName_ = commandPieces[2];
1473  __COUTVS__(20, readOps_.back().addressVarName_);
1474 
1475  namesOfInputArguments_.emplace(readOps_.back().addressVarName_);
1476  }
1477 
1478  if(readOps_.back().dataIsVar_)
1479  {
1480  readOps_.back().dataVarName_ = commandPieces[3];
1481  __COUTVS__(20, readOps_.back().dataVarName_);
1482 
1483  namesOfOutputArguments_.emplace(readOps_.back().dataVarName_);
1484  }
1485  }
1486  else if(commandPieces[1][0] == 'w' && commandPieces.size() == 4) // write type
1487  {
1488  TLOG_DEBUG(20) << __COUT_HDR__ << "Write type found." << __E__;
1489  // 2: address or optional variable name
1490  // 3: data or optional variable name
1491 
1492  operations_.push_back(
1493  std::make_pair(macroStruct_t::OP_TYPE_WRITE, writeOps_.size()));
1494 
1495  writeOps_.push_back(macroStruct_t::writeOp_t());
1496  writeOps_.back().addressIsVar_ = localIsVariable(commandPieces[2]);
1497  writeOps_.back().dataIsVar_ = localIsVariable(commandPieces[3]);
1498 
1499  if(!writeOps_.back().addressIsVar_)
1500  {
1501  if(lsbf_) // flip byte order
1502  {
1503  std::string lsbfData = "";
1504 
1505  // always add leading 0 to guarantee do not miss data
1506  commandPieces[2] = "0" + commandPieces[2];
1507  for(unsigned int i = 0; i < commandPieces[2].size() / 2; ++i)
1508  {
1509  __COUTV__(commandPieces[2].size() - 2 * (i + 1));
1510  // add one byte at a time, backwards
1511  lsbfData +=
1512  commandPieces[2][commandPieces[2].size() - 2 * (i + 1)];
1513  lsbfData +=
1514  commandPieces[2][commandPieces[2].size() - 2 * (i + 1) + 1];
1515  __COUTV__(lsbfData);
1516  }
1517  __COUTVS__(20, lsbfData);
1518  StringMacros::getNumber("0x" + lsbfData, writeOps_.back().address_);
1519  }
1520  else
1521  StringMacros::getNumber("0x" + commandPieces[2],
1522  writeOps_.back().address_);
1523  }
1524  else
1525  {
1526  writeOps_.back().addressVarName_ = commandPieces[2];
1527  __COUTVS__(20, writeOps_.back().addressVarName_);
1528 
1529  namesOfInputArguments_.emplace(writeOps_.back().addressVarName_);
1530  }
1531 
1532  if(!writeOps_.back().dataIsVar_)
1533  {
1534  if(lsbf_) // flip byte order
1535  {
1536  std::string lsbfData = "";
1537 
1538  // always add leading 0 to guarantee do not miss data
1539  commandPieces[2] = "0" + commandPieces[3];
1540  for(unsigned int i = 0; i < commandPieces[3].size() / 2; ++i)
1541  {
1542  __COUTVS__(20, commandPieces[3].size() - 2 * (i + 1));
1543  // add one byte at a time, backwards
1544  lsbfData +=
1545  commandPieces[3][commandPieces[3].size() - 2 * (i + 1)];
1546  lsbfData +=
1547  commandPieces[3][commandPieces[3].size() - 2 * (i + 1) + 1];
1548  __COUTVS__(20, lsbfData);
1549  }
1550  __COUTV__(lsbfData);
1551  StringMacros::getNumber("0x" + lsbfData, writeOps_.back().data_);
1552  }
1553  else
1554  StringMacros::getNumber("0x" + commandPieces[3],
1555  writeOps_.back().data_);
1556  }
1557  else
1558  {
1559  writeOps_.back().dataVarName_ = commandPieces[3];
1560  __COUTVS__(20, writeOps_.back().dataVarName_);
1561 
1562  namesOfInputArguments_.emplace(writeOps_.back().dataVarName_);
1563  }
1564  }
1565  else if(commandPieces[1][0] == 'd' && commandPieces.size() == 3) // delay type
1566  {
1567  TLOG_DEBUG(20) << __COUT_HDR__ << "Delay type found." << __E__;
1568  // 2: delay[ms] or optional variable name
1569 
1570  operations_.push_back(
1571  std::make_pair(macroStruct_t::OP_TYPE_DELAY, delayOps_.size()));
1572 
1573  delayOps_.push_back(macroStruct_t::delayOp_t());
1574  delayOps_.back().delayIsVar_ = localIsVariable(commandPieces[2]);
1575 
1576  if(!delayOps_.back().delayIsVar_)
1577  StringMacros::getNumber("0x" + commandPieces[2], delayOps_.back().delay_);
1578  else
1579  {
1580  delayOps_.back().delayVarName_ = commandPieces[2];
1581  __COUTVS__(20, delayOps_.back().delayVarName_);
1582 
1583  namesOfInputArguments_.emplace(delayOps_.back().delayVarName_);
1584  }
1585  }
1586  else // invalid type
1587  {
1588  __SS__ << "Invalid command type '" << commandPieces[1][0]
1589  << "' specified with " << commandPieces.size() << " components."
1590  << __E__;
1591  __SS_THROW__;
1592  }
1593 
1594  } // end sequence commands extraction loop
1595 
1596  __COUT__ << operations_.size() << " operations extracted: \n\t" << readOps_.size()
1597  << " reads \n\t" << writeOps_.size() << " writes \n\t" << delayOps_.size()
1598  << " delays" << __E__;
1599 
1600  __COUT__ << "Input arguments: " << __E__;
1601  for(const auto& inputArg : namesOfInputArguments_)
1602  __COUT__ << "\t" << inputArg << __E__;
1603 
1604  __COUT__ << "Output arguments: " << __E__;
1605  for(const auto& outputArg : namesOfOutputArguments_)
1606  __COUT__ << "\t" << outputArg << __E__;
1607 
1608 } // end macroStruct_t constructor
1609 
1610 //==============================================================================
1614  std::map<std::string /*name*/, uint64_t /*value*/>& variableMap)
1615 {
1616  // Similar to FEVInterface::runSequenceOfCommands()
1617 
1618  __FE_COUT__ << "Running Macro '" << macro.macroName_ << "' of "
1619  << macro.operations_.size() << " operations." << __E__;
1620 
1621  for(auto& op : macro.operations_)
1622  {
1623  if(op.first == macroStruct_t::OP_TYPE_READ)
1624  {
1625  __FE_COUT__ << "Doing read op..." << __E__;
1626  macroStruct_t::readOp_t& readOp = macro.readOps_[op.second];
1627  if(readOp.addressIsVar_)
1628  {
1629  __FE_COUTV__(readOp.addressVarName_);
1630  readOp.address_ = variableMap.at(readOp.addressVarName_);
1631  }
1632 
1633  uint64_t dataValue = 0;
1634 
1635  __FE_COUT__ << std::hex << "Read address: \t 0x" << readOp.address_ << __E__
1636  << std::dec;
1637 
1638  universalRead((char*)&readOp.address_, (char*)&dataValue);
1639 
1640  __FE_COUT__ << std::hex << "Read data: \t 0x" << dataValue << __E__
1641  << std::dec;
1642 
1643  if(readOp.dataIsVar_)
1644  {
1645  __FE_COUTV__(readOp.dataVarName_);
1646  variableMap.at(readOp.dataVarName_) = dataValue;
1647  }
1648 
1649  } // end read op
1650  else if(op.first == macroStruct_t::OP_TYPE_WRITE)
1651  {
1652  __FE_COUT__ << "Doing write op..." << __E__;
1653  macroStruct_t::writeOp_t& writeOp = macro.writeOps_[op.second];
1654  if(writeOp.addressIsVar_)
1655  {
1656  __FE_COUTV__(writeOp.addressVarName_);
1657  writeOp.address_ = variableMap.at(writeOp.addressVarName_);
1658  }
1659  if(writeOp.dataIsVar_)
1660  {
1661  __FE_COUTV__(writeOp.dataVarName_);
1662  writeOp.data_ = variableMap.at(writeOp.dataVarName_);
1663  }
1664 
1665  __FE_COUT__ << std::hex << "Write address: \t 0x" << writeOp.address_ << __E__
1666  << std::dec;
1667  __FE_COUT__ << std::hex << "Write data: \t 0x" << writeOp.data_ << __E__
1668  << std::dec;
1669 
1670  universalWrite((char*)&writeOp.address_, (char*)&writeOp.data_);
1671 
1672  } // end write op
1673  else if(op.first == macroStruct_t::OP_TYPE_DELAY)
1674  {
1675  __FE_COUT__ << "Doing delay op..." << __E__;
1676 
1677  macroStruct_t::delayOp_t& delayOp = macro.delayOps_[op.second];
1678  if(delayOp.delayIsVar_)
1679  {
1680  __FE_COUTV__(delayOp.delayVarName_);
1681  delayOp.delay_ = variableMap.at(delayOp.delayVarName_);
1682  }
1683 
1684  __FE_COUT__ << std::dec << "Delay ms: \t " << delayOp.delay_ << __E__;
1685 
1686  usleep(delayOp.delay_ /*ms*/ * 1000);
1687 
1688  } // end delay op
1689  else // invalid type
1690  {
1691  __FE_SS__ << "Invalid command type '" << op.first << "!'" << __E__;
1692  __FE_SS_THROW__;
1693  }
1694 
1695  } // end operations loop
1696 
1697 } // end runMacro
bool isDisconnected(void) const
ConfigurationTree getNode(const std::string &nodeName, bool doNotThrowOnBrokenUIDLinks=false) const
navigating between nodes
const std::string & getTableName(void) const
getTableName
std::map< std::string, ConfigurationTree > getChildrenMap(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool onlyStatusTrue=false) const
void getValue(T &value) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
void handleSample(const std::string &universalReadValue, std::string &txBuffer, FILE *fpAggregate=0, bool aggregateIsBinaryFormat=false, bool txBufferUsed=true)
static std::string underscoreString(const std::string &str)
void runMacro(FEVInterface::macroStruct_t &macro, std::map< std::string, uint64_t > &variableMap)
runMacro
virtual void resetSlowControlsChannelIterator(void)
virtual in case channels are handled in multiple maps, for example
void(ots::FEVInterface::*)(const frontEndMacroStruct_t &feMacroStruct, FEVInterface::frontEndMacroConstArgs_t argsIn, FEVInterface::frontEndMacroArgs_t argsOut) frontEndMacroFunction_t
void function (vector-of-inputs, vector-of-outputs)
Definition: FEVInterface.h:167
virtual void configureSlowControls(void)
end State Machine handlers
Definition: FEVInterface.cc:93
static void sendAsyncExceptionToGateway(FEVInterface *fe, const std::string &errMsg, bool isPauseException, bool isStopException)
void runSequenceOfCommands(const std::string &treeLinkName)
std::map< std::string, frontEndMacroStruct_t > mapOfFEMacroFunctions_
Map of FE Macro functions members.
Definition: FEVInterface.h:302
static const std::string & getFEMacroConstArgument(frontEndMacroConstArgs_t args, const std::string &argName)
< for specialized access to FE Macro in/out arguments
void addSlowControlsChannels(ConfigurationTree slowControlsGroupLink, std::map< std::string, FESlowControlsChannel > *mapOfSlowControlsChannels)
std::string mfSubject_
for GEN_COUT decorations which would be safe in destructors, e.g. mirror interfaceUID_
Definition: FEVInterface.h:294
std::pair< const std::string, std::string > frontEndMacroArg_t
end Slow Controls
Definition: FEVInterface.h:163
virtual FESlowControlsChannel * getNextSlowControlsChannel(void)
virtual in case channels are handled in multiple maps, for example
void runFrontEndMacro(const std::string &targetInterfaceID, const std::string &feMacroName, const std::vector< FEVInterface::frontEndMacroArg_t > &inputArgs, std::vector< FEVInterface::frontEndMacroArg_t > &outputArgs) const
virtual void universalRead(char *address, char *returnValue)=0
throw std::runtime_error exception on error/timeout
void runSelfFrontEndMacro(const std::string &feMacroName, const std::vector< FEVInterface::frontEndMacroArg_t > &inputArgs, std::vector< FEVInterface::frontEndMacroArg_t > &outputArgs)
static std::string & getFEMacroArgument(frontEndMacroArgs_t args, const std::string &argName)
virtual bool running(void)
Definition: FEVInterface.h:131
bool slowControlsRunning(void)
slow controls workloop calls this
virtual unsigned int getSlowControlsChannelCount(void)
virtual in case channels are handled in multiple maps, for example
void receiveFromFrontEnd(const std::string &requester, T &retValue, unsigned int timeoutInSeconds=1) const
bool workLoopThread(toolbox::task::WorkLoop *workLoop)
end FE Communication helpers //////
void registerFEMacroFunction(const std::string &feMacroName, frontEndMacroFunction_t feMacroFunction, const std::vector< std::string > &namesOfInputArgs, const std::vector< std::string > &namesOfOutputArgs, uint8_t requiredUserPermissions=1, const std::string &allowedCallingFEs="*", const std::string &feMacroTooltip="")
std::map< std::string, FESlowControlsChannel > mapOfSlowControlsChannels_
Slow Controls members.
Definition: FEVInterface.h:151
std::mutex frontEndCommunicationReceiveMutex_
FE communication helpers.
CoreSupervisorBase * parentSupervisor_
< members fully define a front-end macro function
Definition: FEVInterface.h:169
const frontEndMacroFunction_t macroFunction_
Note: must be called using this instance.
Definition: FEVInterface.h:189
macroStruct_t(const std::string &macroString)
macroStruct_t constructor
bool lsbf_
least significant byte first
Definition: FEVInterface.h:253
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
vectorToString ~
static T & getWildCardMatchFromMap(const std::string &needle, std::map< std::string, T > &haystack, std::string *foundKey=0)
defined in included .icc source
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
getMapFromString ~
static bool getNumber(const std::string &s, T &retValue)