@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
@@ -0,0 +1,222 @@
1
+ import type {
2
+ MotionGroupDescription,
3
+ MotionGroupState,
4
+ RobotControllerState,
5
+ } from "@wandelbots/nova-api/v2"
6
+ import { makeAutoObservable, runInAction } from "mobx"
7
+ import { Vector3 } from "three"
8
+ import type { AutoReconnectingWebsocket } from "../AutoReconnectingWebsocket"
9
+ import { tryParseJson } from "../converters"
10
+ import { jointValuesEqual, tcpMotionEqual } from "./motionStateUpdate"
11
+ import type { NovaClient } from "./NovaClient"
12
+ import type { Vector3Simple } from "./types/vector3"
13
+
14
+ const MOTION_DELTA_THRESHOLD = 0.0001
15
+
16
+ function unwrapRotationVector(
17
+ newRotationVectorApi: Vector3Simple,
18
+ currentRotationVectorApi: Vector3Simple,
19
+ ): Vector3Simple {
20
+ const currentRotationVector = new Vector3(
21
+ currentRotationVectorApi[0],
22
+ currentRotationVectorApi[1],
23
+ currentRotationVectorApi[2],
24
+ )
25
+
26
+ const newRotationVector = new Vector3(
27
+ newRotationVectorApi[0],
28
+ newRotationVectorApi[1],
29
+ newRotationVectorApi[2],
30
+ )
31
+
32
+ const currentAngle = currentRotationVector.length()
33
+ const currentAxis = currentRotationVector.normalize()
34
+
35
+ let newAngle = newRotationVector.length()
36
+ let newAxis = newRotationVector.normalize()
37
+
38
+ // Align rotation axes
39
+ if (newAxis.dot(currentAxis) < 0) {
40
+ newAngle = -newAngle
41
+ newAxis = newAxis.multiplyScalar(-1.0)
42
+ }
43
+
44
+ // Shift rotation angle close to previous one to extend domain of rotation angles beyond [0, pi]
45
+ // - this simplifies interpolation and prevents abruptly changing signs of the rotation angles
46
+ let angleDifference = newAngle - currentAngle
47
+ angleDifference -=
48
+ 2.0 * Math.PI * Math.floor((angleDifference + Math.PI) / (2.0 * Math.PI))
49
+
50
+ newAngle = currentAngle + angleDifference
51
+
52
+ return [...newAxis.multiplyScalar(newAngle)] as Vector3Simple
53
+ }
54
+
55
+ /**
56
+ * Store representing the current state of a connected motion group.
57
+ */
58
+ export class MotionStreamConnection {
59
+ static async open(nova: NovaClient, motionGroupId: string) {
60
+ const [_motionGroupIndex, controllerId] = motionGroupId.split("@") as [
61
+ string,
62
+ string,
63
+ ]
64
+
65
+ const controller =
66
+ await nova.api.controller.getCurrentRobotControllerState(controllerId)
67
+ const motionGroup = controller?.motion_groups.find(
68
+ (mg) => mg.motion_group === motionGroupId,
69
+ )
70
+ if (!controller || !motionGroup) {
71
+ throw new Error(
72
+ `Controller ${controllerId} or motion group ${motionGroupId} not found`,
73
+ )
74
+ }
75
+
76
+ const motionStateSocket = nova.openReconnectingWebsocket(
77
+ `/controllers/${controllerId}/motion-groups/${motionGroupId}/state-stream`,
78
+ )
79
+
80
+ // Wait for the first message to get the initial state
81
+ const firstMessage = await motionStateSocket.firstMessage()
82
+ const initialMotionState = tryParseJson(firstMessage.data)
83
+ ?.result as MotionGroupState
84
+
85
+ if (!initialMotionState) {
86
+ throw new Error(
87
+ `Unable to parse initial motion state message ${firstMessage.data}`,
88
+ )
89
+ }
90
+
91
+ console.log(
92
+ `Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:\n `,
93
+ initialMotionState,
94
+ )
95
+
96
+ // Get the motion group description for later usage in jogging
97
+ const description = await nova.api.motionGroup.getMotionGroupDescription(
98
+ controllerId,
99
+ motionGroup.motion_group,
100
+ )
101
+
102
+ return new MotionStreamConnection(
103
+ nova,
104
+ controller,
105
+ motionGroup,
106
+ description,
107
+ initialMotionState,
108
+ motionStateSocket,
109
+ )
110
+ }
111
+
112
+ // Not mobx-observable as this changes very fast; should be observed
113
+ // using animation frames
114
+ rapidlyChangingMotionState: MotionGroupState
115
+
116
+ constructor(
117
+ readonly nova: NovaClient,
118
+ readonly controller: RobotControllerState,
119
+ readonly motionGroup: MotionGroupState,
120
+ readonly description: MotionGroupDescription,
121
+ readonly initialMotionState: MotionGroupState,
122
+ readonly motionStateSocket: AutoReconnectingWebsocket,
123
+ ) {
124
+ this.rapidlyChangingMotionState = initialMotionState
125
+
126
+ motionStateSocket.addEventListener("message", (event) => {
127
+ const latestMotionState = tryParseJson(event.data)?.result as
128
+ | MotionGroupState
129
+ | undefined
130
+
131
+ if (!latestMotionState) {
132
+ throw new Error(
133
+ `Failed to get motion state for ${this.motionGroupId}: ${event.data}`,
134
+ )
135
+ }
136
+
137
+ // handle joint position changes
138
+ if (
139
+ !jointValuesEqual(
140
+ this.rapidlyChangingMotionState.joint_position,
141
+ latestMotionState.joint_position,
142
+ MOTION_DELTA_THRESHOLD,
143
+ )
144
+ ) {
145
+ runInAction(() => {
146
+ this.rapidlyChangingMotionState = latestMotionState
147
+ })
148
+ }
149
+
150
+ // handle tcp pose changes
151
+ if (
152
+ !tcpMotionEqual(
153
+ this.rapidlyChangingMotionState,
154
+ latestMotionState,
155
+ MOTION_DELTA_THRESHOLD,
156
+ )
157
+ ) {
158
+ runInAction(() => {
159
+ if (this.rapidlyChangingMotionState.tcp_pose == null) {
160
+ this.rapidlyChangingMotionState.tcp_pose =
161
+ latestMotionState.tcp_pose
162
+ } else if (
163
+ latestMotionState.tcp_pose?.orientation &&
164
+ latestMotionState.tcp_pose?.position &&
165
+ this.rapidlyChangingMotionState.tcp_pose?.orientation
166
+ ) {
167
+ this.rapidlyChangingMotionState.tcp_pose = {
168
+ position: latestMotionState.tcp_pose.position,
169
+ orientation: unwrapRotationVector(
170
+ latestMotionState.tcp_pose.orientation as Vector3Simple,
171
+ this.rapidlyChangingMotionState.tcp_pose
172
+ .orientation as Vector3Simple,
173
+ ),
174
+ }
175
+ } else {
176
+ console.warn(
177
+ "Received incomplete tcp_pose, ignoring",
178
+ latestMotionState.tcp_pose,
179
+ )
180
+ }
181
+ })
182
+ }
183
+
184
+ // handle standstill changes
185
+ if (
186
+ this.rapidlyChangingMotionState.standstill !==
187
+ latestMotionState.standstill
188
+ ) {
189
+ runInAction(() => {
190
+ this.rapidlyChangingMotionState.standstill =
191
+ latestMotionState.standstill
192
+ })
193
+ }
194
+ })
195
+ makeAutoObservable(this)
196
+ }
197
+
198
+ get motionGroupId() {
199
+ return this.motionGroup.motion_group
200
+ }
201
+
202
+ get controllerId() {
203
+ return this.controller.controller
204
+ }
205
+
206
+ get wandelscriptIdentifier() {
207
+ const num = this.motionGroupId.split("@")[0]
208
+ return `${this.controllerId.replaceAll("-", "_")}_${num}`
209
+ }
210
+
211
+ get joints() {
212
+ return this.initialMotionState.joint_position.map((_, i) => {
213
+ return {
214
+ index: i,
215
+ }
216
+ })
217
+ }
218
+
219
+ dispose() {
220
+ this.motionStateSocket.close()
221
+ }
222
+ }
@@ -8,6 +8,9 @@ import { AutoReconnectingWebsocket } from "../AutoReconnectingWebsocket"
8
8
  import { availableStorage } from "../availableStorage"
