@wandelbots/nova-js 3.2.0-pr.dev-e2e-jogging-test.143.4f02caf → 3.2.0-pr.feat-v2.155.e91b019

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 (66) hide show
  1. package/README.md +60 -50
  2. package/dist/{AutoReconnectingWebsocket-BI1ckzP8.d.ts → AutoReconnectingWebsocket-CoU4ZyD2.d.cts} +1 -3
  3. package/dist/AutoReconnectingWebsocket-CoU4ZyD2.d.cts.map +1 -0
  4. package/dist/{AutoReconnectingWebsocket-Cr7f9016.d.cts → AutoReconnectingWebsocket-D0gTrkzu.d.ts} +1 -3
  5. package/dist/AutoReconnectingWebsocket-D0gTrkzu.d.ts.map +1 -0
  6. package/dist/{LoginWithAuth0-0g0wWRUC.js → LoginWithAuth0-CaX7yo7d.js} +58 -5
  7. package/dist/LoginWithAuth0-CaX7yo7d.js.map +1 -0
  8. package/dist/{LoginWithAuth0-C82OCyDy.cjs → LoginWithAuth0-DaPnTz2I.cjs} +99 -4
  9. package/dist/LoginWithAuth0-DaPnTz2I.cjs.map +1 -0
  10. package/dist/index.cjs +11 -10
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +11 -8
  13. package/dist/index.d.cts.map +1 -1
  14. package/dist/index.d.ts +11 -8
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +3 -3
  17. package/dist/lib/v1/index.cjs +17 -19
  18. package/dist/lib/v1/index.cjs.map +1 -1
  19. package/dist/lib/v1/index.d.cts +4 -2
  20. package/dist/lib/v1/index.d.cts.map +1 -1
  21. package/dist/lib/v1/index.d.ts +4 -2
  22. package/dist/lib/v1/index.d.ts.map +1 -1
  23. package/dist/lib/v1/index.js +7 -10
  24. package/dist/lib/v1/index.js.map +1 -1
  25. package/dist/lib/v2/index.cjs +1087 -11
  26. package/dist/lib/v2/index.cjs.map +1 -1
  27. package/dist/lib/v2/index.d.cts +256 -7
  28. package/dist/lib/v2/index.d.cts.map +1 -1
  29. package/dist/lib/v2/index.d.ts +256 -7
  30. package/dist/lib/v2/index.d.ts.map +1 -1
  31. package/dist/lib/v2/index.js +1082 -12
  32. package/dist/lib/v2/index.js.map +1 -1
  33. package/dist/wandelscriptUtils-CO5GYRij.js +24 -0
  34. package/dist/wandelscriptUtils-CO5GYRij.js.map +1 -0
  35. package/dist/wandelscriptUtils-COHpTIme.d.cts +12 -0
  36. package/dist/wandelscriptUtils-COHpTIme.d.cts.map +1 -0
  37. package/dist/wandelscriptUtils-Cl3GBxOp.d.ts +12 -0
  38. package/dist/wandelscriptUtils-Cl3GBxOp.d.ts.map +1 -0
  39. package/dist/wandelscriptUtils-DwpJ4jCy.cjs +30 -0
  40. package/dist/wandelscriptUtils-DwpJ4jCy.cjs.map +1 -0
  41. package/package.json +2 -2
  42. package/src/LoginWithAuth0.ts +3 -3
  43. package/src/index.ts +1 -0
  44. package/src/lib/converters.ts +5 -23
  45. package/src/lib/v1/MotionStreamConnection.ts +1 -1
  46. package/src/lib/v1/NovaClient.ts +6 -0
  47. package/src/lib/v1/index.ts +6 -0
  48. package/src/lib/v1/mock/MockNovaInstance.ts +0 -1
  49. package/src/lib/v1/wandelscriptUtils.ts +22 -0
  50. package/src/lib/v2/ConnectedMotionGroup.ts +415 -0
  51. package/src/lib/v2/JoggerConnection.ts +647 -0
  52. package/src/lib/v2/MotionStreamConnection.ts +222 -0
  53. package/src/lib/v2/NovaClient.ts +43 -8
  54. package/src/lib/v2/index.ts +5 -0
  55. package/src/lib/v2/mock/MockNovaInstance.ts +385 -1
  56. package/src/lib/v2/motionStateUpdate.ts +76 -0
  57. package/src/lib/v2/types/vector3.ts +1 -0
  58. package/src/lib/v2/wandelscriptUtils.ts +27 -0
  59. package/dist/AutoReconnectingWebsocket-BI1ckzP8.d.ts.map +0 -1
  60. package/dist/AutoReconnectingWebsocket-Cr7f9016.d.cts.map +0 -1
  61. package/dist/LoginWithAuth0-0g0wWRUC.js.map +0 -1
  62. package/dist/LoginWithAuth0-C82OCyDy.cjs.map +0 -1
  63. package/dist/converters-DP2EIVv6.cjs +0 -108
  64. package/dist/converters-DP2EIVv6.cjs.map +0 -1
  65. package/dist/converters-DY6Lf7mb.js +0 -66
  66. package/dist/converters-DY6Lf7mb.js.map +0 -1
@@ -1,11 +1,242 @@
1
- import { AutoReconnectingWebsocket, availableStorage, loginWithAuth0 } from "../../LoginWithAuth0-0g0wWRUC.js";
1
+ import { AutoReconnectingWebsocket, XYZ_TO_VECTOR, availableStorage, loginWithAuth0, tryParseJson } from "../../LoginWithAuth0-CaX7yo7d.js";
2
2
  import axios, { AxiosError, isAxiosError } from "axios";
3
+ import { makeAutoObservable, runInAction, when } from "mobx";
4
+ import * as THREE from "three";
5
+ import { Vector3 } from "three";
6
+ import { Vector3 as Vector3$1 } from "three/src/math/Vector3.js";
3
7
  import urlJoin from "url-join";
4
8
  import * as pathToRegexp from "path-to-regexp";
5
9
  import { ApplicationApi, BUSInputsOutputsApi, CellApi, ControllerApi, ControllerInputsOutputsApi, JoggingApi, KinematicsApi, MotionGroupApi, MotionGroupModelsApi, StoreCollisionComponentsApi, StoreCollisionSetupsApi, StoreObjectApi, SystemApi, TrajectoryCachingApi, TrajectoryExecutionApi, TrajectoryPlanningApi, VirtualControllerApi, VirtualControllerBehaviorApi, VirtualControllerInputsOutputsApi } from "@wandelbots/nova-api/v2";
6
10
 
7
11
  export * from "@wandelbots/nova-api/v2"
8
12
 
