diff options
| author | Loic Guegan <manzerbredes@mailbox.org> | 2022-06-14 17:13:46 +0200 |
|---|---|---|
| committer | Loic Guegan <manzerbredes@mailbox.org> | 2022-06-14 17:13:46 +0200 |
| commit | 7f13c95e16a802d0706e9f5a6d5d845d7fd67631 (patch) | |
| tree | 65cb3c0faec397cb1b06894645d060514f6b242d /esds/plugins | |
| parent | b6877cb81e56c3991d0dbcf9fa579f627a4c2a29 (diff) | |
Major refactoring:
- Create pip package
- Reorganized source code
Diffstat (limited to 'esds/plugins')
| -rw-r--r-- | esds/plugins/__init__.py | 1 | ||||
| -rw-r--r-- | esds/plugins/node_plugin.py | 29 | ||||
| -rw-r--r-- | esds/plugins/operating_states.py | 66 | ||||
| -rw-r--r-- | esds/plugins/power_states.py | 164 | ||||
| -rw-r--r-- | esds/plugins/wireless_area.py | 72 |
5 files changed, 332 insertions, 0 deletions
diff --git a/esds/plugins/__init__.py b/esds/plugins/__init__.py new file mode 100644 index 0000000..abb734a --- /dev/null +++ b/esds/plugins/__init__.py @@ -0,0 +1 @@ +__all__ = [ "node_plugin", "power_states" ] diff --git a/esds/plugins/node_plugin.py b/esds/plugins/node_plugin.py new file mode 100644 index 0000000..325ff8a --- /dev/null +++ b/esds/plugins/node_plugin.py @@ -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) + diff --git a/esds/plugins/operating_states.py b/esds/plugins/operating_states.py new file mode 100644 index 0000000..400aa1b --- /dev/null +++ b/esds/plugins/operating_states.py @@ -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) + + diff --git a/esds/plugins/power_states.py b/esds/plugins/power_states.py new file mode 100644 index 0000000..be3d085 --- /dev/null +++ b/esds/plugins/power_states.py @@ -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.read("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.read("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.read("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.read("clock") + + def on_send_return(self,interface,data,datasize,dst,code): + clock=self.api.read("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.read("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") diff --git a/esds/plugins/wireless_area.py b/esds/plugins/wireless_area.py new file mode 100644 index 0000000..958d4ba --- /dev/null +++ b/esds/plugins/wireless_area.py @@ -0,0 +1,72 @@ +import math +import numpy as np + +# This plugin is outdated +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 + |
