📏 Formats app code
This commit is contained in:
@@ -25,12 +25,18 @@
|
||||
});
|
||||
|
||||
left.on('move', (evt, data) => {
|
||||
input.update(o => {o.left = data.vector; return o;})
|
||||
input.update((o) => {
|
||||
o.left = data.vector;
|
||||
return o;
|
||||
});
|
||||
throttle.throttle(updateData, throttle_timing);
|
||||
});
|
||||
|
||||
left.on('end', (evt, data) => {
|
||||
input.update(o => {o.left = { x: 0, y: 0 }; return o;})
|
||||
input.update((o) => {
|
||||
o.left = { x: 0, y: 0 };
|
||||
return o;
|
||||
});
|
||||
throttle.throttle(updateData, throttle_timing);
|
||||
});
|
||||
|
||||
@@ -43,12 +49,18 @@
|
||||
});
|
||||
|
||||
right.on('move', (evt, data) => {
|
||||
input.update(o => {o.right = data.vector; return o;})
|
||||
input.update((o) => {
|
||||
o.right = data.vector;
|
||||
return o;
|
||||
});
|
||||
throttle.throttle(updateData, throttle_timing);
|
||||
});
|
||||
|
||||
right.on('end', (evt, data) => {
|
||||
input.update(o => {o.right = { x: 0, y: 0 }; return o;})
|
||||
input.update((o) => {
|
||||
o.right = { x: 0, y: 0 };
|
||||
return o;
|
||||
});
|
||||
throttle.throttle(updateData, throttle_timing);
|
||||
});
|
||||
});
|
||||
@@ -62,11 +74,10 @@
|
||||
data[5] = $input.height;
|
||||
data[6] = $input.speed;
|
||||
|
||||
outControllerData.set(data)
|
||||
outControllerData.set(data);
|
||||
|
||||
if(!$emulateModel) socketService.send(data);
|
||||
if (!$emulateModel) socketService.send(data);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<div class="absolute top-0 left-0 w-screen h-screen">
|
||||
|
||||
@@ -2,76 +2,83 @@
|
||||
import socketService from '$lib/services/socket-service';
|
||||
import { Icon, Bars3, XMark, Power, Battery100, Signal, SignalSlash } from 'svelte-hero-icons';
|
||||
import { emulateModel } from '$lib/store';
|
||||
import { Link, useLocation } from 'svelte-routing'
|
||||
import { Link, useLocation } from 'svelte-routing';
|
||||
|
||||
const views = ["Virtual environment", "Robot camera"]
|
||||
const modes = ["Drive", "Choreography"]
|
||||
const views = ['Virtual environment', 'Robot camera'];
|
||||
const modes = ['Drive', 'Choreography'];
|
||||
|
||||
const location = useLocation()
|
||||
const location = useLocation();
|
||||
|
||||
let selected_view = views[0];
|
||||
let selected_modes = modes[0];
|
||||
let settingOpen = window.location.pathname.includes('/settings')
|
||||
let isConnected = socketService.isConnected
|
||||
let settingOpen = window.location.pathname.includes('/settings');
|
||||
let isConnected = socketService.isConnected;
|
||||
|
||||
$: emulateModel.set(selected_view === views[0])
|
||||
$: settingOpen = $location.pathname.includes('/settings')
|
||||
$: emulateModel.set(selected_view === views[0]);
|
||||
$: settingOpen = $location.pathname.includes('/settings');
|
||||
|
||||
const stop = () => {
|
||||
if ($isConnected) {
|
||||
socketService.send(JSON.stringify({type:"system/stop"}))
|
||||
}
|
||||
}
|
||||
const stop = () => {
|
||||
if ($isConnected) {
|
||||
socketService.send(JSON.stringify({ type: 'system/stop' }));
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<div class="topbar absolute left-0 top-0 w-full z-10 flex justify-between bg-zinc-800">
|
||||
<div class="flex gap-2 p-2">
|
||||
{#if settingOpen}
|
||||
<Link to="/">
|
||||
<Icon src={XMark} size="32" />
|
||||
</Link>
|
||||
{:else}
|
||||
<Link to="/settings">
|
||||
<Icon src={Bars3} size="32" />
|
||||
</Link>
|
||||
{/if}
|
||||
<select bind:value={selected_modes} class="rounded-md outline outline-2 text-zinc-200 outline-zinc-600 bg-zinc-800">
|
||||
{#each modes as mode}
|
||||
<option>{mode}</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
||||
<select bind:value={selected_view} class="rounded-md outline outline-2 text-zinc-200 outline-zinc-600 bg-zinc-800">
|
||||
{#each views as view}
|
||||
<option>{view}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-2 p-2">
|
||||
{#if settingOpen}
|
||||
<Link to="/">
|
||||
<Icon src={XMark} size="32" />
|
||||
</Link>
|
||||
{:else}
|
||||
<Link to="/settings">
|
||||
<Icon src={Bars3} size="32" />
|
||||
</Link>
|
||||
{/if}
|
||||
<select
|
||||
bind:value={selected_modes}
|
||||
class="rounded-md outline outline-2 text-zinc-200 outline-zinc-600 bg-zinc-800"
|
||||
>
|
||||
{#each modes as mode}
|
||||
<option>{mode}</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
||||
<div class="flex gap-2 p-2">
|
||||
<button class="action_button bg-zinc-600">
|
||||
<Icon src={Power} size="24" />
|
||||
</button>
|
||||
<button class="action_button"><Icon src={Battery100} size="24" /></button>
|
||||
<button class="action_button"><Icon src={$isConnected ? Signal : SignalSlash} size="24" /></button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="h-full w-20 bg-red-600 text-white" on:click={stop}>STOP</button>
|
||||
</div>
|
||||
<select
|
||||
bind:value={selected_view}
|
||||
class="rounded-md outline outline-2 text-zinc-200 outline-zinc-600 bg-zinc-800"
|
||||
>
|
||||
{#each views as view}
|
||||
<option>{view}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 p-2">
|
||||
<button class="action_button bg-zinc-600">
|
||||
<Icon src={Power} size="24" />
|
||||
</button>
|
||||
<button class="action_button"><Icon src={Battery100} size="24" /></button>
|
||||
<button class="action_button"
|
||||
><Icon src={$isConnected ? Signal : SignalSlash} size="24" /></button
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<button class="h-full w-20 bg-red-600 text-white" on:click={stop}>STOP</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.topbar {
|
||||
height: 50px;
|
||||
}
|
||||
.action_button {
|
||||
border-radius: 4px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
outline: 1px solid #52525b;
|
||||
}
|
||||
</style>
|
||||
.topbar {
|
||||
height: 50px;
|
||||
}
|
||||
.action_button {
|
||||
border-radius: 4px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
outline: 1px solid #52525b;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,149 +1,184 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { CanvasTexture, CircleGeometry, Mesh, MeshBasicMaterial} from 'three';
|
||||
import socketService from '$lib/services/socket-service';
|
||||
import { lerp } from '$lib/utilities';
|
||||
import uzip from 'uzip';
|
||||
import { model, outControllerData } from '$lib/store';
|
||||
import { ForwardKinematics } from '$lib/kinematic';
|
||||
import { location } from '$lib/utilities';
|
||||
import FileService from '$lib/services/file-service';
|
||||
import SceneBuilder from '$lib/sceneBuilder';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { CanvasTexture, CircleGeometry, Mesh, MeshBasicMaterial } from 'three';
|
||||
import socketService from '$lib/services/socket-service';
|
||||
import { lerp } from '$lib/utilities';
|
||||
import uzip from 'uzip';
|
||||
import { model, outControllerData } from '$lib/store';
|
||||
import { ForwardKinematics } from '$lib/kinematic';
|
||||
import { location } from '$lib/utilities';
|
||||
import { fileService } from '$lib/services';
|
||||
import SceneBuilder from '$lib/sceneBuilder';
|
||||
|
||||
const angles = socketService.angles
|
||||
const mpu = socketService.mpu
|
||||
const angles = socketService.angles;
|
||||
const mpu = socketService.mpu;
|
||||
|
||||
let sceneManager:SceneBuilder
|
||||
let canvas: HTMLCanvasElement, streamCanvas: HTMLCanvasElement, stream: HTMLImageElement
|
||||
let context: CanvasRenderingContext2D, texture: CanvasTexture
|
||||
let sceneManager: SceneBuilder;
|
||||
let canvas: HTMLCanvasElement, streamCanvas: HTMLCanvasElement, stream: HTMLImageElement;
|
||||
let context: CanvasRenderingContext2D, texture: CanvasTexture;
|
||||
|
||||
let modelAngles:number[] | Int16Array = new Array(12).fill(0)
|
||||
let modelTargetAngles:number[] | Int16Array = new Array(12).fill(0)
|
||||
let modelAngles: number[] | Int16Array = new Array(12).fill(0);
|
||||
let modelTargetAngles: number[] | Int16Array = new Array(12).fill(0);
|
||||
|
||||
let modelBodyAngles:EulerAngle = { omega: 0, phi: 0, psi: 0 }
|
||||
let modelTargeBodyAngles:EulerAngle = { omega: 0, phi: 0, psi: 0 }
|
||||
let modelBodyAngles: EulerAngle = { omega: 0, phi: 0, psi: 0 };
|
||||
let modelTargeBodyAngles: EulerAngle = { omega: 0, phi: 0, psi: 0 };
|
||||
|
||||
const videoStream = `//${location}/api/stream`;
|
||||
const videoStream = `//${location}/api/stream`;
|
||||
|
||||
let showModel = true, showStream = false
|
||||
let showModel = true,
|
||||
showStream = false;
|
||||
|
||||
const servoNames = [
|
||||
"front_left_shoulder", "front_left_leg", "front_left_foot",
|
||||
"front_right_shoulder", "front_right_leg", "front_right_foot",
|
||||
"rear_left_shoulder", "rear_left_leg", "rear_left_foot",
|
||||
"rear_right_shoulder", "rear_right_leg", "rear_right_foot"
|
||||
]
|
||||
const servoNames = [
|
||||
'front_left_shoulder',
|
||||
'front_left_leg',
|
||||
'front_left_foot',
|
||||
'front_right_shoulder',
|
||||
'front_right_leg',
|
||||
'front_right_foot',
|
||||
'rear_left_shoulder',
|
||||
'rear_left_leg',
|
||||
'rear_left_foot',
|
||||
'rear_right_shoulder',
|
||||
'rear_right_leg',
|
||||
'rear_right_foot'
|
||||
];
|
||||
|
||||
interface EulerAngle {
|
||||
omega: number;
|
||||
phi: number;
|
||||
psi: number;
|
||||
}
|
||||
interface EulerAngle {
|
||||
omega: number;
|
||||
phi: number;
|
||||
psi: number;
|
||||
}
|
||||
|
||||
const degToRad = (val:number) => val * (Math.PI / 180)
|
||||
const degToRad = (val: number) => val * (Math.PI / 180);
|
||||
|
||||
onMount(async () => {
|
||||
await cacheModelFiles()
|
||||
await createScene()
|
||||
onMount(async () => {
|
||||
await cacheModelFiles();
|
||||
await createScene();
|
||||
|
||||
outControllerData.subscribe(data => {
|
||||
socketService.send(JSON.stringify({
|
||||
type: "kinematic/bodystate",
|
||||
angles:[0, (data[1]-128)/3, (data[2]-128) / 4],
|
||||
position:[(data[4]-128)/2, data[5], (data[3]-128)/2]}))
|
||||
})
|
||||
});
|
||||
outControllerData.subscribe((data) => {
|
||||
socketService.send(
|
||||
JSON.stringify({
|
||||
type: 'kinematic/bodystate',
|
||||
angles: [0, (data[1] - 128) / 3, (data[2] - 128) / 4],
|
||||
position: [(data[4] - 128) / 2, data[5], (data[3] - 128) / 2]
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
canvas.remove()
|
||||
})
|
||||
onDestroy(() => {
|
||||
canvas.remove();
|
||||
});
|
||||
|
||||
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 cacheModelFiles = async () => {
|
||||
let data = await fetch('/stl.zip').then((data) => data.arrayBuffer());
|
||||
|
||||
const updateAngles = (name:string, angle:number) => {
|
||||
modelTargetAngles[servoNames.indexOf(name)] = angle * (180/Math.PI)
|
||||
socketService.send(JSON.stringify({type:"kinematic/angle", angle:angle * (180/Math.PI), id:servoNames.indexOf(name)}))
|
||||
}
|
||||
var files = uzip.parse(data);
|
||||
|
||||
const createScene = async () => {
|
||||
sceneManager = new SceneBuilder()
|
||||
.addRenderer({ antialias: true, canvas: canvas, alpha: true})
|
||||
.addPerspectiveCamera({x:-0.5, y:0.5, z:1})
|
||||
.addOrbitControls(10, 30)
|
||||
.addSky()
|
||||
.addGroundPlane({x:0, y:-2, z:0})
|
||||
.addGridHelper({size:250, divisions:125, y:-2})
|
||||
.addAmbientLight({color:0xffffff, intensity:0.7})
|
||||
.addDirectionalLight({x:10, y:100, z:10, color:0xffffff, intensity:1})
|
||||
.addArrowHelper({origin:{x:0, y:0, z:0}, direction:{x:0, y:-2, z:0}})
|
||||
.addFogExp2(0xcccccc, 0.015)
|
||||
.addModel($model)
|
||||
.addDragControl(updateAngles)
|
||||
.handleResize()
|
||||
.addRenderCb(render)
|
||||
.startRenderLoop()
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
addVideoStream()
|
||||
}
|
||||
const updateAngles = (name: string, angle: number) => {
|
||||
modelTargetAngles[servoNames.indexOf(name)] = angle * (180 / Math.PI);
|
||||
socketService.send(
|
||||
JSON.stringify({
|
||||
type: 'kinematic/angle',
|
||||
angle: angle * (180 / Math.PI),
|
||||
id: servoNames.indexOf(name)
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const addVideoStream = () => {
|
||||
context = streamCanvas.getContext("2d");
|
||||
texture = new CanvasTexture( stream );
|
||||
const liveStream = new Mesh( new CircleGeometry(35, 32), new MeshBasicMaterial({ map: texture }))
|
||||
liveStream.position.z = -50
|
||||
liveStream.visible = showStream
|
||||
sceneManager.scene.add(liveStream)
|
||||
}
|
||||
const createScene = async () => {
|
||||
sceneManager = new SceneBuilder()
|
||||
.addRenderer({ antialias: true, canvas: canvas, alpha: true })
|
||||
.addPerspectiveCamera({ x: -0.5, y: 0.5, z: 1 })
|
||||
.addOrbitControls(10, 30)
|
||||
.addSky()
|
||||
.addGroundPlane({ x: 0, y: -2, z: 0 })
|
||||
.addGridHelper({ size: 250, divisions: 125, y: -2 })
|
||||
.addAmbientLight({ color: 0xffffff, intensity: 0.7 })
|
||||
.addDirectionalLight({ x: 10, y: 100, z: 10, color: 0xffffff, intensity: 1 })
|
||||
.addArrowHelper({ origin: { x: 0, y: 0, z: 0 }, direction: { x: 0, y: -2, z: 0 } })
|
||||
.addFogExp2(0xcccccc, 0.015)
|
||||
.addModel($model)
|
||||
.addDragControl(updateAngles)
|
||||
.handleResize()
|
||||
.addRenderCb(render)
|
||||
.startRenderLoop();
|
||||
|
||||
const handleVideoStream = () => {
|
||||
if(!showStream) return
|
||||
context.drawImage(stream, 0, 0)
|
||||
texture.needsUpdate = true;
|
||||
}
|
||||
addVideoStream();
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
const robot = sceneManager.model
|
||||
if(!robot) return
|
||||
const addVideoStream = () => {
|
||||
context = streamCanvas.getContext('2d');
|
||||
texture = new CanvasTexture(stream);
|
||||
const liveStream = new Mesh(
|
||||
new CircleGeometry(35, 32),
|
||||
new MeshBasicMaterial({ map: texture })
|
||||
);
|
||||
liveStream.position.z = -50;
|
||||
liveStream.visible = showStream;
|
||||
sceneManager.scene.add(liveStream);
|
||||
};
|
||||
|
||||
const forwardKinematics = new ForwardKinematics()
|
||||
const handleVideoStream = () => {
|
||||
if (!showStream) return;
|
||||
context.drawImage(stream, 0, 0);
|
||||
texture.needsUpdate = true;
|
||||
};
|
||||
|
||||
const points = forwardKinematics.calculateFootpoints(modelAngles.map(ang => degToRad(ang)) as number[])
|
||||
robot.position.y = Math.max(...points.map(coord => coord[0] / 100)) - 2.7
|
||||
robot.rotation.z = lerp(robot.rotation.z, degToRad($mpu.heading + 90), 0.1)
|
||||
modelTargetAngles = $angles
|
||||
|
||||
handleVideoStream()
|
||||
const render = () => {
|
||||
const robot = sceneManager.model;
|
||||
if (!robot) return;
|
||||
|
||||
for (let i = 0; i < servoNames.length; i++) {
|
||||
modelAngles[i] = lerp(robot.joints[servoNames[i]].angle * (180/Math.PI), modelTargetAngles[i], 0.1)
|
||||
robot.joints[servoNames[i]].setJointValue(degToRad(modelAngles[i]));
|
||||
}
|
||||
const forwardKinematics = new ForwardKinematics();
|
||||
|
||||
modelBodyAngles.omega = lerp(robot.rotation.x * (180/Math.PI), modelTargeBodyAngles.omega - 90, 0.1)
|
||||
modelBodyAngles.phi = lerp(robot.rotation.y * (180/Math.PI), modelTargeBodyAngles.phi, 0.1)
|
||||
modelBodyAngles.psi = lerp(robot.rotation.z * (180/Math.PI), modelTargeBodyAngles.psi + 90, 0.1)
|
||||
}
|
||||
const points = forwardKinematics.calculateFootpoints(
|
||||
modelAngles.map((ang) => degToRad(ang)) as number[]
|
||||
);
|
||||
robot.position.y = Math.max(...points.map((coord) => coord[0] / 100)) - 2.7;
|
||||
robot.rotation.z = lerp(robot.rotation.z, degToRad($mpu.heading + 90), 0.1);
|
||||
modelTargetAngles = $angles;
|
||||
|
||||
handleVideoStream();
|
||||
|
||||
for (let i = 0; i < servoNames.length; i++) {
|
||||
modelAngles[i] = lerp(
|
||||
robot.joints[servoNames[i]].angle * (180 / Math.PI),
|
||||
modelTargetAngles[i],
|
||||
0.1
|
||||
);
|
||||
robot.joints[servoNames[i]].setJointValue(degToRad(modelAngles[i]));
|
||||
}
|
||||
|
||||
modelBodyAngles.omega = lerp(
|
||||
robot.rotation.x * (180 / Math.PI),
|
||||
modelTargeBodyAngles.omega - 90,
|
||||
0.1
|
||||
);
|
||||
modelBodyAngles.phi = lerp(robot.rotation.y * (180 / Math.PI), modelTargeBodyAngles.phi, 0.1);
|
||||
modelBodyAngles.psi = lerp(
|
||||
robot.rotation.z * (180 / Math.PI),
|
||||
modelTargeBodyAngles.psi + 90,
|
||||
0.1
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={sceneManager.handleResize}></svelte:window>
|
||||
|
||||
<svelte:window on:resize={sceneManager.handleResize} />
|
||||
|
||||
{#if showStream}
|
||||
<img
|
||||
bind:this={stream}
|
||||
src={videoStream}
|
||||
class="hidden"
|
||||
alt="Live stream is down"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
{/if}
|
||||
<img
|
||||
bind:this={stream}
|
||||
src={videoStream}
|
||||
class="hidden"
|
||||
alt="Live stream is down"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
{/if}
|
||||
<canvas bind:this={streamCanvas} class="hidden"></canvas>
|
||||
<canvas bind:this={canvas} class="absolute"></canvas>
|
||||
<canvas bind:this={canvas} class="absolute"></canvas>
|
||||
|
||||
@@ -2,43 +2,43 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { jointNames } from '../../lib/store';
|
||||
|
||||
type Servo = {
|
||||
id: number;
|
||||
name: string;
|
||||
minPWM: number;
|
||||
maxPWM: number;
|
||||
pwmFor180: number;
|
||||
};
|
||||
type Servo = {
|
||||
id: number;
|
||||
name: string;
|
||||
minPWM: number;
|
||||
maxPWM: number;
|
||||
pwmFor180: number;
|
||||
};
|
||||
|
||||
let servos: any[] = [];
|
||||
|
||||
onMount(() => {
|
||||
jointNames.subscribe(data => {
|
||||
servos = data.map((name:string, i:number) => {
|
||||
return {
|
||||
id: i,
|
||||
name,
|
||||
minPWM: 0,
|
||||
maxPWM: 0,
|
||||
pwmFor180: 0
|
||||
};
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
jointNames.subscribe((data) => {
|
||||
servos = data.map((name: string, i: number) => {
|
||||
return {
|
||||
id: i,
|
||||
name,
|
||||
minPWM: 0,
|
||||
maxPWM: 0,
|
||||
pwmFor180: 0
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let selectedServo: number | null = null;
|
||||
|
||||
function updateServoValue(index: number, field: keyof Servo, value: number): void {
|
||||
servos[index] = { ...servos[index], [field]: value };
|
||||
}
|
||||
function updateServoValue(index: number, field: keyof Servo, value: number): void {
|
||||
servos[index] = { ...servos[index], [field]: value };
|
||||
}
|
||||
|
||||
const formatServo = (servo:Servo) => {
|
||||
const string = servo.name
|
||||
const name = string.charAt(0).toUpperCase() + string.split('_').join(' ').slice(1);
|
||||
return `${servo.id} ${name}`
|
||||
}
|
||||
const formatServo = (servo: Servo) => {
|
||||
const string = servo.name;
|
||||
const name = string.charAt(0).toUpperCase() + string.split('_').join(' ').slice(1);
|
||||
return `${servo.id} ${name}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="servo-selector">
|
||||
<label for="servo-select">Select Servo:</label>
|
||||
@@ -49,23 +49,36 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{#if selectedServo !== null}
|
||||
<div class="mt-5">
|
||||
<h2>Servo {formatServo(servos[selectedServo])} Calibration</h2>
|
||||
<label for="minPWM">Min PWM:</label>
|
||||
<input type="number" id="minPWM" class="bg-zinc-800"
|
||||
value={servos[selectedServo].minPWM}
|
||||
on:blur={(event) => updateServoValue(selectedServo, 'minPWM', Number(event.target.value))} />
|
||||
{#if selectedServo !== null}
|
||||
<div class="mt-5">
|
||||
<h2>Servo {formatServo(servos[selectedServo])} Calibration</h2>
|
||||
<label for="minPWM">Min PWM:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="minPWM"
|
||||
class="bg-zinc-800"
|
||||
value={servos[selectedServo].minPWM}
|
||||
on:blur={(event) => updateServoValue(selectedServo, 'minPWM', Number(event.target.value))}
|
||||
/>
|
||||
|
||||
<label for="maxPWM">Max PWM:</label>
|
||||
<input type="number" id="maxPWM" class="bg-zinc-800"
|
||||
value={servos[selectedServo].maxPWM}
|
||||
on:blur={(event) => updateServoValue(selectedServo, 'maxPWM', Number(event.target.value))} />
|
||||
<label for="maxPWM">Max PWM:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="maxPWM"
|
||||
class="bg-zinc-800"
|
||||
value={servos[selectedServo].maxPWM}
|
||||
on:blur={(event) => updateServoValue(selectedServo, 'maxPWM', Number(event.target.value))}
|
||||
/>
|
||||
|
||||
<label for="pwmFor180">PWM for 180°:</label>
|
||||
<input type="number" id="pwmFor180" class="bg-zinc-800"
|
||||
value={servos[selectedServo].pwmFor180}
|
||||
on:blur={(event) => updateServoValue(selectedServo, 'pwmFor180', Number(event.target.value))} />
|
||||
</div>
|
||||
{/if}
|
||||
<label for="pwmFor180">PWM for 180°:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="pwmFor180"
|
||||
class="bg-zinc-800"
|
||||
value={servos[selectedServo].pwmFor180}
|
||||
on:blur={(event) =>
|
||||
updateServoValue(selectedServo, 'pwmFor180', Number(event.target.value))}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
<script lang="ts">
|
||||
import socketService from "$lib/services/socket-service";
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
let isConnected = socketService.isConnected
|
||||
let settings = socketService.settings
|
||||
import socketService from '$lib/services/socket-service';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
if ($isConnected) {
|
||||
const message = JSON.stringify({type: 'system/settings'})
|
||||
socketService.send(message)
|
||||
}
|
||||
})
|
||||
let isConnected = socketService.isConnected;
|
||||
let settings = socketService.settings;
|
||||
|
||||
onMount(() => {
|
||||
if ($isConnected) {
|
||||
const message = JSON.stringify({ type: 'system/settings' });
|
||||
socketService.send(message);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full">
|
||||
<div>
|
||||
{#each Object.entries($settings) as entry}
|
||||
<div class="flex gap-8">
|
||||
<div class="w-32">{entry[0]}:</div>
|
||||
<div>{entry[1]}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{#each Object.entries($settings) as entry}
|
||||
<div class="flex gap-8">
|
||||
<div class="w-32">{entry[0]}:</div>
|
||||
<div>{entry[1]}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import { humanFileSize } from "$lib/utilities";
|
||||
import { onMount } from 'svelte';
|
||||
import { humanFileSize } from '$lib/utilities';
|
||||
import socketService from '$lib/services/socket-service';
|
||||
|
||||
let isConnected = socketService.isConnected
|
||||
let settings = socketService.settings
|
||||
let systemInfo = socketService.systemInfo
|
||||
|
||||
onMount(() => {
|
||||
if ($isConnected) {
|
||||
const message = JSON.stringify({type: 'system/info'})
|
||||
socketService.send(message)
|
||||
}
|
||||
})
|
||||
let isConnected = socketService.isConnected;
|
||||
let settings = socketService.settings;
|
||||
let systemInfo = socketService.systemInfo;
|
||||
|
||||
onMount(() => {
|
||||
if ($isConnected) {
|
||||
const message = JSON.stringify({ type: 'system/info' });
|
||||
socketService.send(message);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full">
|
||||
<div class="w-1/3">
|
||||
{#each Object.entries($systemInfo ?? {}) as entry}
|
||||
<div class="flex gap-8">
|
||||
<div class="w-32">{entry[0]}:</div>
|
||||
{#if entry[0].includes("Size") || entry[0].includes("Free") || entry[0].includes("Min")}
|
||||
<div>{humanFileSize(entry[1])}</div>
|
||||
{:else}
|
||||
<div>{entry[1]}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
{#each Object.entries($systemInfo ?? {}) as entry}
|
||||
<div class="flex gap-8">
|
||||
<div class="w-32">{entry[0]}:</div>
|
||||
{#if entry[0].includes('Size') || entry[0].includes('Free') || entry[0].includes('Min')}
|
||||
<div>{humanFileSize(entry[1])}</div>
|
||||
{:else}
|
||||
<div>{entry[1]}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<script lang="ts">
|
||||
import socketService from "$lib/services/socket-service";
|
||||
import { onMount } from 'svelte'
|
||||
import socketService from '$lib/services/socket-service';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isConnected = socketService.isConnected
|
||||
let log = socketService.log
|
||||
|
||||
onMount(() => {
|
||||
if ($isConnected) {
|
||||
const message = JSON.stringify({type: 'system/logs'})
|
||||
socketService.send(message)
|
||||
}
|
||||
})
|
||||
let isConnected = socketService.isConnected;
|
||||
let log = socketService.log;
|
||||
|
||||
onMount(() => {
|
||||
if ($isConnected) {
|
||||
const message = JSON.stringify({ type: 'system/logs' });
|
||||
socketService.send(message);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full">
|
||||
{#each $log as entry}
|
||||
<div>{entry}</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#each $log as entry}
|
||||
<div>{entry}</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user