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]
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
@ -167,6 +174,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
{
// update internal state
this.CommunicationProtocolConnectionState = e;
this.CommunicationProtocolLongIdentifier = this.CommunicationProtocol.LongInstanceIdentifier;
// If ViewModel still displays connected after unintentional disconnect => force to disconnected state
// allows user to change settings

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

@ -6,7 +6,7 @@ internal static class NetworkProtocolHelpers
{
/// <summary>
/// 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".
/// </summary>
/// <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)
{
string limitedHostname = "invalid";
string prefix = "...";
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)
{

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

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

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

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

@ -12,6 +12,7 @@ public class UsbHidProtocol : CommunicationProtocol
public override ProtocolType ProtocolType => ProtocolType.UsbHid;
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 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
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

@ -163,7 +163,9 @@
Source="{Binding Path=ProtocolType, Converter={StaticResource ProtocolTypeIconConverter}}"
ToolTip="{Binding Path=ProtocolType,Converter={StaticResource EnumDescriptionConverter}}"
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.Style>
<Style TargetType="Button" x:Name="CloseButtonStyle">

Loading…
Cancel
Save