namespace Modbus.Device
{
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Data;
using IO;
using Message;
///
/// Modbus slave device.
///
public abstract class ModbusSlave : ModbusDevice
{
internal ModbusSlave(byte unitId, ModbusTransport transport)
: base(transport)
{
DataStore = DataStoreFactory.CreateDefaultDataStore();
UnitId = unitId;
}
///
/// Raised when a Modbus slave receives a request, before processing request function.
///
/// The Modbus request was invalid, and an error response the specified exception should be sent.
public event EventHandler ModbusSlaveRequestReceived;
///
/// Raised when a Modbus slave receives a write request, after processing the write portion of the function.
///
/// For Read/Write Multiple registers (function code 23), this method is raised after writing and before reading.
public event EventHandler WriteComplete;
///
/// Gets or sets the data store.
///
public DataStore DataStore { get; set; }
///
/// Gets or sets the unit ID.
///
public byte UnitId { get; set; }
///
/// Start slave listening for requests.
///
public abstract Task ListenAsync();
internal static ReadCoilsInputsResponse ReadDiscretes(
ReadCoilsInputsRequest request,
DataStore dataStore,
ModbusDataCollection dataSource)
{
DiscreteCollection data;
ReadCoilsInputsResponse response;
data = DataStore.ReadData(
dataStore,
dataSource,
request.StartAddress,
request.NumberOfPoints,
dataStore.SyncRoot);
response = new ReadCoilsInputsResponse(
request.FunctionCode,
request.SlaveAddress,
data.ByteCount,
data);
return response;
}
internal static ReadHoldingInputRegistersResponse ReadRegisters(
ReadHoldingInputRegistersRequest request,
DataStore dataStore,
ModbusDataCollection dataSource)
{
RegisterCollection data;
ReadHoldingInputRegistersResponse response;
data = DataStore.ReadData(
dataStore,
dataSource,
request.StartAddress,
request.NumberOfPoints,
dataStore.SyncRoot);
response = new ReadHoldingInputRegistersResponse(
request.FunctionCode,
request.SlaveAddress,
data);
return response;
}
internal static WriteSingleCoilRequestResponse WriteSingleCoil(
WriteSingleCoilRequestResponse request,
DataStore dataStore,
ModbusDataCollection dataSource)
{
DataStore.WriteData(
dataStore,
new DiscreteCollection(request.Data[0] == Modbus.CoilOn),
dataSource,
request.StartAddress,
dataStore.SyncRoot);
return request;
}
internal static WriteMultipleCoilsResponse WriteMultipleCoils(
WriteMultipleCoilsRequest request,
DataStore dataStore,
ModbusDataCollection dataSource)
{
WriteMultipleCoilsResponse response;
DataStore.WriteData(
dataStore,
request.Data.Take(request.NumberOfPoints),
dataSource,
request.StartAddress,
dataStore.SyncRoot);
response = new WriteMultipleCoilsResponse(
request.SlaveAddress,
request.StartAddress,
request.NumberOfPoints);
return response;
}
internal static WriteSingleRegisterRequestResponse WriteSingleRegister(
WriteSingleRegisterRequestResponse request,
DataStore dataStore,
ModbusDataCollection dataSource)
{
DataStore.WriteData(
dataStore,
request.Data,
dataSource,
request.StartAddress,
dataStore.SyncRoot);
return request;
}
internal static WriteMultipleRegistersResponse WriteMultipleRegisters(
WriteMultipleRegistersRequest request,
DataStore dataStore,
ModbusDataCollection dataSource)
{
WriteMultipleRegistersResponse response;
DataStore.WriteData(
dataStore,
request.Data,
dataSource,
request.StartAddress,
dataStore.SyncRoot);
response = new WriteMultipleRegistersResponse(
request.SlaveAddress,
request.StartAddress,
request.NumberOfPoints);
return response;
}
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Cast is not unneccessary.")]
internal IModbusMessage ApplyRequest(IModbusMessage request)
{
IModbusMessage response;
try
{
//Debug.WriteLine(request.ToString());
var eventArgs = new ModbusSlaveRequestEventArgs(request);
ModbusSlaveRequestReceived?.Invoke(this, eventArgs);
switch (request.FunctionCode)
{
case Modbus.ReadCoils:
response = ReadDiscretes(
(ReadCoilsInputsRequest)request,
DataStore,
DataStore.CoilDiscretes);
break;
case Modbus.ReadInputs:
response = ReadDiscretes(
(ReadCoilsInputsRequest)request,
DataStore,
DataStore.InputDiscretes);
break;
case Modbus.ReadHoldingRegisters:
response = ReadRegisters(
(ReadHoldingInputRegistersRequest)request,
DataStore,
DataStore.HoldingRegisters);
break;
case Modbus.ReadInputRegisters:
response = ReadRegisters(
(ReadHoldingInputRegistersRequest)request,
DataStore,
DataStore.InputRegisters);
break;
case Modbus.Diagnostics:
response = request;
break;
case Modbus.WriteSingleCoil:
response = WriteSingleCoil(
(WriteSingleCoilRequestResponse)request,
DataStore,
DataStore.CoilDiscretes);
WriteComplete?.Invoke(this, eventArgs);
break;
case Modbus.WriteSingleRegister:
response = WriteSingleRegister(
(WriteSingleRegisterRequestResponse)request,
DataStore,
DataStore.HoldingRegisters);
WriteComplete?.Invoke(this, eventArgs);
break;
case Modbus.WriteMultipleCoils:
response = WriteMultipleCoils(
(WriteMultipleCoilsRequest)request,
DataStore,
DataStore.CoilDiscretes);
WriteComplete?.Invoke(this, eventArgs);
break;
case Modbus.WriteMultipleRegisters:
response = WriteMultipleRegisters(
(WriteMultipleRegistersRequest)request,
DataStore,
DataStore.HoldingRegisters);
WriteComplete?.Invoke(this, eventArgs);
break;
case Modbus.ReadWriteMultipleRegisters:
ReadWriteMultipleRegistersRequest readWriteRequest = (ReadWriteMultipleRegistersRequest)request;
WriteMultipleRegisters(
readWriteRequest.WriteRequest,
DataStore,
DataStore.HoldingRegisters);
WriteComplete?.Invoke(this, eventArgs);
response = ReadRegisters(
readWriteRequest.ReadRequest,
DataStore,
DataStore.HoldingRegisters);
break;
default:
string msg = $"Unsupported function code {request.FunctionCode}.";
//Debug.WriteLine(msg);
throw new InvalidModbusRequestException(Modbus.IllegalFunction);
}
}
catch (InvalidModbusRequestException ex)
{
// Catches the exception for an illegal function or a custom exception from the ModbusSlaveRequestReceived event.
response = new SlaveExceptionResponse(
request.SlaveAddress,
(byte)(Modbus.ExceptionOffset + request.FunctionCode),
ex.ExceptionCode);
}
return response;
}
}
}