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 __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 __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 __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: [] }
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
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
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
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