namespace Modbus.Device
{
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
#if SERIAL
using System.IO.Ports;
#endif
using IO;
using Message;
///
/// Modbus serial slave device.
///
public class ModbusSerialSlave : ModbusSlave
{
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;
}
}
#if SERIAL
///
/// Modbus ASCII slave factory method.
///
public static ModbusSerialSlave CreateAscii(byte unitId, SerialPort serialPort)
{
if (serialPort == null)
{
throw new ArgumentNullException(nameof(serialPort));
}
return CreateAscii(unitId, new SerialPortAdapter(serialPort));
}
#endif
///
/// Modbus ASCII slave factory method.
///
public static ModbusSerialSlave CreateAscii(byte unitId, IStreamResource streamResource)
{
if (streamResource == null)
{
throw new ArgumentNullException(nameof(streamResource));
}
return new ModbusSerialSlave(unitId, new ModbusAsciiTransport(streamResource));
}
#if SERIAL
///
/// Modbus RTU slave factory method.
///
public static ModbusSerialSlave CreateRtu(byte unitId, SerialPort serialPort)
{
if (serialPort == null)
{
throw new ArgumentNullException(nameof(serialPort));
}
return CreateRtu(unitId, new SerialPortAdapter(serialPort));
}
#endif
///
/// Modbus RTU slave factory method.
///
public static ModbusSerialSlave CreateRtu(byte unitId, IStreamResource streamResource)
{
if (streamResource == null)
{
throw new ArgumentNullException(nameof(streamResource));
}
return new ModbusSerialSlave(unitId, new ModbusRtuTransport(streamResource));
}
///
/// Start slave listening for requests.
///
public override async Task ListenAsync()
{
while (true)
{
try
{
try
{
//TODO: remove deleay once async will be implemented in transport level
await Task.Delay(20).ConfigureAwait(false);
// read request and build message
byte[] frame = SerialTransport.ReadRequest();
IModbusMessage request = ModbusMessageFactory.CreateModbusRequest(frame);
if (SerialTransport.CheckFrame && !SerialTransport.ChecksumsMatch(request, frame))
{
string msg = $"Checksums failed to match {string.Join(", ", request.MessageFrame)} != {string.Join(", ", frame)}.";
//Debug.WriteLine(msg);
throw new IOException(msg);
}
// only service requests addressed to this particular slave
if (request.SlaveAddress != UnitId)
{
//Debug.WriteLine($"NModbus Slave {UnitId} ignoring request intended for NModbus Slave {request.SlaveAddress}");
continue;
}
// perform action
IModbusMessage response = ApplyRequest(request);
// write response
SerialTransport.Write(response);
}
catch (IOException ioe)
{
//Debug.WriteLine($"IO Exception encountered while listening for requests - {ioe.Message}");
SerialTransport.DiscardInBuffer();
}
catch (TimeoutException te)
{
//Debug.WriteLine($"Timeout Exception encountered while listening for requests - {te.Message}");
SerialTransport.DiscardInBuffer();
}
// TODO better exception handling here, missing FormatException, NotImplemented...
}
catch (InvalidOperationException)
{
// when the underlying transport is disposed
break;
}
}
}
}
}