iotgateway/Plugins/Drivers/PLC.ModBusMaster/NModbus4/Device/ModbusMasterTcpConnection.cs

123 lines
4.4 KiB
C#
Raw Normal View History

namespace Modbus.Device
{
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using IO;
using Message;
/// <summary>
/// Represents an incoming connection from a Modbus master. Contains the slave's logic to process the connection.
/// </summary>
internal class ModbusMasterTcpConnection : ModbusDevice, IDisposable
{
private readonly TcpClient _client;
private readonly string _endPoint;
private readonly Stream _stream;
private readonly ModbusTcpSlave _slave;
private readonly Task _requestHandlerTask;
private readonly byte[] _mbapHeader = new byte[6];
private byte[] _messageFrame;
public ModbusMasterTcpConnection(TcpClient client, ModbusTcpSlave slave)
: base(new ModbusIpTransport(new TcpClientAdapter(client)))
{
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}
if (slave == null)
{
throw new ArgumentNullException(nameof(slave));
}
_client = client;
_endPoint = client.Client.RemoteEndPoint.ToString();
_stream = client.GetStream();
_slave = slave;
_requestHandlerTask = Task.Run((Func<Task>)HandleRequestAsync);
}
/// <summary>
/// Occurs when a Modbus master TCP connection is closed.
/// </summary>
public event EventHandler<TcpConnectionEventArgs> ModbusMasterTcpConnectionClosed;
public string EndPoint
{
get { return _endPoint; }
}
public Stream Stream
{
get { return _stream; }
}
public TcpClient TcpClient
{
get { return _client; }
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_stream.Dispose();
}
base.Dispose(disposing);
}
private async Task HandleRequestAsync()
{
while (true)
{
2022-09-14 12:47:12 +00:00
//Debug.WriteLine($"Begin reading header from Master at IP: {EndPoint}");
int readBytes = await Stream.ReadAsync(_mbapHeader, 0, 6).ConfigureAwait(false);
if (readBytes == 0)
{
2022-09-14 12:47:12 +00:00
//Debug.WriteLine($"0 bytes read, Master at {EndPoint} has closed Socket connection.");
ModbusMasterTcpConnectionClosed?.Invoke(this, new TcpConnectionEventArgs(EndPoint));
return;
}
ushort frameLength = (ushort)IPAddress.HostToNetworkOrder(BitConverter.ToInt16(_mbapHeader, 4));
2022-09-14 12:47:12 +00:00
//Debug.WriteLine($"Master at {EndPoint} sent header: \"{string.Join(", ", _mbapHeader)}\" with {frameLength} bytes in PDU");
_messageFrame = new byte[frameLength];
readBytes = await Stream.ReadAsync(_messageFrame, 0, frameLength).ConfigureAwait(false);
if (readBytes == 0)
{
2022-09-14 12:47:12 +00:00
//Debug.WriteLine($"0 bytes read, Master at {EndPoint} has closed Socket connection.");
ModbusMasterTcpConnectionClosed?.Invoke(this, new TcpConnectionEventArgs(EndPoint));
return;
}
2022-09-14 12:47:12 +00:00
//Debug.WriteLine($"Read frame from Master at {EndPoint} completed {readBytes} bytes");
byte[] frame = _mbapHeader.Concat(_messageFrame).ToArray();
2022-09-14 12:47:12 +00:00
//Debug.WriteLine($"RX from Master at {EndPoint}: {string.Join(", ", frame)}");
var request = ModbusMessageFactory.CreateModbusRequest(_messageFrame);
request.TransactionId = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 0));
// perform action and build response
IModbusMessage response = _slave.ApplyRequest(request);
response.TransactionId = request.TransactionId;
// write response
byte[] responseFrame = Transport.BuildMessageFrame(response);
2022-09-14 12:47:12 +00:00
//Debug.WriteLine($"TX to Master at {EndPoint}: {string.Join(", ", responseFrame)}");
await Stream.WriteAsync(responseFrame, 0, responseFrame.Length).ConfigureAwait(false);
}
}
}
}