implemented Tcp Client Protocol, including UI part,

renamed all Messages to UIMessage,
created StoppedReadingUIMessage,
worked on UDP protocol
master
Jonas Arnold 3 years ago
parent 2d520a2af5
commit 0da1a83e7c
  1. 35
      Common/Helpers/EnumHelpers.cs
  2. 27
      MultiTerm.Core/Types/ProtocolNotConnectedMessage.cs
  3. 27
      MultiTerm.Core/Types/ProtocolNotConnectedUIMessage.cs
  4. 2
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  5. 10
      MultiTerm.Protocols/CommunicationProtocol.cs
  6. 3
      MultiTerm.Protocols/Helpers/ServiceExtensions.cs
  7. 10
      MultiTerm.Protocols/Network/NetworkProtocolSettingsViewModel.cs
  8. 8
      MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs
  9. 188
      MultiTerm.Protocols/Tcp/TcpClientProtocol.cs
  10. 32
      MultiTerm.Protocols/Tcp/TcpClientProtocolSettingsViewModel.cs
  11. 10
      MultiTerm.Protocols/Tcp/TcpConnectedMessage.cs
  12. 4
      MultiTerm.Protocols/Types/ProtocolSettingsInvalidUIMessage.cs
  13. 6
      MultiTerm.Protocols/Types/ProtocolType.cs
  14. 46
      MultiTerm.Protocols/Types/StoppedReadingUIMessage.cs
  15. 2
      MultiTerm.Protocols/Udp/UdpConnectedMessage.cs
  16. 26
      MultiTerm.Protocols/Udp/UdpProtocol.cs
  17. 7
      MultiTerm.Protocols/Udp/UdpProtocolSettingsViewModel.cs
  18. 8
      MultiTerm.Protocols/UsbHid/UsbHidProtocolSettingsViewModel.cs
  19. 6
      MultiTerm.Wpf/MultiTerm.Wpf.csproj
  20. 2
      MultiTerm.Wpf/ValueConverters/ProtocolTypeToIconConverter.cs
  21. 4
      MultiTerm.Wpf/View/SendReceiveView.xaml
  22. 13
      MultiTerm.Wpf/View/SettingsView/NetworkSettingsView.xaml
  23. 11
      MultiTerm.Wpf/View/SettingsView/TcpClientSettingsView.xaml
  24. 11
      MultiTerm.Wpf/View/SettingsView/TcpClientSettingsView.xaml.cs

@ -1,4 +1,7 @@
namespace Common.Helpers; using System.ComponentModel;
using System.Reflection;
namespace Common.Helpers;
public static class EnumHelpers public static class EnumHelpers
{ {
@ -12,4 +15,34 @@ public static class EnumHelpers
{ {
return (T)Enum.Parse(typeof(T), value, true); return (T)Enum.Parse(typeof(T), value, true);
} }
/// <summary>
/// Gets the description of an Enum value.
/// If there is no Description set, the Enum Value will be converted to string.
/// </summary>
/// <param name="enumObject">Enum Object to get Description of.</param>
/// <returns>String with Content of DescriptionAttribute of Enum object.</returns>
public static string GetEnumDescription(Enum enumObject)
{
// guard argument null
if (enumObject == null) { throw new ArgumentNullException(nameof(enumObject)); }
// get field info from enum type
FieldInfo? fieldInfo = enumObject.GetType().GetField(enumObject.ToString());
// return string of enum value if there is no field info
if (fieldInfo == null)
{
return enumObject.ToString();
}
// get description attribute and return if it is present
DescriptionAttribute? descAttrib = (DescriptionAttribute?)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute), true);
if (descAttrib != null)
{
return descAttrib.Description;
}
// if no description attribute was found => return string of enum value
return enumObject.ToString();
}
} }

