123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- 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
- {
- /// <summary>
- /// Modbus master device.
- /// </summary>
- public abstract class ModbusMaster : ModbusDevice, IModbusMaster
- {
- protected ModbusMaster(IModbusTransport transport)
- : base(transport)
- {
- }
- /// <summary>
- /// Reads from 1 to 2000 contiguous coils status.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startAddress">Address to begin reading.</param>
- /// <param name="numberOfPoints">Number of coils to read.</param>
- /// <returns>Coils status.</returns>
- 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);
- }
- /// <summary>
- /// Asynchronously reads from 1 to 2000 contiguous coils status.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startAddress">Address to begin reading.</param>
- /// <param name="numberOfPoints">Number of coils to read.</param>
- /// <returns>A task that represents the asynchronous read operation.</returns>
- public Task<bool[]> ReadCoilsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
- {
- ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 2000);
- var request = new ReadCoilsInputsRequest(
- ModbusFunctionCodes.ReadCoils,
- slaveAddress,
- startAddress,
- numberOfPoints);
- return PerformReadDiscretesAsync(request);
- }
- /// <summary>
- /// Reads from 1 to 2000 contiguous discrete input status.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startAddress">Address to begin reading.</param>
- /// <param name="numberOfPoints">Number of discrete inputs to read.</param>
- /// <returns>Discrete inputs status.</returns>
- 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);
- }
- /// <summary>
- /// Asynchronously reads from 1 to 2000 contiguous discrete input status.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startAddress">Address to begin reading.</param>
- /// <param name="numberOfPoints">Number of discrete inputs to read.</param>
- /// <returns>A task that represents the asynchronous read operation.</returns>
- public Task<bool[]> ReadInputsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
- {
- ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 2000);
- var request = new ReadCoilsInputsRequest(
- ModbusFunctionCodes.ReadInputs,
- slaveAddress,
- startAddress,
- numberOfPoints);
- return PerformReadDiscretesAsync(request);
- }
- /// <summary>
- /// Reads contiguous block of holding registers.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startAddress">Address to begin reading.</param>
- /// <param name="numberOfPoints">Number of holding registers to read.</param>
- /// <returns>Holding registers status.</returns>
- 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);
- }
- /// <summary>
- /// Asynchronously reads contiguous block of holding registers.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startAddress">Address to begin reading.</param>
- /// <param name="numberOfPoints">Number of holding registers to read.</param>
- /// <returns>A task that represents the asynchronous read operation.</returns>
- public Task<ushort[]> ReadHoldingRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
- {
- ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 125);
- var request = new ReadHoldingInputRegistersRequest(
- ModbusFunctionCodes.ReadHoldingRegisters,
- slaveAddress,
- startAddress,
- numberOfPoints);
- return PerformReadRegistersAsync(request);
- }
- /// <summary>
- /// Reads contiguous block of input registers.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startAddress">Address to begin reading.</param>
- /// <param name="numberOfPoints">Number of holding registers to read.</param>
- /// <returns>Input registers status.</returns>
- 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);
- }
- /// <summary>
- /// Asynchronously reads contiguous block of input registers.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startAddress">Address to begin reading.</param>
- /// <param name="numberOfPoints">Number of holding registers to read.</param>
- /// <returns>A task that represents the asynchronous read operation.</returns>
- public Task<ushort[]> ReadInputRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
- {
- ValidateNumberOfPoints("numberOfPoints", numberOfPoints, 125);
- var request = new ReadHoldingInputRegistersRequest(
- ModbusFunctionCodes.ReadInputRegisters,
- slaveAddress,
- startAddress,
- numberOfPoints);
- return PerformReadRegistersAsync(request);
- }
- /// <summary>
- /// Writes a single coil value.
- /// </summary>
- /// <param name="slaveAddress">Address of the device to write to.</param>
- /// <param name="coilAddress">Address to write value to.</param>
- /// <param name="value">Value to write.</param>
- public void WriteSingleCoil(byte slaveAddress, ushort coilAddress, bool value)
- {
- var request = new WriteSingleCoilRequestResponse(slaveAddress, coilAddress, value);
- Transport.UnicastMessage<WriteSingleCoilRequestResponse>(request);
- }
- /// <summary>
- /// Asynchronously writes a single coil value.
- /// </summary>
- /// <param name="slaveAddress">Address of the device to write to.</param>
- /// <param name="coilAddress">Address to write value to.</param>
- /// <param name="value">Value to write.</param>
- /// <returns>A task that represents the asynchronous write operation.</returns>
- public Task WriteSingleCoilAsync(byte slaveAddress, ushort coilAddress, bool value)
- {
- var request = new WriteSingleCoilRequestResponse(slaveAddress, coilAddress, value);
- return PerformWriteRequestAsync<WriteSingleCoilRequestResponse>(request);
- }
- /// <summary>
- /// Writes a single holding register.
- /// </summary>
- /// <param name="slaveAddress">Address of the device to write to.</param>
- /// <param name="registerAddress">Address to write.</param>
- /// <param name="value">Value to write.</param>
- public void WriteSingleRegister(byte slaveAddress, ushort registerAddress, ushort value)
- {
- var request = new WriteSingleRegisterRequestResponse(
- slaveAddress,
- registerAddress,
- value);
- Transport.UnicastMessage<WriteSingleRegisterRequestResponse>(request);
- }
- /// <summary>
- /// Asynchronously writes a single holding register.
- /// </summary>
- /// <param name="slaveAddress">Address of the device to write to.</param>
- /// <param name="registerAddress">Address to write.</param>
- /// <param name="value">Value to write.</param>
- /// <returns>A task that represents the asynchronous write operation.</returns>
- public Task WriteSingleRegisterAsync(byte slaveAddress, ushort registerAddress, ushort value)
- {
- var request = new WriteSingleRegisterRequestResponse(
- slaveAddress,
- registerAddress,
- value);
- return PerformWriteRequestAsync<WriteSingleRegisterRequestResponse>(request);
- }
- /// <summary>
- /// Write a block of 1 to 123 contiguous 16 bit holding registers.
- /// </summary>
- /// <param name="slaveAddress">Address of the device to write to.</param>
- /// <param name="startAddress">Address to begin writing values.</param>
- /// <param name="data">Values to write.</param>
- public void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] data)
- {
- ValidateData("data", data, 123);
- var request = new WriteMultipleRegistersRequest(
- slaveAddress,
- startAddress,
- new RegisterCollection(data));
- Transport.UnicastMessage<WriteMultipleRegistersResponse>(request);
- }
- /// <summary>
- /// Asynchronously writes a block of 1 to 123 contiguous registers.
- /// </summary>
- /// <param name="slaveAddress">Address of the device to write to.</param>
- /// <param name="startAddress">Address to begin writing values.</param>
- /// <param name="data">Values to write.</param>
- /// <returns>A task that represents the asynchronous write operation.</returns>
- public Task WriteMultipleRegistersAsync(byte slaveAddress, ushort startAddress, ushort[] data)
- {
- ValidateData("data", data, 123);
- var request = new WriteMultipleRegistersRequest(
- slaveAddress,
- startAddress,
- new RegisterCollection(data));
- return PerformWriteRequestAsync<WriteMultipleRegistersResponse>(request);
- }
- /// <summary>
- /// Writes a sequence of coils.
- /// </summary>
- /// <param name="slaveAddress">Address of the device to write to.</param>
- /// <param name="startAddress">Address to begin writing values.</param>
- /// <param name="data">Values to write.</param>
- public void WriteMultipleCoils(byte slaveAddress, ushort startAddress, bool[] data)
- {
- ValidateData("data", data, 1968);
- var request = new WriteMultipleCoilsRequest(
- slaveAddress,
- startAddress,
- new DiscreteCollection(data));
- Transport.UnicastMessage<WriteMultipleCoilsResponse>(request);
- }
- /// <summary>
- /// Asynchronously writes a sequence of coils.
- /// </summary>
- /// <param name="slaveAddress">Address of the device to write to.</param>
- /// <param name="startAddress">Address to begin writing values.</param>
- /// <param name="data">Values to write.</param>
- /// <returns>A task that represents the asynchronous write operation.</returns>
- public Task WriteMultipleCoilsAsync(byte slaveAddress, ushort startAddress, bool[] data)
- {
- ValidateData("data", data, 1968);
- var request = new WriteMultipleCoilsRequest(
- slaveAddress,
- startAddress,
- new DiscreteCollection(data));
- return PerformWriteRequestAsync<WriteMultipleCoilsResponse>(request);
- }
- /// <summary>
- /// Performs a combination of one read operation and one write operation in a single Modbus transaction.
- /// The write operation is performed before the read.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startReadAddress">Address to begin reading (Holding registers are addressed starting at 0).</param>
- /// <param name="numberOfPointsToRead">Number of registers to read.</param>
- /// <param name="startWriteAddress">Address to begin writing (Holding registers are addressed starting at 0).</param>
- /// <param name="writeData">Register values to write.</param>
- 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);
- }
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="slaveAddress">Address of device to read values from.</param>
- /// <param name="startReadAddress">Address to begin reading (Holding registers are addressed starting at 0).</param>
- /// <param name="numberOfPointsToRead">Number of registers to read.</param>
- /// <param name="startWriteAddress">Address to begin writing (Holding registers are addressed starting at 0).</param>
- /// <param name="writeData">Register values to write.</param>
- /// <returns>A task that represents the asynchronous operation.</returns>
- public Task<ushort[]> 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);
- }
- /// <summary>
- /// Write a file record to the device.
- /// </summary>
- /// <param name="slaveAdress">Address of device to write values to</param>
- /// <param name="fileNumber">The Extended Memory file number</param>
- /// <param name="startingAddress">The starting register address within the file</param>
- /// <param name="data">The data to be written</param>
- 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<WriteFileRecordResponse>(request);
- }
- /// <summary>
- /// Executes the custom message.
- /// </summary>
- /// <typeparam name="TResponse">The type of the response.</typeparam>
- /// <param name="request">The request.</param>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
- [SuppressMessage("Microsoft.Usage", "CA2223:MembersShouldDifferByMoreThanReturnType")]
- public TResponse ExecuteCustomMessage<TResponse>(IModbusMessage request)
- where TResponse : IModbusMessage, new()
- {
- return Transport.UnicastMessage<TResponse>(request);
- }
- private static void ValidateData<T>(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<T>(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<ReadCoilsInputsResponse>(request);
- return response.Data.Take(request.NumberOfPoints).ToArray();
- }
- private Task<bool[]> PerformReadDiscretesAsync(ReadCoilsInputsRequest request)
- {
- return Task.Factory.StartNew(() => PerformReadDiscretes(request));
- }
- private ushort[] PerformReadRegisters(ReadHoldingInputRegistersRequest request)
- {
- ReadHoldingInputRegistersResponse response =
- Transport.UnicastMessage<ReadHoldingInputRegistersResponse>(request);
- return response.Data.Take(request.NumberOfPoints).ToArray();
- }
- private Task<ushort[]> PerformReadRegistersAsync(ReadHoldingInputRegistersRequest request)
- {
- return Task.Factory.StartNew(() => PerformReadRegisters(request));
- }
- private ushort[] PerformReadRegisters(ReadWriteMultipleRegistersRequest request)
- {
- ReadHoldingInputRegistersResponse response =
- Transport.UnicastMessage<ReadHoldingInputRegistersResponse>(request);
- return response.Data.Take(request.ReadRequest.NumberOfPoints).ToArray();
- }
- private Task<ushort[]> PerformReadRegistersAsync(ReadWriteMultipleRegistersRequest request)
- {
- return Task.Factory.StartNew(() => PerformReadRegisters(request));
- }
- private Task PerformWriteRequestAsync<T>(IModbusMessage request)
- where T : IModbusMessage, new()
- {
- return Task.Factory.StartNew(() => Transport.UnicastMessage<T>(request));
- }
- }
- }
|