[Time Conductor] Realtime presets and history tracking (#3270)

Time conductor realtime preset/history updates

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
Jamie V 2020-10-28 17:46:28 -07:00 committed by GitHub
parent 8cd6a4c6a3
commit 0e6ce7f58b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 28 deletions

View File

@ -35,7 +35,13 @@
</body> </body>
<script> <script>
const THIRTY_SECONDS = 30 * 1000; const THIRTY_SECONDS = 30 * 1000;
const THIRTY_MINUTES = THIRTY_SECONDS * 60; const ONE_MINUTE = THIRTY_SECONDS * 2;
const FIVE_MINUTES = ONE_MINUTE * 5;
const FIFTEEN_MINUTES = FIVE_MINUTES * 3;
const THIRTY_MINUTES = FIFTEEN_MINUTES * 2;
const ONE_HOUR = THIRTY_MINUTES * 2;
const TWO_HOURS = ONE_HOUR * 2;
const ONE_DAY = ONE_HOUR * 24;
[ [
'example/eventGenerator' 'example/eventGenerator'
@ -73,21 +79,21 @@
{ {
label: 'Last Day', label: 'Last Day',
bounds: { bounds: {
start: () => Date.now() - 1000 * 60 * 60 * 24, start: () => Date.now() - ONE_DAY,
end: () => Date.now() end: () => Date.now()
} }
}, },
{ {
label: 'Last 2 hours', label: 'Last 2 hours',
bounds: { bounds: {
start: () => Date.now() - 1000 * 60 * 60 * 2, start: () => Date.now() - TWO_HOURS,
end: () => Date.now() end: () => Date.now()
} }
}, },
{ {
label: 'Last hour', label: 'Last hour',
bounds: { bounds: {
start: () => Date.now() - 1000 * 60 * 60, start: () => Date.now() - ONE_HOUR,
end: () => Date.now() end: () => Date.now()
} }
} }
@ -96,7 +102,7 @@
records: 10, records: 10,
// maximum duration between start and end bounds // maximum duration between start and end bounds
// for utc-based time systems this is in milliseconds // for utc-based time systems this is in milliseconds
limit: 1000 * 60 * 60 * 24 limit: ONE_DAY
}, },
{ {
name: "Realtime", name: "Realtime",
@ -105,7 +111,44 @@
clockOffsets: { clockOffsets: {
start: - THIRTY_MINUTES, start: - THIRTY_MINUTES,
end: THIRTY_SECONDS end: THIRTY_SECONDS
} },
presets: [
{
label: '1 Hour',
bounds: {
start: - ONE_HOUR,
end: THIRTY_SECONDS
}
},
{
label: '30 Minutes',
bounds: {
start: - THIRTY_MINUTES,
end: THIRTY_SECONDS
}
},
{
label: '15 Minutes',
bounds: {
start: - FIFTEEN_MINUTES,
end: THIRTY_SECONDS
}
},
{
label: '5 Minutes',
bounds: {
start: - FIVE_MINUTES,
end: THIRTY_SECONDS
}
},
{
label: '1 Minute',
bounds: {
start: - ONE_MINUTE,
end: THIRTY_SECONDS
}
}
]
} }
] ]
})); }));

View File

