artdaq_mfextensions  v1_06_02
ma_rule.cpp
1 
2 #include "ErrorHandler/MessageAnalyzer/ma_rule.h"
3 #include "ErrorHandler/MessageAnalyzer/ma_parse.h"
4 
5 #include <iostream>
6 
7 using fhicl::ParameterSet;
8 using namespace novadaq::errorhandler;
9 
10 ma_rule::ma_rule(std::string const& rule_name, std::string const& rule_desc, bool repeat, int holdoff_time)
11  : conditions()
12  , conditions_idx()
13  , primitive_cond()
14  , cond_map(NULL)
15  , name_(rule_name)
16  , description_(rule_desc)
17  , condition_expr()
18  , alarm_count(0)
19  , cond_names_()
20  , alarm_msg()
21  , boolean_expr()
22  , domain_expr()
23  , domains()
24  , alarms()
25  , itor_last_alarm(alarms.end())
26  , repeat_alarm(repeat)
27  , holdoff(holdoff_time)
28  , initialized(false)
29  , enabled(true)
30  , actions()
31 {
32 }
33 
34 void ma_rule::parse(std::string const& cond_expr, std::string const& alarm_message, ParameterSet const& act_pset, cond_map_t* cond_map_ptr)
35 {
36  cond_map = cond_map_ptr;
37  condition_expr = cond_expr;
38 
39  // condition expression
40  if (!parse_condition_expr(cond_expr, this))
41  throw std::runtime_error("rule parsing failed");
42 
43  // alarm message
44  alarm_msg.init(this, alarm_message);
45 
46  // actions
47  std::vector<std::string> keys = act_pset.get_pset_names();
48  for (size_t i = 0; i < keys.size(); ++i)
49  {
50  ParameterSet param = act_pset.get<ParameterSet>(keys[i]);
51  actions.push_back(ma_action_factory::create_instance(keys[i], this, param));
52  }
53 
54  // init
55  if (domain_expr.empty())
56  domains.push_back(ma_domain_ctor_any(conditions.size()));
57 
58  initialized = true;
59 }
60 
61 cond_idx_t
62 ma_rule::insert_condition_ptr(std::string const& name, bool primitive)
63 {
64  // cond_map must not be empty
65  assert(cond_map != NULL);
66 
67  TLOG(TLVL_DEBUG) << "insert_cond_ptr: name = " << name << " "
68  << "primitive = " << primitive;
69 
70  // the condition has already been added
71  {
72  idx_t::const_iterator it = conditions_idx.find(name);
73  if (it != conditions_idx.end())
74  {
75  // primitive cond overrides non-primitive cond
76  primitive_cond[it->second] = primitive_cond[it->second] | primitive;
77  return cond_idx_t(conditions[it->second], it->second);
78  ;
79  }
80  }
81 
82  // look for the cond in the rule_engine container
83  cond_map_t::iterator it = cond_map->find(name);
84 
85  if (it == cond_map->end()) // name not found
86  throw std::runtime_error("insert_cond_ptr: condition " + name + " not found");
87 
88  // put this rule to the status notification list of the condition
89  it->second.push_notify_status(this);
90 
91  // register the condition in the rule
92  cond_names_.push_back(name);
93  conditions.push_back(&it->second);
94  primitive_cond.push_back(primitive);
95 
96  assert(conditions.size() == primitive_cond.size());
97  size_t idx = conditions.size() - 1;
98 
99  conditions_idx.insert(std::make_pair(name, idx));
100 
101  return cond_idx_t(&it->second, idx);
102 }
103 
104 void ma_rule::evaluate_domain()
105 {
106  assert(initialized);
107 
108  // re-evaluate domains
109  domains.clear();
110  domain_expr.evaluate(domains);
111 
112  TLOG(TLVL_DEBUG) << description_
113  << ": domain evaluated, size = " << domains.size();
114 }
115 
116 bool ma_rule::recursive_evaluate(ma_domain& value, ma_domain& alarm, ma_domain const& domain, size_t n)
117 {
118  // pre-conditions
119  assert(!domain_is_null(domain[n]));
120 
121  // get range
122  ma_cond_range src(D_NIL, D_NIL);
123  ma_cond_range target(D_NIL, D_NIL);
124 
125  // a primitive condition (Cn) or non-primitive ( COUNT(Cn.$s|t) )
126  // we only loop through possible values for primitive conditions
127  if (primitive_cond[n])
128  conditions[n]->get_cond_range(domain[n], src, target);
129 
130  TLOG(TLVL_DEBUG) << "depth: " << n << " "
131  << "primitive_cond[n]: " << primitive_cond[n];
132 
133  for (int s = src.first; s <= src.second; ++s)
134  {
135  for (int t = target.first; t <= target.second; ++t)
136  {
137  value[n].first = s;
138  value[n].second = t;
139 
140  TLOG(TLVL_DEBUG) << "depth: " << n << " "
141  << "src: " << s << " "
142  << "tgt: " << t;
143 
144  if (n != domain.size() - 1)
145  {
146  if (recursive_evaluate(value, alarm, domain, n + 1))
147  return true;
148  }
149  else
150  {
151  // evaluate and, if found new alarm, no need to continue
152  if (boolean_evaluate(value, alarm, domain))
153  return true;
154  }
155  }
156  }
157 
158  return false;
159 }
160 
161 bool ma_rule::evaluate()
162 {
163  assert(initialized);
164 
165  // if disabled, always returns false
166  if (!enabled) return false;
167 
168  TLOG(TLVL_DEBUG) << description_ << ": evaluate boolean expr...";
169 
170  // loop through domain alternatives
171  for (ma_domains::const_iterator ait = domains.begin(); ait != domains.end(); ++ait)
172  {
173  // each domain consists a set of possible values for each condition
174  ma_domain const& domain = *ait;
175 
176  // pre-condition
177  assert(!domain_is_null(domain));
178 
179  // holds the one possible set of value
180  ma_domain value = ma_domain_ctor_null(domain.size());
181  ma_domain alarm = ma_domain_ctor_null(domain.size());
182 
183  // recursively build and evaluate possible values from domain
184  if (recursive_evaluate(value, alarm, domain, 0))
185  return true;
186  }
187 
188  return false;
189 }
190 
191 bool ma_rule::boolean_evaluate(ma_domain& value, ma_domain& alarm, ma_domain const& domain)
192 {
193  TLOG(TLVL_DEBUG) << "now evaluate boolean_expr with given value";
194 
195  // evaluate as true with given set of values
196  if (boolean_expr.evaluate(value, alarm, domain))
197  {
198  TLOG(TLVL_DEBUG) << "alarm (" << alarm[0].first << ", "
199  << alarm[0].second << ")";
200 
201  std::map<ma_domain, timeval>::iterator it = alarms.find(alarm);
202  if (it == alarms.end())
203  {
204  // this is a new alarm. push the current domain to alarm list
205  timeval tv;
206  gettimeofday(&tv, 0);
207  itor_last_alarm = alarms.insert(std::make_pair(alarm, tv)).first;
208 
209  alarm.clear();
210  alarm = ma_domain_ctor_null(domain.size());
211 
212  // trigger new alarm
213  // ...
214 
215  ++alarm_count;
216 
217  return true;
218  }
219 
220  else if (repeat_alarm)
221  {
222  // not a new alarm, but the repeat_alarm flag has been set
223  timeval tv;
224  gettimeofday(&tv, 0);
225  timeval lt = it->second;
226  if (tv.tv_sec - lt.tv_sec > holdoff)
227  {
228  // passed the holdoff time, can refire a new alarm
229  itor_last_alarm = alarms.insert(it, std::make_pair(alarm, tv));
230 
231  alarm.clear();
232  alarm = ma_domain_ctor_null(domain.size());
233 
234  // trigger new alarm
235  // ...
236 
237  ++alarm_count;
238 
239  return true;
240  }
241  }
242 
243  // otherwise, the alarm has already been triggered, or hasn't passed
244  // the holdoff time
245  TLOG(TLVL_DEBUG) << "this alarm has already been triggered";
246  }
247 
248  // reset alarm
249  alarm.clear();
250  alarm = ma_domain_ctor_null(domain.size());
251 
252  return false;
253 }
254 
255 int ma_rule::act()
256 {
257  int mask = 0;
258  for (ma_actions::const_iterator it = actions.begin(), e = actions.end(); it != e; ++it)
259  {
260  if ((*it)->exec()) ++mask;
261  }
262  return mask;
263 }
264 
265 void ma_rule::reset()
266 {
267  // clear user function state
268  boolean_expr.reset();
269 
270  // reset all related conds
271  cond_vec_t::iterator it = conditions.begin();
272  for (; it != conditions.end(); ++it) (*it)->reset();
273 
274  //domains.clear();
275  alarms.clear();
276  itor_last_alarm = alarms.end();
277  alarm_count = 0;
278 }
279 
280 ma_domain const& ma_rule::get_alarm() const
281 {
282  if (alarms.empty())
283  throw std::runtime_error("get_alarm_message(): no alarm has been triggerd");
284 
285  // make sure that the itor_last_alarm points to something
286  assert(itor_last_alarm != alarms.end());
287 
288  return itor_last_alarm->first;
289 }
290 
291 std::string ma_rule::get_alarm_message()
292 {
293  return alarm_msg.message();
294 }
295 
296 // ----------------------------------------------------------------
297 //
298 // get condition index and pointer given a name
299 cond_idx_t
300 ma_rule::get_cond_idx(std::string const& name) const
301 {
302  idx_t::const_iterator it = conditions_idx.find(name);
303  if (it == conditions_idx.end())
304  throw std::runtime_error("get_cond_idx: name '" + name + "' not found");
305  return cond_idx_t(conditions[it->second], it->second);
306 }
307 
308 // get pointer to the condition
310 ma_rule::get_cond(std::string const& name) const
311 {
312  idx_t::const_iterator it = conditions_idx.find(name);
313  if (it == conditions_idx.end())
314  throw std::runtime_error("get_cond: name '" + name + "' not found");
315  return conditions[it->second];
316 }
317 
318 // get index to the condition
319 size_t
320 ma_rule::get_idx(std::string const& name) const
321 {
322  idx_t::const_iterator it = conditions_idx.find(name);
323  if (it == conditions_idx.end())
324  throw std::runtime_error("get_cond: name '" + name + "' not found");
325  return it->second;
326 }
327 
328 // get the size of condition container
329 size_t
330 ma_rule::get_cond_size() const
331 {
332  assert(conditions.size() == conditions_idx.size());
333  return conditions.size();
334 }
335 
336 // update the "notify_on_source" or "notify_on_target" list
337 // for corresponding conditions
338 void ma_rule::update_notify_list(std::string const& name, arg_t arg)
339 {
340  idx_t::const_iterator it = conditions_idx.find(name);
341 
342  if (it == conditions_idx.end())
343  throw std::runtime_error("update_notify_list: name '" + name + "' not found");
344 
345  (arg == SOURCE) ? conditions[it->second]->push_notify_source(this)
346  : conditions[it->second]->push_notify_target(this);
347 }