Compare commits
53 Commits
fix-stacke
...
imagery-fr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
082a89440e | ||
|
|
c729732541 | ||
|
|
8d3737912b | ||
|
|
d6a71adb7f | ||
|
|
8397b13c57 | ||
|
|
6a4ceb5219 | ||
|
|
c25b196b8f | ||
|
|
cd5cc4c76c | ||
|
|
f8b818e78b | ||
|
|
6cea1a77e5 | ||
|
|
88ff09857b | ||
|
|
b409d3cb1e | ||
|
|
a64e3e5ca0 | ||
|
|
d6ba2f8b4c | ||
|
|
4f1642a8d6 | ||
|
|
9b73b45ba9 | ||
|
|
157564487d | ||
|
|
fcbd8c682a | ||
|
|
fa2197f9c1 | ||
|
|
f3f833a337 | ||
|
|
0d23fe3d14 | ||
|
|
1d645a8472 | ||
|
|
a28ec45f71 | ||
|
|
fcc6bb9873 | ||
|
|
c4ce405b1e | ||
|
|
b95f844a4e | ||
|
|
63e04caab6 | ||
|
|
956cfbd01f | ||
|
|
6c77be32c7 | ||
|
|
af4c7c9ca0 | ||
|
|
1697362994 | ||
|
|
e69911385f | ||
|
|
6478267cbe | ||
|
|
22d53c1ccd | ||
|
|
633a95dd27 | ||
|
|
50ff26ad5d | ||
|
|
f056e8e57b | ||
|
|
320217f8c4 | ||
|
|
b43fef6e21 | ||
|
|
d04c29345b | ||
|
|
49afec5cdd | ||
|
|
43a8901c34 | ||
|
|
28d97be60e | ||
|
|
4bb2b35124 | ||
|
|
060a1b17db | ||
|
|
db50b8b732 | ||
|
|
62de05808e | ||
|
|
4e5c74ecef | ||
|
|
d9dad09dfd | ||
|
|
1580a61092 | ||
|
|
c236444a05 | ||
|
|
39c1eb1d5b | ||
|
|
4633436cbd |
@@ -61,11 +61,25 @@
|
||||
<div class="c-imagery__control-bar">
|
||||
<div class="c-imagery__time">
|
||||
<div class="c-imagery__timestamp u-style-receiver js-style-receiver">{{ time }}</div>
|
||||
|
||||
<!-- image fresh -->
|
||||
<div
|
||||
v-if="canTrackDuration"
|
||||
:class="{'c-imagery--new': isImageNew && !refreshCSS}"
|
||||
class="c-imagery__age icon-timer"
|
||||
>{{ formattedDuration }}</div>
|
||||
|
||||
<!-- rover position fresh -->
|
||||
<div
|
||||
v-if="hasRelatedTelemetry && roverPositionIsFresh"
|
||||
class="c-imagery__age icon-check c-imagery--new"
|
||||
>POS</div>
|
||||
|
||||
<!-- camera position fresh -->
|
||||
<div
|
||||
v-if="hasRelatedTelemetry && cameraPositionIsFresh"
|
||||
class="c-imagery__age icon-check c-imagery--new"
|
||||
>CAM</div>
|
||||
</div>
|
||||
<div class="h-local-controls">
|
||||
<button
|
||||
@@ -97,6 +111,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||
@@ -115,6 +130,8 @@ const TWENTYFOUR_HOURS = EIGHT_HOURS * 3;
|
||||
const ARROW_RIGHT = 39;
|
||||
const ARROW_LEFT = 37;
|
||||
|
||||
const FRAME_ID_KEY = 'frame_id';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
data() {
|
||||
@@ -137,7 +154,14 @@ export default {
|
||||
refreshCSS: false,
|
||||
keyString: undefined,
|
||||
focusedImageIndex: undefined,
|
||||
numericDuration: undefined
|
||||
focusedImageRelatedData: {}, // update to focusedImageRelatedTelemetry once all merged
|
||||
focusedImageFrameId: undefined,
|
||||
numericDuration: undefined,
|
||||
metadataEndpoints: {},
|
||||
hasRelatedTelemetry: false,
|
||||
relatedTelemetry: {},
|
||||
latestRelatedTelemetry: {},
|
||||
latestFrameId: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -195,16 +219,81 @@ export default {
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
roverPositionIsFresh() {
|
||||
let isFresh = undefined;
|
||||
let latest = this.latestRelatedTelemetry;
|
||||
let focused = this.focusedImageRelatedData;
|
||||
|
||||
if (this.hasRelatedTelemetry) {
|
||||
isFresh = true;
|
||||
for (let key of this.roverKeys) {
|
||||
// if we have related telemetry for this key, we have an areEqual function,
|
||||
// and we have values for latest and focued
|
||||
let tolerance = this.relatedTelemetry[key].tolerance || 1;
|
||||
|
||||
if (this.relatedTelemetry[key] && latest[key] && focused[key]) {
|
||||
if (!this.equalWithinTolerance(latest[key], focused[key], tolerance)) {
|
||||
isFresh = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// last check to make sure in the same frame
|
||||
// if no frame, comparison will still be equal undefined === undefined
|
||||
if (isFresh) {
|
||||
isFresh = this.latestFrameId === this.focusedImageFrameId;
|
||||
}
|
||||
}
|
||||
|
||||
return isFresh;
|
||||
},
|
||||
cameraPositionIsFresh() {
|
||||
let isFresh = undefined;
|
||||
let latest = this.latestRelatedTelemetry;
|
||||
let focused = this.focusedImageRelatedData;
|
||||
|
||||
if (this.hasRelatedTelemetry) {
|
||||
isFresh = true;
|
||||
|
||||
// camera freshness relies on rover position freshness
|
||||
if (this.roverPositionIsFresh) {
|
||||
for (let key of this.cameraKeys) {
|
||||
// if we have related telemetry for this key, we have an areEqual function,
|
||||
// and we have values for latest and focued
|
||||
let tolerance = this.relatedTelemetry[key].tolerance || 1;
|
||||
|
||||
if (this.relatedTelemetry[key] && latest[key] && focused[key]) {
|
||||
if (!this.equalWithinTolerance(latest[key], focused[key], tolerance)) {
|
||||
isFresh = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isFresh = false;
|
||||
}
|
||||
}
|
||||
|
||||
return isFresh;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
latestRelatedTelemetry() {
|
||||
console.log('latest related telemetry has changed');
|
||||
},
|
||||
focusedImageRelatedData() {
|
||||
console.log('focused related telemetry has changed');
|
||||
},
|
||||
focusedImageIndex() {
|
||||
console.log('focused image index changed');
|
||||
this.trackDuration();
|
||||
this.resetAgeCSS();
|
||||
this.updateRelatedTelemetryForFocusedImage();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
async mounted() {
|
||||
// listen
|
||||
console.log('testing viper: 41231');
|
||||
this.openmct.time.on('bounds', this.boundsChange);
|
||||
this.openmct.time.on('timeSystem', this.timeSystemChange);
|
||||
this.openmct.time.on('clock', this.clockChange);
|
||||
@@ -212,8 +301,18 @@ export default {
|
||||
// set
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.imageHints = { ...this.metadata.valuesForHints(['image'])[0] };
|
||||
this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
|
||||
this.imageFormatter = this.openmct.telemetry.getValueFormatter(this.metadata.valuesForHints(['image'])[0]);
|
||||
this.imageFormatter = this.openmct.telemetry.getValueFormatter(this.imageHints);
|
||||
this.telemetryKeyWithFrameId = 'Rover Heading';
|
||||
this.roverKeys = ['Rover Heading', 'Rover Roll', 'Rover Yaw', 'Rover Pitch'];
|
||||
this.cameraKeys = ['Camera Pan', 'Camera Tilt'];
|
||||
this.sunKeys = ['Sun Orientation'];
|
||||
|
||||
// DELETE WHEN DONE
|
||||
if (!this.imageHints.relatedTelemetry) {
|
||||
this.temporaryForImageEnhancements();
|
||||
}
|
||||
|
||||
// initialize
|
||||
this.timeKey = this.timeSystem.key;
|
||||
@@ -222,6 +321,14 @@ export default {
|
||||
// kickoff
|
||||
this.subscribe();
|
||||
this.requestHistory();
|
||||
await this.initializeRelatedTelemetry();
|
||||
this.updateRelatedTelemetryForFocusedImage();
|
||||
|
||||
// track latest telemetry values for rover, camera and sun for comparison
|
||||
this.trackLatestRelatedTelemetry();
|
||||
|
||||
// for when people are scrolling through images quickly
|
||||
_.debounce(this.updateRelatedTelemetryForFocusedImage, 400);
|
||||
},
|
||||
updated() {
|
||||
this.scrollToRight();
|
||||
@@ -236,8 +343,259 @@ export default {
|
||||
this.openmct.time.off('bounds', this.boundsChange);
|
||||
this.openmct.time.off('timeSystem', this.timeSystemChange);
|
||||
this.openmct.time.off('clock', this.clockChange);
|
||||
|
||||
// unsubscribe from related telemetry
|
||||
if (this.hasRelatedTelemetry) {
|
||||
for (let key of this.relatedTelemetry.keys) {
|
||||
if (this.relatedTelemetry[key].unsubscribe) {
|
||||
this.relatedTelemetry[key].unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// for local dev, to be DELETED
|
||||
temporaryForImageEnhancements() {
|
||||
this.searchService = this.openmct.$injector.get('searchService');
|
||||
this.temporaryDev = true;
|
||||
|
||||
// mock related telemetry metadata
|
||||
this.imageHints.relatedTelemetry = {};
|
||||
|
||||
// populate temp keys in imageHints for local testing
|
||||
[...this.roverKeys, ...this.cameraKeys, ...this.sunKeys].forEach(key => {
|
||||
|
||||
this.imageHints.relatedTelemetry[key] = {
|
||||
dev: true,
|
||||
areEqual: function (valueOne, valueTwo) {
|
||||
const DECIMAL_COMPARISON_TOLERANCE = 1;
|
||||
const WHOLE = Math.pow(10, DECIMAL_COMPARISON_TOLERANCE);
|
||||
|
||||
return Math.floor(valueOne.toFixed(DECIMAL_COMPARISON_TOLERANCE) * WHOLE) === Math.floor(valueTwo.toFixed(DECIMAL_COMPARISON_TOLERANCE) * WHOLE);
|
||||
},
|
||||
realtime: {
|
||||
telemetryObjectId: key,
|
||||
valueKey: 'sin'
|
||||
},
|
||||
historical: {
|
||||
telemetryObjectId: key,
|
||||
valueKey: 'sin'
|
||||
},
|
||||
devInit: async () => {
|
||||
const searchResults = await this.searchService.query(key);
|
||||
const endpoint = searchResults.hits[0].id;
|
||||
const domainObject = await this.openmct.objects.get(endpoint);
|
||||
|
||||
return domainObject;
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
async initializeRelatedTelemetry() {
|
||||
if (this.imageHints.relatedTelemetry === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let loadedResolve;
|
||||
this.relatedTelemetryLoaded = new Promise((resolve, reject) => {
|
||||
loadedResolve = resolve;
|
||||
});
|
||||
|
||||
// DELETE
|
||||
if (this.temporaryDev) {
|
||||
let searchIndexBuildDelay = new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, 3000);
|
||||
});
|
||||
|
||||
await searchIndexBuildDelay;
|
||||
}
|
||||
|
||||
let keys = Object.keys(this.imageHints.relatedTelemetry);
|
||||
|
||||
this.hasRelatedTelemetry = true;
|
||||
this.relatedTelemetry = {
|
||||
keys,
|
||||
...this.imageHints.relatedTelemetry
|
||||
};
|
||||
|
||||
// grab historical and subscribe to realtime
|
||||
for (let key of keys) {
|
||||
let historicalId;
|
||||
let realtimeId;
|
||||
|
||||
if (this.relatedTelemetry[key].historical) {
|
||||
if (this.relatedTelemetry[key].historical.telemetryObjectId) {
|
||||
historicalId = this.relatedTelemetry[key].historical.telemetryObjectId;
|
||||
} else {
|
||||
this.relatedTelemetry[key].historicalValuesOnTelemetry = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.relatedTelemetry[key].realtime && this.relatedTelemetry[key].realtime.telemetryObjectId) {
|
||||
realtimeId = this.relatedTelemetry[key].realtime.telemetryObjectId;
|
||||
}
|
||||
|
||||
// if we have a historical object id, then values will NOT be on the imagery datum
|
||||
if (historicalId) {
|
||||
|
||||
// DELETE temp
|
||||
if (this.relatedTelemetry[key].dev) {
|
||||
this.relatedTelemetry[key].historicalDomainObject = await this.relatedTelemetry[key].devInit();
|
||||
} else {
|
||||
this.relatedTelemetry[key].historicalDomainObject = await this.openmct.objects.get(historicalId);
|
||||
}
|
||||
|
||||
this.relatedTelemetry[key].requestLatestFor = async (datum) => {
|
||||
const options = {
|
||||
start: this.openmct.time.bounds().start,
|
||||
end: datum[this.timeKey],
|
||||
strategy: 'latest'
|
||||
};
|
||||
let results = await this.openmct.telemetry
|
||||
.request(this.relatedTelemetry[key].historicalDomainObject, options);
|
||||
|
||||
return results[results.length - 1];
|
||||
};
|
||||
}
|
||||
|
||||
if (realtimeId) {
|
||||
|
||||
if (this.relatedTelemetry[key].dev) {
|
||||
this.relatedTelemetry[key].realtimeDomainObject = await this.relatedTelemetry[key].devInit();
|
||||
} else {
|
||||
this.relatedTelemetry[key].realtimeDomainObject = await this.openmct.objects.get(realtimeId);
|
||||
}
|
||||
|
||||
// set up listeners
|
||||
this.relatedTelemetry[key].listeners = [];
|
||||
this.relatedTelemetry[key].subscribe = (callback) => {
|
||||
|
||||
if (!this.relatedTelemetry[key].isSubscribed) {
|
||||
this.subscribeToDataForKey(key);
|
||||
}
|
||||
|
||||
if (!this.relatedTelemetry[key].listeners.includes(callback)) {
|
||||
this.relatedTelemetry[key].listeners.push(callback);
|
||||
|
||||
return () => {
|
||||
this.relatedTelemetry[key].listeners.remove(callback);
|
||||
};
|
||||
} else {
|
||||
return () => {};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
loadedResolve();
|
||||
},
|
||||
async getMostRecentFrameId(key, targetDatum) {
|
||||
if (!this.hasRelatedTelemetry) {
|
||||
throw new Error(`${this.domainObject.name} does not have any related telemetry`);
|
||||
}
|
||||
|
||||
let mostRecent;
|
||||
let valuesOnTelemetry = this.relatedTelemetry[key].historicalValuesOnTelemetry;
|
||||
|
||||
if (valuesOnTelemetry) {
|
||||
mostRecent = targetDatum[FRAME_ID_KEY];
|
||||
}
|
||||
|
||||
mostRecent = await this.relatedTelemetry[key].requestLatestFor(targetDatum);
|
||||
|
||||
return mostRecent[FRAME_ID_KEY];
|
||||
|
||||
},
|
||||
async getMostRecentRelatedTelemetry(key, targetDatum) {
|
||||
if (!this.hasRelatedTelemetry) {
|
||||
throw new Error(`${this.domainObject.name} does not have any related telemetry`);
|
||||
}
|
||||
|
||||
if (!this.relatedTelemetry[key]) {
|
||||
throw new Error(`${key} does not exist on related telemetry`);
|
||||
}
|
||||
|
||||
await this.relatedTelemetryLoaded;
|
||||
|
||||
let mostRecent;
|
||||
let valueKey = this.relatedTelemetry[key].historical.valueKey;
|
||||
let valuesOnTelemetry = this.relatedTelemetry[key].historicalValuesOnTelemetry;
|
||||
|
||||
if (valuesOnTelemetry) {
|
||||
mostRecent = targetDatum[valueKey];
|
||||
|
||||
if (mostRecent) {
|
||||
return mostRecent;
|
||||
} else {
|
||||
console.warn(`Related Telemetry for ${key} does NOT exist on this telemetry datum as configuration implied.`);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mostRecent = await this.relatedTelemetry[key].requestLatestFor(targetDatum);
|
||||
|
||||
return mostRecent[valueKey];
|
||||
},
|
||||
// will subscribe to data for this key if not already done
|
||||
subscribeToDataForKey(key) {
|
||||
if (this.relatedTelemetry[key].isSubscribed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.relatedTelemetry[key].realtimeDomainObject) {
|
||||
this.relatedTelemetry[key].unsubscribe = this.openmct.telemetry.subscribe(
|
||||
this.relatedTelemetry[key].realtimeDomainObject, datum => {
|
||||
this.relatedTelemetry[key].listeners.forEach(callback => {
|
||||
callback(datum);
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
this.relatedTelemetry[key].isSubscribed = true;
|
||||
}
|
||||
},
|
||||
async updateRelatedTelemetryForFocusedImage() {
|
||||
if (!this.hasRelatedTelemetry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set data ON image telemetry as well as in focusedImageRelatedData
|
||||
for (let key of this.relatedTelemetry.keys) {
|
||||
if (this.relatedTelemetry[key] && this.relatedTelemetry[key].historical) {
|
||||
let valuesOnTelemetry = this.relatedTelemetry[key].historicalValuesOnTelemetry;
|
||||
let value = await this.getMostRecentRelatedTelemetry(key, this.focusedImage);
|
||||
|
||||
if (!valuesOnTelemetry) {
|
||||
this.imageHistory[this.focusedImageIndex][key] = value; // manually add to telemetry
|
||||
}
|
||||
|
||||
this.$set(this.focusedImageRelatedData, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// get frame ID
|
||||
this.latestFrameId = await this.getMostRecentFrameId(this.telemetryKeyWithFrameId, this.focusedImage);
|
||||
},
|
||||
trackLatestRelatedTelemetry() {
|
||||
console.log('begin tracking latest');
|
||||
[...this.roverKeys, ...this.cameraKeys, ...this.sunKeys].forEach(key => {
|
||||
if (this.relatedTelemetry[key] && this.relatedTelemetry[key].subscribe) {
|
||||
this.relatedTelemetry[key].subscribe((datum) => {
|
||||
let valueKey = this.relatedTelemetry[key].realtime.valueKey;
|
||||
this.$set(this.latestRelatedTelemetry, key, datum[valueKey]);
|
||||
|
||||
// if it is the telemetry with the frame ID track latest
|
||||
if (key === this.telemetryKeyWithFrameId) {
|
||||
this.latestFrameId = datum[FRAME_ID_KEY];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
focusElement() {
|
||||
this.$el.focus();
|
||||
},
|
||||
@@ -509,6 +867,12 @@ export default {
|
||||
},
|
||||
isLeftOrRightArrowKey(keyCode) {
|
||||
return [ARROW_RIGHT, ARROW_LEFT].includes(keyCode);
|
||||
},
|
||||
equalWithinTolerance(valueOne, valueTwo, tolerance) {
|
||||
const DECIMAL_COMPARISON_TOLERANCE = tolerance;
|
||||
const WHOLE = Math.pow(10, DECIMAL_COMPARISON_TOLERANCE);
|
||||
|
||||
return Math.floor(valueOne.toFixed(DECIMAL_COMPARISON_TOLERANCE) * WHOLE) === Math.floor(valueTwo.toFixed(DECIMAL_COMPARISON_TOLERANCE) * WHOLE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,13 +71,14 @@
|
||||
}
|
||||
|
||||
&__age {
|
||||
border-radius: $controlCr;
|
||||
border-radius: $smallCr;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: baseline;
|
||||
padding: 1px $interiorMarginSm;
|
||||
align-items: center;
|
||||
padding: 2px $interiorMarginSm;
|
||||
|
||||
&:before {
|
||||
font-size: 0.9em;
|
||||
opacity: 0.5;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
@@ -86,8 +87,9 @@
|
||||
&--new {
|
||||
// New imagery
|
||||
$bgColor: $colorOk;
|
||||
color: $colorOkFg;
|
||||
background: rgba($bgColor, 0.5);
|
||||
@include flash($animName: flashImageAge, $dur: 250ms, $valStart: rgba($colorOk, 0.7), $valEnd: rgba($colorOk, 0));
|
||||
@include flash($animName: flashImageAge, $iter: 2, $dur: 250ms, $valStart: rgba($colorOk, 0.7), $valEnd: rgba($colorOk, 0));
|
||||
}
|
||||
|
||||
&__thumbs-wrapper {
|
||||
|
||||
Reference in New Issue
Block a user