Ejemplo n.º 1
0
	def __init__(self, identity, left, right, rate, delay, size):
		Reporter.__init__(self, identity)				
		self.left_node = left
		self.right_node = right

		# Need to standardize units to kbit/ms, kbits, and ms		
		self.capacity_kbit_per_ms = float(rate)				# 1000 Kilobits in a Megabit, 1000 ms in a s
		self.ms_prop_delay = float(delay)					# Already standardized
		self.kbits_in_each_buffer = 8.0 * float(size) 		# 8 = conversion from BYTE to BIT, ignore 1024 vs 1000 convention

		self.left_buff = LinkBuffer(self.kbits_in_each_buffer)
		self.right_buff = LinkBuffer(self.kbits_in_each_buffer)

		self.bidirectional_queueing_delay_memory = [-1] * constants.QUEUEING_DELAY_WINDOW
Ejemplo n.º 2
0
    def __init__(self, identity, left, right, rate, delay, size):
        Reporter.__init__(self, identity)
        self.left_node = left
        self.right_node = right

        # Need to standardize units to kbit/ms, kbits, and ms
        self.capacity_kbit_per_ms = float(
            rate)  # 1000 Kilobits in a Megabit, 1000 ms in a s
        self.ms_prop_delay = float(delay)  # Already standardized
        self.kbits_in_each_buffer = 8.0 * float(
            size
        )  # 8 = conversion from BYTE to BIT, ignore 1024 vs 1000 convention

        self.left_buff = LinkBuffer(self.kbits_in_each_buffer)
        self.right_buff = LinkBuffer(self.kbits_in_each_buffer)

        self.bidirectional_queueing_delay_memory = [
            -1
        ] * constants.QUEUEING_DELAY_WINDOW
Ejemplo n.º 3
0
    def __init__(self, identifier, rate, delay, buffer_size, node1, node2):
        """
        A network link.

        Args:
            identifier (str):           The name of the link.
            rate (float):               The link capacity, in Mbps.
            delay (int):                Propagation delay, in ms.
            buffer_size (int):          The buffer size, in KB.
            node1 (Node):               The first endpoint of the link.
            node2 (Node):               The second endpoint of the link.
        """
        super(Link, self).__init__()

        self.id = identifier
        self.rate = rate
        self.delay = delay
        self.buffer_size = buffer_size * 1024
        self.node1 = node1
        self.node2 = node2

        self.node1.add_link(self)
        self.node2.add_link(self)

        # This determines whether the link is in use to handle half-duplex
        self.in_use = False
        self.current_dir = None

        # The buffer of packets going towards node 1 or node 2
        self.buffer = LinkBuffer(self)

        # Bytes sent over this link
        self.bytesSent = 0.0
        # Time it has taken so far to send these bytes
        self.sendTime = 0.0
        self.packets_on_link = {1: [], 2: []}
Ejemplo n.º 4
0
    def __init__(self, identifier, rate, delay, buffer_size, node1, node2):
        """
        A network link.

        Args:
            identifier (str):           The name of the link.
            rate (float):               The link capacity, in Mbps.
            delay (int):                Propagation delay, in ms.
            buffer_size (int):          The buffer size, in KB.
            node1 (Node):               The first endpoint of the link.
            node2 (Node):               The second endpoint of the link.
        """
        super(Link, self).__init__()

        self.id = identifier
        self.rate = rate
        self.delay = delay
        self.buffer_size = buffer_size * 1024
        self.node1 = node1
        self.node2 = node2

        self.node1.add_link(self)
        self.node2.add_link(self)

        # This determines whether the link is in use to handle half-duplex
        self.in_use = False
        self.current_dir = None

        # The buffer of packets going towards node 1 or node 2
        self.buffer = LinkBuffer(self)

        # Bytes sent over this link
        self.bytesSent = 0.0
        # Time it has taken so far to send these bytes
        self.sendTime = 0.0
        self.packets_on_link = {
            1: [],
            2: []
        }
