/// <summary> /// Starts the Station /// </summary> public void Start() { while (!StopExecution) { //events (in order of priority): // - high priority frame ready (examples : nak is ready to be sent, we need to resend a frame for which a nak was received, we need to resend a frame for which the timeout occured) // - ready to send on wire and frame to send available // - ack timer (receiver : we weren't able to send the ack with a data frame, we need to send it now!) // - data received on wire (correct or corrupt) //checks to do every time: // - check timeouts (sender : haven't received my ack yet, resend frame soon) // timer needs: // ack: // - nothing // - cancel : canceled whenever a frame is sent. There is only one instance of this timer at all times. // - activate : there is no timer currently and we receive a frame // timeout: // - expired frame sequence number // - cancel : cancel with a bool transmitterReady = transmitter.TransmitterReady(StationId); bool transmitterDataReceived = transmitter.DataReceived(StationId); if (transmitterReady && HighPriorityFrames.Count > 0) // - high priority frame ready (examples : nak is ready to be sent, we need to resend a frame for which a nak was received, we need to resend a frame for which the timeout occured) { // Gets next high priority frame Frame frame = HighPriorityFrames.Dequeue(); // Update frame Ack to latest Ack frame.Ack = DecrementSequenceNumber(NextFrameToReceive); // Makes sure that if we send a data frame, it is still a frame that was not acknowledge. We shouldn't resend a frame that was aknowledge. if (frame.Type != Constants.FrameType.Data || OutputBuffer.ContainsKey(frame.Id % BufferSize)) { // Send the frame SendFrame(frame); if (frame.Type == Constants.FrameType.Data) { // If we are sending a frame that was in timeout, we need to register another timeout for it to make sure we send it again should an error occur once again RegisterTimeout(frame.Id); } } } else if (transmitterReady && FrameReadyToSend) // - ready to send on wire and frame to send available { UInt16 ack = DecrementSequenceNumber(NextFrameToReceive); Frame nextFrame = BuildDataFrame(NextFrameToSendSequenceNumber, ack); // if we are sending a frame, we have the Ack in it, so we do not need to send the ack later anymore. We stop the timer. AckTimer.Stop(); // Makes sure that we are not sending an Ack later, it is included in the current frame. Important because the AckTimer could have ended before we canceled it. sendAck = false; // Mark the frame as sent and keep a reference on it in the outputBuffer to show that we are awaiting an Ack for this frame. OutputBuffer.Add(nextFrame.Id % BufferSize, nextFrame); // Send the frame SendFrame(nextFrame); // Register a timeout timer for the sent frame RegisterTimeout(NextFrameToSendSequenceNumber); // Increment the frame to send sequence number because we have sent the current one. NextFrameToSendSequenceNumber = IncrementSequenceNumber(NextFrameToSendSequenceNumber); } else if (transmitterReady && sendAck) // - ack timer (receiver : we weren't able to send the ack with a data frame, we need to send it now!) { // Build an Ack frame Frame ackFrame = new Frame(Constants.FrameType.Ack, DecrementSequenceNumber(NextFrameToReceive)); // Inform the program that we did send the Ack sendAck = false; SendFrame(ackFrame); } else if (transmitterDataReceived) // data received on wire (correct or corrupt) { Frame frameReceived = GetReceivedFrame(); if (frameReceived == null) { // Notify subscriber that frame is being received, but it is corrupted sendFrameDelegate(new Frame(UInt16.MaxValue, Constants.FrameType.Data, UInt16.MaxValue, new BitArray(0), 0), Constants.FrameEvent.FrameReceivedCorrupted, StationId); // The frame was corrupted, we prepare a NAK, but only if we have not sent another one already if (NoNakSentForNextAwaitedFrame) { // Set the LastFrameSequenceNumberForNak to the currently awaited frame's sequence number LastFrameSequenceNumberForNak = NextFrameToReceive; // Build a Nak frame Frame nakFrame = new Frame(Constants.FrameType.Nak, DecrementSequenceNumber(NextFrameToReceive)); // Put the Nak frame in the high priority queue to send it as soon as possible HighPriorityFrames.Enqueue(nakFrame); } } else { // The frame is correct if (frameReceived.Type == Constants.FrameType.Data) { // If the frame is not the Awaited frame, we preemptively prepare a Nak, but only if no Nak was sent already, because the Awaited frame was probably lost if (frameReceived.Id != NextFrameToReceive && NoNakSentForNextAwaitedFrame) { // Set the LastFrameSequenceNumberForNak to the currently awaited frame's sequence number LastFrameSequenceNumberForNak = NextFrameToReceive; // Build a NAK frame Frame nakFrame = new Frame(Constants.FrameType.Nak, DecrementSequenceNumber(NextFrameToReceive)); // Put the Nak frame in the high priority queue to send it as soon as possible HighPriorityFrames.Enqueue(nakFrame); } // Check if the frame id fits in the input buffer. If it does not, we ignore its data if (IsBetween(NextFrameToReceive, frameReceived.Id, LastFrameToReceive)) { // we can add it to the input buffer if not already there if (!InputBuffer.ContainsKey(frameReceived.Id % BufferSize)) { InputBuffer.Add(frameReceived.Id % BufferSize, frameReceived); // Notify subscriber that frame is being received and was new (so it is ok) sendFrameDelegate(frameReceived, ReturnTypeOfLastReceivedFrame == HammingHelper.Status.OK ? Constants.FrameEvent.FrameReceivedOk : Constants.FrameEvent.FrameReceivedCorrected, StationId); } else { // Notify subscriber that frame is being received, but is a duplicate sendFrameDelegate(frameReceived, Constants.FrameEvent.FrameReceivedDuplicate, StationId); } } else { // Notify subscriber that frame is being received, but is not a frame that is awaited currently (so it is dropped). sendFrameDelegate(frameReceived, Constants.FrameEvent.FrameReceivedNotAwaited, StationId); } // Try to pass data to the superior layer (in the fileStream) if we have the next ordered frames while (InputBuffer.ContainsKey(NextFrameToReceive % BufferSize)) { // Write to frame data to the file byte[] frameData = new byte[frameReceived.Data.Length / 8]; frameReceived.Data.CopyTo(frameData, 0); outputFileStream.Write(frameData, 0, (int)frameReceived.DataSize); outputFileStream.Flush(); // Remove the frame from the input buffer InputBuffer.Remove(NextFrameToReceive % BufferSize); // Increment the awaited frame sequence number because this one has been treated. This also reset the LastFrameSequenceNumberForNak value so that it is not mistaken for another Frame with the same sequence number later on. NextFrameToReceive = IncrementSequenceNumber(NextFrameToReceive); // Reset LastFrameSequenceNumberForNak to impossible value, because we changed the NextAwaitedFrameSequenceNumber LastFrameSequenceNumberForNak = MaxSequence; // Start timer for an ack, but only if not already started if (!AckTimer.Enabled && !sendAck) { AckTimer.Start(); } } } else if (frameReceived.Type == Constants.FrameType.Nak) { // frameReceived.Ack represent the last Acknoloedged frame by the receiver, so frameReceived.Ack + 1 is the one the Nak was aiming at. UInt16 nakSequenceNumber = IncrementSequenceNumber(frameReceived.Ack); if (IsBetween(FirstFrameSent, nakSequenceNumber, NextFrameToSendSequenceNumber)) // valid sequence number for current window { if (OutputBuffer.ContainsKey(nakSequenceNumber % BufferSize)) { // If Nak refers to a frame in the outputBuffer, this mean it is indeed a frame that we sent earlier. We need to send it again very soon HighPriorityFrames.Enqueue(OutputBuffer[nakSequenceNumber % BufferSize]); } } // Notify subscriber that a Nak frame has been received sendFrameDelegate(frameReceived, ReturnTypeOfLastReceivedFrame == HammingHelper.Status.OK ? Constants.FrameEvent.FrameReceivedOk : Constants.FrameEvent.FrameReceivedCorrected, StationId); } else if (frameReceived.Type == Constants.FrameType.Ack) { // Notify subscriber that an ack frame has been received sendFrameDelegate(frameReceived, ReturnTypeOfLastReceivedFrame == HammingHelper.Status.OK ? Constants.FrameEvent.FrameReceivedOk : Constants.FrameEvent.FrameReceivedCorrected, StationId); } // Update the NextAwaitedAckSequenceNumber value with the Ack in the frame. while (IsBetween(FirstFrameSent, frameReceived.Ack, NextFrameToSendSequenceNumber)) { System.Timers.Timer timeoutTimer; // Remove the timeout timer associated with this frame sequence number if (TimeoutTimers.TryRemove(FirstFrameSent, out timeoutTimer)) { timeoutTimer.Stop(); } OutputBuffer.Remove(FirstFrameSent % BufferSize); FirstFrameSent = IncrementSequenceNumber(FirstFrameSent); } } } // Every time we iterate, we need to check for timeout events in the TimeoutTimers dictionnary. if (TimeoutTimers.Any(x => x.Value.Enabled == false)) // timeout (sender : haven't received my ack yet, resend frame) { // Check if any timeout occured on frames that were sent foreach (KeyValuePair <UInt16, System.Timers.Timer> finishedTimeoutTimer in TimeoutTimers.Where(x => x.Value.Enabled == false)) { // Get the expired frame to resend Frame frameToResend = OutputBuffer[finishedTimeoutTimer.Key % BufferSize]; // Send as soon as possible HighPriorityFrames.Enqueue(frameToResend); // Remove the timeout from the dictionnary System.Timers.Timer temp = new System.Timers.Timer(); TimeoutTimers.TryRemove(finishedTimeoutTimer.Key, out temp); } } } // Indicate that execution was stopped ExecutionStopped.Set(); }