@ -1,27 +0,0 @@
using Common.Messaging;
namespace MultiTerm.Core.Types;
public class ProtocolNotConnectedMessage : IUserInterfaceMessage
{
private readonly string reason;
public MessageImportance Importance => MessageImportance.Medium;
public ProtocolNotConnectedMessage(string reason)
{
this.reason = reason;
}
public override string ToString()
{
if (string.IsNullOrEmpty(reason))
{
return $"Protocol is not connected.";
}
else
{
return $"{reason}: Protocol is not connected.";
}
}
}

@ -0,0 +1,27 @@
using Common.Messaging;
namespace MultiTerm.Core.Types;
public sealed class ProtocolNotConnectedUIMessage : IUserInterfaceMessage
{
private readonly string message;
public MessageImportance Importance => MessageImportance.Medium;
public ProtocolNotConnectedUIMessage(string message)
{
this.message = message;
}
public override string ToString()
{
if (string.IsNullOrEmpty(this.message))
{
return $"Protocol is not connected.";
}
else
{
return $"Protocol is not connected: {this.message}";
}
}
}

@ -192,7 +192,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
// inform user and quit if communication protocol is not connected // inform user and quit if communication protocol is not connected
if (this.CommunicationProtocol.State != ProtocolConnectionState.Connected) if (this.CommunicationProtocol.State != ProtocolConnectionState.Connected)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolNotConnectedMessage("Cannot send message, Protocol is not connected.")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolNotConnectedUIMessage("Cannot send message."));
return false; return false;
} }

@ -1,4 +1,5 @@
using Common.Logging; using Common.Helpers;
using Common.Logging;
using Common.Messaging; using Common.Messaging;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Protocols.Model; using MultiTerm.Protocols.Model;
@ -226,4 +227,11 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
Thread.Sleep(1); Thread.Sleep(1);
} }
} }
#region Helpers
public string GetProtocolAndInstanceIdentifier()
{
return $"{EnumHelpers.GetEnumDescription(this.ProtocolType)} {this.InstanceIdentifier}";
}
#endregion
} }

@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using MultiTerm.Protocols.Factories; using MultiTerm.Protocols.Factories;
using MultiTerm.Protocols.Serial; using MultiTerm.Protocols.Serial;
using MultiTerm.Protocols.Tcp;
using MultiTerm.Protocols.Udp; using MultiTerm.Protocols.Udp;
using MultiTerm.Protocols.UsbHid; using MultiTerm.Protocols.UsbHid;
@ -20,12 +21,14 @@ public static class ServiceExtensions
services.AddTransient<ICommunicationProtocol, SerialProtocol>(); services.AddTransient<ICommunicationProtocol, SerialProtocol>();
services.AddTransient<ICommunicationProtocol, UsbHidProtocol>(); services.AddTransient<ICommunicationProtocol, UsbHidProtocol>();
services.AddTransient<ICommunicationProtocol, UdpProtocol>(); services.AddTransient<ICommunicationProtocol, UdpProtocol>();
services.AddTransient<ICommunicationProtocol, TcpClientProtocol>();
// TODO extend // TODO extend
// add all settings view model implementations to the services collection // add all settings view model implementations to the services collection
services.AddTransient<IProtocolSettingsViewModel, SerialProtocolSettingsViewModel>(); services.AddTransient<IProtocolSettingsViewModel, SerialProtocolSettingsViewModel>();
services.AddTransient<IProtocolSettingsViewModel, UsbHidProtocolSettingsViewModel>(); services.AddTransient<IProtocolSettingsViewModel, UsbHidProtocolSettingsViewModel>();
services.AddTransient<IProtocolSettingsViewModel, UdpProtocolSettingsViewModel>(); services.AddTransient<IProtocolSettingsViewModel, UdpProtocolSettingsViewModel>();
services.AddTransient<IProtocolSettingsViewModel, TcpClientProtocolSettingsViewModel>();
// TODO extend // TODO extend
// add a function to the services collection, which is used by the CommunicationProtocolFactory // add a function to the services collection, which is used by the CommunicationProtocolFactory

