Esempio n. 1
0
        /// <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();
        }