@viamrobotics/test-widgets 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api-docs-href.d.ts +8 -0
- package/dist/api-docs-href.js +17 -0
- package/dist/components/angle-unit-toggle.svelte +1 -4
- package/dist/components/api-section.svelte +21 -23
- package/dist/components/api-section.svelte.d.ts +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/is-moving.svelte +3 -1
- package/dist/components/is-moving.svelte.d.ts +1 -0
- package/dist/components/mutation-section.svelte +19 -22
- package/dist/components/mutation-section.svelte.d.ts +2 -0
- package/dist/components/section-title.svelte +63 -0
- package/dist/components/section-title.svelte.d.ts +14 -0
- package/dist/components/widgets/arm/arm.svelte +17 -24
- package/dist/components/widgets/arm/joint-position-editor.svelte +89 -0
- package/dist/components/widgets/arm/joint-position-editor.svelte.d.ts +11 -0
- package/dist/components/widgets/arm/joint-position-quick-move.svelte +85 -0
- package/dist/components/widgets/arm/joint-position-quick-move.svelte.d.ts +9 -0
- package/dist/components/widgets/arm/joint-position-slider.svelte +36 -0
- package/dist/components/widgets/arm/joint-position-slider.svelte.d.ts +9 -0
- package/dist/components/widgets/arm/move-to-joint-positions.svelte +73 -127
- package/dist/components/widgets/arm/move-to-joint-positions.svelte.d.ts +1 -0
- package/dist/components/widgets/audio-input/audio-input.svelte +2 -0
- package/dist/components/widgets/audio-output/audio-output.svelte +2 -0
- package/dist/components/widgets/base/base.svelte +9 -1
- package/dist/components/widgets/camera/camera.svelte +184 -135
- package/dist/components/widgets/camera/get-pano-coverage-from-xmp.d.ts +31 -0
- package/dist/components/widgets/camera/get-pano-coverage-from-xmp.js +56 -0
- package/dist/components/widgets/camera/live-or-polling-video.svelte +1 -0
- package/dist/components/widgets/camera/live-three-sixty-camera-view.svelte +61 -0
- package/dist/components/widgets/camera/live-three-sixty-camera-view.svelte.d.ts +9 -0
- package/dist/components/widgets/camera/pano-sphere-scene.svelte +54 -0
- package/dist/components/widgets/camera/pano-sphere-scene.svelte.d.ts +10 -0
- package/dist/components/widgets/camera/three-sixty-camera-view.svelte +33 -43
- package/dist/components/widgets/camera/three-sixty-camera-view.svelte.d.ts +5 -3
- package/dist/components/widgets/discovery/discovery.svelte +1 -0
- package/dist/components/widgets/encoder/encoder.svelte +2 -0
- package/dist/components/widgets/gantry/gantry.svelte +28 -5
- package/dist/components/widgets/generic/generic.svelte +8 -1
- package/dist/components/widgets/gripper/gripper.svelte +11 -5
- package/dist/components/widgets/gripper/is-holding-something.svelte +1 -0
- package/dist/components/widgets/input-controller/input-controller.svelte +2 -1
- package/dist/components/widgets/motor/motor.svelte +11 -1
- package/dist/components/widgets/movement-sensor/movement-sensor.svelte +53 -26
- package/dist/components/widgets/power-sensor/power-sensor.svelte +4 -0
- package/dist/components/widgets/sensor/sensor.svelte +1 -0
- package/dist/components/widgets/servo/servo.svelte +10 -2
- package/dist/components/widgets/slam/move-on-map.svelte +4 -1
- package/dist/components/widgets/slam/slam.svelte +4 -1
- package/dist/components/widgets/vision-service/context.svelte.d.ts +1 -0
- package/dist/components/widgets/vision-service/context.svelte.js +7 -0
- package/dist/components/widgets/vision-service/detection-row.svelte +38 -0
- package/dist/components/widgets/vision-service/detection-row.svelte.d.ts +8 -0
- package/dist/components/widgets/vision-service/detection.svelte +7 -4
- package/dist/components/widgets/vision-service/legend.svelte +14 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ The playground (`pnpm dev`) can be used to develop the test-cards against prod r
|
|
|
10
10
|
|
|
11
11
|
This is useful if you need to validate the sdk against specific behavior of modules or need to replicate a bug from another robot (why replicate locally when you could just develop directly against the robot with the bug?).
|
|
12
12
|
|
|
13
|
-
To setup your playground, create a `.env.local` in the test-
|
|
13
|
+
To setup your playground, create a `.env.local` in the test-widgets directory with the following format (no need to create two robots):
|
|
14
14
|
|
|
15
15
|
```json
|
|
16
16
|
VITE_PLAYGROUND_ROBOTS='
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the docs URL for a given resource API + method, or undefined
|
|
3
|
+
* when the method has no dedicated anchor (e.g. GetStatus, GetSourceNames).
|
|
4
|
+
*
|
|
5
|
+
* @param api - RDK API string, e.g. "rdk:component:movement_sensor"
|
|
6
|
+
* @param method - camelCase method name, e.g. "getPosition"
|
|
7
|
+
*/
|
|
8
|
+
export declare const apiDocsHref: (api: string, method: string) => string | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const NO_LINK_METHODS = new Set(['getStatus', 'getSourceNames']);
|
|
2
|
+
/**
|
|
3
|
+
* Returns the docs URL for a given resource API + method, or undefined
|
|
4
|
+
* when the method has no dedicated anchor (e.g. GetStatus, GetSourceNames).
|
|
5
|
+
*
|
|
6
|
+
* @param api - RDK API string, e.g. "rdk:component:movement_sensor"
|
|
7
|
+
* @param method - camelCase method name, e.g. "getPosition"
|
|
8
|
+
*/
|
|
9
|
+
export const apiDocsHref = (api, method) => {
|
|
10
|
+
if (NO_LINK_METHODS.has(method))
|
|
11
|
+
return undefined;
|
|
12
|
+
const isService = api.includes(':service:');
|
|
13
|
+
// RDK API strings use underscores (movement_sensor); docs URLs use hyphens (movement-sensor)
|
|
14
|
+
const subtype = api.split(':').at(-1).replaceAll('_', '-');
|
|
15
|
+
const kind = isService ? 'services' : 'components';
|
|
16
|
+
return `https://docs.viam.com/reference/apis/${kind}/${subtype}/#${method.toLowerCase()}`;
|
|
17
|
+
};
|
|
@@ -15,10 +15,7 @@
|
|
|
15
15
|
aria-label={useRadians ? 'Switch to Degrees' : 'Switch to Radians'}
|
|
16
16
|
class="text-gray-6 hover:border-medium hover:bg-medium active:bg-gray-2 justify-items-end p-0.5"
|
|
17
17
|
>
|
|
18
|
-
<Icon
|
|
19
|
-
name="sync"
|
|
20
|
-
size="xs"
|
|
21
|
-
/>
|
|
18
|
+
<Icon name="sync" />
|
|
22
19
|
</button>
|
|
23
20
|
|
|
24
21
|
<span slot="description">
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte'
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
4
4
|
|
|
5
|
-
import { Icon, Tooltip } from '@viamrobotics/prime-core'
|
|
6
5
|
import { twMerge } from 'tailwind-merge'
|
|
7
6
|
|
|
7
|
+
import SectionTitle from './section-title.svelte'
|
|
8
|
+
|
|
8
9
|
interface Props extends HTMLAttributes<HTMLElement> {
|
|
9
10
|
title?: string | undefined
|
|
10
11
|
tooltip?: string | undefined
|
|
11
12
|
description?: string | undefined
|
|
12
13
|
bottomText?: string | undefined
|
|
14
|
+
/** RDK API string; presence renders the title as a linked monospace method name */
|
|
15
|
+
api?: string | undefined
|
|
13
16
|
class?: string
|
|
14
17
|
children?: Snippet
|
|
15
18
|
}
|
|
@@ -19,6 +22,7 @@
|
|
|
19
22
|
tooltip,
|
|
20
23
|
description,
|
|
21
24
|
bottomText,
|
|
25
|
+
api,
|
|
22
26
|
class: className = '',
|
|
23
27
|
children,
|
|
24
28
|
...rest
|
|
@@ -29,32 +33,26 @@
|
|
|
29
33
|
|
|
30
34
|
<section
|
|
31
35
|
class={twMerge('flex grow flex-col gap-4 p-4', className)}
|
|
32
|
-
aria-labelledby={headingID}
|
|
36
|
+
aria-labelledby={title ? headingID : undefined}
|
|
33
37
|
{...rest}
|
|
34
38
|
>
|
|
35
39
|
{#if title}
|
|
36
40
|
<div class="flex flex-col gap-0.5">
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
>
|
|
53
|
-
{tooltip}
|
|
54
|
-
</p>
|
|
55
|
-
</Tooltip>
|
|
56
|
-
{/if}
|
|
57
|
-
</h3>
|
|
41
|
+
{#if api}
|
|
42
|
+
<SectionTitle
|
|
43
|
+
{title}
|
|
44
|
+
{tooltip}
|
|
45
|
+
{api}
|
|
46
|
+
headingId={headingID}
|
|
47
|
+
/>
|
|
48
|
+
{:else}
|
|
49
|
+
<h3
|
|
50
|
+
class="flex flex-row items-center gap-1 text-sm font-semibold"
|
|
51
|
+
id={headingID}
|
|
52
|
+
>
|
|
53
|
+
{title}
|
|
54
|
+
</h3>
|
|
55
|
+
{/if}
|
|
58
56
|
{#if description}
|
|
59
57
|
<p class="text-subtle-2 text-xs">{description}</p>
|
|
60
58
|
{/if}
|
|
@@ -5,6 +5,8 @@ interface Props extends HTMLAttributes<HTMLElement> {
|
|
|
5
5
|
tooltip?: string | undefined;
|
|
6
6
|
description?: string | undefined;
|
|
7
7
|
bottomText?: string | undefined;
|
|
8
|
+
/** RDK API string; presence renders the title as a linked monospace method name */
|
|
9
|
+
api?: string | undefined;
|
|
8
10
|
class?: string;
|
|
9
11
|
children?: Snippet;
|
|
10
12
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as ConnectionStatus } from './connection-status.svelte';
|
|
2
|
+
export { default as SectionTitle } from './section-title.svelte';
|
|
2
3
|
export { default as ArmWidget } from './widgets/arm/arm.svelte';
|
|
3
4
|
export { default as ArmMoveToJointPositionsWidget } from './widgets/arm/move-to-joint-positions-widget.svelte';
|
|
4
5
|
export { default as ArmMoveToPositionWidget } from './widgets/arm/move-to-position-widget.svelte';
|
package/dist/components/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as ConnectionStatus } from './connection-status.svelte';
|
|
2
|
+
export { default as SectionTitle } from './section-title.svelte';
|
|
2
3
|
export { default as ArmWidget } from './widgets/arm/arm.svelte';
|
|
3
4
|
export { default as ArmMoveToJointPositionsWidget } from './widgets/arm/move-to-joint-positions-widget.svelte';
|
|
4
5
|
export { default as ArmMoveToPositionWidget } from './widgets/arm/move-to-position-widget.svelte';
|
|
@@ -33,10 +33,11 @@
|
|
|
33
33
|
client: Client
|
|
34
34
|
partID: string
|
|
35
35
|
resourceName: string
|
|
36
|
+
api: string
|
|
36
37
|
children?: Snippet
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
const { client: clientClass, partID, resourceName, children }: Props = $props()
|
|
40
|
+
const { client: clientClass, partID, resourceName, api, children }: Props = $props()
|
|
40
41
|
|
|
41
42
|
const client = $derived(
|
|
42
43
|
createResourceClient<Arm | Base | Gantry | Gripper | Motor | Servo>(
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
|
|
52
53
|
<ApiSection
|
|
53
54
|
title="IsMoving"
|
|
55
|
+
{api}
|
|
54
56
|
bottomText="Updates automatically"
|
|
55
57
|
class="grow"
|
|
56
58
|
>
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte'
|
|
3
3
|
|
|
4
|
-
import { Icon, Tooltip } from '@viamrobotics/prime-core'
|
|
5
4
|
import { twMerge } from 'tailwind-merge'
|
|
6
5
|
|
|
7
6
|
import ErrorDisplay from './error.svelte'
|
|
7
|
+
import SectionTitle from './section-title.svelte'
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
title: string
|
|
11
11
|
tooltip?: string | undefined
|
|
12
12
|
description?: string | undefined
|
|
13
13
|
lastError: Error | null
|
|
14
|
+
/** RDK API string; presence renders the title as a linked monospace method name */
|
|
15
|
+
api?: string | undefined
|
|
14
16
|
class?: string
|
|
15
17
|
titleInput?: Snippet
|
|
16
18
|
error?: Snippet
|
|
@@ -22,6 +24,7 @@
|
|
|
22
24
|
tooltip,
|
|
23
25
|
description,
|
|
24
26
|
lastError = null,
|
|
27
|
+
api,
|
|
25
28
|
class: className,
|
|
26
29
|
titleInput,
|
|
27
30
|
error,
|
|
@@ -37,27 +40,21 @@
|
|
|
37
40
|
>
|
|
38
41
|
<div class={twMerge('flex grow flex-row flex-wrap gap-2', className)}>
|
|
39
42
|
<div class="flex max-w-[200px] grow flex-col gap-0.5 pr-4">
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
>
|
|
56
|
-
{tooltip}
|
|
57
|
-
</p>
|
|
58
|
-
</Tooltip>
|
|
59
|
-
{/if}
|
|
60
|
-
</h3>
|
|
43
|
+
{#if api}
|
|
44
|
+
<SectionTitle
|
|
45
|
+
{title}
|
|
46
|
+
{tooltip}
|
|
47
|
+
{api}
|
|
48
|
+
headingId={headingID}
|
|
49
|
+
/>
|
|
50
|
+
{:else}
|
|
51
|
+
<h3
|
|
52
|
+
class="flex flex-row items-center gap-1 text-sm font-semibold"
|
|
53
|
+
id={headingID}
|
|
54
|
+
>
|
|
55
|
+
{title}
|
|
56
|
+
</h3>
|
|
57
|
+
{/if}
|
|
61
58
|
{#if description}
|
|
62
59
|
<p class="text-subtle-2 text-xs">{description}</p>
|
|
63
60
|
{/if}
|
|
@@ -4,6 +4,8 @@ interface Props {
|
|
|
4
4
|
tooltip?: string | undefined;
|
|
5
5
|
description?: string | undefined;
|
|
6
6
|
lastError: Error | null;
|
|
7
|
+
/** RDK API string; presence renders the title as a linked monospace method name */
|
|
8
|
+
api?: string | undefined;
|
|
7
9
|
class?: string;
|
|
8
10
|
titleInput?: Snippet;
|
|
9
11
|
error?: Snippet;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
|
|
4
|
+
import { Icon, Tooltip } from '@viamrobotics/prime-core'
|
|
5
|
+
|
|
6
|
+
import { apiDocsHref } from '../api-docs-href'
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
title: string
|
|
10
|
+
tooltip?: string | undefined
|
|
11
|
+
/** RDK API string, e.g. "rdk:component:camera". Required — SectionTitle is only for headers that map to an API method; the docs link is built from this plus the title. */
|
|
12
|
+
api: string
|
|
13
|
+
/** Optional external ID for the <h3>; generated internally if omitted */
|
|
14
|
+
headingId?: string | undefined
|
|
15
|
+
/** Inline content rendered after the title inside the <h3> (e.g. unit labels) */
|
|
16
|
+
suffix?: Snippet
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { title, tooltip, api, headingId, suffix }: Props = $props()
|
|
20
|
+
|
|
21
|
+
const generatedID = $props.id()
|
|
22
|
+
const id = $derived(headingId ?? generatedID)
|
|
23
|
+
|
|
24
|
+
// Method names are the camelCase form of the PascalCase title (GetPosition → getPosition).
|
|
25
|
+
const method = $derived(title.charAt(0).toLowerCase() + title.slice(1))
|
|
26
|
+
const href = $derived(apiDocsHref(api, method))
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<h3
|
|
30
|
+
class="flex flex-row items-center gap-1 text-sm font-semibold"
|
|
31
|
+
{id}
|
|
32
|
+
>
|
|
33
|
+
{#if href}
|
|
34
|
+
<a
|
|
35
|
+
{href}
|
|
36
|
+
target="_blank"
|
|
37
|
+
rel="noopener noreferrer external"
|
|
38
|
+
class="decoration-gray-5 hover:decoration-default font-mono underline underline-offset-3"
|
|
39
|
+
>
|
|
40
|
+
{title}
|
|
41
|
+
</a>
|
|
42
|
+
{:else}
|
|
43
|
+
<span class="font-mono">{title}</span>
|
|
44
|
+
{/if}
|
|
45
|
+
{#if suffix}
|
|
46
|
+
{@render suffix()}
|
|
47
|
+
{/if}
|
|
48
|
+
{#if tooltip}
|
|
49
|
+
<Tooltip>
|
|
50
|
+
<Icon
|
|
51
|
+
name="information-outline"
|
|
52
|
+
cx="text-gray-6"
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
<p
|
|
56
|
+
slot="description"
|
|
57
|
+
class="text-xs whitespace-pre-line"
|
|
58
|
+
>
|
|
59
|
+
{tooltip}
|
|
60
|
+
</p>
|
|
61
|
+
</Tooltip>
|
|
62
|
+
{/if}
|
|
63
|
+
</h3>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
title: string;
|
|
4
|
+
tooltip?: string | undefined;
|
|
5
|
+
/** RDK API string, e.g. "rdk:component:camera". Required — SectionTitle is only for headers that map to an API method; the docs link is built from this plus the title. */
|
|
6
|
+
api: string;
|
|
7
|
+
/** Optional external ID for the <h3>; generated internally if omitted */
|
|
8
|
+
headingId?: string | undefined;
|
|
9
|
+
/** Inline content rendered after the title inside the <h3> (e.g. unit labels) */
|
|
10
|
+
suffix?: Snippet;
|
|
11
|
+
}
|
|
12
|
+
declare const SectionTitle: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type SectionTitle = ReturnType<typeof SectionTitle>;
|
|
14
|
+
export default SectionTitle;
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
import { getJointPositionLimits, type KinematicsJSON } from './joint-position-limits'
|
|
20
20
|
import MoveToJointPositions from './move-to-joint-positions.svelte'
|
|
21
21
|
import MoveToPosition from './move-to-position.svelte'
|
|
22
|
-
import QuickMove from './quick-move.svelte'
|
|
23
22
|
|
|
24
23
|
interface Props {
|
|
25
24
|
partID: string
|
|
@@ -38,9 +37,9 @@
|
|
|
38
37
|
const jointPositionsQuery = createResourceQuery(client, 'getJointPositions', options)
|
|
39
38
|
const endPositionQuery = createResourceQuery(client, 'getEndPosition', options)
|
|
40
39
|
const kinematicsQuery = createResourceQuery(client, 'getKinematics', options)
|
|
40
|
+
const isMovingQuery = createResourceQuery(client, 'isMoving', options)
|
|
41
41
|
|
|
42
42
|
const moveToJointPosMutation = createResourceMutation(client, 'moveToJointPositions')
|
|
43
|
-
const quickMoveToJointPosMutation = createResourceMutation(client, 'moveToJointPositions')
|
|
44
43
|
const stopMutation = createResourceMutation(client, 'stop')
|
|
45
44
|
const moveToPosMutation = createResourceMutation(client, 'moveToPosition')
|
|
46
45
|
|
|
@@ -48,10 +47,6 @@
|
|
|
48
47
|
moveToJointPosMutation.mutate([jointPositionsList], {})
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
const quickMoveToJointPositions = (jointPositionsList: number[]) => {
|
|
52
|
-
quickMoveToJointPosMutation.mutate([jointPositionsList], {})
|
|
53
|
-
}
|
|
54
|
-
|
|
55
50
|
const moveToPosition = (position: Pose) => {
|
|
56
51
|
moveToPosMutation.mutate([position], {})
|
|
57
52
|
}
|
|
@@ -62,10 +57,11 @@
|
|
|
62
57
|
<div class="flex flex-col gap-4 lg:flex-row lg:gap-0 lg:divide-x">
|
|
63
58
|
<!-- Main control sections -->
|
|
64
59
|
<div
|
|
65
|
-
class="flex flex-col gap-4 lg:grid lg:grow lg:grid-cols-2 lg:gap-0 lg:divide-x xl:grid-cols-
|
|
60
|
+
class="flex flex-col gap-4 lg:grid lg:grow lg:grid-cols-2 lg:gap-0 lg:divide-x xl:grid-cols-3"
|
|
66
61
|
>
|
|
67
62
|
<ApiSection
|
|
68
63
|
title="GetJointPositions"
|
|
64
|
+
api="rdk:component:arm"
|
|
69
65
|
bottomText="Updates automatically"
|
|
70
66
|
>
|
|
71
67
|
<Query query={jointPositionsQuery}>
|
|
@@ -74,7 +70,10 @@
|
|
|
74
70
|
{/if}
|
|
75
71
|
</Query>
|
|
76
72
|
</ApiSection>
|
|
77
|
-
<ApiSection
|
|
73
|
+
<ApiSection
|
|
74
|
+
title="MoveToJointPositions"
|
|
75
|
+
api="rdk:component:arm"
|
|
76
|
+
>
|
|
78
77
|
<Queries queries={[jointPositionsQuery, kinematicsQuery]}>
|
|
79
78
|
{#if jointPositionsQuery.data && kinematicsQuery.data}
|
|
80
79
|
<MoveToJointPositions
|
|
@@ -82,11 +81,15 @@
|
|
|
82
81
|
{moveToJointPositions}
|
|
83
82
|
lastError={moveToJointPosMutation.error}
|
|
84
83
|
jointLimitsDegrees={getJointPositionLimits(kinematicsQuery.data as KinematicsJSON)}
|
|
84
|
+
isMoving={isMovingQuery.data ?? false}
|
|
85
85
|
/>
|
|
86
86
|
{/if}
|
|
87
87
|
</Queries>
|
|
88
88
|
</ApiSection>
|
|
89
|
-
<ApiSection
|
|
89
|
+
<ApiSection
|
|
90
|
+
title="MoveToPosition"
|
|
91
|
+
api="rdk:component:arm"
|
|
92
|
+
>
|
|
90
93
|
<Query query={endPositionQuery}>
|
|
91
94
|
{#if endPositionQuery.data}
|
|
92
95
|
<MoveToPosition
|
|
@@ -97,27 +100,16 @@
|
|
|
97
100
|
{/if}
|
|
98
101
|
</Query>
|
|
99
102
|
</ApiSection>
|
|
100
|
-
<ApiSection
|
|
101
|
-
title="Quick move"
|
|
102
|
-
bottomText="Press a button to execute"
|
|
103
|
-
>
|
|
104
|
-
<Query query={jointPositionsQuery}>
|
|
105
|
-
{#if jointPositionsQuery.data}
|
|
106
|
-
<QuickMove
|
|
107
|
-
positions={jointPositionsQuery.data.values}
|
|
108
|
-
moveToJointPositions={quickMoveToJointPositions}
|
|
109
|
-
lastError={quickMoveToJointPosMutation.error}
|
|
110
|
-
/>
|
|
111
|
-
{/if}
|
|
112
|
-
</Query>
|
|
113
|
-
</ApiSection>
|
|
114
103
|
</div>
|
|
115
104
|
|
|
116
105
|
<!-- Control actions sidebar -->
|
|
117
106
|
<div
|
|
118
107
|
class="flex flex-row gap-4 lg:ml-auto lg:w-full lg:max-w-40 lg:flex-col lg:gap-0 lg:divide-y"
|
|
119
108
|
>
|
|
120
|
-
<ApiSection
|
|
109
|
+
<ApiSection
|
|
110
|
+
title="Stop"
|
|
111
|
+
api="rdk:component:arm"
|
|
112
|
+
>
|
|
121
113
|
<StopButton
|
|
122
114
|
error={stopMutation.error}
|
|
123
115
|
onStop={() => {
|
|
@@ -127,6 +119,7 @@
|
|
|
127
119
|
</ApiSection>
|
|
128
120
|
<IsMoving
|
|
129
121
|
client={ArmClient}
|
|
122
|
+
api="rdk:component:arm"
|
|
130
123
|
{partID}
|
|
131
124
|
{resourceName}
|
|
132
125
|
/>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button, Icon, Tooltip } from '@viamrobotics/prime-core'
|
|
3
|
+
|
|
4
|
+
import Table from '../../table.svelte'
|
|
5
|
+
|
|
6
|
+
import { type JointLimit } from './joint-position-limits'
|
|
7
|
+
import JointPositionSlider from './joint-position-slider.svelte'
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
desiredPositions: number[]
|
|
11
|
+
positions: number[]
|
|
12
|
+
jointLimitsDegrees: JointLimit[]
|
|
13
|
+
useRadians: boolean
|
|
14
|
+
moveToJointPositions: (jointPositions: number[]) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
desiredPositions = $bindable(),
|
|
19
|
+
positions,
|
|
20
|
+
jointLimitsDegrees,
|
|
21
|
+
useRadians,
|
|
22
|
+
moveToJointPositions,
|
|
23
|
+
}: Props = $props()
|
|
24
|
+
|
|
25
|
+
const getSliderMin = (index: number): number => jointLimitsDegrees[index]?.minDegrees ?? -180
|
|
26
|
+
const getSliderMax = (index: number): number => jointLimitsDegrees[index]?.maxDegrees ?? 180
|
|
27
|
+
|
|
28
|
+
const execute = () => {
|
|
29
|
+
moveToJointPositions([...desiredPositions])
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const resetToZero = () => {
|
|
33
|
+
desiredPositions = positions.map(() => 0)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const resetToCurrent = () => {
|
|
37
|
+
desiredPositions = [...positions]
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<Table>
|
|
42
|
+
<thead>
|
|
43
|
+
<tr>
|
|
44
|
+
<th>Joint</th>
|
|
45
|
+
<th>Position ({useRadians ? 'radians' : 'degrees'})</th>
|
|
46
|
+
</tr>
|
|
47
|
+
</thead>
|
|
48
|
+
<tbody>
|
|
49
|
+
{#each [...desiredPositions.keys()] as index (index)}
|
|
50
|
+
<tr>
|
|
51
|
+
<th scope="row">{index}</th>
|
|
52
|
+
<th>
|
|
53
|
+
<JointPositionSlider
|
|
54
|
+
bind:value={desiredPositions[index]}
|
|
55
|
+
minDegrees={getSliderMin(index)}
|
|
56
|
+
maxDegrees={getSliderMax(index)}
|
|
57
|
+
{useRadians}
|
|
58
|
+
/>
|
|
59
|
+
</th>
|
|
60
|
+
</tr>
|
|
61
|
+
{/each}
|
|
62
|
+
</tbody>
|
|
63
|
+
</Table>
|
|
64
|
+
|
|
65
|
+
<div class="mb-2 flex flex-col gap-2">
|
|
66
|
+
<span class="flex flex-row gap-2">
|
|
67
|
+
<h4 class="text-xs font-semibold">Quick set</h4>
|
|
68
|
+
<Tooltip>
|
|
69
|
+
<Icon
|
|
70
|
+
name="information-outline"
|
|
71
|
+
cx="text-gray-6"
|
|
72
|
+
/>
|
|
73
|
+
<span slot="description">Will update the slider values but will not execute</span>
|
|
74
|
+
</Tooltip>
|
|
75
|
+
</span>
|
|
76
|
+
<div class="flex flex-col gap-2 sm:flex-row">
|
|
77
|
+
<Button onclick={resetToZero}>Zero</Button>
|
|
78
|
+
<Button onclick={resetToCurrent}>Current position</Button>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<Button
|
|
83
|
+
class="mt-auto w-fit"
|
|
84
|
+
icon="play-circle-outline"
|
|
85
|
+
variant="dark"
|
|
86
|
+
onclick={execute}
|
|
87
|
+
>
|
|
88
|
+
Execute
|
|
89
|
+
</Button>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type JointLimit } from './joint-position-limits';
|
|
2
|
+
interface Props {
|
|
3
|
+
desiredPositions: number[];
|
|
4
|
+
positions: number[];
|
|
5
|
+
jointLimitsDegrees: JointLimit[];
|
|
6
|
+
useRadians: boolean;
|
|
7
|
+
moveToJointPositions: (jointPositions: number[]) => void;
|
|
8
|
+
}
|
|
9
|
+
declare const JointPositionEditor: import("svelte").Component<Props, {}, "desiredPositions">;
|
|
10
|
+
type JointPositionEditor = ReturnType<typeof JointPositionEditor>;
|
|
11
|
+
export default JointPositionEditor;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button, Icon, Tooltip } from '@viamrobotics/prime-core'
|
|
3
|
+
|
|
4
|
+
import Table from '../../table.svelte'
|
|
5
|
+
import { degreesToRadians, formatNumeric } from '../../../format'
|
|
6
|
+
|
|
7
|
+
const INCREMENT_DEGREES = 5
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
positions: number[]
|
|
11
|
+
moveToJointPositions: (jointPositions: number[]) => void
|
|
12
|
+
useRadians: boolean
|
|
13
|
+
isMoving: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { positions, moveToJointPositions, useRadians, isMoving }: Props = $props()
|
|
17
|
+
|
|
18
|
+
const toDisplayAngle = (degrees: number) => (useRadians ? degreesToRadians(degrees) : degrees)
|
|
19
|
+
|
|
20
|
+
const currentDisplayPositions = $derived(positions.map((degrees) => toDisplayAngle(degrees)))
|
|
21
|
+
|
|
22
|
+
const quickMove = (index: number, increment: number) => {
|
|
23
|
+
if (isMoving) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
const clonedPositions = positions.map((position, positionIndex) =>
|
|
27
|
+
positionIndex === index ? position + increment : position
|
|
28
|
+
)
|
|
29
|
+
moveToJointPositions(clonedPositions)
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<Tooltip>
|
|
34
|
+
<span class="flex items-center gap-1 text-xs text-amber-600">
|
|
35
|
+
<Icon
|
|
36
|
+
name="alert"
|
|
37
|
+
size="sm"
|
|
38
|
+
cx="text-amber-600"
|
|
39
|
+
/>
|
|
40
|
+
Quick move executes immediately
|
|
41
|
+
</span>
|
|
42
|
+
<span slot="description">
|
|
43
|
+
Each ±5° button sends a move command as soon as it is pressed. Buttons are disabled while the
|
|
44
|
+
arm is moving.
|
|
45
|
+
</span>
|
|
46
|
+
</Tooltip>
|
|
47
|
+
|
|
48
|
+
<Table>
|
|
49
|
+
<thead>
|
|
50
|
+
<tr>
|
|
51
|
+
<th>Joint</th>
|
|
52
|
+
<th>Move (degrees)</th>
|
|
53
|
+
</tr>
|
|
54
|
+
</thead>
|
|
55
|
+
<tbody>
|
|
56
|
+
{#each positions as position, index (index)}
|
|
57
|
+
{@const currentValue = currentDisplayPositions[index] ?? position}
|
|
58
|
+
{@const unit = useRadians ? ' rad' : '°'}
|
|
59
|
+
<tr>
|
|
60
|
+
<th scope="row">{index}</th>
|
|
61
|
+
<th>
|
|
62
|
+
<div class="flex h-full max-h-6.5 w-full items-center justify-center gap-1.5">
|
|
63
|
+
<Button
|
|
64
|
+
aria-label="Decrease joint {index} by 5 degrees"
|
|
65
|
+
disabled={isMoving}
|
|
66
|
+
onclick={() => quickMove(index, -INCREMENT_DEGREES)}
|
|
67
|
+
>
|
|
68
|
+
−5°
|
|
69
|
+
</Button>
|
|
70
|
+
<span class="min-w-16 grow text-center text-xs text-gray-700 tabular-nums">
|
|
71
|
+
{formatNumeric(currentValue)}{unit}
|
|
72
|
+
</span>
|
|
73
|
+
<Button
|
|
74
|
+
aria-label="Increase joint {index} by 5 degrees"
|
|
75
|
+
disabled={isMoving}
|
|
76
|
+
onclick={() => quickMove(index, INCREMENT_DEGREES)}
|
|
77
|
+
>
|
|
78
|
+
+5°
|
|
79
|
+
</Button>
|
|
80
|
+
</div>
|
|
81
|
+
</th>
|
|
82
|
+
</tr>
|
|
83
|
+
{/each}
|
|
84
|
+
</tbody>
|
|
85
|
+
</Table>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
positions: number[];
|
|
3
|
+
moveToJointPositions: (jointPositions: number[]) => void;
|
|
4
|
+
useRadians: boolean;
|
|
5
|
+
isMoving: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare const JointPositionQuickMove: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type JointPositionQuickMove = ReturnType<typeof JointPositionQuickMove>;
|
|
9
|
+
export default JointPositionQuickMove;
|