artdaq_core  v3_06_00
MonitoredQuantity.cc
1 #include "artdaq-core/Core/MonitoredQuantity.hh"
2 
3 #include <algorithm>
4 #include <cmath>
5 
6 using namespace artdaq;
7 
8 MonitoredQuantity::MonitoredQuantity(
9  DURATION_T expectedCalculationInterval,
10  DURATION_T timeWindowForRecentResults)
11  : _expectedCalculationInterval(expectedCalculationInterval)
12 {
13  setNewTimeWindowForRecentResults(timeWindowForRecentResults);
14  enabled = true;
15 }
16 
17 void MonitoredQuantity::addSample(const double value)
18 {
19  if (!enabled) { return; }
20  boost::mutex::scoped_lock sl(_accumulationMutex);
21  if (_lastCalculationTime <= 0.0)
22  {
23  _lastCalculationTime = getCurrentTime();
24  }
25  ++_workingSampleCount;
26  _workingValueSum += value;
27  _workingValueSumOfSquares += (value * value);
28  if (value < _workingValueMin) { _workingValueMin = value; }
29  if (value > _workingValueMax) { _workingValueMax = value; }
30  _workingLastSampleValue = value;
31 }
32 
33 void MonitoredQuantity::addSample(const int value)
34 {
35  addSample(static_cast<double>(value));
36 }
37 
38 void MonitoredQuantity::addSample(const uint32_t value)
39 {
40  addSample(static_cast<double>(value));
41 }
42 
43 void MonitoredQuantity::addSample(const uint64_t value)
44 {
45  addSample(static_cast<double>(value));
46 }
47 
49 {
50  if (!enabled) { return false; }
51  if (_lastCalculationTime <= 0.0) { return false; }
52  if (currentTime - _lastCalculationTime < _expectedCalculationInterval)
53  {
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  size_t 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  lastSampleValue = latestLastLatchedSampleValue;
86  lastCalculationTime = _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  recentBinnedSampleCounts[_workingBinId] = latestSampleCount;
97  recentBinnedValueSums[_workingBinId] = latestValueSum;
98  _binValueSumOfSquares[_workingBinId] = latestValueSumOfSquares;
99  _binValueMin[_workingBinId] = latestValueMin;
100  _binValueMax[_workingBinId] = latestValueMax;
101  recentBinnedDurations[_workingBinId] = latestDuration;
102  recentBinnedEndTimes[_workingBinId] = _lastCalculationTime;
103  if (latestDuration > 0.0)
104  {
105  lastValueRate = latestValueSum / latestDuration;
106  }
107  else
108  {
109  lastValueRate = 0.0;
110  }
111  recentSampleCount = 0;
112  recentValueSum = 0.0;
114  recentValueMin = INFINITY;
115  recentValueMax = -INFINITY;
116  recentDuration = 0.0;
117  for (unsigned int idx = 0; idx < _binCount; ++idx)
118  {
121  recentValueSumOfSquares += _binValueSumOfSquares[idx];
122  if (_binValueMin[idx] < recentValueMin)
123  {
124  recentValueMin = _binValueMin[idx];
125  }
126  if (_binValueMax[idx] > recentValueMax)
127  {
128  recentValueMax = _binValueMax[idx];
129  }
131  }
132  // update the working bin ID here so that we are ready for
133  // the next calculation request
134  ++_workingBinId;
135  if (_workingBinId >= _binCount) { _workingBinId = 0; }
136  // calculate the derived full values
137  if (fullDuration > 0.0)
138  {
139  fullSampleRate = static_cast<double>(fullSampleCount) / fullDuration;
140  fullValueRate = static_cast<double>(fullValueSum) / fullDuration;
141  }
142  else
143  {
144  fullSampleRate = 0.0;
145  fullValueRate = 0.0;
146  }
147  if (fullSampleCount > 0)
148  {
149  fullValueAverage = fullValueSum / static_cast<double>(fullSampleCount);
150  double squareAvg = fullValueSumOfSquares / static_cast<double>(fullSampleCount);
151  double avg = fullValueSum / static_cast<double>(fullSampleCount);
152  double sigSquared = squareAvg - avg * avg;
153  if (sigSquared > 0.0)
154  {
155  fullValueRMS = sqrt(sigSquared);
156  }
157  else
158  {
159  fullValueRMS = 0.0;
160  }
161  }
162  else
163  {
164  fullValueAverage = 0.0;
165  fullValueRMS = 0.0;
166  }
167  // calculate the derived recent values
168  if (recentDuration > 0.0)
169  {
170  recentSampleRate = static_cast<double>(recentSampleCount) / recentDuration;
171  recentValueRate = static_cast<double>(recentValueSum) / recentDuration;
172  }
173  else
174  {
175  recentSampleRate = 0.0;
176  recentValueRate = 0.0;
177  }
178  if (recentSampleCount > 0)
179  {
180  recentValueAverage = recentValueSum / static_cast<double>(recentSampleCount);
181  double squareAvg = recentValueSumOfSquares /
182  static_cast<double>(recentSampleCount);
183  double avg = recentValueSum / static_cast<double>(recentSampleCount);
184  double sigSquared = squareAvg - avg * avg;
185  if (sigSquared > 0.0)
186  {
187  recentValueRMS = sqrt(sigSquared);
188  }
189  else
190  {
191  recentValueRMS = 0.0;
192  }
193  }
194  else
195  {
196  recentValueAverage = 0.0;
197  recentValueRMS = 0.0;
198  }
199  }
200  return true;
201 }
202 
203 void MonitoredQuantity::_reset_accumulators()
204 {
205  _lastCalculationTime = 0;
206  _workingSampleCount = 0;
207  _workingValueSum = 0.0;
208  _workingValueSumOfSquares = 0.0;
209  _workingValueMin = INFINITY;
210  _workingValueMax = -INFINITY;
211  _workingLastSampleValue = 0;
212 }
213 
214 void MonitoredQuantity::_reset_results()
215 {
216  _workingBinId = 0;
217  for (unsigned int idx = 0; idx < _binCount; ++idx)
218  {
219  recentBinnedSampleCounts[idx] = 0;
220  recentBinnedValueSums[idx] = 0.0;
221  _binValueSumOfSquares[idx] = 0.0;
222  _binValueMin[idx] = INFINITY;
223  _binValueMax[idx] = -INFINITY;
224  recentBinnedDurations[idx] = 0.0;
225  recentBinnedEndTimes[idx] = 0.0;
226  }
227  fullSampleCount = 0;
228  fullSampleRate = 0.0;
229  fullValueSum = 0.0;
230  fullValueSumOfSquares = 0.0;
231  fullValueAverage = 0.0;
232  fullValueRMS = 0.0;
233  fullValueMin = INFINITY;
234  fullValueMax = -INFINITY;
235  fullValueRate = 0.0;
236  fullDuration = 0.0;
237  recentSampleCount = 0;
238  recentSampleRate = 0.0;
239  recentValueSum = 0.0;
241  recentValueAverage = 0.0;
242  recentValueRMS = 0.0;
243  recentValueMin = INFINITY;
244  recentValueMax = -INFINITY;
245  recentValueRate = 0.0;
246  recentDuration = 0.0;
247  lastSampleValue = 0.0;
248  lastValueRate = 0.0;
249 }
250 
252 {
253  {
254  boost::mutex::scoped_lock sl(_accumulationMutex);
255  _reset_accumulators();
256  }
257  {
258  boost::mutex::scoped_lock sl(_resultsMutex);
259  _reset_results();
260  }
261 }
262 
264 {
265  if (!enabled)
266  {
267  reset();
268  enabled = true;
269  }
270 }
271 
273 {
274  // It is faster to just set _enabled to false than to test and set
275  // it conditionally.
276  enabled = false;
277 }
278 
280 {
281  // lock the results objects since we're dramatically changing the
282  // bins used for the recent results
283  {
284  boost::mutex::scoped_lock sl(_resultsMutex);
285  _intervalForRecentStats = interval;
286  // determine how many bins we should use in our sliding window
287  // by dividing the input time window by the expected calculation
288  // interval and rounding to the nearest integer.
289  // In case that the calculation interval is larger then the
290  // interval for recent stats, keep the last one.
291  _binCount = std::max(1U, static_cast<unsigned int>(std::lround(_intervalForRecentStats / _expectedCalculationInterval)));
292  // create the vectors for the binned quantities
293  recentBinnedSampleCounts.reserve(_binCount);
294  recentBinnedValueSums.reserve(_binCount);
295  _binValueSumOfSquares.reserve(_binCount);
296  _binValueMin.reserve(_binCount);
297  _binValueMax.reserve(_binCount);
298  recentBinnedDurations.reserve(_binCount);
299  recentBinnedEndTimes.reserve(_binCount);
300  _reset_results();
301  }
302  {
303  boost::mutex::scoped_lock sl(_accumulationMutex);
304  _reset_accumulators();
305  }
306  // call the reset method to populate the correct initial values
307  // for the internal sample data
308  //reset();
309 }
310 
313 {
314  timeout /= 10;
315  {
316  boost::mutex::scoped_lock sl(_accumulationMutex);
317  if (_workingSampleCount == 0) { return true; }
318  }
319  auto sleepTime = static_cast<int64_t>(timeout * 100000.0);
320  for (auto idx = 0; idx < 10; ++idx)
321  {
322  usleep(sleepTime);
323  {
324  boost::mutex::scoped_lock sl(_accumulationMutex);
325  if (_workingSampleCount == 0) { return true; }
326  }
327  }
328  return false;
329 }
330 
332 {
333  boost::mutex::scoped_lock results(_resultsMutex);
354  s.recentBinnedSampleCounts.resize(_binCount);
355  s.recentBinnedValueSums.resize(_binCount);
356  s.recentBinnedDurations.resize(_binCount);
357  s.recentBinnedEndTimes.resize(_binCount);
358  unsigned int sourceBinId = _workingBinId;
359  for (unsigned int idx = 0; idx < _binCount; ++idx)
360  {
361  if (sourceBinId >= _binCount) { sourceBinId = 0; }
363  s.recentBinnedValueSums[idx] = recentBinnedValueSums[sourceBinId];
364  s.recentBinnedDurations[idx] = recentBinnedDurations[sourceBinId];
365  s.recentBinnedEndTimes[idx] = recentBinnedEndTimes[sourceBinId];
366  ++sourceBinId;
367  }
371  s.enabled = enabled;
372 }
373 
375 {
376  TIME_POINT_T result = -1.0;
377  timeval now;
378  if (gettimeofday(&now, nullptr) == 0)
379  {
380  result = static_cast<TIME_POINT_T>(now.tv_sec);
381  result += static_cast<TIME_POINT_T>(now.tv_usec) / (1000 * 1000);
382  }
383  return result;
384 }
385 
387 {
388  boost::mutex::scoped_lock results(_resultsMutex);
389  return lastCalculationTime;
390 }
391 
393 {
394  boost::mutex::scoped_lock results(_resultsMutex);
395  return fullDuration;
396 }
397 
399 {
400  boost::mutex::scoped_lock results(_resultsMutex);
401  return recentValueSum;
402 }
403 
405 {
406  boost::mutex::scoped_lock results(_resultsMutex);
407  return recentValueAverage;
408 }
409 
411 {
412  boost::mutex::scoped_lock results(_resultsMutex);
413  return fullSampleCount;
414 }
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) ...
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.
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) ...
DURATION_T fullDuration
The full duration of sampling.
bool waitUntilAccumulatorsHaveBeenFlushed(DURATION_T timeout) const
Blocks while the MonitoredQuantity is flushed, up to timeout duration.
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.
size_t recentSampleCount
The number of samples in the &quot;recent&quot; time window.
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.
size_t fullSampleCount
The total number of samples represented.
std::vector< size_t > recentBinnedSampleCounts
Sample counts for each instance of calculateStatistics in _intervalForRecentStats (rolling window) ...
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.
size_t getFullSampleCount() const
Access the count of samples for the entire history of the MonitoredQuantity.
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.
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.