// 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 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 _metricsHistory = []; final StreamController _metricsController = StreamController.broadcast(); Timer? _pingTimer; Timer? _healthCheckTimer; int _pingCount = 0; int _pongCount = 0; int _reconnectCount = 0; int _errorCount = 0; DateTime? _connectionStartTime; String? _currentServerUrl; Stream get metricsStream => _metricsController.stream; HealthMetrics? get currentMetrics => _metricsHistory.isNotEmpty ? _metricsHistory.last : null; List 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 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 = {}; 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? data]) { print('🏥 HealthMonitor: $event${data != null ? ' | Data: $data' : ''}'); } void dispose() { stopMonitoring(); _metricsController.close(); } }