13
+ //#region src/lib/v2/motionStateUpdate.ts
14
+ function jointValuesEqual(oldJointValues, newJointValues, changeDeltaThreshold) {
15
+ if (newJointValues.length !== oldJointValues.length) return true;
16
+ for (let jointIndex = 0; jointIndex < newJointValues.length; jointIndex++) if (Math.abs(newJointValues[jointIndex] - oldJointValues[jointIndex]) > changeDeltaThreshold) return false;
17
+ return true;
18
+ }
19
+ function poseEqual(oldTcp, newTcp, changeDeltaThreshold) {
20
+ if (oldTcp === void 0 && newTcp || oldTcp && newTcp === void 0) return false;
21
+ if (oldTcp?.orientation === void 0 || newTcp?.orientation === void 0 || oldTcp?.position === void 0 || newTcp?.position === void 0) return false;
22
+ if (oldTcp === void 0 || newTcp === void 0) return true;
23
+ let changedDelta = 0;
24
+ changedDelta += Math.abs(oldTcp.orientation[0] - newTcp.orientation[0]);
25
+ changedDelta += Math.abs(oldTcp.orientation[1] - newTcp.orientation[1]);
26
+ changedDelta += Math.abs(oldTcp.orientation[2] - newTcp.orientation[2]);
27
+ changedDelta += Math.abs(oldTcp.position[0] - newTcp.position[0]);
28
+ changedDelta += Math.abs(oldTcp.position[1] - newTcp.position[1]);
29
+ changedDelta += Math.abs(oldTcp.position[2] - newTcp.position[2]);
30
+ return changedDelta <= changeDeltaThreshold;
31
+ }
32
+ function tcpMotionEqual(oldMotionState, newMotionState, changeDeltaThreshold) {
33
+ return oldMotionState.coordinate_system === newMotionState.coordinate_system && oldMotionState.tcp === newMotionState.tcp && poseEqual(oldMotionState.tcp_pose, newMotionState.tcp_pose, changeDeltaThreshold);
34
+ }
35
+
36
+ //#endregion
37
+ //#region src/lib/v2/ConnectedMotionGroup.ts
38
+ const MOTION_DELTA_THRESHOLD$1 = 1e-4;
39
+ /**
40
+ * Store representing the current state of a connected motion group.
41
+ */
42
+ var ConnectedMotionGroup = class ConnectedMotionGroup {
43
+ static async connect(nova, motionGroupId) {
44
+ const [_motionGroupIndex, controllerId] = motionGroupId.split("@");
45
+ const controller = await nova.api.controller.getCurrentRobotControllerState(controllerId);
46
+ const motionGroup = controller?.motion_groups.find((mg) => mg.motion_group === motionGroupId);
47
+ if (!controller || !motionGroup) throw new Error(`Controller ${controllerId} or motion group ${motionGroupId} not found`);
48
+ const motionStateSocket = nova.openReconnectingWebsocket(`/controllers/${controllerId}/motion-groups/${motionGroupId}/state-stream`);
49
+ const firstMessage = await motionStateSocket.firstMessage();
50
+ const initialMotionState = tryParseJson(firstMessage.data)?.result;
51
+ if (!initialMotionState) throw new Error(`Unable to parse initial motion state message ${firstMessage.data}`);
52
+ console.log(`Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:\n `, initialMotionState);
53
+ const isVirtual = (await nova.api.controller.getRobotController(controller.controller)).configuration.kind === "VirtualController";
54
+ const description = await nova.api.motionGroup.getMotionGroupDescription(controllerId, motionGroup.motion_group);
55
+ const tcps = Object.entries(description.tcps || {}).map(([id, tcp]) => ({
56
+ id,
57
+ readable_name: tcp.name,
58
+ position: tcp.pose.position,
59
+ orientation: tcp.pose.orientation
60
+ }));
61
+ const controllerStateSocket = nova.openReconnectingWebsocket(`/controllers/${controller.controller}/state-stream?response_rate=1000`);
62
+ const firstControllerMessage = await controllerStateSocket.firstMessage();
63
+ const initialControllerState = tryParseJson(firstControllerMessage.data)?.result;
64
+ if (!initialControllerState) throw new Error(`Unable to parse initial controller state message ${firstControllerMessage.data}`);
65
+ console.log(`Connected controller state websocket to controller ${controller.controller}. Initial state:\n `, initialControllerState);
66
+ return new ConnectedMotionGroup(nova, controller, motionGroup, initialMotionState, motionStateSocket, isVirtual, tcps, description, initialControllerState, controllerStateSocket);
67
+ }
68
+ constructor(nova, controller, motionGroup, initialMotionState, motionStateSocket, isVirtual, tcps, description, initialControllerState, controllerStateSocket) {
69
+ this.nova = nova;
70
+ this.controller = controller;
71
+ this.motionGroup = motionGroup;
72
+ this.initialMotionState = initialMotionState;
73
+ this.motionStateSocket = motionStateSocket;
74
+ this.isVirtual = isVirtual;
75
+ this.tcps = tcps;
76
+ this.description = description;
77
+ this.initialControllerState = initialControllerState;
78
+ this.controllerStateSocket = controllerStateSocket;
79
+ this.connectedJoggingSocket = null;
80
+ this.joggingVelocity = 10;
81
+ this.activationState = "inactive";
82
+ this.rapidlyChangingMotionState = initialMotionState;
83
+ this.controllerState = initialControllerState;
84
+ controllerStateSocket.addEventListener("message", (event) => {
85
+ const data = tryParseJson(event.data)?.result;
86
+ if (!data) return;
87
+ runInAction(() => {
88
+ this.controllerState = data;
89
+ });
90
+ });
91
+ motionStateSocket.addEventListener("message", (event) => {
92
+ const latestMotionState = tryParseJson(event.data)?.result;
93
+ if (!latestMotionState) throw new Error(`Failed to get motion state for ${this.motionGroupId}: ${event.data}`);
94
+ if (!jointValuesEqual(this.rapidlyChangingMotionState.joint_position, latestMotionState.joint_position, MOTION_DELTA_THRESHOLD$1)) runInAction(() => {
95
+ this.rapidlyChangingMotionState = latestMotionState;
96
+ });
97
+ if (!tcpMotionEqual(this.rapidlyChangingMotionState, latestMotionState, MOTION_DELTA_THRESHOLD$1)) runInAction(() => {
98
+ this.rapidlyChangingMotionState.tcp_pose = latestMotionState.tcp_pose;
99
+ });
100
+ if (this.rapidlyChangingMotionState.standstill !== latestMotionState.standstill) runInAction(() => {
101
+ this.rapidlyChangingMotionState.standstill = latestMotionState.standstill;
102
+ });
103
+ });
104
+ makeAutoObservable(this);
105
+ }
106
+ get motionGroupId() {
107
+ return this.motionGroup.motion_group;
108
+ }
109
+ get controllerId() {
110
+ return this.controller.controller;
111
+ }
112
+ get modelFromController() {
113
+ return this.description.motion_group_model;
114
+ }
115
+ get wandelscriptIdentifier() {
116
+ const num = this.motionGroupId.split("@")[0];
117
+ return `${this.controllerId.replaceAll("-", "_")}_${num}`;
118
+ }
119
+ /** Jogging velocity in radians for rotation and joint movement */
120
+ get joggingVelocityRads() {
121
+ return this.joggingVelocity * Math.PI / 180;
122
+ }
123
+ get joints() {
124
+ return this.initialMotionState.joint_position.map((_, i) => {
125
+ return { index: i };
126
+ });
127
+ }
128
+ get dhParameters() {
129
+ return this.description.dh_parameters;
130
+ }
131
+ get safetyZones() {
132
+ return this.description.safety_zones;
133
+ }
134
+ /** Gets the robot mounting position offset in 3D viz coordinates */
135
+ get mountingPosition() {
136
+ if (!this.description.mounting) return [
137
+ 0,
138
+ 0,
139
+ 0
140
+ ];
141
+ return [
142
+ (this.description.mounting.position?.[0] || 0) / 1e3,
143
+ (this.description.mounting.position?.[1] || 0) / 1e3,
144
+ (this.description.mounting.position?.[2] || 0) / 1e3
145
+ ];
146
+ }
147
+ /** Gets the robot mounting position rotation in 3D viz coordinates */
148
+ get mountingQuaternion() {
149
+ const rotationVector = new THREE.Vector3(this.description.mounting?.orientation?.[0] || 0, this.description.mounting?.orientation?.[1] || 0, this.description.mounting?.orientation?.[2] || 0);
150
+ const magnitude = rotationVector.length();
151
+ const axis = rotationVector.normalize();
152
+ return new THREE.Quaternion().setFromAxisAngle(axis, magnitude);
153
+ }
154
+ /**
155
+ * Whether the controller is currently in a safety state
156
+ * corresponding to an emergency stop
157
+ */
158
+ get isEstopActive() {
159
+ return ["SAFETY_STATE_ROBOT_EMERGENCY_STOP", "SAFETY_STATE_DEVICE_EMERGENCY_STOP"].includes(this.controllerState.safety_state);
160
+ }
161
+ /**
162
+ * Whether the controller is in a safety state
163
+ * that may be non-functional for robot pad purposes
164
+ */
165
+ get isMoveableSafetyState() {
166
+ return ["SAFETY_STATE_NORMAL", "SAFETY_STATE_REDUCED"].includes(this.controllerState.safety_state);
167
+ }
168
+ /**
169
+ * Whether the controller is in an operation mode that allows movement
170
+ */
171
+ get isMoveableOperationMode() {
172
+ return [
173
+ "OPERATION_MODE_AUTO",
174
+ "OPERATION_MODE_MANUAL",
175
+ "OPERATION_MODE_MANUAL_T1",
176
+ "OPERATION_MODE_MANUAL_T2"
177
+ ].includes(this.controllerState.operation_mode);
178
+ }
179
+ /**
180
+ * Whether the robot is currently active and can be moved, based on the
181
+ * safety state, operation mode and servo toggle activation state.
182
+ */
183
+ get canBeMoved() {
184
+ return this.isMoveableSafetyState && this.isMoveableOperationMode && this.activationState === "active";
185
+ }
186
+ async deactivate() {
187
+ if (this.activationState !== "active") {
188
+ console.error("Tried to deactivate while already deactivating");
189
+ return;
190
+ }
191
+ runInAction(() => {
192
+ this.activationState = "deactivating";
193
+ });
194
+ try {
195
+ await this.nova.api.controller.setDefaultMode(this.controllerId, "ROBOT_SYSTEM_MODE_MONITOR");
196
+ runInAction(() => {
197
+ this.activationState = "inactive";
198
+ });
199
+ } catch (err) {
200
+ runInAction(() => {
201
+ this.activationState = "active";
202
+ });
203
+ throw err;
204
+ }
205
+ }
206
+ async activate() {
207
+ if (this.activationState !== "inactive") {
208
+ console.error("Tried to activate while already activating");
209
+ return;
210
+ }
211
+ runInAction(() => {
212
+ this.activationState = "activating";
213
+ });
214
+ try {
215
+ await this.nova.api.controller.setDefaultMode(this.controllerId, "ROBOT_SYSTEM_MODE_CONTROL");
216
+ runInAction(() => {
217
+ this.activationState = "active";
218
+ });
219
+ } catch (err) {
220
+ runInAction(() => {
221
+ this.activationState = "inactive";
222
+ });
223
+ throw err;
224
+ }
225
+ }
226
+ toggleActivation() {
227
+ if (this.activationState === "inactive") this.activate();
228
+ else if (this.activationState === "active") this.deactivate();
229
+ }
230
+ dispose() {
231
+ this.motionStateSocket.close();
232
+ if (this.connectedJoggingSocket) this.connectedJoggingSocket.close();
233
+ }
234
+ setJoggingVelocity(velocity) {
235
+ this.joggingVelocity = velocity;
236
+ }
237
+ };
238
+
239
+ //#endregion
9
240
  //#region src/lib/v2/NovaCellAPIClient.ts
