move openmct plugin to typescript
All checks were successful
Go / build (1.21) (push) Successful in 1m5s
Go / build (1.22) (push) Successful in 1m4s

remove unused livestream test function
This commit is contained in:
saji 2024-03-08 17:08:43 -06:00
parent 13205c1668
commit 4829dd50c7
9 changed files with 1089 additions and 663 deletions

View file

@ -2,7 +2,6 @@ package gotelem
import ( import (
"log/slog" "log/slog"
"math"
"os" "os"
"sync" "sync"
"testing" "testing"
@ -25,53 +24,6 @@ func makeEvent() skylab.BusEvent {
} }
// makeLiveSystem starts a process that is used to continuously stream
// data into a Broker. Every 100ms it will send either a BmsMeasurement
// or WslVelocity. The values will be static for WslVelocity (to
// make comparison easier) but will be dynamic for BmsMeasurement.
//
func liveStream(done chan bool, broker *Broker) {
bmsPkt := &skylab.BmsMeasurement{
Current: 1.23,
BatteryVoltage: 11111,
AuxVoltage: 22222,
}
wslPkt := &skylab.WslVelocity{
MotorVelocity: 0,
VehicleVelocity: 100.0,
}
var next skylab.Packet = bmsPkt
for {
select {
case <-done:
return
case <-time.After(100 * time.Millisecond):
// send the next packet.
if next == bmsPkt {
bmsPkt.Current = float32(math.Sin(float64(time.Now().Unix()) / 2.0))
ev := skylab.BusEvent{
Timestamp: time.Now(),
Name: next.String(),
Data: next,
}
broker.Publish("livestream", ev)
next = wslPkt
} else {
// send the wsl
ev := skylab.BusEvent{
Timestamp: time.Now(),
Name: next.String(),
Data: next,
}
broker.Publish("livestream", ev)
next = bmsPkt
}
}
}
}
func TestBroker(t *testing.T) { func TestBroker(t *testing.T) {
t.Parallel() t.Parallel()

View file

@ -1,18 +0,0 @@
{
"compilerOptions": {
// "baseUrl": "./src",
"target": "es6",
"checkJs": true,
"allowJs": true,
"moduleResolution": "nodenext",
"module": "nodenext",
"allowSyntheticDefaultImports": true,
"paths": {
"openmct": ["./node_modules/openmct/dist/openmct.d.ts"]
}
},
"exclude": [
"./dist/**/*",
"webpack.*.js"
]
}

1139
web/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
"name": "g1_openmct", "name": "g1_openmct",
"version": "1.0.0", "version": "1.0.0",
"description": "dev environment for openmct plugins for g1 strategy tool", "description": "dev environment for openmct plugins for g1 strategy tool",
"main": "index.js", "type": "module",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.prod.js", "build": "webpack --config webpack.prod.js",
@ -13,19 +13,23 @@
"dependencies": { "dependencies": {
"dotenv-webpack": "^8.0.1", "dotenv-webpack": "^8.0.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"openmct": "^2.0.5", "openmct": "^3.2.1",
"socket.io": "^4.6.0", "socket.io": "^4.7.4",
"socket.io-client": "^4.6.0" "socket.io-client": "^4.7.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.25",
"@types/webpack": "^5.28.5",
"copy-webpack-plugin": "^12.0.2", "copy-webpack-plugin": "^12.0.2",
"eslint": "^8.28.0", "eslint": "^8.57.0",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.6.0",
"ts-loader": "^9.4.2", "ts-loader": "^9.5.1",
"typescript": "^4.9.5", "ts-node": "^10.9.2",
"webpack": "^5.75.0", "typescript": "^5.4.2",
"webpack-cli": "^5.0.1", "typescript-eslint": "^7.1.1",
"webpack-dev-server": "^4.11.1", "webpack": "^5.90.3",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.2",
"webpack-merge": "^5.10.0" "webpack-merge": "^5.10.0"
} }
} }

View file

@ -1,238 +0,0 @@
import openmct from 'openmct';
import PhoebusPlugin from "./phoebusPlugin";
openmct.setAssetPath('openmct');
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.Timeline());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
openmct.install(openmct.plugins.Timer());
openmct.install(openmct.plugins.Timelist());
openmct.time.clock('local', { start: -5 * 60 * 1000, end: 0 });
openmct.time.timeSystem('utc');
openmct.install(openmct.plugins.Espresso());
openmct.install(
openmct.plugins.Conductor({
menuOptions: [
{
name: 'Fixed',
timeSystem: 'utc',
bounds: {
start: Date.now() - 30000000,
end: Date.now()
},
},
{
name: 'Realtime',
timeSystem: 'utc',
clock: 'local',
clockOffsets: {
start: -30000000,
end: 30000
},
}
]
})
);
if (process.env.BASE_URL) {
console.log("got a thing")
console.log(process.env.BASE_URL)
}
var schemaCached = null;
function getSchema() {
if (schemaCached === null) {
return fetch(`${process.env.BASE_URL}/api/v1/schema`).then((resp) => {
const res = resp.json()
console.log("got schema, caching", res);
schemaCached = res
return res
})
}
return Promise.resolve(schemaCached)
}
const objectProvider = {
get: function (id) {
return getSchema().then((schema) => {
if (id.key === "car") {
const comp = schema.packets.map((x) => {
return {
key: x.name,
namespace: "umnsvp"
}
})
return {
identifier: id,
name: "the solar car",
type: 'folder',
location: 'ROOT',
composition: comp
}
}
var pkt = schema.packets.find((x) => x.name === id.key)
if (pkt) {
// if the key matches one of the packet names,
// we know it's a field.
const comp = pkt.data.map((field) => {
return {
// we have to do this since
// we can't get the packet name otherwise.
key: `${pkt.name}.${field.name}`,
namespace: "umnsvp"
}
})
return {
identifier: id,
name: pkt.name,
type: 'folder',
composition: comp
}
}
// at this point it's definitely a field aka umnsvp-datum
var [pktName, fieldName] = id.key.split('.')
return {
identifier: id,
name: fieldName,
type: 'umnsvp-datum',
telemetry: {
values: [
{
key: "value",
source: "val",
name: "Value",
"format": "float",
hints: {
range: 1
}
},
{
key: "utc",
source: "ts",
name: "Timestamp",
format: "utc",
hints: {
domain: 1
}
}
]
}
}
})
}
}
const TelemHistoryProvider = {
supportsRequest: function (dObj) {
return dObj.type === 'umnsvp-datum'
},
request: function (dObj, opt) {
var [pktName, fieldName] = dObj.identifier.key.split('.')
var url = `${process.env.BASE_URL}/api/v1/packets/${pktName}/${fieldName}?`
var params = new URLSearchParams({
start: new Date(opt.start).toISOString(),
end: new Date(opt.end).toISOString(),
})
console.log((opt.end - opt.start) / opt.size)
return fetch(url + params).then((resp) => {
return resp.json()
})
}
}
function TelemRealtimeProvider() {
return function (openmct) {
const url = `${process.env.BASE_URL.replace(/^http/, 'ws')}/api/v1/packets/subscribe?`
// we put our websocket connection here.
let connection = new WebSocket(url)
// connections contains name: callback mapping
let callbacks = {}
// names contains a set of *packet names*
let names = new Set()
function handleMessage(event) {
const data = JSON.parse(event.data)
for (const [key, value] of Object.entries(data.data)) {
const id = `${data.name}.${key}`
if (id in callbacks) {
// we should construct a telem point and make a callback.
callbacks[id]({
"ts": data.ts,
"val": value
})
}
}
}
function updateWebsocket() {
const params = new URLSearchParams({
name: Array.from(names)
})
connection = new WebSocket(url + params)
connection.onmessage = handleMessage
}
let provider = {
supportsSubscribe: function (dObj) {
return dObj.type === "umnsvp-datum"
},
subscribe: function (dObj, callback) {
console.log("subscribe called %s", JSON.stringify(dObj))
// identifier is packetname.fieldname. we add the packet name to the set.
const key = dObj.identifier.key
const [pktName, _] = key.split('.')
// add our callback to the dictionary,
// add the packet name to the set
callbacks[key] = callback
names.add(pktName)
// update the websocket URL with the new name.
updateWebsocket()
return function unsubscribe() {
names.delete(pktName)
delete callbacks[key]
}
}
}
openmct.telemetry.addProvider(provider)
}
}
function GotelemPlugin() {
return function install(openmct) {
openmct.types.addType('umnsvp-datum', {
name: "UMN SVP Data Field",
description: "A data field of a packet from the car",
creatable: false,
cssClass: "icon-telemetry"
})
openmct.objects.addRoot({
namespace: "umnsvp",
key: 'car'
}, openmct.priority.HIGH)
openmct.objects.addProvider('umnsvp', objectProvider);
openmct.telemetry.addProvider(TelemHistoryProvider)
}
}
openmct.install(GotelemPlugin())
openmct.install(TelemRealtimeProvider())
openmct.start();

