123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- using System;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Net;
- using NModbus.Logging;
- using NModbus.Unme.Common;
- namespace NModbus.IO
- {
- /// <summary>
- /// Transport for Internet protocols.
- /// Refined Abstraction - http://en.wikipedia.org/wiki/Bridge_Pattern
- /// </summary>
- public class ModbusIpTransport : ModbusTransport
- {
- private static readonly object _transactionIdLock = new object();
- private ushort _transactionId;
- public ModbusIpTransport(IStreamResource streamResource, IModbusFactory modbusFactory, IModbusLogger logger)
- : base(streamResource, modbusFactory, logger)
- {
- if (streamResource == null) throw new ArgumentNullException(nameof(streamResource));
- }
- public static byte[] ReadRequestResponse(IStreamResource streamResource, IModbusLogger logger)
- {
- if (streamResource == null) throw new ArgumentNullException(nameof(streamResource));
- if (logger == null) throw new ArgumentNullException(nameof(logger));
- // read header
- var mbapHeader = new byte[6];
- int numBytesRead = 0;
- while (numBytesRead != 6)
- {
- int bRead = streamResource.Read(mbapHeader, numBytesRead, 6 - numBytesRead);
- if (bRead == 0)
- {
- throw new IOException("Read resulted in 0 bytes returned.");
- }
- numBytesRead += bRead;
- }
- logger.Debug($"MBAP header: {string.Join(", ", mbapHeader)}");
- var frameLength = (ushort)IPAddress.HostToNetworkOrder(BitConverter.ToInt16(mbapHeader, 4));
- logger.Debug($"{frameLength} bytes in PDU.");
- // read message
- var messageFrame = new byte[frameLength];
- numBytesRead = 0;
- while (numBytesRead != frameLength)
- {
- int bRead = streamResource.Read(messageFrame, numBytesRead, frameLength - numBytesRead);
- if (bRead == 0)
- {
- throw new IOException("Read resulted in 0 bytes returned.");
- }
- numBytesRead += bRead;
- }
- logger.Debug($"PDU: {frameLength}");
- var frame = mbapHeader.Concat(messageFrame).ToArray();
- logger.LogFrameRx(frame);
- return frame;
- }
- public static byte[] GetMbapHeader(IModbusMessage message)
- {
- byte[] transactionId = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)message.TransactionId));
- byte[] length = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)(message.ProtocolDataUnit.Length + 1)));
- var stream = new MemoryStream(7);
- stream.Write(transactionId, 0, transactionId.Length);
- stream.WriteByte(0);
- stream.WriteByte(0);
- stream.Write(length, 0, length.Length);
- stream.WriteByte(message.SlaveAddress);
- return stream.ToArray();
- }
- /// <summary>
- /// Create a new transaction ID.
- /// </summary>
- public virtual ushort GetNewTransactionId()
- {
- lock (_transactionIdLock)
- {
- _transactionId = _transactionId == ushort.MaxValue ? (ushort)1 : ++_transactionId;
- }
- return _transactionId;
- }
- public IModbusMessage CreateMessageAndInitializeTransactionId<T>(byte[] fullFrame)
- where T : IModbusMessage, new()
- {
- byte[] mbapHeader = fullFrame.Slice(0, 6).ToArray();
- byte[] messageFrame = fullFrame.Slice(6, fullFrame.Length - 6).ToArray();
- IModbusMessage response = CreateResponse<T>(messageFrame);
- response.TransactionId = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(mbapHeader, 0));
- return response;
- }
- public override byte[] BuildMessageFrame(IModbusMessage message)
- {
- byte[] header = GetMbapHeader(message);
- byte[] pdu = message.ProtocolDataUnit;
- var messageBody = new MemoryStream(header.Length + pdu.Length);
- messageBody.Write(header, 0, header.Length);
- messageBody.Write(pdu, 0, pdu.Length);
- return messageBody.ToArray();
- }
- public override void Write(IModbusMessage message)
- {
- message.TransactionId = GetNewTransactionId();
- byte[] frame = BuildMessageFrame(message);
- Logger.LogFrameTx(frame);
- StreamResource.Write(frame, 0, frame.Length);
- }
- public override byte[] ReadRequest()
- {
- return ReadRequestResponse(StreamResource, Logger);
- }
- public override IModbusMessage ReadResponse<T>()
- {
- return CreateMessageAndInitializeTransactionId<T>(ReadRequestResponse(StreamResource, Logger));
- }
- internal override void OnValidateResponse(IModbusMessage request, IModbusMessage response)
- {
- if (request.TransactionId != response.TransactionId)
- {
- string msg = $"Response was not of expected transaction ID. Expected {request.TransactionId}, received {response.TransactionId}.";
- throw new IOException(msg);
- }
- }
- public override bool OnShouldRetryResponse(IModbusMessage request, IModbusMessage response)
- {
- if (request.TransactionId > response.TransactionId && request.TransactionId - response.TransactionId < RetryOnOldResponseThreshold)
- {
- // This response was from a previous request
- return true;
- }
- return base.OnShouldRetryResponse(request, response);
- }
- }
- }
|