diff --git a/pons/__init__.py b/pons/__init__.py index bae4bdb..4d58227 100644 --- a/pons/__init__.py +++ b/pons/__init__.py @@ -2,6 +2,7 @@ from .mobility import generate_randomwaypoint_movement, OneMovement, OneMovementManager from .node import generate_nodes, Node, NetworkSettings, Message, BROADCAST_ADDR from .routing import Router, EpidemicRouter +from .energy import EnergyModel, DefaultEnergyModel, ESP32Wifi import random diff --git a/pons/energy.py b/pons/energy.py new file mode 100644 index 0000000..932f4d0 --- /dev/null +++ b/pons/energy.py @@ -0,0 +1,45 @@ +class EnergyModel: + """base class for energy models""" + def __init__(self, max_energy: float, idle: float, receive: float, forward: float, name: str): + """ + @param max_energy: max energy of the model + @param idle: amount of energy the model consumes in idle + @param receive: amount of energy the model consumes upon message receival + @param forward: amount of energy the model consumes upon forward of message + """ + self.max_energy: float = max_energy + self._idle: float = idle + self._receive: float = receive + self._forward: float = forward + self._name: str = name + self.energy: float = max_energy + + def __str__(self) -> str: + return self._name + + def __repr__(self) -> str: + return str(self) + + def on_receive(self): + """handling message receive""" + self.energy -= self._receive + + def on_forward(self): + """handling message forward""" + self.energy -= self._forward + + def on_idle(self, interval: float = 1.0): + """handling idle""" + self.energy -= self._idle * interval + + +class DefaultEnergyModel(EnergyModel): + """unlimited energy model - equivalent to no energy model""" + def __init__(self): + super().__init__(100, 0, 0, 0, "DefaultEnergyModel") + + +class ESP32Wifi(EnergyModel): + """energy model simulating the esp32 using wifi""" + def __init__(self, initial_energy: int = 100000): + super().__init__(initial_energy, 90, 100, 190, "ESP32 Wifi") diff --git a/pons/node.py b/pons/node.py index c668f20..243b9a5 100644 --- a/pons/node.py +++ b/pons/node.py @@ -1,12 +1,11 @@ from __future__ import annotations -import math -from copy import deepcopy -import pons import random - +from copy import deepcopy from typing import TYPE_CHECKING +import pons + if TYPE_CHECKING: import pons.routing @@ -71,7 +70,7 @@ class Node(object): """A The ONE movement scenario. """ - def __init__(self, node_id: int, net: List[NetworkSettings] = None, router: pons.routing.Router = None): + def __init__(self, node_id: int, net: List[NetworkSettings] = None, router: pons.routing.Router = None, energy_model: pons.EnergyModel = None): self.id = node_id self.x = 0.0 self.y = 0.0 @@ -80,6 +79,9 @@ def __init__(self, node_id: int, net: List[NetworkSettings] = None, router: pons for n in net: self.net[n.name] = deepcopy(n) self.router = router + self.energy_model = energy_model + if self.energy_model is None: + self.energy_model = pons.DefaultEnergyModel() self.neighbors = {} for net in self.net.values(): self.neighbors[net.name] = [] @@ -112,6 +114,7 @@ def send(self, netsim: pons.NetSim, to_nid: int, msg: Message): for nid in self.neighbors[net.name]: receiver = netsim.nodes[nid] netsim.net_stats["tx"] += 1 + self.energy_model.on_forward() pons.delayed_execution(netsim.env, tx_time, receiver.on_recv(netsim, self.id, msg)) else: @@ -119,6 +122,7 @@ def send(self, netsim: pons.NetSim, to_nid: int, msg: Message): # self.log("sending msg %s to %d" % (msg, to_nid)) receiver = netsim.nodes[to_nid] netsim.net_stats["tx"] += 1 + self.energy_model.on_forward() pons.delayed_execution(netsim.env, tx_time, receiver.on_recv(netsim, self.id, msg)) else: @@ -136,6 +140,7 @@ def on_recv(self, netsim: pons.NetSim, from_nid: int, msg: Message): if from_nid in self.neighbors[net.name]: # print("Node %d received msg %s from %d" % (to_nid, msg, from_nid)) netsim.net_stats["rx"] += 1 + self.energy_model.on_receive() if self.router is not None: if msg.id == "HELLO": self.router.on_scan_received(deepcopy(msg), from_nid) @@ -147,11 +152,33 @@ def on_recv(self, netsim: pons.NetSim, from_nid: int, msg: Message): netsim.net_stats["drop"] += 1 -def generate_nodes(num_nodes: int, offset: int = 0, net: List[NetworkSettings] = None, router: pons.routing.Router = None): +def generate_nodes( + num_nodes: int, + offset: int = 0, + net: List[NetworkSettings] = None, + router: pons.routing.Router = None, + energy_model: pons.EnergyModel or List[pons.EnergyModel] = None +): + """ + generates nodes + @param num_nodes: number of nodes + @param offset: offset for the node ids + @param net: a list of network settings + @param router: the router to use (optional) + @param energy_model: the energy model(s) (optional) - + Either one energy model or a list of models matching num_nodes. + If only one is given, it is used for every nodes. + """ nodes = [] - if net == None: + if net is None: net = [] + if energy_model is None: + energy_model = pons.DefaultEnergyModel() + if isinstance(energy_model, pons.EnergyModel): + energy_model = [deepcopy(energy_model) for i in range(0, num_nodes)] + if len(energy_model) != num_nodes: + raise ValueError("size of energy_model should match num_nodes") for i in range(num_nodes): nodes.append(Node(i + offset, net=deepcopy(net), - router=deepcopy(router))) + router=deepcopy(router), energy_model=energy_model[i])) return nodes diff --git a/pons/routing/directdelivery.py b/pons/routing/directdelivery.py index f823960..5b6f532 100644 --- a/pons/routing/directdelivery.py +++ b/pons/routing/directdelivery.py @@ -1,4 +1,5 @@ from .router import Router +import pons class DirectDeliveryRouter(Router): @@ -28,21 +29,20 @@ def on_peer_discovered(self, peer_id): for msg in self.store: self.forward(msg) - def on_msg_received(self, msg, remote_id): - # self.log("msg received: %s from %d" % (msg, remote_id)) + def on_msg_received(self, msg: pons.Message, remote_id: int): self.netsim.routing_stats['relayed'] += 1 if not self.is_msg_known(msg): self.remember(remote_id, msg) msg.hops += 1 self.store_add(msg) if msg.dst == self.my_id: - # self.log("msg arrived", self.my_id) + # print("msg arrived", self.my_id) self.netsim.routing_stats['delivered'] += 1 self.netsim.routing_stats['hops'] += msg.hops self.netsim.routing_stats['latency'] += self.env.now - msg.created else: - # self.log("msg not arrived yet", self.my_id) + # print("msg not arrived yet", self.my_id) self.forward(msg) else: - # self.log("msg already known", self.history) + # print("msg already known", self.history) self.netsim.routing_stats['dups'] += 1 diff --git a/pons/routing/epidemic.py b/pons/routing/epidemic.py index 2aa8079..6dab455 100644 --- a/pons/routing/epidemic.py +++ b/pons/routing/epidemic.py @@ -1,4 +1,5 @@ from .router import Router +import pons class EpidemicRouter(Router): @@ -41,21 +42,20 @@ def on_peer_discovered(self, peer_id): else: self.forward(msg) - def on_msg_received(self, msg, remote_id): - # self.log("msg received: %s from %d" % (msg, remote_id)) + def on_msg_received(self, msg: pons.Message, remote_id: int): self.netsim.routing_stats['relayed'] += 1 if not self.is_msg_known(msg): self.remember(remote_id, msg) msg.hops += 1 self.store_add(msg) if msg.dst == self.my_id: - # self.log("msg arrived %s" % msg) + # print("msg arrived", self.my_id) self.netsim.routing_stats['delivered'] += 1 self.netsim.routing_stats['hops'] += msg.hops self.netsim.routing_stats['latency'] += self.env.now - msg.created else: - # self.log("msg not arrived yet", self.my_id) + # print("msg not arrived yet", self.my_id) self.forward(msg) else: - # self.log("msg already known", self.history) - self.netsim.routing_stats['dups'] += 1 + # print("msg already known", self.history) + self.netsim.routing_stats['dups'] += 1 \ No newline at end of file diff --git a/pons/routing/firstcontact.py b/pons/routing/firstcontact.py index c2c9122..4df4d51 100644 --- a/pons/routing/firstcontact.py +++ b/pons/routing/firstcontact.py @@ -1,4 +1,5 @@ from .router import Router +import pons class FirstContactRouter(Router): @@ -41,21 +42,20 @@ def on_peer_discovered(self, peer_id): for msg in self.store: self.forward(msg) - def on_msg_received(self, msg, remote_id): - # self.log("msg received: %s from %d" % (msg, remote_id)) + def on_msg_received(self, msg: pons.Message, remote_id: int): self.netsim.routing_stats['relayed'] += 1 if not self.is_msg_known(msg): self.remember(remote_id, msg) msg.hops += 1 self.store_add(msg) if msg.dst == self.my_id: - # self.log("msg arrived", self.my_id) + # print("msg arrived", self.my_id) self.netsim.routing_stats['delivered'] += 1 self.netsim.routing_stats['hops'] += msg.hops self.netsim.routing_stats['latency'] += self.env.now - msg.created else: - # self.log("msg not arrived yet", self.my_id) + # print("msg not arrived yet", self.my_id) self.forward(msg) else: - # self.log("msg already known", self.history) - self.netsim.routing_stats['dups'] += 1 + # print("msg already known", self.history) + self.netsim.routing_stats['dups'] += 1 \ No newline at end of file diff --git a/pons/routing/router.py b/pons/routing/router.py index a5b208f..f81413c 100644 --- a/pons/routing/router.py +++ b/pons/routing/router.py @@ -1,5 +1,3 @@ - -from copy import copy import pons HELLO_MSG_SIZE = 42 @@ -68,7 +66,8 @@ def start(self, netsim: pons.NetSim, my_id: int): self.env.process(self.scan()) def scan(self): - while True: + node = self.netsim.nodes[self.my_id] + while node.energy_model.energy > 0: # print("[%s] scanning..." % self.my_id) # assume some kind of peer discovery mechanism @@ -88,6 +87,8 @@ def scan(self): yield self.env.timeout(self.scan_interval) def on_scan_received(self, msg: pons.Message, remote_id: int): + if self.netsim.nodes[self.my_id].energy_model.energy <= 0: + return # self.log("[%s] scan received: %s from %d" % # (self.my_id, msg, remote_id)) if msg.id == "HELLO" and remote_id not in self.peers: @@ -95,7 +96,7 @@ def on_scan_received(self, msg: pons.Message, remote_id: int): # self.log("NEW PEER: %d" % remote_id) self.on_peer_discovered(remote_id) # elif remote_id in self.peers: - # self.log("DUP PEER: %d" % remote_id) + # self.log("DUP PEER: %d" % remote_id) def on_peer_discovered(self, peer_id): self.log("peer discovered: %d" % peer_id) diff --git a/pons/routing/sprayandwait.py b/pons/routing/sprayandwait.py index 1b24332..6c79d8e 100644 --- a/pons/routing/sprayandwait.py +++ b/pons/routing/sprayandwait.py @@ -2,6 +2,7 @@ import copy import math +import pons class SprayAndWaitRouter(Router): @@ -58,8 +59,7 @@ def on_peer_discovered(self, peer_id): for msg in self.store: self.forward(msg) - def on_msg_received(self, msg, remote_id): - # self.log("msg received: %s from %d" % (msg, remote_id)) + def on_msg_received(self, msg: pons.Message, remote_id: int): self.netsim.routing_stats['relayed'] += 1 if not self.is_msg_known(msg): self.remember(remote_id, msg) @@ -75,4 +75,4 @@ def on_msg_received(self, msg, remote_id): self.forward(msg) else: # print("msg already known", self.history) - self.netsim.routing_stats['dups'] += 1 + self.netsim.routing_stats['dups'] += 1 \ No newline at end of file diff --git a/pons/simulation.py b/pons/simulation.py index 4a779d4..4fe3cf4 100644 --- a/pons/simulation.py +++ b/pons/simulation.py @@ -1,6 +1,7 @@ import simpy import time import pons +from multiprocessing import Process class NetSim(object): @@ -45,6 +46,17 @@ def start_peers_logger(self, interval=1.0): for node in self.nodes: print(node.neighbors) + def start_energy_manager(self, interval=1.0): + """start an energy manager""" + while True: + yield self.env.timeout(interval) + for node in self.nodes: + node.energy_model.on_idle(interval) + + def should_start_energy_manager(self) -> bool: + """check if any node has another energy model than the default one""" + return not all(isinstance(node, pons.DefaultEnergyModel) for node in self.nodes) + def setup(self): print("initialize simulation") @@ -58,6 +70,9 @@ def setup(self): if self.config is not None and self.config.get("peers_logger", True): self.env.process(self.start_peers_logger()) + if self.should_start_energy_manager(): + self.env.process(self.start_energy_manager()) + for n in self.nodes: n.start(self) diff --git a/sim.py b/sim.py index b99dc0f..2c40fd1 100644 --- a/sim.py +++ b/sim.py @@ -25,8 +25,7 @@ net = pons.NetworkSettings("WIFI_50m", range=NET_RANGE) epidemic = pons.routing.EpidemicRouter(capacity=CAPACITY) -nodes = pons.generate_nodes( - NUM_NODES, net=[net], router=epidemic) +nodes = pons.generate_nodes(NUM_NODES, net=[net], router=epidemic) config = {"movement_logger": False, "peers_logger": False} msggenconfig = {"type": "single", "interval": 30, "src": ( diff --git a/sim_with_energy.py b/sim_with_energy.py new file mode 100644 index 0000000..a7aecc8 --- /dev/null +++ b/sim_with_energy.py @@ -0,0 +1,52 @@ +import random +import json + +# import cProfile + +import pons +import pons.routing + +RANDOM_SEED = 42 +# SIM_TIME = 3600*24*7 +SIM_TIME = 3600*24 +NET_RANGE = 50 +NUM_NODES = 3 +WORLD_SIZE = (1000, 1000) +CAPACITY = 10000 +# size of ENERGY_MODELS should match NUM_NODES +ENERGY_MODELS = [ + pons.ESP32Wifi(1000000), + pons.DefaultEnergyModel(), + pons.ESP32Wifi(5000000), +] +# CAPACITY = 0 + +# Setup and start the simulation +print('Python Opportunistic Network Simulator') +random.seed(RANDOM_SEED) + +moves = pons.generate_randomwaypoint_movement( + SIM_TIME, NUM_NODES, WORLD_SIZE[0], WORLD_SIZE[1], max_pause=60.0) + +net = pons.NetworkSettings("WIFI_50m", range=NET_RANGE) +epidemic = pons.routing.EpidemicRouter(capacity=CAPACITY) + + +nodes = pons.generate_nodes(NUM_NODES, net=[net], router=epidemic, energy_model=ENERGY_MODELS) +config = {"movement_logger": False, "peers_logger": False} + +msggenconfig = {"type": "single", "interval": 30, "src": ( + 0, NUM_NODES), "dst": (0, NUM_NODES), "size": 100, "id": "M", "ttl": 3600} + +netsim = pons.NetSim(SIM_TIME, WORLD_SIZE, nodes, moves, + config=config, msggens=[msggenconfig]) + +netsim.setup() + +# m = pons.Message("MSG1", 1, 2, 100, 0) +# pons.delayed_execution(netsim.env, 0, nodes[0].router.add(m)) +# cProfile.run("netsim.run()") +netsim.run() + +print(json.dumps(netsim.net_stats, indent=4)) +print(json.dumps(netsim.routing_stats, indent=4))