Ejemplo n.º 5
0
class Link(Reporter):

    left_node = ""
    right_node = ""

    capacity_kbit_per_ms = -1
    kbits_in_each_buffer = -1
    ms_prop_delay = -1

    left_buff = []
    right_buff = []
    sim = ""

    packet_loading = False
    packets_in_flight = 0
    transmission_direction = ""  # left-to-right, right-to-left
    switch_direction_flag = False

    bidirectional_queueing_delay_memory = [-1
                                           ] * constants.QUEUEING_DELAY_WINDOW

    def ms_tx_delay(self, packet):
        return (packet.get_kbits() / self.capacity_kbit_per_ms)  # ms

    def ms_total_delay(self, packet):
        return self.ms_tx_delay(packet) + self.ms_prop_delay  # ms

    # Call Node initialization code, with the Node ID (required unique)
    # Initializes itself
    # Inputs: rate (Mbps), Delay (ms), size (KB)
    # We need to standardize units to kbit/ms, kbits, and ms
    def __init__(self, identity, left, right, rate, delay, size):
        Reporter.__init__(self, identity)
        self.left_node = left
        self.right_node = right

        # Need to standardize units to kbit/ms, kbits, and ms
        self.capacity_kbit_per_ms = float(
            rate)  # 1000 Kilobits in a Megabit, 1000 ms in a s
        self.ms_prop_delay = float(delay)  # Already standardized
        self.kbits_in_each_buffer = 8.0 * float(
            size
        )  # 8 = conversion from BYTE to BIT, ignore 1024 vs 1000 convention

        self.left_buff = LinkBuffer(self.kbits_in_each_buffer)
        self.right_buff = LinkBuffer(self.kbits_in_each_buffer)

        self.bidirectional_queueing_delay_memory = [
            -1
        ] * constants.QUEUEING_DELAY_WINDOW

    def set_event_simulator(self, sim):
        self.sim = sim
        self.left_buff.set_event_simulator(sim)
        self.left_buff.set_my_link(self)
        self.left_buff.set_my_direction(constants.LTR)
        self.right_buff.set_event_simulator(sim)
        self.right_buff.set_my_link(self)
        self.right_buff.set_my_direction(constants.RTL)

    def get_left(self):
        return self.left_node

    def get_right(self):
        return self.right_node

    def get_rate(self):
        return self.capacity_kbit_per_ms

    def get_delay(self):
        return self.ms_prop_delay

    def get_buff(self):
        return self.kbits_in_each_buffer

    # return time average of queueing delay buffer
    def get_occupancy(self):
        '''
		for v in self.bidirectional_queueing_delay_memory:
			sys.stderr.write("QD: %0.3e\n"%v)
		'''
        return self.average_delay_buffer()

    def average_delay_buffer(self):
        delay = 0.0
        cnt = 0.0
        for v in self.bidirectional_queueing_delay_memory:
            if v >= 0:
                delay += v
                cnt += 1.0
        delay /= cnt
        return delay

    # reset packet loading, decide how to transfer_next_packet
    def packet_transmitted(self, packet):
        self.packet_loading = False
        self.sim.request_event(\
         Handle_Packet_Propagation(packet, self.get_id(),\
         self.sim.get_current_time() + self.ms_prop_delay))
        self.packets_in_flight += 1
        self.transfer_next_packet()

    # decrement packets in flight counter, decide how to transfer_next_packet
    def packet_propagated(self):
        self.packets_in_flight -= 1
        self.transfer_next_packet()

    # load packet-to-send to the appropriate buffer, then decide how to
    # transfer it
    def send(self, packet, sender_id):
        notDroppedFlag = True
        if sender_id == self.left_node:
            notDroppedFlag = self.left_buff.enqueue(packet)
            self.transfer_next_packet()
        elif sender_id == self.right_node:
            notDroppedFlag = self.right_buff.enqueue(packet)
            self.transfer_next_packet()
        else:
            raise ValueError('Packet received by Link %s \
				from unknown Node %s' % (self.ID, sender_id))

        if (not notDroppedFlag):
            print "\nDEBUG: (in link) packets dropped : %s, packet %d\n" % (
                self.get_id(), packet.get_ID())
            if constants.MEASUREMENT_ENABLE:
                print constants.MEASURE_PACKET_LOSS((packet.get_flow(),\
                       packet.get_type(),\
                       packet.get_ID(),\
                       self.sim.get_current_time()))

    def transfer_next_packet(self):
        if (not self.packet_loading) and (self.packets_in_flight == 0):

            lt = self.left_buff.get_head_timestamp() \
               if self.left_buff.can_dequeue() else -1
            rt = self.right_buff.get_head_timestamp() \
               if self.right_buff.can_dequeue() else -1

            if (lt >= 0) or (rt >= 0):
                if (lt >= 0) and (rt >= 0):
                    if self.switch_direction_flag:
                        # there are packets on both sides,
                        # and it's time to switch link direction
                        self.switch_direction_flag = False
                        if (self.transmission_direction == constants.LTR):
                            self.transmission_direction = constants.RTL
                            packet_to_transmit, queue_delay = self.right_buff.dequeue(
                            )
                            packet_to_transmit.set_curr_dest(self.get_left())
                        else:
                            self.transmission_direction = constants.LTR
                            packet_to_transmit, queue_delay = self.left_buff.dequeue(
                            )
                            packet_to_transmit.set_curr_dest(self.get_right())
                    elif (lt < rt):
                        # Start loading Packet from head of left buffer into channel
                        self.transmission_direction = constants.LTR
                        packet_to_transmit, queue_delay = self.left_buff.dequeue(
                        )
                        packet_to_transmit.set_curr_dest(self.get_right())
                    else:
                        # Start loading Packet from head of right buffer into channel
                        self.transmission_direction = constants.RTL
                        packet_to_transmit, queue_delay = self.right_buff.dequeue(
                        )
                        packet_to_transmit.set_curr_dest(self.get_left())
                elif (lt >= 0):
                    # Start loading Packet from head of left buffer into channel
                    self.transmission_direction = constants.LTR
                    packet_to_transmit, queue_delay = self.left_buff.dequeue()
                    packet_to_transmit.set_curr_dest(self.get_right())
                else:
                    # Start loading Packet from head of right buffer into channel
                    self.transmission_direction = constants.RTL
                    packet_to_transmit, queue_delay = self.right_buff.dequeue()
                    packet_to_transmit.set_curr_dest(self.get_left())

                self.packet_loading = True
                completion_time = \
                 self.sim.get_current_time() + self.ms_tx_delay(packet_to_transmit)

                self.sim.request_event(\
                 Handle_Packet_Transmission( packet_to_transmit,\
                        self.get_id(),\
                         completion_time))

                self.bidirectional_queueing_delay_memory.pop(0)
                self.bidirectional_queueing_delay_memory.append(queue_delay)
            else:
                pass

        elif (not self.packet_loading) and (self.packets_in_flight > 0):
            lt = self.left_buff.get_head_timestamp() \
             if self.left_buff.can_dequeue() else -1
            rt = self.right_buff.get_head_timestamp() \
             if self.right_buff.can_dequeue() else -1
            self.attempt_to_transmit_in_same_direction(lt, rt)

        else:
            pass

    # lt, rt are left and right heads-of-buffer timestamps
    def attempt_to_transmit_in_same_direction(self, lt, rt):
        # if there are any more to send in the current direction,
        # can they get there before the timestamp of the head of the
        # other buffer?
        if self.transmission_direction == constants.RTL:
            buff = self.right_buff
            curr_dest = self.get_left()
            t1 = lt
            t2 = rt
        else:
            buff = self.left_buff
            curr_dest = self.get_right()
            t1 = rt
            t2 = lt

        if t2 >= 0:
            proposed_receive_time = self.sim.get_current_time() + \
             self.ms_total_delay(buff.see_head_packet())
            if ((t1 >= 0) and (proposed_receive_time < t1)) or (t1 < 0):
                packet_to_transmit, queue_delay = buff.dequeue()
                packet_to_transmit.set_curr_dest(curr_dest)
                self.packet_loading = True
                completion_time = self.sim.get_current_time() + \
                 self.ms_tx_delay(packet_to_transmit)
                self.sim.request_event(\
                 Handle_Packet_Transmission( packet_to_transmit,\
                       self.get_id(),\
                        completion_time))

                self.bidirectional_queueing_delay_memory.pop(0)
                self.bidirectional_queueing_delay_memory.append(queue_delay)

            else:
                self.switch_direction_flag = True