@ -39,12 +39,12 @@ public abstract partial class NetworkProtocolSettingsViewModel : ProtocolSetting
{ {
if (String.IsNullOrEmpty(this.Hostname)) if (String.IsNullOrEmpty(this.Hostname))
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.Hostname), "Null or empty")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.Hostname), "Null or empty"));
return false; return false;
} }
if (this.Port < IPEndPoint.MinPort && this.Port > IPEndPoint.MaxPort) if (this.Port < IPEndPoint.MinPort && this.Port > IPEndPoint.MaxPort)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.Port), "Out of range")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.Port), "Out of range"));
return false; return false;
} }
// try parse to IP address // try parse to IP address
@ -62,17 +62,17 @@ public abstract partial class NetworkProtocolSettingsViewModel : ProtocolSetting
} }
catch (ArgumentOutOfRangeException) catch (ArgumentOutOfRangeException)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.Hostname), "Length is greater than 255 characters")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.Hostname), "Length is greater than 255 characters"));
return false; return false;
} }
catch (SocketException) catch (SocketException)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.Hostname), "Error encountered when resolving hostname")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.Hostname), "Error encountered when resolving hostname"));
return false; return false;
} }
catch (ArgumentException) catch (ArgumentException)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.Hostname), "Invalid IP address")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.Hostname), "Invalid IP address"));
return false; return false;
} }

@ -53,22 +53,22 @@ public partial class SerialProtocolSettingsViewModel : ProtocolSettingsViewModel
{ {
if (String.IsNullOrEmpty(this.PortName)) if (String.IsNullOrEmpty(this.PortName))
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.PortName), "Null or empty")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.PortName), "Null or empty"));
return false; return false;
} }
if (this.PortName.ToLower().StartsWith("com") == false) if (this.PortName.ToLower().StartsWith("com") == false)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.PortName), "Must start with COM")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.PortName), "Must start with COM"));
return false; return false;
} }
if ((this.DataBits < 5 || this.DataBits > 8) && this.DataBits != 16) if ((this.DataBits < 5 || this.DataBits > 8) && this.DataBits != 16)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.DataBits), "Must be 5...8 or 16")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.DataBits), "Must be 5...8 or 16"));
return false; return false;
} }
if(this.BaudRate < 0) if(this.BaudRate < 0)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.BaudRate), "Must be larger than 0")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.BaudRate), "Must be larger than 0"));
return false; return false;
} }

