Skip to content

Commit a5008df

Browse files
committed
feat: add MeasureProcessor to process full measure (such as total)
Fixes #160
1 parent 8ace0ff commit a5008df

File tree

14 files changed

+259
-267
lines changed

14 files changed

+259
-267
lines changed

analysis/src/test/java/ComputeTest.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,12 @@ void averageShouldWork() {
7878
final var m3c3 = 0.0;
7979

8080
final var measure = new OngoingPowerMeasure(metadata);
81-
measure.registerProcessorFor(0, new MeanComponentProcessor());
82-
measure.registerProcessorFor(1, new MeanComponentProcessor());
83-
measure.registerProcessorFor(2, new MeanComponentProcessor());
81+
final var avgProc1 = new MeanComponentProcessor();
82+
measure.registerProcessorFor(0, avgProc1);
83+
final var avgProc2 = new MeanComponentProcessor();
84+
measure.registerProcessorFor(1, avgProc2);
85+
final var avgProc3 = new MeanComponentProcessor();
86+
measure.registerProcessorFor(2, avgProc3);
8487

8588
final var components = new double[metadata.componentCardinality()];
8689
components[0] = m1c1;
@@ -104,17 +107,13 @@ void averageShouldWork() {
104107

105108
assertEquals((m1c1 + m2c1 + m3c1) / 3, c1Avg, 0.0001,
106109
"Average did not match the expected value");
107-
final var processors = measure.processors();
108-
assertEquals(c1Avg,
109-
processors.processorFor(0, MeanComponentProcessor.class).map(MeanComponentProcessor::mean).orElseThrow());
110+
assertEquals(c1Avg, avgProc1.mean());
110111
assertEquals((m1c2 + m2c2 + m3c2) / 3, c2Avg, 0.0001,
111112
"Average did not match the expected value");
112-
assertEquals(c2Avg,
113-
processors.processorFor(1, MeanComponentProcessor.class).map(MeanComponentProcessor::mean).orElseThrow());
113+
assertEquals(c2Avg, avgProc2.mean());
114114
assertEquals(0, c3Avg, 0.0001,
115115
"Average did not match the expected value");
116-
assertEquals(0,
117-
processors.processorFor(2, MeanComponentProcessor.class).map(MeanComponentProcessor::mean).orElseThrow());
116+
assertEquals(0, avgProc3.mean());
118117

119118
System.out.println(PowerMeasure.asString(measure));
120119
}
Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
package net.laprun.sustainability.power.analysis;
22

3-
public interface ComponentProcessor {
3+
public interface ComponentProcessor extends Outputable {
44
default void recordComponentValue(double value, long timestamp) {
55
}
6-
7-
default String name() {
8-
return this.getClass().getSimpleName();
9-
}
10-
11-
default String output() {
12-
return "";
13-
}
146
}

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

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
import java.util.ArrayList;
44
import java.util.List;
55
import java.util.Objects;
6-
import java.util.Optional;
76

87
import net.laprun.sustainability.power.SensorMetadata;
98

109
public class DefaultProcessors implements Processors {
1110
private final List<ComponentProcessor>[] processors;
12-
private ComponentProcessor totalProcessor;
11+
private List<MeasureProcessor> measureProcessors;
1312

1413
@SuppressWarnings("unchecked")
1514
public DefaultProcessors(int componentCardinality) {
@@ -18,6 +17,10 @@ public DefaultProcessors(int componentCardinality) {
1817

1918
@Override
2019
public void recordMeasure(double[] components, long timestamp) {
20+
if (measureProcessors != null) {
21+
measureProcessors.forEach(processor -> processor.recordMeasure(components, timestamp));
22+
}
23+
2124
for (var index = 0; index < components.length; index++) {
2225
final var fIndex = index;
2326
final var componentProcessors = processors[index];
@@ -39,43 +42,31 @@ public void registerProcessorFor(int componentIndex, ComponentProcessor processo
3942
}
4043
}
4144

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-
5945
@Override
6046
public List<ComponentProcessor> processorsFor(int componentIndex) {
6147
final var componentProcessors = processors[componentIndex];
6248
return Objects.requireNonNullElseGet(componentProcessors, List::of);
6349
}
6450

6551
@Override
66-
public <T extends ComponentProcessor> Optional<T> processorFor(int componentIndex, Class<T> expectedType) {
67-
final var componentProcessors = processors[componentIndex];
68-
if (componentProcessors != null) {
69-
return componentProcessors.stream().filter(expectedType::isInstance).map(expectedType::cast).findFirst();
70-
} else {
71-
return Optional.empty();
52+
public void registerMeasureProcessor(MeasureProcessor processor) {
53+
if (processor != null) {
54+
if (measureProcessors == null) {
55+
measureProcessors = new ArrayList<>();
56+
}
57+
measureProcessors.add(processor);
7258
}
7359
}
7460

61+
@Override
62+
public List<MeasureProcessor> measureProcessors() {
63+
return Objects.requireNonNullElseGet(measureProcessors, List::of);
64+
}
65+
7566
@Override
7667
public String output(SensorMetadata metadata) {
7768
StringBuilder builder = new StringBuilder();
78-
builder.append("# Processors\n");
69+
builder.append("# Component Processors\n");
7970
for (int i = 0; i < processors.length; i++) {
8071
final var name = metadata.metadataFor(i).name();
8172
builder.append(" - ").append(name).append(" component:\n");
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package net.laprun.sustainability.power.analysis;
2+
3+
public interface MeasureProcessor extends Outputable {
4+
default void recordMeasure(double[] measure, long timestamp) {
5+
}
6+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package net.laprun.sustainability.power.analysis;
2+
3+
public interface Outputable {
4+
default String name() {
5+
return this.getClass().getSimpleName();
6+
}
7+
8+
default String output() {
9+
return "";
10+
}
11+
}

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,31 @@
11
package net.laprun.sustainability.power.analysis;
22

33
import java.util.List;
4-
import java.util.Optional;
54

65
import net.laprun.sustainability.power.SensorMetadata;
76

7+
@SuppressWarnings("unused")
88
public interface Processors {
99
Processors empty = new Processors() {
1010
};
1111

1212
default void recordMeasure(double[] components, long timestamp) {
1313
}
1414

15-
default void recordTotal(double total, long timestamp) {
16-
}
17-
1815
default void registerProcessorFor(int componentIndex, ComponentProcessor processor) {
1916
}
2017

21-
default void registerTotalProcessor(ComponentProcessor processor) {
18+
default List<ComponentProcessor> processorsFor(int componentIndex) {
19+
return List.of();
2220
}
2321

24-
default Optional<ComponentProcessor> totalProcessor() {
25-
return Optional.empty();
22+
default void registerMeasureProcessor(MeasureProcessor processor) {
2623
}
2724

28-
default List<ComponentProcessor> processorsFor(int componentIndex) {
25+
default List<MeasureProcessor> measureProcessors() {
2926
return List.of();
3027
}
3128

32-
default <T extends ComponentProcessor> Optional<T> processorFor(int componentIndex, Class<T> expectedType) {
33-
return Optional.empty();
34-
}
35-
3629
default String output(SensorMetadata metadata) {
3730
return "";
3831
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package net.laprun.sustainability.power.analysis;
2+
3+
import java.util.Arrays;
4+
import java.util.Objects;
5+
import java.util.function.Function;
6+
import java.util.stream.Collectors;
7+
8+
import net.laprun.sustainability.power.Errors;
9+
import net.laprun.sustainability.power.SensorMetadata;
10+
import net.laprun.sustainability.power.SensorUnit;
11+
12+
public class TotalMeasureProcessor implements MeasureProcessor {
13+
private final String name;
14+
private double minTotal = Double.MAX_VALUE;
15+
private double maxTotal;
16+
private double accumulatedTotal;
17+
private final Function<double[], Double> formula;
18+
19+
public TotalMeasureProcessor(SensorMetadata metadata, int... totalComponentIndices) {
20+
Objects.requireNonNull(totalComponentIndices, "Must specify component indices that will aggregated in a total");
21+
22+
final var errors = new Errors();
23+
final var totalComponents = Arrays.stream(totalComponentIndices)
24+
.mapToObj(i -> toTotalComponent(metadata, i, errors))
25+
.toArray(TotalComponent[]::new);
26+
name = Arrays.stream(totalComponents)
27+
.map(TotalComponent::name)
28+
.collect(Collectors.joining(" + ", "Aggregated total from (", ")"));
29+
formula = components -> {
30+
double result = 0;
31+
for (var totalComponent : totalComponents) {
32+
result += components[totalComponent.index] * totalComponent.factor;
33+
}
34+
return result;
35+
};
36+
37+
if (errors.hasErrors()) {
38+
throw new IllegalArgumentException(errors.formatErrors());
39+
}
40+
}
41+
42+
private TotalComponent toTotalComponent(SensorMetadata metadata, int index, Errors errors) {
43+
final var cm = metadata.metadataFor(index);
44+
final var name = cm.name();
45+
if (!cm.isWattCommensurable()) {
46+
errors.addError("Component " + name
47+
+ " is not commensurate with a power measure. It needs to be expressible in Watts.");
48+
}
49+
50+
final var factor = SensorUnit.of(cm.unit()).getUnit().factor();
51+
return new TotalComponent(name, index, factor);
52+
}
53+
54+
public double total() {
55+
return accumulatedTotal;
56+
}
57+
58+
public double minMeasuredTotal() {
59+
return minTotal;
60+
}
61+
62+
public double maxMeasuredTotal() {
63+
return maxTotal;
64+
}
65+
66+
private record TotalComponent(String name, int index, double factor) {
67+
}
68+
69+
@Override
70+
public String name() {
71+
return name;
72+
}
73+
74+
@Override
75+
public String output() {
76+
return MeasureProcessor.super.output();
77+
}
78+
79+
@Override
80+
public void recordMeasure(double[] measure, long timestamp) {
81+
final double recordedTotal = formula.apply(measure);
82+
accumulatedTotal += recordedTotal;
83+
if (recordedTotal < minTotal) {
84+
minTotal = recordedTotal;
85+
}
86+
if (recordedTotal > maxTotal) {
87+
maxTotal = recordedTotal;
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)