using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using NModbus.Data;
using NModbus.Message;
namespace NModbus.Device
{
///
/// Modbus master device.
///
public abstract class ModbusMaster : ModbusDevice, IModbusMaster
{
protected ModbusMaster(IModbusTransport transport)
: base(transport)
{
}
///
/// Reads from 1 to 2000 contiguous coils status.
///
/// Address of device to read values from.
/// Address to begin reading.
/// Number of coils to read.
/// Coils status.
public bool[] ReadCoils(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 2000);
var request = new ReadCoilsInputsRequest(
ModbusFunctionCodes.ReadCoils,
slaveAddress,
startAddress,
numberOfPoints);
return PerformReadDiscretes(request);
}
///
/// Asynchronously reads from 1 to 2000 contiguous coils status.
///
/// Address of device to read values from.
/// Address to begin reading.
/// Number of coils to read.
/// A task that represents the asynchronous read operation.
public Task ReadCoilsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 2000);
var request = new ReadCoilsInputsRequest(
ModbusFunctionCodes.ReadCoils,
slaveAddress,
startAddress,
numberOfPoints);
return PerformReadDiscretesAsync(request);
}
///
/// Reads from 1 to 2000 contiguous discrete input status.
///
/// Address of device to read values from.
/// Address to begin reading.
/// Number of discrete inputs to read.
/// Discrete inputs status.
public bool[] ReadInputs(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 2000);
var request = new ReadCoilsInputsRequest(
ModbusFunctionCodes.ReadInputs,
slaveAddress,
startAddress,
numberOfPoints);
return PerformReadDiscretes(request);
}
///
/// Asynchronously reads from 1 to 2000 contiguous discrete input status.
///
/// Address of device to read values from.
/// Address to begin reading.
/// Number of discrete inputs to read.
/// A task that represents the asynchronous read operation.
public Task ReadInputsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 2000);
var request = new ReadCoilsInputsRequest(
ModbusFunctionCodes.ReadInputs,
slaveAddress,
startAddress,
numberOfPoints);
return PerformReadDiscretesAsync(request);
}
///
/// Reads contiguous block of holding registers.
///
/// Address of device to read values from.
/// Address to begin reading.
/// Number of holding registers to read.
/// Holding registers status.
public ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 125);
var request = new ReadHoldingInputRegistersRequest(
ModbusFunctionCodes.ReadHoldingRegisters,
slaveAddress,
startAddress,
numberOfPoints);
return PerformReadRegisters(request);
}
///
/// Asynchronously reads contiguous block of holding registers.
///
/// Address of device to read values from.
/// Address to begin reading.
/// Number of holding registers to read.
/// A task that represents the asynchronous read operation.
public Task ReadHoldingRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 125);
var request = new ReadHoldingInputRegistersRequest(
ModbusFunctionCodes.ReadHoldingRegisters,
slaveAddress,
startAddress,
numberOfPoints);
return PerformReadRegistersAsync(request);
}
///
/// Reads contiguous block of input registers.
///
/// Address of device to read values from.
/// Address to begin reading.
/// Number of holding registers to read.
/// Input registers status.
public ushort[] ReadInputRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 125);
var request = new ReadHoldingInputRegistersRequest(
ModbusFunctionCodes.ReadInputRegisters,
slaveAddress,
startAddress,
numberOfPoints);
return PerformReadRegisters(request);
}
///
/// Asynchronously reads contiguous block of input registers.
///
/// Address of device to read values from.
/// Address to begin reading.
/// Number of holding registers to read.
/// A task that represents the asynchronous read operation.
public Task ReadInputRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 125);
var request = new ReadHoldingInputRegistersRequest(
ModbusFunctionCodes.ReadInputRegisters,
slaveAddress,
startAddress,
numberOfPoints);
return PerformReadRegistersAsync(request);
}
///
/// Writes a single coil value.
///
/// Address of the device to write to.
/// Address to write value to.
/// Value to write.
public void WriteSingleCoil(byte slaveAddress, ushort coilAddress, bool value)
{
var request = new WriteSingleCoilRequestResponse(slaveAddress, coilAddress, value);
Transport.UnicastMessage(request);
}
///
/// Asynchronously writes a single coil value.
///
/// Address of the device to write to.
/// Address to write value to.
/// Value to write.
/// A task that represents the asynchronous write operation.
public Task WriteSingleCoilAsync(byte slaveAddress, ushort coilAddress, bool value)
{
var request = new WriteSingleCoilRequestResponse(slaveAddress, coilAddress, value);
return PerformWriteRequestAsync(request);
}
///
/// Writes a single holding register.
///
/// Address of the device to write to.
/// Address to write.
/// Value to write.
public void WriteSingleRegister(byte slaveAddress, ushort registerAddress, ushort value)
{
var request = new WriteSingleRegisterRequestResponse(
slaveAddress,
registerAddress,
value);
Transport.UnicastMessage(request);
}
///
/// Asynchronously writes a single holding register.
///
/// Address of the device to write to.
/// Address to write.
/// Value to write.
/// A task that represents the asynchronous write operation.
public Task WriteSingleRegisterAsync(byte slaveAddress, ushort registerAddress, ushort value)
{
var request = new WriteSingleRegisterRequestResponse(
slaveAddress,
registerAddress,
value);
return PerformWriteRequestAsync(request);
}
///
/// Write a block of 1 to 123 contiguous 16 bit holding registers.
///
/// Address of the device to write to.
/// Address to begin writing values.
/// Values to write.
public void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] data)
{
ValidateData("data", data, 123);
var request = new WriteMultipleRegistersRequest(
slaveAddress,
startAddress,
new RegisterCollection(data));
Transport.UnicastMessage(request);
}
///
/// Asynchronously writes a block of 1 to 123 contiguous registers.
///
/// Address of the device to write to.
/// Address to begin writing values.
/// Values to write.
/// A task that represents the asynchronous write operation.
public Task WriteMultipleRegistersAsync(byte slaveAddress, ushort startAddress, ushort[] data)
{
ValidateData("data", data, 123);
var request = new WriteMultipleRegistersRequest(
slaveAddress,
startAddress,
new RegisterCollection(data));
return PerformWriteRequestAsync(request);
}
///
/// Writes a sequence of coils.
///
/// Address of the device to write to.
/// Address to begin writing values.
/// Values to write.
public void WriteMultipleCoils(byte slaveAddress, ushort startAddress, bool[] data)
{
ValidateData("data", data, 1968);
var request = new WriteMultipleCoilsRequest(
slaveAddress,
startAddress,
new DiscreteCollection(data));
Transport.UnicastMessage(request);
}
///
/// Asynchronously writes a sequence of coils.
///
/// Address of the device to write to.
/// Address to begin writing values.
/// Values to write.
/// A task that represents the asynchronous write operation.
public Task WriteMultipleCoilsAsync(byte slaveAddress, ushort startAddress, bool[] data)
{
ValidateData("data", data, 1968);
var request = new WriteMultipleCoilsRequest(
slaveAddress,
startAddress,
new DiscreteCollection(data));
return PerformWriteRequestAsync(request);
}
///
/// Performs a combination of one read operation and one write operation in a single Modbus transaction.
/// The write operation is performed before the read.
///
/// Address of device to read values from.
/// Address to begin reading (Holding registers are addressed starting at 0).
/// Number of registers to read.
/// Address to begin writing (Holding registers are addressed starting at 0).
/// Register values to write.
public ushort[] ReadWriteMultipleRegisters(
byte slaveAddress,
ushort startReadAddress,
ushort numberOfPointsToRead,
ushort startWriteAddress,
ushort[] writeData)
{
ValidateNumberOfPoints("numberOfPointsToRead", numberOfPointsToRead, 125);
ValidateData("writeData", writeData, 121);
var request = new ReadWriteMultipleRegistersRequest(
slaveAddress,
startReadAddress,
numberOfPointsToRead,
startWriteAddress,
new RegisterCollection(writeData));
return PerformReadRegisters(request);
}
///
/// Asynchronously performs a combination of one read operation and one write operation in a single Modbus transaction.
/// The write operation is performed before the read.
///
/// Address of device to read values from.
/// Address to begin reading (Holding registers are addressed starting at 0).
/// Number of registers to read.
/// Address to begin writing (Holding registers are addressed starting at 0).
/// Register values to write.
/// A task that represents the asynchronous operation.
public Task ReadWriteMultipleRegistersAsync(
byte slaveAddress,
ushort startReadAddress,
ushort numberOfPointsToRead,
ushort startWriteAddress,
ushort[] writeData)
{
ValidateNumberOfPoints("numberOfPointsToRead", numberOfPointsToRead, 125);
ValidateData("writeData", writeData, 121);
var request = new ReadWriteMultipleRegistersRequest(
slaveAddress,
startReadAddress,
numberOfPointsToRead,
startWriteAddress,
new RegisterCollection(writeData));
return PerformReadRegistersAsync(request);
}
///
/// Write a file record to the device.
///
/// Address of device to write values to
/// The Extended Memory file number
/// The starting register address within the file
/// The data to be written
public void WriteFileRecord(byte slaveAdress, ushort fileNumber, ushort startingAddress, byte[] data)
{
ValidateMaxData("data", data, 244);
var request = new WriteFileRecordRequest(slaveAdress, new FileRecordCollection(
fileNumber, startingAddress, data));
Transport.UnicastMessage(request);
}
///
/// Executes the custom message.
///
/// The type of the response.
/// The request.
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
[SuppressMessage("Microsoft.Usage", "CA2223:MembersShouldDifferByMoreThanReturnType")]
public TResponse ExecuteCustomMessage(IModbusMessage request)
where TResponse : IModbusMessage, new()
{
return Transport.UnicastMessage(request);
}
private static void ValidateData(string argumentName, T[] data, int maxDataLength)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (data.Length == 0 || data.Length > maxDataLength)
{
string msg = $"The length of argument {argumentName} must be between 1 and {maxDataLength} inclusive.";
throw new ArgumentException(msg);
}
}
private static void ValidateMaxData(string argumentName, T[] data, int maxDataLength)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (data.Length > maxDataLength)
{
string msg = $"The length of argument {argumentName} must not be greater than {maxDataLength}.";
throw new ArgumentException(msg);
}
}
private static void ValidateNumberOfPoints(string argumentName, ushort numberOfPoints, ushort maxNumberOfPoints)
{
if (numberOfPoints < 1 || numberOfPoints > maxNumberOfPoints)
{
string msg = $"Argument {argumentName} must be between 1 and {maxNumberOfPoints} inclusive.";
throw new ArgumentException(msg);
}
}
private bool[] PerformReadDiscretes(ReadCoilsInputsRequest request)
{
ReadCoilsInputsResponse response = Transport.UnicastMessage(request);
return response.Data.Take(request.NumberOfPoints).ToArray();
}
private Task PerformReadDiscretesAsync(ReadCoilsInputsRequest request)
{
return Task.Factory.StartNew(() => PerformReadDiscretes(request));
}
private ushort[] PerformReadRegisters(ReadHoldingInputRegistersRequest request)
{
ReadHoldingInputRegistersResponse response =
Transport.UnicastMessage(request);
return response.Data.Take(request.NumberOfPoints).ToArray();
}
private Task PerformReadRegistersAsync(ReadHoldingInputRegistersRequest request)
{
return Task.Factory.StartNew(() => PerformReadRegisters(request));
}
private ushort[] PerformReadRegisters(ReadWriteMultipleRegistersRequest request)
{
ReadHoldingInputRegistersResponse response =
Transport.UnicastMessage(request);
return response.Data.Take(request.ReadRequest.NumberOfPoints).ToArray();
}
private Task PerformReadRegistersAsync(ReadWriteMultipleRegistersRequest request)
{
return Task.Factory.StartNew(() => PerformReadRegisters(request));
}
private Task PerformWriteRequestAsync(IModbusMessage request)
where T : IModbusMessage, new()
{
return Task.Factory.StartNew(() => Transport.UnicastMessage(request));
}
}
}