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