def _trySendingFrame(self): """Send the next frame waiting in the sendBuffer, if there is any.""" if self._transmitting: # Another frame is currently being transmitted. Wait for next call. return if not self._transmitQueue: # Nothing to transmit. return type, sn = self._transmitQueue.pop(0) if type == self.ACK or type == self.SREJ: # Create a new Ack frame and send it. ack = self._newFrame() ack.SN = self._SNmax # Allowed since payload is empty ack.RN = sn if type == self.SREJ: ack.SREJ = 1 ACTIVITY_INDICATION(self, "tx", "Send SREJ RN=%s" % str(ack.RN), "red", 0, 0) else: ACTIVITY_INDICATION(self, "tx", "Send ACK RN=%s" % str(ack.RN), "grey", 0, 0) checksum = crc32(ack.serialize()[:-4]) & ((1L << 32) - 1) ack.FCS = checksum self._transmitting = True self._device.phy.send(ack.serialize()) else: assert (type == self.FIRSTTR or type == self.RETR) # Create a new data packet and send it bitstream = self._sendBuffer[sn] frame = self._newFrame() frame.SN = sn frame.RN = self._VR frame.data = bitstream checksum = crc32(frame.serialize()[:-4]) & ((1L << 32) - 1) frame.FCS = checksum # Set the retransmission timer if sn in self._retransmissionTimer: timer = self._retransmissionTimer.pop(sn) CANCEL(timer) self._retransmissionTimer[sn] = SCHEDULE( self.retransmissionTimeout, self._timeout, (sn, )) # Send the frame and update the statistics if type == self.RETR: self.packetRetransmissions += 1 ACTIVITY_INDICATION(self, "tx", "Retr SN=%s" % str(frame.SN), "orange", 0, 0) else: ACTIVITY_INDICATION(self, "tx", "Send SN=%s" % str(frame.SN), "yellow", 0, 0) self.packetsSent += 1 self._transmitting = True self._device.phy.send(frame.serialize())
def _checkAck(self, frame): """Look if the frame contains an ACK and handle it.""" if self._outstandingFrame != None: # We are waiting for an ACK if frame.RN == (self._VS[frame.SrcAddr] + 1) % 2: # POSITIVE ACKNOWLEDGEMENT ACTIVITY_INDICATION(self, "rx", "ACK ok") self._outstandingFrame = None if self._retransmissionTimer: CANCEL(self._retransmissionTimer) self._retransmissionTimer = None self._VS[frame.SrcAddr] = frame.RN self._trySendingFrame()
def _retransmit(self): """Retransmits the current frame. Called either because of a negative ack or by a retransmission timeout. """ ACTIVITY_INDICATION(self, "tx", "Retransmit", "orange", 0, 0) if self._retransmissionTimer: CANCEL(self._retransmissionTimer) self._retransmissionTimer = SCHEDULE(self.retransmissionTimeout, self._timeout) self.packetRetransmissions += 1 self.packetsSent += 1 self._device.phy.send(self._sendBuffer.serialize())
def _retransmit(self, sn): """Cancel all waiting frame transmissions and resent the whole window. """ # Only keep waiting acknowledgements in the transmit queue. self._transmitQueue = [ el for el in self._transmitQueue if el[0] == self.ACK ] for timer in self._retransmissionTimer.values(): CANCEL(timer) self._retransmissionTimer = {} # Place the whole window into the transmission queue for sn in self._sendBuffer.keys(): bisect.insort(self._transmitQueue, (self.RETR, sn)) self._trySendingFrame()
def _checkAck(self, frame): """Look if the frame contains an ACK or SREJ and handle it.""" if self._SNmin == self._SNmax: # We do not expect a new ack. return RN = frame.RN if frame.SREJ: # This is a selective repeat request. Retransmit RN # if the window allows it if ((RN - self._SNmin) % self._SN_MOD < (self._SNmax - self._SNmin) % self._SN_MOD): ACTIVITY_INDICATION(self, "rx", "SREJ RN=%s" % str(RN)) self._retransmit(RN) return if ((RN - self._SNmin) % self._SN_MOD > (self._SNmax - self._SNmin) % self._SN_MOD): # ACK outside window. Do nothing ACTIVITY_INDICATION(self, "rx", "DupACK RN=%s" % str(RN)) return # RN is inside the window. In contrast to Go-back-n, it only # acknowledges one packet, not all packets before RN ACTIVITY_INDICATION(self, "rx", "Rcv ACK RN=%s" % str(RN)) bitstream = self._sendBuffer.pop(RN, None) if bitstream: if (self.RETR, RN) in self._transmitQueue: self._transmitQueue.remove((self.RETR, RN)) timer = self._retransmissionTimer.pop(RN, None) if timer: CANCEL(timer) # Let's see if we can move the window. Move it up to the first non # acknowledged packet. while (self._SNmin != self._SNmax and self._SNmin not in self._sendBuffer): assert ((self.FIRSTTR, self._SNmin) not in self._transmitQueue) # Move the left window edge and see if we can accept new frames self._SNmin = (self._SNmin + 1) % self._SN_MOD if (self._SNmax - self._SNmin) % self._SN_MOD < self._winSize: self._device.XOFF = False # Inform the upper layer that it can sent the next packet # @@@ FIXME: this is not clean. Better provide a queue for upperLayer in self._upperLayers: upperLayer.sendStatus(0, bitstream)
def _checkAck(self, frame): """Look if the frame contains an ACK and handle it.""" if self._outstandingFrame != None: # We are waiting for an ACK if frame.RN == (self._VS[frame.SrcAddr] + 1) % 2: # POSITIVE ACKNOWLEDGEMENT ACTIVITY_INDICATION(self, "rx", "ACK ok") self._outstandingFrame = None if self._retransmissionTimer: CANCEL(self._retransmissionTimer) self._retransmissionTimer = None self._VS[frame.SrcAddr] = frame.RN # Do a backoff. *** COLLISION AVOIDANCE *** self._backingOff = True SCHEDULE(self._computeBackoff(), self._endBackoff) ACTIVITY_INDICATION(self, "tx", "CA backoff", "darkblue", 1, 2)
def _checkAck(self, frame): """Look if the frame contains an ACK and handle it.""" if self._sendBuffer != None: # We are waiting for an ACK if frame.RN == self._VS: # POSITIVE ACKNOWLEDGEMENT ACTIVITY_INDICATION(self, "rx", "ACK") bitstream = self._sendBuffer.data self._sendBuffer = None CANCEL(self._retransmissionTimer) self._retransmissionTimer = None self._device.XOFF = False # Allow the next packet to be sent # Inform the upper layer that it can sent the next packet # @@@ FIXME: this is not clean. Better provide a queuing layer for upperLayer in self._upperLayers: upperLayer.sendStatus(0, bitstream) else: # NEGATIVE ACKNOWLEDGEMENT. Retransmit the packet. ACTIVITY_INDICATION(self, "rx", "NAK") self._retransmit()
def send(self, bitstream): """Accept a block of data and simulate transmission on the medium. Called by the MAC layer to transmit a block of data. Can be called multiple times while Phy.transmitting is active (). In this case, if the previous block has not yet been completely transmitted onto the medium, the remaining data is discarded and the transmission continues with the new data. Therefore, the MAC should call this method only after having received a MAC.sendStatus notification or if a collision has been detected and the MAC wants to stop the transmission of data and continue with a jam signal. Arguments: bitstream:Bitstream -- Block of data to transmit onto the medium. Return value: None. """ if self._transmitting == False: raise RuntimeError("Attempt to transmit without previously " "activating transmission on NIU: " + self._niu._node.hostname + "." + self._niu.devicename + ".phy") if self._transmittedData == None: # New transmission self._transmittedData = bitstream self._transmitStartTime = TIME() self._niu.medium.startTransmission(self._niu) transmissionDelay = len(bitstream) * 8 / self._dataRate self._completeTxEventId = SCHEDULE(transmissionDelay, self._completeTransmission) if self._receiveActivities > 0: self._niu.mac.collisionDetect() else: # Interrupt current transmission and send new data bytelen = int( ((TIME() - self._transmitStartTime) * self._dataRate + 0.05) / 8) self._transmittedData = self._transmittedData[0:bytelen] + bitstream CANCEL(self._completeTxEventId) transmissionDelay = len(bitstream) * 8 / self._dataRate self._completeTxEventId = SCHEDULE(transmissionDelay, self._completeTransmission)
def _checkAck(self, frame): """Look if the frame contains an ACK and handle it.""" RN = frame.RN # Test if the RN is inside the window if self._SNmin == self._SNmax: # We do not expect a new ack. return if ((RN - self._SNmin) % self._SN_MOD > (self._SNmax - self._SNmin) % self._SN_MOD): # ACK outside window. Do nothing ACTIVITY_INDICATION(self, "rx", "DupACK RN=%s" % str(RN)) return # RN is inside the window and acknowledges all frames before RN. # Remove the acknowledged frames from the window, transmission # queue and cancel the timers. ACTIVITY_INDICATION(self, "rx", "Rcv ACK RN=%s" % str(RN)) while self._SNmin != RN: assert ((self.FIRSTTR, self._SNmin) not in self._transmitQueue) if (self.RETR, self._SNmin) in self._transmitQueue: self._transmitQueue.remove((self.RETR, self._SNmin)) bitstream = self._sendBuffer.pop(self._SNmin) timer = self._retransmissionTimer.pop(self._SNmin, None) if timer: CANCEL(timer) # Move the left window edge and see if we can accept new frames. self._SNmin = (self._SNmin + 1) % self._SN_MOD if (self._SNmax - self._SNmin) % self._SN_MOD < self._winSize: self._device.XOFF = False # Inform the upper layer that it can sent the next packet # @@@ FIXME: this is not clean. Better provide a queue for upperLayer in self._upperLayers: upperLayer.sendStatus(0, bitstream)