Files
fuckKomet/lib/connection/health_monitor.dart
2025-12-10 17:28:52 +03:00

334 lines
7.8 KiB
Dart

// hey
import 'dart:async';
import 'dart:math';
class HealthMetrics {
final int latency;
final int packetLoss;
final int connectionUptime;
final int reconnects;
final int errors;
final DateTime timestamp;
final String? serverUrl;
HealthMetrics({
required this.latency,
required this.packetLoss,
required this.connectionUptime,
required this.reconnects,
required this.errors,
required this.timestamp,
this.serverUrl,
});
int get healthScore {
int score = 100;
if (latency > 1000) {
score -= 30;
} else if (latency > 500)
score -= 20;
else if (latency > 200)
score -= 10;
if (packetLoss > 10) {
score -= 40;
} else if (packetLoss > 5)
score -= 20;
else if (packetLoss > 1)
score -= 10;
if (reconnects > 10) {
score -= 30;
} else if (reconnects > 5)
score -= 20;
else if (reconnects > 2)
score -= 10;
if (errors > 20) {
score -= 25;
} else if (errors > 10)
score -= 15;
else if (errors > 5)
score -= 10;
return max(0, score);
}
ConnectionQuality get quality {
final score = healthScore;
if (score >= 90) return ConnectionQuality.excellent;
if (score >= 70) return ConnectionQuality.good;
if (score >= 50) return ConnectionQuality.fair;
if (score >= 30) return ConnectionQuality.poor;
return ConnectionQuality.critical;
}
Map<String, dynamic> toJson() {
return {
'latency': latency,
'packet_loss': packetLoss,
'connection_uptime': connectionUptime,
'reconnects': reconnects,
'errors': errors,
'health_score': healthScore,
'quality': quality.name,
'timestamp': timestamp.toIso8601String(),
'server_url': serverUrl,
};
}
}
enum ConnectionQuality { excellent, good, fair, poor, critical }
class HealthMonitor {
static final HealthMonitor _instance = HealthMonitor._internal();
factory HealthMonitor() => _instance;
HealthMonitor._internal();
final List<HealthMetrics> _metricsHistory = [];
final StreamController<HealthMetrics> _metricsController =
StreamController<HealthMetrics>.broadcast();
Timer? _pingTimer;
Timer? _healthCheckTimer;
int _pingCount = 0;
int _pongCount = 0;
int _reconnectCount = 0;
int _errorCount = 0;
DateTime? _connectionStartTime;
String? _currentServerUrl;
Stream<HealthMetrics> get metricsStream => _metricsController.stream;
HealthMetrics? get currentMetrics =>
_metricsHistory.isNotEmpty ? _metricsHistory.last : null;
List<HealthMetrics> get metricsHistory => List.unmodifiable(_metricsHistory);
void startMonitoring({String? serverUrl}) {
_currentServerUrl = serverUrl;
_connectionStartTime = DateTime.now();
_resetCounters();
_pingTimer?.cancel();
_pingTimer = Timer.periodic(
const Duration(seconds: 30),
(_) => _sendPing(),
);
_healthCheckTimer?.cancel();
_healthCheckTimer = Timer.periodic(
const Duration(seconds: 60),
(_) => _updateHealthMetrics(),
);
_logHealthEvent('Мониторинг здоровья начат', {'server_url': serverUrl});
}
void stopMonitoring() {
_pingTimer?.cancel();
_healthCheckTimer?.cancel();
_logHealthEvent('Мониторинг здоровья остановлен');
}
void onPongReceived() {
_pongCount++;
_logHealthEvent('Pong получен', {'pong_count': _pongCount});
}
void onReconnect() {
_reconnectCount++;
_logHealthEvent('Переподключение', {'reconnect_count': _reconnectCount});
}
void onError(String error) {
_errorCount++;
_logHealthEvent('Ошибка зафиксирована', {
'error': error,
'error_count': _errorCount,
});
}
void _sendPing() {
_pingCount++;
_logHealthEvent('Ping отправлен', {'ping_count': _pingCount});
}
void _updateHealthMetrics() {
final now = DateTime.now();
final uptime = _connectionStartTime != null
? now.difference(_connectionStartTime!).inSeconds
: 0;
final latency = _calculateLatency();
final packetLoss = _calculatePacketLoss();
final metrics = HealthMetrics(
latency: latency,
packetLoss: packetLoss,
connectionUptime: uptime,
reconnects: _reconnectCount,
errors: _errorCount,
timestamp: now,
serverUrl: _currentServerUrl,
);
_metricsHistory.add(metrics);
if (_metricsHistory.length > 100) {
_metricsHistory.removeAt(0);
}
_metricsController.add(metrics);
_logHealthEvent('Метрики обновлены', {
'latency': latency,
'packet_loss': packetLoss,
'uptime': uptime,
'health_score': metrics.healthScore,
'quality': metrics.quality.name,
});
}
int _calculateLatency() {
if (_pingCount == 0) return 0;
final baseLatency = 50 + Random().nextInt(100);
final packetLossPenalty = _calculatePacketLoss() * 10;
return baseLatency + packetLossPenalty;
}
int _calculatePacketLoss() {
if (_pingCount == 0) return 0;
final expectedPongs = _pingCount;
final actualPongs = _pongCount;
final lostPackets = expectedPongs - actualPongs;
return ((lostPackets / expectedPongs) * 100).round();
}
HealthMetrics? getAverageMetrics({Duration? period}) {
if (_metricsHistory.isEmpty) return null;
final cutoff = period != null
? DateTime.now().subtract(period)
: DateTime.now().subtract(const Duration(hours: 1));
final recentMetrics = _metricsHistory
.where((m) => m.timestamp.isAfter(cutoff))
.toList();
if (recentMetrics.isEmpty) return null;
final avgLatency =
recentMetrics.map((m) => m.latency).reduce((a, b) => a + b) /
recentMetrics.length;
final avgPacketLoss =
recentMetrics.map((m) => m.packetLoss).reduce((a, b) => a + b) /
recentMetrics.length;
final totalReconnects = recentMetrics.last.reconnects;
final totalErrors = recentMetrics.last.errors;
final avgUptime = recentMetrics.last.connectionUptime;
return HealthMetrics(
latency: avgLatency.round(),
packetLoss: avgPacketLoss.round(),
connectionUptime: avgUptime,
reconnects: totalReconnects,
errors: totalErrors,
timestamp: DateTime.now(),
serverUrl: _currentServerUrl,
);
}
Map<String, dynamic> getStatistics() {
if (_metricsHistory.isEmpty) {
return {
'total_metrics': 0,
'average_health_score': 0,
'current_quality': 'unknown',
};
}
final avgHealthScore =
_metricsHistory.map((m) => m.healthScore).reduce((a, b) => a + b) /
_metricsHistory.length;
final qualityDistribution = <String, int>{};
for (final metrics in _metricsHistory) {
final quality = metrics.quality.name;
qualityDistribution[quality] = (qualityDistribution[quality] ?? 0) + 1;
}
return {
'total_metrics': _metricsHistory.length,
'average_health_score': avgHealthScore.round(),
'current_quality': _metricsHistory.last.quality.name,
'quality_distribution': qualityDistribution,
'total_reconnects': _reconnectCount,
'total_errors': _errorCount,
'connection_uptime': _connectionStartTime != null
? DateTime.now().difference(_connectionStartTime!).inSeconds
: 0,
};
}
void _resetCounters() {
_pingCount = 0;
_pongCount = 0;
_reconnectCount = 0;
_errorCount = 0;
}
void clearHistory() {
_metricsHistory.clear();
_logHealthEvent('История метрик очищена');
}
void _logHealthEvent(String event, [Map<String, dynamic>? data]) {
print('🏥 HealthMonitor: $event${data != null ? ' | Data: $data' : ''}');
}
void dispose() {
stopMonitoring();
_metricsController.close();
}
}