@wandelbots/wandelbots-js-react-components 2.54.4 → 2.54.5-pr.feat-upgrade-jogging-to-v2.404.8d3f7c5

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 (82) hide show
  1. package/README.md +1 -1
  2. package/dist/auth0-spa-js.production.esm-Bb4L9JDU.js +1423 -0
  3. package/dist/auth0-spa-js.production.esm-Bb4L9JDU.js.map +1 -0
  4. package/dist/auth0-spa-js.production.esm-CEI5Uihg.cjs +5 -0
  5. package/dist/auth0-spa-js.production.esm-CEI5Uihg.cjs.map +1 -0
  6. package/dist/components/3d-viewport/CoordinateSystemTransform.d.ts +1 -1
  7. package/dist/components/3d-viewport/CoordinateSystemTransform.d.ts.map +1 -1
  8. package/dist/components/3d-viewport/SafetyZonesRenderer.d.ts.map +1 -1
  9. package/dist/components/3d-viewport/collider/ColliderCollection.d.ts +1 -1
  10. package/dist/components/3d-viewport/collider/ColliderCollection.d.ts.map +1 -1
  11. package/dist/components/3d-viewport/collider/ColliderElement.d.ts +1 -1
  12. package/dist/components/3d-viewport/collider/ColliderElement.d.ts.map +1 -1
  13. package/dist/components/3d-viewport/collider/CollisionSceneRenderer.d.ts +1 -1
  14. package/dist/components/3d-viewport/collider/CollisionSceneRenderer.d.ts.map +1 -1
  15. package/dist/components/3d-viewport/collider/colliderShapeToBufferGeometry.d.ts +1 -1
  16. package/dist/components/3d-viewport/collider/colliderShapeToBufferGeometry.d.ts.map +1 -1
  17. package/dist/components/jogging/JoggingCartesianTab.d.ts.map +1 -1
  18. package/dist/components/jogging/JoggingJointTab.d.ts.map +1 -1
  19. package/dist/components/jogging/JoggingPanel.d.ts +1 -1
  20. package/dist/components/jogging/JoggingPanel.d.ts.map +1 -1
  21. package/dist/components/jogging/JoggingStore.d.ts +6 -5
  22. package/dist/components/jogging/JoggingStore.d.ts.map +1 -1
  23. package/dist/components/jogging/PoseCartesianValues.d.ts +2 -2
  24. package/dist/components/jogging/PoseCartesianValues.d.ts.map +1 -1
  25. package/dist/components/jogging/PoseJointValues.d.ts +1 -2
  26. package/dist/components/jogging/PoseJointValues.d.ts.map +1 -1
  27. package/dist/components/robots/RobotAnimator.d.ts +1 -1
  28. package/dist/components/robots/RobotAnimator.d.ts.map +1 -1
  29. package/dist/components/robots/SupportedRobot.d.ts +1 -1
  30. package/dist/components/robots/SupportedRobot.d.ts.map +1 -1
  31. package/dist/components/robots/manufacturerHomePositions.d.ts +1 -1
  32. package/dist/components/robots/manufacturerHomePositions.d.ts.map +1 -1
  33. package/dist/components/utils/errorHandling.test.d.ts +2 -0
  34. package/dist/components/utils/errorHandling.test.d.ts.map +1 -0
  35. package/dist/index.cjs +48 -50
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.js +14010 -18713
  38. package/dist/index.js.map +1 -1
  39. package/dist/lib/ConnectedMotionGroup.d.ts +84 -0
  40. package/dist/lib/ConnectedMotionGroup.d.ts.map +1 -0
  41. package/dist/lib/JoggerConnection.d.ts +112 -0
  42. package/dist/lib/JoggerConnection.d.ts.map +1 -0
  43. package/dist/lib/JoggerConnection.test.d.ts +2 -0
  44. package/dist/lib/JoggerConnection.test.d.ts.map +1 -0
  45. package/dist/lib/MotionStreamConnection.d.ts +24 -0
  46. package/dist/lib/MotionStreamConnection.d.ts.map +1 -0
  47. package/dist/lib/MotionStreamConnection.test.d.ts +2 -0
  48. package/dist/lib/MotionStreamConnection.test.d.ts.map +1 -0
  49. package/dist/lib/motionStateUpdate.d.ts +5 -0
  50. package/dist/lib/motionStateUpdate.d.ts.map +1 -0
  51. package/dist/lib/motionStateUpdate.test.d.ts +2 -0
  52. package/dist/lib/motionStateUpdate.test.d.ts.map +1 -0
  53. package/package.json +2 -2
  54. package/src/components/3d-viewport/CoordinateSystemTransform.tsx +1 -1
  55. package/src/components/3d-viewport/SafetyZonesRenderer.tsx +1 -2
  56. package/src/components/3d-viewport/collider/ColliderCollection.tsx +1 -1
  57. package/src/components/3d-viewport/collider/ColliderElement.tsx +1 -1
  58. package/src/components/3d-viewport/collider/CollisionSceneRenderer.tsx +1 -1
  59. package/src/components/3d-viewport/collider/colliderShapeToBufferGeometry.ts +1 -1
  60. package/src/components/jogging/JoggingCartesianTab.tsx +13 -11
  61. package/src/components/jogging/JoggingJointLimitDetector.tsx +2 -2
  62. package/src/components/jogging/JoggingJointTab.tsx +4 -3
  63. package/src/components/jogging/JoggingPanel.tsx +3 -2
  64. package/src/components/jogging/JoggingStore.ts +44 -39
  65. package/src/components/jogging/PoseCartesianValues.tsx +3 -4
  66. package/src/components/jogging/PoseJointValues.tsx +3 -4
  67. package/src/components/robots/DHRobot.tsx +1 -1
  68. package/src/components/robots/RobotAnimator.tsx +1 -1
  69. package/src/components/robots/SupportedRobot.tsx +1 -1
  70. package/src/components/robots/manufacturerHomePositions.ts +1 -1
  71. package/src/components/utils/errorHandling.test.ts +41 -0
  72. package/src/lib/ConnectedMotionGroup.ts +426 -0
  73. package/src/lib/JoggerConnection.test.ts +120 -0
  74. package/src/lib/JoggerConnection.ts +659 -0
  75. package/src/lib/MotionStreamConnection.test.ts +23 -0
  76. package/src/lib/MotionStreamConnection.ts +221 -0
  77. package/src/lib/motionStateUpdate.test.ts +28 -0
  78. package/src/lib/motionStateUpdate.ts +76 -0
  79. package/dist/auth0-spa-js.production.esm-1QXzndwB.js +0 -950
  80. package/dist/auth0-spa-js.production.esm-1QXzndwB.js.map +0 -1
  81. package/dist/auth0-spa-js.production.esm-BLRAk7Yh.cjs +0 -5
  82. package/dist/auth0-spa-js.production.esm-BLRAk7Yh.cjs.map +0 -1
