Skip to content

Commit 126f330

Browse files
committed
feat: initial Impact Framework exporter
1 parent 7fd673d commit 126f330

File tree

7 files changed

+280
-35
lines changed

7 files changed

+280
-35
lines changed

if-manifest-export/pom.xml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>net.laprun.sustainability</groupId>
9+
<artifactId>power-server-parent</artifactId>
10+
<version>0.0.12-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>power-server-if-manifest-export</artifactId>
14+
<name>power-server : GSF Impact Framework exporter</name>
15+
<description>Exports power measures as an Impact Framework manifest</description>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>net.laprun.sustainability</groupId>
20+
<artifactId>if-manifest-java</artifactId>
21+
<version>0.0.2</version>
22+
</dependency>
23+
<dependency>
24+
<groupId>net.laprun.sustainability</groupId>
25+
<artifactId>power-server-measure</artifactId>
26+
<version>${project.version}</version>
27+
</dependency>
28+
</dependencies>
29+
30+
<build>
31+
<plugins>
32+
<plugin>
33+
<groupId>net.revelc.code.formatter</groupId>
34+
<artifactId>formatter-maven-plugin</artifactId>
35+
<dependencies>
36+
<dependency>
37+
<groupId>net.laprun.sustainability</groupId>
38+
<artifactId>build-tools</artifactId>
39+
<version>${project.version}</version>
40+
</dependency>
41+
</dependencies>
42+
</plugin>
43+
</plugins>
44+
</build>
45+
</project>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package net.laprun.sustainability.power.impactframework.export;
2+
3+
import java.time.Instant;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.Set;
8+
import java.util.TreeMap;
9+
10+
import net.laprun.sustainability.impactframework.manifest.Child;
11+
import net.laprun.sustainability.impactframework.manifest.Initialize;
12+
import net.laprun.sustainability.impactframework.manifest.Input;
13+
import net.laprun.sustainability.impactframework.manifest.Manifest;
14+
import net.laprun.sustainability.impactframework.manifest.Metadata;
15+
import net.laprun.sustainability.impactframework.manifest.Plugin;
16+
import net.laprun.sustainability.impactframework.manifest.Tree;
17+
import net.laprun.sustainability.power.SensorMetadata;
18+
import net.laprun.sustainability.power.measure.PowerMeasure;
19+
import net.laprun.sustainability.power.measure.StoppedPowerMeasure;
20+
21+
public enum IFExporter {
22+
;
23+
24+
private final static String prefix = "power-server-";
25+
private final static Initialize defaultInitialize = new Initialize(Map.of("sum", new Plugin("sum", "builtin", "Sum")));
26+
public static final String CHILD_NAME = prefix + "child";
27+
28+
public static Manifest export(StoppedPowerMeasure measure) {
29+
final var ifMetadata = new Metadata("power-server measure", null, Set.of("power-server"));
30+
31+
final var samples = measure.numberOfSamples();
32+
final List<Input> inputs = new ArrayList<>(samples);
33+
final var sensorMetadata = measure.metadata();
34+
final int samplingFrequency = (int) measure.duration().dividedBy(samples).toMillis();
35+
36+
final var components = sensorMetadata.components();
37+
for (int i = 0; i < samples; i++) {
38+
final var values = measure.getNthTimestampedMeasures(i);
39+
inputs.add(toInput(samplingFrequency, values, components));
40+
}
41+
42+
return new Manifest(ifMetadata, defaultInitialize,
43+
new Tree(Map.of(CHILD_NAME, new Child(inputs))));
44+
}
45+
46+
public static String getInputValueName(String componentName) {
47+
return prefix + componentName;
48+
}
49+
50+
private static Input toInput(int samplingFrequency, PowerMeasure.TimestampedMeasures values,
51+
Map<String, SensorMetadata.ComponentMetadata> components) {
52+
final var inputValues = new TreeMap<String, Object>();
53+
components.values().forEach(
54+
component -> inputValues.put(getInputValueName(component.name()), values.measures()[component.index()]));
55+
return new Input(Instant.ofEpochMilli(values.timestamp()), samplingFrequency, inputValues);
56+
}
57+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package net.laprun.sustainability.power.impactframework.export;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import java.util.Map;
6+
7+
import org.junit.jupiter.api.Test;
8+
9+
import net.laprun.sustainability.power.SensorMetadata;
10+
import net.laprun.sustainability.power.measure.OngoingPowerMeasure;
11+
import net.laprun.sustainability.power.measure.StoppedPowerMeasure;
12+
13+
class IFExporterTest {
14+
public static final String COMPONENT1_NAME = "c1";
15+
public static final String COMPONENT2_NAME = "c2";
16+
public static final String COMPONENT3_NAME = "c3";
17+
private final static SensorMetadata metadata = new SensorMetadata(Map.of(
18+
COMPONENT1_NAME, new SensorMetadata.ComponentMetadata(COMPONENT1_NAME, 0, "component 1", true, "mW"),
19+
COMPONENT2_NAME, new SensorMetadata.ComponentMetadata(COMPONENT2_NAME, 1, "component 2", true, "mW"),
20+
COMPONENT3_NAME, new SensorMetadata.ComponentMetadata(COMPONENT3_NAME, 2, "always zero", false, "mW")), null,
21+
new int[] { 0, 1, 2 });
22+
23+
@Test
24+
void export() {
25+
final var m1c1 = 10.0;
26+
final var m1c2 = 12.0;
27+
final var m1c3 = 0.0;
28+
final var m2c1 = 8.0;
29+
final var m2c2 = 17.0;
30+
final var m2c3 = 0.0;
31+
final var m3c1 = 5.0;
32+
final var m3c2 = 5.0;
33+
final var m3c3 = 0.0;
34+
35+
final var measure = new OngoingPowerMeasure(metadata);
36+
37+
final var components = new double[metadata.componentCardinality()];
38+
components[0] = m1c1;
39+
components[1] = m1c2;
40+
components[2] = m1c3;
41+
measure.recordMeasure(components);
42+
43+
components[0] = m2c1;
44+
components[1] = m2c2;
45+
components[2] = m2c3;
46+
measure.recordMeasure(components);
47+
48+
components[0] = m3c1;
49+
components[1] = m3c2;
50+
components[2] = m3c3;
51+
measure.recordMeasure(components);
52+
53+
final var manifest = IFExporter.export(new StoppedPowerMeasure(measure));
54+
assertNotNull(manifest);
55+
final var child = manifest.tree().children().get(IFExporter.CHILD_NAME);
56+
assertNotNull(child);
57+
final var inputs = child.inputs();
58+
assertNotNull(inputs);
59+
assertEquals(3, inputs.size());
60+
final var input = inputs.get(1);
61+
final var values = input.values();
62+
assertEquals(m2c1, values.get(IFExporter.getInputValueName(COMPONENT1_NAME)));
63+
assertEquals(m2c2, values.get(IFExporter.getInputValueName(COMPONENT2_NAME)));
64+
assertEquals(m2c3, values.get(IFExporter.getInputValueName(COMPONENT3_NAME)));
65+
}
66+
}

measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import java.util.BitSet;
55
import java.util.Objects;
66
import java.util.Optional;
7+
import java.util.stream.DoubleStream;
8+
import java.util.stream.IntStream;
9+
import java.util.stream.Stream;
710

811
import net.laprun.sustainability.power.SensorMetadata;
912
import net.laprun.sustainability.power.analysis.ComponentProcessor;
@@ -15,13 +18,13 @@ public class OngoingPowerMeasure implements PowerMeasure {
1518
private final BitSet nonZeroComponents;
1619
private final int[] totalComponents;
1720
private final int totalIndex;
21+
private final double[][] measures;
22+
private final ComponentProcessor[] analyzers;
1823
private double minTotal = Double.MAX_VALUE;
1924
private double maxTotal;
2025
private double accumulatedTotal;
2126
private int samples;
22-
private final double[][] measures;
2327
private long[] timestamps;
24-
private final ComponentProcessor[] analyzers;
2528

2629
public OngoingPowerMeasure(SensorMetadata sensorMetadata, ComponentProcessor... analyzers) {
2730
this.sensorMetadata = sensorMetadata;
@@ -50,7 +53,6 @@ public SensorMetadata metadata() {
5053
}
5154

5255
public void recordMeasure(double[] components) {
53-
final var previousSize = samples;
5456
samples++;
5557
for (int component = 0; component < components.length; component++) {
5658
final var componentValue = components[component];
@@ -115,15 +117,51 @@ public double maxMeasuredTotal() {
115117

116118
@Override
117119
public Optional<double[]> getMeasuresFor(int component) {
120+
return measuresFor(component, samples);
121+
}
122+
123+
Optional<double[]> measuresFor(int component, int upToIndex) {
118124
if (nonZeroComponents.get(component)) {
119-
final var dest = new double[samples];
120-
System.arraycopy(measures[component], 0, dest, 0, samples);
125+
final var dest = new double[upToIndex];
126+
System.arraycopy(measures[component], 0, dest, 0, upToIndex);
121127
return Optional.of(dest);
122128
} else {
123129
return Optional.empty();
124130
}
125131
}
126132

133+
@Override
134+
public Stream<TimestampedValue> streamTimestampedMeasuresFor(int component, int upToIndex) {
135+
final var componentMeasures = measures[component];
136+
return indicesFor(component, upToIndex)
137+
.mapToObj(index -> new TimestampedValue(timestamps[index], componentMeasures[index]));
138+
}
139+
140+
@Override
141+
public DoubleStream streamMeasuresFor(int component, int upToIndex) {
142+
final var componentMeasures = measures[component];
143+
return indicesFor(component, upToIndex).mapToDouble(index -> componentMeasures[index]);
144+
}
145+
146+
IntStream indicesFor(int component, int upToIndex) {
147+
upToIndex = Math.min(upToIndex, samples - 1);
148+
if (upToIndex >= 0 && nonZeroComponents.get(component)) {
149+
return IntStream.range(0, upToIndex);
150+
} else {
151+
return IntStream.empty();
152+
}
153+
}
154+
155+
@Override
156+
public TimestampedMeasures getNthTimestampedMeasures(int n) {
157+
n = Math.min(n, samples - 1);
158+
final var result = new double[measures.length];
159+
for (int i = 0; i < measures.length; i++) {
160+
result[i] = measures[i][n];
161+
}
162+
return new TimestampedMeasures(timestamps[n], result);
163+
}
164+
127165
@Override
128166
public ComponentProcessor[] analyzers() {
129167
return analyzers;

measure/src/main/java/net/laprun/sustainability/power/measure/PowerMeasure.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.time.Duration;
44
import java.util.Optional;
5+
import java.util.stream.DoubleStream;
6+
import java.util.stream.Stream;
57

68
import net.laprun.sustainability.power.SensorMetadata;
79
import net.laprun.sustainability.power.analysis.ComponentProcessor;
@@ -38,5 +40,17 @@ static String readableWithUnit(double milliWatts) {
3840

3941
Optional<double[]> getMeasuresFor(int component);
4042

43+
TimestampedMeasures getNthTimestampedMeasures(int n);
44+
45+
Stream<TimestampedValue> streamTimestampedMeasuresFor(int component, int upToIndex);
46+
47+
DoubleStream streamMeasuresFor(int component, int upToIndex);
48+
49+
record TimestampedValue(long timestamp, double value) {
50+
}
51+
52+
record TimestampedMeasures(long timestamp, double[] measures) {
53+
}
54+
4155
ComponentProcessor[] analyzers();
4256
}

measure/src/main/java/net/laprun/sustainability/power/measure/StoppedPowerMeasure.java

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,30 @@
22

33
import java.time.Duration;
44
import java.util.Optional;
5+
import java.util.stream.DoubleStream;
6+
import java.util.stream.Stream;
57

68
import net.laprun.sustainability.power.SensorMetadata;
79
import net.laprun.sustainability.power.analysis.ComponentProcessor;
810

911
@SuppressWarnings("unused")
1012
public class StoppedPowerMeasure implements PowerMeasure {
11-
private final SensorMetadata sensorMetadata;
13+
private final OngoingPowerMeasure measure;
1214
private final int samples;
1315
private final Duration duration;
1416
private final double total;
1517
private final double min;
1618
private final double max;
17-
private final double[][] measures;
1819
private final ComponentProcessor[] processors;
1920

20-
public StoppedPowerMeasure(PowerMeasure powerMeasure) {
21-
this.sensorMetadata = powerMeasure.metadata();
21+
public StoppedPowerMeasure(OngoingPowerMeasure powerMeasure) {
22+
this.measure = powerMeasure;
2223
this.duration = powerMeasure.duration();
2324
this.total = powerMeasure.total();
2425
this.min = powerMeasure.minMeasuredTotal();
2526
this.max = powerMeasure.maxMeasuredTotal();
2627
this.samples = powerMeasure.numberOfSamples();
2728
final var cardinality = metadata().componentCardinality();
28-
measures = new double[cardinality][samples];
29-
for (int i = 0; i < cardinality; i++) {
30-
measures[i] = powerMeasure.getMeasuresFor(i).orElse(null);
31-
}
3229
processors = powerMeasure.analyzers();
3330
}
3431

@@ -59,12 +56,34 @@ public int numberOfSamples() {
5956

6057
@Override
6158
public SensorMetadata metadata() {
62-
return sensorMetadata;
59+
return measure.metadata();
6360
}
6461

6562
@Override
6663
public Optional<double[]> getMeasuresFor(int component) {
67-
return Optional.ofNullable(measures[component]);
64+
return measure.measuresFor(component, samples);
65+
}
66+
67+
@Override
68+
public Stream<TimestampedValue> streamTimestampedMeasuresFor(int component, int upToIndex) {
69+
upToIndex = ensureIndex(upToIndex);
70+
return measure.streamTimestampedMeasuresFor(component, upToIndex);
71+
}
72+
73+
@Override
74+
public DoubleStream streamMeasuresFor(int component, int upToIndex) {
75+
upToIndex = ensureIndex(upToIndex);
76+
return measure.streamMeasuresFor(component, upToIndex);
77+
}
78+
79+
@Override
80+
public TimestampedMeasures getNthTimestampedMeasures(int n) {
81+
n = ensureIndex(n);
82+
return measure.getNthTimestampedMeasures(n);
83+
}
84+
85+
private int ensureIndex(int upToIndex) {
86+
return Math.min(upToIndex, samples - 1);
6887
}
6988

7089
@Override

0 commit comments

Comments
 (0)