diff --git a/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js b/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js index ae914c6e9c..181477d457 100644 --- a/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js +++ b/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js @@ -328,38 +328,12 @@ test('Example Imagery in Display layout', async ({ page }) => { return newImageCount; }, { - message: "verify that new images still stream in", + message: "verify that old images are discarded", timeout: 6 * 1000 - }).toBeGreaterThan(imageCount); + }).toBe(imageCount); // Verify selected image is still displayed await expect(selectedImage).toBeVisible(); - - // Unpause imagery - await page.locator('.pause-play').click(); - - //Get background-image url from background-image css prop - const backgroundImage = page.locator('.c-imagery__main-image__background-image'); - let backgroundImageUrl = await backgroundImage.evaluate((el) => { - return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1]; - }); - let backgroundImageUrl1 = backgroundImageUrl.slice(1, -1); //forgive me, padre - console.log('backgroundImageUrl1 ' + backgroundImageUrl1); - - let backgroundImageUrl2; - await expect.poll(async () => { - // Verify next image has updated - let backgroundImageUrlNext = await backgroundImage.evaluate((el) => { - return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1]; - }); - backgroundImageUrl2 = backgroundImageUrlNext.slice(1, -1); //forgive me, padre - - return backgroundImageUrl2; - }, { - message: "verify next image has updated", - timeout: 6 * 1000 - }).not.toBe(backgroundImageUrl1); - console.log('backgroundImageUrl2 ' + backgroundImageUrl2); }); test.describe('Example imagery thumbnails resize in display layouts', () => { diff --git a/example/imagery/plugin.js b/example/imagery/plugin.js index 47d6f4ef70..2f323356dd 100644 --- a/example/imagery/plugin.js +++ b/example/imagery/plugin.js @@ -168,7 +168,7 @@ function getImageUrlListFromConfig(configuration) { } function getImageLoadDelay(domainObject) { - const imageLoadDelay = domainObject.configuration.imageLoadDelayInMilliSeconds; + const imageLoadDelay = Math.trunc(Number(domainObject.configuration.imageLoadDelayInMilliSeconds)); if (!imageLoadDelay) { openmctInstance.objects.mutate(domainObject, 'configuration.imageLoadDelayInMilliSeconds', DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS); @@ -190,7 +190,9 @@ function getRealtimeProvider() { subscribe: (domainObject, callback) => { const delay = getImageLoadDelay(domainObject); const interval = setInterval(() => { - callback(pointForTimestamp(Date.now(), domainObject.name, getImageSamples(domainObject.configuration), delay)); + const imageSamples = getImageSamples(domainObject.configuration); + const datum = pointForTimestamp(Date.now(), domainObject.name, imageSamples, delay); + callback(datum); }, delay); return () => { @@ -229,8 +231,9 @@ function getLadProvider() { }, request: (domainObject, options) => { const delay = getImageLoadDelay(domainObject); + const datum = pointForTimestamp(Date.now(), domainObject.name, getImageSamples(domainObject.configuration), delay); - return Promise.resolve([pointForTimestamp(Date.now(), domainObject.name, delay)]); + return Promise.resolve([datum]); } }; } diff --git a/src/plugins/imagery/mixins/imageryData.js b/src/plugins/imagery/mixins/imageryData.js index c5251d1218..cbec9b23b4 100644 --- a/src/plugins/imagery/mixins/imageryData.js +++ b/src/plugins/imagery/mixins/imageryData.js @@ -30,7 +30,7 @@ export default { this.timeSystemChange = this.timeSystemChange.bind(this); this.setDataTimeContext = this.setDataTimeContext.bind(this); this.setDataTimeContext(); - this.openmct.objectViews.on('clearData', this.clearData); + this.openmct.objectViews.on('clearData', this.dataCleared); // set this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier); @@ -44,8 +44,11 @@ export default { this.timeKey = this.timeSystem.key; this.timeFormatter = this.getFormatter(this.timeKey); - // kickoff - this.subscribe(); + this.telemetryCollection = this.openmct.telemetry.requestCollection(this.domainObject, {}); + this.telemetryCollection.on('add', this.dataAdded); + this.telemetryCollection.on('remove', this.dataRemoved); + this.telemetryCollection.on('clear', this.dataCleared); + this.telemetryCollection.load(); }, beforeDestroy() { if (this.unsubscribe) { @@ -54,9 +57,31 @@ export default { } this.stopFollowingDataTimeContext(); - this.openmct.objectViews.off('clearData', this.clearData); + this.openmct.objectViews.off('clearData', this.dataCleared); + + this.telemetryCollection.off('add', this.dataAdded); + this.telemetryCollection.off('remove', this.dataRemoved); + this.telemetryCollection.off('clear', this.dataCleared); + + this.telemetryCollection.destroy(); }, methods: { + dataAdded(dataToAdd) { + const normalizedDataToAdd = dataToAdd.map(datum => this.normalizeDatum(datum)); + this.imageHistory = this.imageHistory.concat(normalizedDataToAdd); + }, + dataCleared() { + this.imageHistory = []; + }, + dataRemoved(dataToRemove) { + this.imageHistory = this.imageHistory.filter(existingDatum => { + const shouldKeep = dataToRemove.some(datumToRemove => { + return (existingDatum.utc !== datumToRemove.utc); + }); + + return shouldKeep; + }); + }, setDataTimeContext() { this.stopFollowingDataTimeContext(); this.timeContext = this.openmct.time.getContextForView(this.objectPath); @@ -70,19 +95,6 @@ export default { this.timeContext.off('timeSystem', this.timeSystemChange); } }, - isDatumValid(datum) { - //TODO: Add a check to see if there are duplicate images (identical image timestamp and url subsequently) - if (!datum) { - return false; - } - - const datumTimeCheck = this.parseTime(datum); - const bounds = this.timeContext.bounds(); - - const isOutOfBounds = datumTimeCheck < bounds.start || datumTimeCheck > bounds.end; - - return !isOutOfBounds; - }, formatImageUrl(datum) { if (!datum) { return; @@ -124,40 +136,6 @@ export default { // forcibly reset the imageContainer size to prevent an aspect ratio distortion delete this.imageContainerWidth; delete this.imageContainerHeight; - - return this.requestHistory(); - }, - async requestHistory() { - this.requestCount++; - const requestId = this.requestCount; - const bounds = this.timeContext.bounds(); - - const data = await this.openmct.telemetry - .request(this.domainObject, bounds) || []; - // wait until new request resolves to do comparison - if (this.requestCount !== requestId) { - return this.imageHistory = []; - } - - const imagery = data.filter(this.isDatumValid).map(this.normalizeDatum); - this.imageHistory = imagery; - }, - clearData(domainObjectToClear) { - // global clearData button is accepted therefore no truthy check on inputted param - const clearDataForObjectSelected = Boolean(domainObjectToClear); - if (clearDataForObjectSelected) { - const idsEqual = this.openmct.objects.areIdsEqual( - domainObjectToClear.identifier, - this.domainObject.identifier - ); - if (!idsEqual) { - return; - } - } - - // splice array to encourage garbage collection - this.imageHistory.splice(0, this.imageHistory.length); - }, timeSystemChange() { this.timeSystem = this.timeContext.timeSystem(); @@ -165,22 +143,7 @@ export default { this.timeFormatter = this.getFormatter(this.timeKey); this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER); }, - subscribe() { - this.unsubscribe = this.openmct.telemetry - .subscribe(this.domainObject, (datum) => { - let parsedTimestamp = this.parseTime(datum); - let bounds = this.timeContext.bounds(); - if (!(parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end)) { - return; - } - - if (this.isDatumValid(datum)) { - this.imageHistory.push(this.normalizeDatum(datum)); - } - }); - }, normalizeDatum(datum) { - const formattedTime = this.formatTime(datum); const url = this.formatImageUrl(datum); const time = this.parseTime(formattedTime); diff --git a/src/plugins/imagery/pluginSpec.js b/src/plugins/imagery/pluginSpec.js index 031ec6fcb9..0eddb3ce76 100644 --- a/src/plugins/imagery/pluginSpec.js +++ b/src/plugins/imagery/pluginSpec.js @@ -88,6 +88,7 @@ describe("The Imagery View Layouts", () => { let openmct; let parent; let child; + let historicalProvider; let imageTelemetry = generateTelemetry(START - TEN_MINUTES, COUNT); let imageryObject = { identifier: { @@ -122,50 +123,6 @@ describe("The Imagery View Layouts", () => { "priority": 3 }, "source": "url" - // "relatedTelemetry": { - // "heading": { - // "comparisonFunction": comparisonFunction, - // "historical": { - // "telemetryObjectId": "heading", - // "valueKey": "value" - // } - // }, - // "roll": { - // "comparisonFunction": comparisonFunction, - // "historical": { - // "telemetryObjectId": "roll", - // "valueKey": "value" - // } - // }, - // "pitch": { - // "comparisonFunction": comparisonFunction, - // "historical": { - // "telemetryObjectId": "pitch", - // "valueKey": "value" - // } - // }, - // "cameraPan": { - // "comparisonFunction": comparisonFunction, - // "historical": { - // "telemetryObjectId": "cameraPan", - // "valueKey": "value" - // } - // }, - // "cameraTilt": { - // "comparisonFunction": comparisonFunction, - // "historical": { - // "telemetryObjectId": "cameraTilt", - // "valueKey": "value" - // } - // }, - // "sunOrientation": { - // "comparisonFunction": comparisonFunction, - // "historical": { - // "telemetryObjectId": "sunOrientation", - // "valueKey": "value" - // } - // } - // } }, { "name": "Name", @@ -209,6 +166,13 @@ describe("The Imagery View Layouts", () => { telemetryPromiseResolve = resolve; }); + historicalProvider = { + request: () => { + return Promise.resolve(imageTelemetry); + } + }; + spyOn(openmct.telemetry, 'findRequestProvider').and.returnValue(historicalProvider); + spyOn(openmct.telemetry, 'request').and.callFake(() => { telemetryPromiseResolve(imageTelemetry); @@ -626,6 +590,20 @@ describe("The Imagery View Layouts", () => { end: START + (5 * ONE_MINUTE) }); + const mockClock = jasmine.createSpyObj("clock", [ + "on", + "off", + "currentValue" + ]); + mockClock.key = 'mockClock'; + mockClock.currentValue.and.returnValue(1); + + openmct.time.addClock(mockClock); + openmct.time.clock('mockClock', { + start: START - (5 * ONE_MINUTE), + end: START + (5 * ONE_MINUTE) + }); + openmct.router.path = [{ identifier: { key: 'test-timestrip', @@ -660,7 +638,7 @@ describe("The Imagery View Layouts", () => { it("on mount should show imagery within the given bounds", (done) => { Vue.nextTick(() => { const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper'); - expect(imageElements.length).toEqual(6); + expect(imageElements.length).toEqual(5); done(); }); }); @@ -680,5 +658,46 @@ describe("The Imagery View Layouts", () => { }); }); }); + + it("should remove images when clock advances", async () => { + openmct.time.tick(ONE_MINUTE * 2); + await Vue.nextTick(); + await Vue.nextTick(); + const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper'); + expect(imageElements.length).toEqual(4); + }); + + it("should remove images when start bounds shorten", async () => { + openmct.time.timeSystem('utc', { + start: START, + end: START + (5 * ONE_MINUTE) + }); + await Vue.nextTick(); + await Vue.nextTick(); + const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper'); + expect(imageElements.length).toEqual(1); + }); + + it("should remove images when end bounds shorten", async () => { + openmct.time.timeSystem('utc', { + start: START - (5 * ONE_MINUTE), + end: START - (2 * ONE_MINUTE) + }); + await Vue.nextTick(); + await Vue.nextTick(); + const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper'); + expect(imageElements.length).toEqual(4); + }); + + it("should remove images when both bounds shorten", async () => { + openmct.time.timeSystem('utc', { + start: START - (2 * ONE_MINUTE), + end: START + (2 * ONE_MINUTE) + }); + await Vue.nextTick(); + await Vue.nextTick(); + const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper'); + expect(imageElements.length).toEqual(3); + }); }); });