diff --git a/client/package.json b/client/package.json index 9c5ea84..bee0177 100644 --- a/client/package.json +++ b/client/package.json @@ -45,5 +45,5 @@ "last 1 safari version" ] }, - "proxy": "http://localhost:5000" + "proxy": "http://localhost:5001" } diff --git a/client/src/api/orientation.js b/client/src/api/orientation.js new file mode 100644 index 0000000..b40edb6 --- /dev/null +++ b/client/src/api/orientation.js @@ -0,0 +1,51 @@ +// API functions for the Experiment tab + +import { fetchData } from './api'; +import { dateToRFC } from './utils'; + +const NUMBER_OF_VARIANTS = 2; + +const queryMagFieldData = async (start, end) => { + let endpoint = `adcs/magf` + + const params = new URLSearchParams({start: dateToRFC(start), end: dateToRFC(end)}) + + const requests = Array.from({ length: NUMBER_OF_VARIANTS }, (_, i) => + fetchData(`${endpoint}/${i + 1}?${params.toString()}`), + ); + + const responses = await Promise.all(requests); + + return responses.map((magfieldData) => + magfieldData.map(item => [item.variant, item.timestamp, item.LSB, item.X, item.Y, item.Z]) + ); +} + +const queryAngFieldData = async (start, end) => { + let endpoint = `adcs/angv` + + const params = new URLSearchParams({start: dateToRFC(start), end: dateToRFC(end)}) + + const requests = Array.from({ length: NUMBER_OF_VARIANTS }, (_, i) => + fetchData(`${endpoint}/${i + 1}?${params.toString()}`), + ); + + const responses = await Promise.all(requests); + + return responses.map((angFieldData) => + angFieldData.map(item => [item.variant, item.timestamp, item.X, item.Y, item.Z]) + ); +} + + +export const fetchMagneticField = async (start, end) => { + + const magnetic = await queryMagFieldData(start, end); + return { magnetic }; +}; + +export const fetchAngVelData = async (start, end) => { + + const angvel = await queryAngFieldData(start, end); + return { angvel }; +}; diff --git a/client/src/pages/OrientationTab.js b/client/src/pages/OrientationTab.js index c52053e..3f68e81 100644 --- a/client/src/pages/OrientationTab.js +++ b/client/src/pages/OrientationTab.js @@ -1,9 +1,228 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { fetchMagneticField, fetchMagField } from '../api/orientation.js'; +import { fetchAngVelData } from '../api/orientation.js'; +import Box from '@mui/material/Box'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Switch from '@mui/material/Switch'; +import TimeseriesChart from '../components/TimeseriesChart.jsx'; +import { useTimeContext } from "../components/TimeRangeContext.jsx"; +import { testTemperatureData, testLuminosityData, testWellActivity } from '../api/testData.js'; -class OrientationTab extends React.Component { - render() { - return null; - } +const CHART_WIDTH = 800; + +const chartSize = { + width: CHART_WIDTH, + height: 400 } -export default OrientationTab; \ No newline at end of file +const formatTime = (isoString) => { + return new Intl.DateTimeFormat("en-CA", { + year: "numeric", + month: "short", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + timeZone: "UTC", + }).format(new Date(isoString)); +}; + +export default function OrientationTab() { + const { timeRange } = useTimeContext(); + + // mag field data + const [magneticData, setMagneticData] = useState([]); + const [magVariants, setMagVariants] = useState([]); + const [magLSB, setMagLSB] = useState([]); + const [magTime, setMagTime] = useState([]); + const [magX, setMagX] = useState([]); + const [magY, setMagY] = useState([]); + const [magZ, setMagZ] = useState([]); + + // angular velocity data + const [angVelData, setAngVelData] = useState([]); + const [angVelVariants, setAngVelVariants] = useState([]); + const [angVelTime, setAngVelTime] = useState([]); + const [angVelX, setAngVelX] = useState([]); + const [angVelY, setAngVelY] = useState([]); + const [angVelZ, setAngVelZ] = useState([]); + + useEffect(() => { + const load = async () => { + const start = new Date('2025-09-01T00:00:00Z'); + const end = new Date('2025-09-10T00:00:00Z'); + + const initialmagvariants = []; + const initialmaglsb = []; + const initialmagtimes = []; + const initialmagx = []; + const initialmagy = []; + const initialmagz = []; + + const initialAngVelVariants = []; + const initialAngVelTimes = []; + const initialAngVelX = []; + const initialAngVelY = []; + const initialAngVelZ = []; + + const { magnetic } = await fetchMagneticField(start, end); + const { angvel } = await fetchAngVelData(start, end); + + // variant -> timestamp -> X -> Y -> Z + for (let i = 0; i < magnetic.length; i++) { + for (let j = 0; j < magnetic[i].length; j++) { + if (magnetic[i][j][0] === '1') { initialmagvariants.push("Primary"); } + else if (magnetic[i][j][0] === '2') { initialmagvariants.push("Backup"); } + initialmaglsb.push(magnetic[i][j][2]); + console.log(magnetic[i][j][2]); + initialmagtimes.push(magnetic[i][j][1]); + initialmagx.push(magnetic[i][j][3]); + initialmagy.push(magnetic[i][j][4]); + initialmagz.push(magnetic[i][j][5]); + } + } + + for (let i = 0; i < angvel.length; i++) { + for (let j = 0; j < angvel[i].length; j++) { + if (angvel[i][j][0] === '1') { initialAngVelVariants.push("Primary"); } + else if (angvel[i][j][0] === '2') { initialAngVelVariants.push("Backup"); } + initialAngVelTimes.push(angvel[i][j][1]); + initialAngVelX.push(angvel[i][j][2]); + initialAngVelY.push(angvel[i][j][3]); + initialAngVelZ.push(angvel[i][j][4]); + } + } + + setMagVariants(initialmagvariants); + setMagLSB(initialmaglsb); + setMagTime(initialmagtimes); + setMagX(initialmagx); + setMagY(initialmagy); + setMagZ(initialmagz); + + setAngVelVariants(initialAngVelVariants); + setAngVelTime(initialAngVelTimes); + setAngVelX(initialAngVelX); + setAngVelY(initialAngVelY); + setAngVelZ(initialAngVelZ); + + setMagneticData(magnetic); + setAngVelData(angvel); + } + load().catch(console.error); + }, []); + + // WORK IN PROGRESS + + return ( + + +
+
+ Magnetic Field Data (uT) +
+
+ Angular Velocity Data (deg/s) +
+
+
+
+

