using System; using System.Globalization; using System.IO; using System.IO.Ports; using log4net; using Modbus.IO; using Modbus.Message; using Modbus.Utility; namespace Modbus.Device { /// /// Modbus serial slave device. /// public class ModbusSerialSlave : ModbusSlave { private static readonly ILog _logger = LogManager.GetLogger(typeof(ModbusSerialSlave)); private ModbusSerialSlave(byte unitId, ModbusTransport transport) : base(unitId, transport) { } private ModbusSerialTransport SerialTransport { get { var transport = Transport as ModbusSerialTransport; if (transport == null) throw new ObjectDisposedException("SerialTransport"); return transport; } } /// /// Modbus ASCII slave factory method. /// public static ModbusSerialSlave CreateAscii(byte unitId, SerialPort serialPort) { if (serialPort == null) throw new ArgumentNullException("serialPort"); return CreateAscii(unitId, new SerialPortAdapter(serialPort)); } /// /// Modbus ASCII slave factory method. /// public static ModbusSerialSlave CreateAscii(byte unitId, IStreamResource streamResource) { if (streamResource == null) throw new ArgumentNullException("streamResource"); return new ModbusSerialSlave(unitId, new ModbusAsciiTransport(streamResource)); } /// /// Modbus RTU slave factory method. /// public static ModbusSerialSlave CreateRtu(byte unitId, SerialPort serialPort) { if (serialPort == null) throw new ArgumentNullException("serialPort"); return CreateRtu(unitId, new SerialPortAdapter(serialPort)); } /// /// Modbus RTU slave factory method. /// public static ModbusSerialSlave CreateRtu(byte unitId, IStreamResource streamResource) { if (streamResource == null) throw new ArgumentNullException("streamResource"); return new ModbusSerialSlave(unitId, new ModbusRtuTransport(streamResource)); } /// /// Start slave listening for requests. /// public override void Listen() { while (true) { try { try { // read request and build message byte[] frame = SerialTransport.ReadRequest(); IModbusMessage request = ModbusMessageFactory.CreateModbusRequest(frame); if (SerialTransport.CheckFrame && !SerialTransport.ChecksumsMatch(request, frame)) { string errorMessage = String.Format(CultureInfo.InvariantCulture, "Checksums failed to match {0} != {1}", StringUtility.Join(", ", request.MessageFrame), StringUtility.Join(", ", frame)); _logger.Error(errorMessage); throw new IOException(errorMessage); } // only service requests addressed to this particular slave if (request.SlaveAddress != UnitId) { _logger.DebugFormat("NModbus Slave {0} ignoring request intended for NModbus Slave {1}", UnitId, request.SlaveAddress); continue; } // perform action IModbusMessage response = ApplyRequest(request); // write response SerialTransport.Write(response); } catch (IOException ioe) { _logger.ErrorFormat("IO Exception encountered while listening for requests - {0}", ioe.Message); SerialTransport.DiscardInBuffer(); } catch (TimeoutException te) { _logger.ErrorFormat("Timeout Exception encountered while listening for requests - {0}", te.Message); SerialTransport.DiscardInBuffer(); } // TODO better exception handling here, missing FormatException, NotImplemented... } catch (InvalidOperationException) { // when the underlying transport is disposed break; } } } } }