internal static RTUResponse CreateNew(Memory <byte> message, RTURequest request) { if (message.Length < 2) { throw new RTUException("The RTU Response Message Length was too short"); } RTUResponse response = new RTUResponse(); byte[] command = message.Slice(0, 2).ToArray(); if (ValidateFunctionCode(command[0]) == false) { throw new RTUException("Invalid Function Code '" + command[0].ToString() + "'"); } response.FunctionCode = command[0]; throwIfResponseError(command[0], command[1]); if (response.FunctionCode != request.FunctionCode) { throw new RTUException("Unexpected Function Code '" + Enum.GetName(typeof(enFunctionCode), response.FunctionCode) + "' - Expecting '" + Enum.GetName(typeof(enFunctionCode), request.FunctionCode) + "'"); } response.Data = message.Length > 1 ? message.Slice(1, message.Length - 1).ToArray() : new byte[0]; return(response); }
private async Task <ReceiveMessageResult> receiveMessageAsync(RTURequest request, byte unitId, int timeout, CancellationToken cancellationToken) { ReceiveMessageResult result = new ReceiveMessageResult { Bytes = 0, Packets = 0, Message = new Memory <byte>(), }; try { List <byte> receivedData = new List <byte>(); DateTime startTimestamp = DateTime.UtcNow; while (DateTime.UtcNow.Subtract(startTimestamp).TotalMilliseconds < timeout && receivedData.Count < RTUResponse.GetMessageLengthHint(request, receivedData) + 3) { Memory <byte> buffer = new byte[300]; TimeSpan receiveTimeout = TimeSpan.FromMilliseconds(timeout).Subtract(DateTime.UtcNow.Subtract(startTimestamp)); if (receiveTimeout.TotalMilliseconds >= 50) { int receivedBytes = await _client.ReceiveAsync(buffer, receiveTimeout, cancellationToken); if (receivedBytes > 0) { receivedData.AddRange(buffer.Slice(0, receivedBytes).ToArray()); result.Bytes += receivedBytes; result.Packets += 1; } } } if (receivedData.Count == 0) { throw new ModbusException("Failed to Receive RTU Message from Modbus Device ID '" + unitId + "' on '" + RemoteHost + ":" + Port + "' - No Data was Received"); } if (receivedData.Count < RTUResponse.GetMessageLengthHint(request, receivedData) + 3) { throw new ModbusException("Failed to Receive RTU Message within the Timeout Period from Modbus Device ID '" + unitId + "' on '" + RemoteHost + ":" + Port + "'"); } if (receivedData[0] != unitId) { throw new ModbusException("Failed to Receive RTU Message from Modbus Device ID '" + unitId + "' on '" + RemoteHost + ":" + Port + "' - The Unit ID did not Match"); } receivedData.RemoveRange(0, 1); receivedData.RemoveRange(receivedData.Count - 2, 2); result.Message = receivedData.ToArray(); } catch (ObjectDisposedException) { throw new ModbusException("Failed to Receive RTU Message from Modbus Device ID '" + unitId + "' on '" + RemoteHost + ":" + Port + "' - The underlying Socket Connection has been Closed"); } catch (TimeoutException) { throw new ModbusException("Failed to Receive RTU Message within the Timeout Period from Modbus Device ID '" + unitId + "' on '" + RemoteHost + ":" + Port + "'"); } catch (System.Net.Sockets.SocketException e) { throw new ModbusException("Failed to Receive RTU Message from Modbus Device ID '" + unitId + "' on '" + RemoteHost + ":" + Port + "'", e); } return(result); }
public async Task <ProcessRequestResult> ProcessRequestAsync(RTURequest request, int timeout, int retries, int?delayBetweenMessages, CancellationToken cancellationToken) { int attempts = 0; Memory <byte> responseMessage = new Memory <byte>(); int bytesSent = 0; int packetsSent = 0; int bytesReceived = 0; int packetsReceived = 0; DateTime startTimestamp = DateTime.UtcNow; while (attempts <= retries) { try { await _requestSemaphore.WaitAsync(cancellationToken); if (attempts > 0) { await destroyAndInitializeClient(request.UnitID, timeout, cancellationToken); } // Build the Request into a Message we can Send ReadOnlyMemory <byte> requestMessage = request.BuildMessage(); TimeSpan timeSinceLastMessage = DateTime.UtcNow.Subtract(_lastMessageTimestamp); if (delayBetweenMessages.HasValue && delayBetweenMessages.Value > 0 && timeSinceLastMessage.TotalMilliseconds < delayBetweenMessages.Value) { await Task.Delay(TimeSpan.FromMilliseconds(delayBetweenMessages.Value - timeSinceLastMessage.TotalMilliseconds), cancellationToken); } // Send the Message SendMessageResult sendResult = await sendMessageAsync(request.UnitID, requestMessage, timeout, cancellationToken); bytesSent += sendResult.Bytes; packetsSent += sendResult.Packets; // Receive a Response ReceiveMessageResult receiveResult = await receiveMessageAsync(request, request.UnitID, timeout, cancellationToken); bytesReceived += receiveResult.Bytes; packetsReceived += receiveResult.Packets; responseMessage = receiveResult.Message; break; } catch (Exception) { if (attempts >= retries) { throw; } } finally { _requestSemaphore.Release(); } // Increment the Attempts attempts++; } try { return(new ProcessRequestResult { BytesSent = bytesSent, PacketsSent = packetsSent, BytesReceived = bytesReceived, PacketsReceived = packetsReceived, Duration = DateTime.UtcNow.Subtract(startTimestamp).TotalMilliseconds, Response = RTUResponse.CreateNew(responseMessage, request), }); } catch (RTUException e) { throw new ModbusException("Received an RTU Error Response from Modbus Device ID '" + request.UnitID + "' on '" + _remoteHost + ":" + _port + "'", e); } }
internal static int GetMessageLengthHint(RTURequest request, List <byte> receivedData) { if (receivedData.Count > 0 && receivedData[0] >= 0x80) { byte functionCode = receivedData[0]; functionCode -= 0x80; if (Enum.IsDefined(typeof(enFunctionCode), functionCode)) { return(2); // Function Code + Exception Code } } int byteCount = 1; // Function Code if (request is ReadHoldingCoilsRequest readHoldingCoils) { byteCount += 1; // Byte Count if (readHoldingCoils.Length > 0) { byteCount += readHoldingCoils.Length / 8; // One Byte for 8 Bits } if (readHoldingCoils.Length % 8 != 0) { byteCount += 1; // Additional Byte for Extra Bits } } else if (request is ReadInputCoilsRequest readInputCoils) { byteCount += 1; // Byte Count if (readInputCoils.Length > 0) { byteCount += readInputCoils.Length / 8; // One Byte for 8 Bits } if (readInputCoils.Length % 8 != 0) { byteCount += 1; // Additional Byte for Extra Bits } } else if (request is ReadHoldingRegistersRequest readHoldingRegisters) { byteCount += 1; // Byte Count byteCount += readHoldingRegisters.Length * 2; // Value Bytes } else if (request is ReadInputRegistersRequest readInputRegisters) { byteCount += 1; // Byte Count byteCount += readInputRegisters.Length * 2; // Value Bytes } else if (request is WriteHoldingCoilRequest || request is WriteHoldingCoilsRequest || request is WriteHoldingRegisterRequest || request is WriteHoldingRegistersRequest) { byteCount += 4; // Address + Single Value or Start Address + Length } if (byteCount < 2) { byteCount = 2; } return(byteCount); }