Initial commit - Pingerino network monitoring application

هذا الالتزام موجود في:
Era
2025-08-19 07:34:27 +03:00
التزام 243f4787dd
41 ملفات معدلة مع 12352 إضافات و0 حذوفات

255
Services/PingService.cs Normal file
عرض الملف

@@ -0,0 +1,255 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using Pingerino.Services.Interfaces;
namespace Pingerino.Services
{
public class PingService : IPingService
{
private readonly ILoggingService _logger;
private readonly List<long> _pingRoundTripTimes = new List<long>();
private readonly List<double> _jitterValues = new List<double>();
private readonly List<double> _packetLossValues = new List<double>();
private readonly object _lockObject = new object();
private Timer _pingTimer;
private string _currentIpAddress;
private bool _disposed;
private CancellationTokenSource _cancellationTokenSource;
public event EventHandler<PingResultEventArgs> PingCompleted;
public event EventHandler<NetworkStatisticsEventArgs> StatisticsUpdated;
public bool IsRunning { get; private set; }
public PingService(ILoggingService logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async System.Threading.Tasks.Task StartPingingAsync(string ipAddress, int intervalMs, CancellationToken cancellationToken = default)
{
if (IsRunning)
{
await StopPingingAsync();
}
_currentIpAddress = ipAddress;
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_pingTimer = new Timer(async _ => await PingCallback(), null, 0, intervalMs);
IsRunning = true;
_logger.LogInformation($"Started pinging {ipAddress} with interval {intervalMs}ms");
}
public async System.Threading.Tasks.Task StopPingingAsync()
{
if (!IsRunning) return;
_pingTimer?.Dispose();
_cancellationTokenSource?.Cancel();
IsRunning = false;
_logger.LogInformation("Stopped pinging");
await System.Threading.Tasks.Task.CompletedTask;
}
public async System.Threading.Tasks.Task<PingResult> PingOnceAsync(string ipAddress, CancellationToken cancellationToken = default)
{
try
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
return new PingResult
{
IpAddress = ipAddress,
IsSuccess = false,
Status = "No network connection available",
ErrorMessage = "Network unavailable"
};
}
using (var ping = new Ping())
{
var reply = await ping.SendPingAsync(ipAddress);
var result = new PingResult
{
IpAddress = ipAddress,
IsSuccess = reply.Status == IPStatus.Success,
RoundTripTime = reply.RoundtripTime,
Status = reply.Status.ToString()
};
if (result.IsSuccess)
{
UpdateStatistics(reply.RoundtripTime);
}
return result;
}
}
catch (Exception ex)
{
_logger.LogError($"Error pinging {ipAddress}", ex);
return new PingResult
{
IpAddress = ipAddress,
IsSuccess = false,
Status = "Error",
ErrorMessage = ex.Message
};
}
}
public void ClearStatistics()
{
lock (_lockObject)
{
_pingRoundTripTimes.Clear();
_jitterValues.Clear();
_packetLossValues.Clear();
}
_logger.LogInformation("Statistics cleared");
}
public NetworkStatistics GetCurrentStatistics()
{
lock (_lockObject)
{
var stats = new NetworkStatistics();
if (_pingRoundTripTimes.Count > 0)
{
stats.MaxPing = _pingRoundTripTimes.Max();
stats.MinPing = _pingRoundTripTimes.Min();
stats.AveragePing = _pingRoundTripTimes.Average();
stats.TotalPings = _pingRoundTripTimes.Count;
stats.SuccessfulPings = _pingRoundTripTimes.Count;
}
if (_jitterValues.Count > 0)
{
stats.MaxJitter = _jitterValues.Max();
stats.MinJitter = _jitterValues.Min();
stats.AverageJitter = _jitterValues.Average();
stats.Jitter = stats.AverageJitter;
}
if (_packetLossValues.Count > 0)
{
stats.MaxPacketLoss = _packetLossValues.Max();
stats.MinPacketLoss = _packetLossValues.Min();
stats.AveragePacketLoss = _packetLossValues.Average();
stats.PacketLoss = stats.AveragePacketLoss;
}
return stats;
}
}
private async System.Threading.Tasks.Task PingCallback()
{
if (_cancellationTokenSource?.Token.IsCancellationRequested == true)
return;
try
{
var result = await PingOnceAsync(_currentIpAddress, _cancellationTokenSource?.Token ?? CancellationToken.None);
PingCompleted?.Invoke(this, new PingResultEventArgs(result, DateTime.Now));
// Calculate and update network statistics periodically
await CalculateNetworkStatisticsAsync();
}
catch (Exception ex)
{
_logger.LogError("Error in ping callback", ex);
}
}
private void UpdateStatistics(long roundTripTime)
{
lock (_lockObject)
{
_pingRoundTripTimes.Add(roundTripTime);
// Calculate jitter if we have previous measurements
if (_pingRoundTripTimes.Count > 1)
{
var previousTime = _pingRoundTripTimes[_pingRoundTripTimes.Count - 2];
var jitter = Math.Abs(roundTripTime - previousTime);
_jitterValues.Add(jitter);
}
}
}
private async System.Threading.Tasks.Task CalculateNetworkStatisticsAsync()
{
try
{
// Calculate packet loss over the last 10 pings
var packetLoss = await CalculatePacketLossAsync(_currentIpAddress, 10);
lock (_lockObject)
{
_packetLossValues.Add(packetLoss);
}
var stats = GetCurrentStatistics();
StatisticsUpdated?.Invoke(this, new NetworkStatisticsEventArgs(stats));
}
catch (Exception ex)
{
_logger.LogError("Error calculating network statistics", ex);
}
}
private async System.Threading.Tasks.Task<double> CalculatePacketLossAsync(string ipAddress, int count)
{
int failedPings = 0;
var tasks = new List<System.Threading.Tasks.Task<bool>>();
for (int i = 0; i < count; i++)
{
tasks.Add(TestSinglePingAsync(ipAddress));
}
var results = await System.Threading.Tasks.Task.WhenAll(tasks);
failedPings = results.Count(r => !r);
return (double)failedPings / count * 100;
}
private async System.Threading.Tasks.Task<bool> TestSinglePingAsync(string ipAddress)
{
try
{
using (var ping = new Ping())
{
var reply = await ping.SendPingAsync(ipAddress);
return reply.Status == IPStatus.Success;
}
}
catch
{
return false;
}
}
public void Dispose()
{
if (_disposed) return;
_pingTimer?.Dispose();
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
_disposed = true;
}
}
}