Files
pip/libs/main/io_utils/pidiagnostics.cpp
peri4 1c7fc39b6c version 4.0.0_alpha
in almost all methods removed timeouts in milliseconds, replaced to PISystemTime
PITimer rewrite, remove internal impl, now only thread implementation, API similar to PIThread
PITimer API no longer pass void*
PIPeer, PIConnection improved stability on reinit and exit
PISystemTime new methods
pisd now exit without hanging
2024-07-30 14:18:02 +03:00

253 lines
7.1 KiB
C++

/*
PIP - Platform Independent Primitives
Speed and quality in/out diagnostics
Andrey Bychkov work.a.b@yandex.ru, Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pidiagnostics.h"
#include "piliterals_time.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::State::State() {
receive_speed = send_speed = PIString::readableSize(0) + "/s";
}
PIDiagnostics::PIDiagnostics(bool start_): PITimer() {
// piCout << "PIDiagnostics construct";
setInterval(10_Hz);
reset();
setDisconnectTimeout(3_s);
changeDisconnectTimeout(3_s);
if (start_) PITimer::start(10_Hz);
// piCout << "PIDiagnostics construct done";
}
PIDiagnostics::~PIDiagnostics() {
PITimer::stopAndWait();
// piCout << "~PIDiagnostics start...";
// piCout << "~PIDiagnostics done!";
}
PIDiagnostics::State PIDiagnostics::state() const {
mutex_state.lock();
State ret = cur_state;
mutex_state.unlock();
return ret;
}
PIDiagnostics::Quality PIDiagnostics::quality() const {
PIDiagnostics::Quality ret;
mutex_state.lock();
ret = cur_state.quality;
mutex_state.unlock();
return ret;
}
PIString PIDiagnostics::receiveSpeed() const {
mutex_state.lock();
PIString ret = cur_state.receive_speed;
mutex_state.unlock();
return ret;
}
PIString PIDiagnostics::sendSpeed() const {
mutex_state.lock();
PIString ret = cur_state.send_speed;
mutex_state.unlock();
return ret;
}
void PIDiagnostics::start() {
PITimer::start(10_Hz);
changeDisconnectTimeout(disconn_);
}
void PIDiagnostics::start(double msecs) {
if (msecs > 0.) {
PITimer::start(PISystemTime::fromMilliseconds(msecs));
changeDisconnectTimeout(disconn_);
}
}
void PIDiagnostics::reset() {
mutex_state.lock();
cur_state = State();
if (disconn_.isNotNull()) {
int hist_size = history_rec.size();
history_rec.clear();
history_send.clear();
history_rec.resize(hist_size);
history_send.resize(hist_size);
}
mutex_state.unlock();
}
void PIDiagnostics::received(int size, bool correct) {
mutex_state.lock();
Entry & e(history_rec.front());
if (correct) {
e.cnt_ok++;
e.bytes_ok += size;
cur_state.received_packets++;
cur_state.received_bytes += size;
} else {
e.cnt_fail++;
e.bytes_fail += size;
cur_state.received_packets_wrong++;
cur_state.received_bytes_wrong += size;
}
e.empty = false;
mutex_state.unlock();
}
void PIDiagnostics::sended(int size) {
mutex_state.lock();
Entry & e(history_send.front());
e.cnt_ok++;
e.bytes_ok += size;
cur_state.sended_packets++;
cur_state.sended_bytes += size;
e.empty = false;
mutex_state.unlock();
}
void PIDiagnostics::tick(int) {
mutex_state.lock();
// piCoutObj << "lock";
int tcnt_recv = 0;
int tcnt_send = 0;
Entry send = calcHistory(history_send, tcnt_send);
Entry recv = calcHistory(history_rec, tcnt_recv);
float itr = disconn_.toSeconds() * (float(tcnt_recv) / history_rec.size());
float its = disconn_.toSeconds() * (float(tcnt_send) / history_send.size());
float hz = 1. / interval().toSeconds();
if (tcnt_recv == 0) {
cur_state.integral_freq = cur_state.immediate_freq = 0;
cur_state.received_packets_per_sec = cur_state.received_bytes_per_sec = 0;
} else {
cur_state.integral_freq = recv.cnt_ok / itr;
cur_state.received_packets_per_sec = ullong(float(recv.cnt_ok) / itr);
cur_state.received_bytes_per_sec = ullong(double(recv.bytes_ok) / itr);
cur_state.immediate_freq = double(history_rec.front().cnt_ok) / hz;
}
if (tcnt_send == 0) {
cur_state.sended_packets_per_sec = cur_state.sended_bytes_per_sec = 0;
} else {
cur_state.sended_packets_per_sec = ullong(float(send.cnt_ok) / its);
cur_state.sended_bytes_per_sec = ullong(double(send.bytes_ok) / its);
}
// piCoutObj << "tick" << recv.cnt_ok << send.cnt_ok;
cur_state.receive_speed = PIString::readableSize(cur_state.received_bytes_per_sec) + "/s";
cur_state.send_speed = PIString::readableSize(cur_state.sended_bytes_per_sec) + "/s";
int arc = recv.cnt_ok + recv.cnt_fail;
float good_percents = 0.f;
if (arc > 0) good_percents = (float)recv.cnt_ok / arc * 100.f;
PIDiagnostics::Quality diag;
if (tcnt_recv == 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 ((tcnt_send + tcnt_recv) != 0) {
// piCoutObj << tcnt_recv << tcnt_send;
history_rec.dequeue();
history_send.dequeue();
Entry e;
e.empty = false;
history_rec.enqueue(e);
history_send.enqueue(e);
}
if (diag != cur_state.quality) {
qualityChanged(diag, cur_state.quality);
cur_state.quality = diag;
}
mutex_state.unlock();
// piCoutObj << "unlock";
}
PIDiagnostics::Entry PIDiagnostics::calcHistory(PIQueue<Entry> & hist, int & cnt) {
Entry e;
cnt = 0;
for (int i = 0; i < hist.size_s(); ++i) {
e.bytes_ok += hist[i].bytes_ok;
e.bytes_fail += hist[i].bytes_fail;
e.cnt_ok += hist[i].cnt_ok;
e.cnt_fail += hist[i].cnt_fail;
if (!hist[i].empty) cnt++;
}
e.empty = false;
// piCoutObj << hist.size() << cnt;
return e;
}
void PIDiagnostics::propertyChanged(const char *) {
changeDisconnectTimeout(property("disconnectTimeout").toSystemTime());
}
void PIDiagnostics::changeDisconnectTimeout(PISystemTime disct) {
mutex_state.lock();
disconn_ = piMax(disct, interval());
if (!interval().isNull()) {
int hist_size = piClampi(piRound(disconn_.toSeconds() / interval().toSeconds()), 1, 65536);
// piCoutObj << hist_size << interval();
history_rec.resize(hist_size);
history_send.resize(hist_size);
} else {
history_rec.resize(1);
history_send.resize(1);
}
// piCoutObj << hist_size << disconn_ << interval();
mutex_state.unlock();
}