@wandelbots/nova-js 2.1.4 → 3.0.0

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 (40) hide show
  1. package/README.md +29 -1
  2. package/dist/{chunk-6WCKJOFL.js → chunk-4FP7NTKS.js} +51 -1
  3. package/dist/chunk-4FP7NTKS.js.map +1 -0
  4. package/dist/{chunk-V3NJLR6P.js → chunk-DOFCSS2H.js} +1 -51
  5. package/dist/chunk-DOFCSS2H.js.map +1 -0
  6. package/dist/index.js +8 -8
  7. package/dist/lib/v1/index.js +6 -6
  8. package/dist/lib/v2/NovaCellAPIClient.d.ts +9 -11
  9. package/dist/lib/v2/NovaCellAPIClient.d.ts.map +1 -1
  10. package/dist/lib/v2/NovaClient.d.ts +0 -8
  11. package/dist/lib/v2/NovaClient.d.ts.map +1 -1
  12. package/dist/lib/v2/index.cjs +28 -771
  13. package/dist/lib/v2/index.cjs.map +1 -1
  14. package/dist/lib/v2/index.d.ts +0 -4
  15. package/dist/lib/v2/index.d.ts.map +1 -1
  16. package/dist/lib/v2/index.js +32 -764
  17. package/dist/lib/v2/index.js.map +1 -1
  18. package/dist/lib/v2/mock/MockNovaInstance.d.ts.map +1 -1
  19. package/package.json +2 -2
  20. package/src/lib/v2/NovaCellAPIClient.ts +22 -22
  21. package/src/lib/v2/NovaClient.ts +0 -28
  22. package/src/lib/v2/index.ts +0 -4
  23. package/src/lib/v2/mock/MockNovaInstance.ts +9 -32
  24. package/dist/chunk-6WCKJOFL.js.map +0 -1
  25. package/dist/chunk-V3NJLR6P.js.map +0 -1
  26. package/dist/lib/v2/ConnectedMotionGroup.d.ts +0 -41
  27. package/dist/lib/v2/ConnectedMotionGroup.d.ts.map +0 -1
  28. package/dist/lib/v2/JoggerConnection.d.ts +0 -53
  29. package/dist/lib/v2/JoggerConnection.d.ts.map +0 -1
  30. package/dist/lib/v2/MotionStreamConnection.d.ts +0 -25
  31. package/dist/lib/v2/MotionStreamConnection.d.ts.map +0 -1
  32. package/dist/lib/v2/ProgramStateConnection.d.ts +0 -53
  33. package/dist/lib/v2/ProgramStateConnection.d.ts.map +0 -1
  34. package/dist/lib/v2/motionStateUpdate.d.ts +0 -4
  35. package/dist/lib/v2/motionStateUpdate.d.ts.map +0 -1
  36. package/src/lib/v2/ConnectedMotionGroup.ts +0 -216
  37. package/src/lib/v2/JoggerConnection.ts +0 -207
  38. package/src/lib/v2/MotionStreamConnection.ts +0 -201
  39. package/src/lib/v2/ProgramStateConnection.ts +0 -249
  40. package/src/lib/v2/motionStateUpdate.ts +0 -55
