@wandelbots/nova-js
Version:
Official JS client for the Wandelbots API
1,444 lines (1,433 loc) • 87.8 kB
JavaScript
import {
isEqual_default
} from "../../chunk-6WCKJOFL.js";
import {
AutoReconnectingWebsocket,
__async,
__spreadProps,
__spreadValues,
availableStorage,
isSameCoordinateSystem,
loginWithAuth0,
tryParseJson
} from "../../chunk-V3NJLR6P.js";
// src/lib/v1/index.ts
export * from "@wandelbots/nova-api/v1";
// src/lib/v1/ConnectedMotionGroup.ts
import { makeAutoObservable, runInAction } from "mobx";
import * as THREE from "three";
// src/lib/v1/motionStateUpdate.ts
function jointValuesEqual(oldJointValues, newJointValues, changeDeltaThreshold) {
if (newJointValues.length !== oldJointValues.length) {
return true;
}
for (let jointIndex = 0; jointIndex < newJointValues.length; jointIndex++) {
if (Math.abs(newJointValues[jointIndex] - oldJointValues[jointIndex]) > changeDeltaThreshold) {
return false;
}
}
return true;
}
function tcpPoseEqual(oldTcp, newTcp, changeDeltaThreshold) {
if (oldTcp === void 0 && newTcp || oldTcp && newTcp === void 0) {
return false;
}
if (oldTcp === void 0 || newTcp === void 0) {
return true;
}
let changedDelta = 0;
changedDelta += Math.abs(oldTcp.orientation.x - newTcp.orientation.x);
changedDelta += Math.abs(oldTcp.orientation.y - newTcp.orientation.y);
changedDelta += Math.abs(oldTcp.orientation.z - newTcp.orientation.z);
changedDelta += Math.abs(oldTcp.position.x - newTcp.position.x);
changedDelta += Math.abs(oldTcp.position.y - newTcp.position.y);
changedDelta += Math.abs(oldTcp.position.z - newTcp.position.z);
if (changedDelta > changeDeltaThreshold) {
return false;
}
return oldTcp.coordinate_system === newTcp.coordinate_system && oldTcp.tcp === newTcp.tcp;
}
// src/lib/v1/ConnectedMotionGroup.ts
var MOTION_DELTA_THRESHOLD = 1e-4;
var ConnectedMotionGroup = class _ConnectedMotionGroup {
constructor(nova, controller, motionGroup, initialMotionState, motionStateSocket, isVirtual, tcps, motionGroupSpecification, safetySetup, mounting, initialControllerState, controllerStateSocket) {
this.nova = nova;
this.controller = controller;
this.motionGroup = motionGroup;
this.initialMotionState = initialMotionState;
this.motionStateSocket = motionStateSocket;
this.isVirtual = isVirtual;
this.tcps = tcps;
this.motionGroupSpecification = motionGroupSpecification;
this.safetySetup = safetySetup;
this.mounting = mounting;
this.initialControllerState = initialControllerState;
this.controllerStateSocket = controllerStateSocket;
this.connectedJoggingCartesianSocket = null;
this.connectedJoggingJointsSocket = null;
// tmp
this.joggingVelocity = 10;
/**
* Reflects activation state of the motion group / robot servos. The
* movement controls in the UI should only be enabled in the "active" state
*/
this.activationState = "inactive";
this.rapidlyChangingMotionState = initialMotionState;
this.controllerState = initialControllerState;
controllerStateSocket.addEventListener("message", (event) => {
var _a;
const data = (_a = tryParseJson(event.data)) == null ? void 0 : _a.result;
if (!data) {
return;
}
runInAction(() => {
this.controllerState = data;
});
});
motionStateSocket.addEventListener("message", (event) => {
var _a;
const motionStateResponse = (_a = tryParseJson(event.data)) == null ? void 0 : _a.result;
if (!motionStateResponse) {
throw new Error(
`Failed to get motion state for ${this.motionGroupId}: ${event.data}`
);
}
if (!jointValuesEqual(
this.rapidlyChangingMotionState.state.joint_position.joints,
motionStateResponse.state.joint_position.joints,
MOTION_DELTA_THRESHOLD
)) {
runInAction(() => {
this.rapidlyChangingMotionState.state = motionStateResponse.state;
});
}
if (!tcpPoseEqual(
this.rapidlyChangingMotionState.tcp_pose,
motionStateResponse.tcp_pose,
MOTION_DELTA_THRESHOLD
)) {
runInAction(() => {
this.rapidlyChangingMotionState.tcp_pose = motionStateResponse.tcp_pose;
});
}
});
makeAutoObservable(this);
}
static connect(nova, motionGroupId, controllers) {
return __async(this, null, function* () {
var _a, _b;
const [_motionGroupIndex, controllerId] = motionGroupId.split("@");
const controller = controllers.find((c) => c.controller === controllerId);
const motionGroup = controller == null ? void 0 : controller.physical_motion_groups.find(
(mg) => mg.motion_group === motionGroupId
);
if (!controller || !motionGroup) {
throw new Error(
`Controller ${controllerId} or motion group ${motionGroupId} not found`
);
}
const motionStateSocket = nova.openReconnectingWebsocket(
`/motion-groups/${motionGroupId}/state-stream`
);
const firstMessage = yield motionStateSocket.firstMessage();
const initialMotionState = (_a = tryParseJson(firstMessage.data)) == null ? void 0 : _a.result;
if (!initialMotionState) {
throw new Error(
`Unable to parse initial motion state message ${firstMessage.data}`
);
}
console.log(
`Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:
`,
initialMotionState
);
const config = yield nova.api.controller.getRobotController(
controller.controller
);
const isVirtual = config.configuration.kind === "VirtualController";
const mounting = yield (() => __async(this, null, function* () {
try {
const mounting2 = yield nova.api.motionGroupInfos.getMounting(
motionGroup.motion_group
);
return mounting2;
} catch (err) {
console.error(
`Error fetching mounting for ${motionGroup.motion_group}`,
err
);
return null;
}
}))();
const controllerStateSocket = nova.openReconnectingWebsocket(
`/controllers/${controller.controller}/state-stream?response_rate=1000`
);
const firstControllerMessage = yield controllerStateSocket.firstMessage();
const initialControllerState = (_b = tryParseJson(firstControllerMessage.data)) == null ? void 0 : _b.result;
if (!initialControllerState) {
throw new Error(
`Unable to parse initial controller state message ${firstControllerMessage.data}`
);
}
console.log(
`Connected controller state websocket to controller ${controller.controller}. Initial state:
`,
initialControllerState
);
const { tcps } = yield nova.api.motionGroupInfos.listTcps(motionGroupId);
const motionGroupSpecification = yield nova.api.motionGroupInfos.getMotionGroupSpecification(motionGroupId);
const safetySetup = yield nova.api.motionGroupInfos.getSafetySetup(motionGroupId);
return new _ConnectedMotionGroup(
nova,
controller,
motionGroup,
initialMotionState,
motionStateSocket,
isVirtual,
tcps,
motionGroupSpecification,
safetySetup,
mounting,
initialControllerState,
controllerStateSocket
);
});
}
get motionGroupId() {
return this.motionGroup.motion_group;
}
get controllerId() {
return this.controller.controller;
}
get modelFromController() {
return this.motionGroup.model_from_controller;
}
get wandelscriptIdentifier() {
const num = this.motionGroupId.split("@")[0];
return `${this.controllerId.replaceAll("-", "_")}_${num}`;
}
/** Jogging velocity in radians for rotation and joint movement */
get joggingVelocityRads() {
return this.joggingVelocity * Math.PI / 180;
}
get joints() {
return this.initialMotionState.state.joint_position.joints.map((_, i) => {
return {
index: i
};
});
}
get dhParameters() {
return this.motionGroupSpecification.dh_parameters;
}
get safetyZones() {
return this.safetySetup.safety_zones;
}
/** Gets the robot mounting position offset in 3D viz coordinates */
get mountingPosition() {
if (!this.mounting) {
return [0, 0, 0];
}
return [
this.mounting.pose.position.x / 1e3,
this.mounting.pose.position.y / 1e3,
this.mounting.pose.position.z / 1e3
];
}
/** Gets the robot mounting position rotation in 3D viz coordinates */
get mountingQuaternion() {
var _a, _b, _c, _d, _e, _f;
const rotationVector = new THREE.Vector3(
((_b = (_a = this.mounting) == null ? void 0 : _a.pose.orientation) == null ? void 0 : _b.x) || 0,
((_d = (_c = this.mounting) == null ? void 0 : _c.pose.orientation) == null ? void 0 : _d.y) || 0,
((_f = (_e = this.mounting) == null ? void 0 : _e.pose.orientation) == null ? void 0 : _f.z) || 0
);
const magnitude = rotationVector.length();
const axis = rotationVector.normalize();
return new THREE.Quaternion().setFromAxisAngle(axis, magnitude);
}
/**
* Whether the controller is currently in a safety state
* corresponding to an emergency stop
*/
get isEstopActive() {
const estopStates = [
"SAFETY_STATE_ROBOT_EMERGENCY_STOP",
"SAFETY_STATE_DEVICE_EMERGENCY_STOP"
];
return estopStates.includes(this.controllerState.safety_state);
}
/**
* Whether the controller is in a safety state
* that may be non-functional for robot pad purposes
*/
get isMoveableSafetyState() {
const goodSafetyStates = [
"SAFETY_STATE_NORMAL",
"SAFETY_STATE_REDUCED"
];
return goodSafetyStates.includes(this.controllerState.safety_state);
}
/**
* Whether the controller is in an operation mode that allows movement
*/
get isMoveableOperationMode() {
const goodOperationModes = [
"OPERATION_MODE_AUTO",
"OPERATION_MODE_MANUAL",
"OPERATION_MODE_MANUAL_T1",
"OPERATION_MODE_MANUAL_T2"
];
return goodOperationModes.includes(this.controllerState.operation_mode);
}
/**
* Whether the robot is currently active and can be moved, based on the
* safety state, operation mode and servo toggle activation state.
*/
get canBeMoved() {
return this.isMoveableSafetyState && this.isMoveableOperationMode && this.activationState === "active";
}
deactivate() {
return __async(this, null, function* () {
if (this.activationState !== "active") {
console.error("Tried to deactivate while already deactivating");
return;
}
runInAction(() => {
this.activationState = "deactivating";
});
try {
yield this.nova.api.controller.setDefaultMode(
this.controllerId,
"MODE_MONITOR"
);
runInAction(() => {
this.activationState = "inactive";
});
} catch (err) {
runInAction(() => {
this.activationState = "active";
});
throw err;
}
});
}
activate() {
return __async(this, null, function* () {
if (this.activationState !== "inactive") {
console.error("Tried to activate while already activating");
return;
}
runInAction(() => {
this.activationState = "activating";
});
try {
yield this.nova.api.controller.setDefaultMode(
this.controllerId,
"MODE_CONTROL"
);
runInAction(() => {
this.activationState = "active";
});
} catch (err) {
runInAction(() => {
this.activationState = "inactive";
});
throw err;
}
});
}
toggleActivation() {
if (this.activationState === "inactive") {
this.activate();
} else if (this.activationState === "active") {
this.deactivate();
}
}
dispose() {
this.motionStateSocket.close();
if (this.connectedJoggingCartesianSocket)
this.connectedJoggingCartesianSocket.close();
if (this.connectedJoggingJointsSocket)
this.connectedJoggingJointsSocket.close();
}
setJoggingVelocity(velocity) {
this.joggingVelocity = velocity;
}
};
// src/lib/v1/getLatestTrajectories.ts
var lastMotionIds = /* @__PURE__ */ new Set();
function getLatestTrajectories(apiClient, sampleTime = 50, responsesCoordinateSystem) {
return __async(this, null, function* () {
const newTrajectories = [];
try {
const motions = yield apiClient.motion.listMotions();
const currentMotionIds = new Set(motions.motions);
const newMotionIds = Array.from(currentMotionIds).filter(
(id) => !lastMotionIds.has(id)
);
for (const motionId of newMotionIds) {
const trajectory = yield apiClient.motion.getMotionTrajectory(
motionId,
sampleTime,
responsesCoordinateSystem
);
newTrajectories.push(trajectory);
}
lastMotionIds = currentMotionIds;
} catch (error) {
console.error("Failed to get latest trajectories:", error);
}
return newTrajectories;
});
}
// src/lib/v1/JoggerConnection.ts
import { Vector3 as Vector32 } from "three/src/math/Vector3.js";
var JoggerConnection = class _JoggerConnection {
constructor(motionStream, opts = {}) {
this.motionStream = motionStream;
this.opts = opts;
// Currently a separate websocket is needed for each mode, pester API people
// to merge these for simplicity
this.cartesianWebsocket = null;
this.jointWebsocket = null;
this.cartesianJoggingOpts = {};
}
static open(_0, _1) {
return __async(this, arguments, function* (nova, motionGroupId, opts = {}) {
const motionStream = yield nova.connectMotionStream(motionGroupId);
return new _JoggerConnection(motionStream, opts);
});
}
get motionGroupId() {
return this.motionStream.motionGroupId;
}
get nova() {
return this.motionStream.nova;
}
get numJoints() {
return this.motionStream.joints.length;
}
get activeJoggingMode() {
if (this.cartesianWebsocket) return "cartesian";
if (this.jointWebsocket) return "joint";
return "increment";
}
get activeWebsocket() {
return this.cartesianWebsocket || this.jointWebsocket;
}
stop() {
return __async(this, null, function* () {
if (this.cartesianWebsocket) {
this.cartesianWebsocket.sendJson({
motion_group: this.motionGroupId,
position_direction: { x: 0, y: 0, z: 0 },
rotation_direction: { x: 0, y: 0, z: 0 },
position_velocity: 0,
rotation_velocity: 0,
tcp: this.cartesianJoggingOpts.tcpId,
coordinate_system: this.cartesianJoggingOpts.coordSystemId
});
}
if (this.jointWebsocket) {
this.jointWebsocket.sendJson({
motion_group: this.motionGroupId,
joint_velocities: new Array(this.numJoints).fill(0)
});
}
});
}
dispose() {
if (this.cartesianWebsocket) {
this.cartesianWebsocket.dispose();
}
if (this.jointWebsocket) {
this.jointWebsocket.dispose();
}
}
setJoggingMode(mode, cartesianJoggingOpts) {
console.log("Setting jogging mode to", mode);
if (cartesianJoggingOpts) {
if (!isEqual_default(this.cartesianJoggingOpts, cartesianJoggingOpts)) {
if (this.cartesianWebsocket) {
this.cartesianWebsocket.dispose();
this.cartesianWebsocket = null;
}
}
this.cartesianJoggingOpts = cartesianJoggingOpts;
}
if (mode !== "cartesian" && this.cartesianWebsocket) {
this.cartesianWebsocket.dispose();
this.cartesianWebsocket = null;
}
if (mode !== "joint" && this.jointWebsocket) {
this.jointWebsocket.dispose();
this.jointWebsocket = null;
}
if (mode === "cartesian" && !this.cartesianWebsocket) {
this.cartesianWebsocket = this.nova.openReconnectingWebsocket(
`/motion-groups/move-tcp`
);
this.cartesianWebsocket.addEventListener(
"message",
(ev) => {
const data = tryParseJson(ev.data);
if (data && "error" in data) {
if (this.opts.onError) {
this.opts.onError(ev.data);
} else {
throw new Error(ev.data);
}
}
}
);
}
if (mode === "joint" && !this.jointWebsocket) {
this.jointWebsocket = this.nova.openReconnectingWebsocket(
`/motion-groups/move-joint`
);
this.jointWebsocket.addEventListener("message", (ev) => {
const data = tryParseJson(ev.data);
if (data && "error" in data) {
if (this.opts.onError) {
this.opts.onError(ev.data);
} else {
throw new Error(ev.data);
}
}
});
}
}
/**
* Start rotation of a single robot joint at the specified velocity
*/
startJointRotation(_0) {
return __async(this, arguments, function* ({
joint,
direction,
velocityRadsPerSec
}) {
if (!this.jointWebsocket) {
throw new Error(
"Joint jogging websocket not connected; call setJoggingMode first"
);
}
const jointVelocities = new Array(this.numJoints).fill(0);
jointVelocities[joint] = direction === "-" ? -velocityRadsPerSec : velocityRadsPerSec;
this.jointWebsocket.sendJson({
motion_group: this.motionGroupId,
joint_velocities: jointVelocities
});
});
}
/**
* Start the TCP moving along a specified axis at a given velocity
*/
startTCPTranslation(_0) {
return __async(this, arguments, function* ({
axis,
direction,
velocityMmPerSec
}) {
if (!this.cartesianWebsocket) {
throw new Error(
"Cartesian jogging websocket not connected; call setJoggingMode first"
);
}
const zeroVector = { x: 0, y: 0, z: 0 };
const joggingVector = Object.assign({}, zeroVector);
joggingVector[axis] = direction === "-" ? -1 : 1;
this.cartesianWebsocket.sendJson({
motion_group: this.motionGroupId,
position_direction: joggingVector,
rotation_direction: zeroVector,
position_velocity: velocityMmPerSec,
rotation_velocity: 0,
tcp: this.cartesianJoggingOpts.tcpId,
coordinate_system: this.cartesianJoggingOpts.coordSystemId
});
});
}
/**
* Start the TCP rotating around a specified axis at a given velocity
*/
startTCPRotation(_0) {
return __async(this, arguments, function* ({
axis,
direction,
velocityRadsPerSec
}) {
if (!this.cartesianWebsocket) {
throw new Error(
"Cartesian jogging websocket not connected; call setJoggingMode first"
);
}
const zeroVector = { x: 0, y: 0, z: 0 };
const joggingVector = Object.assign({}, zeroVector);
joggingVector[axis] = direction === "-" ? -1 : 1;
this.cartesianWebsocket.sendJson({
motion_group: this.motionGroupId,
position_direction: zeroVector,
rotation_direction: joggingVector,
position_velocity: 0,
rotation_velocity: velocityRadsPerSec,
tcp: this.cartesianJoggingOpts.tcpId,
coordinate_system: this.cartesianJoggingOpts.coordSystemId
});
});
}
/**
* Move the robot by a fixed distance in a single cartesian
* axis, either rotating or translating relative to the TCP.
* Promise resolves only after the motion has completed.
*/
runIncrementalCartesianMotion(_0) {
return __async(this, arguments, function* ({
currentTcpPose,
currentJoints,
coordSystemId,
velocityInRelevantUnits,
axis,
direction,
motion
}) {
var _a;
const commands = [];
if (!isSameCoordinateSystem(currentTcpPose.coordinate_system, coordSystemId)) {
throw new Error(
`Current TCP pose coordinate system ${currentTcpPose.coordinate_system} does not match target coordinate system ${coordSystemId}`
);
}
if (motion.type === "translate") {
const targetTcpPosition = Object.assign({}, currentTcpPose.position);
targetTcpPosition[axis] += motion.distanceMm * (direction === "-" ? -1 : 1);
commands.push({
settings: {
limits_override: {
tcp_velocity_limit: velocityInRelevantUnits
}
},
line: {
position: targetTcpPosition,
orientation: currentTcpPose.orientation,
coordinate_system: coordSystemId
}
});
} else if (motion.type === "rotate") {
const currentRotationVector = new Vector32(
currentTcpPose.orientation["x"],
currentTcpPose.orientation["y"],
currentTcpPose.orientation["z"]
);
const currentRotationRad = currentRotationVector.length();
const currentRotationDirection = currentRotationVector.clone().normalize();
const differenceRotationRad = motion.distanceRads * (direction === "-" ? -1 : 1);
const differenceRotationDirection = new Vector32(0, 0, 0);
differenceRotationDirection[axis] = 1;
const f1 = Math.cos(0.5 * differenceRotationRad) * Math.cos(0.5 * currentRotationRad);
const f2 = Math.sin(0.5 * differenceRotationRad) * Math.sin(0.5 * currentRotationRad);
const f3 = Math.sin(0.5 * differenceRotationRad) * Math.cos(0.5 * currentRotationRad);
const f4 = Math.cos(0.5 * differenceRotationRad) * Math.sin(0.5 * currentRotationRad);
const dotProduct = differenceRotationDirection.dot(
currentRotationDirection
);
const crossProduct = differenceRotationDirection.clone().cross(currentRotationDirection);
const newRotationRad = 2 * Math.acos(f1 - f2 * dotProduct);
const f5 = newRotationRad / Math.sin(0.5 * newRotationRad);
const targetTcpOrientation = new Vector32().addScaledVector(crossProduct, f2).addScaledVector(differenceRotationDirection, f3).addScaledVector(currentRotationDirection, f4).multiplyScalar(f5);
commands.push({
settings: {
limits_override: {
tcp_orientation_velocity_limit: velocityInRelevantUnits
}
},
line: {
position: currentTcpPose.position,
orientation: targetTcpOrientation,
coordinate_system: coordSystemId
}
});
}
const motionPlanRes = yield this.nova.api.motion.planMotion({
motion_group: this.motionGroupId,
start_joint_position: currentJoints,
tcp: this.cartesianJoggingOpts.tcpId,
commands
});
const plannedMotion = (_a = motionPlanRes.plan_successful_response) == null ? void 0 : _a.motion;
if (!plannedMotion) {
throw new Error(
`Failed to plan jogging increment motion ${JSON.stringify(motionPlanRes)}`
);
}
yield this.nova.api.motion.streamMoveForward(
plannedMotion,
100,
void 0,
void 0,
void 0,
{
// Might take a while at low velocity
timeout: 1e3 * 60
}
);
});
}
/**
* Rotate a single robot joint by a fixed number of radians
* Promise resolves only after the motion has completed.
*/
runIncrementalJointRotation(_0) {
return __async(this, arguments, function* ({
joint,
currentJoints,
velocityRadsPerSec,
direction,
distanceRads
}) {
var _a;
const targetJoints = [...currentJoints.joints];
targetJoints[joint] += distanceRads * (direction === "-" ? -1 : 1);
const jointVelocityLimits = new Array(
currentJoints.joints.length
).fill(velocityRadsPerSec);
const motionPlanRes = yield this.nova.api.motion.planMotion({
motion_group: this.motionGroupId,
start_joint_position: currentJoints,
commands: [
{
settings: {
limits_override: {
joint_velocity_limits: {
joints: jointVelocityLimits
}
}
},
joint_ptp: {
joints: targetJoints
}
}
]
});
const plannedMotion = (_a = motionPlanRes.plan_successful_response) == null ? void 0 : _a.motion;
if (!plannedMotion) {
console.error("Failed to plan jogging increment motion", motionPlanRes);
return;
}
yield this.nova.api.motion.streamMoveForward(
plannedMotion,
100,
void 0,
void 0,
void 0,
{
// Might take a while at low velocity
timeout: 1e3 * 60
}
);
});
}
};
// src/lib/v1/MotionStreamConnection.ts
import { makeAutoObservable as makeAutoObservable2, runInAction as runInAction2 } from "mobx";
import { Vector3 as Vector33 } from "three";
var MOTION_DELTA_THRESHOLD2 = 1e-4;
function unwrapRotationVector(newRotationVectorApi, currentRotationVectorApi) {
const currentRotationVector = new Vector33(
currentRotationVectorApi.x,
currentRotationVectorApi.y,
currentRotationVectorApi.z
);
const newRotationVector = new Vector33(
newRotationVectorApi.x,
newRotationVectorApi.y,
newRotationVectorApi.z
);
const currentAngle = currentRotationVector.length();
const currentAxis = currentRotationVector.normalize();
let newAngle = newRotationVector.length();
let newAxis = newRotationVector.normalize();
if (newAxis.dot(currentAxis) < 0) {
newAngle = -newAngle;
newAxis = newAxis.multiplyScalar(-1);
}
let angleDifference = newAngle - currentAngle;
angleDifference -= 2 * Math.PI * Math.floor((angleDifference + Math.PI) / (2 * Math.PI));
newAngle = currentAngle + angleDifference;
return newAxis.multiplyScalar(newAngle);
}
var MotionStreamConnection = class _MotionStreamConnection {
constructor(nova, controller, motionGroup, initialMotionState, motionStateSocket) {
this.nova = nova;
this.controller = controller;
this.motionGroup = motionGroup;
this.initialMotionState = initialMotionState;
this.motionStateSocket = motionStateSocket;
this.rapidlyChangingMotionState = initialMotionState;
motionStateSocket.addEventListener("message", (event) => {
var _a;
const motionStateResponse = (_a = tryParseJson(event.data)) == null ? void 0 : _a.result;
if (!motionStateResponse) {
throw new Error(
`Failed to get motion state for ${this.motionGroupId}: ${event.data}`
);
}
if (!jointValuesEqual(
this.rapidlyChangingMotionState.state.joint_position.joints,
motionStateResponse.state.joint_position.joints,
MOTION_DELTA_THRESHOLD2
)) {
runInAction2(() => {
this.rapidlyChangingMotionState.state = motionStateResponse.state;
});
}
if (!tcpPoseEqual(
this.rapidlyChangingMotionState.tcp_pose,
motionStateResponse.tcp_pose,
MOTION_DELTA_THRESHOLD2
)) {
runInAction2(() => {
if (this.rapidlyChangingMotionState.tcp_pose == void 0) {
this.rapidlyChangingMotionState.tcp_pose = motionStateResponse.tcp_pose;
} else {
this.rapidlyChangingMotionState.tcp_pose = {
position: motionStateResponse.tcp_pose.position,
orientation: unwrapRotationVector(
motionStateResponse.tcp_pose.orientation,
this.rapidlyChangingMotionState.tcp_pose.orientation
),
tcp: motionStateResponse.tcp_pose.tcp,
coordinate_system: motionStateResponse.tcp_pose.coordinate_system
};
}
});
}
});
makeAutoObservable2(this);
}
static open(nova, motionGroupId) {
return __async(this, null, function* () {
var _a;
const { instances: controllers } = yield nova.api.controller.listControllers();
const [_motionGroupIndex, controllerId] = motionGroupId.split("@");
const controller = controllers.find((c) => c.controller === controllerId);
const motionGroup = controller == null ? void 0 : controller.physical_motion_groups.find(
(mg) => mg.motion_group === motionGroupId
);
if (!controller || !motionGroup) {
throw new Error(
`Controller ${controllerId} or motion group ${motionGroupId} not found`
);
}
const motionStateSocket = nova.openReconnectingWebsocket(
`/motion-groups/${motionGroupId}/state-stream`
);
const firstMessage = yield motionStateSocket.firstMessage();
console.log("got first message", firstMessage);
const initialMotionState = (_a = tryParseJson(firstMessage.data)) == null ? void 0 : _a.result;
if (!initialMotionState) {
throw new Error(
`Unable to parse initial motion state message ${firstMessage.data}`
);
}
console.log(
`Connected motion state websocket to motion group ${motionGroup.motion_group}. Initial state:
`,
initialMotionState
);
return new _MotionStreamConnection(
nova,
controller,
motionGroup,
initialMotionState,
motionStateSocket
);
});
}
get motionGroupId() {
return this.motionGroup.motion_group;
}
get controllerId() {
return this.controller.controller;
}
get modelFromController() {
return this.motionGroup.model_from_controller;
}
get wandelscriptIdentifier() {
const num = this.motionGroupId.split("@")[0];
return `${this.controllerId.replaceAll("-", "_")}_${num}`;
}
get joints() {
return this.initialMotionState.state.joint_position.joints.map((_, i) => {
return {
index: i
};
});
}
dispose() {
this.motionStateSocket.close();
}
};
// src/lib/v1/NovaCellAPIClient.ts
import {
ApplicationApi,
CellApi,
ControllerApi,
ControllerIOsApi,
CoordinateSystemsApi,
DeviceConfigurationApi,
LibraryProgramApi,
LibraryProgramMetadataApi,
LibraryRecipeApi,
LibraryRecipeMetadataApi,
MotionApi,
MotionGroupApi,
MotionGroupInfosApi,
MotionGroupJoggingApi,
MotionGroupKinematicApi,
ProgramApi,
ProgramValuesApi,
StoreCollisionComponentsApi,
StoreCollisionScenesApi,
StoreObjectApi,
SystemApi,
VirtualRobotApi,
VirtualRobotBehaviorApi,
VirtualRobotModeApi,
VirtualRobotSetupApi
} from "@wandelbots/nova-api/v1";
import axios from "axios";
var NovaCellAPIClient = class {
constructor(cellId, opts) {
this.cellId = cellId;
this.opts = opts;
this.system = this.withUnwrappedResponsesOnly(SystemApi);
this.cell = this.withUnwrappedResponsesOnly(CellApi);
this.deviceConfig = this.withCellId(DeviceConfigurationApi);
this.motionGroup = this.withCellId(MotionGroupApi);
this.motionGroupInfos = this.withCellId(MotionGroupInfosApi);
this.controller = this.withCellId(ControllerApi);
this.program = this.withCellId(ProgramApi);
this.programValues = this.withCellId(ProgramValuesApi);
this.controllerIOs = this.withCellId(ControllerIOsApi);
this.motionGroupKinematic = this.withCellId(MotionGroupKinematicApi);
this.motion = this.withCellId(MotionApi);
this.coordinateSystems = this.withCellId(CoordinateSystemsApi);
this.application = this.withCellId(ApplicationApi);
this.applicationGlobal = this.withUnwrappedResponsesOnly(ApplicationApi);
this.motionGroupJogging = this.withCellId(MotionGroupJoggingApi);
this.virtualRobot = this.withCellId(VirtualRobotApi);
this.virtualRobotSetup = this.withCellId(VirtualRobotSetupApi);
this.virtualRobotMode = this.withCellId(VirtualRobotModeApi);
this.virtualRobotBehavior = this.withCellId(VirtualRobotBehaviorApi);
this.libraryProgramMetadata = this.withCellId(LibraryProgramMetadataApi);
this.libraryProgram = this.withCellId(LibraryProgramApi);
this.libraryRecipeMetadata = this.withCellId(LibraryRecipeMetadataApi);
this.libraryRecipe = this.withCellId(LibraryRecipeApi);
this.storeObject = this.withCellId(StoreObjectApi);
this.storeCollisionComponents = this.withCellId(
StoreCollisionComponentsApi
);
this.storeCollisionScenes = this.withCellId(StoreCollisionScenesApi);
}
/**
* Some TypeScript sorcery which alters the API class methods so you don't
* have to pass the cell id to every single one, and de-encapsulates the
* response data
*/
withCellId(ApiConstructor) {
var _a, _b;
const apiClient = new ApiConstructor(
__spreadProps(__spreadValues({}, this.opts), {
isJsonMime: (mime) => {
return mime === "application/json";
}
}),
(_a = this.opts.basePath) != null ? _a : "",
(_b = this.opts.axiosInstance) != null ? _b : axios.create()
);
for (const key of Reflect.ownKeys(Reflect.getPrototypeOf(apiClient))) {
if (key !== "constructor" && typeof apiClient[key] === "function") {
const originalFunction = apiClient[key];
apiClient[key] = (...args) => {
return originalFunction.apply(apiClient, [this.cellId, ...args]).then((res) => res.data);
};
}
}
return apiClient;
}
/**
* As withCellId, but only does the response unwrapping
*/
withUnwrappedResponsesOnly(ApiConstructor) {
var _a, _b;
const apiClient = new ApiConstructor(
__spreadProps(__spreadValues({}, this.opts), {
isJsonMime: (mime) => {
return mime === "application/json";
}
}),
(_a = this.opts.basePath) != null ? _a : "",
(_b = this.opts.axiosInstance) != null ? _b : axios.create()
);
for (const key of Reflect.ownKeys(Reflect.getPrototypeOf(apiClient))) {
if (key !== "constructor" && typeof apiClient[key] === "function") {
const originalFunction = apiClient[key];
apiClient[key] = (...args) => {
return originalFunction.apply(apiClient, args).then((res) => res.data);
};
}
}
return apiClient;
}
};
// src/lib/v1/NovaClient.ts
import axios2, { isAxiosError } from "axios";
import urlJoin from "url-join";
// src/lib/v1/mock/MockNovaInstance.ts
import { AxiosError } from "axios";
import * as pathToRegexp from "path-to-regexp";
var MockNovaInstance = class {
constructor() {
this.connections = [];
}
handleAPIRequest(config) {
return __async(this, null, function* () {
var _a, _b, _c;
const apiHandlers = [
{
method: "GET",
path: "/cells/:cellId/controllers",
handle() {
return {
instances: [
{
controller: "mock-ur5e",
model_name: "UniversalRobots::Controller",
host: "mock-ur5e",
allow_software_install_on_controller: true,
physical_motion_groups: [
{
motion_group: "0@mock-ur5e",
name_from_controller: "UR5e",
active: false,
model_from_controller: "UniversalRobots_UR5e"
}
],
has_error: false,
error_details: ""
}
]
};
}
},
{
method: "GET",
path: "/cells/:cellId/controllers/:controllerId",
handle() {
return {
configuration: {
kind: "VirtualController",
manufacturer: "universalrobots",
position: "[0,-1.571,-1.571,-1.571,1.571,-1.571,0]",
type: "universalrobots-ur5e"
},
name: "mock-ur5"
};
}
},
{
method: "GET",
path: "/cells/:cellId/motion-groups/:motionGroupId/specification",
handle() {
return {
dh_parameters: [
{
alpha: 1.5707963267948966,
theta: 0,
a: 0,
d: 162.25,
reverse_rotation_direction: false
},
{
alpha: 0,
theta: 0,
a: -425,
d: 0,
reverse_rotation_direction: false
},
{
alpha: 0,
theta: 0,
a: -392.2,
d: 0,
reverse_rotation_direction: false
},
{
alpha: 1.5707963267948966,
theta: 0,
a: 0,
d: 133.3,
reverse_rotation_direction: false
},
{
alpha: -1.5707963267948966,
theta: 0,
a: 0,
d: 99.7,
reverse_rotation_direction: false
},
{
alpha: 0,
theta: 0,
a: 0,
d: 99.6,
reverse_rotation_direction: false
}
],
mechanical_joint_limits: [
{
joint: "JOINTNAME_AXIS_1",
lower_limit: -6.335545063018799,
upper_limit: 6.335545063018799,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_2",
lower_limit: -6.335545063018799,
upper_limit: 6.335545063018799,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_3",
lower_limit: -6.335545063018799,
upper_limit: 6.335545063018799,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_4",
lower_limit: -6.335545063018799,
upper_limit: 6.335545063018799,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_5",
lower_limit: -6.335545063018799,
upper_limit: 6.335545063018799,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_6",
lower_limit: -6.335545063018799,
upper_limit: 6.335545063018799,
unlimited: false
}
]
};
}
},
{
method: "GET",
path: "/cells/:cellId/motion-groups/:motionGroupId/safety-setup",
handle() {
return {
safety_settings: [
{
safety_state: "SAFETY_NORMAL",
settings: {
joint_position_limits: [
{
joint: "JOINTNAME_AXIS_1",
lower_limit: -2.96705961227417,
upper_limit: 2.96705961227417,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_2",
lower_limit: -1.7453292608261108,
upper_limit: 2.7925267219543457,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_3",
lower_limit: -3.3161256313323975,
upper_limit: 0.40142571926116943,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_4",
lower_limit: -3.4906585216522217,
upper_limit: 3.4906585216522217,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_5",
lower_limit: -2.4434609413146973,
upper_limit: 2.4434609413146973,
unlimited: false
},
{
joint: "JOINTNAME_AXIS_6",
lower_limit: -4.71238899230957,
upper_limit: 4.71238899230957,
unlimited: false
}
],
joint_velocity_limits: [
{
joint: "JOINTNAME_AXIS_1",
limit: 3.1415927410125732
},
{
joint: "JOINTNAME_AXIS_2",
limit: 3.1415927410125732
},
{
joint: "JOINTNAME_AXIS_3",
limit: 3.4906585216522217
},
{
joint: "JOINTNAME_AXIS_4",
limit: 6.108652591705322
},
{
joint: "JOINTNAME_AXIS_5",
limit: 6.108652591705322
},
{
joint: "JOINTNAME_AXIS_6",
limit: 6.981317043304443
}
],
joint_acceleration_limits: [],
joint_torque_limits: [],
tcp_velocity_limit: 1800
}
}
],
safety_zones: [
{
id: 1,
priority: 0,
geometry: {
compound: {
child_geometries: [
{
convex_hull: {
vertices: [
{
x: -800,
y: -1330,
z: -1820
},
{
x: 1650,
y: -1330,
z: -1820
},
{
x: 1650,
y: 1330,
z: -1820
},
{
x: -800,
y: 1330,
z: -1820
}
]
},
init_pose: {
position: {
x: 0,
y: 0,
z: 0
},
orientation: {
x: 0,
y: 0,
z: 0,
w: 1
}
},
id: "box"
},
{
convex_hull: {
vertices: [
{
x: -800,
y: -1330,
z: -1820
},
{
x: 1650,
y: -1330,
z: -1820
},
{
x: 1650,
y: -1330,
z: 1500
},
{
x: -800,
y: -1330,
z: 1500
}
]
},
init_pose: {
position: {
x: 0,
y: 0,
z: 0
},
orientation: {
x: 0,
y: 0,
z: 0,
w: 1
}
},
id: "box"
},
{
convex_hull: {
vertices: [
{
x: -800,
y: -1330,
z: -1820
},
{
x: -800,
y: 1330,
z: -1820
},
{
x: -800,
y: 1330,
z: 1500
},
{
x: -800,
y: -1330,
z: 1500
}
]
},
init_pose: {
position: {
x: 0,
y: 0,
z: 0
},
orientation: {
x: 0,
y: 0,
z: 0,
w: 1
}
},
id: "box"
},
{
convex_hull: {
vertices: [
{
x: 1650,
y: 1330,
z: 1500
},
{
x: -800,
y: 1330,
z: 1500
},
{
x: -800,
y: -1330,
z: 1500
},
{
x: 1650,
y: -1330,
z: 1500
}
]
},
init_pose: {
position: {
x: 0,
y: 0,
z: 0
},
orientation: {
x: 0,
y: 0,
z: 0,
w: 1
}
},
id: "box"
},
{
convex_hull: {
vertices: [
{
x: 1650,
y: 1330,
z: 1500
},
{
x: -800,
y: 1330,
z: 1500
},
{
x: -800,
y: 1330,
z: -1820
},
{
x: 1650,
y: 1330,
z: -1820
}
]
},
init_pose: {
position: {
x: 0,
y: 0,
z: 0
},
orientation: {
x: 0,
y: 0,
z: 0,
w: 1
}
},
id: "box"
},
{
convex_hull: {
vertices: [
{
x: 1650,
y: 1330,
z: 1500
},
{
x: 1650,
y: -1330,
z: 1500
},
{
x: 1650,
y: -1330,
z: -1820
},
{