diff --git a/web/frontend/src/Analysis.root.svelte b/web/frontend/src/Analysis.root.svelte
index 2e6f5b5..1cfadc9 100644
--- a/web/frontend/src/Analysis.root.svelte
+++ b/web/frontend/src/Analysis.root.svelte
@@ -1,10 +1,11 @@
+
+{#if data.length > 0}
+
+{:else}
+ Cannot render histogram: No data!
+{/if}
+
+
diff --git a/web/frontend/src/plots/MetricPlot.svelte b/web/frontend/src/plots/MetricPlot.svelte
index c0440fd..509672c 100644
--- a/web/frontend/src/plots/MetricPlot.svelte
+++ b/web/frontend/src/plots/MetricPlot.svelte
@@ -26,16 +26,17 @@
import { getContext, onMount, onDestroy } from 'svelte'
import { Card } from 'sveltestrap'
+ export let metric
+ export let scope = 'node'
+ export let resources = []
export let width
export let height
export let timestep
export let series
+ export let useStatsSeries = null
export let statisticsSeries = null
export let cluster
export let subCluster
- export let metric
- export let useStatsSeries = null
- export let scope = 'node'
export let isShared = false
if (useStatsSeries == null)
@@ -53,6 +54,70 @@
const backgroundColors = { normal: 'rgba(255, 255, 255, 1.0)', caution: 'rgba(255, 128, 0, 0.3)', alert: 'rgba(255, 0, 0, 0.3)' }
const thresholds = findThresholds(metricConfig, scope, typeof subCluster == 'string' ? cluster.subClusters.find(sc => sc.name == subCluster) : subCluster)
+ // converts the legend into a simple tooltip
+ function legendAsTooltipPlugin({ className, style = { backgroundColor:"rgba(255, 249, 196, 0.92)", color: "black" } } = {}) {
+ let legendEl;
+ const dataSize = series.length
+
+ function init(u, opts) {
+ legendEl = u.root.querySelector(".u-legend");
+
+ legendEl.classList.remove("u-inline");
+ className && legendEl.classList.add(className);
+
+ uPlot.assign(legendEl.style, {
+ textAlign: "left",
+ pointerEvents: "none",
+ display: "none",
+ position: "absolute",
+ left: 0,
+ top: 0,
+ zIndex: 100,
+ boxShadow: "2px 2px 10px rgba(0,0,0,0.5)",
+ ...style
+ });
+
+ // conditional hide series color markers:
+ if (useStatsSeries === true || // Min/Max/Avg Self-Explanatory
+ dataSize === 1 || // Only one Y-Dataseries
+ dataSize > 6 ){ // More than 6 Y-Dataseries
+ const idents = legendEl.querySelectorAll(".u-marker");
+ for (let i = 0; i < idents.length; i++)
+ idents[i].style.display = "none";
+ }
+
+ const overEl = u.over;
+ overEl.style.overflow = "visible";
+
+ // move legend into plot bounds
+ overEl.appendChild(legendEl);
+
+ // show/hide tooltip on enter/exit
+ overEl.addEventListener("mouseenter", () => {legendEl.style.display = null;});
+ overEl.addEventListener("mouseleave", () => {legendEl.style.display = "none";});
+
+ // let tooltip exit plot
+ // overEl.style.overflow = "visible";
+ }
+
+ function update(u) {
+ const { left, top } = u.cursor;
+ const width = u.over.querySelector(".u-legend").offsetWidth;
+ legendEl.style.transform = "translate(" + (left - width - 15) + "px, " + (top + 15) + "px)";
+ }
+
+ if (dataSize <= 12 || useStatsSeries === true) {
+ return {
+ hooks: {
+ init: init,
+ setCursor: update,
+ }
+ }
+ } else { // Setting legend-opts show/live as object with false here will not work ...
+ return {}
+ }
+ }
+
function backgroundColor() {
if (clusterCockpitConfig.plot_general_colorBackground == false
|| !thresholds
@@ -93,7 +158,7 @@
? (statisticsSeries.max.reduce((max, x) => Math.max(max, x), thresholds.normal) || thresholds.normal)
: (series.reduce((max, series) => Math.max(max, series.statistics?.max), thresholds.normal) || thresholds.normal)
: null
- const plotSeries = [{}]
+ const plotSeries = [{label: 'Runtime', value: (u, ts, sidx, didx) => didx == null ? null : formatTime(ts)}]
const plotData = [new Array(longestSeries)]
for (let i = 0; i < longestSeries; i++) // TODO: Cache/Reuse this array?
plotData[0][i] = i * timestep
@@ -103,9 +168,9 @@
plotData.push(statisticsSeries.min)
plotData.push(statisticsSeries.max)
plotData.push(statisticsSeries.mean)
- plotSeries.push({ scale: 'y', width: lineWidth, stroke: 'red' })
- plotSeries.push({ scale: 'y', width: lineWidth, stroke: 'green' })
- plotSeries.push({ scale: 'y', width: lineWidth, stroke: 'black' })
+ plotSeries.push({ label: 'min', scale: 'y', width: lineWidth, stroke: 'red' })
+ plotSeries.push({ label: 'max', scale: 'y', width: lineWidth, stroke: 'green' })
+ plotSeries.push({ label: 'mean', scale: 'y', width: lineWidth, stroke: 'black' })
plotBands = [
{ series: [2,3], fill: 'rgba(0,255,0,0.1)' },
{ series: [3,1], fill: 'rgba(255,0,0,0.1)' }
@@ -114,6 +179,9 @@
for (let i = 0; i < series.length; i++) {
plotData.push(series[i].data)
plotSeries.push({
+ label: scope === 'node' ? resources[i].hostname :
+ // scope === 'accelerator' ? resources[0].accelerators[i] :
+ scope + ' #' + (i+1),
scale: 'y',
width: lineWidth,
stroke: lineColor(i, series.length)
@@ -124,6 +192,9 @@
const opts = {
width,
height,
+ plugins: [
+ legendAsTooltipPlugin()
+ ],
series: plotSeries,
axes: [
{
@@ -177,8 +248,11 @@
x: { time: false },
y: maxY ? { range: [0., maxY * 1.1] } : {}
},
- cursor: { show: false },
- legend: { show: false, live: false }
+ legend : { // Display legend until max 12 Y-dataseries
+ show: (series.length <= 12 || useStatsSeries === true) ? true : false,
+ live: (series.length <= 12 || useStatsSeries === true) ? true : false
+ },
+ cursor: { drag: { x: true, y: true } }
}
// console.log(opts)
@@ -249,16 +323,21 @@
}