|
|
|
|
@ -0,0 +1,197 @@ |
|
|
|
|
using Common.Logging; |
|
|
|
|
using Common.Messaging; |
|
|
|
|
using CommunityToolkit.Mvvm.Messaging; |
|
|
|
|
using MultiTerm.Protocols.Model; |
|
|
|
|
using System.Net.Sockets; |
|
|
|
|
|
|
|
|
|
namespace MultiTerm.Protocols.Udp; |
|
|
|
|
|
|
|
|
|
public class UdpProtocol : CommunicationProtocol |
|
|
|
|
{ |
|
|
|
|
public override Types.ProtocolType ProtocolType => Types.ProtocolType.Udp; |
|
|
|
|
|
|
|
|
|
public override string InstanceIdentifier { get; protected set; } = string.Empty; |
|
|
|
|
|
|
|
|
|
private IUdpProtocolSettings? udpSettings; |
|
|
|
|
private UdpClient? receivingUdpClient; |
|
|
|
|
private UdpClient? sendingUdpClient; |
|
|
|
|
|
|
|
|
|
private const int MaxInstanceIdentifierLength = 20; // maximum number of characters for the InstanceIdentifier |
|
|
|
|
|
|
|
|
|
public UdpProtocol(ILogger logger, IMessenger messenger) : base(logger, messenger) { } |
|
|
|
|
|
|
|
|
|
protected override bool InternalConnect(IProtocolSettings settings) |
|
|
|
|
{ |
|
|
|
|
// check if settings are of correct type |
|
|
|
|
if (settings is not IUdpProtocolSettings udpProtocolSettings) |
|
|
|
|
{ |
|
|
|
|
this.udpSettings = null; |
|
|
|
|
throw new ArgumentException($"Cannot connect due to wrong type of Protocol Settings. " + |
|
|
|
|
$"Check parameter {nameof(settings)}' of '{nameof(InternalConnect)}()' in {nameof(UdpProtocol)}."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// store locally |
|
|
|
|
this.udpSettings = udpProtocolSettings; |
|
|
|
|
|
|
|
|
|
// update identifier |
|
|
|
|
if(this.udpSettings.Hostname != null && this.udpSettings.Hostname.Length >= MaxInstanceIdentifierLength) |
|
|
|
|
{ |
|
|
|
|
this.InstanceIdentifier = $"...{this.udpSettings.Hostname.Substring(this.udpSettings.Hostname.Length - MaxInstanceIdentifierLength, MaxInstanceIdentifierLength)}"; |
|
|
|
|
} |
|
|
|
|
else if(this.udpSettings.Hostname != null) |
|
|
|
|
{ |
|
|
|
|
this.InstanceIdentifier = $"{this.udpSettings.Hostname}"; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
this.InstanceIdentifier = "invalid"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// check if clients are null |
|
|
|
|
if(this.receivingUdpClient != null || this.sendingUdpClient != 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}"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* create udp clients */ |
|
|
|
|
// try opening receiving udp socket |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
this.receivingUdpClient = new UdpClient(this.udpSettings.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.udpSettings.Hostname!, this.udpSettings.Port); |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
this.logger.LogException(ex, $"'{nameof(InternalConnect)}()'Opening Sending UDP Socket failed:", nameof(UdpProtocol)); |
|
|
|
|
// rollback |
|
|
|
|
this.InternalDisconnect(); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
{ |
|
|
|
|
this.sendingUdpClient?.Close(); |
|
|
|
|
this.sendingUdpClient?.Dispose(); |
|
|
|
|
this.sendingUdpClient = 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(); |
|
|
|
|
} |
|
|
|
|
catch (OperationCanceledException) // intentionally cancelled => just break |
|
|
|
|
{ |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
catch (ObjectDisposedException objex) |
|
|
|
|
{ |
|
|
|
|
this.logger.LogException(objex, $"ObjectDisposedException while sending data in {nameof(InternalRead)}", nameof(UdpProtocol)); |
|
|
|
|
this.OnUnintentionallyDisconnected(); |
|
|
|
|
break; // break loop |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
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.udpSettings!.Port} because of exception.", MessageImportance.Medium)); |
|
|
|
|
break; // break loop |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// any data received? |
|
|
|
|
byte[] receivedBytes = receivedResult.Buffer; |
|
|
|
|
if (receivedBytes.Length > 0) |
|
|
|
|
{ |
|
|
|
|
foreach (byte b in receivedBytes) |
|
|
|
|
{ |
|
|
|
|
// report received data |
|
|
|
|
this.OnReceivedData(new ExtendedByte(b)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
{ |
|
|
|
|
this.logger.LogWarn($"'{nameof(InternalSendBytes)}()' got null or empty bytes array.", nameof(UdpProtocol)); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// try sending the data |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
this.sendingUdpClient.Send(bytes, bytes.Length); |
|
|
|
|
} |
|
|
|
|
catch (ObjectDisposedException objex) |
|
|
|
|
{ |
|
|
|
|
this.logger.LogException(objex, $"ObjectDisposedException while sending data in {nameof(InternalSendBytes)}", nameof(UdpProtocol)); |
|
|
|
|
this.OnUnintentionallyDisconnected(); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
this.logger.LogException(ex, $"Exception while sending data in {nameof(InternalSendBytes)}", nameof(UdpProtocol)); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach (byte b in bytes) |
|
|
|
|
{ |
|
|
|
|
// report sent data |
|
|
|
|
this.OnSentData(new ExtendedByte(b)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |