From 110efd15657f9b1084fc2fb215668a956cd25cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Emre=20T=C3=BClek?= Date: Wed, 11 Dec 2024 20:45:59 +0300 Subject: [PATCH] Add project files. --- Network Monitor.sln | 25 +++ Network Monitor/FodyWeavers.xml | 10 + Network Monitor/MainWindow.xaml | 26 +++ Network Monitor/MainWindow.xaml.cs | 73 +++++++ Network Monitor/Network Monitor.csproj | 30 +++ Network Monitor/NetworkMonitorPlugin.cs | 21 ++ Network Monitor/NetworkViewModel.cs | 277 ++++++++++++++++++++++++ 7 files changed, 462 insertions(+) create mode 100644 Network Monitor.sln create mode 100644 Network Monitor/FodyWeavers.xml create mode 100644 Network Monitor/MainWindow.xaml create mode 100644 Network Monitor/MainWindow.xaml.cs create mode 100644 Network Monitor/Network Monitor.csproj create mode 100644 Network Monitor/NetworkMonitorPlugin.cs create mode 100644 Network Monitor/NetworkViewModel.cs diff --git a/Network Monitor.sln b/Network Monitor.sln new file mode 100644 index 0000000..7ec062c --- /dev/null +++ b/Network Monitor.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Network Monitor", "Network Monitor\Network Monitor.csproj", "{EFCE88DA-0570-4D89-8B0E-24E762AB8F63}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EFCE88DA-0570-4D89-8B0E-24E762AB8F63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EFCE88DA-0570-4D89-8B0E-24E762AB8F63}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EFCE88DA-0570-4D89-8B0E-24E762AB8F63}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EFCE88DA-0570-4D89-8B0E-24E762AB8F63}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9C988FCE-3E23-4768-B87B-ACB407B69E4B} + EndGlobalSection +EndGlobal diff --git a/Network Monitor/FodyWeavers.xml b/Network Monitor/FodyWeavers.xml new file mode 100644 index 0000000..160e365 --- /dev/null +++ b/Network Monitor/FodyWeavers.xml @@ -0,0 +1,10 @@ + + + + OxyPlot + OxyPlot.Wpf + OxyPlot.Wpf.Shared + System.ComponentModel.Composition + + + \ No newline at end of file diff --git a/Network Monitor/MainWindow.xaml b/Network Monitor/MainWindow.xaml new file mode 100644 index 0000000..b7056df --- /dev/null +++ b/Network Monitor/MainWindow.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/Network Monitor/MainWindow.xaml.cs b/Network Monitor/MainWindow.xaml.cs new file mode 100644 index 0000000..e9a4781 --- /dev/null +++ b/Network Monitor/MainWindow.xaml.cs @@ -0,0 +1,73 @@ +using System.ComponentModel; +using System.Windows; +using Widgets.Common; + +namespace Network_Monitor +{ + public partial class MainWindow : Window, IWidgetWindow + { + public readonly static string WidgetName = "Network Monitor"; + public readonly static string SettingsFile = "settings.networkmonitor.json"; + private readonly Config config = new(SettingsFile); + + public NetworkViewModel ViewModel { get; set; } + private NetworkViewModel.SettingsStruct Settings = NetworkViewModel.Default; + + public MainWindow() + { + InitializeComponent(); + LoadSettings(); + ViewModel = new NetworkViewModel() + { + Settings = Settings + }; + DataContext = ViewModel; + _ = ViewModel.Start(); + Logger.Info($"{WidgetName} is started"); + } + + public void LoadSettings() + { + try + { + Settings.ReceivedColor = PropertyParser.ToString(config.GetValue("received_color"), Settings.ReceivedColor); + Settings.SentColor = PropertyParser.ToString(config.GetValue("sent_color"), Settings.SentColor); + Settings.TimeLine = PropertyParser.ToFloat(config.GetValue("graphic_timeline"), Settings.TimeLine); + + UsageText.FontSize = PropertyParser.ToFloat(config.GetValue("usage_font_size")); + UsageText.Foreground = PropertyParser.ToColorBrush(config.GetValue("usage_foreground")); + TextR.Foreground = PropertyParser.ToColorBrush(Settings.ReceivedColor); + TextS.Foreground = PropertyParser.ToColorBrush(Settings.SentColor); + MaxBandText.Foreground = UsageText.Foreground; + MaxBandText.FontSize = UsageText.FontSize / 2; + } + catch (Exception) + { + config.Add("usage_font_size", UsageText.FontSize); + config.Add("usage_foreground", UsageText.Foreground); + config.Add("received_color", Settings.ReceivedColor); + config.Add("sent_color", Settings.SentColor); + config.Add("graphic_timeline", Settings.TimeLine); + + config.Save(); + } + } + + protected override void OnClosing(CancelEventArgs e) + { + base.OnClosing(e); + ViewModel.Dispose(); + Logger.Info($"{WidgetName} is closed"); + } + + public WidgetWindow WidgetWindow() + { + return new WidgetWindow(this, WidgetDefaultStruct()); + } + + public static WidgetDefaultStruct WidgetDefaultStruct() + { + return new WidgetDefaultStruct(); + } + } +} diff --git a/Network Monitor/Network Monitor.csproj b/Network Monitor/Network Monitor.csproj new file mode 100644 index 0000000..e24aeea --- /dev/null +++ b/Network Monitor/Network Monitor.csproj @@ -0,0 +1,30 @@ + + + + net8.0-windows + Network_Monitor + enable + true + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + ..\..\Widgets.Common\Widgets.Common\bin\Debug\net8.0-windows\Widgets.Common.dll + + + + + + + + diff --git a/Network Monitor/NetworkMonitorPlugin.cs b/Network Monitor/NetworkMonitorPlugin.cs new file mode 100644 index 0000000..601c740 --- /dev/null +++ b/Network Monitor/NetworkMonitorPlugin.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.Composition; +using System.Windows; +using Widgets.Common; + +namespace Network_Monitor +{ + [Export(typeof(IPlugin))] + internal class NetworkMonitorPlugin : IPlugin + { + public string Name => MainWindow.WidgetName; + public string? ConfigFile => MainWindow.SettingsFile; + public WidgetDefaultStruct WidgetDefaultStruct() + { + return MainWindow.WidgetDefaultStruct(); + } + public WidgetWindow WidgetWindow() + { + return new MainWindow().WidgetWindow(); + } + } +} diff --git a/Network Monitor/NetworkViewModel.cs b/Network Monitor/NetworkViewModel.cs new file mode 100644 index 0000000..b47c796 --- /dev/null +++ b/Network Monitor/NetworkViewModel.cs @@ -0,0 +1,277 @@ +using OxyPlot.Axes; +using OxyPlot.Series; +using OxyPlot; +using System.ComponentModel; +using System.Diagnostics; +using System.Windows; +using Widgets.Common; +using System.Net.NetworkInformation; +using System.Text.RegularExpressions; + +namespace Network_Monitor +{ + public partial class NetworkViewModel : INotifyPropertyChanged,IDisposable + { + [GeneratedRegex(@"[^a-zA-Z0-9 _]")] + private static partial Regex CleanAdaptorNameRegex(); + + private readonly Schedule schedule = new(); + private string scheduleID = ""; + private readonly CancellationTokenSource cancellationTokenSource = new(); + private readonly List BandwdithSteps = [10,50, 100,250,500,1000]; + + public struct SettingsStruct + { + public float TimeLine { get; set; } + public string ReceivedColor { get; set; } + public string SentColor { get; set; } + } + + public static SettingsStruct Default = new() + { + TimeLine = 200, + ReceivedColor = "#9D0A0A", + SentColor = "#fc8403", + }; + + public required SettingsStruct Settings = Default; + + private readonly List _valid_Interfaces = [ + NetworkInterfaceType.Ethernet, + NetworkInterfaceType.Wireless80211, + NetworkInterfaceType.Ppp, + NetworkInterfaceType.Wwanpp, + NetworkInterfaceType.Wwanpp2, + NetworkInterfaceType.Tunnel + ]; + private PerformanceCounter? bytesReceivedCounter; + private PerformanceCounter? bytesSentCounter; + private AreaSeries? receivedSeries; + private AreaSeries? sentSeries; + private int timeCounter; + + private PlotModel? _plotModel; + public PlotModel? NetworkPlotModel + { + get { return _plotModel; } + set + { + _plotModel = value; + OnPropertyChanged(nameof(NetworkPlotModel)); + } + } + + private string _maxbandText = "0 Mbps"; + public string MaxBandText + { + get { return _maxbandText; } + set + { + _maxbandText = value; + OnPropertyChanged(nameof(MaxBandText)); + } + } + + private string _receivedUsageText = "0"; + public string ReceivedUsageText + { + get { return _receivedUsageText; } + set + { + _receivedUsageText = value; + OnPropertyChanged(nameof(ReceivedUsageText)); + } + } + + private string _sentUsageText = "0"; + public string SentUsageText + { + get { return _sentUsageText; } + set + { + _sentUsageText = value; + OnPropertyChanged(nameof(SentUsageText)); + } + } + + public async Task Start() + { + try + { + await Task.Run(() => + { + var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); + NetworkInterface? interfaceUsed = null; + + foreach (var netInterface in networkInterfaces) + { + if (netInterface.OperationalStatus == OperationalStatus.Up) + { + if (_valid_Interfaces.Contains(netInterface.NetworkInterfaceType)) + { + interfaceUsed = netInterface; + } + } + } + + if (interfaceUsed != null) + { + PerformanceCounterCategory category = new("Network Interface"); + String[] instancenames = category.GetInstanceNames(); + + string cleanedInterfaceUsedDescription = CleanAdaptorName(interfaceUsed.Description); + var cleanedInstanceNames = instancenames.Select(CleanAdaptorName).ToList(); + + var matchedInstance = cleanedInstanceNames + .Select((name, index) => new { CleanedName = name, OriginalName = instancenames[index] }) + .FirstOrDefault(x => x.CleanedName == cleanedInterfaceUsedDescription); + + if (matchedInstance != null) + { + bytesReceivedCounter = new PerformanceCounter("Network Interface", "Bytes Received/sec", matchedInstance.OriginalName); + bytesSentCounter = new PerformanceCounter("Network Interface", "Bytes Sent/sec", matchedInstance.OriginalName); + return; + } + } + + throw new Exception("Invladil Ethernet Adaptor."); + + }, cancellationTokenSource.Token); + } + catch (Exception ex) + { + Logger.Warning(ex.Message); + return; + } + + CreatePlot(); + scheduleID = schedule.Secondly(UpdateUsage, 1); + } + + private void CreatePlot() + { + NetworkPlotModel = new PlotModel + { + PlotAreaBorderThickness = new OxyThickness(0), + PlotAreaBorderColor = OxyColors.Transparent, + Padding = new OxyThickness(0) + }; + + var xAxis = new LinearAxis + { + Position = AxisPosition.Bottom, + IsAxisVisible = false, + MajorGridlineStyle = LineStyle.None, + MinorGridlineStyle = LineStyle.None, + MaximumPadding = 0, + MinimumPadding = 0 + }; + + var yAxis = new LinearAxis + { + Minimum = 0, + Maximum = BandwdithSteps.Min(), + Position = AxisPosition.Left, + IsAxisVisible = false, + MajorGridlineStyle = LineStyle.None, + MinorGridlineStyle = LineStyle.None, + MaximumPadding = 0, + MinimumPadding = 0 + }; + + NetworkPlotModel.Axes.Add(xAxis); + NetworkPlotModel.Axes.Add(yAxis); + + receivedSeries = new AreaSeries + { + LineStyle = LineStyle.Solid, + StrokeThickness = 1, + Color = OxyColor.Parse(Settings.ReceivedColor), + }; + + sentSeries = new AreaSeries + { + LineStyle = LineStyle.Solid, + StrokeThickness = 1, + Color = OxyColor.Parse(Settings.SentColor), + }; + + NetworkPlotModel.Series.Add(receivedSeries); + NetworkPlotModel.Series.Add(sentSeries); + } + + private void UpdateUsage() + { + if(bytesReceivedCounter is null || bytesSentCounter is null || + sentSeries is null || receivedSeries is null || NetworkPlotModel is null) return; + + float bytesReceived = bytesReceivedCounter.NextValue() * 8 / 1024 / 1024; + float bytesSent = bytesSentCounter.NextValue() * 8 / 1024 / 1024; + + Application.Current.Dispatcher.Invoke(() => + { + try + { + sentSeries.Points.Add(new DataPoint(timeCounter, bytesSent)); + sentSeries.Points2.Add(new DataPoint(timeCounter, 0)); + receivedSeries.Points.Add(new DataPoint(timeCounter, bytesReceived + bytesSent)); + receivedSeries.Points2.Add(new DataPoint(timeCounter, bytesSent)); + + var maxY = receivedSeries.Points.Max(point => point.Y); + var currentBandwidth = BandwdithSteps.Min(); + NetworkPlotModel.Axes[1].Maximum = currentBandwidth; + + foreach(int BandwidthStep in BandwdithSteps.OrderByDescending(x => x)) + { + if (maxY < BandwidthStep) + { + currentBandwidth = BandwidthStep; + NetworkPlotModel.Axes[1].Maximum = currentBandwidth; + } + } + + MaxBandText = $"{currentBandwidth:F1} Mbps"; + ReceivedUsageText = $"R: {bytesReceived:F1} "; + SentUsageText = $"S: {bytesSent:F1} Mbps"; + + timeCounter++; + + if (receivedSeries.Points.Count > Settings.TimeLine) + { + receivedSeries.Points.RemoveAt(0); + receivedSeries.Points2.RemoveAt(0); + sentSeries.Points.RemoveAt(0); + sentSeries.Points2.RemoveAt(0); + } + + NetworkPlotModel.InvalidatePlot(true); + } + catch (Exception ex) + { + Logger.Error(ex.Message); + } + }); + } + + // Regex Clean Adaptor Name + public static string CleanAdaptorName(string input) + { + return CleanAdaptorNameRegex().Replace(input, ""); + } + + + public void Dispose() + { + schedule.Stop(scheduleID); + cancellationTokenSource.Cancel(); + GC.SuppressFinalize(this); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +}