artdaq_epics_plugin  1.05.06
epics_metric.cc
1 // epics_metric.cc: Epics Metric Plugin
2 // Author: Eric Flumerfelt
3 // Last Modified: 09/25/2015
4 //
5 // An implementation of the MetricPlugin interface for Epics/ChannelAccess
6 
7 #ifndef __EPICS_METRIC__
8 #define __EPICS_METRIC__ 1
9 
10 #include "TRACE/tracemf.h" // order matters -- trace.h (no "mf") is nested from MetricMacros.hh
11 #define TRACE_NAME (app_name_ + "_epics_metric").c_str()
12 
13 #include <unordered_map>
14 #include <utility>
15 #include "artdaq-utilities/Plugins/MetricMacros.hh"
16 #include "messagefacility/MessageLogger/MessageLogger.h"
17 #undef STATIC_ASSERT
18 
19 #ifdef __clang__
20 #pragma clang diagnostic push
21 #pragma clang diagnostic ignored "-Wunused-parameter"
22 #elif __GNUC__ > 9
23 #pragma GCC diagnostic push
24 #pragma GCC diagnostic ignored "-Wdeprecated-copy"
25 #endif
26 #include <cadef.h>
27 #ifdef __clang__
28 #pragma clang diagnostic pop
29 #elif __GNUC__ > 9
30 #pragma GCC diagnostic pop
31 #endif
32 
36 namespace artdaq {
40 class EpicsMetric : public MetricPlugin
41 {
42 private:
43  std::string prefix_;
44  std::unordered_map<std::string, chid> channels_;
45 
46  bool checkChannel_(const std::string& name)
47  {
48  if (channels_.count(name) == 0u)
49  {
50  chid channel;
51  ca_search(name.c_str(), &channel);
52  auto sts = ca_pend_io(5.0);
53  if (sts != ECA_NORMAL)
54  {
55  SEVCHK(ca_clear_channel(channel), NULL);
56  METLOG(TLVL_WARNING) << "Channel \"" << name << "\" not found!";
57  channels_[name] = nullptr;
58  return false;
59  }
60  channels_[name] = channel;
61  return true;
62  }
63  return channels_[name] != nullptr;
64  }
65 
66  std::string parseChannelName_(std::string prefix_, std::string name)
67  {
68  std::string caName = std::move(name);
69  const std::string& caPrefix_ = std::move(prefix_);
70 
71  while (caName.find(' ') != std::string::npos)
72  {
73  caName = caName.replace(caName.find(' '), 1, "_");
74  }
75  while (caName.find('.') != std::string::npos)
76  {
77  caName = caName.replace(caName.find('.'), 1, ":");
78  }
79  while (caName.find('/') != std::string::npos)
80  {
81  caName = caName.replace(caName.find('/'), 1, "_");
82  }
83  while (caName.find("_%") != std::string::npos)
84  {
85  caName = caName.replace(caName.find("_%"), 2, "");
86  }
87  caName = caPrefix_ + ":" + caName;
88 
89  METLOG(TLVL_DEBUG + 32) << "Channel name is: \"" << caName << "\"";
90  return caName;
91  }
92 
93  EpicsMetric(EpicsMetric const&) = delete;
94  EpicsMetric(EpicsMetric&&) = delete;
95  EpicsMetric& operator=(EpicsMetric const&) = delete;
96  EpicsMetric& operator=(EpicsMetric&&) = delete;
97 
98 public:
105  explicit EpicsMetric(fhicl::ParameterSet const& pset, std::string const& app_name, std::string const& metric_name)
106  : MetricPlugin(pset, app_name, metric_name), prefix_(pset.get<std::string>("channel_name_prefix", "artdaq")), channels_() {
107  METLOG(TLVL_DEBUG + 30) << "EpicsMetric CONSTRUCTOR";
108  }
109 
110  ~EpicsMetric() override {
111  TLOG(TLVL_INFO) << "EPICS Metric Destructor";
112  MetricPlugin::stopMetrics(); }
113 
118  std::string getLibName() const override { return "epics"; }
119 
123  void stopMetrics_() override
124  {
125  TLOG(TLVL_INFO) << "EpicsMetric::stopMetrics_";
126  for (const auto& channel : channels_)
127  {
128  if (channel.second != 0)
129  {
130  TLOG(TLVL_INFO) << "Clearing channel " << channel.first;
131  SEVCHK(ca_clear_channel(channel.second), NULL);
132  }
133  }
134  channels_.clear();
135  ca_context_destroy();
136  }
137 
141  void startMetrics_() override {
142  SEVCHK(ca_context_create(ca_enable_preemptive_callback), NULL);
143  }
144 
155  void sendMetric_(const std::string& name, const std::string& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
156  {
157  //std::string caName = prefix_ + ":" + name;
158  std::string caName = parseChannelName_(prefix_, name);
159 
160  std::string tmpValue = value + " " + unit;
161 
162  if (checkChannel_(caName))
163  {
164  // DBR_STRING, 40 characters
165  if (tmpValue.size() > 40)
166  {
167  tmpValue = tmpValue.erase(40);
168  }
169  TLOG(TLVL_DEBUG) << "Putting value " << tmpValue << " into " << caName;
170  SEVCHK(ca_put(DBR_STRING, channels_[caName], tmpValue.c_str()), NULL);
171  SEVCHK(ca_flush_io(), NULL);
172  }
173  }
174 
185  void sendMetric_(const std::string& name, const int& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
186  {
187  // DBR_LONG
188  //std::string caName = prefix_ + ":" + name;
189  std::string caName = parseChannelName_(prefix_, name);
190 
191  if (!unit.empty())
192  {
193  METLOG(TLVL_DEBUG + 32) << "Not sure if I can send ChannelAccess Units...configure in db instead.";
194  }
195 
196  if (checkChannel_(caName))
197  {
198  auto val = static_cast<dbr_long_t>(value);
199  TLOG(TLVL_DEBUG) << "Putting value " << value << " into " << caName;
200  SEVCHK(ca_put(DBR_LONG, channels_[caName], &val), NULL);
201  SEVCHK(ca_flush_io(), NULL);
202  }
203  }
204 
215  void sendMetric_(const std::string& name, const double& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
216  {
217  // DBR_DOUBLE
218  //std::string caName = prefix_ + ":" + name;
219  std::string caName = parseChannelName_(prefix_, name);
220 
221  if (!unit.empty())
222  {
223  METLOG(TLVL_DEBUG + 32) << "Not sure if I can send ChannelAccess Units...configure in db instead.";
224  }
225 
226  if (checkChannel_(caName))
227  {
228  auto val = static_cast<dbr_double_t>(value);
229  TLOG(TLVL_DEBUG) << "Putting value " << value << " into " << caName;
230  SEVCHK(ca_put(DBR_DOUBLE, channels_[caName], &val), NULL);
231  SEVCHK(ca_flush_io(), NULL);
232  }
233  }
234 
245  void sendMetric_(const std::string& name, const float& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
246  {
247  // DBR_FLOAT
248  //std::string caName = prefix_ + ":" + name;
249  std::string caName = parseChannelName_(prefix_, name);
250 
251  if (!unit.empty())
252  {
253  METLOG(TLVL_DEBUG + 32) << "Not sure if I can send ChannelAccess Units...configure in db instead.";
254  }
255 
256  if (checkChannel_(caName))
257  {
258  auto val = static_cast<dbr_float_t>(value);
259  TLOG(TLVL_DEBUG) << "Putting value " << value << " into " << caName;
260  SEVCHK(ca_put(DBR_FLOAT, channels_[caName], &val), NULL);
261  SEVCHK(ca_flush_io(), NULL);
262  }
263  }
264 
275  void sendMetric_(const std::string& name, const uint64_t& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
276  {
277  // DBR_LONG, only unsigned type is only 16 bits, use widest integral field
278  //std::string caName = prefix_ + ":" + name;
279  std::string caName = parseChannelName_(prefix_, name);
280 
281  if (!unit.empty())
282  {
283  METLOG(TLVL_DEBUG + 32) << "Not sure if I can send ChannelAccess Units...configure in db instead.";
284  }
285 
286  if (checkChannel_(caName))
287  {
288  auto val = static_cast<dbr_ulong_t>(value);
289  TLOG(TLVL_DEBUG) << "Putting value " << value << " into " << caName;
290  SEVCHK(ca_put(DBR_LONG, channels_[caName], &val), NULL);
291  SEVCHK(ca_flush_io(), NULL);
292  }
293  }
294 };
295 } // End namespace artdaq
296 
297 DEFINE_ARTDAQ_METRIC(artdaq::EpicsMetric)
298 
299 #endif // End ifndef __EPICS_METRIC__
An instance of the MetricPlugin class that sends metric data using the Channel Access protocol from E...
Definition: epics_metric.cc:40
void stopMetrics_() override
Clears the registered ChannelAccess channels.
void startMetrics_() override
No initialization is needed to start sending metrics.
std::string getLibName() const override
Gets the unique library name of this plugin.
void sendMetric_(const std::string &name, const std::string &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send a string metric data point to ChannelAccess.
void sendMetric_(const std::string &name, const float &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send a float metric data point to ChannelAccess.
EpicsMetric(fhicl::ParameterSet const &pset, std::string const &app_name, std::string const &metric_name)
Construct an instance of the EpicsMetric plugin.
void sendMetric_(const std::string &name, const double &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send a double metric data point to ChannelAccess.
void sendMetric_(const std::string &name, const uint64_t &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send an unsigned integer metric data point to ChannelAccess.
void sendMetric_(const std::string &name, const int &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send an integer metric data point to ChannelAccess.