@ -0,0 +1,188 @@
using Common.Logging;
using Common.Messaging;
using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Protocols.Helpers;
using MultiTerm.Protocols.Model;
using MultiTerm.Protocols.Network;
using MultiTerm.Protocols.Types;
using System.Net;
using System.Net.Sockets;
namespace MultiTerm.Protocols.Tcp;
public class TcpClientProtocol : CommunicationProtocol
{
public override Types.ProtocolType ProtocolType => Types.ProtocolType.Tcp_Client;
public override string InstanceIdentifier { get; protected set; } = string.Empty;
private INetworkProtocolSettings? settings;
private TcpClient? client;
private NetworkStream? stream;
private const int BufferSizeBytes = 1024; // number of bytes when reading data from TCP server
private const int ReadTimeoutMs = 100; // milliseconds until the read operation timeouts
private const int WriteTimeoutMs = 100; // milliseconds until the write operation timeouts
public TcpClientProtocol(ILogger logger, IMessenger messenger) : base(logger, messenger) { }
protected override bool InternalConnect(IProtocolSettings settings)
{
// check if settings are of correct type
if (settings is not INetworkProtocolSettings networkSettings)
{
this.settings = null;
throw new ArgumentException($"Cannot connect due to wrong type of Protocol Settings. " +
$"Check parameter {nameof(settings)}' of '{nameof(InternalConnect)}()' in {nameof(TcpClientProtocol)}.");
}
// store locally
this.settings = networkSettings;
// update identifier
this.InstanceIdentifier = NetworkProtocolHelpers.GetLimitedLengthHostname(this.settings);
// check if client is null
if (this.client != null)
{
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)
try
{
this.client.Connect(this.settings.Hostname, this.settings.Port);
this.stream = client.GetStream();
}
catch (Exception ex)
{
this.logger.LogException(ex, $"'{nameof(InternalConnect)}()' Creating TCP client failed:", nameof(TcpClientProtocol));
// rollback
this.InternalDisconnect();
return false;
}
// set static settings
this.stream.ReadTimeout = ReadTimeoutMs;
this.stream.WriteTimeout = WriteTimeoutMs;
// send message to inform about resolved IP address
if (this.client.Client.RemoteEndPoint is IPEndPoint remoteEndpoint)
{
this.messenger.Send(new TcpConnectedMessage(remoteEndpoint.Address));
}
return true;
}
protected override void InternalDisconnect()
{
// close client
if (this.client != null)
{
this.client?.Close();
this.client?.Dispose();
this.client = null;
}
// close stream
if (this.stream != null)
{
this.stream?.Close();
this.stream?.Dispose();
this.stream = null;
}
// reset settings
this.settings = null;
// send message that indicates that no protocol is connected anymore
this.messenger.Send(new TcpConnectedMessage(null));
}
protected override void InternalRead(CancellationToken ct)
{
while (ct.IsCancellationRequested == false)
{
// try receive message with a buffer
int readByte = -1;
try
{
// will throw ObjectDisposedException if null
// timeout is defined after creation of stream in ctor
readByte = this.stream!.ReadByte();
}
catch (OperationCanceledException) // intentionally cancelled => just break
{
break;
}
catch (ObjectDisposedException objex)
{
this.logger.LogException(objex, $"ObjectDisposedException while sending data in {nameof(InternalRead)}", nameof(TcpClientProtocol));
this.OnUnintentionallyDisconnected();
break; // break loop
}
catch (Exception ex)
{
this.logger.LogException(ex, $"Exception while reading data in {nameof(InternalRead)}", nameof(TcpClientProtocol));
this.messenger.Send<IUserInterfaceMessage>(new StoppedReadingUIMessage(this, ex.Message));
break; // break loop
}
// any data received?
if(readByte != -1) // -1 = end of stream or default value
{
// report received byte
this.OnReceivedData(new ExtendedByte((byte)readByte));
}
}
}
protected override bool InternalSendBytes(byte[] bytes)
{
// check for empty bytes array
if (bytes == null || bytes.Length == 0)
{
this.logger.LogWarn($"'{nameof(InternalSendBytes)}()' got null or empty bytes array.", nameof(TcpClientProtocol));
return false;
}
// try sending each byte individually
foreach (byte b in bytes)
{
try
{
// will throw ObjectDisposedException if null
this.stream!.WriteByte(b);
}
catch (ObjectDisposedException objex)
{
this.logger.LogException(objex, $"ObjectDisposedException while sending data in {nameof(InternalSendBytes)}", nameof(TcpClientProtocol));
this.OnUnintentionallyDisconnected();
return false;
}
catch (Exception ex)
{
this.logger.LogException(ex, $"Exception while sending data in {nameof(InternalSendBytes)}", nameof(TcpClientProtocol));
return false;
}
// report sent data
this.OnSentData(new ExtendedByte(b));
}
return true; // success
}
}

@ -0,0 +1,32 @@
using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Protocols.Network;
using MultiTerm.Protocols.Types;
namespace MultiTerm.Protocols.Tcp;
public class TcpClientProtocolSettingsViewModel : NetworkProtocolSettingsViewModel, IRecipient<TcpConnectedMessage>
{
public override ProtocolType ProtocolType => ProtocolType.Tcp_Client;
public TcpClientProtocolSettingsViewModel(IMessenger messenger) : base(messenger)
{
// register for messages
messenger.Register(this);
}
/// <summary>
/// Sets the <see cref="ResolvedAddress"/> proprty to the resolved address from the message.
/// </summary>
/// <param name="message"></param>
void IRecipient<TcpConnectedMessage>.Receive(TcpConnectedMessage message)
{
if(message.ResolvedAddress == null)
{
this.ResolvedAddress = "";
}
else
{
this.ResolvedAddress = message.ResolvedAddress.ToString();
}
}
}