9
9
  import { MockNovaInstance } from "./mock/MockNovaInstance"
10
10
  import { NovaCellAPIClient } from "./NovaCellAPIClient"
11
+ import { MotionStreamConnection } from "./MotionStreamConnection"
12
+ import { JoggerConnection, type JoggerConnectionOptions } from "./JoggerConnection"
13
+ import { ConnectedMotionGroup } from "./ConnectedMotionGroup"
11
14
 
12
15
  export type NovaClientConfig = {
13
16
  /**
@@ -51,10 +54,6 @@ function permissiveInstanceUrlParse(url: string): string {
51
54
  }
52
55
 
53
56
  /**
54
- * EXPERIMENTAL
55
- *
56
- * This client provides a starting point to migrate NOVA api v2.
57
- * As v2 is still in development, this client has to be considered unstable
58
57
  *
59
58
  * Client for connecting to a Nova instance and controlling robots.
60
59
  */
@@ -66,12 +65,10 @@ export class NovaClient {
66
65
  accessToken: string | null = null
67
66
 
68
67
  constructor(config: NovaClientConfig) {
69
- console.warn("Using experimental NOVA v2 client")
70
68
  const cellId = config.cellId ?? "cell"
71
69
  this.config = {
72
70
  cellId,
73
71
  ...config,
74
- instanceUrl: permissiveInstanceUrlParse(config.instanceUrl),
75
72
  }
76
73
  this.accessToken =
77
74
  config.accessToken ||
@@ -80,6 +77,10 @@ export class NovaClient {
80
77
 
81
78
  if (this.config.instanceUrl === "https://mock.example.com") {
82
79
  this.mock = new MockNovaInstance()
80
+ } else {
81
+ this.config.instanceUrl = permissiveInstanceUrlParse(
82
+ this.config.instanceUrl,
83
+ )
83
84
  }
84
85
 
85
86
  // Set up Axios instance with interceptor for token fetching
@@ -146,7 +147,7 @@ export class NovaClient {
146
147
 
147
148
  this.api = new NovaCellAPIClient(cellId, {
148
149
  ...config,
149
- basePath: urlJoin(this.config.instanceUrl, "/api/v1"),
150
+ basePath: urlJoin(this.config.instanceUrl, "/api/v2"),
150
151
  isJsonMime: (mime: string) => {
151
152
  return mime === "application/json"
152
153
  },
@@ -188,7 +189,7 @@ export class NovaClient {
188
189
  const url = new URL(
189
190
  urlJoin(
190
191
  this.config.instanceUrl,
191
- `/api/v1/cells/${this.config.cellId}`,
192
+ `/api/v2/cells/${this.config.cellId}`,
192
193
  path,
193
194
  ),
194
195
  )
@@ -218,4 +219,38 @@ export class NovaClient {
218
219
  mock: this.mock,
219
220
  })
220
221
  }
222
+
223
+ /**
224
+ * Connect to the motion state websocket(s) for a given motion group
225
+ */
226
+ async connectMotionStream(motionGroupId: string) {
227
+ return await MotionStreamConnection.open(this, motionGroupId)
228
+ }
229
+
230
+ /**
231
+ * Connect to the jogging websocket(s) for a given motion group
232
+ */
233
+ async connectJogger(
234
+ motionGroupId: string,
235
+ options: JoggerConnectionOptions = {},
236
+ ) {
237
+ return await JoggerConnection.open(this, motionGroupId, options)
238
+ }
239
+
240
+ async connectMotionGroups(
241
+ motionGroupIds: string[],
242
+ ): Promise<ConnectedMotionGroup[]> {
243
+ return Promise.all(
244
+ motionGroupIds.map((motionGroupId) =>
245
+ ConnectedMotionGroup.connect(this, motionGroupId),
246
+ ),
247
+ )
248
+ }
249
+
250
+ async connectMotionGroup(
251
+ motionGroupId: string,
252
+ ): Promise<ConnectedMotionGroup> {
253
+ const motionGroups = await this.connectMotionGroups([motionGroupId])
254
+ return motionGroups[0]!
255
+ }
221
256
  }
@@ -1,3 +1,8 @@
1
1
  export * from "@wandelbots/nova-api/v2"
2
+ export * from "./types/vector3"
3
+ export * from "./ConnectedMotionGroup"
2
4
  export * from "./NovaCellAPIClient"
3
5
  export * from "./NovaClient"
6
+ export * from "./JoggerConnection"
7
+ export * from "./MotionStreamConnection"
8
+ export * from "./wandelscriptUtils"