From 5ff2656bf2551248ce322d3640aed47b9a43cea7 Mon Sep 17 00:00:00 2001 From: Arjun Naha Date: Thu, 21 Nov 2024 18:54:14 +0000 Subject: [PATCH] TEL - Add Biome (#53) --- .github/workflows/lint.yml | 2 +- .github/workflows/telemetry.yml | 31 + telemetry/.prettierrc | 5 - telemetry/Dockerfile | 1 - telemetry/biome.json | 31 + telemetry/docker-compose.yml | 1 - telemetry/package.json | 62 +- telemetry/packages/constants/.eslintrc.js | 9 - telemetry/packages/constants/biome.json | 3 + telemetry/packages/constants/package.json | 44 +- .../packages/constants/src/faults/levels.ts | 6 +- telemetry/packages/constants/src/index.ts | 12 +- .../src/openmct/object-types/object-types.ts | 100 +- .../packages/constants/src/pods/common.ts | 128 +- .../packages/constants/src/pods/modes.ts | 34 +- telemetry/packages/constants/src/pods/pods.ts | 1696 +- .../packages/constants/src/pods/states.ts | 60 +- .../src/socket/getMeasurementRoomName.ts | 6 +- .../packages/constants/src/socket/index.ts | 8 +- telemetry/packages/constants/tsconfig.json | 20 +- telemetry/packages/e2e-tests/biome.json | 3 + telemetry/packages/e2e-tests/lib/mqtt.ts | 82 +- telemetry/packages/e2e-tests/package.json | 34 +- .../packages/e2e-tests/playwright.config.ts | 78 +- .../packages/e2e-tests/tests/example.spec.ts | 30 +- telemetry/packages/e2e-tests/tsconfig.json | 8 +- telemetry/packages/eslint-config/README.md | 3 - telemetry/packages/eslint-config/basic.js | 28 - telemetry/packages/eslint-config/package.json | 19 - telemetry/packages/eslint-config/react.js | 23 - telemetry/packages/fake/.eslintrc.js | 9 - telemetry/packages/fake/biome.json | 3 + telemetry/packages/fake/package.json | 43 +- telemetry/packages/fake/src/base.ts | 116 +- telemetry/packages/fake/src/config.ts | 137 +- telemetry/packages/fake/src/index.ts | 18 +- telemetry/packages/fake/src/sensorManager.ts | 252 +- telemetry/packages/fake/src/sensors/index.ts | 39 +- .../packages/fake/src/sensors/keyence.ts | 68 +- .../packages/fake/src/sensors/levitation.ts | 289 +- .../packages/fake/src/sensors/magnetism.ts | 64 +- telemetry/packages/fake/src/sensors/motion.ts | 90 +- .../packages/fake/src/sensors/pressure.ts | 152 +- .../packages/fake/src/sensors/resistance.ts | 60 +- .../packages/fake/src/sensors/temperature.ts | 76 +- telemetry/packages/fake/src/types.ts | 6 +- telemetry/packages/fake/src/utils.ts | 214 +- telemetry/packages/fake/tsconfig.json | 22 +- telemetry/packages/public-app/.eslintrc.json | 8 - telemetry/packages/public-app/app/cards.tsx | 134 +- telemetry/packages/public-app/app/globals.css | 14 +- telemetry/packages/public-app/app/layout.tsx | 22 +- .../public-app/app/loading-screen.tsx | 34 +- telemetry/packages/public-app/app/page.tsx | 40 +- .../packages/public-app/app/providers.tsx | 6 +- telemetry/packages/public-app/biome.json | 3 + .../components/displacement-chart.tsx | 94 +- .../public-app/components/launch-time.tsx | 58 +- .../components/levitation-height.tsx | 174 +- .../public-app/components/social-icons.tsx | 78 +- .../public-app/components/theme-switch.tsx | 22 +- .../public-app/components/velocity-graph.tsx | 90 +- telemetry/packages/public-app/helpers.ts | 62 +- telemetry/packages/public-app/next.config.js | 20 +- telemetry/packages/public-app/package.json | 84 +- .../packages/public-app/postcss.config.js | 10 +- .../packages/public-app/tailwind.config.js | 277 +- telemetry/packages/public-app/tsconfig.json | 36 +- telemetry/packages/server/.eslintrc.js | 9 - telemetry/packages/server/biome.json | 8 + telemetry/packages/server/nest-cli.json | 12 +- telemetry/packages/server/package.json | 103 +- .../packages/server/src/app.controller.ts | 12 +- telemetry/packages/server/src/app.module.ts | 40 +- telemetry/packages/server/src/app.service.ts | 6 +- telemetry/packages/server/src/main.ts | 14 +- .../src/modules/common/types/InfluxRow.ts | 6 +- .../modules/common/utils/toUnixTimestamp.ts | 2 +- .../common/utils/zodEnumFromObjKeys.ts | 6 +- .../controls/PodControls.controller.ts | 26 +- .../modules/controls/PodControls.module.ts | 4 +- .../modules/controls/PodControls.service.ts | 82 +- .../server/src/modules/core/config.ts | 18 +- .../src/modules/influx/Influx.module.ts | 4 +- .../src/modules/influx/Influx.service.ts | 136 +- .../influx/errors/InfluxServiceError.ts | 8 +- .../src/modules/live-logs/LiveLogs.gateway.ts | 28 +- .../modules/live-logs/LiveLogsTransport.ts | 48 +- .../src/modules/logger/Logger.module.ts | 112 +- .../modules/measurement/Measurement.module.ts | 10 +- .../measurement/Measurement.service.ts | 152 +- .../measurement/MeasurementReading.types.ts | 74 +- .../MeasurementReadingValidationError.ts | 8 +- .../utils/doesMeasurementBreachLimits.ts | 34 +- .../modules/mqtt/client/MqttClientModule.ts | 6 +- .../mqtt/ingestion/MqttIngestion.module.ts | 8 +- .../mqtt/ingestion/MqttIngestion.service.ts | 124 +- .../ingestion/errors/MqttIngestionError.ts | 8 +- .../src/modules/openmct/OpenMCT.module.ts | 8 +- .../openmct/data/OpenMCTData.module.ts | 10 +- .../HistoricalTelemetryData.controller.ts | 32 +- .../HistoricalTelemetryData.service.ts | 82 +- .../realtime/RealtimeTelemetryData.gateway.ts | 100 +- .../dictionary/Dictionary.controller.ts | 42 +- .../openmct/dictionary/Dictionary.service.ts | 92 +- .../utils/mapMeasurementToOpenMct.ts | 70 +- .../openmct/faults/Fault.controller.ts | 60 +- .../modules/openmct/faults/Fault.module.ts | 20 +- .../modules/openmct/faults/Fault.service.ts | 504 +- .../HistoricalFaultData.controller.ts | 46 +- .../historical/HistoricalFaultData.service.ts | 106 +- .../realtime/RealtimeFaultData.gateway.ts | 68 +- .../faults/utils/convertToOpenMctFault.ts | 56 +- .../object-types/ObjectTypes.controller.ts | 12 +- .../object-types/ObjectTypes.service.ts | 16 +- .../public-data/PublicData.controller.ts | 318 +- .../modules/public-data/PublicData.module.ts | 10 +- .../modules/public-data/PublicData.service.ts | 176 +- .../remote-logs/RemoteLogs.controller.ts | 32 +- .../modules/remote-logs/RemoteLogs.module.ts | 4 +- .../modules/remote-logs/RemoteLogs.service.ts | 54 +- .../server/src/modules/state/State.module.ts | 8 +- .../server/src/modules/state/State.service.ts | 96 +- .../src/modules/state/StateUpdate.types.ts | 8 +- .../MeasurementReadingValidationError.ts | 8 +- .../src/modules/state/utils/isValidState.ts | 2 +- .../modules/warnings/Warnings.controller.ts | 12 +- .../src/modules/warnings/Warnings.module.ts | 4 +- .../src/modules/warnings/Warnings.service.ts | 22 +- .../packages/server/test/app.e2e-spec.ts | 32 +- telemetry/packages/server/test/jest-e2e.json | 14 +- telemetry/packages/server/tsconfig.json | 40 +- telemetry/packages/tsconfig/base.json | 50 +- telemetry/packages/tsconfig/package.json | 6 +- telemetry/packages/types/.eslintrc.js | 9 - telemetry/packages/types/biome.json | 3 + telemetry/packages/types/package.json | 29 +- telemetry/packages/types/src/index.ts | 30 +- .../src/openmct/openmct-dictionary.types.ts | 56 +- .../types/src/openmct/openmct-fault.types.ts | 58 +- .../src/openmct/openmct-object-types.types.ts | 8 +- .../packages/types/src/pods/pods.types.ts | 52 +- .../packages/types/src/server/responses.ts | 40 +- telemetry/packages/types/tsconfig.json | 14 +- telemetry/packages/ui/.eslintrc.cjs | 10 - telemetry/packages/ui/app/App.tsx | 62 +- .../packages/ui/app/components/error.tsx | 95 - .../packages/ui/app/components/errors.tsx | 97 + .../app/components/shared/latency-chart.tsx | 60 +- .../ui/app/components/shared/logo.tsx | 45 +- .../ui/app/components/shared/pod-state.tsx | 80 +- .../shared/set-levitation-height.tsx | 62 +- .../ui/app/components/sidebar/index.tsx | 125 +- .../ui/app/components/sidebar/latency.tsx | 82 +- .../sidebar/pod-connection-status.tsx | 146 +- .../app/components/sidebar/pod-controls.tsx | 126 +- .../app/components/sidebar/pod-selector.tsx | 66 +- .../ui/app/components/ui/alert-dialog.tsx | 176 +- .../packages/ui/app/components/ui/button.tsx | 84 +- .../packages/ui/app/components/ui/card.tsx | 100 +- .../ui/app/components/ui/carousel.tsx | 391 +- .../packages/ui/app/components/ui/dialog.tsx | 150 +- .../ui/app/components/ui/dropdown-menu.tsx | 268 +- .../packages/ui/app/components/ui/form.tsx | 244 +- .../packages/ui/app/components/ui/input.tsx | 28 +- .../packages/ui/app/components/ui/label.tsx | 26 +- .../ui/app/components/ui/resizable.tsx | 54 +- .../packages/ui/app/components/ui/select.tsx | 232 +- .../packages/ui/app/components/ui/switch.tsx | 38 +- .../ui/app/components/ui/textarea.tsx | 26 +- .../packages/ui/app/components/ui/tooltip.tsx | 34 +- .../connection-statuses.tsx | 104 +- .../mqtt-connection-status.tsx | 148 +- .../pod-connection-statuses.tsx | 118 +- .../server-connection-status.tsx | 172 +- .../views/debug-view/full-controls.tsx | 332 +- .../app/components/views/debug-view/index.tsx | 116 +- .../views/debug-view/mqtt-sender.tsx | 340 +- .../views/debug-view/pod-state-updater.tsx | 86 +- .../ui/app/components/views/log-viewer.tsx | 271 +- .../ui/app/components/views/openmct.tsx | 11 +- .../components/views/state-machine/edges.ts | 410 +- .../components/views/state-machine/index.tsx | 1098 +- .../views/state-machine/nodes/active-node.tsx | 58 +- .../state-machine/nodes/failure-node.tsx | 58 +- .../state-machine/nodes/neutral-node.tsx | 52 +- .../state-machine/nodes/passive-node.tsx | 58 +- .../views/state-machine/nodes/styles.ts | 2 +- .../components/views/state-machine/styles.css | 6 +- .../components/views/state-machine/types.ts | 40 +- .../components/views/state-machine/utils.ts | 28 +- telemetry/packages/ui/app/config.ts | 61 +- telemetry/packages/ui/app/context/errors.tsx | 138 +- .../packages/ui/app/context/live-logs.tsx | 131 +- telemetry/packages/ui/app/context/mqtt.tsx | 305 +- telemetry/packages/ui/app/context/pods.tsx | 502 +- telemetry/packages/ui/app/globals.css | 98 +- .../packages/ui/app/hooks/useKeyPress.ts | 64 +- telemetry/packages/ui/app/lib/controls.ts | 32 +- telemetry/packages/ui/app/lib/logger.ts | 36 +- telemetry/packages/ui/app/lib/utils.ts | 6 +- telemetry/packages/ui/app/main.tsx | 28 +- telemetry/packages/ui/app/providers.tsx | 20 +- .../ui/app/types/MQTTConnectionStatus.ts | 14 +- .../ui/app/types/PodConnectionStatus.ts | 10 +- telemetry/packages/ui/app/types/mqtt.ts | 6 +- telemetry/packages/ui/app/views.tsx | 52 +- telemetry/packages/ui/biome.json | 3 + telemetry/packages/ui/components.json | 32 +- telemetry/packages/ui/mqtt.d.ts | 4 +- telemetry/packages/ui/openmct/core/http.ts | 28 +- telemetry/packages/ui/openmct/main.ts | 46 +- .../packages/ui/openmct/plugins/conductor.ts | 190 +- .../openmct/plugins/data/object-types-data.ts | 4 +- .../ui/openmct/plugins/data/pods-data.ts | 12 +- .../ui/openmct/plugins/dictionary-plugin.ts | 189 +- .../plugins/faults-plugin/constants.ts | 4 +- .../plugins/faults-plugin/faults-plugin.ts | 120 +- .../historical-faults-provider.ts | 37 +- .../faults-plugin/realtime-faults-provider.ts | 84 +- .../plugins/historical-telemetry-plugin.ts | 47 +- .../ui/openmct/plugins/limit-plugin.ts | 238 +- .../plugins/realtime-telemetry-plugin.ts | 110 +- .../plugins/utils/convertNamespaceToPodId.ts | 10 +- .../ui/openmct/types/AugmentedDomainObject.ts | 8 +- telemetry/packages/ui/openmct/types/Datum.ts | 8 +- .../ui/openmct/types/MeasurementReading.ts | 8 +- .../ui/openmct/types/ObjectIdentifier.ts | 4 +- .../RealtimeTelemetryListenerCallback.ts | 6 +- telemetry/packages/ui/package.json | 135 +- telemetry/packages/ui/postcss.config.cjs | 10 +- .../public/mct-dashboards/local-storage.json | 7156 +++--- .../ui/public/openmct/data/dashboards.json | 3200 +-- telemetry/packages/ui/tailwind.config.cjs | 398 +- telemetry/packages/ui/tsconfig.json | 42 +- telemetry/packages/ui/vite.config.ts | 72 +- telemetry/pnpm-lock.yaml | 19288 ++++++++-------- telemetry/telemetry.code-workspace | 43 + telemetry/turbo.json | 70 +- 239 files changed, 24490 insertions(+), 24152 deletions(-) create mode 100644 .github/workflows/telemetry.yml delete mode 100644 telemetry/.prettierrc create mode 100644 telemetry/biome.json delete mode 100644 telemetry/packages/constants/.eslintrc.js create mode 100644 telemetry/packages/constants/biome.json create mode 100644 telemetry/packages/e2e-tests/biome.json delete mode 100644 telemetry/packages/eslint-config/README.md delete mode 100644 telemetry/packages/eslint-config/basic.js delete mode 100644 telemetry/packages/eslint-config/package.json delete mode 100644 telemetry/packages/eslint-config/react.js delete mode 100644 telemetry/packages/fake/.eslintrc.js create mode 100644 telemetry/packages/fake/biome.json delete mode 100644 telemetry/packages/public-app/.eslintrc.json create mode 100644 telemetry/packages/public-app/biome.json delete mode 100644 telemetry/packages/server/.eslintrc.js create mode 100644 telemetry/packages/server/biome.json delete mode 100644 telemetry/packages/types/.eslintrc.js create mode 100644 telemetry/packages/types/biome.json delete mode 100644 telemetry/packages/ui/.eslintrc.cjs delete mode 100644 telemetry/packages/ui/app/components/error.tsx create mode 100644 telemetry/packages/ui/app/components/errors.tsx create mode 100644 telemetry/packages/ui/biome.json create mode 100644 telemetry/telemetry.code-workspace diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a170d39b..5ccfeaf5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -62,4 +62,4 @@ jobs: if git ls-files --eol | grep crlf; then echo "[ERROR] found CRLF line endings, install dos2unix and run 'find . -type f -exec dos2unix {} \;' to fix" exit 1 - fi + fi \ No newline at end of file diff --git a/.github/workflows/telemetry.yml b/.github/workflows/telemetry.yml new file mode 100644 index 00000000..d16eb253 --- /dev/null +++ b/.github/workflows/telemetry.yml @@ -0,0 +1,31 @@ +name: Telemetry + +on: push + +jobs: + telemetry: + name: Lint and build + runs-on: ubuntu-latest + defaults: + run: + working-directory: telemetry + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: 9.5.0 + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + cache-dependency-path: telemetry/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm lint + + - name: Build + run: pnpm build \ No newline at end of file diff --git a/telemetry/.prettierrc b/telemetry/.prettierrc deleted file mode 100644 index 6de9cff5..00000000 --- a/telemetry/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all", - "endOfLine": "lf" -} diff --git a/telemetry/Dockerfile b/telemetry/Dockerfile index 1b39c6e7..f429b8e6 100644 --- a/telemetry/Dockerfile +++ b/telemetry/Dockerfile @@ -14,7 +14,6 @@ COPY packages/types/package.json ./packages/types/ COPY packages/ui/package.json ./packages/ui/ COPY packages/public-app/package.json ./packages/public-app/ COPY packages/fake/package.json ./packages/fake/ -COPY packages/eslint-config/package.json ./packages/eslint-config/ COPY packages/tsconfig/package.json ./packages/tsconfig/ COPY packages/e2e-tests/package.json ./packages/e2e-tests/ COPY pnpm-lock.yaml ./ diff --git a/telemetry/biome.json b/telemetry/biome.json new file mode 100644 index 00000000..53830897 --- /dev/null +++ b/telemetry/biome.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true, + "defaultBranch": "main" + }, + "files": { + "ignoreUnknown": false, + "ignore": [] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + } +} diff --git a/telemetry/docker-compose.yml b/telemetry/docker-compose.yml index 713c31c7..ccab03bc 100644 --- a/telemetry/docker-compose.yml +++ b/telemetry/docker-compose.yml @@ -27,7 +27,6 @@ services: - /usr/src/app/packages/public-app/node_modules - /usr/src/app/packages/public-app/.next - /usr/src/app/packages/fake/node_modules - - /usr/src/app/packages/eslint-config/node_modules - /usr/src/app/packages/e2e-tests/node_modules networks: - telemetry diff --git a/telemetry/package.json b/telemetry/package.json index a465122d..d955b6d5 100644 --- a/telemetry/package.json +++ b/telemetry/package.json @@ -1,30 +1,36 @@ { - "name": "@hyped/telemetry", - "private": true, - "scripts": { - "preinstall": "npx only-allow pnpm", - "start": "turbo run start", - "dev": "turbo run dev", - "dev:test": "turbo run dev:test", - "ci": "turbo run build lint e2e:test", - "lint": "turbo run lint --parallel", - "lint:fix": "turbo run lint:fix --parallel", - "format": "prettier --write \"**/*.{ts,tsx,md}\"", - "e2e:test": "turbo run e2e:test", - "format:check": "prettier --check \"**/*.{ts,tsx,md}\"", - "build": "turbo run build" - }, - "devDependencies": { - "@types/node": "^18.11.18", - "prettier": "3.2.4", - "turbo": "^1.11.3", - "typescript": "^5.3.3" - }, - "pnpm": { - "patchedDependencies": { - "@nestjs/common@9.4.2": "patches/@nestjs__common@9.4.2.patch", - "nest-mqtt@0.2.0": "patches/nest-mqtt@0.2.0.patch", - "openmct@3.2.0": "patches/openmct@3.2.0.patch" - } - } + "name": "@hyped/telemetry", + "private": true, + "scripts": { + "preinstall": "npx only-allow pnpm", + "start": "turbo run start", + "dev": "turbo run dev", + "dev:test": "turbo run dev:test", + "ci": "turbo run build lint e2e:test", + "lint": "turbo run lint --parallel", + "lint:fix": "turbo run lint:fix --parallel", + "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "e2e:test": "turbo run e2e:test", + "format:check": "prettier --check \"**/*.{ts,tsx,md}\"", + "build": "turbo run build" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@types/node": "^18.11.18", + "prettier": "3.2.4", + "turbo": "^1.11.3", + "typescript": "^5.3.3" + }, + "pnpm": { + "patchedDependencies": { + "@nestjs/common@9.4.2": "patches/@nestjs__common@9.4.2.patch", + "nest-mqtt@0.2.0": "patches/nest-mqtt@0.2.0.patch", + "openmct@3.2.0": "patches/openmct@3.2.0.patch" + } + }, + "engines": { + "node": ">=18", + "pnpm": ">=9" + }, + "packageManager": "pnpm@9.5.0" } diff --git a/telemetry/packages/constants/.eslintrc.js b/telemetry/packages/constants/.eslintrc.js deleted file mode 100644 index acab2de9..00000000 --- a/telemetry/packages/constants/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ['@hyped/eslint-config/basic.js'], - parserOptions: { - project: true, - tsconfigRootDir: __dirname, - }, -}; diff --git a/telemetry/packages/constants/biome.json b/telemetry/packages/constants/biome.json new file mode 100644 index 00000000..784b6d85 --- /dev/null +++ b/telemetry/packages/constants/biome.json @@ -0,0 +1,3 @@ +{ + "extends": ["../../biome.json"] +} diff --git a/telemetry/packages/constants/package.json b/telemetry/packages/constants/package.json index f77f1707..595dfa5d 100644 --- a/telemetry/packages/constants/package.json +++ b/telemetry/packages/constants/package.json @@ -1,26 +1,22 @@ { - "name": "@hyped/telemetry-constants", - "private": true, - "version": "1.0.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "module": "dist/telemetry-constants.esm.js", - "files": [ - "dist", - "src" - ], - "scripts": { - "start": "tsdx watch", - "build": "tsdx build", - "lint": "eslint \"src/**/*.ts\" --max-warnings 0 --report-unused-disable-directives", - "lint:fix": "eslint \"src/**/*.ts\" --fix" - }, - "devDependencies": { - "@hyped/telemetry-types": "workspace:*", - "@hyped/eslint-config": "workspace:*", - "@hyped/tsconfig": "workspace:*", - "tsdx": "^0.14.1", - "tslib": "^2.5.3", - "typescript": "^5.3.3" - } + "name": "@hyped/telemetry-constants", + "private": true, + "version": "1.0.0", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "module": "dist/telemetry-constants.esm.js", + "files": ["dist", "src"], + "scripts": { + "start": "dts watch", + "build": "dts build", + "lint": "biome lint .", + "lint:fix": "biome check --write ." + }, + "devDependencies": { + "@hyped/telemetry-types": "workspace:*", + "@hyped/tsconfig": "workspace:*", + "dts-cli": "^2.0.5", + "tslib": "^2.5.3", + "typescript": "^5.3.3" + } } diff --git a/telemetry/packages/constants/src/faults/levels.ts b/telemetry/packages/constants/src/faults/levels.ts index a957e603..57164c85 100644 --- a/telemetry/packages/constants/src/faults/levels.ts +++ b/telemetry/packages/constants/src/faults/levels.ts @@ -1,7 +1,7 @@ export const FAULT_LEVEL = { - WATCH: 'WATCH', - WARNING: 'WARNING', - CRITICAL: 'CRITICAL', + WATCH: 'WATCH', + WARNING: 'WARNING', + CRITICAL: 'CRITICAL', } as const; export type FaultLevel = (typeof FAULT_LEVEL)[keyof typeof FAULT_LEVEL]; diff --git a/telemetry/packages/constants/src/index.ts b/telemetry/packages/constants/src/index.ts index 18864136..76ad210d 100644 --- a/telemetry/packages/constants/src/index.ts +++ b/telemetry/packages/constants/src/index.ts @@ -1,12 +1,12 @@ export { pods, POD_IDS } from './pods/pods'; export type { PodId, Pods } from './pods/pods'; export { - ALL_POD_STATES, - PASSIVE_STATES, - ACTIVE_STATES, - NULL_STATES, - FAILURE_STATES, - getStateType, + ALL_POD_STATES, + PASSIVE_STATES, + ACTIVE_STATES, + NULL_STATES, + FAILURE_STATES, + getStateType, } from './pods/states'; export type { PodStateType, PodStateCategoryType } from './pods/states'; export { MODES, MODE_EXCLUDED_STATES } from './pods/modes'; diff --git a/telemetry/packages/constants/src/openmct/object-types/object-types.ts b/telemetry/packages/constants/src/openmct/object-types/object-types.ts index c8e17ec3..287c3fe6 100644 --- a/telemetry/packages/constants/src/openmct/object-types/object-types.ts +++ b/telemetry/packages/constants/src/openmct/object-types/object-types.ts @@ -1,54 +1,54 @@ import type { OpenMctObjectTypes } from '@hyped/telemetry-types'; export const openMctObjectTypes: OpenMctObjectTypes = [ - { - id: 'temperature', - name: 'Temperature', - icon: 'icon-telemetry', - }, - { - id: 'thermistor', - name: 'Thermistor', - icon: 'icon-telemetry', - }, - { - id: 'acceleration', - name: 'Acceleration', - icon: 'icon-telemetry', - }, - { - id: 'pressure', - name: 'Pressure', - icon: 'icon-telemetry', - }, - { - id: 'hall_effect', - name: 'Hall Effect', - icon: 'icon-telemetry', - }, - { - id: 'displacement', - name: 'Displacement', - icon: 'icon-telemetry', - }, - { - id: 'velocity', - name: 'Velocity', - icon: 'icon-telemetry', - }, - { - id: 'status', - name: 'status', - icon: 'icon-telemetry', - }, - { - id: 'keyence', - name: 'Keyence', - icon: 'icon-telemetry', - }, - { - id: 'brake_feedback', - name: 'Brake Feedback', - icon: 'icon-telemetry', - }, + { + id: 'temperature', + name: 'Temperature', + icon: 'icon-telemetry', + }, + { + id: 'thermistor', + name: 'Thermistor', + icon: 'icon-telemetry', + }, + { + id: 'acceleration', + name: 'Acceleration', + icon: 'icon-telemetry', + }, + { + id: 'pressure', + name: 'Pressure', + icon: 'icon-telemetry', + }, + { + id: 'hall_effect', + name: 'Hall Effect', + icon: 'icon-telemetry', + }, + { + id: 'displacement', + name: 'Displacement', + icon: 'icon-telemetry', + }, + { + id: 'velocity', + name: 'Velocity', + icon: 'icon-telemetry', + }, + { + id: 'status', + name: 'status', + icon: 'icon-telemetry', + }, + { + id: 'keyence', + name: 'Keyence', + icon: 'icon-telemetry', + }, + { + id: 'brake_feedback', + name: 'Brake Feedback', + icon: 'icon-telemetry', + }, ]; diff --git a/telemetry/packages/constants/src/pods/common.ts b/telemetry/packages/constants/src/pods/common.ts index 08410eb4..a0fe7177 100644 --- a/telemetry/packages/constants/src/pods/common.ts +++ b/telemetry/packages/constants/src/pods/common.ts @@ -1,82 +1,82 @@ export const accelerometerCommon = { - format: 'float', - type: 'motion', - unit: 'm/s²', - limits: { - critical: { - low: -150, - high: 150, - }, - }, - rms_noise: 16.25 * 10 ** -3, // RMS rms_noise [mg] at ±15g range (~ ±150m/s^2) - sampling_time: 500, + format: 'float', + type: 'motion', + unit: 'm/s²', + limits: { + critical: { + low: -150, + high: 150, + }, + }, + rms_noise: 16.25 * 10 ** -3, // RMS rms_noise [mg] at ±15g range (~ ±150m/s^2) + sampling_time: 500, } as const; // datasheet: https://www.st.com/en/mems-and-sensors/stts22h.html#st_description_sec-nav-tab export const thermistorCommon = { - format: 'float', - type: 'temperature', - unit: '°C', - limits: { - critical: { - low: -40, - high: 125, - }, - warning: { - low: 20, - high: 100, - }, - }, - rms_noise: 0.05, // RMS rms_noise - sampling_time: 500, // test value. Datasheet specifies clock frequency range as (10 - 400 kHz) + format: 'float', + type: 'temperature', + unit: '°C', + limits: { + critical: { + low: -40, + high: 125, + }, + warning: { + low: 20, + high: 100, + }, + }, + rms_noise: 0.05, // RMS rms_noise + sampling_time: 500, // test value. Datasheet specifies clock frequency range as (10 - 400 kHz) } as const; export const pressureCommon = { - format: 'float', - type: 'pressure', - unit: 'bar', - rms_noise: 1 * 10 ** -3, // placeholder estimate of 1 mbar, to be confirmed with datasheet when chosen sensor confirmed - sampling_time: 500, + format: 'float', + type: 'pressure', + unit: 'bar', + rms_noise: 1 * 10 ** -3, // placeholder estimate of 1 mbar, to be confirmed with datasheet when chosen sensor confirmed + sampling_time: 500, } as const; export const hallEffectCommon = { - format: 'float', - type: 'magnetism', - unit: 'A', - limits: { - critical: { - low: 0, - high: 500, - }, - }, - rms_noise: 0.5, // placeholder guesstimate, waiting on datasheet - sampling_time: 500, + format: 'float', + type: 'magnetism', + unit: 'A', + limits: { + critical: { + low: 0, + high: 500, + }, + }, + rms_noise: 0.5, // placeholder guesstimate, waiting on datasheet + sampling_time: 500, } as const; export const keyenceCommon = { - format: 'integer', - type: 'keyence', - unit: 'number of stripes', - limits: { - critical: { - low: 0, - high: 16, - }, - }, - rms_noise: 0, - sampling_time: 500, + format: 'integer', + type: 'keyence', + unit: 'number of stripes', + limits: { + critical: { + low: 0, + high: 16, + }, + }, + rms_noise: 0, + sampling_time: 500, } as const; export const levitationHeightCommon = { - format: 'float', - type: 'levitation', - unit: 'mm', - limits: { - critical: { - low: 0, - high: 100, - }, - }, - rms_noise: 0, // placeholder - sampling_time: 500, // placeholder + format: 'float', + type: 'levitation', + unit: 'mm', + limits: { + critical: { + low: 0, + high: 100, + }, + }, + rms_noise: 0, // placeholder + sampling_time: 500, // placeholder } as const; diff --git a/telemetry/packages/constants/src/pods/modes.ts b/telemetry/packages/constants/src/pods/modes.ts index 2c263ed3..a41318e6 100644 --- a/telemetry/packages/constants/src/pods/modes.ts +++ b/telemetry/packages/constants/src/pods/modes.ts @@ -1,9 +1,9 @@ -import { ACTIVE_STATES, PodStateType } from './states'; +import { ACTIVE_STATES, type PodStateType } from './states'; export const MODES = { - ALL_SYSTEMS_ON: 'ALL_SYSTEMS_ON', - LEVITATION_ONLY: 'LEVITATION_ONLY', - LIM_ONLY: 'LIM_ONLY', + ALL_SYSTEMS_ON: 'ALL_SYSTEMS_ON', + LEVITATION_ONLY: 'LEVITATION_ONLY', + LIM_ONLY: 'LIM_ONLY', }; export type ModeType = keyof typeof MODES; @@ -11,17 +11,17 @@ export type ModeType = keyof typeof MODES; type ModeStates = Record; export const MODE_EXCLUDED_STATES: ModeStates = { - ALL_SYSTEMS_ON: [], - LEVITATION_ONLY: [ - ACTIVE_STATES.READY_FOR_LAUNCH, - ACTIVE_STATES.ACCELERATE, - ACTIVE_STATES.LIM_BRAKE, - ACTIVE_STATES.FRICTION_BRAKE, - ACTIVE_STATES.BATTERY_RECHARGE, - ], - LIM_ONLY: [ - ACTIVE_STATES.READY_FOR_LEVITATION, - ACTIVE_STATES.BEGIN_LEVITATION, - ACTIVE_STATES.STOP_LEVITATION, - ], + ALL_SYSTEMS_ON: [], + LEVITATION_ONLY: [ + ACTIVE_STATES.READY_FOR_LAUNCH, + ACTIVE_STATES.ACCELERATE, + ACTIVE_STATES.LIM_BRAKE, + ACTIVE_STATES.FRICTION_BRAKE, + ACTIVE_STATES.BATTERY_RECHARGE, + ], + LIM_ONLY: [ + ACTIVE_STATES.READY_FOR_LEVITATION, + ACTIVE_STATES.BEGIN_LEVITATION, + ACTIVE_STATES.STOP_LEVITATION, + ], }; diff --git a/telemetry/packages/constants/src/pods/pods.ts b/telemetry/packages/constants/src/pods/pods.ts index 4231ec58..bc9f0b39 100644 --- a/telemetry/packages/constants/src/pods/pods.ts +++ b/telemetry/packages/constants/src/pods/pods.ts @@ -1,11 +1,11 @@ -import { Pod } from '@hyped/telemetry-types'; +import type { Pod } from '@hyped/telemetry-types'; import { - accelerometerCommon, - hallEffectCommon, - keyenceCommon, - pressureCommon, - thermistorCommon, - levitationHeightCommon, + accelerometerCommon, + hallEffectCommon, + keyenceCommon, + levitationHeightCommon, + pressureCommon, + thermistorCommon, } from './common'; export const POD_IDS = ['pod_1', 'pod_2024'] as const; @@ -13,867 +13,867 @@ export type PodId = (typeof POD_IDS)[number]; export type Pods = Record; export const pods: Pods = { - pod_1: { - id: 'pod_1', - name: 'Pod Ness', - operationMode: 'ALL_SYSTEMS_ON', - measurements: { - // ************************************ ACCELEROMETERS ************************************ // - accelerometer_1: { - name: 'Accelerometer 1', - key: 'accelerometer_1', - ...accelerometerCommon, - }, - accelerometer_2: { - name: 'Accelerometer 2', - key: 'accelerometer_2', - ...accelerometerCommon, - }, - accelerometer_3: { - name: 'Accelerometer 3', - key: 'accelerometer_3', - ...accelerometerCommon, - }, - accelerometer_4: { - name: 'Accelerometer 4', - key: 'accelerometer_4', - ...accelerometerCommon, - }, - accelerometer_avg: { - name: 'Accelerometer Average', - key: 'accelerometer_avg', - ...accelerometerCommon, - }, + pod_1: { + id: 'pod_1', + name: 'Pod Ness', + operationMode: 'ALL_SYSTEMS_ON', + measurements: { + // ************************************ ACCELEROMETERS ************************************ // + accelerometer_1: { + name: 'Accelerometer 1', + key: 'accelerometer_1', + ...accelerometerCommon, + }, + accelerometer_2: { + name: 'Accelerometer 2', + key: 'accelerometer_2', + ...accelerometerCommon, + }, + accelerometer_3: { + name: 'Accelerometer 3', + key: 'accelerometer_3', + ...accelerometerCommon, + }, + accelerometer_4: { + name: 'Accelerometer 4', + key: 'accelerometer_4', + ...accelerometerCommon, + }, + accelerometer_avg: { + name: 'Accelerometer Average', + key: 'accelerometer_avg', + ...accelerometerCommon, + }, - // ************************************ NAVIGATION ************************************ // - displacement: { - name: 'Displacement', - key: 'displacement', - format: 'float', - type: 'motion', - unit: 'm', - limits: { - critical: { - low: 0, - high: 100, - }, - }, - rms_noise: 0, - sampling_time: accelerometerCommon.sampling_time, - }, - velocity: { - name: 'Velocity', - key: 'velocity', - format: 'float', - type: 'motion', - unit: 'm/s', - limits: { - critical: { - low: 0, - high: 50, - }, - }, - rms_noise: accelerometerCommon.rms_noise, - sampling_time: accelerometerCommon.sampling_time, - }, - acceleration: { - name: 'Acceleration', - key: 'acceleration', - format: 'float', - type: 'motion', - unit: 'm/s²', - limits: { - critical: { - low: 0, - high: 5, - }, - }, - rms_noise: accelerometerCommon.rms_noise, - sampling_time: accelerometerCommon.sampling_time, - }, + // ************************************ NAVIGATION ************************************ // + displacement: { + name: 'Displacement', + key: 'displacement', + format: 'float', + type: 'motion', + unit: 'm', + limits: { + critical: { + low: 0, + high: 100, + }, + }, + rms_noise: 0, + sampling_time: accelerometerCommon.sampling_time, + }, + velocity: { + name: 'Velocity', + key: 'velocity', + format: 'float', + type: 'motion', + unit: 'm/s', + limits: { + critical: { + low: 0, + high: 50, + }, + }, + rms_noise: accelerometerCommon.rms_noise, + sampling_time: accelerometerCommon.sampling_time, + }, + acceleration: { + name: 'Acceleration', + key: 'acceleration', + format: 'float', + type: 'motion', + unit: 'm/s²', + limits: { + critical: { + low: 0, + high: 5, + }, + }, + rms_noise: accelerometerCommon.rms_noise, + sampling_time: accelerometerCommon.sampling_time, + }, - // ************************************ PRESSURE ************************************ // - pressure_back_pull: { - name: 'Pressure – Back Pull', - key: 'pressure_back_pull', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 5.5, - }, - warning: { - low: -0.19, - high: 5.2, - }, - }, - }, - pressure_front_pull: { - name: 'Pressure – Front Pull', - key: 'pressure_front_pull', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 5.5, - }, - warning: { - low: -0.19, - high: 5.2, - }, - }, - }, - pressure_front_push: { - name: 'Pressure – Front Push', - key: 'pressure_front_push', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 5.5, - }, - warning: { - low: -0.19, - high: 5.2, - }, - }, - }, - pressure_back_push: { - name: 'Pressure – Back Push', - key: 'pressure_back_push', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 5.5, - }, - warning: { - low: -0.19, - high: 5.2, - }, - }, - }, - pressure_brakes_reservoir: { - name: 'Pressure – Brakes Reservoir', - key: 'pressure_brakes_reservoir', - ...pressureCommon, - limits: { - critical: { - low: 3, - high: 7.4, - }, - warning: { - low: 3.5, - high: 6.9, - }, - }, - }, - pressure_active_suspension_reservoir: { - name: 'Pressure – Active Suspension Reservoir', - key: 'pressure_active_suspension_reservoir', - ...pressureCommon, - limits: { - critical: { - low: 3, - high: 7.4, - }, - warning: { - low: 3.5, - high: 6.9, - }, - }, - }, - pressure_front_brake: { - name: 'Pressure – Front Brake', - key: 'pressure_front_brake', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 4.2, - }, - warning: { - low: -0.19, - high: 4, - }, - }, - }, - pressure_back_brake: { - name: 'Pressure – Back Brake', - key: 'pressure_back_brake', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 4.2, - }, - warning: { - low: -0.19, - high: 4, - }, - }, - }, + // ************************************ PRESSURE ************************************ // + pressure_back_pull: { + name: 'Pressure – Back Pull', + key: 'pressure_back_pull', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 5.5, + }, + warning: { + low: -0.19, + high: 5.2, + }, + }, + }, + pressure_front_pull: { + name: 'Pressure – Front Pull', + key: 'pressure_front_pull', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 5.5, + }, + warning: { + low: -0.19, + high: 5.2, + }, + }, + }, + pressure_front_push: { + name: 'Pressure – Front Push', + key: 'pressure_front_push', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 5.5, + }, + warning: { + low: -0.19, + high: 5.2, + }, + }, + }, + pressure_back_push: { + name: 'Pressure – Back Push', + key: 'pressure_back_push', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 5.5, + }, + warning: { + low: -0.19, + high: 5.2, + }, + }, + }, + pressure_brakes_reservoir: { + name: 'Pressure – Brakes Reservoir', + key: 'pressure_brakes_reservoir', + ...pressureCommon, + limits: { + critical: { + low: 3, + high: 7.4, + }, + warning: { + low: 3.5, + high: 6.9, + }, + }, + }, + pressure_active_suspension_reservoir: { + name: 'Pressure – Active Suspension Reservoir', + key: 'pressure_active_suspension_reservoir', + ...pressureCommon, + limits: { + critical: { + low: 3, + high: 7.4, + }, + warning: { + low: 3.5, + high: 6.9, + }, + }, + }, + pressure_front_brake: { + name: 'Pressure – Front Brake', + key: 'pressure_front_brake', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 4.2, + }, + warning: { + low: -0.19, + high: 4, + }, + }, + }, + pressure_back_brake: { + name: 'Pressure – Back Brake', + key: 'pressure_back_brake', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 4.2, + }, + warning: { + low: -0.19, + high: 4, + }, + }, + }, - // ************************************ THERMISTORS ************************************ // - thermistor_1: { - name: 'Thermistor 1', - key: 'thermistor_1', - ...thermistorCommon, - }, - thermistor_2: { - name: 'Thermistor 2', - key: 'thermistor_2', - ...thermistorCommon, - }, - thermistor_3: { - name: 'Thermistor 3', - key: 'thermistor_3', - ...thermistorCommon, - }, - thermistor_4: { - name: 'Thermistor 4', - key: 'thermistor_4', - ...thermistorCommon, - }, - thermistor_5: { - name: 'Thermistor 5', - key: 'thermistor_5', - ...thermistorCommon, - }, - thermistor_6: { - name: 'Thermistor 6', - key: 'thermistor_6', - ...thermistorCommon, - }, - thermistor_7: { - name: 'Thermistor 7', - key: 'thermistor_7', - ...thermistorCommon, - }, - thermistor_8: { - name: 'Thermistor 8', - key: 'thermistor_8', - ...thermistorCommon, - }, - thermistor_9: { - name: 'Thermistor 9', - key: 'thermistor_9', - ...thermistorCommon, - }, - thermistor_10: { - name: 'Thermistor 10', - key: 'thermistor_10', - ...thermistorCommon, - }, - thermistor_11: { - name: 'Thermistor 11', - key: 'thermistor_11', - ...thermistorCommon, - }, - thermistor_12: { - name: 'Thermistor 12', - key: 'thermistor_12', - ...thermistorCommon, - }, - // thermistor_13: { - // name: 'Thermistor 13', - // key: 'thermistor_13', - // ...thermistorCommon, - // }, - // thermistor_14: { - // name: 'Thermistor 14', - // key: 'thermistor_14', - // ...thermistorCommon, - // }, - // thermistor_15: { - // name: 'Thermistor 15', - // key: 'thermistor_15', - // ...thermistorCommon, - // }, - // thermistor_16: { - // name: 'Thermistor 16', - // key: 'thermistor_16', - // ...thermistorCommon, - // }, + // ************************************ THERMISTORS ************************************ // + thermistor_1: { + name: 'Thermistor 1', + key: 'thermistor_1', + ...thermistorCommon, + }, + thermistor_2: { + name: 'Thermistor 2', + key: 'thermistor_2', + ...thermistorCommon, + }, + thermistor_3: { + name: 'Thermistor 3', + key: 'thermistor_3', + ...thermistorCommon, + }, + thermistor_4: { + name: 'Thermistor 4', + key: 'thermistor_4', + ...thermistorCommon, + }, + thermistor_5: { + name: 'Thermistor 5', + key: 'thermistor_5', + ...thermistorCommon, + }, + thermistor_6: { + name: 'Thermistor 6', + key: 'thermistor_6', + ...thermistorCommon, + }, + thermistor_7: { + name: 'Thermistor 7', + key: 'thermistor_7', + ...thermistorCommon, + }, + thermistor_8: { + name: 'Thermistor 8', + key: 'thermistor_8', + ...thermistorCommon, + }, + thermistor_9: { + name: 'Thermistor 9', + key: 'thermistor_9', + ...thermistorCommon, + }, + thermistor_10: { + name: 'Thermistor 10', + key: 'thermistor_10', + ...thermistorCommon, + }, + thermistor_11: { + name: 'Thermistor 11', + key: 'thermistor_11', + ...thermistorCommon, + }, + thermistor_12: { + name: 'Thermistor 12', + key: 'thermistor_12', + ...thermistorCommon, + }, + // thermistor_13: { + // name: 'Thermistor 13', + // key: 'thermistor_13', + // ...thermistorCommon, + // }, + // thermistor_14: { + // name: 'Thermistor 14', + // key: 'thermistor_14', + // ...thermistorCommon, + // }, + // thermistor_15: { + // name: 'Thermistor 15', + // key: 'thermistor_15', + // ...thermistorCommon, + // }, + // thermistor_16: { + // name: 'Thermistor 16', + // key: 'thermistor_16', + // ...thermistorCommon, + // }, - // ************************************ HALL EFFECTS ************************************ // - hall_effect_1: { - name: 'Hall Effect 1', - key: 'hall_effect_1', - ...hallEffectCommon, - }, - hall_effect_2: { - name: 'Hall Effect 2', - key: 'hall_effect_2', - ...hallEffectCommon, - }, + // ************************************ HALL EFFECTS ************************************ // + hall_effect_1: { + name: 'Hall Effect 1', + key: 'hall_effect_1', + ...hallEffectCommon, + }, + hall_effect_2: { + name: 'Hall Effect 2', + key: 'hall_effect_2', + ...hallEffectCommon, + }, - // ************************************ STATUS ************************************ // - brake_clamp_status: { - name: 'Brake Clamp Status', - key: 'brake_clamp_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'CLAMPED', - }, - { - value: 0, - string: 'UNCLAMPED', - }, - ], - }, - pod_raised_status: { - name: 'Pod Raised Status', - key: 'pod_raised_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'RAISED', - }, - { - value: 0, - string: 'LOWERED', - }, - ], - }, + // ************************************ STATUS ************************************ // + brake_clamp_status: { + name: 'Brake Clamp Status', + key: 'brake_clamp_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'CLAMPED', + }, + { + value: 0, + string: 'UNCLAMPED', + }, + ], + }, + pod_raised_status: { + name: 'Pod Raised Status', + key: 'pod_raised_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'RAISED', + }, + { + value: 0, + string: 'LOWERED', + }, + ], + }, - battery_status: { - name: 'Battery Status', - key: 'battery_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'HEALTHY', - }, - { - value: 0, - string: 'UNHEALTHY', - }, - ], - }, + battery_status: { + name: 'Battery Status', + key: 'battery_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'HEALTHY', + }, + { + value: 0, + string: 'UNHEALTHY', + }, + ], + }, - motor_controller_status: { - name: 'Motor Controller Status', - key: 'motor_controller_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'HEALTHY', - }, - { - value: 0, - string: 'UNHEALTHY', - }, - ], - }, + motor_controller_status: { + name: 'Motor Controller Status', + key: 'motor_controller_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'HEALTHY', + }, + { + value: 0, + string: 'UNHEALTHY', + }, + ], + }, - high_power_status: { - name: 'High Power Status', - key: 'high_power_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'ACTIVE', - }, - { - value: 0, - string: 'OFF', - }, - ], - }, + high_power_status: { + name: 'High Power Status', + key: 'high_power_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'ACTIVE', + }, + { + value: 0, + string: 'OFF', + }, + ], + }, - // ************************************ KEYENCE ************************************ // - keyence_1: { - name: 'Keyence 1', - key: 'keyence_1', - ...keyenceCommon, - }, - keyence_2: { - name: 'Keyence 2', - key: 'keyence_2', - ...keyenceCommon, - }, + // ************************************ KEYENCE ************************************ // + keyence_1: { + name: 'Keyence 1', + key: 'keyence_1', + ...keyenceCommon, + }, + keyence_2: { + name: 'Keyence 2', + key: 'keyence_2', + ...keyenceCommon, + }, - // ************************************ POWER ************************************ // - power_line_resistance: { - name: 'Power Line Resistance', - key: 'power_line_resistance', - format: 'integer', - type: 'resistance', - unit: 'kΩ', - limits: { - critical: { - low: 0, - high: 100, - }, - }, - rms_noise: 0.1, - sampling_time: 500, - }, - }, - }, + // ************************************ POWER ************************************ // + power_line_resistance: { + name: 'Power Line Resistance', + key: 'power_line_resistance', + format: 'integer', + type: 'resistance', + unit: 'kΩ', + limits: { + critical: { + low: 0, + high: 100, + }, + }, + rms_noise: 0.1, + sampling_time: 500, + }, + }, + }, - pod_2024: { - id: 'pod_2024', - name: 'Poddington', - operationMode: 'LEVITATION_ONLY', - measurements: { - // ************************************ ACCELEROMETERS ************************************ // - accelerometer_1: { - name: 'Accelerometer 1', - key: 'accelerometer_1', - ...accelerometerCommon, - }, - accelerometer_2: { - name: 'Accelerometer 2', - key: 'accelerometer_2', - ...accelerometerCommon, - }, - accelerometer_3: { - name: 'Accelerometer 3', - key: 'accelerometer_3', - ...accelerometerCommon, - }, - accelerometer_4: { - name: 'Accelerometer 4', - key: 'accelerometer_4', - ...accelerometerCommon, - }, - accelerometer_avg: { - name: 'Accelerometer Average', - key: 'accelerometer_avg', - ...accelerometerCommon, - }, + pod_2024: { + id: 'pod_2024', + name: 'Poddington', + operationMode: 'LEVITATION_ONLY', + measurements: { + // ************************************ ACCELEROMETERS ************************************ // + accelerometer_1: { + name: 'Accelerometer 1', + key: 'accelerometer_1', + ...accelerometerCommon, + }, + accelerometer_2: { + name: 'Accelerometer 2', + key: 'accelerometer_2', + ...accelerometerCommon, + }, + accelerometer_3: { + name: 'Accelerometer 3', + key: 'accelerometer_3', + ...accelerometerCommon, + }, + accelerometer_4: { + name: 'Accelerometer 4', + key: 'accelerometer_4', + ...accelerometerCommon, + }, + accelerometer_avg: { + name: 'Accelerometer Average', + key: 'accelerometer_avg', + ...accelerometerCommon, + }, - // ************************************ NAVIGATION ************************************ // - displacement: { - name: 'Displacement', - key: 'displacement', - format: 'float', - type: 'motion', - unit: 'm', - limits: { - critical: { - low: 0, - high: 100, - }, - }, - rms_noise: 0, - sampling_time: accelerometerCommon.sampling_time, - }, - velocity: { - name: 'Velocity', - key: 'velocity', - format: 'float', - type: 'motion', - unit: 'm/s', - limits: { - critical: { - low: 0, - high: 50, - }, - }, - rms_noise: 0, - sampling_time: accelerometerCommon.sampling_time, - }, - acceleration: { - name: 'Acceleration', - key: 'acceleration', - format: 'float', - type: 'motion', - unit: 'm/s²', - limits: { - critical: { - low: 0, - high: 5, - }, - }, - rms_noise: accelerometerCommon.rms_noise, - sampling_time: accelerometerCommon.sampling_time, - }, + // ************************************ NAVIGATION ************************************ // + displacement: { + name: 'Displacement', + key: 'displacement', + format: 'float', + type: 'motion', + unit: 'm', + limits: { + critical: { + low: 0, + high: 100, + }, + }, + rms_noise: 0, + sampling_time: accelerometerCommon.sampling_time, + }, + velocity: { + name: 'Velocity', + key: 'velocity', + format: 'float', + type: 'motion', + unit: 'm/s', + limits: { + critical: { + low: 0, + high: 50, + }, + }, + rms_noise: 0, + sampling_time: accelerometerCommon.sampling_time, + }, + acceleration: { + name: 'Acceleration', + key: 'acceleration', + format: 'float', + type: 'motion', + unit: 'm/s²', + limits: { + critical: { + low: 0, + high: 5, + }, + }, + rms_noise: accelerometerCommon.rms_noise, + sampling_time: accelerometerCommon.sampling_time, + }, - // ************************************ PRESSURE ************************************ // - pressure_back_pull: { - name: 'Pressure – Back Pull', - key: 'pressure_back_pull', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 5.5, - }, - warning: { - low: -0.19, - high: 5.2, - }, - }, - }, - pressure_front_pull: { - name: 'Pressure – Front Pull', - key: 'pressure_front_pull', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 5.5, - }, - warning: { - low: -0.19, - high: 5.2, - }, - }, - }, - pressure_front_push: { - name: 'Pressure – Front Push', - key: 'pressure_front_push', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 5.5, - }, - warning: { - low: -0.19, - high: 5.2, - }, - }, - }, - pressure_back_push: { - name: 'Pressure – Back Push', - key: 'pressure_back_push', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 5.5, - }, - warning: { - low: -0.19, - high: 5.2, - }, - }, - }, - pressure_brakes_reservoir: { - name: 'Pressure – Brakes Reservoir', - key: 'pressure_brakes_reservoir', - ...pressureCommon, - limits: { - critical: { - low: 3, - high: 7.4, - }, - warning: { - low: 3.5, - high: 6.9, - }, - }, - }, - pressure_active_suspension_reservoir: { - name: 'Pressure – Active Suspension Reservoir', - key: 'pressure_active_suspension_reservoir', - ...pressureCommon, - limits: { - critical: { - low: 3, - high: 7.4, - }, - warning: { - low: 3.5, - high: 6.9, - }, - }, - }, - pressure_front_brake: { - name: 'Pressure – Front Brake', - key: 'pressure_front_brake', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 4.2, - }, - warning: { - low: -0.19, - high: 4, - }, - }, - }, - pressure_back_brake: { - name: 'Pressure – Back Brake', - key: 'pressure_back_brake', - ...pressureCommon, - limits: { - critical: { - low: -0.2, - high: 4.2, - }, - warning: { - low: -0.19, - high: 4, - }, - }, - }, + // ************************************ PRESSURE ************************************ // + pressure_back_pull: { + name: 'Pressure – Back Pull', + key: 'pressure_back_pull', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 5.5, + }, + warning: { + low: -0.19, + high: 5.2, + }, + }, + }, + pressure_front_pull: { + name: 'Pressure – Front Pull', + key: 'pressure_front_pull', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 5.5, + }, + warning: { + low: -0.19, + high: 5.2, + }, + }, + }, + pressure_front_push: { + name: 'Pressure – Front Push', + key: 'pressure_front_push', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 5.5, + }, + warning: { + low: -0.19, + high: 5.2, + }, + }, + }, + pressure_back_push: { + name: 'Pressure – Back Push', + key: 'pressure_back_push', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 5.5, + }, + warning: { + low: -0.19, + high: 5.2, + }, + }, + }, + pressure_brakes_reservoir: { + name: 'Pressure – Brakes Reservoir', + key: 'pressure_brakes_reservoir', + ...pressureCommon, + limits: { + critical: { + low: 3, + high: 7.4, + }, + warning: { + low: 3.5, + high: 6.9, + }, + }, + }, + pressure_active_suspension_reservoir: { + name: 'Pressure – Active Suspension Reservoir', + key: 'pressure_active_suspension_reservoir', + ...pressureCommon, + limits: { + critical: { + low: 3, + high: 7.4, + }, + warning: { + low: 3.5, + high: 6.9, + }, + }, + }, + pressure_front_brake: { + name: 'Pressure – Front Brake', + key: 'pressure_front_brake', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 4.2, + }, + warning: { + low: -0.19, + high: 4, + }, + }, + }, + pressure_back_brake: { + name: 'Pressure – Back Brake', + key: 'pressure_back_brake', + ...pressureCommon, + limits: { + critical: { + low: -0.2, + high: 4.2, + }, + warning: { + low: -0.19, + high: 4, + }, + }, + }, - // ************************************ THERMISTORS ************************************ // - thermistor_1: { - name: 'Thermistor 1', - key: 'thermistor_1', - ...thermistorCommon, - }, - thermistor_2: { - name: 'Thermistor 2', - key: 'thermistor_2', - ...thermistorCommon, - }, - thermistor_3: { - name: 'Thermistor 3', - key: 'thermistor_3', - ...thermistorCommon, - }, - thermistor_4: { - name: 'Thermistor 4', - key: 'thermistor_4', - ...thermistorCommon, - }, - thermistor_5: { - name: 'Thermistor 5', - key: 'thermistor_5', - ...thermistorCommon, - }, - thermistor_6: { - name: 'Thermistor 6', - key: 'thermistor_6', - ...thermistorCommon, - }, - thermistor_7: { - name: 'Thermistor 7', - key: 'thermistor_7', - ...thermistorCommon, - }, - thermistor_8: { - name: 'Thermistor 8', - key: 'thermistor_8', - ...thermistorCommon, - }, - thermistor_9: { - name: 'Thermistor 9', - key: 'thermistor_9', - ...thermistorCommon, - }, - thermistor_10: { - name: 'Thermistor 10', - key: 'thermistor_10', - ...thermistorCommon, - }, - thermistor_11: { - name: 'Thermistor 11', - key: 'thermistor_11', - ...thermistorCommon, - }, - thermistor_12: { - name: 'Thermistor 12', - key: 'thermistor_12', - ...thermistorCommon, - }, + // ************************************ THERMISTORS ************************************ // + thermistor_1: { + name: 'Thermistor 1', + key: 'thermistor_1', + ...thermistorCommon, + }, + thermistor_2: { + name: 'Thermistor 2', + key: 'thermistor_2', + ...thermistorCommon, + }, + thermistor_3: { + name: 'Thermistor 3', + key: 'thermistor_3', + ...thermistorCommon, + }, + thermistor_4: { + name: 'Thermistor 4', + key: 'thermistor_4', + ...thermistorCommon, + }, + thermistor_5: { + name: 'Thermistor 5', + key: 'thermistor_5', + ...thermistorCommon, + }, + thermistor_6: { + name: 'Thermistor 6', + key: 'thermistor_6', + ...thermistorCommon, + }, + thermistor_7: { + name: 'Thermistor 7', + key: 'thermistor_7', + ...thermistorCommon, + }, + thermistor_8: { + name: 'Thermistor 8', + key: 'thermistor_8', + ...thermistorCommon, + }, + thermistor_9: { + name: 'Thermistor 9', + key: 'thermistor_9', + ...thermistorCommon, + }, + thermistor_10: { + name: 'Thermistor 10', + key: 'thermistor_10', + ...thermistorCommon, + }, + thermistor_11: { + name: 'Thermistor 11', + key: 'thermistor_11', + ...thermistorCommon, + }, + thermistor_12: { + name: 'Thermistor 12', + key: 'thermistor_12', + ...thermistorCommon, + }, - // ************************************ HALL EFFECTS ************************************ // - hall_effect_1: { - name: 'Hall Effect 1', - key: 'hall_effect_1', - ...hallEffectCommon, - }, - hall_effect_2: { - name: 'Hall Effect 2', - key: 'hall_effect_2', - ...hallEffectCommon, - }, + // ************************************ HALL EFFECTS ************************************ // + hall_effect_1: { + name: 'Hall Effect 1', + key: 'hall_effect_1', + ...hallEffectCommon, + }, + hall_effect_2: { + name: 'Hall Effect 2', + key: 'hall_effect_2', + ...hallEffectCommon, + }, - // ************************************ STATUS ************************************ // - brake_clamp_status: { - name: 'Brake Clamp Status', - key: 'brake_clamp_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'CLAMPED', - }, - { - value: 0, - string: 'UNCLAMPED', - }, - ], - }, - pod_raised_status: { - name: 'Pod Raised Status', - key: 'pod_raised_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'RAISED', - }, - { - value: 0, - string: 'LOWERED', - }, - ], - }, + // ************************************ STATUS ************************************ // + brake_clamp_status: { + name: 'Brake Clamp Status', + key: 'brake_clamp_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'CLAMPED', + }, + { + value: 0, + string: 'UNCLAMPED', + }, + ], + }, + pod_raised_status: { + name: 'Pod Raised Status', + key: 'pod_raised_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'RAISED', + }, + { + value: 0, + string: 'LOWERED', + }, + ], + }, - battery_status: { - name: 'Battery Status', - key: 'battery_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'HEALTHY', - }, - { - value: 0, - string: 'UNHEALTHY', - }, - ], - }, + battery_status: { + name: 'Battery Status', + key: 'battery_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'HEALTHY', + }, + { + value: 0, + string: 'UNHEALTHY', + }, + ], + }, - motor_controller_status: { - name: 'Motor Controller Status', - key: 'motor_controller_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'HEALTHY', - }, - { - value: 0, - string: 'UNHEALTHY', - }, - ], - }, + motor_controller_status: { + name: 'Motor Controller Status', + key: 'motor_controller_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'HEALTHY', + }, + { + value: 0, + string: 'UNHEALTHY', + }, + ], + }, - high_power_status: { - name: 'High Power Status', - key: 'high_power_status', - format: 'enum', - type: 'status', - unit: 'state', - enumerations: [ - { - value: 1, - string: 'ACTIVE', - }, - { - value: 0, - string: 'OFF', - }, - ], - }, + high_power_status: { + name: 'High Power Status', + key: 'high_power_status', + format: 'enum', + type: 'status', + unit: 'state', + enumerations: [ + { + value: 1, + string: 'ACTIVE', + }, + { + value: 0, + string: 'OFF', + }, + ], + }, - // ************************************ KEYENCE ************************************ // - keyence_1: { - name: 'Keyence 1', - key: 'keyence_1', - ...keyenceCommon, - }, - keyence_2: { - name: 'Keyence 2', - key: 'keyence_2', - ...keyenceCommon, - }, + // ************************************ KEYENCE ************************************ // + keyence_1: { + name: 'Keyence 1', + key: 'keyence_1', + ...keyenceCommon, + }, + keyence_2: { + name: 'Keyence 2', + key: 'keyence_2', + ...keyenceCommon, + }, - // ************************************ POWER ************************************ // - power_line_resistance: { - name: 'Power Line Resistance', - key: 'power_line_resistance', - format: 'integer', - type: 'resistance', - unit: 'kΩ', - limits: { - critical: { - low: 0, - high: 100, - }, - }, - rms_noise: 0.1, - sampling_time: 500, - }, + // ************************************ POWER ************************************ // + power_line_resistance: { + name: 'Power Line Resistance', + key: 'power_line_resistance', + format: 'integer', + type: 'resistance', + unit: 'kΩ', + limits: { + critical: { + low: 0, + high: 100, + }, + }, + rms_noise: 0.1, + sampling_time: 500, + }, - // ************************************ LEVITATION ************************************ // - levitation_height_1: { - name: 'Levitation Height 1', - key: 'levitation_height_1', - ...levitationHeightCommon, - }, - levitation_height_2: { - name: 'Levitation Height 2', - key: 'levitation_height_2', - ...levitationHeightCommon, - }, - levitation_height_3: { - name: 'Levitation Height 3', - key: 'levitation_height_3', - ...levitationHeightCommon, - }, - levitation_height_4: { - name: 'Levitation Height 4', - key: 'levitation_height_4', - ...levitationHeightCommon, - }, - levitation_height_lateral_1: { - name: 'Levitation Height Lateral 1', - key: 'levitation_height_lateral_1', - format: 'float', - type: 'levitation', - unit: 'mm', - limits: { - critical: { - low: 0, - high: 100, - }, - }, - rms_noise: 2, // from Time-of-Flight datasheet - sampling_time: 500, - }, - levitation_height_lateral_2: { - name: 'Levitation Height 2', - key: 'levitation_height_lateral_2', - format: 'float', - type: 'levitation', - unit: 'mm', - limits: { - critical: { - low: 0, - high: 100, - }, - }, - rms_noise: 2, // from Time-of-Flight datasheet - sampling_time: 500, - }, - }, - }, + // ************************************ LEVITATION ************************************ // + levitation_height_1: { + name: 'Levitation Height 1', + key: 'levitation_height_1', + ...levitationHeightCommon, + }, + levitation_height_2: { + name: 'Levitation Height 2', + key: 'levitation_height_2', + ...levitationHeightCommon, + }, + levitation_height_3: { + name: 'Levitation Height 3', + key: 'levitation_height_3', + ...levitationHeightCommon, + }, + levitation_height_4: { + name: 'Levitation Height 4', + key: 'levitation_height_4', + ...levitationHeightCommon, + }, + levitation_height_lateral_1: { + name: 'Levitation Height Lateral 1', + key: 'levitation_height_lateral_1', + format: 'float', + type: 'levitation', + unit: 'mm', + limits: { + critical: { + low: 0, + high: 100, + }, + }, + rms_noise: 2, // from Time-of-Flight datasheet + sampling_time: 500, + }, + levitation_height_lateral_2: { + name: 'Levitation Height 2', + key: 'levitation_height_lateral_2', + format: 'float', + type: 'levitation', + unit: 'mm', + limits: { + critical: { + low: 0, + high: 100, + }, + }, + rms_noise: 2, // from Time-of-Flight datasheet + sampling_time: 500, + }, + }, + }, }; diff --git a/telemetry/packages/constants/src/pods/states.ts b/telemetry/packages/constants/src/pods/states.ts index 0f221741..1494d399 100644 --- a/telemetry/packages/constants/src/pods/states.ts +++ b/telemetry/packages/constants/src/pods/states.ts @@ -1,55 +1,55 @@ export type PodStateType = keyof typeof ALL_POD_STATES; export const FAILURE_STATES = { - FAILURE_BRAKING: 'FAILURE_BRAKING', + FAILURE_BRAKING: 'FAILURE_BRAKING', } as const; export const PASSIVE_STATES = { - IDLE: 'IDLE', - CALIBRATE: 'CALIBRATE', - SAFE: 'SAFE', + IDLE: 'IDLE', + CALIBRATE: 'CALIBRATE', + SAFE: 'SAFE', } as const; export const ACTIVE_STATES = { - PRECHARGE: 'PRECHARGE', - READY_FOR_LEVITATION: 'READY_FOR_LEVITATION', - BEGIN_LEVITATION: 'BEGIN_LEVITATION', - READY_FOR_LAUNCH: 'READY_FOR_LAUNCH', - ACCELERATE: 'ACCELERATE', - LIM_BRAKE: 'LIM_BRAKE', - FRICTION_BRAKE: 'FRICTION_BRAKE', - STOP_LEVITATION: 'STOP_LEVITATION', - STOPPED: 'STOPPED', - BATTERY_RECHARGE: 'BATTERY_RECHARGE', - CAPACITOR_DISCHARGE: 'CAPACITOR_DISCHARGE', + PRECHARGE: 'PRECHARGE', + READY_FOR_LEVITATION: 'READY_FOR_LEVITATION', + BEGIN_LEVITATION: 'BEGIN_LEVITATION', + READY_FOR_LAUNCH: 'READY_FOR_LAUNCH', + ACCELERATE: 'ACCELERATE', + LIM_BRAKE: 'LIM_BRAKE', + FRICTION_BRAKE: 'FRICTION_BRAKE', + STOP_LEVITATION: 'STOP_LEVITATION', + STOPPED: 'STOPPED', + BATTERY_RECHARGE: 'BATTERY_RECHARGE', + CAPACITOR_DISCHARGE: 'CAPACITOR_DISCHARGE', } as const; export const NULL_STATES = { - UNKNOWN: 'UNKNOWN', + UNKNOWN: 'UNKNOWN', } as const; export const ALL_POD_STATES = { - ...FAILURE_STATES, - ...PASSIVE_STATES, - ...ACTIVE_STATES, - ...NULL_STATES, + ...FAILURE_STATES, + ...PASSIVE_STATES, + ...ACTIVE_STATES, + ...NULL_STATES, }; export const ALL_POD_STATE_TYPES = [ - 'FAILURE', - 'PASSIVE', - 'ACTIVE', - 'NULL', + 'FAILURE', + 'PASSIVE', + 'ACTIVE', + 'NULL', ] as const; export type PodStateCategoryType = (typeof ALL_POD_STATE_TYPES)[number]; export const getStateType = ( - state: string, + state: string, ): (typeof ALL_POD_STATE_TYPES)[number] => { - if (FAILURE_STATES[state as keyof typeof FAILURE_STATES]) return 'FAILURE'; - if (PASSIVE_STATES[state as keyof typeof PASSIVE_STATES]) return 'PASSIVE'; - if (ACTIVE_STATES[state as keyof typeof ACTIVE_STATES]) return 'ACTIVE'; - if (NULL_STATES[state as keyof typeof NULL_STATES]) return 'NULL'; - throw new Error(`Unknown state: ${state}`); + if (FAILURE_STATES[state as keyof typeof FAILURE_STATES]) return 'FAILURE'; + if (PASSIVE_STATES[state as keyof typeof PASSIVE_STATES]) return 'PASSIVE'; + if (ACTIVE_STATES[state as keyof typeof ACTIVE_STATES]) return 'ACTIVE'; + if (NULL_STATES[state as keyof typeof NULL_STATES]) return 'NULL'; + throw new Error(`Unknown state: ${state}`); }; diff --git a/telemetry/packages/constants/src/socket/getMeasurementRoomName.ts b/telemetry/packages/constants/src/socket/getMeasurementRoomName.ts index 0cd4f068..5cb3f83e 100644 --- a/telemetry/packages/constants/src/socket/getMeasurementRoomName.ts +++ b/telemetry/packages/constants/src/socket/getMeasurementRoomName.ts @@ -1,6 +1,6 @@ export function getMeasurementRoomName( - podId: string, - measurementKey: string, + podId: string, + measurementKey: string, ): string { - return `${podId}/measurement/${measurementKey}`; + return `${podId}/measurement/${measurementKey}`; } diff --git a/telemetry/packages/constants/src/socket/index.ts b/telemetry/packages/constants/src/socket/index.ts index 397f46b1..3e89cfe9 100644 --- a/telemetry/packages/constants/src/socket/index.ts +++ b/telemetry/packages/constants/src/socket/index.ts @@ -2,10 +2,10 @@ export const MEASUREMENT_EVENT = 'Measurement'; export const FAULT_EVENT = 'Fault'; export const EVENTS = { - SUBSCRIBE_TO_MEASUREMENT: 'SubscribeToMeasurement', - UNSUBSCRIBE_FROM_MEASUREMENT: 'UnsubscribeFromMeasurement', - SUBSCRIBE_TO_FAULTS: 'SubscribeToFaults', - UNSUBSCRIBE_FROM_FAULTS: 'UnsubscribeFromFaults', + SUBSCRIBE_TO_MEASUREMENT: 'SubscribeToMeasurement', + UNSUBSCRIBE_FROM_MEASUREMENT: 'UnsubscribeFromMeasurement', + SUBSCRIBE_TO_FAULTS: 'SubscribeToFaults', + UNSUBSCRIBE_FROM_FAULTS: 'UnsubscribeFromFaults', }; export { getMeasurementRoomName } from './getMeasurementRoomName'; diff --git a/telemetry/packages/constants/tsconfig.json b/telemetry/packages/constants/tsconfig.json index 310dbb99..8b4d196b 100644 --- a/telemetry/packages/constants/tsconfig.json +++ b/telemetry/packages/constants/tsconfig.json @@ -1,12 +1,12 @@ { - "extends": "@hyped/tsconfig/base.json", - "compilerOptions": { - "outDir": "./dist", - "lib": ["esnext"], - "importHelpers": true, - "sourceMap": true, - "rootDir": "./src" - }, - "include": ["src"], - "exclude": ["node_modules"] + "extends": "@hyped/tsconfig/base.json", + "compilerOptions": { + "outDir": "./dist", + "lib": ["esnext"], + "importHelpers": true, + "sourceMap": true, + "rootDir": "./src" + }, + "include": ["src"], + "exclude": ["node_modules"] } diff --git a/telemetry/packages/e2e-tests/biome.json b/telemetry/packages/e2e-tests/biome.json new file mode 100644 index 00000000..784b6d85 --- /dev/null +++ b/telemetry/packages/e2e-tests/biome.json @@ -0,0 +1,3 @@ +{ + "extends": ["../../biome.json"] +} diff --git a/telemetry/packages/e2e-tests/lib/mqtt.ts b/telemetry/packages/e2e-tests/lib/mqtt.ts index 8f0af886..17626784 100644 --- a/telemetry/packages/e2e-tests/lib/mqtt.ts +++ b/telemetry/packages/e2e-tests/lib/mqtt.ts @@ -1,7 +1,7 @@ import mqtt from 'mqtt'; export const client = mqtt.connect( - process.env.E2E_TEST_MQTT_BROKER || 'mqtt://localhost:1883', + process.env.E2E_TEST_MQTT_BROKER || 'mqtt://localhost:1883', ); type MqttMessageValidation = (receivedTopic: string, message: Buffer) => void; @@ -13,45 +13,45 @@ type MqttMessageValidation = (receivedTopic: string, message: Buffer) => void; * @param timeout Time to wait (in ms) before giving up */ export async function validateMqttMessage( - trigger: () => void, - validate: MqttMessageValidation, - timeout = 1000, + trigger: () => void, + validate: MqttMessageValidation, + timeout = 1000, ): Promise { - const receivedMessages: { topic: string; message: Buffer }[] = []; - - return new Promise(async (resolve, reject) => { - const client = mqtt.connect( - process.env.E2E_TEST_MQTT_BROKER || 'mqtt://localhost:1883', - ); - - client.on('connect', async () => { - await client.subscribeAsync('#'); - - // Handle incoming messages - client.on('message', (receivedTopic, message) => { - receivedMessages.push({ topic: receivedTopic, message }); - }); - - trigger(); - - // Check that the message is in the received messages - const interval = setInterval(() => { - for (const receivedMessage of receivedMessages) { - try { - validate(receivedMessage.topic, receivedMessage.message); - clearInterval(interval); - client.end(); - resolve(); - } catch (e) { - // Ignore errors - } - } - }, 100); - }); - - // Timeout if the message is not received - setTimeout(() => { - reject(new Error(`Timeout waiting for message.`)); - }, timeout); - }); + const receivedMessages: { topic: string; message: Buffer }[] = []; + + return new Promise(async (resolve, reject) => { + const client = mqtt.connect( + process.env.E2E_TEST_MQTT_BROKER || 'mqtt://localhost:1883', + ); + + client.on('connect', async () => { + await client.subscribeAsync('#'); + + // Handle incoming messages + client.on('message', (receivedTopic, message) => { + receivedMessages.push({ topic: receivedTopic, message }); + }); + + trigger(); + + // Check that the message is in the received messages + const interval = setInterval(() => { + for (const receivedMessage of receivedMessages) { + try { + validate(receivedMessage.topic, receivedMessage.message); + clearInterval(interval); + client.end(); + resolve(); + } catch (e) { + // Ignore errors + } + } + }, 100); + }); + + // Timeout if the message is not received + setTimeout(() => { + reject(new Error(`Timeout waiting for message.`)); + }, timeout); + }); } diff --git a/telemetry/packages/e2e-tests/package.json b/telemetry/packages/e2e-tests/package.json index 61f48fe6..e6164751 100644 --- a/telemetry/packages/e2e-tests/package.json +++ b/telemetry/packages/e2e-tests/package.json @@ -1,19 +1,19 @@ { - "name": "@hyped/e2e-tests", - "version": "1.0.0", - "description": "End-to-end tests for HYPED Telemetry", - "main": "index.js", - "scripts": { - "e2e:test": "playwright test" - }, - "dependencies": { - "@hyped/telemetry-server": "workspace:*", - "@hyped/telemetry-ui": "workspace:*", - "mqtt": "^5.5.0" - }, - "devDependencies": { - "@hyped/tsconfig": "workspace:*", - "@playwright/test": "^1.42.1", - "@types/node": "^20.11.28" - } + "name": "@hyped/e2e-tests", + "version": "1.0.0", + "description": "End-to-end tests for HYPED Telemetry", + "main": "index.js", + "scripts": { + "e2e:test": "playwright test" + }, + "dependencies": { + "@hyped/telemetry-server": "workspace:*", + "@hyped/telemetry-ui": "workspace:*", + "mqtt": "^5.5.0" + }, + "devDependencies": { + "@hyped/tsconfig": "workspace:*", + "@playwright/test": "^1.42.1", + "@types/node": "^20.11.28" + } } diff --git a/telemetry/packages/e2e-tests/playwright.config.ts b/telemetry/packages/e2e-tests/playwright.config.ts index 92dbb44c..4ac81201 100644 --- a/telemetry/packages/e2e-tests/playwright.config.ts +++ b/telemetry/packages/e2e-tests/playwright.config.ts @@ -10,48 +10,48 @@ import { defineConfig, devices } from '@playwright/test'; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './tests', - /* Run tests in files in parallel */ - fullyParallel: true, - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, - /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Base URL to use in actions like `await page.goto('/')`. */ - // baseURL: 'http://127.0.0.1:3000', + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', - }, + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, - /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - }, + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, - { - name: 'firefox', - use: { ...devices['Desktop Firefox'] }, - }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, - { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, - }, - ], + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], - /* Run your local dev server before starting the tests */ - webServer: { - command: 'cd ../../ && pnpm dev:test', // run from the root of the monorepo - url: 'http://localhost:5173', - reuseExistingServer: !process.env.CI, - }, + /* Run your local dev server before starting the tests */ + webServer: { + command: 'cd ../../ && pnpm dev:test', // run from the root of the monorepo + url: 'http://localhost:5173', + reuseExistingServer: !process.env.CI, + }, }); diff --git a/telemetry/packages/e2e-tests/tests/example.spec.ts b/telemetry/packages/e2e-tests/tests/example.spec.ts index 52252180..251c4d71 100644 --- a/telemetry/packages/e2e-tests/tests/example.spec.ts +++ b/telemetry/packages/e2e-tests/tests/example.spec.ts @@ -1,23 +1,23 @@ -import { test, expect } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { client, validateMqttMessage } from '../lib/mqtt'; test('has title', async ({ page }) => { - await page.goto('http://localhost:5173'); + await page.goto('http://localhost:5173'); - await expect(page).toHaveTitle('HYPED24 | Telemetry'); + await expect(page).toHaveTitle('HYPED24 | Telemetry'); }); test('example mqtt test', async () => { - await validateMqttMessage( - // Pass in the function which will trigger the MQTT message to be sent. - // For example, this could be pushing a button on the GUI. - () => { - client.publish('hello', 'world'); - }, - // The validation function. Here you can validate that the topic and message body received is as expected. - (topic, message) => { - expect(topic).toBe('hello'); - expect(message.toString()).toBe('world'); - }, - ); + await validateMqttMessage( + // Pass in the function which will trigger the MQTT message to be sent. + // For example, this could be pushing a button on the GUI. + () => { + client.publish('hello', 'world'); + }, + // The validation function. Here you can validate that the topic and message body received is as expected. + (topic, message) => { + expect(topic).toBe('hello'); + expect(message.toString()).toBe('world'); + }, + ); }); diff --git a/telemetry/packages/e2e-tests/tsconfig.json b/telemetry/packages/e2e-tests/tsconfig.json index 956c40ff..20fb9de9 100644 --- a/telemetry/packages/e2e-tests/tsconfig.json +++ b/telemetry/packages/e2e-tests/tsconfig.json @@ -1,6 +1,6 @@ { - "extends": "@hyped/tsconfig/base.json", - "compilerOptions": { - "jsx": "react", - } + "extends": "@hyped/tsconfig/base.json", + "compilerOptions": { + "jsx": "react" + } } diff --git a/telemetry/packages/eslint-config/README.md b/telemetry/packages/eslint-config/README.md deleted file mode 100644 index 2d34cea6..00000000 --- a/telemetry/packages/eslint-config/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# HYPED ESLint Config - -This package contains the ESLint configuration used by HYPED. diff --git a/telemetry/packages/eslint-config/basic.js b/telemetry/packages/eslint-config/basic.js deleted file mode 100644 index 28caa68c..00000000 --- a/telemetry/packages/eslint-config/basic.js +++ /dev/null @@ -1,28 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - extends: [ - 'eslint:recommended', - // This isn't working properly. Getting an error that I can't figure out. - // 'eslint-config-turbo', - 'plugin:@typescript-eslint/recommended', - 'plugin:@typescript-eslint/recommended-requiring-type-checking', - 'prettier', - ], - parser: '@typescript-eslint/parser', - plugins: ['only-warn', '@typescript-eslint'], - ignorePatterns: [ - '**/dist/**/*', - '**/node_modules/**/*', - '.eslintrc.js', - '.eslintrc.cjs', - ], - rules: { - 'no-console': 'error', - // We can tighten up the below rules later. They're not worth the effort at the moment. - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - }, - root: true, -}; diff --git a/telemetry/packages/eslint-config/package.json b/telemetry/packages/eslint-config/package.json deleted file mode 100644 index 6521db11..00000000 --- a/telemetry/packages/eslint-config/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "@hyped/eslint-config", - "version": "0.0.0", - "private": true, - "files": [ - "basic.js", - "react.js" - ], - "dependencies": { - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", - "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-config-turbo": "^1.12.5", - "eslint-plugin-only-warn": "^1.1.0", - "eslint-plugin-react-hooks": "^4.6.0", - "typescript": "^5.4.2" - } -} diff --git a/telemetry/packages/eslint-config/react.js b/telemetry/packages/eslint-config/react.js deleted file mode 100644 index 9d5c2b4d..00000000 --- a/telemetry/packages/eslint-config/react.js +++ /dev/null @@ -1,23 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - extends: [ - './basic.js', - 'plugin:react/recommended', - 'plugin:react-hooks/recommended', - ], - plugins: ['react', 'react-hooks'], - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - settings: { - react: { - version: 'detect', - }, - }, - rules: { - 'react/react-in-jsx-scope': 'off', - 'react/jsx-uses-react': 'off', - }, -}; diff --git a/telemetry/packages/fake/.eslintrc.js b/telemetry/packages/fake/.eslintrc.js deleted file mode 100644 index acab2de9..00000000 --- a/telemetry/packages/fake/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ['@hyped/eslint-config/basic.js'], - parserOptions: { - project: true, - tsconfigRootDir: __dirname, - }, -}; diff --git a/telemetry/packages/fake/biome.json b/telemetry/packages/fake/biome.json new file mode 100644 index 00000000..784b6d85 --- /dev/null +++ b/telemetry/packages/fake/biome.json @@ -0,0 +1,3 @@ +{ + "extends": ["../../biome.json"] +} diff --git a/telemetry/packages/fake/package.json b/telemetry/packages/fake/package.json index 85263ad0..f08bab65 100644 --- a/telemetry/packages/fake/package.json +++ b/telemetry/packages/fake/package.json @@ -1,24 +1,23 @@ { - "name": "@hyped/telemetry-fake", - "private": true, - "version": "0.0.1", - "scripts": { - "dev:test": "node dist/index.js", - "build": "tsdx build", - "build:fake": "tsdx build", - "lint": "eslint \"src/**/*.ts\" --max-warnings 0 --report-unused-disable-directives", - "lint:fix": "eslint \"src/**/*.ts\" --fix" - }, - "dependencies": { - "@hyped/telemetry-constants": "workspace:*", - "mqtt": "^5.3.6", - "tslib": "^2.5.3" - }, - "devDependencies": { - "@hyped/eslint-config": "workspace:*", - "@hyped/telemetry-types": "workspace:*", - "@hyped/tsconfig": "workspace:*", - "tsdx": "^0.14.1", - "typescript": "^5.3.3" - } + "name": "@hyped/telemetry-fake", + "private": true, + "version": "0.0.1", + "scripts": { + "dev:test": "node dist/index.js", + "build": "dts build", + "build:fake": "dts build", + "lint": "biome lint .", + "lint:fix": "biome check --write ." + }, + "dependencies": { + "@hyped/telemetry-constants": "workspace:*", + "mqtt": "^5.3.6", + "tslib": "^2.5.3" + }, + "devDependencies": { + "@hyped/telemetry-types": "workspace:*", + "@hyped/tsconfig": "workspace:*", + "dts-cli": "^2.0.5", + "typescript": "^5.3.3" + } } diff --git a/telemetry/packages/fake/src/base.ts b/telemetry/packages/fake/src/base.ts index e4f7b4d1..0a4de4ee 100644 --- a/telemetry/packages/fake/src/base.ts +++ b/telemetry/packages/fake/src/base.ts @@ -1,67 +1,67 @@ -import { Limits } from '@hyped/telemetry-types'; -import { LiveReading, Readings } from './types'; +import type { Limits } from '@hyped/telemetry-types'; +import type { LiveReading, Readings } from './types'; import { Utilities } from './utils'; export abstract class Sensor { - // Define static objects, updated each timestep // - // Records the actual time each sensor should be sampled next - // This object refers to sensors' sampling times to monitor the next time for each sensors' reading (in real time) - public static nextSamplingTimes: Record; - // Records whether each sensor has been sampled at the current time with a boolean flag for each - public static isSampled: Record = {}; - // Stores most recent sensor readings for all sensors, accessible by all sensors - // Null is used to indicate that the sensor has not been sampled at the current time - public static lastReadings: Record = {}; + // Define static objects, updated each timestep // + // Records the actual time each sensor should be sampled next + // This object refers to sensors' sampling times to monitor the next time for each sensors' reading (in real time) + public static nextSamplingTimes: Record; + // Records whether each sensor has been sampled at the current time with a boolean flag for each + public static isSampled: Record = {}; + // Stores most recent sensor readings for all sensors, accessible by all sensors + // Null is used to indicate that the sensor has not been sampled at the current time + public static lastReadings: Record = {}; - // Sensor properties - readonly type: string; // sensor type (same as the name of the object in sensorData) - readonly format: 'float' | 'integer'; // for random ternary logic (keyence is integer, rest are float) - readonly limits: Limits; - readonly rms_noise: number; - readonly delta_t: number; + // Sensor properties + readonly type: string; // sensor type (same as the name of the object in sensorData) + readonly format: 'float' | 'integer'; // for random ternary logic (keyence is integer, rest are float) + readonly limits: Limits; + readonly rms_noise: number; + readonly delta_t: number; - // Variable sensor data - protected time: number; // current time in seconds + // Variable sensor data + protected time: number; // current time in seconds - // Extract relevant properties from sensor data entries - constructor({ - type, - format, - limits, - rms_noise, - sampling_time, - readings, - }: LiveReading) { - Object.assign(this, { - type, - format, - limits, - rms_noise, - }); - this.delta_t = sampling_time / 1000; // convert ms to s - this.time = 0; - // Add initial sensor values to global readings object - Sensor.lastReadings[this.type] = readings; - } + // Extract relevant properties from sensor data entries + constructor({ + type, + format, + limits, + rms_noise, + sampling_time, + readings, + }: LiveReading) { + Object.assign(this, { + type, + format, + limits, + rms_noise, + }); + this.delta_t = sampling_time / 1000; // convert ms to s + this.time = 0; + // Add initial sensor values to global readings object + Sensor.lastReadings[this.type] = readings; + } - /** - * Main data gen method shared by all sensors - * Returns the Readings object, filtered into the values to be published with MQTT - * For motion, only acceleration, velocitity and displacement are uploaded - * Accelerometers are used to estimate readings, then these values are - * propagated to the three variables above - * @param t time in seconds - */ - abstract getData(t: number): Readings; + /** + * Main data gen method shared by all sensors + * Returns the Readings object, filtered into the values to be published with MQTT + * For motion, only acceleration, velocitity and displacement are uploaded + * Accelerometers are used to estimate readings, then these values are + * propagated to the three variables above + * @param t time in seconds + */ + abstract getData(t: number): Readings; - getRandomData(readings: Readings): Readings { - for (const unit in readings) { - readings[unit] = Utilities.getRandomValue( - readings[unit], - this.rms_noise, - this.format, - ); - } - return readings; - } + getRandomData(readings: Readings): Readings { + for (const unit in readings) { + readings[unit] = Utilities.getRandomValue( + readings[unit], + this.rms_noise, + this.format, + ); + } + return readings; + } } diff --git a/telemetry/packages/fake/src/config.ts b/telemetry/packages/fake/src/config.ts index f837b424..94c84f77 100644 --- a/telemetry/packages/fake/src/config.ts +++ b/telemetry/packages/fake/src/config.ts @@ -1,6 +1,6 @@ import { pods } from '@hyped/telemetry-constants'; -import { Pod, RangeMeasurement } from '@hyped/telemetry-types'; -import { LiveReading, SensorData } from './types'; +import type { Pod, RangeMeasurement } from '@hyped/telemetry-types'; +import type { LiveReading, SensorData } from './types'; type podID = keyof typeof pods; @@ -8,16 +8,16 @@ type podID = keyof typeof pods; * Extracts and categorises relevant sensor data */ const filterMeasurements = (id: podID) => { - const pod = Object.values(pods).find((pod: Pod) => pod.id === id) as Pod; - const filteredData = {} as Record; - // - Object.entries(pod.measurements).forEach(([key, meas]) => { - if (meas.format !== 'enum') { - filteredData[key] = meas; - filteredData[key].name = key.replace(/_[^_]*\d$/, ''); - } - }); - return filteredData; + const pod = Object.values(pods).find((pod: Pod) => pod.id === id) as Pod; + const filteredData = {} as Record; + // + for (const [key, meas] of Object.entries(pod.measurements)) { + if (meas.format !== 'enum') { + filteredData[key] = meas; + filteredData[key].name = key.replace(/_[^_]*\d$/, ''); + } + } + return filteredData; }; export const measurements = filterMeasurements('pod_2024'); @@ -29,32 +29,32 @@ export const measurements = filterMeasurements('pod_2024'); * @returns initial value for a given sensor/measurement */ const getInitialValue = (data: RangeMeasurement): number => { - // Define initial conditions - const initialVals: Record = { - accelerometer: 0, - acceleration: 0, - displacement: 0, - // velocity: measurements.velocity.limits.critical.high * 0.1, // initial velocity > 0 for continuity of logistic function - velocity: 0.3, // m/s (this aligns closely with logistic curve y-intercept) - pressure: data.name.endsWith('reservoir') ? 5 : 1, - thermistor: 25, - keyence: 0, - hall_effect: 0, - levitation_height: 0, - power_line_resistance: 10, - }; + // Define initial conditions + const initialVals: Record = { + accelerometer: 0, + acceleration: 0, + displacement: 0, + // velocity: measurements.velocity.limits.critical.high * 0.1, // initial velocity > 0 for continuity of logistic function + velocity: 0.3, // m/s (this aligns closely with logistic curve y-intercept) + pressure: data.name.endsWith('reservoir') ? 5 : 1, + thermistor: 25, + keyence: 0, + hall_effect: 0, + levitation_height: 0, + power_line_resistance: 10, + }; - // Set initial value based on sensor types defined above - if (Object.prototype.hasOwnProperty.call(initialVals, data.name)) { - return initialVals[data.name]; - } else if (data.name.startsWith('pressure')) { - // Pressure gauges are subdivided into push, pull, brake and reservoir with different initial values - return initialVals.pressure; - } else { - // If the sensor is not recognised, return a random value within the critical limits - const { low, high } = data.limits.critical; - return Math.floor(Math.random() * (high - low)) + low; - } + // Set initial value based on sensor types defined above + if (Object.prototype.hasOwnProperty.call(initialVals, data.name)) { + return initialVals[data.name]; + } + if (data.name.startsWith('pressure')) { + // Pressure gauges are subdivided into push, pull, brake and reservoir with different initial values + return initialVals.pressure; + } + // If the sensor is not recognised, return a random value within the critical limits + const { low, high } = data.limits.critical; + return Math.floor(Math.random() * (high - low)) + low; }; /** @@ -62,37 +62,38 @@ const getInitialValue = (data: RangeMeasurement): number => { * Groups sensors by data source by replacing sensor type with group's type */ export const sensorData: SensorData = Object.fromEntries( - Object.values(measurements) - .reduce( - (acc, sensor): any => { - if (!acc.seen) acc.seen = new Set(); - // Check if the sensor key has already been processed - if (!acc.seen.has(sensor.type)) { - acc.seen.add(sensor.type); - // Add one key value pair for each sensor type - // Each type holds all data on its constituent sensors - acc.entries.push([sensor.type, sensor]); - } - return acc; - }, - { seen: new Set(), entries: [] as [string, RangeMeasurement][] }, - ) - .entries // Set new readings property to the sensors' initial conditions - .map(([name, data]: [string, RangeMeasurement]) => [ - name, - { - ...data, - // Create object with a key-value pair for each measurement of a given sensor type - readings: Object.fromEntries( - Object.keys(measurements) - .filter( - (name) => - !name.endsWith('avg') && measurements[name].type == data.type, - ) - .map((el) => [el, getInitialValue(measurements[el])]), - ), - } as LiveReading, - ]), + Object.values(measurements) + .reduce( + // biome-ignore lint/suspicious/noExplicitAny: + (acc, sensor): any => { + if (!acc.seen) acc.seen = new Set(); + // Check if the sensor key has already been processed + if (!acc.seen.has(sensor.type)) { + acc.seen.add(sensor.type); + // Add one key value pair for each sensor type + // Each type holds all data on its constituent sensors + acc.entries.push([sensor.type, sensor]); + } + return acc; + }, + { seen: new Set(), entries: [] as [string, RangeMeasurement][] }, + ) + .entries // Set new readings property to the sensors' initial conditions + .map(([name, data]: [string, RangeMeasurement]) => [ + name, + { + ...data, + // Create object with a key-value pair for each measurement of a given sensor type + readings: Object.fromEntries( + Object.keys(measurements) + .filter( + (name) => + !name.endsWith('avg') && measurements[name].type === data.type, + ) + .map((el) => [el, getInitialValue(measurements[el])]), + ), + } as LiveReading, + ]), ); // Parameter storing distance of track, once finish point is reached program will end diff --git a/telemetry/packages/fake/src/index.ts b/telemetry/packages/fake/src/index.ts index 1f1689ec..15461d7f 100644 --- a/telemetry/packages/fake/src/index.ts +++ b/telemetry/packages/fake/src/index.ts @@ -1,3 +1,4 @@ +import { sensorData } from './config'; /** * Main file which initialises the generation of the data series and uploads to GUI in real time. * @param runTime (CLI) simulation time in ms (not real time, based on sensor timesteps) @@ -7,23 +8,22 @@ * i.e. simulate all sensors */ import { SensorManager } from './sensorManager'; -import { sensorData } from './config'; const args = process.argv.slice(2); -const shouldRandomise = args.includes('--random') ? true : false; +const shouldRandomise = !!args.includes('--random'); // Filter for user-defined specific sensors, otherwise simulate all Object.keys(sensorData).filter((sensor) => args.includes(sensor)); // Ensure input sensor options are valid and format them appropriately const sensorsToRun = args.includes('--specific') - ? args - .slice(args.indexOf('--specific') + 1) - .map((s: string) => s.toLowerCase()) - .filter((s: string) => - Object.prototype.hasOwnProperty.call(sensorData, s), - ) - : Object.keys(sensorData); + ? args + .slice(args.indexOf('--specific') + 1) + .map((s: string) => s.toLowerCase()) + .filter((s: string) => + Object.prototype.hasOwnProperty.call(sensorData, s), + ) + : Object.keys(sensorData); // Instantiate sensor manager const sensorMgmt = new SensorManager(sensorsToRun); diff --git a/telemetry/packages/fake/src/sensorManager.ts b/telemetry/packages/fake/src/sensorManager.ts index 8257c2a5..5790c757 100644 --- a/telemetry/packages/fake/src/sensorManager.ts +++ b/telemetry/packages/fake/src/sensorManager.ts @@ -1,141 +1,143 @@ import MQTT from 'mqtt'; -import { sensors, SensorInstance } from './sensors/index'; -import { Readings } from './types'; -import { sensorData, trackLength } from './config'; import { Sensor } from './base'; +import { sensorData, trackLength } from './config'; +import { type SensorInstance, sensors } from './sensors/index'; +import type { Readings } from './types'; import { Utilities as utils } from './utils'; export class SensorManager { - // Create array to store sensor instances - private sensors: SensorInstance<(typeof sensors)[keyof typeof sensors]>[] = - []; - // Record the sampling intervals for each sensor - private samplingTimes: Record = {}; - // Global clock for simulation runtime - private globalTime = 0; - // Mqtt client - private client: MQTT.MqttClient; + // Create array to store sensor instances + private sensors: SensorInstance<(typeof sensors)[keyof typeof sensors]>[] = + []; + // Record the sampling intervals for each sensor + private samplingTimes: Record = {}; + // Global clock for simulation runtime + private globalTime = 0; + // Mqtt client + private client: MQTT.MqttClient; - /** - * The sensors form a hierarchical dependency tree - * At the top level is Motion, which relies only on time - * All other sensor data relies on motion readings, either - * directly or as a grandchild of the motion class - * Key = sensor - * Value = parent class - */ - private dependencies: Record = { - motion: null, - keyence: 'motion', - temperature: 'motion', - pressure: 'temperature', - resistance: 'temperature', - magnetism: 'motion', - levitation: 'magnetism', - }; + /** + * The sensors form a hierarchical dependency tree + * At the top level is Motion, which relies only on time + * All other sensor data relies on motion readings, either + * directly or as a grandchild of the motion class + * Key = sensor + * Value = parent class + */ + private dependencies: Record = { + motion: null, + keyence: 'motion', + temperature: 'motion', + pressure: 'temperature', + resistance: 'temperature', + magnetism: 'motion', + levitation: 'magnetism', + }; - /** - * Sensor manager singleton class - * Controls and connects all sensor classes - * @param sensorsToRun user-defined array of sensor names to be run in the current simulation - */ - constructor(private sensorsToRun: string[]) { - // Create sensor instances - this.instantiateSensors(this.sensorsToRun); - // Record fixed sampling time periods - this.sensorsToRun.forEach((s: string) => { - this.samplingTimes[s] = sensorData[s].sampling_time; - }); - // Initialize MQTT connection - this.client = MQTT.connect('MQTT://mosquitto:1883'); - } + /** + * Sensor manager singleton class + * Controls and connects all sensor classes + * @param sensorsToRun user-defined array of sensor names to be run in the current simulation + */ + constructor(private sensorsToRun: string[]) { + // Create sensor instances + this.instantiateSensors(this.sensorsToRun); + // Record fixed sampling time periods + for (const s of this.sensorsToRun) { + this.samplingTimes[s] = sensorData[s].sampling_time; + } + // Initialize MQTT connection + this.client = MQTT.connect('MQTT://mosquitto:1883'); + } - /** - * Runs transient data generation - * Updates global variables each iteration - * End program once runTime has been reached - * @param random boolean input set by user, allows for completely random data - */ - public generateData(random = false): void { - // Calculate base sampling interval using lowest common divisor of all sensors' sampling periods - const interval = utils.gcd(Object.values(this.samplingTimes)); + /** + * Runs transient data generation + * Updates global variables each iteration + * End program once runTime has been reached + * @param random boolean input set by user, allows for completely random data + */ + public generateData(random = false): void { + // Calculate base sampling interval using lowest common divisor of all sensors' sampling periods + const interval = utils.gcd(Object.values(this.samplingTimes)); - const simulationInterval = setInterval(() => { - // Reset all 'sampled' flags to false - this.resetSampledState(); - this.sensors.forEach((sensor) => { - // Generate data if current time corresponds to sensor's sampling time - if ((this.globalTime / 1000) % sensor.delta_t == 0) { - // Get the sensors' output data - const readings: Readings = !random - ? sensor.getData(this.globalTime / 1000) // convert time to seconds for calculations - : sensor.getRandomData(Sensor.lastReadings[sensor.type]); - // Store latest readings and set sensors' sampled state to true - Sensor.lastReadings[sensor.type] = readings; - Sensor.isSampled[sensor.type] = true; + const simulationInterval = setInterval(() => { + // Reset all 'sampled' flags to false + this.resetSampledState(); + for (const sensor of this.sensors) { + // Generate data if current time corresponds to sensor's sampling time + if ((this.globalTime / 1000) % sensor.delta_t === 0) { + // Get the sensors' output data + const readings: Readings = !random + ? sensor.getData(this.globalTime / 1000) // convert time to seconds for calculations + : sensor.getRandomData(Sensor.lastReadings[sensor.type]); + // Store latest readings and set sensors' sampled state to true + Sensor.lastReadings[sensor.type] = readings; + Sensor.isSampled[sensor.type] = true; - // Publish sensor readings under the topic of - // each and for each measurement key to the data broker - Object.entries(readings).forEach(([measurement, value]) => { - this.publishData(measurement, value.toString()); - }); - } - }); + // Publish sensor readings under the topic of + // each and for each measurement key to the data broker + for (const [measurement, value] of Object.entries(readings)) { + this.publishData(measurement, value.toString()); + } + } + } - // Implement exit condition - if (Sensor.lastReadings.motion.displacement >= trackLength) { - clearInterval(simulationInterval); - this.generateData(random); - } + // Implement exit condition + if (Sensor.lastReadings.motion.displacement >= trackLength) { + clearInterval(simulationInterval); + this.generateData(random); + } - this.globalTime += interval; - }, interval); - } + this.globalTime += interval; + }, interval); + } - /** - * Instantiate sensors and their required superclasses and store instances in array - */ - private instantiateSensors(sensorsToRun: string[]): void { - // Record sensors names to be added to instances array - const activeSensors: Set = new Set(); - // Function to activate all sensors required - const getActiveSensors = (name: string): void => { - if (this.dependencies[name] == null) { - activeSensors.add(name); - } else { - getActiveSensors(this.dependencies[name] as string); - activeSensors.add(name); - } - return; - }; - // Populate set - sensorsToRun.forEach((s) => getActiveSensors(s)); - // Define sensor instances - // Correct sorting is automatic as recursion forces all parent class sensors to be - // added before their inheriting classes - activeSensors.forEach((s) => - this.sensors.push(new sensors[s](sensorData[s])), - ); - } + /** + * Instantiate sensors and their required superclasses and store instances in array + */ + private instantiateSensors(sensorsToRun: string[]): void { + // Record sensors names to be added to instances array + const activeSensors: Set = new Set(); + // Function to activate all sensors required + const getActiveSensors = (name: string): void => { + if (this.dependencies[name] == null) { + activeSensors.add(name); + } else { + getActiveSensors(this.dependencies[name] as string); + activeSensors.add(name); + } + return; + }; + // Populate set + for (const s of sensorsToRun) { + getActiveSensors(s); + } + // Define sensor instances + // Correct sorting is automatic as recursion forces all parent class sensors to be + // added before their inheriting classes + for (const s of Array.from(activeSensors)) { + this.sensors.push(new sensors[s](sensorData[s])); + } + } - /** - * Reset all sensors' isSampled flags to false on each iteration - */ - private resetSampledState(): void { - Object.keys(Sensor.isSampled).forEach( - (sensor) => (Sensor.isSampled[sensor] = false), - ); - } + /** + * Reset all sensors' isSampled flags to false on each iteration + */ + private resetSampledState(): void { + for (const sensor of Object.keys(Sensor.isSampled)) { + Sensor.isSampled[sensor] = false; + } + } - /** - * Uploads data through MQTT broker to the frontend - * The properties of the sensors' readings objects are the keys which are appended to the topic path, i.e. ...measurements/[key] - * So simply append the key and publish the value as the payload - * Subscribed clients extract values using payload[measurementKey] - */ - private publishData(measurement: string, reading: string): void { - this.client.publish(`hyped/pod_2024/measurement/${measurement}`, reading, { - qos: 1, - }); - } + /** + * Uploads data through MQTT broker to the frontend + * The properties of the sensors' readings objects are the keys which are appended to the topic path, i.e. ...measurements/[key] + * So simply append the key and publish the value as the payload + * Subscribed clients extract values using payload[measurementKey] + */ + private publishData(measurement: string, reading: string): void { + this.client.publish(`hyped/pod_2024/measurement/${measurement}`, reading, { + qos: 1, + }); + } } diff --git a/telemetry/packages/fake/src/sensors/index.ts b/telemetry/packages/fake/src/sensors/index.ts index c81b647b..bf3d41e5 100644 --- a/telemetry/packages/fake/src/sensors/index.ts +++ b/telemetry/packages/fake/src/sensors/index.ts @@ -1,32 +1,33 @@ +import { Keyence } from './keyence'; +import { Levitation } from './levitation'; +import { Magnetism } from './magnetism'; // Individual sensor classes import { Motion } from './motion'; -import { Keyence } from './keyence'; import { Pressure } from './pressure'; -import { Temperature } from './temperature'; import { Resistance } from './resistance'; -import { Magnetism } from './magnetism'; -import { Levitation } from './levitation'; +import { Temperature } from './temperature'; type SensorType = - | typeof Motion - | typeof Keyence - | typeof Pressure - | typeof Temperature - | typeof Resistance - | typeof Magnetism - | typeof Levitation; + | typeof Motion + | typeof Keyence + | typeof Pressure + | typeof Temperature + | typeof Resistance + | typeof Magnetism + | typeof Levitation; // Instance type for sensor classes +// biome-ignore lint/suspicious/noExplicitAny: export type SensorInstance any> = - InstanceType; + InstanceType; // Export object containing all sensor classes export const sensors = { - motion: Motion, - keyence: Keyence, - temperature: Temperature, - resistance: Resistance, - pressure: Pressure, - magnetism: Magnetism, - levitation: Levitation, + motion: Motion, + keyence: Keyence, + temperature: Temperature, + resistance: Resistance, + pressure: Pressure, + magnetism: Magnetism, + levitation: Levitation, } as Record; diff --git a/telemetry/packages/fake/src/sensors/keyence.ts b/telemetry/packages/fake/src/sensors/keyence.ts index f28f0a69..57e22ff5 100644 --- a/telemetry/packages/fake/src/sensors/keyence.ts +++ b/telemetry/packages/fake/src/sensors/keyence.ts @@ -1,7 +1,7 @@ -import { Motion } from './motion'; import { Sensor } from '../base'; -import { LiveReading, Readings } from '../types'; import { trackLength } from '../config'; +import type { LiveReading, Readings } from '../types'; +import { Motion } from './motion'; /** * Integer value in range [0, 16], which directly corresponds to the track distance. @@ -9,43 +9,39 @@ import { trackLength } from '../config'; * Instead of sensor noise there is measurement tolerance */ export class Keyence extends Motion { - private podLength = 2.5; - - constructor(data: LiveReading) { - super(data); - } + private podLength = 2.5; - getData(t: number): Readings { - // Keyence sensors are evenly distributed along the pod - // Displacement is measured at the nose of the pod - const sensorRegion = - this.podLength / (Object.keys(Sensor.lastReadings.keyence).length - 1); - const noPoles = this.limits.critical.high; + getData(t: number): Readings { + // Keyence sensors are evenly distributed along the pod + // Displacement is measured at the nose of the pod + const sensorRegion = + this.podLength / (Object.keys(Sensor.lastReadings.keyence).length - 1); + const noPoles = this.limits.critical.high; - if (!Sensor.isSampled['motion']) { - this.displacement = super.getData(t).displacement; - Sensor.isSampled['motion'] = true; - } else { - this.displacement = Sensor.lastReadings.motion.displacement; - } + if (!Sensor.isSampled.motion) { + this.displacement = super.getData(t).displacement; + Sensor.isSampled.motion = true; + } else { + this.displacement = Sensor.lastReadings.motion.displacement; + } - this.displacement += this.addTolerance(); + this.displacement += this.addTolerance(); - return Object.fromEntries( - Object.keys(Sensor.lastReadings.keyence).map((key, i) => { - const relDisp = - this.displacement - sensorRegion * i >= 0 - ? this.displacement - sensorRegion * i - : 0; // assert value is positive - return [key, Math.floor(relDisp * (noPoles / trackLength))]; - }), - ); - } + return Object.fromEntries( + Object.keys(Sensor.lastReadings.keyence).map((key, i) => { + const relDisp = + this.displacement - sensorRegion * i >= 0 + ? this.displacement - sensorRegion * i + : 0; // assert value is positive + return [key, Math.floor(relDisp * (noPoles / trackLength))]; + }), + ); + } - /** - * Keyence sensor has single-digit millimetre tolerance - */ - addTolerance() { - return Math.random() * 0.01 * (Math.random() >= 0.5 ? 1 : -1); - } + /** + * Keyence sensor has single-digit millimetre tolerance + */ + addTolerance() { + return Math.random() * 0.01 * (Math.random() >= 0.5 ? 1 : -1); + } } diff --git a/telemetry/packages/fake/src/sensors/levitation.ts b/telemetry/packages/fake/src/sensors/levitation.ts index 2a7c6aa9..d3ce3e82 100644 --- a/telemetry/packages/fake/src/sensors/levitation.ts +++ b/telemetry/packages/fake/src/sensors/levitation.ts @@ -1,150 +1,149 @@ -import { Magnetism } from './magnetism'; import { Sensor } from '../base'; -import { LiveReading, Readings } from '../types'; +import type { LiveReading, Readings } from '../types'; import { Utilities } from '../utils'; +import { Magnetism } from './magnetism'; export class Levitation extends Magnetism { - private timeActive: number; // dynamic time variable - private timeOffset: number; // time at which lev. is activated - private isActive = false; - - private prevVals: number[]; // store recent values to identify reaching steady state - private setpoint = 50; // mm - private inSteadyState = true; - private sse = 0.02; // steady state error (±1% of setpoint) - - private readonly logRiseParams = { - t_0: 1.9, // inflection point - growth: 2.5, // growth rate - t_f: 2.225, // time when logistic function switches to sinusoidal exp. decay - l_peak: 60, // peak amplitude - }; - private readonly oscParams = { - freq: 4, // rad/s - phase: 3.7, // phase angle (rad) - decay: 0.2, // decay rate - amp: this.logRiseParams.l_peak - this.setpoint, // oscillation amplitude - }; - private readonly logFallParams = { - t_0: 2.5, // land on track within 5s - growth: 1.5, // decline smoother than rise - }; - - constructor(data: LiveReading) { - super(data); - // 10 values ensures minimal error for small sampling times - // and sufficient reaction time for large sampling times - this.prevVals = Array(10).fill(0); - } - - // Reset time at rising and falling stages - initiate(t: number) { - this.timeOffset = t; - this.timeActive = 0; - this.isActive = !this.isActive; - this.inSteadyState = false; - } - - getData(t: number): Readings { - // Start relative timekeeping when EM powers on - if (this.isFieldOn() && !this.isActive) this.initiate(t); - - // Keep at zero while on track - // ToF sensor noise negated/ignored as value is fixed and known - if (!this.isActive && this.inSteadyState) - return Sensor.lastReadings.levitation; - - // Code below reachable after EM field first turns on - - // Update levitating time - this.timeActive = t - this.timeOffset; - - // Rising stage - if (this.isActive) { - if (this.timeActive <= this.logRiseParams.t_f) { - this.prevVals.push( - Utilities.logistic( - this.timeActive, - this.logRiseParams.l_peak, - this.logRiseParams.growth, - this.logRiseParams.t_0, - ), - ); - this.prevVals.shift(); - } - - // Oscillation to steady state - else if ( - !this.inSteadyState && - this.timeActive > this.logRiseParams.t_f - ) { - this.prevVals.push( - this.setpoint + - Utilities.oscillateDecay( - this.timeActive - this.logRiseParams.t_f, - this.oscParams.freq, - this.oscParams.phase, - this.oscParams.decay, - this.oscParams.amp, - ), - ); - this.prevVals.shift(); - - this.inSteadyState = this.prevVals.every( - (l) => Math.abs(this.setpoint - l) < (this.sse / 2) * this.setpoint, - ); - } - - // If at steady state, fix value to avoid unneeded computation - else { - this.prevVals.push(this.setpoint); - this.prevVals.shift(); - } - - // Monitor EM field during steady state - if (this.isFieldOn()) return this.updateReadings(this.prevVals); - // Reset time for decline stage - else this.initiate(t); - } - - // Code below reachable once pod decline begins - - // Once pod slows down, begin gradual decline in levitation - if (!this.inSteadyState) { - this.prevVals.push( - this.setpoint * - (1 - - Utilities.logistic( - this.timeActive, - 1, - this.logFallParams.growth, - this.logFallParams.t_0, - )), - ); - - this.inSteadyState = this.prevVals.every( - (l) => Math.abs(l) < (this.sse / 2) * this.setpoint, - ); - - return this.updateReadings(this.prevVals); - } else { - // Once touched down, reset sensor readings to zero - return this.updateReadings([], true); - } - } - - private updateReadings(prevVals: number[], onTrack = false): Readings { - return Object.fromEntries( - Object.keys(Sensor.lastReadings.levitation).map((key) => { - return [ - key, - Utilities.round2DP( - prevVals.slice(-1)[0] + - // Add noise if levitating, otherwise value is fixed so noise is negated - (onTrack ? 0 : Utilities.gaussianRandom(this.rms_noise)), - ), - ]; - }), - ); - } + private timeActive: number; // dynamic time variable + private timeOffset: number; // time at which lev. is activated + private isActive = false; + + private prevVals: number[]; // store recent values to identify reaching steady state + private setpoint = 50; // mm + private inSteadyState = true; + private sse = 0.02; // steady state error (±1% of setpoint) + + private readonly logRiseParams = { + t_0: 1.9, // inflection point + growth: 2.5, // growth rate + t_f: 2.225, // time when logistic function switches to sinusoidal exp. decay + l_peak: 60, // peak amplitude + }; + private readonly oscParams = { + freq: 4, // rad/s + phase: 3.7, // phase angle (rad) + decay: 0.2, // decay rate + amp: this.logRiseParams.l_peak - this.setpoint, // oscillation amplitude + }; + private readonly logFallParams = { + t_0: 2.5, // land on track within 5s + growth: 1.5, // decline smoother than rise + }; + + constructor(data: LiveReading) { + super(data); + // 10 values ensures minimal error for small sampling times + // and sufficient reaction time for large sampling times + this.prevVals = Array(10).fill(0); + } + + // Reset time at rising and falling stages + initiate(t: number) { + this.timeOffset = t; + this.timeActive = 0; + this.isActive = !this.isActive; + this.inSteadyState = false; + } + + getData(t: number): Readings { + // Start relative timekeeping when EM powers on + if (this.isFieldOn() && !this.isActive) this.initiate(t); + + // Keep at zero while on track + // ToF sensor noise negated/ignored as value is fixed and known + if (!this.isActive && this.inSteadyState) + return Sensor.lastReadings.levitation; + + // Code below reachable after EM field first turns on + + // Update levitating time + this.timeActive = t - this.timeOffset; + + // Rising stage + if (this.isActive) { + if (this.timeActive <= this.logRiseParams.t_f) { + this.prevVals.push( + Utilities.logistic( + this.timeActive, + this.logRiseParams.l_peak, + this.logRiseParams.growth, + this.logRiseParams.t_0, + ), + ); + this.prevVals.shift(); + } + + // Oscillation to steady state + else if ( + !this.inSteadyState && + this.timeActive > this.logRiseParams.t_f + ) { + this.prevVals.push( + this.setpoint + + Utilities.oscillateDecay( + this.timeActive - this.logRiseParams.t_f, + this.oscParams.freq, + this.oscParams.phase, + this.oscParams.decay, + this.oscParams.amp, + ), + ); + this.prevVals.shift(); + + this.inSteadyState = this.prevVals.every( + (l) => Math.abs(this.setpoint - l) < (this.sse / 2) * this.setpoint, + ); + } + + // If at steady state, fix value to avoid unneeded computation + else { + this.prevVals.push(this.setpoint); + this.prevVals.shift(); + } + + // Monitor EM field during steady state + if (this.isFieldOn()) return this.updateReadings(this.prevVals); + // Reset time for decline stage + this.initiate(t); + } + + // Code below reachable once pod decline begins + + // Once pod slows down, begin gradual decline in levitation + if (!this.inSteadyState) { + this.prevVals.push( + this.setpoint * + (1 - + Utilities.logistic( + this.timeActive, + 1, + this.logFallParams.growth, + this.logFallParams.t_0, + )), + ); + + this.inSteadyState = this.prevVals.every( + (l) => Math.abs(l) < (this.sse / 2) * this.setpoint, + ); + + return this.updateReadings(this.prevVals); + } + // Once touched down, reset sensor readings to zero + return this.updateReadings([], true); + } + + private updateReadings(prevVals: number[], onTrack = false): Readings { + return Object.fromEntries( + Object.keys(Sensor.lastReadings.levitation).map((key) => { + return [ + key, + Utilities.round2DP( + prevVals.slice(-1)[0] + + // Add noise if levitating, otherwise value is fixed so noise is negated + (onTrack ? 0 : Utilities.gaussianRandom(this.rms_noise)), + ), + ]; + }), + ); + } } diff --git a/telemetry/packages/fake/src/sensors/magnetism.ts b/telemetry/packages/fake/src/sensors/magnetism.ts index 8b1dc8fa..732aad83 100644 --- a/telemetry/packages/fake/src/sensors/magnetism.ts +++ b/telemetry/packages/fake/src/sensors/magnetism.ts @@ -1,42 +1,38 @@ -import { Motion } from './motion'; import { Sensor } from '../base'; -import { LiveReading, Readings } from '../types'; +import type { LiveReading, Readings } from '../types'; import { Utilities as utils } from '../utils'; +import { Motion } from './motion'; export class Magnetism extends Motion { - protected magSetpoint = 250; // A - - constructor(data: LiveReading) { - super(data); - } + protected magSetpoint = 250; // A - getData(t: number): Readings { - if (!Sensor.isSampled['motion']) { - this.velocity = super.getData(t).velocity; - Sensor.isSampled['motion'] = true; - } else { - this.velocity = Sensor.lastReadings.motion.velocity; - } + getData(t: number): Readings { + if (!Sensor.isSampled.motion) { + this.velocity = super.getData(t).velocity; + Sensor.isSampled.motion = true; + } else { + this.velocity = Sensor.lastReadings.motion.velocity; + } - return Object.fromEntries( - Object.keys(Sensor.lastReadings.magnetism).map((key) => { - // binary on or off - instantaneous step change - return [ - key, - (this.velocity >= this.liftoffSpeed ? this.magSetpoint : 0) + - utils.gaussianRandom(this.rms_noise), - ]; - }), - ); - } + return Object.fromEntries( + Object.keys(Sensor.lastReadings.magnetism).map((key) => { + // binary on or off - instantaneous step change + return [ + key, + (this.velocity >= this.liftoffSpeed ? this.magSetpoint : 0) + + utils.gaussianRandom(this.rms_noise), + ]; + }), + ); + } - /** - * For subclasses to check if EM field is powered on - * EM field only switched on when corresponding velocity is detected - * Hence use of last known velocity value instead of generating a new one - */ - protected isFieldOn(): boolean { - this.velocity = Sensor.lastReadings.motion.velocity; - return this.velocity >= this.liftoffSpeed; - } + /** + * For subclasses to check if EM field is powered on + * EM field only switched on when corresponding velocity is detected + * Hence use of last known velocity value instead of generating a new one + */ + protected isFieldOn(): boolean { + this.velocity = Sensor.lastReadings.motion.velocity; + return this.velocity >= this.liftoffSpeed; + } } diff --git a/telemetry/packages/fake/src/sensors/motion.ts b/telemetry/packages/fake/src/sensors/motion.ts index e4d437b5..70bd81d7 100644 --- a/telemetry/packages/fake/src/sensors/motion.ts +++ b/telemetry/packages/fake/src/sensors/motion.ts @@ -1,57 +1,57 @@ import { Sensor } from '../base'; import { measurements } from '../config'; +import type { LiveReading, Readings } from '../types'; import { Utilities } from '../utils'; -import { LiveReading, Readings } from '../types'; export class Motion extends Sensor { - protected displacement: number; - protected velocity: number; - protected acceleration: number; - // Velocity threshold at which levitation is activated - protected liftoffSpeed = 5; + protected displacement: number; + protected velocity: number; + protected acceleration: number; + // Velocity threshold at which levitation is activated + protected liftoffSpeed = 5; - private logParams = { - growth: 0.4, - // Ensures acceleration peaks at its limiting operating value - t_0: 12.5, - // Max vel. set to 95% of upper limit giving a small margin for noise fluctuations - stState: 0.95 * measurements.velocity.limits.critical.high, - }; + private logParams = { + growth: 0.4, + // Ensures acceleration peaks at its limiting operating value + t_0: 12.5, + // Max vel. set to 95% of upper limit giving a small margin for noise fluctuations + stState: 0.95 * measurements.velocity.limits.critical.high, + }; - constructor(accelerometer: LiveReading) { - super(accelerometer); - const { displacement, velocity, acceleration } = Sensor.lastReadings.motion; - Object.assign(this, { displacement, velocity, acceleration }); - } + constructor(accelerometer: LiveReading) { + super(accelerometer); + const { displacement, velocity, acceleration } = Sensor.lastReadings.motion; + Object.assign(this, { displacement, velocity, acceleration }); + } - getData(t: number): Readings { - const velocityEstimate = Utilities.logistic( - t, - this.logParams.stState, - this.logParams.growth, - this.logParams.t_0, - ); + getData(t: number): Readings { + const velocityEstimate = Utilities.logistic( + t, + this.logParams.stState, + this.logParams.growth, + this.logParams.t_0, + ); - // Use estimate to calculate accelerometer reading - let accelerometerReading = - (velocityEstimate - this.velocity) / this.delta_t; - // Assert reading is not above critical limit - accelerometerReading = - accelerometerReading >= this.limits.critical.high - ? this.limits.critical.high - : accelerometerReading; - accelerometerReading += Utilities.gaussianRandom(this.rms_noise); + // Use estimate to calculate accelerometer reading + let accelerometerReading = + (velocityEstimate - this.velocity) / this.delta_t; + // Assert reading is not above critical limit + accelerometerReading = + accelerometerReading >= this.limits.critical.high + ? this.limits.critical.high + : accelerometerReading; + accelerometerReading += Utilities.gaussianRandom(this.rms_noise); - // Use trapezoidal integration to find velocity, displacement - const avgAcceleration = (accelerometerReading + this.acceleration) / 2; - this.velocity += avgAcceleration * this.delta_t; - this.displacement += this.velocity * this.delta_t; - this.acceleration = accelerometerReading; + // Use trapezoidal integration to find velocity, displacement + const avgAcceleration = (accelerometerReading + this.acceleration) / 2; + this.velocity += avgAcceleration * this.delta_t; + this.displacement += this.velocity * this.delta_t; + this.acceleration = accelerometerReading; - return { - acceleration: Utilities.round2DP(this.acceleration), - velocity: Utilities.round2DP(this.velocity), - displacement: Utilities.round2DP(this.displacement), - }; - } + return { + acceleration: Utilities.round2DP(this.acceleration), + velocity: Utilities.round2DP(this.velocity), + displacement: Utilities.round2DP(this.displacement), + }; + } } diff --git a/telemetry/packages/fake/src/sensors/pressure.ts b/telemetry/packages/fake/src/sensors/pressure.ts index eef97c34..1c9d5703 100644 --- a/telemetry/packages/fake/src/sensors/pressure.ts +++ b/telemetry/packages/fake/src/sensors/pressure.ts @@ -1,91 +1,91 @@ -import { Temperature } from './temperature'; import { Sensor } from '../base'; -import { LiveReading, Readings } from '../types'; +import type { LiveReading, Readings } from '../types'; import { Utilities as utils } from '../utils'; +import { Temperature } from './temperature'; export class Pressure extends Temperature { - private pResBrakes0: number; - private pResSusp0: number; + private pResBrakes0: number; + private pResSusp0: number; - private airProps = { - rho: 1.225, // air density (kg/m3) - atm: 101325, // atmospheric pressure (Pa) - }; - private coefficients = { - lossFactor: 0.05, // 5% internal pressure losses - stagnation: 0.5, - wake: 0.25, - brakingFactor: 0.05, // to calculate pressure increase due to braking force - }; + private airProps = { + rho: 1.225, // air density (kg/m3) + atm: 101325, // atmospheric pressure (Pa) + }; + private coefficients = { + lossFactor: 0.05, // 5% internal pressure losses + stagnation: 0.5, + wake: 0.25, + brakingFactor: 0.05, // to calculate pressure increase due to braking force + }; - constructor(data: LiveReading) { - super(data); - // this.prevVals = Array(10).fill(0); - this.pResBrakes0 = data.readings['pressure_brakes_reservoir']; - this.pResSusp0 = data.readings['pressure_active_suspension_reservoir']; - } + constructor(data: LiveReading) { + super(data); + // this.prevVals = Array(10).fill(0); + this.pResBrakes0 = data.readings.pressure_brakes_reservoir; + this.pResSusp0 = data.readings.pressure_active_suspension_reservoir; + } - getData(): Readings { - const newData = { ...Sensor.lastReadings.pressure }; - // Pneumatic pressure gauges - newData['pressure_front_pull'] = this.bernoulli('stagnation'); - newData['pressure_front_push'] = - (1 - this.coefficients.lossFactor) * newData['pressure_front_pull']; - newData['pressure_back_pull'] = this.bernoulli('wake'); - newData['pressure_back_push'] = - (1 - this.coefficients.lossFactor) * newData['pressure_back_pull']; + getData(): Readings { + const newData = { ...Sensor.lastReadings.pressure }; + // Pneumatic pressure gauges + newData.pressure_front_pull = this.bernoulli('stagnation'); + newData.pressure_front_push = + (1 - this.coefficients.lossFactor) * newData.pressure_front_pull; + newData.pressure_back_pull = this.bernoulli('wake'); + newData.pressure_back_push = + (1 - this.coefficients.lossFactor) * newData.pressure_back_pull; - // Reservoir pressure - newData['pressure_brakes_reservoir'] = this.idealGasLaw('brakes'); - newData['pressure_active_suspension_reservoir'] = - this.idealGasLaw('suspension'); + // Reservoir pressure + newData.pressure_brakes_reservoir = this.idealGasLaw('brakes'); + newData.pressure_active_suspension_reservoir = + this.idealGasLaw('suspension'); - // Brake pressure - newData['pressure_front_brake'] = this.brakePressure(); - newData['pressure_back_brake'] = newData['pressure_front_brake']; + // Brake pressure + newData.pressure_front_brake = this.brakePressure(); + newData.pressure_back_brake = newData.pressure_front_brake; - return Object.fromEntries( - Object.entries(newData).map(([key, value]) => { - return [ - key, - utils.round2DP( - (value + utils.gaussianRandom(this.rms_noise)) * 10 ** -5, // convert back to bar - ), - ]; - }), - ); - } + return Object.fromEntries( + Object.entries(newData).map(([key, value]) => { + return [ + key, + utils.round2DP( + (value + utils.gaussianRandom(this.rms_noise)) * 10 ** -5, // convert back to bar + ), + ]; + }), + ); + } - private bernoulli(loc: 'stagnation' | 'wake'): number { - const { rho, atm } = this.airProps; - return atm + this.coefficients[loc] * (rho * Math.pow(this.velocity, 2)); - } + private bernoulli(loc: 'stagnation' | 'wake'): number { + const { rho, atm } = this.airProps; + return atm + this.coefficients[loc] * (rho * this.velocity ** 2); + } - private idealGasLaw(loc: 'brakes' | 'suspension'): number { - this.temp += - this.acceleration < 0 && loc == 'brakes' - ? Math.abs(this.acceleration) * this.coefficients.brakingFactor - : 0; - return loc == 'brakes' - ? this.pResBrakes0 * (this.cToK(this.temp) / this.cToK(this.temp0)) - : this.pResSusp0 * (this.cToK(this.temp) / this.cToK(this.temp0)); - } + private idealGasLaw(loc: 'brakes' | 'suspension'): number { + this.temp += + this.acceleration < 0 && loc === 'brakes' + ? Math.abs(this.acceleration) * this.coefficients.brakingFactor + : 0; + return loc === 'brakes' + ? this.pResBrakes0 * (this.cToK(this.temp) / this.cToK(this.temp0)) + : this.pResSusp0 * (this.cToK(this.temp) / this.cToK(this.temp0)); + } - private brakePressure(): number { - const { atm } = this.airProps; - const p = Sensor.lastReadings.pressure['pressure_front_brake']; - if (this.acceleration > 0 && p > atm) { - return p - 100; // arbitrary value for pressure drop - } else if (this.acceleration > 0) { - return atm; - } else { - this.temp += - Math.abs(this.acceleration) * 5 * this.coefficients.brakingFactor; - return atm * (this.cToK(this.temp) / this.cToK(this.temp0)); - } - } + private brakePressure(): number { + const { atm } = this.airProps; + const p = Sensor.lastReadings.pressure.pressure_front_brake; + if (this.acceleration > 0 && p > atm) { + return p - 100; // arbitrary value for pressure drop + } + if (this.acceleration > 0) { + return atm; + } + this.temp += + Math.abs(this.acceleration) * 5 * this.coefficients.brakingFactor; + return atm * (this.cToK(this.temp) / this.cToK(this.temp0)); + } - private cToK(temp: number): number { - return temp + 273.15; - } + private cToK(temp: number): number { + return temp + 273.15; + } } diff --git a/telemetry/packages/fake/src/sensors/resistance.ts b/telemetry/packages/fake/src/sensors/resistance.ts index 40a09b2a..94cd0570 100644 --- a/telemetry/packages/fake/src/sensors/resistance.ts +++ b/telemetry/packages/fake/src/sensors/resistance.ts @@ -1,39 +1,39 @@ -import { Temperature } from './temperature'; import { Sensor } from '../base'; -import { LiveReading, Readings } from '../types'; +import type { LiveReading, Readings } from '../types'; import { Utilities as utils } from '../utils'; +import { Temperature } from './temperature'; export class Resistance extends Temperature { - private alpha = 5 * 10 ** -3; // Temperature coefficient of resistance (steel) - private r0: number; // Initial value + private alpha = 5 * 10 ** -3; // Temperature coefficient of resistance (steel) + private r0: number; // Initial value - constructor(data: LiveReading) { - super(data); - // Set reference value and convert to ohms for higher precision output values - this.r0 = Sensor.lastReadings.resistance.power_line_resistance * 10 ** 3; - } + constructor(data: LiveReading) { + super(data); + // Set reference value and convert to ohms for higher precision output values + this.r0 = Sensor.lastReadings.resistance.power_line_resistance * 10 ** 3; + } - /** - * Resistance can be assumed constant, seeing little variation with temperature - * change. - * This data verifies the power line's continual safety by checking resistance - * is as expected during operation. - */ - getData(): Readings { - if (!Sensor.isSampled['temperature']) { - this.temp = utils.average(Object.values(super.getData())); - Sensor.isSampled['temperature'] = true; - } + /** + * Resistance can be assumed constant, seeing little variation with temperature + * change. + * This data verifies the power line's continual safety by checking resistance + * is as expected during operation. + */ + getData(): Readings { + if (!Sensor.isSampled.temperature) { + this.temp = utils.average(Object.values(super.getData())); + Sensor.isSampled.temperature = true; + } - const readings = Object.keys(Sensor.lastReadings.resistance).map((key) => { - // R = R0 * (1 + α(T - T0)) - const r = this.r0 * (1 + this.alpha * (this.temp - this.temp0)); - return [ - key, - utils.round2DP((r + utils.gaussianRandom(this.rms_noise)) * 0.001), - ]; - }); + const readings = Object.keys(Sensor.lastReadings.resistance).map((key) => { + // R = R0 * (1 + α(T - T0)) + const r = this.r0 * (1 + this.alpha * (this.temp - this.temp0)); + return [ + key, + utils.round2DP((r + utils.gaussianRandom(this.rms_noise)) * 0.001), + ]; + }); - return Object.fromEntries(readings); - } + return Object.fromEntries(readings); + } } diff --git a/telemetry/packages/fake/src/sensors/temperature.ts b/telemetry/packages/fake/src/sensors/temperature.ts index 8d1ecb48..b74c3baa 100644 --- a/telemetry/packages/fake/src/sensors/temperature.ts +++ b/telemetry/packages/fake/src/sensors/temperature.ts @@ -1,47 +1,47 @@ -import { Motion } from './motion'; import { Sensor } from '../base'; -import { LiveReading, Readings } from '../types'; +import type { LiveReading, Readings } from '../types'; import { Utilities } from '../utils'; +import { Motion } from './motion'; export class Temperature extends Motion { - protected temp: number; - protected temp0: number; + protected temp: number; + protected temp0: number; - // Arbitrary coefficients for estimating temperature changes - private params = { - drag: 0.1, - friction: 0.3, - heatGen: 0.5, - }; + // Arbitrary coefficients for estimating temperature changes + private params = { + drag: 0.1, + friction: 0.3, + heatGen: 0.5, + }; - constructor(data: LiveReading) { - super(data); - // Initial temp used for reference by subclass(es) - this.temp0 = Utilities.average(Object.values(data.readings)); - this.temp = this.temp0; - } + constructor(data: LiveReading) { + super(data); + // Initial temp used for reference by subclass(es) + this.temp0 = Utilities.average(Object.values(data.readings)); + this.temp = this.temp0; + } - getData(): Readings { - this.temp += // Air drag and internal heat generation - Math.pow(this.velocity, 3) * this.params.drag + - this.velocity * this.params.heatGen; - this.temp += // On the track, temperature increases with work done - this.velocity < this.liftoffSpeed - ? Math.pow(this.displacement, 2) * this.params.friction - : Math.pow(this.displacement, 2) * - (this.liftoffSpeed / this.velocity) * - this.displacement * - this.params.friction; + getData(): Readings { + this.temp += // Air drag and internal heat generation + this.velocity ** 3 * this.params.drag + + this.velocity * this.params.heatGen; + this.temp += // On the track, temperature increases with work done + this.velocity < this.liftoffSpeed + ? this.displacement ** 2 * this.params.friction + : this.displacement ** 2 * + (this.liftoffSpeed / this.velocity) * + this.displacement * + this.params.friction; - return Object.fromEntries( - Object.keys(Sensor.lastReadings.temperature).map((key) => { - return [ - key, - Utilities.round2DP( - this.temp + Utilities.gaussianRandom(this.rms_noise), - ), - ]; - }), - ); - } + return Object.fromEntries( + Object.keys(Sensor.lastReadings.temperature).map((key) => { + return [ + key, + Utilities.round2DP( + this.temp + Utilities.gaussianRandom(this.rms_noise), + ), + ]; + }), + ); + } } diff --git a/telemetry/packages/fake/src/types.ts b/telemetry/packages/fake/src/types.ts index 62485a80..03f50f36 100644 --- a/telemetry/packages/fake/src/types.ts +++ b/telemetry/packages/fake/src/types.ts @@ -11,13 +11,13 @@ export type SensorData = Record; * Sensor property containing values for each of its measured quantities */ export type Readings = { - [measurement: string]: number; + [measurement: string]: number; }; /** * Return type for sensor class instantiation */ export type BaseSensor = { - getData: (t: number) => Readings; - getRandomData: (prevValue: number, readings: Readings) => Readings; + getData: (t: number) => Readings; + getRandomData: (prevValue: number, readings: Readings) => Readings; }; diff --git a/telemetry/packages/fake/src/utils.ts b/telemetry/packages/fake/src/utils.ts index 8a37d10f..9c39249a 100644 --- a/telemetry/packages/fake/src/utils.ts +++ b/telemetry/packages/fake/src/utils.ts @@ -1,115 +1,119 @@ export class Utilities { - /** - * Greatest common divisor - */ - public static gcd(nums: number[]): number { - nums = nums.sort((a, b) => b - a); - return nums.reduce((acc, c) => { - return c === 0 ? acc : Utilities.gcd([c, acc % c]); - }); - } + /** + * Greatest common divisor + */ + public static gcd(nums: number[]): number { + const sortedNums = nums.sort((a, b) => b - a); + return sortedNums.reduce((acc, c) => { + return c === 0 ? acc : Utilities.gcd([c, acc % c]); + }); + } - /** - * Simple floating point rounding method - * @param num - * @returns - */ - public static round2DP(num: number): number { - return parseFloat(num.toFixed(2)); - } + /** + * Simple floating point rounding method + * @param num + * @returns + */ + public static round2DP(num: number): number { + return Number.parseFloat(num.toFixed(2)); + } - /** - * Generates random noise value from a Gaussian distribution - * @param mean self-explanatory - * @param std_dev sensor's RMS noise value, used as the standard deviation - * @returns random number defined by the normal distribution of stdDev = RMS noise - */ - public static gaussianRandom(std_dev: number, mean = 0): number { - // Using the Box-Muller transform to generate random values from a normal distribution - const u1 = Math.random(); - const u2 = Math.random(); - const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2); + /** + * Generates random noise value from a Gaussian distribution + * @param mean self-explanatory + * @param std_dev sensor's RMS noise value, used as the standard deviation + * @returns random number defined by the normal distribution of stdDev = RMS noise + */ + public static gaussianRandom(std_dev: number, mean = 0): number { + // Using the Box-Muller transform to generate random values from a normal distribution + const u1 = Math.random(); + const u2 = Math.random(); + const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2); - return parseFloat((z * std_dev + mean).toFixed(2)); - } + return Number.parseFloat((z * std_dev + mean).toFixed(2)); + } - /** - * Generates random value from random distribution defined by provided range - * 99% probability of random value falling within critical limits - * This results in a z-score of 2.576 for confidence level of 99% - */ - public static getRandomValue( - prevValue: number, - rms_noise: number, - format: 'float' | 'integer', - ): number { - return format == 'float' - ? parseFloat(this.gaussianRandom(rms_noise, prevValue).toFixed(2)) - : parseInt(this.gaussianRandom(rms_noise, prevValue).toFixed(2)); - } + /** + * Generates random value from random distribution defined by provided range + * 99% probability of random value falling within critical limits + * This results in a z-score of 2.576 for confidence level of 99% + */ + public static getRandomValue( + prevValue: number, + rms_noise: number, + format: 'float' | 'integer', + ): number { + return format === 'float' + ? Number.parseFloat( + Utilities.gaussianRandom(rms_noise, prevValue).toFixed(2), + ) + : Number.parseInt( + Utilities.gaussianRandom(rms_noise, prevValue).toFixed(2), + ); + } - /** - * Simple arithmetic mean - * @param values numerical sample - * @returns mean value - */ - public static average(values: number[]): number { - return values.reduce((acc, c) => acc + c) / values.length; - } + /** + * Simple arithmetic mean + * @param values numerical sample + * @returns mean value + */ + public static average(values: number[]): number { + return values.reduce((acc, c) => acc + c) / values.length; + } - /** - * Logistic function used as an analytical basis for dynamic variables which change over time - * @param t - current time - * @param peak - asymptotic maximum value - * @param k - exponential growth factor - * @param t0 - time of curve inflection (df²/dt² = 0) - * @returns f(t) - current reading according to idealised model - */ - public static logistic( - t: number, - peak: number, - k: number, // exponential growth rate factor - t0: number, // time at which second derivative reaches a stationary point - ): number { - return parseFloat((peak / (1 + Math.exp(-k * (t - t0)))).toFixed(2)); - } + /** + * Logistic function used as an analytical basis for dynamic variables which change over time + * @param t - current time + * @param peak - asymptotic maximum value + * @param k - exponential growth factor + * @param t0 - time of curve inflection (df²/dt² = 0) + * @returns f(t) - current reading according to idealised model + */ + public static logistic( + t: number, + peak: number, + k: number, // exponential growth rate factor + t0: number, // time at which second derivative reaches a stationary point + ): number { + return Number.parseFloat((peak / (1 + Math.exp(-k * (t - t0)))).toFixed(2)); + } - /** - * Sinusoidal damped oscillation - * @param t - current time - * @param freq - angular frequency - * @param phase - phase shift (angle) - * @param decay - exponential decay factor - * @param amp - oscillation peak amplitude - * @returns f(t) - */ - public static oscillateDecay( - t: number, - freq: number, - phase: number, - decay: number, - amp: number, - ): number { - return parseFloat( - (amp * Math.exp(-decay * t) * Math.cos(freq * t + phase)).toFixed(2), - ); - } + /** + * Sinusoidal damped oscillation + * @param t - current time + * @param freq - angular frequency + * @param phase - phase shift (angle) + * @param decay - exponential decay factor + * @param amp - oscillation peak amplitude + * @returns f(t) + */ + public static oscillateDecay( + t: number, + freq: number, + phase: number, + decay: number, + amp: number, + ): number { + return Number.parseFloat( + (amp * Math.exp(-decay * t) * Math.cos(freq * t + phase)).toFixed(2), + ); + } - /** - * Gets the exponential average of a recent set of values - * @param vals previous values (and chosen length of array) - * @param alpha weighting factor - * @returns exponentially weighted average - */ - public expMovingAvg(vals: number[], alpha: number): number | undefined { - if (alpha <= 0 || alpha > 1 || !vals.length) { - return; - } - let sum = 0; - vals.forEach((v, i) => { - const weight = Math.pow(alpha, vals.length - 1 - i); - sum += v * weight; - }); - return sum / (1 - Math.pow(alpha, vals.length)); - } + /** + * Gets the exponential average of a recent set of values + * @param vals previous values (and chosen length of array) + * @param alpha weighting factor + * @returns exponentially weighted average + */ + public expMovingAvg(vals: number[], alpha: number): number | undefined { + if (alpha <= 0 || alpha > 1 || !vals.length) { + return; + } + let sum = 0; + vals.forEach((v, i) => { + const weight = alpha ** (vals.length - 1 - i); + sum += v * weight; + }); + return sum / (1 - alpha ** vals.length); + } } diff --git a/telemetry/packages/fake/tsconfig.json b/telemetry/packages/fake/tsconfig.json index 469e3157..e362eff5 100644 --- a/telemetry/packages/fake/tsconfig.json +++ b/telemetry/packages/fake/tsconfig.json @@ -1,13 +1,13 @@ { - "extends": "@hyped/tsconfig/base.json", - "compilerOptions": { - "outDir": "./dist", - "lib": ["esnext"], - "importHelpers": true, - "sourceMap": true, - "rootDir": "./src", - "strictPropertyInitialization": false, - }, - "include": ["src", "src/config.ts"], - "exclude": ["node_modules"], + "extends": "@hyped/tsconfig/base.json", + "compilerOptions": { + "outDir": "./dist", + "lib": ["esnext"], + "importHelpers": true, + "sourceMap": true, + "rootDir": "./src", + "strictPropertyInitialization": false + }, + "include": ["src", "src/config.ts"], + "exclude": ["node_modules"] } diff --git a/telemetry/packages/public-app/.eslintrc.json b/telemetry/packages/public-app/.eslintrc.json deleted file mode 100644 index 85e2f35f..00000000 --- a/telemetry/packages/public-app/.eslintrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "root": true, - "extends": ["next/core-web-vitals", "@hyped/eslint-config/react.js"], - "parserOptions": { - "project": true, - "tsconfigRootDir": "__dirname" - } -} diff --git a/telemetry/packages/public-app/app/cards.tsx b/telemetry/packages/public-app/app/cards.tsx index eaa2f023..973d20a0 100644 --- a/telemetry/packages/public-app/app/cards.tsx +++ b/telemetry/packages/public-app/app/cards.tsx @@ -1,60 +1,60 @@ -import { Card, Title, Text, Grid } from '@tremor/react'; -import { VelocityGraph } from '@/components/velocity-graph'; -import { useState } from 'react'; -import { DisplacementChart } from '@/components/displacement-chart'; -import Image from 'next/image'; -import { LaunchTime } from '@/components/launch-time'; -import LevitationHeight from '@/components/levitation-height'; -import { SocialIcons } from '@/components/social-icons'; -import ThemeSwitch from '@/components/theme-switch'; +import { DisplacementChart } from "@/components/displacement-chart"; +import { LaunchTime } from "@/components/launch-time"; +import LevitationHeight from "@/components/levitation-height"; +import { SocialIcons } from "@/components/social-icons"; +import ThemeSwitch from "@/components/theme-switch"; +import { VelocityGraph } from "@/components/velocity-graph"; +import { Card, Grid, Text, Title } from "@tremor/react"; +import Image from "next/image"; +import { useState } from "react"; /** * The cards that are displayed on the dashboard. */ const CARDS = { - VELOCITY: , - ACCELERATION: , - LEVITATION: , + VELOCITY: , + ACCELERATION: , + LEVITATION: , }; -type Card = keyof typeof CARDS; +type CardType = keyof typeof CARDS; export default function Cards() { - const [selected, setSelected] = useState('VELOCITY'); + const [selected, setSelected] = useState("VELOCITY"); - const selectedCardComponent = CARDS[selected]; - const otherCards = (Object.keys(CARDS) as Card[]).filter( - (c) => c !== selected, - ); + const selectedCardComponent = CARDS[selected]; + const otherCards = (Object.keys(CARDS) as CardType[]).filter( + (c) => c !== selected, + ); - return ( -
-
- -
-
- Dashboard - Telemetric data stream from on device sensors. -
- - {selectedCardComponent} - - {otherCards.map((c) => ( - - ))} - -
-
- Theme: -
- - -
-
- ); + return ( +
+
+ +
+
+ Dashboard + Telemetric data stream from on device sensors. +
+ + {selectedCardComponent} + + {otherCards.map((c) => ( + + ))} + +
+
+ Theme: +
+ + +
+
+ ); } /** @@ -62,26 +62,26 @@ export default function Cards() { * @returns The HYPED logo as an image. */ const HypedImage = () => { - const common = { - alt: 'HYPED Logo, with a red E resembling 3 stacked hyperloop pods', - width: 200, - height: 50, - }; + const common = { + alt: "HYPED Logo, with a red E resembling 3 stacked hyperloop pods", + width: 200, + height: 50, + }; - return ( - <> - {common.alt} - {common.alt} - - ); + return ( + <> + {common.alt} + {common.alt} + + ); }; diff --git a/telemetry/packages/public-app/app/globals.css b/telemetry/packages/public-app/app/globals.css index 7fc43a17..f013ef43 100644 --- a/telemetry/packages/public-app/app/globals.css +++ b/telemetry/packages/public-app/app/globals.css @@ -3,14 +3,14 @@ @tailwind utilities; .fade-in-image { - animation: fadeIn 6s; + animation: fadeIn 6s; } @keyframes fadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } } diff --git a/telemetry/packages/public-app/app/layout.tsx b/telemetry/packages/public-app/app/layout.tsx index 290172b4..a8136d79 100644 --- a/telemetry/packages/public-app/app/layout.tsx +++ b/telemetry/packages/public-app/app/layout.tsx @@ -6,20 +6,20 @@ import Providers from './providers'; const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { - title: 'HYPED | Public App', - description: 'Public app for HYPED Telemetry', + title: 'HYPED | Public App', + description: 'Public app for HYPED Telemetry', }; export default function RootLayout({ - children, + children, }: { - children: React.ReactNode; + children: React.ReactNode; }) { - return ( - - - {children} - - - ); + return ( + + + {children} + + + ); } diff --git a/telemetry/packages/public-app/app/loading-screen.tsx b/telemetry/packages/public-app/app/loading-screen.tsx index 38381acc..cf98cc8e 100644 --- a/telemetry/packages/public-app/app/loading-screen.tsx +++ b/telemetry/packages/public-app/app/loading-screen.tsx @@ -6,26 +6,26 @@ import Image from 'next/image'; * Loading screen statuses. Used to simulate a loading process. */ export const STATUSES = { - AUTHENTICATING: 'authenticating', - PROCESSING: 'processing', - GRANTING_ACCESS: 'granting-access', - DONE: 'done', + AUTHENTICATING: 'authenticating', + PROCESSING: 'processing', + GRANTING_ACCESS: 'granting-access', + DONE: 'done', }; export type Status = (typeof STATUSES)[keyof typeof STATUSES]; export default function LoadingScreen({ status }: { status: Status }) { - const text: Record = { - [STATUSES.AUTHENTICATING]: 'Authenticating...', - [STATUSES.PROCESSING]: 'Performing Security Checks...', - [STATUSES.GRANTING_ACCESS]: 'Granting access...', - }; + const text: Record = { + [STATUSES.AUTHENTICATING]: 'Authenticating...', + [STATUSES.PROCESSING]: 'Performing Security Checks...', + [STATUSES.GRANTING_ACCESS]: 'Granting access...', + }; - return ( -
- hyped logo -
-

{text[status]}

-
-
- ); + return ( +
+ hyped logo +
+

{text[status]}

+
+
+ ); } diff --git a/telemetry/packages/public-app/app/page.tsx b/telemetry/packages/public-app/app/page.tsx index 661ea6c7..308c8ee9 100644 --- a/telemetry/packages/public-app/app/page.tsx +++ b/telemetry/packages/public-app/app/page.tsx @@ -3,28 +3,28 @@ import LoadingScreen from '@/app/loading-screen'; import { useEffect, useState } from 'react'; import Cards from './cards'; -import { STATUSES, Status } from './loading-screen'; +import { STATUSES, type Status } from './loading-screen'; export default function Home() { - const [status, setStatus] = useState(STATUSES.AUTHENTICATING); + const [status, setStatus] = useState(STATUSES.AUTHENTICATING); - // Simulate a loading process - // Not the best way to do this but it'll do - useEffect(() => { - setTimeout(() => { - setStatus(STATUSES.PROCESSING); - }, 1500); - setTimeout(() => { - setStatus(STATUSES.GRANTING_ACCESS); - }, 3000); - setTimeout(() => { - setStatus(STATUSES.DONE); - }, 4500); - }, []); + // Simulate a loading process + // Not the best way to do this but it'll do + useEffect(() => { + setTimeout(() => { + setStatus(STATUSES.PROCESSING); + }, 1500); + setTimeout(() => { + setStatus(STATUSES.GRANTING_ACCESS); + }, 3000); + setTimeout(() => { + setStatus(STATUSES.DONE); + }, 4500); + }, []); - return ( -
- {status !== STATUSES.DONE ? : } -
- ); + return ( +
+ {status !== STATUSES.DONE ? : } +
+ ); } diff --git a/telemetry/packages/public-app/app/providers.tsx b/telemetry/packages/public-app/app/providers.tsx index acdfd66a..9b5963e4 100644 --- a/telemetry/packages/public-app/app/providers.tsx +++ b/telemetry/packages/public-app/app/providers.tsx @@ -6,7 +6,7 @@ import { QueryClient, QueryClientProvider } from 'react-query'; const queryClient = new QueryClient(); export default function Providers({ children }: { children: React.ReactNode }) { - return ( - {children} - ); + return ( + {children} + ); } diff --git a/telemetry/packages/public-app/biome.json b/telemetry/packages/public-app/biome.json new file mode 100644 index 00000000..784b6d85 --- /dev/null +++ b/telemetry/packages/public-app/biome.json @@ -0,0 +1,3 @@ +{ + "extends": ["../../biome.json"] +} diff --git a/telemetry/packages/public-app/components/displacement-chart.tsx b/telemetry/packages/public-app/components/displacement-chart.tsx index 77c60af3..bf8982a0 100644 --- a/telemetry/packages/public-app/components/displacement-chart.tsx +++ b/telemetry/packages/public-app/components/displacement-chart.tsx @@ -1,54 +1,54 @@ 'use client'; +import { getDisplacement } from '@/helpers'; +import type { HistoricalValueResponse } from '@hyped/telemetry-types'; import { Card, LineChart, Metric, Text } from '@tremor/react'; -import { useQuery } from 'react-query'; import format from 'date-fns/format'; -import { getDisplacement } from '@/helpers'; -import { HistoricalValueResponse } from '@hyped/telemetry-types'; +import { useQuery } from 'react-query'; /** * The displacement chart. Fetches displacement data from the server. * @returns The displacement chart. */ export const DisplacementChart = () => { - const { data, isLoading, error } = useQuery( - 'displacement', - async () => await getDisplacement(), - { - refetchInterval: 1000, - }, - ); + const { data, isLoading, error } = useQuery( + 'displacement', + async () => await getDisplacement(), + { + refetchInterval: 1000, + }, + ); - if (isLoading) - return ( - - Displacement - Loading... - - ); - if (error) - return ( - - Displacement - Error fetching displacement data - - ); + if (isLoading) + return ( + + Displacement + Loading... + + ); + if (error) + return ( + + Displacement + Error fetching displacement data + + ); - const displacementData = formatData(data); + const displacementData = formatData(data); - return ( - - Displacement - - - ); + return ( + + Displacement + + + ); }; /** @@ -57,12 +57,12 @@ export const DisplacementChart = () => { * @returns The formatted data. */ const formatData = (data: HistoricalValueResponse | undefined) => - data - ? data.map((d) => { - const time = new Date(d.timestamp); - return { - time: format(time, 'HH:mm:ss'), - displacement: d.value, - }; - }) - : []; + data + ? data.map((d) => { + const time = new Date(d.timestamp); + return { + time: format(time, 'HH:mm:ss'), + displacement: d.value, + }; + }) + : []; diff --git a/telemetry/packages/public-app/components/launch-time.tsx b/telemetry/packages/public-app/components/launch-time.tsx index 670db0d3..c03f0f27 100644 --- a/telemetry/packages/public-app/components/launch-time.tsx +++ b/telemetry/packages/public-app/components/launch-time.tsx @@ -1,43 +1,43 @@ 'use client'; +import { getLaunchTime } from '@/helpers'; import { Card, Metric, Text } from '@tremor/react'; import { BadgeDelta } from '@tremor/react'; import { useQuery } from 'react-query'; -import { getLaunchTime } from '@/helpers'; /** * The launch time card. Fetches the launch time from the server. * @returns The launch time card. */ export function LaunchTime() { - const { data } = useQuery('launch-time', async () => await getLaunchTime(), { - refetchInterval: 900, - }); + const { data } = useQuery('launch-time', async () => await getLaunchTime(), { + refetchInterval: 900, + }); - // Calculate the time since launch - const launchTime = data?.launchTime || -1; - const currentTimeInSeconds = Date.now(); - const timeSinceLaunch = - launchTime > 0 ? (currentTimeInSeconds - launchTime) / 1000 : -1; + // Calculate the time since launch + const launchTime = data?.launchTime || -1; + const currentTimeInSeconds = Date.now(); + const timeSinceLaunch = + launchTime > 0 ? (currentTimeInSeconds - launchTime) / 1000 : -1; - return ( - -
- Time since launch - - {timeSinceLaunch > -1 - ? `${Math.floor(timeSinceLaunch / 60)}m ${Math.floor(timeSinceLaunch % 60)}s` - : 'Not launched yet'} - -
-
- 0 ? 'moderateIncrease' : 'moderateDecrease'} - > - {launchTime > 0 ? 'LIVE' : 'Not Launched'} - -
-
- ); + return ( + +
+ Time since launch + + {timeSinceLaunch > -1 + ? `${Math.floor(timeSinceLaunch / 60)}m ${Math.floor(timeSinceLaunch % 60)}s` + : 'Not launched yet'} + +
+
+ 0 ? 'moderateIncrease' : 'moderateDecrease'} + > + {launchTime > 0 ? 'LIVE' : 'Not Launched'} + +
+
+ ); } diff --git a/telemetry/packages/public-app/components/levitation-height.tsx b/telemetry/packages/public-app/components/levitation-height.tsx index 3d1db34f..d8254b4f 100644 --- a/telemetry/packages/public-app/components/levitation-height.tsx +++ b/telemetry/packages/public-app/components/levitation-height.tsx @@ -1,99 +1,99 @@ 'use client'; -import { useState } from 'react'; -import { useQuery } from 'react-query'; -import { LevitationHeightResponse } from '@hyped/telemetry-types'; +import { getLevitationHeight } from '@/helpers'; +import type { LevitationHeightResponse } from '@hyped/telemetry-types'; import { - Card, - ProgressBar, - Text, - Flex, - Button, - Metric, - BadgeDelta, - Title, + BadgeDelta, + Button, + Card, + Flex, + Metric, + ProgressBar, + Text, + Title, } from '@tremor/react'; -import { getLevitationHeight } from '@/helpers'; +import { useState } from 'react'; +import { useQuery } from 'react-query'; export default function LevitationHeight() { - const [showMore, setShowMore] = useState(false); + const [showMore, setShowMore] = useState(false); - const { data, isLoading, error } = useQuery( - 'levitation-height', - async () => await getLevitationHeight(), - { - refetchInterval: 1000, - }, - ); + const { data, isLoading, error } = useQuery( + 'levitation-height', + async () => await getLevitationHeight(), + { + refetchInterval: 1000, + }, + ); - if (isLoading || !data) - return ( - - Levitation Height - Loading... - - ); - if (error) - return ( - - Levitation Height - Error fetching levitation height data - - ); + if (isLoading || !data) + return ( + + Levitation Height + Loading... + + ); + if (error) + return ( + + Levitation Height + Error fetching levitation height data + + ); - /** - * Checks if the pod is levitated based on the levitation height data. - * If any of the levitation height values are greater than 0, the pod is considered levitated. - */ - const isLevitated = data - ? Object.keys(data).some((key) => data[key]?.value > 0) - : false; + /** + * Checks if the pod is levitated based on the levitation height data. + * If any of the levitation height values are greater than 0, the pod is considered levitated. + */ + const isLevitated = data + ? Object.keys(data).some((key) => data[key]?.value > 0) + : false; - const keys = Object.keys(data); - const numSensors = keys.length; + const keys = Object.keys(data); + const numSensors = keys.length; - return ( - - - Levitation Height - {data ? ( - - Elevated - - ) : null} - - Pod Height - {data ? ( - keys - // unless showMore is true, only show the first 4 (if possible) or half of the data - .slice(0, showMore ? numSensors : numSensors > 4 ? 4 : numSensors / 2) - .map((levitationHeight) => { - const { id, value } = data[levitationHeight]; - return ( -
- - {id} - {`${value} mm`} - - -
- ); - }) - ) : ( -
no data
- )} - - - -
- ); + return ( + + + Levitation Height + {data ? ( + + Elevated + + ) : null} + + Pod Height + {data ? ( + keys + // unless showMore is true, only show the first 4 (if possible) or half of the data + .slice(0, showMore ? numSensors : numSensors > 4 ? 4 : numSensors / 2) + .map((levitationHeight) => { + const { id, value } = data[levitationHeight]; + return ( +
+ + {id} + {`${value} mm`} + + +
+ ); + }) + ) : ( +
no data
+ )} + + + +
+ ); } diff --git a/telemetry/packages/public-app/components/social-icons.tsx b/telemetry/packages/public-app/components/social-icons.tsx index 49deb622..2da8b5b3 100644 --- a/telemetry/packages/public-app/components/social-icons.tsx +++ b/telemetry/packages/public-app/components/social-icons.tsx @@ -1,38 +1,38 @@ -import { Linkedin, Facebook, Instagram, Github, Twitter } from 'lucide-react'; +import { Facebook, Github, Instagram, Linkedin, Twitter } from 'lucide-react'; import Link from 'next/link'; /** * Defines the social media icons and their links. */ const SOCIAL_ICONS = { - facebook: { - link: 'https://www.facebook.com/hypedinburgh/', - component: ( - - ), - }, - github: { - link: 'https://github.com/hyp-ed/hyped-2024', - component: , - }, - instagram: { - link: 'https://www.instagram.com/hypedinburgh/', - component: ( - - ), - }, - linkedIn: { - link: 'https://www.linkedin.com/company/hyp-ed/', - component: ( - - ), - }, - twitter: { - link: 'https://twitter.com/hyped_hyperloop', - component: ( - - ), - }, + facebook: { + link: 'https://www.facebook.com/hypedinburgh/', + component: ( + + ), + }, + github: { + link: 'https://github.com/hyp-ed/hyped-2024', + component: , + }, + instagram: { + link: 'https://www.instagram.com/hypedinburgh/', + component: ( + + ), + }, + linkedIn: { + link: 'https://www.linkedin.com/company/hyp-ed/', + component: ( + + ), + }, + twitter: { + link: 'https://twitter.com/hyped_hyperloop', + component: ( + + ), + }, }; /** @@ -40,15 +40,15 @@ const SOCIAL_ICONS = { * @returns The social media icons. */ export const SocialIcons = () => { - const socialIcons = Object.values(SOCIAL_ICONS); + const socialIcons = Object.entries(SOCIAL_ICONS); - return ( -
- {socialIcons.map(({ link, component }, index) => ( - - {component} - - ))} -
- ); + return ( +
+ {socialIcons.map(([key, { link, component }]) => ( + + {component} + + ))} +
+ ); }; diff --git a/telemetry/packages/public-app/components/theme-switch.tsx b/telemetry/packages/public-app/components/theme-switch.tsx index 693b06bd..8e11ca47 100644 --- a/telemetry/packages/public-app/components/theme-switch.tsx +++ b/telemetry/packages/public-app/components/theme-switch.tsx @@ -2,21 +2,21 @@ import { Switch } from '@tremor/react'; import { useEffect, useState } from 'react'; function ThemeSwitch() { - const [theme, setTheme] = useState('dark'); + const [theme, setTheme] = useState('dark'); - useEffect(() => { - document.body.classList.remove('light', 'dark'); - document.body.classList.add(theme); - }, [theme]); + useEffect(() => { + document.body.classList.remove('light', 'dark'); + document.body.classList.add(theme); + }, [theme]); - const [enabled, setEnabled] = useState(theme == 'dark'); + const [enabled, setEnabled] = useState(theme === 'dark'); - const handleThemeChange = (enabled: boolean) => { - setTheme(enabled ? 'dark' : 'light'); - setEnabled(enabled); - }; + const handleThemeChange = (enabled: boolean) => { + setTheme(enabled ? 'dark' : 'light'); + setEnabled(enabled); + }; - return ; + return ; } export default ThemeSwitch; diff --git a/telemetry/packages/public-app/components/velocity-graph.tsx b/telemetry/packages/public-app/components/velocity-graph.tsx index 9c1df60e..2c2ef2e5 100644 --- a/telemetry/packages/public-app/components/velocity-graph.tsx +++ b/telemetry/packages/public-app/components/velocity-graph.tsx @@ -1,7 +1,7 @@ 'use client'; import { getVelocity } from '@/helpers'; -import { HistoricalValueResponse } from '@hyped/telemetry-types'; +import type { HistoricalValueResponse } from '@hyped/telemetry-types'; import { Card, LineChart, Metric, Text } from '@tremor/react'; import { format } from 'date-fns'; import { useQuery } from 'react-query'; @@ -11,44 +11,44 @@ import { useQuery } from 'react-query'; * @returns The velocity chart. */ export const VelocityGraph = () => { - const { data, isLoading, error } = useQuery( - 'velocity', - async () => await getVelocity(), - { - refetchInterval: 1000, - }, - ); + const { data, isLoading, error } = useQuery( + 'velocity', + async () => await getVelocity(), + { + refetchInterval: 1000, + }, + ); - if (isLoading) - return ( - - Velocity - Loading... - - ); - if (error) - return ( - - Velocity - Error fetching velocity data - - ); + if (isLoading) + return ( + + Velocity + Loading... + + ); + if (error) + return ( + + Velocity + Error fetching velocity data + + ); - const velocityData = formatData(data); + const velocityData = formatData(data); - return ( - - Velocity - - - ); + return ( + + Velocity + + + ); }; /** @@ -57,12 +57,12 @@ export const VelocityGraph = () => { * @returns The formatted data. */ const formatData = (data: HistoricalValueResponse | undefined) => - data - ? data.map((d) => { - const time = new Date(d.timestamp); - return { - time: format(time, 'HH:mm:ss'), - velocity: d.value, - }; - }) - : []; + data + ? data.map((d) => { + const time = new Date(d.timestamp); + return { + time: format(time, 'HH:mm:ss'), + velocity: d.value, + }; + }) + : []; diff --git a/telemetry/packages/public-app/helpers.ts b/telemetry/packages/public-app/helpers.ts index e1039401..8c9fa767 100644 --- a/telemetry/packages/public-app/helpers.ts +++ b/telemetry/packages/public-app/helpers.ts @@ -1,8 +1,8 @@ -import { LevitationHeightResponse } from '@hyped/telemetry-types'; -import { - DisplacementResponse, - LaunchTimeResponse, - VelocityResponse, +import type { LevitationHeightResponse } from '@hyped/telemetry-types'; +import type { + DisplacementResponse, + LaunchTimeResponse, + VelocityResponse, } from '@hyped/telemetry-types/dist/server/responses'; /** @@ -18,15 +18,15 @@ const ONE_MINUTE = 60; * @returns The historical displacement data. */ export const getDisplacement = async ( - prev: number = ONE_MINUTE, + prev: number = ONE_MINUTE, ): Promise => { - const now = new Date().getTime(); - const start = now - prev * 1000; - const response = await fetch( - `${SERVER_ENDPOINT}/displacement?start=${start}`, - ); - if (response.status !== 200) throw new Error('Failed to fetch displacement'); - return response.json() as Promise; + const now = new Date().getTime(); + const start = now - prev * 1000; + const response = await fetch( + `${SERVER_ENDPOINT}/displacement?start=${start}`, + ); + if (response.status !== 200) throw new Error('Failed to fetch displacement'); + return response.json() as Promise; }; /** @@ -35,16 +35,16 @@ export const getDisplacement = async ( * @returns The historical levitation height data. */ export const getLevitationHeight = async ( - prev: number = ONE_MINUTE, + prev: number = ONE_MINUTE, ): Promise => { - const now = new Date().getTime(); - const start = now - prev * 1000; - const response = await fetch( - `${SERVER_ENDPOINT}/levitation-height?start=${start}`, - ); - if (response.status !== 200) - throw new Error('Failed to fetch levitation height'); - return response.json() as Promise; + const now = new Date().getTime(); + const start = now - prev * 1000; + const response = await fetch( + `${SERVER_ENDPOINT}/levitation-height?start=${start}`, + ); + if (response.status !== 200) + throw new Error('Failed to fetch levitation height'); + return response.json() as Promise; }; /** @@ -53,13 +53,13 @@ export const getLevitationHeight = async ( * @returns The historical velocity data. */ export const getVelocity = async ( - prev: number = ONE_MINUTE, + prev: number = ONE_MINUTE, ): Promise => { - const now = new Date().getTime(); - const start = now - prev * 1000; - const response = await fetch(`${SERVER_ENDPOINT}/velocity?start=${start}`); - if (response.status !== 200) throw new Error('Failed to fetch velocity'); - return response.json() as Promise; + const now = new Date().getTime(); + const start = now - prev * 1000; + const response = await fetch(`${SERVER_ENDPOINT}/velocity?start=${start}`); + if (response.status !== 200) throw new Error('Failed to fetch velocity'); + return response.json() as Promise; }; /** @@ -67,7 +67,7 @@ export const getVelocity = async ( * @returns The launch time of the pod. */ export const getLaunchTime = async (): Promise => { - const response = await fetch(`${SERVER_ENDPOINT}/launch-time`); - if (response.status !== 200) throw new Error('Failed to fetch launch time'); - return response.json() as Promise; + const response = await fetch(`${SERVER_ENDPOINT}/launch-time`); + if (response.status !== 200) throw new Error('Failed to fetch launch time'); + return response.json() as Promise; }; diff --git a/telemetry/packages/public-app/next.config.js b/telemetry/packages/public-app/next.config.js index 826aa5ad..66dbab00 100644 --- a/telemetry/packages/public-app/next.config.js +++ b/telemetry/packages/public-app/next.config.js @@ -1,15 +1,15 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - webpack: (config, context) => { - // Enable polling based on env variable being set - if (true) { - config.watchOptions = { - poll: 500, - aggregateTimeout: 300, - }; - } - return config; - }, + webpack: (config, context) => { + // Enable polling based on env variable being set + if (process.env.NEXT_WATCH) { + config.watchOptions = { + poll: 500, + aggregateTimeout: 300, + }; + } + return config; + }, }; module.exports = nextConfig; diff --git a/telemetry/packages/public-app/package.json b/telemetry/packages/public-app/package.json index 3b46ff9b..fc845c67 100644 --- a/telemetry/packages/public-app/package.json +++ b/telemetry/packages/public-app/package.json @@ -1,45 +1,43 @@ { - "name": "@hyped/public-app", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev -p 3001", - "dev:test": "next dev -p 3001", - "build": "next build", - "start": "next start", - "lint": "eslint --ext .ts,.tsx . --max-warnings 0 --report-unused-disable-directives", - "lint:fix": "eslint --fix --ext .ts,.tsx ." - }, - "dependencies": { - "@preact/signals-react": "^1.3.6", - "@radix-ui/react-switch": "^1.0.3", - "@tremor/react": "^3.9.2", - "class-variance-authority": "^0.7.0", - "clsx": "^2.0.0", - "date-fns": "^2.30.0", - "influxdb": "^0.0.1", - "lucide-react": "^0.292.0", - "next": "^14.1.0", - "next-themes": "^0.2.1", - "react": "^18", - "react-dom": "^18", - "react-google-charts": "^4.0.1", - "react-query": "^3.39.3", - "react-switch": "^7.0.0", - "tailwind-merge": "^2.0.0", - "tailwindcss-animate": "^1.0.7" - }, - "devDependencies": { - "@hyped/eslint-config": "workspace:*", - "@hyped/telemetry-types": "workspace:*", - "@hyped/tsconfig": "workspace:*", - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - "autoprefixer": "^10", - "eslint-config-next": "13.5.4", - "postcss": "^8", - "tailwindcss": "^3", - "typescript": "^5" - } + "name": "@hyped/public-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev -p 3001", + "dev:test": "next dev -p 3001", + "build": "next build", + "start": "next start", + "lint": "biome lint .", + "lint:fix": "biome check --write ." + }, + "dependencies": { + "@preact/signals-react": "^1.3.6", + "@radix-ui/react-switch": "^1.0.3", + "@tremor/react": "^3.9.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.0.0", + "date-fns": "^2.30.0", + "influxdb": "^0.0.1", + "lucide-react": "^0.292.0", + "next": "^14.1.0", + "next-themes": "^0.2.1", + "react": "^18", + "react-dom": "^18", + "react-google-charts": "^4.0.1", + "react-query": "^3.39.3", + "react-switch": "^7.0.0", + "tailwind-merge": "^2.0.0", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@hyped/telemetry-types": "workspace:*", + "@hyped/tsconfig": "workspace:*", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10", + "postcss": "^8", + "tailwindcss": "^3", + "typescript": "^5" + } } diff --git a/telemetry/packages/public-app/postcss.config.js b/telemetry/packages/public-app/postcss.config.js index 33ad091d..e873f1a4 100644 --- a/telemetry/packages/public-app/postcss.config.js +++ b/telemetry/packages/public-app/postcss.config.js @@ -1,6 +1,6 @@ module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/telemetry/packages/public-app/tailwind.config.js b/telemetry/packages/public-app/tailwind.config.js index d4fb5c27..8960c622 100644 --- a/telemetry/packages/public-app/tailwind.config.js +++ b/telemetry/packages/public-app/tailwind.config.js @@ -1,142 +1,141 @@ /** @type {import('tailwindcss').Config} */ -/* eslint-disable max-len */ module.exports = { - darkMode: 'class', - content: [ - './app/**/*.{js,ts,jsx,tsx}', - './pages/**/*.{js,ts,jsx,tsx}', - './components/**/*.{js,ts,jsx,tsx}', - '../../node_modules/@tremor/**/*.{js,ts,jsx,tsx}', - './node_modules/@tremor/**/*.{js,ts,jsx,tsx}', - ], - theme: { - transparent: 'transparent', - current: 'currentColor', - extend: { - colors: { - // HYPED COLORS - 'hyped-foreground': '#ffffff', - 'hyped-background': '#000000', - 'hyped-red': '#af1f24', - 'hyped-light-gray': '#edeeec', - 'openmct-light-gray': '#535353', - 'openmct-dark-gray': '#222222', - // light mode - tremor: { - brand: { - faint: '#eff6ff', // blue-50 - muted: '#bfdbfe', // blue-200 - subtle: '#60a5fa', // blue-400 - DEFAULT: '#3b82f6', // blue-500 - emphasis: '#1d4ed8', // blue-700 - inverted: '#ffffff', // white - }, - background: { - muted: '#f9fafb', // gray-50 - subtle: '#f3f4f6', // gray-100 - DEFAULT: '#ffffff', // white - emphasis: '#374151', // gray-700 - }, - border: { - DEFAULT: '#e5e7eb', // gray-200 - }, - ring: { - DEFAULT: '#e5e7eb', // gray-200 - }, - content: { - subtle: '#9ca3af', // gray-400 - DEFAULT: '#6b7280', // gray-500 - emphasis: '#374151', // gray-700 - strong: '#111827', // gray-900 - inverted: '#ffffff', // white - }, - }, - // dark mode - 'dark-tremor': { - brand: { - faint: '#0B1229', // custom - muted: '#172554', // blue-950 - subtle: '#1e40af', // blue-800 - DEFAULT: '#3b82f6', // blue-500 - emphasis: '#60a5fa', // blue-400 - inverted: '#030712', // gray-950 - }, - background: { - muted: '#131A2B', // custom - subtle: '#1f2937', // gray-800 - DEFAULT: '#111827', // gray-900 - emphasis: '#d1d5db', // gray-300 - }, - border: { - DEFAULT: '#1f2937', // gray-800 - }, - ring: { - DEFAULT: '#1f2937', // gray-800 - }, - content: { - subtle: '#4b5563', // gray-600 - DEFAULT: '#6b7280', // gray-600 - emphasis: '#e5e7eb', // gray-200 - strong: '#f9fafb', // gray-50 - inverted: '#000000', // black - }, - }, - }, - boxShadow: { - // light - 'tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)', - 'tremor-card': - '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)', - 'tremor-dropdown': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - // dark - 'dark-tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)', - 'dark-tremor-card': - '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)', - 'dark-tremor-dropdown': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - }, - borderRadius: { - 'tremor-small': '0.375rem', - 'tremor-default': '0.5rem', - 'tremor-full': '9999px', - }, - fontSize: { - 'tremor-label': ['0.75rem'], - 'tremor-default': ['0.875rem', { lineHeight: '1.25rem' }], - 'tremor-title': ['1.125rem', { lineHeight: '1.75rem' }], - 'tremor-metric': ['1.875rem', { lineHeight: '2.25rem' }], - }, - }, - }, - safelist: [ - { - pattern: - /^(bg-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - variants: ['hover', 'ui-selected'], - }, - { - pattern: - /^(text-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - variants: ['hover', 'ui-selected'], - }, - { - pattern: - /^(border-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - variants: ['hover', 'ui-selected'], - }, - { - pattern: - /^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - }, - { - pattern: - /^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - }, - { - pattern: - /^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, - }, - ], - plugins: [require('@headlessui/tailwindcss')], + darkMode: 'class', + content: [ + './app/**/*.{js,ts,jsx,tsx}', + './pages/**/*.{js,ts,jsx,tsx}', + './components/**/*.{js,ts,jsx,tsx}', + '../../node_modules/@tremor/**/*.{js,ts,jsx,tsx}', + './node_modules/@tremor/**/*.{js,ts,jsx,tsx}', + ], + theme: { + transparent: 'transparent', + current: 'currentColor', + extend: { + colors: { + // HYPED COLORS + 'hyped-foreground': '#ffffff', + 'hyped-background': '#000000', + 'hyped-red': '#af1f24', + 'hyped-light-gray': '#edeeec', + 'openmct-light-gray': '#535353', + 'openmct-dark-gray': '#222222', + // light mode + tremor: { + brand: { + faint: '#eff6ff', // blue-50 + muted: '#bfdbfe', // blue-200 + subtle: '#60a5fa', // blue-400 + DEFAULT: '#3b82f6', // blue-500 + emphasis: '#1d4ed8', // blue-700 + inverted: '#ffffff', // white + }, + background: { + muted: '#f9fafb', // gray-50 + subtle: '#f3f4f6', // gray-100 + DEFAULT: '#ffffff', // white + emphasis: '#374151', // gray-700 + }, + border: { + DEFAULT: '#e5e7eb', // gray-200 + }, + ring: { + DEFAULT: '#e5e7eb', // gray-200 + }, + content: { + subtle: '#9ca3af', // gray-400 + DEFAULT: '#6b7280', // gray-500 + emphasis: '#374151', // gray-700 + strong: '#111827', // gray-900 + inverted: '#ffffff', // white + }, + }, + // dark mode + 'dark-tremor': { + brand: { + faint: '#0B1229', // custom + muted: '#172554', // blue-950 + subtle: '#1e40af', // blue-800 + DEFAULT: '#3b82f6', // blue-500 + emphasis: '#60a5fa', // blue-400 + inverted: '#030712', // gray-950 + }, + background: { + muted: '#131A2B', // custom + subtle: '#1f2937', // gray-800 + DEFAULT: '#111827', // gray-900 + emphasis: '#d1d5db', // gray-300 + }, + border: { + DEFAULT: '#1f2937', // gray-800 + }, + ring: { + DEFAULT: '#1f2937', // gray-800 + }, + content: { + subtle: '#4b5563', // gray-600 + DEFAULT: '#6b7280', // gray-600 + emphasis: '#e5e7eb', // gray-200 + strong: '#f9fafb', // gray-50 + inverted: '#000000', // black + }, + }, + }, + boxShadow: { + // light + 'tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)', + 'tremor-card': + '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)', + 'tremor-dropdown': + '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', + // dark + 'dark-tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)', + 'dark-tremor-card': + '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)', + 'dark-tremor-dropdown': + '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', + }, + borderRadius: { + 'tremor-small': '0.375rem', + 'tremor-default': '0.5rem', + 'tremor-full': '9999px', + }, + fontSize: { + 'tremor-label': ['0.75rem'], + 'tremor-default': ['0.875rem', { lineHeight: '1.25rem' }], + 'tremor-title': ['1.125rem', { lineHeight: '1.75rem' }], + 'tremor-metric': ['1.875rem', { lineHeight: '2.25rem' }], + }, + }, + }, + safelist: [ + { + pattern: + /^(bg-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, + variants: ['hover', 'ui-selected'], + }, + { + pattern: + /^(text-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, + variants: ['hover', 'ui-selected'], + }, + { + pattern: + /^(border-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, + variants: ['hover', 'ui-selected'], + }, + { + pattern: + /^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, + }, + { + pattern: + /^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, + }, + { + pattern: + /^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, + }, + ], + plugins: [require('@headlessui/tailwindcss')], }; diff --git a/telemetry/packages/public-app/tsconfig.json b/telemetry/packages/public-app/tsconfig.json index 919f8a73..777a947f 100644 --- a/telemetry/packages/public-app/tsconfig.json +++ b/telemetry/packages/public-app/tsconfig.json @@ -1,20 +1,20 @@ { - "extends": "@hyped/tsconfig/base.json", - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "moduleResolution": "bundler", - "jsx": "preserve", - "plugins": [ - { - "name": "next", - }, - ], - "paths": { - "@/*": ["./*"], - }, - "allowJs": true, - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"], + "extends": "@hyped/tsconfig/base.json", + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "moduleResolution": "bundler", + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + }, + "allowJs": true + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] } diff --git a/telemetry/packages/server/.eslintrc.js b/telemetry/packages/server/.eslintrc.js deleted file mode 100644 index acab2de9..00000000 --- a/telemetry/packages/server/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ['@hyped/eslint-config/basic.js'], - parserOptions: { - project: true, - tsconfigRootDir: __dirname, - }, -}; diff --git a/telemetry/packages/server/biome.json b/telemetry/packages/server/biome.json new file mode 100644 index 00000000..708223fd --- /dev/null +++ b/telemetry/packages/server/biome.json @@ -0,0 +1,8 @@ +{ + "extends": ["../../biome.json"], + "javascript": { + "parser": { + "unsafeParameterDecoratorsEnabled": true + } + } +} diff --git a/telemetry/packages/server/nest-cli.json b/telemetry/packages/server/nest-cli.json index f9aa683b..95538fb9 100644 --- a/telemetry/packages/server/nest-cli.json +++ b/telemetry/packages/server/nest-cli.json @@ -1,8 +1,8 @@ { - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } } diff --git a/telemetry/packages/server/package.json b/telemetry/packages/server/package.json index d29c973b..664cec7f 100644 --- a/telemetry/packages/server/package.json +++ b/telemetry/packages/server/package.json @@ -1,54 +1,53 @@ { - "name": "@hyped/telemetry-server", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "nest start --watch", - "dev:test": "nest start --watch", - "build": "nest build", - "start": "nest start", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "lint": "eslint \"src/**/*.ts\" --max-warnings 0 --report-unused-disable-directives", - "lint:fix": "eslint \"src/**/*.ts\" --fix" - }, - "dependencies": { - "@hyped/telemetry-constants": "workspace:*", - "@influxdata/influxdb-client": "^1.33.1", - "@nestjs/common": "^9.2.1", - "@nestjs/core": "^9.2.1", - "@nestjs/microservices": "^9.2.1", - "@nestjs/platform-express": "^9.2.1", - "@nestjs/platform-socket.io": "^9.4.2", - "@nestjs/websockets": "^9.4.2", - "dotenv": "^16.0.3", - "env-var": "^7.3.0", - "mqtt": "^4.3.7", - "nanoid": "^3.3.6", - "nest-mqtt": "^0.2.0", - "nest-winston": "^1.9.1", - "random": "^3.0.6", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.8.0", - "socket.io": "^4.6.2", - "socket.io-client": "^4.6.2", - "winston": "^3.8.2", - "winston-transport": "^4.6.0", - "zod": "^3.21.4" - }, - "devDependencies": { - "@hyped/telemetry-types": "workspace:*", - "@hyped/eslint-config": "workspace:*", - "@hyped/tsconfig": "workspace:*", - "@nestjs/cli": "^9.1.8", - "@nestjs/schematics": "^9.0.4", - "@nestjs/testing": "^9.2.1", - "@types/express": "^4.17.15", - "@types/node": "18.11.18", - "source-map-support": "^0.5.21", - "ts-loader": "^9.4.2", - "ts-node": "^10.9.1", - "tsconfig-paths": "4.1.2", - "typescript": "^5.3.3" - } + "name": "@hyped/telemetry-server", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "nest start --watch", + "dev:test": "nest start --watch", + "build": "nest build", + "start": "nest start", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "biome lint .", + "lint:fix": "biome check --write ." + }, + "dependencies": { + "@hyped/telemetry-constants": "workspace:*", + "@influxdata/influxdb-client": "^1.33.1", + "@nestjs/common": "^9.2.1", + "@nestjs/core": "^9.2.1", + "@nestjs/microservices": "^9.2.1", + "@nestjs/platform-express": "^9.2.1", + "@nestjs/platform-socket.io": "^9.4.2", + "@nestjs/websockets": "^9.4.2", + "dotenv": "^16.0.3", + "env-var": "^7.3.0", + "mqtt": "^4.3.7", + "nanoid": "^3.3.6", + "nest-mqtt": "^0.2.0", + "nest-winston": "^1.9.1", + "random": "^3.0.6", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.8.0", + "socket.io": "^4.6.2", + "socket.io-client": "^4.6.2", + "winston": "^3.8.2", + "winston-transport": "^4.6.0", + "zod": "^3.21.4" + }, + "devDependencies": { + "@hyped/telemetry-types": "workspace:*", + "@hyped/tsconfig": "workspace:*", + "@nestjs/cli": "^9.1.8", + "@nestjs/schematics": "^9.0.4", + "@nestjs/testing": "^9.2.1", + "@types/express": "^4.17.15", + "@types/node": "18.11.18", + "source-map-support": "^0.5.21", + "ts-loader": "^9.4.2", + "ts-node": "^10.9.1", + "tsconfig-paths": "4.1.2", + "typescript": "^5.3.3" + } } diff --git a/telemetry/packages/server/src/app.controller.ts b/telemetry/packages/server/src/app.controller.ts index b391b7ad..7874307b 100644 --- a/telemetry/packages/server/src/app.controller.ts +++ b/telemetry/packages/server/src/app.controller.ts @@ -1,12 +1,12 @@ import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; +import type { AppService } from './app.service'; @Controller() export class AppController { - constructor(private readonly appService: AppService) {} + constructor(private readonly appService: AppService) {} - @Get('ping') - getPing(): string { - return this.appService.getPing(); - } + @Get('ping') + getPing(): string { + return this.appService.getPing(); + } } diff --git a/telemetry/packages/server/src/app.module.ts b/telemetry/packages/server/src/app.module.ts index 6a0fadad..aafa3874 100644 --- a/telemetry/packages/server/src/app.module.ts +++ b/telemetry/packages/server/src/app.module.ts @@ -1,34 +1,34 @@ import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { PodControlsModule } from './modules/controls/PodControls.module'; import { InfluxModule } from './modules/influx/Influx.module'; +import { LiveLogsGateway } from './modules/live-logs/LiveLogs.gateway'; import { LoggerModule } from './modules/logger/Logger.module'; +import { MeasurementModule } from './modules/measurement/Measurement.module'; import { MqttClientModule } from './modules/mqtt/client/MqttClientModule'; import { MqttIngestionModule } from './modules/mqtt/ingestion/MqttIngestion.module'; import { OpenMCTModule } from './modules/openmct/OpenMCT.module'; -import { MeasurementModule } from './modules/measurement/Measurement.module'; import { FaultModule } from './modules/openmct/faults/Fault.module'; -import { PodControlsModule } from './modules/controls/PodControls.module'; -import { WarningsModule } from './modules/warnings/Warnings.module'; -import { RemoteLogsModule } from './modules/remote-logs/RemoteLogs.module'; import { PublicDataModule } from './modules/public-data/PublicData.module'; -import { LiveLogsGateway } from './modules/live-logs/LiveLogs.gateway'; +import { RemoteLogsModule } from './modules/remote-logs/RemoteLogs.module'; +import { WarningsModule } from './modules/warnings/Warnings.module'; @Module({ - imports: [ - LoggerModule, - MqttClientModule, - InfluxModule, - MqttIngestionModule, - OpenMCTModule, - MeasurementModule, - FaultModule, - PodControlsModule, - WarningsModule, - RemoteLogsModule, - PublicDataModule, - ], - controllers: [AppController], - providers: [AppService, LiveLogsGateway], + imports: [ + LoggerModule, + MqttClientModule, + InfluxModule, + MqttIngestionModule, + OpenMCTModule, + MeasurementModule, + FaultModule, + PodControlsModule, + WarningsModule, + RemoteLogsModule, + PublicDataModule, + ], + controllers: [AppController], + providers: [AppService, LiveLogsGateway], }) export class AppModule {} diff --git a/telemetry/packages/server/src/app.service.ts b/telemetry/packages/server/src/app.service.ts index 177eb6cb..6f695ca2 100644 --- a/telemetry/packages/server/src/app.service.ts +++ b/telemetry/packages/server/src/app.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; @Injectable() export class AppService { - getPing(): string { - return 'pong'; - } + getPing(): string { + return 'pong'; + } } diff --git a/telemetry/packages/server/src/main.ts b/telemetry/packages/server/src/main.ts index 0792546e..7dd12598 100644 --- a/telemetry/packages/server/src/main.ts +++ b/telemetry/packages/server/src/main.ts @@ -2,17 +2,17 @@ import * as dotenv from 'dotenv'; dotenv.config(); import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; +import { AppModule } from './app.module'; async function bootstrap() { - const app = await NestFactory.create(AppModule, { - bufferLogs: true, - }); - app.enableCors(); - app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER)); + const app = await NestFactory.create(AppModule, { + bufferLogs: true, + }); + app.enableCors(); + app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER)); - await app.listen(3000); + await app.listen(3000); } void bootstrap(); diff --git a/telemetry/packages/server/src/modules/common/types/InfluxRow.ts b/telemetry/packages/server/src/modules/common/types/InfluxRow.ts index 052efa71..f250d778 100644 --- a/telemetry/packages/server/src/modules/common/types/InfluxRow.ts +++ b/telemetry/packages/server/src/modules/common/types/InfluxRow.ts @@ -1,5 +1,5 @@ export interface InfluxRow { - _time: string; - _value: string; - podId: string; + _time: string; + _value: string; + podId: string; } diff --git a/telemetry/packages/server/src/modules/common/utils/toUnixTimestamp.ts b/telemetry/packages/server/src/modules/common/utils/toUnixTimestamp.ts index 709d8555..a27c9039 100644 --- a/telemetry/packages/server/src/modules/common/utils/toUnixTimestamp.ts +++ b/telemetry/packages/server/src/modules/common/utils/toUnixTimestamp.ts @@ -1,3 +1,3 @@ export function toUnixTimestamp(date: Date) { - return Math.floor(date.getTime() / 1000); + return Math.floor(date.getTime() / 1000); } diff --git a/telemetry/packages/server/src/modules/common/utils/zodEnumFromObjKeys.ts b/telemetry/packages/server/src/modules/common/utils/zodEnumFromObjKeys.ts index 60688f2a..a06ca187 100644 --- a/telemetry/packages/server/src/modules/common/utils/zodEnumFromObjKeys.ts +++ b/telemetry/packages/server/src/modules/common/utils/zodEnumFromObjKeys.ts @@ -1,8 +1,8 @@ import { z } from 'zod'; export function zodEnumFromObjKeys( - obj: Record, + obj: Record, ): z.ZodEnum<[K, ...K[]]> { - const [firstKey, ...otherKeys] = Object.keys(obj) as K[]; - return z.enum([firstKey, ...otherKeys]); + const [firstKey, ...otherKeys] = Object.keys(obj) as K[]; + return z.enum([firstKey, ...otherKeys]); } diff --git a/telemetry/packages/server/src/modules/controls/PodControls.controller.ts b/telemetry/packages/server/src/modules/controls/PodControls.controller.ts index 328707bd..779041d2 100644 --- a/telemetry/packages/server/src/modules/controls/PodControls.controller.ts +++ b/telemetry/packages/server/src/modules/controls/PodControls.controller.ts @@ -1,20 +1,20 @@ import { Controller, Param, Post, Query } from '@nestjs/common'; -import { PodControlsService } from './PodControls.service'; +import type { PodControlsService } from './PodControls.service'; @Controller('pods/:podId/controls') export class PodControlsController { - constructor(private podControlsService: PodControlsService) {} + constructor(private podControlsService: PodControlsService) {} - @Post('levitation-height') - setLevitationHeight( - @Param('podId') podId: string, - @Query('height') height: number, - ) { - return this.podControlsService.setLevitationHeight(height, podId); - } + @Post('levitation-height') + setLevitationHeight( + @Param('podId') podId: string, + @Query('height') height: number, + ) { + return this.podControlsService.setLevitationHeight(height, podId); + } - @Post(':control') - controlPod(@Param('control') control: string, @Param('podId') podId: string) { - return this.podControlsService.sendControlMessage(control, podId); - } + @Post(':control') + controlPod(@Param('control') control: string, @Param('podId') podId: string) { + return this.podControlsService.sendControlMessage(control, podId); + } } diff --git a/telemetry/packages/server/src/modules/controls/PodControls.module.ts b/telemetry/packages/server/src/modules/controls/PodControls.module.ts index 4e99d407..c5614ed4 100644 --- a/telemetry/packages/server/src/modules/controls/PodControls.module.ts +++ b/telemetry/packages/server/src/modules/controls/PodControls.module.ts @@ -3,7 +3,7 @@ import { PodControlsController } from './PodControls.controller'; import { PodControlsService } from './PodControls.service'; @Module({ - controllers: [PodControlsController], - providers: [PodControlsService], + controllers: [PodControlsController], + providers: [PodControlsService], }) export class PodControlsModule {} diff --git a/telemetry/packages/server/src/modules/controls/PodControls.service.ts b/telemetry/packages/server/src/modules/controls/PodControls.service.ts index ce9aa43d..ea31692d 100644 --- a/telemetry/packages/server/src/modules/controls/PodControls.service.ts +++ b/telemetry/packages/server/src/modules/controls/PodControls.service.ts @@ -1,48 +1,48 @@ -import { Injectable, LoggerService, Inject } from '@nestjs/common'; -import { MqttService } from 'nest-mqtt'; import { Logger } from '@/modules/logger/Logger.decorator'; +import { Inject, Injectable, type LoggerService } from '@nestjs/common'; +import { MqttService } from 'nest-mqtt'; @Injectable() export class PodControlsService { - constructor( - @Inject(MqttService) private readonly mqttService: MqttService, - @Logger() - private readonly logger: LoggerService, - ) {} + constructor( + @Inject(MqttService) private readonly mqttService: MqttService, + @Logger() + private readonly logger: LoggerService, + ) {} - /** - * Sends a control message to a pod. - * @param control The control message to send - * @param podId The ID of the pod - * @returns True if the message was sent successfully, false otherwise - */ - async sendControlMessage(control: string, podId: string) { - await this.mqttService.publish( - `hyped/${podId}/controls/${control}`, - control, - ); - this.logger.log( - `Control message "${control}" sent to pod "${podId}"`, - PodControlsService.name, - ); - return true; - } + /** + * Sends a control message to a pod. + * @param control The control message to send + * @param podId The ID of the pod + * @returns True if the message was sent successfully, false otherwise + */ + async sendControlMessage(control: string, podId: string) { + await this.mqttService.publish( + `hyped/${podId}/controls/${control}`, + control, + ); + this.logger.log( + `Control message "${control}" sent to pod "${podId}"`, + PodControlsService.name, + ); + return true; + } - /** - * Sets the levitation height of a pod. - * @param height The height in millimeters - * @param podId The ID of the pod - * @returns True if the message was sent successfully, false otherwise - */ - async setLevitationHeight(height: number, podId: string) { - await this.mqttService.publish( - `hyped/${podId}/controls/levitation_height`, - height.toString(), - ); - this.logger.log( - `Levitation height set to ${height}mm for pod "${podId}"`, - PodControlsService.name, - ); - return true; - } + /** + * Sets the levitation height of a pod. + * @param height The height in millimeters + * @param podId The ID of the pod + * @returns True if the message was sent successfully, false otherwise + */ + async setLevitationHeight(height: number, podId: string) { + await this.mqttService.publish( + `hyped/${podId}/controls/levitation_height`, + height.toString(), + ); + this.logger.log( + `Levitation height set to ${height}mm for pod "${podId}"`, + PodControlsService.name, + ); + return true; + } } diff --git a/telemetry/packages/server/src/modules/core/config.ts b/telemetry/packages/server/src/modules/core/config.ts index 9243ab07..010a3052 100644 --- a/telemetry/packages/server/src/modules/core/config.ts +++ b/telemetry/packages/server/src/modules/core/config.ts @@ -6,15 +6,15 @@ export const INFLUX_URL = env.get('INFLUX_URL').required().asUrlString(); export const INFLUX_TOKEN = env.get('INFLUX_TOKEN').required().asString(); export const INFLUX_ORG = env.get('INFLUX_ORG').required().asString(); export const INFLUX_TELEMETRY_BUCKET = env - .get('INFLUX_TELEMETRY_BUCKET') - .required() - .asString(); + .get('INFLUX_TELEMETRY_BUCKET') + .required() + .asString(); export const INFLUX_FAULTS_BUCKET = env - .get('INFLUX_FAULTS_BUCKET') - .required() - .asString(); + .get('INFLUX_FAULTS_BUCKET') + .required() + .asString(); export const MQTT_BROKER_HOST = env - .get('MQTT_BROKER_HOST') - .required() - .asString(); + .get('MQTT_BROKER_HOST') + .required() + .asString(); diff --git a/telemetry/packages/server/src/modules/influx/Influx.module.ts b/telemetry/packages/server/src/modules/influx/Influx.module.ts index fb4479c5..6ea244e8 100644 --- a/telemetry/packages/server/src/modules/influx/Influx.module.ts +++ b/telemetry/packages/server/src/modules/influx/Influx.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'; import { InfluxService } from './Influx.service'; @Module({ - providers: [InfluxService], - exports: [InfluxService], + providers: [InfluxService], + exports: [InfluxService], }) export class InfluxModule {} diff --git a/telemetry/packages/server/src/modules/influx/Influx.service.ts b/telemetry/packages/server/src/modules/influx/Influx.service.ts index ce6dc0d8..4925791a 100644 --- a/telemetry/packages/server/src/modules/influx/Influx.service.ts +++ b/telemetry/packages/server/src/modules/influx/Influx.service.ts @@ -1,78 +1,86 @@ -import { InfluxDB, QueryApi, WriteApi } from '@influxdata/influxdb-client'; -import { Injectable, LoggerService, OnModuleInit } from '@nestjs/common'; import { - INFLUX_TELEMETRY_BUCKET, - INFLUX_ORG, - INFLUX_TOKEN, - INFLUX_URL, - INFLUX_FAULTS_BUCKET, + INFLUX_FAULTS_BUCKET, + INFLUX_ORG, + INFLUX_TELEMETRY_BUCKET, + INFLUX_TOKEN, + INFLUX_URL, } from '@/modules/core/config'; import { Logger } from '@/modules/logger/Logger.decorator'; +import { + InfluxDB, + type QueryApi, + type WriteApi, +} from '@influxdata/influxdb-client'; +import { + Injectable, + type LoggerService, + type OnModuleInit, +} from '@nestjs/common'; import { InfluxServiceError } from './errors/InfluxServiceError'; @Injectable() export class InfluxService implements OnModuleInit { - private connection: InfluxDB; - public telemetryWrite: WriteApi; - public faultsWrite: WriteApi; - public query: QueryApi; - @Logger() - private readonly logger: LoggerService; + private connection: InfluxDB; + public telemetryWrite: WriteApi; + public faultsWrite: WriteApi; + public query: QueryApi; + @Logger() + private readonly logger: LoggerService; - async $connect() { - this.connection = new InfluxDB({ url: INFLUX_URL, token: INFLUX_TOKEN }); - this.telemetryWrite = this.connection.getWriteApi( - INFLUX_ORG, - INFLUX_TELEMETRY_BUCKET, - 'ns', - { - batchSize: 1, - }, - ); - this.faultsWrite = this.connection.getWriteApi( - INFLUX_ORG, - INFLUX_FAULTS_BUCKET, - 'ns', - { - batchSize: 1, - }, - ); - this.query = this.connection.getQueryApi(INFLUX_ORG); + async $connect() { + this.connection = new InfluxDB({ url: INFLUX_URL, token: INFLUX_TOKEN }); + this.telemetryWrite = this.connection.getWriteApi( + INFLUX_ORG, + INFLUX_TELEMETRY_BUCKET, + 'ns', + { + batchSize: 1, + }, + ); + this.faultsWrite = this.connection.getWriteApi( + INFLUX_ORG, + INFLUX_FAULTS_BUCKET, + 'ns', + { + batchSize: 1, + }, + ); + this.query = this.connection.getQueryApi(INFLUX_ORG); - // Warn if InfluxDB isn't running - try { - await this.query.collectRows('from(bucket: "_monitoring")'); - } catch (e) { - this.logger.warn( - "InfluxDB doesn't seem to be running", - InfluxService.name, - ); - } - } + // Warn if InfluxDB isn't running + try { + await this.query.collectRows('from(bucket: "_monitoring")'); + } catch (e) { + this.logger.warn( + "InfluxDB doesn't seem to be running", + InfluxService.name, + ); + } + } - async onModuleInit() { - await this.$connect(); - if (!this.telemetryWrite) { - throw new InfluxServiceError( - 'InfluxDB telemetry write API not initialized', - ); - } + async onModuleInit() { + await this.$connect(); + if (!this.telemetryWrite) { + throw new InfluxServiceError( + 'InfluxDB telemetry write API not initialized', + ); + } - if (!this.faultsWrite) { - throw new InfluxServiceError('InfluxDB faults write API not initialized'); - } + if (!this.faultsWrite) { + throw new InfluxServiceError('InfluxDB faults write API not initialized'); + } - if (!this.query) { - throw new InfluxServiceError('InfluxDB query API not initialized'); - } - } + if (!this.query) { + throw new InfluxServiceError('InfluxDB query API not initialized'); + } + } - async onModuleDestroy() { - try { - await this.telemetryWrite.close(); - await this.faultsWrite.close(); - } catch (e) { - throw new InfluxServiceError('Failed to close InfluxDB'); - } - } + async onModuleDestroy() { + try { + await this.telemetryWrite.close(); + await this.faultsWrite.close(); + } catch (e) { + throw new InfluxServiceError('Failed to close InfluxDB'); + } + } } diff --git a/telemetry/packages/server/src/modules/influx/errors/InfluxServiceError.ts b/telemetry/packages/server/src/modules/influx/errors/InfluxServiceError.ts index 9160fe64..e1ec5460 100644 --- a/telemetry/packages/server/src/modules/influx/errors/InfluxServiceError.ts +++ b/telemetry/packages/server/src/modules/influx/errors/InfluxServiceError.ts @@ -1,6 +1,6 @@ export class InfluxServiceError extends Error { - constructor(message: string) { - super(message); - this.name = 'InfluxServiceError'; - } + constructor(message: string) { + super(message); + this.name = 'InfluxServiceError'; + } } diff --git a/telemetry/packages/server/src/modules/live-logs/LiveLogs.gateway.ts b/telemetry/packages/server/src/modules/live-logs/LiveLogs.gateway.ts index 470c5462..3381f414 100644 --- a/telemetry/packages/server/src/modules/live-logs/LiveLogs.gateway.ts +++ b/telemetry/packages/server/src/modules/live-logs/LiveLogs.gateway.ts @@ -1,22 +1,22 @@ import { - SubscribeMessage, - WebSocketGateway, - WebSocketServer, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, } from '@nestjs/websockets'; -import { Server, Socket } from 'socket.io'; +import type { Server, Socket } from 'socket.io'; @WebSocketGateway({ - path: '/live-logs', - cors: { - origin: '*', - }, + path: '/live-logs', + cors: { + origin: '*', + }, }) export class LiveLogsGateway { - @WebSocketServer() - socket: Server; + @WebSocketServer() + socket: Server; - @SubscribeMessage('send-log') - handleMessage(_client: Socket, payload: unknown) { - this.socket.emit('log', payload); - } + @SubscribeMessage('send-log') + handleMessage(_client: Socket, payload: unknown) { + this.socket.emit('log', payload); + } } diff --git a/telemetry/packages/server/src/modules/live-logs/LiveLogsTransport.ts b/telemetry/packages/server/src/modules/live-logs/LiveLogsTransport.ts index 38c1634a..50db4308 100644 --- a/telemetry/packages/server/src/modules/live-logs/LiveLogsTransport.ts +++ b/telemetry/packages/server/src/modules/live-logs/LiveLogsTransport.ts @@ -1,33 +1,33 @@ -import Transport from 'winston-transport'; +import type { Socket } from 'socket.io'; import { io } from 'socket.io-client'; -import { Socket } from 'socket.io'; -import { DefaultEventsMap } from 'socket.io/dist/typed-events'; +import type { DefaultEventsMap } from 'socket.io/dist/typed-events'; +import Transport from 'winston-transport'; export class LiveLogsTransport extends Transport { - constructor() { - super(); - this.init(); - } + constructor() { + super(); + this.init(); + } - private socket: Socket; + private socket: Socket; - // iniitalise the socket connection to the server - init() { - const socket = io('http://localhost:3000', { - path: '/live-logs', - }); + // iniitalise the socket connection to the server + init() { + const socket = io('http://localhost:3000', { + path: '/live-logs', + }); - this.socket = socket as unknown as Socket< - DefaultEventsMap, - DefaultEventsMap - >; - } + this.socket = socket as unknown as Socket< + DefaultEventsMap, + DefaultEventsMap + >; + } - log(info: unknown, callback: () => void) { - setImmediate(() => { - this.socket.emit('send-log', info); - }); + log(info: unknown, callback: () => void) { + setImmediate(() => { + this.socket.emit('send-log', info); + }); - callback(); - } + callback(); + } } diff --git a/telemetry/packages/server/src/modules/logger/Logger.module.ts b/telemetry/packages/server/src/modules/logger/Logger.module.ts index b9565645..ac9854cf 100644 --- a/telemetry/packages/server/src/modules/logger/Logger.module.ts +++ b/telemetry/packages/server/src/modules/logger/Logger.module.ts @@ -1,72 +1,76 @@ +import { ENV } from '@/modules/core/config'; import { Module } from '@nestjs/common'; -import { utilities, WinstonModule, WinstonModuleOptions } from 'nest-winston'; +import { + WinstonModule, + type WinstonModuleOptions, + utilities, +} from 'nest-winston'; import { format, transports } from 'winston'; -import { ENV } from '@/modules/core/config'; import { LiveLogsTransport } from '../live-logs/LiveLogsTransport'; // In top-level 'telemetry' directory const LOGGING_DIRECTORY = '../../logs'; const unhandledErrorFormat = format((info) => { - if (info[Symbol.for('level')] === 'error' && info['error']) { - const error = info['error'] as { - name: string; - message: string; - stack: string; - }; - return { - context: error.name, - message: error.message, - // stack: info['error']['stack'], - not using this for now - level: 'error', - [Symbol.for('level')]: 'error', - }; - } - return info; + if (info[Symbol.for('level')] === 'error' && info.error) { + const error = info.error as { + name: string; + message: string; + stack: string; + }; + return { + context: error.name, + message: error.message, + // stack: info['error']['stack'], - not using this for now + level: 'error', + [Symbol.for('level')]: 'error', + }; + } + return info; }); export const loggerOptions: WinstonModuleOptions = { - level: ENV === 'development' ? 'debug' : 'info', - format: format.combine( - unhandledErrorFormat(), - format.timestamp({ - format: 'YYYY-MM-DD HH:mm:ss', - }), - format.errors({ stack: false }), - format.splat(), - format.json(), - ), - transports: [ - new LiveLogsTransport(), - new transports.File({ - filename: 'errors.log', - dirname: LOGGING_DIRECTORY, - level: 'error', - }), - new transports.File({ filename: 'all.log', dirname: LOGGING_DIRECTORY }), - ...(ENV === 'development' - ? [ - new transports.Console({ - format: format.combine( - format.timestamp(), - format.ms(), - utilities.format.nestLike('Telemetry', { - colors: true, - prettyPrint: true, - }), - ), - handleExceptions: true, - }), - ] - : []), - ], - exitOnError: false, + level: ENV === 'development' ? 'debug' : 'info', + format: format.combine( + unhandledErrorFormat(), + format.timestamp({ + format: 'YYYY-MM-DD HH:mm:ss', + }), + format.errors({ stack: false }), + format.splat(), + format.json(), + ), + transports: [ + new LiveLogsTransport(), + new transports.File({ + filename: 'errors.log', + dirname: LOGGING_DIRECTORY, + level: 'error', + }), + new transports.File({ filename: 'all.log', dirname: LOGGING_DIRECTORY }), + ...(ENV === 'development' + ? [ + new transports.Console({ + format: format.combine( + format.timestamp(), + format.ms(), + utilities.format.nestLike('Telemetry', { + colors: true, + prettyPrint: true, + }), + ), + handleExceptions: true, + }), + ] + : []), + ], + exitOnError: false, }; const logger = WinstonModule.forRoot(loggerOptions); @Module({ - imports: [logger], - exports: [logger], + imports: [logger], + exports: [logger], }) export class LoggerModule {} diff --git a/telemetry/packages/server/src/modules/measurement/Measurement.module.ts b/telemetry/packages/server/src/modules/measurement/Measurement.module.ts index d4a9d6a2..d41823ed 100644 --- a/telemetry/packages/server/src/modules/measurement/Measurement.module.ts +++ b/telemetry/packages/server/src/modules/measurement/Measurement.module.ts @@ -1,12 +1,12 @@ -import { Module } from '@nestjs/common'; -import { MeasurementService } from './Measurement.service'; import { InfluxModule } from '@/modules/influx/Influx.module'; import { OpenMCTDataModule } from '@/modules/openmct/data/OpenMCTData.module'; import { FaultModule } from '@/modules/openmct/faults/Fault.module'; +import { Module } from '@nestjs/common'; +import { MeasurementService } from './Measurement.service'; @Module({ - imports: [InfluxModule, OpenMCTDataModule, FaultModule], - providers: [MeasurementService], - exports: [MeasurementService], + imports: [InfluxModule, OpenMCTDataModule, FaultModule], + providers: [MeasurementService], + exports: [MeasurementService], }) export class MeasurementModule {} diff --git a/telemetry/packages/server/src/modules/measurement/Measurement.service.ts b/telemetry/packages/server/src/modules/measurement/Measurement.service.ts index 452bd092..ed7a4e3d 100644 --- a/telemetry/packages/server/src/modules/measurement/Measurement.service.ts +++ b/telemetry/packages/server/src/modules/measurement/Measurement.service.ts @@ -1,98 +1,98 @@ +import type { InfluxService } from '@/modules/influx/Influx.service'; +import { Logger } from '@/modules/logger/Logger.decorator'; +import type { RealtimeTelemetryDataGateway } from '@/modules/openmct/data/realtime/RealtimeTelemetryData.gateway'; +import type { FaultService } from '@/modules/openmct/faults/Fault.service'; import { pods } from '@hyped/telemetry-constants'; import { Point } from '@influxdata/influxdb-client'; -import { Injectable, LoggerService } from '@nestjs/common'; -import { InfluxService } from '@/modules/influx/Influx.service'; -import { Logger } from '@/modules/logger/Logger.decorator'; -import { RealtimeTelemetryDataGateway } from '@/modules/openmct/data/realtime/RealtimeTelemetryData.gateway'; +import { Injectable, type LoggerService } from '@nestjs/common'; import { - MeasurementReading, - MeasurementReadingSchema, + type MeasurementReading, + MeasurementReadingSchema, } from './MeasurementReading.types'; import { MeasurementReadingValidationError } from './errors/MeasurementReadingValidationError'; import { doesMeasurementBreachLimits } from './utils/doesMeasurementBreachLimits'; -import { FaultService } from '@/modules/openmct/faults/Fault.service'; @Injectable() export class MeasurementService { - constructor( - @Logger() - private readonly logger: LoggerService, - private influxService: InfluxService, - private realtimeDataGateway: RealtimeTelemetryDataGateway, - private faultService: FaultService, - ) {} + constructor( + @Logger() + private readonly logger: LoggerService, + private influxService: InfluxService, + private realtimeDataGateway: RealtimeTelemetryDataGateway, + private faultService: FaultService, + ) {} - // This function _is_ ordered in importance - public async addMeasurementReading(props: MeasurementReading) { - const validatedMeasurement = this.validateMeasurementReading(props); + // This function _is_ ordered in importance + public async addMeasurementReading(props: MeasurementReading) { + const validatedMeasurement = this.validateMeasurementReading(props); - if (!validatedMeasurement) { - throw new MeasurementReadingValidationError('Invalid measurement'); - } + if (!validatedMeasurement) { + throw new MeasurementReadingValidationError('Invalid measurement'); + } - const { measurement, reading } = validatedMeasurement; - const { podId, measurementKey, value, timestamp } = reading; + const { measurement, reading } = validatedMeasurement; + const { podId, measurementKey, value, timestamp } = reading; - // First, get the data to the client ASAP - this.realtimeDataGateway.sendMeasurementReading({ - podId, - measurementKey, - value, - timestamp, - }); + // First, get the data to the client ASAP + this.realtimeDataGateway.sendMeasurementReading({ + podId, + measurementKey, + value, + timestamp, + }); - // Then check if it breaches limits - if (measurement.format === 'float' || measurement.format === 'integer') { - const breachLevel = doesMeasurementBreachLimits(measurement, reading); - if (breachLevel) { - this.logger.debug( - `Measurement breached limits {${props.podId}/${props.measurementKey}}: ${breachLevel} with value ${props.value}`, - MeasurementService.name, - ); - await this.faultService.addLimitBreachFault({ - level: breachLevel, - measurement, - tripReading: reading, - }); - } - } + // Then check if it breaches limits + if (measurement.format === 'float' || measurement.format === 'integer') { + const breachLevel = doesMeasurementBreachLimits(measurement, reading); + if (breachLevel) { + this.logger.debug( + `Measurement breached limits {${props.podId}/${props.measurementKey}}: ${breachLevel} with value ${props.value}`, + MeasurementService.name, + ); + await this.faultService.addLimitBreachFault({ + level: breachLevel, + measurement, + tripReading: reading, + }); + } + } - // Then save it to the database - const point = new Point('measurement') - .timestamp(timestamp) - .tag('podId', podId) - .tag('measurementKey', measurementKey) - .tag('format', measurement.format) - .floatField('value', value); + // Then save it to the database + const point = new Point('measurement') + .timestamp(timestamp) + .tag('podId', podId) + .tag('measurementKey', measurementKey) + .tag('format', measurement.format) + .floatField('value', value); - try { - this.influxService.telemetryWrite.writePoint(point); + try { + this.influxService.telemetryWrite.writePoint(point); - this.logger.debug( - `Added measurement {${props.podId}/${props.measurementKey}}: ${props.value}`, - MeasurementService.name, - ); - } catch (e: unknown) { - this.logger.error( - `Failed to add measurement {${props.podId}/${props.measurementKey}}: ${props.value}`, - e, - MeasurementService.name, - ); - } - } + this.logger.debug( + `Added measurement {${props.podId}/${props.measurementKey}}: ${props.value}`, + MeasurementService.name, + ); + } catch (e: unknown) { + this.logger.error( + `Failed to add measurement {${props.podId}/${props.measurementKey}}: ${props.value}`, + e, + MeasurementService.name, + ); + } + } - private validateMeasurementReading(props: MeasurementReading) { - const result = MeasurementReadingSchema.safeParse(props); + private validateMeasurementReading(props: MeasurementReading) { + const result = MeasurementReadingSchema.safeParse(props); - if (!result.success) { - throw new MeasurementReadingValidationError(result.error.message); - } + if (!result.success) { + throw new MeasurementReadingValidationError(result.error.message); + } - const { podId, measurementKey } = result.data; + const { podId, measurementKey } = result.data; - return { - reading: result.data, - measurement: pods[podId]['measurements'][measurementKey], - }; - } + return { + reading: result.data, + measurement: pods[podId].measurements[measurementKey], + }; + } } diff --git a/telemetry/packages/server/src/modules/measurement/MeasurementReading.types.ts b/telemetry/packages/server/src/modules/measurement/MeasurementReading.types.ts index 826b747d..2a6f24a7 100644 --- a/telemetry/packages/server/src/modules/measurement/MeasurementReading.types.ts +++ b/telemetry/packages/server/src/modules/measurement/MeasurementReading.types.ts @@ -1,48 +1,48 @@ -import { pods } from '@hyped/telemetry-constants'; import { zodEnumFromObjKeys } from '@/modules/common/utils/zodEnumFromObjKeys'; +import { pods } from '@hyped/telemetry-constants'; import { z } from 'zod'; export const MeasurementReadingSchema = z - .object({ - podId: zodEnumFromObjKeys(pods), - measurementKey: z.string(), - timestamp: z.string(), // to handle nanoseconds timestamp - value: z.number(), - }) - // Validate measurement exists and enum value is valid (if applicable) - .refine( - ({ podId, measurementKey, value }) => { - const measurement = pods[podId]['measurements'][measurementKey]; + .object({ + podId: zodEnumFromObjKeys(pods), + measurementKey: z.string(), + timestamp: z.string(), // to handle nanoseconds timestamp + value: z.number(), + }) + // Validate measurement exists and enum value is valid (if applicable) + .refine( + ({ podId, measurementKey, value }) => { + const measurement = pods[podId].measurements[measurementKey]; - if (!measurement) { - return false; - } + if (!measurement) { + return false; + } - // Validate enum values - if (measurement.format === 'enum') { - const enumValue = measurement.enumerations.find( - (e) => e.value === value, - ); + // Validate enum values + if (measurement.format === 'enum') { + const enumValue = measurement.enumerations.find( + (e) => e.value === value, + ); - if (!enumValue) { - return false; - } - } + if (!enumValue) { + return false; + } + } - // Validate integers and floats - if ( - (measurement.format === 'float' && isNaN(value)) || - (measurement.format === 'integer' && !Number.isInteger(value)) - ) { - return false; - } + // Validate integers and floats + if ( + (measurement.format === 'float' && Number.isNaN(value)) || + (measurement.format === 'integer' && !Number.isInteger(value)) + ) { + return false; + } - return true; - }, - { - message: - 'Invalid measurement reading - measurement does not exist or invalid enum value', - }, - ); + return true; + }, + { + message: + 'Invalid measurement reading - measurement does not exist or invalid enum value', + }, + ); export type MeasurementReading = z.infer; diff --git a/telemetry/packages/server/src/modules/measurement/errors/MeasurementReadingValidationError.ts b/telemetry/packages/server/src/modules/measurement/errors/MeasurementReadingValidationError.ts index afa78111..f39e10fe 100644 --- a/telemetry/packages/server/src/modules/measurement/errors/MeasurementReadingValidationError.ts +++ b/telemetry/packages/server/src/modules/measurement/errors/MeasurementReadingValidationError.ts @@ -1,6 +1,6 @@ export class MeasurementReadingValidationError extends Error { - constructor(message: string) { - super(message); - this.name = 'MeasurementReadingValidationError'; - } + constructor(message: string) { + super(message); + this.name = 'MeasurementReadingValidationError'; + } } diff --git a/telemetry/packages/server/src/modules/measurement/utils/doesMeasurementBreachLimits.ts b/telemetry/packages/server/src/modules/measurement/utils/doesMeasurementBreachLimits.ts index 4645281b..f6a5092e 100644 --- a/telemetry/packages/server/src/modules/measurement/utils/doesMeasurementBreachLimits.ts +++ b/telemetry/packages/server/src/modules/measurement/utils/doesMeasurementBreachLimits.ts @@ -1,27 +1,27 @@ -import { MeasurementReading } from '../MeasurementReading.types'; -import { RangeMeasurement } from '@hyped/telemetry-types/dist/pods/pods.types'; -import { FaultLevel } from '@hyped/telemetry-constants'; +import type { FaultLevel } from '@hyped/telemetry-constants'; +import type { RangeMeasurement } from '@hyped/telemetry-types/dist/pods/pods.types'; +import type { MeasurementReading } from '../MeasurementReading.types'; export type DoesMeasurementBreachLimitsReturn = false | FaultLevel; export function doesMeasurementBreachLimits( - measurement: RangeMeasurement, - reading: MeasurementReading, + measurement: RangeMeasurement, + reading: MeasurementReading, ): DoesMeasurementBreachLimitsReturn { - const { value } = reading; + const { value } = reading; - const { low, high } = measurement.limits.critical; - if (value < low || value > high) { - return 'CRITICAL'; - } + const { low, high } = measurement.limits.critical; + if (value < low || value > high) { + return 'CRITICAL'; + } - if (measurement.limits.warning) { - const { low, high } = measurement.limits.warning; + if (measurement.limits.warning) { + const { low, high } = measurement.limits.warning; - if (value < low || value > high) { - return 'WARNING'; - } - } + if (value < low || value > high) { + return 'WARNING'; + } + } - return false; + return false; } diff --git a/telemetry/packages/server/src/modules/mqtt/client/MqttClientModule.ts b/telemetry/packages/server/src/modules/mqtt/client/MqttClientModule.ts index 578ded47..9e713062 100644 --- a/telemetry/packages/server/src/modules/mqtt/client/MqttClientModule.ts +++ b/telemetry/packages/server/src/modules/mqtt/client/MqttClientModule.ts @@ -3,11 +3,11 @@ import { MqttModule } from 'nest-mqtt'; import { MQTT_BROKER_HOST } from 'src/modules/core/config'; const mqttClient = MqttModule.forRoot({ - host: MQTT_BROKER_HOST, + host: MQTT_BROKER_HOST, }); @Module({ - imports: [mqttClient], - exports: [mqttClient], + imports: [mqttClient], + exports: [mqttClient], }) export class MqttClientModule {} diff --git a/telemetry/packages/server/src/modules/mqtt/ingestion/MqttIngestion.module.ts b/telemetry/packages/server/src/modules/mqtt/ingestion/MqttIngestion.module.ts index 5486fdcb..9a063b4e 100644 --- a/telemetry/packages/server/src/modules/mqtt/ingestion/MqttIngestion.module.ts +++ b/telemetry/packages/server/src/modules/mqtt/ingestion/MqttIngestion.module.ts @@ -1,10 +1,10 @@ +import { StateModule } from '@/modules/state/State.module'; import { Module } from '@nestjs/common'; -import { MqttIngestionService } from './MqttIngestion.service'; import { MeasurementModule } from 'src/modules/measurement/Measurement.module'; -import { StateModule } from '@/modules/state/State.module'; +import { MqttIngestionService } from './MqttIngestion.service'; @Module({ - imports: [MeasurementModule, StateModule], - providers: [MqttIngestionService], + imports: [MeasurementModule, StateModule], + providers: [MqttIngestionService], }) export class MqttIngestionModule {} diff --git a/telemetry/packages/server/src/modules/mqtt/ingestion/MqttIngestion.service.ts b/telemetry/packages/server/src/modules/mqtt/ingestion/MqttIngestion.service.ts index db456dec..ff1429c3 100644 --- a/telemetry/packages/server/src/modules/mqtt/ingestion/MqttIngestion.service.ts +++ b/telemetry/packages/server/src/modules/mqtt/ingestion/MqttIngestion.service.ts @@ -1,75 +1,79 @@ +import type { MeasurementService } from '@/modules/measurement/Measurement.service'; +import type { StateService } from '@/modules/state/State.service'; +import { + POD_IDS, + type PodId, + type PodStateType, +} from '@hyped/telemetry-constants'; +import { currentTime } from '@influxdata/influxdb-client'; import { Injectable } from '@nestjs/common'; import { Params, Payload, Subscribe } from 'nest-mqtt'; -import { MeasurementService } from '@/modules/measurement/Measurement.service'; -import { currentTime } from '@influxdata/influxdb-client'; -import { StateService } from '@/modules/state/State.service'; import { MqttIngestionError } from './errors/MqttIngestionError'; -import { POD_IDS, PodId, PodStateType } from '@hyped/telemetry-constants'; @Injectable() export class MqttIngestionService { - constructor( - private measurementService: MeasurementService, - private stateService: StateService, - ) {} + constructor( + private measurementService: MeasurementService, + private stateService: StateService, + ) {} - @Subscribe('hyped/+/measurement/+') - async getMeasurementReading( - @Params() rawParams: string[], - @Payload() rawValue: number, // TODOLater: check that this is correct - ) { - const timestamp = currentTime.nanos(); - const podId = rawParams[0]; - const measurementKey = rawParams[1]; - const value = rawValue; + @Subscribe('hyped/+/measurement/+') + async getMeasurementReading( + @Params() rawParams: string[], + @Payload() rawValue: number, // TODOLater: check that this is correct + ) { + const timestamp = currentTime.nanos(); + const podId = rawParams[0]; + const measurementKey = rawParams[1]; + const value = rawValue; - this.validateMqttMessage({ podId, measurementKey, value }); - this.validatePodId(podId); + this.validateMqttMessage({ podId, measurementKey, value }); + this.validatePodId(podId); - await this.measurementService.addMeasurementReading({ - podId, - measurementKey, - value, - timestamp, - }); - } + await this.measurementService.addMeasurementReading({ + podId, + measurementKey, + value, + timestamp, + }); + } - @Subscribe('hyped/+/state') - getStateReading( - @Params() rawParams: string[], - @Payload() rawValue: PodStateType, - ) { - const timestamp = currentTime.nanos(); - const podId = rawParams[0]; - const value = rawValue; + @Subscribe('hyped/+/state') + getStateReading( + @Params() rawParams: string[], + @Payload() rawValue: PodStateType, + ) { + const timestamp = currentTime.nanos(); + const podId = rawParams[0]; + const value = rawValue; - this.validateMqttMessage({ podId, measurementKey: 'state', value }); - this.validatePodId(podId); + this.validateMqttMessage({ podId, measurementKey: 'state', value }); + this.validatePodId(podId); - this.stateService.addStateReading({ - podId, - value, - timestamp, - }); - } + this.stateService.addStateReading({ + podId, + value, + timestamp, + }); + } - private validateMqttMessage({ - podId, - measurementKey, - value, - }: { - podId: string; - measurementKey: string; - value: unknown; - }) { - if (!podId || !measurementKey || value === undefined) { - throw new MqttIngestionError('Invalid MQTT message'); - } - } + private validateMqttMessage({ + podId, + measurementKey, + value, + }: { + podId: string; + measurementKey: string; + value: unknown; + }) { + if (!podId || !measurementKey || value === undefined) { + throw new MqttIngestionError('Invalid MQTT message'); + } + } - private validatePodId(podId: string): asserts podId is PodId { - if (!POD_IDS.includes(podId as PodId)) { - throw new MqttIngestionError('Invalid pod ID'); - } - } + private validatePodId(podId: string): asserts podId is PodId { + if (!POD_IDS.includes(podId as PodId)) { + throw new MqttIngestionError('Invalid pod ID'); + } + } } diff --git a/telemetry/packages/server/src/modules/mqtt/ingestion/errors/MqttIngestionError.ts b/telemetry/packages/server/src/modules/mqtt/ingestion/errors/MqttIngestionError.ts index b0ac0a31..ae99dd7f 100644 --- a/telemetry/packages/server/src/modules/mqtt/ingestion/errors/MqttIngestionError.ts +++ b/telemetry/packages/server/src/modules/mqtt/ingestion/errors/MqttIngestionError.ts @@ -1,6 +1,6 @@ export class MqttIngestionError extends Error { - constructor(message: string) { - super(message); - this.name = 'MqttIngestionError'; - } + constructor(message: string) { + super(message); + this.name = 'MqttIngestionError'; + } } diff --git a/telemetry/packages/server/src/modules/openmct/OpenMCT.module.ts b/telemetry/packages/server/src/modules/openmct/OpenMCT.module.ts index ebce5bb5..ac429708 100644 --- a/telemetry/packages/server/src/modules/openmct/OpenMCT.module.ts +++ b/telemetry/packages/server/src/modules/openmct/OpenMCT.module.ts @@ -1,13 +1,13 @@ import { Module } from '@nestjs/common'; +import { OpenMCTDataModule } from './data/OpenMCTData.module'; import { DictionaryController } from './dictionary/Dictionary.controller'; import { DictionaryService } from './dictionary/Dictionary.service'; import { ObjectTypesController } from './object-types/ObjectTypes.controller'; import { ObjectTypesService } from './object-types/ObjectTypes.service'; -import { OpenMCTDataModule } from './data/OpenMCTData.module'; @Module({ - imports: [OpenMCTDataModule], - controllers: [DictionaryController, ObjectTypesController], - providers: [DictionaryService, ObjectTypesService], + imports: [OpenMCTDataModule], + controllers: [DictionaryController, ObjectTypesController], + providers: [DictionaryService, ObjectTypesService], }) export class OpenMCTModule {} diff --git a/telemetry/packages/server/src/modules/openmct/data/OpenMCTData.module.ts b/telemetry/packages/server/src/modules/openmct/data/OpenMCTData.module.ts index 9bafb130..a9570d74 100644 --- a/telemetry/packages/server/src/modules/openmct/data/OpenMCTData.module.ts +++ b/telemetry/packages/server/src/modules/openmct/data/OpenMCTData.module.ts @@ -1,13 +1,13 @@ import { Module } from '@nestjs/common'; +import { InfluxModule } from 'src/modules/influx/Influx.module'; import { HistoricalTelemetryDataController } from './historical/HistoricalTelemetryData.controller'; import { HistoricalTelemetryDataService } from './historical/HistoricalTelemetryData.service'; -import { InfluxModule } from 'src/modules/influx/Influx.module'; import { RealtimeTelemetryDataGateway } from './realtime/RealtimeTelemetryData.gateway'; @Module({ - imports: [InfluxModule], - controllers: [HistoricalTelemetryDataController], - providers: [HistoricalTelemetryDataService, RealtimeTelemetryDataGateway], - exports: [RealtimeTelemetryDataGateway], + imports: [InfluxModule], + controllers: [HistoricalTelemetryDataController], + providers: [HistoricalTelemetryDataService, RealtimeTelemetryDataGateway], + exports: [RealtimeTelemetryDataGateway], }) export class OpenMCTDataModule {} diff --git a/telemetry/packages/server/src/modules/openmct/data/historical/HistoricalTelemetryData.controller.ts b/telemetry/packages/server/src/modules/openmct/data/historical/HistoricalTelemetryData.controller.ts index 94909c79..25058ca2 100644 --- a/telemetry/packages/server/src/modules/openmct/data/historical/HistoricalTelemetryData.controller.ts +++ b/telemetry/packages/server/src/modules/openmct/data/historical/HistoricalTelemetryData.controller.ts @@ -1,22 +1,22 @@ import { Controller, Get, Param, Query } from '@nestjs/common'; -import { HistoricalTelemetryDataService } from './HistoricalTelemetryData.service'; +import type { HistoricalTelemetryDataService } from './HistoricalTelemetryData.service'; @Controller('openmct/data/historical') export class HistoricalTelemetryDataController { - constructor(private historicalDataService: HistoricalTelemetryDataService) {} + constructor(private historicalDataService: HistoricalTelemetryDataService) {} - @Get('pods/:podId/measurements/:measurementKey') - getHistoricalReading( - @Param('podId') podId: string, - @Param('measurementKey') measurementKey: string, - @Query('start') start: string, - @Query('end') end: string, - ) { - return this.historicalDataService.getHistoricalReading( - podId, - measurementKey, - start, - end, - ); - } + @Get('pods/:podId/measurements/:measurementKey') + getHistoricalReading( + @Param('podId') podId: string, + @Param('measurementKey') measurementKey: string, + @Query('start') start: string, + @Query('end') end: string, + ) { + return this.historicalDataService.getHistoricalReading( + podId, + measurementKey, + start, + end, + ); + } } diff --git a/telemetry/packages/server/src/modules/openmct/data/historical/HistoricalTelemetryData.service.ts b/telemetry/packages/server/src/modules/openmct/data/historical/HistoricalTelemetryData.service.ts index c2e5d8a7..ea6fa2c2 100644 --- a/telemetry/packages/server/src/modules/openmct/data/historical/HistoricalTelemetryData.service.ts +++ b/telemetry/packages/server/src/modules/openmct/data/historical/HistoricalTelemetryData.service.ts @@ -1,58 +1,58 @@ -import { flux, fluxDateTime } from '@influxdata/influxdb-client'; -import { HttpException, Injectable, LoggerService } from '@nestjs/common'; import { INFLUX_TELEMETRY_BUCKET } from '@/core/config'; -import { InfluxService } from '@/modules/influx/Influx.service'; +import type { InfluxRow } from '@/modules/common/types/InfluxRow'; +import type { InfluxService } from '@/modules/influx/Influx.service'; import { Logger } from '@/modules/logger/Logger.decorator'; -import { InfluxRow } from '@/modules/common/types/InfluxRow'; +import { flux, fluxDateTime } from '@influxdata/influxdb-client'; +import { HttpException, Injectable, type LoggerService } from '@nestjs/common'; interface InfluxHistoricalRow extends InfluxRow { - measurementKey: string; - format: string; + measurementKey: string; + format: string; } @Injectable() export class HistoricalTelemetryDataService { - constructor( - private influxService: InfluxService, - @Logger() - private readonly logger: LoggerService, - ) {} + constructor( + private influxService: InfluxService, + @Logger() + private readonly logger: LoggerService, + ) {} - public async getHistoricalReading( - podId: string, - measurementKey: string, - startTimestamp: string, - endTimestamp: string, - ) { - const fluxStart = fluxDateTime( - new Date(parseInt(startTimestamp)).toISOString(), - ); - const fluxEnd = fluxDateTime( - new Date(parseInt(endTimestamp)).toISOString(), - ); + public async getHistoricalReading( + podId: string, + measurementKey: string, + startTimestamp: string, + endTimestamp: string, + ) { + const fluxStart = fluxDateTime( + new Date(Number.parseInt(startTimestamp)).toISOString(), + ); + const fluxEnd = fluxDateTime( + new Date(Number.parseInt(endTimestamp)).toISOString(), + ); - const query = flux` + const query = flux` from(bucket: "${INFLUX_TELEMETRY_BUCKET}") |> range(start: ${fluxStart}, stop: ${fluxEnd}) |> filter(fn: (r) => r["measurementKey"] == "${measurementKey}") |> filter(fn: (r) => r["podId"] == "${podId}")`; - try { - const data = - await this.influxService.query.collectRows(query); + try { + const data = + await this.influxService.query.collectRows(query); - return data.map((row) => ({ - id: row['measurementKey'], - timestamp: new Date(row['_time']).getTime(), - value: row['_value'], - })); - } catch (e: unknown) { - this.logger.error( - `Failed to get historical reading for {${podId}/${measurementKey}}`, - e, - HistoricalTelemetryDataService.name, - ); - throw new HttpException("Couldn't get historical reading", 500); - } - } + return data.map((row) => ({ + id: row.measurementKey, + timestamp: new Date(row._time).getTime(), + value: row._value, + })); + } catch (e: unknown) { + this.logger.error( + `Failed to get historical reading for {${podId}/${measurementKey}}`, + e, + HistoricalTelemetryDataService.name, + ); + throw new HttpException("Couldn't get historical reading", 500); + } + } } diff --git a/telemetry/packages/server/src/modules/openmct/data/realtime/RealtimeTelemetryData.gateway.ts b/telemetry/packages/server/src/modules/openmct/data/realtime/RealtimeTelemetryData.gateway.ts index 7abda4cf..3b24349c 100644 --- a/telemetry/packages/server/src/modules/openmct/data/realtime/RealtimeTelemetryData.gateway.ts +++ b/telemetry/packages/server/src/modules/openmct/data/realtime/RealtimeTelemetryData.gateway.ts @@ -1,65 +1,65 @@ import { Logger } from '@/modules/logger/Logger.decorator'; -import { MeasurementReading } from '@/modules/measurement/MeasurementReading.types'; +import type { MeasurementReading } from '@/modules/measurement/MeasurementReading.types'; import { socket as socketConstants } from '@hyped/telemetry-constants'; -import { LoggerService } from '@nestjs/common'; +import type { LoggerService } from '@nestjs/common'; import { - ConnectedSocket, - MessageBody, - SubscribeMessage, - WebSocketGateway, - WebSocketServer, + ConnectedSocket, + MessageBody, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, } from '@nestjs/websockets'; -import { Server, Socket } from 'socket.io'; +import type { Server, Socket } from 'socket.io'; @WebSocketGateway({ - path: '/openmct/data/realtime', - cors: { - origin: '*', - }, + path: '/openmct/data/realtime', + cors: { + origin: '*', + }, }) export class RealtimeTelemetryDataGateway { - @WebSocketServer() - socket: Server; + @WebSocketServer() + socket: Server; - constructor( - @Logger() - private readonly logger: LoggerService, - ) {} + constructor( + @Logger() + private readonly logger: LoggerService, + ) {} - @SubscribeMessage(socketConstants.EVENTS.SUBSCRIBE_TO_MEASUREMENT) - async subscribeToMeasurement( - @MessageBody() measurementRoom: string, - @ConnectedSocket() client: Socket, - ) { - await client.join(measurementRoom); - } + @SubscribeMessage(socketConstants.EVENTS.SUBSCRIBE_TO_MEASUREMENT) + async subscribeToMeasurement( + @MessageBody() measurementRoom: string, + @ConnectedSocket() client: Socket, + ) { + await client.join(measurementRoom); + } - @SubscribeMessage(socketConstants.EVENTS.UNSUBSCRIBE_FROM_MEASUREMENT) - async unsubscribeFromMeasurement( - @MessageBody() measurementRoom: string, - @ConnectedSocket() client: Socket, - ) { - await client.leave(measurementRoom); - } + @SubscribeMessage(socketConstants.EVENTS.UNSUBSCRIBE_FROM_MEASUREMENT) + async unsubscribeFromMeasurement( + @MessageBody() measurementRoom: string, + @ConnectedSocket() client: Socket, + ) { + await client.leave(measurementRoom); + } - sendMeasurementReading(props: MeasurementReading) { - const { podId, measurementKey, value, timestamp } = props; + sendMeasurementReading(props: MeasurementReading) { + const { podId, measurementKey, value, timestamp } = props; - const measurementRoom = socketConstants.getMeasurementRoomName( - podId, - measurementKey, - ); - this.socket.to(measurementRoom).emit(socketConstants.MEASUREMENT_EVENT, { - podId, - measurementKey, - value, - // convert timestamp from nanoseconds to milliseconds - timestamp: Math.floor(Number(timestamp) / 1e6), - }); + const measurementRoom = socketConstants.getMeasurementRoomName( + podId, + measurementKey, + ); + this.socket.to(measurementRoom).emit(socketConstants.MEASUREMENT_EVENT, { + podId, + measurementKey, + value, + // convert timestamp from nanoseconds to milliseconds + timestamp: Math.floor(Number(timestamp) / 1e6), + }); - this.logger.debug( - `Sending realtime ${value} to ${measurementRoom}`, - RealtimeTelemetryDataGateway.name, - ); - } + this.logger.debug( + `Sending realtime ${value} to ${measurementRoom}`, + RealtimeTelemetryDataGateway.name, + ); + } } diff --git a/telemetry/packages/server/src/modules/openmct/dictionary/Dictionary.controller.ts b/telemetry/packages/server/src/modules/openmct/dictionary/Dictionary.controller.ts index b96bb1be..a1e1ad5e 100644 --- a/telemetry/packages/server/src/modules/openmct/dictionary/Dictionary.controller.ts +++ b/telemetry/packages/server/src/modules/openmct/dictionary/Dictionary.controller.ts @@ -1,29 +1,29 @@ +import type { OpenMctPod } from '@hyped/telemetry-types'; import { Controller, Get, Param } from '@nestjs/common'; -import { DictionaryService } from './Dictionary.service'; -import { OpenMctPod } from '@hyped/telemetry-types'; +import type { DictionaryService } from './Dictionary.service'; @Controller('openmct/dictionary') export class DictionaryController { - constructor(private dictionaryService: DictionaryService) {} + constructor(private dictionaryService: DictionaryService) {} - @Get('pods') - getPodIds() { - const ids = this.dictionaryService.getPodIds(); - return { - ids, - }; - } + @Get('pods') + getPodIds() { + const ids = this.dictionaryService.getPodIds(); + return { + ids, + }; + } - @Get('pods/:podId') - getPod(@Param('podId') podId: string): OpenMctPod { - return this.dictionaryService.getPod(podId); - } + @Get('pods/:podId') + getPod(@Param('podId') podId: string): OpenMctPod { + return this.dictionaryService.getPod(podId); + } - @Get('pods/:podId/measurements/:measurementKey') - getMeasurement( - @Param('podId') podId: string, - @Param('measurementKey') measurementKey: string, - ) { - return this.dictionaryService.getMeasurement(podId, measurementKey); - } + @Get('pods/:podId/measurements/:measurementKey') + getMeasurement( + @Param('podId') podId: string, + @Param('measurementKey') measurementKey: string, + ) { + return this.dictionaryService.getMeasurement(podId, measurementKey); + } } diff --git a/telemetry/packages/server/src/modules/openmct/dictionary/Dictionary.service.ts b/telemetry/packages/server/src/modules/openmct/dictionary/Dictionary.service.ts index 39212c30..6d3f438d 100644 --- a/telemetry/packages/server/src/modules/openmct/dictionary/Dictionary.service.ts +++ b/telemetry/packages/server/src/modules/openmct/dictionary/Dictionary.service.ts @@ -1,52 +1,52 @@ -import { POD_IDS, PodId, pods } from '@hyped/telemetry-constants'; -import { OpenMctDictionary, OpenMctPod } from '@hyped/telemetry-types'; +import { POD_IDS, type PodId, pods } from '@hyped/telemetry-constants'; +import type { OpenMctDictionary, OpenMctPod } from '@hyped/telemetry-types'; import { Injectable } from '@nestjs/common'; import { mapMeasurementToOpenMct } from './utils/mapMeasurementToOpenMct'; @Injectable() export class DictionaryService { - getDictionary(): OpenMctDictionary { - const dictionary: OpenMctDictionary = {}; - POD_IDS.forEach((podId) => { - dictionary[podId] = this.getPod(podId); - }); - return dictionary; - } - - getPodIds() { - return POD_IDS; - } - - getPod(podId: string): OpenMctPod { - this.validatePodId(podId); - const pod = pods[podId]; - - const measurements = Object.values(pod.measurements).map((measurement) => - mapMeasurementToOpenMct(measurement), - ); - - return { - name: pod.name, - id: pod.id, - measurements, - }; - } - - getMeasurement(podId: string, measurementKey: string) { - this.validatePodId(podId); - const pod = pods[podId]; - - const measurement = pod.measurements[measurementKey]; - if (!measurement) { - throw new Error(`Measurement ${measurementKey} not found`); - } - - return mapMeasurementToOpenMct(measurement); - } - - private validatePodId(podId: string): asserts podId is PodId { - if (!POD_IDS.includes(podId as PodId)) { - throw new Error(`Pod ${podId} not found`); - } - } + getDictionary(): OpenMctDictionary { + const dictionary: OpenMctDictionary = {}; + for (const podId of POD_IDS) { + dictionary[podId] = this.getPod(podId); + } + return dictionary; + } + + getPodIds() { + return POD_IDS; + } + + getPod(podId: string): OpenMctPod { + this.validatePodId(podId); + const pod = pods[podId]; + + const measurements = Object.values(pod.measurements).map((measurement) => + mapMeasurementToOpenMct(measurement), + ); + + return { + name: pod.name, + id: pod.id, + measurements, + }; + } + + getMeasurement(podId: string, measurementKey: string) { + this.validatePodId(podId); + const pod = pods[podId]; + + const measurement = pod.measurements[measurementKey]; + if (!measurement) { + throw new Error(`Measurement ${measurementKey} not found`); + } + + return mapMeasurementToOpenMct(measurement); + } + + private validatePodId(podId: string): asserts podId is PodId { + if (!POD_IDS.includes(podId as PodId)) { + throw new Error(`Pod ${podId} not found`); + } + } } diff --git a/telemetry/packages/server/src/modules/openmct/dictionary/utils/mapMeasurementToOpenMct.ts b/telemetry/packages/server/src/modules/openmct/dictionary/utils/mapMeasurementToOpenMct.ts index 265db911..f4c04915 100644 --- a/telemetry/packages/server/src/modules/openmct/dictionary/utils/mapMeasurementToOpenMct.ts +++ b/telemetry/packages/server/src/modules/openmct/dictionary/utils/mapMeasurementToOpenMct.ts @@ -1,39 +1,39 @@ -import { Measurement, OpenMctMeasurement } from '@hyped/telemetry-types'; +import type { Measurement, OpenMctMeasurement } from '@hyped/telemetry-types'; export function mapMeasurementToOpenMct( - measurement: Measurement, + measurement: Measurement, ): OpenMctMeasurement { - return { - name: measurement.name, - key: measurement.key, - type: measurement.type, - values: [ - { - key: 'value', - name: measurement.name, - unit: measurement.unit, - format: measurement.format, - ...('limits' in measurement && { - min: measurement.limits?.critical.low, - max: measurement.limits?.critical.high, - limits: measurement.limits, - }), - ...('enumerations' in measurement && { - enumerations: measurement.enumerations, - }), - hints: { - range: 1, - }, - }, - { - key: 'utc', - source: 'timestamp', - name: 'Timestamp', - format: 'utc', - hints: { - domain: 1, - }, - }, - ], - }; + return { + name: measurement.name, + key: measurement.key, + type: measurement.type, + values: [ + { + key: 'value', + name: measurement.name, + unit: measurement.unit, + format: measurement.format, + ...('limits' in measurement && { + min: measurement.limits?.critical.low, + max: measurement.limits?.critical.high, + limits: measurement.limits, + }), + ...('enumerations' in measurement && { + enumerations: measurement.enumerations, + }), + hints: { + range: 1, + }, + }, + { + key: 'utc', + source: 'timestamp', + name: 'Timestamp', + format: 'utc', + hints: { + domain: 1, + }, + }, + ], + }; } diff --git a/telemetry/packages/server/src/modules/openmct/faults/Fault.controller.ts b/telemetry/packages/server/src/modules/openmct/faults/Fault.controller.ts index dd38d42d..bb7a36cb 100644 --- a/telemetry/packages/server/src/modules/openmct/faults/Fault.controller.ts +++ b/telemetry/packages/server/src/modules/openmct/faults/Fault.controller.ts @@ -1,37 +1,37 @@ import { Body, Controller, Post } from '@nestjs/common'; -import { FaultService } from './Fault.service'; +import type { FaultService } from './Fault.service'; @Controller('openmct/faults') export class FaultsController { - constructor(private faultsService: FaultService) {} + constructor(private faultsService: FaultService) {} - @Post('acknowledge') - acknowledgeFault( - @Body() { faultId, comment }: { faultId: string; comment: string }, - ) { - return this.faultsService.acknowledgeFault(faultId, comment); - } + @Post('acknowledge') + acknowledgeFault( + @Body() { faultId, comment }: { faultId: string; comment: string }, + ) { + return this.faultsService.acknowledgeFault(faultId, comment); + } - @Post('shelve') - shelveFault( - @Body() - { - faultId, - shelved, - shelveDuration, - comment, - }: { - faultId: string; - shelved: boolean; - shelveDuration: number; - comment: string; - }, - ) { - return this.faultsService.shelveFault( - faultId, - shelved, - shelveDuration, - comment, - ); - } + @Post('shelve') + shelveFault( + @Body() + { + faultId, + shelved, + shelveDuration, + comment, + }: { + faultId: string; + shelved: boolean; + shelveDuration: number; + comment: string; + }, + ) { + return this.faultsService.shelveFault( + faultId, + shelved, + shelveDuration, + comment, + ); + } } diff --git a/telemetry/packages/server/src/modules/openmct/faults/Fault.module.ts b/telemetry/packages/server/src/modules/openmct/faults/Fault.module.ts index f04bc4f6..664daaf4 100644 --- a/telemetry/packages/server/src/modules/openmct/faults/Fault.module.ts +++ b/telemetry/packages/server/src/modules/openmct/faults/Fault.module.ts @@ -1,19 +1,19 @@ import { InfluxModule } from '@/modules/influx/Influx.module'; import { Module } from '@nestjs/common'; +import { FaultsController } from './Fault.controller'; import { FaultService } from './Fault.service'; +import { HistoricalFaultsDataController } from './data/historical/HistoricalFaultData.controller'; import { HistoricalFaultDataService } from './data/historical/HistoricalFaultData.service'; import { RealtimeFaultDataGateway } from './data/realtime/RealtimeFaultData.gateway'; -import { HistoricalFaultsDataController } from './data/historical/HistoricalFaultData.controller'; -import { FaultsController } from './Fault.controller'; @Module({ - imports: [InfluxModule], - controllers: [FaultsController, HistoricalFaultsDataController], - providers: [ - FaultService, - HistoricalFaultDataService, - RealtimeFaultDataGateway, - ], - exports: [FaultService], + imports: [InfluxModule], + controllers: [FaultsController, HistoricalFaultsDataController], + providers: [ + FaultService, + HistoricalFaultDataService, + RealtimeFaultDataGateway, + ], + exports: [FaultService], }) export class FaultModule {} diff --git a/telemetry/packages/server/src/modules/openmct/faults/Fault.service.ts b/telemetry/packages/server/src/modules/openmct/faults/Fault.service.ts index e8094bb5..0ab4da5c 100644 --- a/telemetry/packages/server/src/modules/openmct/faults/Fault.service.ts +++ b/telemetry/packages/server/src/modules/openmct/faults/Fault.service.ts @@ -1,262 +1,262 @@ -import { InfluxService } from '@/modules/influx/Influx.service'; +import type { InfluxService } from '@/modules/influx/Influx.service'; import { Logger } from '@/modules/logger/Logger.decorator'; -import { MeasurementReading } from '@/modules/measurement/MeasurementReading.types'; -import { FaultLevel } from '@hyped/telemetry-constants'; -import { OpenMctFault, Unpacked } from '@hyped/telemetry-types'; -import { RangeMeasurement } from '@hyped/telemetry-types/dist/pods/pods.types'; +import type { MeasurementReading } from '@/modules/measurement/MeasurementReading.types'; +import type { FaultLevel } from '@hyped/telemetry-constants'; +import type { OpenMctFault, Unpacked } from '@hyped/telemetry-types'; +import type { HistoricalFaults } from '@hyped/telemetry-types/dist/openmct/openmct-fault.types'; +import type { RangeMeasurement } from '@hyped/telemetry-types/dist/pods/pods.types'; import { Point } from '@influxdata/influxdb-client'; -import { Injectable, LoggerService } from '@nestjs/common'; -import { HistoricalFaultDataService } from './data/historical/HistoricalFaultData.service'; -import { RealtimeFaultDataGateway } from './data/realtime/RealtimeFaultData.gateway'; +import { Injectable, type LoggerService } from '@nestjs/common'; +import type { HistoricalFaultDataService } from './data/historical/HistoricalFaultData.service'; +import type { RealtimeFaultDataGateway } from './data/realtime/RealtimeFaultData.gateway'; import { convertToOpenMctFault } from './utils/convertToOpenMctFault'; -import { HistoricalFaults } from '@hyped/telemetry-types/dist/openmct/openmct-fault.types'; export type Fault = { - level: FaultLevel; - measurement: RangeMeasurement; - tripReading: MeasurementReading; + level: FaultLevel; + measurement: RangeMeasurement; + tripReading: MeasurementReading; }; @Injectable() export class FaultService { - constructor( - @Logger() - private readonly logger: LoggerService, - private influxService: InfluxService, - private historicalService: HistoricalFaultDataService, - private realtimeService: RealtimeFaultDataGateway, - ) {} - - /** - * Adds a limit breach fault to the database and sends it to the client. - * @param fault The fault to add - */ - public async addLimitBreachFault(fault: Fault) { - const { measurement, tripReading } = fault; - - const possibleExistingFaults = - await this.historicalService.getHistoricalFaults({ - podId: tripReading.podId, - measurementKey: measurement.key, - }); - - // If there's an existing fault, update it instead of creating a new one - if (possibleExistingFaults && possibleExistingFaults?.length > 0) { - const existingFault = possibleExistingFaults[0]; - this.logger.debug( - `Found existing fault ${existingFault.openMctFault.fault.id}, updating`, - FaultService.name, - ); - this.updateExistingFault(existingFault, tripReading); - return; - } - - const openMctFault = convertToOpenMctFault(fault); - this.saveFault(fault, openMctFault); - this.realtimeService.sendFault(openMctFault); - } - - /** - * Acknowledges a fault in the database and sends it to the client. - * @param faultId The id of the fault to acknowledge - * @param comment The comment sent with the acknowledgement - */ - public async acknowledgeFault(faultId: string, comment?: string) { - this.logger.debug( - `Acknowledging fault with id ${faultId}. ${comment ? `Comment: ${comment}` : ''}`, - FaultService.name, - ); - - // Get the fault from the database - const possibleFault = await this.historicalService.getHistoricalFaults({ - faultId, - }); - - if (!possibleFault || possibleFault.length === 0) { - this.logger.error( - `Fault with id ${faultId} not found`, - FaultService.name, - ); - return; - } - - const fault = possibleFault[0]; - const updatedFault = fault.openMctFault; - updatedFault.fault.acknowledged = true; - - const now = new Date(Date.now()); - - const point = new Point('fault') - .timestamp(now) - .tag('faultId', updatedFault.fault.id) - .tag('podId', fault.podId) - .tag('measurementKey', fault.measurementKey) - .stringField('fault', JSON.stringify(updatedFault)); - - try { - this.influxService.faultsWrite.writePoint(point); - - this.logger.debug( - `Acknowledged fault with id ${updatedFault.fault.id}`, - FaultService.name, - ); - } catch (e: unknown) { - this.logger.error( - `Failed to acknowledge fault with id ${updatedFault.fault.id}`, - e, - FaultService.name, - ); - } - - this.realtimeService.sendFault(updatedFault); - } - - /** - * Shelves or unshelves a fault in the database and sends it to the client. - * @param faultId The id of the fault to shelve - * @param shelved Whether to shelve or unshelve the fault - * @param shelveDuration The duration to shelve the fault for (in milliseconds) - * @param comment The comment sent with the shelve - */ - public async shelveFault( - faultId: string, - shelved: boolean, - shelveDuration: number, - comment?: string, - ) { - this.logger.debug( - `Shelving fault with id ${faultId} for ${shelveDuration} seconds. ${comment ? `Comment: ${comment}` : ''}`, - FaultService.name, - ); - - // Get the fault from the database - const possibleFault = await this.historicalService.getHistoricalFaults({ - faultId, - }); - - if (!possibleFault || possibleFault.length === 0) { - this.logger.error( - `Fault with id ${faultId} not found`, - FaultService.name, - ); - return; - } - - const fault = possibleFault[0]; - const updatedFault = fault.openMctFault; - updatedFault.fault.shelved = shelved; - - const now = new Date(Date.now()); - - const point = new Point('fault') - .timestamp(now) - .tag('faultId', updatedFault.fault.id) - .tag('podId', fault.podId) - .tag('measurementKey', fault.measurementKey) - .stringField('fault', JSON.stringify(updatedFault)); - - // If the fault is being shelved, set a timer to unshelve it - if (shelved) { - this.logger.debug( - `Setting timer to unshelve fault with id ${faultId}`, - FaultService.name, - ); - // This isn't ideal, since this doesn't persist if the server restarts. - // Also, if the fault is manually unshelved before the timer runs out, the timer should be cancelled and it isn't. - // (So the fault will be unshelved twice, once manually and once automatically. Plus, if the fault is unshelved and then shelved again, the timer won't be reset.) - setTimeout(() => { - this.logger.debug( - `Automatically unshelving fault with id ${faultId} because the shelve duration has passed`, - FaultService.name, - ); - void this.shelveFault(faultId, false, 0, ''); - }, shelveDuration); - } - - try { - this.influxService.faultsWrite.writePoint(point); - - this.logger.debug( - `Shelved fault with id ${updatedFault.fault.id}`, - FaultService.name, - ); - } catch (e: unknown) { - this.logger.error( - `Failed to shelve fault with id ${updatedFault.fault.id}`, - e, - FaultService.name, - ); - } - - this.realtimeService.sendFault(updatedFault); - } - - /** - * Saves a fault to the database. - * @param fault The fault to save - * @param openMctFault The Open MCT fault object to save - */ - private saveFault(fault: Fault, openMctFault: OpenMctFault) { - const { measurement, tripReading } = fault; - - const point = new Point('fault') - .timestamp(tripReading.timestamp) - .tag('faultId', openMctFault.fault.id) - .tag('podId', tripReading.podId) - .tag('measurementKey', measurement.key) - // is influx the right choice? probably not - but we're already using it for telemetry - .stringField('fault', JSON.stringify(openMctFault)); - - try { - this.influxService.faultsWrite.writePoint(point); - - this.logger.debug( - `Adding fault with id ${openMctFault.fault.id}`, - FaultService.name, - ); - } catch (e: unknown) { - this.logger.error( - `Failed to add fault {${openMctFault.fault.id}}`, - e, - FaultService.name, - ); - } - } - - /** - * Updates an existing fault in the database. - * @param influxFault The fault to update - * @param updatedReading The updated reading - */ - private updateExistingFault( - influxFault: Unpacked, - updatedReading: MeasurementReading, - ) { - const updatedFault = influxFault.openMctFault; - updatedFault.fault.currentValueInfo.value = updatedReading.value; - - const point = new Point('fault') - .timestamp(updatedReading.timestamp) - .tag('faultId', updatedFault.fault.id) - .tag('podId', updatedReading.podId) - .tag('measurementKey', updatedReading.measurementKey) - .stringField('fault', JSON.stringify(updatedFault)); - - try { - this.influxService.faultsWrite.writePoint(point); - - this.logger.debug( - `Updating fault with id ${updatedFault.fault.id}`, - FaultService.name, - ); - } catch (e: unknown) { - this.logger.error( - `Failed to update fault with id ${updatedFault.fault.id}`, - e, - FaultService.name, - ); - } - - this.realtimeService.sendFault(updatedFault); - } + constructor( + @Logger() + private readonly logger: LoggerService, + private influxService: InfluxService, + private historicalService: HistoricalFaultDataService, + private realtimeService: RealtimeFaultDataGateway, + ) {} + + /** + * Adds a limit breach fault to the database and sends it to the client. + * @param fault The fault to add + */ + public async addLimitBreachFault(fault: Fault) { + const { measurement, tripReading } = fault; + + const possibleExistingFaults = + await this.historicalService.getHistoricalFaults({ + podId: tripReading.podId, + measurementKey: measurement.key, + }); + + // If there's an existing fault, update it instead of creating a new one + if (possibleExistingFaults && possibleExistingFaults?.length > 0) { + const existingFault = possibleExistingFaults[0]; + this.logger.debug( + `Found existing fault ${existingFault.openMctFault.fault.id}, updating`, + FaultService.name, + ); + this.updateExistingFault(existingFault, tripReading); + return; + } + + const openMctFault = convertToOpenMctFault(fault); + this.saveFault(fault, openMctFault); + this.realtimeService.sendFault(openMctFault); + } + + /** + * Acknowledges a fault in the database and sends it to the client. + * @param faultId The id of the fault to acknowledge + * @param comment The comment sent with the acknowledgement + */ + public async acknowledgeFault(faultId: string, comment?: string) { + this.logger.debug( + `Acknowledging fault with id ${faultId}. ${comment ? `Comment: ${comment}` : ''}`, + FaultService.name, + ); + + // Get the fault from the database + const possibleFault = await this.historicalService.getHistoricalFaults({ + faultId, + }); + + if (!possibleFault || possibleFault.length === 0) { + this.logger.error( + `Fault with id ${faultId} not found`, + FaultService.name, + ); + return; + } + + const fault = possibleFault[0]; + const updatedFault = fault.openMctFault; + updatedFault.fault.acknowledged = true; + + const now = new Date(Date.now()); + + const point = new Point('fault') + .timestamp(now) + .tag('faultId', updatedFault.fault.id) + .tag('podId', fault.podId) + .tag('measurementKey', fault.measurementKey) + .stringField('fault', JSON.stringify(updatedFault)); + + try { + this.influxService.faultsWrite.writePoint(point); + + this.logger.debug( + `Acknowledged fault with id ${updatedFault.fault.id}`, + FaultService.name, + ); + } catch (e: unknown) { + this.logger.error( + `Failed to acknowledge fault with id ${updatedFault.fault.id}`, + e, + FaultService.name, + ); + } + + this.realtimeService.sendFault(updatedFault); + } + + /** + * Shelves or unshelves a fault in the database and sends it to the client. + * @param faultId The id of the fault to shelve + * @param shelved Whether to shelve or unshelve the fault + * @param shelveDuration The duration to shelve the fault for (in milliseconds) + * @param comment The comment sent with the shelve + */ + public async shelveFault( + faultId: string, + shelved: boolean, + shelveDuration: number, + comment?: string, + ) { + this.logger.debug( + `Shelving fault with id ${faultId} for ${shelveDuration} seconds. ${comment ? `Comment: ${comment}` : ''}`, + FaultService.name, + ); + + // Get the fault from the database + const possibleFault = await this.historicalService.getHistoricalFaults({ + faultId, + }); + + if (!possibleFault || possibleFault.length === 0) { + this.logger.error( + `Fault with id ${faultId} not found`, + FaultService.name, + ); + return; + } + + const fault = possibleFault[0]; + const updatedFault = fault.openMctFault; + updatedFault.fault.shelved = shelved; + + const now = new Date(Date.now()); + + const point = new Point('fault') + .timestamp(now) + .tag('faultId', updatedFault.fault.id) + .tag('podId', fault.podId) + .tag('measurementKey', fault.measurementKey) + .stringField('fault', JSON.stringify(updatedFault)); + + // If the fault is being shelved, set a timer to unshelve it + if (shelved) { + this.logger.debug( + `Setting timer to unshelve fault with id ${faultId}`, + FaultService.name, + ); + // This isn't ideal, since this doesn't persist if the server restarts. + // Also, if the fault is manually unshelved before the timer runs out, the timer should be cancelled and it isn't. + // (So the fault will be unshelved twice, once manually and once automatically. Plus, if the fault is unshelved and then shelved again, the timer won't be reset.) + setTimeout(() => { + this.logger.debug( + `Automatically unshelving fault with id ${faultId} because the shelve duration has passed`, + FaultService.name, + ); + void this.shelveFault(faultId, false, 0, ''); + }, shelveDuration); + } + + try { + this.influxService.faultsWrite.writePoint(point); + + this.logger.debug( + `Shelved fault with id ${updatedFault.fault.id}`, + FaultService.name, + ); + } catch (e: unknown) { + this.logger.error( + `Failed to shelve fault with id ${updatedFault.fault.id}`, + e, + FaultService.name, + ); + } + + this.realtimeService.sendFault(updatedFault); + } + + /** + * Saves a fault to the database. + * @param fault The fault to save + * @param openMctFault The Open MCT fault object to save + */ + private saveFault(fault: Fault, openMctFault: OpenMctFault) { + const { measurement, tripReading } = fault; + + const point = new Point('fault') + .timestamp(tripReading.timestamp) + .tag('faultId', openMctFault.fault.id) + .tag('podId', tripReading.podId) + .tag('measurementKey', measurement.key) + // is influx the right choice? probably not - but we're already using it for telemetry + .stringField('fault', JSON.stringify(openMctFault)); + + try { + this.influxService.faultsWrite.writePoint(point); + + this.logger.debug( + `Adding fault with id ${openMctFault.fault.id}`, + FaultService.name, + ); + } catch (e: unknown) { + this.logger.error( + `Failed to add fault {${openMctFault.fault.id}}`, + e, + FaultService.name, + ); + } + } + + /** + * Updates an existing fault in the database. + * @param influxFault The fault to update + * @param updatedReading The updated reading + */ + private updateExistingFault( + influxFault: Unpacked, + updatedReading: MeasurementReading, + ) { + const updatedFault = influxFault.openMctFault; + updatedFault.fault.currentValueInfo.value = updatedReading.value; + + const point = new Point('fault') + .timestamp(updatedReading.timestamp) + .tag('faultId', updatedFault.fault.id) + .tag('podId', updatedReading.podId) + .tag('measurementKey', updatedReading.measurementKey) + .stringField('fault', JSON.stringify(updatedFault)); + + try { + this.influxService.faultsWrite.writePoint(point); + + this.logger.debug( + `Updating fault with id ${updatedFault.fault.id}`, + FaultService.name, + ); + } catch (e: unknown) { + this.logger.error( + `Failed to update fault with id ${updatedFault.fault.id}`, + e, + FaultService.name, + ); + } + + this.realtimeService.sendFault(updatedFault); + } } diff --git a/telemetry/packages/server/src/modules/openmct/faults/data/historical/HistoricalFaultData.controller.ts b/telemetry/packages/server/src/modules/openmct/faults/data/historical/HistoricalFaultData.controller.ts index c7fd2403..07ac6cfa 100644 --- a/telemetry/packages/server/src/modules/openmct/faults/data/historical/HistoricalFaultData.controller.ts +++ b/telemetry/packages/server/src/modules/openmct/faults/data/historical/HistoricalFaultData.controller.ts @@ -1,30 +1,30 @@ +import type { OpenMctHistoricalFaults } from '@hyped/telemetry-types/dist/openmct/openmct-fault.types'; import { Controller, Get, Param } from '@nestjs/common'; -import { HistoricalFaultDataService } from './HistoricalFaultData.service'; -import { OpenMctHistoricalFaults } from '@hyped/telemetry-types/dist/openmct/openmct-fault.types'; +import type { HistoricalFaultDataService } from './HistoricalFaultData.service'; @Controller('openmct/faults/historical') export class HistoricalFaultsDataController { - constructor(private historicalDataService: HistoricalFaultDataService) {} + constructor(private historicalDataService: HistoricalFaultDataService) {} - @Get() - async getAllFaults(): Promise { - const faults = await this.historicalDataService.getHistoricalFaults({}); - return faults.map((fault) => ({ - timestamp: fault.timestamp, - fault: fault.openMctFault, - })); - } + @Get() + async getAllFaults(): Promise { + const faults = await this.historicalDataService.getHistoricalFaults({}); + return faults.map((fault) => ({ + timestamp: fault.timestamp, + fault: fault.openMctFault, + })); + } - @Get('pods/:podId') - async getFaultsForPod( - @Param('podId') podId: string, - ): Promise { - const faults = await this.historicalDataService.getHistoricalFaults({ - podId, - }); - return faults.map((fault) => ({ - timestamp: fault.timestamp, - fault: fault.openMctFault, - })); - } + @Get('pods/:podId') + async getFaultsForPod( + @Param('podId') podId: string, + ): Promise { + const faults = await this.historicalDataService.getHistoricalFaults({ + podId, + }); + return faults.map((fault) => ({ + timestamp: fault.timestamp, + fault: fault.openMctFault, + })); + } } diff --git a/telemetry/packages/server/src/modules/openmct/faults/data/historical/HistoricalFaultData.service.ts b/telemetry/packages/server/src/modules/openmct/faults/data/historical/HistoricalFaultData.service.ts index 19b3f8e8..38f494d3 100644 --- a/telemetry/packages/server/src/modules/openmct/faults/data/historical/HistoricalFaultData.service.ts +++ b/telemetry/packages/server/src/modules/openmct/faults/data/historical/HistoricalFaultData.service.ts @@ -1,75 +1,75 @@ import { INFLUX_FAULTS_BUCKET } from '@/core/config'; -import { InfluxRow } from '@/modules/common/types/InfluxRow'; -import { InfluxService } from '@/modules/influx/Influx.service'; +import type { InfluxRow } from '@/modules/common/types/InfluxRow'; +import type { InfluxService } from '@/modules/influx/Influx.service'; import { Logger } from '@/modules/logger/Logger.decorator'; -import { OpenMctFault } from '@hyped/telemetry-types'; -import { HistoricalFaults } from '@hyped/telemetry-types/dist/openmct/openmct-fault.types'; +import type { OpenMctFault } from '@hyped/telemetry-types'; +import type { HistoricalFaults } from '@hyped/telemetry-types/dist/openmct/openmct-fault.types'; import { fluxString } from '@influxdata/influxdb-client'; -import { HttpException, Injectable, LoggerService } from '@nestjs/common'; +import { HttpException, Injectable, type LoggerService } from '@nestjs/common'; interface InfluxFaultRow extends InfluxRow { - faultId: string; - measurementKey: string; - /** - * This is the result of JSON.stringify on an OpenMctFault - */ - openMctFault: string; + faultId: string; + measurementKey: string; + /** + * This is the result of JSON.stringify on an OpenMctFault + */ + openMctFault: string; } type GetHistoricalFaultsInput = { - podId?: string; - measurementKey?: string; - faultId?: string; + podId?: string; + measurementKey?: string; + faultId?: string; }; @Injectable() export class HistoricalFaultDataService { - constructor( - private influxService: InfluxService, - @Logger() - private readonly logger: LoggerService, - ) {} + constructor( + private influxService: InfluxService, + @Logger() + private readonly logger: LoggerService, + ) {} - public async getHistoricalFaults( - props: GetHistoricalFaultsInput, - ): Promise { - const { podId, measurementKey, faultId } = props; + public async getHistoricalFaults( + props: GetHistoricalFaultsInput, + ): Promise { + const { podId, measurementKey, faultId } = props; - const query = `from(bucket: "${INFLUX_FAULTS_BUCKET}") + const query = `from(bucket: "${INFLUX_FAULTS_BUCKET}") |> range(start: -24h) ${podId ? `|> filter(fn: (r) => r["podId"] == ${fluxString(podId) as unknown as string})` : ''} ${ - measurementKey - ? `|> filter(fn: (r) => r["measurementKey"] == ${ - fluxString(measurementKey) as unknown as string - })` - : '' - } + measurementKey + ? `|> filter(fn: (r) => r["measurementKey"] == ${ + fluxString(measurementKey) as unknown as string + })` + : '' + } ${ - faultId - ? `|> filter(fn: (r) => r["faultId"] == ${fluxString(faultId) as unknown as string})` - : '' - } + faultId + ? `|> filter(fn: (r) => r["faultId"] == ${fluxString(faultId) as unknown as string})` + : '' + } |> group(columns: ["faultId"]) |> last()`; - try { - const data = - await this.influxService.query.collectRows(query); - return data.map((row) => ({ - faultId: row['faultId'], - timestamp: new Date(row['_time']).getTime(), - openMctFault: JSON.parse(row['_value']) as OpenMctFault, - podId: row['podId'], - measurementKey: row['measurementKey'], - })); - } catch (e: unknown) { - this.logger.error( - `Failed to get faults for pod ${podId}`, - e, - HistoricalFaultDataService.name, - ); - throw new HttpException("Couldn't get historical faults", 500); - } - } + try { + const data = + await this.influxService.query.collectRows(query); + return data.map((row) => ({ + faultId: row.faultId, + timestamp: new Date(row._time).getTime(), + openMctFault: JSON.parse(row._value) as OpenMctFault, + podId: row.podId, + measurementKey: row.measurementKey, + })); + } catch (e: unknown) { + this.logger.error( + `Failed to get faults for pod ${podId}`, + e, + HistoricalFaultDataService.name, + ); + throw new HttpException("Couldn't get historical faults", 500); + } + } } diff --git a/telemetry/packages/server/src/modules/openmct/faults/data/realtime/RealtimeFaultData.gateway.ts b/telemetry/packages/server/src/modules/openmct/faults/data/realtime/RealtimeFaultData.gateway.ts index 7351e76f..7d3a096e 100644 --- a/telemetry/packages/server/src/modules/openmct/faults/data/realtime/RealtimeFaultData.gateway.ts +++ b/telemetry/packages/server/src/modules/openmct/faults/data/realtime/RealtimeFaultData.gateway.ts @@ -1,50 +1,50 @@ import { Logger } from '@/modules/logger/Logger.decorator'; import { socket as socketConstants } from '@hyped/telemetry-constants'; -import { OpenMctFault } from '@hyped/telemetry-types'; -import { LoggerService } from '@nestjs/common'; +import type { OpenMctFault } from '@hyped/telemetry-types'; +import type { LoggerService } from '@nestjs/common'; import { - ConnectedSocket, - SubscribeMessage, - WebSocketGateway, - WebSocketServer, + ConnectedSocket, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, } from '@nestjs/websockets'; -import { Server, Socket } from 'socket.io'; +import type { Server, Socket } from 'socket.io'; const FAULT_ROOM = 'faults'; @WebSocketGateway({ - path: '/openmct/faults/realtime', - cors: { - origin: '*', - }, + path: '/openmct/faults/realtime', + cors: { + origin: '*', + }, }) export class RealtimeFaultDataGateway { - @WebSocketServer() - socket: Server; + @WebSocketServer() + socket: Server; - constructor( - @Logger() - private readonly logger: LoggerService, - ) {} + constructor( + @Logger() + private readonly logger: LoggerService, + ) {} - @SubscribeMessage(socketConstants.EVENTS.SUBSCRIBE_TO_FAULTS) - async subscribeToFaults(@ConnectedSocket() client: Socket) { - await client.join(FAULT_ROOM); - } + @SubscribeMessage(socketConstants.EVENTS.SUBSCRIBE_TO_FAULTS) + async subscribeToFaults(@ConnectedSocket() client: Socket) { + await client.join(FAULT_ROOM); + } - @SubscribeMessage(socketConstants.EVENTS.UNSUBSCRIBE_FROM_FAULTS) - async unsubscribeFromFaults(@ConnectedSocket() client: Socket) { - await client.leave(FAULT_ROOM); - } + @SubscribeMessage(socketConstants.EVENTS.UNSUBSCRIBE_FROM_FAULTS) + async unsubscribeFromFaults(@ConnectedSocket() client: Socket) { + await client.leave(FAULT_ROOM); + } - sendFault(fault: OpenMctFault) { - this.socket.to(FAULT_ROOM).emit(socketConstants.FAULT_EVENT, { - fault, - }); + sendFault(fault: OpenMctFault) { + this.socket.to(FAULT_ROOM).emit(socketConstants.FAULT_EVENT, { + fault, + }); - this.logger.debug( - `Sending fault with id ${fault.fault.id}`, - RealtimeFaultDataGateway.name, - ); - } + this.logger.debug( + `Sending fault with id ${fault.fault.id}`, + RealtimeFaultDataGateway.name, + ); + } } diff --git a/telemetry/packages/server/src/modules/openmct/faults/utils/convertToOpenMctFault.ts b/telemetry/packages/server/src/modules/openmct/faults/utils/convertToOpenMctFault.ts index 6465c655..6dd54e17 100644 --- a/telemetry/packages/server/src/modules/openmct/faults/utils/convertToOpenMctFault.ts +++ b/telemetry/packages/server/src/modules/openmct/faults/utils/convertToOpenMctFault.ts @@ -1,6 +1,6 @@ -import { OpenMctFault } from '@hyped/telemetry-types'; +import type { OpenMctFault } from '@hyped/telemetry-types'; import { nanoid } from 'nanoid'; -import { Fault } from '../Fault.service'; +import type { Fault } from '../Fault.service'; /** * Converts a fault to an Open MCT fault object. @@ -8,32 +8,32 @@ import { Fault } from '../Fault.service'; * @returns The Open MCT fault object */ export function convertToOpenMctFault(fault: Fault): OpenMctFault { - const { measurement, tripReading, level } = fault; + const { measurement, tripReading, level } = fault; - const namespace = `/${tripReading.podId}/${measurement.key}`; + const namespace = `/${tripReading.podId}/${measurement.key}`; - return { - type: 'global-alarm-status', - fault: { - id: `${namespace}-${nanoid()}`, - name: `${measurement.name} is out of range`, - namespace, - seqNum: 0, - severity: level, - shortDescription: '', - shelved: false, - acknowledged: false, - triggerTime: tripReading.timestamp.toString(), - triggerValueInfo: { - value: tripReading.value, - rangeCondition: level, - monitoringResult: level, - }, - currentValueInfo: { - value: tripReading.value, - rangeCondition: level, - monitoringResult: level, - }, - }, - }; + return { + type: 'global-alarm-status', + fault: { + id: `${namespace}-${nanoid()}`, + name: `${measurement.name} is out of range`, + namespace, + seqNum: 0, + severity: level, + shortDescription: '', + shelved: false, + acknowledged: false, + triggerTime: tripReading.timestamp.toString(), + triggerValueInfo: { + value: tripReading.value, + rangeCondition: level, + monitoringResult: level, + }, + currentValueInfo: { + value: tripReading.value, + rangeCondition: level, + monitoringResult: level, + }, + }, + }; } diff --git a/telemetry/packages/server/src/modules/openmct/object-types/ObjectTypes.controller.ts b/telemetry/packages/server/src/modules/openmct/object-types/ObjectTypes.controller.ts index d95b7086..5b7bfe7a 100644 --- a/telemetry/packages/server/src/modules/openmct/object-types/ObjectTypes.controller.ts +++ b/telemetry/packages/server/src/modules/openmct/object-types/ObjectTypes.controller.ts @@ -1,12 +1,12 @@ import { Controller, Get } from '@nestjs/common'; -import { ObjectTypesService as ObjectTypesService } from './ObjectTypes.service'; +import type { ObjectTypesService } from './ObjectTypes.service'; @Controller('openmct/object-types') export class ObjectTypesController { - constructor(private objectTypesService: ObjectTypesService) {} + constructor(private objectTypesService: ObjectTypesService) {} - @Get() - getObjectTypes() { - return this.objectTypesService.getObjectTypes(); - } + @Get() + getObjectTypes() { + return this.objectTypesService.getObjectTypes(); + } } diff --git a/telemetry/packages/server/src/modules/openmct/object-types/ObjectTypes.service.ts b/telemetry/packages/server/src/modules/openmct/object-types/ObjectTypes.service.ts index 62d900a0..3473ae86 100644 --- a/telemetry/packages/server/src/modules/openmct/object-types/ObjectTypes.service.ts +++ b/telemetry/packages/server/src/modules/openmct/object-types/ObjectTypes.service.ts @@ -1,14 +1,14 @@ import { openMctObjectTypes } from '@hyped/telemetry-constants'; -import { OpenMctObjectTypes } from '@hyped/telemetry-types'; +import type { OpenMctObjectTypes } from '@hyped/telemetry-types'; import { Injectable } from '@nestjs/common'; @Injectable() export class ObjectTypesService { - /** - * Get the object types for Open MCT. - * @returns The object types for Open MCT from the constants. - */ - getObjectTypes(): OpenMctObjectTypes { - return openMctObjectTypes; - } + /** + * Get the object types for Open MCT. + * @returns The object types for Open MCT from the constants. + */ + getObjectTypes(): OpenMctObjectTypes { + return openMctObjectTypes; + } } diff --git a/telemetry/packages/server/src/modules/public-data/PublicData.controller.ts b/telemetry/packages/server/src/modules/public-data/PublicData.controller.ts index 6a78036d..666fbbf0 100644 --- a/telemetry/packages/server/src/modules/public-data/PublicData.controller.ts +++ b/telemetry/packages/server/src/modules/public-data/PublicData.controller.ts @@ -1,175 +1,175 @@ -import { Controller, Get, HttpException, Param, Query } from '@nestjs/common'; -import { PublicDataService } from './PublicData.service'; -import { HistoricalTelemetryDataService } from '@/modules/openmct/data/historical/HistoricalTelemetryData.service'; -import { - LevitationHeightResponse, - RawLevitationHeight, +import type { HistoricalTelemetryDataService } from '@/modules/openmct/data/historical/HistoricalTelemetryData.service'; +import { POD_IDS, type PodId } from '@hyped/telemetry-constants'; +import type { + LevitationHeightResponse, + RawLevitationHeight, } from '@hyped/telemetry-types'; -import { - HistoricalValueResponse, - LaunchTimeResponse, - LevitationHeight, - StateResponse, +import type { + HistoricalValueResponse, + LaunchTimeResponse, + LevitationHeight, + StateResponse, } from '@hyped/telemetry-types/dist/server/responses'; -import { POD_IDS, PodId } from '@hyped/telemetry-constants'; +import { Controller, Get, HttpException, Param, Query } from '@nestjs/common'; +import type { PublicDataService } from './PublicData.service'; @Controller('pods/:podId/public-data') export class PublicDataController { - constructor( - private publicDataService: PublicDataService, - private historicalTelemetryDataService: HistoricalTelemetryDataService, - ) {} + constructor( + private publicDataService: PublicDataService, + private historicalTelemetryDataService: HistoricalTelemetryDataService, + ) {} - @Get('launch-time') - async getLaunchTime( - @Param('podId') podId: string, - ): Promise { - this.validatePodId(podId); - return this.publicDataService.getLaunchTime(podId); - } + @Get('launch-time') + async getLaunchTime( + @Param('podId') podId: string, + ): Promise { + this.validatePodId(podId); + return this.publicDataService.getLaunchTime(podId); + } - @Get('state') - async getState(@Param('podId') podId: string): Promise { - this.validatePodId(podId); - return this.publicDataService.getState(podId); - } + @Get('state') + async getState(@Param('podId') podId: string): Promise { + this.validatePodId(podId); + return this.publicDataService.getState(podId); + } - @Get('velocity') - async getData( - @Param('podId') podId: string, - @Query('start') startTimestamp: string, - @Query('end') endTimestamp?: string, - ): Promise { - if (!startTimestamp) { - throw new HttpException("Missing 'start' query parameter", 400); - } - this.validatePodId(podId); - return this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'velocity', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ); - } + @Get('velocity') + async getData( + @Param('podId') podId: string, + @Query('start') startTimestamp: string, + @Query('end') endTimestamp?: string, + ): Promise { + if (!startTimestamp) { + throw new HttpException("Missing 'start' query parameter", 400); + } + this.validatePodId(podId); + return this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'velocity', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ); + } - @Get('displacement') - async getDisplacement( - @Param('podId') podId: string, - @Query('start') startTimestamp: string, - @Query('end') endTimestamp?: string, - ): Promise { - if (!startTimestamp) { - throw new HttpException("Missing 'start' query parameter", 400); - } - this.validatePodId(podId); - return this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'displacement', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ); - } + @Get('displacement') + async getDisplacement( + @Param('podId') podId: string, + @Query('start') startTimestamp: string, + @Query('end') endTimestamp?: string, + ): Promise { + if (!startTimestamp) { + throw new HttpException("Missing 'start' query parameter", 400); + } + this.validatePodId(podId); + return this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'displacement', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ); + } - @Get('acceleration') - async getAcceleration( - @Param('podId') podId: string, - @Query('start') startTimestamp: string, - @Query('end') endTimestamp?: string, - ): Promise { - if (!startTimestamp) { - throw new HttpException("Missing 'start' query parameter", 400); - } - this.validatePodId(podId); - return this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'acceleration', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ); - } + @Get('acceleration') + async getAcceleration( + @Param('podId') podId: string, + @Query('start') startTimestamp: string, + @Query('end') endTimestamp?: string, + ): Promise { + if (!startTimestamp) { + throw new HttpException("Missing 'start' query parameter", 400); + } + this.validatePodId(podId); + return this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'acceleration', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ); + } - @Get('levitation-height') - async getLevitationHeight( - @Param('podId') podId: string, - @Query('start') startTimestamp: string, - @Query('end') endTimestamp?: string, - ): Promise { - this.validatePodId(podId); + @Get('levitation-height') + async getLevitationHeight( + @Param('podId') podId: string, + @Query('start') startTimestamp: string, + @Query('end') endTimestamp?: string, + ): Promise { + this.validatePodId(podId); - // TODOLater: this is basically quite bad, but we'll fix it later - const [ - levitation_height_1, - levitation_height_2, - levitation_height_3, - levitation_height_4, - levitation_height_lateral_1, - levitation_height_lateral_2, - ] = await Promise.all([ - this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'levitation_height_1', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ), - this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'levitation_height_2', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ), - this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'levitation_height_3', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ), - this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'levitation_height_4', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ), - this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'levitation_height_lateral_1', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ), - this.historicalTelemetryDataService.getHistoricalReading( - podId, - 'levitation_height_lateral_2', - startTimestamp, - endTimestamp ?? new Date().getTime().toString(), - ), - ]); + // TODOLater: this is basically quite bad, but we'll fix it later + const [ + levitation_height_1, + levitation_height_2, + levitation_height_3, + levitation_height_4, + levitation_height_lateral_1, + levitation_height_lateral_2, + ] = await Promise.all([ + this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'levitation_height_1', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ), + this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'levitation_height_2', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ), + this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'levitation_height_3', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ), + this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'levitation_height_4', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ), + this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'levitation_height_lateral_1', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ), + this.historicalTelemetryDataService.getHistoricalReading( + podId, + 'levitation_height_lateral_2', + startTimestamp, + endTimestamp ?? new Date().getTime().toString(), + ), + ]); - return { - levitation_height_1: this.convertValueToInt(levitation_height_1[0]), - levitation_height_2: this.convertValueToInt(levitation_height_2[0]), - levitation_height_3: this.convertValueToInt(levitation_height_3[0]), - levitation_height_4: this.convertValueToInt(levitation_height_4[0]), - levitation_height_lateral_1: this.convertValueToInt( - levitation_height_lateral_1[0], - ), - levitation_height_lateral_2: this.convertValueToInt( - levitation_height_lateral_2[0], - ), - } satisfies LevitationHeightResponse; - } + return { + levitation_height_1: this.convertValueToInt(levitation_height_1[0]), + levitation_height_2: this.convertValueToInt(levitation_height_2[0]), + levitation_height_3: this.convertValueToInt(levitation_height_3[0]), + levitation_height_4: this.convertValueToInt(levitation_height_4[0]), + levitation_height_lateral_1: this.convertValueToInt( + levitation_height_lateral_1[0], + ), + levitation_height_lateral_2: this.convertValueToInt( + levitation_height_lateral_2[0], + ), + } satisfies LevitationHeightResponse; + } - private convertValueToInt( - levitationHeights: RawLevitationHeight, - ): LevitationHeight { - return { - id: levitationHeights.id, - timestamp: levitationHeights.timestamp, - value: parseInt(levitationHeights.value), - }; - } + private convertValueToInt( + levitationHeights: RawLevitationHeight, + ): LevitationHeight { + return { + id: levitationHeights.id, + timestamp: levitationHeights.timestamp, + value: Number.parseInt(levitationHeights.value), + }; + } - private validatePodId(podId: string) { - if (!POD_IDS.includes(podId as PodId)) { - throw new HttpException('Invalid pod ID', 400); - } - } + private validatePodId(podId: string) { + if (!POD_IDS.includes(podId as PodId)) { + throw new HttpException('Invalid pod ID', 400); + } + } } diff --git a/telemetry/packages/server/src/modules/public-data/PublicData.module.ts b/telemetry/packages/server/src/modules/public-data/PublicData.module.ts index fba83cc2..6bc08910 100644 --- a/telemetry/packages/server/src/modules/public-data/PublicData.module.ts +++ b/telemetry/packages/server/src/modules/public-data/PublicData.module.ts @@ -1,12 +1,12 @@ +import { InfluxModule } from '@/modules/influx/Influx.module'; +import { HistoricalTelemetryDataService } from '@/modules/openmct/data/historical/HistoricalTelemetryData.service'; import { Module } from '@nestjs/common'; import { PublicDataController } from './PublicData.controller'; import { PublicDataService } from './PublicData.service'; -import { HistoricalTelemetryDataService } from '@/modules/openmct/data/historical/HistoricalTelemetryData.service'; -import { InfluxModule } from '@/modules/influx/Influx.module'; @Module({ - imports: [InfluxModule], - controllers: [PublicDataController], - providers: [PublicDataService, HistoricalTelemetryDataService], + imports: [InfluxModule], + controllers: [PublicDataController], + providers: [PublicDataService, HistoricalTelemetryDataService], }) export class PublicDataModule {} diff --git a/telemetry/packages/server/src/modules/public-data/PublicData.service.ts b/telemetry/packages/server/src/modules/public-data/PublicData.service.ts index fa64bb36..0a3ae7a3 100644 --- a/telemetry/packages/server/src/modules/public-data/PublicData.service.ts +++ b/telemetry/packages/server/src/modules/public-data/PublicData.service.ts @@ -1,35 +1,35 @@ -import { HttpException, Injectable, LoggerService } from '@nestjs/common'; +import type { InfluxRow } from '@/modules/common/types/InfluxRow'; +import type { InfluxService } from '@/modules/influx/Influx.service'; import { Logger } from '@/modules/logger/Logger.decorator'; -import { INFLUX_TELEMETRY_BUCKET } from '../core/config'; -import { flux } from '@influxdata/influxdb-client'; -import { InfluxService } from '@/modules/influx/Influx.service'; import { ACTIVE_STATES } from '@hyped/telemetry-constants'; -import { InfluxRow } from '@/modules/common/types/InfluxRow'; -import { - LaunchTimeResponse, - StateResponse, +import type { + LaunchTimeResponse, + StateResponse, } from '@hyped/telemetry-types/dist/server/responses'; +import { flux } from '@influxdata/influxdb-client'; +import { HttpException, Injectable, type LoggerService } from '@nestjs/common'; +import { INFLUX_TELEMETRY_BUCKET } from '../core/config'; interface InfluxStateRow extends InfluxRow { - stateType: string; + stateType: string; } @Injectable() export class PublicDataService { - constructor( - @Logger() - private readonly logger: LoggerService, - private influxService: InfluxService, - ) {} + constructor( + @Logger() + private readonly logger: LoggerService, + private influxService: InfluxService, + ) {} - /** - * Get the current state of a pod. - * @param podId The pod's ID. - * @returns The current state and the previous state of the pod. - */ - public async getState(podId: string): Promise { - // Get the last state reading from InfluxDB (measurement name should be 'state') - const query = flux` + /** + * Get the current state of a pod. + * @param podId The pod's ID. + * @returns The current state and the previous state of the pod. + */ + public async getState(podId: string): Promise { + // Get the last state reading from InfluxDB (measurement name should be 'state') + const query = flux` from(bucket: "${INFLUX_TELEMETRY_BUCKET}") |> range(start: -1d) |> filter(fn: (r) => r["_measurement"] == "state") @@ -39,60 +39,60 @@ export class PublicDataService { |> limit(n: 2) `; - try { - const data = - await this.influxService.query.collectRows(query); + try { + const data = + await this.influxService.query.collectRows(query); - return { - currentState: data[0] - ? { - state: data[0]['_value'], - timestamp: new Date(data[0]['_time']).getTime(), - stateType: data[0]['stateType'], - } - : null, - previousState: data[1] - ? { - state: data[1]['_value'], - timestamp: new Date(data[1]['_time']).getTime(), - stateType: data[1]['stateType'], - } - : null, - }; - } catch (e: unknown) { - this.logger.error( - `Failed to get historical reading for ${podId}'s state`, - e, - PublicDataService.name, - ); - throw new HttpException("Couldn't get pod's state", 500); - } - } + return { + currentState: data[0] + ? { + state: data[0]._value, + timestamp: new Date(data[0]._time).getTime(), + stateType: data[0].stateType, + } + : null, + previousState: data[1] + ? { + state: data[1]._value, + timestamp: new Date(data[1]._time).getTime(), + stateType: data[1].stateType, + } + : null, + }; + } catch (e: unknown) { + this.logger.error( + `Failed to get historical reading for ${podId}'s state`, + e, + PublicDataService.name, + ); + throw new HttpException("Couldn't get pod's state", 500); + } + } - /** - * Get the launch time of a pod. - * We consider the launch time to be when the pod's state changes from "READY" to "ACCELERATING", and we must currently be in active state or "STOPPED". - * @param podId The pod's ID. - * @returns The launch time of the pod. - */ - public async getLaunchTime(podId: string): Promise { - const currentState = await this.getState(podId); + /** + * Get the launch time of a pod. + * We consider the launch time to be when the pod's state changes from "READY" to "ACCELERATING", and we must currently be in active state or "STOPPED". + * @param podId The pod's ID. + * @returns The launch time of the pod. + */ + public async getLaunchTime(podId: string): Promise { + const currentState = await this.getState(podId); - // If the pod is not in an active state or stopped, launch time isn't defined - if ( - currentState.currentState === null || - !( - Object.keys(ACTIVE_STATES).includes(currentState.currentState.state) || - currentState.currentState.state === 'STOPPED' - ) - ) { - return { - launchTime: null, - }; - } + // If the pod is not in an active state or stopped, launch time isn't defined + if ( + currentState.currentState === null || + !( + Object.keys(ACTIVE_STATES).includes(currentState.currentState.state) || + currentState.currentState.state === 'STOPPED' + ) + ) { + return { + launchTime: null, + }; + } - // Get the last "ACCELERATING" state reading from InfluxDB - const query = flux` + // Get the last "ACCELERATING" state reading from InfluxDB + const query = flux` from(bucket: "${INFLUX_TELEMETRY_BUCKET}") |> range(start: -1d) |> filter(fn: (r) => r["_measurement"] == "state") @@ -103,21 +103,21 @@ export class PublicDataService { |> limit(n: 1) `; - try { - const data = - await this.influxService.query.collectRows(query); - const launchTime = new Date(data[0]['_time']).getTime(); + try { + const data = + await this.influxService.query.collectRows(query); + const launchTime = new Date(data[0]._time).getTime(); - return { - launchTime, - }; - } catch (e: unknown) { - this.logger.error( - `Failed to get launch time for ${podId}`, - e, - PublicDataService.name, - ); - throw new HttpException("Couldn't get pod's launch time", 500); - } - } + return { + launchTime, + }; + } catch (e: unknown) { + this.logger.error( + `Failed to get launch time for ${podId}`, + e, + PublicDataService.name, + ); + throw new HttpException("Couldn't get pod's launch time", 500); + } + } } diff --git a/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.controller.ts b/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.controller.ts index aee85cc1..85f802ec 100644 --- a/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.controller.ts +++ b/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.controller.ts @@ -1,23 +1,23 @@ import { Body, Controller, Param, Post } from '@nestjs/common'; -import { RemoteLogsService } from './RemoteLogs.service'; +import type { RemoteLogsService } from './RemoteLogs.service'; @Controller('logs') export class RemoteLogsController { - constructor(private remoteLogsService: RemoteLogsService) {} + constructor(private remoteLogsService: RemoteLogsService) {} - @Post() - logUIMessage(@Body() body: { message: string }) { - return this.remoteLogsService.logRemoteMessage(body.message); - } + @Post() + logUIMessage(@Body() body: { message: string }) { + return this.remoteLogsService.logRemoteMessage(body.message); + } - @Post(':podId') - logUIMessageWithPodID( - @Param('podId') podId: string, - @Body() body: { message: string }, - ) { - return this.remoteLogsService.logRemoteMessageWithPodID( - podId, - body.message, - ); - } + @Post(':podId') + logUIMessageWithPodID( + @Param('podId') podId: string, + @Body() body: { message: string }, + ) { + return this.remoteLogsService.logRemoteMessageWithPodID( + podId, + body.message, + ); + } } diff --git a/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.module.ts b/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.module.ts index 97a531ba..cd0fbd6a 100644 --- a/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.module.ts +++ b/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.module.ts @@ -3,7 +3,7 @@ import { RemoteLogsController } from './RemoteLogs.controller'; import { RemoteLogsService } from './RemoteLogs.service'; @Module({ - controllers: [RemoteLogsController], - providers: [RemoteLogsService], + controllers: [RemoteLogsController], + providers: [RemoteLogsService], }) export class RemoteLogsModule {} diff --git a/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.service.ts b/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.service.ts index bafd8904..caefe771 100644 --- a/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.service.ts +++ b/telemetry/packages/server/src/modules/remote-logs/RemoteLogs.service.ts @@ -1,34 +1,34 @@ -import { Injectable, LoggerService } from '@nestjs/common'; import { Logger } from '@/modules/logger/Logger.decorator'; +import { Injectable, type LoggerService } from '@nestjs/common'; @Injectable() export class RemoteLogsService { - constructor( - @Logger() - private readonly logger: LoggerService, - ) {} + constructor( + @Logger() + private readonly logger: LoggerService, + ) {} - /** - * Logs a message from the GUI, which is not associated with a particular pod. - * @param message The message from the GUI to log - * @returns True if the message was logged successfully, false otherwise - */ - logRemoteMessage(message: string) { - this.logger.verbose(`[GUI] ${message}`, RemoteLogsService.name); - return true; - } + /** + * Logs a message from the GUI, which is not associated with a particular pod. + * @param message The message from the GUI to log + * @returns True if the message was logged successfully, false otherwise + */ + logRemoteMessage(message: string) { + this.logger.verbose(`[GUI] ${message}`, RemoteLogsService.name); + return true; + } - /** - * Logs a message from the GUI, which is associated with a particular pod. - * @param podId The ID of the pod - * @param message The message from the GUI to log - * @returns True if the message was logged successfully, false otherwise - */ - logRemoteMessageWithPodID(podId: string, message: string) { - this.logger.verbose( - `[GUI - Pod ${podId}] ${message}`, - RemoteLogsService.name, - ); - return true; - } + /** + * Logs a message from the GUI, which is associated with a particular pod. + * @param podId The ID of the pod + * @param message The message from the GUI to log + * @returns True if the message was logged successfully, false otherwise + */ + logRemoteMessageWithPodID(podId: string, message: string) { + this.logger.verbose( + `[GUI - Pod ${podId}] ${message}`, + RemoteLogsService.name, + ); + return true; + } } diff --git a/telemetry/packages/server/src/modules/state/State.module.ts b/telemetry/packages/server/src/modules/state/State.module.ts index 5e7ba54b..b13743ef 100644 --- a/telemetry/packages/server/src/modules/state/State.module.ts +++ b/telemetry/packages/server/src/modules/state/State.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; -import { StateService } from './State.service'; import { InfluxModule } from '../influx/Influx.module'; +import { StateService } from './State.service'; @Module({ - imports: [InfluxModule], - providers: [StateService], - exports: [StateService], + imports: [InfluxModule], + providers: [StateService], + exports: [StateService], }) export class StateModule {} diff --git a/telemetry/packages/server/src/modules/state/State.service.ts b/telemetry/packages/server/src/modules/state/State.service.ts index 2b2eb821..454d33d5 100644 --- a/telemetry/packages/server/src/modules/state/State.service.ts +++ b/telemetry/packages/server/src/modules/state/State.service.ts @@ -1,64 +1,64 @@ -import { Inject, Injectable, LoggerService } from '@nestjs/common'; +import type { InfluxService } from '@/modules/influx/Influx.service'; import { Logger } from '@/modules/logger/Logger.decorator'; -import { StateUpdate, StateUpdateSchema } from './StateUpdate.types'; -import { StateUpdateValidationError } from './errors/MeasurementReadingValidationError'; -import { Point } from '@influxdata/influxdb-client'; -import { InfluxService } from '@/modules/influx/Influx.service'; import { getStateType } from '@hyped/telemetry-constants'; +import { Point } from '@influxdata/influxdb-client'; +import { Inject, Injectable, type LoggerService } from '@nestjs/common'; import { MqttService } from 'nest-mqtt'; +import { type StateUpdate, StateUpdateSchema } from './StateUpdate.types'; +import { StateUpdateValidationError } from './errors/MeasurementReadingValidationError'; @Injectable() export class StateService { - constructor( - @Logger() - private readonly logger: LoggerService, - private influxService: InfluxService, - @Inject(MqttService) private readonly mqttService: MqttService, - ) {} + constructor( + @Logger() + private readonly logger: LoggerService, + private influxService: InfluxService, + @Inject(MqttService) private readonly mqttService: MqttService, + ) {} - public addStateReading(props: StateUpdate) { - const validatedState = this.validateStateUpdate(props); + public addStateReading(props: StateUpdate) { + const validatedState = this.validateStateUpdate(props); - const { podId, value: state, timestamp } = validatedState; - const stateType = getStateType(state); + const { podId, value: state, timestamp } = validatedState; + const stateType = getStateType(state); - // If we want to add state to the OpenMCT dashboard, we can do it here + // If we want to add state to the OpenMCT dashboard, we can do it here - // Then save it to the database - const point = new Point('state') - .timestamp(timestamp) - .tag('podId', podId) - .tag('stateType', stateType) - .stringField('state', state); + // Then save it to the database + const point = new Point('state') + .timestamp(timestamp) + .tag('podId', podId) + .tag('stateType', stateType) + .stringField('state', state); - try { - this.influxService.telemetryWrite.writePoint(point); + try { + this.influxService.telemetryWrite.writePoint(point); - this.logger.debug( - `Added state ${props.podId}: ${props.value} (Type: ${stateType})`, - StateService.name, - ); - } catch (e) { - this.logger.error( - `Failed to add state ${props.podId}: ${props.value} (Type: ${stateType})`, - e, - StateService.name, - ); - } - } + this.logger.debug( + `Added state ${props.podId}: ${props.value} (Type: ${stateType})`, + StateService.name, + ); + } catch (e) { + this.logger.error( + `Failed to add state ${props.podId}: ${props.value} (Type: ${stateType})`, + e, + StateService.name, + ); + } + } - /** - * Validates a state update. - * @param props The state update to validate - * @returns The validated state update, or throws an error if invalid - */ - private validateStateUpdate(props: StateUpdate) { - const result = StateUpdateSchema.safeParse(props); + /** + * Validates a state update. + * @param props The state update to validate + * @returns The validated state update, or throws an error if invalid + */ + private validateStateUpdate(props: StateUpdate) { + const result = StateUpdateSchema.safeParse(props); - if (!result.success) { - throw new StateUpdateValidationError(result.error.message); - } + if (!result.success) { + throw new StateUpdateValidationError(result.error.message); + } - return result.data; - } + return result.data; + } } diff --git a/telemetry/packages/server/src/modules/state/StateUpdate.types.ts b/telemetry/packages/server/src/modules/state/StateUpdate.types.ts index 7f7ac729..8518c884 100644 --- a/telemetry/packages/server/src/modules/state/StateUpdate.types.ts +++ b/telemetry/packages/server/src/modules/state/StateUpdate.types.ts @@ -1,11 +1,11 @@ -import { ALL_POD_STATES, pods } from '@hyped/telemetry-constants'; import { zodEnumFromObjKeys } from '@/modules/common/utils/zodEnumFromObjKeys'; +import { ALL_POD_STATES, pods } from '@hyped/telemetry-constants'; import { z } from 'zod'; export const StateUpdateSchema = z.object({ - podId: zodEnumFromObjKeys(pods), - timestamp: z.string(), // to handle nanoseconds timestamp - value: zodEnumFromObjKeys(ALL_POD_STATES), + podId: zodEnumFromObjKeys(pods), + timestamp: z.string(), // to handle nanoseconds timestamp + value: zodEnumFromObjKeys(ALL_POD_STATES), }); export type StateUpdate = z.infer; diff --git a/telemetry/packages/server/src/modules/state/errors/MeasurementReadingValidationError.ts b/telemetry/packages/server/src/modules/state/errors/MeasurementReadingValidationError.ts index 1bf933cc..02de9280 100644 --- a/telemetry/packages/server/src/modules/state/errors/MeasurementReadingValidationError.ts +++ b/telemetry/packages/server/src/modules/state/errors/MeasurementReadingValidationError.ts @@ -1,6 +1,6 @@ export class StateUpdateValidationError extends Error { - constructor(message: string) { - super(message); - this.name = 'StateUpdateValidationError'; - } + constructor(message: string) { + super(message); + this.name = 'StateUpdateValidationError'; + } } diff --git a/telemetry/packages/server/src/modules/state/utils/isValidState.ts b/telemetry/packages/server/src/modules/state/utils/isValidState.ts index a5d88ea6..82836239 100644 --- a/telemetry/packages/server/src/modules/state/utils/isValidState.ts +++ b/telemetry/packages/server/src/modules/state/utils/isValidState.ts @@ -1,5 +1,5 @@ import { ALL_POD_STATES } from '@hyped/telemetry-constants'; export const isValidState = (state: string) => { - return ALL_POD_STATES[state as keyof typeof ALL_POD_STATES] !== undefined; + return ALL_POD_STATES[state as keyof typeof ALL_POD_STATES] !== undefined; }; diff --git a/telemetry/packages/server/src/modules/warnings/Warnings.controller.ts b/telemetry/packages/server/src/modules/warnings/Warnings.controller.ts index 4d6d102d..eac6fe34 100644 --- a/telemetry/packages/server/src/modules/warnings/Warnings.controller.ts +++ b/telemetry/packages/server/src/modules/warnings/Warnings.controller.ts @@ -1,12 +1,12 @@ import { Controller, Param, Post } from '@nestjs/common'; -import { WarningsService } from './Warnings.service'; +import type { WarningsService } from './Warnings.service'; @Controller('pods/:podId/warnings') export class WarningsController { - constructor(private warningsService: WarningsService) {} + constructor(private warningsService: WarningsService) {} - @Post('latency') - createLatencyWarning(@Param('podId') podId: string) { - this.warningsService.createLatencyWarning(podId); - } + @Post('latency') + createLatencyWarning(@Param('podId') podId: string) { + this.warningsService.createLatencyWarning(podId); + } } diff --git a/telemetry/packages/server/src/modules/warnings/Warnings.module.ts b/telemetry/packages/server/src/modules/warnings/Warnings.module.ts index d4678ba2..66221b7a 100644 --- a/telemetry/packages/server/src/modules/warnings/Warnings.module.ts +++ b/telemetry/packages/server/src/modules/warnings/Warnings.module.ts @@ -3,7 +3,7 @@ import { WarningsController } from './Warnings.controller'; import { WarningsService } from './Warnings.service'; @Module({ - controllers: [WarningsController], - providers: [WarningsService], + controllers: [WarningsController], + providers: [WarningsService], }) export class WarningsModule {} diff --git a/telemetry/packages/server/src/modules/warnings/Warnings.service.ts b/telemetry/packages/server/src/modules/warnings/Warnings.service.ts index 731fc8d6..2447a087 100644 --- a/telemetry/packages/server/src/modules/warnings/Warnings.service.ts +++ b/telemetry/packages/server/src/modules/warnings/Warnings.service.ts @@ -1,17 +1,17 @@ -import { Injectable, LoggerService } from '@nestjs/common'; import { Logger } from '@/modules/logger/Logger.decorator'; +import { Injectable, type LoggerService } from '@nestjs/common'; @Injectable() export class WarningsService { - constructor( - @Logger() - private readonly logger: LoggerService, - ) {} + constructor( + @Logger() + private readonly logger: LoggerService, + ) {} - createLatencyWarning(podId: string) { - this.logger.log( - `Creating latency warning for pod ${podId}`, - WarningsService.name, - ); - } + createLatencyWarning(podId: string) { + this.logger.log( + `Creating latency warning for pod ${podId}`, + WarningsService.name, + ); + } } diff --git a/telemetry/packages/server/test/app.e2e-spec.ts b/telemetry/packages/server/test/app.e2e-spec.ts index 50cda623..d86c6d1a 100644 --- a/telemetry/packages/server/test/app.e2e-spec.ts +++ b/telemetry/packages/server/test/app.e2e-spec.ts @@ -1,24 +1,24 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; +import type { INestApplication } from '@nestjs/common'; +import { Test, type TestingModule } from '@nestjs/testing'; import * as request from 'supertest'; import { AppModule } from './../src/app.module'; describe('AppController (e2e)', () => { - let app: INestApplication; + let app: INestApplication; - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); - app = moduleFixture.createNestApplication(); - await app.init(); - }); + app = moduleFixture.createNestApplication(); + await app.init(); + }); - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); }); diff --git a/telemetry/packages/server/test/jest-e2e.json b/telemetry/packages/server/test/jest-e2e.json index e9d912f3..f43e8a69 100644 --- a/telemetry/packages/server/test/jest-e2e.json +++ b/telemetry/packages/server/test/jest-e2e.json @@ -1,9 +1,9 @@ { - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } } diff --git a/telemetry/packages/server/tsconfig.json b/telemetry/packages/server/tsconfig.json index 795f529d..3400636f 100644 --- a/telemetry/packages/server/tsconfig.json +++ b/telemetry/packages/server/tsconfig.json @@ -1,22 +1,22 @@ { - "extends": "@hyped/tsconfig/base.json", - "compilerOptions": { - "module": "commonjs", - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "es2017", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "noEmit": false, - "strictBindCallApply": true, - "strictPropertyInitialization": false, - "paths": { - "@/modules/*": ["src/modules/*"], - "@/core/*": ["src/modules/core/*"], - }, - }, - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"], + "extends": "@hyped/tsconfig/base.json", + "compilerOptions": { + "module": "commonjs", + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "noEmit": false, + "strictBindCallApply": true, + "strictPropertyInitialization": false, + "paths": { + "@/modules/*": ["src/modules/*"], + "@/core/*": ["src/modules/core/*"] + } + }, + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] } diff --git a/telemetry/packages/tsconfig/base.json b/telemetry/packages/tsconfig/base.json index e908bb21..fd99a558 100644 --- a/telemetry/packages/tsconfig/base.json +++ b/telemetry/packages/tsconfig/base.json @@ -1,27 +1,27 @@ { - "$schema": "https://json.schemastore.org/tsconfig", - "display": "Base", - "compilerOptions": { - "composite": false, - "declaration": true, - "declarationMap": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "inlineSources": false, - "isolatedModules": true, - "module": "ESNext", - "moduleResolution": "node", - "noUnusedLocals": false, - "noUnusedParameters": false, - "preserveWatchOutput": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "strictNullChecks": true, - "resolveJsonModule": true, - "noFallthroughCasesInSwitch": true, - "noImplicitAny": true, - "incremental": true - }, - "exclude": ["node_modules"] + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Base", + "compilerOptions": { + "composite": false, + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "isolatedModules": true, + "module": "ESNext", + "moduleResolution": "node", + "noUnusedLocals": false, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "strictNullChecks": true, + "resolveJsonModule": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "incremental": true + }, + "exclude": ["node_modules"] } diff --git a/telemetry/packages/tsconfig/package.json b/telemetry/packages/tsconfig/package.json index 8e9c1a3f..892ed2c8 100644 --- a/telemetry/packages/tsconfig/package.json +++ b/telemetry/packages/tsconfig/package.json @@ -1,5 +1,5 @@ { - "name": "@hyped/tsconfig", - "version": "0.0.0", - "private": true + "name": "@hyped/tsconfig", + "version": "0.0.0", + "private": true } diff --git a/telemetry/packages/types/.eslintrc.js b/telemetry/packages/types/.eslintrc.js deleted file mode 100644 index acab2de9..00000000 --- a/telemetry/packages/types/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ['@hyped/eslint-config/basic.js'], - parserOptions: { - project: true, - tsconfigRootDir: __dirname, - }, -}; diff --git a/telemetry/packages/types/biome.json b/telemetry/packages/types/biome.json new file mode 100644 index 00000000..784b6d85 --- /dev/null +++ b/telemetry/packages/types/biome.json @@ -0,0 +1,3 @@ +{ + "extends": ["../../biome.json"] +} diff --git a/telemetry/packages/types/package.json b/telemetry/packages/types/package.json index d6cb13e5..72f7d3eb 100644 --- a/telemetry/packages/types/package.json +++ b/telemetry/packages/types/package.json @@ -1,17 +1,16 @@ { - "name": "@hyped/telemetry-types", - "private": true, - "version": "0.0.0", - "types": "dist/index.d.ts", - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "lint": "eslint --ext .ts src --max-warnings 0 --report-unused-disable-directives", - "lint:fix": "eslint --ext .ts src --fix" - }, - "dependencies": { - "@hyped/eslint-config": "workspace:*", - "@hyped/tsconfig": "workspace:*", - "typescript": "^5.3.3" - } + "name": "@hyped/telemetry-types", + "private": true, + "version": "0.0.0", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "lint": "biome lint .", + "lint:fix": "biome check --write ." + }, + "dependencies": { + "@hyped/tsconfig": "workspace:*", + "typescript": "^5.3.3" + } } diff --git a/telemetry/packages/types/src/index.ts b/telemetry/packages/types/src/index.ts index 1d82c6e7..dcc5f34d 100644 --- a/telemetry/packages/types/src/index.ts +++ b/telemetry/packages/types/src/index.ts @@ -1,25 +1,25 @@ export type { - Measurement, - RangeMeasurement, - Limits, - Pod, + Measurement, + RangeMeasurement, + Limits, + Pod, } from './pods/pods.types'; export type { - OpenMctDictionary, - OpenMctPod, - OpenMctMeasurement, + OpenMctDictionary, + OpenMctPod, + OpenMctMeasurement, } from './openmct/openmct-dictionary.types'; export type { - OpenMctObjectTypes, - OpenMctObjectType, + OpenMctObjectTypes, + OpenMctObjectType, } from './openmct/openmct-object-types.types'; export type { OpenMctFault } from './openmct/openmct-fault.types'; export type { Unpacked } from './utils/Unpacked'; export type { - RawLevitationHeight, - LevitationHeight, - LevitationHeightResponse, - LaunchTimeResponse, - StateResponse, - HistoricalValueResponse, + RawLevitationHeight, + LevitationHeight, + LevitationHeightResponse, + LaunchTimeResponse, + StateResponse, + HistoricalValueResponse, } from './server/responses'; diff --git a/telemetry/packages/types/src/openmct/openmct-dictionary.types.ts b/telemetry/packages/types/src/openmct/openmct-dictionary.types.ts index f3f10004..f54c2f58 100644 --- a/telemetry/packages/types/src/openmct/openmct-dictionary.types.ts +++ b/telemetry/packages/types/src/openmct/openmct-dictionary.types.ts @@ -1,42 +1,42 @@ -import { Limits } from '../pods/pods.types'; +import type { Limits } from '../pods/pods.types'; /** * Type of an Open MCT measurement. */ export type OpenMctMeasurement = { - name: string; - key: string; - type: string; - values: { - key: string; - name: string; - unit?: string; - format: string; - min?: number; - max?: number; - limits?: Limits; - enumerations?: { - value: number; - string: string; - }[]; - hints?: { - range?: number; - domain?: number; - }; - source?: string; - units?: { - domain: string; - }; - }[]; + name: string; + key: string; + type: string; + values: { + key: string; + name: string; + unit?: string; + format: string; + min?: number; + max?: number; + limits?: Limits; + enumerations?: { + value: number; + string: string; + }[]; + hints?: { + range?: number; + domain?: number; + }; + source?: string; + units?: { + domain: string; + }; + }[]; }; /** * Type of an Open MCT pod. */ export type OpenMctPod = { - id: string; - name: string; - measurements: OpenMctMeasurement[]; + id: string; + name: string; + measurements: OpenMctMeasurement[]; }; /** diff --git a/telemetry/packages/types/src/openmct/openmct-fault.types.ts b/telemetry/packages/types/src/openmct/openmct-fault.types.ts index a072a2ed..51c26325 100644 --- a/telemetry/packages/types/src/openmct/openmct-fault.types.ts +++ b/telemetry/packages/types/src/openmct/openmct-fault.types.ts @@ -1,37 +1,37 @@ export type OpenMctFault = { - type: null | string; - fault: { - acknowledged: boolean; - currentValueInfo: { - value: number; - rangeCondition: string; - monitoringResult: string; - }; - id: string; - name: string; - namespace: string; - seqNum: number; - severity: string; - shelved: boolean; - shortDescription: string; - triggerTime: string; - triggerValueInfo: { - value: number; - rangeCondition: string; - monitoringResult: string; - }; - }; + type: null | string; + fault: { + acknowledged: boolean; + currentValueInfo: { + value: number; + rangeCondition: string; + monitoringResult: string; + }; + id: string; + name: string; + namespace: string; + seqNum: number; + severity: string; + shelved: boolean; + shortDescription: string; + triggerTime: string; + triggerValueInfo: { + value: number; + rangeCondition: string; + monitoringResult: string; + }; + }; }; export type HistoricalFaults = { - faultId: string; - timestamp: number; - openMctFault: OpenMctFault; - podId: string; - measurementKey: string; + faultId: string; + timestamp: number; + openMctFault: OpenMctFault; + podId: string; + measurementKey: string; }[]; export type OpenMctHistoricalFaults = { - timestamp: number; - fault: OpenMctFault; + timestamp: number; + fault: OpenMctFault; }[]; diff --git a/telemetry/packages/types/src/openmct/openmct-object-types.types.ts b/telemetry/packages/types/src/openmct/openmct-object-types.types.ts index 864852b4..138413b1 100644 --- a/telemetry/packages/types/src/openmct/openmct-object-types.types.ts +++ b/telemetry/packages/types/src/openmct/openmct-object-types.types.ts @@ -1,8 +1,8 @@ export type OpenMctObjectType = { - id: string; - name: string; - description?: string; - icon: string; + id: string; + name: string; + description?: string; + icon: string; }; export type OpenMctObjectTypes = OpenMctObjectType[]; diff --git a/telemetry/packages/types/src/pods/pods.types.ts b/telemetry/packages/types/src/pods/pods.types.ts index 65a754ea..361bdc98 100644 --- a/telemetry/packages/types/src/pods/pods.types.ts +++ b/telemetry/packages/types/src/pods/pods.types.ts @@ -1,39 +1,39 @@ // common properties shared by all response variables export type BaseMeasurement = { - name: string; - key: string; - unit: string; - type: string; + name: string; + key: string; + unit: string; + type: string; }; // range limits not to be exceeded // some give warnings when reaching range limits export type Limits = { - warning?: { - low: number; - high: number; - }; - critical: { - low: number; - high: number; - }; + warning?: { + low: number; + high: number; + }; + critical: { + low: number; + high: number; + }; }; // For numerical sensor readings described by operational range sampling parameters export type RangeMeasurement = BaseMeasurement & { - format: 'float' | 'integer'; - limits: Limits; - rms_noise: number; - sampling_time: number; + format: 'float' | 'integer'; + limits: Limits; + rms_noise: number; + sampling_time: number; }; // For discrete status measurements with enumerated states export type EnumMeasurement = BaseMeasurement & { - format: 'enum'; - enumerations: { - value: number; - string: string; - }[]; + format: 'enum'; + enumerations: { + value: number; + string: string; + }[]; }; // export type Measurement as union @@ -41,9 +41,9 @@ export type Measurement = RangeMeasurement | EnumMeasurement; // create Pod type export type Pod = { - name: string; - id: string; - measurements: Record; - // Not ideal given this is defined in the constants package but will do until TOML is done - operationMode: 'ALL_SYSTEMS_ON' | 'LEVITATION_ONLY' | 'LIM_ONLY'; + name: string; + id: string; + measurements: Record; + // Not ideal given this is defined in the constants package but will do until TOML is done + operationMode: 'ALL_SYSTEMS_ON' | 'LEVITATION_ONLY' | 'LIM_ONLY'; }; diff --git a/telemetry/packages/types/src/server/responses.ts b/telemetry/packages/types/src/server/responses.ts index 0342224b..e551693d 100644 --- a/telemetry/packages/types/src/server/responses.ts +++ b/telemetry/packages/types/src/server/responses.ts @@ -1,38 +1,38 @@ export type RawLevitationHeight = { - id: string; - timestamp: number; - value: string; + id: string; + timestamp: number; + value: string; }; export type LevitationHeight = { - id: string; - timestamp: number; - value: number; + id: string; + timestamp: number; + value: number; }; export type LevitationHeightResponse = Record; export type LaunchTimeResponse = { - launchTime: number | null; + launchTime: number | null; }; export type StateResponse = { - currentState: { - state: string; - timestamp: number; - stateType: string; - } | null; - previousState: { - state: string; - timestamp: number; - stateType: string; - } | null; + currentState: { + state: string; + timestamp: number; + stateType: string; + } | null; + previousState: { + state: string; + timestamp: number; + stateType: string; + } | null; }; export type HistoricalValueResponse = { - id: string; - timestamp: number; - value: string; + id: string; + timestamp: number; + value: string; }[]; export type VelocityResponse = HistoricalValueResponse; diff --git a/telemetry/packages/types/tsconfig.json b/telemetry/packages/types/tsconfig.json index 9edc6524..fc063674 100644 --- a/telemetry/packages/types/tsconfig.json +++ b/telemetry/packages/types/tsconfig.json @@ -1,9 +1,9 @@ { - "extends": "@hyped/tsconfig/base.json", - "compilerOptions": { - "outDir": "./dist", - "noEmit": false, - }, - "include": ["src"], - "exclude": ["node_modules"] + "extends": "@hyped/tsconfig/base.json", + "compilerOptions": { + "outDir": "./dist", + "noEmit": false + }, + "include": ["src"], + "exclude": ["node_modules"] } diff --git a/telemetry/packages/ui/.eslintrc.cjs b/telemetry/packages/ui/.eslintrc.cjs deleted file mode 100644 index c2dc8a4b..00000000 --- a/telemetry/packages/ui/.eslintrc.cjs +++ /dev/null @@ -1,10 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ['@hyped/eslint-config/react.js'], - parserOptions: { - project: true, - tsconfigRootDir: __dirname, - }, - ignorePatterns: ['vite.config.ts', 'app/components/ui/**'], -}; diff --git a/telemetry/packages/ui/app/App.tsx b/telemetry/packages/ui/app/App.tsx index 54fe271a..0a5b2b9b 100644 --- a/telemetry/packages/ui/app/App.tsx +++ b/telemetry/packages/ui/app/App.tsx @@ -1,12 +1,12 @@ +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from '@/components/ui/resizable'; import { useState } from 'react'; -import { VIEWS, VIEW_KEYS, VIEW_OPTIONS, ViewOption } from './views'; import { Sidebar } from './components/sidebar'; import { cn } from './lib/utils'; -import { - ResizableHandle, - ResizablePanel, - ResizablePanelGroup, -} from '@/components/ui/resizable'; +import { VIEWS, VIEW_KEYS, VIEW_OPTIONS, type ViewOption } from './views'; /** * The default view to display when the app is opened. @@ -14,30 +14,30 @@ import { const DEFAULT_VIEW: ViewOption = VIEW_OPTIONS.OPEN_MCT; export const App = () => { - const [currentView, setCurrentView] = useState(DEFAULT_VIEW); + const [currentView, setCurrentView] = useState(DEFAULT_VIEW); - return ( - - - {VIEW_KEYS.map((key) => ( -
- {VIEWS[key].component} -
- ))} -
- - - - -
- ); + return ( + + + {VIEW_KEYS.map((key) => ( +
+ {VIEWS[key].component} +
+ ))} +
+ + + + +
+ ); }; diff --git a/telemetry/packages/ui/app/components/error.tsx b/telemetry/packages/ui/app/components/error.tsx deleted file mode 100644 index c355d9d8..00000000 --- a/telemetry/packages/ui/app/components/error.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogTitle, -} from '@/components/ui/alert-dialog'; -import { useErrors } from '@/context/errors'; -import { ChevronLeft, ChevronRight } from 'lucide-react'; -import { useState } from 'react'; -import { DialogHeader } from './ui/dialog'; -import { Button } from './ui/button'; -import { CONTROLS, sendControlMessage } from '@/lib/controls'; -import { useCurrentPod } from '@/context/pods'; - -/** - * Dialog to display error messages from the error context. - */ -export const Error = () => { - const { errors } = useErrors(); - const [index, setIndex] = useState(0); - - const { currentPod } = useCurrentPod(); - - const open = errors.length > 0; - - return ( - - - {/* Error message */} - {errors[index] && ( - <> - - {errors[index].title} - - - {errors[index].message} - - - )} - - {errors[index] && ( - -
- {/* Arrows for navigating between errors */} -
- -

- {index + 1} / {errors.length} -

- -
-
- {/* Emergency Stop Button */} - - {/* Acknowledge button */} - { - errors[index].acknowledge(); - setIndex((i) => (i > 0 ? i - 1 : 0)); - }} - > - Acknowledge - -
-
-
- )} -
-
- ); -}; diff --git a/telemetry/packages/ui/app/components/errors.tsx b/telemetry/packages/ui/app/components/errors.tsx new file mode 100644 index 00000000..b7721915 --- /dev/null +++ b/telemetry/packages/ui/app/components/errors.tsx @@ -0,0 +1,97 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogTitle, +} from '@/components/ui/alert-dialog'; +import { useErrors } from '@/context/errors'; +import { useCurrentPod } from '@/context/pods'; +import { CONTROLS, sendControlMessage } from '@/lib/controls'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; +import { useState } from 'react'; +import { Button } from './ui/button'; +import { DialogHeader } from './ui/dialog'; + +/** + * Dialog to display error messages from the error context. + */ +export const Errors = () => { + const { errors } = useErrors(); + const [index, setIndex] = useState(0); + + const { currentPod } = useCurrentPod(); + + const open = errors.length > 0; + + return ( + + + {/* Error message */} + {errors[index] && ( + <> + + {errors[index].title} + + + {errors[index].message} + + + )} + + {errors[index] && ( + +
+ {/* Arrows for navigating between errors */} +
+ +

+ {index + 1} / {errors.length} +

+ +
+
+ {/* Emergency Stop Button */} + + {/* Acknowledge button */} + { + errors[index].acknowledge(); + setIndex((i) => (i > 0 ? i - 1 : 0)); + }} + > + Acknowledge + +
+
+
+ )} +
+
+ ); +}; diff --git a/telemetry/packages/ui/app/components/shared/latency-chart.tsx b/telemetry/packages/ui/app/components/shared/latency-chart.tsx index c0efc460..e6b65fff 100644 --- a/telemetry/packages/ui/app/components/shared/latency-chart.tsx +++ b/telemetry/packages/ui/app/components/shared/latency-chart.tsx @@ -1,4 +1,4 @@ -import { PreviousLatenciesType } from '@/context/pods'; +import type { PreviousLatenciesType } from '@/context/pods'; import { LineChart } from '@tremor/react'; /** @@ -9,37 +9,37 @@ import { LineChart } from '@tremor/react'; * @returns A LineChart component. */ export const LatencyChart = ({ - data, - minValue = 0, - maxValue = 100, + data, + minValue = 0, + maxValue = 100, }: { - data: PreviousLatenciesType | undefined; - minValue?: number; - maxValue?: number; + data: PreviousLatenciesType | undefined; + minValue?: number; + maxValue?: number; }) => { - return ( -
- {data && data.length > 0 ? ( - - ) : ( -

No latencies to show

- )} -
- ); + return ( +
+ {data && data.length > 0 ? ( + + ) : ( +

No latencies to show

+ )} +
+ ); }; const dataFormatter = (number: number) => `${number.toString()}ms`; diff --git a/telemetry/packages/ui/app/components/shared/logo.tsx b/telemetry/packages/ui/app/components/shared/logo.tsx index c04825ed..b419266f 100644 --- a/telemetry/packages/ui/app/components/shared/logo.tsx +++ b/telemetry/packages/ui/app/components/shared/logo.tsx @@ -1,4 +1,4 @@ -import { SVGProps } from 'react'; +import type { SVGProps } from 'react'; /** * The HYPED logo as an SVG. @@ -6,25 +6,26 @@ import { SVGProps } from 'react'; * @returns An SVG component. */ export const Logo = (props: SVGProps) => ( - - - - - + + HYPED Logo + + + + ); diff --git a/telemetry/packages/ui/app/components/shared/pod-state.tsx b/telemetry/packages/ui/app/components/shared/pod-state.tsx index 33f66d55..ead75b36 100644 --- a/telemetry/packages/ui/app/components/shared/pod-state.tsx +++ b/telemetry/packages/ui/app/components/shared/pod-state.tsx @@ -1,29 +1,29 @@ +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; import { usePod } from '@/context/pods'; import { cn } from '@/lib/utils'; import { - ACTIVE_STATES, - FAILURE_STATES, - NULL_STATES, - PASSIVE_STATES, - PodStateCategoryType, + ACTIVE_STATES, + FAILURE_STATES, + NULL_STATES, + PASSIVE_STATES, + type PodStateCategoryType, } from '@hyped/telemetry-constants'; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from '@/components/ui/card'; import { CircleDashed } from 'lucide-react'; /** * Defines the styling for each pod state. */ export const styles: Record = { - ACTIVE: 'bg-green-700 border-2 border-green-900 text-white', - FAILURE: 'bg-red-700 border-2 border-red-900 text-white', - PASSIVE: 'bg-gray-600 border-2 border-gray-800 text-white', - NULL: '', + ACTIVE: 'bg-green-700 border-2 border-green-900 text-white', + FAILURE: 'bg-red-700 border-2 border-red-900 text-white', + PASSIVE: 'bg-gray-600 border-2 border-gray-800 text-white', + NULL: '', }; /** @@ -32,29 +32,29 @@ export const styles: Record = { * @returns A Card component displaying the pod state. */ export const PodState = ({ podId }: { podId: string }) => { - const { podState: state, name } = usePod(podId); + const { podState: state, name } = usePod(podId); - return ( - - - - Pod State - - The current state of {name} - - -

- {state} -

-
-
- ); + return ( + + + + Pod State + + The current state of {name} + + +

+ {state} +

+
+
+ ); }; diff --git a/telemetry/packages/ui/app/components/shared/set-levitation-height.tsx b/telemetry/packages/ui/app/components/shared/set-levitation-height.tsx index 84bdde8b..b5684d0f 100644 --- a/telemetry/packages/ui/app/components/shared/set-levitation-height.tsx +++ b/telemetry/packages/ui/app/components/shared/set-levitation-height.tsx @@ -1,8 +1,8 @@ +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; import { log } from '@/lib/logger'; import { http } from 'openmct/core/http'; import { useState } from 'react'; -import { Input } from '@/components/ui/input'; -import { Button } from '@/components/ui/button'; /** * Sets the levitation height of a pod. @@ -10,35 +10,35 @@ import { Button } from '@/components/ui/button'; * @returns A component to set the levitation height of a pod. */ export const SetLevitationHeight = ({ podId }: { podId: string }) => { - const [height, setHeight] = useState(null); + const [height, setHeight] = useState(null); - const setLevigationHeight = async () => { - // Don't do anything if the height is invalid - if (!height || height < 0) return; - log(`Setting the levitation height of ${podId} to ${height}mm`); - const url = `pods/${podId}/controls/levitation-height?height=${height}`; - await http.post(url); - // Clear the input - setHeight(null); - }; + const setLevigationHeight = async () => { + // Don't do anything if the height is invalid + if (!height || height < 0) return; + log(`Setting the levitation height of ${podId} to ${height}mm`); + const url = `pods/${podId}/controls/levitation-height?height=${height}`; + await http.post(url); + // Clear the input + setHeight(null); + }; - return ( -
-

Set levitation height (mm):

-
- setHeight(Number(e.target.value))} - /> - -
-
- ); + return ( +
+

Set levitation height (mm):

+
+ setHeight(Number(e.target.value))} + /> + +
+
+ ); }; diff --git a/telemetry/packages/ui/app/components/sidebar/index.tsx b/telemetry/packages/ui/app/components/sidebar/index.tsx index 0c634f28..339e3783 100644 --- a/telemetry/packages/ui/app/components/sidebar/index.tsx +++ b/telemetry/packages/ui/app/components/sidebar/index.tsx @@ -1,14 +1,14 @@ -import { VIEWS, VIEW_KEYS, ViewOption } from '@/views'; +import { Logo } from '@/components/shared/logo'; +import { useCurrentPod } from '@/context/pods'; import { log } from '@/lib/logger'; import { cn } from '@/lib/utils'; +import { VIEWS, VIEW_KEYS, type ViewOption } from '@/views'; import { POD_IDS } from '@hyped/telemetry-constants'; import { useEffect } from 'react'; import toast from 'react-hot-toast'; -import { useCurrentPod } from '@/context/pods'; import { Latency } from './latency'; -import { PodControls } from './pod-controls'; import { PodConnectionStatus } from './pod-connection-status'; -import { Logo } from '@/components/shared/logo'; +import { PodControls } from './pod-controls'; import { PodSelector } from './pod-selector'; /** @@ -16,66 +16,67 @@ import { PodSelector } from './pod-selector'; * AKA the "Controls UI" */ export const Sidebar = ({ - currentView, - setCurrentView, + currentView, + setCurrentView, }: { - currentView: ViewOption; - setCurrentView: React.Dispatch>; + currentView: ViewOption; + setCurrentView: React.Dispatch>; }) => { - const { - currentPod, - pod: { podState }, - } = useCurrentPod(); + const { + currentPod, + pod: { podState }, + } = useCurrentPod(); - // Display notification when the pod state changes - useEffect( - function notifyPodStateChanges() { - toast(`Pod state changed: ${podState}`); - log(`Pod state changed: ${podState}`, currentPod); - }, - [podState, currentPod], - ); + // Display notification when the pod state changes + useEffect( + function notifyPodStateChanges() { + toast(`Pod state changed: ${podState}`); + log(`Pod state changed: ${podState}`, currentPod); + }, + [podState, currentPod], + ); - return ( -
-
- - {/* Status, Latency, State, Title */} -
-

Connection to pod

- - -
-
-

Controls

- {POD_IDS.map((podId) => ( - - ))} -
-
-

View

-
- {VIEW_KEYS.map((key) => ( - - ))} -
-
-
- -
- ); + return ( +
+
+ + {/* Status, Latency, State, Title */} +
+

Connection to pod

+ + +
+
+

Controls

+ {POD_IDS.map((podId) => ( + + ))} +
+
+

View

+
+ {VIEW_KEYS.map((key) => ( + + ))} +
+
+
+ +
+ ); }; diff --git a/telemetry/packages/ui/app/components/sidebar/latency.tsx b/telemetry/packages/ui/app/components/sidebar/latency.tsx index a69add76..bb146e45 100644 --- a/telemetry/packages/ui/app/components/sidebar/latency.tsx +++ b/telemetry/packages/ui/app/components/sidebar/latency.tsx @@ -1,51 +1,51 @@ -import { usePod } from '@/context/pods'; -import { POD_CONNECTION_STATUS } from '@/types/PodConnectionStatus'; import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, } from '@/components/ui/tooltip'; -import { LatencyChart } from '../shared/latency-chart'; +import { usePod } from '@/context/pods'; import { cn } from '@/lib/utils'; +import { POD_CONNECTION_STATUS } from '@/types/PodConnectionStatus'; +import { LatencyChart } from '../shared/latency-chart'; /** * Displays the latency between the base station (GUI) and the pod. */ export const Latency = ({ podId }: { podId: string }) => { - const { latency, connectionStatus, previousLatencies } = usePod(podId); + const { latency, connectionStatus, previousLatencies } = usePod(podId); - return ( - - - - {connectionStatus === POD_CONNECTION_STATUS.CONNECTED && latency ? ( -

- Latency: - 50 && 'text-orange-500', - latency > 100 && 'text-red-500', - )} - > - {latency} ms - -

- ) : ( -

- Latency: N/A -

- )} -
- - - -
-
- ); + return ( + + + + {connectionStatus === POD_CONNECTION_STATUS.CONNECTED && latency ? ( +

+ Latency: + 50 && 'text-orange-500', + latency > 100 && 'text-red-500', + )} + > + {latency} ms + +

+ ) : ( +

+ Latency: N/A +

+ )} +
+ + + +
+
+ ); }; diff --git a/telemetry/packages/ui/app/components/sidebar/pod-connection-status.tsx b/telemetry/packages/ui/app/components/sidebar/pod-connection-status.tsx index 2ac56f83..4131f384 100644 --- a/telemetry/packages/ui/app/components/sidebar/pod-connection-status.tsx +++ b/telemetry/packages/ui/app/components/sidebar/pod-connection-status.tsx @@ -1,87 +1,87 @@ +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@/components/ui/tooltip'; import { usePod } from '@/context/pods'; import { cn } from '@/lib/utils'; import { POD_CONNECTION_STATUS } from '@/types/PodConnectionStatus'; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from '@/components/ui/tooltip'; -import { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; /** * Displays the connection status of a pod to the base station (GUI). * @param podId The ID of the pod */ export const PodConnectionStatus = ({ podId }: { podId: string }) => { - const { connectionStatus, connectionEstablished } = usePod(podId); + const { connectionStatus, connectionEstablished } = usePod(podId); - const [uptime, setUptime] = useState(0); + const [uptime, setUptime] = useState(0); - // Update the uptime every second - useEffect(() => { - if (connectionStatus !== POD_CONNECTION_STATUS.CONNECTED) { - setUptime(0); - return; - } - const interval = setInterval(() => { - setUptime((uptime) => uptime + 1); - }, 1000); - return () => clearInterval(interval); - }, [connectionStatus]); + // Update the uptime every second + useEffect(() => { + if (connectionStatus !== POD_CONNECTION_STATUS.CONNECTED) { + setUptime(0); + return; + } + const interval = setInterval(() => { + setUptime((uptime) => uptime + 1); + }, 1000); + return () => clearInterval(interval); + }, [connectionStatus]); - const CONNECTED = connectionStatus === POD_CONNECTION_STATUS.CONNECTED; - const UNKNOWN = connectionStatus === POD_CONNECTION_STATUS.UNKNOWN; - const DISCONNECTED = connectionStatus === POD_CONNECTION_STATUS.DISCONNECTED; - const ERROR = connectionStatus === POD_CONNECTION_STATUS.ERROR; + const CONNECTED = connectionStatus === POD_CONNECTION_STATUS.CONNECTED; + const UNKNOWN = connectionStatus === POD_CONNECTION_STATUS.UNKNOWN; + const DISCONNECTED = connectionStatus === POD_CONNECTION_STATUS.DISCONNECTED; + const ERROR = connectionStatus === POD_CONNECTION_STATUS.ERROR; - return ( - - - -
-
-

- {connectionStatus} -

-
- - - {/* */} -

- Status: {connectionStatus} -

-

- GUI connection uptime:{' '} - {/* uptime in s then x mins, y secs etc. */} - {Math.floor(uptime / 60)} mins {uptime % 60} secs -

-

- GUI connection established:{' '} - {/* connection time in mm:hh:ss */} - {connectionEstablished - ? connectionEstablished.toLocaleTimeString() - : 'N/A'} -

-
- - - ); + return ( + + + +
+
+

+ {connectionStatus} +

+
+ + + {/* */} +

+ Status: {connectionStatus} +

+

+ GUI connection uptime:{' '} + {/* uptime in s then x mins, y secs etc. */} + {Math.floor(uptime / 60)} mins {uptime % 60} secs +

+

+ GUI connection established:{' '} + {/* connection time in mm:hh:ss */} + {connectionEstablished + ? connectionEstablished.toLocaleTimeString() + : 'N/A'} +

+
+ + + ); }; diff --git a/telemetry/packages/ui/app/components/sidebar/pod-controls.tsx b/telemetry/packages/ui/app/components/sidebar/pod-controls.tsx index ef5f100c..43cda996 100644 --- a/telemetry/packages/ui/app/components/sidebar/pod-controls.tsx +++ b/telemetry/packages/ui/app/components/sidebar/pod-controls.tsx @@ -1,11 +1,11 @@ import { Button } from '@/components/ui/button'; import { CONTROLS, sendControlMessage } from '@/lib/controls'; import { cn } from '@/lib/utils'; +import { Label } from '@radix-ui/react-label'; import { ArrowUpFromLine, Rocket, Siren } from 'lucide-react'; -import { SetLevitationHeight } from '../shared/set-levitation-height'; import { useState } from 'react'; +import { SetLevitationHeight } from '../shared/set-levitation-height'; import { Switch } from '../ui/switch'; -import { Label } from '@radix-ui/react-label'; /** * Displays the pod controls. @@ -14,79 +14,79 @@ import { Label } from '@radix-ui/react-label'; * @returns The pod controls. */ export const PodControls = ({ - podId, - show, + podId, + show, }: { - podId: string; - show: boolean; + podId: string; + show: boolean; }) => { - return ( -
-
-
- - -
- - -
-
- ); + return ( +
+
+
+ + +
+ + +
+
+ ); }; const LevitateButton = ({ podId }: { podId: string }) => ( - + ); const LaunchButton = ({ podId }: { podId: string }) => { - const [enabled, setEnabled] = useState(false); + const [enabled, setEnabled] = useState(false); - return ( -
- {/* This switch is used to enable the launch button */} -
- - -
- -
- ); + return ( +
+ {/* This switch is used to enable the launch button */} +
+ + +
+ +
+ ); }; export const EmergencyStopButton = ({ - podId, - className, + podId, + className, }: { - podId: string; - className?: string; + podId: string; + className?: string; }) => ( - + ); diff --git a/telemetry/packages/ui/app/components/sidebar/pod-selector.tsx b/telemetry/packages/ui/app/components/sidebar/pod-selector.tsx index b49b9ee1..be869fb8 100644 --- a/telemetry/packages/ui/app/components/sidebar/pod-selector.tsx +++ b/telemetry/packages/ui/app/components/sidebar/pod-selector.tsx @@ -1,50 +1,50 @@ -import { POD_IDS, PodId, pods } from '@hyped/telemetry-constants'; +import { useCurrentPod } from '@/context/pods'; +import { POD_IDS, type PodId, pods } from '@hyped/telemetry-constants'; import { Label } from '../ui/label'; import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, } from '../ui/select'; -import { useCurrentPod } from '@/context/pods'; /** * A pod selector component which allows us to select a pod to view/control. */ export const PodSelector = () => { - const { currentPod, setCurrentPod } = useCurrentPod(); + const { currentPod, setCurrentPod } = useCurrentPod(); - return ( -
- - -
- ); + return ( +
+ + +
+ ); }; /** * Returns the pod options for the pod selector by getting the display text for each pod in the `pods.ts` file. */ const PodOptions = () => { - // Get the display text for each pod in the `pods.ts` file - const podOptions = POD_IDS.map((podId) => getDisplayText(podId)); + // Get the display text for each pod in the `pods.ts` file + const podOptions = POD_IDS.map((podId) => getDisplayText(podId)); - return podOptions.map((podOption) => ( - - {podOption} - - )); + return podOptions.map((podOption) => ( + + {podOption} + + )); }; /** @@ -56,6 +56,6 @@ const getDisplayText = (podId: PodId) => `${pods[podId].name} (${podId})`; * Opposite of `getDisplayText()`. Gets the pod ID from the display text. */ const getPodIdFromDisplayText = (displayText: string) => { - const podId = displayText.split('(')[1].split(')')[0]; - return podId as PodId; + const podId = displayText.split('(')[1].split(')')[0]; + return podId as PodId; }; diff --git a/telemetry/packages/ui/app/components/ui/alert-dialog.tsx b/telemetry/packages/ui/app/components/ui/alert-dialog.tsx index cd7595ea..3a16f1ee 100644 --- a/telemetry/packages/ui/app/components/ui/alert-dialog.tsx +++ b/telemetry/packages/ui/app/components/ui/alert-dialog.tsx @@ -1,145 +1,145 @@ // @ts-nocheck -import * as React from 'react'; import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; +import * as React from 'react'; -import { cn } from '@/lib/utils'; import { buttonVariants } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; const AlertDialog = AlertDialogPrimitive.Root; const AlertDialogTrigger = AlertDialogPrimitive.Trigger; const AlertDialogPortal = ({ - className, - ...props + className, + ...props }: AlertDialogPrimitive.AlertDialogPortalProps) => ( - + ); AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName; const AlertDialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - + )); AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; const AlertDialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - - - - + + + + )); AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; const AlertDialogHeader = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes) => ( -
+
); AlertDialogHeader.displayName = 'AlertDialogHeader'; const AlertDialogFooter = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes) => ( -
+
); AlertDialogFooter.displayName = 'AlertDialogFooter'; const AlertDialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; const AlertDialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AlertDialogDescription.displayName = - AlertDialogPrimitive.Description.displayName; + AlertDialogPrimitive.Description.displayName; const AlertDialogAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; const AlertDialogCancel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; export { - AlertDialog, - AlertDialogTrigger, - AlertDialogContent, - AlertDialogHeader, - AlertDialogFooter, - AlertDialogTitle, - AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, + AlertDialog, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, }; diff --git a/telemetry/packages/ui/app/components/ui/button.tsx b/telemetry/packages/ui/app/components/ui/button.tsx index 0709f7f8..5d54c70b 100644 --- a/telemetry/packages/ui/app/components/ui/button.tsx +++ b/telemetry/packages/ui/app/components/ui/button.tsx @@ -1,56 +1,56 @@ -import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; -import { cva, type VariantProps } from 'class-variance-authority'; +import { type VariantProps, cva } from 'class-variance-authority'; +import * as React from 'react'; import { cn } from '@/lib/utils'; const buttonVariants = cva( - 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background', - { - variants: { - variant: { - default: 'bg-primary text-primary-foreground hover:bg-primary/90', - destructive: - 'bg-destructive text-destructive-foreground hover:bg-destructive/90', - outline: - 'border border-input hover:bg-accent hover:text-accent-foreground', - secondary: - 'bg-secondary text-secondary-foreground hover:bg-secondary/80', - ghost: 'hover:bg-accent hover:text-accent-foreground', - link: 'underline-offset-4 hover:underline text-primary', - }, - size: { - default: 'h-10 py-2 px-4', - sm: 'h-9 px-3 rounded-md', - lg: 'h-11 px-8 rounded-md', - }, - }, - defaultVariants: { - variant: 'default', - size: 'default', - }, - }, + 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: + 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + outline: + 'border border-input hover:bg-accent hover:text-accent-foreground', + secondary: + 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'underline-offset-4 hover:underline text-primary', + }, + size: { + default: 'h-10 py-2 px-4', + sm: 'h-9 px-3 rounded-md', + lg: 'h-11 px-8 rounded-md', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, ); export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; } const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : 'button'; - return ( - // @ts-ignore - - ); - }, + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : 'button'; + return ( + // @ts-ignore + + ); + }, ); Button.displayName = 'Button'; diff --git a/telemetry/packages/ui/app/components/ui/card.tsx b/telemetry/packages/ui/app/components/ui/card.tsx index 3028066c..23b110e4 100644 --- a/telemetry/packages/ui/app/components/ui/card.tsx +++ b/telemetry/packages/ui/app/components/ui/card.tsx @@ -3,84 +3,84 @@ import * as React from 'react'; import { cn } from '@/lib/utils'; const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)); Card.displayName = 'Card'; const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)); CardHeader.displayName = 'CardHeader'; const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -

+

)); CardTitle.displayName = 'CardTitle'; const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -

+

)); CardDescription.displayName = 'CardDescription'; const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -

+
)); CardContent.displayName = 'CardContent'; const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)); CardFooter.displayName = 'CardFooter'; export { - Card, - CardHeader, - CardFooter, - CardTitle, - CardDescription, - CardContent, + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, }; diff --git a/telemetry/packages/ui/app/components/ui/carousel.tsx b/telemetry/packages/ui/app/components/ui/carousel.tsx index bf3358d7..5695401d 100644 --- a/telemetry/packages/ui/app/components/ui/carousel.tsx +++ b/telemetry/packages/ui/app/components/ui/carousel.tsx @@ -1,11 +1,11 @@ -import * as React from 'react'; import useEmblaCarousel, { - type UseEmblaCarouselType, + type UseEmblaCarouselType, } from 'embla-carousel-react'; import { ArrowLeft, ArrowRight } from 'lucide-react'; +import * as React from 'react'; -import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; type CarouselApi = UseEmblaCarouselType[1]; type UseCarouselParameters = Parameters; @@ -13,250 +13,249 @@ type CarouselOptions = UseCarouselParameters[0]; type CarouselPlugin = UseCarouselParameters[1]; type CarouselProps = { - opts?: CarouselOptions; - plugins?: CarouselPlugin; - orientation?: 'horizontal' | 'vertical'; - setApi?: (api: CarouselApi) => void; + opts?: CarouselOptions; + plugins?: CarouselPlugin; + orientation?: 'horizontal' | 'vertical'; + setApi?: (api: CarouselApi) => void; }; type CarouselContextProps = { - carouselRef: ReturnType[0]; - api: ReturnType[1]; - scrollPrev: () => void; - scrollNext: () => void; - canScrollPrev: boolean; - canScrollNext: boolean; + carouselRef: ReturnType[0]; + api: ReturnType[1]; + scrollPrev: () => void; + scrollNext: () => void; + canScrollPrev: boolean; + canScrollNext: boolean; } & CarouselProps; const CarouselContext = React.createContext(null); function useCarousel() { - const context = React.useContext(CarouselContext); + const context = React.useContext(CarouselContext); - if (!context) { - throw new Error('useCarousel must be used within a '); - } + if (!context) { + throw new Error('useCarousel must be used within a '); + } - return context; + return context; } const Carousel = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & CarouselProps + HTMLDivElement, + React.HTMLAttributes & CarouselProps >( - ( - { - orientation = 'horizontal', - opts, - setApi, - plugins, - className, - children, - ...props - }, - ref, - ) => { - const [carouselRef, api] = useEmblaCarousel( - { - ...opts, - axis: orientation === 'horizontal' ? 'x' : 'y', - }, - plugins, - ); - const [canScrollPrev, setCanScrollPrev] = React.useState(false); - const [canScrollNext, setCanScrollNext] = React.useState(false); + ( + { + orientation = 'horizontal', + opts, + setApi, + plugins, + className, + children, + ...props + }, + ref, + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === 'horizontal' ? 'x' : 'y', + }, + plugins, + ); + const [canScrollPrev, setCanScrollPrev] = React.useState(false); + const [canScrollNext, setCanScrollNext] = React.useState(false); - const onSelect = React.useCallback((api: CarouselApi) => { - if (!api) { - return; - } + const onSelect = React.useCallback((api: CarouselApi) => { + if (!api) { + return; + } - setCanScrollPrev(api.canScrollPrev()); - setCanScrollNext(api.canScrollNext()); - }, []); + setCanScrollPrev(api.canScrollPrev()); + setCanScrollNext(api.canScrollNext()); + }, []); - const scrollPrev = React.useCallback(() => { - api?.scrollPrev(); - }, [api]); + const scrollPrev = React.useCallback(() => { + api?.scrollPrev(); + }, [api]); - const scrollNext = React.useCallback(() => { - api?.scrollNext(); - }, [api]); + const scrollNext = React.useCallback(() => { + api?.scrollNext(); + }, [api]); - const handleKeyDown = React.useCallback( - (event: React.KeyboardEvent) => { - if (event.key === 'ArrowLeft') { - event.preventDefault(); - scrollPrev(); - } else if (event.key === 'ArrowRight') { - event.preventDefault(); - scrollNext(); - } - }, - [scrollPrev, scrollNext], - ); + const handleKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (event.key === 'ArrowLeft') { + event.preventDefault(); + scrollPrev(); + } else if (event.key === 'ArrowRight') { + event.preventDefault(); + scrollNext(); + } + }, + [scrollPrev, scrollNext], + ); - React.useEffect(() => { - if (!api || !setApi) { - return; - } + React.useEffect(() => { + if (!api || !setApi) { + return; + } - setApi(api); - }, [api, setApi]); + setApi(api); + }, [api, setApi]); - React.useEffect(() => { - if (!api) { - return; - } + React.useEffect(() => { + if (!api) { + return; + } - onSelect(api); - api.on('reInit', onSelect); - api.on('select', onSelect); + onSelect(api); + api.on('reInit', onSelect); + api.on('select', onSelect); - return () => { - api?.off('select', onSelect); - }; - }, [api, onSelect]); + return () => { + api?.off('select', onSelect); + }; + }, [api, onSelect]); - return ( - -
- {children} -
-
- ); - }, + return ( + +
+ {children} +
+
+ ); + }, ); Carousel.displayName = 'Carousel'; const CarouselContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => { - const { carouselRef, orientation } = useCarousel(); + const { carouselRef, orientation } = useCarousel(); - return ( -
-
-
- ); + return ( +
+
+
+ ); }); CarouselContent.displayName = 'CarouselContent'; const CarouselItem = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => { - const { orientation } = useCarousel(); + const { orientation } = useCarousel(); - return ( -
- ); + return ( +
+ ); }); CarouselItem.displayName = 'CarouselItem'; const CarouselPrevious = React.forwardRef< - HTMLButtonElement, - React.ComponentProps + HTMLButtonElement, + React.ComponentProps >(({ className, variant = 'outline', size = 'icon', ...props }, ref) => { - const { orientation, scrollPrev, canScrollPrev } = useCarousel(); + const { orientation, scrollPrev, canScrollPrev } = useCarousel(); - return ( - - ); + return ( + + ); }); CarouselPrevious.displayName = 'CarouselPrevious'; const CarouselNext = React.forwardRef< - HTMLButtonElement, - React.ComponentProps + HTMLButtonElement, + React.ComponentProps >(({ className, variant = 'outline', size = 'icon', ...props }, ref) => { - const { orientation, scrollNext, canScrollNext } = useCarousel(); + const { orientation, scrollNext, canScrollNext } = useCarousel(); - return ( - - ); + return ( + + ); }); CarouselNext.displayName = 'CarouselNext'; export { - type CarouselApi, - Carousel, - CarouselContent, - CarouselItem, - CarouselPrevious, - CarouselNext, + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, }; diff --git a/telemetry/packages/ui/app/components/ui/dialog.tsx b/telemetry/packages/ui/app/components/ui/dialog.tsx index 0da44968..80febabb 100644 --- a/telemetry/packages/ui/app/components/ui/dialog.tsx +++ b/telemetry/packages/ui/app/components/ui/dialog.tsx @@ -1,8 +1,8 @@ // @ts-nocheck -import * as React from 'react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { X } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; @@ -11,113 +11,113 @@ const Dialog = DialogPrimitive.Root; const DialogTrigger = DialogPrimitive.Trigger; const DialogPortal = ({ - className, - ...props + className, + ...props }: DialogPrimitive.DialogPortalProps) => ( - + ); DialogPortal.displayName = DialogPrimitive.Portal.displayName; const DialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - - - {children} - - - Close - - - + + + + {children} + + + Close + + + )); DialogContent.displayName = DialogPrimitive.Content.displayName; const DialogHeader = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes) => ( -
+
); DialogHeader.displayName = 'DialogHeader'; const DialogFooter = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes) => ( -
+
); DialogFooter.displayName = 'DialogFooter'; const DialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DialogTitle.displayName = DialogPrimitive.Title.displayName; const DialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DialogDescription.displayName = DialogPrimitive.Description.displayName; export { - Dialog, - DialogTrigger, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, - DialogDescription, + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, }; diff --git a/telemetry/packages/ui/app/components/ui/dropdown-menu.tsx b/telemetry/packages/ui/app/components/ui/dropdown-menu.tsx index 82ef8b3a..531e22b6 100644 --- a/telemetry/packages/ui/app/components/ui/dropdown-menu.tsx +++ b/telemetry/packages/ui/app/components/ui/dropdown-menu.tsx @@ -1,8 +1,8 @@ // @ts-nocheck -import * as React from 'react'; import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; import { Check, ChevronRight, Circle } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; @@ -19,182 +19,182 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub; const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, children, ...props }, ref) => ( - - {children} - - + + {children} + + )); DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName; + DropdownMenuPrimitive.SubTrigger.displayName; const DropdownMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName; + DropdownMenuPrimitive.SubContent.displayName; const DropdownMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, sideOffset = 4, ...props }, ref) => ( - - - + + + )); DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; const DropdownMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - + )); DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, checked, ...props }, ref) => ( - - - - - - - {children} - + + + + + + + {children} + )); DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName; + DropdownMenuPrimitive.CheckboxItem.displayName; const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - - - - - - {children} - + + + + + + + {children} + )); DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; const DropdownMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - + )); DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; const DropdownMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; const DropdownMenuShortcut = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes) => { - return ( - - ); + return ( + + ); }; DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'; export { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuRadioGroup, + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, }; diff --git a/telemetry/packages/ui/app/components/ui/form.tsx b/telemetry/packages/ui/app/components/ui/form.tsx index 47fe3aed..61cb700a 100644 --- a/telemetry/packages/ui/app/components/ui/form.tsx +++ b/telemetry/packages/ui/app/components/ui/form.tsx @@ -1,179 +1,179 @@ -import * as React from 'react'; -import * as LabelPrimitive from '@radix-ui/react-label'; +import type * as LabelPrimitive from '@radix-ui/react-label'; import { Slot } from '@radix-ui/react-slot'; +import * as React from 'react'; import { - Controller, - ControllerProps, - FieldPath, - FieldValues, - FormProvider, - useFormContext, + Controller, + type ControllerProps, + type FieldPath, + type FieldValues, + FormProvider, + useFormContext, } from 'react-hook-form'; -import { cn } from '@/lib/utils'; import { Label } from '@/components/ui/label'; +import { cn } from '@/lib/utils'; const Form = FormProvider; type FormFieldContextValue< - TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, > = { - name: TName; + name: TName; }; const FormFieldContext = React.createContext( - {} as FormFieldContextValue, + {} as FormFieldContextValue, ); const FormField = < - TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, >({ - ...props + ...props }: ControllerProps) => { - return ( - - - - ); + return ( + + + + ); }; const useFormField = () => { - const fieldContext = React.useContext(FormFieldContext); - const itemContext = React.useContext(FormItemContext); - const { getFieldState, formState } = useFormContext(); - - const fieldState = getFieldState(fieldContext.name, formState); - - if (!fieldContext) { - throw new Error('useFormField should be used within '); - } - - const { id } = itemContext; - - return { - id, - name: fieldContext.name, - formItemId: `${id}-form-item`, - formDescriptionId: `${id}-form-item-description`, - formMessageId: `${id}-form-item-message`, - ...fieldState, - }; + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error('useFormField should be used within '); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; }; type FormItemContextValue = { - id: string; + id: string; }; const FormItemContext = React.createContext( - {} as FormItemContextValue, + {} as FormItemContextValue, ); const FormItem = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes + HTMLDivElement, + React.HTMLAttributes >(({ className, ...props }, ref) => { - const id = React.useId(); + const id = React.useId(); - return ( - -
- - ); + return ( + +
+ + ); }); FormItem.displayName = 'FormItem'; const FormLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { error, formItemId } = useFormField(); - - return ( -