Skip to content
Snippets Groups Projects
Commit 506911d8 authored by Jarrod Pas's avatar Jarrod Pas
Browse files

Refactors routers to callable objects

parent 3c446d4e
No related branches found
No related tags found
1 merge request!3Version 0.2
from collections import OrderedDict
from collections import defaultdict, Iterable, OrderedDict
from itertools import combinations
import random
......@@ -40,7 +40,7 @@ class Network:
if node_factory is None:
node_factory = NodeFactory(tick_rate=1, router=routers['direct'])
self.nodes = [
node_factory(env, self, nid)
node_factory(self, nid)
for nid in range(self.trace.nodes)
]
self.links = [
......@@ -91,37 +91,46 @@ class Network:
def NodeFactory(router, **kwargs):
def factory(env, network, nid):
return Node(env, network, nid, router=router, **kwargs)
def factory(network, nid):
return Node(network, nid, router=router, **kwargs)
return factory
class Node(TickProcess):
''''''
def __init__(self, env, network, nid,
def __init__(self, network, nid,
buffer_size=None, tick_time=1, router=None):
''''''
super().__init__(tick_time)
self.env = env
self.env = network.env
self.network = network
self.id = nid
self.buffer = Buffer(self.env, capacity=buffer_size)
# bind router as a class method
if router is None:
router = routers['direct']
self.router = router #router.__get__(self, Node)
self.router_state = {}
self.router = router(self)
self.start(env)
self.start(self.env)
def route_packets(self):
packets_to_delete = []
for packet in self.buffer:
if self.router(self, packet, self.router_state):
targets, reason, delete = self.router(packet)
# check if there are targets to send to
if targets is None:
continue
# allow for targets to be iterable or single item
if not isinstance(targets, Iterable):
targets = [targets]
self.send(targets, packet, reason)
if delete:
packets_to_delete.append(packet)
for packet in packets_to_delete:
......@@ -134,10 +143,11 @@ class Node(TickProcess):
self.route_packets()
yield self.tick()
def send(self, to, packet, reason=None):
def send(self, targets, packet, reason):
# TODO: transfer delay
packet.send(self, to, reason=reason)
self.network.send_link(self, to, packet)
packet.send(self, targets, reason=reason)
for target in targets:
self.network.send_link(self, target, packet)
def recv(self, packet):
if packet.destination == self:
......@@ -269,18 +279,20 @@ class Packet:
self.path = []
self.stats = dict()
self.stats = defaultdict(int)
self.sent = 0
self.recieved = False
self.duplicates = 0
self.dropped = 0
def send(self, a, b, reason=None):
def send(self, sender, targets, reason=None):
target_ids = [ n.id for n in targets ]
if reason is None:
self.path.append((a.id, b.id))
self.path.append((sender.id, target_ids))
else:
self.path.append((a.id, b.id, reason))
self.stats[f'send_{reason}'] += 1
self.path.append((sender.id, target_ids, reason))
self.sent += 1
def recv(self):
......
from .bubble import bubble
from .direct import direct
from .epidemic import epidemic
from .flooding import flooding
from .hcbf import hcbf
from .bubble import BubbleRouter
from .direct import DirectRouter
from .epidemic import EpidemicRouter
from .flooding import FloodingRouter
from .hcbf import HCBFRouter
types = {
'bubble': bubble,
'direct': direct,
'epidemic': epidemic,
'flooding': flooding,
'hcbf': hcbf,
'bubble': BubbleRouter,
'direct': DirectRouter,
'epidemic': EpidemicRouter,
'flooding': FloodingRouter,
'hcbf': HCBFRouter,
}
from collections import namedtuple
class Router:
'''
Base Router class
-----------------
All routers are callable objects
'''
def __init__(self, node, **kwargs):
self.env = node.env
self.node = node
def __call__(self, packet):
raise NotImplementedError
def bubble(self, packet, state):
''''''
if 'bubble_lp' not in packet.stats:
packet.stats = {
f'bubble_{reason}': 0
for reason in ['direct', 'lp', 'gp']
}
from .community import CommunityRouter
class BubbleRouter(CommunityRouter):
def __call__(self, packet):
if packet.destination in self.node.links:
return packet.destination, 'direct', True
stats = packet.stats
dest = packet.destination
community = self.network.community
if self.local_community:
best, lp = self.best_lp()
if lp > self.lp():
return best, 'lp', True
def send(to, reason):
stats[f'bubble_{reason}'] += 1
self.send(to, packet, reason=reason)
elif self.not_local_community:
best, gp = self.best_gp()
if gp > self.gp():
return best, 'gp', True
if dest in self.links:
send(dest, 'direct')
return True
lp = community.get_lp
gp = community.get_gp
local_community = [
met for met in self.links if met in self.community
]
not_local_community = [
met for met in self.links if met not in self.community
]
if local_community:
max_lp = max(local_community, key=lp)
if lp(max_lp) > lp(self):
send(max_lp, 'lp')
return True
elif not_local_community:
max_gp = max(not_local_community, key=gp)
if gp(max_gp) > gp(self):
send(max_gp, 'gp')
return True
return False
return None, None, False
from pydtn.communities import LouvainCommunity
from .base import Router
class CommunityRouter(Router):
def __init__(self, node, **kwargs):
super().__init__(node, **kwargs)
self.community = self.node.network.community
@property
def local_community(self):
links = self.node.links
community = self.node.community
return [met for met in links if met in community]
@property
def not_local_community(self):
links = self.node.links
community = self.node.community
return [met for met in links if met not in community]
def best(self, nodes, key):
best = max(nodes, key=key)
return best, key(best)
def ui(self):
return self.get_ui(self.node)
def get_ui(self, x):
return self.community.get_ui(x)
def best_ui(self):
return self.best(self.local_community, self.get_ui)
def lp(self):
return self.get_lp(self.node)
def get_lp(self, x):
return self.community.get_lp(x)
def best_lp(self):
return self.best(self.local_community, self.get_lp)
def gp(self):
return self.get_gp(self.node)
def get_gp(self, x):
return self.community.get_gp(x)
def best_gp(self):
return self.best(self.not_local_community, self.get_gp)
def cbc(self, c_y):
return self.get_cbc(self.node, c_y)
def get_cbc(self, x, c_y):
return self.community.get_cbc(x.community, c_y)
def best_cbc(self, c_y):
get_cbc = lambda x: self.get_cbc(x, c_y)
return self.best(self.not_local_community, get_cbc)
def ncf(self, c_y):
return self.get_ncf(self.node, c_y)
def get_ncf(self, x, c_y):
return self.community.get_ncf(x, c_y)
def best_ncf(self, c_y):
get_ncf = lambda x: self.get_ncf(x, c_y)
return self.best(self.local_community, get_ncf)
def direct(self, packet, state):
'''
Routes a packet via direct contact to the destination.
Returns True if the packet was sent successfully, otherwise False.
'''
for met in self.links:
if packet.destination == met:
self.send(met, packet)
return False
from .base import Router
class DirectRouter(Router):
def __call__(self, packet):
if packet.destination in self.node.links:
return packet.destination, 'direct', True
return None, None, False
from collections import defaultdict
def epidemic(self, packet, state):
'''
Routes a packet if it hasn't sent the packet on a link yet.
Always returns False.
'''
if 'sent' not in state:
state['sent'] = defaultdict(dict)
sent = state['sent']
from .base import Router
for met in self.links:
if packet not in sent[met]:
sent[met][packet] = True
self.send(met, packet)
return False
class EpidemicRouter(Router):
def __init__(self, node, **kwargs):
super().__init__(node, **kwargs)
# maps a packet to a set of nodes that the packet has been sent to
self.sent = defaultdict(set)
def __call__(self, packet):
# get list of currently encountered nodes that do not have the packet
targets = [
met
for met in self.node.links
if met not in self.sent[packet]
]
# update set of nodes a packet was sent to
self.sent[packet].update(targets)
# return list of nodes to send packet to
# (targets, remove from buffer, reason)
if targets:
return targets, 'epidemic', False
else:
return None, None, False
def flooding(self, packet, state):
'''
Routes a packet via flooding, i.e. sends all packets on all links.
Always returns False.
'''
for met in self.links:
self.send(met, packet)
return False
from .base import Router
class FloodingRouter(Router):
def __call__(self, packet):
return self.node.links, 'flood', False
def hcbf(self, packet, state):
if 'hcbf_ui' not in packet.stats:
packet.stats = {
f'hcbf_{reason}': 0
for reason in ['direct', 'ui', 'lp','ui_lonely', 'lp_lonely',
'cbc', 'ncf' ]
}
stats = packet.stats
dest = packet.destination
community = self.network.community
def send(to, reason):
stats[f'hcbf_{reason}'] += 1
self.send(to, packet, reason=reason)
# case 1: direct delivery
if dest in self.links:
send(dest, 'direct')
return True
ui = community.get_ui
lp = community.get_lp
cbc = lambda n: community.get_cbc(n.community, dest.community)
ncf = lambda n: community.get_ncf(n, dest.community)
local_community = [
met for met in self.links if met in self.community
]
not_local_community = [
met for met in self.links if met not in self.community
]
if self.community is dest.community and local_community:
max_ui = max(local_community, key=ui)
if ui(max_ui) > ui(self):
send(max_ui, 'ui')
return True
elif ui(max_ui) < ui(self):
return False
# ui(max_ui) == ui(self)
max_lp = max(local_community, key=lp)
if lp(max_lp) > lp(self):
send(max_lp, 'lp')
return True
elif lp(max_lp) < lp(self):
return False
# lp(max_lp) == lp(self)
return False
if not_local_community:
max_cbc = max(not_local_community, key=cbc)
if cbc(max_cbc) > cbc(self):
send(max_cbc, 'cbc')
return True
elif cbc(max_cbc) < cbc(self):
return False
# cbc(max_cbc) == cbc(self)
if local_community:
max_ncf = max(local_community, key=ncf)
if ncf(max_ncf) > ncf(self):
send(max_ncf, 'ncf')
return True
elif ncf(max_ncf) < ncf(self):
return False
# ncf(max_ncf) == ncf(self)
max_ui = max(local_community, key=ui)
if ui(max_ui) > ui(self):
send(max_ui, 'ui_lonely')
return True
elif ui(max_ui) < ui(self):
return False
# ui(max_ui) == ui(self)
max_lp = max(local_community, key=lp)
if lp(max_lp) > lp(self):
send(max_lp, 'lp_lonely')
return True
elif lp(max_lp) < lp(self):
return False
# lp(max_lp) == lp(self)
return False
from .community import CommunityRouter
class HCBFRouter(CommunityRouter):
def __call__(self, packet):
me = self.node
dest = packet.destination
if dest in me.links:
return dest, 'direct', True
if me.community is not dest.community and self.not_local_community:
best, cbc = self.best_cbc(dest.community)
if cbc > self.cbc(dest.community):
return best, 'cbc', True
if self.local_community:
if me.community is not dest.community:
best, ncf = self.best_ncf(dest.community)
if ncf > self.ncf(dest.community):
return best, 'ncf', True
elif ncf < self.ncf(dest.community):
return None, None, False
best, ui = self.best_ui()
if ui > self.ui():
return best, 'ui', True
elif ui < self.ui():
return None, None, False
best, lp = self.best_lp()
if lp > self.lp():
return best, 'lp', True
return None, None, False
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment