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