Add Ip- and Mac address
This commit is contained in:
		| @@ -2,9 +2,14 @@ | |||||||
|     x:Class="SddpViewer.App" |     x:Class="SddpViewer.App" | ||||||
|     xmlns="https://github.com/avaloniaui" |     xmlns="https://github.com/avaloniaui" | ||||||
|     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||||||
|  |     xmlns:collections="clr-namespace:System.Collections;assembly=System.Runtime" | ||||||
|  |     xmlns:sddpViewer="clr-namespace:SddpViewer" | ||||||
|     RequestedThemeVariant="Default"> |     RequestedThemeVariant="Default"> | ||||||
|     <!--  "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options.  --> |     <!--  "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options.  --> | ||||||
|  |  | ||||||
|  |     <Application.Resources> | ||||||
|  |         <sddpViewer:IpAddressComparer x:Key="IpAddressComparer" /> | ||||||
|  |     </Application.Resources> | ||||||
|     <Application.Styles> |     <Application.Styles> | ||||||
|         <FluentTheme /> |         <FluentTheme /> | ||||||
|         <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" /> |         <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" /> | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| using System.Net; | using System.Net; | ||||||
|  | using System.Net.NetworkInformation; | ||||||
|  |  | ||||||
| namespace SddpViewer; | namespace SddpViewer; | ||||||
|  |  | ||||||
| @@ -7,10 +8,11 @@ using System.Threading.Tasks; | |||||||
| using CommunityToolkit.Mvvm.ComponentModel; | using CommunityToolkit.Mvvm.ComponentModel; | ||||||
| using Rssdp; | using Rssdp; | ||||||
|  |  | ||||||
| public partial class DiscoveredDeviceViewModel : ObservableObject | public partial class DiscoveredDeviceViewModel : ObservableObject, IDisposable | ||||||
| { | { | ||||||
|   private readonly DiscoveredSsdpDevice _device; |   private readonly DiscoveredSsdpDevice _device; | ||||||
|   private SsdpDevice? _ssdpDevice; |   private SsdpDevice? _ssdpDevice; | ||||||
|  |   private readonly CancellationTokenSource _cancellationTokenSource = new(); | ||||||
|  |  | ||||||
|   public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device) |   public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device) | ||||||
|   { |   { | ||||||
| @@ -20,19 +22,45 @@ public partial class DiscoveredDeviceViewModel : ObservableObject | |||||||
|     IpAddress = EvaluateIpAddress(device); |     IpAddress = EvaluateIpAddress(device); | ||||||
|     MacAddress = EvaluateMacAddress(); |     MacAddress = EvaluateMacAddress(); | ||||||
|     HostName = EvaluateHostName(); |     HostName = EvaluateHostName(); | ||||||
|  |     RunPing(_cancellationTokenSource.Token); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private async void RunPing(CancellationToken token) | ||||||
|  |   { | ||||||
|  |     try | ||||||
|  |     { | ||||||
|  |       using Ping ping = new(); | ||||||
|  |       while (!token.IsCancellationRequested) | ||||||
|  |       { | ||||||
|  |         var result = await ping.SendPingAsync(IpAddress, TimeSpan.FromSeconds(1), cancellationToken: token).ConfigureAwait(true); | ||||||
|  |         Online = result.Status == IPStatus.Success; | ||||||
|  |         await Task.Delay(1000, token).ConfigureAwait(true); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     catch (TaskCanceledException e) | ||||||
|  |     { | ||||||
|  |       Console.WriteLine(e); | ||||||
|  |     } | ||||||
|  |     catch (OperationCanceledException e) | ||||||
|  |     { | ||||||
|  |       Console.WriteLine(e); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public string HostName { get; set; } |   public string HostName { get; set; } | ||||||
|  |  | ||||||
|   public string MacAddress { get; set; } |   public string MacAddress { get; set; } | ||||||
|  |  | ||||||
|   public string IpAddress { get; set; } |   public IPAddress IpAddress { get; set; } | ||||||
|  |  | ||||||
|   public DateTime DiscoveredAt { get; } |   public DateTime DiscoveredAt { get; } | ||||||
|  |  | ||||||
|   [ObservableProperty] |   [ObservableProperty] | ||||||
|   public partial string ResponseHeader { get; set; } |   public partial string ResponseHeader { get; set; } | ||||||
|  |  | ||||||
|  |   [ObservableProperty] | ||||||
|  |   public partial bool Online { get; set; } | ||||||
|  |  | ||||||
|   /// <summary> |   /// <summary> | ||||||
|   /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice. |   /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice. | ||||||
|   /// </summary> |   /// </summary> | ||||||
| @@ -73,6 +101,11 @@ public partial class DiscoveredDeviceViewModel : ObservableObject | |||||||
|   [ObservableProperty] |   [ObservableProperty] | ||||||
|   public partial string Version { get; set; } = ""; |   public partial string Version { get; set; } = ""; | ||||||
|  |  | ||||||
|  |   public void Cancel() | ||||||
|  |   { | ||||||
|  |     _cancellationTokenSource.Cancel(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public async Task GetFurtherInformationAsync() |   public async Task GetFurtherInformationAsync() | ||||||
|   { |   { | ||||||
|     _ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false); |     _ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false); | ||||||
| @@ -81,7 +114,7 @@ public partial class DiscoveredDeviceViewModel : ObservableObject | |||||||
|     PresentationUrl = _ssdpDevice.PresentationUrl; |     PresentationUrl = _ssdpDevice.PresentationUrl; | ||||||
|     ModelNumber = _ssdpDevice.ModelNumber; |     ModelNumber = _ssdpDevice.ModelNumber; | ||||||
|     Version = _ssdpDevice.SerialNumber?.Split(',').Last() ?? new Version().ToString(); |     Version = _ssdpDevice.SerialNumber?.Split(',').Last() ?? new Version().ToString(); | ||||||
|     HttpClient client = new HttpClient(); |     using var client = new HttpClient(); | ||||||
|     var response = await client.GetAsync(_ssdpDevice.ModelUrl).ConfigureAwait(false); |     var response = await client.GetAsync(_ssdpDevice.ModelUrl).ConfigureAwait(false); | ||||||
|     ResponseHeader = await response.Content.ReadAsStringAsync(); |     ResponseHeader = await response.Content.ReadAsStringAsync(); | ||||||
|   } |   } | ||||||
| @@ -91,11 +124,12 @@ public partial class DiscoveredDeviceViewModel : ObservableObject | |||||||
|   private string GetResponseHeader() => |   private string GetResponseHeader() => | ||||||
|     string.Join("," + Environment.NewLine, _device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}")); |     string.Join("," + Environment.NewLine, _device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}")); | ||||||
|  |  | ||||||
|   private string EvaluateIpAddress(DiscoveredSsdpDevice device) => device.DescriptionLocation.Host; |   private IPAddress EvaluateIpAddress(DiscoveredSsdpDevice device) => | ||||||
|  |     IPAddress.TryParse(device.DescriptionLocation.Host, out var value) ? value : IPAddress.Any; | ||||||
|  |  | ||||||
|   private string EvaluateMacAddress() |   private string EvaluateMacAddress() | ||||||
|   { |   { | ||||||
|     var lookupResult = ArpLookup.Arp.Lookup(IPAddress.Parse(IpAddress)); |     var lookupResult = ArpLookup.Arp.Lookup(IpAddress); | ||||||
|     return lookupResult is null ? "Unknown" : string.Join(":", lookupResult.GetAddressBytes().Select(b => $"{b:x2}")); |     return lookupResult is null ? "Unknown" : string.Join(":", lookupResult.GetAddressBytes().Select(b => $"{b:x2}")); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -103,4 +137,10 @@ public partial class DiscoveredDeviceViewModel : ObservableObject | |||||||
|   { |   { | ||||||
|     return obj is DiscoveredDeviceViewModel viewModel && Equals(viewModel.Usn, Usn); |     return obj is DiscoveredDeviceViewModel viewModel && Equals(viewModel.Usn, Usn); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public void Dispose() | ||||||
|  |   { | ||||||
|  |     _cancellationTokenSource.Cancel(); | ||||||
|  |     _cancellationTokenSource.Dispose(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| // Global using directives | // Global using directives | ||||||
|  |  | ||||||
|  | global using System.Collections; | ||||||
| global using Avalonia; | global using Avalonia; | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								src/IpAddressComparer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/IpAddressComparer.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | namespace SddpViewer; | ||||||
|  |  | ||||||
|  | public class IpAddressComparer : IComparer, IComparer<DiscoveredDeviceViewModel> | ||||||
|  | { | ||||||
|  |   public int Compare(DiscoveredDeviceViewModel? x, DiscoveredDeviceViewModel? y) | ||||||
|  |   { | ||||||
|  |     var aByte = x?.IpAddress.GetAddressBytes().Sum(b => (long)b) ?? 0; | ||||||
|  |     var bByte = y?.IpAddress.GetAddressBytes().Sum(b => (long)b) ?? 0; | ||||||
|  |     return aByte.CompareTo(bByte); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public int Compare(object? x, object? y) => Compare(x as DiscoveredDeviceViewModel, y as DiscoveredDeviceViewModel); | ||||||
|  | } | ||||||
| @@ -43,12 +43,20 @@ | |||||||
|             Grid.Column="0" |             Grid.Column="0" | ||||||
|             Grid.ColumnSpan="2" |             Grid.ColumnSpan="2" | ||||||
|             AutoGenerateColumns="False" |             AutoGenerateColumns="False" | ||||||
|  |             FrozenColumnCount="2" | ||||||
|             IsReadOnly="True" |             IsReadOnly="True" | ||||||
|             ItemsSource="{Binding SddpDevices}"> |             ItemsSource="{Binding SddpDevices}"> | ||||||
|             <DataGrid.Columns> |             <DataGrid.Columns> | ||||||
|  |                 <DataGridCheckBoxColumn | ||||||
|  |                     Width="50" | ||||||
|  |                     Binding="{Binding Online}" | ||||||
|  |                     IsReadOnly="True" /> | ||||||
|                 <DataGridTextColumn Binding="{Binding FriendlyName}" Header="Name" /> |                 <DataGridTextColumn Binding="{Binding FriendlyName}" Header="Name" /> | ||||||
|                 <DataGridTextColumn Binding="{Binding Usn}" Header="Usn" /> |                 <DataGridTextColumn Binding="{Binding Usn}" Header="Usn" /> | ||||||
|                 <DataGridTextColumn Binding="{Binding IpAddress}" Header="Ip address" /> |                 <DataGridTextColumn | ||||||
|  |                     Binding="{Binding IpAddress}" CanUserSort="True" | ||||||
|  |                     CustomSortComparer="{StaticResource IpAddressComparer}" | ||||||
|  |                     Header="Ip address" /> | ||||||
|                 <DataGridTextColumn Binding="{Binding HostName}" Header="Hostname" /> |                 <DataGridTextColumn Binding="{Binding HostName}" Header="Hostname" /> | ||||||
|                 <DataGridTextColumn Binding="{Binding MacAddress}" Header="Mac address" /> |                 <DataGridTextColumn Binding="{Binding MacAddress}" Header="Mac address" /> | ||||||
|                 <DataGridTextColumn Binding="{Binding Version}" Header="Version" /> |                 <DataGridTextColumn Binding="{Binding Version}" Header="Version" /> | ||||||
|   | |||||||
| @@ -14,8 +14,8 @@ public sealed partial class MainWindowViewModel : ObservableObject, IDisposable | |||||||
|   public MainWindowViewModel() |   public MainWindowViewModel() | ||||||
|   { |   { | ||||||
|     NetworkAdapters = NetworkAdapter.GetAvailableNetworkAdapter().ToArray(); |     NetworkAdapters = NetworkAdapter.GetAvailableNetworkAdapter().ToArray(); | ||||||
|     SelectedNetworkAdapter = NetworkAdapters.FirstOrDefault(); |     SelectedNetworkAdapter = NetworkAdapters.FirstOrDefault(x => x.IpAddress.ToString() == DeviceIpAddress); | ||||||
|     SddpDevices = new ObservableCollection<DiscoveredDeviceViewModel>(); |     SddpDevices = []; | ||||||
|     SddpDevices.CollectionChanged += SddpDevices_OnCollectionChanged; |     SddpDevices.CollectionChanged += SddpDevices_OnCollectionChanged; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -23,18 +23,55 @@ public sealed partial class MainWindowViewModel : ObservableObject, IDisposable | |||||||
|   { |   { | ||||||
|     try |     try | ||||||
|     { |     { | ||||||
|       foreach (object eNewItem in e.NewItems ?? Array.Empty<object>()) |       switch (e.Action) | ||||||
|       { |       { | ||||||
|         if (eNewItem is DiscoveredDeviceViewModel discoveredDeviceViewModel) |         case NotifyCollectionChangedAction.Add: | ||||||
|         { |           await AddAction(e.NewItems); | ||||||
|           await discoveredDeviceViewModel.GetFurtherInformationAsync(); |           break; | ||||||
|         } |         case NotifyCollectionChangedAction.Remove: | ||||||
|  |           RemoveAction(e.OldItems); | ||||||
|  |           break; | ||||||
|  |         case NotifyCollectionChangedAction.Replace: | ||||||
|  |           RemoveAction(e.OldItems); | ||||||
|  |           await AddAction(e.NewItems); | ||||||
|  |           break; | ||||||
|  |         case NotifyCollectionChangedAction.Reset: | ||||||
|  |           RemoveAction(sender as ObservableCollection<DiscoveredDeviceViewModel>); | ||||||
|  |           break; | ||||||
|  |         case NotifyCollectionChangedAction.Move: | ||||||
|  |           break; | ||||||
|  |         default: | ||||||
|  |           throw new ArgumentOutOfRangeException(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     catch (Exception ex) |     catch (Exception ex) | ||||||
|     { |     { | ||||||
|       throw; // TODO handle exception |       throw; // TODO handle exception | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private static void RemoveAction(IList? list) | ||||||
|  |   { | ||||||
|  |     foreach (object eOldItem in list ?? Array.Empty<object>()) | ||||||
|  |     { | ||||||
|  |       if (eOldItem is DiscoveredDeviceViewModel discoveredDeviceViewModel) | ||||||
|  |       { | ||||||
|  |         discoveredDeviceViewModel.Dispose(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private static async Task AddAction(IList? list) | ||||||
|  |   { | ||||||
|  |     foreach (object eNewItem in list ?? Array.Empty<object>()) | ||||||
|  |     { | ||||||
|  |       if (eNewItem is DiscoveredDeviceViewModel discoveredDeviceViewModel) | ||||||
|  |       { | ||||||
|  |         await discoveredDeviceViewModel.GetFurtherInformationAsync(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void LocatorOnDeviceUnavailable(object? sender, DeviceUnavailableEventArgs e) |   private void LocatorOnDeviceUnavailable(object? sender, DeviceUnavailableEventArgs e) | ||||||
| @@ -58,16 +95,16 @@ public sealed partial class MainWindowViewModel : ObservableObject, IDisposable | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   [ObservableProperty] |   [ObservableProperty] | ||||||
|   private IReadOnlyList<NetworkAdapter> _networkAdapters; |   public partial IReadOnlyList<NetworkAdapter> NetworkAdapters { get; set; } | ||||||
|  |  | ||||||
|   [ObservableProperty] |   [ObservableProperty] | ||||||
|   private NetworkAdapter? _selectedNetworkAdapter; |   public partial NetworkAdapter? SelectedNetworkAdapter { get; set; } | ||||||
|  |  | ||||||
|   [ObservableProperty] |   [ObservableProperty] | ||||||
|   private string _deviceIpAddress = "192.168.42.193"; |   public partial string DeviceIpAddress { get; set; } = "192.168.42.192"; | ||||||
|  |  | ||||||
|   [ObservableProperty] |   [ObservableProperty] | ||||||
|   private string _notificationFilter = "upnp:rootdevice"; |   public partial string NotificationFilter { get; set; } = "upnp:rootdevice"; | ||||||
|  |  | ||||||
|   private SsdpDeviceLocator? _locator; |   private SsdpDeviceLocator? _locator; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Holger Börchers
					Holger Börchers