Ejemplo n.º 6
0
class Link(Reporter):

	left_node = ""
	right_node = ""
	
	capacity_kbit_per_ms = -1 
	kbits_in_each_buffer = -1 	
	ms_prop_delay = -1
	
	left_buff = []
	right_buff = []
	sim = ""

	packet_loading = False
	packets_in_flight = 0
	transmission_direction = "" # left-to-right, right-to-left
	switch_direction_flag = False

	bidirectional_queueing_delay_memory = [-1] * constants.QUEUEING_DELAY_WINDOW

	def ms_tx_delay (self, packet): 
		return (packet.get_kbits() / self.capacity_kbit_per_ms) # ms

	def ms_total_delay (self, packet): 
		return self.ms_tx_delay(packet) + self.ms_prop_delay # ms
	
	# Call Node initialization code, with the Node ID (required unique)
	# Initializes itself
	# Inputs: rate (Mbps), Delay (ms), size (KB)
	# We need to standardize units to kbit/ms, kbits, and ms
	def __init__(self, identity, left, right, rate, delay, size):
		Reporter.__init__(self, identity)				
		self.left_node = left
		self.right_node = right

		# Need to standardize units to kbit/ms, kbits, and ms		
		self.capacity_kbit_per_ms = float(rate)				# 1000 Kilobits in a Megabit, 1000 ms in a s
		self.ms_prop_delay = float(delay)					# Already standardized
		self.kbits_in_each_buffer = 8.0 * float(size) 		# 8 = conversion from BYTE to BIT, ignore 1024 vs 1000 convention

		self.left_buff = LinkBuffer(self.kbits_in_each_buffer)
		self.right_buff = LinkBuffer(self.kbits_in_each_buffer)

		self.bidirectional_queueing_delay_memory = [-1] * constants.QUEUEING_DELAY_WINDOW

	def set_event_simulator (self, sim):
		self.sim = sim
		self.left_buff.set_event_simulator(sim)
		self.left_buff.set_my_link(self)
		self.left_buff.set_my_direction(constants.LTR)
		self.right_buff.set_event_simulator(sim)
		self.right_buff.set_my_link(self)
		self.right_buff.set_my_direction(constants.RTL)

	def get_left(self):
		return self.left_node

	def get_right(self):
		return self.right_node

	def get_rate(self):
		return self.capacity_kbit_per_ms

	def get_delay(self):
		return self.ms_prop_delay

	def get_buff(self):
		return self.kbits_in_each_buffer

	# return time average of queueing delay buffer
	def get_occupancy(self):		
		'''
		for v in self.bidirectional_queueing_delay_memory:
			sys.stderr.write("QD: %0.3e\n"%v)
		'''
		return self.average_delay_buffer()

	def average_delay_buffer(self):
		delay = 0.0
		cnt = 0.0
		for v in self.bidirectional_queueing_delay_memory:
			if v >= 0:
				delay += v
				cnt += 1.0
		delay /= cnt
		return delay

	# reset packet loading, decide how to transfer_next_packet
	def packet_transmitted (self, packet):
		self.packet_loading = False		
		self.sim.request_event(\
			Handle_Packet_Propagation(packet, self.get_id(),\
			self.sim.get_current_time() + self.ms_prop_delay))
		self.packets_in_flight += 1
		self.transfer_next_packet()

	# decrement packets in flight counter, decide how to transfer_next_packet
	def packet_propagated (self):
		self.packets_in_flight -= 1
		self.transfer_next_packet()

	# load packet-to-send to the appropriate buffer, then decide how to 
	# transfer it
	def send (self, packet, sender_id):				
		notDroppedFlag = True
		if sender_id == self.left_node:			
			notDroppedFlag = self.left_buff.enqueue(packet)
			self.transfer_next_packet()
		elif sender_id == self.right_node:
			notDroppedFlag = self.right_buff.enqueue(packet)
			self.transfer_next_packet()
		else:
			raise ValueError ('Packet received by Link %s \
				from unknown Node %s' % (self.ID, sender_id) )
		
		if (not notDroppedFlag):	
			print "\nDEBUG: (in link) packets dropped : %s, packet %d\n" % (self.get_id(), packet.get_ID())
			if constants.MEASUREMENT_ENABLE: 
				print constants.MEASURE_PACKET_LOSS((packet.get_flow(),\
											packet.get_type(),\
											packet.get_ID(),\
											self.sim.get_current_time()))

	def transfer_next_packet (self):
		if (not self.packet_loading) and (self.packets_in_flight == 0):
			
			lt = self.left_buff.get_head_timestamp() \
						if self.left_buff.can_dequeue() else -1
			rt = self.right_buff.get_head_timestamp() \
						if self.right_buff.can_dequeue() else -1			
			
			if (lt >= 0) or (rt >= 0):				
				if (lt >= 0) and (rt >= 0):
					if self.switch_direction_flag:
						# there are packets on both sides,
						# and it's time to switch link direction
						self.switch_direction_flag = False
						if (self.transmission_direction == constants.LTR):
							self.transmission_direction = constants.RTL
							packet_to_transmit, queue_delay = self.right_buff.dequeue()
							packet_to_transmit.set_curr_dest(self.get_left())
						else:
							self.transmission_direction = constants.LTR
							packet_to_transmit, queue_delay = self.left_buff.dequeue()
							packet_to_transmit.set_curr_dest(self.get_right())
					elif (lt < rt):
						# Start loading Packet from head of left buffer into channel
						self.transmission_direction = constants.LTR
						packet_to_transmit, queue_delay = self.left_buff.dequeue()
						packet_to_transmit.set_curr_dest(self.get_right())
					else:
						# Start loading Packet from head of right buffer into channel
						self.transmission_direction = constants.RTL
						packet_to_transmit, queue_delay = self.right_buff.dequeue()
						packet_to_transmit.set_curr_dest(self.get_left())
				elif (lt >= 0):
					# Start loading Packet from head of left buffer into channel				
					self.transmission_direction = constants.LTR
					packet_to_transmit, queue_delay = self.left_buff.dequeue()
					packet_to_transmit.set_curr_dest(self.get_right())
				else:
					# Start loading Packet from head of right buffer into channel					
					self.transmission_direction = constants.RTL
					packet_to_transmit, queue_delay = self.right_buff.dequeue()
					packet_to_transmit.set_curr_dest(self.get_left())

				self.packet_loading = True
				completion_time = \
					self.sim.get_current_time() + self.ms_tx_delay(packet_to_transmit)
				
				self.sim.request_event(\
					Handle_Packet_Transmission(	packet_to_transmit,\
												self.get_id(),\
					 							completion_time))

				self.bidirectional_queueing_delay_memory.pop(0)
				self.bidirectional_queueing_delay_memory.append(queue_delay)
			else:
				pass

		elif (not self.packet_loading) and (self.packets_in_flight > 0):
			lt = self.left_buff.get_head_timestamp() \
				if self.left_buff.can_dequeue() else -1			
			rt = self.right_buff.get_head_timestamp() \
				if self.right_buff.can_dequeue() else -1
			self.attempt_to_transmit_in_same_direction (lt, rt)

		else:
			pass

	# lt, rt are left and right heads-of-buffer timestamps
	def attempt_to_transmit_in_same_direction (self, lt, rt):		
		# if there are any more to send in the current direction,
		# can they get there before the timestamp of the head of the
		# other buffer?
		if self.transmission_direction == constants.RTL:
			buff = self.right_buff
			curr_dest = self.get_left()
			t1 = lt
			t2 = rt
		else:
			buff = self.left_buff
			curr_dest = self.get_right()
			t1 = rt
			t2 = lt

		if t2 >= 0:	
			proposed_receive_time = self.sim.get_current_time() + \
				self.ms_total_delay(buff.see_head_packet())
			if ((t1 >= 0) and (proposed_receive_time < t1)) or (t1 < 0):	
				packet_to_transmit, queue_delay = buff.dequeue()
				packet_to_transmit.set_curr_dest(curr_dest)
				self.packet_loading = True
				completion_time = self.sim.get_current_time() + \
					self.ms_tx_delay(packet_to_transmit)
				self.sim.request_event(\
					Handle_Packet_Transmission(	packet_to_transmit,\
											self.get_id(),\
				 							completion_time))

				self.bidirectional_queueing_delay_memory.pop(0)
				self.bidirectional_queueing_delay_memory.append(queue_delay)

			else:
				self.switch_direction_flag = True
