mirror of
https://github.com/nasa/openmct.git
synced 2024-12-20 21:53:08 +00:00
2.0.4 merge into master (#5297)
* Release 2.0.3 * Fix tick values for plots ticks in log mode and null check (#5119) * [2297] When there is no display range or range, skip setting the range value when auto scale is turned off. * If the formatted value is a number and a float, set precision to 2 decimal points. * Fix value assignment * Use whole numbers in log mode * Revert whole numbers fix - need floats for values between 0 and 1. * Handle scrolling to focused image on resize/new data (#5121) * Scroll to focused image when view resizes - this will force scrolling to focused image when going to/from view large mode * Scroll to the right if there is no paused focused image * [LAD Tables] Use Telemetry Collections (#5127) * Use telemetry collections to handle bounds checks * added telemetry collection to alphanumeric telemetry view (#5131) * Added animation styling for POS and CAM; adjusted cutoff for isNewImage (#5116) * Added animation styling for POS and CAM; adjusted cutoff for isNewImage * Remove animation from POS and CAM * Fix transactions overwriting latest objects with stale objects on save (#5132) * use object (map) instead of set to track dirty objects * fix tests due to internals change Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov> * Gauge edit enabled 2.0.3 (#5133) * Gauge plugin #4896, add edit mode * Dynamic dial-type Gauge sizing by height and width (#5129) * Improve sizing strategy for gauges. * Do not install gauge by default for now Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov> Co-authored-by: Jamie Vigliotta <jamie.j.vigliotta@nasa.gov> Co-authored-by: Andrew Henry <akhenry@gmail.com> * [Telemetry Collections] Include data with start and end bounds (#5145) * Reverts forced precision for log plots axis labels (#5147) * Condition Widgets trigger hundreds of persistence calls (#5146) Co-authored-by: unlikelyzero <jchill2@gmail.com> * Update version for 2.0.4 (#5255) * Eliminate NaN conditions and clear stale duration (#5248) * Temp source map fix 2.0.4 (#5267) * use dev mode for production * mode -> production * added extra devtool options * wip * Imagery Fixes for release/2.0.4 (#5282) * Fallback for height * Remove duplicated requestHistory call since setDataTimeContext already invokes it on mount * Inverted datumIsNotValid and refactored requestHistory * Remove old datumIsNotValid func * Return false if datum is falsy * Corrected brightness/contrast input * Clone default values to avoid mutation * Changed index of imageTelemetry to an item within bounds * Implement clearData test for imagery differently * x-out clearData tests Co-authored-by: Joshi <simplyrender@gmail.com> * Imagery test fixes (#5293) * Fallback for height * Remove duplicated requestHistory call since setDataTimeContext already invokes it on mount * Inverted datumIsNotValid and refactored requestHistory * Remove old datumIsNotValid func * Return false if datum is falsy * Corrected brightness/contrast input * Clone default values to avoid mutation * Changed index of imageTelemetry to an item within bounds * Implement clearData test for imagery differently * x-out clearData tests * Set bounds on each test rather than the wrapper Co-authored-by: Michael Rogers <contact@mhrogers.com> * Imagery validation fix (#5295) * Remove check for duplicate images * Remove commented out code and add TODO * lint fix * Add missing tests * Use the master version and ignore release/2.0.4 changes Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov> Co-authored-by: Michael Rogers <contact@mhrogers.com> Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com> Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov> Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com> Co-authored-by: Andrew Henry <akhenry@gmail.com> Co-authored-by: unlikelyzero <jchill2@gmail.com>
This commit is contained in:
parent
370e6a0c37
commit
0f0c6a7b17
@ -19,4 +19,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ export default {
|
||||
this.$emit('filtersUpdated', this.filters);
|
||||
},
|
||||
handleResetFilters() {
|
||||
this.filters = DEFAULT_FILTER_VALUES;
|
||||
this.filters = {...DEFAULT_FILTER_VALUES};
|
||||
this.notifyFiltersChanged();
|
||||
},
|
||||
limitZoomRange(factor) {
|
||||
|
@ -403,6 +403,9 @@ export default {
|
||||
formattedDuration() {
|
||||
let result = 'N/A';
|
||||
let negativeAge = -1;
|
||||
if (!Number.isInteger(this.numericDuration)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (this.numericDuration > TWENTYFOUR_HOURS) {
|
||||
negativeAge *= (this.numericDuration / TWENTYFOUR_HOURS);
|
||||
@ -905,8 +908,10 @@ export default {
|
||||
let currentTime = this.timeContext.clock() && this.timeContext.clock().currentValue();
|
||||
if (currentTime === undefined) {
|
||||
this.numericDuration = currentTime;
|
||||
} else {
|
||||
} else if (Number.isInteger(this.parsedSelectedTime)) {
|
||||
this.numericDuration = currentTime - this.parsedSelectedTime;
|
||||
} else {
|
||||
this.numericDuration = undefined;
|
||||
}
|
||||
},
|
||||
resetAgeCSS() {
|
||||
|
@ -72,6 +72,7 @@
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
height: 100%; //fallback value
|
||||
}
|
||||
&__image {
|
||||
// Present to allow Save As... image
|
||||
|
@ -70,22 +70,18 @@ export default {
|
||||
this.timeContext.off('timeSystem', this.timeSystemChange);
|
||||
}
|
||||
},
|
||||
datumIsNotValid(datum) {
|
||||
if (this.imageHistory.length === 0) {
|
||||
isDatumValid(datum) {
|
||||
//TODO: Add a check to see if there are duplicate images (identical image timestamp and url subsequently)
|
||||
if (!datum) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const datumURL = this.formatImageUrl(datum);
|
||||
const lastHistoryURL = this.formatImageUrl(this.imageHistory.slice(-1)[0]);
|
||||
|
||||
// datum is not valid if it matches the last datum in history,
|
||||
// or it is before the last datum in the history
|
||||
const datumTimeCheck = this.parseTime(datum);
|
||||
const historyTimeCheck = this.parseTime(this.imageHistory.slice(-1)[0]);
|
||||
const matchesLast = (datumTimeCheck === historyTimeCheck) && (datumURL === lastHistoryURL);
|
||||
const isStale = datumTimeCheck < historyTimeCheck;
|
||||
const bounds = this.timeContext.bounds();
|
||||
|
||||
return matchesLast || isStale;
|
||||
const isOutOfBounds = datumTimeCheck < bounds.start || datumTimeCheck > bounds.end;
|
||||
|
||||
return !isOutOfBounds;
|
||||
},
|
||||
formatImageUrl(datum) {
|
||||
if (!datum) {
|
||||
@ -132,25 +128,19 @@ export default {
|
||||
return this.requestHistory();
|
||||
},
|
||||
async requestHistory() {
|
||||
let bounds = this.timeContext.bounds();
|
||||
this.requestCount++;
|
||||
const requestId = this.requestCount;
|
||||
this.imageHistory = [];
|
||||
const bounds = this.timeContext.bounds();
|
||||
|
||||
let data = await this.openmct.telemetry
|
||||
const data = await this.openmct.telemetry
|
||||
.request(this.domainObject, bounds) || [];
|
||||
|
||||
if (this.requestCount === requestId) {
|
||||
let imagery = [];
|
||||
data.forEach((datum) => {
|
||||
let image = this.normalizeDatum(datum);
|
||||
if (image) {
|
||||
imagery.push(image);
|
||||
}
|
||||
});
|
||||
//this is to optimize anything that reacts to imageHistory length
|
||||
this.imageHistory = imagery;
|
||||
// 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
|
||||
@ -180,27 +170,29 @@ export default {
|
||||
.subscribe(this.domainObject, (datum) => {
|
||||
let parsedTimestamp = this.parseTime(datum);
|
||||
let bounds = this.timeContext.bounds();
|
||||
if (!(parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) {
|
||||
let image = this.normalizeDatum(datum);
|
||||
if (image) {
|
||||
this.imageHistory.push(image);
|
||||
}
|
||||
if (this.isDatumValid(datum)) {
|
||||
this.imageHistory.push(this.normalizeDatum(datum));
|
||||
}
|
||||
});
|
||||
},
|
||||
normalizeDatum(datum) {
|
||||
if (this.datumIsNotValid(datum)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let image = { ...datum };
|
||||
image.formattedTime = this.formatTime(datum);
|
||||
image.url = this.formatImageUrl(datum);
|
||||
image.time = this.parseTime(image.formattedTime);
|
||||
image.imageDownloadName = this.getImageDownloadName(datum);
|
||||
const formattedTime = this.formatTime(datum);
|
||||
const url = this.formatImageUrl(datum);
|
||||
const time = this.parseTime(formattedTime);
|
||||
const imageDownloadName = this.getImageDownloadName(datum);
|
||||
|
||||
return image;
|
||||
return {
|
||||
...datum,
|
||||
formattedTime,
|
||||
url,
|
||||
time,
|
||||
imageDownloadName
|
||||
};
|
||||
},
|
||||
getFormatter(key) {
|
||||
let metadataValue = this.metadata.value(key) || { format: key };
|
||||
|
@ -84,7 +84,6 @@ describe("The Imagery View Layouts", () => {
|
||||
let telemetryPromise;
|
||||
let telemetryPromiseResolve;
|
||||
let cleanupFirst;
|
||||
let isClearDataTriggered;
|
||||
|
||||
let openmct;
|
||||
let parent;
|
||||
@ -205,20 +204,12 @@ describe("The Imagery View Layouts", () => {
|
||||
cleanupFirst = [];
|
||||
|
||||
openmct = createOpenMct();
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: START - (5 * ONE_MINUTE),
|
||||
end: START + (5 * ONE_MINUTE)
|
||||
});
|
||||
|
||||
telemetryPromise = new Promise((resolve) => {
|
||||
telemetryPromiseResolve = resolve;
|
||||
});
|
||||
|
||||
spyOn(openmct.telemetry, 'request').and.callFake(() => {
|
||||
if (isClearDataTriggered) {
|
||||
return [];
|
||||
}
|
||||
|
||||
telemetryPromiseResolve(imageTelemetry);
|
||||
|
||||
return telemetryPromise;
|
||||
@ -337,44 +328,93 @@ describe("The Imagery View Layouts", () => {
|
||||
expect(imageryView).toBeDefined();
|
||||
});
|
||||
|
||||
describe("imagery view", () => {
|
||||
describe("Clear data action for imagery", () => {
|
||||
let applicableViews;
|
||||
let imageryViewProvider;
|
||||
let imageryView;
|
||||
let componentView;
|
||||
let clearDataPlugin;
|
||||
let clearDataAction;
|
||||
|
||||
beforeEach(() => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: START - (5 * ONE_MINUTE),
|
||||
end: START + (5 * ONE_MINUTE)
|
||||
});
|
||||
|
||||
applicableViews = openmct.objectViews.get(imageryObject, [imageryObject]);
|
||||
imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
|
||||
imageryView = imageryViewProvider.view(imageryObject, [imageryObject]);
|
||||
imageryView.show(child);
|
||||
componentView = imageryView._getInstance().$children[0];
|
||||
|
||||
clearDataPlugin = new ClearDataPlugin(
|
||||
['example.imagery'],
|
||||
{indicator: true}
|
||||
);
|
||||
openmct.install(clearDataPlugin);
|
||||
clearDataAction = openmct.actions.getAction('clear-data-action');
|
||||
|
||||
return Vue.nextTick();
|
||||
});
|
||||
|
||||
it('clear data action is installed', () => {
|
||||
expect(clearDataAction).toBeDefined();
|
||||
});
|
||||
|
||||
it('on clearData action should clear data for object is selected', (done) => {
|
||||
// force show the thumbnails
|
||||
componentView.forceShowThumbnails = true;
|
||||
Vue.nextTick(() => {
|
||||
let clearDataResolve;
|
||||
let telemetryRequestPromise = new Promise((resolve) => {
|
||||
clearDataResolve = resolve;
|
||||
});
|
||||
expect(parent.querySelectorAll('.c-imagery__thumb').length).not.toBe(0);
|
||||
|
||||
openmct.objectViews.on('clearData', (_domainObject) => {
|
||||
return Vue.nextTick(() => {
|
||||
expect(parent.querySelectorAll('.c-imagery__thumb').length).toBe(0);
|
||||
|
||||
clearDataResolve();
|
||||
});
|
||||
});
|
||||
clearDataAction.invoke(imageryObject);
|
||||
|
||||
telemetryRequestPromise.then(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("imagery view", () => {
|
||||
let applicableViews;
|
||||
let imageryViewProvider;
|
||||
let imageryView;
|
||||
|
||||
beforeEach(() => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: START - (5 * ONE_MINUTE),
|
||||
end: START + (5 * ONE_MINUTE)
|
||||
});
|
||||
|
||||
applicableViews = openmct.objectViews.get(imageryObject, [imageryObject]);
|
||||
imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
|
||||
imageryView = imageryViewProvider.view(imageryObject, [imageryObject]);
|
||||
imageryView.show(child);
|
||||
|
||||
imageryView._getInstance().$children[0].forceShowThumbnails = true;
|
||||
|
||||
return Vue.nextTick();
|
||||
});
|
||||
afterEach(() => {
|
||||
isClearDataTriggered = false;
|
||||
// openmct.time.stopClock();
|
||||
// openmct.router.removeListener('change:hash', resolveFunction);
|
||||
// imageryView.destroy();
|
||||
});
|
||||
|
||||
it("on mount should show the the most recent image", (done) => {
|
||||
it("on mount should show the the most recent image", () => {
|
||||
//Looks like we need Vue.nextTick here so that computed properties settle down
|
||||
Vue.nextTick(() => {
|
||||
return Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@ -422,7 +462,7 @@ describe("The Imagery View Layouts", () => {
|
||||
|
||||
it("should show that an image is not new", (done) => {
|
||||
Vue.nextTick(() => {
|
||||
const target = imageTelemetry[2].url;
|
||||
const target = imageTelemetry[4].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
|
||||
Vue.nextTick(() => {
|
||||
@ -544,25 +584,6 @@ describe("The Imagery View Layouts", () => {
|
||||
expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width);
|
||||
done();
|
||||
});
|
||||
|
||||
it('clear data action is installed', () => {
|
||||
expect(clearDataAction).toBeDefined();
|
||||
});
|
||||
|
||||
it('on clearData action should clear data for object is selected', async (done) => {
|
||||
// force show the thumbnails
|
||||
imageryView._getInstance().$children[0].forceShowThumbnails = true;
|
||||
await Vue.nextTick();
|
||||
expect(parent.querySelectorAll('.c-imagery__thumb').length).not.toBe(0);
|
||||
openmct.objectViews.on('clearData', async (_domainObject) => {
|
||||
await Vue.nextTick();
|
||||
expect(parent.querySelectorAll('.c-imagery__thumb').length).toBe(0);
|
||||
done();
|
||||
});
|
||||
// stubbed telemetry data will return empty array when true
|
||||
isClearDataTriggered = true;
|
||||
clearDataAction.invoke(imageryObject);
|
||||
});
|
||||
});
|
||||
|
||||
describe("imagery time strip view", () => {
|
||||
|
@ -26,9 +26,10 @@ const config = {
|
||||
maelstromTheme: './src/plugins/themes/maelstrom-theme.scss'
|
||||
},
|
||||
output: {
|
||||
globalObject: "this",
|
||||
globalObject: 'this',
|
||||
filename: '[name].js',
|
||||
library: '[name]',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
library: 'openmct',
|
||||
libraryTarget: 'umd',
|
||||
publicPath: '',
|
||||
hashFunction: 'xxhash64',
|
||||
|
@ -16,5 +16,5 @@ module.exports = merge(common, {
|
||||
__OPENMCT_ROOT_RELATIVE__: '""'
|
||||
})
|
||||
],
|
||||
devtool: 'source-map'
|
||||
devtool: 'eval-source-map'
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user