using System; using System.Diagnostics; using System.IO; using System.Linq; using NModbus.Extensions; using NModbus.Logging; using NModbus.Utility; namespace NModbus.IO { /// /// Refined Abstraction - http://en.wikipedia.org/wiki/Bridge_Pattern /// internal class ModbusRtuTransport : ModbusSerialTransport, IModbusRtuTransport { public const int RequestFrameStartLength = 7; public const int ResponseFrameStartLength = 4; internal ModbusRtuTransport(IStreamResource streamResource, IModbusFactory modbusFactory, IModbusLogger logger) : base(streamResource, modbusFactory, logger) { if (modbusFactory == null) throw new ArgumentNullException(nameof(modbusFactory)); Debug.Assert(streamResource != null, "Argument streamResource cannot be null."); } internal int RequestBytesToRead(byte[] frameStart) { byte functionCode = frameStart[1]; IModbusFunctionService service = ModbusFactory.GetFunctionServiceOrThrow(functionCode); return service.GetRtuRequestBytesToRead(frameStart); } internal int ResponseBytesToRead(byte[] frameStart) { byte functionCode = frameStart[1]; if (functionCode > Modbus.ExceptionOffset) { return 1; } IModbusFunctionService service = ModbusFactory.GetFunctionServiceOrThrow(functionCode); return service.GetRtuResponseBytesToRead(frameStart); } public virtual byte[] Read(int count) { byte[] frameBytes = new byte[count]; int numBytesReadTotal = 0; while (numBytesReadTotal != count) { int numBytesRead = StreamResource.Read(frameBytes, numBytesReadTotal, count - numBytesReadTotal); if (numBytesRead == 0) { throw new IOException("Read resulted in 0 bytes returned."); } numBytesReadTotal += numBytesRead; } return frameBytes; } public override byte[] BuildMessageFrame(IModbusMessage message) { var messageFrame = message.MessageFrame; var crc = ModbusUtility.CalculateCrc(messageFrame); var messageBody = new MemoryStream(messageFrame.Length + crc.Length); messageBody.Write(messageFrame, 0, messageFrame.Length); messageBody.Write(crc, 0, crc.Length); return messageBody.ToArray(); } public override bool ChecksumsMatch(IModbusMessage message, byte[] messageFrame) { ushort messageCrc = BitConverter.ToUInt16(messageFrame, messageFrame.Length - 2); ushort calculatedCrc = BitConverter.ToUInt16(ModbusUtility.CalculateCrc(message.MessageFrame), 0); return messageCrc == calculatedCrc; } public override IModbusMessage ReadResponse() { byte[] frame = ReadResponse(); Logger.LogFrameRx(frame); return CreateResponse(frame); } private byte[] ReadResponse() { byte[] frameStart = Read(ResponseFrameStartLength); byte[] frameEnd = Read(ResponseBytesToRead(frameStart)); byte[] frame = frameStart.Concat(frameEnd).ToArray(); return frame; } public override void IgnoreResponse() { byte[] frame = ReadResponse(); Logger.LogFrameIgnoreRx(frame); } public override byte[] ReadRequest() { byte[] frameStart = Read(RequestFrameStartLength); byte[] frameEnd = Read(RequestBytesToRead(frameStart)); byte[] frame = frameStart.Concat(frameEnd).ToArray(); Logger.LogFrameRx(frame); return frame; } } }