// originally copied from https://gist.github.com/amir-arad/3c140b3c44b81dcc1ec108e109355c27 export type LineData = { name: string; y: number[]; x: number[]; }; export type GraphPointInput = { annotate: (t: string) => unknown; addtoLine: (n: string, v: number) => unknown; }; export function limitPercision(num: number) { return Math.round(num * 1e2) / 1e2; } export class LinesDataCollector { public readonly lines: Record = {}; public readonly annotations = Array.of<[string, number]>(); private lastAnnotation = ''; constructor(private metrics: Record number> = {}) {} private getLine(name: string) { return this.lines[name] || (this.lines[name] = { name, y: Array.of(), x: Array.of() }); } /** * build a [plotly](https://plotly.com/javascript/) graph configuration * works well with [vscode Debug Visualizer](https://marketplace.visualstudio.com/items?itemName=hediet.debug-visualizer) */ public graph() { return { kind: { plotly: true }, data: Object.values(this.lines), layout: { showlegend: true, legend: { orientation: 'h' }, annotations: this.annotations.map(([text, x], i) => { const y = (i % 2) * 2 - 1; return { x, y, xref: 'x', yref: 'y', text }; }), }, }; } public snapshot(){ const res : Record = {}; for (const line of Object.values(this.lines)){ for (const idx in line.x){ const yValue = line.y[idx]; if (yValue){ res[line.name + '@' + line.x[idx]] = line.y[idx]; } } } return res; } newPoint(time: number): GraphPointInput { const addtoLine = (name: string, value: number) => { const lineData = this.getLine(name); lineData.y.push(limitPercision(value)); lineData.x.push(time); }; const annotate = (text: string) => { if (this.lastAnnotation !== text) { this.lastAnnotation = text; this.annotations.push([text, time]); } }; for (const [name, getVal] of Object.entries(this.metrics)) { addtoLine(name, getVal()); } return { annotate, addtoLine, }; } }