from collections import Iterable from simpy import Store from .buffer import Buffer from .core import TickProcess def NodeFactory(router, **kwargs): def factory(network, nid): return Node(network, nid, router=router, **kwargs) return factory class Node(TickProcess): '''''' def __init__(self, network, nid, buffer_size=None, tick_time=1, router=None): '''''' super().__init__(tick_time) self.env = network.env self.network = network self.id = nid self.bandwidth = float('inf') self.buffer = Buffer(self.env, capacity=buffer_size) if router is None: router = routers['direct'] self.router = router(self) self.start(self.env) self.transfer_queue = Store(self.env) def route_packet(self, packet): ''' Applies the routing heuristic to packet and queues packet for transfer if requested Returns: True - if the packet should be deleted from the buffer False - otherwise ''' # check if packet has expired if packet.expired(self.env.now): return True # ask the router what to do targets, reason, delete = self.router(packet) # check if there are targets to send to if targets is None: return False # allow for targets to be iterable or single item if not isinstance(targets, Iterable): targets = [targets] # add the packet to the transfer queue self.transfer_queue.put((packet, targets, reason)) return delete def route_packets(self): ''' Applies the routing heuristic to all packets in the buffer and queues transfers if requested. Additionally, removes packets from the buffer if the packet has exipred or if the router requests the packet to be removed. ''' packets_to_delete = [] for packet in self.buffer: delete = self.route_packet(packet) if delete: packets_to_delete.append(packet) for packet in packets_to_delete: self.buffer.remove(packet) def process(self, **kwargs): '''''' while True: packet, targets, reason = yield self.transfer_queue.get() failed = False # simulate transfer delay of packet delay = packet.size / self.bandwidth yield self.env.timeout(delay) success = [] # simulates broadcast for target in targets: # try sending packet if self.send(target, packet): success.append(target) self.router.on_send_success(target, packet) else: failed = True self.router.on_send_failure(target, packet) packet.send(self, success, reason=reason) if failed: delete = self.route_packet(packet) if delete: self.buffer.remove(packet) def send(self, target, packet): ''' Instantly sends a packet to a target if they are connected. Returns: True - if successful False - otherwise ''' if self.connected_to(target): return target.recv(packet) else: return False def recv(self, packet): ''' Tells the node to recieve a packet. Returns: True - if the node was able to recieved the packet. False - otherwise, triggered by buffer being full. ''' if packet.destination == self: packet.recv() return True else: return self.buffer.add(packet) @property def community(self): return self.network.community[self] def connected_to(self, other): return self.network[self][other]['state'] @property def links(self): ''' Returns a list of connected links. ''' return { met: data for met, data in self.network[self].items() if self.connected_to(met) } def __repr__(self): return 'Node(id={})'.format(self.id)