@wandelbots/nova-js 1.17.1-pr.feat-added-v2-client.64.9ac2247

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 (95) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +202 -0
  3. package/dist/LoginWithAuth0.d.ts +7 -0
  4. package/dist/LoginWithAuth0.d.ts.map +1 -0
  5. package/dist/chunk-V3NJLR6P.js +336 -0
  6. package/dist/chunk-V3NJLR6P.js.map +1 -0
  7. package/dist/index.cjs +390 -0
  8. package/dist/index.cjs.map +1 -0
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +54 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/AutoReconnectingWebsocket.d.ts +43 -0
  14. package/dist/lib/AutoReconnectingWebsocket.d.ts.map +1 -0
  15. package/dist/lib/availableStorage.d.ts +15 -0
  16. package/dist/lib/availableStorage.d.ts.map +1 -0
  17. package/dist/lib/converters.d.ts +26 -0
  18. package/dist/lib/converters.d.ts.map +1 -0
  19. package/dist/lib/errorHandling.d.ts +4 -0
  20. package/dist/lib/errorHandling.d.ts.map +1 -0
  21. package/dist/lib/v1/ConnectedMotionGroup.d.ts +77 -0
  22. package/dist/lib/v1/ConnectedMotionGroup.d.ts.map +1 -0
  23. package/dist/lib/v1/JoggerConnection.d.ts +94 -0
  24. package/dist/lib/v1/JoggerConnection.d.ts.map +1 -0
  25. package/dist/lib/v1/MotionStreamConnection.d.ts +25 -0
  26. package/dist/lib/v1/MotionStreamConnection.d.ts.map +1 -0
  27. package/dist/lib/v1/NovaCellAPIClient.d.ts +66 -0
  28. package/dist/lib/v1/NovaCellAPIClient.d.ts.map +1 -0
  29. package/dist/lib/v1/NovaClient.d.ts +67 -0
  30. package/dist/lib/v1/NovaClient.d.ts.map +1 -0
  31. package/dist/lib/v1/ProgramStateConnection.d.ts +53 -0
  32. package/dist/lib/v1/ProgramStateConnection.d.ts.map +1 -0
  33. package/dist/lib/v1/getLatestTrajectories.d.ts +4 -0
  34. package/dist/lib/v1/getLatestTrajectories.d.ts.map +1 -0
  35. package/dist/lib/v1/index.cjs +3957 -0
  36. package/dist/lib/v1/index.cjs.map +1 -0
  37. package/dist/lib/v1/index.d.ts +9 -0
  38. package/dist/lib/v1/index.d.ts.map +1 -0
  39. package/dist/lib/v1/index.js +3662 -0
  40. package/dist/lib/v1/index.js.map +1 -0
  41. package/dist/lib/v1/mock/MockNovaInstance.d.ts +13 -0
  42. package/dist/lib/v1/mock/MockNovaInstance.d.ts.map +1 -0
  43. package/dist/lib/v1/motionStateUpdate.d.ts +4 -0
  44. package/dist/lib/v1/motionStateUpdate.d.ts.map +1 -0
  45. package/dist/lib/v2/ConnectedMotionGroup.d.ts +41 -0
  46. package/dist/lib/v2/ConnectedMotionGroup.d.ts.map +1 -0
  47. package/dist/lib/v2/JoggerConnection.d.ts +53 -0
  48. package/dist/lib/v2/JoggerConnection.d.ts.map +1 -0
  49. package/dist/lib/v2/MotionStreamConnection.d.ts +25 -0
  50. package/dist/lib/v2/MotionStreamConnection.d.ts.map +1 -0
  51. package/dist/lib/v2/NovaCellAPIClient.d.ts +64 -0
  52. package/dist/lib/v2/NovaCellAPIClient.d.ts.map +1 -0
  53. package/dist/lib/v2/NovaClient.d.ts +67 -0
  54. package/dist/lib/v2/NovaClient.d.ts.map +1 -0
  55. package/dist/lib/v2/ProgramStateConnection.d.ts +53 -0
  56. package/dist/lib/v2/ProgramStateConnection.d.ts.map +1 -0
  57. package/dist/lib/v2/index.cjs +2239 -0
  58. package/dist/lib/v2/index.cjs.map +1 -0
  59. package/dist/lib/v2/index.d.ts +8 -0
  60. package/dist/lib/v2/index.d.ts.map +1 -0
  61. package/dist/lib/v2/index.js +1947 -0
  62. package/dist/lib/v2/index.js.map +1 -0
  63. package/dist/lib/v2/mock/MockNovaInstance.d.ts +13 -0
  64. package/dist/lib/v2/mock/MockNovaInstance.d.ts.map +1 -0
  65. package/dist/lib/v2/motionStateUpdate.d.ts +4 -0
  66. package/dist/lib/v2/motionStateUpdate.d.ts.map +1 -0
  67. package/dist/lib/v2/vectorUtils.d.ts +7 -0
  68. package/dist/lib/v2/vectorUtils.d.ts.map +1 -0
  69. package/package.json +67 -0
  70. package/src/LoginWithAuth0.ts +90 -0
  71. package/src/index.ts +5 -0
  72. package/src/lib/AutoReconnectingWebsocket.ts +163 -0
  73. package/src/lib/availableStorage.ts +46 -0
  74. package/src/lib/converters.ts +74 -0
  75. package/src/lib/errorHandling.ts +26 -0
  76. package/src/lib/v1/ConnectedMotionGroup.ts +419 -0
  77. package/src/lib/v1/JoggerConnection.ts +480 -0
  78. package/src/lib/v1/MotionStreamConnection.ts +202 -0
  79. package/src/lib/v1/NovaCellAPIClient.ts +180 -0
  80. package/src/lib/v1/NovaClient.ts +232 -0
  81. package/src/lib/v1/ProgramStateConnection.ts +267 -0
  82. package/src/lib/v1/getLatestTrajectories.ts +36 -0
  83. package/src/lib/v1/index.ts +8 -0
  84. package/src/lib/v1/mock/MockNovaInstance.ts +1302 -0
  85. package/src/lib/v1/motionStateUpdate.ts +55 -0
  86. package/src/lib/v2/ConnectedMotionGroup.ts +216 -0
  87. package/src/lib/v2/JoggerConnection.ts +207 -0
  88. package/src/lib/v2/MotionStreamConnection.ts +201 -0
  89. package/src/lib/v2/NovaCellAPIClient.ts +174 -0
  90. package/src/lib/v2/NovaClient.ts +230 -0
  91. package/src/lib/v2/ProgramStateConnection.ts +255 -0
  92. package/src/lib/v2/index.ts +7 -0
  93. package/src/lib/v2/mock/MockNovaInstance.ts +982 -0
  94. package/src/lib/v2/motionStateUpdate.ts +55 -0
  95. package/src/lib/v2/vectorUtils.ts +36 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Safety wrapper around browser localStorage providing context availability
