From 33d17e4367303080b7736e8e838e8f2489679207 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 25 Nov 2024 15:15:08 +0100 Subject: [PATCH] feat: initial Impact Framework exporter --- if-manifest-export/pom.xml | 45 +++++++++++++ .../impactframework/export/IFExporter.java | 57 ++++++++++++++++ .../export/IFExporterTest.java | 66 +++++++++++++++++++ .../power/measure/OngoingPowerMeasure.java | 48 ++++++++++++-- .../power/measure/PowerMeasure.java | 14 ++++ .../power/measure/StoppedPowerMeasure.java | 39 ++++++++--- pom.xml | 46 +++++++------ 7 files changed, 280 insertions(+), 35 deletions(-) create mode 100644 if-manifest-export/pom.xml create mode 100644 if-manifest-export/src/main/java/net/laprun/sustainability/power/impactframework/export/IFExporter.java create mode 100644 if-manifest-export/src/test/java/net/laprun/sustainability/power/impactframework/export/IFExporterTest.java diff --git a/if-manifest-export/pom.xml b/if-manifest-export/pom.xml new file mode 100644 index 0000000..8504628 --- /dev/null +++ b/if-manifest-export/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + + net.laprun.sustainability + power-server-parent + 0.0.12-SNAPSHOT + + + power-server-if-manifest-export + power-server : GSF Impact Framework exporter + Exports power measures as an Impact Framework manifest + + + + net.laprun.sustainability + if-manifest-java + 0.0.2 + + + net.laprun.sustainability + power-server-measure + ${project.version} + + + + + + + net.revelc.code.formatter + formatter-maven-plugin + + + net.laprun.sustainability + build-tools + ${project.version} + + + + + + diff --git a/if-manifest-export/src/main/java/net/laprun/sustainability/power/impactframework/export/IFExporter.java b/if-manifest-export/src/main/java/net/laprun/sustainability/power/impactframework/export/IFExporter.java new file mode 100644 index 0000000..9bfdcd1 --- /dev/null +++ b/if-manifest-export/src/main/java/net/laprun/sustainability/power/impactframework/export/IFExporter.java @@ -0,0 +1,57 @@ +package net.laprun.sustainability.power.impactframework.export; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import net.laprun.sustainability.impactframework.manifest.Child; +import net.laprun.sustainability.impactframework.manifest.Initialize; +import net.laprun.sustainability.impactframework.manifest.Input; +import net.laprun.sustainability.impactframework.manifest.Manifest; +import net.laprun.sustainability.impactframework.manifest.Metadata; +import net.laprun.sustainability.impactframework.manifest.Plugin; +import net.laprun.sustainability.impactframework.manifest.Tree; +import net.laprun.sustainability.power.SensorMetadata; +import net.laprun.sustainability.power.measure.PowerMeasure; +import net.laprun.sustainability.power.measure.StoppedPowerMeasure; + +public enum IFExporter { + ; + + private final static String prefix = "power-server-"; + private final static Initialize defaultInitialize = new Initialize(Map.of("sum", new Plugin("sum", "builtin", "Sum"))); + public static final String CHILD_NAME = prefix + "child"; + + public static Manifest export(StoppedPowerMeasure measure) { + final var ifMetadata = new Metadata("power-server measure", null, Set.of("power-server")); + + final var samples = measure.numberOfSamples(); + final List inputs = new ArrayList<>(samples); + final var sensorMetadata = measure.metadata(); + final int samplingFrequency = (int) measure.duration().dividedBy(samples).toMillis(); + + final var components = sensorMetadata.components(); + for (int i = 0; i < samples; i++) { + final var values = measure.getNthTimestampedMeasures(i); + inputs.add(toInput(samplingFrequency, values, components)); + } + + return new Manifest(ifMetadata, defaultInitialize, + new Tree(Map.of(CHILD_NAME, new Child(inputs)))); + } + + public static String getInputValueName(String componentName) { + return prefix + componentName; + } + + private static Input toInput(int samplingFrequency, PowerMeasure.TimestampedMeasures values, + Map components) { + final var inputValues = new TreeMap(); + components.values().forEach( + component -> inputValues.put(getInputValueName(component.name()), values.measures()[component.index()])); + return new Input(Instant.ofEpochMilli(values.timestamp()), samplingFrequency, inputValues); + } +} diff --git a/if-manifest-export/src/test/java/net/laprun/sustainability/power/impactframework/export/IFExporterTest.java b/if-manifest-export/src/test/java/net/laprun/sustainability/power/impactframework/export/IFExporterTest.java new file mode 100644 index 0000000..af0e665 --- /dev/null +++ b/if-manifest-export/src/test/java/net/laprun/sustainability/power/impactframework/export/IFExporterTest.java @@ -0,0 +1,66 @@ +package net.laprun.sustainability.power.impactframework.export; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import net.laprun.sustainability.power.SensorMetadata; +import net.laprun.sustainability.power.measure.OngoingPowerMeasure; +import net.laprun.sustainability.power.measure.StoppedPowerMeasure; + +class IFExporterTest { + public static final String COMPONENT1_NAME = "c1"; + public static final String COMPONENT2_NAME = "c2"; + public static final String COMPONENT3_NAME = "c3"; + private final static SensorMetadata metadata = new SensorMetadata(Map.of( + COMPONENT1_NAME, new SensorMetadata.ComponentMetadata(COMPONENT1_NAME, 0, "component 1", true, "mW"), + COMPONENT2_NAME, new SensorMetadata.ComponentMetadata(COMPONENT2_NAME, 1, "component 2", true, "mW"), + COMPONENT3_NAME, new SensorMetadata.ComponentMetadata(COMPONENT3_NAME, 2, "always zero", false, "mW")), null, + new int[] { 0, 1, 2 }); + + @Test + void export() { + final var m1c1 = 10.0; + final var m1c2 = 12.0; + final var m1c3 = 0.0; + final var m2c1 = 8.0; + final var m2c2 = 17.0; + final var m2c3 = 0.0; + final var m3c1 = 5.0; + final var m3c2 = 5.0; + final var m3c3 = 0.0; + + final var measure = new OngoingPowerMeasure(metadata); + + final var components = new double[metadata.componentCardinality()]; + components[0] = m1c1; + components[1] = m1c2; + components[2] = m1c3; + measure.recordMeasure(components); + + components[0] = m2c1; + components[1] = m2c2; + components[2] = m2c3; + measure.recordMeasure(components); + + components[0] = m3c1; + components[1] = m3c2; + components[2] = m3c3; + measure.recordMeasure(components); + + final var manifest = IFExporter.export(new StoppedPowerMeasure(measure)); + assertNotNull(manifest); + final var child = manifest.tree().children().get(IFExporter.CHILD_NAME); + assertNotNull(child); + final var inputs = child.inputs(); + assertNotNull(inputs); + assertEquals(3, inputs.size()); + final var input = inputs.get(1); + final var values = input.values(); + assertEquals(m2c1, values.get(IFExporter.getInputValueName(COMPONENT1_NAME))); + assertEquals(m2c2, values.get(IFExporter.getInputValueName(COMPONENT2_NAME))); + assertEquals(m2c3, values.get(IFExporter.getInputValueName(COMPONENT3_NAME))); + } +} diff --git a/measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java b/measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java index a59386e..2d2ebf4 100644 --- a/measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java +++ b/measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java @@ -4,6 +4,9 @@ import java.util.BitSet; import java.util.Objects; import java.util.Optional; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.Stream; import net.laprun.sustainability.power.SensorMetadata; import net.laprun.sustainability.power.analysis.ComponentProcessor; @@ -15,13 +18,13 @@ public class OngoingPowerMeasure implements PowerMeasure { private final BitSet nonZeroComponents; private final int[] totalComponents; private final int totalIndex; + private final double[][] measures; + private final ComponentProcessor[] analyzers; private double minTotal = Double.MAX_VALUE; private double maxTotal; private double accumulatedTotal; private int samples; - private final double[][] measures; private long[] timestamps; - private final ComponentProcessor[] analyzers; public OngoingPowerMeasure(SensorMetadata sensorMetadata, ComponentProcessor... analyzers) { this.sensorMetadata = sensorMetadata; @@ -50,7 +53,6 @@ public SensorMetadata metadata() { } public void recordMeasure(double[] components) { - final var previousSize = samples; samples++; for (int component = 0; component < components.length; component++) { final var componentValue = components[component]; @@ -115,15 +117,51 @@ public double maxMeasuredTotal() { @Override public Optional getMeasuresFor(int component) { + return measuresFor(component, samples); + } + + Optional measuresFor(int component, int upToIndex) { if (nonZeroComponents.get(component)) { - final var dest = new double[samples]; - System.arraycopy(measures[component], 0, dest, 0, samples); + final var dest = new double[upToIndex]; + System.arraycopy(measures[component], 0, dest, 0, upToIndex); return Optional.of(dest); } else { return Optional.empty(); } } + @Override + public Stream streamTimestampedMeasuresFor(int component, int upToIndex) { + final var componentMeasures = measures[component]; + return indicesFor(component, upToIndex) + .mapToObj(index -> new TimestampedValue(timestamps[index], componentMeasures[index])); + } + + @Override + public DoubleStream streamMeasuresFor(int component, int upToIndex) { + final var componentMeasures = measures[component]; + return indicesFor(component, upToIndex).mapToDouble(index -> componentMeasures[index]); + } + + IntStream indicesFor(int component, int upToIndex) { + upToIndex = Math.min(upToIndex, samples - 1); + if (upToIndex >= 0 && nonZeroComponents.get(component)) { + return IntStream.range(0, upToIndex); + } else { + return IntStream.empty(); + } + } + + @Override + public TimestampedMeasures getNthTimestampedMeasures(int n) { + n = Math.min(n, samples - 1); + final var result = new double[measures.length]; + for (int i = 0; i < measures.length; i++) { + result[i] = measures[i][n]; + } + return new TimestampedMeasures(timestamps[n], result); + } + @Override public ComponentProcessor[] analyzers() { return analyzers; diff --git a/measure/src/main/java/net/laprun/sustainability/power/measure/PowerMeasure.java b/measure/src/main/java/net/laprun/sustainability/power/measure/PowerMeasure.java index 5ffe3ee..81dc449 100644 --- a/measure/src/main/java/net/laprun/sustainability/power/measure/PowerMeasure.java +++ b/measure/src/main/java/net/laprun/sustainability/power/measure/PowerMeasure.java @@ -2,6 +2,8 @@ import java.time.Duration; import java.util.Optional; +import java.util.stream.DoubleStream; +import java.util.stream.Stream; import net.laprun.sustainability.power.SensorMetadata; import net.laprun.sustainability.power.analysis.ComponentProcessor; @@ -38,5 +40,17 @@ static String readableWithUnit(double milliWatts) { Optional getMeasuresFor(int component); + TimestampedMeasures getNthTimestampedMeasures(int n); + + Stream streamTimestampedMeasuresFor(int component, int upToIndex); + + DoubleStream streamMeasuresFor(int component, int upToIndex); + + record TimestampedValue(long timestamp, double value) { + } + + record TimestampedMeasures(long timestamp, double[] measures) { + } + ComponentProcessor[] analyzers(); } diff --git a/measure/src/main/java/net/laprun/sustainability/power/measure/StoppedPowerMeasure.java b/measure/src/main/java/net/laprun/sustainability/power/measure/StoppedPowerMeasure.java index 82a7728..cc826e9 100644 --- a/measure/src/main/java/net/laprun/sustainability/power/measure/StoppedPowerMeasure.java +++ b/measure/src/main/java/net/laprun/sustainability/power/measure/StoppedPowerMeasure.java @@ -2,33 +2,30 @@ import java.time.Duration; import java.util.Optional; +import java.util.stream.DoubleStream; +import java.util.stream.Stream; import net.laprun.sustainability.power.SensorMetadata; import net.laprun.sustainability.power.analysis.ComponentProcessor; @SuppressWarnings("unused") public class StoppedPowerMeasure implements PowerMeasure { - private final SensorMetadata sensorMetadata; + private final OngoingPowerMeasure measure; private final int samples; private final Duration duration; private final double total; private final double min; private final double max; - private final double[][] measures; private final ComponentProcessor[] processors; - public StoppedPowerMeasure(PowerMeasure powerMeasure) { - this.sensorMetadata = powerMeasure.metadata(); + public StoppedPowerMeasure(OngoingPowerMeasure powerMeasure) { + this.measure = powerMeasure; this.duration = powerMeasure.duration(); this.total = powerMeasure.total(); this.min = powerMeasure.minMeasuredTotal(); this.max = powerMeasure.maxMeasuredTotal(); this.samples = powerMeasure.numberOfSamples(); final var cardinality = metadata().componentCardinality(); - measures = new double[cardinality][samples]; - for (int i = 0; i < cardinality; i++) { - measures[i] = powerMeasure.getMeasuresFor(i).orElse(null); - } processors = powerMeasure.analyzers(); } @@ -59,12 +56,34 @@ public int numberOfSamples() { @Override public SensorMetadata metadata() { - return sensorMetadata; + return measure.metadata(); } @Override public Optional getMeasuresFor(int component) { - return Optional.ofNullable(measures[component]); + return measure.measuresFor(component, samples); + } + + @Override + public Stream streamTimestampedMeasuresFor(int component, int upToIndex) { + upToIndex = ensureIndex(upToIndex); + return measure.streamTimestampedMeasuresFor(component, upToIndex); + } + + @Override + public DoubleStream streamMeasuresFor(int component, int upToIndex) { + upToIndex = ensureIndex(upToIndex); + return measure.streamMeasuresFor(component, upToIndex); + } + + @Override + public TimestampedMeasures getNthTimestampedMeasures(int n) { + n = ensureIndex(n); + return measure.getNthTimestampedMeasures(n); + } + + private int ensureIndex(int upToIndex) { + return Math.min(upToIndex, samples - 1); } @Override diff --git a/pom.xml b/pom.xml index ca02bd4..af984f4 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,7 @@ measure server analysis + if-manifest-export @@ -104,6 +105,31 @@ + + + + net.revelc.code.formatter + formatter-maven-plugin + ${formatter-plugin.version} + + eclipse-format.xml + LF + ${format.skip} + + + + net.revelc.code + impsort-maven-plugin + ${impsort-plugin.version} + + true + java.,javax.,jakarta.,org.,com. + * + ${format.skip} + + + + org.apache.maven.plugins @@ -184,27 +210,10 @@ net.revelc.code.formatter formatter-maven-plugin - ${formatter-plugin.version} - - - - eclipse-format.xml - LF - ${format.skip} - net.revelc.code impsort-maven-plugin - ${impsort-plugin.version} - - - 17 - java.,javax.,jakarta.,org.,com. - * - ${format.skip} - true - @@ -235,9 +244,6 @@ net.revelc.code impsort-maven-plugin - - true - sort-imports