View file

@ -1,243 +0,0 @@
// import openmct from "openmct";
import {_} from "lodash";
import { io } from "socket.io-client";
// Pho
function PhoebusObjectProvider(identifier) {
// console.log("obj provider", identifier)
const board_regex = /^board\.([a-z-_]+)$/
const packet_regex = /^board\.([a-z-_]+)\.([a-z-_]+)$/
const data_field_regex = /^board\.([a-z-_]+)\.([a-z-_]+)\.([a-z-_]+)$/ // board.wavesculptor.wsl_heatsink_motor_temp.motor_temp
const data_field_regex_num = /^board\.([a-z-_]+)\.([a-z-_]+)\.([A-Za-z0-9]+)$/ // board.car_control.dashboard_system_timeout_test.flag_set0
const packet_regex_underscore_num = /^board\.([a-z-_]+)\.([a-z-_]+(_[A-Za-z0-9]+)+)$/ // board.steering.steering_press_count_1
const data_field_regex_underscore_num1 = /^board\.([a-z-_]+)\.([a-z-_]+(_[A-Za-z0-9]+)+)\.([A-Za-z0-9]+)$/ // board.steering.steering_press_count_1.button1
const data_field_regex_underscore_num2 = /^board\.([a-z-_]+)\.([a-z-_]+)\.([a-z-_]+(_[A-Za-z0-9]+)+)$/ // board.wavesculptor.wsr_status_information.error_flags_0
const data_field_regex_underscore_num3 = /^board\.([a-z-_]+)\.([a-z-_]+(_[A-Za-z0-9]+)+)\.([A-Za-z0-9]+(_[A-Za-z0-9]+)+)$/ // board.wavesculptor.wsr_15_165_voltage_rail.reference_165v
if (identifier.key === 'car') {
// get the car schema from the phoebus server
return Promise.resolve({
identifier: identifier,
name: "car!",
type: 'folder',
location: "ROOT"
})
} else if (board_regex.test(identifier.key)) {
// it's a board object. get the last word (ie board.bms -> bms)
// then use it to construct a get request
// console.log("doing board for ", identifier)
const board_name = identifier.key.match(board_regex)[1]
return fetch(`${process.env.VUE_APP_TELEM}/schema/` + board_name).then((resp) => {
return resp.json()
})
} else if (packet_regex.test(identifier.key)) {
const m = identifier.key.match(packet_regex)
const board_name = m[1]
const pkt_name = m[2]
return fetch([`${process.env.VUE_APP_TELEM}/schema`, board_name, pkt_name].join("/")).then((resp) => {
return resp.json()
})
} else if (data_field_regex.test(identifier.key)) {
const m = identifier.key.match(data_field_regex)
const board_name = m[1]
const pkt_name = m[2]
const df_name = m[3]
return fetch([`${process.env.VUE_APP_TELEM}/schema`, board_name, pkt_name, df_name].join("/")).then((resp) => {
return resp.json();
})
} else if (data_field_regex_num.test(identifier.key)) {
const m = identifier.key.match(data_field_regex_num)
const board_name = m[1]
const pkt_name = m[2]
const df_name = m[3]
return fetch([`${process.env.VUE_APP_TELEM}/schema`, board_name, pkt_name, df_name].join("/")).then((resp) => {
return resp.json();
})
} else if (packet_regex_underscore_num.test(identifier.key)) {
const m = identifier.key.match(packet_regex_underscore_num)
const board_name = m[1]
const pkt_name = m[2]
return fetch([`${process.env.VUE_APP_TELEM}/schema`, board_name, pkt_name].join("/")).then((resp) => {
return resp.json();
})
} else if (data_field_regex_underscore_num1.test(identifier.key)) {
const m = identifier.key.match(data_field_regex_underscore_num1)
const board_name = m[1]
const pkt_name = m[2]
const df_name = m[4]
return fetch([`${process.env.VUE_APP_TELEM}/schema`, board_name, pkt_name, df_name].join("/")).then((resp) => {
return resp.json();
})
} else if (data_field_regex_underscore_num2.test(identifier.key)) {
const m = identifier.key.match(data_field_regex_underscore_num2)
const board_name = m[1]
const pkt_name = m[2]
const df_name = m[3]
return fetch([`${process.env.VUE_APP_TELEM}/schema`, board_name, pkt_name, df_name].join("/")).then((resp) => {
return resp.json();
})
} else if (data_field_regex_underscore_num3.test(identifier.key)) {
const m = identifier.key.match(data_field_regex_underscore_num3)
const board_name = m[1]
const pkt_name = m[2]
const df_name = m[4]
return fetch([`${process.env.VUE_APP_TELEM}/schema`, board_name, pkt_name, df_name].join("/")).then((resp) => {
return resp.json();
})
}
console.error("bad! no match for object.")
return {}
}
function getSchema() {
return fetch(`${process.env.VUE_APP_TELEM}/schema`).then((resp) => {
const res = resp.json()
console.log("got schema", res)
return res
})
}
const CarCompositionProvider = {
appliesTo: function (domainObject) {
return domainObject.identifier.namespace === "umnsvp.phoebus" &&
domainObject.identifier.key === "car"
},
load: function (domainObject) {
return getSchema()
}
}
// the board composition provider takes a board type object, and gets all the telem objects for that board.
// it's like a folder.
const BoardCompositionProvider = {
appliesTo: function (domainObject) {
return domainObject.identifier.namespace === "umnsvp.phoebus" &&
domainObject.type === "umnsvp-board"
},
load: function (domainObject) {
// console.log("board comp provider for ", domainObject.packets)
return Promise.resolve(domainObject.packets)
// const board_name = domainObject.identifier.key.match(/^board\.([a-z-_]+)$/)
// return fetch("${process.env.VUE_APP_TELEM}/schema/" + board_name[1] + "/packets").then((resp) => {
// return resp.json()
// })
}
}
const PacketCompositionProvider = {
appliesTo: function (domainObject) {
return domainObject.identifier.namespace === "umnsvp.phoebus" && domainObject.type === "umnsvp-packet"
},
load: function (domainObject) {
// console.log("packet comp provider for ", domainObject.data_fields)
return Promise.resolve(domainObject.data_fields)
}
}
const PhoebusCompositionProviders = [
CarCompositionProvider,
BoardCompositionProvider,
PacketCompositionProvider
]
function PhoebusRealTime() {
const socket = io(`ws://localhost:${process.env.VUE_APP_TELEM_PORT}`)
let callback_list = {};
socket.on("packet", (data) => {
// if (data[0].packet_name === "wsl_bus_measurement") {
// console.log("got a wave meas packt", data[0])
// }
try {
const data_fields = data[0].data[0] // object that defines the measurement in each packet which are sent to openmct
// const key = ["board", data[0].board, data[0].packet_name].join(".")
console.log(Object.keys(data_fields))
console.log(callback_list)
// for (let i = 0; i < callback_list.length; i++) {
// if (Object.keys(data_fields).includes())
// }
Object.keys(data_fields).forEach((data_field) => {
let key = ["board", data[0].board, data[0].packet_name, data_field].join(".");
if (Object.keys(callback_list).includes(key)) {
let callBack = callback_list[key];
let timestamp = new Date().getTime();
// let realTimeData = data_fields;
let realTimeData = {}
realTimeData[data_field] = data_fields[data_field]
realTimeData.timestamp = timestamp;
realTimeData.id = key;
// console.log(realTimeData)
callBack(realTimeData);
}
})
} catch (e) {
// do nothing
console.warn("a", e)
}
})
const provider = {
supportsSubscribe(domainObject) {
console.log("checking if supports subscribe", domainObject)
return domainObject.type === "umnsvp-data"
},
subscribe(domainObject, callback) {
console.log("subscribing for ", domainObject)
console.log("callback", callback)
// register us in the callback list
callback_list[domainObject.identifier.key] = callback
return function unsubscribe() {
delete callback_list[domainObject.identifier.key]
}
}
}
return provider;
}
function PhoebusHistorical() {
return {
supportsRequest(domainObject) {
return domainObject.type === "umnsvp-packet"
},
request(domainObject, options) {
// return fetch()
}
}
}
function PhoebusPlugin() {
return function install(openmct) {
openmct.types.addType('umnsvp-board', {
name: "UMN SVP Board",
description: 'A board that sends telemetry packets/data',
createable: false,
cssClass: "icon-suitcase"
})
openmct.types.addType('umnsvp-packet', {
name: "UMN SVP Packet",
description: "A packet of telemetry from the car",
creatable: false,
cssClass: "icon-suitcase"
})
openmct.types.addType('umnsvp-data', {
name: "UMN SVP Data Field",
description: "A data field of a packet from the car",
creatable: false,
cssClass: "icon-telemetry"
})
openmct.objects.addRoot({
namespace: "umnsvp.phoebus",
key: 'car'
})
openmct.objects.addProvider('umnsvp.phoebus', {get:PhoebusObjectProvider})
openmct.telemetry.addProvider(PhoebusRealTime())
PhoebusCompositionProviders.forEach((provider) => {
openmct.composition.addProvider((provider))
})
console.log("hello~!")
}
}
export default PhoebusPlugin;

