Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions Source/NETworkManager.Converters/BandwidthBytesToSpeedConverter.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
using System;
using System;
using System.Globalization;
using System.Windows.Data;
using NETworkManager.Utilities;

namespace NETworkManager.Converters;

/// <summary>
/// Converts a byte-per-second value to a human-readable speed string.
/// Pass the converter parameter "bytes" to format as byte/s (e.g. "1.54 MB/s");
/// any other value (default) formats as bit/s (e.g. "12.3 Mbit/s") to match the chart axis.
/// </summary>
public sealed class BandwidthBytesToSpeedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null
? $"{FileSizeConverter.GetBytesReadable((long)value * 8)}it/s ({FileSizeConverter.GetBytesReadable((long)value)}/s)"
: "-/-";
if (value == null)
return "-/-";

var bytesPerSecond = (long)value;

return parameter as string == "bytes"
? $"{FileSizeConverter.GetBytesReadable(bytesPerSecond)}/s"
: $"{FileSizeConverter.GetBytesReadable(bytesPerSecond * 8)}it/s";
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

This file was deleted.

23 changes: 23 additions & 0 deletions Source/NETworkManager.Converters/BytesToExactStringConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Windows.Data;
using NETworkManager.Localization.Resources;

namespace NETworkManager.Converters;

/// <summary>
/// Converts a byte count to its exact value with thousands separators and a "Bytes" unit
/// (e.g. "6,783,176,192 Bytes"). Intended for tooltips that complement a human-readable size.
/// </summary>
public sealed class BytesToExactStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null ? string.Format(culture, "{0:N0} {1}", (long)value, Strings.Bytes) : "-/-";
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ public static DocumentationIdentifier GetIdentifierBySettingsName(SettingsName n
SettingsName.Profiles => DocumentationIdentifier.SettingsProfiles,
SettingsName.Settings => DocumentationIdentifier.SettingsSettings,
SettingsName.Dashboard => GetIdentifierByApplicationName(ApplicationName.Dashboard),
SettingsName.NetworkInterface => GetIdentifierByApplicationName(ApplicationName.NetworkInterface),
SettingsName.IPScanner => GetIdentifierByApplicationName(ApplicationName.IPScanner),
SettingsName.PortScanner => GetIdentifierByApplicationName(ApplicationName.PortScanner),
SettingsName.PingMonitor => GetIdentifierByApplicationName(ApplicationName.PingMonitor),
Expand Down
63 changes: 49 additions & 14 deletions Source/NETworkManager.Models/Network/BandwidthMeter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Windows.Threading;

namespace NETworkManager.Models.Network;
Expand Down Expand Up @@ -30,6 +32,12 @@ private void Timer_Tick(object sender, EventArgs e)

#region Variables

/// <summary>
/// The default sample interval in milliseconds. Kept as a constant because the speed
/// calculation and the consuming view model derive timing from it.
/// </summary>
public const double DefaultUpdateInterval = 1000;

public double UpdateInterval
{
get;
Expand All @@ -42,12 +50,13 @@ public double UpdateInterval

field = value;
}
} = 1000;
} = DefaultUpdateInterval;

public bool IsRunning => _timer.IsEnabled;

private readonly DispatcherTimer _timer = new();
private readonly System.Net.NetworkInformation.NetworkInterface _networkInterface;
private readonly Stopwatch _stopwatch = new();
private long _previousBytesSent;
private long _previousBytesReceived;
private bool _canUpdate; // Collect initial data for correct calculation
Expand Down Expand Up @@ -76,34 +85,60 @@ public void Stop()
{
_timer.Stop();

// Reset
// Reset, so the next sample re-seeds the baseline (avoids a speed spike after a pause).
_canUpdate = false;
}

