@wandelbots/wandelbots-js-react-components 3.5.1-pr.feat-add-eight-abbs-v2.450.4419c66 → 3.6.0-pr.feat-model-retrieval-from-rdp.463.3cb7d2e

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 (172) hide show
  1. package/README.md +79 -10
  2. package/dist/3d.cjs.js +2 -0
  3. package/dist/3d.cjs.js.map +1 -0
  4. package/dist/3d.d.ts +11 -0
  5. package/dist/3d.d.ts.map +1 -0
  6. package/dist/3d.es.js +16 -0
  7. package/dist/3d.es.js.map +1 -0
  8. package/dist/LoadingCover-6gWr11KP.js +88 -0
  9. package/dist/LoadingCover-6gWr11KP.js.map +1 -0
  10. package/dist/LoadingCover-CukpS_aj.cjs +2 -0
  11. package/dist/LoadingCover-CukpS_aj.cjs.map +1 -0
  12. package/dist/WandelscriptEditor-7eN-Yw7m.js +140 -0
  13. package/dist/WandelscriptEditor-7eN-Yw7m.js.map +1 -0
  14. package/dist/WandelscriptEditor-D6_vS5Uk.cjs +2 -0
  15. package/dist/WandelscriptEditor-D6_vS5Uk.cjs.map +1 -0
  16. package/dist/auth0-spa-js.production.esm-BMSlxZC5.js +3877 -0
  17. package/dist/auth0-spa-js.production.esm-BMSlxZC5.js.map +1 -0
  18. package/dist/auth0-spa-js.production.esm-DZ6lsBvD.cjs +5 -0
  19. package/dist/auth0-spa-js.production.esm-DZ6lsBvD.cjs.map +1 -0
  20. package/dist/components/3d-viewport/CoordinateSystemTransform.d.ts +1 -1
  21. package/dist/components/3d-viewport/CoordinateSystemTransform.d.ts.map +1 -1
  22. package/dist/components/3d-viewport/SafetyZonesRenderer.d.ts.map +1 -1
  23. package/dist/components/3d-viewport/collider/ColliderCollection.d.ts +1 -1
  24. package/dist/components/3d-viewport/collider/ColliderCollection.d.ts.map +1 -1
  25. package/dist/components/3d-viewport/collider/ColliderElement.d.ts +1 -1
  26. package/dist/components/3d-viewport/collider/ColliderElement.d.ts.map +1 -1
  27. package/dist/components/3d-viewport/collider/CollisionSceneRenderer.d.ts +1 -1
  28. package/dist/components/3d-viewport/collider/CollisionSceneRenderer.d.ts.map +1 -1
  29. package/dist/components/3d-viewport/collider/colliderShapeToBufferGeometry.d.ts +1 -1
  30. package/dist/components/3d-viewport/collider/colliderShapeToBufferGeometry.d.ts.map +1 -1
  31. package/dist/components/ProgramStateIndicator.d.ts +3 -3
  32. package/dist/components/ProgramStateIndicator.d.ts.map +1 -1
  33. package/dist/components/RobotCard.d.ts +5 -4
  34. package/dist/components/RobotCard.d.ts.map +1 -1
  35. package/dist/components/jogging/JoggingBlocked.d.ts +7 -0
  36. package/dist/components/jogging/JoggingBlocked.d.ts.map +1 -0
  37. package/dist/components/jogging/JoggingCartesianTab.d.ts.map +1 -1
  38. package/dist/components/jogging/JoggingOptions.d.ts.map +1 -1
  39. package/dist/components/jogging/JoggingPanel.d.ts +1 -1
  40. package/dist/components/jogging/JoggingPanel.d.ts.map +1 -1
  41. package/dist/components/jogging/JoggingStore.d.ts +10 -5
  42. package/dist/components/jogging/JoggingStore.d.ts.map +1 -1
  43. package/dist/components/jogging/PoseCartesianValues.d.ts +2 -2
  44. package/dist/components/jogging/PoseCartesianValues.d.ts.map +1 -1
  45. package/dist/components/jogging/PoseJointValues.d.ts +1 -2
  46. package/dist/components/jogging/PoseJointValues.d.ts.map +1 -1
  47. package/dist/components/robots/DHRobot.d.ts.map +1 -1
  48. package/dist/components/robots/GenericRobot.d.ts +2 -2
  49. package/dist/components/robots/GenericRobot.d.ts.map +1 -1
  50. package/dist/components/robots/Robot.d.ts +2 -2
  51. package/dist/components/robots/Robot.d.ts.map +1 -1
  52. package/dist/components/robots/RobotAnimator.d.ts +2 -2
  53. package/dist/components/robots/RobotAnimator.d.ts.map +1 -1
  54. package/dist/components/robots/SupportedRobot.d.ts +4 -4
  55. package/dist/components/robots/SupportedRobot.d.ts.map +1 -1
  56. package/dist/components/robots/manufacturerHomePositions.d.ts +1 -1
  57. package/dist/components/robots/manufacturerHomePositions.d.ts.map +1 -1
  58. package/dist/components/robots/robotModelLogic.d.ts +1 -1
  59. package/dist/components/robots/robotModelLogic.d.ts.map +1 -1
  60. package/dist/components/safetyBar/ControllerTypeIndicator.d.ts.map +1 -1
  61. package/dist/components/safetyBar/OperationModeIndicator.d.ts +2 -2
  62. package/dist/components/safetyBar/OperationModeIndicator.d.ts.map +1 -1
  63. package/dist/components/safetyBar/SafetyBar.d.ts +3 -3
  64. package/dist/components/safetyBar/SafetyBar.d.ts.map +1 -1
  65. package/dist/components/safetyBar/SafetyStateIndicator.d.ts +2 -2
  66. package/dist/components/safetyBar/SafetyStateIndicator.d.ts.map +1 -1
  67. package/dist/components/utils/errorHandling.d.ts.map +1 -1
  68. package/dist/components/utils/errorHandling.test.d.ts +2 -0
  69. package/dist/components/utils/errorHandling.test.d.ts.map +1 -0
  70. package/dist/core.cjs.js +2 -0
  71. package/dist/core.cjs.js.map +1 -0
  72. package/dist/core.d.ts +33 -0
  73. package/dist/core.d.ts.map +1 -0
  74. package/dist/core.es.js +54 -0
  75. package/dist/core.es.js.map +1 -0
  76. package/dist/externalizeComponent-CkVWk2F_.cjs +24 -0
  77. package/dist/externalizeComponent-CkVWk2F_.cjs.map +1 -0
  78. package/dist/externalizeComponent-Dc3fViZA.js +489 -0
  79. package/dist/externalizeComponent-Dc3fViZA.js.map +1 -0
  80. package/dist/index.cjs.js +2 -0
  81. package/dist/index.cjs.js.map +1 -0
  82. package/dist/index.d.ts +3 -39
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.es.js +69 -0
  85. package/dist/index.es.js.map +1 -0
  86. package/dist/interpolation-DAXKfoDS.cjs +20 -0
  87. package/dist/interpolation-DAXKfoDS.cjs.map +1 -0
  88. package/dist/interpolation-DcPbemBD.js +6924 -0
  89. package/dist/interpolation-DcPbemBD.js.map +1 -0
  90. package/dist/lib/ConnectedMotionGroup.d.ts +90 -0
  91. package/dist/lib/ConnectedMotionGroup.d.ts.map +1 -0
  92. package/dist/lib/JoggerConnection.d.ts +113 -0
  93. package/dist/lib/JoggerConnection.d.ts.map +1 -0
  94. package/dist/lib/JoggerConnection.test.d.ts +2 -0
  95. package/dist/lib/JoggerConnection.test.d.ts.map +1 -0
  96. package/dist/lib/MotionStreamConnection.d.ts +24 -0
  97. package/dist/lib/MotionStreamConnection.d.ts.map +1 -0
  98. package/dist/lib/MotionStreamConnection.test.d.ts +2 -0
  99. package/dist/lib/MotionStreamConnection.test.d.ts.map +1 -0
  100. package/dist/lib/motionStateUpdate.d.ts +7 -0
  101. package/dist/lib/motionStateUpdate.d.ts.map +1 -0
  102. package/dist/lib/motionStateUpdate.test.d.ts +2 -0
  103. package/dist/lib/motionStateUpdate.test.d.ts.map +1 -0
  104. package/dist/manufacturerHomePositions-BaS-GAxw.js +1025 -0
  105. package/dist/manufacturerHomePositions-BaS-GAxw.js.map +1 -0
  106. package/dist/manufacturerHomePositions-cD52vnEi.cjs +2 -0
  107. package/dist/manufacturerHomePositions-cD52vnEi.cjs.map +1 -0
  108. package/dist/theming-B7ojcJTM.js +22239 -0
  109. package/dist/theming-B7ojcJTM.js.map +1 -0
  110. package/dist/theming-C9apy9Ov.cjs +115 -0
  111. package/dist/theming-C9apy9Ov.cjs.map +1 -0
  112. package/dist/wandelscript.cjs.js +2 -0
  113. package/dist/wandelscript.cjs.js.map +1 -0
  114. package/dist/wandelscript.d.ts +2 -0
  115. package/dist/wandelscript.d.ts.map +1 -0
  116. package/dist/wandelscript.es.js +5 -0
  117. package/dist/wandelscript.es.js.map +1 -0
  118. package/package.json +68 -30
  119. package/src/3d.ts +15 -0
  120. package/src/components/3d-viewport/CoordinateSystemTransform.tsx +1 -1
  121. package/src/components/3d-viewport/SafetyZonesRenderer.tsx +1 -2
  122. package/src/components/3d-viewport/collider/ColliderCollection.tsx +1 -1
  123. package/src/components/3d-viewport/collider/ColliderElement.tsx +1 -1
  124. package/src/components/3d-viewport/collider/CollisionSceneRenderer.tsx +1 -1
  125. package/src/components/3d-viewport/collider/colliderShapeToBufferGeometry.ts +1 -1
  126. package/src/components/AppHeader.md +1 -1
  127. package/src/components/ProgramStateIndicator.tsx +3 -6
  128. package/src/components/RobotCard.tsx +5 -8
  129. package/src/components/jogging/JoggingBlocked.tsx +37 -0
  130. package/src/components/jogging/JoggingCartesianTab.tsx +13 -11
  131. package/src/components/jogging/JoggingJointLimitDetector.tsx +2 -2
  132. package/src/components/jogging/JoggingJointTab.tsx +4 -4
  133. package/src/components/jogging/JoggingOptions.tsx +6 -5
  134. package/src/components/jogging/JoggingPanel.tsx +6 -3
  135. package/src/components/jogging/JoggingStore.ts +66 -39
  136. package/src/components/jogging/PoseCartesianValues.tsx +3 -4
  137. package/src/components/jogging/PoseJointValues.tsx +3 -4
  138. package/src/components/robots/DHRobot.tsx +2 -3
  139. package/src/components/robots/GenericRobot.tsx +56 -13
  140. package/src/components/robots/Robot.tsx +2 -2
  141. package/src/components/robots/RobotAnimator.test.tsx +7 -22
  142. package/src/components/robots/RobotAnimator.tsx +8 -13
  143. package/src/components/robots/SupportedRobot.tsx +13 -8
  144. package/src/components/robots/manufacturerHomePositions.ts +1 -1
  145. package/src/components/robots/robotModelLogic.ts +40 -6
  146. package/src/components/safetyBar/ControllerTypeIndicator.tsx +4 -2
  147. package/src/components/safetyBar/OperationModeIndicator.tsx +7 -5
  148. package/src/components/safetyBar/SafetyBar.tsx +3 -6
  149. package/src/components/safetyBar/SafetyStateIndicator.tsx +9 -7
  150. package/src/components/utils/errorHandling.test.ts +41 -0
  151. package/src/components/utils/errorHandling.ts +4 -0
  152. package/src/core.ts +33 -0
  153. package/src/env.d.ts +3 -0
  154. package/src/i18n/locales/de/translations.json +3 -0
  155. package/src/i18n/locales/en/translations.json +3 -0
  156. package/src/index.ts +4 -43
  157. package/src/lib/ConnectedMotionGroup.ts +444 -0
  158. package/src/lib/JoggerConnection.test.ts +120 -0
  159. package/src/lib/JoggerConnection.ts +674 -0
  160. package/src/lib/MotionStreamConnection.test.ts +23 -0
  161. package/src/lib/MotionStreamConnection.ts +189 -0
  162. package/src/lib/motionStateUpdate.test.ts +28 -0
  163. package/src/lib/motionStateUpdate.ts +117 -0
  164. package/src/wandelscript.ts +2 -0
  165. package/dist/auth0-spa-js.production.esm-1QXzndwB.js +0 -950
  166. package/dist/auth0-spa-js.production.esm-1QXzndwB.js.map +0 -1
  167. package/dist/auth0-spa-js.production.esm-BLRAk7Yh.cjs +0 -5
  168. package/dist/auth0-spa-js.production.esm-BLRAk7Yh.cjs.map +0 -1
  169. package/dist/index.cjs +0 -155
  170. package/dist/index.cjs.map +0 -1
  171. package/dist/index.js +0 -34927
  172. package/dist/index.js.map +0 -1