View file

@ -1,10 +1,11 @@
const path = require('path'); import path from 'path';
const HtmlWebpackPlugin = require('html-webpack-plugin'); import {fileURLToPath} from 'url';
const CopyPlugin = require("copy-webpack-plugin"); import HtmlWebpackPlugin from 'html-webpack-plugin';
const Dotenv = require('dotenv-webpack'); import CopyPlugin from 'copy-webpack-plugin';
import DotenvWebpackPlugin from 'dotenv-webpack';
module.exports = { const config = {
entry: './src/app.js', entry: './src/app.ts',
module: { module: {
rules: [ rules: [
{ {
@ -19,7 +20,7 @@ module.exports = {
template: 'src/index.html', template: 'src/index.html',
filename: 'index.html', filename: 'index.html',
}), }),
new Dotenv(), new DotenvWebpackPlugin(),
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ from: "**/*", to: "openmct/", context: "node_modules/openmct/dist"}, { from: "**/*", to: "openmct/", context: "node_modules/openmct/dist"},
@ -34,6 +35,8 @@ module.exports = {
}, },
output: { output: {
filename: 'main.js', filename: 'main.js',
path: path.resolve(__dirname, 'dist'), path: path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'dist'),
}, },
}; };
export default config

View file

@ -1,8 +1,8 @@
const { merge } = require('webpack-merge'); import {merge} from 'webpack-merge';
const common = require('./webpack.common.js'); import common from "./webpack.common.js"
const webpack = require('webpack'); import webpack from 'webpack'
module.exports = merge(common, { const config = merge(common, {
mode: "development", mode: "development",
devtool: 'inline-source-map', devtool: 'inline-source-map',
plugins: [ plugins: [
@ -20,3 +20,5 @@ module.exports = merge(common, {
}, },
}, },
}) })
export default config

View file

@ -1,13 +1,16 @@
const { merge } = require('webpack-merge'); import { merge } from 'webpack-merge'
const common = require('./webpack.common.js'); import common from './webpack.common.js'
import webpack from 'webpack'
module.exports = merge(common, { const config = merge(common, {
mode: "production", mode: "production",
plugins: [ plugins: [
new webpack.EnvironmentPlugin({ new webpack.EnvironmentPlugin({
NODE_ENV: "production", NODE_ENV: "production",
BASE_URL: "", BASE_URL: "",
}); })
], ],
devtool: 'source-map', devtool: 'source-map',
}) })
export default config