00001 #include "artdaq-core/Core/MonitoredQuantity.hh" 00002 00003 #include <algorithm> 00004 #include <math.h> 00005 00006 using namespace artdaq; 00007 00008 MonitoredQuantity::MonitoredQuantity 00009 ( 00010 DURATION_T expectedCalculationInterval, 00011 DURATION_T timeWindowForRecentResults 00012 ) : 00013 _expectedCalculationInterval(expectedCalculationInterval) 00014 { 00015 setNewTimeWindowForRecentResults(timeWindowForRecentResults); 00016 enabled = true; 00017 } 00018 00019 void MonitoredQuantity::addSample(const double value) 00020 { 00021 if (!enabled) { return; } 00022 boost::mutex::scoped_lock sl(_accumulationMutex); 00023 if (_lastCalculationTime <= 0.0) 00024 { 00025 _lastCalculationTime = getCurrentTime(); 00026 } 00027 ++_workingSampleCount; 00028 _workingValueSum += value; 00029 _workingValueSumOfSquares += (value * value); 00030 if (value < _workingValueMin) { _workingValueMin = value; } 00031 if (value > _workingValueMax) { _workingValueMax = value; } 00032 _workingLastSampleValue = value; 00033 } 00034 00035 void MonitoredQuantity::addSample(const int value) 00036 { 00037 addSample(static_cast<double>(value)); 00038 } 00039 00040 void MonitoredQuantity::addSample(const uint32_t value) 00041 { 00042 addSample(static_cast<double>(value)); 00043 } 00044 00045 void MonitoredQuantity::addSample(const uint64_t value) 00046 { 00047 addSample(static_cast<double>(value)); 00048 } 00049 00050 bool MonitoredQuantity::calculateStatistics(TIME_POINT_T currentTime) 00051 { 00052 if (!enabled) { return false; } 00053 if (_lastCalculationTime <= 0.0) { return false; } 00054 if (currentTime - _lastCalculationTime < _expectedCalculationInterval) 00055 { 00056 return false; 00057 } 00058 // create local copies of the working values to minimize the 00059 // time that we could block a thread trying to add a sample. 00060 // Also, reset the working values. 00061 long long latestSampleCount; 00062 double latestValueSum; 00063 double latestValueSumOfSquares; 00064 double latestValueMin; 00065 double latestValueMax; 00066 DURATION_T latestDuration; 00067 double latestLastLatchedSampleValue; 00068 { 00069 boost::mutex::scoped_lock sl(_accumulationMutex); 00070 latestSampleCount = _workingSampleCount; 00071 latestValueSum = _workingValueSum; 00072 latestValueSumOfSquares = _workingValueSumOfSquares; 00073 latestValueMin = _workingValueMin; 00074 latestValueMax = _workingValueMax; 00075 latestDuration = currentTime - _lastCalculationTime; 00076 latestLastLatchedSampleValue = _workingLastSampleValue; 00077 _lastCalculationTime = currentTime; 00078 _workingSampleCount = 0; 00079 _workingValueSum = 0.0; 00080 _workingValueSumOfSquares = 0.0; 00081 _workingValueMin = INFINITY; 00082 _workingValueMax = -INFINITY; 00083 } 00084 // lock out any interaction with the results while we update them 00085 { 00086 boost::mutex::scoped_lock sl(_resultsMutex); 00087 lastSampleValue = latestLastLatchedSampleValue; 00088 lastCalculationTime = _lastCalculationTime; 00089 // we simply add the latest results to the full set 00090 fullSampleCount += latestSampleCount; 00091 fullValueSum += latestValueSum; 00092 fullValueSumOfSquares += latestValueSumOfSquares; 00093 if (latestValueMin < fullValueMin) { fullValueMin = latestValueMin; } 00094 if (latestValueMax > fullValueMax) { fullValueMax = latestValueMax; } 00095 fullDuration += latestDuration; 00096 // for the recent results, we need to replace the contents of 00097 // the working bin and re-calculate the recent values 00098 recentBinnedSampleCounts[_workingBinId] = latestSampleCount; 00099 recentBinnedValueSums[_workingBinId] = latestValueSum; 00100 _binValueSumOfSquares[_workingBinId] = latestValueSumOfSquares; 00101 _binValueMin[_workingBinId] = latestValueMin; 00102 _binValueMax[_workingBinId] = latestValueMax; 00103 recentBinnedDurations[_workingBinId] = latestDuration; 00104 recentBinnedEndTimes[_workingBinId] = _lastCalculationTime; 00105 if (latestDuration > 0.0) 00106 { 00107 lastValueRate = latestValueSum / latestDuration; 00108 } 00109 else 00110 { 00111 lastValueRate = 0.0; 00112 } 00113 recentSampleCount = 0; 00114 recentValueSum = 0.0; 00115 recentValueSumOfSquares = 0.0; 00116 recentValueMin = INFINITY; 00117 recentValueMax = -INFINITY; 00118 recentDuration = 0.0; 00119 for (unsigned int idx = 0; idx < _binCount; ++idx) 00120 { 00121 recentSampleCount += recentBinnedSampleCounts[idx]; 00122 recentValueSum += recentBinnedValueSums[idx]; 00123 recentValueSumOfSquares += _binValueSumOfSquares[idx]; 00124 if (_binValueMin[idx] < recentValueMin) 00125 { 00126 recentValueMin = _binValueMin[idx]; 00127 } 00128 if (_binValueMax[idx] > recentValueMax) 00129 { 00130 recentValueMax = _binValueMax[idx]; 00131 } 00132 recentDuration += recentBinnedDurations[idx]; 00133 } 00134 // update the working bin ID here so that we are ready for 00135 // the next calculation request 00136 ++_workingBinId; 00137 if (_workingBinId >= _binCount) { _workingBinId = 0; } 00138 // calculate the derived full values 00139 if (fullDuration > 0.0) 00140 { 00141 fullSampleRate = static_cast<double>(fullSampleCount) / fullDuration; 00142 fullValueRate = static_cast<double>(fullValueSum) / fullDuration; 00143 } 00144 else 00145 { 00146 fullSampleRate = 0.0; 00147 fullValueRate = 0.0; 00148 } 00149 if (fullSampleCount > 0) 00150 { 00151 fullValueAverage = fullValueSum / static_cast<double>(fullSampleCount); 00152 double squareAvg = fullValueSumOfSquares / static_cast<double>(fullSampleCount); 00153 double avg = fullValueSum / static_cast<double>(fullSampleCount); 00154 double sigSquared = squareAvg - avg * avg; 00155 if (sigSquared > 0.0) 00156 { 00157 fullValueRMS = sqrt(sigSquared); 00158 } 00159 else 00160 { 00161 fullValueRMS = 0.0; 00162 } 00163 } 00164 else 00165 { 00166 fullValueAverage = 0.0; 00167 fullValueRMS = 0.0; 00168 } 00169 // calculate the derived recent values 00170 if (recentDuration > 0.0) 00171 { 00172 recentSampleRate = static_cast<double>(recentSampleCount) / recentDuration; 00173 recentValueRate = static_cast<double>(recentValueSum) / recentDuration; 00174 } 00175 else 00176 { 00177 recentSampleRate = 0.0; 00178 recentValueRate = 0.0; 00179 } 00180 if (recentSampleCount > 0) 00181 { 00182 recentValueAverage = recentValueSum / static_cast<double>(recentSampleCount); 00183 double squareAvg = recentValueSumOfSquares / 00184 static_cast<double>(recentSampleCount); 00185 double avg = recentValueSum / static_cast<double>(recentSampleCount); 00186 double sigSquared = squareAvg - avg * avg; 00187 if (sigSquared > 0.0) 00188 { 00189 recentValueRMS = sqrt(sigSquared); 00190 } 00191 else 00192 { 00193 recentValueRMS = 0.0; 00194 } 00195 } 00196 else 00197 { 00198 recentValueAverage = 0.0; 00199 recentValueRMS = 0.0; 00200 } 00201 } 00202 return true; 00203 } 00204 00205 void MonitoredQuantity::_reset_accumulators() 00206 { 00207 _lastCalculationTime = 0; 00208 _workingSampleCount = 0; 00209 _workingValueSum = 0.0; 00210 _workingValueSumOfSquares = 0.0; 00211 _workingValueMin = INFINITY; 00212 _workingValueMax = -INFINITY; 00213 _workingLastSampleValue = 0; 00214 } 00215 00216 void MonitoredQuantity::_reset_results() 00217 { 00218 _workingBinId = 0; 00219 for (unsigned int idx = 0; idx < _binCount; ++idx) 00220 { 00221 recentBinnedSampleCounts[idx] = 0; 00222 recentBinnedValueSums[idx] = 0.0; 00223 _binValueSumOfSquares[idx] = 0.0; 00224 _binValueMin[idx] = INFINITY; 00225 _binValueMax[idx] = -INFINITY; 00226 recentBinnedDurations[idx] = 0.0; 00227 recentBinnedEndTimes[idx] = 0.0; 00228 } 00229 fullSampleCount = 0; 00230 fullSampleRate = 0.0; 00231 fullValueSum = 0.0; 00232 fullValueSumOfSquares = 0.0; 00233 fullValueAverage = 0.0; 00234 fullValueRMS = 0.0; 00235 fullValueMin = INFINITY; 00236 fullValueMax = -INFINITY; 00237 fullValueRate = 0.0; 00238 fullDuration = 0.0; 00239 recentSampleCount = 0; 00240 recentSampleRate = 0.0; 00241 recentValueSum = 0.0; 00242 recentValueSumOfSquares = 0.0; 00243 recentValueAverage = 0.0; 00244 recentValueRMS = 0.0; 00245 recentValueMin = INFINITY; 00246 recentValueMax = -INFINITY; 00247 recentValueRate = 0.0; 00248 recentDuration = 0.0; 00249 lastSampleValue = 0.0; 00250 lastValueRate = 0.0; 00251 } 00252 00253 void MonitoredQuantity::reset() 00254 { 00255 { 00256 boost::mutex::scoped_lock sl(_accumulationMutex); 00257 _reset_accumulators(); 00258 } 00259 { 00260 boost::mutex::scoped_lock sl(_resultsMutex); 00261 _reset_results(); 00262 } 00263 } 00264 00265 void MonitoredQuantity::enable() 00266 { 00267 if (!enabled) 00268 { 00269 reset(); 00270 enabled = true; 00271 } 00272 } 00273 00274 void MonitoredQuantity::disable() 00275 { 00276 // It is faster to just set _enabled to false than to test and set 00277 // it conditionally. 00278 enabled = false; 00279 } 00280 00281 void MonitoredQuantity::setNewTimeWindowForRecentResults(DURATION_T interval) 00282 { 00283 // lock the results objects since we're dramatically changing the 00284 // bins used for the recent results 00285 { 00286 boost::mutex::scoped_lock sl(_resultsMutex); 00287 _intervalForRecentStats = interval; 00288 // determine how many bins we should use in our sliding window 00289 // by dividing the input time window by the expected calculation 00290 // interval and rounding to the nearest integer. 00291 // In case that the calculation interval is larger then the 00292 // interval for recent stats, keep the last one. 00293 _binCount = std::max(1U, 00294 static_cast<unsigned int>( 00295 (_intervalForRecentStats / _expectedCalculationInterval) + 0.5 00296 ) 00297 ); 00298 // create the vectors for the binned quantities 00299 recentBinnedSampleCounts.reserve(_binCount); 00300 recentBinnedValueSums.reserve(_binCount); 00301 _binValueSumOfSquares.reserve(_binCount); 00302 _binValueMin.reserve(_binCount); 00303 _binValueMax.reserve(_binCount); 00304 recentBinnedDurations.reserve(_binCount); 00305 recentBinnedEndTimes.reserve(_binCount); 00306 _reset_results(); 00307 } 00308 { 00309 boost::mutex::scoped_lock sl(_accumulationMutex); 00310 _reset_accumulators(); 00311 } 00312 // call the reset method to populate the correct initial values 00313 // for the internal sample data 00314 //reset(); 00315 } 00316 00317 bool MonitoredQuantity:: 00318 waitUntilAccumulatorsHaveBeenFlushed(DURATION_T timeout) const 00319 { 00320 timeout /= 10; 00321 { 00322 boost::mutex::scoped_lock sl(_accumulationMutex); 00323 if (_workingSampleCount == 0) { return true; } 00324 } 00325 auto sleepTime = static_cast<long>(timeout * 100000.0); 00326 for (auto idx = 0; idx < 10; ++idx) 00327 { 00328 usleep(sleepTime); 00329 { 00330 boost::mutex::scoped_lock sl(_accumulationMutex); 00331 if (_workingSampleCount == 0) { return true; } 00332 } 00333 } 00334 return false; 00335 } 00336 00337 void 00338 MonitoredQuantity::getStats(MonitoredQuantityStats& s) const 00339 { 00340 boost::mutex::scoped_lock results(_resultsMutex); 00341 s.fullSampleCount = fullSampleCount; 00342 s.fullSampleRate = fullSampleRate; 00343 s.fullValueSum = fullValueSum; 00344 s.fullValueSumOfSquares = fullValueSumOfSquares; 00345 s.fullValueAverage = fullValueAverage; 00346 s.fullValueRMS = fullValueRMS; 00347 s.fullValueMin = fullValueMin; 00348 s.fullValueMax = fullValueMax; 00349 s.fullValueRate = fullValueRate; 00350 s.fullDuration = fullDuration; 00351 s.recentSampleCount = recentSampleCount; 00352 s.recentSampleRate = recentSampleRate; 00353 s.recentValueSum = recentValueSum; 00354 s.recentValueSumOfSquares = recentValueSumOfSquares; 00355 s.recentValueAverage = recentValueAverage; 00356 s.recentValueRMS = recentValueRMS; 00357 s.recentValueMin = recentValueMin; 00358 s.recentValueMax = recentValueMax; 00359 s.recentValueRate = recentValueRate; 00360 s.recentDuration = recentDuration; 00361 s.recentBinnedSampleCounts.resize(_binCount); 00362 s.recentBinnedValueSums.resize(_binCount); 00363 s.recentBinnedDurations.resize(_binCount); 00364 s.recentBinnedEndTimes.resize(_binCount); 00365 unsigned int sourceBinId = _workingBinId; 00366 for (unsigned int idx = 0; idx < _binCount; ++idx) 00367 { 00368 if (sourceBinId >= _binCount) { sourceBinId = 0; } 00369 s.recentBinnedSampleCounts[idx] = recentBinnedSampleCounts[sourceBinId]; 00370 s.recentBinnedValueSums[idx] = recentBinnedValueSums[sourceBinId]; 00371 s.recentBinnedDurations[idx] = recentBinnedDurations[sourceBinId]; 00372 s.recentBinnedEndTimes[idx] = recentBinnedEndTimes[sourceBinId]; 00373 ++sourceBinId; 00374 } 00375 s.lastSampleValue = lastSampleValue; 00376 s.lastValueRate = lastValueRate; 00377 s.lastCalculationTime = lastCalculationTime; 00378 s.enabled = enabled; 00379 } 00380 00381 MonitoredQuantity::TIME_POINT_T MonitoredQuantity::getCurrentTime() 00382 { 00383 TIME_POINT_T result = -1.0; 00384 timeval now; 00385 if (gettimeofday(&now, 0) == 0) 00386 { 00387 result = static_cast<TIME_POINT_T>(now.tv_sec); 00388 result += static_cast<TIME_POINT_T>(now.tv_usec) / (1000 * 1000); 00389 } 00390 return result; 00391 } 00392 00393 MonitoredQuantity::TIME_POINT_T MonitoredQuantity::getLastCalculationTime() const 00394 { 00395 boost::mutex::scoped_lock results(_resultsMutex); 00396 return lastCalculationTime; 00397 } 00398 00399 MonitoredQuantity::DURATION_T MonitoredQuantity::getFullDuration() const 00400 { 00401 boost::mutex::scoped_lock results(_resultsMutex); 00402 return fullDuration; 00403 } 00404 00405 double MonitoredQuantity::getRecentValueSum() const 00406 { 00407 boost::mutex::scoped_lock results(_resultsMutex); 00408 return recentValueSum; 00409 } 00410 00411 double MonitoredQuantity::getRecentValueAverage() const 00412 { 00413 boost::mutex::scoped_lock results(_resultsMutex); 00414 return recentValueAverage; 00415 } 00416 00417 long long MonitoredQuantity::getFullSampleCount() const 00418 { 00419 boost::mutex::scoped_lock results(_resultsMutex); 00420 return fullSampleCount; 00421 }