Repository navigation
Expand file tree
/
Copy pathBandwidthMeter.cs
More file actions
144 lines (108 loc) · 3.92 KB
/
Copy pathBandwidthMeter.cs
File metadata and controls
144 lines (108 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Windows.Threading;
namespace NETworkManager.Models.Network;
public class BandwidthMeter
{
#region Constructor
public BandwidthMeter(string id)
{
_timer.Interval = TimeSpan.FromMilliseconds(UpdateInterval);
_timer.Tick += Timer_Tick;
_networkInterface = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()
.FirstOrDefault(x => x.Id == id);
}
#endregion
#region Events
private void Timer_Tick(object sender, EventArgs e)
{
Update();
}
#endregion
#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;
set
{
if (value == field)
return;
_timer.Interval = TimeSpan.FromMilliseconds(value);
field = value;
}
} = 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
#endregion
#region Public events
public event EventHandler<BandwidthMeterSpeedArgs> UpdateSpeed;
protected virtual void OnUpdateSpeed(BandwidthMeterSpeedArgs e)
{
UpdateSpeed?.Invoke(this, e);
}
#endregion
#region Methods
public void Start()
{
_timer.Start();
}
public void Stop()
{
_timer.Stop();
// Reset, so the next sample re-seeds the baseline (avoids a speed spike after a pause).
_canUpdate = false;
}
private void Update()
{
// The interface may have been removed/disabled after this meter was created.
if (_networkInterface == null)
return;
IPInterfaceStatistics stats;
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;
}
var totalBytesSent = stats.BytesSent;
var totalBytesReceived = stats.BytesReceived;
// 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;
}
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
}