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
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package net.laprun.sustainability.power.measure;

import java.time.Duration;
import java.util.Arrays;

public enum Cursor {
;

public static PartialCursor cursorOver(long[] timestamps, long timestamp, Duration duration, long initialOffset,
long averagePeriodHint) {
// adjusted timestamp for modding
System.out.println(Arrays.toString(timestamps));
System.out.println("timestamp = " + timestamp);
final var timestampForDiv = timestamp - initialOffset;
final var durationAsMs = duration.toMillis();
System.out.println("durationAsMs = " + durationAsMs);

// cannot find an interval for a timestamp that is before the recording started
if (timestampForDiv < 0) {
return PartialCursor.empty;
}

if (timestamps.length < 2) {
// if we don't have a sample period, use the full measure
double ratio = 1.0;
if (averagePeriodHint > 0) {
ratio = (double) durationAsMs / averagePeriodHint;
}
return new PartialCursor(0, 0, ratio, ratio);
}

// estimate sample period based on 2 samples interval
if (averagePeriodHint <= 0) {
averagePeriodHint = timestamps[1] - timestamps[0];
}

// first, find potential first sample based on timestamp
int startIndex = (int) Math.floorDiv(timestampForDiv, averagePeriodHint);
System.out.println("startIndex = " + startIndex);
int endIndex = (int) Math.floorDiv(timestampForDiv + durationAsMs, averagePeriodHint);
System.out.println("endIndex = " + endIndex);

if (startIndex == endIndex) {
final long previousTimestamp = startIndex == 0 ? initialOffset : timestamps[startIndex - 1];
final long slotDuration = timestamps[startIndex] - previousTimestamp;
var ratio = (double) durationAsMs / slotDuration;
return new PartialCursor(startIndex, endIndex, ratio, -1);
}

// get the index with the timestamp right after the one we're looking for since what we're interested in is the portion of the measure that gets recorded after the timestamp we want
long afterTimestamp = timestamps[startIndex];
final long startOffset = afterTimestamp - timestamp;
double startRatio = 0;
if (startOffset > 0) {
startRatio = (double) startOffset / (afterTimestamp - timestamps[startIndex - 1]);
}

// look for the index that records the first timestamp that's after the one we're looking for added to the duration
afterTimestamp = timestamps[endIndex];
final long slotDuration = afterTimestamp - timestamps[endIndex - 1];
final long endOffset = slotDuration - (afterTimestamp - timestamp - durationAsMs);
double endRatio = 0;
if (endOffset > 0) {
endRatio = (double) endOffset / slotDuration;
}

return new PartialCursor(startIndex, endIndex, startRatio, endRatio);
}

public record PartialCursor(int startIndex, int endIndex, double firstMeasureRatio, double lastMeasureRatio) {

public static final PartialCursor empty = new PartialCursor(-1, -1, 0.0, 0.0);

public double sum(double[] values) {
if (values == null || values.length == 0 || this == empty || values.length < startIndex + endIndex) {
return 0.0;
}

if (startIndex == endIndex) {
return values[startIndex] * firstMeasureRatio;
}

double sum = values[startIndex] * firstMeasureRatio;
for (int i = startIndex + 1; i < endIndex; i++) {
sum += values[i];
}
sum += values[endIndex] * lastMeasureRatio;

return sum;
}

public double[] viewOf(double[] values) {
if (values == null || values.length == 0 || this == empty || values.length < startIndex + endIndex) {
return new double[0];
}

if (startIndex == endIndex) {
return new double[] { values[startIndex] * firstMeasureRatio };
}

final int len = endIndex - startIndex + 1;
final double[] view = new double[len];
view[0] = values[startIndex] * firstMeasureRatio;
System.arraycopy(values, startIndex + 1, view, 1, len - 1 - 1);
view[len - 1] = values[endIndex] * lastMeasureRatio;
return view;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,21 @@ public class OngoingPowerMeasure extends ProcessorAware implements PowerMeasure
private final List<RegisteredSyntheticComponent> syntheticComponents;
private int samples;
private long[] timestamps;
private long samplePeriod;

public OngoingPowerMeasure(SensorMetadata metadata, SyntheticComponent... syntheticComponents) {
this(metadata, -1, syntheticComponents);
}

public OngoingPowerMeasure(SensorMetadata metadata, long samplePeriod, SyntheticComponent... syntheticComponents) {
super(Processors.empty);

startedAt = System.currentTimeMillis();
final var numComponents = metadata.componentCardinality();
measures = new double[numComponents][DEFAULT_SIZE];
nonZeroComponents = new BitSet(numComponents);
timestamps = new long[DEFAULT_SIZE];
this.samplePeriod = samplePeriod;

if (syntheticComponents != null) {
final var builder = SensorMetadata.from(metadata);
Expand All @@ -47,7 +53,7 @@ public OngoingPowerMeasure(SensorMetadata metadata, SyntheticComponent... synthe
}

@Override
public int numberOfSamples() {
public synchronized int numberOfSamples() {
return samples;
}

Expand All @@ -57,19 +63,20 @@ public SensorMetadata metadata() {
}

public void recordMeasure(double[] components) {
samples++;
ensureArraysSize();

final var timestamp = System.currentTimeMillis();
timestamps[samples - 1] = timestamp;

for (int component = 0; component < components.length; component++) {
final var componentValue = components[component];
// record that the value is not zero
if (componentValue != 0) {
nonZeroComponents.set(component);
synchronized (this) {
timestamps[samples - 1] = timestamp;
for (int component = 0; component < components.length; component++) {
final var componentValue = components[component];
// record that the value is not zero
if (componentValue != 0) {
nonZeroComponents.set(component);
}
measures[component][samples - 1] = componentValue;
}
measures[component][samples - 1] = componentValue;
}

final var processors = processors();
Expand All @@ -82,7 +89,8 @@ public void recordMeasure(double[] components) {
}
}

private void ensureArraysSize() {
private synchronized void ensureArraysSize() {
samples++;
final int currentSize = timestamps.length;
if (currentSize <= samples) {
final var newSize = currentSize * 2;
Expand Down Expand Up @@ -123,12 +131,16 @@ private static boolean targetComponentExistsAndIsRecorder(int component, Registe
}

@Override
public TimestampedMeasures getNthTimestampedMeasures(int n) {
public synchronized TimestampedMeasures getNthTimestampedMeasures(int n) {
n = Math.min(n, samples - 1);
final var result = new double[measures.length];
for (int i = 0; i < measures.length; i++) {
result[i] = measures[i][n];
}
return new TimestampedMeasures(timestamps[n], result);
}

public Cursor.PartialCursor getCursorOver(long timestamp, Duration duration) {
return Cursor.cursorOver(timestamps, timestamp, duration, startedAt, samplePeriod);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package net.laprun.sustainability.power.measure;

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

import java.time.Duration;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class CursorTest {

@ParameterizedTest
@ValueSource(longs = { -1, 0, 100, 98, 105 })
void cursorOverSimple(long periodHint) {
final var timestamps = new long[] { 100, 200, 300, 400, 500, 600, 700, 800, 900 };

final var cursor = Cursor.cursorOver(timestamps, 225, Duration.ofMillis(540 - 225), 0,
periodHint);

assertEquals(2, cursor.startIndex());
assertEquals(5, cursor.endIndex());
assertEquals(0.75, cursor.firstMeasureRatio(), 0.0001);
assertEquals(0.4, cursor.lastMeasureRatio(), 0.0001);

final var measures = new double[] { 100, 200, 300, 400, 500, 600, 700, 800, 900 };
assertEquals(300 * 0.75 + 400 + 500 + 0.4 * 600, cursor.sum(measures), 0.0001);
final double[] view = cursor.viewOf(measures);
assertEquals(4, view.length);
assertEquals(300 * 0.75, view[0], 0.0001);
assertEquals(400, view[1], 0.0001);
assertEquals(500, view[2], 0.0001);
assertEquals(600 * 0.4, view[3], 0.0001);
}

@Test
void cursorOverOneMeasure() {
final var timestamps = new long[] { 100, 200, 300, 400, 500, 600, 700, 800, 900 };

final var cursor = Cursor.cursorOver(timestamps, 1, Duration.ofMillis(10), 0,
100);

assertEquals(0, cursor.startIndex());
assertEquals(0, cursor.endIndex());
assertEquals(0.1, cursor.firstMeasureRatio(), 0.0001);
assertEquals(-1, cursor.lastMeasureRatio(), 0.0001);

final var measures = new double[] { 100, 200, 300, 400, 500, 600, 700, 800, 900 };
assertEquals(100 * 0.1, cursor.sum(measures), 0.0001);
final double[] view = cursor.viewOf(measures);
assertEquals(1, view.length);
assertEquals(100 * 0.1, view[0], 0.0001);
}
}