10
241
  /**
11
242
  * API client providing type-safe access to all the Nova API REST endpoints
@@ -79,7 +310,6 @@ var NovaCellAPIClient = class {
79
310
  //#endregion
80
311
  //#region src/lib/v2/mock/MockNovaInstance.ts
81
312
  /**
82
- * EXPERIMENTAL
83
313
  * Ultra-simplified mock Nova server for testing stuff
84
314
  */
85
315
  var MockNovaInstance = class {
@@ -88,6 +318,437 @@ var MockNovaInstance = class {
88
318
  }
89
319
  async handleAPIRequest(config) {
90
320
  const apiHandlers = [
321
+ {
322
+ method: "GET",
323
+ path: "/cells/:cellId/controllers/:controllerId/state",
324
+ handle() {
325
+ return {
326
+ mode: "MODE_CONTROL",
327
+ last_error: [],
328
+ timestamp: "2025-10-16T09:19:26.634534092Z",
329
+ sequence_number: 1054764,
330
+ controller: "mock-ur5e",
331
+ operation_mode: "OPERATION_MODE_AUTO",
332
+ safety_state: "SAFETY_STATE_NORMAL",
333
+ velocity_override: 100,
334
+ motion_groups: [{
335
+ timestamp: "2025-10-16T09:19:26.634534092Z",
336
+ sequence_number: 1054764,
337
+ motion_group: "0@mock-ur5e",
338
+ controller: "mock-ur5e",
339
+ joint_position: [
340
+ 1.487959623336792,
341
+ -1.8501918315887451,
342
+ 1.8003005981445312,
343
+ 6.034560203552246,
344
+ 1.4921919107437134,
345
+ 1.593459963798523
346
+ ],
347
+ joint_limit_reached: { limit_reached: [
348
+ false,
349
+ false,
350
+ false,
351
+ false,
352
+ false,
353
+ false
354
+ ] },
355
+ joint_torque: [],
356
+ joint_current: [
357
+ 0,
358
+ 0,
359
+ 0,
360
+ 0,
361
+ 0,
362
+ 0
363
+ ],
364
+ flange_pose: {
365
+ position: [
366
+ 107.6452433732927,
367
+ -409.0402987746852,
368
+ 524.2402132330305
369
+ ],
370
+ orientation: [
371
+ .9874434028353319,
372
+ -.986571714997442,
373
+ 1.3336589451098142
374
+ ]
375
+ },
376
+ tcp: "Flange",
377
+ tcp_pose: {
378
+ position: [
379
+ 107.6452433732927,
380
+ -409.0402987746852,
381
+ 524.2402132330305
382
+ ],
383
+ orientation: [
384
+ .9874434028353319,
385
+ -.986571714997442,
386
+ 1.3336589451098142
387
+ ]
388
+ },
389
+ payload: "",
390
+ coordinate_system: "",
391
+ standstill: true
392
+ }]
393
+ };
394
+ }
395
+ },
396
+ {
397
+ method: "GET",
398
+ path: "/cells/:cellId/controllers/:controllerId/motion-groups/:motionGroupId/description",
399
+ handle() {
400
+ return {
401
+ motion_group_model: "UniversalRobots_UR5e",
402
+ mounting: {
403
+ position: [
404
+ 0,
405
+ 0,
406
+ 0
407
+ ],
408
+ orientation: [
409
+ 0,
410
+ 0,
411
+ 0
412
+ ]
413
+ },
414
+ tcps: {
415
+ Flange: {
416
+ name: "Default-Flange",
417
+ pose: {
418
+ position: [
419
+ 0,
420
+ 0,
421
+ 0
422
+ ],
423
+ orientation: [
424
+ 0,
425
+ 0,
426
+ 0
427
+ ]
428
+ }
429
+ },
430
+ "complex-tcp-position": {
431
+ name: "Complex TCP Position",
432
+ pose: {
433
+ position: [
434
+ -200,
435
+ 300,
436
+ 150
437
+ ],
438
+ orientation: [
439
+ -.12139440409113832,
440
+ -.06356210998212003,
441
+ -.2023240068185639
442
+ ]
443
+ }
444
+ },
445
+ "offset-150mm-xy": {
446
+ name: "-150mm XY Offset",
447
+ pose: {
448
+ position: [
449
+ -150,
450
+ -150,
451
+ 0
452
+ ],
453
+ orientation: [
454
+ 0,
455
+ 0,
456
+ 0
457
+ ]
458
+ }
459
+ },
460
+ "rotated-90deg-z": {
461
+ name: "90° Z Axis Rotation",
462
+ pose: {
463
+ position: [
464
+ 0,
465
+ 0,
466
+ 0
467
+ ],
468
+ orientation: [
469
+ 0,
470
+ 0,
471
+ 1.5708
472
+ ]
473
+ }
474
+ }
475
+ },
476
+ payloads: { "FPay-0": {
477
+ name: "FPay-0",
478
+ payload: 0,
479
+ center_of_mass: [
480
+ 0,
481
+ 0,
482
+ 0
483
+ ],
484
+ moment_of_inertia: [
485
+ 0,
486
+ 0,
487
+ 0
488
+ ]
489
+ } },
490
+ cycle_time: 8,
491
+ dh_parameters: [
492
+ {
493
+ alpha: 1.5707963267948966,
494
+ d: 162.25
495
+ },
496
+ { a: -425 },
497
+ { a: -392.2 },
498
+ {
499
+ alpha: 1.5707963267948966,
500
+ d: 133.3
501
+ },
502
+ {
503
+ alpha: -1.5707963267948966,
504
+ d: 99.7
505
+ },
506
+ { d: 99.6 }
507
+ ],
508
+ operation_limits: {
509
+ auto_limits: {
510
+ joints: [
511
+ {
512
+ position: {
513
+ lower_limit: -6.283185307179586,
514
+ upper_limit: 6.283185307179586
515
+ },
516
+ velocity: 3.34159255027771,
517
+ acceleration: 40,
518
+ torque: 150
519
+ },
520
+ {
521
+ position: {
522
+ lower_limit: -6.283185307179586,
523
+ upper_limit: 6.283185307179586
524
+ },
525
+ velocity: 3.34159255027771,
526
+ acceleration: 40,
527
+ torque: 150
528
+ },
529
+ {
530
+ position: {
531
+ lower_limit: -6.283185307179586,
532
+ upper_limit: 6.283185307179586
533
+ },
534
+ velocity: 3.34159255027771,
535
+ acceleration: 40,
536
+ torque: 150
537
+ },
538
+ {
539
+ position: {
540
+ lower_limit: -6.283185307179586,
541
+ upper_limit: 6.283185307179586
542
+ },
543
+ velocity: 3.34159255027771,
544
+ acceleration: 40,
545
+ torque: 28
546
+ },
547
+ {
548
+ position: {
549
+ lower_limit: -6.283185307179586,
550
+ upper_limit: 6.283185307179586
551
+ },
552
+ velocity: 3.34159255027771,
553
+ acceleration: 40,
554
+ torque: 28
555
+ },
556
+ {
557
+ position: {
558
+ lower_limit: -6.283185307179586,
559
+ upper_limit: 6.283185307179586
560
+ },
561
+ velocity: 3.34159255027771,
562
+ acceleration: 40,
563
+ torque: 28
564
+ }
565
+ ],
566
+ tcp: { velocity: 5e3 },
567
+ elbow: { velocity: 5e3 },
568
+ flange: { velocity: 5e3 }
569
+ },
570
+ manual_limits: {
571
+ joints: [
572
+ {
573
+ position: {
574
+ lower_limit: -6.283185307179586,
575
+ upper_limit: 6.283185307179586
576
+ },
577
+ velocity: 3.34159255027771,
578
+ acceleration: 40,
579
+ torque: 150
580
+ },
581
+ {
582
+ position: {
583
+ lower_limit: -6.283185307179586,
584
+ upper_limit: 6.283185307179586
585
+ },
586
+ velocity: 3.34159255027771,
587
+ acceleration: 40,
588
+ torque: 150
589
+ },
590
+ {
591
+ position: {
592
+ lower_limit: -6.283185307179586,
593
+ upper_limit: 6.283185307179586
594
+ },
595
+ velocity: 3.34159255027771,
596
+ acceleration: 40,
597
+ torque: 150
598
+ },
599
+ {
600
+ position: {
601
+ lower_limit: -6.283185307179586,
602
+ upper_limit: 6.283185307179586
603
+ },
604
+ velocity: 3.34159255027771,
605
+ acceleration: 40,
606
+ torque: 28
607
+ },
608
+ {
609
+ position: {
610
+ lower_limit: -6.283185307179586,
611
+ upper_limit: 6.283185307179586
612
+ },
613
+ velocity: 3.34159255027771,
614
+ acceleration: 40,
615
+ torque: 28
616
+ },
617
+ {
618
+ position: {
619
+ lower_limit: -6.283185307179586,
620
+ upper_limit: 6.283185307179586
621
+ },
622
+ velocity: 3.34159255027771,
623
+ acceleration: 40,
624
+ torque: 28
625
+ }
626
+ ],
627
+ tcp: { velocity: 5e3 }
628
+ },
629
+ manual_t1_limits: {
630
+ joints: [
631
+ {
632
+ position: {
633
+ lower_limit: -6.283185307179586,
634
+ upper_limit: 6.283185307179586
635
+ },
636
+ velocity: 3.34159255027771,
637
+ acceleration: 40,
638
+ torque: 150
639
+ },
640
+ {
641
+ position: {
642
+ lower_limit: -6.283185307179586,
643
+ upper_limit: 6.283185307179586
644
+ },
645
+ velocity: 3.34159255027771,
646
+ acceleration: 40,
647
+ torque: 150
648
+ },
649
+ {
650
+ position: {
651
+ lower_limit: -6.283185307179586,
652
+ upper_limit: 6.283185307179586
653
+ },
654
+ velocity: 3.34159255027771,
655
+ acceleration: 40,
656
+ torque: 150
657
+ },
658
+ {
659
+ position: {
660
+ lower_limit: -6.283185307179586,
661
+ upper_limit: 6.283185307179586
662
+ },
663
+ velocity: 3.34159255027771,
664
+ acceleration: 40,
665
+ torque: 28
666
+ },
667
+ {
668
+ position: {
669
+ lower_limit: -6.283185307179586,
670
+ upper_limit: 6.283185307179586
671
+ },
672
+ velocity: 3.34159255027771,
673
+ acceleration: 40,
674
+ torque: 28
675
+ },
676
+ {
677
+ position: {
678
+ lower_limit: -6.283185307179586,
679
+ upper_limit: 6.283185307179586
680
+ },
681
+ velocity: 3.34159255027771,
682
+ acceleration: 40,
683
+ torque: 28
684
+ }
685
+ ],
686
+ tcp: { velocity: 5e3 }
687
+ },
688
+ manual_t2_limits: {
689
+ joints: [
690
+ {
691
+ position: {
692
+ lower_limit: -6.283185307179586,
693
+ upper_limit: 6.283185307179586
694
+ },
695
+ velocity: 3.34159255027771,
696
+ acceleration: 40,
697
+ torque: 150
698
+ },
699
+ {
700
+ position: {
701
+ lower_limit: -6.283185307179586,
702
+ upper_limit: 6.283185307179586
703
+ },
704
+ velocity: 3.34159255027771,
705
+ acceleration: 40,
706
+ torque: 150
707
+ },
708
+ {
709
+ position: {
710
+ lower_limit: -6.283185307179586,
711
+ upper_limit: 6.283185307179586
712
+ },
713
+ velocity: 3.34159255027771,
714
+ acceleration: 40,
715
+ torque: 150
716
+ },
717
+ {
718
+ position: {
719
+ lower_limit: -6.283185307179586,
720
+ upper_limit: 6.283185307179586
721
+ },
722
+ velocity: 3.34159255027771,
723
+ acceleration: 40,
724
+ torque: 28
725
+ },
726
+ {
727
+ position: {
728
+ lower_limit: -6.283185307179586,
729
+ upper_limit: 6.283185307179586
730
+ },
731
+ velocity: 3.34159255027771,
732
+ acceleration: 40,
733
+ torque: 28
734
+ },
735
+ {
736
+ position: {
737
+ lower_limit: -6.283185307179586,
738
+ upper_limit: 6.283185307179586
739
+ },
740
+ velocity: 3.34159255027771,
741
+ acceleration: 40,
742
+ torque: 28
743
+ }
744
+ ],
745
+ tcp: { velocity: 5e3 }
746
+ }
747
+ },
748
+ serial_number: "WBVirtualRobot"
749
+ };
750
+ }
751
+ },
91
752
  {
92
753
  method: "GET",
93
754
  path: "/cells/:cellId/controllers",
@@ -1254,6 +1915,380 @@ const defaultMotionState = { result: {
1254
1915
  }
1255
1916
  } };
1256
1917
 
1918
+ //#endregion
1919
+ //#region src/lib/v2/MotionStreamConnection.ts
1920
+ const MOTION_DELTA_THRESHOLD = 1e-4;
1921
+ function unwrapRotationVector(newRotationVectorApi, currentRotationVectorApi) {
1922
+ const currentRotationVector = new Vector3(currentRotationVectorApi[0], currentRotationVectorApi[1], currentRotationVectorApi[2]);
1923
+ const newRotationVector = new Vector3(newRotationVectorApi[0], newRotationVectorApi[1], newRotationVectorApi[2]);
1924
+ const currentAngle = currentRotationVector.length();
1925
+ const currentAxis = currentRotationVector.normalize();
1926
+ let newAngle = newRotationVector.length();
1927
+ let newAxis = newRotationVector.normalize();
1928
+ if (newAxis.dot(currentAxis) < 0) {
1929
+ newAngle = -newAngle;
1930
+ newAxis = newAxis.multiplyScalar(-1);
1931
+ }
1932
+ let angleDifference = newAngle - currentAngle;
1933
+ angleDifference -= 2 * Math.PI * Math.floor((angleDifference + Math.PI) / (2 * Math.PI));
1934
+ newAngle = currentAngle + angleDifference;
1935
+ return [...newAxis.multiplyScalar(newAngle)];
1936
+ }
1937
+ /**
1938
+ * Store representing the current state of a connected motion group.
1939
+ */
1940
+ var MotionStreamConnection = class MotionStreamConnection {
1941
+ static async open(nova, motionGroupId) {
1942
+ const [_motionGroupIndex, controllerId] = motionGroupId.split("@");
1943
+ const controller = await nova.api.controller.getCurrentRobotControllerState(controllerId);
1944
+ const motionGroup = controller?.motion_groups.find((mg) => mg.motion_group === motionGroupId);
1945
+ if (!controller || !motionGroup) throw new Error(`Controller ${controllerId} or motion group ${motionGroupId} not found`);
1946
+ const motionStateSocket = nova.openReconnectingWebsocket(`/controllers/${controllerId}/motion-groups/${motionGroupId}/state-stream`);
1947
+ const firstMessage = await motionStateSocket.firstMessage();
1948
+ const initialMotionState = tryParseJson(firstMessage.data)?.result;
1949
+ if (!initialMotionState) throw new Error(`Unable to parse initial motion state message ${firstMessage.data}`);
1950
+ console.log(`Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:\n `, initialMotionState);
1951
+ return new MotionStreamConnection(nova, controller, motionGroup, await nova.api.motionGroup.getMotionGroupDescription(controllerId, motionGroup.motion_group), initialMotionState, motionStateSocket);
1952
+ }
1953
+ constructor(nova, controller, motionGroup, description, initialMotionState, motionStateSocket) {
1954
+ this.nova = nova;
1955
+ this.controller = controller;
1956
+ this.motionGroup = motionGroup;
1957
+ this.description = description;
1958
+ this.initialMotionState = initialMotionState;
1959
+ this.motionStateSocket = motionStateSocket;
1960
+ this.rapidlyChangingMotionState = initialMotionState;
1961
+ motionStateSocket.addEventListener("message", (event) => {
1962
+ const latestMotionState = tryParseJson(event.data)?.result;
1963
+ if (!latestMotionState) throw new Error(`Failed to get motion state for ${this.motionGroupId}: ${event.data}`);
1964
+ if (!jointValuesEqual(this.rapidlyChangingMotionState.joint_position, latestMotionState.joint_position, MOTION_DELTA_THRESHOLD)) runInAction(() => {
1965
+ this.rapidlyChangingMotionState = latestMotionState;
1966
+ });
1967
+ if (!tcpMotionEqual(this.rapidlyChangingMotionState, latestMotionState, MOTION_DELTA_THRESHOLD)) runInAction(() => {
1968
+ if (this.rapidlyChangingMotionState.tcp_pose == null) this.rapidlyChangingMotionState.tcp_pose = latestMotionState.tcp_pose;
1969
+ else if (latestMotionState.tcp_pose?.orientation && latestMotionState.tcp_pose?.position && this.rapidlyChangingMotionState.tcp_pose?.orientation) this.rapidlyChangingMotionState.tcp_pose = {
1970
+ position: latestMotionState.tcp_pose.position,
1971
+ orientation: unwrapRotationVector(latestMotionState.tcp_pose.orientation, this.rapidlyChangingMotionState.tcp_pose.orientation)
1972
+ };
1973
+ else console.warn("Received incomplete tcp_pose, ignoring", latestMotionState.tcp_pose);
1974
+ });
1975
+ if (this.rapidlyChangingMotionState.standstill !== latestMotionState.standstill) runInAction(() => {
1976
+ this.rapidlyChangingMotionState.standstill = latestMotionState.standstill;
1977
+ });
1978
+ });
1979
+ makeAutoObservable(this);
1980
+ }
1981
+ get motionGroupId() {
1982
+ return this.motionGroup.motion_group;
1983
+ }
1984
+ get controllerId() {
1985
+ return this.controller.controller;
1986
+ }
1987
+ get wandelscriptIdentifier() {
1988
+ const num = this.motionGroupId.split("@")[0];
1989
+ return `${this.controllerId.replaceAll("-", "_")}_${num}`;
1990
+ }
1991
+ get joints() {
1992
+ return this.initialMotionState.joint_position.map((_, i) => {
1993
+ return { index: i };
1994
+ });
1995
+ }
1996
+ dispose() {
1997
+ this.motionStateSocket.close();
1998
+ }
1999
+ };
2000
+
2001
+ //#endregion
2002
+ //#region src/lib/v2/JoggerConnection.ts
2003
+ var JoggerConnection = class JoggerConnection {
2004
+ /**
2005
+ * Initialize the jogging connection using jogging endpoint or trajectory endpoint depending on the selected mode.
2006
+ *
2007
+ * @param nova - The Nova client instance
2008
+ * @param motionGroupId - The ID of the motion group to connect to
2009
+ * @param options - Configuration options for the jogger connection
2010
+ * @param options.mode - The jogging mode to initialize:
2011
+ * - "jogging": Continuous jogging mode with persistent websocket for real-time velocity commands
2012
+ * - "trajectory": Incremental jogging mode for fixed-distance motions via trajectory planning
2013
+ * - "off": No jogging enabled, all websockets closed (default)
2014
+ * @param options.timeout - Timeout in milliseconds for websocket initialization (default: 5000ms)
2015
+ * @param options.tcp - TCP frame to use for motions (default: from motion group)
2016
+ * //param options.coordinateSystem - Coordinate system for jogging commands (default: "world"). Please note: Currently not implemented
2017
+ * @param options.orientation - If set to "tool", jogging TcpVelocityRequest will use `use_tool_coordinate_system` option (default: "world")
2018
+ * @param options.onError - Error handler for websocket errors
2019
+ * @returns Promise resolving to initialized JoggerConnection instance
2020
+ */
2021
+ static async open(nova, motionGroupId, options = {}) {
2022
+ const jogger = new JoggerConnection(await nova.connectMotionStream(motionGroupId), options);
2023
+ await jogger.setJoggingMode(jogger.mode);
2024
+ return jogger;
2025
+ }
2026
+ constructor(motionStream, options = {}) {
2027
+ this.motionStream = motionStream;
2028
+ this.options = options;
2029
+ this.ENDPOINT_JOGGING = "/execution/jogging";
2030
+ this.ENDPOINT_TRAJECTORY = "/execution/trajectory";
2031
+ this.DEFAULT_MODE = "off";
2032
+ this.DEFAULT_TCP = "Flange";
2033
+ this.DEFAULT_INIT_TIMEOUT = 5e3;
2034
+ this.DEFAULT_ORIENTATION = "world";
2035
+ this.mode = "off";
2036
+ this.joggingSocket = null;
2037
+ this.trajectorySocket = null;
2038
+ this.timeout = this.DEFAULT_INIT_TIMEOUT;
2039
+ this.tcp = options?.tcp || motionStream.motionGroup.tcp || this.DEFAULT_TCP;
2040
+ this.orientation = options?.orientation || this.DEFAULT_ORIENTATION;
2041
+ this.timeout = options?.timeout || this.DEFAULT_INIT_TIMEOUT;
2042
+ this.mode = options?.mode || this.DEFAULT_MODE;
2043
+ this.onError = options?.onError;
2044
+ }
2045
+ async updateOptions(options) {
2046
+ if (options.tcp) this.tcp = options.tcp;
2047
+ if (options.timeout) this.timeout = options.timeout;
2048
+ if (options.mode) this.mode = options.mode;
2049
+ if (options.orientation) this.orientation = options.orientation;
2050
+ if (options.onError) this.onError = options.onError;
2051
+ this.setJoggingMode(this.mode, false);
2052
+ }
2053
+ get motionGroupId() {
2054
+ return this.motionStream.motionGroupId;
2055
+ }
2056
+ get nova() {
2057
+ return this.motionStream.nova;
2058
+ }
2059
+ get numJoints() {
2060
+ return this.motionStream.joints.length;
2061
+ }
2062
+ async stop() {
2063
+ if (this.joggingSocket) {
2064
+ const velocity = new Array(this.numJoints).fill(0);
2065
+ this.joggingSocket.sendJson({
2066
+ message_type: "JointVelocityRequest",
2067
+ velocity
2068
+ });
2069
+ }
2070
+ if (this.trajectorySocket) this.trajectorySocket.sendJson({ message_type: "PauseMovementRequest" });
2071
+ }
2072
+ async dispose() {
2073
+ const sockets = [this.joggingSocket, this.trajectorySocket].filter((s) => s !== null);
2074
+ sockets.forEach((s) => {
2075
+ s.dispose();
2076
+ });
2077
+ this.joggingSocket = null;
2078
+ this.trajectorySocket = null;
2079
+ return Promise.all(sockets.map((s) => s.closed()));
2080
+ }
2081
+ async setJoggingMode(mode, skipReinitializeIfSameMode = true) {
2082
+ if (this.mode === mode && skipReinitializeIfSameMode) return;
2083
+ this.dispose();
2084
+ this.mode = mode;
2085
+ if (this.mode === "jogging") return this.initializeJoggingWebsocket();
2086
+ }
2087
+ async initializeJoggingWebsocket() {
2088
+ return new Promise((resolve, reject) => {
2089
+ const connectionFailedTimeout = setTimeout(() => {
2090
+ reject(/* @__PURE__ */ new Error(`Jogging initialization timeout after ${this.timeout} seconds`));
2091
+ }, this.timeout);
2092
+ this.joggingSocket = this.nova.openReconnectingWebsocket(this.ENDPOINT_JOGGING);
2093
+ this.joggingSocket.addEventListener("message", (ev) => {
2094
+ const data = tryParseJson(ev.data);
2095
+ if (data?.result?.kind === "INITIALIZE_RECEIVED") {
2096
+ clearTimeout(connectionFailedTimeout);
2097
+ resolve();
2098
+ return;
2099
+ }
2100
+ if (data && "error" in data || data?.result?.kind === "MOTION_ERROR") {
2101
+ clearTimeout(connectionFailedTimeout);
2102
+ if (this.onError) this.onError(ev.data);
2103
+ else reject(new Error(ev.data));
2104
+ }
2105
+ });
2106
+ this.joggingSocket.sendJson({
2107
+ message_type: "InitializeJoggingRequest",
2108
+ motion_group: this.motionGroupId,
2109
+ tcp: this.tcp
2110
+ });
2111
+ });
2112
+ }
2113
+ /**
2114
+ * Jogging: Start rotation of a single robot joint at the specified velocity
2115
+ */
2116
+ async rotateJoints({ joint, direction, velocityRadsPerSec }) {
2117
+ if (!this.joggingSocket || this.mode !== "jogging") throw new Error("Joint jogging websocket not connected; create one by setting jogging mode to 'jogging'");
2118
+ const velocity = new Array(this.numJoints).fill(0);
2119
+ velocity[joint] = direction === "-" ? -velocityRadsPerSec : velocityRadsPerSec;
2120
+ this.joggingSocket.sendJson({
2121
+ message_type: "JointVelocityRequest",
2122
+ velocity
2123
+ });
2124
+ }
2125
+ /**
2126
+ * Jogging: Start the TCP moving along a specified axis at a given velocity
2127
+ */
2128
+ async translateTCP({ axis, direction, velocityMmPerSec }) {
2129
+ if (!this.joggingSocket || this.mode !== "jogging") throw new Error("Continuous jogging websocket not connected; create one by setting jogging mode to 'jogging'");
2130
+ const rotation = [
2131
+ 0,
2132
+ 0,
2133
+ 0
2134
+ ];
2135
+ const translation = [
2136
+ 0,
2137
+ 0,
2138
+ 0
2139
+ ];
2140
+ translation[XYZ_TO_VECTOR[axis]] = direction === "-" ? -velocityMmPerSec : velocityMmPerSec;
2141
+ this.joggingSocket.sendJson({
2142
+ message_type: "TcpVelocityRequest",
2143
+ translation,
2144
+ rotation,
2145
+ use_tool_coordinate_system: this.orientation === "tool"
2146
+ });
2147
+ }
2148
+ /**
2149
+ * Jogging: Start the TCP rotating around a specified axis at a given velocity
2150
+ */
2151
+ async rotateTCP({ axis, direction, velocityRadsPerSec }) {
2152
+ if (!this.joggingSocket || this.mode !== "jogging") throw new Error("Continuous jogging websocket not connected; create one by setting jogging mode to 'jogging'");
2153
+ const rotation = [
2154
+ 0,
2155
+ 0,
2156
+ 0
2157
+ ];
2158
+ const translation = [
2159
+ 0,
2160
+ 0,
2161
+ 0
2162
+ ];
2163
+ rotation[XYZ_TO_VECTOR[axis]] = direction === "-" ? -velocityRadsPerSec : velocityRadsPerSec;
2164
+ this.joggingSocket.sendJson({
2165
+ message_type: "TcpVelocityRequest",
2166
+ translation,
2167
+ rotation
2168
+ });
2169
+ }
2170
+ /**
2171
+ * Trajectory jogging:
2172
+ *
2173
+ * Move the robot by a fixed distance in a single cartesian
2174
+ * axis, either rotating or translating relative to the TCP.
2175
+ * Promise resolves only after the motion has completed.
2176
+ */
2177
+ async runIncrementalCartesianMotion({ currentTcpPose, currentJoints, coordSystemId, velocityInRelevantUnits, axis, direction, motion }) {
2178
+ const commands = [];
2179
+ if (this.mode !== "trajectory") throw new Error("Set jogging mode to 'trajectory' to run incremental cartesian motions");
2180
+ if (motion.type === "translate") {
2181
+ if (!currentTcpPose.position) throw new Error("Current pose has no position, cannot perform translation");
2182
+ const targetTcpPosition = [...currentTcpPose.position];
2183
+ targetTcpPosition[XYZ_TO_VECTOR[axis]] += motion.distanceMm * (direction === "-" ? -1 : 1);
2184
+ commands.push({
2185
+ limits_override: { tcp_velocity_limit: velocityInRelevantUnits },
2186
+ path: {
2187
+ path_definition_name: "PathLine",
2188
+ target_pose: {
2189
+ position: targetTcpPosition,
2190
+ orientation: currentTcpPose.orientation
2191
+ }
2192
+ }
2193
+ });
2194
+ } else if (motion.type === "rotate") {
2195
+ if (!currentTcpPose.orientation) throw new Error("Current pose has no orientation, cannot perform rotation");
2196
+ const currentRotationVector = new Vector3$1(currentTcpPose.orientation[0], currentTcpPose.orientation[1], currentTcpPose.orientation[2]);
2197
+ const currentRotationRad = currentRotationVector.length();
2198
+ const currentRotationDirection = currentRotationVector.clone().normalize();
2199
+ const differenceRotationRad = motion.distanceRads * (direction === "-" ? -1 : 1);
2200
+ const differenceRotationDirection = new Vector3$1(0, 0, 0);
2201
+ differenceRotationDirection[axis] = 1;
2202
+ const f1 = Math.cos(.5 * differenceRotationRad) * Math.cos(.5 * currentRotationRad);
2203
+ const f2 = Math.sin(.5 * differenceRotationRad) * Math.sin(.5 * currentRotationRad);
2204
+ const f3 = Math.sin(.5 * differenceRotationRad) * Math.cos(.5 * currentRotationRad);
2205
+ const f4 = Math.cos(.5 * differenceRotationRad) * Math.sin(.5 * currentRotationRad);
2206
+ const dotProduct = differenceRotationDirection.dot(currentRotationDirection);
2207
+ const crossProduct = differenceRotationDirection.clone().cross(currentRotationDirection);
2208
+ const newRotationRad = 2 * Math.acos(f1 - f2 * dotProduct);
2209
+ const f5 = newRotationRad / Math.sin(.5 * newRotationRad);
2210
+ const targetTcpOrientation = new Vector3$1().addScaledVector(crossProduct, f2).addScaledVector(differenceRotationDirection, f3).addScaledVector(currentRotationDirection, f4).multiplyScalar(f5);
2211
+ commands.push({
2212
+ limits_override: { tcp_orientation_velocity_limit: velocityInRelevantUnits },
2213
+ path: {
2214
+ path_definition_name: "PathLine",
2215
+ target_pose: {
2216
+ position: currentTcpPose.position,
2217
+ orientation: [...targetTcpOrientation]
2218
+ }
2219
+ }
2220
+ });
2221
+ }
2222
+ const description = this.motionStream.description;
2223
+ if (description.cycle_time === void 0) {
2224
+ console.warn("Current motion group has no cycle time, cannot plan jogging motion");
2225
+ return;
2226
+ }
2227
+ const motion_group_setup = {
2228
+ motion_group_model: description.motion_group_model,
2229
+ cycle_time: description.cycle_time,
2230
+ mounting: description.mounting,
2231
+ global: description.operation_limits.auto_limits
2232
+ };
2233
+ const motionPlanRes = await this.nova.api.trajectoryPlanning.planTrajectory({
2234
+ motion_group_setup,
2235
+ start_joint_position: currentJoints,
2236
+ motion_commands: commands
2237
+ });
2238
+ const trajectoryData = motionPlanRes.response;
2239
+ if (!trajectoryData) throw new Error(`Failed to plan jogging increment motion ${JSON.stringify(motionPlanRes)}`);
2240
+ if (this.trajectorySocket) {
2241
+ console.warn("Trajectory jogging websocket already open; will close");
2242
+ this.trajectorySocket.dispose();
2243
+ }
2244
+ this.trajectorySocket = this.nova.openReconnectingWebsocket(this.ENDPOINT_TRAJECTORY);
2245
+ const messageInitializeMovementResponse = (result) => {
2246
+ if (!result || result.add_trajectory_error || result.message) if (this.onError) this.onError(result);
2247
+ else throw new Error(result?.add_trajectory_error?.message || result?.message || "Failed to execute trajectory, unknown error");
2248
+ if (!this.trajectorySocket) throw new Error(`Failed to execute trajectory, websocket not available anymore`);
2249
+ this.trajectorySocket.sendJson({
2250
+ message_type: "StartMovementRequest",
2251
+ direction: "DIRECTION_FORWARD"
2252
+ });
2253
+ };
2254
+ const waitForMovementToStartAndFinish = async () => {
2255
+ await when(() => !this.motionStream.rapidlyChangingMotionState.standstill);
2256
+ await when(() => this.motionStream.rapidlyChangingMotionState.standstill);
2257
+ this.trajectorySocket?.dispose();
2258
+ this.trajectorySocket = null;
2259
+ };
2260
+ const waitForMovementToFinish = async () => {
2261
+ await when(() => this.motionStream.rapidlyChangingMotionState.standstill);
2262
+ this.trajectorySocket?.dispose();
2263
+ this.trajectorySocket = null;
2264
+ };
2265
+ const messageStartMovementResponse = async (data) => {
2266
+ if (data?.message) if (this.onError) {
2267
+ this.onError(data);
2268
+ return;
2269
+ } else throw new Error(data.message || "Failed to execute trajectory, unknown error");
2270
+ if (this.motionStream.rapidlyChangingMotionState.standstill) await waitForMovementToStartAndFinish();
2271
+ else await waitForMovementToFinish();
2272
+ };
2273
+ this.trajectorySocket.addEventListener("message", (ev) => {
2274
+ const data = tryParseJson(ev.data);
2275
+ if (!data?.result?.kind) throw new Error(`Failed to execute trajectory: Received invalid message ${ev.data}`);
2276
+ if (data.result.kind === "INITIALIZE_RECEIVED") messageInitializeMovementResponse(data.result);
2277
+ else if (data.result.kind === "START_RECEIVED") messageStartMovementResponse(data);
2278
+ else throw new Error(`Failed to execute trajectory, cannot handle message type "${data.result.kind}"`);
2279
+ });
2280
+ this.trajectorySocket.sendJson({
2281
+ message_type: "InitializeMovementRequest",
2282
+ trajectory: {
2283
+ message_type: "TrajectoryData",
2284
+ motion_group: this.motionGroupId,
2285
+ data: trajectoryData,
2286
+ tcp: this.tcp
2287
+ }
2288
+ });
2289
+ }
2290
+ };
2291
+
1257
2292
  //#endregion
1258
2293
  //#region src/lib/v2/NovaClient.ts
1259
2294
  function permissiveInstanceUrlParse(url) {
@@ -1261,10 +2296,6 @@ function permissiveInstanceUrlParse(url) {
1261
2296
  return new URL(url).toString();
1262
2297
  }
1263
2298
  /**
1264
- * EXPERIMENTAL
1265
- *
1266
- * This client provides a starting point to migrate NOVA api v2.
1267
- * As v2 is still in development, this client has to be considered unstable
1268
2299
  *
1269
2300
  * Client for connecting to a Nova instance and controlling robots.
1270
2301
  */
@@ -1272,15 +2303,14 @@ var NovaClient = class {
1272
2303
  constructor(config) {
1273
2304
  this.authPromise = null;
1274
2305
  this.accessToken = null;
1275
- console.warn("Using experimental NOVA v2 client");
1276
2306
  const cellId = config.cellId ?? "cell";
1277
2307
  this.config = {
1278
2308
  cellId,
1279
- ...config,
1280
- instanceUrl: permissiveInstanceUrlParse(config.instanceUrl)
2309
+ ...config
1281
2310
  };
1282
2311
  this.accessToken = config.accessToken || availableStorage.getString("wbjs.access_token") || null;
1283
2312
  if (this.config.instanceUrl === "https://mock.example.com") this.mock = new MockNovaInstance();
2313
+ else this.config.instanceUrl = permissiveInstanceUrlParse(this.config.instanceUrl);
1284
2314
  const axiosInstance = axios.create({
1285
2315
  baseURL: urlJoin(this.config.instanceUrl, "/api/v2"),
1286
2316
  headers: typeof window !== "undefined" && window.location.origin.includes("localhost") ? {} : { "X-Wandelbots-Client": "Wandelbots-Nova-JS-SDK" }
@@ -1312,7 +2342,7 @@ var NovaClient = class {
1312
2342
  });
1313
2343
  this.api = new NovaCellAPIClient(cellId, {
1314
2344
  ...config,
1315
- basePath: urlJoin(this.config.instanceUrl, "/api/v1"),
2345
+ basePath: urlJoin(this.config.instanceUrl, "/api/v2"),
1316
2346
  isJsonMime: (mime) => {
1317
2347
  return mime === "application/json";
1318
2348
  },
@@ -1337,7 +2367,7 @@ var NovaClient = class {
1337
2367
  }
1338
2368
  }
1339
2369
  makeWebsocketURL(path) {
1340
- const url = new URL(urlJoin(this.config.instanceUrl, `/api/v1/cells/${this.config.cellId}`, path));
2370
+ const url = new URL(urlJoin(this.config.instanceUrl, `/api/v2/cells/${this.config.cellId}`, path));
1341
2371
  url.protocol = url.protocol.replace("http", "ws");
1342
2372
  url.protocol = url.protocol.replace("https", "wss");
1343
2373
  if (this.accessToken) url.searchParams.append("token", this.accessToken);
@@ -1355,8 +2385,48 @@ var NovaClient = class {
1355
2385
  openReconnectingWebsocket(path) {
1356
2386
  return new AutoReconnectingWebsocket(this.makeWebsocketURL(path), { mock: this.mock });
1357
2387
  }
2388
+ /**
2389
+ * Connect to the motion state websocket(s) for a given motion group
2390
+ */
2391
+ async connectMotionStream(motionGroupId) {
2392
+ return await MotionStreamConnection.open(this, motionGroupId);
2393
+ }
2394
+ /**
2395
+ * Connect to the jogging websocket(s) for a given motion group
2396
+ */
2397
+ async connectJogger(motionGroupId, options = {}) {
2398
+ return await JoggerConnection.open(this, motionGroupId, options);
2399
+ }
2400
+ async connectMotionGroups(motionGroupIds) {
2401
+ return Promise.all(motionGroupIds.map((motionGroupId) => ConnectedMotionGroup.connect(this, motionGroupId)));
2402
+ }
2403
+ async connectMotionGroup(motionGroupId) {
2404
+ return (await this.connectMotionGroups([motionGroupId]))[0];
2405
+ }
1358
2406
  };
1359
2407
 
1360
2408
  //#endregion
1361
- export { NovaCellAPIClient, NovaClient };
2409
+ //#region src/lib/v2/wandelscriptUtils.ts
2410
+ /**
2411
+ * Convert a Pose object representing a motion group position
2412
+ * into a string which represents that pose in Wandelscript.
2413
+ */
2414
+ function poseToWandelscriptString(pose) {
2415
+ const position = [
2416
+ pose.position?.[0] ?? 0,
2417
+ pose.position?.[1] ?? 0,
2418
+ pose.position?.[2] ?? 0
2419
+ ];
2420
+ const orientation = [
2421
+ pose.orientation?.[0] ?? 0,
2422
+ pose.orientation?.[1] ?? 0,
2423
+ pose.orientation?.[2] ?? 0
2424
+ ];
2425
+ const positionValues = position.map((v) => v.toFixed(1));
2426
+ const rotationValues = orientation.map((v) => v.toFixed(4));
2427
+ return `(${positionValues.concat(rotationValues).join(", ")})`;
2428
+ }
2429
+
2430
+ //#endregion
2431
+ export { ConnectedMotionGroup, JoggerConnection, MotionStreamConnection, NovaCellAPIClient, NovaClient, poseToWandelscriptString };
1362
2432
  //# sourceMappingURL=index.js.map