From bfa601d212a8623ad060144e5af2dccc343d1e21 Mon Sep 17 00:00:00 2001 From: BoiHanny <114599052+BoiHanny@users.noreply.github.com> Date: Sat, 16 Nov 2024 21:01:50 +0100 Subject: [PATCH 01/12] Refactor NetworkStatisticsModule for better performance Refactored NetworkStatisticsModule to improve functionality and readability. Replaced System.Timers.Timer with System.Threading.Timer, and removed unused using directives. Introduced new private fields and a constructor for initialization. Changed method visibility to encapsulate internal logic. Simplified monitoring logic and replaced PerformanceCounter with NetworkInterface statistics. Added InterfaceStats class and method to get the most active network interface. Improved exception handling, logging, and updated Dispose method. --- .../Modules/NetworkStatisticsModule.cs | 481 +++++++----------- 1 file changed, 184 insertions(+), 297 deletions(-) diff --git a/vrcosc-magicchatbox/Classes/Modules/NetworkStatisticsModule.cs b/vrcosc-magicchatbox/Classes/Modules/NetworkStatisticsModule.cs index 7750c27c..6767fb31 100644 --- a/vrcosc-magicchatbox/Classes/Modules/NetworkStatisticsModule.cs +++ b/vrcosc-magicchatbox/Classes/Modules/NetworkStatisticsModule.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; +using System.Linq; using System.Net.NetworkInformation; using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using System.Timers; +using System.Threading; using vrcosc_magicchatbox.Classes.DataAndSecurity; using vrcosc_magicchatbox.DataAndSecurity; using vrcosc_magicchatbox.ViewModels; @@ -15,8 +14,14 @@ namespace vrcosc_magicchatbox.Classes.Modules public class NetworkStatisticsModule : INotifyPropertyChanged, IDisposable { private Timer _updateTimer; + private NetworkInterface _activeNetworkInterface; + private bool _isMonitoring; + private long _previousBytesReceived; + private long _previousBytesSent; + public bool IsInitialized { get; private set; } - public double interval { get; set; } = 1000; + public double Interval { get; set; } = 1000; + private double _currentDownloadSpeedMbps; private double _currentUploadSpeedMbps; private double _maxDownloadSpeedMbps; @@ -24,10 +29,22 @@ public class NetworkStatisticsModule : INotifyPropertyChanged, IDisposable private double _networkUtilization; private double _totalDownloadedMB; private double _totalUploadedMB; - public int ErrorCount = 0; + public event PropertyChangedEventHandler PropertyChanged; + + public NetworkStatisticsModule(double interval) + { + Interval = interval; + ViewModel.Instance.PropertyChanged += PropertyChangedHandler; + + if (ShouldStartMonitoring()) + { + InitializeNetworkStats(); + StartModule(); + } + } - public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e) + private void PropertyChangedHandler(object sender, PropertyChangedEventArgs e) { if (IsRelevantPropertyChange(e.PropertyName)) { @@ -35,31 +52,25 @@ public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e) { if (!IsInitialized) { - InitializeNetworkStatsAsync(); - IsInitialized = true; + InitializeNetworkStats(); } StartModule(); } else { - if (!IsInitialized) - { - InitializeNetworkStatsAsync(); - IsInitialized = true; - } StopModule(); } } } - - public bool ShouldStartMonitoring() + private bool ShouldStartMonitoring() { - return ViewModel.Instance.IntgrNetworkStatistics && ViewModel.Instance.IsVRRunning && ViewModel.Instance.IntgrNetworkStatistics_VR || - ViewModel.Instance.IntgrNetworkStatistics && !ViewModel.Instance.IsVRRunning && ViewModel.Instance.IntgrNetworkStatistics_DESKTOP; + return ViewModel.Instance.IntgrNetworkStatistics && + ((ViewModel.Instance.IsVRRunning && ViewModel.Instance.IntgrNetworkStatistics_VR) || + (!ViewModel.Instance.IsVRRunning && ViewModel.Instance.IntgrNetworkStatistics_DESKTOP)); } - public bool IsRelevantPropertyChange(string propertyName) + private bool IsRelevantPropertyChange(string propertyName) { return propertyName == nameof(ViewModel.Instance.IntgrNetworkStatistics) || propertyName == nameof(ViewModel.Instance.IsVRRunning) || @@ -67,267 +78,164 @@ public bool IsRelevantPropertyChange(string propertyName) propertyName == nameof(ViewModel.Instance.IntgrNetworkStatistics_DESKTOP); } - private PerformanceCounter downloadCounter; - private PerformanceCounter uploadCounter; - private DateTime previousUpdateTime; - - public double TotalDownloadedMB - { - get { return _totalDownloadedMB; } - set { SetProperty(ref _totalDownloadedMB, value); } - } - - public double TotalUploadedMB - { - get { return _totalUploadedMB; } - set { SetProperty(ref _totalUploadedMB, value); } - } - - public double CurrentDownloadSpeedMbps - { - get { return _currentDownloadSpeedMbps; } - set { SetProperty(ref _currentDownloadSpeedMbps, value); } - } - - public double CurrentUploadSpeedMbps - { - get { return _currentUploadSpeedMbps; } - set { SetProperty(ref _currentUploadSpeedMbps, value); } - } - - public double MaxDownloadSpeedMbps - { - get { return _maxDownloadSpeedMbps; } - set { SetProperty(ref _maxDownloadSpeedMbps, value); } - } - - public double MaxUploadSpeedMbps - { - get { return _maxUploadSpeedMbps; } - set { SetProperty(ref _maxUploadSpeedMbps, value); } - } - - public double NetworkUtilization - { - get { return _networkUtilization; } - set { SetProperty(ref _networkUtilization, value); } - } - - public event PropertyChangedEventHandler PropertyChanged; - - public NetworkStatisticsModule(double interval) + private void InitializeNetworkStats() { - this.interval = interval; - ViewModel.Instance.PropertyChanged += PropertyChangedHandler; - if (ShouldStartMonitoring()) + _activeNetworkInterface = GetActiveNetworkInterface(); + if (_activeNetworkInterface != null) { - if(!IsInitialized) - { - InitializeNetworkStatsAsync(); - IsInitialized = true; - } - StartModule(); - } - - } + var speedInMbps = _activeNetworkInterface.Speed / 1e6; + MaxDownloadSpeedMbps = speedInMbps; + MaxUploadSpeedMbps = speedInMbps; - public void StartModule() - { - if (_updateTimer != null && !_updateTimer.Enabled) - { - _updateTimer.Start(); - } - } + var stats = _activeNetworkInterface.GetIPv4Statistics(); + _previousBytesReceived = stats.BytesReceived; + _previousBytesSent = stats.BytesSent; - public void StopModule() - { - if (_updateTimer != null && _updateTimer.Enabled) - { - _updateTimer.Stop(); + IsInitialized = true; } - } - - public void ForceUpdate() - { - OnTimedEvent(null, null); - } - - private async Task InitializeNetworkStatsAsync() - { - // Attempt to initialize performance counters with an active network interface - await Task.Run(() => - { - if (!InitializePerformanceCounters()) + else { - // If initialization fails, subscribe to the NetworkAddressChanged event to retry when the network status changes - NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged; + // Handle the case when no active network interface is found + Logging.WriteException(new Exception("No active network interface found"), MSGBox: false); + IsInitialized = false; } - }); - - // Initialize and start the update timer - _updateTimer = new Timer(interval) - { - AutoReset = true, - Enabled = false - }; - _updateTimer.Elapsed += OnTimedEvent; - - previousUpdateTime = DateTime.Now; } - private bool InitializePerformanceCounters() + private NetworkInterface GetActiveNetworkInterface() { try { - // Dispose of existing counters - downloadCounter?.Dispose(); - uploadCounter?.Dispose(); + var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces() + .Where(ni => + ni.OperationalStatus == OperationalStatus.Up && + ni.NetworkInterfaceType != NetworkInterfaceType.Loopback && + ni.NetworkInterfaceType != NetworkInterfaceType.Tunnel && + ni.GetIPProperties().UnicastAddresses.Count > 0).ToList(); - // Attempt to select an active network interface - NetworkInterface activeNetworkInterface = GetActiveNetworkInterface(); + if (networkInterfaces.Count == 0) + return null; - if (activeNetworkInterface != null) + // Measure initial bytes sent/received + var interfaceStats = new List(); + foreach (var ni in networkInterfaces) { - // Get the correct instance name for the PerformanceCounter - string instanceName = GetInstanceNameForPerformanceCounter(activeNetworkInterface); - if (instanceName == null) + var stats = ni.GetIPv4Statistics(); + interfaceStats.Add(new InterfaceStats { - // Handle the case where no matching instance name is found - // For example, log this issue and use a default instance name or skip setting up counters - return false; - } - - // Initialize Performance Counters for the selected network interface - downloadCounter = new PerformanceCounter("Network Interface", "Bytes Received/sec", instanceName); - uploadCounter = new PerformanceCounter("Network Interface", "Bytes Sent/sec", instanceName); - - // Update maximum speeds based on the active network interface - MaxDownloadSpeedMbps = activeNetworkInterface.Speed / 8e6; // Convert from bits to Megabytes - MaxUploadSpeedMbps = activeNetworkInterface.Speed / 8e6; // Convert from bits to Megabytes - - return true; // Initialization succeeded + NetworkInterface = ni, + InitialBytesReceived = stats.BytesReceived, + InitialBytesSent = stats.BytesSent + }); } - return false; // No active network interface was found - } - catch (Exception ex) - { - Logging.WriteException(ex, MSGBox: false); - ErrorCount++; - return false; - } - - } - - private string GetInstanceNameForPerformanceCounter(NetworkInterface networkInterface) - { - try - { - var category = new PerformanceCounterCategory("Network Interface"); - string[] instanceNames = category.GetInstanceNames(); - string normalizedInterfaceName = NormalizeInterfaceName(networkInterface.Description); + // Wait for a short interval + Thread.Sleep(500); - foreach (string instanceName in instanceNames) + // Measure bytes sent/received after the interval + foreach (var stat in interfaceStats) { - if (instanceName.Equals(normalizedInterfaceName, StringComparison.OrdinalIgnoreCase)) - { - return instanceName; - } + var stats = stat.NetworkInterface.GetIPv4Statistics(); + stat.BytesReceivedDiff = stats.BytesReceived - stat.InitialBytesReceived; + stat.BytesSentDiff = stats.BytesSent - stat.InitialBytesSent; + stat.TotalBytesDiff = stat.BytesReceivedDiff + stat.BytesSentDiff; } - return null; - } - catch (InvalidOperationException ex) - { - // Log the exception for debugging purposes - Logging.WriteException(ex, MSGBox: false); - ErrorCount++; - // Notify user or take steps to repair or provide alternative - HandlePerformanceCounterError(); + // Select the network interface with the highest total bytes difference + var mostActiveInterface = interfaceStats.OrderByDescending(s => s.TotalBytesDiff).FirstOrDefault(); - return null; + if (mostActiveInterface != null && mostActiveInterface.TotalBytesDiff > 0) + { + return mostActiveInterface.NetworkInterface; + } + else + { + // If no activity detected, return the first interface + return networkInterfaces.FirstOrDefault(); + } } catch (Exception ex) { Logging.WriteException(ex, MSGBox: false); - ErrorCount++; return null; } } - private void HandlePerformanceCounterError() + private class InterfaceStats { - Logging.WriteException(new Exception("Performance counter error"), MSGBox: false); + public NetworkInterface NetworkInterface { get; set; } + public long InitialBytesReceived { get; set; } + public long InitialBytesSent { get; set; } + public long BytesReceivedDiff { get; set; } + public long BytesSentDiff { get; set; } + public long TotalBytesDiff { get; set; } } - - private string NormalizeInterfaceName(string interfaceName) + public void StartModule() { - // Replace invalid characters based on the mappings provided by Microsoft documentation - return interfaceName - .Replace('(', '[') - .Replace(')', ']') - .Replace('#', '_') - .Replace('\\', '_') - .Replace('/', '_'); - // Add any other normalization rules if needed + if (_isMonitoring) + return; + + _updateTimer = new Timer(OnTimedEvent, null, 0, (int)Interval); + _isMonitoring = true; } + public void StopModule() + { + if (!_isMonitoring) + return; + + _updateTimer?.Change(Timeout.Infinite, 0); + _updateTimer?.Dispose(); + _updateTimer = null; + _isMonitoring = false; + } - private NetworkInterface GetActiveNetworkInterface() + private void OnTimedEvent(object state) { try { - // Retrieve all network interfaces - NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); - - // Select the first active network interface that is up and has an IPv4 address and is not a loopback or tunnel - foreach (var ni in networkInterfaces) + if (_activeNetworkInterface == null) { - if (ni.OperationalStatus == OperationalStatus.Up && - // more that 0 less that 10000 (10Gbps) - ni.Speed > 0 && ni.Speed < 10000000000 && - ni.NetworkInterfaceType != NetworkInterfaceType.Loopback && - ni.NetworkInterfaceType != NetworkInterfaceType.Tunnel && - ni.GetIPProperties().UnicastAddresses.Count > 0) // Check for an IPv4 address - { - return ni; - } + InitializeNetworkStats(); + if (_activeNetworkInterface == null) + return; } - return null; // No active network interface found - } - catch (Exception ex) - { - ErrorCount++; - Logging.WriteException(ex, MSGBox: false); - return null; - } - - } + var stats = _activeNetworkInterface.GetIPv4Statistics(); - private void ResetCurrentStatistics() - { - // Reset current network statistics - CurrentDownloadSpeedMbps = 0; - CurrentUploadSpeedMbps = 0; - NetworkUtilization = 0; - } + // Calculate the differences since the last check + var bytesReceivedDiff = stats.BytesReceived - _previousBytesReceived; + var bytesSentDiff = stats.BytesSent - _previousBytesSent; - private void OnNetworkAddressChanged(object sender, EventArgs e) - { - // When the network address changes, try to re-initialize the performance counters - if (InitializePerformanceCounters()) - { - // If the re-initialization is successful, reset the current statistics - ResetCurrentStatistics(); + // Update previous values + _previousBytesReceived = stats.BytesReceived; + _previousBytesSent = stats.BytesSent; + + // Calculate speeds in Mbps + var intervalInSeconds = Interval / 1000; + CurrentDownloadSpeedMbps = (bytesReceivedDiff * 8) / 1e6 / intervalInSeconds; + CurrentUploadSpeedMbps = (bytesSentDiff * 8) / 1e6 / intervalInSeconds; + + // Update total downloaded and uploaded data in MB + TotalDownloadedMB += bytesReceivedDiff / 1e6; + TotalUploadedMB += bytesSentDiff / 1e6; + + // Update network utilization + NetworkUtilization = Math.Min(100, (CurrentDownloadSpeedMbps / MaxDownloadSpeedMbps) * 100); + + // Notify property changes + OnPropertyChanged(nameof(CurrentDownloadSpeedMbps)); + OnPropertyChanged(nameof(CurrentUploadSpeedMbps)); + OnPropertyChanged(nameof(TotalDownloadedMB)); + OnPropertyChanged(nameof(TotalUploadedMB)); + OnPropertyChanged(nameof(NetworkUtilization)); } - else + catch (Exception ex) { - // Log or handle the error if no active network interface is found after a network change - // This is application-specific and you should decide how to handle this case + Logging.WriteException(ex, MSGBox: false); } } + public string GenerateDescription() { const int maxLineWidth = 25; @@ -348,18 +256,18 @@ public string GenerateDescription() networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Max Down: ")} {FormatSpeed(MaxDownloadSpeedMbps)}"); if (ViewModel.Instance.NetworkStats_ShowMaxUp) - networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Max Up: ")}{FormatSpeed(MaxUploadSpeedMbps)}"); + networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Max Up: ")} {FormatSpeed(MaxUploadSpeedMbps)}"); if (ViewModel.Instance.NetworkStats_ShowTotalDown) - networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Total Down: ")}{FormatData(TotalDownloadedMB)}"); + networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Total Down: ")} {FormatData(TotalDownloadedMB)}"); if (ViewModel.Instance.NetworkStats_ShowTotalUp) - networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Total Up: ")}{FormatData(TotalUploadedMB)}"); + networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Total Up: ")} {FormatData(TotalUploadedMB)}"); if (ViewModel.Instance.NetworkStats_ShowNetworkUtilization) - networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Network utilization: ")}{NetworkUtilization:N2} %"); + networkStatsDescriptions.Add($"{ConvertToSuperScriptIfNeeded("Network utilization: ")} {NetworkUtilization:N2} %"); - if(networkStatsDescriptions.Count == 0) + if (networkStatsDescriptions.Count == 0) { return ""; } @@ -410,7 +318,6 @@ public string GenerateDescription() } return string.Join("\v", lines); - } private string FormatSpeed(double speedMbps) @@ -447,87 +354,67 @@ private string FormatData(double dataMB) return $"{dataMB:N2} {ConvertToSuperScriptIfNeeded("MB")}"; } - - private void OnTimedEvent(Object source, ElapsedEventArgs e) + // Implement property getters and setters with OnPropertyChanged notifications + public double CurrentDownloadSpeedMbps { - try - { - // Current time - DateTime currentTime = DateTime.Now; + get => _currentDownloadSpeedMbps; + set => SetProperty(ref _currentDownloadSpeedMbps, value); + } - // Calculate the number of seconds elapsed - double elapsedSeconds = (currentTime - previousUpdateTime).TotalSeconds; - previousUpdateTime = currentTime; + public double CurrentUploadSpeedMbps + { + get => _currentUploadSpeedMbps; + set => SetProperty(ref _currentUploadSpeedMbps, value); + } - // Fetch current network usage once - double currentDownloadBytes = downloadCounter.NextValue(); - double currentUploadBytes = uploadCounter.NextValue(); + public double MaxDownloadSpeedMbps + { + get => _maxDownloadSpeedMbps; + set => SetProperty(ref _maxDownloadSpeedMbps, value); + } - // Calculate speeds in Mbps - CurrentDownloadSpeedMbps = currentDownloadBytes / 1e6 * 8; - CurrentUploadSpeedMbps = currentUploadBytes / 1e6 * 8; + public double MaxUploadSpeedMbps + { + get => _maxUploadSpeedMbps; + set => SetProperty(ref _maxUploadSpeedMbps, value); + } - // Update total downloaded and uploaded data in MB - TotalDownloadedMB += currentDownloadBytes / 1e6 * elapsedSeconds; - TotalUploadedMB += currentUploadBytes / 1e6 * elapsedSeconds; + public double NetworkUtilization + { + get => _networkUtilization; + set => SetProperty(ref _networkUtilization, value); + } - // Update network utilization - NetworkUtilization = Math.Min(100, (CurrentDownloadSpeedMbps / MaxDownloadSpeedMbps) * 100); - } - catch (Exception ex) - { - Logging.WriteException(ex, MSGBox: false); - ErrorCount++; - if(ErrorCount > 3) - { - ViewModel.Instance.IntgrNetworkStatistics = false; - ErrorCount = 0; - } - } + public double TotalDownloadedMB + { + get => _totalDownloadedMB; + set => SetProperty(ref _totalDownloadedMB, value); } - protected void OnPropertyChanged(string propertyName) + public double TotalUploadedMB { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + get => _totalUploadedMB; + set => SetProperty(ref _totalUploadedMB, value); } protected bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) { - try - { - if (EqualityComparer.Default.Equals(storage, value)) - { - return false; - } - storage = value; - OnPropertyChanged(propertyName); - return true; - } - catch (Exception ex) - { - // Log the exception - Logging.WriteException(ex); + if (EqualityComparer.Default.Equals(storage, value)) return false; - } + storage = value; + OnPropertyChanged(propertyName); + return true; } + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } public void Dispose() { - try - { - _updateTimer?.Stop(); - _updateTimer?.Dispose(); - downloadCounter?.Dispose(); - uploadCounter?.Dispose(); - NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged; - } - catch (Exception ex) - { - // Log the exception - Logging.WriteException(ex); - } + StopModule(); + ViewModel.Instance.PropertyChanged -= PropertyChangedHandler; } - } } From 17c3c588d7c7f28778a197875a94e58605ecca76 Mon Sep 17 00:00:00 2001 From: BoiHanny <114599052+BoiHanny@users.noreply.github.com> Date: Sat, 16 Nov 2024 21:19:33 +0100 Subject: [PATCH 02/12] Update version and adjust GitHubChanges TextBlock layout Updated version number in MagicChatbox.csproj from 0.9.052 to 0.9.053. Moved GitHubChanges TextBlock into a StackPanel with VerticalAlignment set to Bottom in MainWindow.xaml. Added Margin and RenderOptions.BitmapScalingMode to enhance UI appearance. --- vrcosc-magicchatbox/MagicChatbox.csproj | 2 +- vrcosc-magicchatbox/MainWindow.xaml | 43 +++++++++++++------------ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/vrcosc-magicchatbox/MagicChatbox.csproj b/vrcosc-magicchatbox/MagicChatbox.csproj index 51d64970..05cdb022 100644 --- a/vrcosc-magicchatbox/MagicChatbox.csproj +++ b/vrcosc-magicchatbox/MagicChatbox.csproj @@ -2,7 +2,7 @@ WinExe - 0.9.052 + 0.9.053 net8.0-windows10.0.22000.0 vrcosc_magicchatbox enable diff --git a/vrcosc-magicchatbox/MainWindow.xaml b/vrcosc-magicchatbox/MainWindow.xaml index 5b24b8b1..b30771a8 100644 --- a/vrcosc-magicchatbox/MainWindow.xaml +++ b/vrcosc-magicchatbox/MainWindow.xaml @@ -7573,27 +7573,30 @@ FontSize="12" Foreground="#FF7D7397" Text="{Binding UpdateStatustxt}" /> - - - - - + + + + + +