Ejemplo n.º 7
0
class Link(EventTarget):
    def __init__(self, identifier, rate, delay, buffer_size, node1, node2):
        """
        A network link.

        Args:
            identifier (str):           The name of the link.
            rate (float):               The link capacity, in Mbps.
            delay (int):                Propagation delay, in ms.
            buffer_size (int):          The buffer size, in KB.
            node1 (Node):               The first endpoint of the link.
            node2 (Node):               The second endpoint of the link.
        """
        super(Link, self).__init__()

        self.id = identifier
        self.rate = rate
        self.delay = delay
        self.buffer_size = buffer_size * 1024
        self.node1 = node1
        self.node2 = node2

        self.node1.add_link(self)
        self.node2.add_link(self)

        # This determines whether the link is in use to handle half-duplex
        self.in_use = False
        self.current_dir = None

        # The buffer of packets going towards node 1 or node 2
        self.buffer = LinkBuffer(self)

        # Bytes sent over this link
        self.bytesSent = 0.0
        # Time it has taken so far to send these bytes
        self.sendTime = 0.0
        self.packets_on_link = {
            1: [],
            2: []
        }

    def __repr__(self):
        return "Link[%s,%s--%s]" % (self.id, self.node1, self.node2)

    def static_cost(self):
        """
        Defines the static cost of sending a packet across the link

        :return: Static link cost
        :rtype: float
        """
        return self.rate

    def dynamic_cost(self):
        """
        Defines the dynamic cost of sending a packet across the link

        :return: Dynamic link cost
        :rtype: float
        """
        return self.static_cost() + self.buffer.fixedAvgBufferTime

    def fix_dynamic_cost(self, time):
        """
        Fixes the dynamic cost of the link. Should be fixed right before
        updating the dynamic routing table.

        :param time: Time to fix the dynamic cost at.
        :type time: float
        :return: Nothing
        :rtype: None
        """
        self.buffer.fix_avg_buffer_time(time)

    def reset_dynamic_cost(self, time):
        """
        Resets the metrics used for calculating dynamic cost. Should be reset
        after updating the dynamic routing table.

        :param time: Time when the reset is happening
        :type time: float
        :return: Nothing
        :rtype: None
        """
        self.buffer.reset_buffer_metrics(time)

    def transmission_delay(self, packet):
        packet_size = packet.size() * 8  # in bits
        speed = self.rate * 1e6 / 1e3    # in bits/ms
        return packet_size / float(speed)

    def get_node_by_direction(self, direction):
        if direction == LinkBuffer.NODE_1_ID:
            return self.node1
        elif direction == LinkBuffer.NODE_2_ID:
            return self.node2
        return None

    def get_direction_by_node(self, node):
        if node == self.node1:
            return 1
        elif node == self.node2:
            return 2
        return 0

    def other_node(self, ref_node):
        """
        Returns the other node that is not the given reference node

        :param ref_node: Node not to return
        :type ref_node: Node
        :return: Node that is not the given node connected by this link
        :rtype: Node
        """
        assert ref_node is self.node1 or ref_node is self.node2, \
            "Given reference node is not even connected by this link"
        return self.node1 if ref_node is self.node2 else self.node2

    def send(self, time, packet, origin, from_free=False):
        """
        Sends a packet to a destination.

        Args:
            time (int):                The time at which the packet was sent.
            packet (Packet):           The packet.
            origin (Host|Router):      The node origin of the packet.
        """
        origin_id = self.get_direction_by_node(origin)
        dst_id = 3 - origin_id
        destination = self.get_node_by_direction(dst_id)
        if self.in_use or self.packets_on_link[origin_id] != []:
            if self.current_dir is not None:
                Logger.debug(time, "Link %s in use, currently sending to node "
                                   "%d (trying to send %s)"
                             % (self.id, self.current_dir, packet))
            else:
                Logger.debug(time, "Link %s in use, currently sending to node "
                                   "%d (trying to send %s)"
                             % (self.id, origin_id, packet))
            if self.buffer.size() >= self.buffer_size:
                # Drop packet if buffer is full
                Logger.debug(time, "Buffer full; packet %s dropped." % packet)
                self.dispatch(DroppedPacketEvent(time, self.id))
                return
            self.buffer.add_to_buffer(packet, dst_id, time)
        else:
            if not from_free and self.buffer.buffers[dst_id] != []:
                # Since events are not necessarily executed in the order we
                # would expect, there may be a case where the link was free
                # (nothing on the other side and nothing currently being put
                # on) but the actual event had not yet fired.
                #
                # In such a case, the buffer will not have been popped from
                # yet, so put the packet we want to send on the buffer and
                # take the first packet instead.
                self.buffer.add_to_buffer(packet, dst_id, time)
                packet = self.buffer.pop_from_buffer(dst_id, time)
            Logger.debug(time, "Link %s free, sending packet %s to %s" % (self.id, packet, destination))
            self.in_use = True
            self.current_dir = dst_id
            transmission_delay = self.transmission_delay(packet)

            self.dispatch(PacketSentOverLinkEvent(time, packet, destination, self))

            # Link will be free to send to same spot once packet has passed
            # through fully, but not to send from the current destination until
            # the packet has completely passed.
            # Transmission delay is delay to put a packet onto the link
            self.dispatch(LinkFreeEvent(time + transmission_delay, self, dst_id, packet))
            self.dispatch(LinkFreeEvent(time + transmission_delay + self.delay, self, self.get_other_id(dst_id), packet))
            self.update_link_throughput(time, packet,
                                        time + transmission_delay + self.delay)

    def update_link_throughput(self, time, packet, time_received):
        """
        Update the link throughput

        :param time: Time when this update is occurring
        :type time: float
        :param packet: Packet we're updating the throughput with
        :type packet: Packet
        :param time_received: Time the packet was received at the other node
        :type time_received: float
        :return: Nothing
        :rtype: None
        """
        self.bytesSent += packet.size()
        self.sendTime = time_received
        assert self.sendTime != 0, "Packet should not be received at time 0."
        throughput = (8 * self.bytesSent) / (self.sendTime / 1000)  # bits/s
        Logger.debug(time, "%s throughput is %f" % (self, throughput))
        self.dispatch(LinkThroughputEvent(time, self.id, throughput))

    @classmethod
    def get_other_id(cls, dst_id):
        """
        Get the node id of the other node that is not the given destination id.

        :param dst_id: Destination ID
        :type dst_id: int
        :return: ID of the node that is not the destination ID
        :rtype:
        """
        return LinkBuffer.NODE_1_ID \
            if dst_id == LinkBuffer.NODE_2_ID else LinkBuffer.NODE_2_ID