@ -0,0 +1,10 @@
using System.Net;
namespace MultiTerm.Protocols.Tcp;
/// <summary>
/// A message that is sent by the <see cref="TcpClientProtocol"/> to report that it has connected to an endpoint.
/// Main reason to use this message is to find out the <see cref="ResolvedAddress"/> of the endpoint.
/// May be null, e.g. if no IP is connected anymore.
/// </summary>
internal record TcpConnectedMessage(IPAddress? ResolvedAddress);

@ -2,14 +2,14 @@
namespace MultiTerm.Protocols.Types; namespace MultiTerm.Protocols.Types;
public sealed class ProtocolSettingsInvalidMessage : IUserInterfaceMessage public sealed class ProtocolSettingsInvalidUIMessage : IUserInterfaceMessage
{ {
public MessageImportance Importance => MessageImportance.Medium; public MessageImportance Importance => MessageImportance.Medium;
public string SettingName { get; } public string SettingName { get; }
public string Message { get; } public string Message { get; }
public ProtocolSettingsInvalidMessage(string settingName, string message) public ProtocolSettingsInvalidUIMessage(string settingName, string message)
{ {
this.SettingName = settingName; this.SettingName = settingName;
this.Message = message; this.Message = message;

@ -17,10 +17,10 @@ public enum ProtocolType
UsbHid, UsbHid,
/// <summary> /// <summary>
/// TCP Protocol /// TCP Client Protocol
/// </summary> /// </summary>
[Description("TCP")] [Description("TCP Client")]
Tcp, Tcp_Client,
/// <summary> /// <summary>
/// UDP Protocol /// UDP Protocol

@ -0,0 +1,46 @@
using Common.Messaging;
namespace MultiTerm.Protocols.Types;
public sealed class StoppedReadingUIMessage : IUserInterfaceMessage
{
private readonly string affectedTerminalIdentifier;
private readonly string reason;
public MessageImportance Importance => MessageImportance.Medium;
/// <summary>
/// Create <see cref="StoppedReadingUIMessage"/> with given <paramref name="affectedTerminalIdentifier"/> and <paramref name="reason"/>.
/// </summary>
/// <param name="affectedTerminalIdentifier">let the user know which terminal is affected (e.g. 'UDP 192.168.1.1' or 'Serial COM5')</param>
/// <param name="reason">why the reading was stopped</param>
public StoppedReadingUIMessage(string affectedTerminalIdentifier, string reason = "")
{
this.affectedTerminalIdentifier = affectedTerminalIdentifier;
this.reason = reason;
}
/// <summary>
/// Create <see cref="StoppedReadingUIMessage"/> with given <paramref name="reason"/>.
/// Extracts <see cref="affectedTerminalIdentifier"/> from given <paramref name="communicationProtocol"/>.
/// </summary>
/// <param name="communicationProtocol">root communication protocol instance, to extract affectedTerminalIdentifier from</param>
/// <param name="reason">why the reading was stopped</param>
public StoppedReadingUIMessage(CommunicationProtocol communicationProtocol, string reason = "")
{
this.affectedTerminalIdentifier = communicationProtocol.GetProtocolAndInstanceIdentifier();
this.reason = reason;
}
public override string ToString()
{
if (string.IsNullOrEmpty(this.reason))
{
return $"Stopped Reading from '{this.affectedTerminalIdentifier}' because of unknown issue. Please check logfile.";
}
else
{
return $"Stopped Reading from '{this.affectedTerminalIdentifier}': {this.reason}";
}
}
}

@ -6,4 +6,4 @@ namespace MultiTerm.Protocols.Udp;
/// A message that is sent by the <see cref="UdpProtocol"/> to report that it has connected to an endpoint. /// A message that is sent by the <see cref="UdpProtocol"/> to report that it has connected to an endpoint.
/// Main reason to use this message is to find out the <see cref="ResolvedAddress"/> of the endpoint. /// Main reason to use this message is to find out the <see cref="ResolvedAddress"/> of the endpoint.
/// </summary> /// </summary>
internal record UdpConnectedMessage(IPAddress ResolvedAddress); internal record UdpConnectedMessage(IPAddress? ResolvedAddress);

@ -4,6 +4,7 @@ using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Protocols.Helpers; using MultiTerm.Protocols.Helpers;
using MultiTerm.Protocols.Model; using MultiTerm.Protocols.Model;
using MultiTerm.Protocols.Network; using MultiTerm.Protocols.Network;
using MultiTerm.Protocols.Types;
using System.Net.Sockets; using System.Net.Sockets;
namespace MultiTerm.Protocols.Udp; namespace MultiTerm.Protocols.Udp;
@ -94,24 +95,20 @@ public class UdpProtocol : CommunicationProtocol
} }
// reset settings // reset settings
this.settings = null; this.settings = null;
// send message that indicates that no protocol is connected anymore
this.messenger.Send(new UdpConnectedMessage(null));
} }
protected override void InternalRead(CancellationToken ct) protected override void InternalRead(CancellationToken ct)
{ {
while(ct.IsCancellationRequested == false) while(ct.IsCancellationRequested == false)
{ {
// if receiving Udp Client is null => break
if (this.receivingUdpClient == null)
{
this.OnUnintentionallyDisconnected();
break; // break loop
}
// try receive message // try receive message
UdpReceiveResult receivedResult; UdpReceiveResult receivedResult;
try try
{ {
receivedResult = this.receivingUdpClient.ReceiveAsync(ct).GetAwaiter().GetResult(); // will throw ObjectDisposedException if null
receivedResult = this.receivingUdpClient!.ReceiveAsync(ct).GetAwaiter().GetResult();
} }
catch (OperationCanceledException) // intentionally cancelled => just break catch (OperationCanceledException) // intentionally cancelled => just break
{ {
@ -126,8 +123,7 @@ public class UdpProtocol : CommunicationProtocol
catch (Exception ex) catch (Exception ex)
{ {
this.logger.LogException(ex, $"Exception while reading data in {nameof(InternalRead)}", nameof(UdpProtocol)); this.logger.LogException(ex, $"Exception while reading data in {nameof(InternalRead)}", nameof(UdpProtocol));
this.messenger.Send<IUserInterfaceMessage>(new GenericUserInterfaceMessage($"UDP client ended reading on port " + this.messenger.Send<IUserInterfaceMessage>(new StoppedReadingUIMessage(this, ex.Message));
$"{this.settings!.Port} because of exception.", MessageImportance.Medium));
break; // break loop break; // break loop
} }
@ -146,13 +142,6 @@ public class UdpProtocol : CommunicationProtocol
protected override bool InternalSendBytes(byte[] bytes) protected override bool InternalSendBytes(byte[] bytes)
{ {
// if sending Udp Client is null => return error
if (this.sendingUdpClient == null)
{
this.OnUnintentionallyDisconnected();
return false;
}
// check for empty bytes array // check for empty bytes array
if (bytes == null || bytes.Length == 0) if (bytes == null || bytes.Length == 0)
{ {
@ -163,7 +152,8 @@ public class UdpProtocol : CommunicationProtocol
// try sending the data // try sending the data
try try
{ {
this.sendingUdpClient.Send(bytes, bytes.Length); // will throw ObjectDisposedException if null
this.sendingUdpClient!.Send(bytes, bytes.Length);
} }
catch (ObjectDisposedException objex) catch (ObjectDisposedException objex)
{ {

@ -18,7 +18,14 @@ public partial class UdpProtocolSettingsViewModel : NetworkProtocolSettingsViewM
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
void IRecipient<UdpConnectedMessage>.Receive(UdpConnectedMessage message) void IRecipient<UdpConnectedMessage>.Receive(UdpConnectedMessage message)
{
if (message.ResolvedAddress == null)
{
this.ResolvedAddress = "";
}
else
{ {
this.ResolvedAddress = message.ResolvedAddress.ToString(); this.ResolvedAddress = message.ResolvedAddress.ToString();
} }
} }
}

@ -69,12 +69,12 @@ public partial class UsbHidProtocolSettingsViewModel : ProtocolSettingsViewModel
this.VendorIdHex = this.VendorIdHex.PadLeft(4, '0'); this.VendorIdHex = this.VendorIdHex.PadLeft(4, '0');
if (this.VendorIdHex.Length > 4) if (this.VendorIdHex.Length > 4)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.VendorId), "Must consist of a maximum of 4 hexadecimal characters")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.VendorId), "Must consist of a maximum of 4 hexadecimal characters"));
return false; return false;
} }
if (ushort.TryParse(this.VendorIdHex, System.Globalization.NumberStyles.HexNumber, null, out ushort vendorId) == false) if (ushort.TryParse(this.VendorIdHex, System.Globalization.NumberStyles.HexNumber, null, out ushort vendorId) == false)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.VendorId), "Invalid. Not a 16bit number.")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.VendorId), "Invalid. Not a 16bit number."));
return false; return false;
} }
// set internal // set internal
@ -84,12 +84,12 @@ public partial class UsbHidProtocolSettingsViewModel : ProtocolSettingsViewModel
this.ProductIdHex = this.ProductIdHex.PadLeft(4, '0'); this.ProductIdHex = this.ProductIdHex.PadLeft(4, '0');
if (this.ProductIdHex.Length > 4) if (this.ProductIdHex.Length > 4)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.ProductId), "Must consist of a maximum of 4 hexadecimal characters")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.ProductId), "Must consist of a maximum of 4 hexadecimal characters"));
return false; return false;
} }
if (ushort.TryParse(this.ProductIdHex, System.Globalization.NumberStyles.HexNumber, null, out ushort productId) == false) if (ushort.TryParse(this.ProductIdHex, System.Globalization.NumberStyles.HexNumber, null, out ushort productId) == false)
{ {
this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidMessage(nameof(this.ProductId), "Invalid. Not a 16bit number.")); this.messenger.Send<IUserInterfaceMessage>(new ProtocolSettingsInvalidUIMessage(nameof(this.ProductId), "Invalid. Not a 16bit number."));
return false; return false;
} }
// set internal // set internal

