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