mirror of
https://gitlab.com/manzerbredes/esds.git
synced 2025-06-08 06:17:40 +00:00
Init ESDS repository
This commit is contained in:
commit
c2e6aad09f
106 changed files with 2638 additions and 0 deletions
1
plugins/__init__.py
Normal file
1
plugins/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
__all__ = [ "node_plugin", "power_states" ]
|
29
plugins/node_plugin.py
Normal file
29
plugins/node_plugin.py
Normal 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)
|
||||
|
66
plugins/operating_states.py
Normal file
66
plugins/operating_states.py
Normal 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
164
plugins/power_states.py
Normal 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
71
plugins/wireless_area.py
Normal 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
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue