Compare commits

..

No commits in common. "02314a3309faf3e76ec141861093aaeaf84e9b57" and "00e84bd99a2ccf25363034ac9de7eeba916867a8" have entirely different histories.

2 changed files with 138 additions and 188 deletions

View File

@ -1,82 +0,0 @@
// Copyright 2023 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @typedef PlotList
* @type {object}
* @property {Array=} Series
* @property {Array=} Scalar
* @property {Array=} StreamInstant - Timestamps are in seconds
* @property {Array=} Stream - Timestamps are in nanoseconds
*/
/**
* @typedef QueryData
* @type {object}
* @property {object} yaxes
* @property {?string} legend_orientation
* @property {Array<PlotList>} plots
*/
/**
* @typedef HeaderOrCell
* @type {object}
* @property {array} values
* @property {{color: string}=} fill
* @property {object=} font
* @property {string=} font.family
* @property {number=} font.size
* @property {string=} font.color
* @property {{width: number, color: string}=} line
* @property {Array<number>=} columnwidth
*/
/**
* @typedef TableTrace
* @type {object}
* @property {string=} name
* @property {string} type
* @property {string=} mode
* @property {HeaderOrCell} header
* @property {HeaderOrCell} cells - An Array of columns for the table.
* @property {string=} xaxis
* @property {string=} yaxis
*/
/**
* @typedef GraphTrace
* @type {object}
* @property {string=} name
* @property {string=} fill
* @property type {string}
* @property {string=} mode
* @property {Array} x
* @property {Array} y
* @property {string=} xaxis
* @property {string=} yaxis
*/
/**
* @typedef PlotTrace
* @type {(TableTrace|GraphTrace)}
*/
/**
* @typedef PlotMeta
* @type {object}
* @property {string=} name_format
* @property {string=} yaxis
* @property {("tonexty"|"tozeroy"|"tonextx"|"tozerox"|"toself"|"tonext")=} fill
*/

View File

