import API from './ecosystem-rws.js';
import {Logger} from './../function/log-helper.js';
import {ExceptionIdMap, ErrorCode} from './../exception/exceptionDesc.js';
import {InfoType, WarningType} from '../information/informationCode.js';
export const factoryApiMotion = function (mot) {
const logModule = 'ecosystem-motion';
/**
* The API.MOTION class provides a comprehensive set of interfaces for controlling and monitoring robot motion.
* It includes methods for jogging, aligning, and moving the robot to specific positions, as well as retrieving and setting tools, work objects, and coordinate systems.
* This class simplifies motion control operations, enabling developers to efficiently interact with and manage the robot's movement and positioning.
* @alias API.MOTION
* @namespace
*/
mot.MOTION = new (function () {
let ccounter = null;
let jogStop = false;
let isJogging = false;
let jogTimeout = 0;
/**
* @alias JOGMODE
* @memberof API.MOTION
* @readonly
* @enum {string}
*/
this.JOGMODE = {
Align: 'Align',
GoToPos: 'GoToPos',
ConfigurationJog: 'ConfigurationJog',
Cartesian: 'Cartesian',
AxisGroup1: 'AxisGroup1',
AxisGroup2: 'AxisGroup2',
Current: '',
};
/**
* @alias COORDS
* @memberof API.MOTION
* @readonly
* @enum {string}
* @property {enum} Wobj The work object coordinate system
* @property {enum} Base The Base coordinate system
* @property {enum} Tool The Tool coordinate system
* @property {enum} World The World coordinate system
* @property {enum} Current The active coordinate system
*/
this.COORDS = {
Wobj: 'Wobj',
Base: 'Base',
Tool: 'Tool',
World: 'World',
Current: '',
};
this.MaxJogAxisSpeed = 2000;
/**
* @alias POSITIONTYPES
* @memberof API.MOTION
* @readonly
* @enum {number}
*/
this.POSITIONTYPES = {
robtarget: 1,
jointtarget: 2,
};
// /**
// * @typedef {number} JogDataIdx0
// * @typedef {number} JogDataIdx1
// * @typedef {number} JogDataIdx2
// * @typedef {number} JogDataIdx3
// * @typedef {number} JogDataIdx4
// * @typedef {number} JogDataIdx5
// * @type {[JogDataIdx0, JogDataIdx1, JogDataIdx2, JogDataIdx3, JogDataIdx4, JogDataIdx5]} JogData
// */
/**
* @typedef Trans
* @prop {number} x
* @prop {number} y
* @prop {number} z
* @memberof API.MOTION
*/
/**
* @typedef Rot
* @prop {number} q1
* @prop {number} q2
* @prop {number} q3
* @prop {number} q4
* @memberof API.MOTION
*/
/**
* @typedef RobConf
* @prop {number} cf1
* @prop {number} cf4
* @prop {number} cf6
* @prop {number} cfx
* @memberof API.MOTION
*/
/**
* @typedef ExtAx
* @prop {number} eax_a
* @prop {number} eax_b
* @prop {number} eax_c
* @prop {number} eax_d
* @prop {number} eax_e
* @prop {number} eax_f
* @memberof API.MOTION
*/
/**
* @typedef RobTarget
* @prop {Trans} trans
* @prop {Rot} rot
* @prop {RobConf} robconf
* @prop {ExtAx} extax
* @memberof API.MOTION
*/
/**
* @typedef RobAx
* @prop {number} rax_1
* @prop {number} rax_2
* @prop {number} rax_3
* @prop {number} rax_4
* @prop {number} rax_5
* @prop {number} rax_6
* @memberof API.MOTION
*/
/**
* @typedef JointTarget
* @prop {RobAx} robax
* @prop {ExtAx} extax
* @memberof API.MOTION
*/
/**
* @typedef executeJoggingProps
* @prop {string} [tool]
* @prop {string} [wobj]
* @prop {COORDS} [coords]
* @prop {JOGMODE} [jogMode]
* @prop {JogData} [jogData]
* @prop {RobTarget} [robTarget]
* @prop {JointTarget} [jointTarget]
* @prop {boolean} [doJoint]
* @memberof API.MOTION
*/
/**
* Jogs the robot
* Non-SPOC systems (e.g., RobotWare 7):
* The interface shall work when the two conditions are met:
* 1. Operation mode is manual reduce mode
* 2. User logins as local client
* SPOC systems (e.g., RobotWare 8):
* The interface shall work when the two conditions are met:
* 1. Operation mode is manual reduce mode
* 2. Write access is held
* @alias executeJogging
* @memberof API.MOTION
* @param {JOGMODE} jogMode - Jogging mode
* @param {Object} [options] - Optional jogging options
* @param {JogData} [options.jogData=[500,500,500,500,500,500]] - Jog speed/axis data
* @param {RobTarget|JointTarget|null} [options.targetPosition=null] - Target position
* @param {string} [options.tool=''] - Tool identifier
* @param {string} [options.wobj=''] - Work object identifier
* @param {COORDS|string} [options.coords=''] - Coordinate system
* @returns {Promise<object|void>} - Resolves if jogging succeeds, rejects if failure
* @note SPOC systems (e.g., RobotWare 8): Write access must be held before executing jogging. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to acquire write access explicitly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface directly as motion mastership is handled internally.
* @example
* // Perform aligning
* await API.MOTION.executeJogging(API.MOTION.JOGMODE.Align)
*/
this.executeJogging = async function (
jogMode,
{jogData = [500, 500, 500, 500, 500, 500], targetPosition = null, tool = '', wobj = '', coords = ''} = {},
) {
try {
isJogging = true;
Logger.i(InfoType.RobotOperation, 'Perfroming jogging task with API.MOTION.executeJogging');
jogStop = true;
await API.RWS.requestMastership('motion');
await prepareJogging(tool, wobj, coords, jogMode);
await doJogging(jogData, targetPosition);
} catch (err) {
isJogging = false;
let bHeldMsh = await API.RWS.checkIfHeldMastership('motion');
if (bHeldMsh) {
await API.RWS.releaseMastership('motion');
}
throw err;
}
};
/**
* Prepares the MechUnit and ChangeCounter for jogging
* @alias prepareJogging
* @memberof API.MOTION
* @param {string} tool
* @param {string} wobj
* @param {string} coords
* @param {string} jogMode
* @returns {undefined |Promise<{}>} - Undefined or reject Promise if fails.
* @private
*/
const prepareJogging = async function (tool = '', wobj = '', coords = '', jogMode = '') {
const isSpoc = await API.RWS.CONTROLSTATION.isSpocSystem();
let opMode = await API.CONTROLLER.getOperationMode();
let ctrlState = await API.CONTROLLER.getControllerState();
if (isSpoc) {
if (!(ctrlState === API.CONTROLLER.STATE.MotorsOn && opMode === API.CONTROLLER.OPMODE.ManualR)) {
// check if registered as local control station
const isLocal = await RWS.ControlStation.isLocalConnected();
if (!isLocal) {
throw new Error(ErrorCode.MissingConditionsToJog, {
cause: `Mismatched conditions to jog: control state - ${ctrlState}, operation mode - ${opMode}`,
});
}
}
} else {
if (ctrlState === API.CONTROLLER.STATE.MotorsOn && opMode === API.CONTROLLER.OPMODE.ManualR) {
// check if registered in local client
let isLocalClient = await API.UAS.isLoggedInAsLocalClient();
if (!isLocalClient) {
throw new Error(ErrorCode.MissingPermissionsToJog, {
cause: 'Jogging in manual mode is restricted to the local client.',
});
}
} else {
throw new Error(ErrorCode.MissingConditionsToJog, {
cause: `Mismatched conditions to jog: control state - ${ctrlState}, operation mode - ${opMode}`,
});
}
}
await API.RWS.MOTIONSYSTEM.setMechunit({tool, wobj, jogMode, coords});
ccounter = await API.RWS.MOTIONSYSTEM.getChangeCount();
};
/**
* Cyclic execution during jogging
* @alias doJogging
* @memberof API.MOTION
* @private
* @param {API.MOTION.JogData} jogData
* @param {API.MOTION.RobTarget | API.MOTION.JointTarget} targetPosition
* @param {boolean} once - If true, send jog command only once
* @returns {undefined |Promise<{}>} - Undefined or reject Promise if fails.
*/
const doJogging = async function (jogData, targetPosition = null, once = false) {
jogStop = false;
clearTimeout(jogTimeout);
if (targetPosition !== null) {
if (bJointTarget(targetPosition)) {
await API.RWS.MOTIONSYSTEM.setRobotPositionJoint(targetPosition);
} else {
await API.RWS.MOTIONSYSTEM.setRobotPositionTarget(targetPosition);
}
}
try {
await API.MOTION.sendJogsinLoop(jogData, once);
} catch (err) {
jogStop = true;
throw err;
}
};
/**
* Send once/loop jog commands
* @alias sendJogsinLoop
* @memberof API.MOTION
* @private
* @param {API.MOTION.JogData} jogData
* @param {boolean} once - If true, send jog command only once
* @returns {undefined |Promise<{}>} - Undefined or reject Promise if fails.
*/
this.sendJogsinLoop = async function (jogData, once = false) {
await API.RWS.MOTIONSYSTEM.jog(jogData, ccounter);
if (once) jogStop = true;
if (!jogStop) {
Logger.i(logModule, 'looping jog commands');
jogTimeout = setTimeout(async () => await this.sendJogsinLoop(jogData, once), 200);
}
};
/**
* Stops any running jog
* @alias stopJogging
* @memberof API.MOTION
* @returns {undefined |Promise<{}>} Resolves if stopped correctly, rejects if failed.
* @example
* await API.MOTION.stopJogging()
*/
this.stopJogging = async function () {
if (isJogging) {
clearTimeout(jogTimeout);
const jogData = [0, 0, 0, 0, 0, 0];
jogStop = true;
try {
await API.RWS.MOTIONSYSTEM.jog(jogData, ccounter);
} finally {
await API.RWS.releaseMastership('motion');
}
isJogging = false;
}
};
/**
* Gets the current position of the robot
* @alias getRobotPosition
* @memberof API.MOTION
* @returns {Promise<object>} - An object containing current position of the robot
* @example
* await API.MOTION.getRobotPosition()
*/
this.getRobotPosition = async function () {
let robTarget = await API.RWS.MOTIONSYSTEM.getRobTarget();
return robTarget;
};
/**
* Gets the current tool
* @alias getTool
* @memberof API.MOTION
* @returns {Promise<object>} - Object containing current position of the robot
* @example
* await API.MOTION.getTool()
*/
this.getTool = async function () {
const mechUnit = await API.RWS.MOTIONSYSTEM.getMechunit();
return mechUnit.tool;
};
/**
* Gets the active work object
* @alias getWobj
* @memberof API.MOTION
* @returns {Promise<object>} - An object containing current position of the robot
* @example
* await API.MOTION.getWobj()
*/
this.getWobj = async function () {
const mechUnit = await API.RWS.MOTIONSYSTEM.getMechunit();
return mechUnit.wobj;
};
/**
* Gets the current coordinate system
* @alias getCoord
* @memberof API.MOTION
* @returns {Promise<string>} A string describing the current coordinate system of the robot.
* @example
* await API.MOTION.getCoord()
*/
this.getCoord = async function () {
const mechUnit = await API.RWS.MOTIONSYSTEM.getMechunit();
return mechUnit.coords;
};
/**
* Gets the axis number of the robot.
* @alias getAxisNumber
* @memberof API.MOTION
* @returns {Promise<number>} Axis number of the robot.
* @example
* let axisNum = await API.MOTION.getAxisNumber()
*/
this.getAxisNumber = async function () {
if (typeof this.axisNumber == 'undefined') {
const mechUnit = await API.RWS.MOTIONSYSTEM.getMechunit();
this.axisNumber = parseInt(mechUnit.axes);
}
return this.axisNumber;
};
/**
* Sets the active tool to the specified value
* @alias setTool
* @memberof API.MOTION
* @param {string} tool Name of the tool
* @returns {Promise<object>} Resolves if setting correctly, rejects if failed.
* @note SPOC systems (e.g., RobotWare 8): Write access must be held before deleting setting active tool. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to acquire write access explicitly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface directly as motion mastership is handled internally.
* @example
* await API.MOTION.setTool("tool0")
*/
this.setTool = async function (tool) {
try {
await API.RWS.requestMastership('motion');
try {
await API.RWS.MOTIONSYSTEM.setMechunit({tool});
} catch (error) {
return API.rejectWithStatus('Failed to set tool', {}, {errorCode: ErrorCode.FailedToSetActiveTool});
}
} finally {
await API.RWS.releaseMastership('motion');
}
};
/**
* Sets the active work object to the specified value.
* @alias setWobj
* @memberof API.MOTION
* @param {string} value Name of the work object.
* @returns {Promise<object>} Resolves if setting correctly, rejects if failed.
* @note SPOC systems (e.g., RobotWare 8): Write access must be held before deleting setting active wobj. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to acquire write access explicitly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface directly as motion mastership is handled internally.
* @example
* await API.MOTION.setWobj("wobj0")
*/
this.setWobj = async function (value) {
try {
await API.RWS.requestMastership('motion');
try {
await API.RWS.MOTIONSYSTEM.setMechunit({wobj: value});
} catch (error) {
return API.rejectWithStatus('Failed to set work object', {}, {errorCode: ErrorCode.FailedToSetActiveWobj});
}
} finally {
await API.RWS.releaseMastership('motion');
}
};
// TODO: add stopJogging as mouseup event for Align functionality
/**
* Performs align task
* Non-SPOC systems (e.g., RobotWare 7):
* The interface shall work when the two conditions are met:
* 1. Operation mode is manual reduce mode
* 2. User logins as local client
* SPOC systems (e.g., RobotWare 8):
* The interface shall work when the two conditions are met:
* 1. Operation mode is manual reduce mode
* 2. Write access is held
* @alias align
* @memberof API.MOTION
* @param {number} speedRatio Align speed ratio, whose value range is 1 to 100.
* @param {API.MechUnits} tool If empty, use active tool as align tool
* @param {API.MechUnits} wobj If empty, use active wobj as align wobj
* @param {API.MechUnits} coord If empty, use active coord as align coord
* @returns {undefined |Promise<{}>} - Resolves if aligning succeeds, rejects if fails
* @note SPOC systems (e.g., RobotWare 8): Write access must be held before executing performing align. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to acquire write access explicitly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface directly as motion mastership is handled internally.
* @example
* await API.MOTION.align(10)
*/
this.align = async function (speedRatio, {tool = '', wobj = '', coords = ''} = {}) {
try {
Logger.i(InfoType.RobotOperation, 'Perfroming align task with API.MOTION.align');
let speed = speedRatio > 100 ? 100 : speedRatio < 2 ? 2 : speedRatio;
let speedValue = (this.MaxJogAxisSpeed * speed) / 100;
let jogData = [0, 0, 0, 0, 0, 0];
// check axis number
let axisNumber = await this.getAxisNumber();
if (axisNumber == 4) {
jogData = [speedValue, speedValue, speedValue, speedValue, 0, 0];
} else {
jogData = [speedValue, speedValue, speedValue, speedValue, speedValue, speedValue];
}
const jogMode = API.MOTION.JOGMODE.Align;
await API.MOTION.executeJogging(jogMode, {jogData, tool, wobj, coords});
} catch (e) {
isJogging = false;
throw e;
}
};
// TODO: add stopJogging as mouseup event for GoTo functionality
/**
* Executes the GOTO task.
* Non-SPOC systems (e.g., RobotWare 7):
* The interface shall work when the two conditions are met:
* 1. Operation mode is manual reduce mode
* 2. User logins as local client
* SPOC systems (e.g., RobotWare 8):
* The interface shall work when the two conditions are met:
* 1. Operation mode is manual reduce mode
* 2. Write access is held
* @alias goToPosition
* @memberof API.MOTION
* @param {number} speedRatio GOTO movement speed ratio, whose value range is 1 to 100.
* @param {RobTarget | JointTarget} targetPosition - The target location of the GOTO task
* @param {API.MechUnits} tool If empty, use active tool as align tool
* @param {API.MechUnits} wobj If empty, use active wobj as align wobj
* @param {API.MechUnits} coord If empty, use active coord as align coord
* @returns {undefined |Promise<{}>} - Resolves if the motion task is executed, rejects if failed
* @note SPOC systems (e.g., RobotWare 8): Write access must be held before executing performing GoTo. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to acquire write access explicitly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface directly as motion mastership is handled internally.
* @example
* let variableValue = await API.RAPID.getVariableValue("MainModule","pos1");
* await API.MOTION.goToPosition(10, variableValue)
*/
this.goToPosition = async function (speedRatio, targetPosition, {tool = '', wobj = '', coords = ''} = {}) {
try {
Logger.i(InfoType.RobotOperation, 'Performing Goto task with API.MOTION.goToPosition');
if (!targetPosition) {
Logger.w(WarningType.ControllerOperation, 'Empty target position');
return;
}
if (!bRobotTarget(targetPosition) && !bJointTarget(targetPosition)) {
Logger.w(WarningType.ControllerOperation, 'Invalid target position');
return;
}
const speed = speedRatio > 100 ? 100 : speedRatio < 2 ? 2 : speedRatio;
let speedValue = (this.MaxJogAxisSpeed * speed) / 100;
let jogData = [0, 0, 0, 0, 0, 0];
// check axis number
let axisNumber = await this.getAxisNumber();
if (axisNumber == 4) {
jogData = [speedValue, speedValue, speedValue, speedValue, 0, 0];
} else {
jogData = [speedValue, speedValue, speedValue, speedValue, speedValue, speedValue];
}
const jogMode = API.MOTION.JOGMODE.GoToPos;
await API.MOTION.executeJogging(jogMode, {jogData, targetPosition, tool, wobj, coords});
} catch (e) {
isJogging = false;
throw e;
}
};
/**
* Teaches a robot position.
* @alias teachPosition
* @memberof API.MOTION
* @param {API.MOTION.POSITIONTYPES} type Position type
* @param {string} tool The tool used to teach position
* @param {string} wobj The wobj used to teach position
* @param {string} coords The coordinate system used to teach position
* @param {string} mechunit The mechanical unit used to teach position
* @returns {undefined | Promise<{RobTarget}> | Promise<{JointTarget}>} - The teached position
* @example
* let position = await API.MOTION.teachPosition(API.MOTION.POSITIONTYPES.robtarget);
*/
this.teachPosition = async function (type, {tool = '', wobj = '', coords = '', mechunit = 'ROB_1'} = {}) {
let currentPosition;
Logger.i(InfoType.RobotOperation, 'Teaching current robot position with API.MOTION.teachPosition');
try {
if (type == this.POSITIONTYPES.robtarget) {
currentPosition = await API.RWS.MOTIONSYSTEM.getRobTarget(tool, wobj, coords, mechunit);
} else if (type == this.POSITIONTYPES.jointtarget) {
currentPosition = await API.RWS.MOTIONSYSTEM.getJointTarget(mechunit);
}
return currentPosition;
} catch (error) {
return API.rejectWithStatus('Failed to teach current robot position', error, {
errorCode: ErrorCode.FailedToTeachPosition,
});
}
};
const bJointTarget = function (position) {
return position.robax && position.extax;
};
const bRobotTarget = function (position) {
return position.trans && position.rot && position.robconf && position.extax;
};
})();
mot.constructedMotion = true;
};
if (typeof API.constructedMotion === 'undefined') {
factoryApiMotion(API);
}
export default API;