@wandelbots/wandelbots-js-react-components 1.3.1 → 1.3.2

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.
Files changed (77) hide show
  1. package/package.json +18 -7
  2. package/src/components/3d-viewport/CoordinateSystemTransform.tsx +44 -0
  3. package/src/components/3d-viewport/PresetEnvironment.tsx +78 -0
  4. package/src/components/3d-viewport/SafetyZonesRenderer.tsx +54 -0
  5. package/src/components/LoadingButton.stories.tsx +61 -0
  6. package/src/components/LoadingButton.tsx +19 -0
  7. package/src/components/LoadingCover.tsx +75 -0
  8. package/src/components/ThemeSelect.tsx +49 -0
  9. package/src/components/VelocitySlider.stories.tsx +32 -0
  10. package/src/components/VelocitySlider.tsx +52 -0
  11. package/src/components/jogging/JoggingCartesianAxisControl.stories.tsx +41 -0
  12. package/src/components/jogging/JoggingCartesianAxisControl.tsx +127 -0
  13. package/src/components/jogging/JoggingCartesianTab.tsx +265 -0
  14. package/src/components/jogging/JoggingCartesianValues.tsx +45 -0
  15. package/src/components/jogging/JoggingFreedriveTab.tsx +9 -0
  16. package/src/components/jogging/JoggingJointLimitDetector.tsx +51 -0
  17. package/src/components/jogging/JoggingJointRotationControl.stories.tsx +38 -0
  18. package/src/components/jogging/JoggingJointRotationControl.tsx +197 -0
  19. package/src/components/jogging/JoggingJointTab.tsx +93 -0
  20. package/src/components/jogging/JoggingJointValues.tsx +45 -0
  21. package/src/components/jogging/JoggingOptions.tsx +96 -0
  22. package/src/components/jogging/JoggingPanel.stories.tsx +26 -0
  23. package/src/components/jogging/JoggingPanel.tsx +148 -0
  24. package/src/components/jogging/JoggingStore.tsx +294 -0
  25. package/src/components/jogging/JoggingVelocitySlider.tsx +56 -0
  26. package/src/components/robots/ABB_1200_07_7.tsx +123 -0
  27. package/src/components/robots/AxisConfig.ts +3 -0
  28. package/src/components/robots/DHRobot.tsx +129 -0
  29. package/src/components/robots/FANUC_ARC_Mate_100iD.tsx +187 -0
  30. package/src/components/robots/FANUC_ARC_Mate_120iD.tsx +187 -0
  31. package/src/components/robots/FANUC_CRX10iA.tsx +167 -0
  32. package/src/components/robots/FANUC_CRX25iA.tsx +167 -0
  33. package/src/components/robots/FANUC_CRX25iAL.tsx +178 -0
  34. package/src/components/robots/KUKA_KR210_R2700.tsx +291 -0
  35. package/src/components/robots/KUKA_KR270_R2700.tsx +244 -0
  36. package/src/components/robots/RobotAnimator.tsx +83 -0
  37. package/src/components/robots/SupportedRobot.tsx +131 -0
  38. package/src/components/robots/UniversalRobots_UR10.tsx +112 -0
  39. package/src/components/robots/UniversalRobots_UR10e.tsx +275 -0
  40. package/src/components/robots/UniversalRobots_UR3.tsx +112 -0
  41. package/src/components/robots/UniversalRobots_UR3e.tsx +112 -0
  42. package/src/components/robots/UniversalRobots_UR5.tsx +111 -0
  43. package/src/components/robots/UniversalRobots_UR5e.tsx +280 -0
  44. package/src/components/robots/Yaskawa_AR1440.tsx +152 -0
  45. package/src/components/robots/Yaskawa_AR1730.tsx +165 -0
  46. package/src/components/robots/Yaskawa_AR2010.tsx +159 -0
  47. package/src/components/robots/Yaskawa_AR3120.tsx +160 -0
  48. package/src/components/robots/Yaskawa_AR900.tsx +121 -0
  49. package/src/components/utils/converters.ts +23 -0
  50. package/src/components/utils/errorHandling.ts +30 -0
  51. package/src/components/utils/hooks.tsx +54 -0
  52. package/src/components/utils/robotTreeQuery.ts +27 -0
  53. package/src/components/wandelscript-editor/WandelscriptEditor.stories.tsx +45 -0
  54. package/src/components/wandelscript-editor/WandelscriptEditor.tsx +114 -0
  55. package/src/components/wandelscript-editor/wandelscript.tmLanguage.ts +62 -0
  56. package/src/declarations.d.ts +10 -0
  57. package/src/i18n/config.ts +27 -0
  58. package/src/i18n/locales/de/translations.json +12 -0
  59. package/src/i18n/locales/en/translations.json +12 -0
  60. package/src/icons/arrowForwardFilled.tsx +7 -0
  61. package/src/icons/axis-x.svg +3 -0
  62. package/src/icons/axis-y.svg +3 -0
  63. package/src/icons/axis-z.svg +3 -0
  64. package/src/icons/expandFilled.tsx +11 -0
  65. package/src/icons/home.tsx +12 -0
  66. package/src/icons/index.ts +6 -0
  67. package/src/icons/infoOutlined.tsx +10 -0
  68. package/src/icons/jogging.svg +3 -0
  69. package/src/icons/robot.svg +3 -0
  70. package/src/icons/robot.tsx +14 -0
  71. package/src/icons/rotation.svg +4 -0
  72. package/src/icons/wbLogo.tsx +21 -0
  73. package/src/index.ts +7 -0
  74. package/src/themes/color.tsx +74 -0
  75. package/src/themes/theme.ts +150 -0
  76. package/src/themes/wbTheme.stories.tsx +64 -0
  77. package/src/themes/wbTheme.ts +186 -0
