Files
DP44/Common/DTS.Common.IConnection/.svn/pristine/de/de184397e923290483007303e40b128b0ecf7592.svn-base

453 lines
16 KiB
Plaintext
Raw Normal View History

2026-04-17 14:55:32 -04:00
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using DTS.Common.DASResource;
using DTS.Common.Enums.DASFactory;
using DTS.Common.Interface.Connection;
using DTS.Common.Utilities.Logging;
namespace DTS.Common
{
public class EthernetConnection : IConnection
{
/// <summary>
/// returns true if the unit is soft disconnected
/// </summary>
public bool IsSoftDisconnected { get; private set; } = false;
/// <summary>
/// soft disconnects the unit, kills the socket and disposes it
/// </summary>
public void SoftDisconnect()
{
if (!Enums.Hardware.HardwareConstants.AllowSoftDisconnects) { return; }
if (Sock.Connected)
{
Sock.Disconnect(false);
Sock.Dispose();
IsSoftDisconnected = true;
APILogger.Log($"SoftDisconnect {ConnectString}");
System.Threading.Thread.Sleep(50);
}
else
{
APILogger.Log($"SoftDisconnect denied {ConnectString}");
}
}
public bool RequiresKeepAliveReset { get; set; } = false;
/// <summary>
/// soft connects the unit
/// </summary>
public void SoftConnect()
{
if (!Enums.Hardware.HardwareConstants.AllowSoftDisconnects) { return; }
if (Sock.Connected)
{
APILogger.Log($"SoftConnect denied {ConnectString}");
return;
}
var parameters = Connect_String.Split(':');
//create and setup socket
Create(Connect_String, _hostIPAddress);
if (RequiresKeepAliveReset)
{
APILogger.Log("SoftConnect connecting to HB");
//before we connect to the socket we must talk to the command port and setup
//keep alive
var sock2 = CreateSock(ConnectString, _hostIPAddress);
sock2.Connect(parameters[0], 8200);
var msg = "<60,5,4>";
sock2.Send(System.Text.Encoding.ASCII.GetBytes(msg));
System.Threading.Thread.Sleep(100);
var bytes = new byte[1024];
sock2.Receive(bytes);
sock2.Disconnect(false);
sock2.Dispose();
//we're done with the command port, go connect to the actual device port
}
int attempt = 0;
while (attempt < 3)
{
attempt++;
try
{
Sock.Connect(parameters[0], Convert.ToInt32(parameters[1]));
attempt = 4;//break out
}
catch (SocketException se)
{
APILogger.Log(se);
System.Threading.Thread.Sleep(1000);
}
}
IsSoftDisconnected = false;
APILogger.Log($"Soft connect {parameters[0]}:{parameters[1]}");
}
void IConnection.KeepAliveErrorReceived()
{
if (Sock != null)
{
if (Sock.Connected)
{
Sock.Shutdown(SocketShutdown.Both);
Sock.Close();
}
Sock.Dispose();
Sock = null;
}
OnDisconnected?.Invoke(this, new EventArgs());
}
public string GetConnectionData()
{
try
{
return null == Sock ? "" : $"local: {Sock.LocalEndPoint}, Remote: {Sock.RemoteEndPoint}";
}
catch (Exception) { }
return "";
}
public Socket Sock;
protected string Connect_String;
private bool _disposed;
public event EventHandler OnDisconnected;
public bool Connected => Sock != null && Sock.Connected;
//public bool connected;
public string ConnectString => Connect_String;
public SocketFlags Flags { get; set; }
public EthernetConnection()
{
_disposed = false;
}
protected EthernetConnection(Socket sock)
{
_disposed = false;
Sock = sock;
}
~EthernetConnection()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed){ return; }
if (disposing)
{
try
{
if (Sock != null)
{
APILogger.Log("sock.Disconnect() - during dispose", ConnectString);
if (Sock.Connected)
{
Sock.Shutdown(SocketShutdown.Both);
Sock.Close();
}
Sock.Dispose();
}
Sock = null;
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
_disposed = true;
}
public void Create(string connectString)
{
//Create(ConnectString); this is recursive!
}
/// <summary>
/// the host ip address, sent during original connect and preserved for future connect/disconnect
/// </summary>
private string _hostIPAddress;
/// <summary>
/// creates and setups up a socket, returns the socket created
/// </summary>
/// <param name="connectString"></param>
/// <param name="hostIPAddress"></param>
/// <returns></returns>
public Socket CreateSock(string connectString, string hostIPAddress)
{
var sock2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
sock2.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
sock2.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
sock2.SendBufferSize = DFConstantsAndEnums.SendBufferSizeBytes;
sock2.ReceiveBufferSize = DFConstantsAndEnums.ReceiveBufferSizeBytes;
// https://benohead.com/windows-network-connections-timing-quickly-temporary-connectivity-loss/
//KeepAliveTime: default value is 2hr
//KeepAliveInterval: default value is 1s and Detect 5 times
const uint dummy = 0; //lenth = 4
var inOptionValues = new byte[System.Runtime.InteropServices.Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0); // Keepalive On true
BitConverter.GetBytes(DFConstantsAndEnums.LocalKeepAliveTimeOutMS)
.CopyTo(inOptionValues, System.Runtime.InteropServices.Marshal.SizeOf(dummy));
BitConverter.GetBytes(DFConstantsAndEnums.LocalKeepAliveRetryIntervalMS)
.CopyTo(inOptionValues, System.Runtime.InteropServices.Marshal.SizeOf(dummy) * 2);
sock2.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
if (!string.IsNullOrEmpty(hostIPAddress))
{
APILogger.Log($"Binding to {hostIPAddress}");
sock2.Bind(new IPEndPoint(IPAddress.Parse(hostIPAddress), 0));
}
return sock2;
}
public void Create(string connectString, string hostIPAddress)
{
_hostIPAddress = hostIPAddress;
Sock = CreateSock(connectString, hostIPAddress);
Connect_String = connectString;
}
#region Connect
public IAsyncResult BeginConnect(AsyncCallback cb, object state)
{
if (Sock == null)
{
// "EthernetConnection.BeginConnect: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_BeginConnect_Err3);
}
if (string.IsNullOrEmpty(Connect_String))
{
// "EthernetConnection.BeginConnect: Connect_String is empty"
throw new Exception(DASResource.Strings.EthernetConnection_BeginConnect_Err1);
}
var parameters = Connect_String.Split(':');
if (parameters.Length != 2)
{
// "EthernetConnection.BeginConnect: Connect_String is invalid"
throw new Exception(DASResource.Strings.EthernetConnection_BeginConnect_Err2);
}
if (!int.TryParse(parameters[1], out var portNum))
{
// "EthernetConnection.BeginConnect: Connect_String has invalid port number"
throw new Exception(DASResource.Strings.EthernetConnection_BeginConnect_Err4);
}
return Sock.BeginConnect(parameters[0], portNum, cb, state);
}
public void EndConnect(IAsyncResult ar)
{
if (Sock == null)
{
APILogger.Log("EthernetConnection::EndConnect socket is not created");
// "EthernetConnection.EndConnect: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_EndConnect_Err1);
}
APILogger.Log("Successfully connected local: " + Sock.LocalEndPoint + " remote: " + Sock.RemoteEndPoint);
Sock.EndConnect(ar);
}
#endregion
#region Disconnect
public IAsyncResult BeginDisconnect(bool reuseSocket,
AsyncCallback cb,
object state)
{
APILogger.Log("BeginDisconnect", ConnectString);
if (Sock == null)
{
// "EthernetConnection.BeginDisconnect: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_BeginDisconnect_Err1);
}
//if know the socket is not connected there's no point in trying to disconnect
//however we need to let the caller know that they shouldn't wait for the
//EndDisconnect
if (!Sock.Connected)
{
throw new SocketException(DFConstantsAndEnums.WSAEISCONN);
}
return Sock.BeginDisconnect(reuseSocket, cb, state);
}
public void EndDisconnect(IAsyncResult asyncResult)
{
APILogger.Log("EndDisconnect()", ConnectString);
if (Sock == null)
{
// "EthernetConnection.EndDisconnect: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_EndDisconnect_Err1);
}
Sock.EndDisconnect(asyncResult);
}
#endregion
#region Accept
public IAsyncResult BeginAccept(AsyncCallback callback,
object state)
{
if (Sock == null)
{
// "EthernetConnection.BeginAccept: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_BeginAccept_Err1);
}
return Sock.BeginAccept(callback, state);
}
public IConnection EndAccept(IAsyncResult asyncResult)
{
if (Sock == null)
{
// "EthernetConnection.EndAccept: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_EndAccept_Err1);
}
var newSock = Sock.EndAccept(asyncResult);
var newConnection = new EthernetConnection(newSock);
return newConnection;
}
#endregion
#region Bind
public void Bind(int port)
{
if (Sock == null)
{
// "EthernetConnection.Bind: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_Bind_Err1);
}
var ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
var ipAddress = ipHostInfo.AddressList[0];
var localEndPoint = new IPEndPoint(ipAddress, port);
APILogger.Log($"EthernetConnection.Bind, binding to {localEndPoint.Address} - {ipHostInfo.HostName}");
Sock.Bind(localEndPoint);
}
#endregion
#region Listen
public void Listen(int backlog)
{
if (Sock == null)
{
// "EthernetConnection.Listen: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_Listen_Err1);
}
Sock.Listen(backlog);
}
#endregion
#region Send
public IAsyncResult BeginSend(byte[] buffer, int offset, int size,
AsyncCallback cb, object state)
{
if (Sock == null)
{
// "EthernetConnection.BeginSend: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_BeginSend_Err1);
}
if (cb == null)
{
// "EthernetConnection.BeginSend: callback can't be null"
throw new Exception(DASResource.Strings.EthernetConnection_BeginSend_Err2);
}
//FB 18389 if the connection is not open do not begin send process
if (!Sock.Connected)
{
// "EthernetConnection.BeginSend: socket is not connected."
throw new Exception(DASResource.Strings.EthernetConnection_BeginSend_Err3);
}
return Sock.BeginSend(buffer, offset, size, Flags, cb, state);
}
public int EndSend(IAsyncResult ar)
{
if (Sock == null)
{
// "EthernetConnection.EndSend: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_EndSend_Err1);
}
try
{
var ret = Sock.EndSend(ar);
return ret;
}
catch (Exception)
{
Sock = null;
throw;
}
}
public Task<int> SendAsync(byte[] sendBuffer, int bufferStartOffset, int bufferSizeToSend)
{
return Task<int>.Factory.FromAsync(
(callback, state) => BeginSend(sendBuffer, bufferStartOffset, bufferSizeToSend, callback, state),
EndSend, state: null
);
}
#endregion
#region Receive
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size,
AsyncCallback cb, object state)
{
if (Sock == null)
{
// "EthernetConnection.BeginReceive: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_BeginReceive_Err1);
}
if (cb == null)
{
// "EthernetConnection.BeginReceive: callback can't be null"
throw new Exception(DASResource.Strings.EthernetConnection_BeginReceive_Err2);
}
return Sock.BeginReceive(buffer, offset, size, Flags, cb, state);
}
public int EndReceive(IAsyncResult ar)
{
if (Sock == null)
{
// "EthernetConnection.EndReceive: socket is not created"
throw new Exception(DASResource.Strings.EthernetConnection_EndReceive_Err1);
}
return Sock.EndReceive(ar);
}
#endregion
}
}