Skip to content
Snippets Groups Projects
node.py 4.24 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jarrod Pas's avatar
    Jarrod Pas committed
    from collections import Iterable
    
    
    from simpy import Store
    
    
    Jarrod Pas's avatar
    Jarrod Pas committed
    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')
    
    
    Jarrod Pas's avatar
    Jarrod Pas committed
            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)
    
    Jarrod Pas's avatar
    Jarrod Pas committed
                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
    
    
    Jarrod Pas's avatar
    Jarrod Pas committed
        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.
            '''
    
    Jarrod Pas's avatar
    Jarrod Pas committed
            if packet.destination == self:
                packet.recv()
    
                return True
    
    Jarrod Pas's avatar
    Jarrod Pas committed
            else:
    
                return self.buffer.add(packet)
    
    Jarrod Pas's avatar
    Jarrod Pas committed
    
        @property
        def community(self):
            return self.network.community[self]
    
    
        def connected_to(self, other):
            return self.network[self][other]['state']
    
    
    Jarrod Pas's avatar
    Jarrod Pas committed
        @property
        def links(self):
            '''
            Returns a list of connected links.
            '''
    
            return {
    
    Jarrod Pas's avatar
    Jarrod Pas committed
                met: data
                for met, data in self.network[self].items()
    
                if self.connected_to(met)
    
    Jarrod Pas's avatar
    Jarrod Pas committed
            }
    
        def __repr__(self):
            return 'Node(id={})'.format(self.id)