3
+ * checks and JSON parsing
4
+ */
5
+ class AvailableStorage {
6
+ available = typeof window !== "undefined" && !!window.localStorage
7
+
8
+ getJSON<T>(key: string): Partial<T> | null {
9
+ if (!this.available) return null
10
+
11
+ const result = window.localStorage.getItem(key)
12
+ if (result === null) return null
13
+
14
+ try {
15
+ return JSON.parse(result)
16
+ } catch (err) {
17
+ return null
18
+ }
19
+ }
20
+
21
+ setJSON(key: string, obj: any) {
22
+ if (!this.available) return null
23
+
24
+ window.localStorage.setItem(key, JSON.stringify(obj))
25
+ }
26
+
27
+ delete(key: string) {
28
+ if (!this.available) return null
29
+
30
+ window.localStorage.removeItem(key)
31
+ }
32
+
33
+ setString(key: string, value: string) {
34
+ if (!this.available) return null
35
+
36
+ window.localStorage.setItem(key, value)
37
+ }
38
+
39
+ getString(key: string): string | null {
40
+ if (!this.available) return null
41
+
42
+ return window.localStorage.getItem(key)
43
+ }
44
+ }
45
+
46
+ export const availableStorage = new AvailableStorage()
@@ -0,0 +1,74 @@
1
+ import type { Pose } from "@wandelbots/nova-api/v1"
2
+
3
+ /** Try to parse something as JSON; return undefined if we can't */
4
+ export function tryParseJson(json: unknown): any {
5
+ try {
6
+ return JSON.parse(json as string)
7
+ } catch {
8
+ return undefined
9
+ }
10
+ }
11
+
12
+ /** Try to turn something into JSON; return undefined if we can't */
13
+ export function tryStringifyJson(json: unknown): string | undefined {
14
+ try {
15
+ return JSON.stringify(json)
16
+ } catch {
17
+ return undefined
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Converts object parameters to query string.
23
+ * e.g. { a: "1", b: "2" } => "?a=1&b=2"
24
+ * {} => ""
25
+ */
26
+ export function makeUrlQueryString(obj: Record<string, string>): string {
27
+ const str = new URLSearchParams(obj).toString()
28
+ return str ? `?${str}` : ""
29
+ }
30
+
31
+ /** Convert radians to degrees */
32
+ export function radiansToDegrees(radians: number): number {
33
+ return radians * (180 / Math.PI)
34
+ }
35
+
36
+ /** Convert degrees to radians */
37
+ export function degreesToRadians(degrees: number): number {
38
+ return degrees * (Math.PI / 180)
39
+ }
40
+
41
+ /**
42
+ * Convert a Pose object representing a motion group position
43
+ * into a string which represents that pose in Wandelscript.
44
+ */
45
+ export function poseToWandelscriptString(
46
+ pose: Pick<Pose, "position" | "orientation">,
47
+ ) {
48
+ const position = [pose.position.x, pose.position.y, pose.position.z]
49
+ const orientation = [
50
+ pose.orientation?.x ?? 0,
51
+ pose.orientation?.y ?? 0,
52
+ pose.orientation?.z ?? 0,
53
+ ]
54
+
55
+ const positionValues = position.map((v) => v.toFixed(1))
56
+ // Rotation needs more precision since it's in radians
57
+ const rotationValues = orientation.map((v) => v.toFixed(4))
58
+
59
+ return `(${positionValues.concat(rotationValues).join(", ")})`
60
+ }
61
+
62
+ /**
63
+ * Check for coordinate system id equivalence, accounting for the "world" default
64
+ * on empty/undefined values.
65
+ */
66
+ export function isSameCoordinateSystem(
67
+ firstCoordSystem: string | undefined,
68
+ secondCoordSystem: string | undefined,
69
+ ) {
70
+ if (!firstCoordSystem) firstCoordSystem = "world"
71
+ if (!secondCoordSystem) secondCoordSystem = "world"
72
+
73
+ return firstCoordSystem === secondCoordSystem
74
+ }
@@ -0,0 +1,26 @@
1
+ import { AxiosError } from "axios"
2
+ import { tryStringifyJson } from "./converters"
3
+
4
+ export function delay(ms: number) {
5
+ return new Promise((resolve) => setTimeout(resolve, ms))
6
+ }
7
+
8
+ export function makeShortErrorMessage(err: unknown) {
9
+ if (err instanceof AxiosError && err.response) {
10
+ return `${err.response?.status} ${err.response?.statusText}: ${JSON.stringify(err.response?.data)}`
11
+ } else if (err instanceof Error) {
12
+ return err.message
13
+ } else {
14
+ return `Unexpected error: ${err}`
15
+ }
16
+ }
17
+
18
+ export function makeErrorMessage(err: unknown) {
19
+ if (err instanceof AxiosError && err.response) {
20
+ return `${err.response?.status} ${err.response?.statusText} from ${err.response?.config.url}: ${JSON.stringify(err.response?.data)}`
21
+ } else if (err instanceof Error) {
22
+ return err.message
23
+ } else {
24
+ return `Unexpected error: ${tryStringifyJson(err) || err}`
25
+ }
26
+ }
@@ -0,0 +1,419 @@
1
+ import type {
2
+ ControllerInstance,
3
+ MotionGroupPhysical,
4
+ MotionGroupSpecification,
5
+ MotionGroupStateResponse,
6
+ Mounting,
7
+ RobotControllerState,
8
+ RobotControllerStateOperationModeEnum,
9
+ RobotControllerStateSafetyStateEnum,
10
+ RobotTcp,
11
+ SafetySetup,
12
+ } from "@wandelbots/nova-api/v1"
13
+ import { makeAutoObservable, runInAction } from "mobx"
14
+ import * as THREE from "three"
15
+ import type { AutoReconnectingWebsocket } from "../AutoReconnectingWebsocket"
16
+ import { tryParseJson } from "../converters"
17
+ import { jointValuesEqual, tcpPoseEqual } from "./motionStateUpdate"
18
+ import type { NovaClient } from "./NovaClient"
19
+
20
+ const MOTION_DELTA_THRESHOLD = 0.0001
21
+
22
+ export type MotionGroupOption = {
23
+ selectionId: string
24
+ } & MotionGroupPhysical
25
+
26
+ /**
27
+ * Store representing the current state of a connected motion group.
28
+ */
29
+ export class ConnectedMotionGroup {
30
+ static async connect(
31
+ nova: NovaClient,
32
+ motionGroupId: string,
33
+ controllers: ControllerInstance[],
34
+ ) {
35
+ const [_motionGroupIndex, controllerId] = motionGroupId.split("@") as [
36
+ string,
37
+ string,
38
+ ]
39
+ const controller = controllers.find((c) => c.controller === controllerId)
40
+ const motionGroup = controller?.physical_motion_groups.find(
41
+ (mg) => mg.motion_group === motionGroupId,
42
+ )
43
+ if (!controller || !motionGroup) {
44
+ throw new Error(
45
+ `Controller ${controllerId} or motion group ${motionGroupId} not found`,
46
+ )
47
+ }
48
+
49
+ const motionStateSocket = nova.openReconnectingWebsocket(
50
+ `/motion-groups/${motionGroupId}/state-stream`,
51
+ )
52
+
53
+ // Wait for the first message to get the initial state
54
+ const firstMessage = await motionStateSocket.firstMessage()
55
+ const initialMotionState = tryParseJson(firstMessage.data)
56
+ ?.result as MotionGroupStateResponse
57
+
58
+ if (!initialMotionState) {
59
+ throw new Error(
60
+ `Unable to parse initial motion state message ${firstMessage.data}`,
61
+ )
62
+ }
63
+
64
+ console.log(
65
+ `Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:\n `,
66
+ initialMotionState,
67
+ )
68
+
69
+ // Check if robot is virtual or physical
70
+ const config = await nova.api.controller.getRobotController(
71
+ controller.controller,
72
+ )
73
+ const isVirtual = config.configuration.kind === "VirtualController"
74
+
75
+ // If there's a configured mounting, we need it to show the right
76
+ // position of the robot model
77
+ const mounting = await (async () => {
78
+ try {
79
+ const mounting = await nova.api.motionGroupInfos.getMounting(
80
+ motionGroup.motion_group,
81
+ )
82
+ return mounting
83
+ } catch (err) {
84
+ console.error(
85
+ `Error fetching mounting for ${motionGroup.motion_group}`,
86
+ err,
87
+ )
88
+ return null
89
+ }
90
+ })()
91
+
92
+ // Open the websocket to monitor controller state for e.g. e-stop
93
+ const controllerStateSocket = nova.openReconnectingWebsocket(
94
+ `/controllers/${controller.controller}/state-stream?response_rate=1000`,
95
+ )
96
+
97
+ // Wait for the first message to get the initial state
98
+ const firstControllerMessage = await controllerStateSocket.firstMessage()
99
+ const initialControllerState = tryParseJson(firstControllerMessage.data)
100
+ ?.result as RobotControllerState
101
+
102
+ if (!initialControllerState) {
103
+ throw new Error(
104
+ `Unable to parse initial controller state message ${firstControllerMessage.data}`,
105
+ )
106
+ }
107
+
108
+ console.log(
109
+ `Connected controller state websocket to controller ${controller.controller}. Initial state:\n `,
110
+ initialControllerState,
111
+ )
112
+
113
+ // Find out what TCPs this motion group has (we need it for jogging)
114
+ const { tcps } = await nova.api.motionGroupInfos.listTcps(motionGroupId)
115
+
116
+ const motionGroupSpecification =
117
+ await nova.api.motionGroupInfos.getMotionGroupSpecification(motionGroupId)
118
+
119
+ const safetySetup =
120
+ await nova.api.motionGroupInfos.getSafetySetup(motionGroupId)
121
+
122
+ return new ConnectedMotionGroup(
123
+ nova,
124
+ controller,
125
+ motionGroup,
126
+ initialMotionState,
127
+ motionStateSocket,
128
+ isVirtual,
129
+ tcps!,
130
+ motionGroupSpecification,
131
+ safetySetup,
132
+ mounting,
133
+ initialControllerState,
134
+ controllerStateSocket,
135
+ )
136
+ }
137
+
138
+ connectedJoggingCartesianSocket: WebSocket | null = null
139
+ connectedJoggingJointsSocket: WebSocket | null = null
140
+ planData: any | null // tmp
141
+ joggingVelocity: number = 10
142
+
143
+ // Not mobx-observable as this changes very fast; should be observed
144
+ // using animation frames
145
+ rapidlyChangingMotionState: MotionGroupStateResponse
146
+
147
+ // Response rate on the websocket should be a bit slower on this one since
148
+ // we don't use the motion data
149
+ controllerState: RobotControllerState
150
+
151
+ /**
152
+ * Reflects activation state of the motion group / robot servos. The
153
+ * movement controls in the UI should only be enabled in the "active" state
154
+ */
155
+ activationState: "inactive" | "activating" | "deactivating" | "active" =
156
+ "inactive"
157
+
158
+ constructor(
159
+ readonly nova: NovaClient,
160
+ readonly controller: ControllerInstance,
161
+ readonly motionGroup: MotionGroupPhysical,
162
+ readonly initialMotionState: MotionGroupStateResponse,
163
+ readonly motionStateSocket: AutoReconnectingWebsocket,
164
+ readonly isVirtual: boolean,
165
+ readonly tcps: RobotTcp[],
166
+ readonly motionGroupSpecification: MotionGroupSpecification,
167
+ readonly safetySetup: SafetySetup,
168
+ readonly mounting: Mounting | null,
169
+ readonly initialControllerState: RobotControllerState,
170
+ readonly controllerStateSocket: AutoReconnectingWebsocket,
171
+ ) {
172
+ this.rapidlyChangingMotionState = initialMotionState
173
+ this.controllerState = initialControllerState
174
+
175
+ // Track controller state updates (e.g. safety state and operation mode)
176
+ controllerStateSocket.addEventListener("message", (event) => {
177
+ const data = tryParseJson(event.data)?.result
178
+
179
+ if (!data) {
180
+ return
181
+ }
182
+
183
+ runInAction(() => {
184
+ this.controllerState = data
185
+ })
186
+ })
187
+
188
+ motionStateSocket.addEventListener("message", (event) => {
189
+ const motionStateResponse = tryParseJson(event.data)?.result as
190
+ | MotionGroupStateResponse
191
+ | undefined
192
+
193
+ if (!motionStateResponse) {
194
+ throw new Error(
195
+ `Failed to get motion state for ${this.motionGroupId}: ${event.data}`,
196
+ )
197
+ }
198
+
199
+ // handle motionState message
200
+ if (
201
+ !jointValuesEqual(
202
+ this.rapidlyChangingMotionState.state.joint_position.joints,
203
+ motionStateResponse.state.joint_position.joints,
204
+ MOTION_DELTA_THRESHOLD,
205
+ )
206
+ ) {
207
+ runInAction(() => {
208
+ this.rapidlyChangingMotionState.state = motionStateResponse.state
209
+ })
210
+ }
211
+
212
+ // handle tcpPose message
213
+ if (
214
+ !tcpPoseEqual(
215
+ this.rapidlyChangingMotionState.tcp_pose,
216
+ motionStateResponse.tcp_pose,
217
+ MOTION_DELTA_THRESHOLD,
218
+ )
219
+ ) {
220
+ runInAction(() => {
221
+ this.rapidlyChangingMotionState.tcp_pose =
222
+ motionStateResponse.tcp_pose
223
+ })
224
+ }
225
+ })
226
+ makeAutoObservable(this)
227
+ }
228
+
229
+ get motionGroupId() {
230
+ return this.motionGroup.motion_group
231
+ }
232
+
233
+ get controllerId() {
234
+ return this.controller.controller
235
+ }
236
+
237
+ get modelFromController() {
238
+ return this.motionGroup.model_from_controller
239
+ }
240
+
241
+ get wandelscriptIdentifier() {
242
+ const num = this.motionGroupId.split("@")[0]
243
+ return `${this.controllerId.replaceAll("-", "_")}_${num}`
244
+ }
245
+
246
+ /** Jogging velocity in radians for rotation and joint movement */
247
+ get joggingVelocityRads() {
248
+ return (this.joggingVelocity * Math.PI) / 180
249
+ }
250
+
251
+ get joints() {
252
+ return this.initialMotionState.state.joint_position.joints.map((_, i) => {
253
+ return {
254
+ index: i,
255
+ }
256
+ })
257
+ }
258
+
259
+ get dhParameters() {
260
+ return this.motionGroupSpecification.dh_parameters
261
+ }
262
+
263
+ get safetyZones() {
264
+ return this.safetySetup.safety_zones
265
+ }
266
+
267
+ /** Gets the robot mounting position offset in 3D viz coordinates */
268
+ get mountingPosition(): [number, number, number] {
269
+ if (!this.mounting) {
270
+ return [0, 0, 0]
271
+ }
272
+
273
+ return [
274
+ this.mounting.pose.position.x / 1000,
275
+ this.mounting.pose.position.y / 1000,
276
+ this.mounting.pose.position.z / 1000,
277
+ ]
278
+ }
279
+
280
+ /** Gets the robot mounting position rotation in 3D viz coordinates */
281
+ get mountingQuaternion() {
282
+ const rotationVector = new THREE.Vector3(
283
+ this.mounting?.pose.orientation?.x || 0,
284
+ this.mounting?.pose.orientation?.y || 0,
285
+ this.mounting?.pose.orientation?.z || 0,
286
+ )
287
+
288
+ const magnitude = rotationVector.length()
289
+ const axis = rotationVector.normalize()
290
+
291
+ return new THREE.Quaternion().setFromAxisAngle(axis, magnitude)
292
+ }
293
+
294
+ /**
295
+ * Whether the controller is currently in a safety state
296
+ * corresponding to an emergency stop
297
+ */
298
+ get isEstopActive() {
299
+ const estopStates: RobotControllerStateSafetyStateEnum[] = [
300
+ "SAFETY_STATE_ROBOT_EMERGENCY_STOP",
301
+ "SAFETY_STATE_DEVICE_EMERGENCY_STOP",
302
+ ]
303
+
304
+ return estopStates.includes(this.controllerState.safety_state)
305
+ }
306
+
307
+ /**
308
+ * Whether the controller is in a safety state
309
+ * that may be non-functional for robot pad purposes
310
+ */
311
+ get isMoveableSafetyState() {
312
+ const goodSafetyStates: RobotControllerStateSafetyStateEnum[] = [
313
+ "SAFETY_STATE_NORMAL",
314
+ "SAFETY_STATE_REDUCED",
315
+ ]
316
+
317
+ return goodSafetyStates.includes(this.controllerState.safety_state)
318
+ }
319
+
320
+ /**
321
+ * Whether the controller is in an operation mode that allows movement
322
+ */
323
+ get isMoveableOperationMode() {
324
+ const goodOperationModes: RobotControllerStateOperationModeEnum[] = [
325
+ "OPERATION_MODE_AUTO",
326
+ "OPERATION_MODE_MANUAL",
327
+ "OPERATION_MODE_MANUAL_T1",
328
+ "OPERATION_MODE_MANUAL_T2",
329
+ ]
330
+
331
+ return goodOperationModes.includes(this.controllerState.operation_mode)
332
+ }
333
+
334
+ /**
335
+ * Whether the robot is currently active and can be moved, based on the
336
+ * safety state, operation mode and servo toggle activation state.
337
+ */
338
+ get canBeMoved() {
339
+ return (
340
+ this.isMoveableSafetyState &&
341
+ this.isMoveableOperationMode &&
342
+ this.activationState === "active"
343
+ )
344
+ }
345
+
346
+ async deactivate() {
347
+ if (this.activationState !== "active") {
348
+ console.error("Tried to deactivate while already deactivating")
349
+ return
350
+ }
351
+
352
+ runInAction(() => {
353
+ this.activationState = "deactivating"
354
+ })
355
+
356
+ try {
357
+ await this.nova.api.controller.setDefaultMode(
358
+ this.controllerId,
359
+ "MODE_MONITOR",
360
+ )
361
+
362
+ runInAction(() => {
363
+ this.activationState = "inactive"
364
+ })
365
+ } catch (err) {
366
+ runInAction(() => {
367
+ this.activationState = "active"
368
+ })
369
+ throw err
370
+ }
371
+ }
372
+
373
+ async activate() {
374
+ if (this.activationState !== "inactive") {
375
+ console.error("Tried to activate while already activating")
376
+ return
377
+ }
378
+
379
+ runInAction(() => {
380
+ this.activationState = "activating"
381
+ })
382
+
383
+ try {
384
+ await this.nova.api.controller.setDefaultMode(
385
+ this.controllerId,
386
+ "MODE_CONTROL",
387
+ )
388
+
389
+ runInAction(() => {
390
+ this.activationState = "active"
391
+ })
392
+ } catch (err) {
393
+ runInAction(() => {
394
+ this.activationState = "inactive"
395
+ })
396
+ throw err
397
+ }
398
+ }
399
+
400
+ toggleActivation() {
401
+ if (this.activationState === "inactive") {
402
+ this.activate()
403
+ } else if (this.activationState === "active") {
404
+ this.deactivate()
405
+ }
406
+ }
407
+
408
+ dispose() {
409
+ this.motionStateSocket.close()
410
+ if (this.connectedJoggingCartesianSocket)
411
+ this.connectedJoggingCartesianSocket.close()
412
+ if (this.connectedJoggingJointsSocket)
413
+ this.connectedJoggingJointsSocket.close()
414
+ }
415
+
416
+ setJoggingVelocity(velocity: number) {
417
+ this.joggingVelocity = velocity
418
+ }
419
+ }