Remove large series models from reactive data in plots (#6961)

* remove series object from highlights

* remove series models from legend reactive data

* drawing all annotations at once is way faster

* fix multi annotations

* lots of reactive things depending on config

* make annotation lookup faster

* lint

* readd perf test

* address PR comments

* fix highlight typo

---------

Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
This commit is contained in:
Scott Bell
2023-09-06 18:32:36 +02:00
committed by GitHub
parent 0be106f29e
commit 8e917b2679
6 changed files with 265 additions and 180 deletions

View File

@ -32,7 +32,7 @@ const {
waitForPlotsToRender waitForPlotsToRender
} = require('../../appActions'); } = require('../../appActions');
test.describe.fixme('Plot Tagging Performance', () => { test.describe('Plot Tagging Performance', () => {
/** /**
* Given a canvas and a set of points, tags the points on the canvas. * Given a canvas and a set of points, tags the points on the canvas.
* @param {import('@playwright/test').Page} page * @param {import('@playwright/test').Page} page

View File

@ -78,8 +78,8 @@
:rectangles="rectangles" :rectangles="rectangles"
:highlights="highlights" :highlights="highlights"
:show-limit-line-labels="limitLineLabels" :show-limit-line-labels="limitLineLabels"
:annotated-points="annotatedPoints" :annotated-points-by-series="annotatedPointsBySeries"
:annotation-selections="annotationSelections" :annotation-selections-by-series="annotationSelectionsBySeries"
:hidden-y-axis-ids="hiddenYAxisIds" :hidden-y-axis-ids="hiddenYAxisIds"
:annotation-viewing-and-editing-allowed="annotationViewingAndEditingAllowed" :annotation-viewing-and-editing-allowed="annotationViewingAndEditingAllowed"
@plotReinitializeCanvas="initCanvas" @plotReinitializeCanvas="initCanvas"
@ -245,9 +245,9 @@ export default {
data() { data() {
return { return {
altPressed: false, altPressed: false,
annotatedPointsBySeries: {},
highlights: [], highlights: [],
annotatedPoints: [], annotationSelectionsBySeries: {},
annotationSelections: [],
annotationsEverLoaded: false, annotationsEverLoaded: false,
lockHighlightPoint: false, lockHighlightPoint: false,
yKeyOptions: [], yKeyOptions: [],
@ -256,8 +256,6 @@ export default {
plotHistory: [], plotHistory: [],
selectedXKeyOption: {}, selectedXKeyOption: {},
xKeyOptions: [], xKeyOptions: [],
seriesModels: [],
legend: {},
pending: 0, pending: 0,
isRealTime: this.openmct.time.isRealTime(), isRealTime: this.openmct.time.isRealTime(),
loaded: false, loaded: false,
@ -346,6 +344,8 @@ export default {
this.abortController = new AbortController(); this.abortController = new AbortController();
}, },
mounted() { mounted() {
this.seriesModels = [];
this.config = {};
this.yAxisIdVisibility = {}; this.yAxisIdVisibility = {};
this.offsetWidth = 0; this.offsetWidth = 0;
@ -357,7 +357,6 @@ export default {
this.setTimeContext = this.setTimeContext.bind(this); this.setTimeContext = this.setTimeContext.bind(this);
this.config = this.getConfig(); this.config = this.getConfig();
this.legend = this.config.legend;
this.yAxes = [ this.yAxes = [
{ {
id: this.config.yAxis.id, id: this.config.yAxis.id,
@ -455,7 +454,7 @@ export default {
const clickedOption = event.target.closest('.js-autocomplete-options') !== null; const clickedOption = event.target.closest('.js-autocomplete-options') !== null;
if (!clickedInsidePlot && !clickedInsideInspector && !clickedOption) { if (!clickedInsidePlot && !clickedInsideInspector && !clickedOption) {
this.rectangles = []; this.rectangles = [];
this.annotationSelections = []; this.annotationSelectionsBySeries = {};
this.selectPlot(); this.selectPlot();
document.body.removeEventListener('click', this.cancelSelection); document.body.removeEventListener('click', this.cancelSelection);
} }
@ -639,7 +638,7 @@ export default {
}) })
); );
if (rawAnnotationsForPlot) { if (rawAnnotationsForPlot) {
this.annotatedPoints = this.findAnnotationPoints(rawAnnotationsForPlot); this.annotatedPointsBySeries = this.findAnnotationPoints(rawAnnotationsForPlot);
} }
this.annotationsEverLoaded = true; this.annotationsEverLoaded = true;
}, },
@ -795,7 +794,7 @@ export default {
}; };
this.config.xAxis.set('range', newRange); this.config.xAxis.set('range', newRange);
if (!isTick) { if (!isTick) {
this.annotatedPoints = []; this.annotatedPointsBySeries = {};
this.clearPanZoomHistory(); this.clearPanZoomHistory();
this.synchronizeIfBoundsMatch(); this.synchronizeIfBoundsMatch();
this.loadMoreData(newRange, true); this.loadMoreData(newRange, true);
@ -1157,7 +1156,7 @@ export default {
series.closest = series.nearestPoint(point); series.closest = series.nearestPoint(point);
return { return {
series: series, seriesKeyString: series.keyString,
point: series.closest point: series.closest
}; };
}); });
@ -1245,7 +1244,7 @@ export default {
startMarquee(event, annotationEvent) { startMarquee(event, annotationEvent) {
this.rectangles = []; this.rectangles = [];
this.annotationSelections = []; this.annotationSelectionsBySeries = {};
this.canvas.classList.remove('plot-drag'); this.canvas.classList.remove('plot-drag');
this.canvas.classList.add('plot-marquee'); this.canvas.classList.add('plot-marquee');
@ -1273,7 +1272,10 @@ export default {
const nearbyAnnotations = this.gatherNearbyAnnotations(); const nearbyAnnotations = this.gatherNearbyAnnotations();
if (this.annotationViewingAndEditingAllowed && this.annotationSelections.length) { if (
this.annotationViewingAndEditingAllowed &&
Object.keys(this.annotationSelectionsBySeries).length
) {
//no annotations were found, but we are adding some now //no annotations were found, but we are adding some now
return; return;
} }
@ -1353,20 +1355,18 @@ export default {
document.body.addEventListener('click', this.cancelSelection); document.body.addEventListener('click', this.cancelSelection);
}, },
selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBox, event) { selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBoxBySeries, event) {
let targetDomainObjects = {}; let targetDomainObjects = {};
let targetDetails = {}; let targetDetails = {};
let annotations = []; let annotations = [];
pointsInBox.forEach((pointInBox) => { Object.keys(pointsInBoxBySeries).forEach((seriesKey) => {
if (pointInBox.length) { const seriesModel = this.getSeries(seriesKey);
const seriesID = pointInBox[0].series.keyString; const boundingBoxWithId = boundingBoxPerYAxis.find(
const boundingBoxWithId = boundingBoxPerYAxis.find( (box) => box.id === seriesModel.get('yAxisId')
(box) => box.id === pointInBox[0].series.get('yAxisId') );
); targetDetails[seriesKey] = boundingBoxWithId?.boundingBox;
targetDetails[seriesID] = boundingBoxWithId?.boundingBox;
targetDomainObjects[seriesID] = pointInBox[0].series.domainObject; targetDomainObjects[seriesKey] = seriesModel.domainObject;
}
}); });
this.selectPlotAnnotations({ this.selectPlotAnnotations({
targetDetails, targetDetails,
@ -1375,7 +1375,7 @@ export default {
}); });
}, },
findAnnotationPoints(rawAnnotations) { findAnnotationPoints(rawAnnotations) {
const annotationsByPoints = []; const annotationsBySeries = {};
rawAnnotations.forEach((rawAnnotation) => { rawAnnotations.forEach((rawAnnotation) => {
if (rawAnnotation.targets) { if (rawAnnotation.targets) {
const targetValues = Object.values(rawAnnotation.targets); const targetValues = Object.values(rawAnnotation.targets);
@ -1390,6 +1390,9 @@ export default {
if (!series) { if (!series) {
return; return;
} }
if (!annotationsBySeries[seriesId]) {
annotationsBySeries[seriesId] = [];
}
boundingBoxPerYAxis.push({ boundingBoxPerYAxis.push({
id: series.get('yAxisId'), id: series.get('yAxisId'),
@ -1397,15 +1400,23 @@ export default {
}); });
}); });
const pointsInBox = this.getPointsInBox(boundingBoxPerYAxis, rawAnnotation); const pointsInBoxBySeries = this.getPointsInBoxBySeries(
if (pointsInBox && pointsInBox.length) { boundingBoxPerYAxis,
annotationsByPoints.push(pointsInBox.flat()); rawAnnotation
);
if (pointsInBoxBySeries && Object.values(pointsInBoxBySeries).length) {
Object.keys(pointsInBoxBySeries).forEach((seriesKeyString) => {
const pointsInBox = pointsInBoxBySeries[seriesKeyString];
if (pointsInBox && pointsInBox.length) {
annotationsBySeries[seriesKeyString].push(...pointsInBox);
}
});
} }
} }
} }
}); });
return annotationsByPoints.flat(); return annotationsBySeries;
}, },
searchWithFlatbush(seriesData, seriesModel, boundingBox) { searchWithFlatbush(seriesData, seriesModel, boundingBox) {
const flatbush = new Flatbush(seriesData.length); const flatbush = new Flatbush(seriesData.length);
@ -1425,9 +1436,15 @@ export default {
return rangeResults; return rangeResults;
}, },
getPointsInBox(boundingBoxPerYAxis, rawAnnotation) { getSeries(keyStringToFind) {
const foundSeries = this.seriesModels.find((series) => {
return series.keyString === keyStringToFind;
});
return foundSeries;
},
getPointsInBoxBySeries(boundingBoxPerYAxis, rawAnnotation) {
// load series models in KD-Trees // load series models in KD-Trees
const seriesKDTrees = []; const searchResultsBySeries = {};
this.seriesModels.forEach((seriesModel) => { this.seriesModels.forEach((seriesModel) => {
const boundingBoxWithId = boundingBoxPerYAxis.find( const boundingBoxWithId = boundingBoxPerYAxis.find(
(box) => box.id === seriesModel.get('yAxisId') (box) => box.id === seriesModel.get('yAxisId')
@ -1440,16 +1457,15 @@ export default {
const seriesData = seriesModel.getSeriesData(); const seriesData = seriesModel.getSeriesData();
if (seriesData && seriesData.length) { if (seriesData && seriesData.length) {
const searchResults = []; searchResultsBySeries[seriesModel.keyString] = [];
const rangeResults = this.searchWithFlatbush(seriesData, seriesModel, boundingBox); const rangeResults = this.searchWithFlatbush(seriesData, seriesModel, boundingBox);
rangeResults.forEach((id) => { rangeResults.forEach((id) => {
const seriesDatum = seriesData[id]; const seriesDatum = seriesData[id];
if (seriesDatum) { if (seriesDatum) {
const result = { const result = {
series: seriesModel,
point: seriesDatum point: seriesDatum
}; };
searchResults.push(result); searchResultsBySeries[seriesModel.keyString].push(result);
} }
if (rawAnnotation) { if (rawAnnotation) {
@ -1463,13 +1479,10 @@ export default {
seriesDatum.annotationsById[annotationKeyString] = rawAnnotation; seriesDatum.annotationsById[annotationKeyString] = rawAnnotation;
} }
}); });
if (searchResults.length) {
seriesKDTrees.push(searchResults);
}
} }
}); });
return seriesKDTrees; return searchResultsBySeries;
}, },
endAnnotationMarquee(event) { endAnnotationMarquee(event) {
const boundingBoxPerYAxis = []; const boundingBoxPerYAxis = [];
@ -1490,13 +1503,13 @@ export default {
}); });
}); });
const pointsInBox = this.getPointsInBox(boundingBoxPerYAxis); const pointsInBoxBySeries = this.getPointsInBoxBySeries(boundingBoxPerYAxis);
if (!pointsInBox) { if (!pointsInBoxBySeries || Object.values(pointsInBoxBySeries).length === 0) {
return; return;
} }
this.annotationSelections = pointsInBox.flat(); this.annotationSelectionsBySeries = pointsInBoxBySeries;
this.selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBox, event); this.selectNewPlotAnnotations(boundingBoxPerYAxis, this.annotationSelectionsBySeries, event);
}, },
endZoomMarquee() { endZoomMarquee() {
const startPixels = this.marquee.startPixels; const startPixels = this.marquee.startPixels;

View File

@ -101,16 +101,16 @@ export default {
return []; return [];
} }
}, },
annotatedPoints: { annotatedPointsBySeries: {
type: Array, type: Object,
default() { default() {
return []; return {};
} }
}, },
annotationSelections: { annotationSelectionsBySeries: {
type: Array, type: Object,
default() { default() {
return []; return {};
} }
}, },
showLimitLineLabels: { showLimitLineLabels: {
@ -143,17 +143,15 @@ export default {
}, },
deep: true deep: true
}, },
annotatedPoints: { annotatedPointsBySeries: {
handler() { handler() {
this.scheduleDraw(); this.scheduleDraw();
}, }
deep: true
}, },
annotationSelections: { annotationSelectionsBySeries: {
handler() { handler() {
this.scheduleDraw(); this.scheduleDraw();
}, }
deep: true
}, },
rectangles: { rectangles: {
handler() { handler() {
@ -177,6 +175,7 @@ export default {
}, },
mounted() { mounted() {
eventHelpers.extend(this); eventHelpers.extend(this);
this.seriesModels = [];
this.config = this.getConfig(); this.config = this.getConfig();
this.isDestroyed = false; this.isDestroyed = false;
this.lines = []; this.lines = [];
@ -256,7 +255,8 @@ export default {
this.changeAlarmMarkers(newXKey, oldXKey, series); this.changeAlarmMarkers(newXKey, oldXKey, series);
this.changeLimitLines(newXKey, oldXKey, series); this.changeLimitLines(newXKey, oldXKey, series);
}, },
onSeriesAdd(series) { onSeriesAdd(series, index) {
this.seriesModels[index] = series;
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.xKey}`, this.reDraw, this); this.listenTo(series, `change:${HANDLED_ATTRIBUTES.xKey}`, this.reDraw, this);
this.listenTo( this.listenTo(
series, series,
@ -280,10 +280,15 @@ export default {
this.makeChartElement(series); this.makeChartElement(series);
this.makeLimitLines(series); this.makeLimitLines(series);
}, },
onSeriesRemove(series) { onSeriesRemove(seriesToRemove) {
this.stopListening(series); this.stopListening(seriesToRemove);
this.removeChartElement(series); this.removeChartElement(seriesToRemove);
this.scheduleDraw(); this.scheduleDraw();
const seriesIndexToRemove = this.seriesModels.findIndex(
(series) => series.keyString === seriesToRemove.keyString
);
this.seriesModels.splice(seriesIndexToRemove, 1);
}, },
onAddPoint(point, insertIndex, series) { onAddPoint(point, insertIndex, series) {
const mainYAxisId = this.config.yAxis.get('id'); const mainYAxisId = this.config.yAxis.get('id');
@ -642,8 +647,8 @@ export default {
this.drawHighlights(id); this.drawHighlights(id);
// only draw these in fixed time mode or plot is paused // only draw these in fixed time mode or plot is paused
if (this.annotationViewingAndEditingAllowed) { if (this.annotationViewingAndEditingAllowed) {
this.drawAnnotatedPoints(id); this.prepareToDrawAnnotatedPoints(id);
this.drawAnnotationSelections(id); this.prepareToDrawAnnotationSelections(id);
} }
}); });
}, },
@ -698,6 +703,7 @@ export default {
pointSets.forEach(this.drawPoints, this); pointSets.forEach(this.drawPoints, this);
const alarmSets = this.alarmSets.filter(this.matchByYAxisId.bind(this, id)); const alarmSets = this.alarmSets.filter(this.matchByYAxisId.bind(this, id));
alarmSets.forEach(this.drawAlarmPoints, this); alarmSets.forEach(this.drawAlarmPoints, this);
//console.timeEnd('📈 drawSeries');
}, },
updateLimitLines() { updateLimitLines() {
Array.from(this.$refs.limitArea.children).forEach((el) => el.remove()); Array.from(this.$refs.limitArea.children).forEach((el) => el.remove());
@ -827,82 +833,110 @@ export default {
); );
} }
}, },
drawAnnotatedPoints(yAxisId) { prepareToDrawAnnotatedPoints(yAxisId) {
// we should do this by series, and then plot all the points at once instead if (this.annotatedPointsBySeries && Object.values(this.annotatedPointsBySeries).length) {
// of doing it one by one const uniquePointsToDraw = new Set();
if (this.annotatedPoints && this.annotatedPoints.length) {
const uniquePointsToDraw = [];
const annotatedPoints = this.annotatedPoints.filter( Object.keys(this.annotatedPointsBySeries).forEach((seriesKeyString) => {
this.matchByYAxisId.bind(this, yAxisId) const seriesModel = this.getSeries(seriesKeyString);
); const matchesYAxis = this.matchByYAxisId(yAxisId, { series: seriesModel });
annotatedPoints.forEach((annotatedPoint) => { if (!matchesYAxis) {
return;
}
// annotation points are all within range (checked in MctPlot with FlatBush), so we don't need to check // annotation points are all within range (checked in MctPlot with FlatBush), so we don't need to check
const canvasXValue = this.offset[yAxisId].xVal( const annotatedPointBuffer = new Float32Array(
annotatedPoint.point, this.annotatedPointsBySeries[seriesKeyString].length * 2
annotatedPoint.series
); );
const canvasYValue = this.offset[yAxisId].yVal( Object.values(this.annotatedPointsBySeries[seriesKeyString]).forEach(
annotatedPoint.point, (annotatedPoint, index) => {
annotatedPoint.series const canvasXValue = this.offset[yAxisId].xVal(annotatedPoint.point, seriesModel);
const canvasYValue = this.offset[yAxisId].yVal(annotatedPoint.point, seriesModel);
const drawnPointKey = `${canvasXValue}|${canvasYValue}`;
if (!uniquePointsToDraw.has(drawnPointKey)) {
annotatedPointBuffer[index * 2] = canvasXValue;
annotatedPointBuffer[index * 2 + 1] = canvasYValue;
uniquePointsToDraw.add(drawnPointKey);
}
}
); );
const pointToDraw = new Float32Array([canvasXValue, canvasYValue]); this.drawAnnotatedPoints(seriesModel, annotatedPointBuffer);
const drawnPoint = uniquePointsToDraw.some((rawPoint) => { });
return rawPoint[0] === pointToDraw[0] && rawPoint[1] === pointToDraw[1]; }
}); },
if (!drawnPoint) { drawAnnotatedPoints(seriesModel, annotatedPointBuffer) {
uniquePointsToDraw.push(pointToDraw); if (annotatedPointBuffer && seriesModel) {
this.drawAnnotatedPoint(annotatedPoint, pointToDraw); const color = seriesModel.get('color').asRGBAArray();
// set transparency
color[3] = 0.15;
const pointCount = annotatedPointBuffer.length / 2;
const shape = seriesModel.get('markerShape');
this.drawAPI.drawPoints(annotatedPointBuffer, color, pointCount, ANNOTATION_SIZE, shape);
}
},
prepareToDrawAnnotationSelections(yAxisId) {
if (
this.annotationSelectionsBySeries &&
Object.keys(this.annotationSelectionsBySeries).length
) {
Object.keys(this.annotationSelectionsBySeries).forEach((seriesKeyString) => {
const seriesModel = this.getSeries(seriesKeyString);
const matchesYAxis = this.matchByYAxisId(yAxisId, { series: seriesModel });
if (matchesYAxis) {
const annotationSelectionBuffer = new Float32Array(
this.annotationSelectionsBySeries[seriesKeyString].length * 2
);
Object.values(this.annotationSelectionsBySeries[seriesKeyString]).forEach(
(annotatedSelectedPoint, index) => {
const canvasXValue = this.offset[yAxisId].xVal(
annotatedSelectedPoint.point,
seriesModel
);
const canvasYValue = this.offset[yAxisId].yVal(
annotatedSelectedPoint.point,
seriesModel
);
annotationSelectionBuffer[index * 2] = canvasXValue;
annotationSelectionBuffer[index * 2 + 1] = canvasYValue;
}
);
this.drawAnnotationSelections(seriesModel, annotationSelectionBuffer);
} }
}); });
} }
}, },
drawAnnotatedPoint(annotatedPoint, pointToDraw) { drawAnnotationSelections(seriesModel, annotationSelectionBuffer) {
if (annotatedPoint.point && annotatedPoint.series) {
const color = annotatedPoint.series.get('color').asRGBAArray();
// set transparency
color[3] = 0.15;
const pointCount = 1;
const shape = annotatedPoint.series.get('markerShape');
this.drawAPI.drawPoints(pointToDraw, color, pointCount, ANNOTATION_SIZE, shape);
}
},
drawAnnotationSelections(yAxisId) {
if (this.annotationSelections && this.annotationSelections.length) {
const annotationSelections = this.annotationSelections.filter(
this.matchByYAxisId.bind(this, yAxisId)
);
annotationSelections.forEach(this.drawAnnotationSelection.bind(this, yAxisId), this);
}
},
drawAnnotationSelection(yAxisId, annotationSelection) {
const points = new Float32Array([
this.offset[yAxisId].xVal(annotationSelection.point, annotationSelection.series),
this.offset[yAxisId].yVal(annotationSelection.point, annotationSelection.series)
]);
const color = [255, 255, 255, 1]; // white const color = [255, 255, 255, 1]; // white
const pointCount = 1; const pointCount = annotationSelectionBuffer.length / 2;
const shape = annotationSelection.series.get('markerShape'); const shape = seriesModel.get('markerShape');
this.drawAPI.drawPoints(points, color, pointCount, ANNOTATION_SIZE, shape); this.drawAPI.drawPoints(annotationSelectionBuffer, color, pointCount, ANNOTATION_SIZE, shape);
}, },
drawHighlights(yAxisId) { drawHighlights(yAxisId) {
if (this.highlights && this.highlights.length) { if (this.highlights && this.highlights.length) {
const highlights = this.highlights.filter(this.matchByYAxisId.bind(this, yAxisId)); const highlights = this.highlights.filter((highlight) => {
const series = this.getSeries(highlight.seriesKeyString);
return this.matchByYAxisId.bind(yAxisId, { series });
});
highlights.forEach(this.drawHighlight.bind(this, yAxisId), this); highlights.forEach(this.drawHighlight.bind(this, yAxisId), this);
} }
}, },
getSeries(keyStringToFind) {
const foundSeries = this.seriesModels.find((series) => {
return series.keyString === keyStringToFind;
});
return foundSeries;
},
drawHighlight(yAxisId, highlight) { drawHighlight(yAxisId, highlight) {
const series = this.getSeries(highlight.seriesKeyString);
const points = new Float32Array([ const points = new Float32Array([
this.offset[yAxisId].xVal(highlight.point, highlight.series), this.offset[yAxisId].xVal(highlight.point, series),
this.offset[yAxisId].yVal(highlight.point, highlight.series) this.offset[yAxisId].yVal(highlight.point, series)
]); ]);
const color = highlight.series.get('color').asRGBAArray(); const color = series.get('color').asRGBAArray();
const pointCount = 1; const pointCount = 1;
const shape = highlight.series.get('markerShape'); const shape = series.get('markerShape');
this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE, shape); this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE, shape);
}, },

View File

@ -45,7 +45,7 @@
:key="`${seriesObject.keyString}-${seriesIndex}-collapsed`" :key="`${seriesObject.keyString}-${seriesIndex}-collapsed`"
:highlights="highlights" :highlights="highlights"
:value-to-show-when-collapsed="valueToShowWhenCollapsed" :value-to-show-when-collapsed="valueToShowWhenCollapsed"
:series-object="seriesObject" :series-key-string="seriesObject.keyString"
@legendHoverChanged="legendHoverChanged" @legendHoverChanged="legendHoverChanged"
/> />
</div> </div>
@ -72,7 +72,7 @@
<plot-legend-item-expanded <plot-legend-item-expanded
v-for="(seriesObject, seriesIndex) in seriesModels" v-for="(seriesObject, seriesIndex) in seriesModels"
:key="`${seriesObject.keyString}-${seriesIndex}-expanded`" :key="`${seriesObject.keyString}-${seriesIndex}-expanded`"
:series-object="seriesObject" :series-key-string="seriesObject.keyString"
:highlights="highlights" :highlights="highlights"
@legendHoverChanged="legendHoverChanged" @legendHoverChanged="legendHoverChanged"
/> />

View File

@ -71,12 +71,9 @@ export default {
mixins: [stalenessMixin, tooltipHelpers], mixins: [stalenessMixin, tooltipHelpers],
inject: ['openmct', 'domainObject'], inject: ['openmct', 'domainObject'],
props: { props: {
seriesObject: { seriesKeyString: {
type: Object, type: String,
required: true, required: true
default() {
return {};
}
}, },
highlights: { highlights: {
type: Array, type: Array,
@ -112,7 +109,7 @@ export default {
highlights: { highlights: {
handler(newHighlights) { handler(newHighlights) {
const highlightedObject = newHighlights.find( const highlightedObject = newHighlights.find(
(highlight) => highlight.series.keyString === this.seriesObject.keyString (highlight) => highlight.seriesKeyString === this.seriesKeyString
); );
if (newHighlights.length === 0 || highlightedObject) { if (newHighlights.length === 0 || highlightedObject) {
this.initialize(highlightedObject); this.initialize(highlightedObject);
@ -122,28 +119,14 @@ export default {
} }
}, },
mounted() { mounted() {
this.seriesModels = [];
eventHelpers.extend(this); eventHelpers.extend(this);
this.config = this.getConfig(); this.config = this.getConfig();
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.config.series.forEach(this.onSeriesAdd, this);
this.legend = this.config.legend; this.legend = this.config.legend;
this.loaded = true; this.loaded = true;
this.listenTo(
this.seriesObject,
'change:color',
(newColor) => {
this.updateColor(newColor);
},
this
);
this.listenTo(
this.seriesObject,
'change:name',
() => {
this.updateName();
},
this
);
this.subscribeToStaleness(this.seriesObject.domainObject);
this.initialize();
}, },
beforeUnmount() { beforeUnmount() {
this.stopListening(); this.stopListening();
@ -154,8 +137,44 @@ export default {
return configStore.get(configId); return configStore.get(configId);
}, },
onSeriesAdd(series, index) {
this.seriesModels[index] = series;
if (series.keyString === this.seriesKeyString) {
this.listenTo(
series,
'change:color',
(newColor) => {
this.updateColor(newColor);
},
this
);
this.listenTo(
series,
'change:name',
() => {
this.updateName();
},
this
);
this.subscribeToStaleness(series.domainObject);
this.initialize();
}
},
onSeriesRemove(seriesToRemove) {
const seriesIndexToRemove = this.seriesModels.findIndex(
(series) => series.keyString === seriesToRemove.keyString
);
this.seriesModels.splice(seriesIndexToRemove, 1);
},
getSeries(keyStringToFind) {
const foundSeries = this.seriesModels.find((series) => {
return series.keyString === keyStringToFind;
});
return foundSeries;
},
initialize(highlightedObject) { initialize(highlightedObject) {
const seriesObject = highlightedObject?.series || this.seriesObject; const seriesKeyStringToUse = highlightedObject?.seriesKeyString || this.seriesKeyString;
const seriesObject = this.getSeries(seriesKeyStringToUse);
this.isMissing = seriesObject.domainObject.status === 'missing'; this.isMissing = seriesObject.domainObject.status === 'missing';
this.colorAsHexString = seriesObject.get('color').asHexString(); this.colorAsHexString = seriesObject.get('color').asHexString();
@ -187,7 +206,8 @@ export default {
this.colorAsHexString = newColor.asHexString(); this.colorAsHexString = newColor.asHexString();
}, },
updateName() { updateName() {
this.nameWithUnit = this.seriesObject.nameWithUnit(); const seriesObject = this.getSeries(this.seriesKeyString);
this.nameWithUnit = seriesObject.nameWithUnit();
}, },
toggleHover(hover) { toggleHover(hover) {
this.hover = hover; this.hover = hover;
@ -195,15 +215,16 @@ export default {
'legendHoverChanged', 'legendHoverChanged',
this.hover this.hover
? { ? {
seriesKey: this.seriesObject.keyString seriesKey: this.seriesKeyString
} }
: undefined : undefined
); );
}, },
async showToolTip() { async showToolTip() {
const seriesObject = this.getSeries(this.seriesKeyString);
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS; const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
this.buildToolTip( this.buildToolTip(
await this.getTelemetryPathString(this.seriesObject.domainObject.identifier), await this.getTelemetryPathString(seriesObject.domainObject.identifier),
BELOW, BELOW,
'series' 'series'
); );

View File

@ -86,12 +86,9 @@ export default {
mixins: [stalenessMixin, tooltipHelpers], mixins: [stalenessMixin, tooltipHelpers],
inject: ['openmct', 'domainObject'], inject: ['openmct', 'domainObject'],
props: { props: {
seriesObject: { seriesKeyString: {
type: Object, type: String,
required: true, required: true
default() {
return {};
}
}, },
highlights: { highlights: {
type: Array, type: Array,
@ -135,7 +132,7 @@ export default {
highlights: { highlights: {
handler(newHighlights) { handler(newHighlights) {
const highlightedObject = newHighlights.find( const highlightedObject = newHighlights.find(
(highlight) => highlight.series.keyString === this.seriesObject.keyString (highlight) => highlight.seriesKeyString === this.seriesKeyString
); );
if (newHighlights.length === 0 || highlightedObject) { if (newHighlights.length === 0 || highlightedObject) {
this.initialize(highlightedObject); this.initialize(highlightedObject);
@ -145,40 +142,62 @@ export default {
} }
}, },
mounted() { mounted() {
this.seriesModels = [];
eventHelpers.extend(this); eventHelpers.extend(this);
this.config = this.getConfig(); this.config = this.getConfig();
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.config.series.forEach(this.onSeriesAdd, this);
this.legend = this.config.legend; this.legend = this.config.legend;
this.loaded = true; this.loaded = true;
this.listenTo(
this.seriesObject,
'change:color',
(newColor) => {
this.updateColor(newColor);
},
this
);
this.listenTo(
this.seriesObject,
'change:name',
() => {
this.updateName();
},
this
);
this.subscribeToStaleness(this.seriesObject.domainObject);
this.initialize();
}, },
beforeUnmount() { beforeUnmount() {
this.stopListening(); this.stopListening();
}, },
methods: { methods: {
onSeriesAdd(series, index) {
this.seriesModels[index] = series;
if (series.keyString === this.seriesKeyString) {
this.listenTo(
series,
'change:color',
(newColor) => {
this.updateColor(newColor);
},
this
);
this.listenTo(
series,
'change:name',
() => {
this.updateName();
},
this
);
this.subscribeToStaleness(series.domainObject);
this.initialize();
}
},
onSeriesRemove(seriesToRemove) {
const seriesIndexToRemove = this.seriesModels.findIndex(
(series) => series.keyString === seriesToRemove.keyString
);
this.seriesModels.splice(seriesIndexToRemove, 1);
},
getSeries(keyStringToFind) {
const foundSeries = this.seriesModels.find((series) => {
return series.keyString === keyStringToFind;
});
return foundSeries;
},
getConfig() { getConfig() {
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier); const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
return configStore.get(configId); return configStore.get(configId);
}, },
initialize(highlightedObject) { initialize(highlightedObject) {
const seriesObject = highlightedObject?.series || this.seriesObject; const seriesKeyStringToUse = highlightedObject?.seriesKeyString || this.seriesKeyString;
const seriesObject = this.getSeries(seriesKeyStringToUse);
this.isMissing = seriesObject.domainObject.status === 'missing'; this.isMissing = seriesObject.domainObject.status === 'missing';
this.colorAsHexString = seriesObject.get('color').asHexString(); this.colorAsHexString = seriesObject.get('color').asHexString();
@ -209,22 +228,20 @@ export default {
updateColor(newColor) { updateColor(newColor) {
this.colorAsHexString = newColor.asHexString(); this.colorAsHexString = newColor.asHexString();
}, },
updateName(newName) { updateName() {
this.nameWithUnit = this.seriesObject.nameWithUnit(); const seriesObject = this.getSeries(this.seriesKeyString);
this.nameWithUnit = seriesObject.nameWithUnit();
}, },
toggleHover(hover) { toggleHover(hover) {
this.hover = hover; this.hover = hover;
this.$emit('legendHoverChanged', { this.$emit('legendHoverChanged', {
seriesKey: this.hover ? this.seriesObject.keyString : '' seriesKey: this.hover ? this.seriesKeyString : ''
}); });
}, },
async showToolTip() { async showToolTip() {
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS; const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
this.buildToolTip( const seriesIdentifier = this.openmct.objects.parseKeyString(this.seriesKeyString);
await this.getTelemetryPathString(this.seriesObject.domainObject.identifier), this.buildToolTip(await this.getTelemetryPathString(seriesIdentifier), BELOW, 'seriesName');
BELOW,
'seriesName'
);
} }
} }
}; };