Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions analysis/src/test/java/ComputeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

public class ComputeTest {
private final static SensorMetadata metadata = SensorMetadata
.withNewComponent("cp1", null, true, null, false)
.withNewComponent("cp2", null, true, null, false)
.withNewComponent("cp3", null, true, null, false)
.withNewComponent("cp1", null, true, "mW", false)
.withNewComponent("cp2", null, true, "mW", false)
.withNewComponent("cp3", null, true, "mW", false)
.build();

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ public class TotalMeasureProcessor implements MeasureProcessor {
private double maxTotal;
private double accumulatedTotal;
private final Function<double[], Double> formula;
private final SensorUnit expectedResultUnit;

public TotalMeasureProcessor(SensorMetadata metadata, int... totalComponentIndices) {
public TotalMeasureProcessor(SensorMetadata metadata, SensorUnit expectedResultUnit, int... totalComponentIndices) {
Objects.requireNonNull(totalComponentIndices, "Must specify component indices that will aggregated in a total");
this.expectedResultUnit = Objects.requireNonNull(expectedResultUnit, "Must specify expected result unit");

final var errors = new Errors();
final var totalComponents = Arrays.stream(totalComponentIndices)
Expand All @@ -42,25 +44,30 @@ public TotalMeasureProcessor(SensorMetadata metadata, int... totalComponentIndic
private TotalComponent toTotalComponent(SensorMetadata metadata, int index, Errors errors) {
final var cm = metadata.metadataFor(index);
final var name = cm.name();
if (!cm.isWattCommensurable()) {
final var unit = cm.unit();
if (!unit.isCommensurableWith(expectedResultUnit)) {
errors.addError("Component " + name
+ " is not commensurate with a power measure. It needs to be expressible in Watts.");
+ " is not commensurable with the expected base unit: " + expectedResultUnit);
}

final var factor = SensorUnit.of(cm.unit()).getUnit().factor();
final var factor = unit.factor();
return new TotalComponent(name, index, factor);
}

public double total() {
return accumulatedTotal;
return convertToExpectedUnit(accumulatedTotal);
}

public double minMeasuredTotal() {
return minTotal;
return convertToExpectedUnit(minTotal);
}

public double maxMeasuredTotal() {
return maxTotal;
return convertToExpectedUnit(maxTotal);
}

private double convertToExpectedUnit(double value) {
return value * expectedResultUnit.base().conversionFactorTo(expectedResultUnit);
}

private record TotalComponent(String name, int index, double factor) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package net.laprun.sustainability.power.analysis;

import static org.junit.jupiter.api.Assertions.*;

import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

import net.laprun.sustainability.power.SensorMetadata;
import net.laprun.sustainability.power.SensorUnit;

class TotalMeasureProcessorTest {

@Test
void totalShouldFailIfAllComponentsAreNotCommensurable() {
final var inError = "cp2";
final var metadata = SensorMetadata
.withNewComponent("cp1", null, true, "mW", true)
.withNewComponent(inError, null, true, "mJ", true)
.build();

final var expectedResultUnit = SensorUnit.W;
final var e = assertThrows(IllegalArgumentException.class,
() -> new TotalMeasureProcessor(metadata, expectedResultUnit, 0, 1));
assertTrue(e.getMessage().contains("Component " + inError
+ " is not commensurable with the expected base unit: " + expectedResultUnit));
}

@Test
void testTotal() {
final var metadata = SensorMetadata
.withNewComponent("cp1", null, true, "mW", true)
.withNewComponent("cp2", null, true, "mW", true)
.withNewComponent("cp3", null, true, "mW", true)
.build();

final var m1c1 = 10.0;
final var m1c2 = 12.0;
final var m1c3 = 0.0;
final var m1total = m1c1 + m1c2 + m1c3;
final var m2c1 = 8.0;
final var m2c2 = 17.0;
final var m2c3 = 0.0;
final var m2total = m2c1 + m2c2 + m2c3;
final var m3c1 = 5.0;
final var m3c2 = 5.0;
final var m3c3 = 0.0;
final var m3total = m3c1 + m3c2 + m3c3;

final var totalProc = new TotalMeasureProcessor(metadata, SensorUnit.of("mW"), 0, 1, 2);

final var components = new double[metadata.componentCardinality()];
components[0] = m1c1;
components[1] = m1c2;
components[2] = m1c3;
totalProc.recordMeasure(components, System.currentTimeMillis());

components[0] = m2c1;
components[1] = m2c2;
components[2] = m2c3;
totalProc.recordMeasure(components, System.currentTimeMillis());

components[0] = m3c1;
components[1] = m3c2;
components[2] = m3c3;
totalProc.recordMeasure(components, System.currentTimeMillis());

assertEquals(m1c1 + m1c2 + m1c3 + m2c1 + m2c2 + m2c3 + m3c1 + m3c2 + m3c3, totalProc.total());
assertEquals(Stream.of(m1total, m2total, m3total).min(Double::compareTo).orElseThrow(), totalProc.minMeasuredTotal());
assertEquals(Stream.of(m1total, m2total, m3total).max(Double::compareTo).orElseThrow(), totalProc.maxMeasuredTotal());
}

}
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package net.laprun.sustainability.power.measure;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

import net.laprun.sustainability.power.SensorMetadata;
import net.laprun.sustainability.power.analysis.ComponentProcessor;
import net.laprun.sustainability.power.analysis.TotalMeasureProcessor;

public class OngoingPowerMeasureTest {
private final static SensorMetadata metadata = SensorMetadata
Expand All @@ -25,9 +22,9 @@ public class OngoingPowerMeasureTest {
@Test
void checkThatTotalComponentIsProperlyAdded() {
final var metadata = SensorMetadata
.withNewComponent("cp1", null, true, null, false)
.withNewComponent("cp2", null, true, null, false)
.withNewComponent("cp3", null, true, null, false)
.withNewComponent("cp1", null, true, "mW", false)
.withNewComponent("cp2", null, true, "mW", false)
.withNewComponent("cp3", null, true, "mW", false)
.build();
var measure = new OngoingPowerMeasure(metadata);
assertThat(measure.metadata().totalComponents()).isEmpty();
Expand Down Expand Up @@ -75,46 +72,6 @@ void testBasics() {
assertThat(measures.measures()).isEqualTo(new double[] { m3c1, m3c2, m3c3 });
}

@Test
void testTotal() {
final var m1c1 = 10.0;
final var m1c2 = 12.0;
final var m1c3 = 0.0;
final var m1total = m1c1 + m1c2 + m1c3;
final var m2c1 = 8.0;
final var m2c2 = 17.0;
final var m2c3 = 0.0;
final var m2total = m2c1 + m2c2 + m2c3;
final var m3c1 = 5.0;
final var m3c2 = 5.0;
final var m3c3 = 0.0;
final var m3total = m3c1 + m3c2 + m3c3;

final var measure = new OngoingPowerMeasure(metadata);
final var totalProc = new TotalMeasureProcessor(metadata, 0, 1, 2);
measure.registerMeasureProcessor(totalProc);

final var components = new double[metadata.componentCardinality()];
components[0] = m1c1;
components[1] = m1c2;
components[2] = m1c3;
measure.recordMeasure(components);

components[0] = m2c1;
components[1] = m2c2;
components[2] = m2c3;
measure.recordMeasure(components);

components[0] = m3c1;
components[1] = m3c2;
components[2] = m3c3;
measure.recordMeasure(components);

assertEquals(m1c1 + m1c2 + m1c3 + m2c1 + m2c2 + m2c3 + m3c1 + m3c2 + m3c3, totalProc.total());
assertEquals(Stream.of(m1total, m2total, m3total).min(Double::compareTo).orElseThrow(), totalProc.minMeasuredTotal());
assertEquals(Stream.of(m1total, m2total, m3total).max(Double::compareTo).orElseThrow(), totalProc.maxMeasuredTotal());
}

@Test
void processorsShouldBeCalled() {
final var random = Random.from(RandomGenerator.getDefault());
Expand Down
4 changes: 0 additions & 4 deletions metadata/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
<description>Metadata model used by the power-server project, extracted to allow reuse in client projects</description>

<dependencies>
<dependency>
<groupId>eu.hoefel</groupId>
<artifactId>units</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
Expand All @@ -23,6 +22,7 @@ public class SensorMetadata {
private final Map<String, ComponentMetadata> components;
@JsonProperty("documentation")
private final String documentation;
// todo: remove
@JsonProperty("totalComponents")
private final int[] totalComponents;

Expand All @@ -42,6 +42,7 @@ public SensorMetadata(List<ComponentMetadata> components, String documentation)
final var errors = new Errors();
final var indices = new BitSet(cardinality);
final var totalIndices = new BitSet(cardinality);
final var baseUnit = new SensorUnit[1];
components.forEach(component -> {
// check that index is valid
final var index = component.index;
Expand All @@ -58,18 +59,6 @@ public SensorMetadata(List<ComponentMetadata> components, String documentation)
indices.set(index);
}

// check that component's unit is commensurable to Watts if included in total
if (component.isIncludedInTotal) {
if (indexValid) {
totalIndices.set(index);
}

if (!component.isWattCommensurable()) {
errors.addError("Component " + component.name
+ " is not commensurate with a power measure. It needs to be expressible in Watts.");
}
}

if (this.components.containsKey(component.name)) {
errors.addError("Multiple components are named '" + component.name + "': "
+ components.stream().filter(cm -> cm.name.equals(component.name)).toList());
Expand Down Expand Up @@ -100,7 +89,14 @@ public SensorMetadata(List<ComponentMetadata> components, String documentation)
this.totalComponents = totalComponents;
}

public static SensorMetadata.Builder withNewComponent(String name, String description, boolean isAttributed, String unit,
public static SensorMetadata.Builder withNewComponent(String name, String description, boolean isAttributed,
String unitSymbol,
boolean participatesInTotal) {
return new SensorMetadata.Builder().withNewComponent(name, description, isAttributed, unitSymbol, participatesInTotal);
}

public static SensorMetadata.Builder withNewComponent(String name, String description, boolean isAttributed,
SensorUnit unit,
boolean participatesInTotal) {
return new SensorMetadata.Builder().withNewComponent(name, description, isAttributed, unit, participatesInTotal);
}
Expand Down Expand Up @@ -207,7 +203,14 @@ public static class Builder {
private int currentIndex = 0;
private String documentation;

public Builder withNewComponent(String name, String description, boolean isAttributed, String unit,
public Builder withNewComponent(String name, String description, boolean isAttributed, String unitSymbol,
boolean isIncludedInTotal) {
components
.add(new ComponentMetadata(name, currentIndex++, description, isAttributed, unitSymbol, isIncludedInTotal));
return this;
}

public Builder withNewComponent(String name, String description, boolean isAttributed, SensorUnit unit,
boolean isIncludedInTotal) {
components.add(new ComponentMetadata(name, currentIndex++, description, isAttributed, unit, isIncludedInTotal));
return this;
Expand Down Expand Up @@ -240,23 +243,21 @@ public SensorMetadata build() {
* metric for that sensor. Components that take part of the total computation must use a unit commensurable with
* {@link SensorUnit#W}
*/
public record ComponentMetadata(String name, int index, String description, boolean isAttributed, String unit,
public record ComponentMetadata(String name, int index, String description, boolean isAttributed, SensorUnit unit,
boolean isIncludedInTotal) {

public ComponentMetadata {
if (name == null) {
throw new IllegalArgumentException("Component name cannot be null");
}
if (unit == null) {
throw new IllegalArgumentException("Component unit cannot be null");
}
}

/**
* Determines whether or not this component is measuring power (i.e. its value can be converted to Watts)
*
* @return {@code true} if this component's unit is commensurable to Watts, {@code false} otherwise
*/
@JsonIgnore
public boolean isWattCommensurable() {
return unit != null && SensorUnit.of(unit).isWattCommensurable();
public ComponentMetadata(String name, int index, String description, boolean isAttributed, String unitSymbol,
boolean isIncludedInTotal) {
this(name, index, description, isAttributed, SensorUnit.of(unitSymbol), isIncludedInTotal);
}
}
}
Loading
Loading