changed to using only one udp client, enables to reply from the same port that the message was received on,

implemented LongInstanceIdentifier to show full identifier in the UI ToolTip
master
Jonas Arnold 3 years ago
parent d8191bc80b
commit 897908bf13
  1. 8
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  2. 1
      MultiTerm.Protocols/CommunicationProtocol.cs
  3. 5
      MultiTerm.Protocols/Helpers/NetworkProtocolHelpers.cs
  4. 7
      MultiTerm.Protocols/ICommunicationProtocol.cs
  5. 3
      MultiTerm.Protocols/Model/ExtendedByte.cs
  6. 2
      MultiTerm.Protocols/Serial/SerialProtocol.cs
  7. 15
      MultiTerm.Protocols/Tcp/TcpClientProtocol.cs
  8. 67
      MultiTerm.Protocols/Udp/UdpProtocol.cs
  9. 2
      MultiTerm.Protocols/UsbHid/UsbHidProtocol.cs
  10. 4
      MultiTerm.Wpf/View/ShellView.xaml

@ -95,6 +95,13 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
[ObservableProperty] [ObservableProperty]
private ProtocolConnectionState communicationProtocolConnectionState; private ProtocolConnectionState communicationProtocolConnectionState;
/// <summary>
/// <see cref="ICommunicationProtocol.LongInstanceIdentifier"/> as ObservableProperty.
/// Required since <see cref="CommunicationProtocol"/> does not implement <see cref="ObservableObject"/>.
/// </summary>
[ObservableProperty]
private string communicationProtocolLongIdentifier = string.Empty;
#endregion #endregion
@ -167,6 +174,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
{ {
// update internal state // update internal state
this.CommunicationProtocolConnectionState = e; this.CommunicationProtocolConnectionState = e;
this.CommunicationProtocolLongIdentifier = this.CommunicationProtocol.LongInstanceIdentifier;
// If ViewModel still displays connected after unintentional disconnect => force to disconnected state // If ViewModel still displays connected after unintentional disconnect => force to disconnected state
// allows user to change settings // allows user to change settings

@ -24,6 +24,7 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
public abstract ProtocolType ProtocolType { get; } public abstract ProtocolType ProtocolType { get; }
public abstract string InstanceIdentifier { get; protected set; } public abstract string InstanceIdentifier { get; protected set; }
public abstract string LongInstanceIdentifier { get; protected set; }
private ProtocolConnectionState state = ProtocolConnectionState.NotConnected; private ProtocolConnectionState state = ProtocolConnectionState.NotConnected;
public ProtocolConnectionState State public ProtocolConnectionState State

@ -6,7 +6,7 @@ internal static class NetworkProtocolHelpers
{ {
/// <summary> /// <summary>
/// Returns the hostname of the <paramref name="settingsObject"/> but limited to <paramref name="maxLength"/> amount of characters. /// Returns the hostname of the <paramref name="settingsObject"/> but limited to <paramref name="maxLength"/> amount of characters.
/// If the hostname is longer than <paramref name="maxLength"/>, '...' will be prefixed. The resulting string may also be <paramref name="maxLength"/>+3 characters long. /// If the hostname is longer than <paramref name="maxLength"/>, '...' will be postfixed. The resulting string may also be <paramref name="maxLength"/>+3 characters long.
/// If the hostname cannot be read, the result will be "invalid". /// If the hostname cannot be read, the result will be "invalid".
/// </summary> /// </summary>
/// <param name="settingsObject">settings object containing hostname</param> /// <param name="settingsObject">settings object containing hostname</param>
@ -15,11 +15,10 @@ internal static class NetworkProtocolHelpers
public static string GetLimitedLengthHostname(INetworkProtocolSettings settingsObject, int maxLength = 20) public static string GetLimitedLengthHostname(INetworkProtocolSettings settingsObject, int maxLength = 20)
{ {
string limitedHostname = "invalid"; string limitedHostname = "invalid";
string prefix = "...";
if (settingsObject.Hostname != null && settingsObject.Hostname.Length >= maxLength) if (settingsObject.Hostname != null && settingsObject.Hostname.Length >= maxLength)
{ {
limitedHostname = $"{prefix}{settingsObject.Hostname.Substring(settingsObject.Hostname.Length - maxLength, maxLength)}"; limitedHostname = $"{settingsObject.Hostname.Substring(maxLength)}...";
} }
else if (settingsObject.Hostname != null) else if (settingsObject.Hostname != null)
{ {

@ -14,9 +14,16 @@ public interface ICommunicationProtocol
/// <summary> /// <summary>
/// Short identifier string that allows the user to distinguish different instances. /// Short identifier string that allows the user to distinguish different instances.
/// e.g. Cut off IPv6 after first 15 characters.
/// </summary> /// </summary>
string InstanceIdentifier { get; } string InstanceIdentifier { get; }
/// <summary>
/// Long identifier string that provides full information about the instance.
/// e.g. full IPv6.
/// </summary>
string LongInstanceIdentifier { get; }
/// <summary> /// <summary>
/// New data received from connected device. /// New data received from connected device.
/// </summary> /// </summary>

@ -1,5 +1,4 @@
using CommunityToolkit.Mvvm.ComponentModel; using System.Text;
using System.Text;
namespace MultiTerm.Protocols.Model; namespace MultiTerm.Protocols.Model;

@ -12,6 +12,7 @@ public class SerialProtocol : CommunicationProtocol
public override ProtocolType ProtocolType => ProtocolType.Serial; public override ProtocolType ProtocolType => ProtocolType.Serial;
public override string InstanceIdentifier { get; protected set; } = string.Empty; public override string InstanceIdentifier { get; protected set; } = string.Empty;
public override string LongInstanceIdentifier { get; protected set; } = string.Empty;
private ISerialProtocolSettings? serialSettings; private ISerialProtocolSettings? serialSettings;
private SerialPortStream serialPort = new(); private SerialPortStream serialPort = new();
@ -34,6 +35,7 @@ public class SerialProtocol : CommunicationProtocol
// update identifier // update identifier
this.InstanceIdentifier = this.serialSettings.PortName; this.InstanceIdentifier = this.serialSettings.PortName;
this.LongInstanceIdentifier = this.serialSettings.PortName;
// create new serial port // create new serial port
this.serialPort = new() this.serialPort = new()

@ -15,6 +15,7 @@ public class TcpClientProtocol : CommunicationProtocol
public override Types.ProtocolType ProtocolType => Types.ProtocolType.Tcp_Client; public override Types.ProtocolType ProtocolType => Types.ProtocolType.Tcp_Client;
public override string InstanceIdentifier { get; protected set; } = string.Empty; public override string InstanceIdentifier { get; protected set; } = string.Empty;
public override string LongInstanceIdentifier { get; protected set; } = string.Empty;
private INetworkProtocolSettings? settings; private INetworkProtocolSettings? settings;
private TcpClient? client; private TcpClient? client;
@ -41,6 +42,7 @@ public class TcpClientProtocol : CommunicationProtocol
// update identifier // update identifier
this.InstanceIdentifier = NetworkProtocolHelpers.GetLimitedLengthHostname(this.settings); this.InstanceIdentifier = NetworkProtocolHelpers.GetLimitedLengthHostname(this.settings);
this.LongInstanceIdentifier = this.settings.Hostname;
// check if client is null // check if client is null
if (this.client != null) if (this.client != null)
@ -48,19 +50,6 @@ public class TcpClientProtocol : CommunicationProtocol
throw new Exception($"The TCP client was not null when {nameof(InternalConnect)} was called."); throw new Exception($"The TCP client was not null when {nameof(InternalConnect)} was called.");
} }
///* resolve hostname */
//IPAddress ipAddress;
//try
//{
// IPHostEntry ipHostInfo = Dns.GetHostEntry(this.settings.Hostname);
// ipAddress = ipHostInfo.AddressList[0];
//}
//catch (Exception ex)
//{
// this.logger.LogException(ex, $"'{nameof(InternalConnect)}()' Failed to resolve hostname '{this.settings.Hostname}'.", nameof(TcpClientProtocol));
// return false;
//}
/* create client */ /* create client */
//var ipEndPoint = new IPEndPoint(ipAddress, this.settings.Port); //var ipEndPoint = new IPEndPoint(ipAddress, this.settings.Port);
this.client = new(AddressFamily.Unknown); // creates dual stack tcp client (ipv4 and ipv6) this.client = new(AddressFamily.Unknown); // creates dual stack tcp client (ipv4 and ipv6)

@ -15,10 +15,10 @@ public class UdpProtocol : CommunicationProtocol
public override Types.ProtocolType ProtocolType => Types.ProtocolType.Udp; public override Types.ProtocolType ProtocolType => Types.ProtocolType.Udp;
public override string InstanceIdentifier { get; protected set; } = string.Empty; public override string InstanceIdentifier { get; protected set; } = string.Empty;
public override string LongInstanceIdentifier { get; protected set; } = string.Empty;
private INetworkProtocolSettings? settings; private INetworkProtocolSettings? settings;
private UdpClient? receivingUdpClient; private UdpClient? udpClient;
private UdpClient? sendingUdpClient;
private const int ReadTimeoutMs = 100; // milliseconds until the read operation timeouts private const int ReadTimeoutMs = 100; // milliseconds until the read operation timeouts
private const int WriteTimeoutMs = 100; // milliseconds until the write operation timeouts private const int WriteTimeoutMs = 100; // milliseconds until the write operation timeouts
@ -38,68 +38,49 @@ public class UdpProtocol : CommunicationProtocol
// store locally // store locally
this.settings = networkSettings; this.settings = networkSettings;
// update identifier // update identifiers
this.InstanceIdentifier = NetworkProtocolHelpers.GetLimitedLengthHostname(this.settings); this.InstanceIdentifier = NetworkProtocolHelpers.GetLimitedLengthHostname(this.settings);
this.LongInstanceIdentifier = this.settings.Hostname;
// check if clients are null // check if client is null
if(this.receivingUdpClient != null || this.sendingUdpClient != null) if(this.udpClient != null)
{ {
throw new Exception($"A UDP client was not null when {nameof(InternalConnect)} was called: " + throw new Exception($"UDP client was not null when {nameof(InternalConnect)} was called: " +
$"{nameof(receivingUdpClient)} isnull={this.receivingUdpClient == null}," + $"{nameof(udpClient)} isnull={this.udpClient == null},");
$"{nameof(sendingUdpClient)} isnull={this.sendingUdpClient == null}");
} }
/* create udp clients */ /* create udp client */
// try opening receiving udp socket // try opening udp socket
try try
{ {
this.receivingUdpClient = new UdpClient(this.settings.Port); // opens an udp client on this pc and binds it to the port provided
this.udpClient = new UdpClient(this.settings.Port);
// define default remote host and port
this.udpClient.Connect(this.settings.Hostname!, this.settings.Port);
} }
catch (Exception ex) catch (Exception ex)
{ {
this.logger.LogException(ex, $"'{nameof(InternalConnect)}()' Opening Receiving UDP socket failed:", nameof(UdpProtocol)); this.logger.LogException(ex, $"'{nameof(InternalConnect)}()' Opening UDP socket failed:", nameof(UdpProtocol));
// rollback
this.InternalDisconnect();
return false;
}
// try opening sending udp socket
try
{
this.sendingUdpClient = new UdpClient();
this.sendingUdpClient.Connect(this.settings.Hostname!, this.settings.Port);
}
catch (Exception ex)
{
this.logger.LogException(ex, $"'{nameof(InternalConnect)}()' Opening Sending UDP Socket failed:", nameof(UdpProtocol));
// rollback // rollback
this.InternalDisconnect(); this.InternalDisconnect();
return false; return false;
} }
// set static settings // set static settings
this.receivingUdpClient.Client.ReceiveTimeout = ReadTimeoutMs; this.udpClient.Client.ReceiveTimeout = ReadTimeoutMs;
this.sendingUdpClient.Client.SendTimeout = WriteTimeoutMs; this.udpClient.Client.SendTimeout = WriteTimeoutMs;
return true; return true;
} }
protected override void InternalDisconnect() protected override void InternalDisconnect()
{ {
// close receiving udp client // close udp client
if(this.receivingUdpClient != null) if(this.udpClient != null)
{
this.receivingUdpClient?.Close();
this.receivingUdpClient?.Dispose();
this.receivingUdpClient = null;
}
// close sending udp client
if (this.sendingUdpClient != null)
{ {
this.sendingUdpClient?.Close(); this.udpClient?.Close();
this.sendingUdpClient?.Dispose(); this.udpClient?.Dispose();
this.sendingUdpClient = null; this.udpClient = null;
} }
// reset settings // reset settings
this.settings = null; this.settings = null;
@ -117,7 +98,7 @@ public class UdpProtocol : CommunicationProtocol
try try
{ {
// will throw ObjectDisposedException if null // will throw ObjectDisposedException if null
receivedBytes = this.receivingUdpClient!.Receive(ref remoteEndPoint); receivedBytes = this.udpClient!.Receive(ref remoteEndPoint);
} }
catch (OperationCanceledException) // intentionally cancelled => just break catch (OperationCanceledException) // intentionally cancelled => just break
{ {
@ -171,7 +152,7 @@ public class UdpProtocol : CommunicationProtocol
try try
{ {
// will throw ObjectDisposedException if null // will throw ObjectDisposedException if null
this.sendingUdpClient!.Send(bytes, bytes.Length); this.udpClient!.Send(bytes, bytes.Length);
} }
catch (ObjectDisposedException objex) catch (ObjectDisposedException objex)
{ {

@ -12,6 +12,7 @@ public class UsbHidProtocol : CommunicationProtocol
public override ProtocolType ProtocolType => ProtocolType.UsbHid; public override ProtocolType ProtocolType => ProtocolType.UsbHid;
public override string InstanceIdentifier { get; protected set; } = string.Empty; public override string InstanceIdentifier { get; protected set; } = string.Empty;
public override string LongInstanceIdentifier { get; protected set; } = string.Empty;
private const uint ReadTimeoutMs = 100; // timeout after which a new read cycle is started private const uint ReadTimeoutMs = 100; // timeout after which a new read cycle is started
private const uint MaxBytesPerReport = 100; // maximum number of bytes to send per report, including report ID and pther prefixes private const uint MaxBytesPerReport = 100; // maximum number of bytes to send per report, including report ID and pther prefixes
@ -37,6 +38,7 @@ public class UsbHidProtocol : CommunicationProtocol
// update Identifier // update Identifier
this.InstanceIdentifier = $"V:{this.usbHidSettings.VendorId:X4} P:{this.usbHidSettings.ProductId:X4}"; this.InstanceIdentifier = $"V:{this.usbHidSettings.VendorId:X4} P:{this.usbHidSettings.ProductId:X4}";
this.LongInstanceIdentifier = $"Vendor ID:{this.usbHidSettings.VendorId:X4} Product ID:{this.usbHidSettings.ProductId:X4}";
// try create usb hid device // try create usb hid device
try try

@ -163,7 +163,9 @@
Source="{Binding Path=ProtocolType, Converter={StaticResource ProtocolTypeIconConverter}}" Source="{Binding Path=ProtocolType, Converter={StaticResource ProtocolTypeIconConverter}}"
ToolTip="{Binding Path=ProtocolType,Converter={StaticResource EnumDescriptionConverter}}" ToolTip="{Binding Path=ProtocolType,Converter={StaticResource EnumDescriptionConverter}}"
ToolTipService.InitialShowDelay="200"/> ToolTipService.InitialShowDelay="200"/>
<TextBlock Text="{Binding Title, Mode=OneWay}" /> <TextBlock Text="{Binding Title, Mode=OneWay}"
ToolTip="{Binding Path=CommunicationProtocolLongIdentifier}"
ToolTipService.InitialShowDelay="200"/>
<Button Command="{Binding CloseRequestCommand}" Width="20" Padding="0" Margin="8 0 0 0" Content="X"> <Button Command="{Binding CloseRequestCommand}" Width="20" Padding="0" Margin="8 0 0 0" Content="X">
<Button.Style> <Button.Style>
<Style TargetType="Button" x:Name="CloseButtonStyle"> <Style TargetType="Button" x:Name="CloseButtonStyle">

Loading…
Cancel
Save