Skip to content

Commit 8ace0ff

Browse files
committed
refactor: record totals separately in processors, ensure component order
1 parent 181dcf4 commit 8ace0ff

File tree

12 files changed

+198
-53
lines changed

12 files changed

+198
-53
lines changed

if-manifest-export/src/main/java/net/laprun/sustainability/power/impactframework/export/IFExporter.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
import java.time.Instant;
44
import java.util.ArrayList;
5+
import java.util.LinkedHashMap;
56
import java.util.List;
67
import java.util.Map;
78
import java.util.Set;
8-
import java.util.TreeMap;
99

1010
import net.laprun.sustainability.impactframework.manifest.Child;
1111
import net.laprun.sustainability.impactframework.manifest.Initialize;
@@ -14,7 +14,6 @@
1414
import net.laprun.sustainability.impactframework.manifest.Metadata;
1515
import net.laprun.sustainability.impactframework.manifest.Plugin;
1616
import net.laprun.sustainability.impactframework.manifest.Tree;
17-
import net.laprun.sustainability.power.SensorMetadata;
1817
import net.laprun.sustainability.power.measure.PowerMeasure;
1918
import net.laprun.sustainability.power.measure.StoppedPowerMeasure;
2019

@@ -32,11 +31,13 @@ public static Manifest export(StoppedPowerMeasure measure) {
3231
final List<Input> inputs = new ArrayList<>(samples);
3332
final var sensorMetadata = measure.metadata();
3433
final int samplingFrequency = (int) measure.duration().dividedBy(samples).toMillis();
35-
3634
final var components = sensorMetadata.components();
35+
final String[] inputNames = new String[components.size()];
36+
components.values().forEach(component -> inputNames[component.index()] = getInputValueName(component.name()));
37+
3738
for (int i = 0; i < samples; i++) {
3839
final var values = measure.getNthTimestampedMeasures(i);
39-
inputs.add(toInput(samplingFrequency, values, components));
40+
inputs.add(toInput(samplingFrequency, values, inputNames));
4041
}
4142

4243
return new Manifest(ifMetadata, defaultInitialize,
@@ -48,10 +49,12 @@ public static String getInputValueName(String componentName) {
4849
}
4950

5051
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()]));
52+
String[] inputNames) {
53+
final var inputValues = new LinkedHashMap<String, Object>(inputNames.length);
54+
final var measures = values.measures();
55+
for (int i = 0; i < inputNames.length; i++) {
56+
inputValues.put(inputNames[i], measures[i]);
57+
}
5558
return new Input(Instant.ofEpochMilli(values.timestamp()), samplingFrequency, inputValues);
5659
}
5760
}

measure/pom.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
<dependency>
2222
<groupId>org.assertj</groupId>
2323
<artifactId>assertj-core</artifactId>
24-
<version>3.26.3</version>
25-
<scope>test</scope>
2624
</dependency>
2725
</dependencies>
2826

measure/src/main/java/net/laprun/sustainability/power/analysis/DefaultProcessors.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99