@@ -0,0 +1,221 @@
1
+ import { AutoReconnectingWebsocket, tryParseJson } from "@wandelbots/nova-js"
2
+ import type {
3
+ MotionGroupDescription,
4
+ MotionGroupState,
5
+ NovaClient,
6
+ RobotControllerState,
7
+ } from "@wandelbots/nova-js/v2"
8
+ import { makeAutoObservable, runInAction } from "mobx"
9
+ import { Vector3 } from "three"
10
+ import type { Vector3Simple } from "./JoggerConnection"
11
+ import { jointValuesEqual, tcpMotionEqual } from "./motionStateUpdate"
12
+
13
+ const MOTION_DELTA_THRESHOLD = 0.0001
14
+
15
+ function unwrapRotationVector(
16
+ newRotationVectorApi: Vector3Simple,
17
+ currentRotationVectorApi: Vector3Simple,
18
+ ): Vector3Simple {
19
+ const currentRotationVector = new Vector3(
20
+ currentRotationVectorApi[0],
21
+ currentRotationVectorApi[1],
22
+ currentRotationVectorApi[2],
23
+ )
24
+
25
+ const newRotationVector = new Vector3(
26
+ newRotationVectorApi[0],
27
+ newRotationVectorApi[1],
28
+ newRotationVectorApi[2],
29
+ )
30
+
31
+ const currentAngle = currentRotationVector.length()
32
+ const currentAxis = currentRotationVector.normalize()
33
+
34
+ let newAngle = newRotationVector.length()
35
+ let newAxis = newRotationVector.normalize()
36
+
37
+ // Align rotation axes
38
+ if (newAxis.dot(currentAxis) < 0) {
39
+ newAngle = -newAngle
40
+ newAxis = newAxis.multiplyScalar(-1.0)
41
+ }
42
+
43
+ // Shift rotation angle close to previous one to extend domain of rotation angles beyond [0, pi]
44
+ // - this simplifies interpolation and prevents abruptly changing signs of the rotation angles
45
+ let angleDifference = newAngle - currentAngle
46
+ angleDifference -=
47
+ 2.0 * Math.PI * Math.floor((angleDifference + Math.PI) / (2.0 * Math.PI))
48
+
49
+ newAngle = currentAngle + angleDifference
50
+
51
+ return [...newAxis.multiplyScalar(newAngle)] as Vector3Simple
52
+ }
53
+
54
+ /**
55
+ * Store representing the current state of a connected motion group.
56
+ */
57
+ export class MotionStreamConnection {
58
+ static async open(nova: NovaClient, motionGroupId: string) {
59
+ const [_motionGroupIndex, controllerId] = motionGroupId.split("@") as [
60
+ string,
61
+ string,
62
+ ]
63
+
64
+ const controller =
65
+ await nova.api.controller.getCurrentRobotControllerState(controllerId)
66
+ const motionGroup = controller?.motion_groups.find(
67
+ (mg) => mg.motion_group === motionGroupId,
68
+ )
69
+ if (!controller || !motionGroup) {
70
+ throw new Error(
71
+ `Controller ${controllerId} or motion group ${motionGroupId} not found`,
72
+ )
73
+ }
74
+
75
+ const motionStateSocket = nova.openReconnectingWebsocket(
76
+ `/controllers/${controllerId}/motion-groups/${motionGroupId}/state-stream`,
77
+ )
78
+
79
+ // Wait for the first message to get the initial state
80
+ const firstMessage = await motionStateSocket.firstMessage()
81
+ const initialMotionState = tryParseJson(firstMessage.data)
82
+ ?.result as MotionGroupState
83
+
84
+ if (!initialMotionState) {
85
+ throw new Error(
86
+ `Unable to parse initial motion state message ${firstMessage.data}`,
87
+ )
88
+ }
89
+
90
+ console.log(
91
+ `Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:\n `,
92
+ initialMotionState,
93
+ )
94
+
95
+ // Get the motion group description for later usage in jogging
96
+ const description = await nova.api.motionGroup.getMotionGroupDescription(
97
+ controllerId,
98
+ motionGroup.motion_group,
99
+ )
100
+
101
+ return new MotionStreamConnection(
102
+ nova,
103
+ controller,
104
+ motionGroup,
105
+ description,
106
+ initialMotionState,
107
+ motionStateSocket,
108
+ )
109
+ }
110
+
111
+ // Not mobx-observable as this changes very fast; should be observed
112
+ // using animation frames
113
+ rapidlyChangingMotionState: MotionGroupState
114
+
115
+ constructor(
116
+ readonly nova: NovaClient,
117
+ readonly controller: RobotControllerState,
118
+ readonly motionGroup: MotionGroupState,
119
+ readonly description: MotionGroupDescription,
120
+ readonly initialMotionState: MotionGroupState,
121
+ readonly motionStateSocket: AutoReconnectingWebsocket,
122
+ ) {
123
+ this.rapidlyChangingMotionState = initialMotionState
124
+
125
+ motionStateSocket.addEventListener("message", (event) => {
126
+ const latestMotionState = tryParseJson(event.data)?.result as
127
+ | MotionGroupState
128
+ | undefined
129
+
130
+ if (!latestMotionState) {
131
+ throw new Error(
132
+ `Failed to get motion state for ${this.motionGroupId}: ${event.data}`,
133
+ )
134
+ }
135
+
136
+ // handle joint position changes
137
+ if (
138
+ !jointValuesEqual(
139
+ this.rapidlyChangingMotionState.joint_position,
140
+ latestMotionState.joint_position,
141
+ MOTION_DELTA_THRESHOLD,
142
+ )
143
+ ) {
144
+ runInAction(() => {
145
+ this.rapidlyChangingMotionState = latestMotionState
146
+ })
147
+ }
148
+
149
+ // handle tcp pose changes
150
+ if (
151
+ !tcpMotionEqual(
152
+ this.rapidlyChangingMotionState,
153
+ latestMotionState,
154
+ MOTION_DELTA_THRESHOLD,
155
+ )
156
+ ) {
157
+ runInAction(() => {
158
+ if (this.rapidlyChangingMotionState.tcp_pose == null) {
159
+ this.rapidlyChangingMotionState.tcp_pose =
160
+ latestMotionState.tcp_pose
161
+ } else if (
162
+ latestMotionState.tcp_pose?.orientation &&
163
+ latestMotionState.tcp_pose?.position &&
164
+ this.rapidlyChangingMotionState.tcp_pose?.orientation
165
+ ) {
166
+ this.rapidlyChangingMotionState.tcp_pose = {
167
+ position: latestMotionState.tcp_pose.position,
168
+ orientation: unwrapRotationVector(
169
+ latestMotionState.tcp_pose.orientation as Vector3Simple,
170
+ this.rapidlyChangingMotionState.tcp_pose
171
+ .orientation as Vector3Simple,
172
+ ),
173
+ }
174
+ } else {
175
+ console.warn(
176
+ "Received incomplete tcp_pose, ignoring",
177
+ latestMotionState.tcp_pose,
178
+ )
179
+ }
180
+ })
181
+ }
182
+
183
+ // handle standstill changes
184
+ if (
185
+ this.rapidlyChangingMotionState.standstill !==
186
+ latestMotionState.standstill
187
+ ) {
188
+ runInAction(() => {
189
+ this.rapidlyChangingMotionState.standstill =
190
+ latestMotionState.standstill
191
+ })
192
+ }
193
+ })
194
+ makeAutoObservable(this)
195
+ }
196
+
197
+ get motionGroupId() {
198
+ return this.motionGroup.motion_group
199
+ }
200
+
201
+ get controllerId() {
202
+ return this.controller.controller
203
+ }
204
+
205
+ get wandelscriptIdentifier() {
206
+ const num = this.motionGroupId.split("@")[0]
207
+ return `${this.controllerId.replace(/-/g, "_")}_${num}`
208
+ }
209
+
210
+ get joints() {
211
+ return this.initialMotionState.joint_position.map((_, i) => {
212
+ return {
213
+ index: i,
214
+ }
215
+ })
216
+ }
217
+
218
+ dispose() {
219
+ this.motionStateSocket.close()
220
+ }
221
+ }
@@ -0,0 +1,28 @@
1
+ import { describe, expect, test } from "vitest"
2
+ import { poseEqual } from "./motionStateUpdate"
3
+
4
+ describe("poseEqual", async () => {
5
+ test("returns true for identical poses", () => {
6
+ const pose1 = {
7
+ position: [0, 0, 0],
8
+ orientation: [0, 0, 0],
9
+ }
10
+ const pose2 = {
11
+ position: [0, 0, 0],
12
+ orientation: [0, 0, 0],
13
+ }
14
+ expect(poseEqual(pose1, pose2, 0.001)).toBe(true)
15
+ })
16
+
17
+ test("returns false for different poses", () => {
18
+ const pose1 = {
19
+ position: [0, 0, 0],
20
+ orientation: [0, 0, 0],
21
+ }
22
+ const pose2 = {
23
+ position: [1, 1, 1],
24
+ orientation: [1, 1, 1],
25
+ }
26
+ expect(poseEqual(pose1, pose2, 0.001)).toBe(false)
27
+ })
28
+ })
@@ -0,0 +1,76 @@
1
+ import type { MotionGroupState, Pose } from "@wandelbots/nova-js/v2"
2
+
3
+ export function jointValuesEqual(
4
+ oldJointValues: number[],
5
+ newJointValues: number[],
6
+ changeDeltaThreshold: number,
7
+ ): boolean {
8
+ if (newJointValues.length !== oldJointValues.length) {
9
+ return true
10
+ }
11
+
12
+ for (let jointIndex = 0; jointIndex < newJointValues.length; jointIndex++) {
13
+ if (
14
+ // biome-ignore lint/style/noNonNullAssertion: legacy code
15
+ Math.abs(newJointValues[jointIndex]! - oldJointValues[jointIndex]!) >
16
+ changeDeltaThreshold
17
+ ) {
18
+ return false
19
+ }
20
+ }
21
+
22
+ return true
23
+ }
24
+
25
+ export function poseEqual(
26
+ oldTcp: Pose | undefined,
27
+ newTcp: Pose | undefined,
28
+ changeDeltaThreshold: number,
29
+ ): boolean {
30
+ // undefined -> defined (+reverse) transition
31
+ if ((oldTcp === undefined && newTcp) || (oldTcp && newTcp === undefined)) {
32
+ return false
33
+ }
34
+
35
+ // Poses might be incomplete (missing orientation or position)
36
+ if (
37
+ oldTcp?.orientation === undefined ||
38
+ newTcp?.orientation === undefined ||
39
+ oldTcp?.position === undefined ||
40
+ newTcp?.position === undefined
41
+ ) {
42
+ return false
43
+ }
44
+
45
+ // the typechecker cannot resolve states to "!= undefined" if "&&" is used
46
+ if (oldTcp === undefined || newTcp === undefined) {
47
+ return true
48
+ }
49
+
50
+ let changedDelta = 0
51
+ changedDelta += Math.abs(oldTcp.orientation[0] - newTcp.orientation[0])
52
+ changedDelta += Math.abs(oldTcp.orientation[1] - newTcp.orientation[1])
53
+ changedDelta += Math.abs(oldTcp.orientation[2] - newTcp.orientation[2])
54
+ changedDelta += Math.abs(oldTcp.position[0] - newTcp.position[0])
55
+ changedDelta += Math.abs(oldTcp.position[1] - newTcp.position[1])
56
+ changedDelta += Math.abs(oldTcp.position[2] - newTcp.position[2])
57
+
58
+ return changedDelta <= changeDeltaThreshold
59
+ }
60
+
61
+ // Runs poseEqual check + coordinate system/tcp name equality
62
+ export function tcpMotionEqual(
63
+ oldMotionState: MotionGroupState,
64
+ newMotionState: MotionGroupState,
65
+ changeDeltaThreshold: number,
66
+ ): boolean {
67
+ return (
68
+ oldMotionState.coordinate_system === newMotionState.coordinate_system &&
69
+ oldMotionState.tcp === newMotionState.tcp &&
70
+ poseEqual(
71
+ oldMotionState.tcp_pose,
72
+ newMotionState.tcp_pose,
73
+ changeDeltaThreshold,
74
+ )
75
+ )
76
+ }