artdaq_core  v3_01_04
MonitoredQuantity.cc
1 #include "artdaq-core/Core/MonitoredQuantity.hh"
2 
3 #include <algorithm>
4 #include <math.h>
5 
6 using namespace artdaq;
7 
9 (
10  DURATION_T expectedCalculationInterval,
11  DURATION_T timeWindowForRecentResults
12 ) :
13  _expectedCalculationInterval(expectedCalculationInterval)
14 {
15  setNewTimeWindowForRecentResults(timeWindowForRecentResults);
16  enabled = true;
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  {
25  _lastCalculationTime = getCurrentTime();
26  }
27  ++_workingSampleCount;
28  _workingValueSum += value;
29  _workingValueSumOfSquares += (value * value);
30  if (value < _workingValueMin) { _workingValueMin = value; }
31  if (value > _workingValueMax) { _workingValueMax = value; }
32  _workingLastSampleValue = value;
33 }
34 
35 void MonitoredQuantity::addSample(const int value)
36 {
37  addSample(static_cast<double>(value));
38 }
39 
40 void MonitoredQuantity::addSample(const uint32_t value)
41 {
42  addSample(static_cast<double>(value));
43 }
44 
45 void MonitoredQuantity::addSample(const uint64_t value)
46 {
47  addSample(static_cast<double>(value));
48 }
49 
51 {
52  if (!enabled) { return false; }
53  if (_lastCalculationTime <= 0.0) { return false; }
54  if (currentTime - _lastCalculationTime < _expectedCalculationInterval)
55  {
56  return false;
57  }
58  // create local copies of the working values to minimize the
59  // time that we could block a thread trying to add a sample.
60  // Also, reset the working values.
61  long long latestSampleCount;
62  double latestValueSum;
63  double latestValueSumOfSquares;
64  double latestValueMin;
65  double latestValueMax;
66  DURATION_T latestDuration;
67  double latestLastLatchedSampleValue;
68  {
69  boost::mutex::scoped_lock sl(_accumulationMutex);
70  latestSampleCount = _workingSampleCount;
71  latestValueSum = _workingValueSum;
72  latestValueSumOfSquares = _workingValueSumOfSquares;
73  latestValueMin = _workingValueMin;
74  latestValueMax = _workingValueMax;
75  latestDuration = currentTime - _lastCalculationTime;
76  latestLastLatchedSampleValue = _workingLastSampleValue;
77  _lastCalculationTime = currentTime;
78  _workingSampleCount = 0;
79  _workingValueSum = 0.0;
80  _workingValueSumOfSquares = 0.0;
81  _workingValueMin = INFINITY;
82  _workingValueMax = -INFINITY;
83  }
84  // lock out any interaction with the results while we update them
85  {
86  boost::mutex::scoped_lock sl(_resultsMutex);
87  lastSampleValue = latestLastLatchedSampleValue;
88  lastCalculationTime = _lastCalculationTime;
89  // we simply add the latest results to the full set
90  fullSampleCount += latestSampleCount;
91  fullValueSum += latestValueSum;
92  fullValueSumOfSquares += latestValueSumOfSquares;
93  if (latestValueMin < fullValueMin) { fullValueMin = latestValueMin; }
94  if (latestValueMax > fullValueMax) { fullValueMax = latestValueMax; }
95  fullDuration += latestDuration;
96  // for the recent results, we need to replace the contents of
97  // the working bin and re-calculate the recent values
98  recentBinnedSampleCounts[_workingBinId] = latestSampleCount;
99  recentBinnedValueSums[_workingBinId] = latestValueSum;
100  _binValueSumOfSquares[_workingBinId] = latestValueSumOfSquares;
101  _binValueMin[_workingBinId] = latestValueMin;
102  _binValueMax[_workingBinId] = latestValueMax;
103  recentBinnedDurations[_workingBinId] = latestDuration;
104  recentBinnedEndTimes[_workingBinId] = _lastCalculationTime;
105  if (latestDuration > 0.0)
106  {
107  lastValueRate = latestValueSum / latestDuration;
108  }
109  else
110  {
111  lastValueRate = 0.0;
112  }
113  recentSampleCount = 0;
114  recentValueSum = 0.0;
116  recentValueMin = INFINITY;
117  recentValueMax = -INFINITY;
118  recentDuration = 0.0;
119  for (unsigned int idx = 0; idx < _binCount; ++idx)
120  {
123  recentValueSumOfSquares += _binValueSumOfSquares[idx];
124  if (_binValueMin[idx] < recentValueMin)
125  {
126  recentValueMin = _binValueMin[idx];
127  }
128  if (_binValueMax[idx] > recentValueMax)
129  {
130  recentValueMax = _binValueMax[idx];
131  }
133  }
134  // update the working bin ID here so that we are ready for
135  // the next calculation request
136  ++_workingBinId;
137  if (_workingBinId >= _binCount) { _workingBinId = 0; }
138  // calculate the derived full values
139  if (fullDuration > 0.0)
140  {
141  fullSampleRate = static_cast<double>(fullSampleCount) / fullDuration;
142  fullValueRate = static_cast<double>(fullValueSum) / fullDuration;
143  }
144  else
145  {
146  fullSampleRate = 0.0;
147  fullValueRate = 0.0;
148  }
149  if (fullSampleCount > 0)
150  {
151  fullValueAverage = fullValueSum / static_cast<double>(fullSampleCount);
152  double squareAvg = fullValueSumOfSquares / static_cast<double>(fullSampleCount);
153  double avg = fullValueSum / static_cast<double>(fullSampleCount);
154  double sigSquared = squareAvg - avg * avg;
155  if (sigSquared > 0.0)
156  {
157  fullValueRMS = sqrt(sigSquared);
158  }
159  else
160  {
161  fullValueRMS = 0.0;
162  }
163  }
164  else
165  {
166  fullValueAverage = 0.0;
167  fullValueRMS = 0.0;
168  }
169  // calculate the derived recent values
170  if (recentDuration > 0.0)
171  {
172  recentSampleRate = static_cast<double>(recentSampleCount) / recentDuration;
173  recentValueRate = static_cast<double>(recentValueSum) / recentDuration;
174  }
175  else
176  {
177  recentSampleRate = 0.0;
178  recentValueRate = 0.0;
179  }
180  if (recentSampleCount > 0)
181  {
182  recentValueAverage = recentValueSum / static_cast<double>(recentSampleCount);
183  double squareAvg = recentValueSumOfSquares /
184  static_cast<double>(recentSampleCount);
185  double avg = recentValueSum / static_cast<double>(recentSampleCount);
186  double sigSquared = squareAvg - avg * avg;
187  if (sigSquared > 0.0)
188  {
189  recentValueRMS = sqrt(sigSquared);
190  }
191  else
192  {
193  recentValueRMS = 0.0;
194  }
195  }
196  else
197  {
198  recentValueAverage = 0.0;
199  recentValueRMS = 0.0;
200  }
201  }
202  return true;
203 }
204 
205 void MonitoredQuantity::_reset_accumulators()
206 {
207  _lastCalculationTime = 0;
208  _workingSampleCount = 0;
209  _workingValueSum = 0.0;
210  _workingValueSumOfSquares = 0.0;
211  _workingValueMin = INFINITY;
212  _workingValueMax = -INFINITY;
213  _workingLastSampleValue = 0;
214 }
215 
216 void MonitoredQuantity::_reset_results()
217 {
218  _workingBinId = 0;
219  for (unsigned int idx = 0; idx < _binCount; ++idx)
220  {
221  recentBinnedSampleCounts[idx] = 0;
222  recentBinnedValueSums[idx] = 0.0;
223  _binValueSumOfSquares[idx] = 0.0;
224  _binValueMin[idx] = INFINITY;
225  _binValueMax[idx] = -INFINITY;
226  recentBinnedDurations[idx] = 0.0;
227  recentBinnedEndTimes[idx] = 0.0;
228  }
229  fullSampleCount = 0;
230  fullSampleRate = 0.0;
231  fullValueSum = 0.0;
232  fullValueSumOfSquares = 0.0;
233  fullValueAverage = 0.0;
234  fullValueRMS = 0.0;
235  fullValueMin = INFINITY;
236  fullValueMax = -INFINITY;
237  fullValueRate = 0.0;
238  fullDuration = 0.0;
239  recentSampleCount = 0;
240  recentSampleRate = 0.0;
241  recentValueSum = 0.0;
243  recentValueAverage = 0.0;
244  recentValueRMS = 0.0;
245  recentValueMin = INFINITY;
246  recentValueMax = -INFINITY;
247  recentValueRate = 0.0;
248  recentDuration = 0.0;
249  lastSampleValue = 0.0;
250  lastValueRate = 0.0;
251 }
252 
254 {
255  {
256  boost::mutex::scoped_lock sl(_accumulationMutex);
257  _reset_accumulators();
258  }
259  {
260  boost::mutex::scoped_lock sl(_resultsMutex);
261  _reset_results();
262  }
263 }
264 
266 {
267  if (!enabled)
268  {
269  reset();
270  enabled = true;
271  }
272 }
273 
275 {
276  // It is faster to just set _enabled to false than to test and set
277  // it conditionally.
278  enabled = false;
279 }
280 
282 {
283  // lock the results objects since we're dramatically changing the
284  // bins used for the recent results
285  {
286  boost::mutex::scoped_lock sl(_resultsMutex);
287  _intervalForRecentStats = interval;
288  // determine how many bins we should use in our sliding window
289  // by dividing the input time window by the expected calculation
290  // interval and rounding to the nearest integer.
291  // In case that the calculation interval is larger then the
292  // interval for recent stats, keep the last one.
293  _binCount = std::max(1U,
294  static_cast<unsigned int>(
295  (_intervalForRecentStats / _expectedCalculationInterval) + 0.5
296  )
297  );
298  // create the vectors for the binned quantities
299  recentBinnedSampleCounts.reserve(_binCount);
300  recentBinnedValueSums.reserve(_binCount);
301  _binValueSumOfSquares.reserve(_binCount);
302  _binValueMin.reserve(_binCount);
303  _binValueMax.reserve(_binCount);
304  recentBinnedDurations.reserve(_binCount);
305  recentBinnedEndTimes.reserve(_binCount);
306  _reset_results();
307  }
308  {
309  boost::mutex::scoped_lock sl(_accumulationMutex);
310  _reset_accumulators();
311  }
312  // call the reset method to populate the correct initial values
313  // for the internal sample data
314  //reset();
315 }
316 
319 {
320  timeout /= 10;
321  {
322  boost::mutex::scoped_lock sl(_accumulationMutex);
323  if (_workingSampleCount == 0) { return true; }
324  }
325  auto sleepTime = static_cast<long>(timeout * 100000.0);
326  for (auto idx = 0; idx < 10; ++idx)
327  {
328  usleep(sleepTime);
329  {
330  boost::mutex::scoped_lock sl(_accumulationMutex);
331  if (_workingSampleCount == 0) { return true; }
332  }
333  }
334  return false;
335 }
336 
337 void
339 {
340  boost::mutex::scoped_lock results(_resultsMutex);
361  s.recentBinnedSampleCounts.resize(_binCount);
362  s.recentBinnedValueSums.resize(_binCount);
363  s.recentBinnedDurations.resize(_binCount);
364  s.recentBinnedEndTimes.resize(_binCount);
365  unsigned int sourceBinId = _workingBinId;
366  for (unsigned int idx = 0; idx < _binCount; ++idx)
367  {
368  if (sourceBinId >= _binCount) { sourceBinId = 0; }
370  s.recentBinnedValueSums[idx] = recentBinnedValueSums[sourceBinId];
371  s.recentBinnedDurations[idx] = recentBinnedDurations[sourceBinId];
372  s.recentBinnedEndTimes[idx] = recentBinnedEndTimes[sourceBinId];
373  ++sourceBinId;
374  }
378  s.enabled = enabled;
379 }
380 
382 {
383  TIME_POINT_T result = -1.0;
384  timeval now;
385  if (gettimeofday(&now, 0) == 0)
386  {
387  result = static_cast<TIME_POINT_T>(now.tv_sec);
388  result += static_cast<TIME_POINT_T>(now.tv_usec) / (1000 * 1000);
389  }
390  return result;
391 }
392 
394 {
395  boost::mutex::scoped_lock results(_resultsMutex);
396  return lastCalculationTime;
397 }
398 
400 {
401  boost::mutex::scoped_lock results(_resultsMutex);
402  return fullDuration;
403 }
404 
406 {
407  boost::mutex::scoped_lock results(_resultsMutex);
408  return recentValueSum;
409 }
410 
412 {
413  boost::mutex::scoped_lock results(_resultsMutex);
414  return recentValueAverage;
415 }
416 
418 {
419  boost::mutex::scoped_lock results(_resultsMutex);
420  return fullSampleCount;
421 }
bool enabled
Whether the MonitoredQuantity is collecting data.
bool calculateStatistics(TIME_POINT_T currentTime=getCurrentTime())
Forces a calculation of the statistics for the monitored quantity.
std::vector< double > recentBinnedValueSums
Sums for each instance of calculateStatistics in _intervalForRecentStats (rolling window) ...
long long getFullSampleCount() const
Access the count of samples for the entire history of the MonitoredQuantity.
double recentValueAverage
The average of the &quot;recent&quot; samples.
struct containing MonitoredQuantity data
double fullValueMax
The largest value of all sampels.
double recentValueMax
The largest value of the &quot;recent&quot; samples.
double lastSampleValue
Value of the most recent sample.
static TIME_POINT_T getCurrentTime()
Returns the current point in time.
double recentSampleRate
The number of samples in the &quot;recent&quot; time window, divided by the length of that window.
double fullValueAverage
The average of all samples.
long long recentSampleCount
The number of samples in the &quot;recent&quot; time window.
double fullValueSumOfSquares
The sum of the squares of all samples.
std::vector< TIME_POINT_T > recentBinnedEndTimes
Last sample time in each instance of calculateStatistics in _intervalForRecentStats (rolling window) ...
MonitoredQuantity(DURATION_T expectedCalculationInterval, DURATION_T timeWindowForRecentResults)
Instantiates a MonitoredQuantity object.
DURATION_T fullDuration
The full duration of sampling.
bool waitUntilAccumulatorsHaveBeenFlushed(DURATION_T timeout) const
Blocks while the MonitoredQuantity is flushed, up to timeout duration.
long long fullSampleCount
The total number of samples represented.
DURATION_T recentDuration
The length of the &quot;recent&quot; time window.
void addSample(const double value=1.0)
Adds the specified doubled valued sample value to the monitor instance.
double TIME_POINT_T
A point in time.
TIME_POINT_T getLastCalculationTime() const
Access the last calculation time.
std::vector< DURATION_T > recentBinnedDurations
Duration between each instance of calcualteStatistics in _intervalForRecentStats (rolling window) ...
double fullValueRate
The sum of all samples over the full duration of sampling.
double lastValueRate
Latest rate point (sum of values over calculateStatistics interval)
TIME_POINT_T lastCalculationTime
Last time calculateStatistics was called.
double recentValueSumOfSquares
The sum of the squares of the &quot;recent&quot; samples.
double recentValueSum
The sum of the &quot;recent&quot; samples.
double recentValueRate
The sum of the &quot;recent&quot; samples, divided by the length of the &quot;recent&quot; time window.
double recentValueMin
The smallest value of the &quot;recent&quot; samples.
double getRecentValueAverage() const
Access the average of the value samples in the &quot;recent&quot; time span.
double recentValueRMS
The RMS of the &#39;recent" samples.
double getRecentValueSum() const
Access the sum of the value samples in the &quot;recent&quot; time span.
double fullValueMin
The smallest value of all samples.
double fullSampleRate
The total number of samples over the full duration of sampling.
double fullValueRMS
The RMS of all samples.
void getStats(MonitoredQuantityStats &stats) const
Write all our collected statistics into the given Stats struct.
std::vector< long long > recentBinnedSampleCounts
Sample counts for each instance of calculateStatistics in _intervalForRecentStats (rolling window) ...
double fullValueSum
The sum of all samples.
void setNewTimeWindowForRecentResults(DURATION_T interval)
Specifies a new time interval to be used when calculating &quot;recent&quot; statistics.
DURATION_T getFullDuration() const
Access the full duration of the statistics.