1010
public class DefaultProcessors implements Processors {
1111
private final List<ComponentProcessor>[] processors;
12+
private ComponentProcessor totalProcessor;
1213

1314
@SuppressWarnings("unchecked")
1415
public DefaultProcessors(int componentCardinality) {
1516
this.processors = new List[componentCardinality];
1617
}
1718

1819
@Override
19-
public void recordMeasure(double[] components, double total, long timestamp) {
20+
public void recordMeasure(double[] components, long timestamp) {
2021
for (var index = 0; index < components.length; index++) {
2122
final var fIndex = index;
2223
final var componentProcessors = processors[index];
@@ -38,6 +39,23 @@ public void registerProcessorFor(int componentIndex, ComponentProcessor processo
3839
}
3940
}
4041

42+
@Override
43+
public void registerTotalProcessor(ComponentProcessor processor) {
44+
this.totalProcessor = processor;
45+
}
46+
47+
@Override
48+
public void recordTotal(double total, long timestamp) {
49+
if (totalProcessor != null) {
50+
totalProcessor.recordComponentValue(total, timestamp);
51+
}
52+
}
53+
54+
@Override
55+
public Optional<ComponentProcessor> totalProcessor() {
56+
return Optional.ofNullable(totalProcessor);
57+
}
58+
4159
@Override
4260
public List<ComponentProcessor> processorsFor(int componentIndex) {
4361
final var componentProcessors = processors[componentIndex];

measure/src/main/java/net/laprun/sustainability/power/analysis/Processors.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,22 @@ public interface Processors {
99
Processors empty = new Processors() {
1010
};
1111

12-
default void recordMeasure(double[] components, double total, long timestamp) {
12+
default void recordMeasure(double[] components, long timestamp) {
13+
}
14+
15+
default void recordTotal(double total, long timestamp) {
1316
}
1417

1518
default void registerProcessorFor(int componentIndex, ComponentProcessor processor) {
1619
}
1720

21+
default void registerTotalProcessor(ComponentProcessor processor) {
22+
}
23+
24+
default Optional<ComponentProcessor> totalProcessor() {
25+
return Optional.empty();
26+
}
27+
1828
default List<ComponentProcessor> processorsFor(int componentIndex) {
1929
return List.of();
2030
}

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

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,51 @@
1616

1717
public class OngoingPowerMeasure implements PowerMeasure {
1818
private static final int DEFAULT_SIZE = 32;
19-
private final SensorMetadata sensorMetadata;
19+
private final SensorMetadata metadata;
2020
private final long startedAt;
2121
private final BitSet nonZeroComponents;
22-
private final int[] totalComponents;
2322
private final int totalIndex;
2423
private final double[][] measures;
24+
private final boolean shouldComputeTotals;
2525
private double minTotal = Double.MAX_VALUE;
2626
private double maxTotal;
2727
private double accumulatedTotal;
2828
private int samples;
2929
private long[] timestamps;
3030
private Processors processors = Processors.empty;
3131

32-
public OngoingPowerMeasure(SensorMetadata sensorMetadata) {
32+
public OngoingPowerMeasure(SensorMetadata metadata) {
3333
startedAt = System.currentTimeMillis();
3434

35-
final var numComponents = sensorMetadata.componentCardinality();
36-
// we also record the aggregated total for each component participating in the aggregated value
37-
final var measuresNb = numComponents + 1;
35+
final var numComponents = metadata.componentCardinality();
36+
37+
// check if we need to add an aggregated total component
38+
final var totalComponents = metadata.totalComponents();
39+
final int measuresNb;
40+
if (totalComponents.length > 0) {
41+
shouldComputeTotals = true;
42+
measuresNb = numComponents + 1;
43+
44+
final var sumMsg = Arrays.stream(totalComponents)
45+
.mapToObj(i -> metadata.metadataFor(i).name())
46+
.collect(Collectors.joining("+", "Aggregated total from (", ")"));
47+
48+
// todo: compute total component properly (same unit, convert to base unit all components)
49+
this.metadata = SensorMetadata.from(metadata)
50+
.withNewComponent("total", sumMsg, true, "mW", false)
51+
.build();
52+
totalIndex = numComponents;
53+
} else {
54+
shouldComputeTotals = false;
55+
measuresNb = numComponents;
56+
this.metadata = metadata;
57+
totalIndex = -1;
58+
}
59+
3860
measures = new double[measuresNb][DEFAULT_SIZE];
3961
timestamps = new long[DEFAULT_SIZE];
40-
totalIndex = numComponents;
4162
// we don't need to record the total component as a non-zero component since it's almost never zero and we compute the std dev separately
4263
nonZeroComponents = new BitSet(numComponents);
43-
totalComponents = sensorMetadata.totalComponents();
44-
45-
final var sumMsg = Arrays.stream(totalComponents)
46-
.mapToObj(i -> sensorMetadata.metadataFor(i).name())
47-
.collect(Collectors.joining("+", "Aggregated total from (", ")"));
48-
49-
// todo: compute total component properly (same unit, convert to base unit all components)
50-
this.sensorMetadata = SensorMetadata.from(sensorMetadata)
51-
.withNewComponent("total", sumMsg, true, "mW", false)
52-
.build();
5364
}
5465

5566
@Override
@@ -59,7 +70,7 @@ public int numberOfSamples() {
5970

6071
@Override
6172
public SensorMetadata metadata() {
62-
return sensorMetadata;
73+
return metadata;
6374
}
6475

6576
public void recordMeasure(double[] components) {
@@ -75,17 +86,20 @@ public void recordMeasure(double[] components) {
7586
}
7687

7788
// record min / max totals
78-
final var recordedTotal = Compute.sumOfSelectedComponents(components, totalComponents);
79-
recordComponentValue(totalIndex, recordedTotal, timestamp);
80-
accumulatedTotal += recordedTotal;
81-
if (recordedTotal < minTotal) {
82-
minTotal = recordedTotal;
83-
}
84-
if (recordedTotal > maxTotal) {
85-
maxTotal = recordedTotal;
89+
if (shouldComputeTotals) {
90+
final double recordedTotal = Compute.sumOfSelectedComponents(components, metadata.totalComponents());
91+
recordComponentValue(totalIndex, recordedTotal, timestamp);
92+
accumulatedTotal += recordedTotal;
93+
if (recordedTotal < minTotal) {
94+
minTotal = recordedTotal;
95+
}
96+
if (recordedTotal > maxTotal) {
97+
maxTotal = recordedTotal;
98+
}
99+
processors.recordTotal(recordedTotal, timestamp);
86100
}
87101

88-
processors.recordMeasure(components, recordedTotal, timestamp);
102+
processors.recordMeasure(components, timestamp);
89103
}
90104

91105
private void recordComponentValue(int component, double value, long timestamp) {
@@ -180,9 +194,19 @@ public Processors processors() {
180194
public void registerProcessorFor(int component, ComponentProcessor processor) {
181195
if (processor != null) {
182196
if (Processors.empty == processors) {
183-
processors = new DefaultProcessors(sensorMetadata.componentCardinality());
197+
processors = new DefaultProcessors(metadata.componentCardinality());
184198
}
185199
processors.registerProcessorFor(component, processor);
186200
}
187201
}
202+
203+
@Override
204+
public void registerTotalProcessor(ComponentProcessor processor) {
205+
if (processor != null) {
206+
if (Processors.empty == processors) {
207+
processors = new DefaultProcessors(metadata.componentCardinality());
208+
}
209+
processors.registerTotalProcessor(processor);
210+
}
211+
}
188212
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,6 @@ record TimestampedMeasures(long timestamp, double[] measures) {
5757
Processors processors();
5858

5959
void registerProcessorFor(int component, ComponentProcessor processor);
60+
61+
void registerTotalProcessor(ComponentProcessor processor);
6062
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,14 @@ public void registerProcessorFor(int component, ComponentProcessor processor) {
102102
processors.registerProcessorFor(component, processor);
103103
}
104104
}
105+
106+
@Override
107+
public void registerTotalProcessor(ComponentProcessor processor) {
108+
if (processor != null) {
109+
if (Processors.empty == processors) {
110+
processors = new DefaultProcessors(metadata().componentCardinality());
111+
}
112+
processors.registerTotalProcessor(processor);
113+
}
114+
}
105115
}

measure/src/test/java/net/laprun/sustainability/power/measure/OngoingPowerMeasureTest.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,22 @@
1616

1717
public class OngoingPowerMeasureTest {
1818
private final static SensorMetadata metadata = SensorMetadata
19-
.withNewComponent("cp1", null, true, null, false)
20-
.withNewComponent("cp2", null, true, null, false)
21-
.withNewComponent("cp3", null, true, null, false)
19+
.withNewComponent("cp1", null, true, "mW", true)
20+
.withNewComponent("cp2", null, true, "W", true)
21+
.withNewComponent("cp3", null, true, "kW", true)
2222
.build();
2323

24+
@Test
25+
void checkThatTotalComponentIsProperlyAdded() {
26+
final var metadata = SensorMetadata
27+
.withNewComponent("cp1", null, true, null, false)
28+
.withNewComponent("cp2", null, true, null, false)
29+
.withNewComponent("cp3", null, true, null, false)
30+
.build();
31+
var measure = new OngoingPowerMeasure(metadata);
32+
assertThat(measure.metadata().totalComponents()).isEmpty();
33+
}
34+
2435
@Test
2536
void testBasics() {
2637
final var m1c1 = 10.0;
@@ -58,6 +69,13 @@ void testBasics() {
5869
assertThat(measure.getMeasuresFor(1)).hasValue(new double[] { m1c2, m2c2, m3c2 });
5970
assertThat(measure.getMeasuresFor(2)).isEmpty();
6071

72+
var measures = measure.getNthTimestampedMeasures(0);
73+
assertThat(measures.measures()).isEqualTo(new double[] { m1c1, m1c2, m1c3, m1total });
74+
measures = measure.getNthTimestampedMeasures(1);
75+
assertThat(measures.measures()).isEqualTo(new double[] { m2c1, m2c2, m2c3, m2total });
76+
measures = measure.getNthTimestampedMeasures(2);
77+
assertThat(measures.measures()).isEqualTo(new double[] { m3c1, m3c2, m3c3, m3total });
78+
6179
assertEquals(m1c1 + m1c2 + m1c3 + m2c1 + m2c2 + m2c3 + m3c1 + m3c2 + m3c3, measure.total());
6280
assertEquals(Stream.of(m1total, m2total, m3total).min(Double::compareTo).orElseThrow(), measure.minMeasuredTotal());
6381
assertEquals(Stream.of(m1total, m2total, m3total).max(Double::compareTo).orElseThrow(), measure.maxMeasuredTotal());
@@ -74,6 +92,9 @@ void processorsShouldBeCalled() {
7492
final var measure = new OngoingPowerMeasure(metadata);
7593
measure.registerProcessorFor(0, new TestComponentProcessor());
7694

95+
// check that we can also associate a processor for the totals
96+
measure.registerTotalProcessor(new TestComponentProcessor());
97+
7798
final var components = new double[metadata.componentCardinality()];
7899
components[0] = m1c1;
79100
components[1] = m1c2;
@@ -86,11 +107,19 @@ void processorsShouldBeCalled() {
86107
final var processors = measure.processors();
87108
assertThat(processors.processorsFor(0)).hasSize(1);
88109
assertThat(processors.processorsFor(1)).isEmpty();
89-
final var maybeProc = processors.processorFor(0, TestComponentProcessor.class);
110+
assertThat(processors.processorsFor(2)).isEmpty();
111+
112+
var maybeProc = processors.processorFor(0, TestComponentProcessor.class);
90113
assertThat(maybeProc).isPresent();
91-
final var processor = maybeProc.get();
114+
var processor = maybeProc.get();
92115
assertThat(processor.values.getFirst().value()).isEqualTo(m1c1);
93116
assertThat(processor.values.getLast().value()).isEqualTo(m2c1);
117+
118+
maybeProc = processors.totalProcessor().map(TestComponentProcessor.class::cast);
119+
assertThat(maybeProc).isPresent();
120+
processor = maybeProc.get();
121+
assertThat(processor.values.getFirst().value()).isEqualTo(m1c1 + m1c2);
122+
assertThat(processor.values.getLast().value()).isEqualTo(m2c1 + m2c2);
94123
}
95124

96125
private static class TestComponentProcessor implements ComponentProcessor {

metadata/pom.xml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
<dependency>
1717
<groupId>eu.hoefel</groupId>
1818
<artifactId>units</artifactId>
19-
<version>4.1.1</version>
19+
</dependency>
20+
<dependency>
21+
<groupId>org.assertj</groupId>
22+
<artifactId>assertj-core</artifactId>
2023
</dependency>
2124
</dependencies>
2225

@@ -37,4 +40,4 @@
3740
</plugins>
3841
</pluginManagement>
3942
</build>
40-
</project>
43+
</project>

0 commit comments

Comments
 (0)