@ -141,10 +141,11 @@
<ConductorMode class="c-conductor__mode-select" /> <ConductorMode class="c-conductor__mode-select" />
<ConductorTimeSystem class="c-conductor__time-system-select" /> <ConductorTimeSystem class="c-conductor__time-system-select" />
<ConductorHistory <ConductorHistory
v-if="isFixed"
class="c-conductor__history-select" class="c-conductor__history-select"
:offsets="openmct.time.clockOffsets()"
:bounds="bounds" :bounds="bounds"
:time-system="timeSystem" :time-system="timeSystem"
:mode="timeMode"
/> />
</div> </div>
<input <input
@ -210,6 +211,11 @@ export default {
isZooming: false isZooming: false
}; };
}, },
computed: {
timeMode() {
return this.isFixed ? 'fixed' : 'realtime';
}
},
mounted() { mounted() {
document.addEventListener('keydown', this.handleKeyDown); document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp); document.addEventListener('keyup', this.handleKeyUp);

View File

@ -66,7 +66,9 @@
<script> <script>
import toggleMixin from '../../ui/mixins/toggle-mixin'; import toggleMixin from '../../ui/mixins/toggle-mixin';
const LOCAL_STORAGE_HISTORY_KEY = 'tcHistory'; const DEFAULT_DURATION_FORMATTER = 'duration';
const LOCAL_STORAGE_HISTORY_KEY_FIXED = 'tcHistory';
const LOCAL_STORAGE_HISTORY_KEY_REALTIME = 'tcHistoryRealtime';
const DEFAULT_RECORDS = 10; const DEFAULT_RECORDS = 10;
export default { export default {
@ -77,72 +79,115 @@ export default {
type: Object, type: Object,
required: true required: true
}, },
offsets: {
type: Object,
required: false,
default: () => {}
},
timeSystem: { timeSystem: {
type: Object, type: Object,
required: true required: true
},
mode: {
type: String,
required: true
} }
}, },
data() { data() {
return { return {
/** /**
* previous bounds entries available for easy re-use * previous bounds entries available for easy re-use
* @history array of timespans * @realtimeHistory array of timespans
* @timespans {start, end} number representing timestamp * @timespans {start, end} number representing timestamp
*/ */
history: this.getHistoryFromLocalStorage(), realtimeHistory: {},
/**
* previous bounds entries available for easy re-use
* @fixedHistory array of timespans
* @timespans {start, end} number representing timestamp
*/
fixedHistory: {},
presets: [] presets: []
}; };
}, },
computed: { computed: {
currentHistory() {
return this.mode + 'History';
},
isFixed() {
return this.openmct.time.clock() === undefined;
},
hasHistoryPresets() { hasHistoryPresets() {
return this.timeSystem.isUTCBased && this.presets.length; return this.timeSystem.isUTCBased && this.presets.length;
}, },
historyForCurrentTimeSystem() { historyForCurrentTimeSystem() {
const history = this.history[this.timeSystem.key]; const history = this[this.currentHistory][this.timeSystem.key];
return history; return history;
},
storageKey() {
let key = LOCAL_STORAGE_HISTORY_KEY_FIXED;
if (this.mode !== 'fixed') {
key = LOCAL_STORAGE_HISTORY_KEY_REALTIME;
}
return key;
} }
}, },
watch: { watch: {
bounds: { bounds: {
handler() {
// only for fixed time since we track offsets for realtime
if (this.isFixed) {
this.addTimespan();
}
},
deep: true
},
offsets: {
handler() { handler() {
this.addTimespan(); this.addTimespan();
}, },
deep: true deep: true
}, },
timeSystem: { timeSystem: {
handler() { handler(ts) {
this.loadConfiguration(); this.loadConfiguration();
this.addTimespan(); this.addTimespan();
}, },
deep: true deep: true
},
mode: function () {
this.getHistoryFromLocalStorage();
this.initializeHistoryIfNoHistory();
this.loadConfiguration();
} }
}, },
mounted() { mounted() {
this.getHistoryFromLocalStorage();
this.initializeHistoryIfNoHistory(); this.initializeHistoryIfNoHistory();
}, },
methods: { methods: {
getHistoryFromLocalStorage() { getHistoryFromLocalStorage() {
const localStorageHistory = localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY); const localStorageHistory = localStorage.getItem(this.storageKey);
const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined; const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined;
this[this.currentHistory] = history;
return history;
}, },
initializeHistoryIfNoHistory() { initializeHistoryIfNoHistory() {
if (!this.history) { if (!this[this.currentHistory]) {
this.history = {}; this[this.currentHistory] = {};
this.persistHistoryToLocalStorage(); this.persistHistoryToLocalStorage();
} }
}, },
persistHistoryToLocalStorage() { persistHistoryToLocalStorage() {
localStorage.setItem(LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(this.history)); localStorage.setItem(this.storageKey, JSON.stringify(this[this.currentHistory]));
}, },
addTimespan() { addTimespan() {
const key = this.timeSystem.key; const key = this.timeSystem.key;
let [...currentHistory] = this.history[key] || []; let [...currentHistory] = this[this.currentHistory][key] || [];
const timespan = { const timespan = {
start: this.bounds.start, start: this.isFixed ? this.bounds.start : this.offsets.start,
end: this.bounds.end end: this.isFixed ? this.bounds.end : this.offsets.end
}; };
let self = this; let self = this;
@ -160,20 +205,24 @@ export default {
} }
currentHistory.unshift(timespan); currentHistory.unshift(timespan);
this.history[key] = currentHistory; this.$set(this[this.currentHistory], key, currentHistory);
this.persistHistoryToLocalStorage(); this.persistHistoryToLocalStorage();
}, },
selectTimespan(timespan) { selectTimespan(timespan) {
this.openmct.time.bounds(timespan); if (this.isFixed) {
this.openmct.time.bounds(timespan);
} else {
this.openmct.time.clockOffsets(timespan);
}
}, },
selectPresetBounds(bounds) { selectPresetBounds(bounds) {
const start = typeof bounds.start === 'function' ? bounds.start() : bounds.start; const start = typeof bounds.start === 'function' ? bounds.start() : bounds.start;
const end = typeof bounds.end === 'function' ? bounds.end() : bounds.end; const end = typeof bounds.end === 'function' ? bounds.end() : bounds.end;
this.selectTimespan({ this.selectTimespan({
start: start, start,
end: end end
}); });
}, },
loadConfiguration() { loadConfiguration() {
@ -184,7 +233,9 @@ export default {
this.records = this.loadRecords(configurations); this.records = this.loadRecords(configurations);
}, },
loadPresets(configurations) { loadPresets(configurations) {
const configuration = configurations.find(option => option.presets); const configuration = configurations.find(option => {
return option.presets && option.name.toLowerCase() === this.mode;
});
const presets = configuration ? configuration.presets : []; const presets = configuration ? configuration.presets : [];
return presets; return presets;
@ -196,11 +247,24 @@ export default {
return records; return records;
}, },
formatTime(time) { formatTime(time) {
let format = this.timeSystem.timeFormat;
let isNegativeOffset = false;
if (!this.isFixed) {
if (time < 0) {
isNegativeOffset = true;
}
time = Math.abs(time);
format = this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER;
}
const formatter = this.openmct.telemetry.getValueFormatter({ const formatter = this.openmct.telemetry.getValueFormatter({
format: this.timeSystem.timeFormat format: format
}).formatter; }).formatter;
return formatter.format(time); return (isNegativeOffset ? '-' : '') + formatter.format(time);
} }
} }
}; };