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;
}
}
}
}
}