From 20da5eb73dd01fcee8c511104c04b2ef02af6d29 Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Mon, 9 Dec 2024 11:41:04 -0700 Subject: [PATCH 1/7] Working tool_vue template --- openc3/templates/tool_vue/.eslintrc.js | 43 ------------ .../{.prettierrc.js => .prettierrc.cjs} | 0 openc3/templates/tool_vue/babel.config.json | 11 --- openc3/templates/tool_vue/eslint.config.mjs | 68 +++++++++++++++++++ openc3/templates/tool_vue/jsconfig.json | 2 +- openc3/templates/tool_vue/package.json | 49 +++++-------- openc3/templates/tool_vue/src/App.vue | 8 +-- openc3/templates/tool_vue/src/main.js | 35 ++++------ openc3/templates/tool_vue/src/router.js | 37 +++++----- .../src/tools/tool_name/tool_name.vue | 4 +- openc3/templates/tool_vue/vite.config.js | 52 ++++++++++++++ openc3/templates/tool_vue/vue.config.js | 38 ----------- 12 files changed, 173 insertions(+), 174 deletions(-) delete mode 100644 openc3/templates/tool_vue/.eslintrc.js rename openc3/templates/tool_vue/{.prettierrc.js => .prettierrc.cjs} (100%) delete mode 100644 openc3/templates/tool_vue/babel.config.json create mode 100644 openc3/templates/tool_vue/eslint.config.mjs create mode 100644 openc3/templates/tool_vue/vite.config.js delete mode 100644 openc3/templates/tool_vue/vue.config.js diff --git a/openc3/templates/tool_vue/.eslintrc.js b/openc3/templates/tool_vue/.eslintrc.js deleted file mode 100644 index 5679a1e58f..0000000000 --- a/openc3/templates/tool_vue/.eslintrc.js +++ /dev/null @@ -1,43 +0,0 @@ -module.exports = { - root: true, - env: { - node: true, - }, - extends: [ - 'plugin:vue/essential', - 'plugin:prettier/recommended', - '@vue/prettier', - ], - plugins: ['prettier'], - rules: { - 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', - 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', - 'prettier/prettier': [ - 'warn', - { - endOfLine: 'auto', - }, - ], - 'vue/multi-word-component-names': 'off', - 'vue/valid-v-slot': [ - 'error', - { - allowModifiers: true, - }, - ], - }, - parserOptions: { - parser: '@babel/eslint-parser', - }, - overrides: [ - { - files: [ - '**/__tests__/*.{j,t}s?(x)', - '**/tests/unit/**/*.spec.{j,t}s?(x)', - ], - env: { - jest: true, - }, - }, - ], -} diff --git a/openc3/templates/tool_vue/.prettierrc.js b/openc3/templates/tool_vue/.prettierrc.cjs similarity index 100% rename from openc3/templates/tool_vue/.prettierrc.js rename to openc3/templates/tool_vue/.prettierrc.cjs diff --git a/openc3/templates/tool_vue/babel.config.json b/openc3/templates/tool_vue/babel.config.json deleted file mode 100644 index 91fa88dbe0..0000000000 --- a/openc3/templates/tool_vue/babel.config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "presets": ["@vue/cli-plugin-babel/preset"], - "plugins": [ - [ - "babel-plugin-istanbul", - { - "extension": [".js", ".vue"] - } - ] - ] -} diff --git a/openc3/templates/tool_vue/eslint.config.mjs b/openc3/templates/tool_vue/eslint.config.mjs new file mode 100644 index 0000000000..8a83fc378d --- /dev/null +++ b/openc3/templates/tool_vue/eslint.config.mjs @@ -0,0 +1,68 @@ +import prettier from 'eslint-plugin-prettier' +import globals from 'globals' +import parser from 'vue-eslint-parser' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import js from '@eslint/js' +import { FlatCompat } from '@eslint/eslintrc' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, +}) + +export default [ + ...compat.extends( + 'plugin:vue/vue3-essential', + 'plugin:prettier/recommended', + '@vue/prettier', + ), + { + plugins: { + prettier, + }, + + languageOptions: { + globals: { + ...globals.node, + }, + + parser: parser, + ecmaVersion: 2022, + sourceType: 'module', + }, + + rules: { + 'no-console': 'error', + 'no-debugger': 'error', + + 'prettier/prettier': [ + 'warn', + { + endOfLine: 'auto', + }, + ], + + 'vue/multi-word-component-names': 'off', + + 'vue/valid-v-slot': [ + 'error', + { + allowModifiers: true, + }, + ], + }, + }, + { + files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'], + + languageOptions: { + globals: { + ...globals.jest, + }, + }, + }, +] diff --git a/openc3/templates/tool_vue/jsconfig.json b/openc3/templates/tool_vue/jsconfig.json index 93641833e4..787d71d3ad 100644 --- a/openc3/templates/tool_vue/jsconfig.json +++ b/openc3/templates/tool_vue/jsconfig.json @@ -1,6 +1,6 @@ { "exclude": ["node_modules"], "vueCompilerOptions": { - "target": 2.7 + "target": "auto" } } diff --git a/openc3/templates/tool_vue/package.json b/openc3/templates/tool_vue/package.json index 1f0c3a22a8..09c1ecf02f 100644 --- a/openc3/templates/tool_vue/package.json +++ b/openc3/templates/tool_vue/package.json @@ -2,52 +2,35 @@ "name": "<%= tool_name %>", "version": "5.21.0-beta0", "private": true, + "type": "module", "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build --no-module", - "test:unit": "vue-cli-service test:unit", - "lint": "vue-cli-service lint", - "serve:standalone": "vue-cli-service serve --mode standalone", - "test:components": "vue-cli-service test:components" + "serve": "vite build --watch --mode dev-server", + "build": "vite build", + "lint": "eslint src", + "serve:standalone": "vite preview --mode standalone" }, "dependencies": { - "@openc3/tool-common": "5.21.0-beta0", "@astrouxds/astro-web-components": "7.24.0", + "@openc3/js-common": "5.21.0-beta0", + "@openc3/vue-common": "5.21.0-beta0", "axios": "1.7.7", "date-fns": "4.1.0", - "portal-vue": "2.1.7", - "single-spa-vue": "2.5.1", - "sprintf-js": "1.1.3", - "systemjs-webpack-interop": "2.3.7", - "vue": "3.5.4", - "vue-router": "4.4.4", - "vuetify": "3.7.1", - "vuex": "4.1.0" + "lodash": "4.17.21", + "single-spa-vue": "3.0.0", + "sprintf-js": "1.1.3" }, "devDependencies": { - "@babel/eslint-parser": "7.25.9", - "@vue/babel-preset-app": "5.0.8", - "@vue/cli": "5.0.8", - "@vue/cli-plugin-babel": "5.0.8", - "@vue/cli-plugin-eslint": "5.0.8", - "@vue/cli-plugin-router": "5.0.8", - "@vue/cli-plugin-vuex": "5.0.8", - "@vue/cli-service": "5.0.8", + "@vitejs/plugin-vue": "5.1.5", "@vue/eslint-config-prettier": "9.0.0", - "@vue/test-utils": "1.3.0", - "babel-loader": "9.2.1", - "babel-plugin-istanbul": "7.0.0", - "eslint": "8.56.0", + "@vue/test-utils": "2.4.6", + "eslint": "9.16.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-prettier": "5.2.1", "eslint-plugin-vue": "9.30.0", - "html-webpack-plugin": "^5.6.0", "prettier": "3.3.3", "sass": "1.80.4", - "sass-loader": "16.0.2", - "vue-cli-plugin-single-spa": "3.3.0", - "vue-cli-plugin-vuetify": "2.5.8", - "vue-template-compiler": "2.7.16", - "webpack": "5.95.0" + "vite": "5.4.11", + "vue": "3.5.13", + "vue-eslint-parser": "9.4.3" } } diff --git a/openc3/templates/tool_vue/src/App.vue b/openc3/templates/tool_vue/src/App.vue index eedd3cc5ea..ab15e79e00 100644 --- a/openc3/templates/tool_vue/src/App.vue +++ b/openc3/templates/tool_vue/src/App.vue @@ -7,9 +7,7 @@ --> diff --git a/openc3/templates/tool_vue/src/main.js b/openc3/templates/tool_vue/src/main.js index ebce870795..085b923f59 100644 --- a/openc3/templates/tool_vue/src/main.js +++ b/openc3/templates/tool_vue/src/main.js @@ -1,36 +1,25 @@ -import 'systemjs-webpack-interop/auto-public-path/2' -import Vue from 'vue' +import { createApp, h } from 'vue' import singleSpaVue from 'single-spa-vue' import App from './App.vue' import router from './router' -import store from '@openc3/tool-common/src/plugins/store' - -Vue.config.productionTip = false - -import '@openc3/tool-common/src/assets/stylesheets/layout/layout.scss' -import vuetify from '@openc3/tool-common/src/plugins/vuetify' -import Dialog from '@openc3/tool-common/src/plugins/dialog' -import PortalVue from 'portal-vue' -import Notify from '@openc3/tool-common/src/plugins/notify' - -Vue.use(PortalVue) -Vue.use(Dialog) -Vue.use(Notify, { store }) +import { Dialog, Notify, store, vuetify } from '@openc3/vue-common/plugins' const vueLifecycles = singleSpaVue({ - Vue, + createApp, appOptions: { - router, - store, - vuetify, - render(h) { - return h(App, { - props: {}, - }) + render() { + return h(App, {}) }, el: '#openc3-tool', }, + handleInstance: (app) => { + app.use(router) + app.use(store) + app.use(vuetify) + app.use(Dialog) + app.use(Notify, { store }) + }, }) export const bootstrap = vueLifecycles.bootstrap diff --git a/openc3/templates/tool_vue/src/router.js b/openc3/templates/tool_vue/src/router.js index be28e04ba7..774f3ca887 100644 --- a/openc3/templates/tool_vue/src/router.js +++ b/openc3/templates/tool_vue/src/router.js @@ -6,24 +6,25 @@ # if purchased from OpenC3, Inc. */ -import Vue from 'vue' -import Router from 'vue-router' +import { createRouter, createWebHistory } from 'vue-router' +import { prependBasePath } from '@openc3/js-common/utils' +import { NotFound } from '@openc3/vue-common/components' -Vue.use(Router) +const routes = [ + { + path: '/', + name: 'jruby', + component: () => import('./tools/<%= tool_name %>/<%= tool_name %>.vue'), + }, + { + path: '/:pathMatch(.*)*', + name: 'NotFound', + component: NotFound, + }, +] +routes.forEach(prependBasePath) -export default new Router({ - mode: 'history', - base: process.env.BASE_URL, - routes: [ - { - path: '/', - name: '<%= tool_name %>', - component: () => import('./tools/<%= tool_name %>/<%= tool_name %>.vue'), - }, - { - path: '*', - name: 'NotFound', - component: () => import('@openc3/tool-common/src/components/NotFound'), - }, - ], +export default createRouter({ + history: createWebHistory(), + routes, }) diff --git a/openc3/templates/tool_vue/src/tools/tool_name/tool_name.vue b/openc3/templates/tool_vue/src/tools/tool_name/tool_name.vue index 0a892fb619..71c6b5020b 100644 --- a/openc3/templates/tool_vue/src/tools/tool_name/tool_name.vue +++ b/openc3/templates/tool_vue/src/tools/tool_name/tool_name.vue @@ -19,8 +19,8 @@ \ No newline at end of file + diff --git a/openc3/templates/tool_svelte/src/services/api.js b/openc3/templates/tool_svelte/src/services/api.js deleted file mode 100644 index b9a762b72c..0000000000 --- a/openc3/templates/tool_svelte/src/services/api.js +++ /dev/null @@ -1,92 +0,0 @@ -/* -# Copyright 2022 Ball Aerospace & Technologies Corp. -# All Rights Reserved. -# -# This program is free software; you can modify and/or redistribute it -# under the terms of the GNU Affero General Public License -# as published by the Free Software Foundation; version 3 with -# attribution addendums as found in the LICENSE.txt -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. -# All Rights Reserved -# -# This file may also be used under the terms of a commercial license -# if purchased from OpenC3, Inc. -*/ - -import axios from './axios.js' - -const request = async function ( - method, - url, - { data, params = {}, headers, noAuth = false, noScope = false } = {} -) { - if (!noAuth) { - try { - let refreshed = await OpenC3Auth.updateToken( - OpenC3Auth.defaultMinValidity - ) - if (refreshed) { - OpenC3Auth.setTokens() - } - } catch (error) { - OpenC3Auth.login() - } - headers['Authorization'] = localStorage.openc3Token - } - if (!noScope && !params['scope']) { - params['scope'] = window.openc3Scope - } - return axios({ - method, - url, - data, - params, - headers, - }) -} - -const acceptOnlyDefaultHeaders = { - Accept: 'application/json', -} - -const fullDefaultHeaders = { - ...acceptOnlyDefaultHeaders, - 'Content-Type': 'application/json', -} - -export default { - get: function ( - path, - { params, headers = acceptOnlyDefaultHeaders, noScope, noAuth } = {} - ) { - return request('get', path, { params, headers, noScope, noAuth }) - }, - - put: function ( - path, - { data, params, headers = fullDefaultHeaders, noScope, noAuth } = {} - ) { - return request('put', path, { data, params, headers, noScope, noAuth }) - }, - - post: function ( - path, - { data, params, headers = fullDefaultHeaders, noScope, noAuth } = {} - ) { - return request('post', path, { data, params, headers, noScope, noAuth }) - }, - - delete: function ( - path, - { params, headers = acceptOnlyDefaultHeaders, noScope, noAuth } = {} - ) { - return request('delete', path, { params, headers, noScope, noAuth }) - }, -} diff --git a/openc3/templates/tool_svelte/src/services/axios.js b/openc3/templates/tool_svelte/src/services/axios.js deleted file mode 100644 index 996896cd4e..0000000000 --- a/openc3/templates/tool_svelte/src/services/axios.js +++ /dev/null @@ -1,85 +0,0 @@ -/* -# Copyright 2022 Ball Aerospace & Technologies Corp. -# All Rights Reserved. -# -# This program is free software; you can modify and/or redistribute it -# under the terms of the GNU Affero General Public License -# as published by the Free Software Foundation; version 3 with -# attribution addendums as found in the LICENSE.txt -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. -# All Rights Reserved -# -# This file may also be used under the terms of a commercial license -# if purchased from OpenC3, Inc. -*/ - -import axios from 'axios' - -const axiosInstance = axios.create({ - baseURL: location.origin, - timeout: 60000, - params: {}, -}) - -axiosInstance.interceptors.response.use( - (response) => response, - (error) => { - if (error.response) { - if (error.response.status === 401) { - OpenC3Auth.updateToken(OpenC3Auth.defaultMinValidity, true).then( - function (refreshed) { - if (refreshed) { - OpenC3Auth.setTokens() - } - } - ) - } - // Individual tools can set 'Ignore-Errors' to an error code - // they potentially expect, e.g. '500', in which case we ignore it - // For example in CommandSender.vue: - // obs = this.api.cmd(targetName, commandName, paramList, { - // 'Ignore-Errors': '500', - // }) - if ( - error.response.headers['ignore-errors'] && - error.response.headers['ignore-errors'].includes( - error.response.status.toString() - ) - ) { - return Promise.reject(error) - } - let body = `HTTP ${error.response.status} - ` - if (error.response?.statusText) { - body += `${error.response.statusText} ` - } - if (error.response?.config?.data) { - body += `${error.response.config.data} ` - } - if (error.response?.data?.message) { - body += `${error.response.data.message}` - } else if (error.response?.data?.exception) { - body += `${error.response.data.exception}` - } else if (error.response?.data?.error?.message) { - if (error.response.data.error.class) { - body += `${error.response.data.error.class} ` - } - body += `${error.response.data.error.message}` - } else { - body += `${error.response?.data}` - } - alert(`Network error: ${body}`) - throw error - } else { - throw error - } - } -) - -export default axiosInstance diff --git a/openc3/templates/tool_svelte/src/services/cable.js b/openc3/templates/tool_svelte/src/services/cable.js deleted file mode 100644 index 5a83c6aaa0..0000000000 --- a/openc3/templates/tool_svelte/src/services/cable.js +++ /dev/null @@ -1,65 +0,0 @@ -/* -# Copyright 2022 Ball Aerospace & Technologies Corp. -# All Rights Reserved. -# -# This program is free software; you can modify and/or redistribute it -# under the terms of the GNU Affero General Public License -# as published by the Free Software Foundation; version 3 with -# attribution addendums as found in the LICENSE.txt -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# Modified by OpenC3, Inc. -# All changes Copyright 2022, OpenC3, Inc. -# All Rights Reserved -# -# This file may also be used under the terms of a commercial license -# if purchased from OpenC3, Inc. -*/ - -import * as ActionCable from '@rails/actioncable' -//ActionCable.logger.enabled = true -ActionCable.ConnectionMonitor.staleThreshold = 10 - -export default class Cable { - constructor(url = '/openc3-api/cable') { - this._cable = null - this._url = url - } - disconnect() { - if (this._cable) { - this._cable.disconnect() - } - } - createSubscription(channel, scope, callbacks = {}, additionalOptions = {}) { - return OpenC3Auth.updateToken(OpenC3Auth.defaultMinValidity).then( - (refreshed) => { - if (refreshed) { - OpenC3Auth.setTokens() - } - if (this._cable == null) { - let final_url = - this._url + - '?scope=' + - window.openc3Scope + - '&authorization=' + - localStorage.openc3Token - this._cable = ActionCable.createConsumer(encodeURI(final_url)) - } - return this._cable.subscriptions.create( - { - channel, - ...additionalOptions, - }, - callbacks - ) - } - ) - } - recordPing() { - this._cable.connection.monitor.recordPing() - } -} diff --git a/openc3/templates/tool_svelte/src/services/config-parser.js b/openc3/templates/tool_svelte/src/services/config-parser.js deleted file mode 100644 index c640ec8883..0000000000 --- a/openc3/templates/tool_svelte/src/services/config-parser.js +++ /dev/null @@ -1,198 +0,0 @@ -/* -# Copyright 2022 Ball Aerospace & Technologies Corp. -# All Rights Reserved. -# -# This program is free software; you can modify and/or redistribute it -# under the terms of the GNU Affero General Public License -# as published by the Free Software Foundation; version 3 with -# attribution addendums as found in the LICENSE.txt -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# Modified by OpenC3, Inc. -# All changes Copyright 2024, OpenC3, Inc. -# All Rights Reserved -# -# This file may also be used under the terms of a commercial license -# if purchased from OpenC3, Inc. -*/ - -export class ConfigParserError { - constructor(config_parser, message, usage = '', url = '') { - this.keyword = config_parser.keyword - this.parameters = config_parser.parameters - this.filename = config_parser.filename - this.line = config_parser.line - this.lineNumber = config_parser.lineNumber - this.message = message - this.usage = usage - this.url = url - } -} - -export class ConfigParserService { - keyword = null - parameters = [] - filename = '' - line = '' - lineNumber = 0 - url = 'https://docs.openc3.com/docs' - constructor() {} - - verify_num_parameters(min_num_params, max_num_params, usage = '') { - // This syntax works with 0 because each doesn't return any values - // for a backwards range - for (let index = 1; index <= min_num_params; index++) { - // If the parameter is nil (0 based) then we have a problem - if (this.parameters[index - 1] === undefined) { - throw new ConfigParserError( - this, - `Not enough parameters for ${this.keyword}.`, - usage, - this.url, - ) - } - } - // If they pass null for max_params we don't check for a maximum number - if (max_num_params && this.parameters[max_num_params] !== undefined) { - throw new ConfigParserError( - this, - `Too many parameters for ${this.keyword}.`, - usage, - this.url, - ) - } - } - - remove_quotes(string) { - if (string.length < 2) { - return string - } - let first_char = string.charAt(0) - if (first_char !== '"' && first_char !== "'") { - return string - } - let last_char = string.charAt(string.length - 1) - if (first_char !== last_char) { - return string - } - return string.substring(1, string.length - 1) - } - - scan_string(string, rx) { - if (!rx.global) throw "rx must have 'global' flag set" - let r = [] - string.replace(rx, function (match) { - r.push(match) - return match - }) - return r - } - - parse_string( - input_string, - original_filename, - yield_non_keyword_lines, - remove_quotes, - handler, - ) { - let string_concat = false - this.line = '' - this.keyword = null - this.parameters = [] - this.filename = original_filename - - // Break string into lines - let lines = input_string.split('\n') - let numLines = lines.length - - for (let i = 0; i < numLines; i++) { - this.lineNumber = i + 1 - let line = lines[i].trim() - // Ensure the line length is not 0 - if (line.length === 0) { - continue - } - - if (string_concat === true) { - // Skip comment lines after a string concatenation - if (line[0] === '#') { - continue - } - // Remove the opening quote if we're continuing the line - line = line.substring(1, line.length) - } - - // Check for string continuation - let last_char = line.charAt(line.length - 1) - let newline = false - switch (last_char) { - case '+': // String concatenation with newlines - newline = true - // Deliberate fall through - case '\\': // String concatenation - // Trim off the concat character plus any spaces, e.g. "line" \ - let trim = line.substring(0, line.length - 1).trim() - // Now trim off the last quote so it will flow into the next line - this.line += trim.substring(0, trim.length - 1) - if (newline) { - this.line += '\n' - } - string_concat = true - continue - case '&': // Line continuation - this.line += line.substring(0, line.length - 1) - continue - default: - this.line += line - } - string_concat = false - - let rx = /("([^\\"]|\\.)*")|('([^\\']|\\.)*')|\S+/g - let data = this.scan_string(this.line, rx) - let first_item = '' - if (data.length > 0) { - first_item = first_item + data[0] - } - - if (first_item.length === 0 || first_item.charAt(0) === '#') { - this.keyword = null - } else { - this.keyword = first_item.toUpperCase() - } - this.parameters = [] - - // Ignore lines without keywords: comments and blank lines - if (this.keyword === null) { - if (yield_non_keyword_lines) { - handler(this.keyword, this.parameters, this.line, this.lineNumber) - } - this.line = '' - continue - } - - let length = data.length - if (length > 1) { - for (let index = 1; index < length; index++) { - let string = data[index] - - // Don't process trailing comments such as: - // KEYWORD PARAM #This is a comment - if (string.length > 0 && string.charAt(0) === '#') { - break - } - if (remove_quotes) { - this.parameters.push(this.remove_quotes(string)) - } else { - this.parameters.push(string) - } - } - } - handler(this.keyword, this.parameters, this.line, this.lineNumber) - this.line = '' - } // for all the lines - } // parse_string -} // class ConfigParserService diff --git a/openc3/templates/tool_svelte/src/services/openc3-api.js b/openc3/templates/tool_svelte/src/services/openc3-api.js deleted file mode 100644 index 024a25f592..0000000000 --- a/openc3/templates/tool_svelte/src/services/openc3-api.js +++ /dev/null @@ -1,606 +0,0 @@ -/* -# Copyright 2022 Ball Aerospace & Technologies Corp. -# All Rights Reserved. -# -# This program is free software; you can modify and/or redistribute it -# under the terms of the GNU Affero General Public License -# as published by the Free Software Foundation; version 3 with -# attribution addendums as found in the LICENSE.txt -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# Modified by OpenC3, Inc. -# All changes Copyright 2024, OpenC3, Inc. -# All Rights Reserved -# -# This file may also be used under the terms of a commercial license -# if purchased from OpenC3, Inc. -*/ - -import axios from './axios.js' - -export class OpenC3Api { - id = 1 - - constructor() {} - - async exec(method, params, kwparams = {}, headerOptions = {}) { - try { - let refreshed = await OpenC3Auth.updateToken( - OpenC3Auth.defaultMinValidity, - ) - if (refreshed) { - OpenC3Auth.setTokens() - } - } catch (error) { - OpenC3Auth.login() - } - this.id = this.id + 1 - try { - kwparams['scope'] = window.openc3Scope - const response = await axios.post( - '/openc3-api/api', - { - jsonrpc: '2.0', - method: method, - params: params, - id: this.id, - keyword_params: kwparams, - }, - { - headers: { - Authorization: localStorage.openc3Token, - 'Content-Type': 'application/json-rpc', - ...headerOptions, - }, - }, - ) - // let data = response.data - // if (data.error) { - // let err = new Error() - // err.name = data.error.data.class - // err.message = data.error.data.message - // console.log(data.error.data.backtrace.join('\n')) - // throw err - // } - return response.data.result - } catch (error) { - let err = new Error() - if (error.response) { - // The request was made and the server responded with a - // status code that falls out of the range of 2xx - err.name = error.response.data.error.data.class - err.message = error.response.data.error.data.message - } else if (error.request) { - // The request was made but no response was received, `error.request` - // is an instance of XMLHttpRequest in the browser and an instance - // of http.ClientRequest in Node.js - err.name = 'Request error' - err.message = 'Request error, no response received' - } else { - // Something happened in setting up the request and triggered an Error - err.name = 'Unknown error' - } - // console.log(error) - throw err - } - } - - decode_openc3_type(val) { - if (val !== null && typeof val === 'object') { - if (val.json_class == 'Float' && val.raw) { - if (val.raw == 'NaN') { - return NaN - } else if (val.raw == 'Infinity') { - return Infinity - } else if (val.raw == '-Infinity') { - return -Infinity - } - } - } - return null - } - - encode_openc3_type(val) { - if (Number.isNaN(val)) { - return { json_class: 'Float', raw: 'NaN' } - } else if (val == Number.POSITIVE_INFINITY) { - return { json_class: 'Float', raw: 'Infinity' } - } else if (val == Number.NEGATIVE_INFINITY) { - return { json_class: 'Float', raw: '-Infinity' } - } - return null - } - - ensure_offline_access() { - this.offline_access_needed().then((needed) => { - if (needed) { - if (localStorage.openc3OfflineToken) { - this.set_offline_access(localStorage.openc3OfflineToken).then(() => { - delete localStorage.openc3OfflineToken - }) - } else { - OpenC3Auth.getOfflineAccess() - } - } - }) - } - - // *********************************************** - // The following APIs are used by the CmdTlmServer - // *********************************************** - - offline_access_needed() { - return this.exec('offline_access_needed', []) - } - - set_offline_access(offline_access_token) { - return this.exec('set_offline_access', [offline_access_token]) - } - - get_all_interface_info() { - return this.exec('get_all_interface_info', []) - } - - map_target_to_interface(target_name, interface_name) { - return this.exec('map_target_to_interface', [target_name, interface_name]) - } - - connect_interface(interface_name, ...interface_params) { - if (interface_params.length > 0) { - return this.exec('connect_interface', [ - interface_name, - ...interface_params, - ]) - } else { - return this.exec('connect_interface', [interface_name]) - } - } - - disconnect_interface(interface_name) { - return this.exec('disconnect_interface', [interface_name]) - } - - interface_cmd(interface_name, command_name, ...command_params) { - return this.exec('interface_cmd', [ - interface_name, - command_name, - ...command_params, - ]) - } - - get_all_router_info() { - return this.exec('get_all_router_info', []) - } - - connect_router(router_name) { - return this.exec('connect_router', [router_name]) - } - - disconnect_router(router_name) { - return this.exec('disconnect_router', [router_name]) - } - - get_target_interfaces() { - return this.exec('get_target_interfaces', []) - } - - get_tlm_cnts(target_commands) { - return this.exec('get_tlm_cnts', [target_commands]) - } - - get_item(target, packet, item) { - return this.exec('get_item', [target, packet, item]) - } - - get_param(target, packet, item) { - return this.exec('get_param', [target, packet, item]) - } - - get_limits_sets() { - return this.exec('get_limits_sets', []) - } - - get_limits_set() { - return this.exec('get_limits_set', []) - } - - set_limits_set(limits_set) { - return this.exec('set_limits_set', [limits_set]) - } - - // *********************************************** - // End CmdTlmServer APIs - // *********************************************** - - get_target(target_name) { - return this.exec('get_target', [target_name]) - } - - get_target_names() { - return this.exec('get_target_names', []) - } - - get_tlm(target_name, packet_name) { - return this.exec('get_tlm', [target_name, packet_name]) - } - - get_all_tlm(target_name) { - return this.exec('get_all_tlm', [target_name]) - } - - get_all_tlm_names(target_name, hidden = false) { - return this.exec('get_all_tlm_names', [target_name], { hidden: hidden }) - } - - async get_tlm_packet(target_name, packet_name, value_type, stale_time = 30) { - const data = await this.exec('get_tlm_packet', [target_name, packet_name], { - type: value_type, - stale_time: stale_time, - }) - // Make sure data isn't null or undefined. Note this is the only valid use of == or != - if (data != null) { - let len = data.length - let converted = null - for (let i = 0; i < len; i++) { - converted = this.decode_openc3_type(data[i][1]) - if (converted !== null) { - data[i][1] = converted - } - } - } - return data - } - - get_packet_derived_items(target_name, packet_name) { - return this.exec('get_packet_derived_items', [target_name, packet_name]) - } - - get_tlm_buffer(target_name, packet_name) { - return this.exec('get_tlm_buffer', [target_name, packet_name]) - } - - async get_tlm_values(items, stale_time = 30) { - const data = await this.exec('get_tlm_values', [items], { - stale_time: stale_time, - }) - let len = data[0].length - let converted = null - for (let i = 0; i < len; i++) { - converted = this.decode_openc3_type(data[0][i]) - if (converted !== null) { - data[0][i] = converted - } - } - return data - } - - get_limits(target_name, packet_name, item_name) { - return this.exec('get_limits', [target_name, packet_name, item_name]) - } - - async tlm(target_name, packet_name, item_name, value_type = 'CONVERTED') { - let data = null - // Check for the single string syntax: tlm("TGT PKT ITEM") - if (packet_name === undefined) { - data = await this.exec('tlm', [target_name]) - // Check for the single string syntax with type: tlm("TGT PKT ITEM", "RAW") - } else if (item_name === undefined) { - if ( - ['RAW', 'CONVERTED', 'FORMATTED', 'WITH_UNITS'].includes(packet_name) - ) { - data = await this.exec('tlm', [target_name], { type: packet_name }) - } else { - let err = new Error() - err.name = 'TypeError' - err.message = `Invalid data type ${packet_name}. Valid options are RAW, CONVERTED, FORMATTED, and WITH_UNITS.` - throw err - } - } else { - data = await this.exec('tlm', [target_name, packet_name, item_name], { - type: value_type, - }) - } - let converted = this.decode_openc3_type(data) - if (converted !== null) { - data = converted - } - return data - } - - async inject_tlm( - target_name, - packet_name, - item_hash = null, - value_type = 'CONVERTED', - ) { - data = await this.exec( - 'inject_tlm', - [target_name, packet_name, item_hash], - { - type: value_type, - }, - ) - } - - set_tlm(target_name, packet_name, item_name, value_type) { - return this.exec('set_tlm', [target_name, packet_name, item_name], { - type: value_type, - }) - } - - override_tlm(target_name, packet_name, item_name, value_type) { - return this.exec('override_tlm', [target_name, packet_name, item_name], { - type: value_type, - }) - } - - get_overrides() { - return this.exec('get_overrides') - } - - normalize_tlm(target_name, packet_name, item_name, value_type) { - return this.exec('normalize_tlm', [target_name, packet_name, item_name], { - type: value_type, - }) - } - - get_all_cmds(target_name) { - return this.exec('get_all_cmds', [target_name]) - } - - get_all_cmd_names(target_name, hidden = false) { - return this.exec('get_all_cmd_names', [target_name], { hidden: hidden }) - } - - get_cmd(target_name, command_name) { - return this.exec('get_cmd', [target_name, command_name]) - } - - get_cmd_cnts(target_commands) { - return this.exec('get_cmd_cnts', [target_commands]) - } - - get_cmd_value( - target_name, - packet_name, - parameter_name, - value_type = 'CONVERTED', - ) { - return this.exec('get_cmd_value', [ - target_name, - packet_name, - parameter_name, - value_type, - ]) - } - - get_cmd_buffer(target_name, packet_name) { - return this.exec('get_cmd_buffer', [target_name, packet_name]) - } - - // Implementation of functionality shared by cmd methods with param_lists. - _cmd(method, target_name, command_name, param_list, headerOptions) { - let converted = null - for (let key in param_list) { - if (Object.prototype.hasOwnProperty.call(param_list, key)) { - converted = this.encode_openc3_type(param_list[key]) - if (converted !== null) { - param_list[key] = converted - } - } - } - return this.exec( - method, - [target_name, command_name, param_list], - {}, - headerOptions, - ) - } - - get_cmd_hazardous(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('get_cmd_hazardous', target_name) - } else { - return this._cmd( - 'get_cmd_hazardous', - target_name, - command_name, - param_list, - ) - } - } - - cmd(target_name, command_name, param_list, headerOptions = {}) { - if (command_name === undefined) { - return this.exec('cmd', target_name, {}, headerOptions) - } else { - return this._cmd( - 'cmd', - target_name, - command_name, - param_list, - headerOptions, - ) - } - } - - cmd_no_range_check(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('cmd_no_range_check', target_name) - } else { - return this._cmd( - 'cmd_no_range_check', - target_name, - command_name, - param_list, - ) - } - } - - cmd_raw(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('cmd_raw', target_name) - } else { - return this._cmd('cmd_raw', target_name, command_name, param_list) - } - } - - cmd_raw_no_range_check(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('cmd_raw_no_range_check', target_name) - } else { - return this._cmd( - 'cmd_raw_no_range_check', - target_name, - command_name, - param_list, - ) - } - } - - cmd_no_hazardous_check(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('cmd_no_hazardous_check', target_name) - } else { - return this._cmd( - 'cmd_no_hazardous_check', - target_name, - command_name, - param_list, - ) - } - } - - cmd_no_checks(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('cmd_no_checks', target_name) - } else { - return this._cmd('cmd_no_checks', target_name, command_name, param_list) - } - } - - cmd_raw_no_hazardous_check(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('cmd_raw_no_hazardous_check', target_name) - } else { - return this._cmd( - 'cmd_raw_no_hazardous_check', - target_name, - command_name, - param_list, - ) - } - } - - cmd_raw_no_checks(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('cmd_raw_no_checks', target_name) - } else { - return this._cmd( - 'cmd_raw_no_checks', - target_name, - command_name, - param_list, - ) - } - } - - build_cmd(target_name, command_name, param_list) { - if (command_name === undefined) { - return this.exec('build_cmd', target_name) - } else { - return this._cmd('build_cmd', target_name, command_name, param_list) - } - } - - get_interface_names() { - return this.exec('get_interface_names', []) - } - - send_raw(interface_name, data) { - return this.exec('send_raw', [interface_name, data]) - } - - list_configs(tool) { - return this.exec('list_configs', [tool]) - } - - load_config(tool, name) { - return this.exec('load_config', [tool, name]) - } - - save_config(tool, name, data) { - return this.exec('save_config', [tool, name, data]) - } - - delete_config(tool, name) { - return this.exec('delete_config', [tool, name]) - } - - enable_limits(target, packet, item) { - return this.exec('enable_limits', [target, packet, item]) - } - - disable_limits(target, packet, item) { - return this.exec('disable_limits', [target, packet, item]) - } - - get_out_of_limits() { - return this.exec('get_out_of_limits', []) - } - - get_overall_limits_state(ignored) { - return this.exec('get_overall_limits_state', [ignored]) - } - - list_settings() { - return this.exec('list_settings', []) - } - - get_all_settings() { - return this.exec('get_all_settings', []) - } - - get_setting(name) { - return this.exec('get_setting', [name]) - } - - get_settings(array) { - return this.exec('get_settings', array) - } - - set_setting(name, data) { - return this.exec('set_setting', [name, data]) - } - - get_metrics() { - return this.exec('get_metrics', []) - } - - // TODO: Currently unused but seemed like a useful function - async hashString(string) { - if (window.isSecureContext) { - // Encode the string as an arrayBuffer which the subtle crypto API uses - const arrayBuffer = new TextEncoder().encode(string) - // Use the subtle crypto API to perform a SHA256 Sum of the array buffer - // The resulting hash is stored in an array buffer - const hashAsArrayBuffer = await crypto.subtle.digest( - 'SHA-256', - arrayBuffer, - ) - // To create a string we will get the hexadecimal value of each byte of the array buffer - // This gets us an array where each byte of the array buffer becomes one item in the array - const uint8ViewOfHash = new Uint8Array(hashAsArrayBuffer) - // We then convert it to a regular array so we can convert each item to hexadecimal strings - // Where to characters of 0-9 or a-f represent a number between 0 and 16, containing 4 bits of information, so 2 of them is 8 bits (1 byte). - return Array.from(uint8ViewOfHash) - .map((b) => b.toString(16).padStart(2, '0')) - .join('') - } - // else ? - } -} diff --git a/openc3/templates/widget/.eslintrc.js b/openc3/templates/widget/.eslintrc.js deleted file mode 100644 index 5679a1e58f..0000000000 --- a/openc3/templates/widget/.eslintrc.js +++ /dev/null @@ -1,43 +0,0 @@ -module.exports = { - root: true, - env: { - node: true, - }, - extends: [ - 'plugin:vue/essential', - 'plugin:prettier/recommended', - '@vue/prettier', - ], - plugins: ['prettier'], - rules: { - 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', - 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', - 'prettier/prettier': [ - 'warn', - { - endOfLine: 'auto', - }, - ], - 'vue/multi-word-component-names': 'off', - 'vue/valid-v-slot': [ - 'error', - { - allowModifiers: true, - }, - ], - }, - parserOptions: { - parser: '@babel/eslint-parser', - }, - overrides: [ - { - files: [ - '**/__tests__/*.{j,t}s?(x)', - '**/tests/unit/**/*.spec.{j,t}s?(x)', - ], - env: { - jest: true, - }, - }, - ], -} diff --git a/openc3/templates/widget/.prettierrc.js b/openc3/templates/widget/.prettierrc.cjs similarity index 100% rename from openc3/templates/widget/.prettierrc.js rename to openc3/templates/widget/.prettierrc.cjs diff --git a/openc3/templates/widget/babel.config.json b/openc3/templates/widget/babel.config.json deleted file mode 100644 index 91fa88dbe0..0000000000 --- a/openc3/templates/widget/babel.config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "presets": ["@vue/cli-plugin-babel/preset"], - "plugins": [ - [ - "babel-plugin-istanbul", - { - "extension": [".js", ".vue"] - } - ] - ] -} diff --git a/openc3/templates/widget/package.json b/openc3/templates/widget/package.json index a8ce0e9d94..d25f2a8213 100644 --- a/openc3/templates/widget/package.json +++ b/openc3/templates/widget/package.json @@ -1,35 +1,28 @@ { - "name": "widget", + "name": "<%= widget_name %>", "version": "5.21.0-beta0", "private": true, + "type": "module", "scripts": { - "build": "vue-cli-service build --target lib --dest tools/widgets/<%= widget_name %> --formats umd-min <%= widget_path %> --name <%= widget_name %>" + "build": "vite build" }, "dependencies": { "@astrouxds/astro-web-components": "7.24.0", - "vue": "3.5.4", + "@openc3/vue-common": "5.21.0-beta0", "vuetify": "3.7.1" }, "devDependencies": { - "@babel/eslint-parser": "7.25.9", - "@rushstack/eslint-patch": "1.10.4", - "@vue/babel-preset-app": "5.0.8", - "@vue/cli": "5.0.8", - "@vue/cli-plugin-babel": "5.0.8", - "@vue/cli-plugin-eslint": "5.0.8", - "@vue/cli-service": "5.0.8", + "@vitejs/plugin-vue": "5.1.5", "@vue/eslint-config-prettier": "9.0.0", - "babel-loader": "9.2.1", - "babel-plugin-istanbul": "7.0.0", - "eslint": "8.56.0", + "eslint": "9.16.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-prettier": "5.2.1", "eslint-plugin-vue": "9.30.0", "prettier": "3.3.3", "sass": "1.80.4", - "sass-loader": "16.0.2", - "vue-cli-plugin-vuetify": "2.5.8", - "vue-template-compiler": "2.7.16", - "webpack": "5.95.0" + "vite": "5.4.11", + "vue": "3.5.13", + "vite-plugin-style-inject": "0.0.1", + "vue-eslint-parser": "9.4.3" } } diff --git a/openc3/templates/widget/src/Widget.vue b/openc3/templates/widget/src/Widget.vue index 989c2f8b9f..d5bc158fc1 100644 --- a/openc3/templates/widget/src/Widget.vue +++ b/openc3/templates/widget/src/Widget.vue @@ -11,7 +11,7 @@