From 84e8bb8182ded80a0bcee8c187191e931f2e4fb4 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 20 Jan 2025 22:44:40 +0100 Subject: [PATCH] feat: initial display of recorded measures --- .../main/resources/dev-ui/qwc-power-info.js | 43 +++++++++++++------ .../power/quarkus/runtime/PowerMeasurer.java | 21 +++++---- .../power/quarkus/runtime/ServerSampler.java | 16 ++++++- .../quarkus/runtime/devui/PowerService.java | 40 ++++++++++++----- 4 files changed, 83 insertions(+), 37 deletions(-) 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``; } 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); } } }