diff --git a/config.yml.example b/config.yml.example
index e59a4886b..97639f8ed 100644
--- a/config.yml.example
+++ b/config.yml.example
@@ -30,6 +30,7 @@ measurement:
memory.total.cgroup.container.provider.MemoryTotalCgroupContainerProvider: 100
memory.energy.RAPL.MSR.system.provider.MemoryEnergyRaplMsrSystemProvider: 100
network.io.cgroup.container.provider.NetworkIoCgroupContainerProvider: 100
+# psu.energy.ac.system.provider.PsuEnergyAcSystemProvider: 100
admin:
# This address will get an email, when a new project was added through the frontend
diff --git a/frontend/js/stats.js b/frontend/js/stats.js
index 015e184e0..5faf35f79 100644
--- a/frontend/js/stats.js
+++ b/frontend/js/stats.js
@@ -19,6 +19,11 @@ const metrics_info = {
SI_conversion_factor: 1000,
unit_after_conversion: 'J'
},
+ psu_energy_ac_system: {
+ unit: 'mJ',
+ SI_conversion_factor: 1000,
+ unit_after_conversion: 'J'
+ },
memory_energy_rapl_msr_system: {
unit: 'mJ',
SI_conversion_factor: 1000,
@@ -153,7 +158,7 @@ const fillProjectTab = (selector, data) => {
}
const getMetrics = (stats_data, style='apex') => {
- const metrics = {cpu_utilization_containers: [], cpu_utilization_system: [], mem_total: [], network_io: {}, series: {}, atx_energy: 0, cpu_energy: 0, memory_energy: 0}
+ const metrics = {cpu_utilization_containers: [], cpu_utilization_system: [], mem_total: [], network_io: {}, series: {}, atx_energy: 0, psu_ac_energy: 0, cpu_energy: 0, memory_energy: 0}
let accumulate = 0;
@@ -184,6 +189,8 @@ const getMetrics = (stats_data, style='apex') => {
if (accumulate === 1) metrics.cpu_energy += value;
} else if (metric_name == 'atx_energy_dc_channel') {
if (accumulate === 1) metrics.atx_energy += value;
+ } else if (metric_name == 'psu_energy_ac_system') {
+ if (accumulate === 1) metrics.psu_ac_energy += value;
} else if (metric_name == 'memory_energy_rapl_msr_system') {
if (accumulate === 1) metrics.memory_energy += value;
} else if (metric_name == 'memory_total_cgroup_container') {
@@ -345,6 +352,10 @@ const createGraph = (element, data, labels, title) => {
const fillAvgContainers = (stats_data, metrics) => {
+<<<<<<< HEAD
+=======
+ const psu_ac_energy_in_mWh = ((metrics.psu_ac_energy) / 3600) * 1000;
+>>>>>>> dev
const atx_energy_in_mWh = ((metrics.atx_energy) / 3600) * 1000;
const cpu_energy_in_mWh = ((metrics.cpu_energy) / 3600) * 1000;
const memory_energy_in_mWh = ((metrics.memory_energy) / 3600) * 1000;
@@ -365,6 +376,8 @@ const fillAvgContainers = (stats_data, metrics) => {
else if(total_CO2_in_kg < 0.0001) co2_display = { value: total_CO2_in_kg*(10**6), unit: 'mg'};
else if(total_CO2_in_kg < 0.1) co2_display = { value: total_CO2_in_kg*(10**3), unit: 'g'};
+ if(psu_ac_energy_in_mWh) document.querySelector("#psu-ac-energy").innerText = psu_ac_energy_in_mWh.toFixed(2) + " mWh"
+
if(atx_energy_in_mWh) document.querySelector("#atx-energy").innerText = atx_energy_in_mWh.toFixed(2) + " mWh"
if(cpu_energy_in_mWh) document.querySelector("#cpu-energy").innerText = cpu_energy_in_mWh.toFixed(2) + " mWh"
if(cpu_energy_in_mWh) document.querySelector("#component-energy").innerText = (cpu_energy_in_mWh+memory_energy_in_mWh).toFixed(2) + " mWh"
@@ -373,6 +386,10 @@ const fillAvgContainers = (stats_data, metrics) => {
if(cpu_energy_in_mWh) document.querySelector("#component-power").innerText = ((metrics.cpu_energy+metrics.memory_energy)/stats_data.project.measurement_duration_in_s).toFixed(2) + " W"
if(atx_energy_in_mWh) document.querySelector("#atx-power").innerText = (metrics.atx_energy / stats_data.project.measurement_duration_in_s).toFixed(2) + " W"
+<<<<<<< HEAD
+=======
+ if(psu_ac_energy_in_mWh) document.querySelector("#psu-ac-power").innerText = (metrics.psu_ac_energy / stats_data.project.measurement_duration_in_s).toFixed(2) + " W"
+>>>>>>> dev
if(network_io) document.querySelector("#network-io").innerText = network_io.toFixed(2) + " MB"
diff --git a/frontend/stats.html b/frontend/stats.html
index 1706bb5e3..ac79edecf 100644
--- a/frontend/stats.html
+++ b/frontend/stats.html
@@ -252,6 +252,30 @@
compound metrics
+
+
ATX Energy
diff --git a/tools/metric_providers/psu/energy/ac/system/README.md b/tools/metric_providers/psu/energy/ac/system/README.md
new file mode 100644
index 000000000..321b1bdcb
--- /dev/null
+++ b/tools/metric_providers/psu/energy/ac/system/README.md
@@ -0,0 +1,11 @@
+Slimmed down version of check_gude.py from https://github.com/gudesystems/check_gude.py
+for the Blauer Engel für Software Measurements
+===============
+
+This script expects the GUDE Powermeter to be fixed on the IP 192.168.178.32
+
+- Create **venv**: `python3 -m venv venv`
+- Activate: `source venv/bin/activate`
+- Install requests: `pip3 install requests`
+- Run: `python3 check_gude.py`
+
diff --git a/tools/metric_providers/psu/energy/ac/system/check_gude_modified.py b/tools/metric_providers/psu/energy/ac/system/check_gude_modified.py
new file mode 100755
index 000000000..ad7ab1c72
--- /dev/null
+++ b/tools/metric_providers/psu/energy/ac/system/check_gude_modified.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+
+import requests
+import json
+import time
+import subprocess
+
+def main(resolution):
+ url = f'http://192.168.178.32/status.json'
+
+ ONLY_VALUES = 0x4000
+ cgi = {'components': ONLY_VALUES} # simple-sensors + and only values
+
+ resolution = float(resolution);
+
+ target_sleep_time = resolution/1000.0
+
+ while True: # loop until CTRL+C
+ timestamp_before = time.time_ns()
+ time.sleep(target_sleep_time)
+
+ data = json.loads(requests.get(url, params=cgi, verify=False, auth=None).text)
+
+ # print(data) # DEBUG
+ timestamp_after = time.time_ns()
+ effective_sleep_time = timestamp_after - timestamp_before
+ # print(effective_sleep_time / 1_000_000_000) # DEBUG
+ conversion_factor = effective_sleep_time / 1_000_000 # we want microjoule. Therefore / 10**9 to get seconds and then * 10**3 to get mJ
+ print(int(timestamp_after / 1_000), int(data["sensor_values"][0]['values'][0][4]['v']*conversion_factor), flush=True)
+
+if __name__ == "__main__":
+ import argparse
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-i", type=str, help="Resolution")
+
+ args = parser.parse_args()
+
+ if args.i is None:
+ parser.print_help()
+ print("Please supply -i to set resolution in milliseconds")
+ exit(2)
+
+ main(args.i)
diff --git a/tools/metric_providers/psu/energy/ac/system/provider.py b/tools/metric_providers/psu/energy/ac/system/provider.py
new file mode 100644
index 000000000..d8851dc15
--- /dev/null
+++ b/tools/metric_providers/psu/energy/ac/system/provider.py
@@ -0,0 +1,49 @@
+import sys, os
+import subprocess
+
+if __name__ == "__main__":
+ sys.path.append(os.path.dirname(os.path.abspath(__file__))+'/../../../../..')
+from metric_providers.base import BaseMetricProvider
+
+class PsuEnergyAcSystemProvider(BaseMetricProvider):
+ def __init__(self, resolution):
+ self._current_dir = os.path.dirname(os.path.abspath(__file__))
+ self._metric_name = "psu_energy_ac_system"
+ self._metrics = {"time":int, "value":int}
+ self._resolution = resolution
+ super().__init__()
+
+ def start_profiling(self, containers=None):
+ call_string = f"{self._current_dir}/check_gude_modified.py -i {self._resolution}"
+
+ call_string += f" > {self._filename}"
+
+ print(call_string)
+
+ self._ps = subprocess.Popen(
+ [call_string],
+ shell=True,
+ preexec_fn=os.setsid,
+ stderr=subprocess.PIPE
+ # since we are launching the command with shell=True we cannot use ps.terminate() / ps.kill().
+ # This would just kill the executing shell, but not it's child and make the process an orphan.
+ # therefore we use os.setsid here and later call os.getpgid(pid) to get process group that the shell
+ # and the process are running in. These we then can send the signal to and kill them
+ )
+
+
+if __name__ == "__main__":
+ import time
+ import argparse
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("container_id", help="Please provide the container_id")
+ args = parser.parse_args()
+
+ o = PsuEnergyAcSystemProvider(resolution=100)
+
+ print("Starting to profile")
+ o.start_profiling()
+ time.sleep(2)
+ o.stop_profiling()
+ print("Done, check ", o._filename)