@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.
- 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 +43 -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,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/
|
|
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/
|
|
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
|
}
|
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"
|