From 55a00b583c0c5a953a2a119d12c73140c138cb12 Mon Sep 17 00:00:00 2001 From: Eric Williams Date: Sat, 13 Apr 2024 09:37:22 -0400 Subject: [PATCH 01/13] created view for scan configuration --- source/Kangaroo.UI/App.axaml | 5 ++ .../Controls/ScanConfiguratorView.axaml | 51 ++++++++++++++ .../Controls/ScanConfiguratorView.axaml.cs | 13 ++++ .../Controls/ScanConfiguratorViewModel.cs | 69 +++++++++++++++++++ .../Controls/ScanModeTextConverter.cs | 36 ++++++++++ source/Kangaroo.UI/Kangaroo.UI.csproj | 4 ++ .../Kangaroo.UI/Services/ServiceExtensions.cs | 5 ++ .../ViewModels/IpScannerViewModel.cs | 5 +- source/Kangaroo.UI/Views/IpScannerView.axaml | 2 + 9 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml create mode 100644 source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml.cs create mode 100644 source/Kangaroo.UI/Controls/ScanConfiguratorViewModel.cs create mode 100644 source/Kangaroo.UI/Controls/ScanModeTextConverter.cs diff --git a/source/Kangaroo.UI/App.axaml b/source/Kangaroo.UI/App.axaml index b187042..129e23e 100644 --- a/source/Kangaroo.UI/App.axaml +++ b/source/Kangaroo.UI/App.axaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia" xmlns:local="using:Kangaroo.UI" + xmlns:controls="clr-namespace:Kangaroo.UI.Controls" x:Class="Kangaroo.UI.App" RequestedThemeVariant="Default"> @@ -13,4 +14,8 @@ + + + + diff --git a/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml b/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml new file mode 100644 index 0000000..dfa9fec --- /dev/null +++ b/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml.cs b/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml.cs new file mode 100644 index 0000000..3ce8a49 --- /dev/null +++ b/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Microsoft.Extensions.DependencyInjection; + +namespace Kangaroo.UI.Controls; + +public partial class ScanConfiguratorView : UserControl +{ + public ScanConfiguratorView() + { + InitializeComponent(); + DataContext = App.Services.GetRequiredService(); + } +} diff --git a/source/Kangaroo.UI/Controls/ScanConfiguratorViewModel.cs b/source/Kangaroo.UI/Controls/ScanConfiguratorViewModel.cs new file mode 100644 index 0000000..bc01fc0 --- /dev/null +++ b/source/Kangaroo.UI/Controls/ScanConfiguratorViewModel.cs @@ -0,0 +1,69 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; +using Kangaroo.UI.Models; +using Kangaroo.UI.ViewModels; +using System.Collections.ObjectModel; + +namespace Kangaroo.UI.Controls; +public partial class ScanConfiguratorViewModel : ViewModelBase +{ + [ObservableProperty] + private ScanMode _selectedMode = ScanMode.AddressRange; + + [ObservableProperty] + private ObservableCollection _scanModes = new() + { + ScanMode.AddressRange, + ScanMode.SingleAddress, + ScanMode.NetworkSubnet, + ScanMode.SpecifiedAddresses, + ScanMode.NetworkAdapter, + }; + + [ObservableProperty] + private ObservableCollection _adapters = new() + { + new NetworkAdapter { IpAddress = "127.0.0.1", MacAddress = "00000000", Name = "Ethernet" }, + new NetworkAdapter { IpAddress = "192.168.5.5", MacAddress = "00000000", Name = "Wifi" }, + new NetworkAdapter { IpAddress = "127.0.0.1", MacAddress = "00000000", Name = "Ethernet" }, + }; + + [ObservableProperty] + private NetworkAdapter _adapter; + + [ObservableProperty] + private bool _showRangeFields = true; + + [ObservableProperty] + private bool _showSubnetFields = true; + + [ObservableProperty] + private bool _showAdapterFields = true; + + [ObservableProperty] + private string _startAddress; + + [ObservableProperty] + private string _endAddress; + + partial void OnSelectedModeChanged(ScanMode mode) + { + ShowRangeFields = mode == ScanMode.AddressRange; + ShowSubnetFields = mode == ScanMode.NetworkSubnet; + ShowAdapterFields = mode == ScanMode.NetworkAdapter; + } + + public ScanConfiguratorViewModel() + { + + } + + +} + +public class NetworkAdapter +{ + public string Name { get; set; } + public string IpAddress { get; set; } + public string MacAddress { get; set; } +} \ No newline at end of file diff --git a/source/Kangaroo.UI/Controls/ScanModeTextConverter.cs b/source/Kangaroo.UI/Controls/ScanModeTextConverter.cs new file mode 100644 index 0000000..e46ae67 --- /dev/null +++ b/source/Kangaroo.UI/Controls/ScanModeTextConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Globalization; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Kangaroo.UI.Models; + +namespace Kangaroo.UI.Controls; + +public class ScanModeTextConverter : IValueConverter +{ + public static readonly ScanModeTextConverter Instance = new(); + + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is ScanMode mode && targetType.IsAssignableTo(typeof(string))) + { + return mode switch + { + ScanMode.NetworkSubnet => "Network Subnet", + ScanMode.AddressRange => "IP Address Range", + ScanMode.NetworkAdapter => "Network Adapter", + ScanMode.SingleAddress => "IP Address", + ScanMode.SpecifiedAddresses => "IP Addresses", + _ => throw new ArgumentOutOfRangeException() + }; + } + + return new BindingNotification(new InvalidCastException(), BindingErrorType.Error); + } + + public object ConvertBack(object? value, Type targetType, + object? parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/source/Kangaroo.UI/Kangaroo.UI.csproj b/source/Kangaroo.UI/Kangaroo.UI.csproj index de5cf60..c445df9 100644 --- a/source/Kangaroo.UI/Kangaroo.UI.csproj +++ b/source/Kangaroo.UI/Kangaroo.UI.csproj @@ -58,4 +58,8 @@ Designer + + + + diff --git a/source/Kangaroo.UI/Services/ServiceExtensions.cs b/source/Kangaroo.UI/Services/ServiceExtensions.cs index 0bdca57..043d894 100644 --- a/source/Kangaroo.UI/Services/ServiceExtensions.cs +++ b/source/Kangaroo.UI/Services/ServiceExtensions.cs @@ -3,6 +3,7 @@ using System.IO; using Avalonia.Controls; using Avalonia.Controls.Shapes; +using Kangaroo.UI.Controls; using Kangaroo.UI.Services; using Kangaroo.UI.Services.Database; using Kangaroo.UI.ViewModels; @@ -68,6 +69,10 @@ public static HostApplicationBuilder AddViewModels(this HostApplicationBuilder b builder.Services.AddTransient(); builder.Services.AddTransient(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + return builder; } } diff --git a/source/Kangaroo.UI/ViewModels/IpScannerViewModel.cs b/source/Kangaroo.UI/ViewModels/IpScannerViewModel.cs index 0f8aabb..7a3f6f2 100644 --- a/source/Kangaroo.UI/ViewModels/IpScannerViewModel.cs +++ b/source/Kangaroo.UI/ViewModels/IpScannerViewModel.cs @@ -227,8 +227,7 @@ private async Task LoadRecent() private void AddInitialNetworkNode(NetworkNode node, List queryTimes, List latencyTimes, List axisLabels) { NetworkNodes.Insert(0,new NetworkNodeModel(node)); - //NetworkNodes.Add(new NetworkNodeModel(node)); - + queryTimes.Add(node.QueryTime.TotalMilliseconds); latencyTimes.Add(node.Latency != null ? node.Latency!.Value.TotalMilliseconds @@ -246,8 +245,8 @@ private void UpdateActiveNetworkNode(NetworkNode node, List queryTimes, { var nodeToRemove = NetworkNodes.First(n => n.IpAddress == node.IpAddress.ToString()); NetworkNodes.Remove(nodeToRemove); - // NetworkNodes.Add(new NetworkNodeModel(node)); NetworkNodes.Insert(0, new NetworkNodeModel(node)); + queryTimes.Add(node.QueryTime.TotalMilliseconds); latencyTimes.Add(node.Latency != null ? node.Latency!.Value.TotalMilliseconds diff --git a/source/Kangaroo.UI/Views/IpScannerView.axaml b/source/Kangaroo.UI/Views/IpScannerView.axaml index 98ef950..9f5bed3 100644 --- a/source/Kangaroo.UI/Views/IpScannerView.axaml +++ b/source/Kangaroo.UI/Views/IpScannerView.axaml @@ -5,6 +5,7 @@ xmlns:viewModels="clr-namespace:Kangaroo.UI.ViewModels" xmlns:anim="https://github.com/whistyun/AnimatedImage.Avalonia" xmlns:lvc="using:LiveChartsCore.SkiaSharpView.Avalonia" + xmlns:controls="clr-namespace:Kangaroo.UI.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Kangaroo.UI.Views.IpScannerView" x:DataType="viewModels:IpScannerViewModel"> @@ -19,6 +20,7 @@ Grid.Column="0" Margin="20, 5,0,0" Orientation="Horizontal"> + From 2c2d42a1408d0d762e516429dadd0324f6790553 Mon Sep 17 00:00:00 2001 From: Eric Williams Date: Sat, 13 Apr 2024 18:24:58 -0400 Subject: [PATCH 02/13] created scan factory and view closes #33 closes #45 closes #44 --- source/Kangaroo.UI/App.axaml | 3 +- .../Controls/ScanConfiguratorView.axaml | 22 ++- .../Controls/ScanConfiguratorView.axaml.cs | 1 + .../Controls/ScanConfiguratorViewModel.cs | 170 ++++++++++++++++-- .../Controls/ScanModeTextConverter.cs | 21 +++ source/Kangaroo.UI/Controls/ScannerFactory.cs | 134 ++++++++++++++ .../Services/ContainerExtensions.cs | 63 +++++++ .../Kangaroo.UI/Services/ServiceExtensions.cs | 70 -------- source/Kangaroo.UI/Services/ServiceOptions.cs | 13 ++ .../ViewModels/IpScannerViewModel.cs | 65 ++++--- source/Kangaroo.UI/Views/IpScannerView.axaml | 13 +- source/Kangaroo/Assembly.cs | 3 +- .../Kangaroo/Builder/Options/QueryOptions.cs | 7 +- .../Kangaroo/Builder/Options/WithOptions.cs | 27 +++ .../Builder/Pipeline/AddressFactory.cs | 9 + .../Builder/Pipeline/IScannerBuilder.cs | 15 +- .../Builder/Pipeline/ScannerBuilder.cs | 27 +++ 17 files changed, 534 insertions(+), 129 deletions(-) create mode 100644 source/Kangaroo.UI/Controls/ScannerFactory.cs create mode 100644 source/Kangaroo.UI/Services/ContainerExtensions.cs create mode 100644 source/Kangaroo.UI/Services/ServiceOptions.cs create mode 100644 source/Kangaroo/Builder/Options/WithOptions.cs diff --git a/source/Kangaroo.UI/App.axaml b/source/Kangaroo.UI/App.axaml index 129e23e..d42e7ee 100644 --- a/source/Kangaroo.UI/App.axaml +++ b/source/Kangaroo.UI/App.axaml @@ -16,6 +16,7 @@ - + + diff --git a/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml b/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml index dfa9fec..2935f09 100644 --- a/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml +++ b/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml @@ -31,21 +31,35 @@ + + + + + + + + + + + + + + + + - + - - diff --git a/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml.cs b/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml.cs index 3ce8a49..9bcbfb2 100644 --- a/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml.cs +++ b/source/Kangaroo.UI/Controls/ScanConfiguratorView.axaml.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Controls; using Microsoft.Extensions.DependencyInjection; diff --git a/source/Kangaroo.UI/Controls/ScanConfiguratorViewModel.cs b/source/Kangaroo.UI/Controls/ScanConfiguratorViewModel.cs index bc01fc0..a0bc7c2 100644 --- a/source/Kangaroo.UI/Controls/ScanConfiguratorViewModel.cs +++ b/source/Kangaroo.UI/Controls/ScanConfiguratorViewModel.cs @@ -3,10 +3,20 @@ using Kangaroo.UI.Models; using Kangaroo.UI.ViewModels; using System.Collections.ObjectModel; +using System.Linq; +using System.Net; +using System.Net.Sockets; namespace Kangaroo.UI.Controls; public partial class ScanConfiguratorViewModel : ViewModelBase { + private readonly IScannerFactory _factory; + + public ScanConfiguratorViewModel(IScannerFactory factory) + { + _factory = factory; + } + [ObservableProperty] private ScanMode _selectedMode = ScanMode.AddressRange; @@ -16,29 +26,52 @@ public partial class ScanConfiguratorViewModel : ViewModelBase ScanMode.AddressRange, ScanMode.SingleAddress, ScanMode.NetworkSubnet, - ScanMode.SpecifiedAddresses, ScanMode.NetworkAdapter, }; [ObservableProperty] - private ObservableCollection _adapters = new() + private ObservableCollection _adapters = new(AddressFactory.GetInterfaces().Select(i => new NetworkAdapter() { - new NetworkAdapter { IpAddress = "127.0.0.1", MacAddress = "00000000", Name = "Ethernet" }, - new NetworkAdapter { IpAddress = "192.168.5.5", MacAddress = "00000000", Name = "Wifi" }, - new NetworkAdapter { IpAddress = "127.0.0.1", MacAddress = "00000000", Name = "Ethernet" }, - }; + IpAddress = i.GetIPProperties().UnicastAddresses.Where(a => a.Address.AddressFamily != AddressFamily.InterNetwork).FirstOrDefault().Address.ToString(), + Name = i.Name, + MacAddress = i.GetPhysicalAddress().ToString() + + })); [ObservableProperty] - private NetworkAdapter _adapter; + private NetworkAdapter? _adapter; + + partial void OnAdapterChanged(NetworkAdapter? value) + { + if (SelectedMode != ScanMode.NetworkAdapter) + { + return; + } + + if (Adapter != null) + { + _factory.CreateScanner(new ScannerOptions() + { + NetworkInterface = AddressFactory.GetInterfaces().First(i => i.Name == Adapter.Name), + ScanMode = ScanMode.NetworkAdapter, + Timeout = TimeSpan.FromSeconds(1), + Ttl = 10, + WithHttp = true + }); + } + } [ObservableProperty] private bool _showRangeFields = true; [ObservableProperty] - private bool _showSubnetFields = true; + private bool _showSingleFields = false; + + [ObservableProperty] + private bool _showSubnetFields = false; [ObservableProperty] - private bool _showAdapterFields = true; + private bool _showAdapterFields = false; [ObservableProperty] private string _startAddress; @@ -46,18 +79,127 @@ public partial class ScanConfiguratorViewModel : ViewModelBase [ObservableProperty] private string _endAddress; - partial void OnSelectedModeChanged(ScanMode mode) + partial void OnStartAddressChanged(string value) { - ShowRangeFields = mode == ScanMode.AddressRange; - ShowSubnetFields = mode == ScanMode.NetworkSubnet; - ShowAdapterFields = mode == ScanMode.NetworkAdapter; + if (SelectedMode != ScanMode.AddressRange) + { + return; + } + + if (IPAddress.TryParse(StartAddress, out var start) && + IPAddress.TryParse(EndAddress, out var end)) + { + _factory.CreateScanner(new ScannerOptions() + { + ScanMode = ScanMode.AddressRange, + Timeout = TimeSpan.FromSeconds(1), + Ttl = 10, + WithHttp = true, + StartAddress = start, + EndAddress = end + }); + } } + partial void OnEndAddressChanged(string value) + { + if (SelectedMode != ScanMode.AddressRange) + { + return; + } - public ScanConfiguratorViewModel() + if (IPAddress.TryParse(StartAddress, out var start) && + IPAddress.TryParse(EndAddress, out var end)) + { + + _factory.CreateScanner(new ScannerOptions() + { + ScanMode = ScanMode.AddressRange, + Timeout = TimeSpan.FromSeconds(1), + Ttl = 10, + WithHttp = true, + StartAddress = start, + EndAddress = end + }); + } + } + + [ObservableProperty] + private string _ipAddress; + + [ObservableProperty] + private string _netmaskAddress; + + partial void OnIpAddressChanged(string value) { + if (SelectedMode == ScanMode.SingleAddress) + { + if (IPAddress.TryParse(IpAddress, out var singleAddress)) + { + _factory.CreateScanner(new ScannerOptions() + { + ScanMode = SelectedMode, + Timeout = TimeSpan.FromSeconds(1), + Ttl = 10, + WithHttp = true, + SpecificAddress = singleAddress, + }); + } + } + + if (SelectedMode != ScanMode.NetworkSubnet) + { + return; + } + if (IPAddress.TryParse(IpAddress, out var ip) && + IPAddress.TryParse(NetmaskAddress, out var mask)) + { + _factory.CreateScanner(new ScannerOptions() + { + ScanMode = SelectedMode, + Timeout = TimeSpan.FromSeconds(1), + Ttl = 10, + WithHttp = true, + SpecificAddress = ip, + NetmaskAddress = mask + }); + } } + partial void OnNetmaskAddressChanged(string value) + { + if (SelectedMode != ScanMode.NetworkSubnet) + { + return; + } + if (IPAddress.TryParse(IpAddress, out var ip) && + IPAddress.TryParse(NetmaskAddress, out var mask)) + { + + _factory.CreateScanner(new ScannerOptions() + { + ScanMode = SelectedMode, + Timeout = TimeSpan.FromSeconds(1), + Ttl = 10, + WithHttp = true, + SpecificAddress = ip, + NetmaskAddress = mask + }); + } + } + + partial void OnSelectedModeChanged(ScanMode mode) + { + ShowRangeFields = mode == ScanMode.AddressRange; + ShowSubnetFields = mode == ScanMode.NetworkSubnet; + ShowAdapterFields = mode == ScanMode.NetworkAdapter; + ShowSingleFields = mode == ScanMode.SingleAddress; + + if (mode == ScanMode.NetworkAdapter) + { + Adapter = Adapters.FirstOrDefault(); + } + } } diff --git a/source/Kangaroo.UI/Controls/ScanModeTextConverter.cs b/source/Kangaroo.UI/Controls/ScanModeTextConverter.cs index e46ae67..41fde27 100644 --- a/source/Kangaroo.UI/Controls/ScanModeTextConverter.cs +++ b/source/Kangaroo.UI/Controls/ScanModeTextConverter.cs @@ -28,6 +28,27 @@ public class ScanModeTextConverter : IValueConverter return new BindingNotification(new InvalidCastException(), BindingErrorType.Error); } + public object ConvertBack(object? value, Type targetType, + object? parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } +} + +public class NetworkAdapterTextConverter : IValueConverter +{ + public static readonly ScanModeTextConverter Instance = new(); + + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is NetworkAdapter apt && targetType.IsAssignableTo(typeof(string))) + { + return $"{apt.Name} | {apt.IpAddress} | {apt.MacAddress}"; + } + + return new BindingNotification(new InvalidCastException(), BindingErrorType.Error); + } + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { diff --git a/source/Kangaroo.UI/Controls/ScannerFactory.cs b/source/Kangaroo.UI/Controls/ScannerFactory.cs new file mode 100644 index 0000000..32f28de --- /dev/null +++ b/source/Kangaroo.UI/Controls/ScannerFactory.cs @@ -0,0 +1,134 @@ +using Kangaroo.UI.Models; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.NetworkInformation; + +namespace Kangaroo.UI.Controls; + +public interface IScannerFactory +{ + Action? OnScannerCreated { get; set; } + + void CreateScanner(ScannerOptions options); +} + + +public sealed class ScannerFactory : IScannerFactory +{ + + public Action? OnScannerCreated { get; set; } + + public void CreateScanner(ScannerOptions options) + { + switch (options.ScanMode) + { + case ScanMode.AddressRange: + CreateRangeScanner(options); + break; + case ScanMode.NetworkSubnet: + CreateSubnetScanner(options); + break; + case ScanMode.NetworkAdapter: + CreateAdapterScanner(options); + break; + case ScanMode.SingleAddress: + CreateSingleScanner(options); + break; + case ScanMode.SpecifiedAddresses: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private void CreateRangeScanner(ScannerOptions options) + { + ArgumentNullException.ThrowIfNull(options.StartAddress); + ArgumentNullException.ThrowIfNull(options.EndAddress); + + var scanner = new ScannerBuilder() + .WithRange(options.StartAddress, options.EndAddress) + .WithOptions(ops => + { + ops.EnableHttpScan = options.WithHttp; + ops.Ttl = options.Ttl; + ops.Timeout = options.Timeout; + }) + .WithParallelism() + .Build(); + + OnScannerCreated?.Invoke(scanner, true); + } + + private void CreateSingleScanner(ScannerOptions options) + { + ArgumentNullException.ThrowIfNull(options.SpecificAddress); + + var scanner = new ScannerBuilder() + .WithAddress(options.SpecificAddress) + .WithOptions(ops => + { + ops.EnableHttpScan = options.WithHttp; + ops.Ttl = options.Ttl; + ops.Timeout = options.Timeout; + }) + .WithParallelism() + .Build(); + + OnScannerCreated?.Invoke(scanner, true); + } + private void CreateSubnetScanner(ScannerOptions options) + { + ArgumentNullException.ThrowIfNull(options.SpecificAddress); + ArgumentNullException.ThrowIfNull(options.NetmaskAddress); + + var scanner = new ScannerBuilder() + .WithSubnet(options.SpecificAddress, options.NetmaskAddress) + .WithOptions(ops => + { + ops.EnableHttpScan = options.WithHttp; + ops.Ttl = options.Ttl; + ops.Timeout = options.Timeout; + }) + .WithParallelism() + .Build(); + + OnScannerCreated?.Invoke(scanner, true); + } + + private void CreateAdapterScanner(ScannerOptions options) + { + ArgumentNullException.ThrowIfNull(options.NetworkInterface); + + var scanner = new ScannerBuilder() + .WithInterface(options.NetworkInterface) + .WithOptions(ops => + { + ops.EnableHttpScan = options.WithHttp; + ops.Ttl = options.Ttl; + ops.Timeout = options.Timeout; + }) + .WithParallelism() + .Build(); + + OnScannerCreated?.Invoke(scanner, true); + } +} + +public sealed class ScannerOptions +{ + public ScanMode ScanMode { get; set; } + + public bool WithHttp { get; set; } + public int Ttl { get; set; } + public TimeSpan Timeout { get; set; } + + public IPAddress? StartAddress { get; set; } + public IPAddress? EndAddress { get; set; } + public IPAddress? SpecificAddress { get; set; } + public IPAddress? NetmaskAddress { get; set; } + public IEnumerable? SpecificAddresses { get; set; } + public NetworkInterface? NetworkInterface { get; set; } + +} \ No newline at end of file diff --git a/source/Kangaroo.UI/Services/ContainerExtensions.cs b/source/Kangaroo.UI/Services/ContainerExtensions.cs new file mode 100644 index 0000000..7a29103 --- /dev/null +++ b/source/Kangaroo.UI/Services/ContainerExtensions.cs @@ -0,0 +1,63 @@ +using System; +using Kangaroo.UI.Controls; +using Kangaroo.UI.Services.Database; +using Kangaroo.UI.ViewModels; +using Kangaroo.UI.Views; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Kangaroo.UI.Services; + +public static class ContainerExtensions +{ + public static HostApplicationBuilder AddKangaroo(this HostApplicationBuilder builder, Action? options = null) + { + var ops = new ServiceOptions(); + options?.Invoke(ops); + + return builder + .AddDatabaseServices(ops) + .AddScannerServices(ops) + .AddViewModels(ops); + } + + public static HostApplicationBuilder AddDatabaseServices(this HostApplicationBuilder builder, ServiceOptions options) + { + builder.Services.AddTransient(sp => new SqliteDbConnectionFactory(options.DatabaseConnection)); + builder.Services.AddTransient(); + + builder.Services.AddTransient(); + return builder; + } + public static HostApplicationBuilder AddScannerServices(this HostApplicationBuilder builder, ServiceOptions options) + { + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + return builder; + } + + public static HostApplicationBuilder AddViewModels(this HostApplicationBuilder builder, ServiceOptions options) + { + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + return builder; + } +} \ No newline at end of file diff --git a/source/Kangaroo.UI/Services/ServiceExtensions.cs b/source/Kangaroo.UI/Services/ServiceExtensions.cs index 043d894..1eafb99 100644 --- a/source/Kangaroo.UI/Services/ServiceExtensions.cs +++ b/source/Kangaroo.UI/Services/ServiceExtensions.cs @@ -1,82 +1,12 @@ using System; using System.Diagnostics; -using System.IO; using Avalonia.Controls; using Avalonia.Controls.Shapes; -using Kangaroo.UI.Controls; using Kangaroo.UI.Services; -using Kangaroo.UI.Services.Database; -using Kangaroo.UI.ViewModels; -using Kangaroo.UI.Views; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Path = System.IO.Path; namespace Kangaroo.UI.Services; -public class ServiceOptions -{ - public string DatabaseConnection { get; set; } - - public ServiceOptions() - { - DatabaseConnection = $"Data Source={Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}kangaroo_scanner.db"; - } -} - -public static class ContainerExtensions -{ - public static HostApplicationBuilder AddKangaroo(this HostApplicationBuilder builder, Action? options = null) - { - var ops = new ServiceOptions(); - options?.Invoke(ops); - - return builder - .AddDatabaseServices(ops) - .AddScannerServices(ops) - .AddViewModels(ops); - } - - public static HostApplicationBuilder AddDatabaseServices(this HostApplicationBuilder builder, ServiceOptions options) - { - builder.Services.AddTransient(sp => new SqliteDbConnectionFactory(options.DatabaseConnection)); - builder.Services.AddTransient(); - - builder.Services.AddTransient(); - return builder; - } - public static HostApplicationBuilder AddScannerServices(this HostApplicationBuilder builder, ServiceOptions options) - { - builder.Services.AddTransient(); - return builder; - } - - public static HostApplicationBuilder AddViewModels(this HostApplicationBuilder builder, ServiceOptions options) - { - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - - builder.Services.AddTransient(); - builder.Services.AddTransient(); - - builder.Services.AddTransient(); - builder.Services.AddSingleton(); - - builder.Services.AddTransient(); - builder.Services.AddTransient(); - - builder.Services.AddTransient(); - builder.Services.AddTransient(); - - - builder.Services.AddTransient(); - builder.Services.AddTransient(); - - return builder; - } -} - public static class ServiceExtensions { public static IServiceProvider GetServiceProvider(this Control control) diff --git a/source/Kangaroo.UI/Services/ServiceOptions.cs b/source/Kangaroo.UI/Services/ServiceOptions.cs new file mode 100644 index 0000000..d0eaf07 --- /dev/null +++ b/source/Kangaroo.UI/Services/ServiceOptions.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace Kangaroo.UI.Services; + +public class ServiceOptions +{ + public string DatabaseConnection { get; set; } + + public ServiceOptions() + { + DatabaseConnection = $"Data Source={Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}kangaroo_scanner.db"; + } +} \ No newline at end of file diff --git a/source/Kangaroo.UI/ViewModels/IpScannerViewModel.cs b/source/Kangaroo.UI/ViewModels/IpScannerViewModel.cs index 7a3f6f2..2f09716 100644 --- a/source/Kangaroo.UI/ViewModels/IpScannerViewModel.cs +++ b/source/Kangaroo.UI/ViewModels/IpScannerViewModel.cs @@ -15,11 +15,14 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using Kangaroo.UI.Controls; namespace Kangaroo.UI.ViewModels; public partial class IpScannerViewModel : ViewModelBase { + private IScanner? _scanner; + private readonly IScannerFactory _factory; private readonly RecentScansRepository _recentScans; /// @@ -27,12 +30,18 @@ public partial class IpScannerViewModel : ViewModelBase /// public IpScannerViewModel() { - + } - public IpScannerViewModel(RecentScansRepository recentScans) + public IpScannerViewModel(RecentScansRepository recentScans, IScannerFactory factory) { _recentScans = recentScans; + _factory = factory; + factory.OnScannerCreated = (scanner, valid) => + { + ScanEnabled = valid && scanner is not null; + _scanner = scanner; + }; LoadRecent().SafeFireAndForget(); } @@ -40,10 +49,10 @@ public IpScannerViewModel(RecentScansRepository recentScans) private ObservableCollection _networkNodes = new(); [ObservableProperty] - [NotifyPropertyChangedFor(nameof(IsNotScanning))] + //[NotifyPropertyChangedFor(nameof(IsNotScanning))] private bool _isScanning; - public bool IsNotScanning => !IsScanning; + //public bool IsNotScanning => !IsScanning; [ObservableProperty] private bool _scanEnabled; @@ -109,16 +118,16 @@ public IpScannerViewModel(RecentScansRepository recentScans) new Axis { Name = "SECONDS", TextSize = 10, Labeler = d => $"{d / 1000:N2} sec." } }; - partial void OnBeginIpAddressChanged(string value) - { - ScanEnabled = IPAddress.TryParse(BeginIpAddress, out var _) && - IPAddress.TryParse(EndIpAddress, out var _); - } - partial void OnEndIpAddressChanged(string value) - { - ScanEnabled = IPAddress.TryParse(BeginIpAddress, out var _) && - IPAddress.TryParse(EndIpAddress, out var _); - } + //partial void OnBeginIpAddressChanged(string value) + //{ + // ScanEnabled = IPAddress.TryParse(BeginIpAddress, out var _) && + // IPAddress.TryParse(EndIpAddress, out var _); + //} + //partial void OnEndIpAddressChanged(string value) + //{ + // ScanEnabled = IPAddress.TryParse(BeginIpAddress, out var _) && + // IPAddress.TryParse(EndIpAddress, out var _); + //} private CancellationTokenSource _cts; @@ -141,25 +150,33 @@ public async Task StartScan() { return; } + + if (_scanner == null) + { + IsScanning = false; + return; + } _cts = new CancellationTokenSource(); IsScanning = true; NetworkNodes = new ObservableCollection(); - using var scanner = new ScannerBuilder() - .WithRange(IPAddress.Parse(BeginIpAddress), IPAddress.Parse(EndIpAddress)) - .WithHttpScan() - .WithMaxTimeout(TimeSpan.FromMilliseconds(1000)) - .WithMaxHops(10) - .WithParallelism() - .Build(); + //using var scanner = new ScannerBuilder() + // .WithRange(IPAddress.Parse(BeginIpAddress), IPAddress.Parse(EndIpAddress)) + // .WithHttpScan() + // .WithMaxTimeout(TimeSpan.FromMilliseconds(1000)) + // .WithMaxHops(10) + // .WithParallelism() + // .Build(); + + var queryTimes = new List(); var latencyTimes = new List(); var axisLabels = new List(); var counter = 0; var items = 0; - scanner.NodeStatusUpdate = (node, status) => + _scanner.NodeStatusUpdate = (node, status) => { if (status == LiveUpdateStatus.Started) { @@ -176,7 +193,7 @@ public async Task StartScan() } }; - scanner.ScanStatusUpdate = (results, status) => + _scanner.ScanStatusUpdate = (results, status) => { if (status == LiveUpdateStatus.Started) { @@ -197,7 +214,7 @@ public async Task StartScan() try { - var results = await scanner.QueryNetwork(_cts.Token); + var results = await _scanner.QueryNetwork(_cts.Token); UpdateAliveChartData(results, queryTimes, latencyTimes, axisLabels); diff --git a/source/Kangaroo.UI/Views/IpScannerView.axaml b/source/Kangaroo.UI/Views/IpScannerView.axaml index 9f5bed3..4af4f64 100644 --- a/source/Kangaroo.UI/Views/IpScannerView.axaml +++ b/source/Kangaroo.UI/Views/IpScannerView.axaml @@ -20,20 +20,11 @@ Grid.Column="0" Margin="20, 5,0,0" Orientation="Horizontal"> - - - - - - - - - - +