artdaq_core  v3_05_07
MonitoredQuantity.cc
1 #include "artdaq-core/Core/MonitoredQuantity.hh"
2 
3 #include <math.h>
4 #include <algorithm>
5 
6 using namespace artdaq;
7 
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  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  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,
292  static_cast<unsigned int>(
293  (_intervalForRecentStats / _expectedCalculationInterval) + 0.5));
294  // create the vectors for the binned quantities
295  recentBinnedSampleCounts.reserve(_binCount);
296  recentBinnedValueSums.reserve(_binCount);
297  _binValueSumOfSquares.reserve(_binCount);
298  _binValueMin.reserve(_binCount);
299  _binValueMax.reserve(_binCount);
300  recentBinnedDurations.reserve(_binCount);
301  recentBinnedEndTimes.reserve(_binCount);
302  _reset_results();
303  }
304  {
305  boost::mutex::scoped_lock sl(_accumulationMutex);
306  _reset_accumulators();
307  }
308  // call the reset method to populate the correct initial values
309  // for the internal sample data
310  //reset();
311 }
312 
315 {
316  timeout /= 10;
317  {
318  boost::mutex::scoped_lock sl(_accumulationMutex);
319  if (_workingSampleCount == 0) { return true; }
320  }
321  auto sleepTime = static_cast<long>(timeout * 100000.0);
322  for (auto idx = 0; idx < 10; ++idx)
323  {
324  usleep(sleepTime);
325  {
326  boost::mutex::scoped_lock sl(_accumulationMutex);
327  if (_workingSampleCount == 0) { return true; }
328  }
329  }
330  return false;
331 }
332 
334 {
335  boost::mutex::scoped_lock results(_resultsMutex);
356  s.recentBinnedSampleCounts.resize(_binCount);
357  s.recentBinnedValueSums.resize(_binCount);
358  s.recentBinnedDurations.resize(_binCount);
359  s.recentBinnedEndTimes.resize(_binCount);
360  unsigned int sourceBinId = _workingBinId;
361  for (unsigned int idx = 0; idx < _binCount; ++idx)
362  {
363  if (sourceBinId >= _binCount) { sourceBinId = 0; }
365  s.recentBinnedValueSums[idx] = recentBinnedValueSums[sourceBinId];
366  s.recentBinnedDurations[idx] = recentBinnedDurations[sourceBinId];
367  s.recentBinnedEndTimes[idx] = recentBinnedEndTimes[sourceBinId];
368  ++sourceBinId;
369  }
373  s.enabled = enabled;
374 }
375 
377 {
378  TIME_POINT_T result = -1.0;
379  timeval now;
380  if (gettimeofday(&now, 0) == 0)
381  {
382  result = static_cast<TIME_POINT_T>(now.tv_sec);
383  result += static_cast<TIME_POINT_T>(now.tv_usec) / (1000 * 1000);
384  }
385  return result;
386 }
387 
389 {
390  boost::mutex::scoped_lock results(_resultsMutex);
391  return lastCalculationTime;
392 }
393 
395 {
396  boost::mutex::scoped_lock results(_resultsMutex);
397  return fullDuration;
398 }
399 
401 {
402  boost::mutex::scoped_lock results(_resultsMutex);
403  return recentValueSum;
404 }
405 
407 {
408  boost::mutex::scoped_lock results(_resultsMutex);
409  return recentValueAverage;
410 }
411 
413 {
414  boost::mutex::scoped_lock results(_resultsMutex);
415  return fullSampleCount;
416 }
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.