@@ -0,0 +1,265 @@
1
+ import {
2
+ ToggleButtonGroup,
3
+ ToggleButton,
4
+ Stack,
5
+ Typography,
6
+ } from "@mui/material"
7
+ import { observer } from "mobx-react-lite"
8
+ import { JoggingCartesianAxisControl } from "./JoggingCartesianAxisControl"
9
+ import { radiansToDegrees } from "@wandelbots/wandelbots-js"
10
+ import { useTranslation } from "react-i18next"
11
+ import RotationIcon from "../../icons/rotation.svg"
12
+ import XAxisIcon from "../../icons/axis-x.svg"
13
+ import YAxisIcon from "../../icons/axis-y.svg"
14
+ import ZAxisIcon from "../../icons/axis-z.svg"
15
+ import type { JoggingStore, DiscreteIncrementOption } from "./JoggingStore"
16
+ import { JoggingOptions } from "./JoggingOptions"
17
+ import { JoggingVelocitySlider } from "./JoggingVelocitySlider"
18
+ import { useReaction } from "../utils/hooks"
19
+ import { JoggingCartesianValues } from "./JoggingCartesianValues"
20
+ import { JoggingJointLimitDetector } from "./JoggingJointLimitDetector"
21
+
22
+ type JoggingCartesianOpts = {
23
+ axis: "x" | "y" | "z"
24
+ motionType: "translate" | "rotate"
25
+ direction: "-" | "+"
26
+ }
27
+
28
+ export const JoggingCartesianTab = observer(
29
+ ({ store }: { store: JoggingStore }) => {
30
+ const { t } = useTranslation()
31
+
32
+ function onMotionTypeChange(
33
+ _event: React.MouseEvent<HTMLElement>,
34
+ newMotionType: string,
35
+ ) {
36
+ if (newMotionType === "translate" || newMotionType === "rotate")
37
+ store.setSelectedCartesianMotionType(newMotionType)
38
+ }
39
+
40
+ useReaction(
41
+ () => [store.selectedCoordSystemId, store.selectedTcpId],
42
+ () => {
43
+ store.jogger.motionStream.motionStateSocket.changeUrl(
44
+ store.jogger.nova.makeWebsocketURL(`/motion-groups/${store.jogger.motionGroupId}/state-stream?tcp=${store.selectedTcpId}&response_coordinate_system=${store.selectedCoordSystemId}`)
45
+ )
46
+ },
47
+ { fireImmediately: true } as any,
48
+ )
49
+
50
+ async function runIncrementalCartesianJog(
51
+ opts: JoggingCartesianOpts,
52
+ increment: DiscreteIncrementOption,
53
+ ) {
54
+ const tcpPose = store.jogger.motionStream.rapidlyChangingMotionState.tcp_pose
55
+ const jointPosition =
56
+ store.jogger.motionStream.rapidlyChangingMotionState.state.joint_position
57
+ if (!tcpPose) return
58
+
59
+ // await robotPad.withMotionLock(async () => {
60
+ // await jogger.runIncrementalCartesianMotion({
61
+ // currentTcpPose: tcpPose,
62
+ // currentJoints: jointPosition,
63
+ // coordSystemId: store.selectedCoordSystemId,
64
+ // velocityInRelevantUnits: store.velocityInCurrentUnits,
65
+ // axis: opts.axis,
66
+ // direction: opts.direction,
67
+ // motion:
68
+ // store.selectedCartesianMotionType === "translate"
69
+ // ? {
70
+ // type: "translate",
71
+ // distanceMm: increment.mm,
72
+ // }
73
+ // : {
74
+ // type: "rotate",
75
+ // distanceRads: degreesToRadians(increment.degrees),
76
+ // },
77
+ // })
78
+ // })
79
+ }
80
+
81
+ async function startCartesianJogging(opts: JoggingCartesianOpts) {
82
+ if (store.isLocked) return
83
+
84
+ if (store.selectedDiscreteIncrement) {
85
+ return runIncrementalCartesianJog(opts, store.selectedDiscreteIncrement)
86
+ }
87
+
88
+ if (opts.motionType === "translate") {
89
+ await store.jogger.startTCPTranslation({
90
+ axis: opts.axis,
91
+ direction: opts.direction,
92
+ velocityMmPerSec: store.translationVelocityMmPerSec,
93
+ })
94
+ } else {
95
+ await store.jogger.startTCPRotation({
96
+ axis: opts.axis,
97
+ direction: opts.direction,
98
+ velocityRadsPerSec: store.rotationVelocityRadsPerSec,
99
+ })
100
+
101
+ }
102
+ }
103
+
104
+ async function stopJogging() {
105
+ if (store.isLocked) return
106
+
107
+ if (store.selectedDiscreteIncrement) {
108
+ return
109
+ }
110
+
111
+ await store.jogger.stop()
112
+ }
113
+
114
+ const axisList = [
115
+ {
116
+ id: "x",
117
+ color: "#F14D42",
118
+ icon: <XAxisIcon />,
119
+ },
120
+ {
121
+ id: "y",
122
+ color: "#42A705",
123
+ icon: <YAxisIcon />,
124
+ },
125
+ {
126
+ id: "z",
127
+ color: "#0075FF",
128
+ icon: <ZAxisIcon />,
129
+ },
130
+ ] as const
131
+
132
+ function formatMM(value: number) {
133
+ return t("General.mm.variable", { amount: value.toFixed(1) })
134
+ }
135
+
136
+ function formatDegrees(value: number) {
137
+ return t("General.degree.variable", {
138
+ amount: radiansToDegrees(value).toFixed(1),
139
+ })
140
+ }
141
+
142
+ return (
143
+ <Stack>
144
+ {/* Jogging options */}
145
+ <JoggingOptions store={store} />
146
+
147
+ {/* Show Wandelscript string for the current coords */}
148
+ <JoggingCartesianValues store={store} />
149
+
150
+ {/* Translate or rotate toggle */}
151
+ <Stack alignItems="center" marginTop="1rem">
152
+ <ToggleButtonGroup
153
+ value={store.selectedCartesianMotionType}
154
+ onChange={onMotionTypeChange}
155
+ exclusive
156
+ aria-label={t("Jogging.Cartesian.MotionType.lb")}
157
+ sx={{
158
+ "& > button": {
159
+ borderRadius: "8px",
160
+ textTransform: "none",
161
+ padding: "4px 30px",
162
+ },
163
+ }}
164
+ >
165
+ <ToggleButton value="translate">
166
+ {t("Jogging.Cartesian.Translation.bt")}
167
+ </ToggleButton>
168
+ <ToggleButton value="rotate">
169
+ {t("Jogging.Cartesian.Rotation.bt")}
170
+ </ToggleButton>
171
+ </ToggleButtonGroup>
172
+ </Stack>
173
+
174
+ <Stack width="80%" maxWidth="296px" margin="auto">
175
+ {/* Cartesian translate jogging */}
176
+ {store.selectedCartesianMotionType === "translate" &&
177
+ axisList.map((axis) => (
178
+ <JoggingCartesianAxisControl
179
+ key={axis.id}
180
+ color={axis.color}
181
+ disabled={store.isLocked}
182
+ sx={{
183
+ marginTop: "12px",
184
+ }}
185
+ label={
186
+ <>
187
+ {axis.icon}
188
+ <Typography
189
+ sx={{
190
+ fontSize: "24px",
191
+ color: "white",
192
+ }}
193
+ >
194
+ {axis.id.toUpperCase()}
195
+ </Typography>
196
+ </>
197
+ }
198
+ getDisplayedValue={() =>
199
+ formatMM(
200
+ store.jogger.motionStream.rapidlyChangingMotionState.tcp_pose?.position[
201
+ axis.id
202
+ ] || 0,
203
+ )
204
+ }
205
+ startJogging={(direction: "-" | "+") =>
206
+ startCartesianJogging({
207
+ axis: axis.id,
208
+ motionType: "translate",
209
+ direction,
210
+ })
211
+ }
212
+ stopJogging={stopJogging}
213
+ />
214
+ ))}
215
+
216
+ {/* Cartesian rotate jogging */}
217
+ {store.selectedCartesianMotionType === "rotate" &&
218
+ axisList.map((axis) => (
219
+ <JoggingCartesianAxisControl
220
+ key={axis.id}
221
+ color={axis.color}
222
+ disabled={store.isLocked}
223
+ sx={{
224
+ marginTop: "12px",
225
+ }}
226
+ label={
227
+ <>
228
+ <RotationIcon />
229
+ <Typography
230
+ sx={{
231
+ fontSize: "24px",
232
+ color: "white",
233
+ }}
234
+ >
235
+ {axis.id.toUpperCase()}
236
+ </Typography>
237
+ </>
238
+ }
239
+ getDisplayedValue={() =>
240
+ formatDegrees(
241
+ store.jogger.motionStream.rapidlyChangingMotionState.tcp_pose
242
+ ?.orientation?.[axis.id] || 0,
243
+ )
244
+ }
245
+ startJogging={(direction: "-" | "+") =>
246
+ startCartesianJogging({
247
+ axis: axis.id,
248
+ motionType: "rotate",
249
+ direction,
250
+ })
251
+ }
252
+ stopJogging={stopJogging}
253
+ />
254
+ ))}
255
+ </Stack>
256
+
257
+ {/* Velocity slider */}
258
+ <JoggingVelocitySlider store={store} />
259
+
260
+ {/* Show message if joint limits reached */}
261
+ <JoggingJointLimitDetector store={store} />
262
+ </Stack>
263
+ )
264
+ },
265
+ )
@@ -0,0 +1,45 @@
1
+ import { observer } from "mobx-react-lite"
2
+ import { Stack, Typography } from "@mui/material"
3
+ import { useRef } from "react"
4
+ import { poseToWandelscriptString } from "@wandelbots/wandelbots-js"
5
+ import { useAnimationFrame } from "../utils/hooks"
6
+ import { JoggingStore } from "./JoggingStore"
7
+
8
+ export const JoggingCartesianValues = observer(({ store }: { store: JoggingStore }) => {
9
+ const poseStringRef = useRef<HTMLPreElement>(null)
10
+
11
+ function getCurrentPoseString() {
12
+ const tcpPose = store.jogger.motionStream.rapidlyChangingMotionState.tcp_pose
13
+ if (!tcpPose) return ""
14
+ return poseToWandelscriptString(tcpPose)
15
+ }
16
+
17
+ useAnimationFrame(() => {
18
+ if (!poseStringRef.current) {
19
+ return
20
+ }
21
+
22
+ poseStringRef.current.textContent = getCurrentPoseString()
23
+ })
24
+
25
+ return (
26
+ <Stack alignItems="center" marginTop="0.8rem">
27
+ <Typography
28
+ sx={{
29
+ fontSize: "12px",
30
+ marginTop: "0.8rem",
31
+ }}
32
+ >
33
+ {store.selectedTcpId} - {store.selectedCoordSystem?.name}
34
+ </Typography>
35
+ <Typography
36
+ component="pre"
37
+ ref={poseStringRef}
38
+ sx={{
39
+ fontSize: "14px",
40
+ opacity: 0.6,
41
+ }}
42
+ ></Typography>
43
+ </Stack>
44
+ )
45
+ })
@@ -0,0 +1,9 @@
1
+ import { Stack } from "@mui/material"
2
+
3
+ export const JoggingFreedriveTab = () => {
4
+ return (
5
+ <Stack height={"300px"} alignItems={"center"} justifyContent={"center"}>
6
+ {"TODO: JoggingFreedriveTab"}
7
+ </Stack>
8
+ )
9
+ }
@@ -0,0 +1,51 @@
1
+ import { Typography } from "@mui/material"
2
+ import { useTranslation } from "react-i18next"
3
+ import { useState } from "react"
4
+ import { isEqual } from "lodash-es"
5
+ import { MotionStreamConnection } from "@wandelbots/wandelbots-js"
6
+ import { useAnimationFrame } from "../utils/hooks"
7
+ import { JoggingStore } from "./JoggingStore"
8
+
9
+ /**
10
+ * Monitors the active robot motion state and displays a message if
11
+ * any joint limits are reached.
12
+ */
13
+ export const JoggingJointLimitDetector = ({ store }: { store: JoggingStore }) => {
14
+ const { t } = useTranslation()
15
+
16
+ const [jointLimitsReached, setJointLimitsReached] = useState(
17
+ store.jogger.motionStream.rapidlyChangingMotionState.state.joint_limit_reached
18
+ .limit_reached,
19
+ )
20
+
21
+ useAnimationFrame(() => {
22
+ const newLimitsReached =
23
+ store.jogger.motionStream.rapidlyChangingMotionState.state.joint_limit_reached
24
+ .limit_reached
25
+
26
+ if (!isEqual(jointLimitsReached, newLimitsReached)) {
27
+ setJointLimitsReached(newLimitsReached)
28
+ }
29
+ })
30
+
31
+ const jointLimitReachedIndices: number[] = []
32
+ jointLimitsReached.forEach((limitReached, index) => {
33
+ if (limitReached) jointLimitReachedIndices.push(index)
34
+ })
35
+
36
+ if (!jointLimitReachedIndices.length) return null
37
+
38
+ return (
39
+ <Typography
40
+ color="error"
41
+ sx={{
42
+ padding: "1rem",
43
+ textAlign: "center",
44
+ }}
45
+ >
46
+ {t("Jogging.JointLimitsReached.lb", {
47
+ jointNumbers: jointLimitReachedIndices.map((i) => i + 1).join(", "),
48
+ })}
49
+ </Typography>
50
+ )
51
+ }
@@ -0,0 +1,38 @@
1
+ import { Meta, StoryObj } from "@storybook/react";
2
+ import { JoggingJointRotationControl } from "./JoggingJointRotationControl";
3
+ import { useRef } from "react";
4
+ import { useAnimationFrame } from "../utils/hooks";
5
+
6
+ const meta: Meta<typeof JoggingJointRotationControl> = {
7
+ component: JoggingJointRotationControl,
8
+
9
+ args: {
10
+ lowerLimitDegs: -360,
11
+ upperLimitDegs: 360,
12
+ disabled: false,
13
+ },
14
+ render: function Component(args) {
15
+ const joggingDirRef = useRef<"+" | "-" | null>(null);
16
+ const joggingValueRef = useRef(0);
17
+
18
+ useAnimationFrame(() => {
19
+ if (joggingDirRef.current === "+") {
20
+ joggingValueRef.current += 1;
21
+ } else if (joggingDirRef.current === "-") {
22
+ joggingValueRef.current -= 1;
23
+ }
24
+ })
25
+
26
+ return <JoggingJointRotationControl
27
+ {...args}
28
+ startJogging={(direction) => joggingDirRef.current = direction}
29
+ stopJogging={() => joggingDirRef.current = null}
30
+ getValueDegs={() => joggingValueRef.current}
31
+ />;
32
+ },
33
+
34
+ };
35
+ export default meta;
36
+
37
+ export const Default: StoryObj<typeof JoggingJointRotationControl> = {
38
+ };
@@ -0,0 +1,197 @@
1
+ import { IconButton, Slider, Typography } from "@mui/material"
2
+ import Stack from "@mui/material/Stack"
3
+ import { observer, useLocalObservable } from "mobx-react-lite"
4
+ import { I18nextProvider, useTranslation } from "react-i18next"
5
+ import { ChevronLeft, ChevronRight } from "@mui/icons-material"
6
+ import { useAnimationFrame } from "../utils/hooks"
7
+ import { useState } from "react"
8
+ import i18n from '../../i18n/config';
9
+
10
+ type JoggingJointRotationControlProps = {
11
+ startJogging: (direction: "-" | "+") => void
12
+ stopJogging: () => void
13
+ lowerLimitDegs?: number
14
+ upperLimitDegs?: number
15
+ getValueDegs: () => number|undefined
16
+
17
+ disabled?: boolean
18
+ } & React.ComponentProps<typeof Stack>
19
+
20
+ export const JoggingJointRotationControl = observer(
21
+ ({
22
+ startJogging,
23
+ stopJogging,
24
+ lowerLimitDegs,
25
+ upperLimitDegs,
26
+ getValueDegs,
27
+ disabled,
28
+ ...rest
29
+ }: JoggingJointRotationControlProps) => {
30
+ const { t } = useTranslation()
31
+ const [currentValue, setCurrentValue] = useState<number|undefined>()
32
+
33
+ const state = useLocalObservable(() => ({
34
+ activeJoggingDir: null as "-" | "+" | null,
35
+
36
+ startJogging(dir: "-" | "+") {
37
+ this.activeJoggingDir = dir
38
+ startJogging(dir)
39
+ },
40
+
41
+ stopJogging() {
42
+ this.activeJoggingDir = null
43
+ stopJogging()
44
+ },
45
+ }))
46
+
47
+ useAnimationFrame(() => {
48
+ setCurrentValue(getValueDegs())
49
+ })
50
+
51
+ function onPointerDownMinus(ev: React.PointerEvent) {
52
+ // Stop right click from triggering jog
53
+ if (ev.button === 0) state.startJogging("-")
54
+ }
55
+
56
+ function onPointerDownPlus(ev: React.PointerEvent) {
57
+ if (ev.button === 0) state.startJogging("+")
58
+ }
59
+
60
+ function formatDegrees(value: number | undefined, precision = 1) {
61
+ if (value === undefined || isNaN(value)) return ""
62
+
63
+ const output = t("General.degree.variable", {
64
+ amount: value.toFixed(precision),
65
+ })
66
+
67
+ if (value > 0 && precision === 0) {
68
+ return `+${output}`
69
+ } else {
70
+ return output
71
+ }
72
+ }
73
+
74
+ return (
75
+ <I18nextProvider i18n={i18n}>
76
+ <Stack
77
+ height="64px"
78
+ width="100%"
79
+ maxWidth="300px"
80
+ direction="row"
81
+ {...rest}
82
+ >
83
+ <IconButton
84
+ onPointerDown={onPointerDownMinus}
85
+ onPointerUp={state.stopJogging}
86
+ onPointerOut={state.stopJogging}
87
+ disabled={disabled}
88
+ sx={{
89
+ width: "52px",
90
+ color: "white",
91
+ alignContent: "center",
92
+ borderRadius: "16px 0px 0px 16px",
93
+ backgroundColor:
94
+ state.activeJoggingDir === "-" ? "#495975 !important" : "#38445A",
95
+ "& svg": {
96
+ width: "42px",
97
+ height: "42px",
98
+ },
99
+ }}
100
+ >
101
+ <ChevronLeft />
102
+ </IconButton>
103
+
104
+ <Stack
105
+ flexGrow={1}
106
+ alignItems="center"
107
+ justifyContent="center"
108
+ sx={{
109
+ borderStyle: "solid",
110
+ borderLeftWidth: 0,
111
+ borderRightWidth: 0,
112
+ borderTopWidth: "4px",
113
+ borderBottomWidth: "4px",
114
+ backgroundColor: "#38445A",
115
+ borderColor: "#38445A",
116
+ paddingLeft: "20px",
117
+ paddingRight: "20px",
118
+ zIndex: 1,
119
+ }}
120
+ >
121
+ <Typography
122
+ sx={{
123
+ fontSize: "15px",
124
+ position: "relative",
125
+ top: "5px",
126
+ }}
127
+ >
128
+ {formatDegrees(currentValue)}
129
+ </Typography>
130
+
131
+ <Slider
132
+ disabled
133
+ aria-label="Joint position"
134
+ min={lowerLimitDegs}
135
+ max={upperLimitDegs}
136
+ value={currentValue}
137
+ track={false}
138
+ sx={{
139
+ "& .MuiSlider-mark": {
140
+ display: "none",
141
+ },
142
+ "& .MuiSlider-thumb": {
143
+ width: "5px",
144
+ height: "10px",
145
+ borderRadius: "2px",
146
+ },
147
+ "& .MuiSlider-markLabel": {
148
+ top: "20px",
149
+ fontSize: "12px",
150
+ },
151
+ "& .MuiSlider-rail": {
152
+ backgroundColor: "#1F283A",
153
+ opacity: 1,
154
+ },
155
+ }}
156
+ marks={
157
+ lowerLimitDegs !== undefined &&
158
+ upperLimitDegs !== undefined && [
159
+ {
160
+ value: lowerLimitDegs,
161
+ label: formatDegrees(lowerLimitDegs, 0),
162
+ },
163
+ {
164
+ value: upperLimitDegs,
165
+ label: formatDegrees(upperLimitDegs, 0),
166
+ },
167
+ ]
168
+ }
169
+ />
170
+ </Stack>
171
+
172
+ <IconButton
173
+ onPointerDown={onPointerDownPlus}
174
+ onPointerUp={state.stopJogging}
175
+ onPointerOut={state.stopJogging}
176
+ disabled={disabled}
177
+ sx={{
178
+ width: "52px",
179
+ color: "white",
180
+ alignContent: "center",
181
+ fontSize: "37px",
182
+ borderRadius: "0px 16px 16px 0px",
183
+ backgroundColor:
184
+ state.activeJoggingDir === "+" ? "#495975 !important" : "#38445A",
185
+ "& svg": {
186
+ width: "42px",
187
+ height: "42px",
188
+ },
189
+ }}
190
+ >
191
+ <ChevronRight />
192
+ </IconButton>
193
+ </Stack>
194
+ </I18nextProvider>
195
+ )
196
+ },
197
+ )
@@ -0,0 +1,93 @@
1
+ import { Stack, Typography } from "@mui/material"
2
+ import { observer } from "mobx-react-lite"
3
+ import { radiansToDegrees } from "@wandelbots/wandelbots-js"
4
+ import type { JoggingStore } from "./JoggingStore"
5
+ import { JoggingVelocitySlider } from "./JoggingVelocitySlider"
6
+ import { JoggingJointRotationControl } from "./JoggingJointRotationControl"
7
+ import { JoggingJointValues } from "./JoggingJointValues"
8
+
9
+ export const JoggingJointTab = observer(
10
+ ({ store }: { store: JoggingStore; }) => {
11
+ async function startJointJogging(opts: {
12
+ joint: number
13
+ direction: "-" | "+"
14
+ }) {
15
+ await store.jogger.startJointRotation({
16
+ joint: opts.joint,
17
+ direction: opts.direction,
18
+ velocityRadsPerSec: store.rotationVelocityRadsPerSec,
19
+ })
20
+ }
21
+
22
+ async function stopJointJogging() {
23
+ await store.jogger.stop()
24
+ }
25
+
26
+ return (
27
+ <Stack>
28
+ <JoggingJointValues store={store} />
29
+ <Stack>
30
+ {store.jogger.motionStream.joints.map((joint) => {
31
+ const jointLimits =
32
+ store.motionGroupSpec.mechanical_joint_limits?.[
33
+ joint.index
34
+ ]
35
+ const lowerLimitDegs =
36
+ jointLimits?.lower_limit !== undefined
37
+ ? radiansToDegrees(jointLimits.lower_limit)
38
+ : undefined
39
+ const upperLimitDegs =
40
+ jointLimits?.upper_limit !== undefined
41
+ ? radiansToDegrees(jointLimits.upper_limit)
42
+ : undefined
43
+
44
+ return (
45
+ <Stack
46
+ direction="row"
47
+ alignItems="center"
48
+ gap={2}
49
+ key={`joint-${joint.index}`}
50
+ marginTop="0.8rem"
51
+ >
52
+ <Typography
53
+ sx={{
54
+ flexGrow: 1,
55
+ textAlign: "right",
56
+ }}
57
+ >{`J${joint.index + 1}`}</Typography>
58
+ <JoggingJointRotationControl
59
+ key={joint.index}
60
+ disabled={store.isLocked}
61
+ lowerLimitDegs={lowerLimitDegs}
62
+ upperLimitDegs={upperLimitDegs}
63
+ getValueDegs={() => {
64
+ const value =
65
+ store.jogger.motionStream.rapidlyChangingMotionState.state
66
+ .joint_position.joints[joint.index]
67
+ return value !== undefined
68
+ ? radiansToDegrees(value)
69
+ : undefined
70
+ }}
71
+ startJogging={(direction: "-" | "+") =>
72
+ startJointJogging({
73
+ joint: joint.index,
74
+ direction,
75
+ })
76
+ }
77
+ stopJogging={stopJointJogging}
78
+ />
79
+ {/* Just to balance out the right side */}
80
+ <Typography
81
+ sx={{
82
+ flexGrow: 1,
83
+ }}
84
+ />
85
+ </Stack>
86
+ )
87
+ })}
88
+ </Stack>
89
+ <JoggingVelocitySlider store={store} />
90
+ </Stack>
91
+ )
92
+ },
93
+ )