otsdaq-epics  3.01.00
EpicsInterface_slowcontrols.cc
1 #include "alarm.h" //Holds strings that we can use to access the alarm status, severity, and parameters
2 #include "epicsMutex.h"
3 #include "otsdaq-epics/ControlsInterfacePlugins/EpicsInterface.h"
4 #include "otsdaq/ConfigurationInterface/ConfigurationManager.h"
5 #include "otsdaq/Macros/SlowControlsPluginMacros.h"
6 #include "otsdaq/TablePlugins/SlowControlsTableBase/SlowControlsTableBase.h"
7 
8 #pragma GCC diagnostic push
9 //#include "/mu2e/ups/epics/v3_15_4/Linux64bit+2.6-2.12-e10/include/alarm.h"
10 //#include "alarmString.h"
11 #include "cadef.h" //EPICS Channel Access:
21 #pragma GCC diagnostic pop
22 
23 // clang-format off
24 #define DEBUG false
25 #define PV_FILE_NAME std::string(getenv("SERVICE_DATA_PATH")) + "/SlowControlsDashboardData/pv_list.dat";
26 #define PV_CSV_DIR "/home/mu2edcs/mu2e-dcs/make_db/csv";
27 
28 using namespace ots;
29 
30 const std::string EpicsInterface::EPICS_NO_ALARM = "NO_ALARM";
31 const std::string EpicsInterface::EPICS_INVALID_ALARM = "INVALID";
32 const std::string EpicsInterface::EPICS_MINOR_ALARM = "MINOR";
33 const std::string EpicsInterface::EPICS_MAJOR_ALARM = "MAJOR";
34 
35 // clang-format on
36 
37 EpicsInterface::EpicsInterface(const std::string& pluginType,
38  const std::string& interfaceUID,
39  const ConfigurationTree& theXDAQContextConfigTree,
40  const std::string& controlsConfigurationPath)
42  pluginType, interfaceUID, theXDAQContextConfigTree, controlsConfigurationPath)
43 {
44  // this allows for handlers to happen "asynchronously"
45  SEVCHK(ca_context_create(ca_enable_preemptive_callback),
46  "EpicsInterface::EpicsInterface() : "
47  "ca_enable_preemptive_callback_init()");
48 }
49 
50 EpicsInterface::~EpicsInterface() { destroy(); }
51 
52 void EpicsInterface::destroy()
53 {
54  // __GEN_COUT__ << "mapOfPVInfo_.size() = " << mapOfPVInfo_.size() << __E__;
55  for(auto it = mapOfPVInfo_.begin(); it != mapOfPVInfo_.end(); it++)
56  {
57  cancelSubscriptionToChannel(it->first);
58  destroyChannel(it->first);
59  delete(it->second->parameterPtr);
60  delete(it->second);
61  mapOfPVInfo_.erase(it);
62  }
63 
64  // __GEN_COUT__ << "mapOfPVInfo_.size() = " << mapOfPVInfo_.size() << __E__;
65  SEVCHK(ca_poll(), "EpicsInterface::destroy() : ca_poll");
66  dbSystemLogout();
67  return;
68 }
69 
70 void EpicsInterface::initialize()
71 {
72  __GEN_COUT__ << "Epics Interface now initializing!";
73  destroy();
74  dbSystemLogin();
75  loadListOfPVs();
76  return;
77 }
78 
79 std::vector<std::string> EpicsInterface::getChannelList()
80 {
81  std::vector<std::string> pvList;
82  pvList.resize(mapOfPVInfo_.size());
83  for(auto pv : mapOfPVInfo_)
84  {
85  __GEN_COUT__ << "getPVList() add: " + pv.first << __E__;
86  pvList.push_back(pv.first);
87  }
88  return pvList;
89 }
90 
91 std::string EpicsInterface::getList(const std::string& format)
92 {
93  std::string pvList;
94 
95  std::string refreshRate = "";
96  PGresult* res;
97  char buffer[1024];
98 
99  // pvList = "[\"None\"]";
100  // std::cout << "SUCA: Returning pvList as: " << pvList << __E__;
101  // return pvList;
102 
103  __GEN_COUT__ << "Epics Interface now retrieving pvList!";
104 
105  if(format == "JSON")
106  {
107  __GEN_COUT__ << "Getting list in JSON format! There are " << mapOfPVInfo_.size()
108  << " pv's.";
109  if(mapOfPVInfo_.size() == 0 && loginErrorMsg_ != "")
110  {
111  __GEN_SS__ << "No PVs found and error message: " << loginErrorMsg_ << __E__;
112  __GEN_SS_THROW__;
113  }
114 
115  // pvList = "{\"PVList\" : [";
116  pvList = "[";
117  for(auto it = mapOfPVInfo_.begin(); it != mapOfPVInfo_.end(); it++)
118  {
119  if(dcsArchiveDbConnStatus_ == 1)
120  {
121  res = PQexec(dcsArchiveDbConn, buffer);
122  /*int num = */ snprintf(
123  buffer,
124  sizeof(buffer),
125  "SELECT smpl_mode_id, smpl_per FROM channel WHERE name = '%s'",
126  (it->first).c_str());
127 
128  if(PQresultStatus(res) == PGRES_TUPLES_OK)
129  {
130  int smplMode = 0;
131  try
132  {
133  smplMode = std::stoi(PQgetvalue(res, 0, 0));
134  }
135  catch(const std::exception& e)
136  {
137  }
138  if(smplMode == 2)
139  refreshRate = PQgetvalue(res, 0, 1);
140  PQclear(res);
141  __GEN_COUT__
142  << "getList() \"sample rate\" SELECT result: " << it->first << ":"
143  << refreshRate << " (smpl_mode_id = " << smplMode << ")" << __E__;
144  }
145  else
146  {
147  __GEN_COUT__ << "SELECT failed: " << PQerrorMessage(dcsArchiveDbConn)
148  << __E__;
149  PQclear(res);
150  }
151  }
152  // pvList += "\"" + it->first + ":" + refreshRate + "\", ";
153  pvList += "\"" + it->first + "\", ";
154  //__GEN_COUT__ << it->first << __E__;
155  }
156  pvList.resize(pvList.size() - 2);
157  pvList += "]"; //}";
158  __GEN_COUT__ << pvList << __E__;
159  return pvList;
160  }
161  return pvList;
162 }
163 
164 void EpicsInterface::subscribe(const std::string& pvName)
165 {
166  if(!checkIfPVExists(pvName))
167  {
168  __GEN_COUT__ << pvName << " doesn't exist!" << __E__;
169  return;
170  }
171  createChannel(pvName);
172  usleep(10000); // what makes the console hang at startup
173  subscribeToChannel(pvName, mapOfPVInfo_.find(pvName)->second->channelType);
174  // SEVCHK(ca_poll(), "EpicsInterface::subscribe() : ca_poll"); //print outs
175  // that handle takeover the console; can make our own error handler
176  return;
177 }
178 
179 //{"PVList" : ["Mu2e_BeamData_IOC/CurrentTime"]}
180 void EpicsInterface::subscribeJSON(const std::string& JSONNameString)
181 {
182  // if(DEBUG){__GEN_COUT__ << pvList << __E__;;}
183 
184  std::string JSON = "{\"PVList\" :";
185  std::string pvName;
186  std::string pvList = JSONNameString; // FIXME -- someday fix parsing to not do
187  // so many copies/substr
188  if(pvList.find(JSON) != std::string::npos)
189  {
190  pvList = pvList.substr(pvList.find(JSON) + JSON.length(), std::string::npos);
191  do
192  {
193  pvList = pvList.substr(pvList.find("\"") + 1,
194  std::string::npos); // eliminate up to the next "
195  pvName = pvList.substr(0, pvList.find("\"")); //
196  // if(DEBUG){__GEN_COUT__ << "Read PV Name: " << pvName << __E__;}
197  pvList = pvList.substr(pvList.find("\"") + 1, std::string::npos);
198  // if(DEBUG){__GEN_COUT__ << "pvList : " << pvList << __E__;}
199 
200  if(checkIfPVExists(pvName))
201  {
202  createChannel(pvName);
203  subscribeToChannel(pvName,
204  mapOfPVInfo_.find(pvName)->second->channelType);
205  SEVCHK(ca_poll(), "EpicsInterface::subscribeJSON : ca_poll");
206  }
207  else if(DEBUG)
208  {
209  __GEN_COUT__ << pvName << " not found in file! Not subscribing!" << __E__;
210  }
211 
212  } while(pvList.find(",") != std::string::npos);
213  }
214 
215  return;
216 }
217 
218 void EpicsInterface::unsubscribe(const std::string& pvName)
219 {
220  if(!checkIfPVExists(pvName))
221  {
222  __GEN_COUT__ << pvName << " doesn't exist!" << __E__;
223  return;
224  }
225 
226  cancelSubscriptionToChannel(pvName);
227  return;
228 }
229 
234 void EpicsInterface::eventCallback(struct event_handler_args eha)
235 {
236  // chid chid = eha.chid;
237  if(eha.status == ECA_NORMAL)
238  {
239  // int i;
240  union db_access_val* pBuf = (union db_access_val*)eha.dbr;
241  if(DEBUG)
242  {
243  printf("channel %s: ", ca_name(eha.chid));
244  }
245 
246  //__COUT__ << "event_handler_args.type: " << eha.type << __E__;
247  switch(eha.type)
248  {
249  // case DBR_CTRL_CHAR:
250  // if (DEBUG)
251  // {
252  // __COUT__ << "Response Type: DBR_CTRL_CHAR" << __E__;
253  // }
254  // ((EpicsInterface *)eha.usr)
255  // ->writePVControlValueToRecord(
256  // ca_name(eha.chid),
257  // ((struct dbr_ctrl_char *)
258  // eha.dbr)); // write the PV's control values to
259  // records break;
260  case DBR_CTRL_DOUBLE:
261  if(DEBUG)
262  {
263  __COUT__ << "Response Type: DBR_CTRL_DOUBLE" << __E__;
264  }
265  ((EpicsInterface*)eha.usr)
266  ->writePVControlValueToRecord(
267  ca_name(eha.chid),
268  ((struct dbr_ctrl_double*)
269  eha.dbr)); // write the PV's control values to records
270  break;
271  case DBR_DOUBLE:
272  if(DEBUG)
273  {
274  __COUT__ << "Response Type: DBR_DOUBLE" << __E__;
275  }
276  ((EpicsInterface*)eha.usr)
277  ->writePVValueToRecord(
278  ca_name(eha.chid),
279  std::to_string(
280  *((double*)eha.dbr))); // write the PV's value to records
281  break;
282  case DBR_STS_STRING:
283  if(DEBUG)
284  {
285  __COUT__ << "Response Type: DBR_STS_STRING" << __E__;
286  }
287  ((EpicsInterface*)eha.usr)
288  ->writePVAlertToQueue(ca_name(eha.chid),
289  epicsAlarmConditionStrings[pBuf->sstrval.status],
290  epicsAlarmSeverityStrings[pBuf->sstrval.severity]);
291  /*if(DEBUG)
292  {
293  printf("current %s:\n", eha.count > 1?"values":"value");
294  for (i = 0; i < eha.count; i++)
295  {
296  printf("%s\t", *(&(pBuf->sstrval.value) + i));
297  if ((i+1)%6 == 0) printf("\n");
298  }
299  printf("\n");
300  }*/
301  break;
302  case DBR_STS_SHORT:
303  if(DEBUG)
304  {
305  __COUT__ << "Response Type: DBR_STS_SHORT" << __E__;
306  }
307  ((EpicsInterface*)eha.usr)
308  ->writePVAlertToQueue(ca_name(eha.chid),
309  epicsAlarmConditionStrings[pBuf->sshrtval.status],
310  epicsAlarmSeverityStrings[pBuf->sshrtval.severity]);
311  /*if(DEBUG)
312  {
313  printf("current %s:\n", eha.count > 1?"values":"value");
314  for (i = 0; i < eha.count; i++){
315  printf("%-10d", *(&(pBuf->sshrtval.value) + i));
316  if ((i+1)%8 == 0) printf("\n");
317  }
318  printf("\n");
319  }*/
320  break;
321  case DBR_STS_FLOAT:
322  if(DEBUG)
323  {
324  __COUT__ << "Response Type: DBR_STS_FLOAT" << __E__;
325  }
326  ((EpicsInterface*)eha.usr)
327  ->writePVAlertToQueue(ca_name(eha.chid),
328  epicsAlarmConditionStrings[pBuf->sfltval.status],
329  epicsAlarmSeverityStrings[pBuf->sfltval.severity]);
330  /*if(DEBUG)
331  {
332  printf("current %s:\n", eha.count > 1?"values":"value");
333  for (i = 0; i < eha.count; i++){
334  printf("-10.4f", *(&(pBuf->sfltval.value) + i));
335  if ((i+1)%8 == 0) printf("\n");
336  }
337  printf("\n");
338  }*/
339  break;
340  case DBR_STS_ENUM:
341  if(DEBUG)
342  {
343  __COUT__ << "Response Type: DBR_STS_ENUM" << __E__;
344  }
345  ((EpicsInterface*)eha.usr)
346  ->writePVAlertToQueue(ca_name(eha.chid),
347  epicsAlarmConditionStrings[pBuf->senmval.status],
348  epicsAlarmSeverityStrings[pBuf->senmval.severity]);
349  /*if(DEBUG)
350  {
351  printf("current %s:\n", eha.count > 1?"values":"value");
352  for (i = 0; i < eha.count; i++){
353  printf("%d ", *(&(pBuf->senmval.value) + i));
354  }
355  printf("\n");
356  }*/
357  break;
358  case DBR_STS_CHAR:
359  if(DEBUG)
360  {
361  __COUT__ << "Response Type: DBR_STS_CHAR" << __E__;
362  }
363  ((EpicsInterface*)eha.usr)
364  ->writePVAlertToQueue(ca_name(eha.chid),
365  epicsAlarmConditionStrings[pBuf->schrval.status],
366  epicsAlarmSeverityStrings[pBuf->schrval.severity]);
367  /*if(DEBUG)
368  {
369  printf("current %s:\n", eha.count > 1?"values":"value");
370  for (i = 0; i < eha.count; i++){
371  printf("%-5", *(&(pBuf->schrval.value) + i));
372  if ((i+1)%15 == 0) printf("\n");
373  }
374  printf("\n");
375  }*/
376  break;
377  case DBR_STS_LONG:
378  if(DEBUG)
379  {
380  __COUT__ << "Response Type: DBR_STS_LONG" << __E__;
381  }
382  ((EpicsInterface*)eha.usr)
383  ->writePVAlertToQueue(ca_name(eha.chid),
384  epicsAlarmConditionStrings[pBuf->slngval.status],
385  epicsAlarmSeverityStrings[pBuf->slngval.severity]);
386  /*if(DEBUG)
387  {
388  printf("current %s:\n", eha.count > 1?"values":"value");
389  for (i = 0; i < eha.count; i++){
390  printf("%-15d", *(&(pBuf->slngval.value) + i));
391  if((i+1)%5 == 0) printf("\n");
392  }
393  printf("\n");
394  }*/
395  break;
396  case DBR_STS_DOUBLE:
397  if(DEBUG)
398  {
399  __COUT__ << "Response Type: DBR_STS_DOUBLE" << __E__;
400  }
401  ((EpicsInterface*)eha.usr)
402  ->writePVAlertToQueue(ca_name(eha.chid),
403  epicsAlarmConditionStrings[pBuf->sdblval.status],
404  epicsAlarmSeverityStrings[pBuf->sdblval.severity]);
405  /*if(DEBUG)
406  {
407  printf("current %s:\n", eha.count > 1?"values":"value");
408  for (i = 0; i < eha.count; i++){
409  printf("%-15.4f", *(&(pBuf->sdblval.value) + i));
410  }
411  printf("\n");
412  }*/
413  break;
414  default:
415  if(ca_name(eha.chid))
416  {
417  if(DEBUG)
418  {
419  __COUT__ << " EpicsInterface::eventCallback: PV Name = "
420  << ca_name(eha.chid) << __E__;
421  __COUT__ << (char*)eha.dbr << __E__;
422  }
423  ((EpicsInterface*)eha.usr)
424  ->writePVValueToRecord(
425  ca_name(eha.chid),
426  (char*)eha.dbr); // write the PV's value to records
427  }
428  break;
429  }
430  /* if get operation failed, print channel name and message */
431  }
432  else
433  printf("channel %s: get operation failed\n", ca_name(eha.chid));
434 
435  return;
436 }
437 
438 void EpicsInterface::eventCallbackAlarm(struct event_handler_args eha)
439 {
440  __COUT__ << " EpicsInterface::eventCallbackAlarm" << __E__;
441  // chid chid = eha.chid;
442  if(eha.status == ECA_NORMAL)
443  {
444  __COUT__ << " EpicsInterface::eventCallbackAlarm: PV Name = " << ca_name(eha.chid)
445  << __E__;
446  if(((EpicsInterface*)eha.usr)->newAlarmCallback_ != nullptr)
447  ((EpicsInterface*)eha.usr)->newAlarmCallback_();
448  }
449  return;
450 }
451 
452 void EpicsInterface::staticChannelCallbackHandler(struct connection_handler_args cha)
453 {
454  __COUT__ << "webClientChannelCallbackHandler" << __E__;
455 
456  ((PVHandlerParameters*)ca_puser(cha.chid))->webClient->channelCallbackHandler(cha);
457  return;
458 }
459 
460 void EpicsInterface::channelCallbackHandler(struct connection_handler_args& cha)
461 {
462  std::string pv = ((PVHandlerParameters*)ca_puser(cha.chid))->pvName;
463  if(cha.op == CA_OP_CONN_UP)
464  {
465  __GEN_COUT__ << pv << cha.chid << " connected! " << __E__;
466 
467  mapOfPVInfo_.find(pv)->second->channelType = ca_field_type(cha.chid);
468  readPVRecord(pv);
469 
470  /*status_ =
471  ca_array_get_callback(dbf_type_to_DBR_STS(mapOfPVInfo_.find(pv)->second->channelType),
472  ca_element_count(cha.chid), cha.chid, eventCallback, this);
473  SEVCHK(status_, "ca_array_get_callback");*/
474  }
475  else
476  __GEN_COUT__ << pv << " disconnected!" << __E__;
477 
478  return;
479 }
480 
481 bool EpicsInterface::checkIfPVExists(const std::string& pvName)
482 {
483  if(DEBUG)
484  {
485  __GEN_COUT__ << "EpicsInterface::checkIfPVExists(): PV Info Map Length is "
486  << mapOfPVInfo_.size() << __E__;
487  }
488 
489  if(mapOfPVInfo_.find(pvName) != mapOfPVInfo_.end())
490  return true;
491 
492  return false;
493 }
494 
495 void EpicsInterface::loadListOfPVs()
496 {
497  __GEN_COUT__ << "LOADING LIST OF PVS!!!!";
498  /*
499  std::string pv_csv_dir_path = PV_CSV_DIR;
500  std::vector<std::string> files = std::vector<std::string>();
501  DIR* dp;
502  struct dirent* dirp;
503  if((dp = opendir(pv_csv_dir_path.c_str())) == NULL)
504  {
505  std::cout << "Error opening: " << pv_csv_dir_path << __E__;
506  return;
507  }
508 
509  while((dirp = readdir(dp)) != NULL)
510  {
511  files.push_back(std::string(dirp->d_name));
512  }
513  closedir(dp);
514 
515  // Initialize Channel Access
516  status_ = ca_task_initialize();
517  SEVCHK(status_, "EpicsInterface::loadListOfPVs() : Unable to initialize");
518  if(status_ != ECA_NORMAL)
519  exit(-1);
520 
521  // for each file
522  // int referenceLength = 0;
523  std::vector<std::string> csv_line;
524  std::string pv_name, cluster, category, system, sensor;
525  cluster = "Mu2e";
526  unsigned int i, j;
527 
528  // First two entries will be . & ..
529  for(i = 2; i < files.size(); i++)
530  {
531  // std::cout << pv_csv_dir_path << "/" <<files[i] << __E__;
532  std::string pv_list_file = pv_csv_dir_path + "/" + files[i];
533  __GEN_COUT__ << "Reading: " << pv_list_file << __E__;
534 
535  // read file
536  // for each line in file
537  // std::string pv_list_file = PV_FILE_NAME;
538  //__GEN_COUT__ << pv_list_file;
539 
540  std::ifstream infile(pv_list_file);
541  if(!infile.is_open())
542  {
543  __GEN_SS__ << "Failed to open PV list file: '" << pv_list_file << "'" << __E__;
544  __GEN_SS_THROW__;
545  }
546  __GEN_COUT__ << "Reading file" << __E__;
547 
548  // make map of pvname -> PVInfo
549  // Example line of csv
550  // CompStatus,daq01,fans_fastest_rpm,0,rpm,16e3,12e3,2e3,1e3,,,,Passive,,fans_fastest_rpm
551  // daq01
552  for(std::string line; getline(infile, line);)
553  {
554  //__GEN_COUT__ << line << __E__;
555  csv_line.clear();
556  std::istringstream ss(line);
557  std::string token;
558 
559  while(std::getline(ss, token, ','))
560  csv_line.push_back(token);
561  if(csv_line.at(0)[0] != '#')
562  {
563  category = csv_line.at(0);
564  system = csv_line.at(1);
565  sensor = csv_line.at(2);
566 
567  pv_name = cluster + "_" + category + "_" + system + "/" + sensor;
568  //__GEN_COUT__ << pv_name << __E__;
569  mapOfPVInfo_[pv_name] = new PVInfo(DBR_STRING);
570  }
571  }
572  __GEN_COUT__ << "Finished reading: " << pv_list_file << __E__;
573  }
574  */
575  // HERE GET PVS LIST FROM DB
576  if(dcsArchiveDbConnStatus_ == 1)
577  {
578  PGresult* res;
579  char buffer[1024];
580  std::string pv_name;
581  std::string cluster = "Mu2e";
582 
583  __GEN_COUT__ << "Reading database PVS List" << __E__;
584  snprintf(buffer, sizeof(buffer), "SELECT name FROM channel");
585  res = PQexec(dcsArchiveDbConn, buffer);
586 
587  if(PQresultStatus(res) == PGRES_TUPLES_OK)
588  {
589  for(int i = 0; i < PQntuples(res); i++)
590  {
591  pv_name = PQgetvalue(res, i, 0);
592  if(!pv_name.empty())
593  mapOfPVInfo_[pv_name] = new PVInfo(DBR_STRING);
594  else
595  __GEN_COUT__ << "Empty pv name for position = " << i << __E__;
596  }
597  __GEN_COUT__ << "Finished reading database PVs List!" << __E__;
598  PQclear(res);
599  }
600  else
601  {
602  __GEN_COUT__ << "SELECT failed: " << PQerrorMessage(dcsArchiveDbConn)
603  << __E__;
604  PQclear(res);
605  }
606  }
607 
608  __GEN_COUT__ << "Here is our pv list!" << __E__;
609  // subscribe for each pv
610  for(auto pv : mapOfPVInfo_)
611  {
612  __GEN_COUT__ << pv.first << __E__;
613  subscribe(pv.first);
614  }
615 
616  // channels are subscribed to by here.
617 
618  // get parameters (e.g. HIHI("upper alarm") HI("upper warning") LOLO("lower
619  // alarm")) for each pv
620  // for(auto pv : mapOfPVInfo_)
621  // {
622  // getControlValues(pv.first);
623  // }
624 
625  __GEN_COUT__ << "Finished reading file and subscribing to pvs!" << __E__;
626  SEVCHK(ca_pend_event(0.0),
627  "EpicsInterface::subscribe() : ca_pend_event(0.0)"); // Start listening
628 
629  return;
630 }
631 
632 void EpicsInterface::getControlValues(const std::string& pvName)
633 {
634  if(true)
635  {
636  __GEN_COUT__ << "EpicsInterface::getControlValues(" << pvName << ")" << __E__;
637  }
638  if(!checkIfPVExists(pvName))
639  {
640  __GEN_COUT__ << pvName << " doesn't exist!" << __E__;
641  return;
642  }
643 
644  SEVCHK(ca_array_get_callback(
645  // DBR_CTRL_CHAR,
646  DBR_CTRL_DOUBLE,
647  0,
648  mapOfPVInfo_.find(pvName)->second->channelID,
649  eventCallback,
650  this),
651  "ca_array_get_callback");
652  // SEVCHK(ca_poll(), "EpicsInterface::getControlValues() : ca_poll");
653  return;
654 }
655 
656 void EpicsInterface::createChannel(const std::string& pvName)
657 {
658  if(!checkIfPVExists(pvName))
659  {
660  __GEN_COUT__ << pvName << " doesn't exist!" << __E__;
661  return;
662  }
663  __GEN_COUT__ << "Trying to create channel to " << pvName << ":"
664  << mapOfPVInfo_.find(pvName)->second->channelID << __E__;
665 
666  if(mapOfPVInfo_.find(pvName)->second != NULL) // Check to see if the pvName
667  // maps to a null pointer so we
668  // don't have any errors
669  if(mapOfPVInfo_.find(pvName)->second->channelID !=
670  NULL) // channel might exist, subscription doesn't so create a
671  // subscription
672  {
673  // if state of channel is connected then done, use it
674  if(ca_state(mapOfPVInfo_.find(pvName)->second->channelID) == cs_conn)
675  {
676  if(DEBUG)
677  {
678  __GEN_COUT__ << "Channel to " << pvName << " already exists!"
679  << __E__;
680  }
681  return;
682  }
683  if(DEBUG)
684  {
685  __GEN_COUT__
686  << "Channel to " << pvName
687  << " exists, but is not connected! Destroying current channel."
688  << __E__;
689  }
690  destroyChannel(pvName);
691  }
692 
693  // create pvs handler
694  if(mapOfPVInfo_.find(pvName)->second->parameterPtr == NULL)
695  {
696  mapOfPVInfo_.find(pvName)->second->parameterPtr =
697  new PVHandlerParameters(pvName, this);
698  }
699 
700  // at this point, make a new channel
701  SEVCHK(ca_create_channel(pvName.c_str(),
702  staticChannelCallbackHandler,
703  mapOfPVInfo_.find(pvName)->second->parameterPtr,
704  0,
705  &(mapOfPVInfo_.find(pvName)->second->channelID)),
706  "EpicsInterface::createChannel() : ca_create_channel");
707  __GEN_COUT__ << "channelID: " << pvName
708  << mapOfPVInfo_.find(pvName)->second->channelID << __E__;
709 
710  SEVCHK(ca_replace_access_rights_event(mapOfPVInfo_.find(pvName)->second->channelID,
711  accessRightsCallback),
712  "EpicsInterface::createChannel() : ca_replace_access_rights_event");
713  // SEVCHK(ca_poll(), "EpicsInterface::createChannel() : ca_poll"); //This
714  // routine will perform outstanding channel access background activity and then
715  // return.
716  return;
717 }
718 
719 void EpicsInterface::destroyChannel(const std::string& pvName)
720 {
721  if(mapOfPVInfo_.find(pvName)->second != NULL)
722  {
723  if(mapOfPVInfo_.find(pvName)->second->channelID != NULL)
724  {
725  status_ = ca_clear_channel(mapOfPVInfo_.find(pvName)->second->channelID);
726  SEVCHK(status_, "EpicsInterface::destroyChannel() : ca_clear_channel");
727  if(status_ == ECA_NORMAL)
728  {
729  mapOfPVInfo_.find(pvName)->second->channelID = NULL;
730  if(DEBUG)
731  {
732  __GEN_COUT__ << "Killed channel to " << pvName << __E__;
733  }
734  }
735  SEVCHK(ca_poll(), "EpicsInterface::destroyChannel() : ca_poll");
736  }
737  else
738  {
739  if(DEBUG)
740  {
741  __GEN_COUT__ << "No channel to " << pvName << " exists" << __E__;
742  }
743  }
744  }
745  return;
746 }
747 
748 void EpicsInterface::accessRightsCallback(struct access_rights_handler_args args)
749 {
750  chid chid = args.chid;
751 
752  printChidInfo(chid, "EpicsInterface::createChannel() : accessRightsCallback");
753 }
754 
755 void EpicsInterface::printChidInfo(chid chid, const std::string& message)
756 {
757  __COUT__ << message.c_str() << __E__;
758  __COUT__ << "pv: " << ca_name(chid) << " type(" << ca_field_type(chid)
759  << ") nelements(" << ca_element_count(chid) << ") host("
760  << ca_host_name(chid) << ")" << __E__;
761  __COUT__ << "read(" << ca_read_access(chid) << ") write(" << ca_write_access(chid)
762  << ") state(" << ca_state(chid) << ")" << __E__;
763 }
764 
765 void EpicsInterface::subscribeToChannel(const std::string& pvName,
766  chtype /*subscriptionType*/)
767 {
768  if(!checkIfPVExists(pvName))
769  {
770  __GEN_COUT__ << pvName << " doesn't exist!" << __E__;
771  return;
772  }
773  if(DEBUG)
774  {
775  __GEN_COUT__ << "Trying to subscribe to " << pvName << ":"
776  << mapOfPVInfo_.find(pvName)->second->channelID << __E__;
777  }
778 
779  if(mapOfPVInfo_.find(pvName)->second != NULL) // Check to see if the pvName
780  // maps to a null pointer so we
781  // don't have any errors
782  {
783  if(mapOfPVInfo_.find(pvName)->second->eventID !=
784  NULL) // subscription already exists
785  {
786  if(DEBUG)
787  {
788  __GEN_COUT__ << "Already subscribed to " << pvName << "!" << __E__;
789  }
790  // FIXME No way to check if the event ID is valid
791  // Just cancel the subscription if it already exists?
792  }
793  }
794 
795  // int i=0;
796  // while(ca_state(mapOfPVInfo_.find(pvName)->second->channelID) == cs_conn
797  //&& i<2) Sleep(1); if(i==2)
798  // {__SS__;throw std::runtime_error(ss.str() + "Channel failed for "
799  //+
800  // pvName);}
801 
802  SEVCHK(ca_create_subscription(
803  dbf_type_to_DBR(mapOfPVInfo_.find(pvName)->second->channelType),
804  1,
805  mapOfPVInfo_.find(pvName)->second->channelID,
806  DBE_VALUE | DBE_ALARM | DBE_PROPERTY,
807  eventCallback,
808  this,
809  &(mapOfPVInfo_.find(pvName)->second->eventID)),
810  "EpicsInterface::subscribeToChannel() : ca_create_subscription "
811  "dbf_type_to_DBR");
812 
813  SEVCHK(ca_create_subscription(DBR_STS_DOUBLE,
814  1,
815  mapOfPVInfo_.find(pvName)->second->channelID,
816  DBE_VALUE | DBE_ALARM | DBE_PROPERTY,
817  eventCallback,
818  this,
819  &(mapOfPVInfo_.find(pvName)->second->eventID)),
820  "EpicsInterface::subscribeToChannel() : ca_create_subscription "
821  "DBR_STS_DOUBLE");
822 
823  SEVCHK(ca_create_subscription(DBR_CTRL_DOUBLE,
824  1,
825  mapOfPVInfo_.find(pvName)->second->channelID,
826  DBE_VALUE | DBE_ALARM | DBE_PROPERTY,
827  eventCallback,
828  this,
829  &(mapOfPVInfo_.find(pvName)->second->eventID)),
830  "EpicsInterface::subscribeToChannel() : ca_create_subscription");
831  SEVCHK(ca_create_subscription(DBR_CTRL_DOUBLE,
832  1,
833  mapOfPVInfo_.find(pvName)->second->channelID,
834  DBE_ALARM,
835  eventCallbackAlarm,
836  this,
837  &(mapOfPVInfo_.find(pvName)->second->eventID)),
838  "EpicsInterface::subscribeToChannel() : ca_create_subscription");
839 
840  if(DEBUG)
841  {
842  __GEN_COUT__ << "EpicsInterface::subscribeToChannel: Created Subscription to "
843  << mapOfPVInfo_.find(pvName)->first << "!\n"
844  << __E__;
845  }
846  // SEVCHK(ca_poll(), "EpicsInterface::subscribeToChannel() : ca_poll");
847  return;
848 }
849 
850 void EpicsInterface::cancelSubscriptionToChannel(const std::string& pvName)
851 {
852  if(mapOfPVInfo_.find(pvName)->second != NULL)
853  if(mapOfPVInfo_.find(pvName)->second->eventID != NULL)
854  {
855  status_ = ca_clear_subscription(mapOfPVInfo_.find(pvName)->second->eventID);
856  SEVCHK(status_,
857  "EpicsInterface::cancelSubscriptionToChannel() : "
858  "ca_clear_subscription");
859  if(status_ == ECA_NORMAL)
860  {
861  mapOfPVInfo_.find(pvName)->second->eventID = NULL;
862  if(DEBUG)
863  {
864  __GEN_COUT__ << "Killed subscription to " << pvName << __E__;
865  }
866  }
867  SEVCHK(ca_poll(), "EpicsInterface::cancelSubscriptionToChannel() : ca_poll");
868  }
869  else
870  {
871  if(DEBUG)
872  {
873  __GEN_COUT__ << pvName << "does not have a subscription!" << __E__;
874  }
875  }
876  else
877  {
878  // __GEN_COUT__ << pvName << "does not have a subscription!" << __E__;
879  }
880  // SEVCHK(ca_flush_io(),"ca_flush_io");
881  return;
882 }
883 
884 void EpicsInterface::readValueFromPV(const std::string& /*pvName*/)
885 {
886  // SEVCHK(ca_get(DBR_String, 0, mapOfPVInfo_.find(pvName)->second->channelID,
887  // &(mapOfPVInfo_.find(pvName)->second->pvValue), eventCallback,
888  // &(mapOfPVInfo_.find(pvName)->second->callbackPtr)), "ca_get");
889 
890  return;
891 }
892 
893 void EpicsInterface::writePVControlValueToRecord(
894  const std::string& pvName,
895  // struct dbr_ctrl_char*
896  // pdata)
897  struct dbr_ctrl_double* pdata)
898 {
899  if(DEBUG)
900  {
901  __GEN_COUT__ << "Reading Control Values from " << pvName << "!" << __E__;
902  }
903 
904  if(!checkIfPVExists(pvName))
905  {
906  __GEN_COUT__ << pvName << " doesn't exist!" << __E__;
907  return;
908  }
909  mapOfPVInfo_.find(pvName)->second->settings = *pdata;
910 
911  if(DEBUG)
912  {
913  __GEN_COUT__ << "pvName: " << pvName << __E__;
914  __GEN_COUT__ << "status: " << pdata->status << __E__;
915  __GEN_COUT__ << "severity: " << pdata->severity << __E__;
916  __GEN_COUT__ << "units: " << pdata->units << __E__;
917  __GEN_COUT__ << "upper disp limit: " << (int)(pdata->upper_disp_limit) << __E__;
918  __GEN_COUT__ << "lower disp limit: " << pdata->lower_disp_limit << __E__;
919  __GEN_COUT__ << "upper alarm limit: " << pdata->upper_alarm_limit << __E__;
920  __GEN_COUT__ << "upper warning limit: " << pdata->upper_warning_limit << __E__;
921  __GEN_COUT__ << "lower warning limit: " << pdata->lower_warning_limit << __E__;
922  __GEN_COUT__ << "lower alarm limit: " << pdata->lower_alarm_limit << __E__;
923  __GEN_COUT__ << "upper control limit: " << pdata->upper_ctrl_limit << __E__;
924  __GEN_COUT__ << "lower control limit: " << pdata->lower_ctrl_limit << __E__;
925  //__GEN_COUT__ << "RISC_pad: " << pdata->RISC_pad << __E__;
926  __GEN_COUT__ << "Value: " << pdata->value << __E__;
927  }
928  return;
929 }
930 
932 void EpicsInterface::writePVValueToRecord(const std::string& pvName,
933  const std::string& pdata)
934 {
935  std::pair<time_t, std::string> currentRecord(time(0), pdata);
936 
937  if(!checkIfPVExists(pvName))
938  {
939  __GEN_COUT__ << pvName << " doesn't exist!" << __E__;
940  return;
941  }
942  // __GEN_COUT__ << pdata << __E__;
943 
944  PVInfo* pvInfo = mapOfPVInfo_.find(pvName)->second;
945 
946  if(pvInfo->mostRecentBufferIndex != pvInfo->dataCache.size() - 1 &&
947  pvInfo->mostRecentBufferIndex != (unsigned int)(-1))
948  {
949  if(pvInfo->dataCache[pvInfo->mostRecentBufferIndex].first == currentRecord.first)
950  {
951  pvInfo->valueChange = true; // false;
952  }
953  else
954  {
955  pvInfo->valueChange = true;
956  }
957 
958  ++pvInfo->mostRecentBufferIndex;
959  pvInfo->dataCache[pvInfo->mostRecentBufferIndex] = currentRecord;
960  }
961  else
962  {
963  pvInfo->dataCache[0] = currentRecord;
964  pvInfo->mostRecentBufferIndex = 0;
965  }
966  // debugConsole(pvName);
967 
968  return;
969 }
970 
971 void EpicsInterface::writePVAlertToQueue(const std::string& pvName,
972  const char* status,
973  const char* severity)
974 {
975  if(!checkIfPVExists(pvName))
976  {
977  __GEN_COUT__ << pvName << " doesn't exist!" << __E__;
978  return;
979  }
980  PVAlerts alert(time(0), status, severity);
981  mapOfPVInfo_.find(pvName)->second->alerts.push(alert);
982  //__GEN_COUT__ << "writePVAlertToQueue(): " << pvName << " " << status << " "
983  //<< severity << __E__;
984 
985  // debugConsole(pvName);
986 
987  return;
988 }
989 
990 void EpicsInterface::readPVRecord(const std::string& pvName)
991 {
992  status_ = ca_array_get_callback(
993  dbf_type_to_DBR_STS(mapOfPVInfo_.find(pvName)->second->channelType),
994  ca_element_count(mapOfPVInfo_.find(pvName)->second->channelID),
995  mapOfPVInfo_.find(pvName)->second->channelID,
996  eventCallback,
997  this);
998  SEVCHK(status_, "EpicsInterface::readPVRecord(): ca_array_get_callback");
999  return;
1000 }
1001 
1002 void EpicsInterface::debugConsole(const std::string& pvName)
1003 {
1004  __GEN_COUT__ << "==============================================================="
1005  "==============="
1006  << __E__;
1007  for(unsigned int it = 0; it < mapOfPVInfo_.find(pvName)->second->dataCache.size() - 1;
1008  it++)
1009  {
1010  if(it == mapOfPVInfo_.find(pvName)->second->mostRecentBufferIndex)
1011  {
1012  __GEN_COUT__ << "-----------------------------------------------------------"
1013  "----------"
1014  << __E__;
1015  }
1016  __GEN_COUT__ << "Iteration: " << it << " | "
1017  << mapOfPVInfo_.find(pvName)->second->mostRecentBufferIndex << " | "
1018  << mapOfPVInfo_.find(pvName)->second->dataCache[it].second << __E__;
1019  if(it == mapOfPVInfo_.find(pvName)->second->mostRecentBufferIndex)
1020  {
1021  __GEN_COUT__ << "-----------------------------------------------------------"
1022  "----------"
1023  << __E__;
1024  }
1025  }
1026  __GEN_COUT__ << "==============================================================="
1027  "==============="
1028  << __E__;
1029  __GEN_COUT__ << "Status: "
1030  << " | " << mapOfPVInfo_.find(pvName)->second->alerts.size() << " | "
1031  << mapOfPVInfo_.find(pvName)->second->alerts.front().status << __E__;
1032  __GEN_COUT__ << "Severity: "
1033  << " | " << mapOfPVInfo_.find(pvName)->second->alerts.size() << " | "
1034  << mapOfPVInfo_.find(pvName)->second->alerts.front().severity << __E__;
1035  __GEN_COUT__ << "==============================================================="
1036  "==============="
1037  << __E__;
1038 
1039  return;
1040 }
1041 
1042 void EpicsInterface::popQueue(const std::string& pvName)
1043 {
1044  if(DEBUG)
1045  {
1046  __GEN_COUT__ << "EpicsInterface::popQueue() " << __E__;
1047  }
1048  mapOfPVInfo_.find(pvName)->second->alerts.pop();
1049 
1050  if(mapOfPVInfo_.find(pvName)->second->alerts.empty())
1051  {
1052  readPVRecord(pvName);
1053  SEVCHK(ca_poll(), "EpicsInterface::popQueue() : ca_poll");
1054  }
1055  return;
1056 }
1057 
1058 //========================================================================================================================
1059 std::array<std::string, 4> EpicsInterface::getCurrentValue(const std::string& pvName)
1060 {
1061  __GEN_COUT__ << "void EpicsInterface::getCurrentValue() reached" << __E__;
1062 
1063  if(mapOfPVInfo_.find(pvName) != mapOfPVInfo_.end())
1064  {
1065  PVInfo* pv = mapOfPVInfo_.find(pvName)->second;
1066  std::string time, value, status, severity;
1067 
1068  int index = pv->mostRecentBufferIndex;
1069 
1070  __GEN_COUT__ << pv << index << __E__;
1071 
1072  if(0 <= index && index < pv->circularBufferSize)
1073  {
1074  //__GEN_COUT__ << pv->dataCache[index].first <<" "<< std::time(0)-60 <<
1075  //__E__;
1076 
1077  time = std::to_string(pv->dataCache[index].first);
1078  value = pv->dataCache[index].second;
1079  status = pv->alerts.back().status;
1080  severity = pv->alerts.back().severity;
1081  }
1082  else if(index == -1)
1083  {
1084  time = "N/a";
1085  value = "N/a";
1086  status = "DC";
1087  severity = "DC";
1088  }
1089  else
1090  {
1091  time = "N/a";
1092  value = "N/a";
1093  status = "UDF";
1094  severity = "INVALID";
1095  }
1096  // Time, Value, Status, Severity
1097 
1098  __GEN_COUT__ << "Index: " << index << __E__;
1099  __GEN_COUT__ << "Time: " << time << __E__;
1100  __GEN_COUT__ << "Value: " << value << __E__;
1101  __GEN_COUT__ << "Status: " << status << __E__;
1102  __GEN_COUT__ << "Severity: " << severity << __E__;
1103 
1104  /* if(pv->valueChange)
1105  {
1106  pv->valueChange = false;
1107  }
1108  else
1109  {
1110  __GEN_COUT__ << pvName << " has no change" << __E__;
1111  time = "NO_CHANGE";
1112  value = "";
1113  status = "";
1114  severity = "";
1115  }
1116  */
1117  std::array<std::string, 4> currentValues = {time, value, status, severity};
1118 
1119  return currentValues;
1120  }
1121  else
1122  {
1123  __GEN_COUT__ << pvName << " was not found!" << __E__;
1124  __GEN_COUT__ << "Trying to resubscribe to " << pvName << __E__;
1125  // subscribe(pvName);
1126  }
1127 
1128  std::array<std::string, 4> currentValues = {"PV Not Found", "NF", "N/a", "N/a"};
1129  // std::string currentValues [4] = {"N/a", "N/a", "N/a", "N/a"};
1130  return currentValues;
1131 }
1132 
1133 //========================================================================================================================
1134 std::array<std::string, 9> EpicsInterface::getSettings(const std::string& pvName)
1135 {
1136  __GEN_COUT__ << "EpicsInterface::getPVSettings() reached" << __E__;
1137 
1138  if(mapOfPVInfo_.find(pvName) != mapOfPVInfo_.end())
1139  {
1140  std::string units = "DC'd", upperDisplayLimit = "DC'd",
1141  lowerDisplayLimit = "DC'd", upperAlarmLimit = "DC'd",
1142  upperWarningLimit = "DC'd", lowerWarningLimit = "DC'd",
1143  lowerAlarmLimit = "DC'd", upperControlLimit = "DC'd",
1144  lowerControlLimit = "DC'd";
1145  if(mapOfPVInfo_.find(pvName)->second != NULL) // Check to see if the pvName
1146  // maps to a null pointer so
1147  // we don't have any errors
1148  if(mapOfPVInfo_.find(pvName)->second->channelID !=
1149  NULL) // channel might exist, subscription doesn't so create a
1150  // subscription
1151  {
1152  // dbr_ctrl_char* set = &mapOfPVInfo_.find(pvName)->second->settings;
1153  dbr_ctrl_double* set = &mapOfPVInfo_.find(pvName)->second->settings;
1154 
1155  // sprintf(&units[0],"%d",set->units);
1156  units = set->units;
1157  upperDisplayLimit = std::to_string(set->upper_disp_limit);
1158  lowerDisplayLimit = std::to_string(set->lower_disp_limit);
1159  upperWarningLimit = std::to_string(set->upper_warning_limit);
1160  lowerWarningLimit = std::to_string(set->lower_warning_limit);
1161  upperAlarmLimit = std::to_string(set->upper_alarm_limit);
1162  lowerAlarmLimit = std::to_string(set->lower_alarm_limit);
1163  upperControlLimit = std::to_string(set->upper_ctrl_limit);
1164  lowerControlLimit = std::to_string(set->lower_ctrl_limit);
1165 
1166  __GEN_COUT__ << "Units : " << units << __E__;
1167  __GEN_COUT__ << "Upper Display Limit: " << upperDisplayLimit << __E__;
1168  __GEN_COUT__ << "Lower Display Limit: " << lowerDisplayLimit << __E__;
1169  __GEN_COUT__ << "Upper Alarm Limit : " << upperAlarmLimit << __E__;
1170  __GEN_COUT__ << "Upper Warning Limit: " << upperWarningLimit << __E__;
1171  __GEN_COUT__ << "Lower Warning Limit: " << lowerWarningLimit << __E__;
1172  __GEN_COUT__ << "Lower Alarm Limit : " << lowerAlarmLimit << __E__;
1173  __GEN_COUT__ << "Upper Control Limit: " << upperControlLimit << __E__;
1174  __GEN_COUT__ << "Lower Control Limit: " << lowerControlLimit << __E__;
1175  }
1176 
1177  std::array<std::string, 9> s = {units,
1178  upperDisplayLimit,
1179  lowerDisplayLimit,
1180  upperAlarmLimit,
1181  upperWarningLimit,
1182  lowerWarningLimit,
1183  lowerAlarmLimit,
1184  upperControlLimit,
1185  lowerControlLimit};
1186 
1187  return s;
1188  }
1189  else
1190  {
1191  __GEN_COUT__ << pvName << " was not found!" << __E__;
1192  __GEN_COUT__ << "Trying to resubscribe to " << pvName << __E__;
1193  subscribe(pvName);
1194  }
1195  std::array<std::string, 9> s = {
1196  "DC'd", "DC'd", "DC'd", "DC'd", "DC'd", "DC'd", "DC'd", "DC'd", "DC'd"};
1197  return s;
1198 }
1199 
1200 //========================================================================================================================
1201 void EpicsInterface::dbSystemLogin()
1202 {
1203  dcsArchiveDbConnStatus_ = 0;
1204  dcsAlarmDbConnStatus_ = 0;
1205  dcsLogDbConnStatus_ = 0;
1206 
1207  char* dbname_ = const_cast<char*>(
1208  getenv("DCS_ARCHIVE_DATABASE") ? getenv("DCS_ARCHIVE_DATABASE") : "dcs_archive");
1209  char* dbhost_ = const_cast<char*>(
1210  getenv("DCS_ARCHIVE_DATABASE_HOST") ? getenv("DCS_ARCHIVE_DATABASE_HOST") : "");
1211  char* dbport_ = const_cast<char*>(
1212  getenv("DCS_ARCHIVE_DATABASE_PORT") ? getenv("DCS_ARCHIVE_DATABASE_PORT") : "");
1213  char* dbuser_ = const_cast<char*>(
1214  getenv("DCS_ARCHIVE_DATABASE_USER") ? getenv("DCS_ARCHIVE_DATABASE_USER") : "");
1215  char* dbpwd_ = const_cast<char*>(
1216  getenv("DCS_ARCHIVE_DATABASE_PWD") ? getenv("DCS_ARCHIVE_DATABASE_PWD") : "");
1217 
1218  // open db connections
1219  char dcsArchiveDbConnInfo[1024];
1220  sprintf(dcsArchiveDbConnInfo,
1221  "dbname=%s host=%s port=%s \
1222  user=%s password=%s",
1223  dbname_,
1224  dbhost_,
1225  dbport_,
1226  dbuser_,
1227  dbpwd_);
1228 
1229  // dcs_archive Db Connection
1230  dcsArchiveDbConn = PQconnectdb(dcsArchiveDbConnInfo);
1231 
1232  if(PQstatus(dcsArchiveDbConn) == CONNECTION_BAD)
1233  {
1234  loginErrorMsg_ = "Unable to connect to the dcs_archive database!";
1235  __GEN_COUT__ << "Unable to connect to the dcs_archive database!\n" << __E__;
1236  PQfinish(dcsArchiveDbConn);
1237  }
1238  else
1239  {
1240  __GEN_COUT__ << "Connected to the dcs_archive database!\n" << __E__;
1241  dcsArchiveDbConnStatus_ = 1;
1242  }
1243 
1244  // dcs_alarm Db Connection
1245  dbname_ = const_cast<char*>(
1246  getenv("DCS_ALARM_DATABASE") ? getenv("DCS_ALARM_DATABASE") : "dcs_alarm");
1247  dbhost_ = const_cast<char*>(
1248  getenv("DCS_ALARM_DATABASE_HOST") ? getenv("DCS_ALARM_DATABASE_HOST") : "");
1249  dbport_ = const_cast<char*>(
1250  getenv("DCS_ALARM_DATABASE_PORT") ? getenv("DCS_ALARM_DATABASE_PORT") : "");
1251  dbuser_ = const_cast<char*>(
1252  getenv("DCS_ALARM_DATABASE_USER") ? getenv("DCS_ALARM_DATABASE_USER") : "");
1253  dbpwd_ = const_cast<char*>(
1254  getenv("DCS_ALARM_DATABASE_PWD") ? getenv("DCS_ALARM_DATABASE_PWD") : "");
1255  char dcsAlarmDbConnInfo[1024];
1256  sprintf(dcsAlarmDbConnInfo,
1257  "dbname=%s host=%s port=%s \
1258  user=%s password=%s",
1259  dbname_,
1260  dbhost_,
1261  dbport_,
1262  dbuser_,
1263  dbpwd_);
1264 
1265  dcsAlarmDbConn = PQconnectdb(dcsAlarmDbConnInfo);
1266 
1267  if(PQstatus(dcsAlarmDbConn) == CONNECTION_BAD)
1268  {
1269  loginErrorMsg_ = "Unable to connect to the dcs_alarm database!";
1270  __GEN_COUT__ << "Unable to connect to the dcs_alarm database!\n" << __E__;
1271  PQfinish(dcsAlarmDbConn);
1272  }
1273  else
1274  {
1275  __GEN_COUT__ << "Connected to the dcs_alarm database!\n" << __E__;
1276  dcsAlarmDbConnStatus_ = 1;
1277  }
1278 
1279  // dcs_log Db Connection
1280  dbname_ = const_cast<char*>(getenv("DCS_LOG_DATABASE") ? getenv("DCS_LOG_DATABASE")
1281  : "dcs_log");
1282  dbhost_ = const_cast<char*>(
1283  getenv("DCS_LOG_DATABASE_HOST") ? getenv("DCS_LOG_DATABASE_HOST") : "");
1284  dbport_ = const_cast<char*>(
1285  getenv("DCS_LOG_DATABASE_PORT") ? getenv("DCS_LOG_DATABASE_PORT") : "");
1286  dbuser_ = const_cast<char*>(
1287  getenv("DCS_LOG_DATABASE_USER") ? getenv("DCS_LOG_DATABASE_USER") : "");
1288  dbpwd_ = const_cast<char*>(
1289  getenv("DCS_LOG_DATABASE_PWD") ? getenv("DCS_LOG_DATABASE_PWD") : "");
1290  char dcsLogDbConnInfo[1024];
1291  sprintf(dcsLogDbConnInfo,
1292  "dbname=%s host=%s port=%s \
1293  user=%s password=%s",
1294  dbname_,
1295  dbhost_,
1296  dbport_,
1297  dbuser_,
1298  dbpwd_);
1299 
1300  dcsLogDbConn = PQconnectdb(dcsLogDbConnInfo);
1301 
1302  if(PQstatus(dcsLogDbConn) == CONNECTION_BAD)
1303  {
1304  loginErrorMsg_ = "Unable to connect to the dcs_log database!";
1305  __GEN_COUT__ << "Unable to connect to the dcs_log database!\n" << __E__;
1306  PQfinish(dcsLogDbConn);
1307  }
1308  else
1309  {
1310  __GEN_COUT__ << "Connected to the dcs_log database!\n" << __E__;
1311  dcsLogDbConnStatus_ = 1;
1312  }
1313 }
1314 
1315 //========================================================================================================================
1316 void EpicsInterface::dbSystemLogout()
1317 {
1318  if(PQstatus(dcsArchiveDbConn) == CONNECTION_OK)
1319  {
1320  PQfinish(dcsArchiveDbConn);
1321  __GEN_COUT__ << "DCS_ARCHIVE DB CONNECTION CLOSED\n" << __E__;
1322  }
1323  if(PQstatus(dcsAlarmDbConn) == CONNECTION_OK)
1324  {
1325  PQfinish(dcsAlarmDbConn);
1326  __GEN_COUT__ << "DCS_ALARM DB CONNECTION CLOSED\n" << __E__;
1327  }
1328  if(PQstatus(dcsLogDbConn) == CONNECTION_OK)
1329  {
1330  PQfinish(dcsLogDbConn);
1331  __GEN_COUT__ << "DCS_LOG DB CONNECTION CLOSED\n" << __E__;
1332  }
1333 }
1334 
1335 //========================================================================================================================
1336 std::vector<std::vector<std::string>> EpicsInterface::getChannelHistory(
1337  const std::string& pvName, int startTime, int endTime)
1338 {
1339  __GEN_COUT__ << "getChannelHistory() reached" << __E__;
1340  std::vector<std::vector<std::string>> history;
1341 
1342  if(mapOfPVInfo_.find(pvName) != mapOfPVInfo_.end())
1343  {
1344  if(dcsArchiveDbConnStatus_ == 1)
1345  {
1346  PGresult* res = nullptr;
1347  try
1348  {
1349  char buffer[1024];
1350  std::string row;
1351 
1352  std::string select =
1353  "SELECT FLOOR(EXTRACT(EPOCH FROM smpl_time)), float_val, "
1354  "status.name, severity.name, smpl_per "
1355  "FROM channel, sample, status, severity "
1356  "WHERE "
1357  "channel.channel_id = sample.channel_id "
1358  "AND sample.severity_id = severity.severity_id "
1359  "AND sample.status_id = status.status_id "
1360  "AND channel.name = \'%s\' "
1361  "AND float_val IS NOT NULL "
1362  "AND smpl_time >= TO_TIMESTAMP(\'%d\') "
1363  "AND smpl_time < TO_TIMESTAMP(\'%d\') ";
1364  if(endTime - startTime < 432000)
1365  select += "ORDER BY smpl_time desc";
1366  else if((endTime - startTime > 432000) && (endTime - startTime <= 864000))
1367  select +=
1368  "AND EXTRACT(MINUTE FROM smpl_time) % 10 = 0 ORDER BY smpl_time "
1369  "desc";
1370  else if((endTime - startTime > 864000) &&
1371  (endTime - startTime <= 1296000))
1372  select +=
1373  "AND EXTRACT(MINUTE FROM smpl_time) % 15 = 0 ORDER BY smpl_time "
1374  "desc";
1375  else if(endTime - startTime > 1296000)
1376  select +=
1377  "AND EXTRACT(MINUTE FROM smpl_time) % 60 = 0 ORDER BY smpl_time "
1378  "desc";
1379 
1380  // VIEW LAST 10 UPDATES
1381  /*int num =*/snprintf(buffer,
1382  sizeof(buffer),
1383  select.c_str(),
1384  pvName.c_str(),
1385  startTime,
1386  endTime);
1387 
1388  res = PQexec(dcsArchiveDbConn, buffer);
1389 
1390  if(PQresultStatus(res) != PGRES_TUPLES_OK)
1391  {
1392  __SS__ << "getChannelHistory(): SELECT FROM ARCHIVER DATABASE "
1393  "FAILED!!! PQ ERROR: "
1394  << PQresultErrorMessage(res) << __E__;
1395  PQclear(res);
1396  __SS_THROW__;
1397  }
1398 
1399  if(PQntuples(res) > 0)
1400  {
1401  /* first, print out the attribute names */
1402  int nFields = PQnfields(res);
1403  history.resize(PQntuples(res));
1404 
1405  /* next, print out the rows */
1406  for(int i = 0; i < PQntuples(res); i++)
1407  {
1408  history[i].resize(nFields);
1409  for(int j = 0; j < nFields; j++)
1410  {
1411  history[i][j] = PQgetvalue(res, i, j);
1412  row.append(PQgetvalue(res, i, j));
1413  row.append(" ");
1414  }
1415  row.append("\n");
1416  }
1417  __GEN_COUT__ << "getChannelHistory(): row from select: " << row
1418  << __E__;
1419  PQclear(res);
1420  }
1421  }
1422  catch(...)
1423  {
1424  __SS__ << "getChannelHistory(): FAILING GETTING DATA FROM ARCHIVER "
1425  "DATABASE!!! PQ ERROR: "
1426  << PQresultErrorMessage(res) << __E__;
1427  try
1428  {
1429  throw;
1430  } //one more try to printout extra info
1431  catch(const std::exception& e)
1432  {
1433  ss << "Exception message: " << e.what();
1434  }
1435  catch(...)
1436  {
1437  }
1438  __SS_THROW__;
1439  }
1440  }
1441  else
1442  {
1443  __SS__ << "getChannelHistory(): ARCHIVER DATABASE CONNECTION FAILED!!! "
1444  << __E__;
1445  __SS_THROW__;
1446  }
1447  }
1448  else
1449  {
1450  history.resize(1);
1451  history[0] = {"PV Not Found", "NF", "N/a", "N/a"};
1452  __GEN_COUT__ << "getChannelHistory() pvName " << pvName << " was not found!"
1453  << __E__;
1454  __GEN_COUT__ << "Trying to resubscribe to " << pvName << __E__;
1455  subscribe(pvName);
1456  }
1457 
1458  return history;
1459 } // end getChannelHistory()
1460 
1461 //========================================================================================================================
1462 std::vector<std::vector<std::string>> EpicsInterface::getLastAlarms(
1463  const std::string& pvName)
1464 {
1465  __GEN_COUT__ << "EpicsInterface::getLastAlarms() reached" << __E__;
1466  std::vector<std::vector<std::string>> alarms;
1467 
1468  if(dcsAlarmDbConnStatus_ == 1)
1469  {
1470  PGresult* res = nullptr;
1471  try
1472  {
1473  char buffer[1024];
1474  std::string row;
1475 
1476  // ACTION FOR ALARM DB CHANNEL TABLE
1477  /*int num =*/snprintf(buffer,
1478  sizeof(buffer),
1479  "SELECT pv.component_id \
1480  , alarm_tree.name \
1481  , pv.descr \
1482  , pv.pv_value \
1483  , status.name as status \
1484  , severity.name as severity \
1485  , pv.alarm_time \
1486  , pv.enabled_ind \
1487  , pv.annunciate_ind \
1488  , pv.latch_ind \
1489  , pv.delay \
1490  , pv.filter \
1491  , pv.delay_count \
1492  , pv.act_global_alarm_ind \
1493  FROM alarm_tree, pv, status, severity \
1494  WHERE pv.component_id = alarm_tree.component_id \
1495  AND pv.status_id = status.status_id \
1496  AND pv.severity_id = severity.severity_id \
1497  AND alarm_tree.name LIKE \'%%%s%%\' \
1498  ORDER BY pv.severity_id DESC;",
1499  pvName.c_str());
1500 
1501  res = PQexec(dcsAlarmDbConn, buffer);
1502  __COUT__ << "getLastAlarms(): SELECT pv table PQntuples(res): "
1503  << PQntuples(res) << __E__;
1504 
1505  if(PQresultStatus(res) != PGRES_TUPLES_OK)
1506  {
1507  __SS__
1508  << "getLastAlarms(): SELECT FROM ALARM DATABASE FAILED!!! PQ ERROR: "
1509  << PQresultErrorMessage(res) << __E__;
1510  PQclear(res);
1511  __SS_THROW__;
1512  }
1513 
1514  if(PQntuples(res) > 0)
1515  {
1516  // UPDATE ALARMS LIST
1517  int nFields = PQnfields(res);
1518  alarms.resize(PQntuples(res));
1519 
1520  /* next, print out the rows */
1521  for(int i = 0; i < PQntuples(res); i++)
1522  {
1523  alarms[i].resize(nFields);
1524  for(int j = 0; j < nFields; j++)
1525  {
1526  alarms[i][j] = PQgetvalue(res, i, j);
1527  row.append(PQgetvalue(res, i, j));
1528  row.append(" ");
1529  }
1530  row.append("\n");
1531  //__GEN_COUT__ << "getLastAlarms(): " << row << __E__;
1532  }
1533  }
1534  else
1535  {
1536  alarms.resize(1);
1537  alarms[0] = {
1538  "0",
1539  "Alarms List Not Found",
1540  "N/a",
1541  "N/a",
1542  "N/a",
1543  "N/a",
1544  "N/a",
1545  "N/a",
1546  "N/a",
1547  "N/a",
1548  "N/a",
1549  "N/a",
1550  "N/a",
1551  "N/a",
1552  };
1553  }
1554 
1555  PQclear(res);
1556  }
1557  catch(...)
1558  {
1559  __SS__ << "getLastAlarms(): FAILING GETTING DATA FROM ARCHIVER DATABASE!!! "
1560  "PQ ERROR: "
1561  << PQresultErrorMessage(res) << __E__;
1562  try
1563  {
1564  throw;
1565  } //one more try to printout extra info
1566  catch(const std::exception& e)
1567  {
1568  ss << "Exception message: " << e.what();
1569  }
1570  catch(...)
1571  {
1572  }
1573  __SS_THROW__;
1574  }
1575  }
1576  else
1577  {
1578  __SS__ << "getLastAlarms(): ALARM DATABASE CONNECTION FAILED!!! " << __E__;
1579  __SS_THROW__;
1580  }
1581  return alarms;
1582 } // end getLastAlarms()
1583 
1584 //========================================================================================================================
1585 std::vector<std::vector<std::string>> EpicsInterface::getAlarmsLog(
1586  const std::string& pvName)
1587 {
1588  __GEN_COUT__ << "EpicsInterface::getAlarmsLog() reached" << __E__;
1589  std::vector<std::vector<std::string>> alarmsHistory;
1590 
1591  if(dcsLogDbConnStatus_ == 1)
1592  {
1593  PGresult* res = nullptr;
1594  try
1595  {
1596  char buffer[1024];
1597  std::string row;
1598 
1599  // ACTION FOR ALARM DB CHANNEL TABLE
1600  /*int num = */ snprintf(
1601  buffer,
1602  sizeof(buffer),
1603  "SELECT DISTINCT \
1604  message.id \
1605  , message.name \
1606  , message_content.value \
1607  , msg_property_type.name as \"status\" \
1608  , message.severity \
1609  , message.datum as \"time\" \
1610  FROM message, message_content, msg_property_type \
1611  WHERE message.id = message_content.message_id \
1612  AND message_content.msg_property_type_id = msg_property_type.id \
1613  AND message.type = 'alarm' \
1614  AND message.severity != 'OK' \
1615  AND message.datum >= current_date -20 \
1616  AND message.name LIKE '%%%s%%' \
1617  ORDER BY message.datum DESC;",
1618  pvName.c_str());
1619 
1620  res = PQexec(dcsLogDbConn, buffer);
1621  __COUT__ << "getAlarmsLog(): SELECT message table PQntuples(res): "
1622  << PQntuples(res) << __E__;
1623 
1624  if(PQresultStatus(res) != PGRES_TUPLES_OK)
1625  {
1626  __SS__ << "getAlarmsLog(): SELECT FROM ALARM LOG DATABASE FAILED!!! PQ "
1627  "ERROR: "
1628  << PQresultErrorMessage(res) << __E__;
1629  PQclear(res);
1630  __SS_THROW__;
1631  }
1632 
1633  if(PQntuples(res) > 0)
1634  {
1635  // UPDATE ALARMS LIST
1636  int nFields = PQnfields(res);
1637  alarmsHistory.resize(PQntuples(res));
1638 
1639  /* next, print out the rows */
1640  for(int i = 0; i < PQntuples(res); i++)
1641  {
1642  alarmsHistory[i].resize(nFields);
1643  for(int j = 0; j < nFields; j++)
1644  {
1645  alarmsHistory[i][j] = PQgetvalue(res, i, j);
1646  row.append(PQgetvalue(res, i, j));
1647  row.append(" ");
1648  }
1649  row.append("\n");
1650  //__GEN_COUT__ << "getAlarmsLog(): " << row << __E__;
1651  }
1652  }
1653  else
1654  {
1655  alarmsHistory.resize(1);
1656  alarmsHistory[0] = {
1657  "0",
1658  "Alarms List Not Found",
1659  "N/a",
1660  "N/a",
1661  "N/a",
1662  "N/a",
1663  };
1664  }
1665 
1666  PQclear(res);
1667  }
1668  catch(...)
1669  {
1670  __SS__ << "getAlarmsLog(): FAILING GETTING DATA FROM ARCHIVER DATABASE!!! PQ "
1671  "ERROR: "
1672  << PQresultErrorMessage(res) << __E__;
1673  try
1674  {
1675  throw;
1676  } //one more try to printout extra info
1677  catch(const std::exception& e)
1678  {
1679  ss << "Exception message: " << e.what();
1680  }
1681  catch(...)
1682  {
1683  }
1684  __SS_THROW__;
1685  }
1686  }
1687  else
1688  {
1689  __SS__ << "getAlarmsLog(): ALARM LOG DATABASE CONNECTION FAILED!!! " << __E__;
1690  __SS_THROW__;
1691  }
1692  return alarmsHistory;
1693 } // end getAlarmsLog()
1694 
1695 //========================================================================================================================
1702 std::vector<std::string> EpicsInterface::checkAlarm(const std::string& pvName,
1703  bool ignoreMinor /*=false*/)
1704 {
1705  __COUT__ << "checkAlarm()" << __E__;
1706 
1707  auto pvIt = mapOfPVInfo_.find(pvName);
1708  if(pvIt == mapOfPVInfo_.end())
1709  {
1710  __SS__ << "While checking for alarm status, PV name '" << pvName
1711  << "' was not found in PV list!" << __E__;
1712  __SS_THROW__;
1713  }
1714 
1715  auto valueArray = getCurrentValue(pvIt->first);
1716 
1717  std::string& time = valueArray[0];
1718  std::string& value = valueArray[1];
1719  std::string& status = valueArray[2];
1720  std::string& severity = valueArray[3];
1721  __COUTV__(pvName);
1722  __COUTV__(time);
1723  __COUTV__(value);
1724  __COUTV__(status);
1725  __COUTV__(severity);
1726  if(severity == EPICS_NO_ALARM || (ignoreMinor && severity == EPICS_MINOR_ALARM))
1727  return std::vector<std::string>(); // empty vector, i.e. no alarm
1728 
1729  // if here, alarm!
1730  return std::vector<std::string>({pvIt->first, time, value, status, severity});
1731 } // end checkAlarm()
1732 
1733 //========================================================================================================================
1735 std::vector<std::vector<std::string>> EpicsInterface::checkAlarmNotifications()
1736 {
1737  std::vector<std::vector<std::string>> alarmReturn;
1738  std::vector<std::string> alarmRow;
1739  auto linkToAlarmsToNotify =
1740  getSelfNode().getNode("LinkToAlarmAlertNotificationsTable");
1741 
1742  if(!linkToAlarmsToNotify.isDisconnected())
1743  {
1744  auto alarmsToNotifyGroups = linkToAlarmsToNotify.getChildren();
1745 
1746  for(const auto& alarmsToNotifyGroup : alarmsToNotifyGroups)
1747  {
1748  __COUT__ << "checkAlarmNotifications() alarmsToNotifyGroup: "
1749  << alarmsToNotifyGroup.first << __E__;
1750 
1751  auto alarmsToNotify =
1752  alarmsToNotifyGroup.second.getNode("LinkToAlarmsToMonitorTable");
1753  if(!alarmsToNotify.isDisconnected())
1754  {
1755  for(const auto& alarmToNotify : alarmsToNotify.getChildren())
1756  {
1757  __COUT__ << "checkAlarmNotifications() alarmToNotify: "
1758  << alarmToNotify.first << __E__;
1759 
1760  try
1761  {
1762  alarmRow =
1763  checkAlarm(alarmToNotify.second.getNode("AlarmChannelName")
1764  .getValue<std::string>(),
1765  alarmToNotify.second.getNode("IgnoreMinorSeverity")
1766  .getValue<bool>());
1767  }
1768  catch(const std::exception& e)
1769  {
1770  __COUT__ << "checkAlarmNotifications() alarmToNotify: "
1771  << alarmToNotify.first << " not in PVs List!!!" << __E__;
1772  continue;
1773  }
1774  alarmRow.push_back(alarmToNotify.first);
1775  alarmRow.push_back(alarmsToNotifyGroup.second.getNode("WhoToNotify")
1776  .getValue<std::string>());
1777  alarmRow.push_back(alarmsToNotifyGroup.second.getNode("DoSendEmail")
1778  .getValue<std::string>());
1779  alarmRow.push_back(alarmsToNotifyGroup.first);
1780  alarmReturn.push_back(alarmRow);
1781  }
1782  }
1783  }
1784  }
1785  // __COUT__
1786  // << "checkAlarmNotifications().size(): "
1787  // << alarmReturn.size()
1788  // << " content:";
1789  // for (const auto& row : alarmReturn)
1790  // for(const auto& s : row)
1791  // __COUT__ << " " + s;
1792  // __COUT__<< __E__;
1793 
1794  return alarmReturn;
1795 } // end checkAlarmNotifications()
1796 
1797 //========================================================================================================================
1799 void EpicsInterface::handleAlarmsForFSM(const std::string& fsmTransitionName,
1800  ConfigurationTree linkToAlarmsToMonitor)
1801 {
1802  if(!linkToAlarmsToMonitor.isDisconnected())
1803  {
1804  auto alarmsToMonitor = linkToAlarmsToMonitor.getChildren();
1805 
1806  __SS__;
1807 
1808  ss << "During '" << fsmTransitionName
1809  << "'... Alarms monitoring (count=" << alarmsToMonitor.size() << "):" << __E__;
1810  for(const auto& alarmToMonitor : alarmsToMonitor)
1811  ss << "\t" << alarmToMonitor.first << __E__;
1812  ss << __E__;
1813 
1814  unsigned foundCount = 0;
1815  for(const auto& alarmToMonitor : alarmsToMonitor)
1816  {
1817  std::vector<std::string> alarmReturn = checkAlarm(
1818  alarmToMonitor.second.getNode("AlarmChannelName").getValue<std::string>(),
1819  alarmToMonitor.second.getNode("IgnoreMinorSeverity").getValue<bool>());
1820 
1821  if(alarmReturn.size())
1822  {
1823  ss << "Found alarm for channel '" << alarmReturn[0] << "' = {"
1824  << "time=" << alarmReturn[1] << ", value=" << alarmReturn[2]
1825  << ", status=" << alarmReturn[3] << ", severity=" << alarmReturn[4]
1826  << "}!" << __E__;
1827  ++foundCount;
1828  }
1829  }
1830  if(foundCount)
1831  {
1832  ss << __E__ << "Total alarms found = " << foundCount << __E__;
1833  __SS_THROW__;
1834  }
1835  __COUT__ << ss.str();
1836  }
1837  else
1838  __COUT__ << "Disconnected alarms to monitor!" << __E__;
1839 
1840 } // end handleAlarmsForFSM()
1841 
1842 //========================================================================================================================
1845 {
1846  handleAlarmsForFSM("configure",
1847  getSelfNode().getNode("LinkToConfigureAlarmsToMonitorTable"));
1848 
1849  __COUT__ << "configure(): Preparing EPICS for PVs..." << __E__;
1850 
1851  // Steps to update EPICS
1852  // 1. DTC_TABLE handles: scp *.dbg mu2edcs:mu2edaq01.fnal.gov:mu2e-dcs/apps/OTSReader/db/
1853  // 2. SQL insert or modify of ROW for PV
1854  // 3. force restart SW-IOC instance
1855  // 4. mark 'dirty' for EPICS cronjob restart or archiver and
1856 
1857  std::string
1858  slowControlsChannelsSourceTablesString = // "DTCInterfaceTable,CFOInterfaceTable"
1859  getSelfNode()
1860  .getNode("SlowControlsChannelSourceTableList")
1861  .getValueWithDefault<std::string>("");
1862 
1863  __COUTV__(slowControlsChannelsSourceTablesString);
1864 
1865  std::vector<std::string> slowControlsChannelsSourceTables =
1866  StringMacros::getVectorFromString(slowControlsChannelsSourceTablesString);
1867  __COUTV__(StringMacros::vectorToString(slowControlsChannelsSourceTables));
1868 
1869  for(const auto& slowControlsChannelsSourceTable : slowControlsChannelsSourceTables)
1870  {
1871  __COUTV__(slowControlsChannelsSourceTable);
1872 
1873  const SlowControlsTableBase* slowControlsTable =
1874  getConfigurationManager()->getTable<SlowControlsTableBase>(
1875  slowControlsChannelsSourceTable);
1876 
1877  if(slowControlsTable->slowControlsChannelListHasChanged())
1878  {
1879  __COUT__ << "configure(): Handling channel list change!" << __E__;
1880 
1881  std::vector<std::pair<std::string, std::vector<std::string>>> channels;
1882  slowControlsTable->getSlowControlsChannelList(channels);
1883 
1884  for(const auto& channel : channels)
1885  {
1886  std::string pvName = channel.first;
1887  std::string descr = channel.second.at(0);
1888  int grp_id = 4;
1889  int smpl_mode_id = 1;
1890  double smpl_val = 0.;
1891  double smpl_per = 60.;
1892  int retent_id = 9999;
1893  double retent_val = 9999.;
1894 
1895  double low_disp_rng = 0.;
1896  double high_disp_rng = 0.;
1897  double low_warn_lmt = atof(channel.second.at(1).c_str());
1898  double high_warn_lmt = atof(channel.second.at(2).c_str());
1899  double low_alarm_lmt = atof(channel.second.at(3).c_str());
1900  double high_alarm_lmt = atof(channel.second.at(4).c_str());
1901  int prec = atoi(channel.second.at(5).c_str());
1902  std::string unit = channel.second.at(6);
1903 
1904  if(!checkIfPVExists(pvName))
1905  {
1906  mapOfPVInfo_[pvName] = new PVInfo(DBR_STRING);
1907  __COUT__ << "configure(): new PV '" << pvName
1908  << "' found! Now subscribing" << __E__;
1909  subscribe(pvName);
1910  }
1911 
1912  if(dcsArchiveDbConnStatus_ == 1)
1913  {
1914  PGresult* res = nullptr;
1915  char buffer[1024];
1916  try
1917  {
1918  // ACTION FOR DB ARCHIVER CHANNEL TABLE
1919  snprintf(buffer,
1920  sizeof(buffer),
1921  "SELECT name FROM channel WHERE name = '%s';",
1922  pvName.c_str());
1923 
1924  res = PQexec(dcsArchiveDbConn, buffer);
1925  __COUT__ << "configure(): SELECT channel table PQntuples(res): "
1926  << PQntuples(res) << __E__;
1927 
1928  if(PQresultStatus(res) != PGRES_TUPLES_OK)
1929  {
1930  __SS__ << "configure(): SELECT FOR DATABASE CHANNEL TABLE "
1931  "FAILED!!! PV Name: "
1932  << pvName << " PQ ERROR: " << PQresultErrorMessage(res)
1933  << __E__;
1934  PQclear(res);
1935  __SS_THROW__;
1936  }
1937 
1938  if(PQntuples(res) > 0)
1939  {
1940  // UPDATE DB ARCHIVER CHANNEL TABLE
1941  PQclear(res);
1942  __COUT__ << "configure(): Updating PV: " << pvName
1943  << " in the Archiver Database channel table"
1944  << __E__;
1945  snprintf(buffer,
1946  sizeof(buffer),
1947  "UPDATE channel SET \
1948  grp_id=%d \
1949  , smpl_mode_id=%d \
1950  , smpl_val=%f \
1951  , smpl_per=%f \
1952  , retent_id=%d \
1953  , retent_val=%f \
1954  WHERE name = '%s';",
1955  grp_id,
1956  smpl_mode_id,
1957  smpl_val,
1958  smpl_per,
1959  retent_id,
1960  retent_val,
1961  pvName.c_str());
1962  //__COUT__ << "configure(): channel update select: " << buffer << __E__;
1963 
1964  res = PQexec(dcsArchiveDbConn, buffer);
1965 
1966  if(PQresultStatus(res) != PGRES_COMMAND_OK)
1967  {
1968  __SS__ << "configure(): CHANNEL UPDATE INTO DATABASE "
1969  "CHANNEL TABLE FAILED!!! PV Name: "
1970  << pvName
1971  << " PQ ERROR: " << PQresultErrorMessage(res)
1972  << __E__;
1973  PQclear(res);
1974  __SS_THROW__;
1975  }
1976  PQclear(res);
1977  }
1978  else
1979  {
1980  // INSERT INTO DB ARCHIVER CHANNEL TABLE
1981  PQclear(res);
1982  __COUT__ << "configure(): Writing new PV in the Archiver "
1983  "Database channel table"
1984  << __E__;
1985  snprintf(buffer,
1986  sizeof(buffer),
1987  "INSERT INTO channel( \
1988  name \
1989  , descr \
1990  , grp_id \
1991  , smpl_mode_id \
1992  , smpl_val \
1993  , smpl_per \
1994  , retent_id \
1995  , retent_val) \
1996  VALUES ('%s', '%s', %d, %d, %f, %f, %d, %f);",
1997  pvName.c_str(),
1998  descr.c_str(),
1999  grp_id,
2000  smpl_mode_id,
2001  smpl_val,
2002  smpl_per,
2003  retent_id,
2004  retent_val);
2005 
2006  res = PQexec(dcsArchiveDbConn, buffer);
2007  if(PQresultStatus(res) != PGRES_COMMAND_OK)
2008  {
2009  __SS__ << "configure(): CHANNEL INSERT INTO DATABASE "
2010  "CHANNEL TABLE FAILED!!! PV Name: "
2011  << pvName
2012  << " PQ ERROR: " << PQresultErrorMessage(res)
2013  << __E__;
2014  PQclear(res);
2015  __SS_THROW__;
2016  }
2017  PQclear(res);
2018  }
2019 
2020  // ACTION FOR DB ARCHIVER NUM_METADATA TABLE
2021  snprintf(buffer,
2022  sizeof(buffer),
2023  "SELECT channel.channel_id FROM channel, num_metadata "
2024  "WHERE channel.channel_id = num_metadata.channel_id AND "
2025  "channel.name = '%s';",
2026  pvName.c_str());
2027 
2028  res = PQexec(dcsArchiveDbConn, buffer);
2029  __COUT__
2030  << "configure(): SELECT num_metadata table PQntuples(res): "
2031  << PQntuples(res) << __E__;
2032 
2033  if(PQresultStatus(res) != PGRES_TUPLES_OK)
2034  {
2035  __SS__ << "configure(): SELECT FOR DATABASE NUM_METADATA "
2036  "TABLE FAILED!!! PV Name: "
2037  << pvName << " PQ ERROR: " << PQresultErrorMessage(res)
2038  << __E__;
2039  PQclear(res);
2040  __SS_THROW__;
2041  }
2042 
2043  if(PQntuples(res) > 0)
2044  {
2045  // UPDATE DB ARCHIVER NUM_METADATA TABLE
2046  std::string channel_id = PQgetvalue(res, 0, 0);
2047  __COUT__ << "configure(): Updating PV: " << pvName
2048  << " channel_id: " << channel_id
2049  << " in the Archiver Database num_metadata table"
2050  << __E__;
2051  PQclear(res);
2052  snprintf(buffer,
2053  sizeof(buffer),
2054  "UPDATE num_metadata SET \
2055  low_disp_rng=%f \
2056  , high_disp_rng=%f \
2057  , low_warn_lmt=%f \
2058  , high_warn_lmt=%f \
2059  , low_alarm_lmt=%f \
2060  , high_alarm_lmt=%f \
2061  , prec=%d \
2062  , unit='%s' \
2063  WHERE channel_id='%s';",
2064  low_disp_rng,
2065  high_disp_rng,
2066  low_warn_lmt,
2067  high_warn_lmt,
2068  low_alarm_lmt,
2069  high_alarm_lmt,
2070  prec,
2071  unit.c_str(),
2072  channel_id.c_str());
2073 
2074  res = PQexec(dcsArchiveDbConn, buffer);
2075  if(PQresultStatus(res) != PGRES_COMMAND_OK)
2076  {
2077  __SS__ << "configure(): CHANNEL UPDATE INTO DATABASE "
2078  "NUM_METADATA TABLE FAILED!!! PV "
2079  "Name(channel_id): "
2080  << pvName << " " << channel_id
2081  << " PQ ERROR: " << PQresultErrorMessage(res)
2082  << __E__;
2083  PQclear(res);
2084  __SS_THROW__;
2085  }
2086  PQclear(res);
2087  }
2088  else
2089  {
2090  // INSERT INTO DB ARCHIVER NUM_METADATA TABLE
2091  snprintf(buffer,
2092  sizeof(buffer),
2093  "SELECT channel_id FROM channel WHERE name = '%s';",
2094  pvName.c_str());
2095 
2096  res = PQexec(dcsArchiveDbConn, buffer);
2097  __COUT__
2098  << "configure(): SELECT channel table to check "
2099  "channel_id for num_metadata table. PQntuples(res): "
2100  << PQntuples(res) << __E__;
2101 
2102  if(PQresultStatus(res) != PGRES_TUPLES_OK)
2103  {
2104  __SS__ << "configure(): SELECT TO DATABASE CHANNEL TABLE "
2105  "FOR NUM_MATADATA TABLE FAILED!!! PV Name: "
2106  << pvName
2107  << " PQ ERROR: " << PQresultErrorMessage(res)
2108  << __E__;
2109  PQclear(res);
2110  __SS_THROW__;
2111  }
2112 
2113  if(PQntuples(res) > 0)
2114  {
2115  std::string channel_id = PQgetvalue(res, 0, 0);
2116  __COUT__ << "configure(): Writing new PV in the Archiver "
2117  "Database num_metadata table"
2118  << __E__;
2119  PQclear(res);
2120 
2121  snprintf(buffer,
2122  sizeof(buffer),
2123  "INSERT INTO num_metadata( \
2124  channel_id \
2125  , low_disp_rng \
2126  , high_disp_rng \
2127  , low_warn_lmt \
2128  , high_warn_lmt \
2129  , low_alarm_lmt \
2130  , high_alarm_lmt \
2131  , prec \
2132  , unit) \
2133  VALUES ('%s',%f,%f,%f,%f,%f,%f,%d,'%s');",
2134  channel_id.c_str(),
2135  low_disp_rng,
2136  high_disp_rng,
2137  low_warn_lmt,
2138  high_warn_lmt,
2139  low_alarm_lmt,
2140  high_alarm_lmt,
2141  prec,
2142  unit.c_str());
2143 
2144  res = PQexec(dcsArchiveDbConn, buffer);
2145  if(PQresultStatus(res) != PGRES_COMMAND_OK)
2146  {
2147  __SS__ << "configure(): CHANNEL INSERT INTO DATABASE "
2148  "NUM_METADATA TABLE FAILED!!! PV Name: "
2149  << pvName
2150  << " PQ ERROR: " << PQresultErrorMessage(res)
2151  << __E__;
2152  PQclear(res);
2153  __SS_THROW__;
2154  }
2155  PQclear(res);
2156  }
2157  else
2158  {
2159  __SS__ << "configure(): CHANNEL INSERT INTO DATABASE "
2160  "NUM_METADATA TABLE FAILED!!! PV Name: "
2161  << pvName << " NOT RECOGNIZED IN CHANNEL TABLE"
2162  << __E__;
2163  PQclear(res);
2164  __SS_THROW__;
2165  }
2166  }
2167  }
2168  catch(...)
2169  {
2170  __SS__ << "configure(): CHANNEL INSERT OR UPDATE INTO DATABASE "
2171  "FAILED!!! "
2172  << " PQ ERROR: " << PQresultErrorMessage(res) << __E__;
2173  try
2174  {
2175  throw;
2176  } //one more try to printout extra info
2177  catch(const std::exception& e)
2178  {
2179  ss << "Exception message: " << e.what();
2180  }
2181  catch(...)
2182  {
2183  }
2184  __SS_THROW__;
2185  }
2186  }
2187  else
2188  {
2189  // RAR 21-Dec-2022: remove exception throwing for cases when db connection not expected
2190  __COUT_INFO__ << "configure(): Archiver Database connection does not "
2191  "exist, so skipping channel update."
2192  << __E__;
2193  // __SS_THROW__;
2194  break;
2195  }
2196  } // end channel name loop
2197  }
2198  } // end slowControlsChannelsSourceTables loop
2199 } // end configure()
2200 
2201 DEFINE_OTS_SLOW_CONTROLS(EpicsInterface)
bool isDisconnected(void) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
std::vector< std::string > checkAlarm(const std::string &pvName, bool ignoreMinor=false)
virtual void configure(void) override
Configure override for Epics.
std::vector< std::vector< std::string > > checkAlarmNotifications(void) override
Check Alarms from Epics.
virtual bool slowControlsChannelListHasChanged(void) const
std::vector< std::pair< time_t, std::string > > dataCache
(10, std::pair<time_t, std::string> (0, ""));
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")