Init ESDS repository

This commit is contained in:
Loic Guegan 2022-06-09 21:48:32 +02:00
commit c2e6aad09f
106 changed files with 2638 additions and 0 deletions

1
plugins/__init__.py Normal file
View file

@ -0,0 +1 @@
__all__ = [ "node_plugin", "power_states" ]

29
plugins/node_plugin.py Normal file
View file

@ -0,0 +1,29 @@
class NodePlugin:
"""
Node plugins get register to the node API get notified when events occurs.
The call and return suffixes are used for methods that are called at the beginning
and the end, respectively, of API calls triggered by the node source code.
Changing this API could brake most of the node plugins.
"""
def __init__(self,plugin_name,api):
self.api=api
self.plugin_name=plugin_name
api.plugin_register(self)
def on_send_call(self,interface,data,datasize,dst):
pass
def on_send_return(self,interface,data,datasize,dst,code):
pass
def on_receive_return(self,interface,data,start_at,end_at):
pass
def on_terminated(self):
pass
def log(self,msg):
self.api.log(self.plugin_name+"(NP) "+msg)

View file

@ -0,0 +1,66 @@
#!/usr/bin/env python
from plugins.node_plugin import *
######################
# _ ____ ___ #
# / \ | _ \_ _| #
# / _ \ | |_) | | #
# / ___ \| __/| | #
# /_/ \_\_| |___| #
# #
######################
# import plugins.operating_states as op
# # Load the directional transition graph from graph.txt starting at the "vertex1" state
# opstate=op.OperatingStates(api,"graph.txt","vertex1")
# Format of the graph.txt file consists in one edge per line
# that consists on the source vertex and destination vertex sperated by a space
# As an example:
# vertex1 vertex2
# vertex1 vertex3
# vertex3 vertex2
# vertex2 vertex1
#
# opstate.register_callback(boom)
# # On each state transition boom will be called as boom(src_state,dst_state)
# # This way the boom callback can contains power_state transitions for examples
# opstate.goto("vertex2") # works
# opstate.goto("vertex3") # wont work
# opstate.goto("vertex1") # work since we are on vertex2
class OperatingStates(NodePlugin):
"""
OperatingStates plugin
"""
def __init__(self,api, state_file, initial_state):
self.transitions=list()
self.callbacks=list()
self.state=initial_state
with open(state_file) as fp:
for i, line in enumerate(fp):
self.transitions.append(line)
super().__init__("OperatingStates",api)
def goto(self,state):
if (self.state+" "+state) in self.transitions:
old_state=self.state
self.state=state
for c in self.callbacks:
c(old_state,state)
else:
self.log("Invalid transition "+self.state+" => "+state)
def get_state(self):
return(self.state)
def register_callback(self,callback):
"""
The callback will be called on each state transition
Callback takes two arguments which are:
- The source state
- The destination state
"""
self.callbacks.append(callback)

164
plugins/power_states.py Normal file
View file

