/* PIP - Platform Independent Primitives Speed and quality in/out diagnostics Copyright (C) 2015 Ivan Pelipenko peri4ko@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "pidiagnostics.h" /** \class PIDiagnostics * \brief Connection quality diagnostics * \details * \section PIDiagnostics_sec0 Synopsis * This class provide abstract connection quality diagnostics and * counting. You should create instance of %PIDiagnostics and on * packet receive call function \a received(), on packet send call * function \a sended(). %PIDiagnostics calculates correct, wrong * and sended counters, packets per second, bytes per seconds, * immediate and integral receive frequencies and receive/send speeds * in human readable representation. There statistics are calculates * one time per period, by default 1 second. To calculate them you * should start %PIDiagnostics with function \a start() or pass \b true * to constructor. * */ PIDiagnostics::PIDiagnostics(bool start_): PITimer(PITimer::Pool) { reset(); if (start_) start(); } void PIDiagnostics::reset() { setDisconnectTimeout(3.); lock(); qual = PIDiagnostics::Unknown; speedIn = speedOut = PIString::readableSize(0) + "/s"; ifreq = immediate_freq = integral_freq = 0.f; cur_pckt = rec_once = 0; wrong_count = receive_count = send_count = 0; packets_in_sec = packets_out_sec = bytes_in_sec = bytes_out_sec = 0; unlock(); } void PIDiagnostics::received(int size, bool correct) { lock(); rec_once = 1; if (correct) { float el = tm.elapsed_s(); tm.reset(); if (el > 0.f) immediate_freq = ifreq = 1.f / el; else immediate_freq = ifreq = 0.f; receive_count++; } else { immediate_freq = ifreq = 0.f; wrong_count++; } addToHistory(history_rec, size, correct); unlock(); } void PIDiagnostics::sended(int size) { lock(); send_count++; addToHistory(history_send, size); unlock(); } void PIDiagnostics::tick(void * data, int delimiter) { lock(); checkHistory(history_rec); checkHistory(history_send); PIDiagnostics::Quality diag; immediate_freq = ifreq; ifreq = 0.f; int bps[2]; int cpckt[2]; bps[0] = bps[1] = 0; cpckt[0] = cpckt[1] = 0; packets_in_sec = packets_out_sec = 0; piForeachC (Entry & e, history_rec) { if (e.ok) { bps[0] += e.bytes; packets_in_sec++; } cpckt[e.ok ? 1 : 0]++; } piForeachC (Entry & e, history_send) { bps[1] += e.bytes; packets_out_sec++; } bytes_in_sec = bps[0] / disconn_; bytes_out_sec = bps[1] / disconn_; packets_in_sec /= disconn_; packets_out_sec /= disconn_; speedIn = PIString::readableSize(bytes_in_sec) + "/s"; speedOut = PIString::readableSize(bytes_out_sec) + "/s"; int arc = cpckt[0] + cpckt[1]; float good_percents = 0.f; if (arc > 0) good_percents = (float)cpckt[1] / arc * 100.f; if (disconn_ > 0.) integral_freq = cpckt[1] / disconn_; else integral_freq = 0.; if (rec_once == 0) { diag = PIDiagnostics::Unknown; } else { if (good_percents == 0.f) diag = PIDiagnostics::Failure; else if (good_percents <= 20.f) diag = PIDiagnostics::Bad; else if (good_percents > 20.f && good_percents <= 80.f) diag = PIDiagnostics::Average; else diag = PIDiagnostics::Good; } if (diag != qual) { qualityChanged(diag, qual); qual = diag; } unlock(); } void PIDiagnostics::addToHistory(PIVector & hist, int bytes, bool ok) { Entry e; e.time = PISystemTime::current(true); e.bytes = bytes; e.ok = ok; checkHistory(hist); hist << e; } void PIDiagnostics::checkHistory(PIVector< PIDiagnostics::Entry > & hist) { PISystemTime ctm = PISystemTime::current(true); for (int i = 0; i < hist.size_s(); ++i) { if ((ctm - hist[i].time).abs() > disconn_st) { hist.remove(i); --i; } } } void PIDiagnostics::propertyChanged(const PIString &) { disconn_ = property("disconnectTimeout").toFloat(); changeDisconnectTimeout(); } void PIDiagnostics::changeDisconnectTimeout() { lock(); disconn_st = PISystemTime::fromSeconds(disconn_); unlock(); }