@ -33,4 +33,10 @@
<Resource Include="Assets\mdi-serial-port.png" /> <Resource Include="Assets\mdi-serial-port.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Update="View\SettingsView\TcpClientSettingsView.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project> </Project>

@ -24,7 +24,7 @@ internal class ProtocolTypeToIconConverter : IValueConverter
{ {
ProtocolType.Serial => "mdi-serial-port.png", ProtocolType.Serial => "mdi-serial-port.png",
ProtocolType.UsbHid => "mdi-keyboard.png", ProtocolType.UsbHid => "mdi-keyboard.png",
ProtocolType.Tcp => "mdi-network.png", ProtocolType.Tcp_Client => "mdi-network.png",
ProtocolType.Udp => "mdi-network.png", ProtocolType.Udp => "mdi-network.png",
_ => throw new NotImplementedException(), _ => throw new NotImplementedException(),
}; };

@ -9,6 +9,7 @@
xmlns:protocol_serial="clr-namespace:MultiTerm.Protocols.Serial;assembly=MultiTerm.Protocols" xmlns:protocol_serial="clr-namespace:MultiTerm.Protocols.Serial;assembly=MultiTerm.Protocols"
xmlns:protocol_usbhid="clr-namespace:MultiTerm.Protocols.UsbHid;assembly=MultiTerm.Protocols" xmlns:protocol_usbhid="clr-namespace:MultiTerm.Protocols.UsbHid;assembly=MultiTerm.Protocols"
xmlns:protocol_udp="clr-namespace:MultiTerm.Protocols.Udp;assembly=MultiTerm.Protocols" xmlns:protocol_udp="clr-namespace:MultiTerm.Protocols.Udp;assembly=MultiTerm.Protocols"
xmlns:protocol_tcp="clr-namespace:MultiTerm.Protocols.Tcp;assembly=MultiTerm.Protocols"
xmlns:types="clr-namespace:MultiTerm.Core.Types;assembly=MultiTerm.Core" xmlns:types="clr-namespace:MultiTerm.Core.Types;assembly=MultiTerm.Core"
xmlns:settings_view="clr-namespace:MultiTerm.Wpf.View.SettingsView" xmlns:settings_view="clr-namespace:MultiTerm.Wpf.View.SettingsView"
xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl" xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl"
@ -54,6 +55,9 @@
<DataTemplate DataType="{x:Type protocol_udp:UdpProtocolSettingsViewModel}"> <DataTemplate DataType="{x:Type protocol_udp:UdpProtocolSettingsViewModel}">
<settings_view:UdpSettingsView/> <settings_view:UdpSettingsView/>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="{x:Type protocol_tcp:TcpClientProtocolSettingsViewModel}">
<settings_view:TcpClientSettingsView/>
</DataTemplate>
</ContentControl.Resources> </ContentControl.Resources>
</ContentControl> </ContentControl>
</GroupBox> </GroupBox>