private void Update()
{
var stats = _networkInterface.GetIPv4Statistics();
// The interface may have been removed/disabled after this meter was created.
if (_networkInterface == null)
return;

var totalBytesSent = stats.BytesSent;
var totalBytesReceived = stats.BytesReceived;
IPInterfaceStatistics stats;

var byteSentSpeed = totalBytesSent - _previousBytesSent;
var byteReceivedSpeed = totalBytesReceived - _previousBytesReceived;
try
{
// IPStatistics covers both IPv4 and IPv6 traffic on the interface.
stats = _networkInterface.GetIPStatistics();
}
catch (NetworkInformationException)
{
// Transient failure (e.g. adapter going down) - skip this tick.
return;
}

_previousBytesSent = stats.BytesSent;
_previousBytesReceived = stats.BytesReceived;
var totalBytesSent = stats.BytesSent;
var totalBytesReceived = stats.BytesReceived;

// Need to collect initial data for correct calculation...
// First sample after start/resume: seed the baseline and start timing, no speed yet.
if (!_canUpdate)
{
_previousBytesSent = totalBytesSent;
_previousBytesReceived = totalBytesReceived;
_stopwatch.Restart();
_canUpdate = true;

return;
}

OnUpdateSpeed(new BandwidthMeterSpeedArgs(DateTime.Now, totalBytesReceived, totalBytesSent, byteReceivedSpeed,
byteSentSpeed));
var elapsedSeconds = _stopwatch.Elapsed.TotalSeconds;
_stopwatch.Restart();

// Clamp negative deltas: cumulative counters can reset/wrap (driver reset, disable/enable).
var deltaSent = Math.Max(0, totalBytesSent - _previousBytesSent);
var deltaReceived = Math.Max(0, totalBytesReceived - _previousBytesReceived);

// Derive a true per-second rate from the measured elapsed time (robust against timer jitter).
var byteSentSpeed = elapsedSeconds > 0 ? (long)(deltaSent / elapsedSeconds) : 0;
var byteReceivedSpeed = elapsedSeconds > 0 ? (long)(deltaReceived / elapsedSeconds) : 0;

_previousBytesSent = totalBytesSent;
_previousBytesReceived = totalBytesReceived;

OnUpdateSpeed(new BandwidthMeterSpeedArgs(DateTime.Now, totalBytesReceived, totalBytesSent,
byteReceivedSpeed, byteSentSpeed));
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public static class GlobalStaticConfiguration

// Application: Network Interface
public static ExportFileType NetworkInterface_ExportFileType => ExportFileType.Csv;
public static int NetworkInterface_BandwidthChartTime => 60;

// Application: WiFi
public static bool WiFi_Show2dot4GHzNetworks => true;
Expand Down
16 changes: 16 additions & 0 deletions Source/NETworkManager.Settings/SettingsInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,22 @@ public ExportFileType NetworkInterface_ExportFileType
}
} = GlobalStaticConfiguration.NetworkInterface_ExportFileType;

/// <summary>
/// Gets or sets the bandwidth chart time window in seconds.
/// </summary>
public int NetworkInterface_BandwidthChartTime
{
get;
set
{
if (value == field)
return;

field = value;
OnPropertyChanged();
}
} = GlobalStaticConfiguration.NetworkInterface_BandwidthChartTime;

#endregion

#region WiFi
Expand Down
1 change: 1 addition & 0 deletions Source/NETworkManager.Settings/SettingsName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum SettingsName
Profiles,
Settings,
Dashboard,
NetworkInterface,
IPScanner,
PortScanner,
PingMonitor,
Expand Down
2 changes: 2 additions & 0 deletions Source/NETworkManager.Settings/SettingsViewManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public static class SettingsViewManager
// Applications
new(SettingsName.Dashboard, ApplicationManager.GetIcon(ApplicationName.Dashboard),
SettingsGroup.Application),
new(SettingsName.NetworkInterface, ApplicationManager.GetIcon(ApplicationName.NetworkInterface),
SettingsGroup.Application),
new(SettingsName.IPScanner, ApplicationManager.GetIcon(ApplicationName.IPScanner),
SettingsGroup.Application),
new(SettingsName.PortScanner, ApplicationManager.GetIcon(ApplicationName.PortScanner),
Expand Down
46 changes: 46 additions & 0 deletions Source/NETworkManager/Controls/LiveChartsBandwidthTooltip.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<UserControl x:Class="NETworkManager.Controls.LiveChartsBandwidthTooltip"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<Border Background="{DynamicResource MahApps.Brushes.Window.Background}"
BorderBrush="{DynamicResource MahApps.Brushes.Gray8}"
BorderThickness="1">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
Style="{StaticResource InfoTextBlock}"
HorizontalAlignment="Center"
Text="{Binding HeaderText}" />
<ItemsControl Grid.Row="2" Grid.Column="0"
ItemsSource="{Binding TooltipEntries}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Height="2" Width="14"
Fill="{Binding SeriesColor}" />
<TextBlock Grid.Column="2" Text="{Binding Value}"
Style="{StaticResource DefaultTextBlock}"
VerticalAlignment="Center" Foreground="{DynamicResource MahApps.Brushes.Gray3}" />
<TextBlock Grid.Column="4" Text="{Binding SeriesName}"
Style="{StaticResource AccentTextBlock}"
VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Border>
</UserControl>
Loading
Loading