otsdaq  v2_05_02_indev
XDAQContextTable_table.cc
1 #include "otsdaq/ConfigurationInterface/ConfigurationManager.h"
2 #include "otsdaq/Macros/TablePluginMacros.h"
3 #include "otsdaq/TablePlugins/XDAQContextTable.h"
4 
5 #include <stdio.h>
6 #include <fstream> // std::fstream
7 #include <iostream>
8 
9 using namespace ots;
10 
11 // clang-format off
12 
13 #define XDAQ_RUN_FILE std::string(__ENV__("XDAQ_CONFIGURATION_DATA_PATH")) + "/" + std::string(__ENV__("XDAQ_CONFIGURATION_XML")) + ".xml"
14 #define APP_PRIORITY_FILE std::string(__ENV__("XDAQ_CONFIGURATION_DATA_PATH")) + "/" + "xdaqAppStateMachinePriority"
15 
16 const std::string XDAQContextTable::DEPRECATED_SUPERVISOR_CLASS = "ots::Supervisor"; // still allowed for now, in StartOTS
17 const std::string XDAQContextTable::GATEWAY_SUPERVISOR_CLASS = "ots::GatewaySupervisor";
18 const std::string XDAQContextTable::WIZARD_SUPERVISOR_CLASS = "ots::WizardSupervisor";
19 const std::set<std::string> XDAQContextTable::FETypeClassNames_ = { "ots::FESupervisor", "ots::FEDataManagerSupervisor", "ots::ARTDAQFEDataManagerSupervisor"};
20 const std::set<std::string> XDAQContextTable::DMTypeClassNames_ = { "ots::DataManagerSupervisor", "ots::FEDataManagerSupervisor", "ots::ARTDAQFEDataManagerSupervisor"};
21 const std::set<std::string> XDAQContextTable::LogbookTypeClassNames_ = { "ots::LogbookSupervisor"};
22 const std::set<std::string> XDAQContextTable::MacroMakerTypeClassNames_ = { "ots::MacroMakerSupervisor"};
23 const std::set<std::string> XDAQContextTable::ChatTypeClassNames_ = { "ots::ChatSupervisor"};
24 const std::set<std::string> XDAQContextTable::ConsoleTypeClassNames_ = { "ots::ConsoleSupervisor"};
25 const std::set<std::string> XDAQContextTable::ConfigurationGUITypeClassNames_ = { "ots::TableGUISupervisor"};
26 
27 
28 const uint8_t XDAQContextTable::XDAQApplication::DEFAULT_PRIORITY = 100;
29 
30 XDAQContextTable::ColContext XDAQContextTable::colContext_ = XDAQContextTable::ColContext(); // initialize static member
31 XDAQContextTable::ColApplication XDAQContextTable::colApplication_ = XDAQContextTable::ColApplication(); // initialize static member
32 XDAQContextTable::ColApplicationProperty XDAQContextTable::colAppProperty_ = XDAQContextTable::ColApplicationProperty(); // initialize static member
33 
34 // clang-format on
35 
36 //==============================================================================
37 XDAQContextTable::XDAQContextTable(void)
38 : TableBase(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME), artdaqSupervisorContext_((unsigned int)-1)
39 {
41  // WARNING: the names used in C++ MUST match the Table INFO //
43 }
44 
45 //==============================================================================
46 XDAQContextTable::~XDAQContextTable(void) {}
47 
48 //==============================================================================
49 void XDAQContextTable::init(ConfigurationManager* configManager)
50 {
51  //__COUT__ << "init" << __E__;
52  extractContexts(configManager);
53 
54  bool isFirstAppInContext = configManager->isOwnerFirstAppInContext();
55 
56  //__COUTV__(isFirstAppInContext);
57  if(!isFirstAppInContext)
58  return;
59 
60  {
62  // generate xdaq run parameter file
63  std::fstream fs;
64  fs.open(XDAQ_RUN_FILE, std::fstream::out | std::fstream::trunc);
65  if(fs.fail())
66  {
67  __SS__ << "Failed to open XDAQ run file: " << XDAQ_RUN_FILE << __E__;
68  __SS_THROW__;
69  }
70  outputXDAQXML((std::ostream&)fs);
71  fs.close();
72  }
73 } //end init()
74 
75 //==============================================================================
76 std::string XDAQContextTable::getContextAddress(const std::string& contextUID, bool wantHttp) const
77 {
78  if(contextUID == "X")
79  return "";
80  for(auto& context : contexts_)
81  {
82  if(context.contextUID_ == contextUID)
83  {
84  if(wantHttp)
85  return context.address_;
86  auto address = context.address_;
87  if(address.find("http://") == 0)
88  {
89  address = address.replace(0, 7, "");
90  }
91  if(address.find("https://") == 0)
92  {
93  address = address.replace(0, 8, "");
94  }
95  return address;
96  }
97  }
98  return "";
99 } // end getContextAddress()
100 
101 //==============================================================================
102 const XDAQContextTable::XDAQContext* XDAQContextTable::getTheARTDAQSupervisorContext() const
103 {
104  if(artdaqSupervisorContext_ >= contexts_.size())
105  return nullptr;
106  return &contexts_[artdaqSupervisorContext_];
107 } // end getTheARTDAQSupervisorContext()
108 
109 //==============================================================================
110 ConfigurationTree XDAQContextTable::getContextNode(const ConfigurationManager* configManager, const std::string& contextUID)
111 {
112  return configManager->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME).getNode(contextUID);
113 } // end getContextNode()
114 
115 //==============================================================================
116 ConfigurationTree XDAQContextTable::getApplicationNode(const ConfigurationManager* configManager, const std::string& contextUID, const std::string& appUID)
117 {
118  return configManager->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
119  .getNode(contextUID + "/" + colContext_.colLinkToApplicationTable_ + "/" + appUID);
120 } // end getApplicationNode()
121 
122 //==============================================================================
123 ConfigurationTree XDAQContextTable::getSupervisorConfigNode(const ConfigurationManager* configManager, const std::string& contextUID, const std::string& appUID)
124 {
125  return configManager->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
126  .getNode(contextUID + "/" + XDAQContextTable::colContext_.colLinkToApplicationTable_ + "/" + appUID + "/" +
127  XDAQContextTable::colApplication_.colLinkToSupervisorTable_);
128 } // end getSupervisorConfigNode()
129 
130 //==============================================================================
131 // extractContexts
132 // Could be called by other tables if they need to access the context.
133 // This doesn't re-write config files, it just re-makes constructs in software.
134 void XDAQContextTable::extractContexts(ConfigurationManager* configManager)
135 {
136  //__COUT__ << "*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*" << __E__;
137  //__COUT__ << configManager->__SELF_NODE__ << __E__;
138 
139  // __COUT__ << configManager->getNode(this->getTableName()).getValueAsString()
140  // << __E__;
141 
142  auto children = configManager->__SELF_NODE__.getChildren();
143 
144  contexts_.clear(); // reset
145  artdaqSupervisorContext_ = (unsigned int)-1; // reset
146 
147  // Enforce that app IDs do not repeat!
148  // Note: this is important because there are maps in MacroMaker and
149  // SupervisorDescriptorInfoBase that rely on localId() as key
150  std::set<unsigned int /*appId*/> appIdSet;
151 
152  for(auto& child : children)
153  {
154  contexts_.push_back(XDAQContext());
155  //__COUT__ << child.first << __E__;
156  // __COUT__ << child.second.getNode(colContextUID_) << __E__;
157 
158  contexts_.back().contextUID_ = child.first;
159 
160  contexts_.back().sourceConfig_ =
161  child.second.getTableName() + "_v" + child.second.getTableVersion().toString() + " @ " + std::to_string(child.second.getTableCreationTime());
162  child.second.getNode(colContext_.colContextUID_).getValue(contexts_.back().contextUID_);
163  child.second.getNode(colContext_.colStatus_).getValue(contexts_.back().status_);
164  child.second.getNode(colContext_.colId_).getValue(contexts_.back().id_);
165  child.second.getNode(colContext_.colAddress_).getValue(contexts_.back().address_);
166  child.second.getNode(colContext_.colPort_).getValue(contexts_.back().port_);
167  // conversion to default happens at TableView.icc
168  // if(contexts_.back().port_ == 0) //convert 0 to ${OTS_MAIN_PORT}
169  // contexts_.back().port_ = atoi(__ENV__("OTS_MAIN_PORT"));
170  if(contexts_.back().port_ < 1024 || contexts_.back().port_ > 49151)
171  {
172  __SS__ << "Illegal xdaq Context port: " << contexts_.back().port_ << ". Port must be between 1024 and 49151." << __E__;
173  }
174  // child.second.getNode(colContext_.colARTDAQDataPort_).getValue(contexts_.back().artdaqDataPort_);
175 
176  //__COUT__ << contexts_.back().address_ << __E__;
177  auto appLink = child.second.getNode(colContext_.colLinkToApplicationTable_);
178  if(appLink.isDisconnected())
179  {
180  __SS__ << "Application link is disconnected!" << __E__;
181  __SS_THROW__;
182  }
183 
184  // add xdaq applications to this context
185  auto appChildren = appLink.getChildren();
186  for(auto appChild : appChildren)
187  {
188  //__COUT__ << "Loop: " << child.first << "/" << appChild.first << __E__;
189 
190  contexts_.back().applications_.push_back(XDAQApplication());
191 
192  contexts_.back().applications_.back().applicationGroupID_ = child.first;
193  contexts_.back().applications_.back().sourceConfig_ = appChild.second.getTableName() + "_v" + appChild.second.getTableVersion().toString() + " @ " +
194  std::to_string(appChild.second.getTableCreationTime());
195 
196  appChild.second.getNode(colApplication_.colApplicationUID_).getValue(contexts_.back().applications_.back().applicationUID_);
197  appChild.second.getNode(colApplication_.colStatus_).getValue(contexts_.back().applications_.back().status_);
198  appChild.second.getNode(colApplication_.colClass_).getValue(contexts_.back().applications_.back().class_);
199  appChild.second.getNode(colApplication_.colId_).getValue(contexts_.back().applications_.back().id_);
200 
201  // assert Gateway is 200
202  if((contexts_.back().applications_.back().id_ == 200 &&
203  contexts_.back().applications_.back().class_ != XDAQContextTable::GATEWAY_SUPERVISOR_CLASS &&
204  contexts_.back().applications_.back().class_ != XDAQContextTable::DEPRECATED_SUPERVISOR_CLASS) ||
205  (contexts_.back().applications_.back().id_ != 200 &&
206  (contexts_.back().applications_.back().class_ == XDAQContextTable::GATEWAY_SUPERVISOR_CLASS ||
207  contexts_.back().applications_.back().class_ == XDAQContextTable::DEPRECATED_SUPERVISOR_CLASS)))
208  {
209  __SS__ << "XDAQ Application ID of 200 is reserved for the Gateway "
210  "Supervisor "
211  << XDAQContextTable::GATEWAY_SUPERVISOR_CLASS << ". Conflict specifically at id=" << contexts_.back().applications_.back().id_
212  << " appName=" << contexts_.back().applications_.back().applicationUID_ << __E__;
213  __SS_THROW__;
214  }
215 
216  // assert NO app id repeats if context/app enabled
217  if(contexts_.back().status_ && contexts_.back().applications_.back().status_)
218  {
219  // assert NO app id repeats
220  if(appIdSet.find(contexts_.back().applications_.back().id_) != appIdSet.end())
221  {
222  __SS__ << "XDAQ Application IDs are not unique. Specifically at id=" << contexts_.back().applications_.back().id_
223  << " appName=" << contexts_.back().applications_.back().applicationUID_ << __E__;
224  __COUT_ERR__ << "\n" << ss.str();
225  __SS_THROW__;
226  }
227  appIdSet.insert(contexts_.back().applications_.back().id_);
228  }
229 
230  // convert defaults to values
231  if(appChild.second.getNode(colApplication_.colInstance_).isDefaultValue())
232  contexts_.back().applications_.back().instance_ = 1;
233  else
234  appChild.second.getNode(colApplication_.colInstance_).getValue(contexts_.back().applications_.back().instance_);
235 
236  if(appChild.second.getNode(colApplication_.colNetwork_).isDefaultValue())
237  contexts_.back().applications_.back().network_ = "local";
238  else
239  appChild.second.getNode(colApplication_.colNetwork_).getValue(contexts_.back().applications_.back().network_);
240 
241  if(appChild.second.getNode(colApplication_.colGroup_).isDefaultValue())
242  contexts_.back().applications_.back().group_ = "daq";
243  else
244  appChild.second.getNode(colApplication_.colGroup_).getValue(contexts_.back().applications_.back().group_);
245 
246  appChild.second.getNode(colApplication_.colModule_).getValue(contexts_.back().applications_.back().module_);
247 
248  // force deprecated Supervisor to GatewaySupervisor class
249  if(contexts_.back().applications_.back().class_ == XDAQContextTable::DEPRECATED_SUPERVISOR_CLASS)
250  {
251  contexts_.back().applications_.back().class_ = XDAQContextTable::GATEWAY_SUPERVISOR_CLASS;
252  __COUT__ << "Fixing deprecated Supervisor class from " << XDAQContextTable::DEPRECATED_SUPERVISOR_CLASS << " to "
253  << (contexts_.back().applications_.back().class_);
254  }
255  if(contexts_.back().applications_.back().module_.find("libSupervisor.so") != std::string::npos)
256  {
257  __COUT__ << "Fixing deprecated Supervisor class from " << contexts_.back().applications_.back().module_ << " to ";
258  contexts_.back().applications_.back().module_ =
259  contexts_.back().applications_.back().module_.substr(
260  0, contexts_.back().applications_.back().module_.size() - std::string("Supervisor.so").size()) +
261  "GatewaySupervisor.so";
262  std::cout << contexts_.back().applications_.back().module_ << __E__;
263  }
264 
265  try
266  {
267  appChild.second.getNode(colApplication_.colConfigurePriority_)
268  .getValue(contexts_.back().applications_.back().stateMachineCommandPriority_["Configure"]);
269  appChild.second.getNode(colApplication_.colStartPriority_)
270  .getValue(contexts_.back().applications_.back().stateMachineCommandPriority_["Start"]);
271  appChild.second.getNode(colApplication_.colStopPriority_).getValue(contexts_.back().applications_.back().stateMachineCommandPriority_["Stop"]);
272  }
273  catch(...)
274  {
275  __COUT__ << "Ignoring missing state machine priorities..." << __E__;
276  }
277 
278  auto appPropertyLink = appChild.second.getNode(colApplication_.colLinkToPropertyTable_);
279  if(!appPropertyLink.isDisconnected())
280  {
281  // add xdaq application properties to this context
282 
283  auto appPropertyChildren = appPropertyLink.getChildren();
284 
285  //__COUT__ << "appPropertyChildren.size() " << appPropertyChildren.size()
286  //<< __E__;
287 
288  for(auto appPropertyChild : appPropertyChildren)
289  {
290  contexts_.back().applications_.back().properties_.push_back(XDAQApplicationProperty());
291  contexts_.back().applications_.back().properties_.back().status_ =
292  appPropertyChild.second.getNode(colAppProperty_.colStatus_).getValue<bool>();
293  contexts_.back().applications_.back().properties_.back().name_ =
294  appPropertyChild.second.getNode(colAppProperty_.colPropertyName_).getValue<std::string>();
295  contexts_.back().applications_.back().properties_.back().type_ =
296  appPropertyChild.second.getNode(colAppProperty_.colPropertyType_).getValue<std::string>();
297  contexts_.back().applications_.back().properties_.back().value_ =
298  appPropertyChild.second.getNode(colAppProperty_.colPropertyValue_).getValue<std::string>();
299 
300  //__COUT__ <<
301  // contexts_.back().applications_.back().properties_.back().name_ <<
302  // __E__;
303  }
304  }
305  // else
306  // __COUT__ << "Disconnected." << __E__;
307  }
308 
309  // check for artdaq Supervisor in context
310  {
311  if(!contexts_.back().status_)
312  continue; // skip if disabled
313 
314  for(auto& app : contexts_.back().applications_)
315  {
316  if(app.class_ == // if artdaq interface supervisor
317  "ots::ARTDAQSupervisor" &&
318  app.status_)
319  {
320  __COUT__ << "Found " << app.class_ << " in context '" << contexts_.back().contextUID_ << "'" << __E__;
321 
322  if(artdaqSupervisorContext_ < contexts_.size())
323  {
324  __SS__ << "Error! Only one artdaq Supervisor is allowed to be active - "
325  << "two encountered in context '" << contexts_[artdaqSupervisorContext_].contextUID_ << "' and '" << contexts_.back().contextUID_
326  << "'..." << __E__;
327 
328  artdaqSupervisorContext_ = (unsigned int)-1; // reset
329 
330  __SS_THROW__;
331  }
332 
333  artdaqSupervisorContext_ = contexts_.size() - 1;
334  // break; //continue to look for invalid configuration
335  }
336  } // end artdaq app loop
337 
338  } // end artdaq context handling
339 
340  } // end primary context loop
341 
342 } // end extractContexts()
343 
344 //==============================================================================
345 void XDAQContextTable::outputXDAQXML(std::ostream& out)
346 {
347  // each generated context will look something like this:
348  //<xc:Context id="0" url="http://${SUPERVISOR_SERVER}:${PORT}">
352  //</xc:Context>
353 
354  // print xml header information and declare xc partition
355  out << "<?xml version='1.0'?>\n"
356  << "<xc:Partition \txmlns:xsi\t= \"http://www.w3.org/2001/XMLSchema-instance\"\n"
357  << "\t\txmlns:soapenc\t= \"http://schemas.xmlsoap.org/soap/encoding/\"\n"
358  << "\t\txmlns:xc\t= "
359  "\"http://xdaq.web.cern.ch/xdaq/xsd/2004/XMLConfiguration-30\">\n\n";
360 
361  // print partition open
362  // for each context
363  // open context
364  // for each app in context
365  // print application
366  // print module
367  // close context
368  // close partition
369 
370  char tmp[200];
371  for(XDAQContext& context : contexts_)
372  {
373  //__COUT__ << context.contextUID_ << __E__;
374 
375  sprintf(tmp, "\t<!-- ContextUID='%s' sourceConfig='%s' -->", context.contextUID_.c_str(), context.sourceConfig_.c_str());
376  out << tmp << "\n";
377 
378  if(!context.status_) // comment out if disabled
379  out << "\t<!--\n";
380 
381  sprintf(tmp, "\t<xc:Context id=\"%u\" url=\"%s:%u\">", context.id_, context.address_.c_str(), context.port_);
382  out << tmp << "\n\n";
383 
384  for(XDAQApplication& app : context.applications_)
385  {
386  //__COUT__ << app.id_ << __E__;
387 
388  if(context.status_)
389  {
390  sprintf(tmp,
391  "\t\t<!-- Application GroupID = '%s' UID='%s' sourceConfig='%s' -->",
392  app.applicationGroupID_.c_str(),
393  app.applicationUID_.c_str(),
394  app.sourceConfig_.c_str());
395  out << tmp << "\n";
396 
397  if(!app.status_) // comment out if disabled
398  out << "\t\t<!--\n";
399  }
400 
401  sprintf(tmp,
402  "\t\t<xc:Application class=\"%s\" id=\"%u\" instance=\"%u\" "
403  "network=\"%s\" group=\"%s\">\n",
404  app.class_.c_str(),
405  app.id_,
406  app.instance_,
407  app.network_.c_str(),
408  app.group_.c_str());
409  out << tmp;
410 
412  int foundColon = app.class_.rfind(':');
413  if(foundColon >= 0)
414  ++foundColon;
415  else
416  {
417  __SS__ << "Illegal XDAQApplication class name value of '" << app.class_ << "' - please check the entry for app ID = " << app.id_ << __E__;
418  __SS_THROW__;
419  }
420  out << "\t\t\t<properties xmlns=\"urn:xdaq-application:" << app.class_.substr(foundColon) << "\" xsi:type=\"soapenc:Struct\">\n";
421 
422  //__COUT__ << "app.properties_ " << app.properties_.size() << __E__;
423  for(XDAQApplicationProperty& appProperty : app.properties_)
424  {
425  if(appProperty.type_ == "ots::SupervisorProperty")
426  continue; // skip ots Property values
427 
428  if(!appProperty.status_)
429  out << "\t\t\t\t<!--\n";
430 
431  sprintf(tmp,
432  "\t\t\t\t<%s xsi:type=\"%s\">%s</%s>\n",
433  appProperty.name_.c_str(),
434  appProperty.type_.c_str(),
435  appProperty.value_.c_str(),
436  appProperty.name_.c_str());
437  out << tmp;
438 
439  if(!appProperty.status_)
440  out << "\t\t\t\t-->\n";
441  }
442  out << "\t\t\t</properties>\n";
444 
445  out << "\t\t</xc:Application>\n";
446 
447  sprintf(tmp, "\t\t<xc:Module>%s</xc:Module>\n", app.module_.c_str());
448  out << tmp;
449 
450  if(context.status_ && !app.status_)
451  out << "\t\t-->\n";
452  out << "\n";
453 
454  // __COUT__ << "DONE" << __E__;
455  }
456 
457  out << "\t</xc:Context>\n";
458  if(!context.status_)
459  out << "\t-->\n";
460  out << "\n";
461  }
462 
463  //__COUT__ << "DONE" << __E__;
464  out << "</xc:Partition>\n\n\n";
465 }
466 
467 //==============================================================================
468 std::string XDAQContextTable::getContextUID(const std::string& url) const
469 {
470  for(auto context : contexts_)
471  {
472  if(!context.status_)
473  continue;
474 
475  if(url == context.address_ + ":" + std::to_string(context.port_))
476  return context.contextUID_;
477  }
478  return "";
479 }
480 
481 //==============================================================================
482 std::string XDAQContextTable::getApplicationUID(const std::string& url, unsigned int id) const
483 {
484  //__COUTV__(url); __COUTV__(id);
485  for(auto context : contexts_)
486  {
487  //__COUT__ << "Checking " << (context.address_ + ":" +
488  // std::to_string(context.port_)) << __E__;
489  //__COUTV__(context.status_);
490 
491  if(!context.status_)
492  continue;
493 
494  //__COUT__ << "Checking " << (context.address_ + ":" +
495  // std::to_string(context.port_)) << __E__;
496  if(url == context.address_ + ":" + std::to_string(context.port_))
497  for(auto application : context.applications_)
498  {
499  //__COUTV__(application.status_); __COUTV__(application.id_);
500  if(!application.status_)
501  continue;
502 
503  if(application.id_ == id)
504  {
505  return application.applicationUID_;
506  }
507  }
508  }
509  return "";
510 } //end getApplicationUID()
511 
512 //==============================================================================
513 // only considers ON contexts and applications
514 std::string XDAQContextTable::getContextOfApplication(ConfigurationManager* configManager, const std::string& appUID) const
515 {
516  //look through all contexts until first appUID found
517 
518  auto childrenMap = configManager->__SELF_NODE__.getChildren();
519 
520  for(auto& context : childrenMap)
521  {
522  if(!context.second.getNode(XDAQContextTable::colContext_.colStatus_).getValue<bool>())
523  continue;
524 
525  ConfigurationTree appLink = context.second.getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_);
526  if(appLink.isDisconnected()) continue;
527 
528  auto appMap = appLink.getChildren();
529  for(auto& app : appMap)
530  {
531  if(!app.second.getNode(XDAQContextTable::colApplication_.colStatus_).getValue<bool>())
532  continue;
533 
534  if(app.first == appUID)
535  return context.first; //return context UID
536  } //end app search loop
537  } //end context search loop
538 
539  __SS__ << "Application '" << appUID << "' search found no parent context!" << __E__;
540  __SS_THROW__;
541 } //end getContextOfApplication()
542 
543 //==============================================================================
544 // only considers ON contexts and applications
545 std::string XDAQContextTable::getContextOfGateway(ConfigurationManager* configManager) const
546 {
547  //look through all contexts until first gateway found
548 
549  auto childrenMap = configManager->__SELF_NODE__.getChildren();
550 
551  for(auto& context : childrenMap)
552  {
553  if(!context.second.getNode(XDAQContextTable::colContext_.colStatus_).getValue<bool>())
554  continue;
555 
556  ConfigurationTree appLink = context.second.getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_);
557  if(appLink.isDisconnected()) continue;
558 
559  auto appMap = appLink.getChildren();
560  for(auto& app : appMap)
561  {
562  if(!app.second.getNode(XDAQContextTable::colApplication_.colStatus_).getValue<bool>())
563  continue;
564 
565  std::string className = app.second.getNode(XDAQContextTable::colApplication_.colClass_).getValue<std::string>();
566  if(className ==XDAQContextTable::GATEWAY_SUPERVISOR_CLASS ||
567  className == XDAQContextTable::DEPRECATED_SUPERVISOR_CLASS)
568  return context.first; //return context UID
569  } //end app search loop
570  } //end context search loop
571 
572  __SS__ << "Gateway Application search found no parent context!" << __E__;
573  __SS_THROW__;
574 } //end getContextOfGateway()
575 
576 DEFINE_OTS_TABLE(XDAQContextTable)
void outputXDAQXML(std::ostream &out)