Add Ip- and Mac address
This commit is contained in:
parent
0e75065ff7
commit
af503dc1bb
@ -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;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user