@@ -1,14 +1,18 @@
1
1
  import { tryParseJson } from "@wandelbots/nova-js"
2
2
  import type {
3
3
  CoordinateSystem,
4
- JoggerConnection,
5
- MotionGroupSpecification,
4
+ MotionGroupDescription,
6
5
  RobotTcp,
7
- } from "@wandelbots/nova-js/v1"
6
+ } from "@wandelbots/nova-js/v2"
8
7
  import { countBy } from "lodash-es"
9
8
  import keyBy from "lodash-es/keyBy"
10
9
  import uniqueId from "lodash-es/uniqueId"
11
10
  import { autorun, makeAutoObservable, type IReactionDisposer } from "mobx"
11
+ import type {
12
+ JoggerConnection,
13
+ JoggerOrientation,
14
+ Vector3Simple,
15
+ } from "../../lib/JoggerConnection"
12
16
 
13
17
  const discreteIncrementOptions = [
14
18
  { id: "0.1", mm: 0.1, degrees: 0.05 },
@@ -55,6 +59,9 @@ export class JoggingStore {
55
59
  /** Locks to prevent UI interactions during certain operations */
56
60
  locks = new Set<string>()
57
61
 
62
+ /** Block jogging UI interactions when connection is taken by another jogger */
63
+ blocked: boolean = false
64
+
58
65
  /**
59
66
  * Id of selected coordinate system from among those defined on the API side
60
67
  */
@@ -105,7 +112,7 @@ export class JoggingStore {
105
112
  maxRotationVelocityDegPerSec: number = 60
106
113
 
107
114
  /** Whether to show the coordinate system select dropdown in the UI */
108
- showCoordSystemSelect: boolean = true
115
+ showCoordSystemSelect: boolean = false
109
116
 
110
117
  /** Whether to show the TCP select dropdown in the UI */
111
118
  showTcpSelect: boolean = true
@@ -138,34 +145,34 @@ export class JoggingStore {
138
145
  const { nova } = jogger
139
146
 
140
147
  // Find out what TCPs this motion group has (we need it for jogging)
141
- const [motionGroupSpec, { coordinatesystems }, { tcps }] =
142
- await Promise.all([
143
- nova.api.motionGroupInfos.getMotionGroupSpecification(
144
- jogger.motionGroupId,
145
- ),
146
-
147
- // Fetch coord systems so user can select between them
148
- nova.api.coordinateSystems.listCoordinateSystems("ROTATION_VECTOR"),
149
-
150
- // Same for TCPs
151
- nova.api.motionGroupInfos.listTcps(
152
- jogger.motionGroupId,
153
- "ROTATION_VECTOR",
154
- ),
155
- ])
156
-
157
- return new JoggingStore(
158
- jogger,
159
- motionGroupSpec,
160
- coordinatesystems || [],
161
- tcps || [],
162
- )
148
+ const [coordinatesystems, description] = await Promise.all([
149
+ // Fetch coord systems so user can select between them
150
+ nova.api.controller.listCoordinateSystems(
151
+ jogger.motionStream.controllerId,
152
+ "ROTATION_VECTOR",
153
+ ),
154
+
155
+ // Same for TCPs and other info from description
156
+ nova.api.motionGroup.getMotionGroupDescription(
157
+ jogger.motionStream.controllerId,
158
+ jogger.motionGroupId,
159
+ ),
160
+ ])
161
+
162
+ const tcps = Object.entries(description.tcps || {}).map(([id, tcp]) => ({
163
+ id,
164
+ readable_name: tcp.name,
165
+ position: tcp.pose.position as Vector3Simple,
166
+ orientation: tcp.pose.orientation as Vector3Simple,
167
+ }))
168
+
169
+ return new JoggingStore(jogger, coordinatesystems || [], description, tcps)
163
170
  }
164
171
 
165
172
  constructor(
166
173
  readonly jogger: JoggerConnection,
167
- readonly motionGroupSpec: MotionGroupSpecification,
168
174
  readonly coordSystems: CoordinateSystem[],
175
+ readonly motionGroupDescription: MotionGroupDescription,
169
176
  readonly tcps: RobotTcp[],
170
177
  ) {
171
178
  // TODO workaround for default coord system on backend having a canonical id
@@ -179,13 +186,21 @@ export class JoggingStore {
179
186
  this.selectedCoordSystemId = coordSystems[0]?.coordinate_system || "world"
180
187
  this.selectedTcpId = tcps[0]?.id || ""
181
188
 
189
+ // Make all properties observable and actions auto-bound
182
190
  makeAutoObservable(this, {}, { autoBind: true })
183
191
 
192
+ // Register blocked watching
193
+ this.jogger.onBlocked = () => {
194
+ this.block()
195
+ }
196
+
184
197
  // Load user settings from local storage if available
185
198
  this.loadFromLocalStorage()
186
199
 
187
200
  // Automatically save user settings to local storage when save changes
188
201
  this.disposers.push(autorun(() => this.saveToLocalStorage()))
202
+
203
+ // Assign joggingStore to window
189
204
  ;(window as any).joggingStore = this
190
205
  }
191
206
 
@@ -201,30 +216,31 @@ export class JoggingStore {
201
216
  }
202
217
 
203
218
  async deactivate() {
204
- const websocket = this.jogger.activeWebsocket
205
-
206
- this.jogger.setJoggingMode("increment")
207
-
208
- if (websocket) {
209
- await websocket.closed()
219
+ if (this.jogger.mode === "jogging") {
220
+ return this.jogger.stop()
210
221
  }
211
222
  }
212
223
 
213
224
  /** Activate the jogger with current settings */
214
225
  async activate() {
215
226
  if (this.currentTab.id === "cartesian") {
216
- const cartesianJoggingOpts = {
217
- tcpId: this.selectedTcpId,
218
- coordSystemId: this.activeCoordSystemId,
227
+ if (
228
+ this.jogger.tcp !== this.selectedTcpId ||
229
+ this.jogger.orientation !== this.selectedOrientation
230
+ ) {
231
+ this.jogger.setOptions({
232
+ tcp: this.selectedTcpId,
233
+ orientation: this.selectedOrientation as JoggerOrientation,
234
+ })
219
235
  }
220
236
 
221
237
  if (this.activeDiscreteIncrement) {
222
- this.jogger.setJoggingMode("increment", cartesianJoggingOpts)
238
+ this.jogger.setJoggingMode("trajectory")
223
239
  } else {
224
- this.jogger.setJoggingMode("cartesian", cartesianJoggingOpts)
240
+ this.jogger.setJoggingMode("jogging")
225
241
  }
226
242
  } else {
227
- this.jogger.setJoggingMode("joint")
243
+ this.jogger.setJoggingMode("jogging")
228
244
  }
229
245
 
230
246
  return this.jogger
@@ -433,6 +449,17 @@ export class JoggingStore {
433
449
  this.locks.delete(id)
434
450
  }
435
451
 
452
+ block() {
453
+ this.blocked = true
454
+ }
455
+
456
+ unblock() {
457
+ this.blocked = false
458
+ if (this.jogger.mode === "jogging") {
459
+ this.jogger.initializeJoggingWebsocket()
460
+ }
461
+ }
462
+
436
463
  /** Lock the UI until the given async callback resolves */
437
464
  async withMotionLock(fn: () => Promise<void>) {
438
465
  const lockId = uniqueId()
@@ -1,13 +1,12 @@
1
1
  import { Button, Stack } from "@mui/material"
2
- import { poseToWandelscriptString } from "@wandelbots/nova-js"
3
- import type { TcpPose } from "@wandelbots/nova-js/v1"
2
+ import { poseToWandelscriptString, type Pose } from "@wandelbots/nova-js/v2"
4
3
  import { observer } from "mobx-react-lite"
5
4
  import { useState } from "react"
6
5
  import { externalizeComponent } from "../../externalizeComponent"
7
6
  import { CopyableText } from "../CopyableText"
8
7
 
9
8
  export type PoseCartesianValuesProps = {
10
- tcpPose: TcpPose
9
+ tcpPose: Pose
11
10
  showCopyButton?: boolean
12
11
  }
13
12
 
@@ -44,7 +43,7 @@ export const PoseCartesianValues = externalizeComponent(
44
43
  onClick={handleCopy}
45
44
  sx={{ flexShrink: 0 }}
46
45
  >
47
- { copyMessage ? copyMessage : "Copy"}
46
+ {copyMessage ? copyMessage : "Copy"}
48
47
  </Button>
49
48
  )}
50
49
  </Stack>
@@ -1,19 +1,18 @@
1
1
  import { Button, Stack } from "@mui/material"
2
- import type { Joints } from "@wandelbots/nova-api/v1"
3
2
  import { observer } from "mobx-react-lite"
4
3
  import { useState } from "react"
5
4
  import { externalizeComponent } from "../../externalizeComponent"
6
5
  import { CopyableText } from "../CopyableText"
7
6
 
8
7
  export type PoseJointValuesProps = {
9
- joints: Joints
8
+ joints: Array<number>
10
9
  showCopyButton?: boolean
11
10
  }
12
11
 
13
12
  export const PoseJointValues = externalizeComponent(
14
13
  observer(({ joints, showCopyButton = false }: PoseJointValuesProps) => {
15
14
  const [copyMessage, setCopyMessage] = useState<string | null>(null)
16
- const poseString = `[${joints.joints.map((j: number) => parseFloat(j.toFixed(4))).join(", ")}]`
15
+ const poseString = `[${joints.map((j: number) => parseFloat(j.toFixed(4))).join(", ")}]`
17
16
 
18
17
  const handleCopy = async () => {
19
18
  try {
@@ -43,7 +42,7 @@ export const PoseJointValues = externalizeComponent(
43
42
  onClick={handleCopy}
44
43
  sx={{ flexShrink: 0 }}
45
44
  >
46
- { copyMessage ? copyMessage : "Copy"}
45
+ {copyMessage ? copyMessage : "Copy"}
47
46
  </Button>
48
47
  )}
49
48
  </Stack>
@@ -1,5 +1,5 @@
1
1
  import { Line } from "@react-three/drei"
2
- import type { DHParameter } from "@wandelbots/nova-api/v1"
2
+ import type { DHParameter } from "@wandelbots/nova-js/v2"
3
3
  import React, { useRef } from "react"
4
4
  import type * as THREE from "three"
5
5
  import { Matrix4, Quaternion, Vector3 } from "three"
@@ -113,8 +113,7 @@ export function DHRobot({
113
113
  {dhParameters!.map((param, index) => {
114
114
  const { a, b } = getLinePoints(
115
115
  param,
116
- rapidlyChangingMotionState.state.joint_position.joints[index] ??
117
- 0,
116
+ rapidlyChangingMotionState.joint_position[index] ?? 0,
118
117
  )
119
118
  const jointName = `dhrobot_J0${index}`
120
119
  return (
@@ -1,12 +1,12 @@
1
1
  import { useGLTF } from "@react-three/drei"
2
2
  import type { ThreeElements } from "@react-three/fiber"
3
- import React, { useCallback } from "react"
3
+ import React, { useCallback, useEffect, useState } from "react"
4
4
  import type { Group, Mesh } from "three"
5
5
  import { type Object3D } from "three"
6
6
  import { isFlange, parseRobotModel } from "./robotModelLogic"
7
7
 
8
8
  export type RobotModelProps = {
9
- modelURL: string
9
+ modelURL: string | Promise<string>
10
10
  /**
11
11
  * Called after a robot model has been loaded and
12
12
  * rendered into the threejs scene
@@ -19,16 +19,19 @@ function isMesh(node: Object3D): node is Mesh {
19
19
  return node.type === "Mesh"
20
20
  }
21
21
 
22
- export function GenericRobot({
23
- modelURL,
24
- flangeRef,
25
- postModelRender,
26
- ...props
27
- }: RobotModelProps) {
28
- const { gltf } = parseRobotModel(
29
- useGLTF(modelURL),
30
- modelURL.split("/").pop() || modelURL,
31
- )
22
+ // Separate component that only renders when we have a valid URL
23
+ function LoadedRobotModel({
24
+ url,
25
+ flangeRef,
26
+ postModelRender,
27
+ ...props
28
+ }: {
29
+ url: string
30
+ flangeRef?: React.Ref<Group>
31
+ postModelRender?: () => void
32
+ } & ThreeElements["group"]) {
33
+ const gltfResult = useGLTF(url)
34
+ const { gltf } = parseRobotModel(gltfResult, 'robot.glb')
32
35
 
33
36
  const groupRef: React.RefCallback<Group> = useCallback(
34
37
  (group) => {
@@ -36,7 +39,7 @@ export function GenericRobot({
36
39
  postModelRender()
37
40
  }
38
41
  },
39
- [modelURL],
42
+ [postModelRender],
40
43
  )
41
44
 
42
45
  function renderNode(node: Object3D): React.ReactNode {
@@ -72,3 +75,43 @@ export function GenericRobot({
72
75
  </group>
73
76
  )
74
77
  }
78
+
79
+ export function GenericRobot({
80
+ modelURL,
81
+ flangeRef,
82
+ postModelRender,
83
+ ...props
84
+ }: RobotModelProps) {
85
+ const [resolvedURL, setResolvedURL] = useState<string | null>(null)
86
+
87
+ useEffect(() => {
88
+ const resolveURL = async () => {
89
+ try {
90
+ if (typeof modelURL === 'string') {
91
+ setResolvedURL(modelURL)
92
+ } else {
93
+ const url = await modelURL
94
+ setResolvedURL(url)
95
+ }
96
+ } catch (error) {
97
+ console.error('Failed to resolve model URL:', error)
98
+ }
99
+ }
100
+
101
+ resolveURL()
102
+ }, [modelURL])
103
+
104
+ // Don't render until we have a resolved URL
105
+ if (!resolvedURL) {
106
+ return null // Loading state
107
+ }
108
+
109
+ return (
110
+ <LoadedRobotModel
111
+ url={resolvedURL}
112
+ flangeRef={flangeRef}
113
+ postModelRender={postModelRender}
114
+ {...props}
115
+ />
116
+ )
117
+ }
@@ -1,13 +1,13 @@
1
1
  import type { ThreeElements } from "@react-three/fiber"
2
2
 
3
- import type { ConnectedMotionGroup } from "@wandelbots/nova-js/v1"
4
3
  import type { Group } from "three"
4
+ import type { ConnectedMotionGroup } from "../../lib/ConnectedMotionGroup"
5
5
  import { defaultGetModel } from "./robotModelLogic"
6
6
  import { SupportedRobot } from "./SupportedRobot"
7
7
 
8
8
  export type RobotProps = {
9
9
  connectedMotionGroup: ConnectedMotionGroup
10
- getModel?: (modelFromController: string) => string
10
+ getModel?: (modelFromController: string) => Promise<string>
11
11
  flangeRef?: React.Ref<Group>
12
12
  transparentColor?: string
13
13
  postModelRender?: () => void
@@ -1,7 +1,4 @@
1
- import type {
2
- DHParameter,
3
- MotionGroupStateResponse,
4
- } from "@wandelbots/nova-api/v1"
1
+ import type { DHParameter, MotionGroupState } from "@wandelbots/nova-api/v2"
5
2
  import { describe, expect, it, vi } from "vitest"
6
3
  import RobotAnimator from "./RobotAnimator"
7
4
 
@@ -26,12 +23,8 @@ describe("RobotAnimator", () => {
26
23
 
27
24
  it("should handle props with different numbers of joints", () => {
28
25
  // Test that the component accepts different numbers of DH parameters
29
- const mockMotionState4Joints: MotionGroupStateResponse = {
30
- state: {
31
- joint_position: {
32
- joints: [0.1, 0.2, 0.3, 0.4],
33
- },
34
- },
26
+ const mockMotionState4Joints: MotionGroupState = {
27
+ joint_position: [0.1, 0.2, 0.3, 0.4],
35
28
  } as any
36
29
 
37
30
  const mockDHParameters4Joints: DHParameter[] = [
@@ -41,12 +34,8 @@ describe("RobotAnimator", () => {
41
34
  { theta: 0, reverse_rotation_direction: false },
42
35
  ]
43
36
 
44
- const mockMotionState7Joints: MotionGroupStateResponse = {
45
- state: {
46
- joint_position: {
47
- joints: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7],
48
- },
49
- },
37
+ const mockMotionState7Joints: MotionGroupState = {
38
+ joint_position: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7],
50
39
  } as any
51
40
 
52
41
  const mockDHParameters7Joints: DHParameter[] = Array(7).fill({
@@ -63,9 +52,7 @@ describe("RobotAnimator", () => {
63
52
  }
64
53
  // Verify props are correctly typed
65
54
  expect(props4.dhParameters).toHaveLength(4)
66
- expect(
67
- props4.rapidlyChangingMotionState.state.joint_position.joints,
68
- ).toHaveLength(4)
55
+ expect(props4.rapidlyChangingMotionState.joint_position).toHaveLength(4)
69
56
  }).not.toThrow()
70
57
 
71
58
  expect(() => {
@@ -75,9 +62,7 @@ describe("RobotAnimator", () => {
75
62
  children: null,
76
63
  }
77
64
  expect(props7.dhParameters).toHaveLength(7)
78
- expect(
79
- props7.rapidlyChangingMotionState.state.joint_position.joints,
80
- ).toHaveLength(7)
65
+ expect(props7.rapidlyChangingMotionState.joint_position).toHaveLength(7)
81
66
  }).not.toThrow()
82
67
  })
83
68
 
@@ -1,8 +1,5 @@
1
1
  import { useFrame, useThree } from "@react-three/fiber"
2
- import type {
3
- DHParameter,
4
- MotionGroupStateResponse,
5
- } from "@wandelbots/nova-api/v1"
2
+ import type { DHParameter, MotionGroupState } from "@wandelbots/nova-js/v2"
6
3
  import React, { useEffect, useRef } from "react"
7
4
  import type { Group, Object3D } from "three"
8
5
  import { useAutorun } from "../utils/hooks"
@@ -10,7 +7,7 @@ import { ValueInterpolator } from "../utils/interpolation"
10
7
  import { collectJoints } from "./robotModelLogic"
11
8
 
12
9
  type RobotAnimatorProps = {
13
- rapidlyChangingMotionState: MotionGroupStateResponse
10
+ rapidlyChangingMotionState: MotionGroupState
14
11
  dhParameters: DHParameter[]
15
12
  onRotationChanged?: (joints: Object3D[], jointValues: number[]) => void
16
13
  children: React.ReactNode
@@ -29,10 +26,9 @@ export default function RobotAnimator({
29
26
 
30
27
  // Initialize interpolator
31
28
  useEffect(() => {
32
- const initialJointValues =
33
- rapidlyChangingMotionState.state.joint_position.joints.filter(
34
- (item) => item !== undefined,
35
- )
29
+ const initialJointValues = rapidlyChangingMotionState.joint_position.filter(
30
+ (item) => item !== undefined,
31
+ )
36
32
 
37
33
  interpolatorRef.current = new ValueInterpolator(initialJointValues, {
38
34
  tension: 120, // Controls spring stiffness - higher values create faster, more responsive motion
@@ -91,10 +87,9 @@ export default function RobotAnimator({
91
87
  }
92
88
 
93
89
  useAutorun(() => {
94
- const newJointValues =
95
- rapidlyChangingMotionState.state.joint_position.joints.filter(
96
- (item) => item !== undefined,
97
- )
90
+ const newJointValues = rapidlyChangingMotionState.joint_position.filter(
91
+ (item) => item !== undefined,
92
+ )
98
93
 
99
94
  requestAnimationFrame(() => updateJoints(newJointValues))
100
95
  })
@@ -1,8 +1,5 @@
1
1
  import type { ThreeElements } from "@react-three/fiber"
2
- import type {
3
- DHParameter,
4
- MotionGroupStateResponse,
5
- } from "@wandelbots/nova-api/v1"
2
+ import type { DHParameter, MotionGroupState } from "@wandelbots/nova-js/v2"
6
3
  import { Suspense, useCallback, useEffect, useState } from "react"
7
4
  import { DHRobot } from "./DHRobot"
8
5
 
@@ -16,16 +13,16 @@ import { applyGhostStyle, removeGhostStyle } from "./ghostStyle"
16
13
  import { defaultGetModel } from "./robotModelLogic"
17
14
 
18
15
  export type DHRobotProps = {
19
- rapidlyChangingMotionState: MotionGroupStateResponse
16
+ rapidlyChangingMotionState: MotionGroupState
20
17
  dhParameters: Array<DHParameter>
21
18
  } & ThreeElements["group"]
22
19
 
23
20
  export type SupportedRobotProps = {
24
- rapidlyChangingMotionState: MotionGroupStateResponse
21
+ rapidlyChangingMotionState: MotionGroupState
25
22
  modelFromController: string
26
23
  dhParameters: DHParameter[]
27
24
  flangeRef?: React.Ref<THREE.Group>
28
- getModel?: (modelFromController: string) => string
25
+ getModel?: (modelFromController: string) => Promise<string> | undefined
29
26
  postModelRender?: () => void
30
27
  transparentColor?: string
31
28
  } & ThreeElements["group"]
@@ -80,7 +77,15 @@ export const SupportedRobot = externalizeComponent(
80
77
  dhParameters={dhParameters}
81
78
  >
82
79
  <GenericRobot
83
- modelURL={getModel(modelFromController)}
80
+ modelURL={(() => {
81
+ const result = getModel(modelFromController)
82
+ if (!result) {
83
+ const mockBlob = new Blob([], { type: 'model/gltf-binary' })
84
+ const mockFile = new File([mockBlob], `${modelFromController}.glb`, { type: 'model/gltf-binary' })
85
+ return Promise.resolve(URL.createObjectURL(mockFile))
86
+ }
87
+ return result
88
+ })()}
84
89
  postModelRender={postModelRender}
85
90
  flangeRef={flangeRef}
86
91
  {...props}
@@ -1,4 +1,4 @@
1
- import { Manufacturer } from "@wandelbots/nova-api/v1"
1
+ import { Manufacturer } from "@wandelbots/nova-js/v2"
2
2
 
3
3
  /**
4
4
  * Default home configs for different robot manufacturers.
@@ -1,13 +1,47 @@
1
+ import { NovaClient } from "@wandelbots/nova-js/v2"
1
2
  import type { Object3D } from "three"
2
3
  import type { GLTF } from "three-stdlib"
3
- import { version } from "../../../package.json"
4
4
 
5
- export function defaultGetModel(modelFromController: string): string {
6
- let useVersion = version
7
- if (version.startsWith("0.")) {
8
- useVersion = ""
5
+ const modelCache = new Map<string, Promise<string>>()
6
+
7
+ export async function defaultGetModel(modelFromController: string): Promise<string> {
8
+ // Check cache first
9
+ if (modelCache.has(modelFromController)) {
10
+ return modelCache.get(modelFromController)!
9
11
  }
10
- return `https://cdn.jsdelivr.net/gh/wandelbotsgmbh/wandelbots-js-react-components${useVersion ? `@${useVersion}` : ""}/public/models/${modelFromController}.glb`
12
+
13
+ // Create the promise and cache it immediately to prevent duplicate calls
14
+ const modelPromise = (async () => {
15
+ const instanceUrl = import.meta.env.WANDELAPI_BASE_URL || import.meta.env.VITE_NOVA_INSTANCE_URL
16
+
17
+ const nova = new NovaClient({ instanceUrl })
18
+
19
+ // Configure axios to handle binary responses for GLB files
20
+ const apiInstance = nova.api.motionGroupModels as any
21
+ if (apiInstance.axios?.interceptors) {
22
+ apiInstance.axios.interceptors.request.use((config: any) => {
23
+ if (config.url?.includes('/glb')) {
24
+ config.responseType = 'blob'
25
+ }
26
+ return config
27
+ })
28
+ }
29
+
30
+ try {
31
+ const file = await nova.api.motionGroupModels.getMotionGroupGlbModel(modelFromController)
32
+
33
+ // Create object URL from the file and return it
34
+ const url = URL.createObjectURL(file)
35
+ return url
36
+ } catch (error) {
37
+ console.error("Failed to fetch model:", error)
38
+ throw error
39
+ }
40
+ })()
41
+
42
+ // Cache the promise
43
+ modelCache.set(modelFromController, modelPromise)
44
+ return modelPromise
11
45
  }
12
46
 
13
47
  /**
@@ -1,5 +1,6 @@
1
1
  import { useTheme, type PopoverOrigin } from "@mui/material"
2
2
  import { observer } from "mobx-react-lite"
3
+ import { useId } from "react"
3
4
  import { Trans, useTranslation } from "react-i18next"
4
5
  import ControllerTypePhysicalIcon from "./icons/controller-type-physical.svg"
5
6
  import ControllerTypeVirtualIcon from "./icons/controller-type-virtual.svg"
@@ -22,12 +23,13 @@ export const ControllerTypeIndicator = observer(
22
23
  compact,
23
24
  }: ControllerTypeIndicatorProps) => {
24
25
  const theme = useTheme()
26
+ const componentId = useId()
25
27
  const { t } = useTranslation()
26
28
 
27
29
  if (isVirtual) {
28
30
  return (
29
31
  <IndicatorWithExplanation
30
- id="motion-group-virtual"
32
+ id={`motion-group-virtual-${componentId}`}
31
33
  icon={ControllerTypeVirtualIcon}
32
34
  color={theme.palette.tertiary.main}
33
35
  name={t("SafetyBar.ControllerType.Virtual.lb")}
@@ -52,7 +54,7 @@ export const ControllerTypeIndicator = observer(
52
54
 
53
55
  return (
54
56
  <IndicatorWithExplanation
55
- id="motion-group-physical"
57
+ id={`motion-group-physical-${componentId}`}
56
58
  icon={ControllerTypePhysicalIcon}
57
59
  color={theme.palette.primary.main}
58
60
  name={t("SafetyBar.ControllerType.Physical.lb")}
@@ -1,6 +1,7 @@
1
1
  import { useTheme, type PopoverOrigin } from "@mui/material"
2
- import type { RobotControllerStateOperationModeEnum } from "@wandelbots/nova-js/v1"
2
+ import type { OperationMode } from "@wandelbots/nova-js/v2"
3
3
  import { observer } from "mobx-react-lite"
4
+ import { useId } from "react"
4
5
  import { Trans, useTranslation } from "react-i18next"
5
6
  import OperationModeAutomaticIcon from "./icons/operation-mode-automatic.svg"
6
7
  import OperationModeErrorIcon from "./icons/operation-mode-error.svg"
@@ -8,7 +9,7 @@ import OperationModeManualIcon from "./icons/operation-mode-manual.svg"
8
9
  import { IndicatorWithExplanation } from "./IndicatorWithExplanation"
9
10
 
10
11
  interface OperationModeIndicatorProps {
11
- operationMode: RobotControllerStateOperationModeEnum
12
+ operationMode: OperationMode
12
13
  anchorOrigin?: PopoverOrigin
13
14
  transformOrigin?: PopoverOrigin
14
15
  compact: boolean
@@ -23,12 +24,13 @@ export const OperationModeIndicator = observer(
23
24
  }: OperationModeIndicatorProps) => {
24
25
  const { t } = useTranslation()
25
26
  const theme = useTheme()
27
+ const componentId = useId()
26
28
 
27
29
  switch (operationMode) {
28
30
  case "OPERATION_MODE_AUTO":
29
31
  return (
30
32
  <IndicatorWithExplanation
31
- id="operation-mode-auto"
33
+ id={`operation-mode-auto-${componentId}`}
32
34
  icon={OperationModeAutomaticIcon}
33
35
  title={t("SafetyBar.OperationMode.ti")}
34
36
  name={t("SafetyBar.OperationMode.Automatic.ti")}
@@ -49,7 +51,7 @@ export const OperationModeIndicator = observer(
49
51
  case "OPERATION_MODE_MANUAL_T2": {
50
52
  return (
51
53
  <IndicatorWithExplanation
52
- id="operation-mode-manual"
54
+ id={`operation-mode-manual-${componentId}`}
53
55
  icon={OperationModeManualIcon}
54
56
  color={theme.palette.warning.main}
55
57
  title={t("SafetyBar.OperationMode.ti")}
@@ -71,7 +73,7 @@ export const OperationModeIndicator = observer(
71
73
  default:
72
74
  return (
73
75
  <IndicatorWithExplanation
74
- id="operation-mode-error"
76
+ id={`operation-mode-error-${componentId}`}
75
77
  icon={OperationModeErrorIcon}
76
78
  color={theme.palette.warning.main}
77
79
  title={t("SafetyBar.OperationMode.ti")}