@ -0,0 +1,164 @@
#!/usr/bin/env python
from plugins.node_plugin import *
# PowerStates allows you to measure the energy consumption of a
# node that go through several power states during the simulation
# Two version of Powerstates is provided by mean of two classes:
# - Powerstates: Allow you to set power to any user's defined values
# - PowerstatesFromFile: Allow you to set power from states defined in a file
######################
# _ ____ ___ #
# / \ | _ \_ _| #
# / _ \ | |_) | | #
# / ___ \| __/| | #
# /_/ \_\_| |___| #
# #
######################
# #Regarding PowerStates:
# import Powerstates as ps
# pstates=ps.PowerStates(<node>,<power_init>)
# pstates.set_power(<power>) # Switch the power consumption to <power>
# pstates.report_energy() # Display the current node energy consumption up to the current simulated time
# pstates.report_power_changes() # Display all the power changes up to the current simulated time
# #Regarding PowerStatesFromFile:
# #Format of <file> is one <entry> per line that follow this format <state-0>:<state-1>:...:<state-n>
# #Each line can corresponds to one node
# import Powerstates as ps
# pstates=ps.PowerStatesFromFile(<node>,<file>,<entry-line>) # Create a power states on node <node> using line <entry-line> of file <file>
# pstates.set_state(<id>) # Switch to the <id> power states
# pstates.report_energy() # Display the current node energy consumption up to the current simulated time
# pstates.report_power_changes() # Display all the power changes up to the current simulated time
# pstates.report_state_changes() # Display all the states changes up to the current simulated time
class PowerStates(NodePlugin):
"""
PowerStates model the energy consumed by the various changes of power consumption of a node over time.
"""
def __init__(self,node,power_init):
self.node=node
self.clock=self.node.clock()
self.energy=0
self.power=power_init
self.power_changes=dict()
self.set_power(power_init)
super().__init__("Powerstates",api)
def set_power(self,power_watt):
cur_clock=self.node.clock()
self.energy+=self.power*(cur_clock-self.clock)
self.clock=cur_clock
if self.power != power_watt:
self.power_changes[cur_clock]=power_watt
self.power=power_watt
return cur_clock
def report_energy(self):
self.set_power(self.power)
self.node.log("[PowerStates Plugin] Consumed "+str(self.energy) +"J")
def report_power_changes(self):
self.set_power(self.power)
for key in self.power_changes.keys():
self.node.log("[PowerStates Plugin] At t="+str(key)+" power is "+str(self.power_changes[key])+"W")
class PowerStatesFromFile(PowerStates):
"""
A version of Powerstates that load the power values from a file.
"""
def __init__(self,node,state_file,entry_line=1):
self.node=node
self.state_changes=dict()
self.states=[]
self.state=0
with open(state_file) as fp:
for i, line in enumerate(fp):
if i+1 == entry_line:
self.states=line.split(":")
self.states=[float(i) for i in self.states]
assert len(self.states) > 0
super().__init__(node,self.states[0])
self.set_state(0)
def set_state(self,state_id):
assert state_id < len(self.states)
clock=super().set_power(self.states[state_id])
if self.state != state_id:
self.state_changes[clock]=state_id
self.state=state_id
def report_state_changes(self):
self.set_state(self.state)
for key in self.state_changes.keys():
self.node.log("[PowerStates Plugin] At t="+str(key)+" state is "+str(self.state_changes[key]))
class PowerStatesComms(NodePlugin):
"""
Monitor the energy consumed by the network interfaces by mean of power states.
Note that for finer grained predictions, bytes and packet power consumption must be accounted.
Which is not the case with these power states.
"""
def __init__(self,api):
super().__init__("PowerStatesComms",api)
self.energy_dynamic=0.0 # Store the dynamic part of the energy consumption
self.power=dict() # Store the power states
self.tx_clock=0 # Dynamic clock (store the time at which a the last tx starts
self.idle_clock=api.clock() # Store the start time (to compute the idle part of the energy consumption)
def on_receive_return(self,interface,data,start_at,end_at):
duration=float(end_at)-float(start_at)
self.energy_dynamic+=self.power[interface]["rx"]*duration
def on_send_call(self,interface,data,datasize,dst):
self.tx_clock=self.api.clock()
def on_send_return(self,interface,data,datasize,dst,code):
clock=self.api.clock()
duration=(clock-float(self.tx_clock))
self.energy_dynamic+=self.power[interface]["tx"]*duration
self.tx_clock=clock # Any value could be use here
def set_power(self,interface,idle,tx,rx):
self.power[interface]=dict()
self.power[interface]["idle"]=idle
self.power[interface]["rx"]=rx
self.power[interface]["tx"]=tx
def get_idle(self):
clock=self.api.clock()
idle=0
for interface in self.power.keys():
idle+=(clock-self.idle_clock)*self.power[interface]["idle"]
return idle
def get_receive_queue_energy(self,interface):
"""
Not that call to on_receive_return may not have happened yet (or never).
Thus we should manually compute the energy consumption stored in each queues of the node.
"""
energy=0
# For each interface we should check if there is received data that has not been consumed
for data in list(self.api["interfaces"][interface].queue):
start_at=float(data[1])
end_at=float(data[2])
energy+=(end_at-start_at)*self.power[interface]["rx"]
return energy
def get_energy(self):
queue_energy=0
for interface in self.power.keys():
queue_energy+=self.get_receive_queue_energy(interface)
return self.get_idle()+self.energy_dynamic+queue_energy
def report_energy(self):
self.log("Communications consumed "+str(round(self.get_energy(),2))+"J")

71
plugins/wireless_area.py Normal file
View file

@ -0,0 +1,71 @@
import math
import numpy as np
class WirelessArea:
def __init__(self):
self.nodes=list()
def dump_nodes(self):
i=0
for node in self.nodes:
x,y,z,com_range=node
print("Node {} at ({},{},{}) with a communication range of {}m".format(i,x,y,z,com_range))
i+=1
def dump_infos(self):
print("Number of nodes {}".format(len(self.nodes)))
adjacency=self.generate_adjacency_matrix(fill_diagonal=False)
print("Nodes average degree is {}".format(np.mean(np.sum(adjacency,axis=0))))
x = [node[0] for node in self.nodes]
y = [node[1] for node in self.nodes]
z = [node[2] for node in self.nodes]
com_range = [node[3] for node in self.nodes]
print("Nodes locations ranges: x in [{},{}] y in [{},{}] z in [{},{}]".format(min(x),max(x),min(y),max(y),min(z),max(z)))
print("Node communication ranges in [{},{}]".format(min(com_range),max(com_range)))
def add_node(self,x,y,z,com_range):
self.nodes.append((x,y,z,com_range))
def get_neighbours(self,node_id):
node=self.nodes[node_id]
neighbours=list()
for i in range(0,len(self.nodes)):
if i != node_id:
neighbour=self.nodes[i]
if math.dist(node[0:3],neighbour[0:3]) <= node[3]:
neighbours.append(i)
return neighbours
def generate_dot(self,filepath):
is_strict=False
com_range=self.nodes[0][3]
for node in self.nodes:
if node[3] != com_range:
is_strict=True
break
with open(filepath, "w") as f:
if is_strict:
f.write("digraph G {\n")
else:
f.write("strict graph G {\n")
for i in range(0,len(self.nodes)):
neighbours=self.get_neighbours(i)
for n in neighbours:
if is_strict:
f.write("{}->{}\n".format(i,n))
else:
f.write("{}--{}\n".format(i,n))
f.write("}")
def generate_adjacency_matrix(self,fill_diagonal=True):
matrix=np.full((len(self.nodes),len(self.nodes)),0)
if fill_diagonal:
np.fill_diagonal(matrix,1) # Required by ESDS
for i in range(0,len(self.nodes)):
neighbours=self.get_neighbours(i)
for n in neighbours:
matrix[i,n]=1
return matrix