using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using log4net; using Modbus.Message; using Modbus.Utility; namespace Modbus.IO { /// /// Refined Abstraction - http://en.wikipedia.org/wiki/Bridge_Pattern /// internal class ModbusRtuTransport : ModbusSerialTransport { public const int RequestFrameStartLength = 7; public const int ResponseFrameStartLength = 4; private static readonly ILog _logger = LogManager.GetLogger(typeof(ModbusRtuTransport)); internal ModbusRtuTransport(IStreamResource streamResource) : base(streamResource) { Debug.Assert(streamResource != null, "Argument streamResource cannot be null."); } internal override byte[] BuildMessageFrame(IModbusMessage message) { List messageBody = new List(); messageBody.Add(message.SlaveAddress); messageBody.AddRange(message.ProtocolDataUnit); messageBody.AddRange(ModbusUtility.CalculateCrc(message.MessageFrame)); return messageBody.ToArray(); } internal override bool ChecksumsMatch(IModbusMessage message, byte[] messageFrame) { return BitConverter.ToUInt16(messageFrame, messageFrame.Length - 2) == BitConverter.ToUInt16(ModbusUtility.CalculateCrc(message.MessageFrame), 0); } internal override IModbusMessage ReadResponse() { byte[] frameStart = Read(ResponseFrameStartLength); byte[] frameEnd = Read(ResponseBytesToRead(frameStart)); byte[] frame = CollectionUtility.Concat(frameStart, frameEnd); _logger.InfoFormat("RX: {0}", StringUtility.Join(", ", frame)); return CreateResponse(frame); } internal override byte[] ReadRequest() { byte[] frameStart = Read(RequestFrameStartLength); byte[] frameEnd = Read(RequestBytesToRead(frameStart)); byte[] frame = CollectionUtility.Concat(frameStart, frameEnd); _logger.InfoFormat("RX: {0}", StringUtility.Join(", ", frame)); return frame; } public virtual byte[] Read(int count) { byte[] frameBytes = new byte[count]; int numBytesRead = 0; while (numBytesRead != count) numBytesRead += StreamResource.Read(frameBytes, numBytesRead, count - numBytesRead); return frameBytes; } public static int RequestBytesToRead(byte[] frameStart) { byte functionCode = frameStart[1]; int numBytes; switch (functionCode) { case Modbus.ReadCoils: case Modbus.ReadInputs: case Modbus.ReadHoldingRegisters: case Modbus.ReadInputRegisters: case Modbus.WriteSingleCoil: case Modbus.WriteSingleRegister: case Modbus.Diagnostics: numBytes = 1; break; case Modbus.WriteMultipleCoils: case Modbus.WriteMultipleRegisters: byte byteCount = frameStart[6]; numBytes = byteCount + 2; break; default: string errorMessage = String.Format(CultureInfo.InvariantCulture, "Function code {0} not supported.", functionCode); _logger.Error(errorMessage); throw new NotImplementedException(errorMessage); } return numBytes; } public static int ResponseBytesToRead(byte[] frameStart) { byte functionCode = frameStart[1]; // exception response if (functionCode > Modbus.ExceptionOffset) return 1; int numBytes; switch (functionCode) { case Modbus.ReadCoils: case Modbus.ReadInputs: case Modbus.ReadHoldingRegisters: case Modbus.ReadInputRegisters: numBytes = frameStart[2] + 1; break; case Modbus.WriteSingleCoil: case Modbus.WriteSingleRegister: case Modbus.WriteMultipleCoils: case Modbus.WriteMultipleRegisters: case Modbus.Diagnostics: numBytes = 4; break; default: string errorMessage = String.Format(CultureInfo.InvariantCulture, "Function code {0} not supported.", functionCode); _logger.Error(errorMessage); throw new NotImplementedException(errorMessage); } return numBytes; } } }