public async Task RetriesOnSlaveExceptionCodeSlaveDeviceBusyIgnoringRetryCount() { // Arrange var target = new Mock <ModbusTransport>(new Mock <IPipeResource>().Object, Mock.Of <ITransactionIdProvider>(), Mock.Of <ILogger <IModbusMaster> >()) { CallBase = true }; target.Object.Retries = 3; target.Object.SlaveBusyUsesRetryCount = false; var request = new ReadHoldingRegistersRequest(1, 1, 1); var response = ModbusResponseFactory.CreateResponse <ReadHoldingRegistersResponse>( new byte[] { 1, 3, 2, 0, 1 }); var busyResponse = ModbusResponseFactory.CreateResponse <SlaveExceptionResponse>( new byte[] { 1, 129, (byte)SlaveExceptionCode.SlaveDeviceBusy }); target.SetupWriteRequestAsync(request); target.SetupReadResponseAsync <ReadHoldingRegistersResponse>(busyResponse, busyResponse, busyResponse, busyResponse, response); // Act var actual = await target.Object.SendAsync <ReadHoldingRegistersResponse>(request); // Assert Assert.Equal(response, actual); target.Protected().As <IModbusTransportMock>() .Verify(x => x.WriteRequestAsync(request, It.IsAny <CancellationToken>()), Times.Exactly(5)); target.Protected().As <IModbusTransportMock>() .Verify(x => x.ReadResponseAsync <ReadHoldingRegistersResponse>(It.IsAny <CancellationToken>()), Times.Exactly(5)); target.Protected().As <IModbusTransportMock>() .Verify(x => x.RetryReadResponse(request, response), Times.Once()); target.Protected().As <IModbusTransportMock>() .Verify(x => x.Validate(request, response), Times.Once()); }
public async Task ThrowsOnInvalidCrc() { // Arrange var request = new ReadHoldingRegistersRequest(1, 1, 1); var response = new byte[] { 1, 3, 2, 0, 0, 1, 1 }; var responseSequence = new ReadOnlySequence <byte>(response.AsMemory()); var pipeAdapterMock = new Mock <IPipeResource>(); pipeAdapterMock.Setup(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>())).Returns(Task.CompletedTask); pipeAdapterMock.Setup(x => x.ReadAsync(It.IsAny <int>(), It.IsAny <CancellationToken>())).ReturnsAsync(responseSequence); var transactionIdProviderMock = new Mock <ITransactionIdProvider>(); transactionIdProviderMock.Setup(x => x.NewId()).Returns(1); var crcCalculator = new Mock <ICrcCalculator>(); crcCalculator.Setup(x => x.Calculate(It.IsAny <ReadOnlyMemory <byte> >())).Returns(0); var target = new ModbusRtuOverTcpTransport(pipeAdapterMock.Object, transactionIdProviderMock.Object, crcCalculator.Object, Mock.Of <ILogger <IModbusMaster> >()); // Act var ex = await Assert.ThrowsAsync <IOException>(() => target.SendAsync <ReadHoldingRegistersResponse>(request, It.IsAny <CancellationToken>())); // Assert pipeAdapterMock.Verify(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>()), Times.Once()); pipeAdapterMock.Verify(x => x.ReadAsync(It.IsAny <int>(), It.IsAny <CancellationToken>()), Times.Once()); pipeAdapterMock.Verify(x => x.MarkConsumed(responseSequence), Times.Once()); }
public async Task ThrowsOnSlaveExceptionResponse(SlaveExceptionCode exceptionCode) { // Arrange var target = new Mock <ModbusTransport>(new Mock <IPipeResource>().Object, Mock.Of <ITransactionIdProvider>(), Mock.Of <ILogger <IModbusMaster> >()) { CallBase = true }; var request = new ReadHoldingRegistersRequest(1, 1, 1); var response = ModbusResponseFactory.CreateResponse <SlaveExceptionResponse>( new byte[] { 1, 129, (byte)exceptionCode }); target.SetupWriteRequestAsync(request); target.SetupReadResponseAsync <ReadHoldingRegistersResponse>(response); // Act var ex = await Assert.ThrowsAsync <SlaveException>(() => target.Object.SendAsync <ReadHoldingRegistersResponse>(request)); // Assert Assert.Equal(1, ex.SlaveAddress); Assert.Equal(129, ex.FunctionCode); Assert.Equal(exceptionCode, ex.SlaveExceptionCode); target.Protected().As <IModbusTransportMock>() .Verify(x => x.WriteRequestAsync(request, It.IsAny <CancellationToken>()), Times.Once()); target.Protected().As <IModbusTransportMock>() .Verify(x => x.ReadResponseAsync <ReadHoldingRegistersResponse>(It.IsAny <CancellationToken>()), Times.Once()); }
public async Task ThrowsOnOldTransactionIfRetryCountExceeded() { // Arrange var request = new ReadHoldingRegistersRequest(1, 1, 1); var oldResponse = new ReadOnlySequence <byte>(new byte[] { 0, 1, 0, 0, 0, 5, 1, 3, 2, 0, 0 }.AsMemory()); var newResponse = new ReadOnlySequence <byte>(new byte[] { 0, 2, 0, 0, 0, 5, 1, 3, 2, 0, 0 }.AsMemory()); var pipeAdapterMock = new Mock <IPipeResource>(); pipeAdapterMock.Setup(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>())).Returns(Task.CompletedTask); pipeAdapterMock.SetupSequence(x => x.ReadAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(oldResponse) .ReturnsAsync(oldResponse) .ReturnsAsync(oldResponse) .ReturnsAsync(newResponse); var transactionIdProviderMock = new Mock <ITransactionIdProvider>(); transactionIdProviderMock.Setup(x => x.NewId()).Returns(2); var target = new ModbusIpTransport(pipeAdapterMock.Object, Mock.Of <IModbusLogger>(), transactionIdProviderMock.Object) { Retries = 2 }; // Act await Assert.ThrowsAsync <IOException>(() => target.SendAsync <ReadHoldingRegistersResponse>(request)); // Assert pipeAdapterMock.Verify(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>()), Times.Exactly(3)); pipeAdapterMock.Verify(x => x.ReadAsync(It.IsAny <CancellationToken>()), Times.Exactly(3)); }
public async Task RetriesOnOldTransactionIfDifferenceIsAboveThreshold() { // Arrange var request = new ReadHoldingRegistersRequest(1, 1, 1); var oldResponse = new ReadOnlySequence <byte>(new byte[] { 0, 3, 0, 0, 0, 5, 1, 3, 2, 0, 0 }.AsMemory()); var newResponse = new ReadOnlySequence <byte>(new byte[] { 0, 5, 0, 0, 0, 5, 1, 3, 2, 0, 0 }.AsMemory()); var pipeAdapterMock = new Mock <IPipeResource>(); pipeAdapterMock.Setup(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>())).Returns(Task.CompletedTask); pipeAdapterMock.SetupSequence(x => x.ReadAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(oldResponse) .ReturnsAsync(newResponse); var transactionIdProviderMock = new Mock <ITransactionIdProvider>(); transactionIdProviderMock.Setup(x => x.NewId()).Returns(5); var target = new ModbusIpTransport(pipeAdapterMock.Object, Mock.Of <IModbusLogger>(), transactionIdProviderMock.Object) { Retries = 0, RetryOnOldResponseThreshold = 3 }; // Act var actual = await target.SendAsync <ReadHoldingRegistersResponse>(request); // Assert pipeAdapterMock.Verify(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>()), Times.Once()); pipeAdapterMock.Verify(x => x.ReadAsync(It.IsAny <CancellationToken>()), Times.Exactly(2)); }
public async Task ReadsPipeAgainIfNotEnoughtData() { // Arrange var request = new ReadHoldingRegistersRequest(1, 1, 1); var response = new byte[] { 0, 1, 0, 0, 0, 5, 1, 3, 2, 0, 0 }; var responseSequence = new ReadOnlySequence <byte>(response.AsMemory()); var pipeAdapterMock = new Mock <IPipeResource>(); pipeAdapterMock.Setup(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>())).Returns(Task.CompletedTask); pipeAdapterMock.Setup(x => x.ReadAsync(6, It.IsAny <CancellationToken>())).ReturnsAsync(responseSequence.Slice(0, 6)); pipeAdapterMock.Setup(x => x.ReadAsync(11, It.IsAny <CancellationToken>())).ReturnsAsync(responseSequence); var transactionIdProviderMock = new Mock <ITransactionIdProvider>(); transactionIdProviderMock.Setup(x => x.NewId()).Returns(1); var target = new ModbusTcpTransport(pipeAdapterMock.Object, transactionIdProviderMock.Object, Mock.Of <ILogger <IModbusMaster> >()); // Act var actual = await target.SendAsync <ReadHoldingRegistersResponse>(request, It.IsAny <CancellationToken>()); // Assert pipeAdapterMock.Verify(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>()), Times.Once()); pipeAdapterMock.Verify(x => x.ReadAsync(6, It.IsAny <CancellationToken>()), Times.Once()); pipeAdapterMock.Verify(x => x.ReadAsync(6, It.IsAny <CancellationToken>()), Times.Once()); pipeAdapterMock.Verify(x => x.MarkExamined(responseSequence.Slice(0, 6)), Times.Once()); pipeAdapterMock.Verify(x => x.MarkConsumed(responseSequence), Times.Once()); }
public async Task ThrowsOnSlaveExceptionCodeSlaveDeviceBusyWhenExceedesRetryCount() { // Arrange var target = new Mock <ModbusTransport>(new Mock <IPipeResource>().Object, Mock.Of <IModbusLogger>(), Mock.Of <ITransactionIdProvider>()) { CallBase = true }; target.Object.Retries = 2; target.Object.SlaveBusyUsesRetryCount = true; var request = new ReadHoldingRegistersRequest(1, 1, 1); var busyResponse = ModbusResponseFactory.CreateResponse <SlaveExceptionResponse>( new byte[] { 1, 129, (byte)SlaveExceptionCode.SlaveDeviceBusy }); target.SetupWriteRequestAsync(request); target.SetupReadResponseAsync <ReadHoldingRegistersResponse>(busyResponse); // Act var ex = await Assert.ThrowsAsync <SlaveException>(() => target.Object.SendAsync <ReadHoldingRegistersResponse>(request)); // Assert Assert.Equal(1, ex.SlaveAddress); Assert.Equal(129, ex.FunctionCode); Assert.Equal(SlaveExceptionCode.SlaveDeviceBusy, ex.SlaveExceptionCode); target.Protected().As <IModbusTransportMock>() .Verify(x => x.WriteRequestAsync(request, It.IsAny <CancellationToken>()), Times.Exactly(3)); target.Protected().As <IModbusTransportMock>() .Verify(x => x.ReadResponseAsync <ReadHoldingRegistersResponse>(It.IsAny <CancellationToken>()), Times.Exactly(3)); }
public override ModbusFunction ReadHoldingRegisters(ReadHoldingRegistersRequest request) { var registers = ReadRegisters(request.Quantity); var response = new ReadHoldingRegistersResponse(registers); return(response); }
public async Task ReadsPipeUntilEnoughtData() { // Arrange var request = new ReadHoldingRegistersRequest(1, 1, 1); var response = new byte[] { 0, 1, 0, 0, 0, 5, 1, 3, 2, 0, 0 }; var responseSequence = new ReadOnlySequence <byte>(response.AsMemory(0, 11)); var pipeAdapterMock = new Mock <IPipeResource>(); pipeAdapterMock.Setup(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>())).Returns(Task.CompletedTask); pipeAdapterMock.SetupSequence(x => x.ReadAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(new ReadOnlySequence <byte>(response.AsMemory(0, 1))) .ReturnsAsync(new ReadOnlySequence <byte>(response.AsMemory(0, 3))) .ReturnsAsync(new ReadOnlySequence <byte>(response.AsMemory(0, 5))) .ReturnsAsync(new ReadOnlySequence <byte>(response.AsMemory(0, 7))) .ReturnsAsync(new ReadOnlySequence <byte>(response.AsMemory(0, 9))) .ReturnsAsync(responseSequence); var transactionIdProviderMock = new Mock <ITransactionIdProvider>(); transactionIdProviderMock.Setup(x => x.NewId()).Returns(1); var target = new ModbusIpTransport(pipeAdapterMock.Object, Mock.Of <IModbusLogger>(), transactionIdProviderMock.Object); // Act var actual = await target.SendAsync <ReadHoldingRegistersResponse>(request, It.IsAny <CancellationToken>()); // Assert pipeAdapterMock.Verify(x => x.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), It.IsAny <CancellationToken>()), Times.Once()); pipeAdapterMock.Verify(x => x.ReadAsync(It.IsAny <CancellationToken>()), Times.Exactly(6)); pipeAdapterMock.Verify(x => x.AdvanceTo(responseSequence.Start), Times.Exactly(5)); pipeAdapterMock.Verify(x => x.AdvanceTo(responseSequence.End), Times.Once()); }
public void ReadHoldingRegistersRequestToString() { // Arrange var request = new ReadHoldingRegistersRequest(5, 1, 10); // Act/Assert Assert.Equal("Read 10 holding registers starting at address 1 from slave 5", request.ToString()); }
public void ModbusRequestThrowsOnInvalidBufferSize() { // Arrange var request = new ReadHoldingRegistersRequest(1, 1, 1); var memory = new Memory <byte>(new byte[4]); // Act/Assert Assert.Throws <ArgumentOutOfRangeException>("buffer", () => request.WriteTo(memory)); }
public async Task <ushort[]> ReadHoldingRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken token = default) { ValidateNumberOfPoints(numberOfPoints, 125); var request = new ReadHoldingRegistersRequest(slaveAddress, startAddress, numberOfPoints); var response = await Transport.SendAsync <ReadHoldingRegistersResponse>(request, token).ConfigureAwait(false); return(response.Data); }
public void ReadHoldingRegisters(ushort?unitId, ref List <HoldingRegister> holdingRegisters, bool?isExtendedUnitId) { if (!RegisterListIsContiguous(holdingRegisters)) { throw new Exception("Register list must be contiguous and contain no duplicates."); } var request = new ReadHoldingRegistersRequest(unitId.HasValue ? unitId.Value : UnitId, holdingRegisters, isExtendedUnitId.HasValue ? isExtendedUnitId.Value : UseExtendedAddressing); var response = Transport.UnicastMessage <ReadHoldingRegistersResponse>(request); holdingRegisters = response.HoldingRegisters; }
public async Task <ReadRegistersResult> ReadHoldingRegistersAsync(ushort startAddress, ushort length, CancellationToken cancellationToken) { lock (_isInitializedLock) { if (_isInitialized == false) { throw new ModbusException("This Modbus RTU Device must be Initialized first before any Requests can be Processed"); } } if (startAddress > MaximumAddress) { throw new ArgumentOutOfRangeException(nameof(startAddress), "The Start Address is greater than the Maximum Allowed Value of '" + MaximumAddress + "'"); } if (length == 0) { throw new ArgumentOutOfRangeException(nameof(length), "The Length cannot be Zero"); } if (length > MaximumRegistersReadLength) { throw new ArgumentOutOfRangeException(nameof(length), "The Length is greater than the Maximum Allowed Value of '" + MaximumRegistersReadLength + "'"); } ReadHoldingRegistersRequest request = ReadHoldingRegistersRequest.CreateNew(this, startAddress, length); ProcessRequestResult requestResult = await _channel.ProcessRequestAsync(request, _timeout, _retries, _delayBetweenMessages, cancellationToken); return(new ReadRegistersResult { BytesSent = requestResult.BytesSent, PacketsSent = requestResult.PacketsSent, BytesReceived = requestResult.BytesReceived, PacketsReceived = requestResult.PacketsReceived, Duration = requestResult.Duration, Values = ReadHoldingRegistersResponse.ExtractValues(request, requestResult.Response), }); }
internal static short[] ExtractValues(ReadHoldingRegistersRequest request, RTUResponse response) { if (response.Data.Length < 1) { throw new RTUException("The Response Data Length was too short to extract the Byte Count"); } int expectedByteCount = request.Length * 2; if (response.Data[0] != expectedByteCount) { throw new RTUException("The Response Byte Count '" + response.Data[0].ToString() + "' did not match the Expected Byte Count '" + expectedByteCount.ToString() + "'"); } if (response.Data.Length < expectedByteCount + 1) { throw new RTUException("The Response Data Length of '" + response.Data.Length.ToString() + "' was too short - Expecting a Length of '" + (expectedByteCount + 1).ToString() + "'"); } if (request.Length == 0) { return(new short[0]); } Memory <byte> bytes = response.Data.AsMemory().Slice(1, expectedByteCount); List <short> values = new List <short>(); for (int i = 0; i < bytes.Length; i += 2) { Span <byte> valueBytes = bytes.Slice(i, 2).Span; valueBytes.Reverse(); values.Add(BitConverter.ToInt16(valueBytes)); } return(values.ToArray()); }
static void TestMessage() { ModbusMessage fc01Req = new ReadCoilsRequest(0x13, 0x13); ModbusMessage fc01Res = new ReadCoilsResponse(true, false, true, true, false, false, true, true, true, true, false, true, false, true, true, false, true, false, true, false); ModbusMessage fc02Req = new ReadDiscreteInputsRequest(0xc4, 0x16); ModbusMessage fc02Res = new ReadDiscreteInputsResponse(false, false, true, true, false, true, false, true, true, true, false, true, true, false, true, true, true, false, true, false, true, true); ModbusMessage fc03Req = new ReadHoldingRegistersRequest(0x6b, 0x03); ModbusMessage fc03Res = new ReadHoldingRegistersResponse(0x022b, 0x0000, 0x0064); ModbusMessage fc04Req = new ReadInputRegistersRequest(0x08, 0x01); ModbusMessage fc04Res = new ReadInputRegistersResponse(0x000a); ModbusMessage fc05Req = new WriteSingleCoilRequest(0xac, true); ModbusMessage fc05Res = new WriteSingleCoilResponse(0xac, true); ModbusMessage fc06Req = new WriteSingleRegisterRequest(0x01, 0x0003); ModbusMessage fc06Res = new WriteSingleRegisterResponse(0x01, 0x0003); ModbusMessage fc0fReq = new WriteMultipleCoilsRequest(0x13, true, false, true, true, false, false, true, true, true, false); ModbusMessage fc0fRes = new WriteMultipleCoilsResponse(0x13, 0x0a); ModbusMessage fc10Req = new WriteMultipleRegistersRequest(0x01, 0x000a, 0x0102); ModbusMessage fc10Res = new WriteMultipleRegistersResponse(0x01, 0x02); PrintMessagePDU(fc01Req); PrintMessagePDU(fc01Res); PrintMessagePDU(fc02Req); PrintMessagePDU(fc02Res); PrintMessagePDU(fc03Req); PrintMessagePDU(fc03Res); PrintMessagePDU(fc04Req); PrintMessagePDU(fc04Res); PrintMessagePDU(fc05Req); PrintMessagePDU(fc05Res); PrintMessagePDU(fc06Req); PrintMessagePDU(fc06Res); PrintMessagePDU(fc0fReq); PrintMessagePDU(fc0fRes); PrintMessagePDU(fc10Req); PrintMessagePDU(fc10Res); Assert(MessagePDU(fc01Req) == "Req.: 01 00 13 00 13", $"{nameof(fc01Req)} Fail"); Assert(MessagePDU(fc01Res) == "Res.: 01 03 cd 6b 05", $"{nameof(fc01Res)} Fail"); Assert(MessagePDU(fc02Req) == "Req.: 02 00 c4 00 16", $"{nameof(fc02Req)} Fail"); Assert(MessagePDU(fc02Res) == "Res.: 02 03 ac db 35", $"{nameof(fc02Res)} Fail"); Assert(MessagePDU(fc03Req) == "Req.: 03 00 6b 00 03", $"{nameof(fc03Req)} Fail"); Assert(MessagePDU(fc03Res) == "Res.: 03 06 02 2b 00 00 00 64", $"{nameof(fc03Res)} Fail"); Assert(MessagePDU(fc04Req) == "Req.: 04 00 08 00 01", $"{nameof(fc04Req)} Fail"); Assert(MessagePDU(fc04Res) == "Res.: 04 02 00 0a", $"{nameof(fc04Res)} Fail"); Assert(MessagePDU(fc05Req) == "Req.: 05 00 ac ff 00", $"{nameof(fc05Req)} Fail"); Assert(MessagePDU(fc05Res) == "Res.: 05 00 ac ff 00", $"{nameof(fc05Res)} Fail"); Assert(MessagePDU(fc06Req) == "Req.: 06 00 01 00 03", $"{nameof(fc06Req)} Fail"); Assert(MessagePDU(fc06Res) == "Res.: 06 00 01 00 03", $"{nameof(fc06Res)} Fail"); Assert(MessagePDU(fc0fReq) == "Req.: 0f 00 13 00 0a 02 cd 01", $"{nameof(fc0fReq)} Fail"); Assert(MessagePDU(fc0fRes) == "Res.: 0f 00 13 00 0a", $"{nameof(fc0fRes)} Fail"); Assert(MessagePDU(fc10Req) == "Req.: 10 00 01 00 02 04 00 0a 01 02", $"{nameof(fc10Req)} Fail"); Assert(MessagePDU(fc10Res) == "Res.: 10 00 01 00 02", $"{nameof(fc10Res)} Fail"); Assert(fc01Req.FunctionCode == (FunctionCode)0x01); Assert(fc01Res.FunctionCode == (FunctionCode)0x01); Assert(fc02Req.FunctionCode == (FunctionCode)0x02); Assert(fc02Res.FunctionCode == (FunctionCode)0x02); Assert(fc03Req.FunctionCode == (FunctionCode)0x03); Assert(fc03Res.FunctionCode == (FunctionCode)0x03); Assert(fc04Req.FunctionCode == (FunctionCode)0x04); Assert(fc04Res.FunctionCode == (FunctionCode)0x04); Assert(fc05Req.FunctionCode == (FunctionCode)0x05); Assert(fc05Res.FunctionCode == (FunctionCode)0x05); Assert(fc06Req.FunctionCode == (FunctionCode)0x06); Assert(fc06Res.FunctionCode == (FunctionCode)0x06); Assert(fc0fReq.FunctionCode == (FunctionCode)0x0f); Assert(fc0fRes.FunctionCode == (FunctionCode)0x0f); Assert(fc10Req.FunctionCode == (FunctionCode)0x10); Assert(fc10Res.FunctionCode == (FunctionCode)0x10); }
public ushort ReadHoldingRegistersAsync(ushort startingAddress, ushort quantity) { var function = new ReadHoldingRegistersRequest(startingAddress, quantity); return(CallModbusFunction(function)); }
public ReadHoldingRegistersResponse ReadHoldingRegisters(ushort startingAddress, ushort quantity) { var function = new ReadHoldingRegistersRequest(startingAddress, quantity); return(CallModbusFunctionSync <ReadHoldingRegistersResponse>(function)); }
public abstract ModbusFunction ReadHoldingRegisters(ReadHoldingRegistersRequest request);