modbus_relay/connection/stats/
connection.rs

1use std::{
2    collections::HashMap,
3    net::SocketAddr,
4    time::{Duration, SystemTime},
5};
6
7use serde::Serialize;
8
9use super::{ClientStats, IpStats};
10
11#[derive(Debug, Serialize)]
12pub struct Stats {
13    pub total_connections: u64,
14    pub active_connections: usize,
15    pub total_requests: u64,
16    pub total_errors: u64,
17    pub requests_per_second: f64,
18    pub avg_response_time_ms: u64,
19    pub per_ip_stats: HashMap<SocketAddr, IpStats>,
20}
21
22impl Stats {
23    pub fn from_client_stats(stats: &HashMap<SocketAddr, ClientStats>) -> Self {
24        let mut total_active = 0;
25        let mut total_requests = 0;
26        let mut total_errors = 0;
27        let mut total_response_time = 0u64;
28        let mut response_time_count = 0;
29        let mut per_ip = HashMap::new();
30
31        // Calculate totals and build per-IP stats
32        for (addr, client) in stats {
33            total_active += client.active_connections;
34            total_requests += client.total_requests;
35            total_errors += client.total_errors;
36
37            if client.avg_response_time_ms > 0 {
38                total_response_time += client.avg_response_time_ms;
39                response_time_count += 1;
40            }
41
42            per_ip.insert(
43                *addr,
44                IpStats {
45                    active_connections: client.active_connections,
46                    total_requests: client.total_requests,
47                    total_errors: client.total_errors,
48                    last_active: client.last_active,
49                    last_error: client.last_error,
50                    avg_response_time_ms: client.avg_response_time_ms,
51                },
52            );
53        }
54
55        Self {
56            total_connections: total_active as u64,
57            active_connections: total_active,
58            total_requests,
59            total_errors,
60            requests_per_second: Self::calculate_requests_per_second(stats),
61            avg_response_time_ms: if response_time_count > 0 {
62                total_response_time / response_time_count
63            } else {
64                0
65            },
66            per_ip_stats: per_ip,
67        }
68    }
69
70    fn calculate_requests_per_second(stats: &HashMap<SocketAddr, ClientStats>) -> f64 {
71        let now = SystemTime::now();
72        let window = Duration::from_secs(60);
73        let mut recent_requests = 0;
74
75        for client in stats.values() {
76            if let Ok(duration) = now.duration_since(client.last_active)
77                && duration <= window
78            {
79                recent_requests += client.total_requests as usize;
80            }
81        }
82
83        recent_requests as f64 / window.as_secs_f64()
84    }
85}