otsdaq_utilities  v2_03_00
ControlsDashboardSupervisor.cc
1 #include "otsdaq-utilities/ControlsDashboard/ControlsDashboardSupervisor.h"
2 #include <sys/stat.h> //for stat() quickly checking if file exists
3 #include <dirent.h> //for DIR
4 #include <dirent.h> //for DIR
5 #include <sys/stat.h> //for stat() quickly checking if file exists
6 
7 #include "otsdaq-core/SlowControlsCore/SlowControlsVInterface.h"
8 #include "otsdaq-core/PluginMakers/MakeSlowControls.h"
9 
10 using namespace ots;
11 
12 #define PAGES_DIRECTORY \
13  std::string(getenv("SERVICE_DATA_PATH")) + "/ControlsDashboardData/pages/";
14 
15 XDAQ_INSTANTIATOR_IMPL(ControlsDashboardSupervisor)
16 
17 //========================================================================================================================
18 ControlsDashboardSupervisor::ControlsDashboardSupervisor(xdaq::ApplicationStub* stub)
19  : CoreSupervisorBase(stub)
20 {
21  __SUP_COUT__ << "Constructor." << __E__;
22 
23  INIT_MF("ControlsDashboardSupervisor");
24 
25  init();
26 
27  __SUP_COUT__ << "Constructed." << __E__;
28 } //end constructor
29 
30 //========================================================================================================================
31 ControlsDashboardSupervisor::~ControlsDashboardSupervisor(void)
32 {
33  __SUP_COUT__ << "Destructor." << __E__;
34  destroy();
35  __SUP_COUT__ << "Destructed." << __E__;
36 } // end destructor()
37 
38 //========================================================================================================================
39 void ControlsDashboardSupervisor::destroy(void)
40 {
41  // called by destructor
42  delete interface_;
43 } // end destroy()
44 
45 //========================================================================================================================
46 void ControlsDashboardSupervisor::init(void)
47 // called by constructor
48 {
49  UID_ = 0;
50 
51  __COUT__ << std::endl;
52  ConfigurationTree node = CorePropertySupervisorBase::getSupervisorTableNode();
53  std::string pluginType = node.getNode("ControlsInterfacePluginType").getValue();
54  __COUTV__(pluginType);
55 
56  interface_ = makeSlowControls(
57  pluginType
58  , node.getUIDAsString()
59  , CorePropertySupervisorBase::getContextTreeNode()
60  , CorePropertySupervisorBase::getSupervisorConfigurationPath());
61  __COUT__ << std::endl;
62 
63  __COUT__ << "Finished init() w/ interface: " << pluginType << std::endl;
64 
65  //interface_->initialize();
66  //std::thread([&](){interface_->initialize();}).detach(); //thread completes after creating, subscribing, and getting parameters for all pvs
67 
68 } //end init()
69 
70 //========================================================================================================================
71 // setSupervisorPropertyDefaults
72 // override to set defaults for supervisor property values (before user settings
73 // override)
74 void ControlsDashboardSupervisor::setSupervisorPropertyDefaults()
75 {
76  CorePropertySupervisorBase::setSupervisorProperty(
77  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes, "*");
78 }
79 
80 //========================================================================================================================
81 // forceSupervisorPropertyValues
82 // override to force supervisor property values (and ignore user settings)
83 void ControlsDashboardSupervisor::forceSupervisorPropertyValues()
84 {
85  CorePropertySupervisorBase::setSupervisorProperty(
86  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes, "poll");
87 }
88 
89 //========================================================================================================================
90 void ControlsDashboardSupervisor::request(const std::string& requestType, cgicc::Cgicc& cgiIn,
91  HttpXmlDocument& xmlOut, const WebUsers::RequestUserInfo& userInfo)
92 {
93 // __COUT__ << std::endl;
94 // cgicc::Cgicc cgi(in);
95 // __COUT__ << std::endl;
96 // std::string requestType = CgiDataUtilities::getData(cgi,"RequestType");
97 // __COUT__ << request << std::endl;
98 // __COUT__ << this->getApplicationDescriptor()->getLocalId() << " " << requestType << " : end"<< std::endl;
99 
100 
101 // if(requestType == "")
102 // {
103 // Default(in, out);
104 // return;
105 // }
106 //
107 // HttpXmlDocument xmldoc;
108 // uint64_t activeSessionIndex;
109 // std::string user;
110 // uint8_t userPermissions;
111 //
112 // //**** start LOGIN GATEWAY CODE ***//
113 // {
114 // bool automaticCommand = requestType == "poll"; //automatic commands should not refresh cookie code.. only user initiated commands should!
115 // bool checkLock = true;
116 // bool getUser = false;
117 // bool requireLock = false;
118 //
119 // if(!theRemoteWebUsers_.xmlRequestToGateway(
120 // cgi,
121 // out,
122 // &xmldoc,
123 // allSupervisorInfo_,
124 // &userPermissions, //acquire user's access level (optionally null pointer)
125 // !automaticCommand, //true/false refresh cookie code
126 // 1, //set access level requirement to pass gateway
127 // checkLock, //true/false enable check that system is unlocked or this user has the lock
128 // requireLock, //true/false requires this user has the lock to proceed
129 // 0,//&userWithLock, //acquire username with lock (optionally null pointer)
130 // //(getUser?&user:0), //acquire username of this user (optionally null pointer)
131 // &username,
132 // 0, //acquire user's Display Name
133 // &activeSessionIndex //acquire user's session index associated with the cookieCode
134 // ))
135 // { //failure
136 // __COUT__ << "Failed Login Gateway: " <<
137 // out->str() << std::endl; //print out return string on failure
138 // return;
139 // }
140 // }
141 // //**** end LOGIN GATEWAY CODE ***//
142 //
143 //
144  try
145  {
146  __SUP_COUT__ << "User name is " << userInfo.username_ << "." << __E__;
147  __SUP_COUT__ << "User permission level for request '" << requestType << "' is " <<
148  unsigned(userInfo.permissionLevel_) << "." << __E__;
149 
150 
151  //handle request per requestType
152  handleRequest(requestType,xmlOut,cgiIn,userInfo.username_);
153  }
154  catch(const std::runtime_error& e)
155  {
156  __SS__ << "Error occurred handling request '" << requestType <<
157  "': " << e.what() << __E__;
158  __SUP_COUT__ << ss.str();
159  xmlOut.addTextElementToData("Error",ss.str());
160  }
161  catch(...)
162  {
163  __SS__ << "Unknown error occurred handling request '" << requestType <<
164  "!'" << __E__;
165  __SUP_COUT__ << ss.str();
166  xmlOut.addTextElementToData("Error",ss.str());
167  }
168 
169 } //end request()
170 //========================================================================================================================
171 void ControlsDashboardSupervisor::handleRequest(const std::string Command,
172  HttpXmlDocument& xmlOut, cgicc::Cgicc& cgiIn,
173  const std::string &username)
174 {
175  //return xml doc holding server response
176  __COUT__ << std::endl;
177 
178  if(Command == "poll")
179  {
180  std::string uid = CgiDataUtilities::getOrPostData(cgiIn, "uid");
181  Poll(cgiIn, xmlOut, uid);
182  }
183  else if(Command == "generateUID")
184  {
185  std::string pvList = CgiDataUtilities::getOrPostData(cgiIn, "PVList");
186  GenerateUID(cgiIn, xmlOut, pvList);
187  }
188  else if(Command == "GetPVSettings")
189  {
190  std::string pvList = CgiDataUtilities::getOrPostData(cgiIn, "PVList");
191  GetPVSettings(cgiIn, xmlOut, pvList);
192  xmlOut.addTextElementToData("id", CgiDataUtilities::getData(cgiIn, "id"));
193  }
194  else if(Command == "getList")
195  {
196  GetList(cgiIn, xmlOut);
197  }
198  else if(Command == "getPages")
199  {
200  __COUT__ << "Requesting pages from server! " << std::endl;
201  GetPages(cgiIn, xmlOut);
202  }
203  else if(Command == "loadPage")
204  {
205  std::string page = CgiDataUtilities::getData(cgiIn, "Page");
206  __COUT__ << this->getApplicationDescriptor()->getLocalId() << " " << page
207  << std::endl;
208 
209  loadPage(cgiIn, xmlOut, page);
210  }
211  __COUT__ << std::endl;
212 
213  // xmlOut.outputXmlDocument((std::ostringstream*) out, true);
214 } //end handleRequest
215 
216 
217 //========================================================================================================================
218 void ControlsDashboardSupervisor::Poll(cgicc::Cgicc& cgiIn,
219  HttpXmlDocument& xmlOut,
220  std::string UID)
221 {
222  __COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
223  << "Polling on UID:" << UID << std::endl;
224 
225  std::map<int, std::set<std::string>>::iterator mapReference;
226 
227  if(UID != "" &&
228  (mapReference = pvDependencyLookupMap_.find(std::stoi(UID))) !=
229  pvDependencyLookupMap_.end()) // We have their current list of PV Dependencies
230  {
231  std::string JSONMessage = "{ ";
232 
233  for(auto pv : mapReference->second)
234  {
235  __COUT__ << pv << std::endl;
236  // PVInfo * pvInfo = interface_->mapOfPVInfo_.find(pv)->second;
237  //__COUT__ << pv << ":" << (pvInfo?"Good":"Bad") << std::endl;
238  // interface_->getCurrentPVValue(pv);
239  std::array<std::string, 4> pvInformation = interface_->getCurrentValue(pv);
240 
241  __COUT__ << pv << ": " << pvInformation[1] << " : " << pvInformation[3]
242  << std::endl;
243 
244  if(pvInformation[0] != "NO_CHANGE")
245  {
246  //__COUT__ << "Reached" << std::endl;
247  JSONMessage += "\"" + pv + "\": {";
248 
249  /*if(pvInfo->mostRecentBufferIndex - 1 < 0)
250  {
251  std::string value = pvInfo->dataCache[pvInfo->dataCache.size()].second
252  std::string time =
253  }*/
254 
255  JSONMessage += "\"Timestamp\" : \"" + pvInformation[0] + "\",";
256  JSONMessage += "\"Value\" : \"" + pvInformation[1] + "\",";
257  JSONMessage += "\"Status\" : \"" + pvInformation[2] + "\",";
258  JSONMessage += "\"Severity\" : \"" + pvInformation[3] + "\"},";
259  }
260  else
261  {
262  __COUT__ << "No change in value since last poll: " << pv << std::endl;
263  }
264 
265  // Handle Channels that disconnect, etc
266  if(pvInformation[3] == "INVALID")
267  {
268  interface_->subscribe(pv);
269  }
270 
271  //__COUT__ << pv << ":" << (pvInfo?"Good":"Bad") << std::endl;
272  //__COUT__ << pv << ":" << pvInfo->mostRecentBufferIndex -1 << std::endl;
273  //__COUT__ << pv << " : " << pvInfo->dataCache[(pvInfo->mostRecentBufferIndex
274  //-1)].second << std::endl;
275  }
276 
277  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
278  JSONMessage += "}";
279  __COUT__ << JSONMessage << std::endl;
280  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
281 
282  /*for (std::set<unsigned long>::iterator it = mapReference->second->begin(); it !=
283  mapReference->second.end(); ++it)
284  {
285  //__COUT__ << this->getApplicationDescriptor()->getLocalId() << it <<
286  std::endl;
287 
288 
289  }*/
290  /*std::string fakeData = std::string("{")
291  + "\"Mu2e_CompStatus_daq01/system_temperature\":
292  \"40.5\","
293  + "\"Mu2e_CompStatus_daq01/load_one\": \"378.2\","
294  + "\"Mu2e_Weather_2/timestamp\": \"11.14.45.2016.4.8\","
295  + "\"Mu2e_CompStatus_daq01/system_temperature\":
296  \"43.4\","
297  + "\"Mu2e_CompStatus_daq01/load_one\":\"80\","
298  + "\"Mu2e_Weather_2/timestamp\": \"11.14.45.2016.4.8\""
299  + "}";
300  xmlOut.addTextElementToData("JSON", fakeData); //add to response*/
301  }
302  else // UID is not in our map so force them to generate a new one
303  {
304  xmlOut.addTextElementToData("JSON",
305  "{ \"message\": \"NOT_FOUND\"}"); // add to response
306  }
307 }
308 //========================================================================================================================
309 void ControlsDashboardSupervisor::GetPVSettings(cgicc::Cgicc& cgiIn,
310  HttpXmlDocument& xmlOut,
311  std::string pvList)
312 {
313  __COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
314  << "Getting settings for " << pvList << std::endl;
315 
316  std::string JSONMessage = "{ ";
317 
318  std::string pv;
319  size_t pos = 0;
320  size_t nextPos;
321  size_t lastIndex = pvList.find_last_of(",");
322  std::cout << "**********************" << pvList.size() << std::endl;
323  if(pvList.size() > 0)
324  {
325  while((nextPos = pvList.find(",", pos)) != std::string::npos)
326  {
327  pv = pvList.substr(pos, nextPos - pos);
328 
329  __COUT__ << pv << std::endl;
330 
331  std::array<std::string, 9> pvSettings = interface_->getSettings(pv);
332 
333  JSONMessage += "\"" + pv + "\": {";
334  JSONMessage += "\"Units \": \"" + pvSettings[0] + "\",";
335  JSONMessage += "\"Upper_Display_Limit\": \"" + pvSettings[1] + "\",";
336  JSONMessage += "\"Lower_Display_Limit\": \"" + pvSettings[2] + "\",";
337  JSONMessage += "\"Upper_Alarm_Limit \": \"" + pvSettings[3] + "\",";
338  JSONMessage += "\"Upper_Warning_Limit\": \"" + pvSettings[4] + "\",";
339  JSONMessage += "\"Lower_Warning_Limit\": \"" + pvSettings[5] + "\",";
340  JSONMessage += "\"Lower_Alarm_Limit \": \"" + pvSettings[6] + "\",";
341  JSONMessage += "\"Upper_Control_Limit\": \"" + pvSettings[7] + "\",";
342  JSONMessage += "\"Lower_Control_Limit\": \"" + pvSettings[8] + "\"},";
343 
344  pos = nextPos + 1;
345  }
346 
347  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
348  JSONMessage += "}";
349 
350  __COUT__ << JSONMessage << std::endl;
351  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
352  }
353  else
354  {
355  xmlOut.addTextElementToData(
356  "JSON", "{ \"message\": \"GetPVSettings\"}"); // add to response
357  }
358 }
359 //========================================================================================================================
360 void ControlsDashboardSupervisor::GenerateUID(cgicc::Cgicc& cgiIn,
361  HttpXmlDocument& xmlOut,
362  std::string pvlist)
363 {
364  __COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
365  << "Generating UID" << std::endl;
366 
367  std::set<std::string> pvDependencies;
368  std::string uid;
369  std::string pv;
370  size_t pos = 0;
371  size_t nextPos;
372  size_t lastIndex = pvlist.find_last_of(",");
373 
374  if(pvlist.size() > 0)
375  {
376  // pvlist.substr(2);
377  __COUT__ << pvlist << std::endl;
378 
379  while((nextPos = pvlist.find(",", pos)) != std::string::npos)
380  {
381  pv = pvlist.substr(pos, nextPos - pos);
382  //__COUT__ << UID_ << ":" << pos << "-" << nextPos << " ->" << pv <<
383  // std::endl;
384  pvDependencies.insert(pv);
385  pos = nextPos + 1;
386  }
387 
388  pvDependencyLookupMap_.insert(
389  std::pair<int, std::set<std::string>>(++UID_, pvDependencies));
390 
391  uid = (std::string("{ \"message\": \"") + std::to_string(UID_) + "\"}");
392  }
393  else
394  {
395  __COUT__ << this->getApplicationDescriptor()->getLocalId()
396  << " PVList invalid: " << pvlist << std::endl;
397  uid = "{ \"message\": \"-1\"}";
398  }
399 
400  __COUT__ << this->getApplicationDescriptor()->getLocalId() << " NEW UID: " << UID_
401  << std::endl;
402 
403  xmlOut.addTextElementToData("JSON", uid); // add to response
404 }
405 //========================================================================================================================
406 void ControlsDashboardSupervisor::GetList(cgicc::Cgicc& cgiIn, HttpXmlDocument& xmlOut)
407 {
408 
409  if(interface_ != NULL)
410  {
411 
412  __COUT__ << "Interface is defined! Attempting to get list!" << std::endl;
413  __COUT__ << this->getApplicationDescriptor()->getLocalId() << std::endl;
414  std::cout << " " << interface_->getList("JSON") << std::endl;
415 
416  xmlOut.addTextElementToData("JSON", interface_->getList("JSON")); //add to response
417  }
418  else
419  {
420  __COUT__ << "Interface undefined! Failed to get list!" << std::endl;
421  xmlOut.addTextElementToData("JSON","[\"None\"]");
422  }
423 }
424 //========================================================================================================================
425 void ControlsDashboardSupervisor::GetPages(cgicc::Cgicc& cgiIn, HttpXmlDocument& xmlOut)
426 {
427  /*DIR * dir;
428  struct dirent * ent;
429  std::string pathToPages = PAGES_DIRECTORY;
430 
431  std::vector<std::string> pages;
432 
433  __COUT__ << this->getApplicationDescriptor()->getLocalId() << "Path to pages: " <<
434  pathToPages << std::endl; if((dir = opendir (pathToPages.c_str())) != NULL)
435  {
436  while((ent = readdir(dir)) != NULL)
437  {
438  pages.push_back(ent->d_name);
439  __COUT__ << this->getApplicationDescriptor()->getLocalId() << " GetPages"
440  << ent->d_name << std::endl;
441  }
442  closedir(dir);
443  }
444  else
445  {
446  __COUT__ << this->getApplicationDescriptor()->getLocalId() << "Could not open
447  directory: " << pathToPages << std::endl; return;
448  }*/
449  std::vector<std::string> pages;
450 
451  listFiles("", true, &pages);
452 
453  std::string returnJSON = "[";
454  for(auto it = pages.begin(); it != pages.end(); it++)
455  {
456  if(*it != "." && *it != "..")
457  returnJSON += "\"" + *it + "\", ";
458  }
459  if(returnJSON.size() > 2 && returnJSON.compare("[") != 0)
460  {
461  __COUT__ << "Found pages on server!" << std::endl;
462  returnJSON.resize(returnJSON.size() - 2);
463  returnJSON += "]";
464  }
465  else
466  {
467  // No pages on the server
468  __COUT__ << "No pages found on server!" << std::endl;
469  returnJSON = "[\"None\"]";
470  }
471  std::cout << returnJSON << std::endl;
472 
473  xmlOut.addTextElementToData("JSON", returnJSON); // add to response
474 }
475 //========================================================================================================================
476 void ControlsDashboardSupervisor::loadPage(cgicc::Cgicc& cgiIn,
477  HttpXmlDocument& xmlOut,
478  std::string page)
479 {
480  // FIXME Filter out malicious attacks i.e. ../../../../../ stuff
481  struct stat buffer;
482  if(page.find("..") != std::string::npos)
483  {
484  __COUT__ << this->getApplicationDescriptor()->getLocalId()
485  << "Error! Request using '..': " << page << std::endl;
486  }
487  else if(page.find("~") != std::string::npos)
488  {
489  __COUT__ << this->getApplicationDescriptor()->getLocalId()
490  << "Error! Request using '~': " << page << std::endl;
491  }
492  else if(!(stat(page.c_str(), &buffer) == 0))
493  {
494  __COUT__ << this->getApplicationDescriptor()->getLocalId()
495  << "Error! File not found: " << page << std::endl;
496  }
497 
498  std::string file = PAGES_DIRECTORY file += "/" + page;
499  __COUT__ << this->getApplicationDescriptor()->getLocalId()
500  << "Trying to load page: " << page << std::endl;
501  __COUT__ << this->getApplicationDescriptor()->getLocalId()
502  << "Trying to load page: " << file << std::endl;
503  // read file
504  // for each line in file
505 
506  std::ifstream infile(file);
507  std::cout << "Reading file" << std::endl;
508  std::string JSONpage = "";
509  for(std::string line; getline(infile, line);)
510  {
511  std::cout << line << std::endl;
512  JSONpage += line;
513  }
514  std::cout << "Finished reading file" << std::endl;
515 
516  xmlOut.addTextElementToData("JSON", JSONpage); // add to response
517 }
518 //========================================================================================================================
519 void ControlsDashboardSupervisor::Subscribe(cgicc::Cgicc& cgiIn, HttpXmlDocument& xmlOut)
520 {
521 }
522 //========================================================================================================================
523 void ControlsDashboardSupervisor::Unsubscribe(cgicc::Cgicc& cgiIn,
524  HttpXmlDocument& xmlOut)
525 {
526 }
527 //========================================================================================================================
528 //========================================================================================================================
529 //================================================== UTILITIES
530 //===========================================================
531 //========================================================================================================================
532 bool ControlsDashboardSupervisor::isDir(std::string dir)
533 {
534  struct stat fileInfo;
535  stat(dir.c_str(), &fileInfo);
536  if(S_ISDIR(fileInfo.st_mode))
537  {
538  return true;
539  }
540  else
541  {
542  return false;
543  }
544 }
545 //========================================================================================================================
546 void ControlsDashboardSupervisor::listFiles(std::string baseDir,
547  bool recursive,
548  std::vector<std::string>* pages)
549 {
550  std::string base = PAGES_DIRECTORY;
551  base += baseDir;
552 
553  DIR* dp;
554  struct dirent* dirp;
555  if((dp = opendir(base.c_str())) == NULL)
556  {
557  std::cout << "[ERROR: " << errno << " ] Couldn't open " << base << "."
558  << std::endl;
559  return;
560  }
561  else
562  {
563  while((dirp = readdir(dp)) != NULL)
564  {
565  if(dirp->d_name != std::string(".") && dirp->d_name != std::string(".."))
566  {
567  if(isDir(base + dirp->d_name) == true && recursive == true)
568  {
569  // pages->push_back(baseDir + dirp->d_name);
570  std::cout << "[DIR]\t" << baseDir << dirp->d_name << "/" << std::endl;
571  listFiles(baseDir + dirp->d_name + "/", true, pages);
572  }
573  else
574  {
575  pages->push_back(baseDir + dirp->d_name);
576  std::cout << "[FILE]\t" << baseDir << dirp->d_name << std::endl;
577  }
578  }
579  }
580  closedir(dp);
581  }
582 }