diff --git a/API.md b/API.md index d5416e72fc..c5f27843c2 100644 --- a/API.md +++ b/API.md @@ -27,7 +27,7 @@ - [Request Strategies **draft**](#request-strategies-draft) - [`latest` request strategy](#latest-request-strategy) - [`minmax` request strategy](#minmax-request-strategy) - - [Telemetry Formats **draft**](#telemetry-formats-draft) + - [Telemetry Formats](#telemetry-formats) - [Registering Formats](#registering-formats) - [Telemetry Data](#telemetry-data) - [Telemetry Datums](#telemetry-datums) @@ -525,7 +525,7 @@ example: MinMax queries are issued by plots, and may be issued by other types as well. The aim is to reduce the amount of data returned but still faithfully represent the full extent of the data. In order to do this, the view calculates the maximum data resolution it can display (i.e. the number of horizontal pixels in a plot) and sends that as the `size`. The response should include at least one minimum and one maximum value per point of resolution. -#### Telemetry Formats **draft** +#### Telemetry Formats Telemetry format objects define how to interpret and display telemetry data. They have a simple structure: diff --git a/platform/commonUI/formats/bundle.js b/platform/commonUI/formats/bundle.js deleted file mode 100644 index c4f31c72fc..0000000000 --- a/platform/commonUI/formats/bundle.js +++ /dev/null @@ -1,72 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2021, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -define([ - "./src/FormatProvider", - "./src/DurationFormat" -], function ( - FormatProvider, - DurationFormat -) { - return { - name: "platform/commonUI/formats", - definition: { - "name": "Format Registry", - "description": "Provides a registry for formats, which allow parsing and formatting of values.", - "extensions": { - "components": [ - { - "provides": "formatService", - "type": "provider", - "implementation": FormatProvider, - "depends": [ - "formats[]" - ] - } - ], - "formats": [ - { - "key": "duration", - "implementation": DurationFormat - } - ], - "constants": [ - { - "key": "DEFAULT_TIME_FORMAT", - "value": "utc" - } - ], - "licenses": [ - { - "name": "d3", - "version": "3.0.0", - "description": "Incorporates modified code from d3 Time Scales", - "author": "Mike Bostock", - "copyright": "Copyright 2010-2016 Mike Bostock. " - + "All rights reserved.", - "link": "https://github.com/d3/d3/blob/master/LICENSE" - } - ] - } - } - }; -}); diff --git a/platform/commonUI/formats/src/DurationFormat.js b/platform/commonUI/formats/src/DurationFormat.js deleted file mode 100644 index 593bf31414..0000000000 --- a/platform/commonUI/formats/src/DurationFormat.js +++ /dev/null @@ -1,62 +0,0 @@ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT Web includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -define([ - 'moment' -], function ( - moment -) { - - var DATE_FORMAT = "HH:mm:ss", - DATE_FORMATS = [ - DATE_FORMAT - ]; - - /** - * Formatter for duration. Uses moment to produce a date from a given - * value, but output is formatted to display only time. Can be used for - * specifying a time duration. For specifying duration, it's best to - * specify a date of January 1, 1970, as the ms offset will equal the - * duration represented by the time. - * - * @implements {Format} - * @constructor - * @memberof platform/commonUI/formats - */ - function DurationFormat() { - this.key = "duration"; - } - - DurationFormat.prototype.format = function (value) { - return moment.utc(value).format(DATE_FORMAT); - }; - - DurationFormat.prototype.parse = function (text) { - return moment.duration(text).asMilliseconds(); - }; - - DurationFormat.prototype.validate = function (text) { - return moment.utc(text, DATE_FORMATS, true).isValid(); - }; - - return DurationFormat; -}); diff --git a/platform/commonUI/formats/src/FormatProvider.js b/platform/commonUI/formats/src/FormatProvider.js deleted file mode 100644 index ab49c25574..0000000000 --- a/platform/commonUI/formats/src/FormatProvider.js +++ /dev/null @@ -1,123 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2021, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -define([ - -], function ( - -) { - - /** - * An object used to convert between numeric values and text values, - * typically used to display these values to the user and to convert - * user input to a numeric format, particularly for time formats. - * @interface Format - */ - /** - * Parse text (typically user input) to a numeric value. - * Behavior is undefined when the text cannot be parsed; - * `validate` should be called first if the text may be invalid. - * @method Format#parse - * @memberof Format# - * @param {string} text the text to parse - * @returns {number} the parsed numeric value - */ - - /** - * @property {string} key A unique identifier for this formatter. - * @memberof Format# - */ - - /** - * Determine whether or not some text (typically user input) can - * be parsed to a numeric value by this format. - * @method validate - * @memberof Format# - * @param {string} text the text to parse - * @returns {boolean} true if the text can be parsed - */ - - /** - * Convert a numeric value to a text value for display using - * this format. - * @method format - * @memberof Format# - * @param {number} value the numeric value to format - * @param {number} [minValue] Contextual information for scaled formatting used in linear scales such as conductor - * and plot axes. Specifies the smallest number on the scale. - * @param {number} [maxValue] Contextual information for scaled formatting used in linear scales such as conductor - * and plot axes. Specifies the largest number on the scale - * @param {number} [count] Contextual information for scaled formatting used in linear scales such as conductor - * and plot axes. The number of labels on the scale. - * @returns {string} the text representation of the value - */ - - /** - * Provides access to `Format` objects which can be used to - * convert values between human-readable text and numeric - * representations. - * @interface FormatService - */ - - /** - * Look up a format by its symbolic identifier. - * @method getFormat - * @memberof FormatService# - * @param {string} key the identifier for this format - * @returns {Format} the format - * @throws {Error} errors when the requested format is unrecognized - */ - - /** - * Provides formats from the `formats` extension category. - * @constructor - * @implements {FormatService} - * @memberof platform/commonUI/formats - * @param {Array.} format constructors, - * from the `formats` extension category. - */ - function FormatProvider(formats) { - var formatMap = {}; - - function addToMap(Format) { - var key = Format.key; - if (key && !formatMap[key]) { - formatMap[key] = new Format(); - } - } - - formats.forEach(addToMap); - this.formatMap = formatMap; - } - - FormatProvider.prototype.getFormat = function (key) { - var format = this.formatMap[key]; - if (!format) { - throw new Error("FormatProvider: No format found for " + key); - } - - return format; - }; - - return FormatProvider; - -}); diff --git a/platform/commonUI/formats/test/FormatProviderSpec.js b/platform/commonUI/formats/test/FormatProviderSpec.js deleted file mode 100644 index 37497e8a5b..0000000000 --- a/platform/commonUI/formats/test/FormatProviderSpec.js +++ /dev/null @@ -1,69 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2021, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -define( - ['../src/FormatProvider'], - function (FormatProvider) { - - var KEYS = ['a', 'b', 'c']; - - describe("The FormatProvider", function () { - var mockFormats, - mockFormatInstances, - provider; - - beforeEach(function () { - mockFormatInstances = KEYS.map(function (k) { - return jasmine.createSpyObj( - 'format-' + k, - ['parse', 'validate', 'format'] - ); - }); - // Return constructors - mockFormats = KEYS.map(function (k, i) { - function MockFormat() { - return mockFormatInstances[i]; - } - - MockFormat.key = k; - - return MockFormat; - }); - provider = new FormatProvider(mockFormats); - }); - - it("looks up formats by key", function () { - KEYS.forEach(function (k, i) { - expect(provider.getFormat(k)) - .toEqual(mockFormatInstances[i]); - }); - }); - - it("throws an error about unknown formats", function () { - expect(function () { - provider.getFormat('some-unknown-format'); - }).toThrow(); - }); - - }); - } -); diff --git a/src/MCT.js b/src/MCT.js index 7857493f21..bf0181af50 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -299,7 +299,6 @@ define([ this.install(this.plugins.ObjectInterceptors()); this.install(this.plugins.NonEditableFolder()); this.install(this.plugins.DeviceClassifier()); - this.install(this.plugins.UTCTimeFormat()); } MCT.prototype = Object.create(EventEmitter.prototype); diff --git a/src/api/objects/TransactionSpec.js b/src/api/objects/TransactionSpec.js index f9f48b1c53..e616b623f7 100644 --- a/src/api/objects/TransactionSpec.js +++ b/src/api/objects/TransactionSpec.js @@ -9,7 +9,7 @@ describe("Transaction Class", () => { beforeEach(() => { objectAPI = { makeKeyString: (identifier) => utils.makeKeyString(identifier), - save: (object) => object, + save: (object) => Promise.resolve(object), mutate: (object, prop, value) => { object[prop] = value; @@ -60,7 +60,7 @@ describe("Transaction Class", () => { mockDomainObjects.forEach(transaction.add.bind(transaction)); expect(transaction.dirtyObjects.size).toEqual(3); - spyOn(objectAPI, 'save'); + spyOn(objectAPI, 'save').and.callThrough(); transaction.commit() .then(success => { expect(transaction.dirtyObjects.size).toEqual(0); diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index 6a774b6360..16527fa1f0 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -144,6 +144,7 @@ define([ this.metadataCache = new WeakMap(); this.formatMapCache = new WeakMap(); this.valueFormatterCache = new WeakMap(); + this.formatters = new Map(); this.requestAbortControllers = new Set(); } @@ -445,17 +446,6 @@ define([ return _.sortBy(options, sortKeys); }; - /** - * @private - */ - TelemetryAPI.prototype.getFormatService = function () { - if (!this.formatService) { - this.formatService = this.openmct.$injector.get('formatService'); - } - - return this.formatService; - }; - /** * Get a value formatter for a given valueMetadata. * @@ -465,7 +455,7 @@ define([ if (!this.valueFormatterCache.has(valueMetadata)) { this.valueFormatterCache.set( valueMetadata, - new TelemetryValueFormatter(valueMetadata, this.getFormatService()) + new TelemetryValueFormatter(valueMetadata, this.formatters) ); } @@ -479,9 +469,7 @@ define([ * @returns {Format} */ TelemetryAPI.prototype.getFormatter = function (key) { - const formatMap = this.getFormatService().formatMap; - - return formatMap[key]; + return this.formatters.get(key); }; /** @@ -512,12 +500,7 @@ define([ * @param {Format} format the */ TelemetryAPI.prototype.addFormat = function (format) { - this.openmct.legacyExtension('formats', { - key: format.key, - implementation: function () { - return format; - } - }); + this.formatters.set(format.key, format); }; /** diff --git a/src/api/telemetry/TelemetryValueFormatter.js b/src/api/telemetry/TelemetryValueFormatter.js index e0ed726f8a..148b08366d 100644 --- a/src/api/telemetry/TelemetryValueFormatter.js +++ b/src/api/telemetry/TelemetryValueFormatter.js @@ -29,7 +29,7 @@ define([ ) { // TODO: needs reference to formatService; - function TelemetryValueFormatter(valueMetadata, formatService) { + function TelemetryValueFormatter(valueMetadata, formatMap) { const numberFormatter = { parse: function (x) { return Number(x); @@ -43,13 +43,7 @@ define([ }; this.valueMetadata = valueMetadata; - try { - this.formatter = formatService - .getFormat(valueMetadata.format, valueMetadata); - } catch (e) { - // TODO: Better formatting - this.formatter = numberFormatter; - } + this.formatter = formatMap.get(valueMetadata.format) || numberFormatter; if (valueMetadata.format === 'enum') { this.formatter = {}; diff --git a/src/installDefaultBundles.js b/src/installDefaultBundles.js index dc28edcf92..681d5bbdcd 100644 --- a/src/installDefaultBundles.js +++ b/src/installDefaultBundles.js @@ -27,7 +27,6 @@ const DEFAULTS = [ 'platform/commonUI/browse', 'platform/commonUI/edit', 'platform/commonUI/dialog', - 'platform/commonUI/formats', 'platform/commonUI/general', 'platform/commonUI/inspect', 'platform/commonUI/mobile', @@ -60,7 +59,6 @@ define([ '../platform/commonUI/browse/bundle', '../platform/commonUI/dialog/bundle', '../platform/commonUI/edit/bundle', - '../platform/commonUI/formats/bundle', '../platform/commonUI/general/bundle', '../platform/commonUI/inspect/bundle', '../platform/commonUI/mobile/bundle', diff --git a/src/plugins/UTCTimeFormat/plugin.js b/src/plugins/UTCTimeFormat/plugin.js deleted file mode 100644 index e956f68e03..0000000000 --- a/src/plugins/UTCTimeFormat/plugin.js +++ /dev/null @@ -1,29 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2021, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -import UTCTimeFormat from './UTCTimeFormat'; - -export default function () { - return function install(openmct) { - openmct.telemetry.addFormat(new UTCTimeFormat()); - }; -} diff --git a/src/plugins/UTCTimeFormat/pluginSpec.js b/src/plugins/UTCTimeFormat/pluginSpec.js deleted file mode 100644 index a6923ef7da..0000000000 --- a/src/plugins/UTCTimeFormat/pluginSpec.js +++ /dev/null @@ -1,94 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2021, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -import UTCTimeFormat from './UTCTimeFormat.js'; - -describe('the plugin', () => { - const UTC_KEY = 'utc'; - const JUNK = 'junk'; - const MOON_LANDING_TIMESTAMP = -14256000000; - const MOON_LANDING_DEFAULT_FORMAT = '1969-07-20 00:00:00.000Z'; - const MOON_LANDING_FORMATTED_DATES = [ - '1969-07-20 00:00:00.000', - '1969-07-20 00:00:00.000+00:00', - '1969-07-20 00:00:00', - '1969-07-20 00:00', - '1969-07-20' - ]; - - let utcFormatter; - - beforeEach(() => { - utcFormatter = new UTCTimeFormat(); - }); - - describe('creates a new UTC based formatter', function () { - it("with the key 'utc'", () => { - expect(utcFormatter.key).toBe(UTC_KEY); - }); - - it('that will format a timestamp in UTC Standard Date', () => { - //default format - expect(utcFormatter.format(MOON_LANDING_TIMESTAMP)).toBe( - MOON_LANDING_DEFAULT_FORMAT - ); - - //possible formats - const formattedDates = utcFormatter.DATE_FORMATS.map((format) => - utcFormatter.format(MOON_LANDING_TIMESTAMP, format) - ); - - expect(formattedDates).toEqual(MOON_LANDING_FORMATTED_DATES); - }); - - it('that will parse an UTC Standard Date into milliseconds', () => { - //default format - expect(utcFormatter.parse(MOON_LANDING_DEFAULT_FORMAT)).toBe( - MOON_LANDING_TIMESTAMP - ); - - //possible formats - const parsedDates = MOON_LANDING_FORMATTED_DATES.map((format) => - utcFormatter.parse(format) - ); - - parsedDates.forEach((v) => expect(v).toEqual(MOON_LANDING_TIMESTAMP)); - }); - - it('that will validate correctly', () => { - //default format - expect(utcFormatter.validate(MOON_LANDING_DEFAULT_FORMAT)).toBe( - true - ); - - //possible formats - const validatedFormats = MOON_LANDING_FORMATTED_DATES.map((date) => - utcFormatter.validate(date) - ); - - validatedFormats.forEach((v) => expect(v).toBe(true)); - - //junk - expect(utcFormatter.validate(JUNK)).toBe(false); - }); - }); -}); diff --git a/src/plugins/displayLayout/CustomStringFormatterSpec.js b/src/plugins/displayLayout/CustomStringFormatterSpec.js index bd19078fb9..e4be84bbcd 100644 --- a/src/plugins/displayLayout/CustomStringFormatterSpec.js +++ b/src/plugins/displayLayout/CustomStringFormatterSpec.js @@ -44,13 +44,12 @@ describe('CustomStringFormatter', function () { element = document.createElement('div'); child = document.createElement('div'); element.appendChild(child); - CUSTOM_FORMATS.forEach(openmct.telemetry.addFormat.bind({openmct})); + CUSTOM_FORMATS.forEach((formatter) => { + openmct.telemetry.addFormat(formatter); + }); openmct.on('start', done); openmct.startHeadless(); - spyOn(openmct.telemetry, 'getFormatter'); - openmct.telemetry.getFormatter.and.callFake((key) => CUSTOM_FORMATS.find(d => d.key === key)); - customStringFormatter = new CustomStringFormatter(openmct, valueMetadata); }); diff --git a/src/plugins/localTimeSystem/LocalTimeFormat.js b/src/plugins/localTimeSystem/LocalTimeFormat.js index 719dc7c42c..b51b538e3e 100644 --- a/src/plugins/localTimeSystem/LocalTimeFormat.js +++ b/src/plugins/localTimeSystem/LocalTimeFormat.js @@ -49,6 +49,7 @@ define([ * @memberof platform/commonUI/formats */ function LocalTimeFormat() { + this.key = 'local-format'; } /** diff --git a/src/plugins/localTimeSystem/plugin.js b/src/plugins/localTimeSystem/plugin.js index bac85a0ac6..228107a213 100644 --- a/src/plugins/localTimeSystem/plugin.js +++ b/src/plugins/localTimeSystem/plugin.js @@ -30,11 +30,7 @@ define([ return function () { return function (openmct) { openmct.time.addTimeSystem(new LocalTimeSystem()); - - openmct.legacyExtension('formats', { - key: 'local-format', - implementation: LocalTimeFormat - }); + openmct.telemetry.addFormat(new LocalTimeFormat()); }; }; }); diff --git a/src/plugins/plot/configuration/XAxisModel.js b/src/plugins/plot/configuration/XAxisModel.js index 1875c26864..58f2413c26 100644 --- a/src/plugins/plot/configuration/XAxisModel.js +++ b/src/plugins/plot/configuration/XAxisModel.js @@ -71,8 +71,7 @@ export default class XAxisModel extends Model { defaults(options) { const bounds = options.openmct.time.bounds(); const timeSystem = options.openmct.time.timeSystem(); - const format = options.openmct.$injector.get('formatService') - .getFormat(timeSystem.timeFormat); + const format = options.openmct.telemetry.getFormatter(timeSystem.timeFormat); return { name: timeSystem.name, diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 2ebd22468e..28c798babd 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -73,7 +73,6 @@ define([ './hyperlink/plugin', './clock/plugin', './DeviceClassifier/plugin', - './UTCTimeFormat/plugin', './timer/plugin' ], function ( _, @@ -128,7 +127,6 @@ define([ Hyperlink, Clock, DeviceClassifier, - UTCTimeFormat, Timer ) { const bundleMap = { @@ -144,7 +142,7 @@ define([ }; }); - plugins.UTCTimeSystem = UTCTimeSystem; + plugins.UTCTimeSystem = UTCTimeSystem.default; plugins.LocalTimeSystem = LocalTimeSystem; plugins.RemoteClock = RemoteClock.default; @@ -237,7 +235,6 @@ define([ plugins.Clock = Clock.default; plugins.Timer = Timer.default; plugins.DeviceClassifier = DeviceClassifier.default; - plugins.UTCTimeFormat = UTCTimeFormat.default; return plugins; }); diff --git a/src/plugins/timeConductor/ConductorHistory.vue b/src/plugins/timeConductor/ConductorHistory.vue index 79e23d57c3..7b010b0b59 100644 --- a/src/plugins/timeConductor/ConductorHistory.vue +++ b/src/plugins/timeConductor/ConductorHistory.vue @@ -41,6 +41,7 @@ const LOCAL_STORAGE_HISTORY_KEY_REALTIME = 'tcHistoryRealtime'; const DEFAULT_RECORDS = 10; import { millisecondsToDHMS } from "utils/duration"; +import UTCTimeFormat from "../utcTimeSystem/UTCTimeFormat.js"; export default { inject: ['openmct', 'configuration'], @@ -263,7 +264,15 @@ export default { format: format }).formatter; - return (isNegativeOffset ? '-' : '') + formatter.format(time, 'YYYY-MM-DD HH:mm:ss'); + let formattedDate; + + if (formatter instanceof UTCTimeFormat) { + formattedDate = formatter.format(time, formatter.DATE_FORMATS.PRECISION_SECONDS); + } else { + formattedDate = formatter.format(time); + } + + return (isNegativeOffset ? '-' : '') + formattedDate; }, showHistoryMenu() { const elementBoundingClientRect = this.$refs.historyButton.getBoundingClientRect(); diff --git a/src/plugins/utcTimeSystem/DurationFormat.js b/src/plugins/utcTimeSystem/DurationFormat.js new file mode 100644 index 0000000000..455828294b --- /dev/null +++ b/src/plugins/utcTimeSystem/DurationFormat.js @@ -0,0 +1,37 @@ + +import moment from 'moment'; + +const DATE_FORMAT = "HH:mm:ss"; +const DATE_FORMATS = [ + DATE_FORMAT +]; + +/** + * Formatter for duration. Uses moment to produce a date from a given + * value, but output is formatted to display only time. Can be used for + * specifying a time duration. For specifying duration, it's best to + * specify a date of January 1, 1970, as the ms offset will equal the + * duration represented by the time. + * + * @implements {Format} + * @constructor + * @memberof platform/commonUI/formats + */ +class DurationFormat { + constructor() { + this.key = "duration"; + } + format(value) { + return moment.utc(value).format(DATE_FORMAT); + } + + parse(text) { + return moment.duration(text).asMilliseconds(); + } + + validate(text) { + return moment.utc(text, DATE_FORMATS, true).isValid(); + } +} + +export default DurationFormat; diff --git a/src/plugins/UTCTimeFormat/UTCTimeFormat.js b/src/plugins/utcTimeSystem/UTCTimeFormat.js similarity index 71% rename from src/plugins/UTCTimeFormat/UTCTimeFormat.js rename to src/plugins/utcTimeSystem/UTCTimeFormat.js index 49728f3d80..bcf7ee805a 100644 --- a/src/plugins/UTCTimeFormat/UTCTimeFormat.js +++ b/src/plugins/utcTimeSystem/UTCTimeFormat.js @@ -34,39 +34,39 @@ export default class UTCTimeFormat { constructor() { this.key = 'utc'; this.DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS'; - this.DATE_FORMATS = [ - this.DATE_FORMAT, - this.DATE_FORMAT + 'Z', - 'YYYY-MM-DD HH:mm:ss', - 'YYYY-MM-DD HH:mm', - 'YYYY-MM-DD' - ]; + this.DATE_FORMATS = { + PRECISION_DEFAULT: this.DATE_FORMAT, + PRECISION_DEFAULT_WITH_ZULU: this.DATE_FORMAT + 'Z', + PRECISION_SECONDS: 'YYYY-MM-DD HH:mm:ss', + PRECISION_MINUTES: 'YYYY-MM-DD HH:mm', + PRECISION_DAYS: 'YYYY-MM-DD' + }; } /** * @param {string} formatString * @returns the value of formatString if the value is a string type and exists in the DATE_FORMATS array; otherwise the DATE_FORMAT value. */ - validateFormatString(formatString) { - return typeof formatString === 'string' - && this.DATE_FORMATS.includes(formatString) - ? formatString - : this.DATE_FORMAT; + isValidFormatString(formatString) { + return Object.values(this.DATE_FORMATS).includes(formatString); } /** * @param {number} value The value to format. - * @param {string} formatString The string format to format. Default "YYYY-MM-DD HH:mm:ss.SSS" + "Z" - * @returns {string} the formatted date(s) according to the proper parameter of formatString or the default value of "YYYY-MM-DD HH:mm:ss.SSS" + "Z". - * If multiple values were requested, then an array of + * @returns {string} the formatted date(s). If multiple values were requested, then an array of * formatted values will be returned. Where a value could not be formatted, `undefined` will be returned at its position * in the array. */ format(value, formatString) { if (value !== undefined) { - const format = this.validateFormatString(formatString); const utc = moment.utc(value); + if (formatString !== undefined && !this.isValidFormatString(formatString)) { + throw "Invalid format requested from UTC Time Formatter "; + } + + let format = formatString || this.DATE_FORMATS.PRECISION_DEFAULT; + return utc.format(format) + (formatString ? '' : 'Z'); } else { return value; @@ -78,10 +78,11 @@ export default class UTCTimeFormat { return text; } - return moment.utc(text, this.DATE_FORMATS).valueOf(); + return moment.utc(text, Object.values(this.DATE_FORMATS)).valueOf(); } validate(text) { - return moment.utc(text, this.DATE_FORMATS, true).isValid(); + return moment.utc(text, Object.values(this.DATE_FORMATS), true).isValid(); } + } diff --git a/src/plugins/utcTimeSystem/plugin.js b/src/plugins/utcTimeSystem/plugin.js index 2f2a2ae506..94b4e50f99 100644 --- a/src/plugins/utcTimeSystem/plugin.js +++ b/src/plugins/utcTimeSystem/plugin.js @@ -20,22 +20,21 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([ - "./UTCTimeSystem", - "./LocalClock" -], function ( - UTCTimeSystem, - LocalClock -) { - /** - * Install a time system that supports UTC times. It also installs a local - * clock source that ticks every 100ms, providing UTC times. - */ - return function () { - return function (openmct) { - const timeSystem = new UTCTimeSystem(); - openmct.time.addTimeSystem(timeSystem); - openmct.time.addClock(new LocalClock.default(100)); - }; +import UTCTimeSystem from './UTCTimeSystem'; +import LocalClock from './LocalClock'; +import UTCTimeFormat from './UTCTimeFormat'; +import DurationFormat from './DurationFormat'; + +/** + * Install a time system that supports UTC times. It also installs a local + * clock source that ticks every 100ms, providing UTC times. + */ +export default function () { + return function (openmct) { + const timeSystem = new UTCTimeSystem(); + openmct.time.addTimeSystem(timeSystem); + openmct.time.addClock(new LocalClock(100)); + openmct.telemetry.addFormat(new UTCTimeFormat()); + openmct.telemetry.addFormat(new DurationFormat()); }; -}); +} diff --git a/src/plugins/utcTimeSystem/pluginSpec.js b/src/plugins/utcTimeSystem/pluginSpec.js index 0817c8a91e..0a1f0f0233 100644 --- a/src/plugins/utcTimeSystem/pluginSpec.js +++ b/src/plugins/utcTimeSystem/pluginSpec.js @@ -26,9 +26,11 @@ import { createOpenMct, resetApplicationState } from 'utils/testing'; +import UTCTimeFormat from './UTCTimeFormat.js'; describe("The UTC Time System", () => { const UTC_SYSTEM_AND_FORMAT_KEY = 'utc'; + const DURATION_FORMAT_KEY = 'duration'; let openmct; let utcTimeSystem; let mockTimeout; @@ -100,4 +102,93 @@ describe("The UTC Time System", () => { expect(mockListener).toHaveBeenCalledWith(jasmine.any(Number)); }); }); + + describe("UTC Time Format", () => { + let utcTimeFormatter; + + beforeEach(() => { + utcTimeFormatter = openmct.telemetry.getFormatter(UTC_SYSTEM_AND_FORMAT_KEY); + }); + + it("is installed by the plugin", () => { + expect(utcTimeFormatter).toBeDefined(); + }); + + it("formats from ms since Unix epoch into Open MCT UTC time format", () => { + const TIME_IN_MS = 1638574560945; + const TIME_AS_STRING = "2021-12-03 23:36:00.945Z"; + + const formattedTime = utcTimeFormatter.format(TIME_IN_MS); + expect(formattedTime).toEqual(TIME_AS_STRING); + + }); + + it("formats from ms since Unix epoch into terse UTC formats", () => { + const utcTimeFormatterInstance = new UTCTimeFormat(); + + const TIME_IN_MS = 1638574560945; + const EXPECTED_FORMATS = { + PRECISION_DEFAULT: "2021-12-03 23:36:00.945", + PRECISION_SECONDS: "2021-12-03 23:36:00", + PRECISION_MINUTES: "2021-12-03 23:36", + PRECISION_DAYS: "2021-12-03" + }; + + Object.keys(EXPECTED_FORMATS).forEach((formatKey) => { + const formattedTime = utcTimeFormatterInstance.format(TIME_IN_MS, utcTimeFormatterInstance.DATE_FORMATS[formatKey]); + expect(formattedTime).toEqual(EXPECTED_FORMATS[formatKey]); + }); + }); + + it("parses from Open MCT UTC time format to ms since Unix epoch.", () => { + const TIME_IN_MS = 1638574560945; + const TIME_AS_STRING = "2021-12-03 23:36:00.945Z"; + + const parsedTime = utcTimeFormatter.parse(TIME_AS_STRING); + expect(parsedTime).toEqual(TIME_IN_MS); + }); + + it("validates correctly formatted Open MCT UTC times.", () => { + const TIME_AS_STRING = "2021-12-03 23:36:00.945Z"; + + const isValid = utcTimeFormatter.validate(TIME_AS_STRING); + expect(isValid).toBeTrue(); + }); + }); + + describe("Duration Format", () => { + let durationTimeFormatter; + + beforeEach(() => { + durationTimeFormatter = openmct.telemetry.getFormatter(DURATION_FORMAT_KEY); + }); + + it("is installed by the plugin", () => { + expect(durationTimeFormatter).toBeDefined(); + }); + + it("formats from ms into Open MCT duration format", () => { + const TIME_IN_MS = 2000; + const TIME_AS_STRING = "00:00:02"; + + const formattedTime = durationTimeFormatter.format(TIME_IN_MS); + expect(formattedTime).toEqual(TIME_AS_STRING); + + }); + + it("parses from Open MCT duration format to ms", () => { + const TIME_IN_MS = 2000; + const TIME_AS_STRING = "00:00:02"; + + const parsedTime = durationTimeFormatter.parse(TIME_AS_STRING); + expect(parsedTime).toEqual(TIME_IN_MS); + }); + + it("validates correctly formatted Open MCT duration strings.", () => { + const TIME_AS_STRING = "00:00:02"; + + const isValid = durationTimeFormatter.validate(TIME_AS_STRING); + expect(isValid).toBeTrue(); + }); + }); });