internal async Task <ProcessRequestResult> ProcessRequestAsync(FINSRequest request, int timeout, int retries, 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 { if (!_semaphore.Wait(0)) { await _semaphore.WaitAsync(cancellationToken); } if (attempts > 0) { await DestroyAndInitializeClient(timeout, cancellationToken); } // Build the Request into a Message we can Send ReadOnlyMemory <byte> requestMessage = request.BuildMessage(getNextRequestId()); // Send the Message SendMessageResult sendResult = await SendMessageAsync(requestMessage, timeout, cancellationToken); bytesSent += sendResult.Bytes; packetsSent += sendResult.Packets; // Receive a Response ReceiveMessageResult receiveResult = await ReceiveMessageAsync(timeout, cancellationToken); bytesReceived += receiveResult.Bytes; packetsReceived += receiveResult.Packets; responseMessage = receiveResult.Message; break; } catch (Exception) { if (attempts >= retries) { throw; } } finally { _semaphore.Release(); } // Increment the Attempts attempts++; } try { return(new ProcessRequestResult { BytesSent = bytesSent, PacketsSent = packetsSent, BytesReceived = bytesReceived, PacketsReceived = packetsReceived, Duration = DateTime.UtcNow.Subtract(startTimestamp).TotalMilliseconds, Response = FINSResponse.CreateNew(responseMessage, request), }); } catch (FINSException e) { if (e.Message.Contains("Service ID") && responseMessage.Length >= 9 && responseMessage.Span[9] != request.ServiceID) { try { if (!_semaphore.Wait(0)) { await _semaphore.WaitAsync(cancellationToken); } await PurgeReceiveBuffer(timeout, cancellationToken); } catch { } finally { _semaphore.Release(); } } throw new OmronException("Received a FINS Error Response from Omron PLC '" + _remoteHost + ":" + _port + "'", e); } }
internal static FINSResponse CreateNew(Memory <byte> message, FINSRequest request) { if (message.Length < HEADER_LENGTH + COMMAND_LENGTH + RESPONSE_CODE_LENGTH) { throw new FINSException("The FINS Response Message Length was too short"); } FINSResponse response = new FINSResponse(); byte[] header = message.Slice(0, HEADER_LENGTH).ToArray(); response.ServiceID = header[9]; byte[] command = message.Slice(HEADER_LENGTH, COMMAND_LENGTH).ToArray(); if (ValidateFunctionCode(command[0]) == false) { throw new FINSException("Invalid Function Code '" + command[0].ToString() + "'"); } response.FunctionCode = command[0]; if (response.FunctionCode != request.FunctionCode) { throw new FINSException("Unexpected Function Code '" + Enum.GetName(typeof(enFunctionCode), response.FunctionCode) + "' - Expecting '" + Enum.GetName(typeof(enFunctionCode), request.FunctionCode) + "'"); } if (ValidateSubFunctionCode(command[0], command[1]) == false) { throw new FINSException("Invalid Sub Function Code '" + command[1].ToString() + "' for Function Code '" + command[0].ToString() + "'"); } response.SubFunctionCode = command[1]; if (response.SubFunctionCode != request.SubFunctionCode) { throw new FINSException("Unexpected Sub Function Code '" + getSubFunctionCodeName(response.FunctionCode, response.SubFunctionCode) + "' - Expecting '" + getSubFunctionCodeName(request.FunctionCode, request.SubFunctionCode) + "'"); } byte[] responseCode = message.Slice(HEADER_LENGTH + COMMAND_LENGTH, RESPONSE_CODE_LENGTH).ToArray(); if (hasNetworkRelayError(responseCode[0])) { throw new FINSException("A Network Relay Error has occurred"); } response.MainResponseCode = getMainResponseCode(responseCode[0]); response.SubResponseCode = getSubResponseCode(responseCode[1]); throwIfResponseError(response.MainResponseCode, response.SubResponseCode); if (request.ServiceID != response.ServiceID) { throw new FINSException("The Service ID for the FINS Request '" + request.ServiceID + "' did not match the FINS Response '" + response.ServiceID + "'"); } response.Data = message.Length > HEADER_LENGTH + COMMAND_LENGTH + RESPONSE_CODE_LENGTH ? message.Slice(HEADER_LENGTH + COMMAND_LENGTH + RESPONSE_CODE_LENGTH, message.Length - (HEADER_LENGTH + COMMAND_LENGTH + RESPONSE_CODE_LENGTH)).ToArray() : new byte[0]; return(response); }