📦 Moves all model loading to model-utilities
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import { BufferGeometry, Line, LineBasicMaterial, Mesh, MeshBasicMaterial, Object3D, SphereGeometry, Vector3, type NormalBufferAttributes, type Object3DEventMap } from 'three';
|
import { BufferGeometry, Line, LineBasicMaterial, Mesh, MeshBasicMaterial, Object3D, SphereGeometry, Vector3, type NormalBufferAttributes, type Object3DEventMap } from 'three';
|
||||||
import uzip from 'uzip';
|
|
||||||
import { ModesEnum, kinematicData, mode, model, outControllerData, servoAnglesOut, servoAngles, mpu, jointNames } from '$lib/stores';
|
import { ModesEnum, kinematicData, mode, model, outControllerData, servoAnglesOut, servoAngles, mpu, jointNames } from '$lib/stores';
|
||||||
import { footColor, isEmbeddedApp, throttler, toeWorldPositions } from '$lib/utilities';
|
import { footColor, isEmbeddedApp, populateModelCache, throttler, toeWorldPositions } from '$lib/utilities';
|
||||||
import { fileService } from '$lib/services';
|
|
||||||
import SceneBuilder from '$lib/sceneBuilder';
|
import SceneBuilder from '$lib/sceneBuilder';
|
||||||
import { lerp, degToRad } from 'three/src/math/MathUtils';
|
import { lerp, degToRad } from 'three/src/math/MathUtils';
|
||||||
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
||||||
@@ -76,7 +74,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await cacheModelFiles()
|
await populateModelCache();
|
||||||
await createScene();
|
await createScene();
|
||||||
if (!isEmbeddedApp && panel) createPanel();
|
if (!isEmbeddedApp && panel) createPanel();
|
||||||
servoAngles.subscribe(updateAnglesFromStore)
|
servoAngles.subscribe(updateAnglesFromStore)
|
||||||
@@ -130,17 +128,6 @@
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheModelFiles = async () => {
|
|
||||||
let data = await fetch('/stl.zip').then((data) => data.arrayBuffer());
|
|
||||||
|
|
||||||
var files = uzip.parse(data);
|
|
||||||
|
|
||||||
for (const [path, data] of Object.entries(files) as [path: string, data: Uint8Array][]) {
|
|
||||||
const url = new URL(path, window.location.href);
|
|
||||||
fileService.saveFile(url.toString(), data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateAngles = (name: string, angle: number) => {
|
const updateAngles = (name: string, angle: number) => {
|
||||||
modelTargetAngles[$jointNames.indexOf(name)] = angle * (180 / Math.PI);
|
modelTargetAngles[$jointNames.indexOf(name)] = angle * (180 / Math.PI);
|
||||||
Throttler.throttle(() => servoAnglesOut.set(modelTargetAngles.map(num => Math.round(num))), 100)
|
Throttler.throttle(() => servoAnglesOut.set(modelTargetAngles.map(num => Math.round(num))), 100)
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ export * from './logging-store';
|
|||||||
export * from './model-store';
|
export * from './model-store';
|
||||||
export * from './socket';
|
export * from './socket';
|
||||||
export * from './fullscreen';
|
export * from './fullscreen';
|
||||||
|
export * from './telemetry';
|
||||||
|
export * from './analytics';
|
||||||
|
export * from './user';
|
||||||
|
|||||||
@@ -2,9 +2,35 @@ import { Color, LoaderUtils, Vector3 } from 'three';
|
|||||||
import URDFLoader, { type URDFRobot } from 'urdf-loader';
|
import URDFLoader, { type URDFRobot } from 'urdf-loader';
|
||||||
import { XacroLoader } from 'xacro-parser';
|
import { XacroLoader } from 'xacro-parser';
|
||||||
import { Result } from '$lib/utilities';
|
import { Result } from '$lib/utilities';
|
||||||
|
import { jointNames, model } from '$lib/stores';
|
||||||
|
import uzip from 'uzip';
|
||||||
|
import { fileService } from '$lib/services';
|
||||||
|
|
||||||
let model_xml: XMLDocument;
|
let model_xml: XMLDocument;
|
||||||
|
|
||||||
|
export const populateModelCache = async () => {
|
||||||
|
await cacheModelFiles();
|
||||||
|
const modelRes = await loadModelAsync('/spot_micro.urdf.xacro');
|
||||||
|
if (modelRes.isOk()) {
|
||||||
|
const [urdf, JOINT_NAME] = modelRes.inner;
|
||||||
|
jointNames.set(JOINT_NAME);
|
||||||
|
model.set(urdf);
|
||||||
|
} else {
|
||||||
|
console.error(modelRes.inner, { exception: modelRes.exception });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const cacheModelFiles = async () => {
|
||||||
|
let data = await fetch('/stl.zip');
|
||||||
|
|
||||||
|
var files = uzip.parse(await data.arrayBuffer());
|
||||||
|
|
||||||
|
for (const [path, data] of Object.entries(files) as [path: string, data: Uint8Array][]) {
|
||||||
|
const url = new URL(path, window.location.href);
|
||||||
|
fileService.saveFile(url.toString(), data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const loadModelAsync = async (
|
export const loadModelAsync = async (
|
||||||
url: string
|
url: string
|
||||||
): Promise<Result<[URDFRobot, string[]], string>> => {
|
): Promise<Result<[URDFRobot, string[]], string>> => {
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import { user } from '$lib/stores/user';
|
|
||||||
import { telemetry } from '$lib/stores/telemetry';
|
|
||||||
import { analytics } from '$lib/stores/analytics';
|
|
||||||
import type { userProfile } from '$lib/stores/user';
|
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { Modals, closeModal } from 'svelte-modals';
|
import { Modals, closeModal } from 'svelte-modals';
|
||||||
import Toast from '$lib/components/toasts/Toast.svelte';
|
import Toast from '$lib/components/toasts/Toast.svelte';
|
||||||
@@ -13,7 +9,19 @@
|
|||||||
import Menu from './menu.svelte';
|
import Menu from './menu.svelte';
|
||||||
import Statusbar from './statusbar.svelte';
|
import Statusbar from './statusbar.svelte';
|
||||||
import Login from './login.svelte';
|
import Login from './login.svelte';
|
||||||
import { ModesEnum, kinematicData, mode, outControllerData, servoAngles, servoAnglesOut, socket } from '$lib/stores';
|
import {
|
||||||
|
telemetry,
|
||||||
|
analytics,
|
||||||
|
user,
|
||||||
|
type userProfile,
|
||||||
|
ModesEnum,
|
||||||
|
kinematicData,
|
||||||
|
mode,
|
||||||
|
outControllerData,
|
||||||
|
servoAngles,
|
||||||
|
servoAnglesOut,
|
||||||
|
socket
|
||||||
|
} from '$lib/stores';
|
||||||
import type { Analytics, Battery, DownloadOTA } from '$lib/types/models';
|
import type { Analytics, Battery, DownloadOTA } from '$lib/types/models';
|
||||||
import { api } from '$lib/api';
|
import { api } from '$lib/api';
|
||||||
|
|
||||||
@@ -21,35 +29,37 @@
|
|||||||
if ($user.bearer_token !== '') {
|
if ($user.bearer_token !== '') {
|
||||||
await validateUser($user);
|
await validateUser($user);
|
||||||
}
|
}
|
||||||
const ws_token = $page.data.features.security ? '?access_token=' + $user.bearer_token : '';
|
const ws_token = $page.data.features.security ? '?access_token=' + $user.bearer_token : '';
|
||||||
socket.init(`ws://${window.location.host}/ws/events${ws_token}`);
|
socket.init(`ws://${window.location.host}/ws/events${ws_token}`);
|
||||||
|
|
||||||
addEventListeners();
|
addEventListeners();
|
||||||
|
|
||||||
outControllerData.subscribe((data) => socket.sendEvent("input", {data}));
|
outControllerData.subscribe((data) => socket.sendEvent('input', { data }));
|
||||||
mode.subscribe((data) => socket.sendEvent("mode", {data}));
|
mode.subscribe((data) => socket.sendEvent('mode', { data }));
|
||||||
servoAnglesOut.subscribe((data) => socket.sendEvent("angles", {data}));
|
servoAnglesOut.subscribe((data) => socket.sendEvent('angles', { data }));
|
||||||
kinematicData.subscribe((data) => socket.sendEvent("position", {data}));
|
kinematicData.subscribe((data) => socket.sendEvent('position', { data }));
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
removeEventListeners();
|
removeEventListeners();
|
||||||
});
|
});
|
||||||
|
|
||||||
const addEventListeners = () => {
|
const addEventListeners = () => {
|
||||||
socket.on('open', handleOpen);
|
socket.on('open', handleOpen);
|
||||||
socket.on('close', handleClose);
|
socket.on('close', handleClose);
|
||||||
socket.on('error', handleError);
|
socket.on('error', handleError);
|
||||||
socket.on('rssi', handleNetworkStatus);
|
socket.on('rssi', handleNetworkStatus);
|
||||||
socket.on('mode', (data:ModesEnum) => mode.set(data));
|
socket.on('mode', (data: ModesEnum) => mode.set(data));
|
||||||
socket.on('angles', (angles:number[]) => { if (angles.length) servoAngles.set(angles)});
|
socket.on('angles', (angles: number[]) => {
|
||||||
|
if (angles.length) servoAngles.set(angles);
|
||||||
|
});
|
||||||
if ($page.data.features.analytics) socket.on('analytics', handleAnalytics);
|
if ($page.data.features.analytics) socket.on('analytics', handleAnalytics);
|
||||||
if ($page.data.features.battery) socket.on('battery', handleBattery);
|
if ($page.data.features.battery) socket.on('battery', handleBattery);
|
||||||
if ($page.data.features.download_firmware) socket.on('otastatus', handleOAT);
|
if ($page.data.features.download_firmware) socket.on('otastatus', handleOAT);
|
||||||
if ($page.data.features.sonar) socket.on('sonar', data => console.log(data))
|
if ($page.data.features.sonar) socket.on('sonar', (data) => console.log(data));
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeEventListeners = () => {
|
const removeEventListeners = () => {
|
||||||
socket.off('analytics', handleAnalytics);
|
socket.off('analytics', handleAnalytics);
|
||||||
socket.off('open', handleOpen);
|
socket.off('open', handleOpen);
|
||||||
socket.off('close', handleClose);
|
socket.off('close', handleClose);
|
||||||
@@ -58,12 +68,12 @@
|
|||||||
socket.off('otastatus', handleOAT);
|
socket.off('otastatus', handleOAT);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function validateUser(userdata: userProfile) {
|
async function validateUser(userdata: userProfile) {
|
||||||
const result = await api.get('/api/verifyAuthorization')
|
const result = await api.get('/api/verifyAuthorization');
|
||||||
if (result.isErr()){
|
if (result.isErr()) {
|
||||||
user.invalidate();
|
user.invalidate();
|
||||||
console.error('Error:', result.inner);
|
console.error('Error:', result.inner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOpen = () => {
|
const handleOpen = () => {
|
||||||
@@ -71,7 +81,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
notifications.error('Connection to device lost', 5000);
|
// notifications.error('Connection to device lost', 5000);
|
||||||
telemetry.setRSSI(0);
|
telemetry.setRSSI(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,7 +96,6 @@
|
|||||||
const handleOAT = (data: DownloadOTA) => telemetry.setDownloadOTA(data);
|
const handleOAT = (data: DownloadOTA) => telemetry.setDownloadOTA(data);
|
||||||
|
|
||||||
let menuOpen = false;
|
let menuOpen = false;
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -103,14 +112,12 @@
|
|||||||
<Statusbar />
|
<Statusbar />
|
||||||
|
|
||||||
<!-- Main page content here -->
|
<!-- Main page content here -->
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
<!-- Side Navigation -->
|
<!-- Side Navigation -->
|
||||||
<div class="drawer-side z-30 shadow-lg">
|
<div class="drawer-side z-30 shadow-lg">
|
||||||
<label for="main-menu" class="drawer-overlay" />
|
<label for="main-menu" class="drawer-overlay" />
|
||||||
<Menu
|
<Menu on:menuClicked={() => (menuOpen = false)} />
|
||||||
on:menuClicked={() => menuOpen = false}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import { jointNames, model } from '$lib/stores';
|
export const prerender = false;
|
||||||
import { loadModelAsync } from '$lib/utilities/model-utilities';
|
|
||||||
|
|
||||||
export const prerender = true;
|
|
||||||
export const ssr = false;
|
export const ssr = false;
|
||||||
|
|
||||||
const registerFetchIntercept = async () => {
|
const registerFetchIntercept = async () => {
|
||||||
@@ -14,20 +11,8 @@ const registerFetchIntercept = async () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadModelFiles = async () => {
|
export const load = async () => {
|
||||||
const modelRes = await loadModelAsync('/spot_micro.urdf.xacro');
|
|
||||||
if (modelRes.isOk()) {
|
|
||||||
const [urdf, JOINT_NAME] = modelRes.inner;
|
|
||||||
jointNames.set(JOINT_NAME);
|
|
||||||
model.set(urdf);
|
|
||||||
} else {
|
|
||||||
console.error(modelRes.inner, { exception: modelRes.exception });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const load = async ({ fetch }) => {
|
|
||||||
await registerFetchIntercept();
|
await registerFetchIntercept();
|
||||||
await loadModelFiles();
|
|
||||||
const result = await fetch('/api/features');
|
const result = await fetch('/api/features');
|
||||||
const features = await result.json();
|
const features = await result.json();
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user