/// <summary> /// Read from serial provider with retry /// </summary> /// <returns>Data from port or null on error</returns> private byte[] TryPortRead() { var backoff = TimeSpan.Zero; var retry = 3; byte[] deviceData; do { // Device always responds with 11 bytes deviceData = SerialProvider.Read(11); if (backoff != TimeSpan.Zero) { Thread.Sleep(backoff); } backoff += TimeSpan.FromMilliseconds(50); } while (deviceData is null && retry-- > 0); return(deviceData); }
/// <summary> /// Send poll message to device and return a validated response. /// If the response is malformed, null will be returned. /// </summary> /// <returns>Device response</returns> private ApexResponseMessage SendPollMessage() { // Replay the last message or build a new message var nextMessage = _apexState.LastMessage ?? GetNextHostMessage(); var payload = nextMessage.Serialize(); SerialProvider.Write(payload); // Device always responds with 11 bytes var deviceData = TryPortRead(); var pollResponse = new ApexResponseMessage(deviceData); // Log the parsed command and response together for easier analysis Logger?.Trace("{0} poll message: {1}", GetType().Name, nextMessage); Logger?.Trace("{0} poll response: {1}", GetType().Name, pollResponse); // The response was invalid or incomplete if (!pollResponse.IsValid) { // Request retransmission (by not modifying the ACK and payload) _apexState.LastMessage = nextMessage; // Update counter for responsive check if (pollResponse.IsEmptyResponse) { _apexState.NonResponsiveCount++; } // If there is a protocol violation and strict mode is enabled, // report the problem and request a retransmit if (pollResponse.HasProtocolViolation && Config.StrictMode) { Logger?.Error("{0} Invalid message: {1}", GetType().Name, deviceData.ToHexString()); Logger?.Error("{0} Problems: {1}", GetType().Name, string.Join(Environment.NewLine, pollResponse.AllPacketIssues)); return(null); } // Target is responsive, this is just a bad message if (!IsUnresponsive) { return(null); } // Target is unresponsive, check if client needs to be notified if (!_apexState.NotifiedLostConnection) { LostConnection(); _apexState.NotifiedLostConnection = true; } Logger?.Trace("{0} Device is not responding", GetType().Name); return(null); } // If ACK does not match, then device is requesting a retransmit if (nextMessage.Ack != pollResponse.Ack) { // Request retransmission _apexState.LastMessage = nextMessage; return(null); } // Update state with successful polling notification _apexState.RegisterSuccessfulPoll(); return(pollResponse); }