💄 Simplify calibration UX

This commit is contained in:
Rune Harlyk
2025-06-27 22:39:18 +02:00
parent 98262b2efc
commit 40025a55c3
4 changed files with 150 additions and 128 deletions
+2
View File
@@ -36,6 +36,8 @@ export { default as FileIcon } from '~icons/mdi/file'
export { default as FolderIcon } from '~icons/mdi/folder-outline' export { default as FolderIcon } from '~icons/mdi/folder-outline'
export { default as FolderOpenOutline } from '~icons/mdi/folder-open-outline' export { default as FolderOpenOutline } from '~icons/mdi/folder-open-outline'
export { default as TrashIcon } from '~icons/mdi/trash' export { default as TrashIcon } from '~icons/mdi/trash'
export { default as RotateCcw } from '~icons/mdi/rotate-left'
export { default as RotateCw } from '~icons/mdi/rotate-right'
export { default as Down } from '~icons/tabler/chevron-down' export { default as Down } from '~icons/tabler/chevron-down'
export { default as Cancel } from '~icons/tabler/x' export { default as Cancel } from '~icons/tabler/x'
@@ -1,9 +1,12 @@
<script lang="ts"> <script lang="ts">
import Servos from './servos.svelte'; import Servos from './servos.svelte'
import ServoTable from './ServoTable.svelte'; import ServoTable from './ServoTable.svelte'
let servoId = $state(0)
let pwm = $state(306)
</script> </script>
<div class="mx-0 my-1 flex flex-col space-y-4 sm:mx-8 sm:my-8"> <div class="mx-0 my-1 flex flex-col space-y-4 sm:mx-8 sm:my-8">
<Servos /> <Servos bind:servoId bind:pwm />
<ServoTable /> <ServoTable {servoId} {pwm} />
</div> </div>
@@ -1,73 +1,113 @@
<script lang="ts"> <script lang="ts">
import { api } from '$lib/api'; import { api } from '$lib/api'
import { onMount } from 'svelte'; import { onMount } from 'svelte'
interface Props { import { RotateCw, RotateCcw } from '$lib/components/icons'
data?: any; interface Props {
data?: any
servoId?: number
pwm?: number
}
let {
data = $bindable({
servos: []
}),
pwm = $bindable(306),
servoId = $bindable(0)
}: Props = $props()
const updateValue = (event: Event, index: number, key: string) => {
data.servos[index][key] = Number((event.target as HTMLInputElement).value)
}
const syncConfig = async () => {
await api.post('/api/servo/config', data)
}
const toggleDirection = async (index: number) => {
data.servos[index].direction = data.servos[index].direction === 1 ? -1 : 1
await syncConfig()
}
onMount(async () => {
const result = await api.get('/api/servo/config')
if (result.isOk()) {
data = result.inner
} }
})
let { data = $bindable({ const setCenterPWM = async () => {
servos: [] console.log('setCenterPWM', servoId, pwm)
}) }: Props = $props(); data.servos[servoId]['center_pwm'] = pwm
await syncConfig()
const updateValue = (event, index, key) => { }
data.servos[index][key] = event.target.innerText;
};
const syncConfig = async () => {
await api.post('/api/servo/config', data);
};
onMount(async () => {
const result = await api.get('/api/servo/config');
if (result.isOk()) {
data = result.inner;
}
});
</script> </script>
<div class="overflow-x-auto"> <div>
<table class="table table-xs"> <button class="btn btn-sm btn-primary" onclick={() => setCenterPWM()}>Set center pwm</button>
<thead> </div>
<tr>
<th>Center PWM</th> <div class="overflow-x-auto">
<th>Center Angle</th> <table class="table table-xs">
<th>Direction</th> <thead>
<th>Conversion</th> <tr>
</tr> <th>Servo</th>
</thead> <th>Center PWM</th>
<tbody> <th>Center Angle</th>
{#each data.servos as servo, index} <th>Direction</th>
<tr> <th>Conversion</th>
<td </tr>
contenteditable="true" </thead>
onblur={syncConfig} <tbody>
oninput={event => updateValue(event, index, 'center_pwm')} {#each data.servos as servo, index}
> <tr class="hover:bg-base-200">
{servo.center_pwm} <td class="font-medium">Servo {index}</td>
</td> <td>
<td <input
contenteditable="true" type="number"
onblur={syncConfig} class="input input-sm input-bordered w-20"
oninput={event => updateValue(event, index, 'center_angle')} value={servo.center_pwm}
> onblur={syncConfig}
{servo.center_angle} oninput={event => updateValue(event, index, 'center_pwm')}
</td> min="80"
<td max="600" />
contenteditable="true" </td>
onblur={syncConfig} <td>
oninput={event => updateValue(event, index, 'direction')} <input
> type="number"
{servo.direction} step="0.1"
</td> class="input input-sm input-bordered w-20"
<td value={servo.center_angle}
contenteditable="true" onblur={syncConfig}
onblur={syncConfig} oninput={event => updateValue(event, index, 'center_angle')}
oninput={event => updateValue(event, index, 'conversion')} min="-90"
> max="90" />
{servo.conversion} </td>
</td> <td>
</tr> <button
{/each} class="btn btn-sm btn-ghost"
</tbody> title="Toggle direction {servo.direction}"
</table> onclick={() => toggleDirection(index)}>
{#if servo.direction === 1}
<RotateCw class="w-4 h-4 text-green-500" />
{:else}
<RotateCcw class="w-4 h-4" />
{/if}
</button>
</td>
<td>
<input
type="number"
step="0.01"
class="input input-sm input-bordered w-20"
value={servo.conversion}
onblur={syncConfig}
oninput={event => updateValue(event, index, 'conversion')}
min="0"
max="10" />
</td>
</tr>
{/each}
</tbody>
</table>
</div> </div>
+34 -57
View File
@@ -1,38 +1,23 @@
<script lang="ts"> <script lang="ts">
import SettingsCard from '$lib/components/SettingsCard.svelte'
import type { ServoConfiguration, Servo } from '$lib/types/models'
import Spinner from '$lib/components/Spinner.svelte'
import { socket } from '$lib/stores' import { socket } from '$lib/stores'
import { onDestroy, onMount } from 'svelte'
import { throttler as Throttler } from '$lib/utilities' import { throttler as Throttler } from '$lib/utilities'
import { MotorOutline } from '$lib/components/icons'
let isLoading = false let { servoId = $bindable(0), pwm = $bindable(306) } = $props()
let active = $state(false) let active = $state(false)
let servoId = $state(0)
let allServos = $state(false) let allServos = $state(false)
const throttler = new Throttler() const throttler = new Throttler()
const sweep = (event: any) => { const activateServo = () => {
let channel = event.detail.channel
socket.sendEvent('servoConfiguration', { servos: [{ channel, sweep: true }] })
}
const activateServo = (event: any) => {
socket.sendEvent('servoState', { active: 1 }) socket.sendEvent('servoState', { active: 1 })
} }
const deactivateServo = (event: any) => { const deactivateServo = () => {
socket.sendEvent('servoState', { active: 0 }) socket.sendEvent('servoState', { active: 0 })
} }
let pwm = $state(306)
const updatePWM = () => { const updatePWM = () => {
throttler.throttle(() => { throttler.throttle(() => {
socket.sendEvent('servoPWM', { servo_id: servoId, pwm }) socket.sendEvent('servoPWM', { servo_id: servoId, pwm })
@@ -44,43 +29,35 @@
} }
</script> </script>
<SettingsCard collapsible={false}> <div class="flex flex-col">
{#snippet icon()} <h2 class="text-lg">General servo configuration</h2>
<MotorOutline class="lex-shrink-0 mr-2 h-6 w-6 self-end" /> <span>Servo</span>
{/snippet} <span>{pwm}</span>
{#snippet title()} </div>
<span>Servo</span> <input
{/snippet} type="range"
{pwm} min="80"
<input max="600"
type="range" bind:value={pwm}
min="80" oninput={updatePWM}
max="600" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" />
bind:value={pwm}
oninput={updatePWM}
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" />
{#if isLoading} <div class="flex flex-col">
<Spinner /> <h2 class="text-lg">General servo configuration</h2>
{:else} <span>
<div class="flex flex-col"> <label for="mode">All servoes</label>
<h2 class="text-lg">General servo configuration</h2> <input type="checkbox" class="toggle" bind:checked={allServos} onchange={toggleMode} />
<span> </span>
<label for="mode">All servoes</label> <span>
<input type="checkbox" class="toggle" bind:checked={allServos} onchange={toggleMode} /> <label for="active">Active</label>
</span> <input
<span> type="checkbox"
<label for="active">Active</label> class="toggle"
<input bind:checked={active}
type="checkbox" onchange={active ? activateServo : deactivateServo} />
class="toggle" </span>
bind:checked={active} <span class="flex items-center gap-2">
onchange={active ? activateServo : deactivateServo} /> <label for="servoId">Servo active {servoId}</label>
</span> <input type="range" min="0" max="11" step="1" bind:value={servoId} />
<span class="flex items-center gap-2"> </span>
<label for="servoId">Servo active {servoId}</label> </div>
<input type="range" min="0" max="11" step="1" bind:value={servoId} />
</span>
</div>
{/if}
</SettingsCard>