otsdaq  3.09.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  __FE_COUTT__ << "Registering FE Macro Function: '" << feMacroName
928  << "' with requiredUserPermissions=" << requiredUserPermissions
929  << " allowedCallingFEs=" << allowedCallingFEs << __E__;
930 
931  mapOfFEMacroFunctions_.insert(std::pair<std::string, frontEndMacroStruct_t>(
932  feMacroName,
933  frontEndMacroStruct_t(feMacroName,
934  feMacroFunction,
935  namesOfInputArgs,
936  namesOfOutputArgs,
937  requiredUserPermissions,
938  allowedCallingFEs,
939  feMacroTooltip)));
940 
941 } // end registerFEMacroFunction()
942 
943 //==============================================================================
949 const std::string& FEVInterface::getFEMacroConstArgument(frontEndMacroConstArgs_t& args,
950  const std::string& argName)
951 {
952  for(const frontEndMacroArg_t& pair : args)
953  {
954  //check arg names and ignore after ( to allow Defaults/Notes to change over time, while not breaking user history
955  if(pair.first.substr(0, pair.first.find('(')) ==
956  argName.substr(0, argName.find('(')))
957  {
958  __COUT__ << argName << ": " << pair.second << __E__;
959  return pair.second;
960  }
961  }
962  __SS__ << "Requested input argument '" << argName
963  << "' not found in list of arguments." << __E__;
964  ss << "\nHere is the list of arguments: \n";
965  for(const frontEndMacroArg_t& pair : args)
966  ss << "\t - " << pair.first << "\n";
967  __SS_THROW__;
968 } //end getFEMacroConstArgument()
969 
970 //==============================================================================
973 template<>
974 std::string ots::getFEMacroConstArgumentValue<std::string>(
975  FEVInterface::frontEndMacroConstArgs_t& args,
976  const std::string& argName,
977  const std::string& defaultValue)
978 {
979  const std::string& data = FEVInterface::getFEMacroConstArgument(args, argName);
980 
981  // default value is used only if the user leaves "Default" or "DEFAULT"
982  if(data == "Default" || data == "DEFAULT")
983  return defaultValue;
984 
985  return data;
986 } //end getFEMacroConstArgumentValue()
987 //==============================================================================
990 template<>
991 std::string ots::getFEMacroArgumentValue<std::string>(
992  FEVInterface::frontEndMacroArgs_t& args, const std::string& argName)
993 {
994  return FEVInterface::getFEMacroArgument(args, argName);
995 } //end getFEMacroArgumentValue()
996 
997 //==============================================================================
1002 std::string& FEVInterface::getFEMacroArgument(frontEndMacroArgs_t& args,
1003  const std::string& argName)
1004 {
1005  for(std::pair<const std::string /* output arg name */,
1006  std::string /* arg output value */>& pair : args)
1007  {
1008  if(pair.first == argName)
1009  return pair.second;
1010  }
1011  __SS__ << "Requested argument not found with name '" << argName << "'" << __E__;
1012  __SS_THROW__;
1013 } //end getFEMacroArgument()
1014 
1015 //==============================================================================
1020 void FEVInterface::runSequenceOfCommands(const std::string& treeLinkName)
1021 {
1022  std::map<uint64_t, uint64_t> writeHistory;
1023  uint64_t writeAddress, writeValue, bitMask;
1024  uint8_t bitPosition;
1025 
1026  std::string writeBuffer;
1027  std::string readBuffer;
1028  char msg[1000];
1029  bool ignoreError = true;
1030 
1031  // ignore errors getting sequence of commands through tree (since it is optional)
1032  try
1033  {
1034  ConfigurationTree configSeqLink =
1035  theXDAQContextConfigTree_.getNode(theConfigurationPath_)
1036  .getNode(treeLinkName);
1037 
1038  // but throw errors if problems executing the sequence of commands
1039  try
1040  {
1041  if(configSeqLink.isDisconnected())
1042  __FE_COUT__ << "Disconnected configure sequence" << __E__;
1043  else
1044  {
1045  __FE_COUT__ << "Handling configure sequence." << __E__;
1046  auto childrenMap = configSeqLink.getChildrenMap();
1047  for(const auto& child : childrenMap)
1048  {
1049  // WriteAddress and WriteValue fields
1050 
1051  writeAddress =
1052  child.second.getNode("WriteAddress").getValue<uint64_t>();
1053  writeValue = child.second.getNode("WriteValue").getValue<uint64_t>();
1054  bitPosition =
1055  child.second.getNode("StartingBitPosition").getValue<uint8_t>();
1056  bitMask =
1057  (1 << child.second.getNode("BitFieldSize").getValue<uint8_t>()) -
1058  1;
1059 
1060  writeValue &= bitMask;
1061  writeValue <<= bitPosition;
1062  bitMask = ~(bitMask << bitPosition);
1063 
1064  // place into write history
1065  if(writeHistory.find(writeAddress) == writeHistory.end())
1066  writeHistory[writeAddress] = 0; // init to 0
1067 
1068  writeHistory[writeAddress] &= bitMask; // clear incoming bits
1069  writeHistory[writeAddress] |= writeValue; // add incoming bits
1070 
1071  sprintf(msg,
1072  "\t Writing %s: \t %ld(0x%lX) \t %ld(0x%lX)",
1073  child.first.c_str(),
1074  writeAddress,
1075  writeAddress,
1076  writeHistory[writeAddress],
1077  writeHistory[writeAddress]);
1078 
1079  __FE_COUT__ << msg << __E__;
1080 
1081  universalWrite((char*)&writeAddress,
1082  (char*)&(writeHistory[writeAddress]));
1083  }
1084  }
1085  }
1086  catch(...)
1087  {
1088  ignoreError = false;
1089  throw;
1090  }
1091  }
1092  catch(...)
1093  {
1094  if(!ignoreError)
1095  throw;
1096  // else ignoring error
1097  __FE_COUT__
1098  << "Unable to access sequence of commands through configuration tree. "
1099  << "Assuming no sequence. " << __E__;
1100  }
1101 } // end runSequenceOfCommands()
1102 
1103 //==============================================================================
1112  const std::string& feMacroName,
1113  // not equivalent to __ARGS__
1114  // left non-const value so caller can modify inputArgs as they are being created
1115  const std::vector<FEVInterface::frontEndMacroArg_t>& argsIn,
1116  std::vector<FEVInterface::frontEndMacroArg_t>& argsOut)
1117 {
1118  // have pointer to virtual FEInterface, find Macro structure
1119  auto FEMacroIt = this->getMapOfFEMacroFunctions().find(feMacroName);
1120  if(FEMacroIt == this->getMapOfFEMacroFunctions().end())
1121  {
1122  __CFG_SS__ << "FE Macro '" << feMacroName << "' of interfaceID '"
1123  << getInterfaceUID() << "' was not found!" << __E__;
1124  __CFG_COUT_ERR__ << "\n" << ss.str();
1125  __CFG_SS_THROW__;
1126  }
1127  const FEVInterface::frontEndMacroStruct_t& feMacro = FEMacroIt->second;
1128 
1129  // check for input arg name match
1130  for(unsigned int i = 0;
1131  i < argsIn.size() && i < feMacro.namesOfInputArguments_.size();
1132  ++i)
1133  if(argsIn[i].first != feMacro.namesOfInputArguments_[i])
1134  {
1135  __CFG_SS__ << "FE Macro '" << feMacro.feMacroName_ << "' of interfaceID '"
1136  << getInterfaceUID() << "' was attempted with a mismatch in"
1137  << " a name of an input argument. " << argsIn[i].first
1138  << " was given. " << feMacro.namesOfInputArguments_[i]
1139  << " expected." << __E__;
1140  __CFG_COUT_ERR__ << "\n" << ss.str();
1141  __CFG_SS_THROW__;
1142  }
1143 
1144  // check namesOfInputArguments_
1145  if(feMacro.namesOfInputArguments_.size() != argsIn.size())
1146  {
1147  __CFG_SS__ << "FE Macro '" << feMacro.feMacroName_ << "' of interfaceID '"
1148  << getInterfaceUID() << "' was attempted with a mismatch in"
1149  << " number of input arguments. " << argsIn.size() << " were given. "
1150  << feMacro.namesOfInputArguments_.size() << " expected." << __E__;
1151  __CFG_COUT_ERR__ << "\n" << ss.str();
1152  __CFG_SS_THROW__;
1153  }
1154 
1155  __CFG_COUT__ << "# of input args = " << argsIn.size() << __E__;
1156  for(auto& argIn : argsIn)
1157  __CFG_COUT__ << argIn.first << ": " << argIn.second << __E__;
1158 
1159  __CFG_COUT__ << "Launching FE Macro '" << feMacro.feMacroName_ << "' ..." << __E__;
1160 
1161  argsOut.clear();
1162  for(unsigned int i = 0; i < feMacro.namesOfOutputArguments_.size(); ++i)
1163  argsOut.push_back(FEVInterface::frontEndMacroArg_t(
1164  feMacro.namesOfOutputArguments_[i], "DEFAULT"));
1165 
1166  // run it!
1167  (this->*(feMacro.macroFunction_))(feMacro, argsIn, argsOut);
1168 
1169  __CFG_COUT__ << "FE Macro complete!" << __E__;
1170 
1171  __CFG_COUT__ << "# of output args = " << argsOut.size() << __E__;
1172  for(const auto& arg : argsOut)
1173  __CFG_COUT__ << arg.first << ": " << arg.second << __E__;
1174 
1175 } // end runSelfFrontEndMacro()
1176 
1177 //==============================================================================
1182  const std::string& targetInterfaceID,
1183  const std::string& feMacroName,
1184  const std::vector<FEVInterface::frontEndMacroArg_t>& inputArgs,
1185  std::vector<FEVInterface::frontEndMacroArg_t>& outputArgs) const
1186 {
1187  __FE_COUTV__(targetInterfaceID);
1188  __FE_COUTV__(VStateMachine::parentSupervisor_);
1189  __FE_COUTV__(VStateMachine::parentSupervisor_->getSupervisorUID());
1190 
1191  auto MacroMakerSupervisors = VStateMachine::parentSupervisor_->allSupervisorInfo_
1192  .getAllMacroMakerTypeSupervisorInfo();
1193  __FE_COUTV__(MacroMakerSupervisors.size());
1194 
1195  if(!MacroMakerSupervisors.size())
1196  {
1197  __FE_SS__ << "No MacroMakerSupervisors found! Notify admins." << __E__;
1198  __FE_SS_THROW__;
1199  }
1200 
1201  std::vector<FEVInterface::frontEndMacroArg_t> encodedInputArgs;
1202  for(auto& arg : inputArgs)
1203  {
1204  __FE_COUT__ << arg.first << ": " << arg.second << __E__;
1205  encodedInputArgs.push_back(
1206  std::make_pair(StringMacros::encodeURIComponent(arg.first),
1207  StringMacros::encodeURIComponent(arg.second)));
1208  }
1209 
1210  std::string inputArgsStr = StringMacros::vectorToString(
1211  encodedInputArgs, ";" /*primaryDelimeter*/, "," /*secondaryDelimeter*/);
1212 
1213  __FE_COUTV__(inputArgsStr);
1214 
1215  xoap::MessageReference message =
1216  SOAPUtilities::makeSOAPMessageReference("FECommunication");
1217 
1218  SOAPParameters parameters;
1219  parameters.addParameter("type", "feMacro");
1220  parameters.addParameter("requester", FEVInterface::interfaceUID_);
1221  parameters.addParameter("targetInterfaceID", targetInterfaceID);
1222  parameters.addParameter("feMacroName", feMacroName);
1223  parameters.addParameter("inputArgs", inputArgsStr);
1224  SOAPUtilities::addParameters(message, parameters);
1225 
1226  __FE_COUT__ << "Sending FE communication: " << SOAPUtilities::translate(message)
1227  << __E__;
1228 
1229  xoap::MessageReference replyMessage =
1230  VStateMachine::parentSupervisor_->SOAPMessenger::sendWithSOAPReply(
1231  MacroMakerSupervisors.begin()->second.getDescriptor(), message);
1232 
1233  __FE_COUT__ << "Response received: " << SOAPUtilities::translate(replyMessage)
1234  << __E__;
1235 
1236  SOAPParameters rxParameters;
1237  rxParameters.addParameter("Error");
1238  SOAPUtilities::receive(replyMessage, rxParameters);
1239 
1240  std::string error = rxParameters.getValue("Error");
1241 
1242  if(error != "")
1243  {
1244  // error occurred!
1245  __FE_SS__ << "Error transmitting request to target interface '"
1246  << targetInterfaceID << "' from '" << FEVInterface::interfaceUID_
1247  << ".' " << error << __E__;
1248  __FE_SS_THROW__;
1249  }
1250 
1251  // extract output args
1252  SOAPParameters argsOutParameter;
1253  argsOutParameter.addParameter("outputArgs");
1254  SOAPUtilities::receive(replyMessage, argsOutParameter);
1255 
1256  std::string outputArgsStr = argsOutParameter.getValue("outputArgs");
1257  std::set<char> pairDelimiter({';'}), nameValueDelimiter({','});
1258 
1259  std::map<std::string, std::string> mapToReturn;
1261  outputArgsStr, mapToReturn, pairDelimiter, nameValueDelimiter);
1262 
1263  outputArgs.clear();
1264  for(auto& mapPair : mapToReturn)
1265  outputArgs.push_back(mapPair);
1266 
1267 } // end runFrontEndMacro()
1268 
1269 //==============================================================================
1274 void FEVInterface::receiveFromFrontEnd(const std::string& requester,
1275  std::string& retValue,
1276  unsigned int timeoutInSeconds) const
1277 {
1278  __FE_COUTV__(requester);
1279  __FE_COUTV__(parentSupervisor_);
1280 
1281  std::string data = "0";
1282  // bool found = false;
1283  while(1)
1284  {
1285  // mutex scope
1286  {
1287  std::lock_guard<std::mutex> lock(
1288  parentInterfaceManager_->frontEndCommunicationReceiveMutex_);
1289 
1290  auto receiveBuffersForTargetIt =
1291  parentInterfaceManager_->frontEndCommunicationReceiveBuffer_.find(
1292  FEVInterface::interfaceUID_);
1293  if(receiveBuffersForTargetIt !=
1294  parentInterfaceManager_->frontEndCommunicationReceiveBuffer_.end())
1295  {
1296  __FE_COUT__ << "Number of source buffers found for front-end '"
1297  << FEVInterface::interfaceUID_
1298  << "': " << receiveBuffersForTargetIt->second.size() << __E__;
1299 
1300  for(auto& buffPair : receiveBuffersForTargetIt->second)
1301  __FE_COUTV__(buffPair.first);
1302 
1303  // match requester to map of buffers
1304  std::string sourceBufferId = "";
1305  std::queue<std::string /*value*/>& sourceBuffer =
1307  requester, receiveBuffersForTargetIt->second, &sourceBufferId);
1308 
1309  __FE_COUT__ << "Found source buffer '" << sourceBufferId << "' with size "
1310  << sourceBuffer.size() << __E__;
1311 
1312  if(sourceBuffer.size())
1313  {
1314  __FE_COUT__ << "Found a value in queue of size "
1315  << sourceBuffer.size() << __E__;
1316 
1317  // remove from receive buffer
1318  retValue = sourceBuffer.front();
1319  sourceBuffer.pop();
1320  return;
1321  }
1322  else
1323  __FE_COUT__ << "Source buffer empty for '" << requester << "'"
1324  << __E__;
1325  }
1326 
1327  // else, not found...
1328 
1329  // if out of time, throw error
1330  if(!timeoutInSeconds)
1331  {
1332  __FE_SS__ << "Timeout (" << timeoutInSeconds
1333  << " s) waiting for front-end communication from " << requester
1334  << "." << __E__;
1335  __FE_SS_THROW__;
1336  }
1337  // else, there is still hope
1338 
1339  } // end mutex scope
1340 
1341  // else try again in a sec
1342  __FE_COUT__ << "Waiting for front-end communication from " << requester << " for "
1343  << timeoutInSeconds << " more seconds..." << __E__;
1344 
1345  --timeoutInSeconds;
1346  sleep(1); // wait a sec
1347  } // end timeout loop
1348 
1349  // should never get here
1350 } // end receiveFromFrontEnd()
1351 
1352 //==============================================================================
1357 std::string FEVInterface::receiveFromFrontEnd(const std::string& requester,
1358  unsigned int timeoutInSeconds) const
1359 {
1360  std::string retValue;
1361  FEVInterface::receiveFromFrontEnd(requester, retValue, timeoutInSeconds);
1362  return retValue;
1363 } // end receiveFromFrontEnd()
1364 
1365 //==============================================================================
1367 FEVInterface::macroStruct_t::macroStruct_t(const std::string& macroString)
1368 {
1369  __COUTVS__(20, macroString);
1370 
1371  // example macro string:
1372  // {"name":"testPublic","sequence":"0:w:1001:writeVal,1:r:1001:","time":"Sat Feb 0
1373  // 9 2019 10:42:03 GMT-0600 (Central Standard Time)","notes":"","LSBF":"false"}@
1374 
1375  std::vector<std::string> extractVec;
1376  StringMacros::getVectorFromString(macroString, extractVec, {'"'});
1377 
1378  __COUTVS__(20, StringMacros::vectorToString(extractVec, " ||| "));
1379 
1380  enum
1381  {
1382  MACRONAME_NAME_INDEX = 1,
1383  MACRONAME_VALUE_INDEX = 3,
1384  SEQUENCE_NAME_INDEX = 5,
1385  SEQUENCE_VALUE_INDEX = 7,
1386  LSBF_NAME_INDEX = 17,
1387  LSFBF_VALUE_INDEX = 19,
1388  };
1389 
1390  // verify fields in sequence (for sanity)
1391  if(MACRONAME_NAME_INDEX >= extractVec.size() ||
1392  extractVec[MACRONAME_NAME_INDEX] != "name")
1393  {
1394  __SS__ << "Invalid sequence, 'name' expected in position " << MACRONAME_NAME_INDEX
1395  << __E__;
1396  __SS_THROW__;
1397  }
1398  if(SEQUENCE_NAME_INDEX >= extractVec.size() ||
1399  extractVec[SEQUENCE_NAME_INDEX] != "sequence")
1400  {
1401  __SS__ << "Invalid sequence, 'sequence' expected in position "
1402  << SEQUENCE_NAME_INDEX << __E__;
1403  __SS_THROW__;
1404  }
1405  if(LSBF_NAME_INDEX >= extractVec.size() || extractVec[LSBF_NAME_INDEX] != "LSBF")
1406  {
1407  __SS__ << "Invalid sequence, 'LSBF' expected in position " << LSBF_NAME_INDEX
1408  << __E__;
1409  __SS_THROW__;
1410  }
1411  macroName_ = extractVec[MACRONAME_VALUE_INDEX];
1412  __COUTVS__(20, macroName_);
1413  lsbf_ = extractVec[LSFBF_VALUE_INDEX] == "false" ? false : true;
1414  __COUTVS__(20, lsbf_);
1415  std::string& sequence = extractVec[SEQUENCE_VALUE_INDEX];
1416  __COUTVS__(20, sequence);
1417 
1418  std::vector<std::string> sequenceCommands;
1419  StringMacros::getVectorFromString(sequence, sequenceCommands, {','});
1420 
1421  __COUTVS__(20, StringMacros::vectorToString(sequenceCommands, " ### "));
1422 
1423  for(auto& command : sequenceCommands)
1424  {
1425  __COUTVS__(20, command);
1426 
1427  // Note: the only way to distinguish between variable and data
1428  // is hex characters or not (lower and upper case allowed for hex)
1429 
1430  std::vector<std::string> commandPieces;
1431  StringMacros::getVectorFromString(command, commandPieces, {':'});
1432 
1433  __COUTVS__(20, StringMacros::vectorToString(commandPieces, " ### "));
1434 
1435  __COUTVS__(20, commandPieces.size());
1436 
1437  // command format
1438  // index | type | address/sleep[ms] | data
1439  // d is delay (1+2)
1440  // w is write (1+3)
1441  // r is read (1+2/3 with arg)
1442 
1443  // extract input arguments, as the variables in address/data fields
1444  // extract output arguments, as the variables in read data fields
1445 
1446  if(commandPieces.size() < 3 || commandPieces.size() > 4 ||
1447  commandPieces[1].size() != 1)
1448  {
1449  __SS__ << "Invalid command type specified in command string: " << command
1450  << __E__;
1451  __SS_THROW__;
1452  }
1453 
1454  //==========================
1455  // Use lambda to identify variable name in field
1456  std::function<bool(const std::string& /*field value*/
1457  )>
1458  localIsVariable = [/*capture variable*/](const std::string& fieldValue) {
1459  // create local message facility subject
1460  std::string mfSubject_ = "isVar";
1461  __GEN_COUTVS__(20, fieldValue);
1462 
1463  // return false if all hex characters found
1464  for(const auto& c : fieldValue)
1465  if(!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
1466  (c >= 'A' && c <= 'F')))
1467  return true; // is variable name!
1468  return false; // else is a valid hex string, so not variable name
1469  }; // end local lambda localIsVariable()
1470 
1471  if(commandPieces[1][0] == 'r' && commandPieces.size() == 4) // read type
1472  {
1473  TLOG_DEBUG(20) << __COUT_HDR__ << "Read type found." << __E__;
1474  // 2: address or optional variable name
1475  // 3: optional variable name
1476 
1477  operations_.push_back(
1478  std::make_pair(macroStruct_t::OP_TYPE_READ, readOps_.size()));
1479 
1480  readOps_.push_back(macroStruct_t::readOp_t());
1481  readOps_.back().addressIsVar_ = localIsVariable(commandPieces[2]);
1482  readOps_.back().dataIsVar_ = localIsVariable(commandPieces[3]);
1483 
1484  if(!readOps_.back().addressIsVar_)
1485  {
1486  if(lsbf_) // flip byte order
1487  {
1488  std::string lsbfData = "";
1489 
1490  // always add leading 0 to guarantee do not miss data
1491  commandPieces[2] = "0" + commandPieces[2];
1492  for(unsigned int i = 0; i < commandPieces[2].size() / 2; ++i)
1493  {
1494  __COUTVS__(20, commandPieces[2].size() - 2 * (i + 1));
1495  // add one byte at a time, backwards
1496  lsbfData +=
1497  commandPieces[2][commandPieces[2].size() - 2 * (i + 1)];
1498  lsbfData +=
1499  commandPieces[2][commandPieces[2].size() - 2 * (i + 1) + 1];
1500  __COUTV__(lsbfData);
1501  }
1502  __COUTVS__(20, lsbfData);
1503  StringMacros::getNumber("0x" + lsbfData, readOps_.back().address_);
1504  }
1505  else
1506  StringMacros::getNumber("0x" + commandPieces[2],
1507  readOps_.back().address_);
1508  }
1509  else
1510  {
1511  readOps_.back().addressVarName_ = commandPieces[2];
1512  __COUTVS__(20, readOps_.back().addressVarName_);
1513 
1514  namesOfInputArguments_.emplace(readOps_.back().addressVarName_);
1515  }
1516 
1517  if(readOps_.back().dataIsVar_)
1518  {
1519  readOps_.back().dataVarName_ = commandPieces[3];
1520  __COUTVS__(20, readOps_.back().dataVarName_);
1521 
1522  namesOfOutputArguments_.emplace(readOps_.back().dataVarName_);
1523  }
1524  }
1525  else if(commandPieces[1][0] == 'w' && commandPieces.size() == 4) // write type
1526  {
1527  TLOG_DEBUG(20) << __COUT_HDR__ << "Write type found." << __E__;
1528  // 2: address or optional variable name
1529  // 3: data or optional variable name
1530 
1531  operations_.push_back(
1532  std::make_pair(macroStruct_t::OP_TYPE_WRITE, writeOps_.size()));
1533 
1534  writeOps_.push_back(macroStruct_t::writeOp_t());
1535  writeOps_.back().addressIsVar_ = localIsVariable(commandPieces[2]);
1536  writeOps_.back().dataIsVar_ = localIsVariable(commandPieces[3]);
1537 
1538  if(!writeOps_.back().addressIsVar_)
1539  {
1540  if(lsbf_) // flip byte order
1541  {
1542  std::string lsbfData = "";
1543 
1544  // always add leading 0 to guarantee do not miss data
1545  commandPieces[2] = "0" + commandPieces[2];
1546  for(unsigned int i = 0; i < commandPieces[2].size() / 2; ++i)
1547  {
1548  __COUTV__(commandPieces[2].size() - 2 * (i + 1));
1549  // add one byte at a time, backwards
1550  lsbfData +=
1551  commandPieces[2][commandPieces[2].size() - 2 * (i + 1)];
1552  lsbfData +=
1553  commandPieces[2][commandPieces[2].size() - 2 * (i + 1) + 1];
1554  __COUTV__(lsbfData);
1555  }
1556  __COUTVS__(20, lsbfData);
1557  StringMacros::getNumber("0x" + lsbfData, writeOps_.back().address_);
1558  }
1559  else
1560  StringMacros::getNumber("0x" + commandPieces[2],
1561  writeOps_.back().address_);
1562  }
1563  else
1564  {
1565  writeOps_.back().addressVarName_ = commandPieces[2];
1566  __COUTVS__(20, writeOps_.back().addressVarName_);
1567 
1568  namesOfInputArguments_.emplace(writeOps_.back().addressVarName_);
1569  }
1570 
1571  if(!writeOps_.back().dataIsVar_)
1572  {
1573  if(lsbf_) // flip byte order
1574  {
1575  std::string lsbfData = "";
1576 
1577  // always add leading 0 to guarantee do not miss data
1578  commandPieces[2] = "0" + commandPieces[3];
1579  for(unsigned int i = 0; i < commandPieces[3].size() / 2; ++i)
1580  {
1581  __COUTVS__(20, commandPieces[3].size() - 2 * (i + 1));
1582  // add one byte at a time, backwards
1583  lsbfData +=
1584  commandPieces[3][commandPieces[3].size() - 2 * (i + 1)];
1585  lsbfData +=
1586  commandPieces[3][commandPieces[3].size() - 2 * (i + 1) + 1];
1587  __COUTVS__(20, lsbfData);
1588  }
1589  __COUTV__(lsbfData);
1590  StringMacros::getNumber("0x" + lsbfData, writeOps_.back().data_);
1591  }
1592  else
1593  StringMacros::getNumber("0x" + commandPieces[3],
1594  writeOps_.back().data_);
1595  }
1596  else
1597  {
1598  writeOps_.back().dataVarName_ = commandPieces[3];
1599  __COUTVS__(20, writeOps_.back().dataVarName_);
1600 
1601  namesOfInputArguments_.emplace(writeOps_.back().dataVarName_);
1602  }
1603  }
1604  else if(commandPieces[1][0] == 'd' && commandPieces.size() == 3) // delay type
1605  {
1606  TLOG_DEBUG(20) << __COUT_HDR__ << "Delay type found." << __E__;
1607  // 2: delay[ms] or optional variable name
1608 
1609  operations_.push_back(
1610  std::make_pair(macroStruct_t::OP_TYPE_DELAY, delayOps_.size()));
1611 
1612  delayOps_.push_back(macroStruct_t::delayOp_t());
1613  delayOps_.back().delayIsVar_ = localIsVariable(commandPieces[2]);
1614 
1615  if(!delayOps_.back().delayIsVar_)
1616  StringMacros::getNumber("0x" + commandPieces[2], delayOps_.back().delay_);
1617  else
1618  {
1619  delayOps_.back().delayVarName_ = commandPieces[2];
1620  __COUTVS__(20, delayOps_.back().delayVarName_);
1621 
1622  namesOfInputArguments_.emplace(delayOps_.back().delayVarName_);
1623  }
1624  }
1625  else // invalid type
1626  {
1627  __SS__ << "Invalid command type '" << commandPieces[1][0]
1628  << "' specified with " << commandPieces.size() << " components."
1629  << __E__;
1630  __SS_THROW__;
1631  }
1632 
1633  } // end sequence commands extraction loop
1634 
1635  __COUTT__ << operations_.size() << " operations extracted: \n\t" << readOps_.size()
1636  << " reads \n\t" << writeOps_.size() << " writes \n\t" << delayOps_.size()
1637  << " delays" << __E__;
1638 
1639  __COUTT__ << "Input arguments: " << __E__;
1640  for(const auto& inputArg : namesOfInputArguments_)
1641  __COUTT__ << "\t" << inputArg << __E__;
1642 
1643  __COUTT__ << "Output arguments: " << __E__;
1644  for(const auto& outputArg : namesOfOutputArguments_)
1645  __COUTT__ << "\t" << outputArg << __E__;
1646 
1647 } // end macroStruct_t constructor
1648 
1649 //==============================================================================
1653  std::map<std::string /*name*/, uint64_t /*value*/>& variableMap)
1654 {
1655  // Similar to FEVInterface::runSequenceOfCommands()
1656 
1657  __FE_COUT__ << "Running Macro '" << macro.macroName_ << "' of "
1658  << macro.operations_.size() << " operations." << __E__;
1659 
1660  for(auto& op : macro.operations_)
1661  {
1662  if(op.first == macroStruct_t::OP_TYPE_READ)
1663  {
1664  __FE_COUT__ << "Doing read op..." << __E__;
1665  macroStruct_t::readOp_t& readOp = macro.readOps_[op.second];
1666  if(readOp.addressIsVar_)
1667  {
1668  __FE_COUTV__(readOp.addressVarName_);
1669  readOp.address_ = variableMap.at(readOp.addressVarName_);
1670  }
1671 
1672  uint64_t dataValue = 0;
1673 
1674  __FE_COUT__ << std::hex << "Read address: \t 0x" << readOp.address_ << __E__
1675  << std::dec;
1676 
1677  universalRead((char*)&readOp.address_, (char*)&dataValue);
1678 
1679  __FE_COUT__ << std::hex << "Read data: \t 0x" << dataValue << __E__
1680  << std::dec;
1681 
1682  if(readOp.dataIsVar_)
1683  {
1684  __FE_COUTV__(readOp.dataVarName_);
1685  variableMap.at(readOp.dataVarName_) = dataValue;
1686  }
1687 
1688  } // end read op
1689  else if(op.first == macroStruct_t::OP_TYPE_WRITE)
1690  {
1691  __FE_COUT__ << "Doing write op..." << __E__;
1692  macroStruct_t::writeOp_t& writeOp = macro.writeOps_[op.second];
1693  if(writeOp.addressIsVar_)
1694  {
1695  __FE_COUTV__(writeOp.addressVarName_);
1696  writeOp.address_ = variableMap.at(writeOp.addressVarName_);
1697  }
1698  if(writeOp.dataIsVar_)
1699  {
1700  __FE_COUTV__(writeOp.dataVarName_);
1701  writeOp.data_ = variableMap.at(writeOp.dataVarName_);
1702  }
1703 
1704  __FE_COUT__ << std::hex << "Write address: \t 0x" << writeOp.address_ << __E__
1705  << std::dec;
1706  __FE_COUT__ << std::hex << "Write data: \t 0x" << writeOp.data_ << __E__
1707  << std::dec;
1708 
1709  universalWrite((char*)&writeOp.address_, (char*)&writeOp.data_);
1710 
1711  } // end write op
1712  else if(op.first == macroStruct_t::OP_TYPE_DELAY)
1713  {
1714  __FE_COUT__ << "Doing delay op..." << __E__;
1715 
1716  macroStruct_t::delayOp_t& delayOp = macro.delayOps_[op.second];
1717  if(delayOp.delayIsVar_)
1718  {
1719  __FE_COUTV__(delayOp.delayVarName_);
1720  delayOp.delay_ = variableMap.at(delayOp.delayVarName_);
1721  }
1722 
1723  __FE_COUT__ << std::dec << "Delay ms: \t " << delayOp.delay_ << __E__;
1724 
1725  usleep(delayOp.delay_ /*ms*/ * 1000);
1726 
1727  } // end delay op
1728  else // invalid type
1729  {
1730  __FE_SS__ << "Invalid command type '" << op.first << "!'" << __E__;
1731  __FE_SS_THROW__;
1732  }
1733 
1734  } // end operations loop
1735 
1736 } // end runMacro
1737 
1738 //==============================================================================
1739 void FEVInterface::setFEMacroPercentDone(unsigned int percentDone)
1740 {
1741  std::lock_guard<std::mutex> lock(feMacroPercentDoneMutex_);
1742  feMacroPercentDoneMap_[std::this_thread::get_id()] =
1743  static_cast<int>(percentDone > 100 ? 100 : percentDone);
1744 } // end setFEMacroPercentDone()
1745 
1746 //==============================================================================
1747 int FEVInterface::getFEMacroPercentDone(std::thread::id threadID) const
1748 {
1749  std::lock_guard<std::mutex> lock(feMacroPercentDoneMutex_);
1750  auto it = feMacroPercentDoneMap_.find(threadID);
1751  if(it == feMacroPercentDoneMap_.end())
1752  return -1;
1753  return it->second;
1754 } // end getFEMacroPercentDone()
1755 
1756 //==============================================================================
1757 void FEVInterface::clearFEMacroPercentDone(std::thread::id threadID)
1758 {
1759  std::lock_guard<std::mutex> lock(feMacroPercentDoneMutex_);
1760  feMacroPercentDoneMap_.erase(threadID);
1761 } // end clearFEMacroPercentDone()
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:176
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:311
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:170
std::string mfSubject_
for GEN_COUT decorations which would be safe in destructors, e.g. mirror interfaceUID_
Definition: FEVInterface.h:303
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)
void setFEMacroPercentDone(unsigned int percentDone)
FE Macro progress reporting (for async macros via FESupervisor)
static std::string & getFEMacroArgument(frontEndMacroArgs_t args, const std::string &argName)
virtual bool running(void)
Definition: FEVInterface.h:138
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:158
std::mutex frontEndCommunicationReceiveMutex_
FE communication helpers.
CoreSupervisorBase * parentSupervisor_
defines used also by OtsConfigurationWizardSupervisor
< members fully define a front-end macro function
Definition: FEVInterface.h:178
const frontEndMacroFunction_t macroFunction_
Note: must be called using this instance.
Definition: FEVInterface.h:198
macroStruct_t(const std::string &macroString)
macroStruct_t constructor
bool lsbf_
least significant byte first
Definition: FEVInterface.h:262
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)