Skip to content
Snippets Groups Projects
node.py 4.24 KiB
Newer Older
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)