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