diff --git a/Common/Helpers/EnumHelpers.cs b/Common/Helpers/EnumHelpers.cs
index 0961b0d..9403b8c 100644
--- a/Common/Helpers/EnumHelpers.cs
+++ b/Common/Helpers/EnumHelpers.cs
@@ -1,4 +1,7 @@
-namespace Common.Helpers;
+using System.ComponentModel;
+using System.Reflection;
+
+namespace Common.Helpers;
public static class EnumHelpers
{
@@ -12,4 +15,34 @@ public static class EnumHelpers
{
return (T)Enum.Parse(typeof(T), value, true);
}
+
+ ///
+ /// Gets the description of an Enum value.
+ /// If there is no Description set, the Enum Value will be converted to string.
+ ///
+ /// Enum Object to get Description of.
+ /// String with Content of DescriptionAttribute of Enum object.
+ 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();
+ }
}
diff --git a/MultiTerm.Core/Types/ProtocolNotConnectedMessage.cs b/MultiTerm.Core/Types/ProtocolNotConnectedMessage.cs
deleted file mode 100644
index d1e3da3..0000000
--- a/MultiTerm.Core/Types/ProtocolNotConnectedMessage.cs
+++ /dev/null
@@ -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.";
- }
- }
-}
diff --git a/MultiTerm.Core/Types/ProtocolNotConnectedUIMessage.cs b/MultiTerm.Core/Types/ProtocolNotConnectedUIMessage.cs
new file mode 100644
index 0000000..ec0feeb
--- /dev/null
+++ b/MultiTerm.Core/Types/ProtocolNotConnectedUIMessage.cs
@@ -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}";
+ }
+ }
+}
diff --git a/MultiTerm.Core/ViewModel/TerminalViewModel.cs b/MultiTerm.Core/ViewModel/TerminalViewModel.cs
index 5b266a8..74cfb1e 100644
--- a/MultiTerm.Core/ViewModel/TerminalViewModel.cs
+++ b/MultiTerm.Core/ViewModel/TerminalViewModel.cs
@@ -192,7 +192,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
// inform user and quit if communication protocol is not connected
if (this.CommunicationProtocol.State != ProtocolConnectionState.Connected)
{
- this.messenger.Send(new ProtocolNotConnectedMessage("Cannot send message, Protocol is not connected."));
+ this.messenger.Send(new ProtocolNotConnectedUIMessage("Cannot send message."));
return false;
}
diff --git a/MultiTerm.Protocols/CommunicationProtocol.cs b/MultiTerm.Protocols/CommunicationProtocol.cs
index 836fda0..cb68dd2 100644
--- a/MultiTerm.Protocols/CommunicationProtocol.cs
+++ b/MultiTerm.Protocols/CommunicationProtocol.cs
@@ -1,4 +1,5 @@
-using Common.Logging;
+using Common.Helpers;
+using Common.Logging;
using Common.Messaging;
using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Protocols.Model;
@@ -226,4 +227,11 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
Thread.Sleep(1);
}
}
+
+ #region Helpers
+ public string GetProtocolAndInstanceIdentifier()
+ {
+ return $"{EnumHelpers.GetEnumDescription(this.ProtocolType)} {this.InstanceIdentifier}";
+ }
+ #endregion
}
diff --git a/MultiTerm.Protocols/Helpers/ServiceExtensions.cs b/MultiTerm.Protocols/Helpers/ServiceExtensions.cs
index 822156e..f70a6de 100644
--- a/MultiTerm.Protocols/Helpers/ServiceExtensions.cs
+++ b/MultiTerm.Protocols/Helpers/ServiceExtensions.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using MultiTerm.Protocols.Factories;
using MultiTerm.Protocols.Serial;
+using MultiTerm.Protocols.Tcp;
using MultiTerm.Protocols.Udp;
using MultiTerm.Protocols.UsbHid;
@@ -20,12 +21,14 @@ public static class ServiceExtensions
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
// TODO extend
// add all settings view model implementations to the services collection
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
// TODO extend
// add a function to the services collection, which is used by the CommunicationProtocolFactory
diff --git a/MultiTerm.Protocols/Network/NetworkProtocolSettingsViewModel.cs b/MultiTerm.Protocols/Network/NetworkProtocolSettingsViewModel.cs
index 7d3eea7..f441748 100644
--- a/MultiTerm.Protocols/Network/NetworkProtocolSettingsViewModel.cs
+++ b/MultiTerm.Protocols/Network/NetworkProtocolSettingsViewModel.cs
@@ -39,12 +39,12 @@ public abstract partial class NetworkProtocolSettingsViewModel : ProtocolSetting
{
if (String.IsNullOrEmpty(this.Hostname))
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.Hostname), "Null or empty"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.Hostname), "Null or empty"));
return false;
}
if (this.Port < IPEndPoint.MinPort && this.Port > IPEndPoint.MaxPort)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.Port), "Out of range"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.Port), "Out of range"));
return false;
}
// try parse to IP address
@@ -62,17 +62,17 @@ public abstract partial class NetworkProtocolSettingsViewModel : ProtocolSetting
}
catch (ArgumentOutOfRangeException)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.Hostname), "Length is greater than 255 characters"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.Hostname), "Length is greater than 255 characters"));
return false;
}
catch (SocketException)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.Hostname), "Error encountered when resolving hostname"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.Hostname), "Error encountered when resolving hostname"));
return false;
}
catch (ArgumentException)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.Hostname), "Invalid IP address"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.Hostname), "Invalid IP address"));
return false;
}
diff --git a/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs b/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs
index 28bb498..9bd321f 100644
--- a/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs
+++ b/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs
@@ -53,22 +53,22 @@ public partial class SerialProtocolSettingsViewModel : ProtocolSettingsViewModel
{
if (String.IsNullOrEmpty(this.PortName))
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.PortName), "Null or empty"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.PortName), "Null or empty"));
return false;
}
if (this.PortName.ToLower().StartsWith("com") == false)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.PortName), "Must start with COM"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.PortName), "Must start with COM"));
return false;
}
if ((this.DataBits < 5 || this.DataBits > 8) && this.DataBits != 16)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.DataBits), "Must be 5...8 or 16"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.DataBits), "Must be 5...8 or 16"));
return false;
}
if(this.BaudRate < 0)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.BaudRate), "Must be larger than 0"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.BaudRate), "Must be larger than 0"));
return false;
}
diff --git a/MultiTerm.Protocols/Tcp/TcpClientProtocol.cs b/MultiTerm.Protocols/Tcp/TcpClientProtocol.cs
new file mode 100644
index 0000000..64cd407
--- /dev/null
+++ b/MultiTerm.Protocols/Tcp/TcpClientProtocol.cs
@@ -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(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
+ }
+}
diff --git a/MultiTerm.Protocols/Tcp/TcpClientProtocolSettingsViewModel.cs b/MultiTerm.Protocols/Tcp/TcpClientProtocolSettingsViewModel.cs
new file mode 100644
index 0000000..068bde6
--- /dev/null
+++ b/MultiTerm.Protocols/Tcp/TcpClientProtocolSettingsViewModel.cs
@@ -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
+{
+ public override ProtocolType ProtocolType => ProtocolType.Tcp_Client;
+
+ public TcpClientProtocolSettingsViewModel(IMessenger messenger) : base(messenger)
+ {
+ // register for messages
+ messenger.Register(this);
+ }
+
+ ///
+ /// Sets the proprty to the resolved address from the message.
+ ///
+ ///
+ void IRecipient.Receive(TcpConnectedMessage message)
+ {
+ if(message.ResolvedAddress == null)
+ {
+ this.ResolvedAddress = "";
+ }
+ else
+ {
+ this.ResolvedAddress = message.ResolvedAddress.ToString();
+ }
+ }
+}
diff --git a/MultiTerm.Protocols/Tcp/TcpConnectedMessage.cs b/MultiTerm.Protocols/Tcp/TcpConnectedMessage.cs
new file mode 100644
index 0000000..8122595
--- /dev/null
+++ b/MultiTerm.Protocols/Tcp/TcpConnectedMessage.cs
@@ -0,0 +1,10 @@
+using System.Net;
+
+namespace MultiTerm.Protocols.Tcp;
+
+///
+/// A message that is sent by the to report that it has connected to an endpoint.
+/// Main reason to use this message is to find out the of the endpoint.
+/// May be null, e.g. if no IP is connected anymore.
+///
+internal record TcpConnectedMessage(IPAddress? ResolvedAddress);
\ No newline at end of file
diff --git a/MultiTerm.Protocols/Types/ProtocolSettingsInvalidMessage.cs b/MultiTerm.Protocols/Types/ProtocolSettingsInvalidUIMessage.cs
similarity index 73%
rename from MultiTerm.Protocols/Types/ProtocolSettingsInvalidMessage.cs
rename to MultiTerm.Protocols/Types/ProtocolSettingsInvalidUIMessage.cs
index 82553c8..09af8c2 100644
--- a/MultiTerm.Protocols/Types/ProtocolSettingsInvalidMessage.cs
+++ b/MultiTerm.Protocols/Types/ProtocolSettingsInvalidUIMessage.cs
@@ -2,14 +2,14 @@
namespace MultiTerm.Protocols.Types;
-public sealed class ProtocolSettingsInvalidMessage : IUserInterfaceMessage
+public sealed class ProtocolSettingsInvalidUIMessage : IUserInterfaceMessage
{
public MessageImportance Importance => MessageImportance.Medium;
public string SettingName { get; }
public string Message { get; }
- public ProtocolSettingsInvalidMessage(string settingName, string message)
+ public ProtocolSettingsInvalidUIMessage(string settingName, string message)
{
this.SettingName = settingName;
this.Message = message;
diff --git a/MultiTerm.Protocols/Types/ProtocolType.cs b/MultiTerm.Protocols/Types/ProtocolType.cs
index 18f3e56..60e33af 100644
--- a/MultiTerm.Protocols/Types/ProtocolType.cs
+++ b/MultiTerm.Protocols/Types/ProtocolType.cs
@@ -17,10 +17,10 @@ public enum ProtocolType
UsbHid,
///
- /// TCP Protocol
+ /// TCP Client Protocol
///
- [Description("TCP")]
- Tcp,
+ [Description("TCP Client")]
+ Tcp_Client,
///
/// UDP Protocol
diff --git a/MultiTerm.Protocols/Types/StoppedReadingUIMessage.cs b/MultiTerm.Protocols/Types/StoppedReadingUIMessage.cs
new file mode 100644
index 0000000..ff178c8
--- /dev/null
+++ b/MultiTerm.Protocols/Types/StoppedReadingUIMessage.cs
@@ -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;
+
+ ///
+ /// Create with given and .
+ ///
+ /// let the user know which terminal is affected (e.g. 'UDP 192.168.1.1' or 'Serial COM5')
+ /// why the reading was stopped
+ public StoppedReadingUIMessage(string affectedTerminalIdentifier, string reason = "")
+ {
+ this.affectedTerminalIdentifier = affectedTerminalIdentifier;
+ this.reason = reason;
+ }
+
+ ///
+ /// Create with given .
+ /// Extracts from given .
+ ///
+ /// root communication protocol instance, to extract affectedTerminalIdentifier from
+ /// why the reading was stopped
+ 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}";
+ }
+ }
+}
diff --git a/MultiTerm.Protocols/Udp/UdpConnectedMessage.cs b/MultiTerm.Protocols/Udp/UdpConnectedMessage.cs
index 3862c61..ff92c9a 100644
--- a/MultiTerm.Protocols/Udp/UdpConnectedMessage.cs
+++ b/MultiTerm.Protocols/Udp/UdpConnectedMessage.cs
@@ -6,4 +6,4 @@ namespace MultiTerm.Protocols.Udp;
/// A message that is sent by the to report that it has connected to an endpoint.
/// Main reason to use this message is to find out the of the endpoint.
///
-internal record UdpConnectedMessage(IPAddress ResolvedAddress);
\ No newline at end of file
+internal record UdpConnectedMessage(IPAddress? ResolvedAddress);
\ No newline at end of file
diff --git a/MultiTerm.Protocols/Udp/UdpProtocol.cs b/MultiTerm.Protocols/Udp/UdpProtocol.cs
index 0de0bf7..c948668 100644
--- a/MultiTerm.Protocols/Udp/UdpProtocol.cs
+++ b/MultiTerm.Protocols/Udp/UdpProtocol.cs
@@ -4,6 +4,7 @@ using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Protocols.Helpers;
using MultiTerm.Protocols.Model;
using MultiTerm.Protocols.Network;
+using MultiTerm.Protocols.Types;
using System.Net.Sockets;
namespace MultiTerm.Protocols.Udp;
@@ -52,7 +53,7 @@ public class UdpProtocol : CommunicationProtocol
}
catch (Exception ex)
{
- this.logger.LogException(ex, $"'{nameof(InternalConnect)}()'Opening Receiving UDP socket failed:", nameof(UdpProtocol));
+ this.logger.LogException(ex, $"'{nameof(InternalConnect)}()' Opening Receiving UDP socket failed:", nameof(UdpProtocol));
// rollback
this.InternalDisconnect();
return false;
@@ -67,7 +68,7 @@ public class UdpProtocol : CommunicationProtocol
}
catch (Exception ex)
{
- this.logger.LogException(ex, $"'{nameof(InternalConnect)}()'Opening Sending UDP Socket failed:", nameof(UdpProtocol));
+ this.logger.LogException(ex, $"'{nameof(InternalConnect)}()' Opening Sending UDP Socket failed:", nameof(UdpProtocol));
// rollback
this.InternalDisconnect();
return false;
@@ -94,24 +95,20 @@ public class UdpProtocol : CommunicationProtocol
}
// reset settings
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)
{
while(ct.IsCancellationRequested == false)
{
- // if receiving Udp Client is null => break
- if (this.receivingUdpClient == null)
- {
- this.OnUnintentionallyDisconnected();
- break; // break loop
- }
-
// try receive message
UdpReceiveResult receivedResult;
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
{
@@ -126,8 +123,7 @@ public class UdpProtocol : CommunicationProtocol
catch (Exception ex)
{
this.logger.LogException(ex, $"Exception while reading data in {nameof(InternalRead)}", nameof(UdpProtocol));
- this.messenger.Send(new GenericUserInterfaceMessage($"UDP client ended reading on port " +
- $"{this.settings!.Port} because of exception.", MessageImportance.Medium));
+ this.messenger.Send(new StoppedReadingUIMessage(this, ex.Message));
break; // break loop
}
@@ -146,13 +142,6 @@ public class UdpProtocol : CommunicationProtocol
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
if (bytes == null || bytes.Length == 0)
{
@@ -163,7 +152,8 @@ public class UdpProtocol : CommunicationProtocol
// try sending the data
try
{
- this.sendingUdpClient.Send(bytes, bytes.Length);
+ // will throw ObjectDisposedException if null
+ this.sendingUdpClient!.Send(bytes, bytes.Length);
}
catch (ObjectDisposedException objex)
{
diff --git a/MultiTerm.Protocols/Udp/UdpProtocolSettingsViewModel.cs b/MultiTerm.Protocols/Udp/UdpProtocolSettingsViewModel.cs
index f5580aa..16330c5 100644
--- a/MultiTerm.Protocols/Udp/UdpProtocolSettingsViewModel.cs
+++ b/MultiTerm.Protocols/Udp/UdpProtocolSettingsViewModel.cs
@@ -19,6 +19,13 @@ public partial class UdpProtocolSettingsViewModel : NetworkProtocolSettingsViewM
///
void IRecipient.Receive(UdpConnectedMessage message)
{
- this.ResolvedAddress = message.ResolvedAddress.ToString();
+ if (message.ResolvedAddress == null)
+ {
+ this.ResolvedAddress = "";
+ }
+ else
+ {
+ this.ResolvedAddress = message.ResolvedAddress.ToString();
+ }
}
}
diff --git a/MultiTerm.Protocols/UsbHid/UsbHidProtocolSettingsViewModel.cs b/MultiTerm.Protocols/UsbHid/UsbHidProtocolSettingsViewModel.cs
index 76f6cea..d5b28fc 100644
--- a/MultiTerm.Protocols/UsbHid/UsbHidProtocolSettingsViewModel.cs
+++ b/MultiTerm.Protocols/UsbHid/UsbHidProtocolSettingsViewModel.cs
@@ -69,12 +69,12 @@ public partial class UsbHidProtocolSettingsViewModel : ProtocolSettingsViewModel
this.VendorIdHex = this.VendorIdHex.PadLeft(4, '0');
if (this.VendorIdHex.Length > 4)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.VendorId), "Must consist of a maximum of 4 hexadecimal characters"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.VendorId), "Must consist of a maximum of 4 hexadecimal characters"));
return false;
}
if (ushort.TryParse(this.VendorIdHex, System.Globalization.NumberStyles.HexNumber, null, out ushort vendorId) == false)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.VendorId), "Invalid. Not a 16bit number."));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.VendorId), "Invalid. Not a 16bit number."));
return false;
}
// set internal
@@ -84,12 +84,12 @@ public partial class UsbHidProtocolSettingsViewModel : ProtocolSettingsViewModel
this.ProductIdHex = this.ProductIdHex.PadLeft(4, '0');
if (this.ProductIdHex.Length > 4)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.ProductId), "Must consist of a maximum of 4 hexadecimal characters"));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.ProductId), "Must consist of a maximum of 4 hexadecimal characters"));
return false;
}
if (ushort.TryParse(this.ProductIdHex, System.Globalization.NumberStyles.HexNumber, null, out ushort productId) == false)
{
- this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.ProductId), "Invalid. Not a 16bit number."));
+ this.messenger.Send(new ProtocolSettingsInvalidUIMessage(nameof(this.ProductId), "Invalid. Not a 16bit number."));
return false;
}
// set internal
diff --git a/MultiTerm.Wpf/MultiTerm.Wpf.csproj b/MultiTerm.Wpf/MultiTerm.Wpf.csproj
index 2090a4c..becc936 100644
--- a/MultiTerm.Wpf/MultiTerm.Wpf.csproj
+++ b/MultiTerm.Wpf/MultiTerm.Wpf.csproj
@@ -33,4 +33,10 @@
+
+
+ Code
+
+
+
diff --git a/MultiTerm.Wpf/ValueConverters/ProtocolTypeToIconConverter.cs b/MultiTerm.Wpf/ValueConverters/ProtocolTypeToIconConverter.cs
index 9450e09..a9447a6 100644
--- a/MultiTerm.Wpf/ValueConverters/ProtocolTypeToIconConverter.cs
+++ b/MultiTerm.Wpf/ValueConverters/ProtocolTypeToIconConverter.cs
@@ -24,7 +24,7 @@ internal class ProtocolTypeToIconConverter : IValueConverter
{
ProtocolType.Serial => "mdi-serial-port.png",
ProtocolType.UsbHid => "mdi-keyboard.png",
- ProtocolType.Tcp => "mdi-network.png",
+ ProtocolType.Tcp_Client => "mdi-network.png",
ProtocolType.Udp => "mdi-network.png",
_ => throw new NotImplementedException(),
};
diff --git a/MultiTerm.Wpf/View/SendReceiveView.xaml b/MultiTerm.Wpf/View/SendReceiveView.xaml
index d82b647..599e62b 100644
--- a/MultiTerm.Wpf/View/SendReceiveView.xaml
+++ b/MultiTerm.Wpf/View/SendReceiveView.xaml
@@ -9,6 +9,7 @@
xmlns:protocol_serial="clr-namespace:MultiTerm.Protocols.Serial;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_tcp="clr-namespace:MultiTerm.Protocols.Tcp;assembly=MultiTerm.Protocols"
xmlns:types="clr-namespace:MultiTerm.Core.Types;assembly=MultiTerm.Core"
xmlns:settings_view="clr-namespace:MultiTerm.Wpf.View.SettingsView"
xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl"
@@ -54,6 +55,9 @@
+
+
+
diff --git a/MultiTerm.Wpf/View/SettingsView/NetworkSettingsView.xaml b/MultiTerm.Wpf/View/SettingsView/NetworkSettingsView.xaml
index 03b3393..72eed13 100644
--- a/MultiTerm.Wpf/View/SettingsView/NetworkSettingsView.xaml
+++ b/MultiTerm.Wpf/View/SettingsView/NetworkSettingsView.xaml
@@ -47,26 +47,27 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/MultiTerm.Wpf/View/SettingsView/TcpClientSettingsView.xaml b/MultiTerm.Wpf/View/SettingsView/TcpClientSettingsView.xaml
new file mode 100644
index 0000000..9473e8d
--- /dev/null
+++ b/MultiTerm.Wpf/View/SettingsView/TcpClientSettingsView.xaml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/MultiTerm.Wpf/View/SettingsView/TcpClientSettingsView.xaml.cs b/MultiTerm.Wpf/View/SettingsView/TcpClientSettingsView.xaml.cs
new file mode 100644
index 0000000..3f307e5
--- /dev/null
+++ b/MultiTerm.Wpf/View/SettingsView/TcpClientSettingsView.xaml.cs
@@ -0,0 +1,11 @@
+using System.Windows.Controls;
+
+namespace MultiTerm.Wpf.View.SettingsView;
+
+public partial class TcpClientSettingsView : UserControl
+{
+ public TcpClientSettingsView()
+ {
+ InitializeComponent();
+ }
+}