Initial commit - Pingerino network monitoring application
هذا الالتزام موجود في:
117
Services/ConfigurationService.cs
Normal file
117
Services/ConfigurationService.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using Pingerino.Services.Interfaces;
|
||||
|
||||
namespace Pingerino.Services
|
||||
{
|
||||
public class ConfigurationService : IConfigurationService
|
||||
{
|
||||
private readonly ILoggingService _logger;
|
||||
|
||||
public ConfigurationService(ILoggingService logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
Load();
|
||||
}
|
||||
|
||||
public T GetValue<T>(string key, T defaultValue = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var property = Properties.Settings.Default.GetType().GetProperty(key);
|
||||
if (property != null)
|
||||
{
|
||||
var value = property.GetValue(Properties.Settings.Default);
|
||||
if (value != null && value is T)
|
||||
{
|
||||
return (T)value;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error getting configuration value for key: {key}", ex);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValue<T>(string key, T value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var property = Properties.Settings.Default.GetType().GetProperty(key);
|
||||
if (property != null && property.CanWrite)
|
||||
{
|
||||
property.SetValue(Properties.Settings.Default, value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error setting configuration value for key: {key}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
Properties.Settings.Default.Save();
|
||||
_logger.LogInformation("Configuration saved successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error saving configuration", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
Properties.Settings.Default.Reload();
|
||||
_logger.LogInformation("Configuration loaded successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error loading configuration", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public string LastUsedIpAddress
|
||||
{
|
||||
get => GetValue(nameof(Properties.Settings.Default.LastUsedIpAddress), "8.8.8.8");
|
||||
set => SetValue(nameof(Properties.Settings.Default.LastUsedIpAddress), value);
|
||||
}
|
||||
|
||||
public int PingInterval
|
||||
{
|
||||
get => GetValue("PingInterval", 300);
|
||||
set => SetValue("PingInterval", value);
|
||||
}
|
||||
|
||||
public bool AutoPingEnabled
|
||||
{
|
||||
get => GetValue(nameof(Properties.Settings.Default.AutoPingEnabled), false);
|
||||
set => SetValue(nameof(Properties.Settings.Default.AutoPingEnabled), value);
|
||||
}
|
||||
|
||||
public Point FormPosition
|
||||
{
|
||||
get => GetValue(nameof(Properties.Settings.Default.FormPosition), Point.Empty);
|
||||
set => SetValue(nameof(Properties.Settings.Default.FormPosition), value);
|
||||
}
|
||||
|
||||
public string IpAddressValue
|
||||
{
|
||||
get => GetValue(nameof(Properties.Settings.Default.IpAddressValue), "8.8.8.8");
|
||||
set => SetValue(nameof(Properties.Settings.Default.IpAddressValue), value);
|
||||
}
|
||||
|
||||
public string TextBoxIntervalValue
|
||||
{
|
||||
get => GetValue(nameof(Properties.Settings.Default.TextBoxIntervalValue), "300");
|
||||
set => SetValue(nameof(Properties.Settings.Default.TextBoxIntervalValue), value);
|
||||
}
|
||||
}
|
||||
}
|
21
Services/Interfaces/IConfigurationService.cs
Normal file
21
Services/Interfaces/IConfigurationService.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Pingerino.Services.Interfaces
|
||||
{
|
||||
public interface IConfigurationService
|
||||
{
|
||||
T GetValue<T>(string key, T defaultValue = default);
|
||||
void SetValue<T>(string key, T value);
|
||||
void Save();
|
||||
void Load();
|
||||
|
||||
// Specific application settings
|
||||
string LastUsedIpAddress { get; set; }
|
||||
int PingInterval { get; set; }
|
||||
bool AutoPingEnabled { get; set; }
|
||||
Point FormPosition { get; set; }
|
||||
string IpAddressValue { get; set; }
|
||||
string TextBoxIntervalValue { get; set; }
|
||||
}
|
||||
}
|
20
Services/Interfaces/ILoggingService.cs
Normal file
20
Services/Interfaces/ILoggingService.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Pingerino.Services.Interfaces
|
||||
{
|
||||
public interface ILoggingService
|
||||
{
|
||||
void LogInformation(string message);
|
||||
void LogWarning(string message);
|
||||
void LogError(string message, Exception exception = null);
|
||||
void LogDebug(string message);
|
||||
}
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Debug,
|
||||
Information,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
}
|
31
Services/Interfaces/INetworkService.cs
Normal file
31
Services/Interfaces/INetworkService.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pingerino.Services.Interfaces
|
||||
{
|
||||
public interface INetworkService : IDisposable
|
||||
{
|
||||
System.Threading.Tasks.Task<string> GetPublicIpAddressAsync(CancellationToken cancellationToken = default);
|
||||
System.Threading.Tasks.Task<List<NetworkAdapterInfo>> GetNetworkAdaptersAsync();
|
||||
System.Threading.Tasks.Task<bool> ResetNetworkAdapterAsync(string adapterName, IProgress<int> progress = null, CancellationToken cancellationToken = default);
|
||||
System.Threading.Tasks.Task<NetworkAdapterInfo> GetActiveEthernetAdapterAsync();
|
||||
System.Threading.Tasks.Task<bool> IsNetworkAvailableAsync();
|
||||
}
|
||||
|
||||
public class NetworkAdapterInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string PhysicalAddress { get; set; }
|
||||
public bool IsDhcpEnabled { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
public string SubnetMask { get; set; }
|
||||
public string DefaultGateway { get; set; }
|
||||
public string DhcpServer { get; set; }
|
||||
public List<string> DnsServers { get; set; } = new List<string>();
|
||||
public bool IsActive { get; set; }
|
||||
public string InterfaceType { get; set; }
|
||||
}
|
||||
}
|
67
Services/Interfaces/IPingService.cs
Normal file
67
Services/Interfaces/IPingService.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pingerino.Services.Interfaces
|
||||
{
|
||||
public interface IPingService : IDisposable
|
||||
{
|
||||
event EventHandler<PingResultEventArgs> PingCompleted;
|
||||
event EventHandler<NetworkStatisticsEventArgs> StatisticsUpdated;
|
||||
|
||||
System.Threading.Tasks.Task StartPingingAsync(string ipAddress, int intervalMs, CancellationToken cancellationToken = default);
|
||||
System.Threading.Tasks.Task StopPingingAsync();
|
||||
System.Threading.Tasks.Task<PingResult> PingOnceAsync(string ipAddress, CancellationToken cancellationToken = default);
|
||||
void ClearStatistics();
|
||||
NetworkStatistics GetCurrentStatistics();
|
||||
bool IsRunning { get; }
|
||||
}
|
||||
|
||||
public class PingResultEventArgs : EventArgs
|
||||
{
|
||||
public PingResult Result { get; }
|
||||
public DateTime Timestamp { get; }
|
||||
|
||||
public PingResultEventArgs(PingResult result, DateTime timestamp)
|
||||
{
|
||||
Result = result;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
public class NetworkStatisticsEventArgs : EventArgs
|
||||
{
|
||||
public NetworkStatistics Statistics { get; }
|
||||
|
||||
public NetworkStatisticsEventArgs(NetworkStatistics statistics)
|
||||
{
|
||||
Statistics = statistics;
|
||||
}
|
||||
}
|
||||
|
||||
public class PingResult
|
||||
{
|
||||
public string IpAddress { get; set; }
|
||||
public bool IsSuccess { get; set; }
|
||||
public long RoundTripTime { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
|
||||
public class NetworkStatistics
|
||||
{
|
||||
public double Jitter { get; set; }
|
||||
public double PacketLoss { get; set; }
|
||||
public long MaxPing { get; set; }
|
||||
public long MinPing { get; set; }
|
||||
public double AveragePing { get; set; }
|
||||
public double MaxJitter { get; set; }
|
||||
public double MinJitter { get; set; }
|
||||
public double AverageJitter { get; set; }
|
||||
public double MaxPacketLoss { get; set; }
|
||||
public double MinPacketLoss { get; set; }
|
||||
public double AveragePacketLoss { get; set; }
|
||||
public int TotalPings { get; set; }
|
||||
public int SuccessfulPings { get; set; }
|
||||
}
|
||||
}
|
16
Services/Interfaces/ISystemService.cs
Normal file
16
Services/Interfaces/ISystemService.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pingerino.Services.Interfaces
|
||||
{
|
||||
public interface ISystemService : IDisposable
|
||||
{
|
||||
System.Threading.Tasks.Task<bool> CleanTemporaryFilesAsync(IProgress<int> progress = null, CancellationToken cancellationToken = default);
|
||||
System.Threading.Tasks.Task<bool> CreateStartupTaskAsync(string taskName, string executablePath);
|
||||
System.Threading.Tasks.Task<bool> RemoveStartupTaskAsync(string taskName);
|
||||
System.Threading.Tasks.Task<bool> IsStartupTaskExistsAsync(string taskName);
|
||||
void OpenNetworkAdaptersControl();
|
||||
System.Threading.Tasks.Task<bool> RunExternalScriptAsync(string scriptPath);
|
||||
}
|
||||
}
|
59
Services/LoggingService.cs
Normal file
59
Services/LoggingService.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Pingerino.Services.Interfaces;
|
||||
|
||||
namespace Pingerino.Services
|
||||
{
|
||||
public class LoggingService : ILoggingService
|
||||
{
|
||||
private readonly string _logFilePath;
|
||||
private readonly object _lockObject = new object();
|
||||
|
||||
public LoggingService()
|
||||
{
|
||||
var logDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Pingerino", "Logs");
|
||||
Directory.CreateDirectory(logDirectory);
|
||||
_logFilePath = Path.Combine(logDirectory, $"pingerino_{DateTime.Now:yyyyMMdd}.log");
|
||||
}
|
||||
|
||||
public void LogInformation(string message)
|
||||
{
|
||||
WriteLog(LogLevel.Information, message);
|
||||
}
|
||||
|
||||
public void LogWarning(string message)
|
||||
{
|
||||
WriteLog(LogLevel.Warning, message);
|
||||
}
|
||||
|
||||
public void LogError(string message, Exception exception = null)
|
||||
{
|
||||
var fullMessage = exception != null ? $"{message} - Exception: {exception}" : message;
|
||||
WriteLog(LogLevel.Error, fullMessage);
|
||||
}
|
||||
|
||||
public void LogDebug(string message)
|
||||
{
|
||||
WriteLog(LogLevel.Debug, message);
|
||||
}
|
||||
|
||||
private void WriteLog(LogLevel level, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
var logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{level}] {message}";
|
||||
File.AppendAllText(_logFilePath, logEntry + Environment.NewLine);
|
||||
|
||||
// Also write to console for debugging
|
||||
Console.WriteLine(logEntry);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore logging errors to prevent infinite loops
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
195
Services/NetworkService.cs
Normal file
195
Services/NetworkService.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Pingerino.Services.Interfaces;
|
||||
|
||||
namespace Pingerino.Services
|
||||
{
|
||||
public class NetworkService : INetworkService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILoggingService _logger;
|
||||
private bool _disposed;
|
||||
|
||||
public NetworkService(ILoggingService logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_httpClient = new HttpClient
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(10)
|
||||
};
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<string> GetPublicIpAddressAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetStringAsync("http://ipinfo.io/ip");
|
||||
var publicIp = response.Trim();
|
||||
_logger.LogInformation($"Retrieved public IP: {publicIp}");
|
||||
return publicIp;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Failed to retrieve public IP address", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<List<NetworkAdapterInfo>> GetNetworkAdaptersAsync()
|
||||
{
|
||||
return await System.Threading.Tasks.Task.Run(() =>
|
||||
{
|
||||
var adapters = new List<NetworkAdapterInfo>();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (nic.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
continue;
|
||||
|
||||
var properties = nic.GetIPProperties();
|
||||
var adapter = new NetworkAdapterInfo
|
||||
{
|
||||
Name = nic.Name,
|
||||
Description = nic.Description,
|
||||
PhysicalAddress = nic.GetPhysicalAddress().ToString(),
|
||||
IsActive = nic.OperationalStatus == OperationalStatus.Up,
|
||||
InterfaceType = nic.NetworkInterfaceType.ToString()
|
||||
};
|
||||
|
||||
// Get IPv4 properties if available
|
||||
try
|
||||
{
|
||||
var ipv4Properties = properties.GetIPv4Properties();
|
||||
adapter.IsDhcpEnabled = ipv4Properties.IsDhcpEnabled;
|
||||
}
|
||||
catch
|
||||
{
|
||||
adapter.IsDhcpEnabled = false;
|
||||
}
|
||||
|
||||
// Get IP addresses
|
||||
var ipv4Address = properties.UnicastAddresses
|
||||
.FirstOrDefault(ip => ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
|
||||
|
||||
if (ipv4Address != null)
|
||||
{
|
||||
adapter.IpAddress = ipv4Address.Address.ToString();
|
||||
adapter.SubnetMask = ipv4Address.IPv4Mask?.ToString();
|
||||
}
|
||||
|
||||
// Get gateway
|
||||
var gateway = properties.GatewayAddresses.FirstOrDefault();
|
||||
if (gateway != null)
|
||||
{
|
||||
adapter.DefaultGateway = gateway.Address.ToString();
|
||||
}
|
||||
|
||||
// Get DHCP server
|
||||
var dhcpServer = properties.DhcpServerAddresses.FirstOrDefault();
|
||||
if (dhcpServer != null)
|
||||
{
|
||||
adapter.DhcpServer = dhcpServer.ToString();
|
||||
}
|
||||
|
||||
// Get DNS servers
|
||||
adapter.DnsServers = properties.DnsAddresses.Select(dns => dns.ToString()).ToList();
|
||||
|
||||
adapters.Add(adapter);
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Retrieved {adapters.Count} network adapters");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error retrieving network adapters", ex);
|
||||
}
|
||||
|
||||
return adapters;
|
||||
});
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<bool> ResetNetworkAdapterAsync(string adapterName, IProgress<int> progress = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
progress?.Report(10);
|
||||
_logger.LogInformation($"Starting network reset for adapter: {adapterName}");
|
||||
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/c ipconfig /release \"{adapterName}\" && ipconfig /renew \"{adapterName}\"",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
Verb = "runas"
|
||||
};
|
||||
|
||||
progress?.Report(30);
|
||||
|
||||
using (var process = Process.Start(processInfo))
|
||||
{
|
||||
if (process == null)
|
||||
{
|
||||
_logger.LogError("Failed to start network reset process");
|
||||
return false;
|
||||
}
|
||||
|
||||
progress?.Report(60);
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
progress?.Report(90);
|
||||
|
||||
var success = process.ExitCode == 0;
|
||||
if (success)
|
||||
{
|
||||
_logger.LogInformation($"Network reset completed successfully for {adapterName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError($"Network reset failed with exit code: {process.ExitCode}");
|
||||
}
|
||||
|
||||
progress?.Report(100);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error during network reset", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<NetworkAdapterInfo> GetActiveEthernetAdapterAsync()
|
||||
{
|
||||
var adapters = await GetNetworkAdaptersAsync();
|
||||
return adapters.FirstOrDefault(a =>
|
||||
a.InterfaceType == NetworkInterfaceType.Ethernet.ToString() &&
|
||||
a.IsActive);
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<bool> IsNetworkAvailableAsync()
|
||||
{
|
||||
return await System.Threading.Tasks.Task.Run(() => NetworkInterface.GetIsNetworkAvailable());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_httpClient?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
255
Services/PingService.cs
Normal file
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;
|
||||
}
|
||||
}
|
||||
}
|
238
Services/SystemService.cs
Normal file
238
Services/SystemService.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32.TaskScheduler;
|
||||
using Pingerino.Services.Interfaces;
|
||||
|
||||
namespace Pingerino.Services
|
||||
{
|
||||
public class SystemService : ISystemService
|
||||
{
|
||||
private readonly ILoggingService _logger;
|
||||
private bool _disposed;
|
||||
|
||||
public SystemService(ILoggingService logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<bool> CleanTemporaryFilesAsync(IProgress<int> progress = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Starting temporary files cleanup");
|
||||
|
||||
var folders = new[]
|
||||
{
|
||||
Environment.ExpandEnvironmentVariables(@"%systemroot%\temp"),
|
||||
Environment.ExpandEnvironmentVariables(@"%systemroot%\Prefetch"),
|
||||
Environment.ExpandEnvironmentVariables(@"%userprofile%\LocalS~1\Temp"),
|
||||
Environment.ExpandEnvironmentVariables(@"%userprofile%\AppData\Local\Temp")
|
||||
};
|
||||
|
||||
int totalSteps = folders.Length * 2;
|
||||
int currentStep = 0;
|
||||
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return false;
|
||||
|
||||
await CleanFolderAsync(folder, progress, currentStep, totalSteps);
|
||||
currentStep += 2;
|
||||
}
|
||||
|
||||
progress?.Report(100);
|
||||
_logger.LogInformation("Temporary files cleanup completed");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error during temporary files cleanup", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task CleanFolderAsync(string folderPath, IProgress<int> progress, int currentStep, int totalSteps)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(folderPath))
|
||||
return;
|
||||
|
||||
var dirInfo = new DirectoryInfo(folderPath);
|
||||
|
||||
// Delete files
|
||||
await System.Threading.Tasks.Task.Run(() =>
|
||||
{
|
||||
foreach (var file in dirInfo.GetFiles())
|
||||
{
|
||||
try
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore individual file deletion errors
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
progress?.Report((currentStep + 1) * 100 / totalSteps);
|
||||
|
||||
// Delete directories
|
||||
await System.Threading.Tasks.Task.Run(() =>
|
||||
{
|
||||
foreach (var dir in dirInfo.GetDirectories())
|
||||
{
|
||||
try
|
||||
{
|
||||
dir.Delete(true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore individual directory deletion errors
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
progress?.Report((currentStep + 2) * 100 / totalSteps);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"Error cleaning folder {folderPath}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<bool> CreateStartupTaskAsync(string taskName, string executablePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ts = new TaskService())
|
||||
{
|
||||
var td = ts.NewTask();
|
||||
|
||||
td.RegistrationInfo.Description = taskName;
|
||||
td.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
td.Principal.LogonType = TaskLogonType.InteractiveToken;
|
||||
td.Triggers.Add(new LogonTrigger());
|
||||
|
||||
var execAction = new ExecAction(executablePath)
|
||||
{
|
||||
WorkingDirectory = Path.GetDirectoryName(executablePath)
|
||||
};
|
||||
td.Actions.Add(execAction);
|
||||
|
||||
var currentUserName = WindowsIdentity.GetCurrent().Name;
|
||||
ts.RootFolder.RegisterTaskDefinition(taskName, td, TaskCreation.CreateOrUpdate,
|
||||
currentUserName, null, TaskLogonType.InteractiveToken);
|
||||
|
||||
_logger.LogInformation($"Startup task '{taskName}' created successfully");
|
||||
return await System.Threading.Tasks.Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error creating startup task '{taskName}'", ex);
|
||||
return await System.Threading.Tasks.Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<bool> RemoveStartupTaskAsync(string taskName)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ts = new TaskService())
|
||||
{
|
||||
ts.RootFolder.DeleteTask(taskName, false);
|
||||
|
||||
_logger.LogInformation($"Startup task '{taskName}' removed successfully");
|
||||
return await System.Threading.Tasks.Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error removing startup task '{taskName}'", ex);
|
||||
return await System.Threading.Tasks.Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<bool> IsStartupTaskExistsAsync(string taskName)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ts = new TaskService())
|
||||
{
|
||||
var existingTask = ts.GetTask(taskName);
|
||||
return await System.Threading.Tasks.Task.FromResult(existingTask != null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error checking startup task '{taskName}'", ex);
|
||||
return await System.Threading.Tasks.Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenNetworkAdaptersControl()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start("control", "ncpa.cpl");
|
||||
_logger.LogInformation("Opened network adapters control panel");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error opening network adapters control panel", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<bool> RunExternalScriptAsync(string scriptPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(scriptPath))
|
||||
{
|
||||
_logger.LogWarning($"Script not found: {scriptPath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = scriptPath,
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
using (var process = Process.Start(processInfo))
|
||||
{
|
||||
if (process == null)
|
||||
{
|
||||
_logger.LogError("Failed to start external script");
|
||||
return false;
|
||||
}
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
var success = process.ExitCode == 0;
|
||||
_logger.LogInformation($"External script execution completed with exit code: {process.ExitCode}");
|
||||
return success;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error running external script: {scriptPath}", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
المرجع في مشكلة جديدة
حظر مستخدم