From 1b7dbe282b4f30d6e55f7469c2072302dfb98ae2 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Feb 2024 17:58:42 +0100 Subject: [PATCH 01/33] =?UTF-8?q?=F0=9F=9A=A1=20Adds=20more=20stores=20for?= =?UTF-8?q?=20different=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/lib/socket.ts | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/app/src/lib/socket.ts b/app/src/lib/socket.ts index ca2692b..d331afb 100644 --- a/app/src/lib/socket.ts +++ b/app/src/lib/socket.ts @@ -4,6 +4,14 @@ export type WebSocketStatus = 'OPEN' | 'CONNECTING' | 'CLOSED' export const isConnected = writable(false) +export const angles = writable(new Int16Array(12).fill(0)) +export const log = writable([]) +export const battery = writable({}) +export const mpu = writable({heading:0}) +export const distances = writable({}) +export const settings = writable({}) +export const systemInfo = writable({}) + export const dataBuffer = writable(new Float32Array(13)) export const servoBuffer:Writable = writable(new Int16Array(12)) @@ -42,11 +50,32 @@ const _disconnected = () => { isConnected.set(false) } -const _message = (event) => { +const _message = (event:any) => { if (event.data instanceof ArrayBuffer) { let buffer = new Int8Array(event.data); if(buffer.length === 44) { dataBuffer.set(new Float32Array(buffer.buffer) ) } - } else dataBuffer.set(JSON.parse(event.data)); + } else { + let data = event.data + try { + data = JSON.parse(event.data) + } catch (error) { + console.warn(error) + } + switch (data.type) { + case "angles": + angles.set(data.angles) + break + case "mpu": + mpu.set(data.mpu) + break + case "distances": + distances.set(data.distances) + break + case "battery": + battery.set(data.battery) + break + } + } } \ No newline at end of file From 6d35e27991ad5388df30f7c77a0854102dd81383 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Feb 2024 17:59:07 +0100 Subject: [PATCH 02/33] =?UTF-8?q?=F0=9F=8E=BF=20Adds=20initial=20controlle?= =?UTF-8?q?r=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/lib/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/lib/store.ts b/app/src/lib/store.ts index 3136c3c..53bb155 100644 --- a/app/src/lib/store.ts +++ b/app/src/lib/store.ts @@ -6,4 +6,4 @@ export const emulateModel = writable(true); export const input = writable({left:{x:0, y:0}, right:{x:0, y:0}, height:70, speed:0}); -export const outControllerData = writable(new Uint8Array(6)); +export const outControllerData = writable(new Uint8Array([0, 128, 128, 128, 128, 70, 0])); From 5bb1a0a675b5892f5e0d70266641a0bc19e8d6db Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Feb 2024 18:00:00 +0100 Subject: [PATCH 03/33] =?UTF-8?q?=F0=9F=A6=9C=20Adds=20new=20topbar=20insp?= =?UTF-8?q?ired=20by=20real=20spot=20controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/components/Topbar.svelte | 152 +++++++++---------------------- 1 file changed, 45 insertions(+), 107 deletions(-) diff --git a/app/src/components/Topbar.svelte b/app/src/components/Topbar.svelte index c606468..5327598 100644 --- a/app/src/components/Topbar.svelte +++ b/app/src/components/Topbar.svelte @@ -1,117 +1,55 @@ -
-
-
- -
-
{Math.floor($dataBuffer[0])}°🌡️
- {Math.floor($dataBuffer[9]*10)/10}V
- {Math.floor($dataBuffer[8]*1000)/1000}A -
+ +
+
+ + + +
-
-
-
- {#if $width !== 0} -
- - - -
- {/if} -
-
- - - -
- - - - -
-
-
+ +
+ + + +
+
+
- + \ No newline at end of file From e6f3786ffd6e291c2abf10b9521609ca29a4e47d Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Feb 2024 18:01:21 +0100 Subject: [PATCH 04/33] =?UTF-8?q?=F0=9F=8F=83=E2=80=8D=E2=99=80=EF=B8=8F?= =?UTF-8?q?=20Adds=20mock=20spot=20server=20to=20develop=20of=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/.gitignore | 1 + mock/kinematic.js | 397 ++++++++++++++++++++++++++++++++++++ mock/package.json | 18 ++ mock/pnpm-lock.yaml | 483 ++++++++++++++++++++++++++++++++++++++++++++ mock/server.js | 316 +++++++++++++++++++++++++++++ 5 files changed, 1215 insertions(+) create mode 100644 mock/.gitignore create mode 100644 mock/kinematic.js create mode 100644 mock/package.json create mode 100644 mock/pnpm-lock.yaml create mode 100644 mock/server.js diff --git a/mock/.gitignore b/mock/.gitignore new file mode 100644 index 0000000..30bc162 --- /dev/null +++ b/mock/.gitignore @@ -0,0 +1 @@ +/node_modules \ No newline at end of file diff --git a/mock/kinematic.js b/mock/kinematic.js new file mode 100644 index 0000000..41de371 --- /dev/null +++ b/mock/kinematic.js @@ -0,0 +1,397 @@ +export default class Kinematic { + l1; + l2; + l3; + l4; + + L; + W; + + constructor() { + this.l1 = 50; + this.l2 = 20; + this.l3 = 120; + this.l4 = 155; + + this.L = 140; + this.W = 75; + } + + bodyIK(omega, phi, psi, xm, ym, zm) { + const { cos, sin } = Math; + + const Rx = [ + [1, 0, 0, 0], + [0, cos(omega), -sin(omega), 0], + [0, sin(omega), cos(omega), 0], + [0, 0, 0, 1], + ]; + const Ry = [ + [cos(phi), 0, sin(phi), 0], + [0, 1, 0, 0], + [-sin(phi), 0, cos(phi), 0], + [0, 0, 0, 1], + ]; + const Rz = [ + [cos(psi), -sin(psi), 0, 0], + [sin(psi), cos(psi), 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + ]; + const Rxyz = this.matrixMultiply(this.matrixMultiply(Rx, Ry), Rz); + + const T = [ + [0, 0, 0, xm], + [0, 0, 0, ym], + [0, 0, 0, zm], + [0, 0, 0, 0], + ]; + const Tm = this.matrixAdd(T, Rxyz); + + const sHp = sin(Math.PI / 2); + const cHp = cos(Math.PI / 2); + + return [ + this.matrixMultiply(Tm, [ + [cHp, 0, sHp, this.L / 2], + [0, 1, 0, 0], + [-sHp, 0, cHp, this.W / 2], + [0, 0, 0, 1], + ]), + this.matrixMultiply(Tm, [ + [cHp, 0, sHp, this.L / 2], + [0, 1, 0, 0], + [-sHp, 0, cHp, -this.W / 2], + [0, 0, 0, 1], + ]), + this.matrixMultiply(Tm, [ + [cHp, 0, sHp, -this.L / 2], + [0, 1, 0, 0], + [-sHp, 0, cHp, this.W / 2], + [0, 0, 0, 1], + ]), + this.matrixMultiply(Tm, [ + [cHp, 0, sHp, -this.L / 2], + [0, 1, 0, 0], + [-sHp, 0, cHp, -this.W / 2], + [0, 0, 0, 1], + ]), + ]; + } + + legIK(point) { + const [x, y, z] = point; + const { atan2, cos, sin, sqrt, acos } = Math; + + let F; + + try { + F = sqrt(x ** 2 + y ** 2 - this.l1 ** 2); + if (isNaN(F)) throw new Error("F is NaN"); + } catch (error) { + F = this.l1; + } + const G = F - this.l2; + const H = sqrt(G ** 2 + z ** 2); + + const theta1 = -atan2(y, x) - atan2(F, -this.l1); + let theta3; + try { + theta3 = acos( + (H ** 2 - this.l3 ** 2 - this.l4 ** 2) / (2 * this.l3 * this.l4) + ); + if (isNaN(theta3)) throw new Error("theta3 is NaN"); + } catch (error) { + theta3 = 0; + } + const theta2 = + atan2(z, G) - + atan2(this.l4 * sin(theta3), this.l3 + this.l4 * cos(theta3)); + + return [theta1, theta2, theta3]; + } + + matrixMultiply(a, b) { + const result = []; + + for (let i = 0; i < a.length; i++) { + const row = []; + + for (let j = 0; j < b[0].length; j++) { + let sum = 0; + + for (let k = 0; k < a[i].length; k++) { + sum += a[i][k] * b[k][j]; + } + + row.push(sum); + } + + result.push(row); + } + + return result; + } + + multiplyVector(matrix, vector) { + const rows = matrix.length; + const cols = matrix[0].length; + const vectorLength = vector.length; + + if (cols !== vectorLength) { + throw new Error( + "Matrix and vector dimensions do not match for multiplication." + ); + } + + const result = []; + + for (let i = 0; i < rows; i++) { + let sum = 0; + + for (let j = 0; j < cols; j++) { + sum += matrix[i][j] * vector[j]; + } + + result.push(sum); + } + + return result; + } + + matrixAdd(a, b) { + const result = []; + + for (let i = 0; i < a.length; i++) { + const row = []; + + for (let j = 0; j < a[i].length; j++) { + row.push(a[i][j] + b[i][j]); + } + + result.push(row); + } + + return result; + } + + calcLegPoints(angles) { + const [theta1, theta2, theta3] = angles; + const theta23 = theta2 + theta3; + + const T0 = [0, 0, 0, 1]; + const T1 = this.vectorAdd(T0, [ + -this.l1 * Math.cos(theta1), + this.l1 * Math.sin(theta1), + 0, + 0, + ]); + const T2 = this.vectorAdd(T1, [ + -this.l2 * Math.sin(theta1), + -this.l2 * Math.cos(theta1), + 0, + 0, + ]); + const T3 = this.vectorAdd(T2, [ + -this.l3 * Math.sin(theta1) * Math.cos(theta2), + -this.l3 * Math.cos(theta1) * Math.cos(theta2), + this.l3 * Math.sin(theta2), + 0, + ]); + const T4 = this.vectorAdd(T3, [ + -this.l4 * Math.sin(theta1) * Math.cos(theta23), + -this.l4 * Math.cos(theta1) * Math.cos(theta23), + this.l4 * Math.sin(theta23), + 0, + ]); + + return [T0, T1, T2, T3, T4]; + } + + calcIK(Lp, angles, center) { + const [omega, phi, psi] = angles; + const [xm, ym, zm] = center; + + const [Tlf, Trf, Tlb, Trb] = this.bodyIK(omega, phi, psi, xm, ym, zm); + + const Ix = [ + [-1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + ]; + + return [ + this.legIK(this.multiplyVector(this.matrixInverse(Tlf), Lp[0])), + this.legIK( + this.multiplyVector( + Ix, + this.multiplyVector(this.matrixInverse(Trf), Lp[1]) + ) + ), + this.legIK(this.multiplyVector(this.matrixInverse(Tlb), Lp[2])), + this.legIK( + this.multiplyVector( + Ix, + this.multiplyVector(this.matrixInverse(Trb), Lp[3]) + ) + ), + ]; + } + + vectorAdd(a, b) { + return a.map((val, index) => val + b[index]); + } + + matrixInverse(matrix) { + const det = this.determinant(matrix); + const adjugate = this.adjugate(matrix); + const scalar = 1 / det; + const inverse = []; + + for (let i = 0; i < matrix.length; i++) { + const row = []; + + for (let j = 0; j < matrix[i].length; j++) { + row.push(adjugate[i][j] * scalar); + } + + inverse.push(row); + } + + return inverse; + } + + determinant(matrix) { + if (matrix.length !== matrix[0].length) { + throw new Error("The matrix is not square."); + } + + if (matrix.length === 2) { + return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]; + } + + let det = 0; + + for (let i = 0; i < matrix.length; i++) { + const sign = i % 2 === 0 ? 1 : -1; + const subMatrix = []; + + for (let j = 1; j < matrix.length; j++) { + const row = []; + + for (let k = 0; k < matrix.length; k++) { + if (k !== i) { + row.push(matrix[j][k]); + } + } + + subMatrix.push(row); + } + + det += sign * matrix[0][i] * this.determinant(subMatrix); + } + + return det; + } + + adjugate(matrix) { + if (matrix.length !== matrix[0].length) { + throw new Error("The matrix is not square."); + } + + const adjugate = []; + + for (let i = 0; i < matrix.length; i++) { + const row = []; + + for (let j = 0; j < matrix[i].length; j++) { + const sign = (i + j) % 2 === 0 ? 1 : -1; + const subMatrix = []; + + for (let k = 0; k < matrix.length; k++) { + if (k !== i) { + const subRow = []; + + for (let l = 0; l < matrix.length; l++) { + if (l !== j) { + subRow.push(matrix[k][l]); + } + } + + subMatrix.push(subRow); + } + } + + const cofactor = sign * this.determinant(subMatrix); + row.push(cofactor); + } + + adjugate.push(row); + } + + return this.transpose(adjugate); + } + + transpose(matrix) { + const transposed = []; + + for (let i = 0; i < matrix.length; i++) { + const row = []; + + for (let j = 0; j < matrix[i].length; j++) { + row.push(matrix[j][i]); + } + + transposed.push(row); + } + + return transposed; + } +} + +class ForwardKinematics { + l1; + l2; + l3; + l4; + + constructor() { + this.l1 = 50; + this.l2 = 20; + this.l3 = 120; + this.l4 = 155; + } + + calculateFootpoint(theta1, theta2, theta3) { + const { cos, sin } = Math; + + const x = + this.l1 * cos(theta1) + + this.l2 * cos(theta1) + + this.l3 * cos(theta1 + theta2) + + this.l4 * cos(theta1 + theta2 + theta3); + const y = + this.l1 * sin(theta1) + + this.l2 * sin(theta1) + + this.l3 * sin(theta1 + theta2) + + this.l4 * sin(theta1 + theta2 + theta3); + const z = 0; + + return [x, y, z]; + } + + calculateFootpoints(angles) { + const footpoints = []; + + for (let i = 0; i < angles.length; i += 3) { + const theta1 = angles[i]; + const theta2 = angles[i + 1]; + const theta3 = angles[i + 2]; + const footpoint = this.calculateFootpoint(theta1, theta2, theta3); + footpoints.push(footpoint); + } + + return footpoints; + } +} diff --git a/mock/package.json b/mock/package.json new file mode 100644 index 0000000..ab6414c --- /dev/null +++ b/mock/package.json @@ -0,0 +1,18 @@ +{ + "name": "mock", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "cors": "^2.8.5", + "express": "^4.18.2", + "ws": "^8.16.0" + } +} diff --git a/mock/pnpm-lock.yaml b/mock/pnpm-lock.yaml new file mode 100644 index 0000000..30d71be --- /dev/null +++ b/mock/pnpm-lock.yaml @@ -0,0 +1,483 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + cors: + specifier: ^2.8.5 + version: 2.8.5 + express: + specifier: ^4.18.2 + version: 4.18.2 + ws: + specifier: ^8.16.0 + version: 8.16.0 + +packages: + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + + /array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + dev: false + + /body-parser@1.20.1: + resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: false + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.2.0 + dev: false + + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: false + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: false + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: false + + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: false + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: false + + /express@4.18.2: + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.5.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + dev: false + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + dev: false + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + dev: false + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: false + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: false + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: false + + /merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + dev: false + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: false + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: false + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: false + + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + + /path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + dev: false + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + + /qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + + /raw-body@2.5.1: + resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + dev: false + + /set-function-length@1.2.0: + resolution: {integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: false + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + dev: false + + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: false + + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + + /type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: false + + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: false + + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: false + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false diff --git a/mock/server.js b/mock/server.js new file mode 100644 index 0000000..44ada30 --- /dev/null +++ b/mock/server.js @@ -0,0 +1,316 @@ +import express from "express"; +import cors from "cors"; +import Kinematic from "./kinematic.js"; +import { WebSocketServer } from "ws"; + +const app = express(); +const kinematic = new Kinematic(); +const wss = new WebSocketServer({ port: 8080 }); + +app.use(cors()); +app.use(express.json()); + +const port = 3000; +const subscriptions = {}; + +const randomFloatFromInterval = (min, max) => + Math.floor((Math.random() * (max - min + 1) + min) * 100) / 100; + +const radToDeg = (val) => val * (180 / Math.PI); +const degToRad = (val) => val * (Math.PI / 180); + +function subscribeClientToCategory(ws, category) { + if (!subscriptions[category]) { + subscriptions[category] = new Set(); + } + subscriptions[category].add(ws); +} + +const unsubscribeClientFromCategory = (ws, category) => { + if (!subscriptions[category]) return; + subscriptions[category].delete(ws); + if (subscriptions[category].size === 0) { + delete subscriptions[category].size; + } +}; + +const sendUpdateToSubscribers = (category, data) => { + if (subscriptions[category]) { + const message = JSON.stringify(data); + for (const client of subscriptions[category]) { + client.send(message); + } + } +}; + +const model = { + battery: { + voltage: randomFloatFromInterval(7.6, 8.2), + ampere: randomFloatFromInterval(0.2, 3), + power_button: false, + }, + servos: { + angles: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + dir: [-1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1], + on: true, + }, + mpu: { + x: 0, + y: 0, + z: 0, + heading: 240, + temperature: 21, + }, + display: [[]], + distance_sensors: { + left: 22, + right: 23, + }, + appTime: 1123321, + connectivity: { + ssid: "best network", + ip: "192.168.0.118", + mDNS: "leika.local", + rssi: 100, + }, + running: true, +}; + +const settings = { + useMetric: true, + name: "Leika", + ssid: "Rune private network", + pass: "12345678", + ap: "Leika", + apPass: "12345678", + apChannel: 1, +}; + +const logs = [ + "Booting up", + "Starting webserver", + "Loading setting", + "Connected to Rune private network", +]; + +const system = { + HeapSize: 400000, + HeapFree: 0, + HeapMin: 0, + DmaHeapSize: 0, + DmaHeapFree: 0, + DmaHeapMin: 0, + PsramSize: 400000, + PsramFree: 0, + PsramMin: 0, + ChipModel: 0, + ChipRevision: 0, + ChipCores: 2, + CpuFreqMHz: 80, + SketchSize: 0, + FreeSketchSpace: 10200, + FlashChipSize: 0, + CpuUsed: 0, + CpuUsedCore0: 0, + CpuUsedCore1: 0, + arduinoVersion: "3.2.1", +}; + +const updateBattery = () => { + model.battery.voltage = randomFloatFromInterval(7.6, 8.2); + model.battery.ampere = randomFloatFromInterval(0.2, 3); + return model.battery; +}; + +const updateMpu = () => { + model.mpu.x = randomFloatFromInterval(0, 1); + model.mpu.y = randomFloatFromInterval(0, 1); + model.mpu.z = randomFloatFromInterval(0, 1); + model.mpu.temperature = randomFloatFromInterval(20, 22); + return model.mpu; +}; + +const updateDistances = () => { + model.distance_sensors = { + left: randomFloatFromInterval(10, 220), + right: randomFloatFromInterval(10, 220), + }; + return model.distance_sensors; +}; + +const updateDistance = (position) => { + model.distance_sensors[position] = randomFloatFromInterval(10, 220); + return model.distance_sensors[position]; +}; + +const updateSystem = () => { + system.CpuUsedCore0 = randomFloatFromInterval(0, 100); + system.CpuUsedCore1 = randomFloatFromInterval(0, 100); + system.CpuUsed = + Math.floor((system.CpuUsedCore0 + system.CpuUsedCore1) / 0.02) / 100; + system.HeapFree = randomFloatFromInterval(0, 20000); + system.HeapMin = randomFloatFromInterval(0, 20000); + return system; +}; + +const updateBodyState = (angles, position) => { + const Lp = [ + [100, -100, 100, 1], + [100, -100, -100, 1], + [-100, -100, 100, 1], + [-100, -100, -100, 1], + ]; + + model.servos.angles = kinematic + .calcIK( + Lp, + angles.map((x) => degToRad(x)), + position + ) + .flat() + .map((x, i) => radToDeg(x * model.servos.dir[i])); + return model.servos.angles; +}; + +const updateAngle = (id, angle) => { + model.servos.angles[id] = angle; + return model.servos.angles; +}; + +const updateAngles = (angles) => { + model.servos.angles = angles; + return model.servos.angles; +}; + +wss.on("connection", (ws) => { + ws.on("error", console.error); + + ws.on("message", (message) => { + let data = message; + try { + data = JSON.parse(message); + } catch (error) { + return; + } + switch (data.type) { + case "subscribe": + subscribeClientToCategory(ws, data.category); + break; + case "unsubscribe": + unsubscribeClientFromCategory(ws, data.category); + break; + case "sensor/battery": + ws.send({ type: "battery", battery: JSON.stringify(updateBattery()) }); + break; + case "sensor/mpu": + ws.send({ type: "battery", mpu: JSON.stringify(updateMpu()) }); + break; + case "sensor/distances": + ws.send(JSON.stringify(updateDistances())); + break; + case "sensor/distance": + ws.send(JSON.stringify({ distance: updateDistance(data.position) })); + break; + case "kinematic/angle": + if (data.angle && data.id) { + model.servos.angles[data.id] = data.angle; + ws.send( + JSON.stringify({ type: "angles", angles: model.servos.angles }) + ); + } else { + ws.send(JSON.stringify(updateAngle(data.id, data.angle))); + } + break; + case "kinematic/angles": + if (data.angles) { + model.servos.angles = data.angles; + ws.send( + JSON.stringify({ + type: "angles", + angles: model.servos.angles, + }) + ); + } else { + ws.send(JSON.stringify(updateAngles(data.angles))); + } + break; + case "kinematic/bodystate": + if (data.angles) { + ws.send( + JSON.stringify({ + type: "angles", + angles: updateBodyState(data.angles, data.position), + }) + ); + } else { + ws.send(JSON.stringify({ angles: model.servos.angles })); + } + break; + case "system/log": + ws.send(JSON.stringify(logs)); + break; + case "system/info": + ws.send(JSON.stringify(updateSystem())); + break; + case "system/settings": + if (data.settings) { + Object.entries(data.settings).forEach( + ([key, value]) => (settings[key] = value) + ); + ws.send(JSON.stringify(settings)); + } else { + ws.send(JSON.stringify(settings)); + } + break; + case "system/stop": + model.running = false; + ws.send(JSON.stringify(settings)); + break; + default: + ws.send(JSON.stringify({ error: "Unknown request type" })); + } + }); + + ws.on("close", () => { + for (const category in subscriptions) { + unsubscribeClientFromCategory(ws, category); + } + }); +}); + +app.get("/sensor/battery", (req, res) => res.send(updateBattery())); +app.get("/sensor/mpu", (req, res) => res.send(updateMpu())); +app.get("/sensor/distances", (req, res) => res.send(updateDistances())); +app.get("/sensor/distance/:position", (req, res) => + res.send({ distance: updateDistance(req.params.position) }) +); + +// ----------------------------------------------------------- // + +app.post("/kinematic/angle/:id", (req, res) => + res.send(updateAngle(req.params.id, req.body.angle)) +); +app.post("/kinematic/angles/", (req, res) => + res.send(updateAngles(req.body.angles)) +); +app.get("/kinematic/bodystate", (req, res) => res.send(model.servos.angles)); +app.post("/kinematic/bodystate", (req, res) => { + sendUpdateToSubscribers("angles", model.servos.angles); + res.send(updateBodyState(req.body.angles, req.body.position)); +}); + +// ----------------------------------------------------------- // + +app.get("/system/log", (req, res) => res.send(logs)); +app.get("/system/info", (req, res) => res.send(updateSystem())); +app.get("/system/settings", (req, res) => res.send(settings)); +app.post("/system/settings", (req, res) => { + Object.entries(req.body).forEach((x) => (settings[x[0]] = x[1])); + res.send(settings); +}); +app.post("/system/stop", (req, res) => { + model.running = false; + model.res.send(settings); +}); + +app.listen(port, () => console.log(`Open at http://localhost:${port}`)); From d81679309c12a46145ce2cfab1e9895913aa84b6 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Feb 2024 18:01:57 +0100 Subject: [PATCH 05/33] Adds initial API documentation --- robot/api.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 robot/api.md diff --git a/robot/api.md b/robot/api.md new file mode 100644 index 0000000..f89602d --- /dev/null +++ b/robot/api.md @@ -0,0 +1,25 @@ +# API + +https://dev.bostondynamics.com/docs/concepts/choreography/choreography_in_tablet.html + +| HTTP Method | Endpoint | Description | Parameters | +|-------------|----------------|----------------------------|---------------------------| +| GET | /api/sensor/battery | Retrieve the battery state | | +| GET | /api/sensor/mpu | Retrieve the mpu state | | +| GET | /api/sensor/magnetometer | Retrieve the magnetometer state | | +| GET | /api/sensor/distances | Retrieve the distances state | | +| GET | /api/sensor/distance/{position} | Retrieve the distance state | `position`: The position of the distance sensor **LEFT** and **RIGHT** | +| GET | /api/sensor/stream | Retrieve the camera stream | | +| GET | /api/actuator | Retrieve the actuator states | | +| GET | /api/actuator/{id} | Retrieve the actuator state for `id` | `id`: The ID of the actuator | +| POST | /api/actuator/{id} | Set the actuator state | `id`: The ID of the actuator| +| GET | /api/kinematics/feet | Retrieve the current feet positions as (x, y, z) coordinates| | +| GET | /api/kinematics/body | Retrieve the current body position as a (x, y, z) coordinates| | +| GET | /api/kinematics/bodystate | Retrieve the current body and feet positions | | +| GET | /api/system/log | Retrieve the system log | | +| GET | /api/system/info | Retrieve the system information | | +| GET | /api/system/settings | Retrieve the system settings | | +| POST | /api/system/settings | Set the system settings | | +| POST | /api/system/reset | Reset system | | +| POST | /api/system/power/off | Power of the system | | +| POST | /api/system/stop | Stop power to actuators | `id`: The stop level **CUT**, **SETTLE_THEN_CUT**, **NONE** | From 2a63ba8f5360571ec34030dc7a9da604307ae2d4 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Feb 2024 18:02:08 +0100 Subject: [PATCH 06/33] Adds a about the robot page --- robot/readme.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 robot/readme.md diff --git a/robot/readme.md b/robot/readme.md new file mode 100644 index 0000000..ce6f716 --- /dev/null +++ b/robot/readme.md @@ -0,0 +1,31 @@ +# ABOUT SPOT MICRO + +## Cameras + +## Hips and joints + +## Robot specifications + +### Dimensions + +### Environment + +| Specification | Value | +| --- | --- | +| Ingress protection | *IP42 | +| Operating temperature | 0C to 30C | + +### Power + +### Sensing + +| Specification | Value | +| --- | --- | +| Camera type | single | +| Field of view | 160 degrees | + +### Connectivity + +| Specification | Value | +| --- | --- | +| Wifi | 802.11 | From 0eb5473abd2fb7e1eb44ef32ce12358457257786 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Feb 2024 18:02:40 +0100 Subject: [PATCH 07/33] =?UTF-8?q?=F0=9F=A4=93=20Renames=20folder=20to=20do?= =?UTF-8?q?cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {robot => docs}/api.md | 0 {robot => docs}/readme.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {robot => docs}/api.md (100%) rename {robot => docs}/readme.md (100%) diff --git a/robot/api.md b/docs/api.md similarity index 100% rename from robot/api.md rename to docs/api.md diff --git a/robot/readme.md b/docs/readme.md similarity index 100% rename from robot/readme.md rename to docs/readme.md From 85092642d6b350c3e8afb065b41113f87dcca58d Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sat, 3 Feb 2024 18:04:57 +0100 Subject: [PATCH 08/33] =?UTF-8?q?=F0=9F=90=B2=20Simplifies=20modelview=20b?= =?UTF-8?q?y=20having=20mock=20server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/components/Model/ModelView.svelte | 70 ++++++----------------- 1 file changed, 18 insertions(+), 52 deletions(-) diff --git a/app/src/components/Model/ModelView.svelte b/app/src/components/Model/ModelView.svelte index 687c283..556c9d1 100644 --- a/app/src/components/Model/ModelView.svelte +++ b/app/src/components/Model/ModelView.svelte @@ -1,7 +1,7 @@ @@ -14,13 +18,13 @@ - {#each modes as mode} {/each} - {#each views as view} {/each} From 20a424198996ff25ee2ffb74894eaf615fe04747 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sun, 4 Feb 2024 00:55:08 +0100 Subject: [PATCH 13/33] =?UTF-8?q?=F0=9F=8C=90=20Adds=20new=20build=20for?= =?UTF-8?q?=20web=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/package.json | 4 +++- app/pnpm-lock.yaml | 11 +++++++++++ app/vite.config.ts | 7 +++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/package.json b/app/package.json index d0d3f9d..f554ad5 100644 --- a/app/package.json +++ b/app/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build", + "build": "cross-env FOR_EMBEDDED=true vite build", + "build:web": "cross-env FOR_EMBEDDED=false vite build", "preview": "vite preview", "check": "svelte-check --tsconfig ./tsconfig.json", "format": "prettier --plugin-search-dir . --write ." @@ -17,6 +18,7 @@ "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "autoprefixer": "^10.4.17", + "cross-env": "^7.0.3", "husky": "^9.0.7", "lint-staged": "^15.2.0", "postcss": "^8.4.33", diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index 133aa9d..9211806 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -43,6 +43,9 @@ devDependencies: autoprefixer: specifier: ^10.4.17 version: 10.4.17(postcss@8.4.33) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 husky: specifier: ^9.0.7 version: 9.0.7 @@ -989,6 +992,14 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} diff --git a/app/vite.config.ts b/app/vite.config.ts index 1599344..2b29ffc 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -3,11 +3,14 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'; import { viteSingleFile } from 'vite-plugin-singlefile'; import viteCompression from 'vite-plugin-compression'; +const forEmbedded = process.env.FOR_EMBEDDED == 'true' + // https://vitejs.dev/config/ export default defineConfig({ - plugins: [svelte(), viteSingleFile(), viteCompression({deleteOriginFile: true})], + plugins: [svelte(), viteSingleFile(), + ...(forEmbedded ? [ viteCompression({deleteOriginFile: true})]: [])], build: { - outDir: '../data', + outDir: forEmbedded ? '../data': './build', emptyOutDir: true } }); From b9a1497fdf67dbc2e00e80315156e0fe92f2cfd6 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sun, 4 Feb 2024 00:56:00 +0100 Subject: [PATCH 14/33] =?UTF-8?q?=F0=9F=8E=AE=20Makes=20each=20socket=20co?= =?UTF-8?q?nnection=20control=20different=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/server.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/mock/server.js b/mock/server.js index 44ada30..80d535f 100644 --- a/mock/server.js +++ b/mock/server.js @@ -19,6 +19,15 @@ const randomFloatFromInterval = (min, max) => const radToDeg = (val) => val * (180 / Math.PI); const degToRad = (val) => val * (Math.PI / 180); +function createNewClientState() { + return { + model: JSON.parse(JSON.stringify(model)), + settings: JSON.parse(JSON.stringify(settings)), + logs: [...logs], + subscriptions: {}, + }; +} + function subscribeClientToCategory(ws, category) { if (!subscriptions[category]) { subscriptions[category] = new Set(); @@ -183,6 +192,8 @@ const updateAngles = (angles) => { }; wss.on("connection", (ws) => { + const clientState = createNewClientState(); + ws.clientState = clientState; ws.on("error", console.error); ws.on("message", (message) => { @@ -213,9 +224,12 @@ wss.on("connection", (ws) => { break; case "kinematic/angle": if (data.angle && data.id) { - model.servos.angles[data.id] = data.angle; + ws.clientState.model.servos.angles[data.id] = data.angle; ws.send( - JSON.stringify({ type: "angles", angles: model.servos.angles }) + JSON.stringify({ + type: "angles", + angles: ws.clientState.model.servos.angles, + }) ); } else { ws.send(JSON.stringify(updateAngle(data.id, data.angle))); @@ -223,11 +237,11 @@ wss.on("connection", (ws) => { break; case "kinematic/angles": if (data.angles) { - model.servos.angles = data.angles; + ws.clientState.model.servos.angles = data.angles; ws.send( JSON.stringify({ type: "angles", - angles: model.servos.angles, + angles: ws.clientState.model.servos.angles, }) ); } else { From acdafaa3cfa5dff319512d23762d562c6c879d12 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sun, 4 Feb 2024 00:56:37 +0100 Subject: [PATCH 15/33] =?UTF-8?q?=F0=9F=8C=90=20Make=20server=20also=20hos?= =?UTF-8?q?t=20files,=20for=20when=20building=20for=20web?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/server.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mock/server.js b/mock/server.js index 80d535f..5591607 100644 --- a/mock/server.js +++ b/mock/server.js @@ -326,5 +326,8 @@ app.post("/system/stop", (req, res) => { model.running = false; model.res.send(settings); }); - +app.get("/", function (req, res) { + res.status(200).sendFile(`/`, { root: path }); +}); +app.get("*.*", express.static(path, { maxAge: "1y" })); app.listen(port, () => console.log(`Open at http://localhost:${port}`)); From b61548f04d6812a901f67fb074619aed3af0465f Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sun, 4 Feb 2024 22:11:22 +0100 Subject: [PATCH 16/33] =?UTF-8?q?=F0=9F=90=95=E2=80=8D=F0=9F=A6=BA=20Remov?= =?UTF-8?q?es=20singlefile=20plugin=20for=20embedded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/vite.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/vite.config.ts b/app/vite.config.ts index 2b29ffc..3e9328b 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -7,8 +7,8 @@ const forEmbedded = process.env.FOR_EMBEDDED == 'true' // https://vitejs.dev/config/ export default defineConfig({ - plugins: [svelte(), viteSingleFile(), - ...(forEmbedded ? [ viteCompression({deleteOriginFile: true})]: [])], + plugins: [svelte(), + ...(forEmbedded ? [ viteSingleFile(), viteCompression({deleteOriginFile: true})]: [])], build: { outDir: forEmbedded ? '../data': './build', emptyOutDir: true From e1f8856b1db2f56d57ea7839cbcac21f7b06eb75 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sun, 4 Feb 2024 22:11:43 +0100 Subject: [PATCH 17/33] =?UTF-8?q?=F0=9F=A6=A7=20Adds=20new=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/package.json b/app/package.json index f554ad5..b53ed0e 100644 --- a/app/package.json +++ b/app/package.json @@ -5,8 +5,9 @@ "type": "module", "scripts": { "dev": "vite", + "dev:mock": "vite --mode MOCK", "build": "cross-env FOR_EMBEDDED=true vite build", - "build:web": "cross-env FOR_EMBEDDED=false vite build", + "build:web": "cross-env FOR_EMBEDDED=false vite build --mode WEB", "preview": "vite preview", "check": "svelte-check --tsconfig ./tsconfig.json", "format": "prettier --plugin-search-dir . --write ." From 153696416517e3fcea5317cb8c0ca6485f1e896a Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sun, 4 Feb 2024 22:13:33 +0100 Subject: [PATCH 18/33] =?UTF-8?q?=F0=9F=A6=8B=20Adds=20location=20for=20so?= =?UTF-8?q?cket=20connection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/App.svelte | 10 +++++----- app/src/lib/location.ts | 7 ++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/App.svelte b/app/src/App.svelte index 0de1362..b598616 100644 --- a/app/src/App.svelte +++ b/app/src/App.svelte @@ -6,13 +6,13 @@ import Controller from './routes/Controller.svelte'; import Config from './routes/Config.svelte'; import Health from './routes/SystemHealth.svelte'; - import location from './lib/location'; import Sidebar from './components/Sidebar.svelte'; import FileCache from './lib/cache'; + import { socketLocation } from './lib/location'; - export let url = window.location.pathname; + export let url = window.location.pathname onMount(() => { - connect(`ws://${location}`); + connect(socketLocation); registerFetchIntercept() }); @@ -21,9 +21,9 @@ window.fetch = async (...args) => { const [resource, config] = args; await FileCache.openDatabase(); - let file; + let file: BodyInit | Uint8Array | undefined | null; try { - file = await FileCache.getFile(resource.url); + file = await FileCache.getFile(resource.toString()); } catch (error) { console.log(error); } diff --git a/app/src/lib/location.ts b/app/src/lib/location.ts index dfb142e..6c00c47 100644 --- a/app/src/lib/location.ts +++ b/app/src/lib/location.ts @@ -1,3 +1,8 @@ -const location = import.meta.env.DEV ? "leika.local" : window.location.host +const forWeb = import.meta.env.MODE === "WEB" +const mock = import.meta.env.MODE === "MOCK" + +const location = mock ? `${window.location.hostname}:2096` : "leika.local" + +export const socketLocation = forWeb ? `wss://${window.location.hostname}:2096` : `ws://${location}` export default location; \ No newline at end of file From 749d6d10ad56dd0f610b8f0b17a0ce26519c11d7 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sun, 4 Feb 2024 22:14:22 +0100 Subject: [PATCH 19/33] =?UTF-8?q?=F0=9F=90=A8=20Removes=20sidebar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/App.svelte | 4 +- app/src/components/Sidebar.svelte | 31 --------------- app/src/components/Topbar.svelte | 9 +++-- app/src/components/settings/Info.svelte | 3 ++ app/src/lib/socket.ts | 2 +- app/src/lib/store.ts | 2 - app/src/routes/Settings.svelte | 51 +++++++++++++++++++++++++ 7 files changed, 62 insertions(+), 40 deletions(-) delete mode 100644 app/src/components/Sidebar.svelte create mode 100644 app/src/components/settings/Info.svelte create mode 100644 app/src/routes/Settings.svelte diff --git a/app/src/App.svelte b/app/src/App.svelte index b598616..38c2ec3 100644 --- a/app/src/App.svelte +++ b/app/src/App.svelte @@ -6,9 +6,9 @@ import Controller from './routes/Controller.svelte'; import Config from './routes/Config.svelte'; import Health from './routes/SystemHealth.svelte'; - import Sidebar from './components/Sidebar.svelte'; import FileCache from './lib/cache'; import { socketLocation } from './lib/location'; + import Settings from './routes/Settings.svelte'; export let url = window.location.pathname onMount(() => { @@ -37,9 +37,9 @@ -
+
diff --git a/app/src/components/Sidebar.svelte b/app/src/components/Sidebar.svelte deleted file mode 100644 index e7d94f1..0000000 --- a/app/src/components/Sidebar.svelte +++ /dev/null @@ -1,31 +0,0 @@ - - -
- -
- - - -
-
diff --git a/app/src/components/Topbar.svelte b/app/src/components/Topbar.svelte index 66a8de1..770e911 100644 --- a/app/src/components/Topbar.svelte +++ b/app/src/components/Topbar.svelte @@ -1,7 +1,8 @@ + +
+ +
+ + {#each menu as link} + + {/each} + +
+
From 8286c5f3b5df007f56b6e74a9a04894dd0605423 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Mon, 5 Feb 2024 18:07:59 +0100 Subject: [PATCH 20/33] =?UTF-8?q?=F0=9F=92=80=20Removes=20files=20serving?= =?UTF-8?q?=20from=20mock=20server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/server.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mock/server.js b/mock/server.js index 5591607..8409daf 100644 --- a/mock/server.js +++ b/mock/server.js @@ -326,8 +326,4 @@ app.post("/system/stop", (req, res) => { model.running = false; model.res.send(settings); }); -app.get("/", function (req, res) { - res.status(200).sendFile(`/`, { root: path }); -}); -app.get("*.*", express.static(path, { maxAge: "1y" })); app.listen(port, () => console.log(`Open at http://localhost:${port}`)); From 9347bccf5f2c854d9f5241cf1042f4f063dbf02a Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Mon, 5 Feb 2024 18:13:17 +0100 Subject: [PATCH 21/33] =?UTF-8?q?=F0=9F=A6=89=20Makes=20use=20of=20unique?= =?UTF-8?q?=20model=20per=20socket=20connection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/server.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mock/server.js b/mock/server.js index 8409daf..26c30a8 100644 --- a/mock/server.js +++ b/mock/server.js @@ -162,7 +162,7 @@ const updateSystem = () => { return system; }; -const updateBodyState = (angles, position) => { +const updateBodyState = (model, angles, position) => { const Lp = [ [100, -100, 100, 1], [100, -100, -100, 1], @@ -253,7 +253,7 @@ wss.on("connection", (ws) => { ws.send( JSON.stringify({ type: "angles", - angles: updateBodyState(data.angles, data.position), + angles: updateBodyState(ws.clientState.model, data.angles, data.position), }) ); } else { @@ -310,7 +310,7 @@ app.post("/kinematic/angles/", (req, res) => app.get("/kinematic/bodystate", (req, res) => res.send(model.servos.angles)); app.post("/kinematic/bodystate", (req, res) => { sendUpdateToSubscribers("angles", model.servos.angles); - res.send(updateBodyState(req.body.angles, req.body.position)); + res.send(updateBodyState(model, req.body.angles, req.body.position)); }); // ----------------------------------------------------------- // From cdd3966dd5b5d7e6a611c01fed8ddd4cae5171bf Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Mon, 5 Feb 2024 21:02:03 +0100 Subject: [PATCH 22/33] =?UTF-8?q?=F0=9F=92=A9=20Makes=20topbar=20setting?= =?UTF-8?q?=20remove=20canvas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/components/Model/ModelView.svelte | 6 +++++- app/src/components/Topbar.svelte | 22 ++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/app/src/components/Model/ModelView.svelte b/app/src/components/Model/ModelView.svelte index 82706dd..c1d276a 100644 --- a/app/src/components/Model/ModelView.svelte +++ b/app/src/components/Model/ModelView.svelte @@ -1,5 +1,5 @@
-
- - - +
+ {#if settingOpen} + + + + {:else} + + + + {/if} + {#each servos as servo} + + {/each} + +
+ +{#if selectedServo !== null} +
+

Servo {formatServo(servos[selectedServo])} Calibration

+ + updateServoValue(selectedServo, 'minPWM', Number(event.target.value))} /> + + + updateServoValue(selectedServo, 'maxPWM', Number(event.target.value))} /> + + + updateServoValue(selectedServo, 'pwmFor180', Number(event.target.value))} /> +
+{/if} +
From 9fdb1de483de1dfbb6c87880c92660deff9a008a Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Tue, 6 Feb 2024 19:21:15 +0100 Subject: [PATCH 31/33] =?UTF-8?q?=F0=9F=90=85=20Adds=20alias=20for=20commo?= =?UTF-8?q?n=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/components/Controls.svelte | 6 +++--- app/src/components/Topbar.svelte | 4 ++-- .../{Model/ModelView.svelte => Views/Model.svelte} | 14 +++++++------- app/src/{ => components}/Views/Stream.svelte | 2 +- app/src/{components/Model => lib}/sceneBuilder.ts | 12 +++++++++--- app/src/routes/Config.svelte | 4 ++-- app/src/routes/Controller.svelte | 10 +++++----- app/tsconfig.json | 9 ++++++++- app/vite.config.ts | 11 ++++++++++- 9 files changed, 47 insertions(+), 25 deletions(-) rename app/src/components/{Model/ModelView.svelte => Views/Model.svelte} (95%) rename app/src/{ => components}/Views/Stream.svelte (90%) rename app/src/{components/Model => lib}/sceneBuilder.ts (96%) diff --git a/app/src/components/Controls.svelte b/app/src/components/Controls.svelte index 5932c3e..9ef3569 100644 --- a/app/src/components/Controls.svelte +++ b/app/src/components/Controls.svelte @@ -1,9 +1,9 @@
{#if $emulateModel} - + {:else} {/if} diff --git a/app/tsconfig.json b/app/tsconfig.json index 319ff21..2e1fbc5 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -14,7 +14,14 @@ */ "allowJs": true, "checkJs": true, - "isolatedModules": true + "isolatedModules": true, + "paths": { + "$lib/*": ["./src/lib/*"], + "$utils/*": ["./src/utils/*"], + "$components/*": ["./src/components/*"], + "$stores/*": ["./src/stores/*"] + } + }, "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/app/vite.config.ts b/app/vite.config.ts index 3e9328b..9c50df8 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from 'vite'; import { svelte } from '@sveltejs/vite-plugin-svelte'; import { viteSingleFile } from 'vite-plugin-singlefile'; import viteCompression from 'vite-plugin-compression'; +import path from 'path' const forEmbedded = process.env.FOR_EMBEDDED == 'true' @@ -12,5 +13,13 @@ export default defineConfig({ build: { outDir: forEmbedded ? '../data': './build', emptyOutDir: true - } + }, + resolve: { + alias: { + '$lib': path.resolve('./src/lib/'), + '$components': path.resolve('./src/components'), + '$utils': path.resolve('./src/utils'), + '$stores': path.resolve('./src/stores'), + }, + }, }); From a2bb5b5312af66d9ac9f0675248add47955f34cc Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Tue, 6 Feb 2024 19:57:26 +0100 Subject: [PATCH 32/33] =?UTF-8?q?=F0=9F=90=A8=20Removes=20unused=20compone?= =?UTF-8?q?nts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/App.svelte | 4 --- app/src/components/Card.svelte | 3 -- app/src/components/CardHeader.svelte | 1 - app/src/components/Input.svelte | 10 ------ app/src/components/index.ts | 2 -- app/src/routes/Config.svelte | 50 ---------------------------- app/src/routes/SystemHealth.svelte | 34 ------------------- 7 files changed, 104 deletions(-) delete mode 100644 app/src/components/Card.svelte delete mode 100644 app/src/components/CardHeader.svelte delete mode 100644 app/src/components/Input.svelte delete mode 100644 app/src/components/index.ts delete mode 100644 app/src/routes/Config.svelte delete mode 100644 app/src/routes/SystemHealth.svelte diff --git a/app/src/App.svelte b/app/src/App.svelte index ef8ca4d..448f0fe 100644 --- a/app/src/App.svelte +++ b/app/src/App.svelte @@ -4,8 +4,6 @@ import TopBar from './components/TopBar.svelte'; import { connect } from './lib/socket'; import Controller from './routes/Controller.svelte'; - import Config from './routes/Config.svelte'; - import Health from './routes/SystemHealth.svelte'; import FileCache from './lib/cache'; import { socketLocation } from './lib/location'; import Settings from './routes/Settings.svelte'; @@ -45,7 +43,5 @@
- -
diff --git a/app/src/components/Card.svelte b/app/src/components/Card.svelte deleted file mode 100644 index 7d45283..0000000 --- a/app/src/components/Card.svelte +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/app/src/components/CardHeader.svelte b/app/src/components/CardHeader.svelte deleted file mode 100644 index 5e06e43..0000000 --- a/app/src/components/CardHeader.svelte +++ /dev/null @@ -1 +0,0 @@ -

\ No newline at end of file diff --git a/app/src/components/Input.svelte b/app/src/components/Input.svelte deleted file mode 100644 index 8120123..0000000 --- a/app/src/components/Input.svelte +++ /dev/null @@ -1,10 +0,0 @@ - -
- - -
\ No newline at end of file diff --git a/app/src/components/index.ts b/app/src/components/index.ts deleted file mode 100644 index 9cc745d..0000000 --- a/app/src/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as Card } from "./Card.svelte"; -export { default as CardHeader } from "./CardHeader.svelte"; \ No newline at end of file diff --git a/app/src/routes/Config.svelte b/app/src/routes/Config.svelte deleted file mode 100644 index 4f52ecb..0000000 --- a/app/src/routes/Config.svelte +++ /dev/null @@ -1,50 +0,0 @@ - - -
- - Servo calibration -
Servo
- -
- -
- - - -
-
-
-
Min pwm
- -
Pwm at Halfway from min
-
Max pwm
-
Overall angle
-
Buffer
{buffer}
-
Conversion rate
{conversionRate}
-
pwm for 0° mark
{zeroMark}
-
pwm for 180° Mark
{oneEightyMark}
-
-
-
diff --git a/app/src/routes/SystemHealth.svelte b/app/src/routes/SystemHealth.svelte deleted file mode 100644 index 9855815..0000000 --- a/app/src/routes/SystemHealth.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - -
-
- Heap allocation: -
Total free:{humanFileSize($dataBuffer[8])}
-
Max free block:{humanFileSize($dataBuffer[12])}
-
Min:{humanFileSize($dataBuffer[10])}
-
- -
- PSRam allocation: -
Free{humanFileSize($dataBuffer[9])}
-
Min:{humanFileSize($dataBuffer[11])}
-
Max block:{humanFileSize($dataBuffer[13])}
-
-
From 02d3eac032ca76364b3d4e1a32993590eaa4fd1e Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Thu, 8 Feb 2024 13:51:35 +0100 Subject: [PATCH 33/33] =?UTF-8?q?=F0=9F=8C=B4=20Removes=20more=20unused=20?= =?UTF-8?q?code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/components/Views/Model.svelte | 41 --------------------------- 1 file changed, 41 deletions(-) diff --git a/app/src/components/Views/Model.svelte b/app/src/components/Views/Model.svelte index 7d0496e..bc7ca07 100644 --- a/app/src/components/Views/Model.svelte +++ b/app/src/components/Views/Model.svelte @@ -134,47 +134,6 @@ const render = () => { -
- -
- {#if showStream}