diff --git a/deployment/src/main/resources/dev-ui/qwc-power-info.js b/deployment/src/main/resources/dev-ui/qwc-power-info.js
index 37cd8d7..6f69553 100644
--- a/deployment/src/main/resources/dev-ui/qwc-power-info.js
+++ b/deployment/src/main/resources/dev-ui/qwc-power-info.js
@@ -16,7 +16,8 @@ export class QwcPowerInfo extends QwcHotReloadElement {
_remoteMetadata: {state: true},
_localMetadata: {state: true},
_status: {state: true},
- _running: {state: true}
+ _running: {state: true},
+ _measure: {state: true},
};
constructor() {
@@ -46,14 +47,30 @@ export class QwcPowerInfo extends QwcHotReloadElement {
${this.renderStartOrStop()}
${this.metadata(this._localMetadata, "Local synthetic components (if any)", "No ongoing measure")}
${this.metadata(this._remoteMetadata, "System power metadata", "Couldn't retrieve metadata")}
+ ${this.displayMeasures()}
-
- `;
+ `;
} else {
return html`Info unavailable`;
}
}
+ displayMeasures() {
+ if (this._measure) {
+ return html`
+
+
+ Measure ${this._measure.result} (${this._measure.samplesCount} samples)
+
+
+
+ ${this._measure.measures.map(measure => html`- ${measure}
`)}
+
+
+ `
+ }
+ }
+
renderStartOrStop() {
let iconType = this._running ? "stop" : "play";
let label = this._running ? "Stop" : "Start";
@@ -75,7 +92,7 @@ export class QwcPowerInfo extends QwcHotReloadElement {
}
_metadata(metadata, emptyMsg) {
- if (Object.keys(metadata).length !== 0) {
+ if (metadata !== undefined && Object.keys(metadata).length !== 0) {
return html`
${metadata.map(component => html`- ${this.component(component)}
`)}
`;
} else {
return html`${emptyMsg}`;
@@ -91,16 +108,16 @@ export class QwcPowerInfo extends QwcHotReloadElement {
}
_startOrStop() {
- let action = this._running ? "stop" : "start";
- this.jsonRpc.startOrStop({start: !this._running}).then(jsonRpcResponse => {
- let outcome = jsonRpcResponse.result;
- if (!outcome) {
- notifier.showErrorMessage("Couldn't " + action + " power measure");
- } else {
- this.hotReload();
- // keep the notification open indefinitely if we're stopped to be able to see the results
- notifier.showInfoMessage(outcome, "bottom-start", action === "stop" ? 15 : 5);
+ let stop = this._running;
+ this.jsonRpc.startOrStop({start: !stop}).then(jsonRpcResponse => {
+ let msg = "Started";
+ if (stop) {
+ this._measure = jsonRpcResponse.result;
+ msg = "Stopped (" + this._measure.samplesCount + " samples taken)";
}
+
+ this.hotReload();
+ notifier.showInfoMessage(msg);
});
}
}
diff --git a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/PowerMeasurer.java b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/PowerMeasurer.java
index 35440d2..6f26458 100644
--- a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/PowerMeasurer.java
+++ b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/PowerMeasurer.java
@@ -2,6 +2,7 @@
import java.lang.management.ManagementFactory;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -36,6 +37,13 @@ public PowerMeasurer(ServerSampler sampler) {
this.withErrorHandler(null);
}
+ @SuppressWarnings("unused")
+ public static double cpuShareOfJVMProcess() {
+ final var processCpuLoad = osBean.getProcessCpuLoad();
+ final var cpuLoad = osBean.getCpuLoad();
+ return (processCpuLoad < 0 || cpuLoad <= 0) ? 0 : processCpuLoad / cpuLoad;
+ }
+
public ServerSampler sampler() {
return sampler;
}
@@ -48,13 +56,6 @@ public Metadata measureMetadata(Function completed) {
sampler.withCompletedHandler(completed);
return this;
@@ -82,9 +83,7 @@ public void start(long durationInSeconds, long frequencyInMilliseconds) {
}
}
- public void stop() {
- if (isRunning()) {
- sampler.stop();
- }
+ public Optional stop() {
+ return isRunning() ? Optional.of(sampler.stop()) : Optional.empty();
}
}
diff --git a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java
index 0b15cfe..9b0b136 100644
--- a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java
+++ b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java
@@ -49,9 +49,15 @@ public static class TotalStoppedPowerMeasure extends StoppedPowerMeasure {
private final double max;
private final double avg;
private final double stdDev;
+ private final OngoingPowerMeasure measure; // todo: remove
+
+ public TotalStoppedPowerMeasure(TotalStoppedPowerMeasure other) {
+ this(other.underlyingMeasure(), other.total, other.min, other.max, other.avg, other.stdDev);
+ }
public TotalStoppedPowerMeasure(OngoingPowerMeasure powerMeasure, double total, double min, double max, double avg, double stdDev) {
super(powerMeasure);
+ this.measure = powerMeasure;
this.total = total;
this.min = min;
this.max = max;
@@ -84,6 +90,10 @@ public String toString() {
return String.format("total: %s (min: %s / max: %s / avg: %s / σ: %s)", withUnit(total), withUnit(min), withUnit(max), withUnit(avg), withUnit(stdDev));
}
+ public OngoingPowerMeasure underlyingMeasure() {
+ return measure;
+ }
+
public static String withUnit(double mWValue) {
var unit = "mW";
double value = mWValue;
@@ -201,7 +211,7 @@ public OngoingPowerMeasure start(long durationInSeconds, long frequencyInMillise
public void stopOnError(Throwable e) {
// ignore HttpClosedException todo: figure out why this exception occurs in the first place!
- if (!(e instanceof HttpClosedException)) {
+ if (!(e instanceof HttpClosedException) && errorHandler != null) {
errorHandler.accept(e);
}
status = "error: measure failed (" + e.getMessage() + ")";
@@ -243,7 +253,9 @@ public synchronized TotalStoppedPowerMeasure stop() {
final var stats = totalStats.statistics();
final var measured = new TotalStoppedPowerMeasure(measure, stats.getSum(), stats.getMin(), stats.getMax(), stats.getMean(), stats.getStandardDeviation());
measure = null;
- completed.accept(measured);
+ if (completed != null) {
+ completed.accept(measured);
+ }
status = "stopped";
return measured;
}
diff --git a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/devui/PowerService.java b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/devui/PowerService.java
index 59a9242..03366a0 100644
--- a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/devui/PowerService.java
+++ b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/devui/PowerService.java
@@ -3,22 +3,17 @@
import java.util.List;
import java.util.function.Function;
-import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import net.laprun.sustainability.power.SensorMetadata;
+import net.laprun.sustainability.power.analysis.DescriptiveStatisticsComponentProcessor;
import net.laprun.sustainability.power.quarkus.runtime.PowerMeasurer;
+import net.laprun.sustainability.power.quarkus.runtime.ServerSampler;
public class PowerService {
public static final Function converter = cm -> new ComponentMetadata(cm.name(), cm.index(), cm.description(), cm.unitAsSymbol());
@Inject
PowerMeasurer measurer;
- private String status;
-
- @PostConstruct
- public void init() {
- measurer.withCompletedHandler((stoppedMeasure) -> status = stoppedMeasure.toString());
- }
public boolean isRunning() {
return measurer.isRunning();
@@ -38,13 +33,36 @@ public List localMetadata() {
public record ComponentMetadata(String name, int index, String description, String unit) {}
- public String startOrStop(boolean start) {
+ public Result startOrStop(boolean start) {
if(start) {
measurer.start(0, 500);
- return "Started";
+ return null;
} else {
- measurer.stop();
- return "Stopped: " + status;
+ return measurer.stop().map(Result::new).orElse(null);
+ }
+ }
+
+ public static class Result extends ServerSampler.TotalStoppedPowerMeasure {
+
+ public Result(ServerSampler.TotalStoppedPowerMeasure stoppedMeasure) {
+ super(stoppedMeasure);
+ }
+
+ public String getResult() {
+ return toString();
+ }
+
+ public int getSamplesCount() {
+ return numberOfSamples();
+ }
+
+ public double[] getMeasures() {
+ // todo: this should be made easier
+ return underlyingMeasure().processors().processorsFor(4).stream()
+ .findFirst()
+ .map(DescriptiveStatisticsComponentProcessor.class::cast)
+ .map(dscp -> dscp.statistics().getValues())
+ .orElse(null);
}
}
}