Skip to content

Commit 181dcf4

Browse files
committed
feat: register processors per component, add processor output
Fixes #154
1 parent 42f203c commit 181dcf4

File tree

11 files changed

+268
-40
lines changed

11 files changed

+268
-40
lines changed

analysis/src/main/java/net/laprun/sustainability/power/analysis/MeanComponentProcessor.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,18 @@ public void recordComponentValue(double value, long timestamp) {
1010
count++;
1111
mean = mean == 0 ? value : (previousSize * mean + value) / count;
1212
}
13+
14+
public double mean() {
15+
return mean;
16+
}
17+
18+
@Override
19+
public String name() {
20+
return "mean";
21+
}
22+
23+
@Override
24+
public String output() {
25+
return "" + mean;
26+
}
1327
}

analysis/src/test/java/ComputeTest.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
import static org.junit.jupiter.api.Assertions.assertEquals;
22

3-
import java.util.List;
43
import java.util.Random;
54
import java.util.random.RandomGenerator;
65

76
import org.junit.jupiter.api.Test;
87

98
import net.laprun.sustainability.power.SensorMetadata;
109
import net.laprun.sustainability.power.analysis.Compute;
10+
import net.laprun.sustainability.power.analysis.MeanComponentProcessor;
1111
import net.laprun.sustainability.power.measure.OngoingPowerMeasure;
12+
import net.laprun.sustainability.power.measure.PowerMeasure;
1213