+ VARIANT: + { + magVariants.map((variant, index) => ( +

{variant}

+ )) + } +

+

+ LSB: + { + magLSB.map((lsb, index) => ( +

{String(lsb)}

+ )) + } +

+

+ Time: + { + magTime.map((time, index) => ( +

{formatTime(time)}

+ )) + } +

+

+ X: + { + magX.map((x, index) => ( +

{x}

+ )) + } +

+

+ Y: + { + magY.map((y, index) => ( +

{y}

+ )) + } +

+

+ Z: + { + magZ.map((z, index) => ( +

{z}

+ )) + } +

+
+
+
+

+ VARIANT: + { + angVelVariants.map((variant, index) => ( +

{variant}

+ )) + } +

+

+ Time: + { + angVelTime.map((time, index) => ( +

{formatTime(time)}

+ )) + } +

+

+ X: + { + angVelX.map((x, index) => ( +

{x}

+ )) + } +

+

+ Y: + { + angVelY.map((y, index) => ( +

{y}

+ )) + } +

+

+ Z: + { + angVelZ.map((z, index) => ( +

{z}

+ )) + } +

+
+
+
+
+
+
+ ); +} diff --git a/server/Dockerfile b/server/Dockerfile index 841527e..baa16ac 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -8,6 +8,6 @@ RUN npm install COPY . . -EXPOSE 5000 +EXPOSE 5001 CMD ["npm", "start"] \ No newline at end of file diff --git a/server/config/env.js b/server/config/env.js index 63ed10e..42d985b 100644 --- a/server/config/env.js +++ b/server/config/env.js @@ -9,7 +9,7 @@ const db_password = process.env.DB_PASS; const db_adminToken = process.env.DB_TOKEN; const db_bucket = process.env.DB_BUCKET; const db_org = process.env.DB_ORG; -const api_port = 5000; +const api_port = 5001; // port 5000 is used by macOS if(!db_url){ console.error('DB_URL is not set'); diff --git a/server/docker-compose.yaml b/server/docker-compose.yaml index f245b04..bb19bee 100644 --- a/server/docker-compose.yaml +++ b/server/docker-compose.yaml @@ -5,7 +5,7 @@ services: build: context: . ports: - - "5000:5000" + - "5001:5001" links: - influxdb depends_on: diff --git a/server/package-lock.json b/server/package-lock.json index 31f5ff2..6423158 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@influxdata/influxdb-client": "^1.35.0", "@influxdata/influxdb-client-apis": "^1.35.0", - "express": "^4.19.2" + "express": "^4.22.1" }, "devDependencies": { "@eslint/js": "^9.21.0", @@ -859,38 +859,39 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -916,6 +917,21 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/express/node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", diff --git a/server/package.json b/server/package.json index fd720cb..9c6b119 100644 --- a/server/package.json +++ b/server/package.json @@ -12,7 +12,7 @@ "dependencies": { "@influxdata/influxdb-client": "^1.35.0", "@influxdata/influxdb-client-apis": "^1.35.0", - "express": "^4.19.2" + "express": "^4.22.1" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/tools/telemetryInputCli/package-lock.json b/tools/telemetryInputCli/package-lock.json new file mode 100644 index 0000000..ec41b6c --- /dev/null +++ b/tools/telemetryInputCli/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "telemetryInputCli", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}