Ejemplo n.º 8
0
class Link(EventTarget):
    def __init__(self, identifier, rate, delay, buffer_size, node1, node2):
        """
        A network link.

        Args:
            identifier (str):           The name of the link.
            rate (float):               The link capacity, in Mbps.
            delay (int):                Propagation delay, in ms.
            buffer_size (int):          The buffer size, in KB.
            node1 (Node):               The first endpoint of the link.
            node2 (Node):               The second endpoint of the link.
        """
        super(Link, self).__init__()

        self.id = identifier
        self.rate = rate
        self.delay = delay
        self.buffer_size = buffer_size * 1024
        self.node1 = node1
        self.node2 = node2

        self.node1.add_link(self)
        self.node2.add_link(self)

        # This determines whether the link is in use to handle half-duplex
        self.in_use = False
        self.current_dir = None

        # The buffer of packets going towards node 1 or node 2
        self.buffer = LinkBuffer(self)

        # Bytes sent over this link
        self.bytesSent = 0.0
        # Time it has taken so far to send these bytes
        self.sendTime = 0.0
        self.packets_on_link = {1: [], 2: []}

    def __repr__(self):
        return "Link[%s,%s--%s]" % (self.id, self.node1, self.node2)

    def static_cost(self):
        """
        Defines the static cost of sending a packet across the link

        :return: Static link cost
        :rtype: float
        """
        return self.rate

    def dynamic_cost(self):
        """
        Defines the dynamic cost of sending a packet across the link

        :return: Dynamic link cost
        :rtype: float
        """
        return self.static_cost() + self.buffer.fixedAvgBufferTime

    def fix_dynamic_cost(self, time):
        """
        Fixes the dynamic cost of the link. Should be fixed right before
        updating the dynamic routing table.

        :param time: Time to fix the dynamic cost at.
        :type time: float
        :return: Nothing
        :rtype: None
        """
        self.buffer.fix_avg_buffer_time(time)

    def reset_dynamic_cost(self, time):
        """
        Resets the metrics used for calculating dynamic cost. Should be reset
        after updating the dynamic routing table.

        :param time: Time when the reset is happening
        :type time: float
        :return: Nothing
        :rtype: None
        """
        self.buffer.reset_buffer_metrics(time)

    def transmission_delay(self, packet):
        packet_size = packet.size() * 8  # in bits
        speed = self.rate * 1e6 / 1e3  # in bits/ms
        return packet_size / float(speed)

    def get_node_by_direction(self, direction):
        if direction == LinkBuffer.NODE_1_ID:
            return self.node1
        elif direction == LinkBuffer.NODE_2_ID:
            return self.node2
        return None

    def get_direction_by_node(self, node):
        if node == self.node1:
            return 1
        elif node == self.node2:
            return 2
        return 0

    def other_node(self, ref_node):
        """
        Returns the other node that is not the given reference node

        :param ref_node: Node not to return
        :type ref_node: Node
        :return: Node that is not the given node connected by this link
        :rtype: Node
        """
        assert ref_node is self.node1 or ref_node is self.node2, \
            "Given reference node is not even connected by this link"
        return self.node1 if ref_node is self.node2 else self.node2

    def send(self, time, packet, origin, from_free=False):
        """
        Sends a packet to a destination.

        Args:
            time (int):                The time at which the packet was sent.
            packet (Packet):           The packet.
            origin (Host|Router):      The node origin of the packet.
        """
        origin_id = self.get_direction_by_node(origin)
        dst_id = 3 - origin_id
        destination = self.get_node_by_direction(dst_id)
        if self.in_use or self.packets_on_link[origin_id] != []:
            if self.current_dir is not None:
                Logger.debug(
                    time, "Link %s in use, currently sending to node "
                    "%d (trying to send %s)" %
                    (self.id, self.current_dir, packet))
            else:
                Logger.debug(
                    time, "Link %s in use, currently sending to node "
                    "%d (trying to send %s)" % (self.id, origin_id, packet))
            if self.buffer.size() >= self.buffer_size:
                # Drop packet if buffer is full
                Logger.debug(time, "Buffer full; packet %s dropped." % packet)
                self.dispatch(DroppedPacketEvent(time, self.id))
                return
            self.buffer.add_to_buffer(packet, dst_id, time)
        else:
            if not from_free and self.buffer.buffers[dst_id] != []:
                # Since events are not necessarily executed in the order we
                # would expect, there may be a case where the link was free
                # (nothing on the other side and nothing currently being put
                # on) but the actual event had not yet fired.
                #
                # In such a case, the buffer will not have been popped from
                # yet, so put the packet we want to send on the buffer and
                # take the first packet instead.
                self.buffer.add_to_buffer(packet, dst_id, time)
                packet = self.buffer.pop_from_buffer(dst_id, time)
            Logger.debug(
                time, "Link %s free, sending packet %s to %s" %
                (self.id, packet, destination))
            self.in_use = True
            self.current_dir = dst_id
            transmission_delay = self.transmission_delay(packet)

            self.dispatch(
                PacketSentOverLinkEvent(time, packet, destination, self))

            # Link will be free to send to same spot once packet has passed
            # through fully, but not to send from the current destination until
            # the packet has completely passed.
            # Transmission delay is delay to put a packet onto the link
            self.dispatch(
                LinkFreeEvent(time + transmission_delay, self, dst_id, packet))
            self.dispatch(
                LinkFreeEvent(time + transmission_delay + self.delay, self,
                              self.get_other_id(dst_id), packet))
            self.update_link_throughput(time, packet,
                                        time + transmission_delay + self.delay)

    def update_link_throughput(self, time, packet, time_received):
        """
        Update the link throughput

        :param time: Time when this update is occurring
        :type time: float
        :param packet: Packet we're updating the throughput with
        :type packet: Packet
        :param time_received: Time the packet was received at the other node
        :type time_received: float
        :return: Nothing
        :rtype: None
        """
        self.bytesSent += packet.size()
        self.sendTime = time_received
        assert self.sendTime != 0, "Packet should not be received at time 0."
        throughput = (8 * self.bytesSent) / (self.sendTime / 1000)  # bits/s
        Logger.debug(time, "%s throughput is %f" % (self, throughput))
        self.dispatch(LinkThroughputEvent(time, self.id, throughput))

    @classmethod
    def get_other_id(cls, dst_id):
        """
        Get the node id of the other node that is not the given destination id.

        :param dst_id: Destination ID
        :type dst_id: int
        :return: ID of the node that is not the destination ID
        :rtype:
        """
        return LinkBuffer.NODE_1_ID \
            if dst_id == LinkBuffer.NODE_2_ID else LinkBuffer.NODE_2_ID