@wandelbots/nova-js 3.2.0-pr.dev-e2e-jogging-test.143.34230ab → 3.2.0-pr.feat-v2.155.c906166
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.
- package/README.md +60 -50
- package/dist/{AutoReconnectingWebsocket-BI1ckzP8.d.ts → AutoReconnectingWebsocket-CoU4ZyD2.d.cts} +1 -3
- package/dist/AutoReconnectingWebsocket-CoU4ZyD2.d.cts.map +1 -0
- package/dist/{AutoReconnectingWebsocket-Cr7f9016.d.cts → AutoReconnectingWebsocket-D0gTrkzu.d.ts} +1 -3
- package/dist/AutoReconnectingWebsocket-D0gTrkzu.d.ts.map +1 -0
- package/dist/{LoginWithAuth0-0g0wWRUC.js → LoginWithAuth0-CaX7yo7d.js} +58 -5
- package/dist/LoginWithAuth0-CaX7yo7d.js.map +1 -0
- package/dist/{LoginWithAuth0-C82OCyDy.cjs → LoginWithAuth0-DaPnTz2I.cjs} +99 -4
- package/dist/LoginWithAuth0-DaPnTz2I.cjs.map +1 -0
- package/dist/index.cjs +11 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -8
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +11 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/lib/v1/index.cjs +17 -19
- package/dist/lib/v1/index.cjs.map +1 -1
- package/dist/lib/v1/index.d.cts +4 -2
- package/dist/lib/v1/index.d.cts.map +1 -1
- package/dist/lib/v1/index.d.ts +4 -2
- package/dist/lib/v1/index.d.ts.map +1 -1
- package/dist/lib/v1/index.js +7 -10
- package/dist/lib/v1/index.js.map +1 -1
- package/dist/lib/v2/index.cjs +1087 -11
- package/dist/lib/v2/index.cjs.map +1 -1
- package/dist/lib/v2/index.d.cts +256 -7
- package/dist/lib/v2/index.d.cts.map +1 -1
- package/dist/lib/v2/index.d.ts +256 -7
- package/dist/lib/v2/index.d.ts.map +1 -1
- package/dist/lib/v2/index.js +1082 -12
- package/dist/lib/v2/index.js.map +1 -1
- package/dist/wandelscriptUtils-CO5GYRij.js +24 -0
- package/dist/wandelscriptUtils-CO5GYRij.js.map +1 -0
- package/dist/wandelscriptUtils-COHpTIme.d.cts +12 -0
- package/dist/wandelscriptUtils-COHpTIme.d.cts.map +1 -0
- package/dist/wandelscriptUtils-Cl3GBxOp.d.ts +12 -0
- package/dist/wandelscriptUtils-Cl3GBxOp.d.ts.map +1 -0
- package/dist/wandelscriptUtils-DwpJ4jCy.cjs +30 -0
- package/dist/wandelscriptUtils-DwpJ4jCy.cjs.map +1 -0
- package/package.json +2 -2
- package/src/LoginWithAuth0.ts +3 -3
- package/src/index.ts +1 -0
- package/src/lib/converters.ts +5 -23
- package/src/lib/v1/MotionStreamConnection.ts +1 -1
- package/src/lib/v1/NovaClient.ts +6 -0
- package/src/lib/v1/index.ts +6 -0
- package/src/lib/v1/mock/MockNovaInstance.ts +0 -1
- package/src/lib/v1/wandelscriptUtils.ts +22 -0
- package/src/lib/v2/ConnectedMotionGroup.ts +415 -0
- package/src/lib/v2/JoggerConnection.ts +647 -0
- package/src/lib/v2/MotionStreamConnection.ts +222 -0
- package/src/lib/v2/NovaClient.ts +46 -8
- package/src/lib/v2/index.ts +5 -0
- package/src/lib/v2/mock/MockNovaInstance.ts +385 -1
- package/src/lib/v2/motionStateUpdate.ts +76 -0
- package/src/lib/v2/types/vector3.ts +1 -0
- package/src/lib/v2/wandelscriptUtils.ts +27 -0
- package/dist/AutoReconnectingWebsocket-BI1ckzP8.d.ts.map +0 -1
- package/dist/AutoReconnectingWebsocket-Cr7f9016.d.cts.map +0 -1
- package/dist/LoginWithAuth0-0g0wWRUC.js.map +0 -1
- package/dist/LoginWithAuth0-C82OCyDy.cjs.map +0 -1
- package/dist/converters-DP2EIVv6.cjs +0 -108
- package/dist/converters-DP2EIVv6.cjs.map +0 -1
- package/dist/converters-DY6Lf7mb.js +0 -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
|
+
}
|
package/src/lib/v2/NovaClient.ts
CHANGED
|
@@ -8,6 +8,12 @@ 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 {
|
|
13
|
+
JoggerConnection,
|
|
14
|
+
type JoggerConnectionOptions,
|
|
15
|
+
} from "./JoggerConnection"
|
|
16
|
+
import { ConnectedMotionGroup } from "./ConnectedMotionGroup"
|
|
11
17
|
|
|
12
18
|
export type NovaClientConfig = {
|
|
13
19
|
/**
|
|
@@ -51,10 +57,6 @@ function permissiveInstanceUrlParse(url: string): string {
|
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
/**
|
|
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
60
|
*
|
|
59
61
|
* Client for connecting to a Nova instance and controlling robots.
|
|
60
62
|
*/
|
|
@@ -66,12 +68,10 @@ export class NovaClient {
|
|
|
66
68
|
accessToken: string | null = null
|
|
67
69
|
|
|
68
70
|
constructor(config: NovaClientConfig) {
|
|
69
|
-
console.warn("Using experimental NOVA v2 client")
|
|
70
71
|
const cellId = config.cellId ?? "cell"
|
|
71
72
|
this.config = {
|
|
72
73
|
cellId,
|
|
73
74
|
...config,
|
|
74
|
-
instanceUrl: permissiveInstanceUrlParse(config.instanceUrl),
|
|
75
75
|
}
|
|
76
76
|
this.accessToken =
|
|
77
77
|
config.accessToken ||
|
|
@@ -80,6 +80,10 @@ export class NovaClient {
|
|
|
80
80
|
|
|
81
81
|
if (this.config.instanceUrl === "https://mock.example.com") {
|
|
82
82
|
this.mock = new MockNovaInstance()
|
|
83
|
+
} else {
|
|
84
|
+
this.config.instanceUrl = permissiveInstanceUrlParse(
|
|
85
|
+
this.config.instanceUrl,
|
|
86
|
+
)
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
// Set up Axios instance with interceptor for token fetching
|
|
@@ -146,7 +150,7 @@ export class NovaClient {
|
|
|
146
150
|
|
|
147
151
|
this.api = new NovaCellAPIClient(cellId, {
|
|
148
152
|
...config,
|
|
149
|
-
basePath: urlJoin(this.config.instanceUrl, "/api/
|
|
153
|
+
basePath: urlJoin(this.config.instanceUrl, "/api/v2"),
|
|
150
154
|
isJsonMime: (mime: string) => {
|
|
151
155
|
return mime === "application/json"
|
|
152
156
|
},
|
|
@@ -188,7 +192,7 @@ export class NovaClient {
|
|
|
188
192
|
const url = new URL(
|
|
189
193
|
urlJoin(
|
|
190
194
|
this.config.instanceUrl,
|
|
191
|
-
`/api/
|
|
195
|
+
`/api/v2/cells/${this.config.cellId}`,
|
|
192
196
|
path,
|
|
193
197
|
),
|
|
194
198
|
)
|
|
@@ -218,4 +222,38 @@ export class NovaClient {
|
|
|
218
222
|
mock: this.mock,
|
|
219
223
|
})
|
|
220
224
|
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Connect to the motion state websocket(s) for a given motion group
|
|
228
|
+
*/
|
|
229
|
+
async connectMotionStream(motionGroupId: string) {
|
|
230
|
+
return await MotionStreamConnection.open(this, motionGroupId)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Connect to the jogging websocket(s) for a given motion group
|
|
235
|
+
*/
|
|
236
|
+
async connectJogger(
|
|
237
|
+
motionGroupId: string,
|
|
238
|
+
options: JoggerConnectionOptions = {},
|
|
239
|
+
) {
|
|
240
|
+
return await JoggerConnection.open(this, motionGroupId, options)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async connectMotionGroups(
|
|
244
|
+
motionGroupIds: string[],
|
|
245
|
+
): Promise<ConnectedMotionGroup[]> {
|
|
246
|
+
return Promise.all(
|
|
247
|
+
motionGroupIds.map((motionGroupId) =>
|
|
248
|
+
ConnectedMotionGroup.connect(this, motionGroupId),
|
|
249
|
+
),
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async connectMotionGroup(
|
|
254
|
+
motionGroupId: string,
|
|
255
|
+
): Promise<ConnectedMotionGroup> {
|
|
256
|
+
const motionGroups = await this.connectMotionGroups([motionGroupId])
|
|
257
|
+
return motionGroups[0]!
|
|
258
|
+
}
|
|
221
259
|
}
|
package/src/lib/v2/index.ts
CHANGED
|
@@ -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"
|