def _trySendingFrame(self): """Only send if no carrier is sensed and we are not in backoff.""" if self._transmitting or self._outstandingFrame: # Another frame is currently being transmitted. Wait for next call. return if not self._transmitQueue: # Nothing to transmit. return if self._backingOff: # Still waiting backoff return if self._device.phy.carrierSense(): # Channel is occupied. Do backoff. *** COLLISION AVOIDANCE *** self._backingOff = True SCHEDULE(self._computeBackoff(), self._endBackoff) ACTIVITY_INDICATION(self, "tx", "CA backoff", "darkblue", 1, 2) return type, dstAddr, bitstream = self._transmitQueue.pop(0) if type == self.ACK: # Create a new Ack frame and send it. ACTIVITY_INDICATION(self, "tx", "ACK/NAK", "grey", 0, 0) self._phySendFrame(dstAddr=dstAddr, data="") else: if type == self.RETR: self.packetRetransmissions += 1 ACTIVITY_INDICATION(self, "tx", "Resend", "orange", 0, 0) else: ACTIVITY_INDICATION(self, "tx", "Send", "yellow", 0, 0) self._resetBackoff() self.packetsSent += 1 self._retransmissionTimer = SCHEDULE(self.retransmissionTimeout, self._timeout) self._outstandingFrame = self._phySendFrame(dstAddr, bitstream)
def _backoff(self): """Compute backoff and schedule retransmission. This method is called after the end of the jam transmission. In half duplex mode, it does the following: - compute a backoff - enter backoff mode - schedule the retransmission of the frame. If the maximum number of collisions is already reached, abandon the frame and signal an error to the upper layer. """ ACTIVITY_INDICATION(self, "tx", "backoff", "blue", 1, 2) self._jamming = False if self._transmissionAttemps >= 16: # Transmission failed. Update statistics, inform dl and clean up SCHEDULE(0.0, self._niu.dl.sendStatus, (_EXCESSIVE_COLLISION_ERROR, self._sendBuffer)) self.excessiveCollisions += 1 print "Excessive collisions" self._transmissionAttemps = 0 self._sendBuffer = None self._niu.XOFF = False return k = min(10, self._transmissionAttemps) r = int(random() * (1 << k)) if self._niu.phy.getDataRate() == 1000e6: backoff = r * self._GIGA_SLOTTIME * self._niu.phy.bittime() else: backoff = r * self._SLOTTIME * self._niu.phy.bittime() SCHEDULE(backoff, self._mediumAccess)
def send(self, bitstream): """Simulate the transmission of the data on the medium. Schedule the call to inform the DL entity when finished. Arguments: bitstream:Bitstream -- Block of data to transmit onto the medium. Return value: None. """ self._niu.medium.startTransmission(self._niu) transmissionDelay = len(bitstream) * 8 / self._dataRate SCHEDULE(transmissionDelay, self._niu.medium.completeTransmission, (self._niu, bitstream)) SCHEDULE(transmissionDelay, self._niu.dl.sendStatus, (0, bitstream))
def send(self, bitstream): """Encapsulate the data into a frame and send it to the phy layer. May only be called if self._device.XOFF is False, i.e. if the device is ready to accept a new data packet from the higher layer. """ # Make sure there is no other packet already in transit. assert (self._device.XOFF == False and not self._sendBuffer) # Block any new transmission until this packet has been acknowledged self._device.XOFF = True # Create a new frame with CRC and sequence numbers. frame = self._newFrame() frame.SN = self._VS self._VS = (self._VS + 1) % 2 frame.RN = self._VR frame.data = bitstream checksum = crc32(frame.serialize()[:-4]) & ((1L << 32) - 1) frame.FCS = checksum # Set the retransmission timer self._sendBuffer = frame self._retransmissionTimer = SCHEDULE(self.retransmissionTimeout, self._timeout) # Sent the frame ACTIVITY_INDICATION(self, "tx", "Send", "yellow", 0, 0) self.packetsSent += 1 self._device.phy.send(frame.serialize()) return 0
def _trySendingFrame(self): """Send the next frame waiting in the transmitQ, if there is any.""" if self._transmitting or self._outstandingFrame: # Another frame is currently being transmitted. Wait for next call. return if not self._transmitQueue: # Nothing to transmit. return type, dstAddr, bitstream = self._transmitQueue.pop(0) if type == self.ACK: # Create a new Ack frame and send it. ACTIVITY_INDICATION(self, "tx", "ACK/NAK", "grey", 0, 0) self._phySendFrame(dstAddr=dstAddr, data="") else: if type == self.RETR: self.packetRetransmissions += 1 ACTIVITY_INDICATION(self, "tx", "Resend", "orange", 0, 0) else: ACTIVITY_INDICATION(self, "tx", "Send", "yellow", 0, 0) self._resetBackoff() self.packetsSent += 1 self._retransmissionTimer = SCHEDULE(self.retransmissionTimeout, self._timeout) self._outstandingFrame = self._phySendFrame(dstAddr, bitstream)
def _timeout(self): """Called when a retransmission timeout occurs.""" ACTIVITY_INDICATION(self, "tx", "TIMEOUT") self._retransmissionTimer = None self._consecutiveCollisions += 1 self._backingOff = True ACTIVITY_INDICATION(self, "tx", "Retr backoff", "blue", 1, 2) SCHEDULE(self._computeBackoff(), self._endBackoff)
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 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 _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 _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 _sendPacket(self): """Send PDUs to the lower layer until the whole page is transmitted. At the end, start a new OFF period by calling generate.""" length = min(self._pageSize, self._pduSize) bitstream = self._uniqueBitstream(length) self.send(bitstream) self.octetsTransmitted += length self.pdusTransmitted += 1 self._pageSize -= length if self._pageSize > 0: delay = (self._pduSize*8.0) / self._onRate SCHEDULE(delay, self._sendPacket) else: self.pagesTransmitted += 1 self.generate()
def completeTransmission(self, txNIU, data): """Finish a transmission and deliver the data to receiving NIUs. By calling this method, a phy layer entity of an attached NIU indicates that it has finished the transmission previously started by calling startTransmission. The data provided to the medium is delivered, after a propagation delay, to the phy entities of attached NIUs by calling phy.receive. Arguments: txNIU:NIU -- Transmitting NIU data:Bitstream -- Transmitted data Return value: None. """ for rxNIU, rxPos in self._niuDict.items(): if rxNIU != txNIU: propDelay = self._distances[(txNIU, rxNIU)] / self.signalSpeed SCHEDULE(propDelay, rxNIU.phy.receive, (data, ))
def startTransmission(self, txNIU): """Start a transmission on the medium. A phy protocol entity calls this method to indicate that it starts the transmission of a signal. The other NIU is informed, after the propagation delay, that a new transmission has started by calling the method phy.newChannelActivity of its phy entity. No data is actually transmitted. This will be done by the function completeTransmission, which is called by the phy layer entity at the end of the transmission. Arguments: niu:NIU -- Transmitting NIU Return value: None. """ txPos = self._niuDict[txNIU] for rxNIU, rxPos in self._niuDict.items(): if rxNIU != txNIU: propDelay = abs(rxPos - txPos) / self.signalSpeed SCHEDULE(propDelay, rxNIU.phy.newChannelActivity)
def sendStatus(self, status, bitstream): """Terminate the transmission, inform dl, and clean up. This method is called from the phy layer, when a transmission previously initiated with PHY.send terminates. First check if we are sending a jam. In this case, the jam transmission has completed. Switch of the transmission and call the backoff method. If not jamming, terminate the transmission, update the statistics, clean up and inform the upper layer. A status==0 indicates success. Any other status indicates an error. """ self._latestTransmitActivity = TIME() if self._jamming: ACTIVITY_INDICATION(self, "tx") self._niu.phy.transmitting(False) self._backoff() return ACTIVITY_INDICATION(self, "tx") self._niu.phy.transmitting(False) # Terminate the transmission if not status: self.framesTransmittedOK += 1 self.octetsTransmittedOK += len(self._sendBuffer.data) if self._transmissionAttemps > 2: self.multipleCollisionFrames += 1 if self._transmissionAttemps == 2: self.singleCollisionFrames += 1 else: # Discard the frame and inform DL (LLC) self.octetsTransmittedError += len(self._sendBuffer.data) status = _UNKNOWN_TRANSMISSION_ERROR SCHEDULE(0.0, self._niu.dl.sendStatus, (status, self._sendBuffer)) self._transmissionAttemps = 0 self._sendBuffer = None self._niu.XOFF = False
def generate(self): length = max(9,int(self._pduSizeRNG())) self.send(self._uniqueBitstream(length)) self.octetsTransmitted += length self.pdusTransmitted += 1 SCHEDULE(self._interarrivalRNG(), self.generate)
def _timeout(self): """Called when a retransmission timeout occurs.""" ACTIVITY_INDICATION(self, "tx", "TIMEOUT") self._retransmissionTimer = None self._consecutiveCollisions += 1 SCHEDULE(self._computeBackoff(), self._retransmit)
def start(self): SCHEDULE(self.interval, self.generate)
def generate(self): """Schedule a new page transmission after a random OFF time.""" offTime = self._offTimeRNG() offTime = min(offTime, self._offTimeMax) offTime = max(offTime, self._offTimeMin) SCHEDULE(offTime, self._sendPage)
def _mediumAccess(self): """Acquire the transmit medium, transmit the frame and inform dl. This function tries to transmit a current frame according to the CSMA/CD algorithm, as described in the standard, Section 4.2.3.2. The following elements of the standard are implemented: - Half duplex transmission: CSMA/CD - Full duplex transmission - Carrier sense (in half duplex) - Interframe spacing (in half and full duplex) - Collision detection and enforcement through jam (in half duplex) - Exponential backoff and retransmission (half duplex) - Carrier extension (in half duplex for data rate > 100 Mb/s The following elements are not implemented: - Frame bursting (used for data rates > 100 Mb/s). """ assert (self._sendBuffer != None) if self._niu.phy.getDuplexMode() == FULL_DUPLEX: # Transmission without contention. Only respect interframe gap gaptime = self._INTERFRAME_GAP * self._niu.phy.bittime() currentgap = TIME() - self._latestTransmitActivity if currentgap < gaptime: ACTIVITY_INDICATION(self, "tx", "gaptime", "grey", 3, 2) SCHEDULE(gaptime - currentgap, self._mediumAccess) return self._transmissionAttemps += 1 ACTIVITY_INDICATION(self, "tx", "send FD", "green", 0, 0) self._niu.phy.transmitting(activate=True) self._niu.phy.send(self._sendBuffer.serialize()) return else: # Transmission in half duplex mode # 1. Carrier sense if self._niu.phy.carrierSense(): ACTIVITY_INDICATION(self, "tx", "carrierSense", "blue", 3, 2) self._waitingForIdleChannel = True # Wait until channel activities end. The MAC.channelIdle # method will call us then return self._waitingForIdleChannel = False # 2. Interframe gap gaptime = self._INTERFRAME_GAP * self._niu.phy.bittime() currentgap = TIME() - max(self._latestTransmitActivity, self._latestReceiveActivity) if gaptime - currentgap > self._niu.phy.bittime() / 100: ACTIVITY_INDICATION(self, "tx", "gaptime", "grey", 3, 2) gapjitter = gaptime * random() / 100 # Avoid dicrete synchro. SCHEDULE(gaptime - currentgap + gapjitter, self._mediumAccess) return # 3. Here we go. Initiate the transmission. Wait for the # transmissionCompleted or collisionDetect signal self._transmissionAttemps += 1 ACTIVITY_INDICATION(self, "tx", "send HD", "green", 0, 0) self._niu.phy.transmitting(activate=True) self._niu.phy.send(self._sendBuffer.serialize()) return
def generate(self): self.send(self._uniqueBitstream(self._pduSize)) self.octetsTransmitted += self._pduSize self.pdusTransmitted += 1 SCHEDULE(self._interarrival, self.generate)
def generate(self): data = 'y'*self.length # A string 'xxxxx' of the correct length packet = data + self.checksum(data) self.send(packet) SCHEDULE(self.interval, self.generate)