artdaq_core  v1_07_01
 All Classes Namespaces Functions
MonitoredQuantity.cc
1 #include "artdaq-core/Core/MonitoredQuantity.hh"
2 
3 #include <algorithm>
4 #include <math.h>
5 
6 using namespace artdaq;
7 
8 MonitoredQuantity::MonitoredQuantity
9 (
10  DURATION_T expectedCalculationInterval,
11  DURATION_T timeWindowForRecentResults
12 ):
13  _enabled(true),
14  _expectedCalculationInterval(expectedCalculationInterval)
15 {
16  setNewTimeWindowForRecentResults(timeWindowForRecentResults);
17 }
18 
19 void MonitoredQuantity::addSample(const double value)
20 {
21  if (! _enabled) {return;}
22  boost::mutex::scoped_lock sl(_accumulationMutex);
23  if (_lastCalculationTime <= 0.0) {
24  _lastCalculationTime = getCurrentTime();
25  }
26  ++_workingSampleCount;
27  _workingValueSum += value;
28  _workingValueSumOfSquares += (value * value);
29  if (value < _workingValueMin) { _workingValueMin = value; }
30  if (value > _workingValueMax) { _workingValueMax = value; }
31  _workingLastSampleValue = value;
32 }
33 
34 void MonitoredQuantity::addSample(const int value)
35 {
36  addSample(static_cast<double>(value));
37 }
38 
39 void MonitoredQuantity::addSample(const uint32_t value)
40 {
41  addSample(static_cast<double>(value));
42 }
43 
44 void MonitoredQuantity::addSample(const uint64_t value)
45 {
46  addSample(static_cast<double>(value));
47 }
48 
49 bool MonitoredQuantity::calculateStatistics(TIME_POINT_T currentTime)
50 {
51  if (! _enabled) {return false;}
52  if (_lastCalculationTime <= 0.0) {return false;}
53  if (currentTime - _lastCalculationTime < _expectedCalculationInterval) {
54  return false;
55  }
56  // create local copies of the working values to minimize the
57  // time that we could block a thread trying to add a sample.
58  // Also, reset the working values.
59  long long latestSampleCount;
60  double latestValueSum;
61  double latestValueSumOfSquares;
62  double latestValueMin;
63  double latestValueMax;
64  DURATION_T latestDuration;
65  double latestLastLatchedSampleValue;
66  {
67  boost::mutex::scoped_lock sl(_accumulationMutex);
68  latestSampleCount = _workingSampleCount;
69  latestValueSum = _workingValueSum;
70  latestValueSumOfSquares = _workingValueSumOfSquares;
71  latestValueMin = _workingValueMin;
72  latestValueMax = _workingValueMax;
73  latestDuration = currentTime - _lastCalculationTime;
74  latestLastLatchedSampleValue = _workingLastSampleValue;
75  _lastCalculationTime = currentTime;
76  _workingSampleCount = 0;
77  _workingValueSum = 0.0;
78  _workingValueSumOfSquares = 0.0;
79  _workingValueMin = INFINITY;
80  _workingValueMax = -INFINITY;
81  }
82  // lock out any interaction with the results while we update them
83  {
84  boost::mutex::scoped_lock sl(_resultsMutex);
85  _lastLatchedSampleValue = latestLastLatchedSampleValue;
86  _lastLatchedCalculationTime = _lastCalculationTime;
87  // we simply add the latest results to the full set
88  _fullSampleCount += latestSampleCount;
89  _fullValueSum += latestValueSum;
90  _fullValueSumOfSquares += latestValueSumOfSquares;
91  if (latestValueMin < _fullValueMin) {_fullValueMin = latestValueMin;}
92  if (latestValueMax > _fullValueMax) {_fullValueMax = latestValueMax;}
93  _fullDuration += latestDuration;
94  // for the recent results, we need to replace the contents of
95  // the working bin and re-calculate the recent values
96  _binSampleCount[_workingBinId] = latestSampleCount;
97  _binValueSum[_workingBinId] = latestValueSum;
98  _binValueSumOfSquares[_workingBinId] = latestValueSumOfSquares;
99  _binValueMin[_workingBinId] = latestValueMin;
100  _binValueMax[_workingBinId] = latestValueMax;
101  _binDuration[_workingBinId] = latestDuration;
102  _binEndTime[_workingBinId] = _lastCalculationTime;
103  if (latestDuration > 0.0) {
104  _lastLatchedValueRate = latestValueSum / latestDuration;
105  }
106  else {
107  _lastLatchedValueRate = 0.0;
108  }
109  _recentSampleCount = 0;
110  _recentValueSum = 0.0;
111  _recentValueSumOfSquares = 0.0;
112  _recentValueMin = INFINITY;
113  _recentValueMax = -INFINITY;
114  _recentDuration = 0.0;
115  for (unsigned int idx = 0; idx < _binCount; ++idx) {
116  _recentSampleCount += _binSampleCount[idx];
117  _recentValueSum += _binValueSum[idx];
118  _recentValueSumOfSquares += _binValueSumOfSquares[idx];
119  if (_binValueMin[idx] < _recentValueMin) {
120  _recentValueMin = _binValueMin[idx];
121  }
122  if (_binValueMax[idx] > _recentValueMax) {
123  _recentValueMax = _binValueMax[idx];
124  }
125  _recentDuration += _binDuration[idx];
126  }
127  // update the working bin ID here so that we are ready for
128  // the next calculation request
129  ++_workingBinId;
130  if (_workingBinId >= _binCount) {_workingBinId = 0;}
131  // calculate the derived full values
132  if (_fullDuration > 0.0) {
133  _fullSampleRate = static_cast<double>(_fullSampleCount) / _fullDuration;
134  _fullValueRate = static_cast<double>(_fullValueSum) / _fullDuration;
135  }
136  else {
137  _fullSampleRate = 0.0;
138  _fullValueRate = 0.0;
139  }
140  if (_fullSampleCount > 0) {
141  _fullValueAverage = _fullValueSum / static_cast<double>(_fullSampleCount);
142  double squareAvg = _fullValueSumOfSquares / static_cast<double>(_fullSampleCount);
143  double avg = _fullValueSum / static_cast<double>(_fullSampleCount);
144  double sigSquared = squareAvg - avg * avg;
145  if (sigSquared > 0.0) {
146  _fullValueRMS = sqrt(sigSquared);
147  }
148  else {
149  _fullValueRMS = 0.0;
150  }
151  }
152  else {
153  _fullValueAverage = 0.0;
154  _fullValueRMS = 0.0;
155  }
156  // calculate the derived recent values
157  if (_recentDuration > 0.0) {
158  _recentSampleRate = static_cast<double>(_recentSampleCount) / _recentDuration;
159  _recentValueRate = static_cast<double>(_recentValueSum) / _recentDuration;
160  }
161  else {
162  _recentSampleRate = 0.0;
163  _recentValueRate = 0.0;
164  }
165  if (_recentSampleCount > 0) {
166  _recentValueAverage = _recentValueSum / static_cast<double>(_recentSampleCount);
167  double squareAvg = _recentValueSumOfSquares /
168  static_cast<double>(_recentSampleCount);
169  double avg = _recentValueSum / static_cast<double>(_recentSampleCount);
170  double sigSquared = squareAvg - avg * avg;
171  if (sigSquared > 0.0) {
172  _recentValueRMS = sqrt(sigSquared);
173  }
174  else {
175  _recentValueRMS = 0.0;
176  }
177  }
178  else {
179  _recentValueAverage = 0.0;
180  _recentValueRMS = 0.0;
181  }
182  }
183  return true;
184 }
185 
186 void MonitoredQuantity::_reset_accumulators()
187 {
188  _lastCalculationTime = 0;
189  _workingSampleCount = 0;
190  _workingValueSum = 0.0;
191  _workingValueSumOfSquares = 0.0;
192  _workingValueMin = INFINITY;
193  _workingValueMax = -INFINITY;
194  _workingLastSampleValue = 0;
195 }
196 
197 void MonitoredQuantity::_reset_results()
198 {
199  _workingBinId = 0;
200  for (unsigned int idx = 0; idx < _binCount; ++idx) {
201  _binSampleCount[idx] = 0;
202  _binValueSum[idx] = 0.0;
203  _binValueSumOfSquares[idx] = 0.0;
204  _binValueMin[idx] = INFINITY;
205  _binValueMax[idx] = -INFINITY;
206  _binDuration[idx] = 0.0;
207  _binEndTime[idx] = 0.0;
208  }
209  _fullSampleCount = 0;
210  _fullSampleRate = 0.0;
211  _fullValueSum = 0.0;
212  _fullValueSumOfSquares = 0.0;
213  _fullValueAverage = 0.0;
214  _fullValueRMS = 0.0;
215  _fullValueMin = INFINITY;
216  _fullValueMax = -INFINITY;
217  _fullValueRate = 0.0;
218  _fullDuration = 0.0;
219  _recentSampleCount = 0;
220  _recentSampleRate = 0.0;
221  _recentValueSum = 0.0;
222  _recentValueSumOfSquares = 0.0;
223  _recentValueAverage = 0.0;
224  _recentValueRMS = 0.0;
225  _recentValueMin = INFINITY;
226  _recentValueMax = -INFINITY;
227  _recentValueRate = 0.0;
228  _recentDuration = 0.0;
229  _lastLatchedSampleValue = 0.0;
230  _lastLatchedValueRate = 0.0;
231 }
232 
233 void MonitoredQuantity::reset()
234 {
235  {
236  boost::mutex::scoped_lock sl(_accumulationMutex);
237  _reset_accumulators();
238  }
239  {
240  boost::mutex::scoped_lock sl(_resultsMutex);
241  _reset_results();
242  }
243 }
244 
245 void MonitoredQuantity::enable()
246 {
247  if (! _enabled) {
248  reset();
249  _enabled = true;
250  }
251 }
252 
253 void MonitoredQuantity::disable()
254 {
255  // It is faster to just set _enabled to false than to test and set
256  // it conditionally.
257  _enabled = false;
258 }
259 
260 void MonitoredQuantity::setNewTimeWindowForRecentResults(DURATION_T interval)
261 {
262  // lock the results objects since we're dramatically changing the
263  // bins used for the recent results
264  {
265  boost::mutex::scoped_lock sl(_resultsMutex);
266  _intervalForRecentStats = interval;
267  // determine how many bins we should use in our sliding window
268  // by dividing the input time window by the expected calculation
269  // interval and rounding to the nearest integer.
270  // In case that the calculation interval is larger then the
271  // interval for recent stats, keep the last one.
272  _binCount = std::max(1U,
273  static_cast<unsigned int>(
274  (_intervalForRecentStats / _expectedCalculationInterval) + 0.5
275  )
276  );
277  // create the vectors for the binned quantities
278  _binSampleCount.reserve(_binCount);
279  _binValueSum.reserve(_binCount);
280  _binValueSumOfSquares.reserve(_binCount);
281  _binValueMin.reserve(_binCount);
282  _binValueMax.reserve(_binCount);
283  _binDuration.reserve(_binCount);
284  _binEndTime.reserve(_binCount);
285  _reset_results();
286  }
287  {
288  boost::mutex::scoped_lock sl(_accumulationMutex);
289  _reset_accumulators();
290  }
291  // call the reset method to populate the correct initial values
292  // for the internal sample data
293  //reset();
294 }
295 
296 bool MonitoredQuantity::
297 waitUntilAccumulatorsHaveBeenFlushed(DURATION_T timeout) const
298 {
299  {
300  boost::mutex::scoped_lock sl(_accumulationMutex);
301  if (_workingSampleCount == 0) {return true;}
302  }
303  long sleepTime = static_cast<long>(timeout * 100000.0);
304  for (int idx = 0; idx < 10; ++idx) {
305  usleep(sleepTime);
306  {
307  boost::mutex::scoped_lock sl(_accumulationMutex);
308  if (_workingSampleCount == 0) {return true;}
309  }
310  }
311  return false;
312 }
313 
314 void
315 MonitoredQuantity::getStats(Stats & s) const
316 {
317  boost::mutex::scoped_lock results(_resultsMutex);
318  s.fullSampleCount = _fullSampleCount;
319  s.fullSampleRate = _fullSampleRate;
320  s.fullValueSum = _fullValueSum;
321  s.fullValueSumOfSquares = _fullValueSumOfSquares;
322  s.fullValueAverage = _fullValueAverage;
323  s.fullValueRMS = _fullValueRMS;
324  s.fullValueMin = _fullValueMin;
325  s.fullValueMax = _fullValueMax;
326  s.fullValueRate = _fullValueRate;
327  s.fullDuration = _fullDuration;
328  s.recentSampleCount = _recentSampleCount;
329  s.recentSampleRate = _recentSampleRate;
330  s.recentValueSum = _recentValueSum;
331  s.recentValueSumOfSquares = _recentValueSumOfSquares;
332  s.recentValueAverage = _recentValueAverage;
333  s.recentValueRMS = _recentValueRMS;
334  s.recentValueMin = _recentValueMin;
335  s.recentValueMax = _recentValueMax;
336  s.recentValueRate = _recentValueRate;
337  s.recentDuration = _recentDuration;
338  s.recentBinnedSampleCounts.resize(_binCount);
339  s.recentBinnedValueSums.resize(_binCount);
340  s.recentBinnedDurations.resize(_binCount);
341  s.recentBinnedEndTimes.resize(_binCount);
342  unsigned int sourceBinId = _workingBinId;
343  for (unsigned int idx = 0; idx < _binCount; ++idx) {
344  if (sourceBinId >= _binCount) {sourceBinId = 0;}
345  s.recentBinnedSampleCounts[idx] = _binSampleCount[sourceBinId];
346  s.recentBinnedValueSums[idx] = _binValueSum[sourceBinId];
347  s.recentBinnedDurations[idx] = _binDuration[sourceBinId];
348  s.recentBinnedEndTimes[idx] = _binEndTime[sourceBinId];
349  ++sourceBinId;
350  }
351  s.lastSampleValue = _lastLatchedSampleValue;
352  s.lastValueRate = _lastLatchedValueRate;
353  s.lastCalculationTime = _lastLatchedCalculationTime;
354  s.enabled = _enabled;
355 }
356 
357 MonitoredQuantity::TIME_POINT_T MonitoredQuantity::getCurrentTime()
358 {
359  TIME_POINT_T result = -1.0;
360  timeval now;
361  if (gettimeofday(&now, 0) == 0) {
362  result = static_cast<TIME_POINT_T>(now.tv_sec);
363  result += static_cast<TIME_POINT_T>(now.tv_usec) / (1000 * 1000);
364  }
365  return result;
366 }
367 
368 MonitoredQuantity::TIME_POINT_T MonitoredQuantity::lastCalculationTime() const
369 {
370  boost::mutex::scoped_lock results(_resultsMutex);
371  return _lastLatchedCalculationTime;
372 }
373 
374 MonitoredQuantity::DURATION_T MonitoredQuantity::fullDuration() const
375 {
376  boost::mutex::scoped_lock results(_resultsMutex);
377  return _fullDuration;
378 }
379 
380 double MonitoredQuantity::recentValueSum() const
381 {
382  boost::mutex::scoped_lock results(_resultsMutex);
383  return _recentValueSum;
384 }
385 
386 double MonitoredQuantity::recentValueAverage() const
387 {
388  boost::mutex::scoped_lock results(_resultsMutex);
389  return _recentValueAverage;
390 }
391 
392 long long MonitoredQuantity::fullSampleCount() const
393 {
394  boost::mutex::scoped_lock results(_resultsMutex);
395  return _fullSampleCount;
396 }