otsdaq_utilities  v2_05_02_indev
SlowControlsDashboardSupervisor.cc
1 #include "otsdaq-utilities/SlowControlsDashboard/SlowControlsDashboardSupervisor.h"
2 #include <dirent.h> //for DIR
3 #include <sys/stat.h> //for stat() quickly checking if file exists
4 #include <thread> //for std::thread
5 
6 #include "otsdaq/PluginMakers/MakeSlowControls.h"
7 #include "otsdaq/SlowControlsCore/SlowControlsVInterface.h"
8 
9 #include <boost/regex.hpp>
10 
11 using namespace ots;
12 
13 #define CONTROLS_SUPERVISOR_DATA_PATH \
14  std::string(__ENV__("SERVICE_DATA_PATH")) + "/ControlsDashboardData/"
15 #define PAGES_DIRECTORY CONTROLS_SUPERVISOR_DATA_PATH + "pages/"
16 
17 XDAQ_INSTANTIATOR_IMPL(SlowControlsDashboardSupervisor)
18 
19 //==============================================================================
21  xdaq::ApplicationStub* stub)
22  : CoreSupervisorBase(stub)
23 {
24  __SUP_COUT__ << "Constructor." << __E__;
25 
26  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
27 
28  // make controls dashboard supervisor directories in case they don't exist
29  mkdir(((std::string)(CONTROLS_SUPERVISOR_DATA_PATH)).c_str(), 0755);
30  mkdir(((std::string)(PAGES_DIRECTORY)).c_str(), 0755);
31 
32  interface_ = NULL;
33  alarmNotifyRefreshRate_ = 60; // seconds
34 
35  init();
36 
37  __SUP_COUT__ << "Constructed." << __E__;
38 } // end constructor
39 
40 //==============================================================================
41 SlowControlsDashboardSupervisor::~SlowControlsDashboardSupervisor(void)
42 {
43  __SUP_COUT__ << "Destructor." << __E__;
44  destroy();
45  __SUP_COUT__ << "Destructed." << __E__;
46 } // end destructor()
47 
48 //==============================================================================
49 void SlowControlsDashboardSupervisor::destroy(void)
50 {
51  // called by destructor
52  delete interface_;
53 } // end destroy()
54 
55 //==============================================================================
56 void SlowControlsDashboardSupervisor::init(void)
57 // called by constructor
58 {
59  UID_ = 0;
60 
61  __SUP_COUT__ << __E__;
62  ConfigurationTree node = CorePropertySupervisorBase::getSupervisorTableNode();
63  std::string pluginType;
64 
65  try
66  {
67  pluginType =
68  node.getNode("SlowControlsInterfacePluginType").getValue<std::string>();
69  }
70  catch(...)
71  {
72  // failed to get plugin type through supervisor table link, so try app property
73  __COUT__ << "Pluging type was not definded through supervisor table, trying "
74  "supervisor property..."
75  << __E__;
76  pluginType = CorePropertySupervisorBase::getSupervisorProperty(
77  "ControlsInterfacePluginType");
78  }
79 
80  __COUTV__(pluginType);
81 
82  interface_ =
83  makeSlowControls(pluginType,
84  CorePropertySupervisorBase::getSupervisorUID(),
85  CorePropertySupervisorBase::getContextTreeNode(),
86  CorePropertySupervisorBase::getSupervisorConfigurationPath());
87  __COUT__ << __E__;
88 
89  //
90  // interface_->initialize();
91  std::thread(
93 
94  // lockout the messages array for the remainder of the scope
95  // this guarantees the reading thread can safely access the messages
96  std::lock_guard<std::mutex> lock(cs->pluginBusyMutex_);
97 
98  cs->interface_->initialize();
99  },
100  this)
101  .detach(); // thread completes after creating, subscribing, and getting
102  // // parameters for all channels
103 
104  //
105  // checkSubscription
106  std::thread(
108 
109  // lockout the messages array for the remainder of the scope
110  // this guarantees the reading thread can safely access the messages
111  // std::lock_guard<std::mutex> lock(cs->pluginBusyMutex_);
112  // cs->checkSubscriptions(cs);
113  cs->checkSlowControlsAlarms(cs);
114  },
115  this)
116  .detach(); // thread check EPICS slow controls alarms
117 
118  __SUP_COUT__ << "Finished init() w/ interface: " << pluginType << __E__;
119 
120  // add interface plugin to state machine list
121  CoreSupervisorBase::theStateMachineImplementation_.push_back(interface_);
122 } // end init()
123 
124 //==============================================================================
125 // Manage channel subscriptions to Interface
126 void SlowControlsDashboardSupervisor::checkSlowControlsAlarms(
128 {
129  {
130  std::lock_guard<std::mutex> lock(cs->alarmCheckThreadErrorMutex_);
131  cs->alarmCheckThreadError_ = "";
132  }
133 
134  while(true)
135  {
136  try
137  {
138  for(const auto& alarm : cs->interface_->checkAlarmNotifications())
139  {
140  if(alarm.size() > 8)
141  {
142  time_t rawtime = static_cast<time_t>(std::stoi(alarm[1]));
143  char* dt = ctime(&rawtime);
144  std::string subject = "Slow Control Alarm Notification";
145  std::string message =
146  "PV: " + alarm[0] + "\n" + " at time: " + dt + "\n" +
147  " value: " + alarm[2] + "" + " status: " + alarm[3] + "" +
148  " severity: " + alarm[4];
149 
150  // __COUT__
151  // << "checkSlowControlsAlarms() subject '" << subject
152  // << "' message '" << message
153  // << "' alarm name '" << alarm[5]
154  // << "' notify to '" << alarm[8]
155  // << "' at '" << alarm[6]
156  // << "' send mail " << alarm[7]
157  // << __E__;
158 
159  // toList can be "*", or "Tracker:10", "Ryan, Antonio"
160  // theRemoteWebUsers_.sendSystemMessage(
161  // "*" /*toList*/, "Subject", "Message", false /*doEmail*/);
162  theRemoteWebUsers_.sendSystemMessage(
163  alarm[6], subject, message, alarm[7] == "Yes" ? true : false);
164  }
165  }
166  }
167  catch(const std::runtime_error& e)
168  {
169  __SS__ << e.what() << '\n';
170  std::lock_guard<std::mutex> lock(cs->alarmCheckThreadErrorMutex_);
171  cs->alarmCheckThreadError_ = ss.str();
172  __COUT_ERR__ << ss.str();
173  }
174  catch(const std::exception& e)
175  {
176  __SS__ << e.what() << '\n';
177  std::lock_guard<std::mutex> lock(cs->alarmCheckThreadErrorMutex_);
178  cs->alarmCheckThreadError_ = ss.str();
179  __COUT_ERR__ << ss.str();
180  }
181  catch(...)
182  {
183  __SS__ << "checkSlowControlsAlarms() ERROR While sendin alarm messages"
184  << __E__;
185  std::lock_guard<std::mutex> lock(cs->alarmCheckThreadErrorMutex_);
186  cs->alarmCheckThreadError_ = ss.str();
187  __COUT_ERR__ << ss.str();
188  }
189 
190  sleep(alarmNotifyRefreshRate_);
191  __COUT__ << "checkSlowControlsAlarms() n. "
192  << cs->interface_->checkAlarmNotifications().size() << __E__;
193  }
194 } // end checkSlowControlsAlarms()
195 
196 //==============================================================================
197 // Manage channel subscriptions to Interface
198 void SlowControlsDashboardSupervisor::checkSubscriptions(
200 {
201  __COUT__ << "checkSubscriptions() initializing..." << __E__;
202  std::vector<std::string> channelList;
203  std::vector<int> channelRefreshRates;
204  while(true)
205  {
206  channelList = {"FIRST VALUE"};
207  channelRefreshRates = {};
208  std::map<int, std::set<std::string>>::iterator mapReference =
209  cs->channelDependencyLookupMap_.begin();
210  while(mapReference !=
211  cs->channelDependencyLookupMap_
212  .end()) // We have here current list of Channel Dependencies
213  {
214  for(auto channel : mapReference->second)
215  {
216  int refreshRate = 15; // seconds
217  channelRefreshRates.push_back(refreshRate);
218 
219  __COUT__ << "THREAD actual time: " << std::time(NULL)
220  << "; uidPollTimeMap + 10 * refreshTime: "
221  << cs->uidPollTimeMap_.at(mapReference->first) + 10 * refreshRate
222  << " seconds" << __E__;
223  if(std::time(NULL) >
224  cs->uidPollTimeMap_.at(mapReference->first) + 10 * refreshRate)
225  {
226  try
227  {
228  cs->channelDependencyLookupMap_.erase(mapReference->first);
229  continue;
230  }
231  catch(const std::exception& e)
232  {
233  continue;
234  }
235  }
236 
237  std::vector<std::string>::iterator it =
238  find(channelList.begin(), channelList.end(), channel);
239  if(it == channelList.end())
240  {
241  // cs->interface_->unsubscribe(channel);
242  // cs->interface_->subscribe(channel);
243  channelList.push_back(channel);
244  __COUT__ << "Channel: " << channel << " refreshRate: " << refreshRate
245  << " seconds" << __E__;
246  __COUT__ << "channelDependencyLookupMap_.size(): "
247  << cs->channelDependencyLookupMap_.size()
248  << " UID: " << mapReference->first
249  << " mapReference->second.size(): "
250  << mapReference->second.size() << __E__;
251  }
252 
253  sleep(1);
254  }
255  mapReference++;
256  }
257  int minTime = 30; // seconds
258  if(channelRefreshRates.size() > 0)
259  minTime =
260  *min_element(channelRefreshRates.begin(), channelRefreshRates.end());
261  sleep(minTime);
262  __COUT__ << "Loop over channels subscribing - waiting time: " << minTime
263  << " seconds" << __E__;
264  }
265 }
266 
267 //==============================================================================
268 // setSupervisorPropertyDefaults
269 // override to set defaults for supervisor property values (before user settings
270 // override)
271 void SlowControlsDashboardSupervisor::setSupervisorPropertyDefaults()
272 {
273  CorePropertySupervisorBase::setSupervisorProperty(
274  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes,
275  "*");
276 }
277 
278 //==============================================================================
279 // forceSupervisorPropertyValues
280 // override to force supervisor property values (and ignore user settings)
281 void SlowControlsDashboardSupervisor::forceSupervisorPropertyValues()
282 {
283  CorePropertySupervisorBase::setSupervisorProperty(
284  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
285  "poll");
286 }
287 
288 //==============================================================================
289 void SlowControlsDashboardSupervisor::request(const std::string& requestType,
290  cgicc::Cgicc& cgiIn,
291  HttpXmlDocument& xmlOut,
292  const WebUsers::RequestUserInfo& userInfo)
293 {
294  try
295  {
296  if(requestType != "getPages" && !pluginBusyMutex_.try_lock())
297  {
298  __SUP_SS__ << "Controls plugin is still initializing. Please try again in a "
299  "few minutes!"
300  << __E__;
301  __SUP_SS_THROW__;
302  }
303 
304  __SUP_COUT__ << "User name is " << userInfo.username_ << "." << __E__;
305  __SUP_COUT__ << "User permission level for request '" << requestType << "' is "
306  << unsigned(userInfo.permissionLevel_)
307  << "(isAdmin=" << (userInfo.isAdmin() ? "Yes" : "No") << ")."
308  << __E__;
309 
310  // handle request per requestType
311  handleRequest(requestType, xmlOut, cgiIn, userInfo);
312  }
313  catch(const std::runtime_error& e)
314  {
315  __SUP_SS__ << "Error occurred handling request '" << requestType
316  << "': " << e.what() << __E__;
317  __SUP_COUT__ << ss.str();
318  xmlOut.addTextElementToData("Error", ss.str());
319  }
320  catch(...)
321  {
322  __SS__ << "Unknown error occurred handling request '" << requestType << "!'"
323  << __E__;
324  __SUP_COUT__ << ss.str();
325  xmlOut.addTextElementToData("Error", ss.str());
326  }
327 
328  pluginBusyMutex_.unlock();
329  __SUP_COUT__ << __E__;
330 } // end request()
331 
332 //==============================================================================
333 void SlowControlsDashboardSupervisor::handleRequest(
334  const std::string Command,
335  HttpXmlDocument& xmlOut,
336  cgicc::Cgicc& cgiIn,
337  const WebUsers::RequestUserInfo& userInfo)
338 {
339  // return xml doc holding server response
340  __SUP_COUT__ << __E__;
341 
342  if(Command == "poll")
343  {
344  std::string uid = CgiDataUtilities::getOrPostData(cgiIn, "uid");
345  Poll(cgiIn, xmlOut, uid);
346  }
347  else if(Command == "userActivityHeartbeat")
348  {
349  //web client code should call this while user is building
350  // in Edit Mode.
351  //Do nothing, just keep login alive
352  }
353  else if(Command == "generateUID")
354  {
355  std::string channelList = CgiDataUtilities::getOrPostData(cgiIn, "pvList");
356  GenerateUID(cgiIn, xmlOut, channelList);
357  }
358  else if(Command == "isUserAdmin")
359  {
360  std::string json = std::string("{ \"message\": \"");
361  json += (userInfo.isAdmin() ? "Yes" : "No");
362  json += "\"}";
363  xmlOut.addTextElementToData("JSON", json.c_str());
364  }
365  else if(Command == "getUserPermissions")
366  {
367  GetUserPermissions(cgiIn, xmlOut, userInfo);
368  }
369  else if(Command == "getPVSettings")
370  {
371  __SUP_COUT__ << "Channel settings requested from server! " << __E__;
372  GetChannelSettings(cgiIn, xmlOut);
373  xmlOut.addTextElementToData("id", CgiDataUtilities::getData(cgiIn, "id"));
374  }
375  else if(Command == "getPVArchiverData")
376  {
377  __SUP_COUT__ << "Archived Channel data requested from server! " << __E__;
378  GetChannelArchiverData(cgiIn, xmlOut);
379  xmlOut.addTextElementToData("id", CgiDataUtilities::getData(cgiIn, "id"));
380  }
381  else if(Command == "getList")
382  {
383  __SUP_COUT__ << "Channel List requested from server! " << __E__;
384  GetList(cgiIn, xmlOut);
385  }
386  else if(Command == "getPages")
387  {
388  __SUP_COUT__ << "Requesting pages from server! " << __E__;
389  GetPages(cgiIn, xmlOut);
390  }
391  else if(Command == "loadPage")
392  {
393  std::string page = CgiDataUtilities::getData(cgiIn, "Page");
394  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " " << page
395  << __E__;
396 
397  loadPage(cgiIn, xmlOut, page, userInfo);
398  }
399  else if(Command == "loadPhoebusPage")
400  {
401  std::string page = CgiDataUtilities::getData(cgiIn, "Page");
402  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " " << page
403  << __E__;
404 
405  loadPhoebusPage(cgiIn, xmlOut, page, userInfo);
406  }
407  else if(Command == "createControlsPage")
408  {
409  SaveControlsPage(cgiIn, xmlOut, userInfo);
410  }
411  else if(Command == "createPhoebusControlsPage")
412  {
413  SavePhoebusControlsPage(cgiIn, xmlOut, userInfo);
414  }
415  else if(Command == "getLastAlarmsData")
416  {
417  __SUP_COUT__ << "Last Alams Data requested from server! " << __E__;
418  GetLastAlarmsData(cgiIn, xmlOut);
419  xmlOut.addTextElementToData("id", CgiDataUtilities::getData(cgiIn, "id"));
420  }
421  else if(Command == "getAlarmsLogData")
422  {
423  __SUP_COUT__ << "Alams Log Data requested from server! " << __E__;
424  GetAlarmsLogData(cgiIn, xmlOut);
425  xmlOut.addTextElementToData("id", CgiDataUtilities::getData(cgiIn, "id"));
426  }
427 
428  __SUP_COUT__ << "" << __E__;
429 } // end handleRequest()
430 
431 //==============================================================================
432 void SlowControlsDashboardSupervisor::Poll(cgicc::Cgicc& /*cgiIn*/,
433  HttpXmlDocument& xmlOut,
434  std::string UID)
435 {
436  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
437  << "Polling on UID:" << UID << __E__;
438 
439  std::map<int, std::set<std::string>>::iterator mapReference;
440 
441  if(UID != "" && (mapReference = channelDependencyLookupMap_.find(std::stoi(UID))) !=
442  channelDependencyLookupMap_
443  .end()) // We have their current list of Channel Dependencies
444  {
445  uidPollTimeMap_.at(std::stoi(UID)) = std::time(NULL);
446  std::string JSONMessage = "{ ";
447 
448  for(auto channel : mapReference->second)
449  {
450  //channel = channel.substr(0, channel.find(":"));
451 
452  __SUP_COUT__ << channel << __E__;
453 
454  std::array<std::string, 4> channelInformation =
455  interface_->getCurrentValue(channel);
456 
457  __SUP_COUT__ << channel << ": " << channelInformation[1] << " : "
458  << channelInformation[3] << __E__;
459 
460  if(channelInformation[0] != "NO_CHANGE")
461  {
462  //__SUP_COUT__ << "Reached" << __E__;
463  JSONMessage += "\"" + channel + "\": {";
464  JSONMessage += "\"Timestamp\":\"" + channelInformation[0] + "\",";
465  JSONMessage += "\"Value\":\"" + channelInformation[1] + "\",";
466  JSONMessage += "\"Status\":\"" + channelInformation[2] + "\",";
467  JSONMessage += "\"Severity\":\"" + channelInformation[3] + "\"},";
468  }
469  else
470  {
471  __SUP_COUT__ << "No change in value since last poll: " << channel
472  << __E__;
473  }
474 
475  // Handle Channels that disconnect, etc
476  if(channelInformation[3] == "INVALID")
477  {
478  interface_->unsubscribe(channel);
479  interface_->subscribe(channel);
480  }
481  }
482 
483  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
484  JSONMessage += "}";
485  __SUP_COUT__ << JSONMessage << __E__;
486  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
487  }
488  else // UID is not in our map so force them to generate a new one
489  {
490  xmlOut.addTextElementToData("JSON",
491  "{ \"message\": \"NOT_FOUND\"}"); // add to response
492  }
493 }
494 
495 //==============================================================================
496 void SlowControlsDashboardSupervisor::GetChannelSettings(cgicc::Cgicc& cgiIn,
497  HttpXmlDocument& xmlOut)
498 {
499  std::string channelList = CgiDataUtilities::postData(cgiIn, "pvList");
500 
501  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
502  << "Getting settings for " << channelList << __E__;
503 
504  std::string JSONMessage = "{ ";
505 
506  __SUP_COUT__ << "**********************" << channelList.size() << __E__;
507  if(channelList.size() > 0)
508  {
509  std::string channel;
510  size_t pos = 0;
511  size_t nextPos;
512  while((nextPos = channelList.find(",", pos)) != std::string::npos)
513  {
514  channel = channelList.substr(pos, nextPos - pos);
515 
516  __SUP_COUT__ << channel << __E__;
517 
518  std::array<std::string, 9> channelSettings = interface_->getSettings(channel);
519 
520  JSONMessage += "\"" + channel + "\": {";
521  JSONMessage += "\"Units\": \"" + channelSettings[0] + "\",";
522  JSONMessage += "\"Upper_Display_Limit\": \"" + channelSettings[1] + "\",";
523  JSONMessage += "\"Lower_Display_Limit\": \"" + channelSettings[2] + "\",";
524  JSONMessage += "\"Upper_Alarm_Limit\": \"" + channelSettings[3] + "\",";
525  JSONMessage += "\"Upper_Warning_Limit\": \"" + channelSettings[4] + "\",";
526  JSONMessage += "\"Lower_Warning_Limit\": \"" + channelSettings[5] + "\",";
527  JSONMessage += "\"Lower_Alarm_Limit\": \"" + channelSettings[6] + "\",";
528  JSONMessage += "\"Upper_Control_Limit\": \"" + channelSettings[7] + "\",";
529  JSONMessage += "\"Lower_Control_Limit\": \"" + channelSettings[8] + "\"},";
530 
531  pos = nextPos + 1;
532  }
533 
534  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
535  JSONMessage += "}";
536 
537  __SUP_COUT__ << JSONMessage << __E__;
538  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
539  }
540  else
541  {
542  __SUP_COUT__ << "Did not find any settings because Channel list is length zero!"
543  << __E__;
544 
545  xmlOut.addTextElementToData(
546  "JSON", "{ \"message\": \"GetPVSettings\"}"); // add to response
547  }
548 }
549 
550 //==============================================================================
551 void SlowControlsDashboardSupervisor::GetChannelArchiverData(cgicc::Cgicc& cgiIn,
552  HttpXmlDocument& xmlOut)
553 {
554  __SUP_COUT__ << "Requesting archived data!" << __E__;
555 
556  std::string channelList = CgiDataUtilities::postData(cgiIn, "pvList");
557 
558  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
559  << "Getting History for " << channelList << __E__;
560 
561  __SUP_COUT__ << "channelList.size(): " << channelList.size() << __E__;
562  if(channelList.size() > 0)
563  {
564  std::string channel;
565  size_t pos = 0;
566  size_t nextPos;
567  while((nextPos = channelList.find(",", pos)) != std::string::npos)
568  {
569  channel = channelList.substr(pos, nextPos - pos);
570 
571  __SUP_COUT__ << channel << __E__;
572 
573  std::vector<std::vector<std::string>> channelInformation =
574  interface_->getChannelHistory(channel);
575  __SUP_COUT__ << channel << ": " << channelInformation[0][1] << " : "
576  << channelInformation[0][3] << __E__;
577 
578  for(auto channelData : channelInformation)
579  {
580  std::string JSONMessage = "{ ";
581  JSONMessage += "\"" + channel + "\": {";
582  JSONMessage += "\"Timestamp\":\"" + channelData[0] + "\",";
583  JSONMessage += "\"Value\":\"" + channelData[1] + "\",";
584  JSONMessage += "\"Status\":\"" + channelData[2] + "\",";
585  JSONMessage += "\"Severity\":\"" + channelData[3] + "\"},";
586 
587  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
588  JSONMessage += "}";
589  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
590  }
591  //__SUP_COUT__ << JSONMessage << __E__;
592  pos = nextPos + 1;
593  }
594  }
595  else
596  {
597  __SUP_COUT__ << "Did not find any data because Channel list is length zero!"
598  << __E__;
599 
600  xmlOut.addTextElementToData(
601  "JSON", "{ \"message\": \"GetChannelArchiverData\"}"); // add to response
602  }
603 } // end GetChannelArchiverData()
604 
605 //==============================================================================
606 void SlowControlsDashboardSupervisor::GetLastAlarmsData(cgicc::Cgicc& cgiIn,
607  HttpXmlDocument& xmlOut)
608 {
609  __SUP_COUT__ << "Requesting last alarms data!" << __E__;
610 
611  std::string channelList = CgiDataUtilities::postData(cgiIn, "pvList");
612 
613  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
614  << "Getting last Alarms for " << channelList << __E__;
615  __SUP_COUT__ << "channelList.size(): " << channelList.size() << __E__;
616 
617  std::vector<std::vector<std::string>> alarms;
618 
619  // create lambda function to fill JSONMessage
620  std::function<void(HttpXmlDocument&, std::vector<std::vector<std::string>>&)>
621  jsonFiller = [](HttpXmlDocument& xmlOut,
622  std::vector<std::vector<std::string>>& alarms) {
623  if(alarms.size())
624  for(auto& alarmRow : alarms)
625  {
626  std::string JSONMessage = "{ ";
627  JSONMessage += "\"id\":\"" + alarmRow[0] + "\",";
628  JSONMessage += "\"pvName\":\"" + alarmRow[1] + "\",";
629  JSONMessage += "\"pvDescription\":\"" + alarmRow[2] + "\",";
630  JSONMessage += "\"pvValue\":\"" + alarmRow[3] + "\",";
631  JSONMessage += "\"pvStatus\":\"" + alarmRow[4] + "\",";
632  JSONMessage += "\"pvSeverity\":\"" + alarmRow[5] + "\",";
633  JSONMessage += "\"pvTime\":\"" + alarmRow[6] + "\",";
634 
635  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
636  JSONMessage += "}";
637  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
638  // __SUP_COUT__ << JSONMessage << __E__;
639  }
640  };
641 
642  if(channelList.size() > 0)
643  {
644  std::string channel;
645  size_t pos = 0;
646  size_t nextPos;
647  while((nextPos = channelList.find(",", pos)) != std::string::npos)
648  {
649  channel = channelList.substr(pos, nextPos - pos);
650 
651  alarms = interface_->getLastAlarms(channel);
652  __SUP_COUT__ << "get Last Alarms for channel: " << channel << __E__;
653  jsonFiller(xmlOut, alarms);
654  pos = nextPos + 1;
655  }
656  }
657  else
658  {
659  alarms = interface_->getLastAlarms("");
660  __SUP_COUT__ << "get Last Alarms for all channels" << __E__;
661  jsonFiller(xmlOut, alarms);
662  }
663 } // end GetLastAlarmsData()
664 
665 //==============================================================================
666 void SlowControlsDashboardSupervisor::GetAlarmsLogData(cgicc::Cgicc& cgiIn,
667  HttpXmlDocument& xmlOut)
668 {
669  __SUP_COUT__ << "Requesting alarms log data!" << __E__;
670 
671  std::string channelList = CgiDataUtilities::postData(cgiIn, "pvList");
672 
673  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
674  << "Getting Alarms Log for " << channelList << __E__;
675  __SUP_COUT__ << "channelList.size(): " << channelList.size() << __E__;
676 
677  std::vector<std::vector<std::string>> alarmsLog;
678 
679  // create lambda function to fill JSONMessage
680  std::function<void(HttpXmlDocument&, std::vector<std::vector<std::string>>&)>
681  jsonFiller = [](HttpXmlDocument& xmlOut,
682  std::vector<std::vector<std::string>>& alarmsLog) {
683  if (alarmsLog.size())
684  for(auto& alarmRow : alarmsLog)
685  {
686  std::string JSONMessage = "{ ";
687  JSONMessage += "\"id\":\"" + alarmRow[0] + "\",";
688  JSONMessage += "\"pvName\":\"" + alarmRow[1] + "\",";
689  JSONMessage += "\"pvValue\":\"" + alarmRow[2] + "\",";
690  JSONMessage += "\"pvStatus\":\"" + alarmRow[3] + "\",";
691  JSONMessage += "\"pvSeverity\":\"" + alarmRow[4] + "\",";
692  JSONMessage += "\"pvTime\":\"" + alarmRow[5] + "\",";
693 
694  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
695  JSONMessage += "}";
696  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
697  // __SUP_COUT__ << JSONMessage << __E__;
698  }
699  };
700 
701  if(channelList.size() > 0)
702  {
703  std::string channel;
704  size_t pos = 0;
705  size_t nextPos;
706  while((nextPos = channelList.find(",", pos)) != std::string::npos)
707  {
708  channel = channelList.substr(pos, nextPos - pos);
709 
710  alarmsLog = interface_->getAlarmsLog(channel);
711  __SUP_COUT__ << "get Alarms Log for channel: " << channel << __E__;
712  jsonFiller(xmlOut, alarmsLog);
713  pos = nextPos + 1;
714  }
715  }
716  else
717  {
718  alarmsLog = interface_->getAlarmsLog("");
719  __SUP_COUT__ << "get Alarms Log for all channels" << __E__;
720  jsonFiller(xmlOut, alarmsLog);
721  }
722 } // end GetAlarmsLogData()
723 
724 //==============================================================================
725 void
726 SlowControlsDashboardSupervisor::GetUserPermissions(
727  cgicc::Cgicc & /*cgiIn*/,
728  HttpXmlDocument & /*xmlOut*/,
729  const WebUsers::RequestUserInfo& /*userInfo*/)
730 {
731  return;
732 }
733 
734 //==============================================================================
735 void SlowControlsDashboardSupervisor::GenerateUID(cgicc::Cgicc& /*cgiIn*/,
736  HttpXmlDocument& xmlOut,
737  std::string channelList)
738 {
739  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
740  << "Generating UID" << __E__;
741 
742  std::set<std::string> channelDependencies;
743  StringMacros::getSetFromString(channelList, channelDependencies);
744 
745  //make entry for new UID_ in UID-to-channel map
746  channelDependencyLookupMap_.insert(
747  std::pair<int, std::set<std::string>>(++UID_, channelDependencies));
748 
749  uidPollTimeMap_.insert(std::pair<int, long int>(UID_, std::time(NULL)));
750 
751  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " NEW UID: " << UID_
752  << " maps to " << channelDependencies.size() << " channels" << __E__;
753 
754  xmlOut.addTextElementToData("JSON",
755  std::string("{ \"message\": \"") +
756  std::to_string(UID_) +
757  "\"}"); // add to response
758 } // end GenerateUID()
759 
760 //==============================================================================
761 void SlowControlsDashboardSupervisor::GetList(cgicc::Cgicc& /*cgiIn*/,
762  HttpXmlDocument& xmlOut)
763 {
764  if(interface_ != NULL)
765  {
766  __SUP_COUT__ << "Interface is defined! Attempting to get list!" << __E__;
767  // __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() <<
768  // __E__;
769  __SUP_COUT__ << " " << interface_->getList("JSON") << __E__;
770 
771  xmlOut.addTextElementToData("JSON",
772  interface_->getList("JSON")); // add to response
773  }
774  else
775  {
776  __SUP_COUT__ << "Interface undefined! Failed to get list!" << __E__;
777  xmlOut.addTextElementToData("JSON", "[\"None\"]");
778  }
779 } // end GetList()
780 
781 //==============================================================================
782 void SlowControlsDashboardSupervisor::GetPages(cgicc::Cgicc& /*cgiIn*/,
783  HttpXmlDocument& xmlOut)
784 {
785  std::vector<std::string> pages;
786 
787  listFiles("", true, &pages);
788 
789  std::string returnJSON = "[";
790  for(auto it = pages.begin(); it != pages.end(); it++)
791  {
792  if(*it != "." && *it != "..")
793  returnJSON += "\"" + *it + "\", ";
794  }
795  if(returnJSON.size() > 2 && returnJSON.compare("[") != 0)
796  {
797  __SUP_COUT__ << "Found pages on server!" << __E__;
798  returnJSON.resize(returnJSON.size() - 2);
799  returnJSON += "]";
800  }
801  else
802  {
803  // No pages on the server
804  __SUP_COUT__ << "No pages found on server!" << __E__;
805  returnJSON = "[\"None\"]";
806  }
807  __SUP_COUT__ << returnJSON << __E__;
808 
809  xmlOut.addTextElementToData("JSON", returnJSON); // add to response
810 } // end GetPages()
811 
812 //==============================================================================
813 void SlowControlsDashboardSupervisor::loadPage(cgicc::Cgicc& /*cgiIn*/,
814  HttpXmlDocument& xmlOut,
815  std::string page,
816  const WebUsers::RequestUserInfo& /*userInfo*/)
817 {
818  page = StringMacros::decodeURIComponent(page);
819 
820  // FIXME Filter out malicious attacks i.e. ../../../../../ stuff
821  struct stat buffer;
822  if(page.find("..") != std::string::npos)
823  {
824  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
825  << "Error! Request using '..': " << page << __E__;
826  }
827  else if(page.find("~") != std::string::npos)
828  {
829  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
830  << "Error! Request using '~': " << page << __E__;
831  }
832  else if(!(stat(page.c_str(), &buffer) == 0))
833  {
834  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
835  << "Error! File not found: " << page << __E__;
836  }
837  // Remove double / in path
838 
839  __SUP_COUT__ << page << __E__;
840 
841  if(page.at(0) == '/')
842  {
843  __SUP_COUT__ << "First character is '/'" << __E__;
844  page.erase(page.begin(), page.begin() + 1);
845  __SUP_COUT__ << page << __E__;
846  }
847 
848  std::string file = CONTROLS_SUPERVISOR_DATA_PATH;
849  file += page;
850  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
851  << "Trying to load page: " << page << __E__;
852  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
853  << "Trying to load page: " << file << __E__;
854  // read file
855  // for each line in file
856 
857  std::ifstream infile(file);
858  if(infile.fail())
859  {
860  __SUP_COUT__ << "Failed reading file: " << file << __E__;
861 
862  xmlOut.addTextElementToData("Time", "[\"Not Found\"]"); // add to response
863  xmlOut.addTextElementToData("Notes", "[\"Not Found\"]"); // add to response
864  xmlOut.addTextElementToData(
865  "Page", StringMacros::encodeURIComponent(page)); // add to response
866  return;
867  }
868  __SUP_COUT__ << "Reading file" << __E__;
869 
870  std::string time = "";
871  std::string notes = "";
872  std::string controlsPage = "";
873 
874  for(std::string line; getline(infile, line);)
875  {
876  __SUP_COUT__ << line << __E__;
877  if(!line.substr(0, 5).compare("Time:"))
878  {
879  time = line.substr(6);
880  }
881  else if(!line.substr(0, 6).compare("Notes:"))
882  {
883  notes = line.substr(7);
884  }
885  else if(!line.substr(0, 5).compare("Page:"))
886  {
887  controlsPage = line.substr(6);
888  }
889  }
890  __SUP_COUT__ << "Finished reading file" << __E__;
891  __SUP_COUTV__(time);
892  __SUP_COUTV__(notes);
893  __SUP_COUTV__(controlsPage);
894 
895  xmlOut.addTextElementToData("Time", time); // add to response
896  xmlOut.addTextElementToData("Notes", notes); // add to response
897  xmlOut.addTextElementToData("Page", controlsPage); // add to response
898 } // end loadPage()
899 
900 void SlowControlsDashboardSupervisor::loadPhoebusPage(
901  cgicc::Cgicc& /*cgiIn*/,
902  HttpXmlDocument& xmlOut,
903  std::string page,
904  const WebUsers::RequestUserInfo& /*userInfo*/)
905 {
906  page = StringMacros::decodeURIComponent(page);
907 
908  // FIXME Filter out malicious attacks i.e. ../../../../../ stuff
909  struct stat buffer;
910  if(page.find("..") != std::string::npos)
911  {
912  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
913  << "Error! Request using '..': " << page << __E__;
914  }
915  else if(page.find("~") != std::string::npos)
916  {
917  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
918  << "Error! Request using '~': " << page << __E__;
919  }
920  else if(!(stat(page.c_str(), &buffer) == 0))
921  {
922  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
923  << "Error! File not found: " << page << __E__;
924  }
925  // Remove double / in path
926 
927  __SUP_COUT__ << page << __E__;
928 
929  if(page.at(0) == '/')
930  {
931  __SUP_COUT__ << "First character is '/'" << __E__;
932  page.erase(page.begin(), page.begin() + 1);
933  __SUP_COUT__ << page << __E__;
934  }
935 
936  std::string file = CONTROLS_SUPERVISOR_DATA_PATH;
937  file += page;
938  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
939  << "Trying to load page: " << page << __E__;
940  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
941  << "Trying to load page: " << file << __E__;
942 
943  // read file
944  __SUP_COUT__ << "Reading file" << __E__;
945  std::ifstream infile(file);
946  if(infile.fail())
947  {
948  __SUP_COUT__ << "Failed reading file: " << file << __E__;
949  xmlOut.addTextElementToData(
950  "Page", StringMacros::encodeURIComponent(page)); // add to response
951  return;
952  }
953 
954  std::string xml;
955  for(std::string line; getline(infile, line);)
956  {
957  xml += line + "\n";
958  }
959  __SUP_COUT__ << xml << __E__;
960  xmlOut.addTextElementToData("PHOEBUS", xml);
961 
962 } // end loadPhoebusPage()
963 
964 //==============================================================================
965 void SlowControlsDashboardSupervisor::SaveControlsPage(
966  cgicc::Cgicc& cgiIn,
967  HttpXmlDocument& /*xmlOut*/,
968  const WebUsers::RequestUserInfo& /*userInfo*/)
969 {
970  __SUP_COUT__ << "ControlsDashboard wants to create a Controls Page!" << __E__;
971 
972  std::string controlsPageName = CgiDataUtilities::postData(cgiIn, "Name");
973  std::string pageString = CgiDataUtilities::postData(cgiIn, "Page");
974  std::string Time = CgiDataUtilities::postData(cgiIn, "Time");
975  std::string Notes =
976  StringMacros::decodeURIComponent(CgiDataUtilities::postData(cgiIn, "Notes"));
977  std::string isControlsPagePublic = CgiDataUtilities::postData(cgiIn, "isPublic");
978 
979  __SUP_COUTV__(controlsPageName);
980  __SUP_COUTV__(pageString);
981  __SUP_COUTV__(Notes);
982  __SUP_COUTV__(Time);
983  __SUP_COUTV__(isControlsPagePublic);
984 
985  if(controlsPageName == "")
986  return;
987 
988  __SUP_COUTV__(CONTROLS_SUPERVISOR_DATA_PATH);
989 
990  std::string fullPath;
991  if(isControlsPagePublic == "true")
992  fullPath = (std::string)CONTROLS_SUPERVISOR_DATA_PATH + "public/";
993  else
994  fullPath = (std::string)CONTROLS_SUPERVISOR_DATA_PATH + "private/";
995 
996  __SUP_COUTV__(fullPath);
997 
998  std::string file = fullPath + controlsPageName;
999 
1000  __SUP_COUTV__("Saving Controls Page to: " + file);
1001 
1002  std::string extension = file.substr(file.length() - 4, 4);
1003  if(extension != ".dat")
1004  {
1005  __SUP_COUT__ << "Extension : " << extension << __E__;
1006  file += std::string(".dat");
1007  }
1008  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
1009  << "Trying to save page: " << controlsPageName << __E__;
1010  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
1011  << "Trying to save page as: " << file << __E__;
1012  // read file
1013  // for each line in file
1014 
1015  std::ofstream outputFile;
1016  outputFile.open(file);
1017  outputFile << "Time: " << Time << "\n";
1018  outputFile << "Notes: " << Notes << "\n";
1019  outputFile << "Page: " << pageString;
1020  outputFile.close();
1021 
1022  __SUP_COUT__ << "Finished writing file" << __E__;
1023 
1024  return;
1025 }
1026 
1027 //==============================================================================
1028 void SlowControlsDashboardSupervisor::SavePhoebusControlsPage(
1029  cgicc::Cgicc& cgiIn,
1030  HttpXmlDocument& /*xmlOut*/,
1031  const WebUsers::RequestUserInfo& /*userInfo*/)
1032 {
1033  __SUP_COUT__ << "ControlsDashboard wants to create a Controls Page!" << __E__;
1034 
1035  std::string controlsPageName = CgiDataUtilities::postData(cgiIn, "Name");
1036  std::string pageString = CgiDataUtilities::postData(cgiIn, "Page");
1037  std::string isControlsPagePublic = CgiDataUtilities::postData(cgiIn, "isPublic");
1038 
1039  __SUP_COUTV__(controlsPageName);
1040  __SUP_COUTV__(pageString);
1041  __SUP_COUTV__(isControlsPagePublic);
1042 
1043  if(controlsPageName == "")
1044  return;
1045 
1046  __SUP_COUTV__(CONTROLS_SUPERVISOR_DATA_PATH);
1047 
1048  std::string fullPath;
1049  if(isControlsPagePublic == "true")
1050  fullPath = (std::string)CONTROLS_SUPERVISOR_DATA_PATH + "public/";
1051  else
1052  fullPath = (std::string)CONTROLS_SUPERVISOR_DATA_PATH + "private/";
1053 
1054  __SUP_COUTV__(fullPath);
1055 
1056  std::string file = fullPath + controlsPageName;
1057 
1058  __SUP_COUTV__("Saving Controls Page to: " + file);
1059 
1060  std::string extension = file.substr(file.length() - 4, 4);
1061  if(extension != ".bob")
1062  {
1063  __SUP_COUT__ << "Extension : " << extension << __E__;
1064  file += std::string(".bob");
1065  }
1066  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
1067  << "Trying to save page: " << controlsPageName << __E__;
1068  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
1069  << "Trying to save page as: " << file << __E__;
1070  // read file
1071  // for each line in file
1072 
1073  std::ofstream outputFile;
1074  outputFile.open(file);
1075  outputFile << pageString << "\n";
1076  outputFile.close();
1077 
1078  __SUP_COUT__ << "Finished writing file" << __E__;
1079 
1080  return;
1081 }
1082 
1083 //==============================================================================
1084 void SlowControlsDashboardSupervisor::Subscribe(cgicc::Cgicc& /*cgiIn*/,
1085  HttpXmlDocument& /*xmlOut*/)
1086 {
1087 }
1088 
1089 //==============================================================================
1090 void SlowControlsDashboardSupervisor::Unsubscribe(cgicc::Cgicc& /*cgiIn*/,
1091  HttpXmlDocument& /*xmlOut*/)
1092 {
1093 }
1094 
1095 //==============================================================================
1096 //==============================================================================
1097 //================================================== UTILITIES
1098 //===========================================================
1099 //==============================================================================
1100 bool SlowControlsDashboardSupervisor::isDir(std::string dir)
1101 {
1102  struct stat fileInfo;
1103  stat(dir.c_str(), &fileInfo);
1104  if(S_ISDIR(fileInfo.st_mode))
1105  {
1106  return true;
1107  }
1108  else
1109  {
1110  return false;
1111  }
1112 }
1113 
1114 //==============================================================================
1115 void SlowControlsDashboardSupervisor::listFiles(std::string baseDir,
1116  bool recursive,
1117  std::vector<std::string>* pages)
1118 {
1119  std::string base = CONTROLS_SUPERVISOR_DATA_PATH;
1120  base += baseDir;
1121 
1122  DIR* dp;
1123  struct dirent* dirp;
1124  if((dp = opendir(base.c_str())) == NULL)
1125  {
1126  __SUP_COUT__ << "[ERROR: " << errno << " ] Couldn't open " << base << "."
1127  << __E__;
1128  return;
1129  }
1130  else
1131  {
1132  while((dirp = readdir(dp)) != NULL)
1133  {
1134  if(dirp->d_name != std::string(".") && dirp->d_name != std::string(".."))
1135  {
1136  if(isDir(base + dirp->d_name) == true && recursive == true)
1137  {
1138  // pages->push_back(baseDir + dirp->d_name);
1139  __SUP_COUT__ << "[DIR]\t" << baseDir << dirp->d_name << "/" << __E__;
1140  listFiles(baseDir + dirp->d_name + "/", true, pages);
1141  }
1142  else
1143  {
1144  pages->push_back(baseDir + dirp->d_name);
1145  __SUP_COUT__ << "[FILE]\t" << baseDir << dirp->d_name << __E__;
1146  }
1147  }
1148  }
1149  closedir(dp);
1150  }
1151 }