Skip to content

Commit 90b89b0

Browse files
committed
feat: add support for processable synthetic components
1 parent 5391781 commit 90b89b0

File tree

7 files changed

+137
-11
lines changed

7 files changed

+137
-11
lines changed

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,22 @@ public void recordMeasure(double[] components, long timestamp) {
2323
}
2424

2525
for (var index = 0; index < components.length; index++) {
26-
final var fIndex = index;
27-
final var componentProcessors = processors[index];
28-
if (componentProcessors != null) {
29-
componentProcessors.forEach(proc -> proc.recordComponentValue(components[fIndex], timestamp));
30-
}
26+
recordComponentValue(components[index], timestamp, index);
27+
}
28+
}
29+
30+
private void recordComponentValue(double value, long timestamp, int componentIndex) {
31+
final var componentProcessors = processors[componentIndex];
32+
if (componentProcessors != null) {
33+
componentProcessors.forEach(proc -> proc.recordComponentValue(value, timestamp));
3134
}
3235
}
3336

37+
@Override
38+
public void recordSyntheticComponentValue(double syntheticValue, long timestamp, int componentIndex) {
39+
recordComponentValue(syntheticValue, componentIndex, componentIndex);
40+
}
41+
3442
@Override
3543
public void registerProcessorFor(int componentIndex, ComponentProcessor processor) {
3644
if (processor != null) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,7 @@ default List<MeasureProcessor> measureProcessors() {
2929
default String output(SensorMetadata metadata) {
3030
return "";
3131
}
32+
33+
default void recordSyntheticComponentValue(double syntheticValue, long timestamp, int componentIndex) {
34+
}
3235
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package net.laprun.sustainability.power.analysis;
2+
3+
import net.laprun.sustainability.power.SensorMetadata;
4+
5+
public class RegisteredSyntheticComponent implements SyntheticComponent {
6+
private final SyntheticComponent syntheticComponent;
7+
private final int computedIndex;
8+
9+
public RegisteredSyntheticComponent(SyntheticComponent syntheticComponent, int computedIndex) {
10+
this.syntheticComponent = syntheticComponent;
11+
this.computedIndex = computedIndex;
12+
}
13+
14+
@Override
15+
public SensorMetadata.ComponentMetadata metadata() {
16+
return syntheticComponent.metadata();
17+
}
18+
19+
@Override
20+
public double synthesizeFrom(double[] components, long timestamp) {
21+
return syntheticComponent.synthesizeFrom(components, timestamp);
22+
}
23+
24+
public int computedIndex() {
25+
return computedIndex;
26+
}
27+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package net.laprun.sustainability.power.analysis;
2+
3+
import net.laprun.sustainability.power.SensorMetadata;
4+
5+
public interface SyntheticComponent {
6+
7+
SensorMetadata.ComponentMetadata metadata();
8+
9+
double synthesizeFrom(double[] components, long timestamp);
10+
}

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

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,51 @@
11
package net.laprun.sustainability.power.measure;
22

33
import java.time.Duration;
4+
import java.util.Arrays;
45
import java.util.BitSet;
6+
import java.util.List;
57
import java.util.Optional;
68
import java.util.stream.DoubleStream;
79
import java.util.stream.IntStream;
810
import java.util.stream.Stream;
911

1012
import net.laprun.sustainability.power.SensorMetadata;
1113
import net.laprun.sustainability.power.analysis.Processors;
14+
import net.laprun.sustainability.power.analysis.RegisteredSyntheticComponent;
15+
import net.laprun.sustainability.power.analysis.SyntheticComponent;
1216

1317
public class OngoingPowerMeasure extends ProcessorAware implements PowerMeasure {
1418
private static final int DEFAULT_SIZE = 32;
1519
private final SensorMetadata metadata;
1620
private final long startedAt;
1721
private final BitSet nonZeroComponents;
1822
private final double[][] measures;
23+
private final List<RegisteredSyntheticComponent> syntheticComponents;
1924
private int samples;
2025
private long[] timestamps;
2126

22-
public OngoingPowerMeasure(SensorMetadata metadata) {
27+
public OngoingPowerMeasure(SensorMetadata metadata, SyntheticComponent... syntheticComponents) {
2328
super(Processors.empty);
2429

2530
startedAt = System.currentTimeMillis();
26-
this.metadata = metadata;
27-
2831
final var numComponents = metadata.componentCardinality();
2932
measures = new double[numComponents][DEFAULT_SIZE];
30-
timestamps = new long[DEFAULT_SIZE];
31-
// 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
3233
nonZeroComponents = new BitSet(numComponents);
34+
timestamps = new long[DEFAULT_SIZE];
35+
36+
if (syntheticComponents != null) {
37+
final var builder = SensorMetadata.from(metadata);
38+
for (var component : syntheticComponents) {
39+
builder.withNewComponent(component.metadata());
40+
}
41+
this.metadata = builder.build();
42+
this.syntheticComponents = Arrays.stream(syntheticComponents)
43+
.map(sc -> new RegisteredSyntheticComponent(sc, this.metadata.metadataFor(sc.metadata().name()).index()))
44+
.toList();
45+
} else {
46+
this.syntheticComponents = List.of();
47+
this.metadata = metadata;
48+
}
3349
}
3450

3551
@Override
@@ -54,7 +70,14 @@ public void recordMeasure(double[] components) {
5470
recordComponentValue(component, componentValue, timestamp);
5571
}
5672

57-
processors().recordMeasure(components, timestamp);
73+
final var processors = processors();
74+
processors.recordMeasure(components, timestamp);
75+
76+
if (!syntheticComponents.isEmpty()) {
77+
syntheticComponents.forEach(sc -> processors.recordSyntheticComponentValue(sc.synthesizeFrom(components, timestamp),
78+
timestamp, sc.computedIndex()));
79+
80+
}
5881
}
5982

6083
private void recordComponentValue(int component, double value, long timestamp) {

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
import org.junit.jupiter.api.Test;
1111

1212
import net.laprun.sustainability.power.SensorMetadata;
13+
import net.laprun.sustainability.power.SensorUnit;
1314
import net.laprun.sustainability.power.analysis.ComponentProcessor;
1415
import net.laprun.sustainability.power.analysis.MeasureProcessor;
16+
import net.laprun.sustainability.power.analysis.SyntheticComponent;
1517

1618
public class OngoingPowerMeasureTest {
1719
private final static SensorMetadata metadata = SensorMetadata
@@ -97,6 +99,42 @@ void processorsShouldBeCalled() {
9799
assertThat(measureProc.values.getLast().measures()).isEqualTo(new double[] { m2c1, m2c2, 0 });
98100
}
99101

102+
@Test
103+
void syntheticComponentsShouldWork() {
104+
final var random = Random.from(RandomGenerator.getDefault());
105+
final var m1c1 = random.nextDouble();
106+
final var m2c1 = random.nextDouble();
107+
108+
final var doublerName = "doubler";
109+
final var doubler = new SyntheticComponent() {
110+
@Override
111+
public SensorMetadata.ComponentMetadata metadata() {
112+
return new SensorMetadata.ComponentMetadata(doublerName, "doubler desc", true, SensorUnit.mW);
113+
}
114+
115+
@Override
116+
public double synthesizeFrom(double[] components, long timestamp) {
117+
return components[0] * 2;
118+
}
119+
};
120+
121+
final var measure = new OngoingPowerMeasure(metadata, doubler);
122+
final var testProc = new TestComponentProcessor();
123+
// need to get updated metadata
124+
final var doublerIndex = measure.metadata().metadataFor(doublerName).index();
125+
measure.registerProcessorFor(doublerIndex, testProc);
126+
127+
final var components = new double[metadata.componentCardinality()];
128+
components[0] = m1c1;
129+
measure.recordMeasure(components);
130+
131+
components[0] = m2c1;
132+
measure.recordMeasure(components);
133+
134+
assertThat(testProc.values.getFirst().value()).isEqualTo(m1c1 * 2);
135+
assertThat(testProc.values.getLast().value()).isEqualTo(m2c1 * 2);
136+
}
137+
100138
private static class TestComponentProcessor implements ComponentProcessor {
101139
final List<PowerMeasure.TimestampedValue> values = new ArrayList<>();
102140

metadata/src/main/java/net/laprun/sustainability/power/SensorMetadata.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,15 @@ public Builder withDocumentation(String documentation) {
202202
public SensorMetadata build() {
203203
return new SensorMetadata(components, documentation);
204204
}
205+
206+
public Builder withNewComponent(ComponentMetadata metadata) {
207+
if (-1 == metadata.index) {
208+
metadata = new SensorMetadata.ComponentMetadata(metadata.name, currentIndex++, metadata.description,
209+
metadata.isAttributed, metadata.unit);
210+
}
211+
components.add(metadata);
212+
return this;
213+
}
205214
}
206215

207216
/**
@@ -229,6 +238,14 @@ public record ComponentMetadata(String name, int index, String description, bool
229238
}
230239
}
231240

241+
/**
242+
* Creates a ComponentMetadata that will be automatically assigned an index whenever added to a {@link SensorMetadata},
243+
* based on the contextual order of how other components have been added.
244+
*/
245+
public ComponentMetadata(String name, String description, boolean isAttributed, SensorUnit unit) {
246+
this(name, -1, description, isAttributed, unit);
247+
}
248+
232249
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
233250
public ComponentMetadata(@JsonProperty("name") String name, @JsonProperty("index") int index,
234251
@JsonProperty("description") String description,

0 commit comments

Comments
 (0)