@@ -1,216 +0,0 @@
1
- import type {
2
- Controller,
3
- MotionGroupPhysical,
4
- MotionGroupSpecification,
5
- MotionGroupState,
6
- RobotTcp,
7
- SafetySetup,
8
- } from "@wandelbots/nova-api/v2"
9
- import { AxiosError } from "axios"
10
- import { makeAutoObservable, runInAction } from "mobx"
11
- import type { AutoReconnectingWebsocket } from "../AutoReconnectingWebsocket"
12
- import { tryParseJson } from "../converters"
13
- import { jointValuesEqual, tcpPoseEqual } from "./motionStateUpdate"
14
- import type { NovaClient } from "./NovaClient"
15
-
16
- const MOTION_DELTA_THRESHOLD = 0.0001
17
-
18
- export type MotionGroupOption = {
19
- selectionId: string
20
- } & MotionGroupPhysical
21
-
22
- /**
23
- * Store representing the current state of a connected motion group.
24
- */
25
- export class ConnectedMotionGroup {
26
- static async connect(
27
- nova: NovaClient,
28
- motionGroupId: string,
29
- controllers: Controller[],
30
- ) {
31
- const [_motionGroupIndex, controllerId] = motionGroupId.split("@") as [
32
- string,
33
- string,
34
- ]
35
- const controller = controllers.find((c) => c.controller === controllerId)
36
- const motionGroup = controller?.motion_groups.find(
37
- (mg) => mg.motion_group === motionGroupId,
38
- )
39
- if (!controller || !motionGroup) {
40
- throw new Error(
41
- `Controller ${controllerId} or motion group ${motionGroupId} not found`,
42
- )
43
- }
44
-
45
- const motionStateSocket = nova.openReconnectingWebsocket(
46
- `/motion-groups/${motionGroupId}/state-stream`,
47
- )
48
-
49
- // Wait for the first message to get the initial state
50
- const firstMessage = await motionStateSocket.firstMessage()
51
- const initialMotionState = tryParseJson(firstMessage.data)
52
- ?.result as MotionGroupState
53
-
54
- if (!initialMotionState) {
55
- throw new Error(
56
- `Unable to parse initial motion state message ${firstMessage.data}`,
57
- )
58
- }
59
-
60
- console.log(
61
- `Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:\n `,
62
- initialMotionState,
63
- )
64
-
65
- // This is used to determine if the robot is virtual or physical
66
- let isVirtual = false
67
- try {
68
- const opMode =
69
- await nova.api.virtualRobotMode.getOperationMode(controllerId)
70
-
71
- if (opMode) isVirtual = true
72
- } catch (err) {
73
- if (err instanceof AxiosError) {
74
- console.log(
75
- `Received ${err.status} from getOperationMode, concluding that ${controllerId} is physical`,
76
- )
77
- } else {
78
- throw err
79
- }
80
- }
81
-
82
- // Find out what TCPs this motion group has (we need it for jogging)
83
- const { tcps } = await nova.api.motionGroupInfos.listTcps(motionGroupId)
84
-
85
- const motionGroupSpecification =
86
- await nova.api.motionGroupInfos.getMotionGroupSpecification(motionGroupId)
87
-
88
- const safetySetup =
89
- await nova.api.motionGroupInfos.getSafetySetup(motionGroupId)
90
-
91
- return new ConnectedMotionGroup(
92
- nova,
93
- controller,
94
- motionGroup,
95
- initialMotionState,
96
- motionStateSocket,
97
- isVirtual,
98
- tcps!,
99
- motionGroupSpecification,
100
- safetySetup,
101
- )
102
- }
103
-
104
- connectedJoggingCartesianSocket: WebSocket | null = null
105
- connectedJoggingJointsSocket: WebSocket | null = null
106
- planData: any | null // tmp
107
- joggingVelocity: number = 10
108
-
109
- // Not mobx-observable as this changes very fast; should be observed
110
- // using animation frames
111
- rapidlyChangingMotionState: MotionGroupState
112
-
113
- constructor(
114
- readonly nova: NovaClient,
115
- readonly controller: Controller,
116
- readonly motionGroup: MotionGroupPhysical,
117
- readonly initialMotionState: MotionGroupState,
118
- readonly motionStateSocket: AutoReconnectingWebsocket,
119
- readonly isVirtual: boolean,
120
- readonly tcps: RobotTcp[],
121
- readonly motionGroupSpecification: MotionGroupSpecification,
122
- readonly safetySetup: SafetySetup,
123
- ) {
124
- this.rapidlyChangingMotionState = initialMotionState
125
-
126
- motionStateSocket.addEventListener("message", (event) => {
127
- const motionStateResponse = tryParseJson(event.data)?.result as
128
- | MotionGroupState
129
- | undefined
130
-
131
- if (!motionStateResponse) {
132
- throw new Error(
133
- `Failed to get motion state for ${this.motionGroupId}: ${event.data}`,
134
- )
135
- }
136
-
137
- // handle motionState message
138
- if (
139
- !jointValuesEqual(
140
- this.rapidlyChangingMotionState.joint_position.joints,
141
- motionStateResponse.joint_position.joints,
142
- MOTION_DELTA_THRESHOLD,
143
- )
144
- ) {
145
- runInAction(() => {
146
- this.rapidlyChangingMotionState = motionStateResponse
147
- })
148
- }
149
-
150
- // handle tcpPose message
151
- if (
152
- !tcpPoseEqual(
153
- this.rapidlyChangingMotionState.tcp_pose,
154
- motionStateResponse.tcp_pose,
155
- MOTION_DELTA_THRESHOLD,
156
- )
157
- ) {
158
- runInAction(() => {
159
- this.rapidlyChangingMotionState.tcp_pose =
160
- motionStateResponse.tcp_pose
161
- })
162
- }
163
- })
164
- makeAutoObservable(this)
165
- }
166
-
167
- get motionGroupId() {
168
- return this.motionGroup.motion_group
169
- }
170
-
171
- get controllerId() {
172
- return this.controller.controller
173
- }
174
-
175
- get modelFromController() {
176
- return this.motionGroup.model_from_controller
177
- }
178
-
179
- get wandelscriptIdentifier() {
180
- const num = this.motionGroupId.split("@")[0]
181
- return `${this.controllerId.replaceAll("-", "_")}_${num}`
182
- }
183
-
184
- /** Jogging velocity in radians for rotation and joint movement */
185
- get joggingVelocityRads() {
186
- return (this.joggingVelocity * Math.PI) / 180
187
- }
188
-
189
- get joints() {
190
- return this.initialMotionState.joint_position.joints.map((_, i) => {
191
- return {
192
- index: i,
193
- }
194
- })
195
- }
196
-
197
- get dhParameters() {
198
- return this.motionGroupSpecification.dh_parameters
199
- }
200
-
201
- get safetyZones() {
202
- return this.safetySetup.safety_zones
203
- }
204
-
205
- dispose() {
206
- this.motionStateSocket.close()
207
- if (this.connectedJoggingCartesianSocket)
208
- this.connectedJoggingCartesianSocket.close()
209
- if (this.connectedJoggingJointsSocket)
210
- this.connectedJoggingJointsSocket.close()
211
- }
212
-
213
- setJoggingVelocity(velocity: number) {
214
- this.joggingVelocity = velocity
215
- }
216
- }
@@ -1,207 +0,0 @@
1
- import type {
2
- InitializeJoggingRequest,
3
- JoggingResponse,
4
- JointVelocityRequest,
5
- MotionGroupState,
6
- TcpVelocityRequest,
7
- } from "@wandelbots/nova-api/v2"
8
- import type { AutoReconnectingWebsocket } from "../AutoReconnectingWebsocket"
9
- import { tryParseJson } from "../converters"
10
- import type { NovaClient } from "./NovaClient"
11
- import { axisToIndex } from "./vectorUtils"
12
-
13
- export type JoggerConnectionOpts = {
14
- /**
15
- * When an error message is received from the jogging websocket, it
16
- * will be passed here. If this handler is not provided, the error will
17
- * instead be thrown as an unhandled error.
18
- */
19
- onError?: (err: unknown) => void
20
- } & Omit<InitializeJoggingRequest, "message_type">
21
-
22
- export class JoggerConnection {
23
- // Currently a separate websocket is needed for each mode, pester API people
24
- // to merge these for simplicity
25
- joggingWebsocket: AutoReconnectingWebsocket | null = null
26
- lastVelocityRequest: JointVelocityRequest | TcpVelocityRequest | null = null
27
- lastResponse: JoggingResponse | null = null
28
-
29
- static async open(
30
- nova: NovaClient,
31
- cell: string,
32
- motionGroupId: string,
33
- opts: JoggerConnectionOpts,
34
- ) {
35
- const motionGroupState =
36
- await nova.api.motionGroupInfos.getCurrentMotionGroupState(motionGroupId)
37
-
38
- return new JoggerConnection(
39
- nova,
40
- cell,
41
- motionGroupId,
42
- motionGroupState,
43
- opts,
44
- )
45
- }
46
-
47
- constructor(
48
- readonly nova: NovaClient,
49
- readonly cell: string,
50
- readonly motionGroupId: string,
51
- readonly motionGroupState: MotionGroupState,
52
- readonly opts: JoggerConnectionOpts,
53
- ) {
54
- this.joggingWebsocket = nova.openReconnectingWebsocket(
55
- `/cells/${cell}/execution/jogging`,
56
- )
57
- this.joggingWebsocket.addEventListener("message", (ev: MessageEvent) => {
58
- const data = tryParseJson(ev.data) as JoggingResponse
59
- if (data && "error" in data) {
60
- if (this.opts.onError) {
61
- this.opts.onError(ev.data)
62
- } else {
63
- throw new Error(ev.data)
64
- }
65
- }
66
- })
67
-
68
- this.joggingWebsocket.addEventListener("message", (ev: MessageEvent) => {
69
- const data = tryParseJson(ev.data)
70
- if (data && "error" in data) {
71
- if (this.opts.onError) {
72
- this.opts.onError(ev.data)
73
- } else {
74
- throw new Error(ev.data)
75
- }
76
- }
77
- })
78
- }
79
-
80
- get jointCount() {
81
- return this.motionGroupState.joint_current?.joints.length
82
- }
83
-
84
- get activeWebsocket() {
85
- return this.joggingWebsocket
86
- }
87
-
88
- async stop() {
89
- // Why not call the stopJogging API endpoint?
90
- // Because this results in the websocket closing and we
91
- // would like to keep it open for now.
92
- if (!this.joggingWebsocket) {
93
- return
94
- }
95
-
96
- if (this.lastVelocityRequest?.message_type === "JointVelocityRequest") {
97
- this.joggingWebsocket.sendJson({
98
- message_type: "JointVelocityRequest",
99
- velocity: {
100
- joints: Array.from(new Array(this.jointCount).keys()).map(() => 0),
101
- },
102
- } as JointVelocityRequest)
103
- }
104
-
105
- if (this.lastVelocityRequest?.message_type === "TcpVelocityRequest") {
106
- this.joggingWebsocket.sendJson({
107
- message_type: "TcpVelocityRequest",
108
- rotation: [0, 0, 0],
109
- translation: [0, 0, 0],
110
- } as TcpVelocityRequest)
111
- }
112
- }
113
-
114
- dispose() {
115
- if (this.joggingWebsocket) {
116
- this.joggingWebsocket.dispose()
117
- }
118
- }
119
-
120
- /**
121
- * Start rotation of a single robot joint at the specified velocity
122
- */
123
- async startJointRotation({
124
- joint,
125
- velocityRadsPerSec,
126
- }: {
127
- /** Index of the joint to rotate */
128
- joint: number
129
- /** Speed of the rotation in radians per second */
130
- velocityRadsPerSec: number
131
- }) {
132
- if (!this.joggingWebsocket) {
133
- throw new Error(
134
- "Joint jogging websocket not connected. Wait for reconnect or open new jogging connection",
135
- )
136
- }
137
-
138
- const jointVelocities = new Array(this.jointCount).fill(0)
139
- jointVelocities[joint] = velocityRadsPerSec
140
-
141
- this.joggingWebsocket.sendJson({
142
- message_type: "JointVelocityRequest",
143
- velocity: {
144
- joints: jointVelocities,
145
- },
146
- } as JointVelocityRequest)
147
- }
148
-
149
- /**
150
- * Start the TCP moving along a specified axis at a given velocity
151
- */
152
- async startTCPTranslation({
153
- axis,
154
- velocityMmPerSec,
155
- useToolCoordinateSystem,
156
- }: {
157
- axis: "x" | "y" | "z"
158
- velocityMmPerSec: number
159
- useToolCoordinateSystem: boolean
160
- }) {
161
- if (!this.joggingWebsocket) {
162
- throw new Error(
163
- "Cartesian jogging websocket not connected. Wait for reconnect or open new jogging connection",
164
- )
165
- }
166
-
167
- const joggingVector = [0, 0, 0]
168
- joggingVector[axisToIndex(axis)] = velocityMmPerSec
169
-
170
- this.joggingWebsocket.sendJson({
171
- message_type: "TcpVelocityRequest",
172
-
173
- translation: joggingVector,
174
- rotation: [0, 0, 0],
175
- use_tool_coordinate_system: useToolCoordinateSystem,
176
- } as TcpVelocityRequest)
177
- }
178
-
179
- /**
180
- * Start the TCP rotating around a specified axis at a given velocity
181
- */
182
- async startTCPRotation({
183
- axis,
184
- velocityRadsPerSec,
185
- useToolCoordinateSystem,
186
- }: {
187
- axis: "x" | "y" | "z"
188
- velocityRadsPerSec: number
189
- useToolCoordinateSystem: boolean
190
- }) {
191
- if (!this.joggingWebsocket) {
192
- throw new Error(
193
- "Cartesian jogging websocket not connected. Wait for reconnect or open new jogging connection",
194
- )
195
- }
196
-
197
- const joggingVector = [0, 0, 0]
198
- joggingVector[axisToIndex(axis)] = velocityRadsPerSec
199
-
200
- this.joggingWebsocket.sendJson({
201
- message_type: "TcpVelocityRequest",
202
- rotation: joggingVector,
203
- translation: [0, 0, 0],
204
- use_tool_coordinate_system: useToolCoordinateSystem,
205
- } as TcpVelocityRequest)
206
- }
207
- }
@@ -1,201 +0,0 @@
1
- import type {
2
- Controller,
3
- MotionGroupPhysical,
4
- MotionGroupState,
5
- } from "@wandelbots/nova-api/v2"
6
- import { makeAutoObservable, runInAction } from "mobx"
7
- import * as THREE from "three"
8
- import type { AutoReconnectingWebsocket } from "../AutoReconnectingWebsocket"
9
- import { tryParseJson } from "../converters"
10
- import { jointValuesEqual, tcpPoseEqual } from "./motionStateUpdate"
11
- import type { NovaClient } from "./NovaClient"
12
- import { Vector3d, vector3ToArray } from "./vectorUtils"
13
-
14
- const MOTION_DELTA_THRESHOLD = 0.0001
15
-
16
- function unwrapRotationVector(
17
- newRotationVectorApi: Vector3d,
18
- currentRotationVectorApi: Vector3d,
19
- ): Vector3d {
20
- const currentRotationVector = new THREE.Vector3(
21
- currentRotationVectorApi[0],
22
- currentRotationVectorApi[1],
23
- currentRotationVectorApi[2],
24
- )
25
-
26
- const newRotationVector = new THREE.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 vector3ToArray(newAxis.multiplyScalar(newAngle))
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 { controllers: controllers } =
61
- await nova.api.controller.listControllers()
62
-
63
- const [_motionGroupIndex, controllerId] = motionGroupId.split("@") as [
64
- string,
65
- string,
66
- ]
67
- const controller = controllers.find((c) => c.controller === controllerId)
68
- const motionGroup = controller?.motion_groups.find(
69
- (mg) => mg.motion_group === motionGroupId,
70
- )
71
- if (!controller || !motionGroup) {
72
- throw new Error(
73
- `Controller ${controllerId} or motion group ${motionGroupId} not found`,
74
- )
75
- }
76
-
77
- const motionStateSocket = nova.openReconnectingWebsocket(
78
- `/motion-groups/${motionGroupId}/state-stream`,
79
- )
80
-
81
- // Wait for the first message to get the initial state
82
- const firstMessage = await motionStateSocket.firstMessage()
83
- console.log("got first message", firstMessage)
84
- const initialMotionState = tryParseJson(firstMessage.data)
85
- ?.result as MotionGroupState
86
-
87
- if (!initialMotionState) {
88
- throw new Error(
89
- `Unable to parse initial motion state message ${firstMessage.data}`,
90
- )
91
- }
92
-
93
- console.log(
94
- `Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:\n `,
95
- initialMotionState,
96
- )
97
-
98
- return new MotionStreamConnection(
99
- nova,
100
- controller,
101
- motionGroup,
102
- initialMotionState,
103
- motionStateSocket,
104
- )
105
- }
106
-
107
- // Not mobx-observable as this changes very fast; should be observed
108
- // using animation frames
109
- rapidlyChangingMotionState: MotionGroupState
110
-
111
- constructor(
112
- readonly nova: NovaClient,
113
- readonly controller: Controller,
114
- readonly motionGroup: MotionGroupPhysical,
115
- readonly initialMotionState: MotionGroupState,
116
- readonly motionStateSocket: AutoReconnectingWebsocket,
117
- ) {
118
- this.rapidlyChangingMotionState = initialMotionState
119
-
120
- motionStateSocket.addEventListener("message", (event) => {
121
- const motionState = tryParseJson(event.data)?.result as
122
- | MotionGroupState
123
- | undefined
124
-
125
- if (!motionState) {
126
- throw new Error(
127
- `Failed to get motion state for ${this.motionGroupId}: ${event.data}`,
128
- )
129
- }
130
-
131
- // handle motionState message
132
- if (
133
- !jointValuesEqual(
134
- this.rapidlyChangingMotionState.joint_position.joints,
135
- motionState.joint_position.joints,
136
- MOTION_DELTA_THRESHOLD,
137
- )
138
- ) {
139
- runInAction(() => {
140
- this.rapidlyChangingMotionState = motionState
141
- })
142
- }
143
-
144
- // handle tcpPose message
145
- if (
146
- !tcpPoseEqual(
147
- this.rapidlyChangingMotionState.tcp_pose,
148
- motionState.tcp_pose,
149
- MOTION_DELTA_THRESHOLD,
150
- )
151
- ) {
152
- runInAction(() => {
153
- if (this.rapidlyChangingMotionState.tcp_pose == undefined) {
154
- this.rapidlyChangingMotionState.tcp_pose = motionState.tcp_pose
155
- } else {
156
- this.rapidlyChangingMotionState.tcp_pose = {
157
- position: motionState.tcp_pose!.position,
158
- orientation: unwrapRotationVector(
159
- motionState.tcp_pose!.orientation as Vector3d,
160
- this.rapidlyChangingMotionState.tcp_pose!
161
- .orientation as Vector3d,
162
- ),
163
- tcp: motionState.tcp_pose!.tcp,
164
- coordinate_system: motionState.tcp_pose!.coordinate_system,
165
- }
166
- }
167
- })
168
- }
169
- })
170
- makeAutoObservable(this)
171
- }
172
-
173
- get motionGroupId() {
174
- return this.motionGroup.motion_group
175
- }
176
-
177
- get controllerId() {
178
- return this.controller.controller
179
- }
180
-
181
- get modelFromController() {
182
- return this.motionGroup.model_from_controller
183
- }
184
-
185
- get wandelscriptIdentifier() {
186
- const num = this.motionGroupId.split("@")[0]
187
- return `${this.controllerId.replaceAll("-", "_")}_${num}`
188
- }
189
-
190
- get joints() {
191
- return this.initialMotionState.joint_position.joints.map((_, i) => {
192
- return {
193
- index: i,
194
- }
195
- })
196
- }
197
-
198
- dispose() {
199
- this.motionStateSocket.close()
200
- }
201
- }