@viamrobotics/test-widgets 0.7.0 → 0.9.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 +33 -0
- package/dist/client-map.d.ts +30 -30
- package/dist/client-map.js +32 -29
- package/dist/components/index.d.ts +22 -0
- package/dist/components/index.js +22 -0
- package/dist/components/is-moving.svelte +1 -4
- package/dist/components/mutation-section.svelte +1 -1
- package/dist/components/navigation-map/components/obstacle.svelte +2 -1
- package/dist/components/three/capsule-geometry.d.ts +10 -0
- package/dist/components/three/capsule-geometry.js +19 -0
- package/dist/components/three/index.d.ts +1 -0
- package/dist/components/three/index.js +1 -0
- package/dist/components/widgets/arm/arm.svelte +8 -5
- package/dist/components/widgets/arm/get-joint-positions-widget.svelte +36 -0
- package/dist/components/widgets/arm/get-joint-positions-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/arm/is-moving-widget.svelte +20 -0
- package/dist/components/widgets/arm/is-moving-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/arm/move-to-joint-positions-widget.svelte +9 -5
- package/dist/components/widgets/arm/move-to-position.svelte +31 -3
- package/dist/components/widgets/audio-input/get-properties-widget.svelte +43 -0
- package/dist/components/widgets/audio-input/get-properties-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/audio-output/get-properties-widget.svelte +43 -0
- package/dist/components/widgets/audio-output/get-properties-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/base/is-moving-widget.svelte +20 -0
- package/dist/components/widgets/base/is-moving-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/base/quick-move.svelte +12 -5
- package/dist/components/widgets/camera/live-or-polling-video.svelte +8 -1
- package/dist/components/widgets/do-command/do-command.svelte +21 -7
- package/dist/components/widgets/do-command/do-command.svelte.d.ts +8 -1
- package/dist/components/widgets/encoder/encoder-position-type.d.ts +8 -0
- package/dist/components/widgets/encoder/encoder-position-type.js +20 -0
- package/dist/components/widgets/encoder/encoder.svelte +3 -20
- package/dist/components/widgets/encoder/get-position-widget.svelte +54 -0
- package/dist/components/widgets/encoder/get-position-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/gantry/get-position-widget.svelte +43 -0
- package/dist/components/widgets/gantry/get-position-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/gantry/is-moving-widget.svelte +20 -0
- package/dist/components/widgets/gantry/is-moving-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/gripper/gripper.svelte +1 -1
- package/dist/components/widgets/gripper/is-holding-something.svelte +1 -4
- package/dist/components/widgets/gripper/is-moving-widget.svelte +20 -0
- package/dist/components/widgets/gripper/is-moving-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/motion/component-name-select.svelte +29 -0
- package/dist/components/widgets/motion/component-name-select.svelte.d.ts +9 -0
- package/dist/components/widgets/motion/frame-system-config.d.ts +28 -0
- package/dist/components/widgets/motion/frame-system-config.js +30 -0
- package/dist/components/widgets/motion/motion.svelte +27 -0
- package/dist/components/widgets/motion/motion.svelte.d.ts +7 -0
- package/dist/components/widgets/motion/move-widget.svelte +84 -0
- package/dist/components/widgets/motion/move-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/motion/move.svelte +127 -0
- package/dist/components/widgets/motion/move.svelte.d.ts +14 -0
- package/dist/components/widgets/motion/parse-move-args.d.ts +24 -0
- package/dist/components/widgets/motion/parse-move-args.js +23 -0
- package/dist/components/widgets/motion/parse-pasted-pose.d.ts +10 -0
- package/dist/components/widgets/motion/parse-pasted-pose.js +30 -0
- package/dist/components/widgets/motion/pose-in-frame-input.svelte +168 -0
- package/dist/components/widgets/motion/pose-in-frame-input.svelte.d.ts +10 -0
- package/dist/components/widgets/motor/is-moving-widget.svelte +20 -0
- package/dist/components/widgets/motor/is-moving-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/motor/motor.svelte +2 -8
- package/dist/components/widgets/movement-sensor/get-accuracy-widget.svelte +38 -0
- package/dist/components/widgets/movement-sensor/get-accuracy-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/movement-sensor/get-compass-heading-widget.svelte +38 -0
- package/dist/components/widgets/movement-sensor/get-compass-heading-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/movement-sensor/get-orientation-widget.svelte +38 -0
- package/dist/components/widgets/movement-sensor/get-orientation-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/movement-sensor/get-position-widget.svelte +38 -0
- package/dist/components/widgets/movement-sensor/get-position-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/navigation/obstacles/meshes.svelte +1 -2
- package/dist/components/widgets/power-sensor/get-current-widget.svelte +38 -0
- package/dist/components/widgets/power-sensor/get-current-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/power-sensor/get-power-widget.svelte +38 -0
- package/dist/components/widgets/power-sensor/get-power-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/power-sensor/get-voltage-widget.svelte +38 -0
- package/dist/components/widgets/power-sensor/get-voltage-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/servo/is-moving-widget.svelte +20 -0
- package/dist/components/widgets/servo/is-moving-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/slam/get-position-widget.svelte +38 -0
- package/dist/components/widgets/slam/get-position-widget.svelte.d.ts +7 -0
- package/dist/components/widgets/vision-service/vision-service.svelte +9 -10
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/is-known-resource.d.ts +3 -0
- package/dist/is-known-resource.js +5 -0
- package/dist/pip/context.svelte.d.ts +2 -0
- package/dist/pip/context.svelte.js +48 -20
- package/dist/registry.d.ts +28 -0
- package/dist/registry.js +176 -0
- package/dist/resource-triplet.d.ts +37 -0
- package/dist/resource-triplet.js +37 -0
- package/dist/resource-widget-types.d.ts +21 -0
- package/dist/resource-widget-types.js +1 -0
- package/dist/resource.d.ts +0 -9
- package/dist/resource.js +0 -72
- package/dist/show-resource-widget.d.ts +3 -0
- package/dist/show-resource-widget.js +10 -0
- package/package.json +23 -6
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The components the motion service can `Move` are exactly the frames in the
|
|
3
|
+
* machine's frame system config. `Move` builds a kinematic chain to a frame, so
|
|
4
|
+
* anything with a configured frame is a valid target.
|
|
5
|
+
*
|
|
6
|
+
* @param config - The machine's frame system config, or `undefined` while loading.
|
|
7
|
+
* @returns The non-empty reference frame names, sorted alphabetically.
|
|
8
|
+
*/
|
|
9
|
+
export const movableFrameNames = (config) => {
|
|
10
|
+
if (!config) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
return config
|
|
14
|
+
.map((entry) => entry.frame?.referenceFrame ?? '')
|
|
15
|
+
.filter((name) => name !== '')
|
|
16
|
+
.toSorted((a, b) => a.localeCompare(b));
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The parent frame a component is attached to in the machine's frame system.
|
|
20
|
+
* A frame's parent is the reference frame of the pose that positions it (its
|
|
21
|
+
* observer frame), so `getPose` and `Move` can be expressed relative to it.
|
|
22
|
+
*
|
|
23
|
+
* @param config - The machine's frame system config, or `undefined` while loading.
|
|
24
|
+
* @param componentName - The component (frame) whose parent to look up.
|
|
25
|
+
* @returns The parent frame name, or `'world'` when there is no configured parent.
|
|
26
|
+
*/
|
|
27
|
+
export const parentFrame = (config, componentName) => {
|
|
28
|
+
const entry = config?.find((item) => item.frame?.referenceFrame === componentName);
|
|
29
|
+
return entry?.frame?.poseInObserverFrame?.referenceFrame || 'world';
|
|
30
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ApiSection from '../../api-section.svelte'
|
|
3
|
+
import ConnectionStatus from '../../connection-status.svelte'
|
|
4
|
+
|
|
5
|
+
import MoveWidget from './move-widget.svelte'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
partID: string
|
|
9
|
+
resourceName: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { partID, resourceName }: Props = $props()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<ConnectionStatus {partID}>
|
|
16
|
+
{#snippet connected()}
|
|
17
|
+
<ApiSection
|
|
18
|
+
title="Move"
|
|
19
|
+
api="rdk:service:motion"
|
|
20
|
+
>
|
|
21
|
+
<MoveWidget
|
|
22
|
+
{partID}
|
|
23
|
+
{resourceName}
|
|
24
|
+
/>
|
|
25
|
+
</ApiSection>
|
|
26
|
+
{/snippet}
|
|
27
|
+
</ConnectionStatus>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { MotionClient, type RobotClient } from '@viamrobotics/sdk'
|
|
3
|
+
import {
|
|
4
|
+
createResourceClient,
|
|
5
|
+
createResourceMutation,
|
|
6
|
+
createRobotQuery,
|
|
7
|
+
useRobotClient,
|
|
8
|
+
} from '@viamrobotics/svelte-sdk'
|
|
9
|
+
|
|
10
|
+
import ComponentNameSelect from './component-name-select.svelte'
|
|
11
|
+
import { movableFrameNames, parentFrame } from './frame-system-config'
|
|
12
|
+
import Move from './move.svelte'
|
|
13
|
+
import { type MoveInput, parseMoveArgs } from './parse-move-args'
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
partID: string
|
|
17
|
+
resourceName: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { partID, resourceName }: Props = $props()
|
|
21
|
+
|
|
22
|
+
const robotClient = useRobotClient(() => partID)
|
|
23
|
+
const client = createResourceClient(
|
|
24
|
+
MotionClient,
|
|
25
|
+
() => partID,
|
|
26
|
+
() => resourceName
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const move = createResourceMutation(client, 'move')
|
|
30
|
+
const frameSystem = createRobotQuery(robotClient, 'frameSystemConfig', () => ({
|
|
31
|
+
refetchInterval: 5000,
|
|
32
|
+
}))
|
|
33
|
+
const frameNames = $derived(movableFrameNames(frameSystem.data))
|
|
34
|
+
|
|
35
|
+
let selectedName = $state<string>()
|
|
36
|
+
const componentName = $derived(selectedName ?? frameNames[0] ?? '')
|
|
37
|
+
|
|
38
|
+
const destinationFrame = $derived(parentFrame(frameSystem.data, componentName))
|
|
39
|
+
const poseArgs = $derived<Parameters<RobotClient['getPose']>>([
|
|
40
|
+
componentName,
|
|
41
|
+
destinationFrame,
|
|
42
|
+
[],
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
const poseQuery = createRobotQuery(
|
|
46
|
+
robotClient,
|
|
47
|
+
'getPose',
|
|
48
|
+
() => poseArgs,
|
|
49
|
+
() => ({ enabled: componentName !== '' })
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const currentPose = $derived(poseQuery.data?.pose)
|
|
53
|
+
const poseError = $derived(poseQuery.error instanceof Error ? poseQuery.error : null)
|
|
54
|
+
|
|
55
|
+
let parseError = $state<Error>()
|
|
56
|
+
|
|
57
|
+
const executeMove = (input: MoveInput) => {
|
|
58
|
+
try {
|
|
59
|
+
parseError = undefined
|
|
60
|
+
move.mutate(parseMoveArgs(componentName, input), {})
|
|
61
|
+
} catch (error) {
|
|
62
|
+
parseError = error instanceof Error ? error : new Error(String(error))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<div class="flex flex-col gap-4">
|
|
68
|
+
<ComponentNameSelect
|
|
69
|
+
value={componentName}
|
|
70
|
+
options={frameNames}
|
|
71
|
+
onChange={(value) => {
|
|
72
|
+
selectedName = value
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
<Move
|
|
76
|
+
{componentName}
|
|
77
|
+
{currentPose}
|
|
78
|
+
currentReferenceFrame={destinationFrame}
|
|
79
|
+
isPending={move.isPending}
|
|
80
|
+
lastError={parseError ?? move.error ?? poseError}
|
|
81
|
+
storageKey={`${partID}/${resourceName}/motion-move`}
|
|
82
|
+
onExecute={executeMove}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Pose } from '@viamrobotics/sdk'
|
|
3
|
+
|
|
4
|
+
import { Button, Progress } from '@viamrobotics/prime-core'
|
|
5
|
+
import { CodeEditor } from '@viamrobotics/prime-core/code-editor'
|
|
6
|
+
import { PersistedState } from 'runed'
|
|
7
|
+
|
|
8
|
+
import ErrorDisplay from '../../error.svelte'
|
|
9
|
+
|
|
10
|
+
import type { MoveInput } from './parse-move-args'
|
|
11
|
+
|
|
12
|
+
import PoseInFrameInput from './pose-in-frame-input.svelte'
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
componentName: string
|
|
16
|
+
currentPose?: Pose
|
|
17
|
+
currentReferenceFrame?: string
|
|
18
|
+
isPending: boolean
|
|
19
|
+
lastError: Error | null
|
|
20
|
+
storageKey: string
|
|
21
|
+
onExecute: (input: MoveInput) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
componentName,
|
|
26
|
+
currentPose,
|
|
27
|
+
currentReferenceFrame,
|
|
28
|
+
isPending,
|
|
29
|
+
lastError,
|
|
30
|
+
storageKey,
|
|
31
|
+
onExecute,
|
|
32
|
+
}: Props = $props()
|
|
33
|
+
|
|
34
|
+
const zeroPose: Pose = { x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 }
|
|
35
|
+
|
|
36
|
+
let edit = $state<{ component: string; referenceFrame: string; pose: Pose }>()
|
|
37
|
+
const isEdited = $derived(edit?.component === componentName)
|
|
38
|
+
const referenceFrame = $derived(
|
|
39
|
+
isEdited && edit ? edit.referenceFrame : (currentReferenceFrame ?? 'world')
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const pose = $derived(isEdited && edit ? edit.pose : (currentPose ?? zeroPose))
|
|
43
|
+
|
|
44
|
+
// PersistedState is created inside $derived so Svelte can track the storageKey
|
|
45
|
+
// dependency. The localStorage read on construction is benign and idempotent.
|
|
46
|
+
// storageKey is stable for the lifetime of this component.
|
|
47
|
+
const worldState = $derived(new PersistedState(`${storageKey}/world-state`, ''))
|
|
48
|
+
const constraints = $derived(new PersistedState(`${storageKey}/constraints`, ''))
|
|
49
|
+
|
|
50
|
+
const disabled = $derived(componentName === '' || isPending)
|
|
51
|
+
|
|
52
|
+
const execute = () => {
|
|
53
|
+
onExecute({
|
|
54
|
+
referenceFrame,
|
|
55
|
+
pose,
|
|
56
|
+
worldStateJson: worldState.current,
|
|
57
|
+
constraintsJson: constraints.current,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<div class="flex min-w-0 flex-col gap-4">
|
|
63
|
+
<PoseInFrameInput
|
|
64
|
+
{referenceFrame}
|
|
65
|
+
{pose}
|
|
66
|
+
onReferenceFrameChange={(frame) => {
|
|
67
|
+
edit = { component: componentName, referenceFrame: frame, pose }
|
|
68
|
+
}}
|
|
69
|
+
onPoseChange={(next) => {
|
|
70
|
+
edit = { component: componentName, referenceFrame, pose: next }
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
<div class="flex flex-col gap-1">
|
|
75
|
+
<span class="text-xs font-medium">
|
|
76
|
+
World state <abbr class="text-subtle-2">(optional JSON)</abbr>
|
|
77
|
+
</span>
|
|
78
|
+
<CodeEditor
|
|
79
|
+
label="World state"
|
|
80
|
+
language="json"
|
|
81
|
+
value={worldState.current}
|
|
82
|
+
onChange={(next: string) => {
|
|
83
|
+
worldState.current = next
|
|
84
|
+
}}
|
|
85
|
+
class="h-32 overflow-y-auto"
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div class="flex flex-col gap-1">
|
|
90
|
+
<span class="text-xs font-medium">
|
|
91
|
+
Constraints <abbr class="text-subtle-2">(optional JSON)</abbr>
|
|
92
|
+
</span>
|
|
93
|
+
<CodeEditor
|
|
94
|
+
label="Constraints"
|
|
95
|
+
language="json"
|
|
96
|
+
value={constraints.current}
|
|
97
|
+
onChange={(next: string) => {
|
|
98
|
+
constraints.current = next
|
|
99
|
+
}}
|
|
100
|
+
class="h-32 overflow-y-auto"
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="flex items-center gap-2">
|
|
105
|
+
<Button
|
|
106
|
+
class="w-fit"
|
|
107
|
+
icon="play-circle-outline"
|
|
108
|
+
variant="dark"
|
|
109
|
+
{disabled}
|
|
110
|
+
onclick={execute}
|
|
111
|
+
>
|
|
112
|
+
Execute
|
|
113
|
+
</Button>
|
|
114
|
+
{#if isPending}
|
|
115
|
+
<Progress
|
|
116
|
+
size="medium"
|
|
117
|
+
variant="dark"
|
|
118
|
+
/>
|
|
119
|
+
{/if}
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<p class="text-subtle-2 text-xs">
|
|
123
|
+
Move blocks until the motion completes. Stop the component itself to interrupt.
|
|
124
|
+
</p>
|
|
125
|
+
|
|
126
|
+
<ErrorDisplay {lastError} />
|
|
127
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Pose } from '@viamrobotics/sdk';
|
|
2
|
+
import type { MoveInput } from './parse-move-args';
|
|
3
|
+
interface Props {
|
|
4
|
+
componentName: string;
|
|
5
|
+
currentPose?: Pose;
|
|
6
|
+
currentReferenceFrame?: string;
|
|
7
|
+
isPending: boolean;
|
|
8
|
+
lastError: Error | null;
|
|
9
|
+
storageKey: string;
|
|
10
|
+
onExecute: (input: MoveInput) => void;
|
|
11
|
+
}
|
|
12
|
+
declare const Move: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type Move = ReturnType<typeof Move>;
|
|
14
|
+
export default Move;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Constraints, type Pose, type PoseInFrame, WorldState } from '@viamrobotics/sdk';
|
|
2
|
+
/** User-entered inputs for a motion `Move` call. Pose is always mm + degrees. */
|
|
3
|
+
export interface MoveInput {
|
|
4
|
+
referenceFrame: string;
|
|
5
|
+
pose: Pose;
|
|
6
|
+
/** Optional `WorldState` as proto JSON. Empty/whitespace means "omit". */
|
|
7
|
+
worldStateJson: string;
|
|
8
|
+
/** Optional `Constraints` as proto JSON. Empty/whitespace means "omit". */
|
|
9
|
+
constraintsJson: string;
|
|
10
|
+
}
|
|
11
|
+
/** Positional arguments for `MotionClient.move`, ready to spread into `mutate`. */
|
|
12
|
+
export type MoveArgs = [PoseInFrame, string, WorldState | undefined, Constraints | undefined];
|
|
13
|
+
/**
|
|
14
|
+
* Builds the positional arguments for `MotionClient.move` from form inputs.
|
|
15
|
+
*
|
|
16
|
+
* Empty or whitespace-only JSON fields are omitted (passed as `undefined`).
|
|
17
|
+
* Non-empty JSON is parsed with the generated message classes and throws on
|
|
18
|
+
* invalid input — callers should catch and surface the error.
|
|
19
|
+
*
|
|
20
|
+
* @param componentName - Name of the component to move.
|
|
21
|
+
* @param input - The pose, reference frame, and optional world-state/constraints JSON.
|
|
22
|
+
* @returns The `[destination, componentName, worldState?, constraints?]` tuple.
|
|
23
|
+
*/
|
|
24
|
+
export declare const parseMoveArgs: (componentName: string, input: MoveInput) => MoveArgs;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Constraints, WorldState } from '@viamrobotics/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Builds the positional arguments for `MotionClient.move` from form inputs.
|
|
4
|
+
*
|
|
5
|
+
* Empty or whitespace-only JSON fields are omitted (passed as `undefined`).
|
|
6
|
+
* Non-empty JSON is parsed with the generated message classes and throws on
|
|
7
|
+
* invalid input — callers should catch and surface the error.
|
|
8
|
+
*
|
|
9
|
+
* @param componentName - Name of the component to move.
|
|
10
|
+
* @param input - The pose, reference frame, and optional world-state/constraints JSON.
|
|
11
|
+
* @returns The `[destination, componentName, worldState?, constraints?]` tuple.
|
|
12
|
+
*/
|
|
13
|
+
export const parseMoveArgs = (componentName, input) => {
|
|
14
|
+
const destination = {
|
|
15
|
+
referenceFrame: input.referenceFrame,
|
|
16
|
+
pose: input.pose,
|
|
17
|
+
};
|
|
18
|
+
const worldState = input.worldStateJson.trim() === '' ? undefined : WorldState.fromJsonString(input.worldStateJson);
|
|
19
|
+
const constraints = input.constraintsJson.trim() === ''
|
|
20
|
+
? undefined
|
|
21
|
+
: Constraints.fromJsonString(input.constraintsJson);
|
|
22
|
+
return [destination, componentName, worldState, constraints];
|
|
23
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Pose } from '@viamrobotics/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Parses a clipboard string as a pose. Accepts a JSON object with numeric
|
|
4
|
+
* `x`, `y`, `z`, `oX`, `oY`, `oZ`, and `theta` fields — the same shape the arm
|
|
5
|
+
* `MoveToPosition` widget copies, so poses can be pasted between the two.
|
|
6
|
+
*
|
|
7
|
+
* @param data - The pasted string.
|
|
8
|
+
* @returns The parsed pose, or `undefined` if it is not a valid pose.
|
|
9
|
+
*/
|
|
10
|
+
export declare const parsePastedPose: (data: string) => Pose | undefined;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const poseKeys = ['x', 'y', 'z', 'oX', 'oY', 'oZ', 'theta'];
|
|
2
|
+
const isPose = (value) => {
|
|
3
|
+
if (typeof value !== 'object' || value === null) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
const record = value;
|
|
7
|
+
return poseKeys.every((key) => typeof record[key] === 'number');
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Parses a clipboard string as a pose. Accepts a JSON object with numeric
|
|
11
|
+
* `x`, `y`, `z`, `oX`, `oY`, `oZ`, and `theta` fields — the same shape the arm
|
|
12
|
+
* `MoveToPosition` widget copies, so poses can be pasted between the two.
|
|
13
|
+
*
|
|
14
|
+
* @param data - The pasted string.
|
|
15
|
+
* @returns The parsed pose, or `undefined` if it is not a valid pose.
|
|
16
|
+
*/
|
|
17
|
+
export const parsePastedPose = (data) => {
|
|
18
|
+
let parsed;
|
|
19
|
+
try {
|
|
20
|
+
parsed = JSON.parse(data);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
if (!isPose(parsed)) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const { x, y, z, oX, oY, oZ, theta } = parsed;
|
|
29
|
+
return { x, y, z, oX, oY, oZ, theta };
|
|
30
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<script
|
|
2
|
+
lang="ts"
|
|
3
|
+
module
|
|
4
|
+
>
|
|
5
|
+
import type { Pose } from '@viamrobotics/sdk'
|
|
6
|
+
|
|
7
|
+
const poseLabelsList = Object.entries({
|
|
8
|
+
x: 'X',
|
|
9
|
+
y: 'Y',
|
|
10
|
+
z: 'Z',
|
|
11
|
+
oX: 'OX',
|
|
12
|
+
oY: 'OY',
|
|
13
|
+
oZ: 'OZ',
|
|
14
|
+
theta: 'θ',
|
|
15
|
+
}) as [keyof Pose, string][]
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<script lang="ts">
|
|
19
|
+
import { Button, Icon, Input, Label, NumericInput, Tooltip } from '@viamrobotics/prime-core'
|
|
20
|
+
|
|
21
|
+
import AngleUnitToggle from '../../angle-unit-toggle.svelte'
|
|
22
|
+
import CopyButton from '../../copy-button.svelte'
|
|
23
|
+
import PasteButton from '../../paste-button.svelte'
|
|
24
|
+
import Table from '../../table.svelte'
|
|
25
|
+
import { numberValueFromEvent } from '../../../event-handlers'
|
|
26
|
+
import { degreesToRadians, formatNumeric, radiansToDegrees } from '../../../format'
|
|
27
|
+
|
|
28
|
+
import { parsePastedPose } from './parse-pasted-pose'
|
|
29
|
+
|
|
30
|
+
interface Props {
|
|
31
|
+
referenceFrame: string
|
|
32
|
+
pose: Pose
|
|
33
|
+
onReferenceFrameChange: (frame: string) => void
|
|
34
|
+
onPoseChange: (pose: Pose) => void
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { referenceFrame, pose, onReferenceFrameChange, onPoseChange }: Props = $props()
|
|
38
|
+
|
|
39
|
+
let useRadians = $state(false)
|
|
40
|
+
|
|
41
|
+
const displayPose = $derived({
|
|
42
|
+
...pose,
|
|
43
|
+
theta: useRadians ? degreesToRadians(pose.theta) : pose.theta,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const copyData = $derived(JSON.stringify(pose))
|
|
47
|
+
|
|
48
|
+
const handlePaste = (data: string): boolean => {
|
|
49
|
+
const parsed = parsePastedPose(data)
|
|
50
|
+
if (!parsed) {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
onPoseChange(parsed)
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const handleValueChange = (key: keyof Pose, inputValue: number) => {
|
|
58
|
+
const nextValue = key === 'theta' && useRadians ? radiansToDegrees(inputValue) : inputValue
|
|
59
|
+
onPoseChange({ ...pose, [key]: nextValue })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const zero = () => {
|
|
63
|
+
onPoseChange({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const poseUnits = $derived({
|
|
67
|
+
x: 'mm',
|
|
68
|
+
y: 'mm',
|
|
69
|
+
z: 'mm',
|
|
70
|
+
oX: '',
|
|
71
|
+
oY: '',
|
|
72
|
+
oZ: '',
|
|
73
|
+
theta: useRadians ? 'rad' : 'deg',
|
|
74
|
+
})
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<div class="flex min-w-0 flex-col gap-4">
|
|
78
|
+
<div class="flex items-center justify-between">
|
|
79
|
+
<span class="flex flex-row items-center gap-1 text-sm">
|
|
80
|
+
Destination pose
|
|
81
|
+
<Tooltip>
|
|
82
|
+
<Icon
|
|
83
|
+
name="information-outline"
|
|
84
|
+
cx="text-gray-6"
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<span slot="description">
|
|
88
|
+
The target pose expressed in the given reference frame. Translations are in millimeters.
|
|
89
|
+
</span>
|
|
90
|
+
</Tooltip>
|
|
91
|
+
</span>
|
|
92
|
+
<div class="flex gap-1">
|
|
93
|
+
<AngleUnitToggle
|
|
94
|
+
{useRadians}
|
|
95
|
+
onToggle={() => {
|
|
96
|
+
useRadians = !useRadians
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
<CopyButton data={copyData} />
|
|
100
|
+
<PasteButton onPaste={handlePaste} />
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<Label>
|
|
105
|
+
Reference frame
|
|
106
|
+
|
|
107
|
+
<Input
|
|
108
|
+
slot="input"
|
|
109
|
+
value={referenceFrame}
|
|
110
|
+
placeholder="world"
|
|
111
|
+
on:input={(event) => {
|
|
112
|
+
onReferenceFrameChange((event.target as HTMLInputElement).value)
|
|
113
|
+
}}
|
|
114
|
+
/>
|
|
115
|
+
</Label>
|
|
116
|
+
|
|
117
|
+
<Table>
|
|
118
|
+
<thead>
|
|
119
|
+
<tr>
|
|
120
|
+
<th>Pose</th>
|
|
121
|
+
<th>Value</th>
|
|
122
|
+
</tr>
|
|
123
|
+
</thead>
|
|
124
|
+
<tbody>
|
|
125
|
+
{#each poseLabelsList as labelList (labelList)}
|
|
126
|
+
{@const [key, label] = labelList}
|
|
127
|
+
{@const value = Number.parseFloat(formatNumeric(displayPose[key]))}
|
|
128
|
+
<tr>
|
|
129
|
+
<th>
|
|
130
|
+
<span class="relative inline-flex justify-center">
|
|
131
|
+
{label}
|
|
132
|
+
<abbr class="text-subtle-2 absolute left-full ml-1">{poseUnits[key]}</abbr>
|
|
133
|
+
</span>
|
|
134
|
+
</th>
|
|
135
|
+
<th>
|
|
136
|
+
<NumericInput
|
|
137
|
+
cx="max-w-[76px]"
|
|
138
|
+
{value}
|
|
139
|
+
on:change={(event) => {
|
|
140
|
+
handleValueChange(key, numberValueFromEvent(event) ?? 0)
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
</th>
|
|
144
|
+
</tr>
|
|
145
|
+
{/each}
|
|
146
|
+
</tbody>
|
|
147
|
+
</Table>
|
|
148
|
+
|
|
149
|
+
<div class="flex flex-col gap-2">
|
|
150
|
+
<span class="flex flex-row gap-2">
|
|
151
|
+
<h4 class="text-xs font-semibold">Quick set</h4>
|
|
152
|
+
<Tooltip>
|
|
153
|
+
<Icon
|
|
154
|
+
name="information-outline"
|
|
155
|
+
cx="text-gray-6"
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
<span slot="description"> Will update the pose values but will not execute </span>
|
|
159
|
+
</Tooltip>
|
|
160
|
+
</span>
|
|
161
|
+
<Button
|
|
162
|
+
class="w-fit"
|
|
163
|
+
onclick={zero}
|
|
164
|
+
>
|
|
165
|
+
Zero
|
|
166
|
+
</Button>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Pose } from '@viamrobotics/sdk';
|
|
2
|
+
interface Props {
|
|
3
|
+
referenceFrame: string;
|
|
4
|
+
pose: Pose;
|
|
5
|
+
onReferenceFrameChange: (frame: string) => void;
|
|
6
|
+
onPoseChange: (pose: Pose) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const PoseInFrameInput: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type PoseInFrameInput = ReturnType<typeof PoseInFrameInput>;
|
|
10
|
+
export default PoseInFrameInput;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { MotorClient } from '@viamrobotics/sdk'
|
|
3
|
+
|
|
4
|
+
import IsMoving from '../../is-moving.svelte'
|
|
5
|
+
import { ResourceTriplets } from '../../../resource-triplet'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
partID: string
|
|
9
|
+
resourceName: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { partID, resourceName }: Props = $props()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<IsMoving
|
|
16
|
+
client={MotorClient}
|
|
17
|
+
api={ResourceTriplets.Motor}
|
|
18
|
+
{partID}
|
|
19
|
+
{resourceName}
|
|
20
|
+
/>
|
|
@@ -148,10 +148,7 @@
|
|
|
148
148
|
Stepper motors will report ”true” if they are being powered while holding
|
|
149
149
|
a position and while they are turning."
|
|
150
150
|
>
|
|
151
|
-
<Query
|
|
152
|
-
contentCx="h-4"
|
|
153
|
-
query={isPoweredQuery}
|
|
154
|
-
>
|
|
151
|
+
<Query query={isPoweredQuery}>
|
|
155
152
|
{#if isPoweredQuery.data !== undefined}
|
|
156
153
|
{@const [isPowered, powerPct] = isPoweredQuery.data}
|
|
157
154
|
<span class="font-roboto-mono flex flex-row gap-1 text-xs">
|
|
@@ -171,10 +168,7 @@
|
|
|
171
168
|
class="gap-3 p-0"
|
|
172
169
|
tooltip="Reports the position of an encoded motor in revolutions from zero/home."
|
|
173
170
|
>
|
|
174
|
-
<Query
|
|
175
|
-
contentCx=""
|
|
176
|
-
query={positionQuery}
|
|
177
|
-
>
|
|
171
|
+
<Query query={positionQuery}>
|
|
178
172
|
<p class="font-roboto-mono text-default text-xs">
|
|
179
173
|
{formatNumeric(positionQuery.data, 4)}
|
|
180
174
|
</p>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { MovementSensorClient } from '@viamrobotics/sdk'
|
|
3
|
+
import { createResourceClient, createResourceQuery } from '@viamrobotics/svelte-sdk'
|
|
4
|
+
|
|
5
|
+
import ApiSection from '../../api-section.svelte'
|
|
6
|
+
import Query from '../../query.svelte'
|
|
7
|
+
|
|
8
|
+
import Accuracy from './accuracy.svelte'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
partID: string
|
|
12
|
+
resourceName: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { partID, resourceName }: Props = $props()
|
|
16
|
+
|
|
17
|
+
const client = createResourceClient(
|
|
18
|
+
MovementSensorClient,
|
|
19
|
+
() => partID,
|
|
20
|
+
() => resourceName
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const query = createResourceQuery(client, 'getAccuracy', { refetchInterval: 500 })
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<ApiSection
|
|
27
|
+
title="GetAccuracy"
|
|
28
|
+
class="grow"
|
|
29
|
+
>
|
|
30
|
+
<Query
|
|
31
|
+
{query}
|
|
32
|
+
contentCx="h-6"
|
|
33
|
+
>
|
|
34
|
+
{#if query.data !== undefined}
|
|
35
|
+
<Accuracy data={query.data} />
|
|
36
|
+
{/if}
|
|
37
|
+
</Query>
|
|
38
|
+
</ApiSection>
|