@ -47,8 +47,11 @@
<TextBox Text="{Binding Path=Port, Mode=TwoWay, Converter={StaticResource IntToStringConverter}}" Width="50" VerticalContentAlignment="Center"/> <TextBox Text="{Binding Path=Port, Mode=TwoWay, Converter={StaticResource IntToStringConverter}}" Width="50" VerticalContentAlignment="Center"/>
</StackPanel> </StackPanel>
<!-- Display for Resolved IP Address -->
<StackPanel Orientation="Vertical" Margin="20 0 10 0"> </StackPanel>
<!-- Display for Resolved IP Address (outside of Not changeable range) -->
<StackPanel Orientation="Horizontal" Margin="20 0 10 0">
<StackPanel.Style> <StackPanel.Style>
<Style TargetType="{x:Type StackPanel}"> <Style TargetType="{x:Type StackPanel}">
<!-- Defaults to visible --> <!-- Defaults to visible -->
@ -61,10 +64,8 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</StackPanel.Style> </StackPanel.Style>
<Label VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="DarkGray">Resolved IP Address:</Label> <Label Margin="20 0 10 0" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="DarkGray">Resolved IP Address:</Label>
<TextBox IsReadOnly="True" Text="{Binding ResolvedAddress}"/> <TextBox IsReadOnly="True" VerticalContentAlignment="Center" Text="{Binding ResolvedAddress}"/>
</StackPanel>
</StackPanel> </StackPanel>
</DockPanel> </DockPanel>

@ -0,0 +1,11 @@
<UserControl x:Class="MultiTerm.Wpf.View.SettingsView.TcpClientSettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:conv="clr-namespace:MultiTerm.Wpf.ValueConverters"
xmlns:local="clr-namespace:MultiTerm.Wpf.View.SettingsView"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="800">
<local:NetworkSettingsView/>
</UserControl>

@ -0,0 +1,11 @@
using System.Windows.Controls;
namespace MultiTerm.Wpf.View.SettingsView;
public partial class TcpClientSettingsView : UserControl
{
public TcpClientSettingsView()
{
InitializeComponent();
}
}
Loading…
Cancel
Save