otsdaq_utilities  v2_05_02_indev
VisualSupervisor.cc
1 #include "otsdaq-utilities/Visualization/VisualSupervisor.h"
2 #include "otsdaq/XmlUtilities/XmlDocument.h"
3 //#include "otsdaq-utilities/Visualization/fileSystemToXML.h"
4 //#include "otsdaq/RootUtilities/DQMHistos.h"
5 #include <boost/regex.hpp>
6 #include "otsdaq/DataManager/DataManagerSingleton.h"
7 #include "otsdaq/Macros/BinaryStringMacros.h"
8 //#include "otsdaq/otsdaq/Macros/MessageTools.h"
9 #include "otsdaq/DataManager/DQMHistosConsumerBase.h"
10 //#include <boost/regex.hpp>
11 #include "otsdaq/Macros/MessageTools.h"
12 #include "otsdaq/RootUtilities/RootFileExplorer.h"
13 
14 // ROOT documentation
15 // http://root.cern.ch/root/html/index.html
16 
17 #include <TBranchElement.h>
18 #include <TBuffer.h>
19 #include <TBufferJSON.h>
20 #include <TCanvas.h>
21 #include <TClass.h>
22 #include <TDirectory.h>
23 #include <TFile.h>
24 #include <TH1.h>
25 #include <TH2.h>
26 #include <TIterator.h>
27 #include <TKey.h>
28 #include <TProfile.h>
29 #include <TROOT.h>
30 #include <TRegexp.h>
31 #include <TString.h>
32 #include <TTree.h>
33 #include "TBufferFile.h"
34 #include "TObject.h"
35 
36 #include <dirent.h> /*DIR and dirent*/
37 #include <sys/stat.h> /*mkdir*/
38 
39 #include <xdaq/NamespaceURI.h>
40 
41 #include <iostream>
42 #include <mutex>
43 
44 #define ROOT_BROWSER_PATH __ENV__("ROOT_BROWSER_PATH")
45 #define ROOT_DISPLAY_CONFIG_PATH __ENV__("ROOT_DISPLAY_CONFIG_PATH")
46 
47 #define LIVEDQM_DIR std::string("LIVE_DQM")
48 #define PRE_MADE_ROOT_CFG_DIR std::string("Pre-made Views")
49 
50 #define PRE_MADE_ROOT_CFG_FILE_EXT std::string(".rcfg")
51 
52 #define PREFERENCES_PATH std::string(__ENV__("SERVICE_DATA_PATH")) + "/VisualizerData/"
53 #define PREFERENCES_FILE_EXT ".pref"
54 
55 #define ROOT_VIEWER_PERMISSIONS_THRESHOLD 100
56 
57 using namespace ots;
58 
59 #undef __MF_SUBJECT__
60 #define __MF_SUBJECT__ "Visualizer"
61 
62 XDAQ_INSTANTIATOR_IMPL(VisualSupervisor)
63 
64 #undef STDLINE
65 #define STDLINE(X, Y) __COUT__ << X
66 
67 //==============================================================================
68 VisualSupervisor::VisualSupervisor(xdaq::ApplicationStub* stub)
69  : CoreSupervisorBase(stub), theDataManager_(nullptr), loadedRunNumber_(-1)
70 {
71  __SUP_COUT__ << "Constructor." << __E__;
72  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
73 
74  mkdir(((std::string)PREFERENCES_PATH).c_str(), 0755);
75 
76  __SUP_COUT__ << "Constructed." << __E__;
77 } //end constructor
78 
79 //==============================================================================
80 VisualSupervisor::~VisualSupervisor(void)
81 {
82  __SUP_COUT__ << "Destructor." << __E__;
83  destroy();
84  __SUP_COUT__ << "Destructed." << __E__;
85 } //end destructor
86 
87 //==============================================================================
88 void VisualSupervisor::destroy(void)
89 {
90  __SUP_COUT__ << "Destroying..." << __E__;
91 
92  DataManagerSingleton::deleteInstance(CorePropertySupervisorBase::getSupervisorUID());
93  if(theStateMachineImplementation_.size() > 1)
94  {
95  __SS__ << "Not expecting more than one visual data manager!" << __E__;
96  __SS_THROW__;
97  }
98  if(theStateMachineImplementation_.size())
99  theStateMachineImplementation_.pop_back();
100  else
101  __COUT_WARN__ << "No visual data manager was pushed." << __E__;
102 } //end destroy()
103 
104 //==============================================================================
105 void VisualSupervisor::transitionConfiguring(toolbox::Event::Reference /*e*/)
106 {
107  __SUP_COUT__ << "Configuring..." << __E__;
108 
109  { //do like start of CoreSupervisorBase::transitionConfiguring
110  // activate the configuration tree (the first iteration)
111  if(RunControlStateMachine::getIterationIndex() == 0 && RunControlStateMachine::getSubIterationIndex() == 0)
112  {
113  std::pair<std::string /*group name*/, TableGroupKey> theGroup(
114  SOAPUtilities::translate(theStateMachine_.getCurrentMessage()).getParameters().getValue("ConfigurationTableGroupName"),
115  TableGroupKey(SOAPUtilities::translate(theStateMachine_.getCurrentMessage()).getParameters().getValue("ConfigurationTableGroupKey")));
116 
117  __SUP_COUT__ << "Configuration table group name: " << theGroup.first << " key: " << theGroup.second << __E__;
118 
119  theConfigurationManager_->loadTableGroup(theGroup.first, theGroup.second, true /*doActivate*/);
120  }
121  } //end start like CoreSupervisorBase::transitionConfiguring
122 
123  __COUTV__(theConfigurationManager_->__GET_CONFIG__(XDAQContextTable)->getTableName());
124  __COUTV__(theConfigurationManager_->getNode(
125  theConfigurationManager_->__GET_CONFIG__(XDAQContextTable)->getTableName()).getValueAsString());
126 
127  __COUTV__("/" + theConfigurationManager_->__GET_CONFIG__(XDAQContextTable)->getTableName() + CorePropertySupervisorBase::getSupervisorConfigurationPath());
128 
129 
130  ConfigurationTree appLink = theConfigurationManager_->getNode("/" +
131  theConfigurationManager_->__GET_CONFIG__(XDAQContextTable)->getTableName() +
132  CorePropertySupervisorBase::getSupervisorConfigurationPath());
133 
134  __COUTV__(appLink.getValueAsString());
135 
136 
137  if(!appLink.isDisconnected())
138  {
139  theDataManager_ = DataManagerSingleton::getInstance<VisualDataManager>(
140  theConfigurationManager_->getNode(
141  theConfigurationManager_->__GET_CONFIG__(XDAQContextTable)->getTableName()),
142  CorePropertySupervisorBase::getSupervisorConfigurationPath(),
143  CorePropertySupervisorBase::getSupervisorUID());
144 
145  CoreSupervisorBase::theStateMachineImplementation_.push_back(theDataManager_);
146 
147  __SUP_COUT__ << "Done instantiating Visual data manager." << __E__;
148  }
149  else
150  __SUP_COUT__ << "No Visual Supervisor configuration link, so skipping Visual data manager instantiation." << __E__;
151 
152  //just handle FSMs
153  CoreSupervisorBase::transitionConfiguringFSMs();
154 
155  __SUP_COUT__ << "Configured." << __E__;
156 } //end transitionConfiguring()
157 
158 //==============================================================================
159 void VisualSupervisor::transitionHalting(toolbox::Event::Reference e)
160 {
161  __SUP_COUT__ << "Halting..." << __E__;
162 
163  CoreSupervisorBase::transitionHalting(e);
164  destroy();
165 
166  __SUP_COUT__ << "Halted." << __E__;
167 } //end transitionHalting()
168 
169 //==============================================================================
170 // setSupervisorPropertyDefaults
171 // override to set defaults for supervisor property values (before user settings
172 // override)
173 void VisualSupervisor::setSupervisorPropertyDefaults()
174 {
175  CorePropertySupervisorBase::setSupervisorProperty(
176  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AllowNoLoginRequestTypes,
177  "setUserPreferences | getUserPreferences | getDirectoryContents | getRoot | "
178  "getEvents");
179 
180  CorePropertySupervisorBase::setSupervisorProperty(
181  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
182  "*=1 | rootAdminControls=100");
183 }
184 
185 //==============================================================================
186 // forceSupervisorPropertyValues
187 // override to force supervisor property values (and ignore user settings)
188 void VisualSupervisor::forceSupervisorPropertyValues()
189 {
190  CorePropertySupervisorBase::setSupervisorProperty(
191  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
192  "getRoot | getEvents");
193  CorePropertySupervisorBase::setSupervisorProperty(
194  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NoXmlWhiteSpaceRequestTypes,
195  "getRoot | getEvents");
196  // json data in ROOTJS library expects no funny
197  // characters
198  // CorePropertySupervisorBase::setSupervisorProperty(CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NeedUsernameRequestTypes,
199  // "setUserPreferences | getUserPreferences");
200 }
201 
202 //==============================================================================
203 void VisualSupervisor::request(const std::string& requestType,
204  cgicc::Cgicc& cgiIn,
205  HttpXmlDocument& xmlOut,
206  const WebUsers::RequestUserInfo& userInfo)
207 {
208  // Commands
209  // getRawData
210  // getGeometry
211  // getEvents
212  // getRoot
213  // getDirectoryContents
214  // setUserPreferences
215  // getUserPreference
216 
217  // if (requestType == "getHisto" && theDataManager_->getLiveDQMHistos() != 0)
218  // {
219  // // TH1I* histo =
220  // (TH1I*)theDataManager_->getLiveDQMHistos()->get("Planes/Plane_0_Occupancy");
221  //
222  // // theDataManager_->load("Run185_Histo.root","Histograms");
223  // //TH1F* histo1d = theDataManager_->getFileDQMHistos().getHisto1D();
224  // //TCanvas* canvas = theDataManager_->getFileDQMHistos().getCanvas ();
225  // //TH2F* histo2d = theDataManager_->getFileDQMHistos().getHisto2D();
226  // //TProfile* profile = theDataManager_->getFileDQMHistos().getProfile();
227  //
228  // }
229  std::stringstream ss;
230  // ss << "Request type: |" << requestType << "|";
231  // //STDLINE(ss.str(),"") ;
232  if(requestType ==
233  "getRawData") //################################################################################################################
234  {
235  __SUP_COUT__ << __E__;
236  try
237  {
238  // TODO -- add timestamp, so we know if data is new
239 
240  if(theDataManager_ == nullptr)
241  {
242  __SS__ << "No data manager instantiated." << __E__;
243  __SS_THROW__;
244  }
245  __SUP_COUT__ << "Getting Raw data and converting to binary string" << __E__;
246  xmlOut.addBinaryStringToData("rawData", theDataManager_->getRawData());
247  __SUP_COUT__ << __E__;
248  }
249  catch(std::exception const& e)
250  {
251  __SUP_COUT__
252  << "ERROR! Exception while getting raw data. Incoming exception data..."
253  << __E__;
254  __SUP_COUT__ << e.what() << __E__;
255  __SUP_COUT__ << "End Exception Data" << __E__;
256  }
257  catch(...)
258  {
259  __SUP_COUT__ << "ERROR! Something went wrong trying to get raw data."
260  << __E__;
261  __SUP_COUT_INFO__ << "ERROR! Something went wrong trying to get raw data."
262  << __E__;
263  }
264  }
265  else if(
266  requestType == "setUserPreferences" &&
267  userInfo.username_ !=
268  "" /*from allow no user*/) //################################################################################################################
269  {
270  __SUP_COUT__ << "userInfo.username_: " << userInfo.username_ << __E__;
271  std::string fullPath =
272  (std::string)PREFERENCES_PATH + userInfo.username_ + PREFERENCES_FILE_EXT;
273  __SUP_COUT__ << "fullPath: " << fullPath << __E__;
274 
275  std::string radioSelect = CgiDataUtilities::getData(cgiIn, "radioSelect");
276  std::string autoRefresh = CgiDataUtilities::getData(cgiIn, "autoRefresh");
277  std::string autoHide = CgiDataUtilities::getData(cgiIn, "autoHide");
278  std::string hardRefresh = CgiDataUtilities::getData(cgiIn, "hardRefresh");
279  std::string autoRefreshPeriod =
280  CgiDataUtilities::getData(cgiIn, "autoRefreshPeriod");
281 
282  __SUP_COUT__ << "radioSelect: " << radioSelect << __E__;
283  __SUP_COUT__ << "autoRefresh: " << autoRefresh << __E__;
284  __SUP_COUT__ << "autoHide: " << autoHide << __E__;
285  __SUP_COUT__ << "hardRefresh: " << hardRefresh << __E__;
286  __SUP_COUT__ << "autoRefreshPeriod: " << autoRefreshPeriod << __E__;
287 
288  // read existing
289  FILE* fp = fopen(fullPath.c_str(), "r");
290  if(fp)
291  {
292  char line[100];
293  char val[100];
294 
295  fgets(line, 100, fp);
296  sscanf(line, "%*s %s", val);
297  if(radioSelect == "")
298  radioSelect = val;
299 
300  fgets(line, 100, fp);
301  sscanf(line, "%*s %s", val);
302  if(autoRefresh == "")
303  autoRefresh = val;
304 
305  fgets(line, 100, fp);
306  sscanf(line, "%*s %s", val);
307  if(autoHide == "")
308  autoHide = val;
309 
310  fgets(line, 100, fp);
311  sscanf(line, "%*s %s", val);
312  if(hardRefresh == "")
313  hardRefresh = val;
314 
315  fgets(line, 100, fp);
316  sscanf(line, "%*s %s", val);
317  if(autoRefreshPeriod == "")
318  autoRefreshPeriod = val;
319 
320  fclose(fp);
321  }
322 
323  // write new
324  fp = fopen(fullPath.c_str(), "w");
325  if(fp)
326  {
327  fprintf(fp, "radioSelect %s\n", radioSelect.c_str());
328  fprintf(fp, "autoRefresh %s\n", autoRefresh.c_str());
329  fprintf(fp, "autoHide %s\n", autoHide.c_str());
330  fprintf(fp, "hardRefresh %s\n", hardRefresh.c_str());
331  fprintf(fp, "autoRefreshPeriod %s\n", autoRefreshPeriod.c_str());
332  fclose(fp);
333  }
334  else
335  __SUP_COUT_ERR__ << "Failure writing preferences to file: " << fullPath
336  << __E__;
337  }
338  else if(
339  requestType ==
340  "getUserPreferences") //################################################################################################################
341  {
342  __SUP_COUT__ << "userInfo.username_: " << userInfo.username_ << __E__;
343  std::string fullPath =
344  (std::string)PREFERENCES_PATH + userInfo.username_ + PREFERENCES_FILE_EXT;
345  __SUP_COUT__ << "fullPath: " << fullPath << __E__;
346 
347  FILE* fp = fopen(fullPath.c_str(), "r");
348  if(fp)
349  {
350  char line[100];
351  // char val[100];
352  int val;
353 
354  fgets(line, 100, fp);
355  sscanf(line, "%*s %d", &val);
356  if(val < 0 || val > 3)
357  val = 0; // FIXME.. value can get corrupt...
358  xmlOut.addTextElementToData("radioSelect", std::to_string(val));
359  fgets(line, 100, fp);
360  sscanf(line, "%*s %d", &val);
361  xmlOut.addTextElementToData("autoRefresh", std::to_string(val));
362  fgets(line, 100, fp);
363  sscanf(line, "%*s %d", &val);
364  xmlOut.addTextElementToData("autoHide", std::to_string(val));
365  fgets(line, 100, fp);
366  sscanf(line, "%*s %d", &val);
367  xmlOut.addTextElementToData("hardRefresh", std::to_string(val));
368  fgets(line, 100, fp);
369  sscanf(line, "%*s %d", &val);
370  xmlOut.addTextElementToData("autoRefreshPeriod", std::to_string(val));
371  fclose(fp);
372  }
373  else
374  {
375  // else assume user has no preferences yet
376  xmlOut.addTextElementToData("radioSelect", "");
377  xmlOut.addTextElementToData("autoRefresh", "");
378  xmlOut.addTextElementToData("autoHide", "");
379  xmlOut.addTextElementToData("hardRefresh", "");
380  xmlOut.addTextElementToData("autoRefreshPeriod", "");
381  }
382  }
383  else if(
384  requestType ==
385  "getDirectoryContents") //################################################################################################################
386  {
387  // return directory structure for requested path, types are "dir" and "file"
388 
389  std::string rootpath = std::string(ROOT_BROWSER_PATH) + "/";
390  std::string path = CgiDataUtilities::postData(cgiIn, "Path");
391  boost::regex re("%2F");
392  path = boost::regex_replace(path, re, "/"); // Dario: should be transparent for
393  // Ryan's purposes but required by
394  // Extjs
395 
398 
399  // return 1 if user has access to admin controls, else 0
400  char permStr[10];
401  sprintf(permStr,
402  "%d",
403  userInfo.permissionLevel_ >=
404  CoreSupervisorBase::getSupervisorPropertyUserPermissionsThreshold(
405  "rootAdminControls"));
406  xmlOut.addTextElementToData("permissions", permStr); // add permissions
409 
410  std::string dirpath = rootpath + path;
411  if(path == "/" + PRE_MADE_ROOT_CFG_DIR + "/")
412  dirpath = ROOT_DISPLAY_CONFIG_PATH;
413 
414  if(path.find("/" + PRE_MADE_ROOT_CFG_DIR + "/") ==
415  0) // ROOT config path must start the path
416  dirpath = std::string(ROOT_DISPLAY_CONFIG_PATH) + "/" +
417  path.substr(PRE_MADE_ROOT_CFG_DIR.length() + 2);
418 
420  __SUP_COUT__ << "full path: " << dirpath << __E__;
421 
422  DIR* pDIR;
423  struct dirent* entry;
424  bool isNotRtCfg;
425  bool isDir;
426  if((pDIR = opendir(dirpath.c_str())))
427  {
428  xmlOut.addTextElementToData("path", path);
429  xmlOut.addTextElementToData("headOfSearch", "located");
430 
431  // add LIVE if path is / and DQM is active
432  // add Pre-made Views if path is / and ROOT_DISPLAY_CONFIG_PATH isnt already
433  // there
434  if(path == "/")
435  {
437  if(theDataManager_ != nullptr &&
438  theDataManager_->getLiveDQMHistos() != 0)
439  xmlOut.addTextElementToData("dir",
440  LIVEDQM_DIR + ".root"); // add to xml
441 
442  // check for ROOT_DISPLAY_CONFIG_PATH
445  DIR* pRtDIR = opendir(ROOT_DISPLAY_CONFIG_PATH);
446  bool recheck = false;
447  if(!pRtDIR) // if doesn't exist, make it
448  {
449  recheck = true;
450  if(mkdir(ROOT_DISPLAY_CONFIG_PATH,
451  S_IRWXU | (S_IRGRP | S_IXGRP) |
452  (S_IROTH | S_IXOTH))) // mode = drwx r-x r-x
453  __SUP_COUT__ << "Failed to make directory for pre made views: "
454  << ROOT_DISPLAY_CONFIG_PATH << __E__;
455  }
456  else
457  closedir(pRtDIR); // else close and display
458 
459  if(!recheck || (pRtDIR = opendir(ROOT_DISPLAY_CONFIG_PATH)))
460  {
462  xmlOut.addTextElementToData("dir",
463  PRE_MADE_ROOT_CFG_DIR); // add to xml
464  if(recheck)
465  closedir(pRtDIR);
466  }
467  }
469  while((entry = readdir(pDIR)))
470  {
471  //__SUP_COUT__ << int(entry->d_type) << " " << entry->d_name << "\n" <<
472  // __E__;
473  if(entry->d_name[0] != '.' &&
474  (entry->d_type ==
475  0 || // 0 == UNKNOWN (which can happen - seen in SL7 VM)
476  entry->d_type == 4 ||
477  entry->d_type == 8))
478  {
479  //__SUP_COUT__ << int(entry->d_type) << " " << entry->d_name << "\n"
480  //<< __E__;
481  isNotRtCfg =
482  std::string(entry->d_name).find(".rcfg") == std::string::npos;
483  isDir = false;
484 
485  if(entry->d_type == 0)
486  {
487  // unknown type .. determine if directory
489  DIR* pTmpDIR = opendir((dirpath + entry->d_name).c_str());
490  if(pTmpDIR)
491  {
493  isDir = true;
494  closedir(pTmpDIR);
495  }
496  // else //assume file
497  }
498 
499  if((entry->d_type == 8 ||
500  (!isDir && entry->d_type == 0)) // file type
501  && std::string(entry->d_name).find(".root") == std::string::npos &&
502  isNotRtCfg)
503  continue; // skip if not a root file or a config file
504  else if(entry->d_type == 4)
505  isDir = true; // flag directory types
506 
507  // ss.str("") ; ss << "Adding " << entry->d_name << " to xmlOut" ;
509  xmlOut.addTextElementToData(
510  isDir ? "dir" : (isNotRtCfg ? "dir" : "file"), entry->d_name);
511  }
512  }
513  closedir(pDIR);
514  }
515  else
516  __SUP_COUT__ << "Failed to access directory contents!" << __E__;
517  // std::ostringstream* out ;
518  // xmlOut.outputXmlDocument((std::ostringstream*) out, true);
519  }
520  else if(
521  requestType ==
522  "getRoot") //################################################################################################################
523  {
524  // return directory structure for requested ROOT path, types are "dir" and "file"
525  std::string path = CgiDataUtilities::postData(cgiIn, "RootPath");
526  boost::regex re("%2F");
527  path = boost::regex_replace(path, re, "/"); // Dario: should be transparent for
528  // Ryan's purposes but required by
529  // Extjs
530  boost::regex re1("%3A");
531  path = boost::regex_replace(path, re1, ""); // Dario: should be transparent for
532  // Ryan's purposes but required by
533  // Extjs
534 
535  ss.str("");
536  ss << "path : " << path;
537  STDLINE(ss.str(), ACCyan);
538  std::string fullPath = std::string(__ENV__("ROOT_BROWSER_PATH")) + path;
539 
540  __SUP_COUTV__(fullPath);
541 
542  const size_t rootExtensionStart = fullPath.find(".root");
543  const size_t rootExtensionEnd = rootExtensionStart + std::string(".root").size();
544 
545  std::string rootFileName = fullPath.substr(0, rootExtensionEnd);
546 
547  __SUP_COUTV__(rootFileName);
548  std::string rootDirectoryName =
549  rootFileName + ":" +
550  fullPath.substr(rootExtensionEnd, fullPath.size() - rootExtensionEnd + 1);
551 
552  __SUP_COUTV__(rootDirectoryName);
553  std::string::size_type LDQM_pos = path.find("/" + LIVEDQM_DIR + ".root/");
554  __SUP_COUTV__(LDQM_pos);
555  TFile* rootFile = nullptr;
556 
557  if(LDQM_pos == std::string::npos) // If it is not from LIVE_DQM
558  {
559  __SUP_COUTV__(rootFileName);
560  rootFile = TFile::Open(rootFileName.c_str());
561 
562  if(rootFile == nullptr || !rootFile->IsOpen())
563  {
564  __SUP_SS__ << "Failed to access ROOT file: " << rootFileName << __E__;
565  __SUP_SS_THROW__;
566  }
567  }
568  else
569  {
570  if(theDataManager_ != nullptr &&
571  theDataManager_->getLiveDQMHistos() != nullptr)
572  {
573  __SUP_COUT__ << "Attempting to get LIVE ROOT object." << __E__;
574  __SUP_COUTV__(rootDirectoryName);
575  rootDirectoryName = path.substr(("/" + LIVEDQM_DIR + ".root").length());
576  __SUP_COUTV__(rootDirectoryName);
577  rootFile = theDataManager_->getLiveDQMHistos()->getFile();
578 
579  __SUP_COUT__ << "LIVE file name: " << rootFile->GetName() << __E__;
580 
581  if(rootFile == nullptr || !rootFile->IsOpen())
582  {
583  __SUP_SS__ << "Failed to access LIVE ROOT file: " << rootFileName
584  << __E__;
585  __SUP_COUT__ << ss.str();
586  xmlOut.addTextElementToData("Warning", ss.str());
587  return; // do not treat LIVE root file missing as error, .. assume
588  // just not Running
589  }
590  }
591  else
592  {
593  __SUP_SS__ << "Failed to access LIVE ROOT file: " << rootFileName
594  << __E__;
595  __SUP_COUT__ << ss.str();
596  xmlOut.addTextElementToData("Warning", ss.str());
597  return; // do not treat LIVE root file missing as error, .. assume just
598  // not Running
599  }
600  }
601 
602  // at this point initial ROOT object has been successfully opened
603 
604  xmlOut.addTextElementToData("path", path);
605 
606  TDirectory* directory = rootFile->GetDirectory(rootDirectoryName.c_str());
607  TObject* tobject = nullptr;
608 
609  if(!directory) // if not directory yet, peak at object for TTree or
610  // TBranchElement with children
611  {
612  // if TTree or TBranchElement with children, then treat as a directory
613  // else if TBranchElement without children, then treat as leaf ROOT object
614 
615  rootDirectoryName = fullPath.substr(
616  rootExtensionEnd); // re-purpose rootDirectoryName as path within TTree
617 
618  std::vector<std::string> splitTTreePath =
619  StringMacros::getVectorFromString(rootDirectoryName, {'/'});
620  __SUP_COUTV__(StringMacros::vectorToString(splitTTreePath));
621 
622  unsigned int spliti = 0;
623  while(spliti < splitTTreePath.size() && splitTTreePath[spliti].size() == 0)
624  ++spliti; // search for first non-empty
625 
626  if(spliti < splitTTreePath.size())
627  {
628  __SUP_COUTV__(splitTTreePath[spliti]);
629  tobject = (TObject*)rootFile->Get(splitTTreePath[spliti].c_str());
630  ++spliti; // search for next non-empty
631  }
632 
633  if(tobject == nullptr)
634  {
635  __SUP_SS__ << "Failed to access ROOT sub path: " << rootDirectoryName
636  << __E__;
637  __SUP_SS_THROW__;
638  }
639  __SUP_COUTV__(tobject->ClassName());
640 
641  if(std::string(tobject->ClassName()) == "TTree" ||
642  std::string(tobject->ClassName()).find("TBranch") != std::string::npos || // == "TBranchElement" ||
643  std::string(tobject->ClassName()).find("TDirectory") !=
644  std::string::npos)
645  {
646  // continue traversing name split
647  do
648  {
649  while(spliti < splitTTreePath.size() &&
650  splitTTreePath[spliti].size() == 0)
651  ++spliti; // search for next non-empty
652  if(spliti >= splitTTreePath.size())
653  break; // reached end of traversal!
654 
655  __SUP_COUTV__(splitTTreePath[spliti]);
656  __SUP_COUT__ << "Parent class = " << (tobject->ClassName()) << __E__;
657  if(std::string(tobject->ClassName()) == "TTree")
658  tobject = (TObject*)((TTree*)tobject)
659  ->GetBranch(splitTTreePath[spliti].c_str());
660  else if(std::string(tobject->ClassName()).find("TBranch") != std::string::npos)
661  tobject = (TObject*)((TBranchElement*)tobject)
662  ->FindBranch(splitTTreePath[spliti].c_str());
663  else if(std::string(tobject->ClassName()).find("TDirectory") != std::string::npos)
664  tobject = (TObject*)((TDirectoryFile*)tobject)
665  ->Get(splitTTreePath[spliti].c_str());
666 
667  ++spliti; // search for next non-empty
668  } while(tobject);
669 
670  if(tobject == nullptr)
671  {
672  __SUP_SS__ << "Failed to access root sub path: " << rootDirectoryName
673  << __E__;
674  __SUP_SS_THROW__;
675  }
676 
677  __SUP_COUTV__(tobject->ClassName());
678 
679  // now at path's target element with tobject
680  // if no branches, then "file" and tobject stringified
681  // else "dir"
682 
683  TObjArray* objects = nullptr;
684 
685  if(std::string(tobject->ClassName()) == "TTree")
686  objects = ((TTree*)tobject)->GetListOfBranches();
687  else if(std::string(tobject->ClassName()).find("TBranch") != std::string::npos)//== "TBranchElement")
688  objects = ((TBranchElement*)tobject)->GetListOfBranches();
689 
690  if(objects != nullptr && !objects->IsEmpty())
691  {
692  __SUP_COUT__ << "Handling as TTree/TBranchElement directory" << __E__;
693 
694  // treat like a directory, and return branches
695  TObject* obj;
696  TIter nextobj(objects->MakeIterator());
697  TRegexp re("*", kTRUE);
698  while((obj = (TObject*)nextobj()))
699  {
700  std::string name = obj->GetName();
701  TString s = name;
702  if(s.Index(re) == kNPOS)
703  continue;
704 
705  __SUP_COUT__ << "Child class Name: " << obj->IsA()->GetName()
706  << " " << name << __E__;
707 
708  if(std::string(obj->IsA()->GetName()).find("TBranch") != std::string::npos)// == "TBranchElement")
709  {
710  // decide if leave based on children branches
711  __SUP_COUT__
712  << "Child '" << name << "' of type '"
713  << ((TBranchElement*)obj)->GetTypeName() << "' isLeaf="
714  << ((TBranchElement*)obj)->GetListOfBranches()->IsEmpty()
715  << __E__;
716 
717  xmlOut.addTextElementToData(
718  (((TBranchElement*)obj)->GetListOfBranches()->IsEmpty())
719  ? "file"
720  : "dir",
721  name);
722  }
723  else // handle normal way
724  {
725  xmlOut.addTextElementToData(
726  (std::string(obj->IsA()->GetName()).find("Directory") !=
727  std::string::npos ||
728  std::string(obj->IsA()->GetName()) == "TTree")
729  ? "dir"
730  : "file",
731  name);
732  }
733  }
734  return;
735  } // done handling TTree branches
736  } // end TTree and branch handling
737  else if(spliti < splitTTreePath.size())
738  {
739  __COUTV__(rootDirectoryName);
740  // if more name to mystery object (likely TDirectoryFile), then attempt to
741  // get full subpath
742  tobject = (TObject*)rootFile->Get(rootDirectoryName.c_str());
743  }
744 
745  // at this point have tobject to stringify
746  } // peaking for TTree
747 
748  if(directory == nullptr)
749  {
750  __SUP_COUT__ << "This is not a directory!" << __E__;
751 
752  if(tobject == nullptr)
753  {
754  __SUP_SS__
755  << "Something is wrong. Failed to access ROOT TObject at path: "
756  << fullPath << __E__;
757  __SUP_SS_THROW__;
758  }
759 
760  TObject* tobjectClone = nullptr;
761 
762  if(tobject != nullptr) // turns out was a root object path
763  {
764  //ignore lock, because Lore says only crashed with Canvas
765 
766  //FIXME -- check this new histo and gDirectory->Get for memory leak!
767  bool doJSONobject = false;
768  TH1F* h8 = nullptr;
769  std::string tmpClassName = tobject->ClassName();
770  if(tmpClassName.find("TBranch") != std::string::npos)
771  {
772  __COUT__ << "Attempting to plot '" << tobject->ClassName() << "' type." << __E__;
773 
774  h8=new TH1F();
775  TTree * t3 = ((TBranch*)tobject)->GetTree();
776  //__COUT__ << "Attempting to plot '" << t3 << "' type." << __E__;
777  //__COUT__ << "JSON=" << TBufferJSON::ConvertToJSON(h8).Data() << __E__;
778  t3->Draw("Value>>h8","", "goff");
779  tobject = gDirectory->Get("h8");
780 
781  __COUT__ << "Attempting to plot '" << tobject->ClassName() << "' type." << __E__;
782  doJSONobject = true;
783  }
784 
785  // Clone tobject to avoid conflict when it is filled by other
786  // threads
787  if(theDataManager_ != nullptr &&
788  theDataManager_->getLiveDQMHistos() != nullptr &&
789  LDQM_pos == 0)
790  {
791  std::unique_lock<std::mutex> lock(
792  static_cast<DQMHistosConsumerBase*>(
793  theDataManager_->getLiveDQMHistos())
794  ->getFillHistoMutex());
795  tobjectClone = tobject->Clone();
796  }
797  else
798  {
799  tobjectClone = tobject->Clone();
800  }
801 
802 
803 
804  TString json = TBufferJSON::ConvertToJSON(tobjectClone);
805  TBufferFile tBuffer(TBuffer::kWrite);
806  tobjectClone->Streamer(tBuffer);
807 
808  std::string hexString = BinaryStringMacros::binaryStringToHexString(
809  tBuffer.Buffer(), tBuffer.Length());
810 
811  __SUP_COUT__ << "Returning object '" << tobjectClone->GetName()
812  << "' of class '" << tobjectClone->ClassName() << __E__;
813 
814  xmlOut.addTextElementToData("rootType", doJSONobject?"JSON":tobjectClone->ClassName());
815  xmlOut.addTextElementToData("rootData", hexString);
816  xmlOut.addTextElementToData("rootJSON", json.Data());
817 
818  if(tobjectClone != nullptr)
819  delete tobjectClone;
820  if(h8 != nullptr)
821  delete h8;
822  }
823  else
824  __SUP_COUT_ERR__ << "Failed to access:-" << rootDirectoryName << "-"
825  << __E__;
826  STDLINE("Done with it!", ACBlue);
827  }
828  else // handle as directory
829  {
830  __SUP_COUT__ << "directory found getting the content!" << __E__;
831  STDLINE("Directory found getting the content!", ACGreen);
832  TRegexp re("*", kTRUE);
833  if(LDQM_pos == 0)
834  {
835  TObject* obj;
836  TIter nextobj(directory->GetList());
837  while((obj = (TObject*)nextobj()))
838  {
839  TString s = obj->GetName();
840  if(s.Index(re) == kNPOS)
841  continue;
842  __SUP_COUT__ << "Class Name: " << obj->IsA()->GetName() << __E__;
843 
844  xmlOut.addTextElementToData(
845  (std::string(obj->IsA()->GetName()).find("Directory") !=
846  std::string::npos ||
847  std::string(obj->IsA()->GetName()) == "TTree" ||
848  std::string(obj->IsA()->GetName()).find("TBranch") != std::string::npos)// == "TBranchElement")
849  ? "dir"
850  : "file",
851  obj->GetName());
852  // ss.str("") ; ss << "obj->GetName(): " << obj->GetName() ;
853  // //STDLINE(ss.str(),"") ;
854  }
855  }
856  else
857  {
858  TKey* key;
859  TIter next(directory->GetListOfKeys());
860  while((key = (TKey*)next()))
861  {
862  TString s = key->GetName();
863  if(s.Index(re) == kNPOS)
864  continue;
865  __SUP_COUT__ << "Class Name: " << key->GetClassName() << __E__;
866  xmlOut.addTextElementToData(
867  (std::string(key->GetClassName()).find("Directory") !=
868  std::string::npos ||
869  std::string(key->GetClassName()) == "TTree" ||
870  std::string(key->GetClassName()).find("TBranch") != std::string::npos)// == "TBranchElement")
871  ? "dir"
872  : "file",
873  key->GetName());
874  // ss.str("") ; ss << "key->GetName(): " << key->GetName() ;
876  }
877  }
878  }
879  if(LDQM_pos == std::string::npos)
880  rootFile->Close();
881 
882  } // end getRoot handling
883  else if(
884  requestType ==
885  "getEvents") //################################################################################################################
886  {
887  if(theDataManager_ == nullptr)
888  {
889  __SS__ << "No Data Manager instantiated." << __E__;
890  __SS_THROW__;
891  }
892 
893  int Run = atoi(cgiIn("run").c_str());
894 
895  __SUP_COUT__ << "getEvents for run " << Run << __E__;
896 
897  if(Run != (int)loadedRunNumber_ || loadedRunNumber_ == (unsigned int)-1)
898  {
899  theDataManager_->load("Run1684.root", "Monicelli");
900  loadedRunNumber_ = Run;
901  }
902 
903  /*DOMElement* eventsParent =*/ xmlOut.addTextElementToData("events", "");
904  //DOMElement* eventParent;
905  //char str[40];
906 
907  // const Visual3DEvents& events = theDataManager_->getVisual3DEvents();
908  // __SUP_COUT__ << "Preparing hits xml" << __E__;
909  // int numberOfEvents = 0;
910  // for(Visual3DEvents::const_iterator it=events.begin(); it!=events.end() &&
911  // numberOfEvents < 10000; it++, numberOfEvents++)
912  // {
913  // //__SUP_COUT__ << "Event: " << numberOfEvents << __E__;
914  // eventParent = xmlOut.addTextElementToParent("event", str,
915  // eventsParent); const VisualHits& hits = it->getHits();
916  // for(VisualHits::const_iterator itHits=hits.begin();
917  // itHits!=hits.end(); itHits++)
918  // {
919  // sprintf(str,"%f",itHits->x);
920  // xmlOut.addTextElementToParent("xyz_point", str, eventParent);
921  // sprintf(str,"%f",itHits->y);
922  // xmlOut.addTextElementToParent("xyz_point", str, eventParent);
923  // sprintf(str,"%f",itHits->z);
924  // xmlOut.addTextElementToParent("xyz_point", str, eventParent);
925  // //__SUP_COUT__ << "X: " << itHits->x << " Y: " << itHits->y << "
926  // Z:
927  //"
928  //<< itHits->z << __E__;
929  // }
930  // const VisualTracks& tracks = it->getTracks();
931  // for(VisualTracks::const_iterator itTrks=tracks.begin();
932  // itTrks!=tracks.end(); itTrks++)
933  // {
934  // sprintf(str,"%f",itTrks->slopeX);
935  // xmlOut.addTextElementToParent("slope", str, eventParent);
936  // sprintf(str,"%f",itTrks->slopeY);
937  // xmlOut.addTextElementToParent("slope", str, eventParent);
938  // sprintf(str,"%f",itTrks->interceptX);
939  // xmlOut.addTextElementToParent("intcpt", str, eventParent);
940  // sprintf(str,"%f",itTrks->interceptY);
941  // xmlOut.addTextElementToParent("intcpt", str, eventParent);
942  //
943  // }
944  //
945  // }
946  __SUP_COUT__ << "Done hits xml" << __E__;
947  }
948  else if(
949  requestType ==
950  "getGeometry") //################################################################################################################
951  {
952  __SUP_COUT__ << "getGeometry" << __E__;
953 
954  if(theDataManager_ == nullptr)
955  {
956  __SS__ << "No Data Manager instantiated." << __E__;
957  __SS_THROW__;
958  }
959 
960  // FIXME -- this crashes when the file doesn't exist!
961  theDataManager_->load("Run1684.geo", "Geometry");
962 
963  __SUP_COUT__ << "getGeometry" << __E__;
964 
965  /*DOMElement* geometryParent =*/ xmlOut.addTextElementToData("geometry", "");
966  // const Visual3DShapes& shapes =
967  // theDataManager_->getVisual3DGeometry().getShapes();
968  //
969  // __SUP_COUT__ << "getGeometry" << __E__;
970  //
971  //
972  // DOMElement* objectParent;
973  // char str[40];
974  // for(Visual3DShapes::const_iterator itShapes=shapes.begin();
975  // itShapes!=shapes.end(); itShapes++)
976  // {
977  // objectParent = xmlOut.addTextElementToParent("object", str,
978  // geometryParent);
979  // xmlOut.addTextElementToParent("object_type", "LINE_LOOP",
980  // objectParent); sprintf(str,"%d",itShapes->numberOfRows);
981  // xmlOut.addTextElementToParent("object_rows", str, objectParent);
982  // sprintf(str,"%d",itShapes->numberOfColumns);
983  // xmlOut.addTextElementToParent("object_columns", str, objectParent);
984  // for(Points::const_iterator itCorners=itShapes->corners.begin();
985  // itCorners!=itShapes->corners.end(); itCorners++)
986  // {
987  // sprintf(str,"%f",itCorners->x);
988  // xmlOut.addTextElementToParent("xyz_point", str, objectParent);
989  // sprintf(str,"%f",itCorners->y);
990  // xmlOut.addTextElementToParent("xyz_point", str, objectParent);
991  // sprintf(str,"%f",itCorners->z);
992  // xmlOut.addTextElementToParent("xyz_point", str, objectParent);
993  // }
994  // }
995  }
996  else if(
997  requestType ==
998  "getRootConfig") //################################################################################################################
999  {
1000  std::string path = CgiDataUtilities::postData(cgiIn, "RootConfigPath");
1001  __SUP_COUT__ << "path " << path << __E__;
1002 
1003  if(path.find("/" + PRE_MADE_ROOT_CFG_DIR + "/") ==
1004  0) // ROOT config path must start the path
1005  {
1006  path = std::string(ROOT_DISPLAY_CONFIG_PATH) + "/" +
1007  path.substr(PRE_MADE_ROOT_CFG_DIR.length() + 2);
1008  __SUP_COUT__ << "mod path " << path << __E__;
1009  }
1010 
1011  HttpXmlDocument cfgXml;
1012  if(cfgXml.loadXmlDocument(path))
1013  {
1014  xmlOut.addTextElementToData("status", "1");
1015  xmlOut.copyDataChildren(cfgXml); // copy file to output xml
1016  cfgXml.saveXmlDocument(path);
1017  }
1018  else
1019  xmlOut.addTextElementToData("status",
1020  "Failed. File to properly load config file.");
1021  }
1022  else if(
1023  requestType ==
1024  "rootAdminControls") //################################################################################################################
1025  {
1026  // if(userPermissions < ROOT_VIEWER_PERMISSIONS_THRESHOLD)
1027  // {
1028  // __SUP_COUT__ << "Insufficient permissions for Root Viewer Admin
1029  // Controls: " << userPermissions << " < " << ROOT_VIEWER_PERMISSIONS_THRESHOLD <<
1030  // __E__;
1031  // xmlOut.addTextElementToData("status", "Failed. Insufficient user
1032  // permissions.");
1033  // }
1034  // else
1035  // {
1036  std::string cmd = cgiIn("cmd"); // possible commands are
1037  // mkdir
1038  // save
1039  // delete
1040 
1041  std::string path = CgiDataUtilities::postData(cgiIn, "path");
1042  std::string name = CgiDataUtilities::postData(cgiIn, "name");
1043  __SUP_COUT__ << "cmd " << cmd << __E__;
1044  __SUP_COUT__ << "path " << path << __E__;
1045  __SUP_COUT__ << "name " << name << __E__;
1046 
1047  if(path.find("/" + PRE_MADE_ROOT_CFG_DIR + "/") ==
1048  0) // ROOT config path must start the path
1049  {
1050  path = std::string(ROOT_DISPLAY_CONFIG_PATH) + "/" +
1051  path.substr(PRE_MADE_ROOT_CFG_DIR.length() + 2) + name;
1052  __SUP_COUT__ << "mod path " << path << __E__;
1053 
1054  if(cmd == "mkdir")
1055  {
1056  if(mkdir(path.c_str(),
1057  S_IRWXU | (S_IRGRP | S_IXGRP) |
1058  (S_IROTH | S_IXOTH))) // mode = drwx r-x r-x
1059  xmlOut.addTextElementToData("status",
1060  "Failed. Directory create rejected.");
1061  else
1062  xmlOut.addTextElementToData("status", "1"); // success
1063  }
1064  else if(cmd == "save")
1065  {
1066  path += PRE_MADE_ROOT_CFG_FILE_EXT; // add file extension
1067 
1068  bool useRunWildCard =
1069  atoi(CgiDataUtilities::postData(cgiIn, "useRunWildCard")
1070  .c_str()); // 0 or 1
1071  std::string config = CgiDataUtilities::postData(cgiIn, "config");
1072  __SUP_COUT__ << "config " << config << __E__;
1073  __SUP_COUT__ << "useRunWildCard " << useRunWildCard << __E__;
1074 
1075  // check if file already exists
1076  FILE* fp = fopen(path.c_str(), "r");
1077  if(fp)
1078  {
1079  fclose(fp);
1080  xmlOut.addTextElementToData("status", "Failed. File already exists.");
1081  __SUP_COUT__ << " Failed. File already exists." << __E__;
1082  }
1083  else
1084  {
1085  // dump to file
1086  // verify proper format through read back
1087  // if successfully loaded, re-save for formatting
1088 
1089  // dump to file
1090  fp = fopen(path.c_str(), "w");
1091  fputs(config.c_str(), fp);
1092  fclose(fp);
1093 
1094  // verify proper format through read back
1095  HttpXmlDocument cfgXml;
1096  if(cfgXml.loadXmlDocument(path))
1097  {
1098  // successfully loaded, re-save for formatting
1099  cfgXml.saveXmlDocument(path);
1100  xmlOut.addTextElementToData("status", "1"); // success
1101  }
1102  else // failed to load properly
1103  {
1104  xmlOut.addTextElementToData(
1105  "status", "Failed. Fatal. Improper file format.");
1106  if(remove(path.c_str()) != 0)
1107  __SUP_COUT__ << "Failed. Could not remove poorly formed Root "
1108  "config file!"
1109  << __E__;
1110  }
1111  }
1112  }
1113  else if(cmd == "delete")
1114  {
1115  // guess first directory and then file
1116  if(rmdir(path.c_str()) == 0 ||
1117  remove((path + PRE_MADE_ROOT_CFG_FILE_EXT).c_str()) == 0)
1118  xmlOut.addTextElementToData("status", "1"); // success
1119  else
1120  xmlOut.addTextElementToData("status",
1121  "Failed. Target could not be deleted.");
1122  }
1123  else
1124  xmlOut.addTextElementToData("status", "Failed. Unrecognized command.");
1125  }
1126  else
1127  xmlOut.addTextElementToData("status", "Failed. Invalid path.");
1128 
1129  //}
1130  }
1131  else if(
1132  requestType ==
1133  "getMeDirs") //################################################################################################################
1134  {
1135  xmlOut.setDarioStyle(true); // workaround for XML formatting....
1136  std::string fSystemPath = std::string(ROOT_BROWSER_PATH) + "/";
1137  std::string fRootPath = CgiDataUtilities::postData(cgiIn, "Path");
1138  boost::regex re("%2F");
1139  fRootPath = boost::regex_replace(fRootPath, re, "/");
1140  std::string fullPath = fSystemPath + fRootPath;
1141  // fFoldersPath_ = "pippo" ;
1142  STDLINE(std::string("Begin: fSystemPath = ") + fSystemPath, ACWhite);
1143  STDLINE(std::string("Begin: fRootPath = ") + fRootPath, ACWhite);
1144  STDLINE(std::string("Begin: fullPath = ") + fullPath, ACWhite);
1145  // STDLINE(std::string("Begin: fFoldersPath = ")+fFoldersPath_,ACCyan ) ;
1146 
1147  xmlOut.setRootPath(fRootPath);
1148  xmlOut.makeDirectoryBinaryTree(fSystemPath, fRootPath, 0, NULL);
1149  std::ostringstream* out = new std::ostringstream();
1150  xmlOut.outputXmlDocument((std::ostringstream*)out, true);
1151  }
1152  else if(
1153  requestType ==
1154  "getMeRootFile") //################################################################################################################
1155  {
1156  xmlOut.setDarioStyle(true); // workaround for XML formatting....
1157  std::string fSystemPath = std::string(ROOT_BROWSER_PATH) + "/";
1158  std::string fRootPath = CgiDataUtilities::postData(cgiIn, "fRootPath");
1159  std::string fFoldersPath = CgiDataUtilities::postData(cgiIn, "fFoldersPath");
1160  std::string fHistName = CgiDataUtilities::postData(cgiIn, "fHistName");
1161  std::string fFileName = CgiDataUtilities::postData(cgiIn, "fFileName");
1162  boost::regex re("%2F");
1163  fRootPath = boost::regex_replace(fRootPath, re, "/");
1164  fFoldersPath = boost::regex_replace(fFoldersPath, re, "/");
1165  STDLINE(std::string("fSystemPath : ") + fSystemPath, ACCyan);
1166  STDLINE(std::string("fRootPath : ") + fRootPath, ACCyan);
1167  STDLINE(std::string("fFoldersPath: ") + fFoldersPath, ACCyan);
1168  STDLINE(std::string("fHistName : ") + fHistName, ACCyan);
1169  STDLINE(std::string("fFileName : ") + fFileName, ACCyan);
1170  RootFileExplorer* theExplorer = new RootFileExplorer(
1171  fSystemPath, fRootPath, fFoldersPath, fHistName, fFileName, xmlOut);
1172  xmlOut.setDocument(theExplorer->initialize());
1173  // std::ostringstream* out ;
1174  // xmlOut.outputXmlDocument((std::ostringstream*) out, true);
1175  }
1176 }
virtual void request(const std::string &requestType, cgicc::Cgicc &cgiIn, HttpXmlDocument &xmlOut, const WebUsers::RequestUserInfo &userInfo) override