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