otsdaq  3.06.00
SlowControlsTableBase.cc
1 #include "otsdaq/TablePlugins/SlowControlsTableBase/SlowControlsTableBase.h"
2 #include "otsdaq/TablePlugins/XDAQContextTable/XDAQContextTable.h"
3 
4 #include <fstream> // std::fstream
5 
6 using namespace ots;
7 
8 #undef __MF_SUBJECT__
9 #define __MF_SUBJECT__ "SlowControlsTableBase"
10 
11 //==============================================================================
18  std::string* accumulatedExceptions /* =0 */)
19  : TableBase(tableName, accumulatedExceptions)
20 {
21  // December 2021 started seeing an issue where traceTID is found to be cleared to 0
22  // which crashes TRACE if __COUT__ is used in a Table plugin constructor
23  // This check and re-initialization seems to cover up the issue for now.
24  // Why it is cleared to 0 after the constructor sets it to -1 is still unknown.
25  // Note: it seems to only happen on the first alphabetially ARTDAQ Configure Table plugin.
26  if(traceTID == 0)
27  {
28  std::cout << "SlowControlsTableBase Before traceTID=" << traceTID << __E__;
29  char buf[40];
30  traceInit(trace_name(TRACE_NAME, __TRACE_FILE__, buf, sizeof(buf)), 0);
31  std::cout << "SlowControlsTableBase After traceTID=" << traceTID << __E__;
32  __COUT__ << "SlowControlsTableBase TRACE reinit and Constructed." << __E__;
33  }
34 } // end constuctor()
35 
36 //==============================================================================
40 {
41  __SS__ << "Should not call void constructor, table type is lost!" << __E__;
42  __SS_THROW__;
43 } // end illegal default constructor()
44 
45 //==============================================================================
46 SlowControlsTableBase::~SlowControlsTableBase(void) {} // end destructor()
47 
48 //==============================================================================
49 void SlowControlsTableBase::getSlowControlsChannelList(
50  std::vector<std::pair<std::string /*channelName*/, std::vector<std::string>>>&
51  channelList) const
52 {
53  outputEpicsPVFile(lastConfigManager_, &channelList);
54 } // end getSlowControlsChannelList()
55 
56 //==============================================================================
58 {
59  __COUT__ << "channelListHasChanged()" << __E__;
60 
61  if(lastConfigManager_ == nullptr)
62  {
63  __SS__ << "Illegal call to get status of channel list, no config manager has "
64  "been initialized!"
65  << __E__;
66  __SS_THROW__;
67  }
68 
69  // if here, lastConfigManager_ pointer is defined
70  bool changed = outputEpicsPVFile(lastConfigManager_);
71  __COUT__ << "slowControlsChannelListHasChanged(): return " << std::boolalpha
72  << std::to_string(changed) << __E__;
73  return changed;
74 } // end slowControlsChannelListHasChanged()
75 
76 //==============================================================================
77 unsigned int SlowControlsTableBase::slowControlsHandler(
78  std::stringstream& out,
79  std::string& tabStr,
80  std::string& commentStr,
81  std::string& subsystem,
82  std::string& location,
83  ConfigurationTree slowControlsLink,
84  std::vector<std::pair<std::string /*channelName*/, std::vector<std::string>>>*
85  channelList /*= 0*/
86 ) const
87 try
88 {
89  unsigned int numberOfChannels = 0;
90  __COUTS__(2) << "slowControlsHandler \n" << StringMacros::stackTrace() << __E__;
91 
92  if(!slowControlsLink.isDisconnected())
93  // if(1)
94  {
95  std::vector<std::pair<std::string, ConfigurationTree>> channelChildren =
96  slowControlsLink.getChildren();
97 
98  __COUTVS__(2, channelChildren.size());
99  // first do single bit binary fields
100  bool first = true;
101  for(auto& channel : channelChildren)
102  {
103  if(channel.second.getNode(channelColNames_.colChannelDataType_)
104  .getValue<std::string>() != "1b")
105  continue; // skip non-binary fields
106 
107  if(first) // if first, output header
108  {
109  first = false;
110  OUT << "file \"dbt/soft_bi.dbt\" {" << __E__;
111  PUSHTAB;
112  OUT << "pattern { Subsystem, loc, pvar, ZNAM, ONAM, ZSV, OSV, "
113  "COSV, DESC }"
114  << __E__;
115  PUSHTAB;
116  }
117 
118  ++numberOfChannels;
119 
120  std::string pvName = channel.first;
121  std::string comment =
122  channel.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
123  .getValue<std::string>();
124 
125  // output channel
126  OUT << "{ \"" << subsystem << "\", \"" << location << "\", \"" << pvName
127  << "\", \""
128  << "NOT_OK"
129  << "\", \""
130  << "OK"
131  << "\", \""
132  << "MAJOR"
133  << "\", \""
134  << "NO_ALARM"
135  << "\", \""
136  << ""
137  << "\", \"" << comment << "\" }" << __E__;
138 
139  } // end binary channel loop
140  if(!first) // if there was data, then pop tabs
141  {
142  POPTAB;
143  POPTAB;
144  out << "}" << __E__;
145  }
146 
147  // then do 'analog' fields
148  first = true;
149  for(auto& channel : channelChildren)
150  {
151  __COUTVS__(2, channel.first);
152  if(channel.second.getNode(channelColNames_.colChannelDataType_)
153  .getValue<std::string>() == "1b")
154  continue; // skip non-binary fields
155 
156  if(first) // if first, output header
157  {
158  first = false;
159  OUT << "file \"dbt/subst_ai.dbt\" {" << __E__;
160  PUSHTAB;
161  OUT << "pattern { Subsystem, loc, pvar, PREC, EGU, LOLO, LOW, "
162  "HIGH, HIHI, MDEL, ADEL, INP, SCAN, DTYP, DESC }"
163  << __E__;
164  PUSHTAB;
165  }
166 
167  ++numberOfChannels;
168 
169  std::string experimentName = "";
170  try
171  {
172  experimentName = __ENV__("OTS_EPICS_OWNER");
173  }
174  catch(...)
175  {
176  experimentName = __ENV__("OTS_OWNER");
177  }
178  __COUT__ << "experimentName has metricParameterValue: " << experimentName
179  << __E__;
180 
181  std::string pvName = channel.first;
182  std::string comment =
183  channel.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
184  .getValue<std::string>();
185  std::string precision = "0";
186  std::string units = channel.second.getNode(channelColNames_.colUnits_)
187  .getValue<std::string>();
188  // channel.second.getNode(channelColNames_.colChannelDataType_)
189  // .getValue<std::string>();
190  std::string low_alarm_lmt =
191  channel.second.getNode(channelColNames_.colLowLowThreshold_)
192  .getValueWithDefault<std::string>("-1000");
193  std::string low_warn_lmt =
194  channel.second.getNode(channelColNames_.colLowThreshold_)
195  .getValueWithDefault<std::string>("-100");
196  std::string high_warn_lmt =
197  channel.second.getNode(channelColNames_.colHighThreshold_)
198  .getValueWithDefault<std::string>("100");
199  std::string high_alarm_lmt =
200  channel.second.getNode(channelColNames_.colHighHighThreshold_)
201  .getValueWithDefault<std::string>("1000");
202  if(channelList != nullptr)
203  {
204  std::vector<std::string> pvSettings;
205  pvSettings.push_back(comment);
206  pvSettings.push_back(low_warn_lmt);
207  pvSettings.push_back(high_warn_lmt);
208  pvSettings.push_back(low_alarm_lmt);
209  pvSettings.push_back(high_alarm_lmt);
210  pvSettings.push_back(precision);
211  pvSettings.push_back(units);
212  channelList->push_back(std::make_pair(
213  experimentName + ":" + subsystem + ":" + location + ":" + pvName,
214  pvSettings));
215  }
216 
217  // output channel
218  OUT << "{ \"" << subsystem << "\", \"" << location << "\", \"" << pvName
219  << "\", \"" << precision // PREC
220  << "\", \"" << units << "\", \"" << low_alarm_lmt << "\", \""
221  << low_warn_lmt << "\", \"" << high_warn_lmt << "\", \"" << high_alarm_lmt
222  << "\", \""
223  << ""
224  << "\", \"" << // MDEL
225  ""
226  << "\", \"" << // ADEL
227  ""
228  << "\", \"" << // INP
229  ""
230  << "\", \"" << // SCAN
231  ""
232  << "\", \"" << // DTYP
233  comment << "\" }" << __E__;
234 
235  } // end binary channel loop
236  if(!first) // if there was data, then pop tabs
237  {
238  POPTAB;
239  POPTAB;
240  out << "}" << __E__;
241  }
242  }
243  else
244  __COUT__ << "Disconnected EventBuilder Slow Controls metric channels link, so "
245  "assuming "
246  "no slow controls channels."
247  << __E__;
248 
249  return numberOfChannels;
250 } // end localSlowControlsHandler
251 catch(const std::runtime_error& e)
252 {
253  __COUTV__(e.what());
254  throw;
255 }
256 
257 //==============================================================================
260  ConfigurationManager* configManager,
261  std::vector<std::pair<std::string /*channelName*/, std::vector<std::string>>>*
262  channelList /*= 0*/) const
263 {
264  /*
265  the file will look something like this:
266 
267  file name.template {
268  pattern { var1, var2, var3, ... }
269  { sub1_for_set1, sub2_for_set1, sub3_for_set1, ... }
270  { sub1_for_set2, sub2_for_set2, sub3_for_set2, ... }
271  { sub1_for_set3, sub2_for_set3, sub3_for_set3, ... }
272 
273  ...
274  }
275 
276  # for comment lines
277 
278  file "soft_ai.dbt" -- for floating point ("analog") data
279 
280  file "soft_bi.dbt" -- for binary values (on/off, good/bad, etc)
281 
282  file "soft_stringin.dbt" -- for string values (e.g. "states")
283 
284  Subsystem names:
285  https://docs.google.com/spreadsheets/d/1SO8R3O5Xm37X0JdaBiVmbg9p9aXy1Gk13uqiWFCchBo/edit#gid=1775059019
286  DTC maps to: CRV, Tracker, EMCal, STM, TEM
287 
288  Example lines:
289 
290  file "soft_ai.dbt" {
291  pattern { Subsystem, loc, var, PREC, EGU, LOLO, LOW, HIGH, HIHI, MDEL, ADEL,
292  DESC } { "TDAQ", "DataLogger", "RunNumber", "0", "", "-1e23", "-1e23", "1e23",
293  "1e23", "", "", "DataLogger run number" } { "TDAQ", "DataLogger", "AvgEvtSize",
294  "0", "MB/evt", "-1e23", "-1e23", "1e23", "1e23", "", "", "Datalogger avg event
295  size" }
296  }
297 
298  file "soft_bi.dbt" {
299  pattern { Subsystem, loc, pvar, ZNAM, ONAM, ZSV, OSV, COSV, DESC }
300  { "Computer", "daq01", "voltages_ok", "NOT_OK", "OK", "MAJOR",
301  "NO_ALARM", "", "voltages_ok daq01" } { "Computer", "daq02", "voltages_ok",
302  "NOT_OK", "OK", "MAJOR", "NO_ALARM", "", "voltages_ok daq02" }
303  }
304  */
305 
306  std::string filename = setFilePath();
307 
308  __COUT__ << "EPICS PV file: " << filename << __E__;
309 
310  std::string previousConfigFileContents;
311  {
312  std::FILE* fp = std::fopen(filename.c_str(), "rb");
313  if(fp)
314  {
315  std::fseek(fp, 0, SEEK_END);
316  previousConfigFileContents.resize(std::ftell(fp));
317  std::rewind(fp);
318  std::fread(
319  &previousConfigFileContents[0], 1, previousConfigFileContents.size(), fp);
320  std::fclose(fp);
321  }
322  else
323  __COUT_WARN__ << "Could not open EPICS IOC config file at " << filename
324  << __E__;
325 
326  } // done reading
327 
329  // generate xdaq run parameter file
330 
331  std::stringstream out;
332  unsigned int numberOfParameters =
333  slowControlsHandlerConfig(out, configManager, channelList);
334  __COUTVS__(2, numberOfParameters);
335 
336  // check if need to restart EPICS ioc
337  // if dbg string has changed, then mark ioc configuration dirty
338  if(previousConfigFileContents != out.str())
339  {
340  __COUT__ << "Configuration has changed! Marking dirty flag..." << __E__;
341 
342  // only write files if first app in context AND channelList is not passed, i.e. init() is only time we write!
343  // if(isFirstAppInContext_ && channelList == nullptr)
344  if(channelList == nullptr)
345  {
346  std::fstream fout;
347  fout.open(filename, std::fstream::out | std::fstream::trunc);
348  if(fout.fail())
349  {
350  __SS__ << "Failed to open EPICS PV file: " << filename << __E__;
351  __SS_THROW__;
352  }
353 
354  fout << out.str();
355  fout.close();
356 
357  std::string csvFilename = filename.substr(0, filename.length() - 3) + "csv";
358  fout.open(csvFilename, std::fstream::out | std::fstream::trunc);
359  if(fout.fail())
360  {
361  __SS__ << "Failed to open CSV EPICS PV file: " << filename << __E__;
362  __SS_THROW__;
363  }
364 
365  std::string csvOut = out.str();
366  // if (csvOut.find("file \"dbt/subst_ai.dbt\" {\n") != std::string::npos) csvOut = csvOut.replace(csvOut.find("file \"dbt/subst_ai.dbt\" {\n"), 26,
367  // "");
368  csvOut.erase(0, csvOut.find("\n") + 1);
369  if(csvOut.find("pattern {") != std::string::npos)
370  csvOut = csvOut.replace(csvOut.find("pattern {"), 10, "");
371  while(csvOut.find("{") != std::string::npos)
372  csvOut = csvOut.replace(csvOut.find("{"), 1, "");
373  while(csvOut.find("}") != std::string::npos)
374  csvOut = csvOut.replace(csvOut.find("}"), 1, "");
375  while(csvOut.find("\"") != std::string::npos)
376  csvOut = csvOut.replace(csvOut.find("\""), 1, "");
377  while(csvOut.find(" ") != std::string::npos)
378  csvOut = csvOut.replace(csvOut.find(" "), 1, "");
379  while(csvOut.find("\t") != std::string::npos)
380  csvOut = csvOut.replace(csvOut.find("\t"), 1, "");
381 
382  fout << csvOut.substr(0, csvOut.length() - 1);
383  fout.close();
384 
385  std::FILE* fp = fopen(EPICS_DIRTY_FILE_PATH.c_str(), "w");
386  if(fp)
387  {
388  fprintf(fp, "1"); // set dirty flag
389  fclose(fp);
390  }
391  else
392  __COUT_WARN__ << "Could not open dirty file: " << EPICS_DIRTY_FILE_PATH
393  << __E__;
394  }
395 
396  // Indicate that PV list has changed
397  // if otsdaq_epics plugin is listening, then write PV data to archive db: SQL insert or modify of ROW for PV
398  __COUT__ << "outputEpicsPVFile() return true" << __E__;
399  return true;
400  } // end handling of previous contents
401  __COUT__ << "outputEpicsPVFile() return false" << __E__;
402  return false;
403 } // end outputEpicsPVFile()
virtual bool outputEpicsPVFile(ConfigurationManager *configManager, std::vector< std::pair< std::string, std::vector< std::string >>> *channelList=0) const
use table name to have different file names! (instead of DEFINES like in DTC)
virtual bool slowControlsChannelListHasChanged(void) const
Getters.
defines used also by OtsConfigurationWizardSupervisor
static std::string stackTrace(void)