Newer
Older
from itertools import combinations
import random
import networkx as nx
from .routers import types as routers
from .traces import types as traces
class Network:
''''''
def __init__(self, env,
packets=None,
node_factory=None,
community=None,
trace=None):
''''''
self.env = env
# contact trace
if trace is None:
trace = traces['random']()
self.trace = trace
# community detection
self.community = community
if community is not None:
packets = {}
self.packets = PacketGenerator(**packets)
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# create node network
if node_factory is None:
node_factory = NodeFactory(tick_rate=1, router=routers['direct'])
self.nodes = [
node_factory(env, self, nid)
for nid in range(self.trace.nodes)
]
self.links = [
(a, b)
for a, b in combinations(self.nodes, 2)
]
# set up networkx graph
self.graph = nx.Graph()
self.graph.add_nodes_from(self.nodes)
self.graph.add_edges_from([
(a, b, { 'state': False })
for a, b in self.links
])
def set_link(self, a, b, state):
if isinstance(a, int):
a = self.nodes[a]
if isinstance(b, int):
b = self.nodes[b]
edge = self[a][b]
if edge['state'] == state:
return
if state is None:
state = not edge['state']
edge['state'] = state
if self.community:
self.community.set_link(a, b, state, self.env.now)
def toggle_link(self, a, b):
self.set_link(a, b, None)
def send_link(self, a, b, packet):
''''''
# TODO: transfer delay
b.recv(packet)
else:
raise Exception('Nodes {} and {} not connected'.format(a, b))
def __getitem__(self, node):
''''''
return self.graph[node]
def NodeFactory(router, **kwargs):
nid = -1
def factory(env, network, nid):
nid += 1
return Node(env, network, nid, router=router, **kwargs)
return factory
self.env = 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']
def route_packets(self):
packets_to_delete = []
for packet in self.buffer:
if self.router(self, packet, self.router_state):
packets_to_delete.append(packet)
for packet in packets_to_delete:
self.buffer.remove(packet)
def process(self, **kwargs):
''''''
while True:
self.buffer.clean()
self.route_packets()
yield self.tick()
def send(self, to, packet, reason=None):
self.network.send_link(self, to, packet)
def recv(self, packet):
if packet.destination == self:
packet.recv()
else:
self.buffer.add(packet)
'''
def __eq__(self, other):
return self.id == other.id
'''
@property
def community(self):
return self.network.community[self]
@property
def links(self):
'''
Returns a list of connected links.
'''
links = {
met: data
for met, data in self.network[self].items()
if data['state']
}
return links
def __repr__(self):
return 'Node(id={})'.format(self.id)
class Buffer:
def __init__(self, env, capacity=0):
self.env = env
self.capacity = float('inf')
else:
self.capacity = capacity
self.buffer = OrderedDict()
self.used = 0
def clean(self):
packets_to_drop = []
for packet in self:
if packet.ttl < self.env.now:
packets_to_drop.append(packet)
for packet in packets_to_drop:
self.remove(packet)
def add(self, packet):
if self.used < self.capacity:
self.used += 1
self.buffer[packet] = None
else:
raise Exception('buffer full')
def remove(self, packet):
self.used -= 1
del self.buffer[packet]
def __contains__(self, packet):
return packet in self.buffer
def __iter__(self):
return iter(self.buffer)
def __len__(self):
return len(self.buffer)
class PacketGenerator(TickProcess):
def __init__(self, ticks_per_packet=1, start_delay=0, time_to_live=60):
super().__init__(ticks_per_packet)
if time_to_live is None:
raise ValueError('time_to_live must be specified')
self.start_delay = start_delay
self.time_to_live = time_to_live
self.packets = []
def process(self, **kwargs):
network = kwargs['network']
source, dest = random.choice(network.links)
packet = Packet(packet_id, source, dest,
self.env.now + self.time_to_live, None)
self.packets.append(packet)
source.recv(packet)
yield self.env.timeout(self.start_delay)
print('starting packets')
if False:
for i in range(84):
create(packet_id)
packet_id += 1
return
@property
def stats(self):
stats = {}
for packet in self.packets:
for stat, value in packet.stats.items():
if stat not in stats:
stats[stat] = value
else:
stats[stat] += value
stats['packets'] = len(self.packets)
stats['recieved'] = len([p for p in self.packets if p.recieved])
stats['delivery_ratio'] = stats['recieved'] / stats['packets']
stats['duplicates'] = sum([p.duplicates for p in self.packets])
stats['transmissions'] = sum([p.sent for p in self.packets])
stats['delivery_cost'] = stats['transmissions'] / stats['recieved']
ret = ''
for stat, value in sorted(self.stats.items()):
ret += f'{stat}: {value}\n'
for packet in self.packets:
ret += '\n {}: {}'.format(packet, packet.path)
class Packet:
def __init__(self, id, source, destination, ttl, payload):
self.id = id
self.source = source
self.destination = destination
self.ttl = ttl
self.payload = payload
def send(self, a, b, reason=None):
if reason is None:
self.path.append((a.id, b.id))
else:
self.path.append((a.id, b.id, reason))
self.id,
self.source,
self.destination
)