tdaq-develop-2025-02-12
AllSupervisorInfo.cc
1 #include "otsdaq/SupervisorInfo/AllSupervisorInfo.h"
2 
3 #include "otsdaq/Macros/CoutMacros.h"
4 #include "otsdaq/MessageFacility/MessageFacility.h"
5 
6 #include "otsdaq/ConfigurationInterface/ConfigurationManager.h"
7 #include "otsdaq/TablePlugins/XDAQContextTable/XDAQContextTable.h"
8 
9 #include <iostream>
10 
11 using namespace ots;
12 
13 const bool AllSupervisorInfo::MACROMAKER_MODE =
14  ((getenv("MACROMAKER_MODE") ==
15  NULL) // check Macro Maker mode environment variable in a safe way
16  ? (false)
17  : ((std::string(__ENV__("MACROMAKER_MODE")) == "1") ? true : false));
18 
19 //==============================================================================
20 AllSupervisorInfo::AllSupervisorInfo(void)
21  : theSupervisorInfo_(nullptr)
22  , theWizardInfo_(nullptr)
23  , theARTDAQSupervisorInfo_(nullptr)
24 {
25 }
26 
27 //==============================================================================
28 AllSupervisorInfo::AllSupervisorInfo(xdaq::ApplicationContext* applicationContext)
30 {
31  init(applicationContext);
32 }
33 
34 //==============================================================================
35 AllSupervisorInfo::~AllSupervisorInfo(void) { destroy(); }
36 
37 //==============================================================================
38 void AllSupervisorInfo::destroy(void)
39 {
40  allSupervisorInfo_.clear();
41  allFETypeSupervisorInfo_.clear();
42  allDMTypeSupervisorInfo_.clear();
43 
44  theSupervisorInfo_ = nullptr;
45  theWizardInfo_ = nullptr;
46  theARTDAQSupervisorInfo_ = nullptr;
47 
48  SupervisorDescriptorInfoBase::destroy();
49 
50 } // end destroy()
51 
52 //==============================================================================
53 void AllSupervisorInfo::init(xdaq::ApplicationContext* applicationContext)
54 {
55  __COUT__ << "Initializing info based on XDAQ context..." << __E__;
56 
57  AllSupervisorInfo::destroy();
58  SupervisorDescriptorInfoBase::init(applicationContext);
59 
60  auto allDescriptors = SupervisorDescriptorInfoBase::getAllDescriptors();
61  // ready.. loop through all descriptors, and organize
62 
63  // for(const auto& descriptor:allDescriptors)
64  // {
65  // SupervisorInfo tempSupervisorInfo(
66  // descriptor.second /* descriptor */,
67  // "" /* config app name */,"" /* config parent context name */
69  // );
70  //
71  // __COUT__ << "id " << descriptor.second->getLocalId() << " url " <<
72  // descriptor.second->getContextDescriptor()->getURL() << __E__;
73  //
74  // }
75  // __COUTV__(XDAQContextTable::GATEWAY_SUPERVISOR_CLASS);
76 
77  // Steps:
78  // 1. first pass, identify Wiz mode or not
79  // 2. second pass, organize supervisors
80 
81  bool isWizardMode = false;
82  theSupervisorInfo_ = nullptr; // reset
83  theWizardInfo_ = nullptr; // reset
84  theARTDAQSupervisorInfo_ = nullptr; // reset
85 
86  // first pass, identify Wiz mode or not
87  // accept first encountered (wizard or gateway) as the mode
88  for(const auto& descriptor : allDescriptors)
89  {
90  SupervisorInfo tempSupervisorInfo(
91  descriptor.second /* descriptor */,
92  "" /* config app name */,
93  "" /* config parent context name */ // skip configuration info
94  );
95 
96  // check for gateway supervisor
97  if(tempSupervisorInfo.isGatewaySupervisor())
98  {
99  // found normal mode, done with first pass
100  isWizardMode = false;
101  break;
102  }
103  else if(tempSupervisorInfo.isWizardSupervisor())
104  {
105  // found wiz mode, done with first pass
106  isWizardMode = true;
107  break;
108  }
109  }
110 
111  if(AllSupervisorInfo::MACROMAKER_MODE)
112  __COUT__ << "Initializing info for Macro Maker mode XDAQ context..." << __E__;
113  else if(isWizardMode)
114  __COUT__ << "Initializing info for Wiz mode XDAQ context..." << __E__;
115  else
116  __COUT__ << "Initializing info for Normal mode XDAQ context..." << __E__;
117  std::unique_ptr<ConfigurationManager> cfgMgr(
118  (isWizardMode || AllSupervisorInfo::MACROMAKER_MODE)
119  ? 0
120  : new ConfigurationManager());
121  const XDAQContextTable* contextConfig =
122  (isWizardMode || AllSupervisorInfo::MACROMAKER_MODE)
123  ? nullptr
124  : cfgMgr->__GET_CONFIG__(XDAQContextTable);
125  //__COUTV__(contextConfig);
126 
127  // For TRACE controllers in normal mode, temporarily make a map by hostname of supervisors
128  // std::map<std::string /*hostname*/,std::pair<unsigned int, SupervisorInfo>> TRACEAppMap;
129 
130  std::string name, contextName;
131  // do not involve the Configuration Manager
132  // as it adds no valid information to the supervisors
133  // present in wiz mode
134  for(const auto& descriptor : allDescriptors)
135  {
136  name = contextConfig ? contextConfig->getApplicationUID(
137  descriptor.second->getContextDescriptor()->getURL(),
138  descriptor.second->getLocalId())
139  : "" /* config app name */;
140  contextName = contextConfig
141  ? contextConfig->getContextUID(
142  descriptor.second->getContextDescriptor()->getURL())
143  : "" /* config parent context name */;
144 
145  auto /*<iterator,bool>*/ emplacePair =
146  allSupervisorInfo_.emplace(std::pair<unsigned int, SupervisorInfo>(
147  descriptor.second->getLocalId(), // descriptor.first,
148  SupervisorInfo(descriptor.second /* descriptor */, name, contextName)));
149  if(!emplacePair.second)
150  {
151  __SS__ << "Error! Duplicate Application IDs are not allowed. ID ="
152  << descriptor.second->getLocalId() << __E__;
153  __SS_THROW__;
154  }
155 
156  //__COUTV__(emplacePair.first->second.getName());
157  //__COUTV__(emplacePair.first->second.getContextName());
158 
159  // emplace in allTraceControllerSupervisorInfo_ to find app per host to control trace
160  // NOTE: it will fail if there is already an entry for this hostname (which is what we want - keep one app per host)
161  if(emplacePair.first->second.isTypeARTDAQSupervisor())
162  {
163  // make sure artdaq Supervisor represents its host
164  if(theARTDAQSupervisorInfo_)
165  {
166  __SS__ << "Error! Multiple ARTDAQ Supervisors of class "
167  << XDAQContextTable::ARTDAQ_SUPERVISOR_CLASS
168  << " found. There can only be one. ID ="
169  << descriptor.second->getLocalId() << __E__;
170  __SS_THROW__;
171  }
172 
173  theARTDAQSupervisorInfo_ = &(emplacePair.first->second);
174  }
175 
177  // now organize new descriptor by class...
178 
179  // check for gateway supervisor
180  // note: necessarily exclusive to other Supervisor types
181  if(emplacePair.first->second.isGatewaySupervisor())
182  {
183  if(theSupervisorInfo_)
184  {
185  __SS__ << "Error! Multiple Gateway Supervisors of class "
186  << XDAQContextTable::GATEWAY_SUPERVISOR_CLASS
187  << " found. There can only be one. ID ="
188  << descriptor.second->getLocalId() << __E__;
189  __SS_THROW__;
190  }
191  theSupervisorInfo_ = &(emplacePair.first->second);
192  continue;
193  }
194 
195  // check for wizard supervisor
196  // note: necessarily exclusive to other Supervisor types
197  if(emplacePair.first->second.isWizardSupervisor())
198  {
199  if(theWizardInfo_)
200  {
201  __SS__ << "Error! Multiple Wizard Supervisors of class "
202  << XDAQContextTable::WIZARD_SUPERVISOR_CLASS
203  << " found. There can only be one. ID ="
204  << descriptor.second->getLocalId() << __E__;
205  __SS_THROW__;
206  }
207  theWizardInfo_ = &(emplacePair.first->second);
208 
209  continue;
210  }
211 
212  // check for FE type, then add to FE group
213  // note: not necessarily exclusive to other Supervisor types
214  if(emplacePair.first->second.isTypeFESupervisor())
215  {
216  allFETypeSupervisorInfo_.emplace(
217  std::pair<unsigned int, const SupervisorInfo&>(
218  emplacePair.first->second.getId(), emplacePair.first->second));
219  }
220 
221  // check for DM type, then add to DM group
222  // note: not necessarily exclusive to other Supervisor types
223  if(emplacePair.first->second.isTypeDMSupervisor())
224  {
225  allDMTypeSupervisorInfo_.emplace(
226  std::pair<unsigned int, const SupervisorInfo&>(
227  emplacePair.first->second.getId(), emplacePair.first->second));
228  }
229 
230  // check for Logbook type, then add to Logbook group
231  // note: not necessarily exclusive to other Supervisor types
232  if(emplacePair.first->second.isTypeLogbookSupervisor())
233  {
234  allLogbookTypeSupervisorInfo_.emplace(
235  std::pair<unsigned int, const SupervisorInfo&>(
236  emplacePair.first->second.getId(), emplacePair.first->second));
237  }
238 
239  // check for MacroMaker type, then add to MacroMaker group
240  // note: not necessarily exclusive to other Supervisor types
241  if(emplacePair.first->second.isTypeMacroMakerSupervisor())
242  {
243  allMacroMakerTypeSupervisorInfo_.emplace(
244  std::pair<unsigned int, const SupervisorInfo&>(
245  emplacePair.first->second.getId(), emplacePair.first->second));
246  }
247 
248  } // end main extraction loop
249 
250  if(AllSupervisorInfo::MACROMAKER_MODE)
251  {
252  if(theWizardInfo_ || theSupervisorInfo_)
253  {
254  __SS__ << "Error! For MacroMaker mode, must not have one "
255  << XDAQContextTable::GATEWAY_SUPERVISOR_CLASS << " OR one "
256  << XDAQContextTable::WIZARD_SUPERVISOR_CLASS
257  << " as part of the context configuration! "
258  << "One was found." << __E__;
259  __SS_THROW__;
260  }
261  if(allMacroMakerTypeSupervisorInfo_.size() != 1)
262  {
263  __SS__ << "Error! For MacroMaker mode, must have one and only one "
264  << *(XDAQContextTable::MacroMakerTypeClassNames_.begin())
265  << " as part of the context configuration! "
266  << allMacroMakerTypeSupervisorInfo_.size() << " were found." << __E__;
267  __SS_THROW__;
268  }
269  }
270  else if((!theWizardInfo_ && !theSupervisorInfo_) ||
271  (theWizardInfo_ && theSupervisorInfo_))
272  {
273  __SS__ << "Error! Must have one " << XDAQContextTable::GATEWAY_SUPERVISOR_CLASS
274  << " OR one " << XDAQContextTable::WIZARD_SUPERVISOR_CLASS
275  << " as part of the context configuration! "
276  << "Neither (or both) were found." << __E__;
277  __SS_THROW__;
278  }
279 
280  // wrap up TRACE controller map handling
281  {
282  // now create the list of TRACE Controller apps (one per host, but priority to artdaq supervisor)
283  allTraceControllerSupervisorInfo_.clear();
284 
285  if(theARTDAQSupervisorInfo_) // priority to artdaq supervisor
286  {
287  __COUT__ << "The ARTDAQ TRACE-controller app for hostname '"
288  << theARTDAQSupervisorInfo_->getHostname()
289  << "' is CLASS:LID = " << theARTDAQSupervisorInfo_->getClass() << ":"
290  << theARTDAQSupervisorInfo_->getId() << __E__;
291 
292  // DO NOT USE make_pair here.. it somehow infers types that break the map
293  allTraceControllerSupervisorInfo_.emplace(
294  std::pair<std::string, const SupervisorInfo&>(
295  theARTDAQSupervisorInfo_->getHostname(), *theARTDAQSupervisorInfo_));
296  }
297 
298  // the use emplace, because it will fail insert on collisions! (and we want to keep artdaq selection above)
299  for(auto& TRACEApp : allSupervisorInfo_)
300  {
301  // NOTE!! need to copy const SupervisorInfo& from the persistent copy in the allSupervisorInfo
302  if(allTraceControllerSupervisorInfo_
303  .emplace(std::pair<std::string, const SupervisorInfo&>(
304  TRACEApp.second.getHostname(),
305  allSupervisorInfo_.at(TRACEApp.first)))
306  .second)
307  {
308  __COUT__ << "The TRACE-controller app for hostname '"
309  << TRACEApp.second.getHostname()
310  << "' is CLASS:LID = " << TRACEApp.second.getClass() << ":"
311  << TRACEApp.second.getId()
312  << " name = " << TRACEApp.second.getName() << __E__;
313  }
314  // allTraceControllerSupervisorInfo_.emplace(
315  // std::pair<unsigned int, const SupervisorInfo&>(
316  // TRACEApp.second.second.getId(),
317  // allSupervisorInfo_.at(TRACEApp.second.second.getId())));
318  }
319  __COUT__ << "TRACE-controller app count = "
320  << allTraceControllerSupervisorInfo_.size() << __E__;
321 
322  // for(auto& TRACEApp : allTraceControllerSupervisorInfo_)
323  // {
324  // __COUT__ << "The TRACE-controller for hostname = " << TRACEApp.first << "/" << TRACEApp.second.getId() << " is ..."
325  // << " name = " << TRACEApp.second.getName() << " class = " << TRACEApp.second.getClass() << " hostname = " << TRACEApp.second.getHostname()
326  // << __E__;
327  // }
328  }
329 
330  SupervisorDescriptorInfoBase::destroy();
331 
332  __COUT__ << "Supervisor Info initialization complete!" << __E__;
333 
334  // for debugging
335  // getOrderedSupervisorDescriptors("Configure");
336 } // end init()
337 
338 //==============================================================================
339 const SupervisorInfo& AllSupervisorInfo::getSupervisorInfo(xdaq::Application* app) const
340 {
341  auto it = allSupervisorInfo_.find(app->getApplicationDescriptor()->getLocalId());
342  if(it == allSupervisorInfo_.end())
343  {
344  __SS__ << "Could not find: " << app->getApplicationDescriptor()->getLocalId()
345  << std::endl;
346  __SS_THROW__;
347  }
348  return it->second;
349 }
350 
351 //==============================================================================
353  xdaq::Application* app,
354  const std::string& status,
355  const unsigned int progress,
356  const std::string& detail,
357  std::vector<SupervisorInfo::SubappInfo> subapps)
358 {
360  app->getApplicationDescriptor()->getLocalId(), status, progress, detail, subapps);
361 }
362 //==============================================================================
364  const SupervisorInfo& appInfo,
365  const std::string& status,
366  const unsigned int progress,
367  const std::string& detail,
368  std::vector<SupervisorInfo::SubappInfo> subapps)
369 {
370  setSupervisorStatus(appInfo.getId(), status, progress, detail, subapps);
371 }
372 //==============================================================================
374  const unsigned int& id,
375  const std::string& status,
376  const unsigned int progress,
377  const std::string& detail,
378  std::vector<SupervisorInfo::SubappInfo> subapps)
379 {
380  auto it = allSupervisorInfo_.find(id);
381  if(it == allSupervisorInfo_.end())
382  {
383  __SS__ << "Could not find: " << id << __E__;
384  __SS_THROW__;
385  }
386  // non-blocking here, it's ok if the status is not updated (it is probably blocked on purpose, for exampled by the GatewaySupervisor broadcast threads)
387  if(allSupervisorInfoMutex_[id].try_lock())
388  {
389  it->second.setStatus(status, progress, detail);
390  //if subapps size mismatch, then clear to eliminate renamed subapps
391  if(it->second.getSubappInfo().size() != subapps.size())
392  it->second.clearSubapps();
393  for(auto& subapp : subapps)
394  {
395  it->second.copySubappStatus(subapp);
396  }
397  allSupervisorInfoMutex_[id].unlock();
398  }
399 } // end setSupervisorStatus()
400 
401 //==============================================================================
402 void AllSupervisorInfo::clearSupervisorSubappsStatus(const SupervisorInfo& appInfo)
403 {
404  auto it = allSupervisorInfo_.find(appInfo.getId());
405  if(it == allSupervisorInfo_.end())
406  {
407  __SS__ << "Could not find: " << appInfo.getId() << __E__;
408  __SS_THROW__;
409  }
410  // non-blocking here, it's ok if the status is not updated (it is probably blocked on purpose, for exampled by the GatewaySupervisor broadcast threads)
411  if(allSupervisorInfoMutex_[appInfo.getId()].try_lock())
412  {
413  it->second.clearSubapps();
414  allSupervisorInfoMutex_[appInfo.getId()].unlock();
415  }
416 } //end clearSupervisorSubappsStatus()
417 
418 //==============================================================================
419 const SupervisorInfo& AllSupervisorInfo::getGatewayInfo(void) const
420 {
421  if(!theSupervisorInfo_)
422  {
423  __SS__ << "AllSupervisorInfo was not initialized or no Application of type "
424  << XDAQContextTable::GATEWAY_SUPERVISOR_CLASS << " found!" << __E__
425  << StringMacros::stackTrace() << __E__;
426  __SS_THROW__;
427  }
428  return *theSupervisorInfo_;
429 }
430 //==============================================================================
431 XDAQ_CONST_CALL xdaq::ApplicationDescriptor* AllSupervisorInfo::getGatewayDescriptor(
432  void) const
433 {
434  return getGatewayInfo().getDescriptor();
435 }
436 
437 //==============================================================================
438 const SupervisorInfo& AllSupervisorInfo::getWizardInfo(void) const
439 {
440  if(!theWizardInfo_)
441  {
442  __SS__ << "AllSupervisorInfo was not initialized or no Application of type "
443  << XDAQContextTable::WIZARD_SUPERVISOR_CLASS << " found!" << __E__;
444  __SS_THROW__;
445  }
446  return *theWizardInfo_;
447 }
448 //==============================================================================
449 XDAQ_CONST_CALL xdaq::ApplicationDescriptor* AllSupervisorInfo::getWizardDescriptor(
450  void) const
451 {
452  return getWizardInfo().getDescriptor();
453 }
454 
455 //==============================================================================
456 const SupervisorInfo& AllSupervisorInfo::getArtdaqSupervisorInfo(void) const
457 {
458  if(!theARTDAQSupervisorInfo_)
459  {
460  __SS__ << "AllSupervisorInfo was not initialized or no Application of type "
461  << XDAQContextTable::ARTDAQ_SUPERVISOR_CLASS << " found!" << __E__;
462  __SS_THROW__;
463  }
464  return *theARTDAQSupervisorInfo_;
465 } // end getArtdaqSupervisorInfo()
466 
467 //==============================================================================
468 std::vector<std::vector<const SupervisorInfo*>>
469 AllSupervisorInfo::getOrderedSupervisorDescriptors(
470  const std::string& stateMachineCommand, bool onlyGatewayContextSupervisors) const
471 {
472  __COUT__ << "getOrderedSupervisorDescriptors for command " << stateMachineCommand
473  << __E__;
474 
475  std::map<uint64_t /*priority*/, std::vector<unsigned int /*appId*/>>
476  orderedByPriority;
477 
478  try
479  {
480  ConfigurationManager cfgMgr;
481  const std::vector<XDAQContextTable::XDAQContext>& contexts =
482  cfgMgr.__GET_CONFIG__(XDAQContextTable)->getContexts();
483 
484  for(const auto& context : contexts)
485  if(context.status_)
486  for(const auto& app : context.applications_)
487  {
488  if(!app.status_)
489  continue; // skip disabled apps
490 
491  auto it = app.stateMachineCommandPriority_.find(stateMachineCommand);
492  if(it == app.stateMachineCommandPriority_.end())
493  orderedByPriority
495  .push_back(app.id_); // if no priority, then default to
496  // XDAQContextTable::XDAQApplication::DEFAULT_PRIORITY
497  else // take value, and do not allow DEFAULT value of 0 -> force to
498  // XDAQContextTable::XDAQApplication::DEFAULT_PRIORITY
499  orderedByPriority[it->second ? it->second
502  .push_back(app.id_);
503 
504  //__COUT__ << "app.id_ " << app.id_ << __E__;
505  }
506  }
507  catch(...)
508  {
509  __COUT_ERR__ << "SupervisorDescriptorInfoBase could not access the XDAQ Context "
510  "and Application configuration through the Context Table "
511  "Group."
512  << __E__;
513  throw;
514  }
515 
516  __COUT__ << "Here is the order supervisors will be " << stateMachineCommand
517  << "'d:" << __E__;
518 
519  // return ordered set of supervisor infos
520  // skip over Gateway Supervisor,
521  // and other supervisors that do not need state transitions.
522  std::vector<std::vector<const SupervisorInfo*>> retVec;
523  bool createContainer;
524  const std::string whitespace = " ";
525  for(const auto& priorityAppVector : orderedByPriority)
526  {
527  createContainer = true;
528 
529  for(const auto& priorityApp : priorityAppVector.second)
530  {
531  auto it = allSupervisorInfo_.find(priorityApp);
532  if(it == allSupervisorInfo_.end())
533  {
534  __SS__
535  << "Error! Was AllSupervisorInfo properly initialized? The app.id_ "
536  << priorityApp << " priority "
537  << (unsigned int)priorityAppVector.first
538  << " could not be found in AllSupervisorInfo. Was the Context "
539  "changed? Rebooting ots may fix this issue."
540  << __E__;
541  __SS_THROW__;
542  }
543 
544  //__COUT__ << it->second.getName() << " [" << it->second.getId() << "]: " << "
545  // priority? " << (unsigned int)priorityAppVector.first <<
546  //__E__;
547 
548  if(onlyGatewayContextSupervisors &&
549  it->second.getContextName() != theSupervisorInfo_->getContextName())
550  continue; // for shutdown and startup only broadcast to apps that are local to the Gateway supervisor
551 
552  if(it->second.isGatewaySupervisor())
553  continue; // skip gateway supervisor
554  if(it->second.isTypeLogbookSupervisor())
555  continue; // skip logbook supervisor(s)
556  if(it->second.isTypeMacroMakerSupervisor())
557  continue; // skip macromaker supervisor(s)
558  if(it->second.isTypeConfigurationGUISupervisor())
559  continue; // skip configurationGUI supervisor(s)
560  if(it->second.isTypeChatSupervisor())
561  continue; // skip chat supervisor(s)
562  if(it->second.isTypeConsoleSupervisor())
563  continue; // skip console supervisor(s)
564  if(it->second.isTypeCodeEditorSupervisor())
565  continue; // skip code editor supervisor(s)
566 
567  if(createContainer) // create container first time
568  {
569  retVec.push_back(std::vector<const SupervisorInfo*>());
570 
571  // if default priority, create a new vector container for each entry
572  // so they happen in sequence by default
573  // if(priorityAppVector.first !=
574  // XDAQContextTable::XDAQApplication::DEFAULT_PRIORITY)
575  // createContainer = false;
576 
577  createContainer = false;
578  }
579  retVec[retVec.size() - 1].push_back(&(it->second));
580 
581  __COUT__ << "\t" << it->second.getName() << " [LID=" << it->second.getId()
582  << "]: "
583  << (it->second.getName().size() > whitespace.size()
584  ? ""
585  : whitespace.substr(
586  0, whitespace.size() - it->second.getName().size()))
587  << " priority " << (unsigned int)priorityAppVector.first << " count "
588  << retVec[retVec.size() - 1].size() << __E__;
589  }
590  } // end equal priority loop
591  return retVec;
592 } //end getOrderedSupervisorDescriptors()
void setSupervisorStatus(xdaq::Application *app, const std::string &status, const unsigned int progress=100, const std::string &detail="", std::vector< SupervisorInfo::SubappInfo > subapps={})
SETTERs.
bool isWizardMode(void) const
BOOLs.
XDAQ_CONST_CALL xdaq::ApplicationDescriptor * getDescriptor(void) const
Getters ----------------—.
std::string getContextUID(const std::string &url) const
static std::string stackTrace(void)