otsdaq  v2_05_02_indev
SlowControlsTableBase.cc
1 #include "otsdaq/TablePlugins/SlowControlsTableBase/SlowControlsTableBase.h"
2 #include "otsdaq/TablePlugins/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 
12 //==============================================================================
13 // TableBase
14 // If a valid string pointer is passed in accumulatedExceptions
15 // then allowIllegalColumns is set for InfoReader
16 // If accumulatedExceptions pointer = 0, then illegal columns throw std::runtime_error
17 // exception
18 SlowControlsTableBase::SlowControlsTableBase(std::string tableName, std::string* accumulatedExceptions /* =0 */)
19 : TableBase(tableName,accumulatedExceptions)
20 {
21 } // end constuctor()
22 
23 //==============================================================================
24 // SlowControlsTableBase
25 // Default constructor should never be used because table type is lost
26 SlowControlsTableBase::SlowControlsTableBase(void)
27 : TableBase("SlowControlsTableBase")
28 {
29  __SS__ << "Should not call void constructor, table type is lost!" << __E__;
30  __SS_THROW__;
31 } // end illegal default constructor()
32 
33 //==============================================================================
34 SlowControlsTableBase::~SlowControlsTableBase(void) {} // end destructor()
35 
36 //==============================================================================
37 void SlowControlsTableBase::getSlowControlsChannelList(
38  std::vector<std::pair<std::string /*channelName*/, std::vector<std::string>>>&
39  channelList) const
40 {
41  outputEpicsPVFile(lastConfigManager_,&channelList);
42 } //end getSlowControlsChannelList()
43 
44 //==============================================================================
45 bool SlowControlsTableBase::slowControlsChannelListHasChanged(void) const
46 {
47  __COUT__ << "channelListHasChanged()" << __E__;
48  if(isFirstAppInContext_)
49  return channelListHasChanged_;
50 
51  if(lastConfigManager_ == nullptr)
52  {
53  __SS__ << "Illegal call to get status of channel list, no config manager has been initialized!" << __E__;
54  __SS_THROW__;
55  }
56 
57  //if here, lastConfigManager_ pointer is defined
58  bool changed = outputEpicsPVFile(lastConfigManager_);
59  __COUT__ << "slowControlsChannelListHasChanged(): return " << std::boolalpha << std::to_string(changed) << __E__;
60  return changed;
61 } //end slowControlsChannelListHasChanged()
62 
63 //==============================================================================
64 unsigned int SlowControlsTableBase::slowControlsHandler(std::stringstream& out,
65  std::string& tabStr,
66  std::string& commentStr,
67  std::string& subsystem,
68  std::string& location,
69  ConfigurationTree slowControlsLink,
70  std::vector<std::pair<std::string /*channelName*/, std::vector<std::string>>>* channelList /*= 0*/
71 ) const
72 {
73  unsigned int numberOfChannels = 0;
74  __COUT__ << "slowControlsHandler" << __E__;
75 
76  if(!slowControlsLink.isDisconnected())
77  //if(1)
78  {
79  std::vector<std::pair<std::string, ConfigurationTree>> channelChildren = slowControlsLink.getChildren();
80 
81  // first do single bit binary fields
82  bool first = true;
83  for(auto& channel : channelChildren)
84  {
85  if(channel.second.getNode(channelColNames_.colChannelDataType_).getValue<std::string>() != "1b")
86  continue; // skip non-binary fields
87 
88  if(first) // if first, output header
89  {
90  first = false;
91  OUT << "file \"dbt/soft_bi.dbt\" {" << __E__;
92  PUSHTAB;
93  OUT << "pattern { Subsystem, loc, pvar, ZNAM, ONAM, ZSV, OSV, "
94  "COSV, DESC }"
95  << __E__;
96  PUSHTAB;
97  }
98 
99  ++numberOfChannels;
100 
101  std::string pvName = channel.first;
102  std::string comment = channel.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT).getValue<std::string>();
103 
104  // output channel
105  OUT << "{ \"" << subsystem << "\", \"" << location << "\", \"" << pvName << "\", \""
106  << "NOT_OK"
107  << "\", \""
108  << "OK"
109  << "\", \""
110  << "MAJOR"
111  << "\", \""
112  << "NO_ALARM"
113  << "\", \""
114  << ""
115  << "\", \"" << comment << "\" }" << __E__;
116 
117  } // end binary channel loop
118  if(!first) // if there was data, then pop tabs
119  {
120  POPTAB;
121  POPTAB;
122  out << "}" << __E__;
123  }
124 
125  // then do 'analog' fields
126  first = true;
127  for(auto& channel : channelChildren)
128  {
129  if(channel.second.getNode(channelColNames_.colChannelDataType_).getValue<std::string>() == "1b")
130  continue; // skip non-binary fields
131 
132  if(first) // if first, output header
133  {
134  first = false;
135  OUT << "file \"dbt/subst_ai.dbt\" {" << __E__;
136  PUSHTAB;
137  OUT << "pattern { Subsystem, loc, pvar, PREC, EGU, LOLO, LOW, "
138  "HIGH, HIHI, MDEL, ADEL, INP, SCAN, DTYP, DESC }"
139  << __E__;
140  PUSHTAB;
141  }
142 
143  ++numberOfChannels;
144 
145  std::string pvName = channel.first;
146  std::string comment = channel.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT).getValue<std::string>();
147  std::string precision = "0";
148  std::string units = channel.second.getNode(channelColNames_.colUnits_).getValue<std::string>();
149  // channel.second.getNode(channelColNames_.colChannelDataType_)
150  // .getValue<std::string>();
151  std::string low_alarm_lmt = channel.second.getNode(channelColNames_.colLowLowThreshold_).getValueWithDefault<std::string>("-1000");
152  std::string low_warn_lmt = channel.second.getNode(channelColNames_.colLowThreshold_).getValueWithDefault<std::string>("-100");
153  std::string high_warn_lmt = channel.second.getNode(channelColNames_.colHighThreshold_).getValueWithDefault<std::string>("100");
154  std::string high_alarm_lmt = channel.second.getNode(channelColNames_.colHighHighThreshold_).getValueWithDefault<std::string>("1000");
155  if(channelList != nullptr)
156  {
157  std::vector<std::string> pvSettings;
158  pvSettings.push_back(comment);
159  pvSettings.push_back(low_warn_lmt);
160  pvSettings.push_back(high_warn_lmt);
161  pvSettings.push_back(low_alarm_lmt);
162  pvSettings.push_back(high_alarm_lmt);
163  pvSettings.push_back(precision);
164  pvSettings.push_back(units);
165  channelList->push_back(std::make_pair("Mu2e:" + subsystem + ":" + location + ":" + pvName, pvSettings));
166  }
167 
168  // output channel
169  OUT << "{ \"" << subsystem << "\", \"" << location << "\", \"" << pvName << "\", \"" << precision // PREC
170  << "\", \"" << units << "\", \"" << low_alarm_lmt << "\", \"" << low_warn_lmt << "\", \"" << high_warn_lmt << "\", \"" << high_alarm_lmt
171  << "\", \""
172  << ""
173  << "\", \"" << // MDEL
174  ""
175  << "\", \"" << // ADEL
176  ""
177  << "\", \"" << // INP
178  ""
179  << "\", \"" << // SCAN
180  ""
181  << "\", \"" << // DTYP
182  comment << "\" }" << __E__;
183 
184  } // end binary channel loop
185  if(!first) // if there was data, then pop tabs
186  {
187  POPTAB;
188  POPTAB;
189  out << "}" << __E__;
190  }
191  }
192  else
193  __COUT__ << "Disconnected EventBuilder Slow Controls metric channels link, so assuming "
194  "no slow controls channels."
195  << __E__;
196 
197  return numberOfChannels;
198 } // end localSlowControlsHandler
199 
200 //==============================================================================
201 // return channel list if pointer passed
202 bool SlowControlsTableBase::outputEpicsPVFile(ConfigurationManager* configManager,
203  std::vector<std::pair<std::string /*channelName*/, std::vector<std::string>>>* channelList /*= 0*/) const
204 {
205  /*
206  the file will look something like this:
207 
208  file name.template {
209  pattern { var1, var2, var3, ... }
210  { sub1_for_set1, sub2_for_set1, sub3_for_set1, ... }
211  { sub1_for_set2, sub2_for_set2, sub3_for_set2, ... }
212  { sub1_for_set3, sub2_for_set3, sub3_for_set3, ... }
213 
214  ...
215  }
216 
217  # for comment lines
218 
219  file "soft_ai.dbt" -- for floating point ("analog") data
220 
221  file "soft_bi.dbt" -- for binary values (on/off, good/bad, etc)
222 
223  file "soft_stringin.dbt" -- for string values (e.g. "states")
224 
225  Subsystem names:
226  https://docs.google.com/spreadsheets/d/1SO8R3O5Xm37X0JdaBiVmbg9p9aXy1Gk13uqiWFCchBo/edit#gid=1775059019
227  DTC maps to: CRV, Tracker, EMCal, STM, TEM
228 
229  Example lines:
230 
231  file "soft_ai.dbt" {
232  pattern { Subsystem, loc, var, PREC, EGU, LOLO, LOW, HIGH, HIHI, MDEL, ADEL,
233  DESC } { "TDAQ", "DataLogger", "RunNumber", "0", "", "-1e23", "-1e23", "1e23",
234  "1e23", "", "", "DataLogger run number" } { "TDAQ", "DataLogger", "AvgEvtSize",
235  "0", "MB/evt", "-1e23", "-1e23", "1e23", "1e23", "", "", "Datalogger avg event
236  size" }
237  }
238 
239  file "soft_bi.dbt" {
240  pattern { Subsystem, loc, pvar, ZNAM, ONAM, ZSV, OSV, COSV, DESC }
241  { "Computer", "daq01", "voltages_ok", "NOT_OK", "OK", "MAJOR",
242  "NO_ALARM", "", "voltages_ok daq01" } { "Computer", "daq02", "voltages_ok",
243  "NOT_OK", "OK", "MAJOR", "NO_ALARM", "", "voltages_ok daq02" }
244  }
245  */
246 
247  std::string filename = setFilePath();
248 
249  __COUT__ << "EPICS PV file: " << filename << __E__;
250 
251  std::string previousConfigFileContents;
252  {
253  std::FILE* fp = std::fopen(filename.c_str(), "rb");
254  if(fp)
255  {
256  std::fseek(fp, 0, SEEK_END);
257  previousConfigFileContents.resize(std::ftell(fp));
258  std::rewind(fp);
259  std::fread(&previousConfigFileContents[0], 1, previousConfigFileContents.size(), fp);
260  std::fclose(fp);
261  }
262  else
263  __COUT_WARN__ << "Could not open EPICS IOC config file at " << filename << __E__;
264 
265  } // done reading
266 
268  // generate xdaq run parameter file
269 
270  std::stringstream out;
271  unsigned int numberOfParameters = slowControlsHandlerConfig(out, configManager, channelList);
272  __COUTV__(numberOfParameters);
273 
274  // check if need to restart EPICS ioc
275  // if dbg string has changed, then mark ioc configuration dirty
276  if(previousConfigFileContents != out.str())
277  {
278  __COUT__ << "Configuration has changed! Marking dirty flag..." << __E__;
279 
280  // only write files if first app in context AND channelList is not passed, i.e. init() is only time we write!
281  // if(isFirstAppInContext_ && channelList == nullptr)
282  if(channelList == nullptr)
283  {
284  std::fstream fout;
285  fout.open(filename, std::fstream::out | std::fstream::trunc);
286  if(fout.fail())
287  {
288  __SS__ << "Failed to open EPICS PV file: " << filename << __E__;
289  __SS_THROW__;
290  }
291 
292  fout << out.str();
293  fout.close();
294 
295  std::FILE* fp = fopen(EPICS_DIRTY_FILE_PATH.c_str(), "w");
296  if(fp)
297  {
298  fprintf(fp, "1"); // set dirty flag
299  fclose(fp);
300  }
301  else
302  __COUT_WARN__ << "Could not open dirty file: " << EPICS_DIRTY_FILE_PATH << __E__;
303  }
304 
305  // Indicate that PV list has changed
306  // if otsdaq_epics plugin is listening, then write PV data to archive db: SQL insert or modify of ROW for PV
307  __COUT__ << "outputEpicsPVFile() return true" << __E__;
308  return true;
309  } // end handling of previous contents
310  __COUT__ << "outputEpicsPVFile() return false" << __E__;
311  return false;
312 } // end outputEpicsPVFile()