1314
public class ComputeTest {
14-
private final static SensorMetadata metadata = new SensorMetadata(List.of(), null) {
15-
16-
@Override
17-
public int componentCardinality() {
18-
return 3;
19-
}
20-
};
15+
private final static SensorMetadata metadata = SensorMetadata
16+
.withNewComponent("cp1", null, true, null, false)
17+
.withNewComponent("cp2", null, true, null, false)
18+
.withNewComponent("cp3", null, true, null, false)
19+
.build();
2120

2221
@Test
2322
void standardDeviationShouldWork() {
@@ -79,6 +78,9 @@ void averageShouldWork() {
7978
final var m3c3 = 0.0;
8079

8180
final var measure = new OngoingPowerMeasure(metadata);
81+
measure.registerProcessorFor(0, new MeanComponentProcessor());
82+
measure.registerProcessorFor(1, new MeanComponentProcessor());
83+
measure.registerProcessorFor(2, new MeanComponentProcessor());
8284

8385
final var components = new double[metadata.componentCardinality()];
8486
components[0] = m1c1;
@@ -102,9 +104,18 @@ void averageShouldWork() {
102104

103105
assertEquals((m1c1 + m2c1 + m3c1) / 3, c1Avg, 0.0001,
104106
"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());
105110
assertEquals((m1c2 + m2c2 + m3c2) / 3, c2Avg, 0.0001,
106111
"Average did not match the expected value");
112+
assertEquals(c2Avg,
113+
processors.processorFor(1, MeanComponentProcessor.class).map(MeanComponentProcessor::mean).orElseThrow());
107114
assertEquals(0, c3Avg, 0.0001,
108115
"Average did not match the expected value");
116+
assertEquals(0,
117+
processors.processorFor(2, MeanComponentProcessor.class).map(MeanComponentProcessor::mean).orElseThrow());
118+
119+
System.out.println(PowerMeasure.asString(measure));
109120
}
110121
}

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

Lines changed: 0 additions & 4 deletions
This file was deleted.

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@
33
public interface ComponentProcessor {
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+
}
614
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package net.laprun.sustainability.power.analysis;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Objects;
6+
import java.util.Optional;
7+
8+
import net.laprun.sustainability.power.SensorMetadata;
9+
10+
public class DefaultProcessors implements Processors {
11+
private final List<ComponentProcessor>[] processors;
12+
13+
@SuppressWarnings("unchecked")
14+
public DefaultProcessors(int componentCardinality) {
15+
this.processors = new List[componentCardinality];
16+
}
17+
18+
@Override
19+
public void recordMeasure(double[] components, double total, long timestamp) {
20+
for (var index = 0; index < components.length; index++) {
21+
final var fIndex = index;
22+
final var componentProcessors = processors[index];
23+
if (componentProcessors != null) {
24+
componentProcessors.forEach(proc -> proc.recordComponentValue(components[fIndex], timestamp));
25+
}
26+
}
27+
}
28+
29+
@Override
30+
public void registerProcessorFor(int componentIndex, ComponentProcessor processor) {
31+
if (processor != null) {
32+
var processorsForComponent = processors[componentIndex];
33+
if (processorsForComponent == null) {
34+
processorsForComponent = new ArrayList<>();
35+
processors[componentIndex] = processorsForComponent;
36+
}
37+
processorsForComponent.add(processor);
38+
}
39+
}
40+
41+
@Override
42+
public List<ComponentProcessor> processorsFor(int componentIndex) {
43+
final var componentProcessors = processors[componentIndex];
44+
return Objects.requireNonNullElseGet(componentProcessors, List::of);
45+
}
46+
47+
@Override
48+
public <T extends ComponentProcessor> Optional<T> processorFor(int componentIndex, Class<T> expectedType) {
49+
final var componentProcessors = processors[componentIndex];
50+
if (componentProcessors != null) {
51+
return componentProcessors.stream().filter(expectedType::isInstance).map(expectedType::cast).findFirst();
52+
} else {
53+
return Optional.empty();
54+
}
55+
}
56+
57+
@Override
58+
public String output(SensorMetadata metadata) {
59+
StringBuilder builder = new StringBuilder();
60+
builder.append("# Processors\n");
61+
for (int i = 0; i < processors.length; i++) {
62+
final var name = metadata.metadataFor(i).name();
63+
builder.append(" - ").append(name).append(" component:\n");
64+
for (var processor : processorsFor(i)) {
65+
builder.append(" * ").append(processor.name())
66+
.append(": ").append(processor.output()).append("\n");
67+
}
68+
}
69+
return builder.toString();
70+
}
71+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package net.laprun.sustainability.power.analysis;
2+
3+
import java.util.List;
4+
import java.util.Optional;
5+
6+
import net.laprun.sustainability.power.SensorMetadata;
7+
8+
public interface Processors {
9+
Processors empty = new Processors() {
10+
};
11+
12+
default void recordMeasure(double[] components, double total, long timestamp) {
13+
}
14+
15+
default void registerProcessorFor(int componentIndex, ComponentProcessor processor) {
16+
}
17+
18+
default List<ComponentProcessor> processorsFor(int componentIndex) {
19+
return List.of();
20+
}
21+
22+
default <T extends ComponentProcessor> Optional<T> processorFor(int componentIndex, Class<T> expectedType) {
23+
return Optional.empty();
24+
}
25+
26+
default String output(SensorMetadata metadata) {
27+
return "";
28+
}
29+
}

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

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package net.laprun.sustainability.power.measure;
22

33
import java.time.Duration;
4+
import java.util.Arrays;
45
import java.util.BitSet;
5-
import java.util.Objects;
66
import java.util.Optional;
7+
import java.util.stream.Collectors;
78
import java.util.stream.DoubleStream;
89
import java.util.stream.IntStream;
910
import java.util.stream.Stream;
1011

1112
import net.laprun.sustainability.power.SensorMetadata;
1213
import net.laprun.sustainability.power.analysis.ComponentProcessor;
14+
import net.laprun.sustainability.power.analysis.DefaultProcessors;
15+
import net.laprun.sustainability.power.analysis.Processors;
1316

1417
public class OngoingPowerMeasure implements PowerMeasure {
1518
private static final int DEFAULT_SIZE = 32;
@@ -19,15 +22,14 @@ public class OngoingPowerMeasure implements PowerMeasure {
1922
private final int[] totalComponents;
2023
private final int totalIndex;
2124
private final double[][] measures;
22-
private final ComponentProcessor[] analyzers;
2325
private double minTotal = Double.MAX_VALUE;
2426
private double maxTotal;
2527
private double accumulatedTotal;
2628
private int samples;
2729
private long[] timestamps;
30+
private Processors processors = Processors.empty;
2831

29-
public OngoingPowerMeasure(SensorMetadata sensorMetadata, ComponentProcessor... analyzers) {
30-
this.sensorMetadata = sensorMetadata;
32+
public OngoingPowerMeasure(SensorMetadata sensorMetadata) {
3133
startedAt = System.currentTimeMillis();
3234

3335
final var numComponents = sensorMetadata.componentCardinality();
@@ -39,7 +41,15 @@ public OngoingPowerMeasure(SensorMetadata sensorMetadata, ComponentProcessor...
3941
// 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
4042
nonZeroComponents = new BitSet(numComponents);
4143
totalComponents = sensorMetadata.totalComponents();
42-
this.analyzers = Objects.requireNonNullElseGet(analyzers, () -> new ComponentProcessor[0]);
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();
4353
}
4454

4555
@Override
@@ -54,28 +64,31 @@ public SensorMetadata metadata() {
5464

5565
public void recordMeasure(double[] components) {
5666
samples++;
67+
final var timestamp = System.currentTimeMillis();
5768
for (int component = 0; component < components.length; component++) {
5869
final var componentValue = components[component];
5970
// record that the value is not zero
6071
if (componentValue != 0) {
6172
nonZeroComponents.set(component);
6273
}
63-
recordComponentValue(component, componentValue);
74+
recordComponentValue(component, componentValue, timestamp);
6475
}
6576

6677
// record min / max totals
6778
final var recordedTotal = Compute.sumOfSelectedComponents(components, totalComponents);
68-
recordComponentValue(totalIndex, recordedTotal);
79+
recordComponentValue(totalIndex, recordedTotal, timestamp);
6980
accumulatedTotal += recordedTotal;
7081
if (recordedTotal < minTotal) {
7182
minTotal = recordedTotal;
7283
}
7384
if (recordedTotal > maxTotal) {
7485
maxTotal = recordedTotal;
7586
}
87+
88+
processors.recordMeasure(components, recordedTotal, timestamp);
7689
}
7790

78-
private void recordComponentValue(int component, double value) {
91+
private void recordComponentValue(int component, double value, long timestamp) {
7992
final var currentSize = measures[component].length;
8093
if (currentSize <= samples) {
8194
final var newSize = currentSize * 2;
@@ -88,12 +101,8 @@ private void recordComponentValue(int component, double value) {
88101
System.arraycopy(timestamps, 0, newTimestamps, 0, currentSize);
89102
timestamps = newTimestamps;
90103
}
91-
final var timestamp = System.currentTimeMillis();
92-
timestamps[component] = timestamp;
104+
timestamps[samples - 1] = timestamp;
93105
measures[component][samples - 1] = value;
94-
for (var analyzer : analyzers) {
95-
analyzer.recordComponentValue(value, timestamp);
96-
}
97106
}
98107

99108
@Override
@@ -163,7 +172,17 @@ public TimestampedMeasures getNthTimestampedMeasures(int n) {
163172
}
164173

165174
@Override
166-
public ComponentProcessor[] analyzers() {
167-
return analyzers;
175+
public Processors processors() {
176+
return processors;
177+
}
178+
179+
@Override
180+
public void registerProcessorFor(int component, ComponentProcessor processor) {
181+
if (processor != null) {
182+
if (Processors.empty == processors) {
183+
processors = new DefaultProcessors(sensorMetadata.componentCardinality());
184+
}
185+
processors.registerProcessorFor(component, processor);
186+
}
168187
}
169188
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import net.laprun.sustainability.power.SensorMetadata;
99
import net.laprun.sustainability.power.analysis.ComponentProcessor;
10+
import net.laprun.sustainability.power.analysis.Processors;
1011

1112
public interface PowerMeasure {
1213

@@ -15,9 +16,10 @@ static String asString(PowerMeasure measure) {
1516
final var durationInSeconds = measure.duration().getSeconds();
1617
final var samples = measure.numberOfSamples();
1718
final var measuredMilliWatts = measure.total();
18-
return String.format("%s [min: %.3f, max: %.3f] (%ds, %s samples)",
19+
return String.format("%s [min: %.3f, max: %.3f] (%ds, %s samples)\n---\n%s",
1920
readableWithUnit(measuredMilliWatts),
20-
measure.minMeasuredTotal(), measure.maxMeasuredTotal(), durationInSeconds, samples);
21+
measure.minMeasuredTotal(), measure.maxMeasuredTotal(), durationInSeconds, samples,
22+
measure.processors().output(measure.metadata()));
2123
}
2224

2325
static String readableWithUnit(double milliWatts) {
@@ -52,5 +54,7 @@ record TimestampedValue(long timestamp, double value) {
5254
record TimestampedMeasures(long timestamp, double[] measures) {
5355
}
5456

55-
ComponentProcessor[] analyzers();
57+
Processors processors();
58+
59+
void registerProcessorFor(int component, ComponentProcessor processor);
5660
}

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import net.laprun.sustainability.power.SensorMetadata;
99
import net.laprun.sustainability.power.analysis.ComponentProcessor;
10+
import net.laprun.sustainability.power.analysis.DefaultProcessors;
11+
import net.laprun.sustainability.power.analysis.Processors;
1012

1113
@SuppressWarnings("unused")
1214
public class StoppedPowerMeasure implements PowerMeasure {
@@ -16,7 +18,7 @@ public class StoppedPowerMeasure implements PowerMeasure {
1618
private final double total;
1719
private final double min;
1820
private final double max;
19-
private final ComponentProcessor[] processors;
21+
private Processors processors;
2022

2123
public StoppedPowerMeasure(OngoingPowerMeasure powerMeasure) {
2224
this.measure = powerMeasure;
@@ -26,7 +28,7 @@ public StoppedPowerMeasure(OngoingPowerMeasure powerMeasure) {
2628
this.max = powerMeasure.maxMeasuredTotal();
2729
this.samples = powerMeasure.numberOfSamples();
2830
final var cardinality = metadata().componentCardinality();
29-
processors = powerMeasure.analyzers();
31+
processors = powerMeasure.processors();
3032
}
3133

3234
@Override
@@ -87,7 +89,17 @@ private int ensureIndex(int upToIndex) {
8789
}
8890

8991
@Override
90-
public ComponentProcessor[] analyzers() {
92+
public Processors processors() {
9193
return processors;
9294
}
95+
96+
@Override
97+
public void registerProcessorFor(int component, ComponentProcessor processor) {
98+
if (processor != null) {
99+
if (Processors.empty == processors) {
100+
processors = new DefaultProcessors(metadata().componentCardinality());
101+
}
102+
processors.registerProcessorFor(component, processor);
103+
}
104+
}
93105
}

0 commit comments

Comments
 (0)