/** * Acknowledge frames we've sent and removes the from the sent queue. * This method is called for each DATA or ACK frame where we have the 'ack' property. * * @param ackNum the last ack from the NCP */ private void AckSentQueue(int ackNum) { // Handle the timer if it's running if (_sentTime.HasValue) { StopTimer(); receiveTimeout = (int)((receiveTimeout * 7 / 8) + ((DateTime.Now - _sentTime.Value).TotalMilliseconds / 2)); if (receiveTimeout < T_RX_ACK_MIN) { receiveTimeout = T_RX_ACK_MIN; } else if (receiveTimeout > T_RX_ACK_MAX) { receiveTimeout = T_RX_ACK_MAX; } Log.Verbose("ASH: RX Timer took {TimeSpent}ms, timer now {ReceiveTimeout}ms", (int)(DateTime.Now - _sentTime.Value).TotalMilliseconds, receiveTimeout); _sentTime = null; } AshFrameData ackedFrame = null; while (_sentQueue.TryPeek(out ackedFrame) && ackedFrame.GetFrmNum() != ackNum) { _sentQueue.TryDequeue(out ackedFrame); Log.Debug("ASH: Frame acked and removed {Frame}", ackedFrame); } }
private bool SendNextFrame() { // We're not allowed to send if we're not connected if (!_stateConnected) { return(false); } // Check how many frames are outstanding if (_sentQueue.Count >= TX_WINDOW) { // check timer task if (_timer == null) { StartRetryTimer(); } return(false); } EzspFrameRequest nextFrame = null; if (!_sendQueue.TryDequeue(out nextFrame) || nextFrame == null) { // Nothing to send return(false); } // Encapsulate the EZSP frame into the ASH packet Log.Verbose("TX ASH EZSP: {Frame}", nextFrame); AshFrameData ashFrame = new AshFrameData(nextFrame); _retries = 0; SendFrame(ashFrame); return(true); }
private void SendRetry() { Log.Debug("ASH: Retry Sent Queue Length {Count}", _sentQueue.Count); AshFrameData ashFrame = null; if (!_sentQueue.TryPeek(out ashFrame) || ashFrame == null) { Log.Debug("ASH: Retry nothing to resend!"); return; } ashFrame.SetReTx(); OutputFrame(ashFrame); }
private void ParserTaskLoop() { Log.Debug("AshFrameHandler parser task started"); int exceptionCnt = 0; while (!_parserCancellationToken.IsCancellationRequested) { try { int[] packetData = GetPacket(); if (packetData == null) { continue; } AshFrame packet = AshFrame.CreateFromInput(packetData); AshFrame responseFrame = null; if (packet == null) { Log.Debug("<-- RX ASH error: BAD PACKET {Frame}", FrameToString(packetData)); // Send a NAK responseFrame = new AshFrameNak(_ackNum); } else { Log.Debug("<-- RX ASH frame: {Frame}", packet.ToString()); // Reset the exception counter exceptionCnt = 0; // Extract the flags for DATA/ACK/NAK frames switch (packet.GetFrameType()) { case AshFrame.FrameType.DATA: _statsRxData++; // Always use the ackNum - even if this frame is discarded AckSentQueue(packet.GetAckNum()); AshFrameData dataPacket = (AshFrameData)packet; // Check for out of sequence frame number if (packet.GetFrmNum() == _ackNum) { // Frame was in sequence - prepare the response _ackNum = (_ackNum + 1) & 0x07; responseFrame = new AshFrameAck(_ackNum); // Get the EZSP frame EzspFrameResponse response = EzspFrame.CreateHandler(dataPacket.GetDataBuffer()); Log.Verbose("ASH RX EZSP: {Response}", response); if (response == null) { Log.Debug("ASH: No frame handler created for {Packet}", packet); } else { NotifyTransactionComplete(response); HandleIncomingFrame(response); } } else if (!dataPacket.GetReTx()) { // Send a NAK - this is out of sequence and not a retransmission Log.Debug("ASH: Frame out of sequence - expected {Expected}, received {Received}", _ackNum, packet.GetFrmNum()); responseFrame = new AshFrameNak(_ackNum); } else { // Send an ACK - this was out of sequence but was a retransmission responseFrame = new AshFrameAck(_ackNum); } break; case AshFrame.FrameType.ACK: _statsRxAcks++; AckSentQueue(packet.GetAckNum()); break; case AshFrame.FrameType.NAK: _statsRxNaks++; SendRetry(); break; case AshFrame.FrameType.RSTACK: // Stack has been reset! HandleReset((AshFrameRstAck)packet); break; case AshFrame.FrameType.ERROR: // Stack has entered FAILED state HandleError((AshFrameError)packet); break; default: break; } } // Due to possible I/O buffering, it is important to note that the Host could receive several // valid or invalid frames after triggering a reset of the NCP. The Host must discard all frames // and errors until a valid RSTACK frame is received. if (!_stateConnected) { continue; } // Send the next frame // Note that ASH protocol requires the host always sends an ack. // Piggybacking on data packets is not allowed if (responseFrame != null) { SendFrame(responseFrame); } SendNextFrame(); } catch (Exception e) { Log.Error(e, "AshFrameHandler Exception: ", e); if (exceptionCnt++ > 10) { Log.Error("AshFrameHandler exception count exceeded: {Exception}"); _parserCancellationToken.Cancel(); } } } Log.Debug("AshFrameHandler exited."); }