@ -12,6 +12,66 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @typedef PlotList
* @type {object}
* @property {Array=} Series
* @property {Array=} Scalar
* @property {Array<{timestamp: string, line: string}>=} StreamInstant - Timestamps are in seconds
* @property {Array<{timestamp: string, line: string}>=} Stream - Timestamps are in nanoseconds
*/
/**
* @typedef QueryData
* @type {object}
* @property {object} yaxes
* @property {?string} legend_orientation
* @property {Array<PlotList>} plots
*/
/**
* @typedef HeaderOrCell
* @type {object}
* @property {array} values
* @property {{color: string}=} fill
* @property {object=} font
* @property {string=} font.family
* @property {number=} font.size
* @property {string=} font.color
* @property {{width: number, color: string}=} line
* @property {Array<number>=} columnwidth
*/
/**
* @typedef TableTrace
* @type {object}
* @property {string=} name
* @property {string} type
* @property {string=} mode
* @property {HeaderOrCell} header
* @property {HeaderOrCell} cells - An Array of columns for the table.
* @property {string=} xaxis
* @property {string=} yaxis
*/
/**
* @typedef GraphTrace
* @type {object}
* @property {string=} name
* @property {string=} fill
* @property type {string}
* @property {string=} mode
* @property {Array} x
* @property {Array} y
* @property {string=} xaxis
* @property {string=} yaxis
*/
/**
* @typedef PlotTrace
* @type {(TableTrace|GraphTrace)}
*/
/**
* Map ansi terminal codes to html color codes.
* @param {string} line
@ -233,7 +293,7 @@ export class GraphPlot extends HTMLElement {
/**
* Formats the name for the plot trace.
* @param {PlotMeta} meta
* @param {{name_format: ?string}} meta
* @param {Map<string, string>} labels
* @return string
*/
@ -277,9 +337,8 @@ export class GraphPlot extends HTMLElement {
const element = document.createElement("div");
const select = document.createElement("select");
select.setAttribute("name", id);
// TODO(jwall): This is how you set boolean attributes. Use the attribute name... :-(
// TODO(jwall): This is how you set boolean attributes. Use the attribute named... :-(
select.setAttribute("multiple", "multiple");
select.setAttribute("size", "3");
const optElement = document.createElement("option");
const optValue = "Select All: " + key;
optElement.innerText = optValue;
@ -381,100 +440,6 @@ export class GraphPlot extends HTMLElement {
};
}
/**
* @param {any} triple
*/
buildSeriesPlot(triple) {
const labels = /** @type {Map<String, String>} */(triple[0]);
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
return null;
}
}
const meta = /** @type {PlotMeta} */(triple[1]);
var yaxis = meta.yaxis || "y";
// https://plotly.com/javascript/reference/layout/yaxis/
const series = triple[2];
const trace = /** @type GraphTrace */({
type: "scatter",
mode: "lines+text",
x: [],
y: [],
// We always share the x axis for timeseries graphs.
xaxis: "x",
yaxis: yaxis,
//yhoverformat: yaxis.tickformat,
});
if (meta.fill) {
trace.fill = meta.fill;
}
var name = this.formatName(meta, labels);
if (name) { trace.name = name; }
for (const point of series) {
trace.x.push(new Date(point.timestamp * 1000));
trace.y.push(point.value);
}
return trace;
}
/**
* @param {any} triple
*/
buildScalarPlot(triple) {
const labels = /** @type {Map<String,String>} */(triple[0]);
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
return null;
}
}
const meta = /** @type {PlotMeta} */(triple[1]);
const series = triple[2];
const trace = /** @type GraphTrace */({
type: "bar",
x: [],
y: [],
yhoverformat: meta["d3_tick_format"],
});
var name = this.formatName(meta, labels);
if (name) { trace.name = name; }
trace.y.push(series.value);
trace.x.push(trace.name);
return trace;
}
/**
* @param {Array} stream
*/
buildStreamPlot(stream) {
const dateColumn = [];
const metaColumn = [];
const logColumn = [];
loopStream: for (const pair of stream) {
const labels = pair[0];
var labelList = [];
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
continue loopStream;
}
labelList.push(`${label}:${labels[label]}`);
}
const labelsName = labelList.join("<br>");
const lines = pair[1];
for (const line of lines) {
// For streams the timestamps are in nanoseconds
let timestamp = new Date(line.timestamp / 1000000);
dateColumn.push(timestamp.toISOString());
metaColumn.push(labelsName);
logColumn.push(ansiToHtml(line.line));
}
}
return [dateColumn, metaColumn, logColumn];
}
/**
* Update the graph with new data.
*
@ -517,19 +482,62 @@ export class GraphPlot extends HTMLElement {
var nextYaxis = this.yaxisNameGenerator();
if (subplot.Series) {
// https://plotly.com/javascript/reference/scatter/
for (const triple of subplot.Series) {
const trace = this.buildSeriesPlot(triple);
if (trace) {
traces.push(trace);
loopSeries: for (const triple of subplot.Series) {
const labels = triple[0];
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
continue loopSeries;
}
}
const meta = triple[1];
var yaxis = meta.yaxis || "y";
// https://plotly.com/javascript/reference/layout/yaxis/
const series = triple[2];
const trace = /** @type GraphTrace */({
type: "scatter",
mode: "lines+text",
x: [],
y: [],
// We always share the x axis for timeseries graphs.
xaxis: "x",
yaxis: yaxis,
//yhoverformat: yaxis.tickformat,
});
if (meta.fill) {
trace.fill = meta.fill;
}
var name = this.formatName(meta, labels);
if (name) { trace.name = name; }
for (const point of series) {
trace.x.push(new Date(point.timestamp * 1000));
trace.y.push(point.value);
}
traces.push(trace);
}
} else if (subplot.Scalar) {
// https://plotly.com/javascript/reference/bar/
for (const triple of subplot.Scalar) {
const trace = this.buildScalarPlot(triple);
if (trace) {
traces.push(trace);
loopScalar: for (const triple of subplot.Scalar) {
const labels = triple[0];
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
continue loopScalar;
}
}
const meta = triple[1];
const series = triple[2];
const trace = /** @type GraphTrace */({
type: "bar",
x: [],
y: [],
yhoverformat: meta["d3_tick_format"],
});
var name = this.formatName(meta, labels);
if (name) { trace.name = name; }
trace.y.push(series.value);
trace.x.push(trace.name);
traces.push(trace);
}
} else if (subplot.Stream) {
// TODO(jwall): It would be nice if scroll behavior would handle replots better.
@ -550,7 +558,31 @@ export class GraphPlot extends HTMLElement {
fill: { color: layout.plot_bgcolor }
},
});
const [dateColumn, metaColumn, logColumn] = this.buildStreamPlot(subplot.Stream);
const dateColumn = [];
const metaColumn = [];
const logColumn = [];
loopStream: for (const pair of subplot.Stream) {
const labels = pair[0];
var labelList = [];
for (var label in labels) {
var show = this.#filteredLabelSets[label];
if (show && !show.includes(labels[label])) {
continue loopStream;
}
labelList.push(`${label}:${labels[label]}`);
}
const labelsName = labelList.join("<br>");
const lines = pair[1];
// TODO(jwall): Headers
for (const line of lines) {
// For streams the timestamps are in nanoseconds
let timestamp = new Date(line.timestamp / 1000000);
dateColumn.push(timestamp.toISOString());
metaColumn.push(labelsName);
logColumn.push(ansiToHtml(line.line));
}
}
trace.cells.values.push(dateColumn);
trace.cells.values.push(metaColumn);
trace.cells.values.push(logColumn);