import API from './ecosystem-base.js';
import {Logger} from './../function/log-helper.js';
import {ErrorCode, ExceptionIdMap} from './../exception/exceptionDesc.js';
import {InfoType, WarningType} from '../information/informationCode.js';
import Store from '../store/store.js';
import {ControlStation, Pending} from '../store/const.js';
const factoryApiRws = function (rws) {
/**
* The API.RWS namespace is an extension to the Omnicore SDK.
* @alias API.RWS
* @namespace
*/
rws.RWS = new (function () {
const logModule = 'ecosystem-rws';
this.MASTERSHIP = {
Nomaster: 'nomaster',
Remote: 'remote',
Local: 'local',
Internal: 'internal',
};
this.LEADTHROUGHSTATUS = {
Inactive: 'Inactive',
Active: 'Active',
};
function parseJSON(json) {
try {
return JSON.parse(json);
} catch (error) {
return undefined;
}
}
function rwsPost(url, body, error_msg) {
return RWS.Network.post(url, body)
.then((ret) => {
if (!ret || !ret.response) {
return Promise.resolve();
}
const responseText = ret.response;
try {
const content = JSON.parse(responseText);
return Promise.resolve(content);
} catch (e) {
return Promise.resolve(responseText);
}
})
.catch((err) => {
return API.rejectWithStatus(error_msg, err, {errorCode: ErrorCode.FailedToSendRWSPost});
});
}
async function rwsPostRequest(url, body) {
const ret = await RWS.Network.post(url, body);
if (!ret || !ret.response) {
return;
}
const responseText = ret.response;
try {
const content = JSON.parse(responseText);
return content;
} catch (e) {
return responseText;
}
}
/**
* @typedef {'edit' | 'motion' } MastershipType
* @memberof API.RWS
*/
/**
* Requests edit/motion mastership.
* @alias requestMastership
* @memberof API.RWS
* @param {MastershipType} type The mastership type, which can be 'edit' or 'motion'.
* @note SPOC systems (e.g., RobotWare 8): This function internally calls RWS.Mastership.request() / RWS.MotionMastership.request(), which invokes appSpocWriteAccessRequired to check if write access is held. If write access is not held, an error will be thrown.
* @note Non-SPOC systems (e.g., RobotWare 7): Edit/motion mastership is requested directly.
* @returns {Promise<any>}
* @example
* API.RWS.requestMastership('edit')
*/
this.requestMastership = function (type = 'edit') {
if (type === 'edit')
return RWS.Mastership.request()
.then(() => Promise.resolve())
.catch((err) => {
return API.rejectWithStatus('Could not get edit Mastership.', err, {
errorCode: ErrorCode.FailedToRequestEditMastership,
});
});
else if (type == 'motion') {
return RWS.MotionMastership.request()
.then(() => Promise.resolve())
.catch((err) => {
return API.rejectWithStatus(
'Could not get motion Mastership.',
err,
ErrorCode.FailedToRequestMotionMastership,
logModule,
);
});
} else if (type != '') type = type + '/';
return rwsPost(`/rw/mastership/${type}request`, '', `Request ${type} mastership failed.`);
};
/**
* Explicitly request mastership if the system is RW7 and request write access if the system is RW8.
* If the system is RW7, it can work for both Auto mode and Manual mode.
* @param {string} type 'edit' | 'motion', only needed by RW7 system
* @param {boolean} autoRegisterControlStation Whether to automatically register as a remote control station if not registered when requesting write access. Only applicable for RW8 system.
* @alias requestMastershipExplicitly
* @memberof API.RWS
* @returns {Promise<any>}
*/
this.requestMastershipExplicitly = async function ({type = 'edit', autoRegisterControlStation = false} = {}) {
if (await API.RWS.CONTROLSTATION.isSpocSystem()) {
// RW8
return API.RWS.CONTROLSTATION.requestWriteAccess(autoRegisterControlStation);
}
// RW7
const isTPU = API.isTPU();
if (isTPU) {
if (type == 'edit') {
return RWS.Mastership.request();
} else if (type == 'motion') {
return RWS.MotionMastership.request();
} else {
throw new Error('Invalid mastership type');
}
}
// Non-TPU
if (type == 'motion') {
return RWS.Network.post('/rw/mastership/motion/request');
} else if (type != 'edit') {
throw new Error('Invalid mastership type');
}
// handle edit mastership for non-FP RW7 system
const _opmode = await RWS.Controller.getOperationMode();
const _isInAutoMode = _opmode === API.CONTROLLER.OPMODE.Auto;
const parsedMsh = await RWS.Mastership.getStatus();
try {
if (parsedMsh.mastership == 'nomaster') {
await RWS.Network.post('/rw/mastership/edit/request'); // cannot call RWS.Mastership.request() because it uses isWebView to check if it is in TPU
} else if (parsedMsh.mastershipheldbyme && parsedMsh.mastershipheldbyme == 'TRUE') {
return;
} else {
// when non-auto & a local client exists
if (!_isInAutoMode) {
try {
await API.RWS.USER.reqRMMP();
await API.sleep(1000);
await API.RWS.USER.pollRMMPState();
} catch (error) {
throw new Error(ErrorCode.FailedToRequestRMMP);
}
await RWS.Network.post('/rw/mastership/edit/request');
} else {
// cannot request access when it is held by others in auto mode
throw new Error(ErrorCode.EditMastershipHeldByOther);
}
}
} catch (e) {
if (typeof e.message == 'string') {
if (e.message === ErrorCode.EditMastershipHeldByOther || e.message === ErrorCode.FailedToRequestRMMP) {
throw e;
}
}
return new Error(ErrorCode.FailedToRequestEditMastership);
}
};
/**
* Explicitly release mastership if the system is RW7 and release write access if the system is RW8.
* If the system is RW7, it can work for both Auto mode and Manual mode.
* @param {*} type 'edit' | 'motion', only needed by RW7 system
* @alias releaseMastershipExplicitly
* @memberof API.RWS
* @returns {Promise<any>}
*/
this.releaseMastershipExplicitly = async function (type = 'edit') {
if (await API.RWS.CONTROLSTATION.isSpocSystem()) {
// RW8
return API.RWS.CONTROLSTATION.releaseWriteAccess();
} else {
// RW7
const isTPU = API.isTPU();
if (isTPU) {
if (type == 'edit') {
return RWS.Mastership.release();
} else if (type == 'motion') {
return RWS.MotionMastership.release();
} else {
throw new Error('Invalid mastership type');
}
}
// Non-TPU
if (type === 'motion') {
return RWS.Network.post('/rw/mastership/motion/release');
} else if (type !== 'edit') {
throw new Error('Invalid mastership type');
}
try {
const parsedMsh = await RWS.Mastership.getStatus();
if (parsedMsh.mastershipheldbyme && parsedMsh.mastershipheldbyme == 'TRUE') {
await RWS.Network.post('/rw/mastership/edit/release');
await API.RWS.USER.relRMMP();
}
} catch (e) {
return API.rejectWithStatus(
'Failed to release edit mastership',
e,
ErrorCode.FailedToReleaseEditMastership,
logModule,
);
}
}
};
/**
* @alias releaseMastership
* @memberof API.RWS
* @param {MastershipType} type The mastership type, which can be 'edit' or 'motion'.
* @note SPOC systems (e.g., RobotWare 8): This function internally calls RWS.Mastership.release() / RWS.MotionMastership.release(), which returns directly when in SPOC system.
* @note Non-SPOC systems (e.g., RobotWare 7): Edit / motion mastership is released directly.
* @returns {Promise<any>}
* @example
* API.RWS.releaseMastership('edit')
*/
this.releaseMastership = function (type = 'edit') {
if (type === 'edit')
return RWS.Mastership.release()
.then(() => Promise.resolve())
.catch((err) => {
return API.rejectWithStatus('Could not release edit Mastership.', err, {
errorCode: ErrorCode.FailedToReleaseEditMastership,
});
});
else if (type == 'motion') {
return RWS.MotionMastership.release()
.then(() => Promise.resolve())
.catch((err) => {
return API.rejectWithStatus(
'Could not release motion Mastership.',
err,
ErrorCode.FailedToReleaseMotionMastership,
logModule,
);
});
} else if (type != '') type = type + '/';
return rwsPost(`/rw/mastership/${type}release`, '', `Release ${type} mastership failed.`);
};
/**
* Executes the function with holding the specified mastership.
* @alias executeWithMastership
* @memberof API.RWS
* @param {any} args The arguments for function calling.
* @param {Function} func The function to be called.
* @param {MastershipType} type The mastership type, which can be 'edit' or 'motion'.
* @returns {Promise<any>}
*/
const executeWithMastership = async function (func, args, type = 'edit') {
let hasMastership = false;
try {
await API.RWS.requestMastership(type);
hasMastership = true;
await func.apply(API.RWS, args);
} finally {
if (hasMastership) {
await API.RWS.releaseMastership(type);
}
}
};
/**
* @alias getMastershipState
* @note SPOC systems (e.g., RobotWare 8): mastership concept is not supported. Use {@link API.RWS.CONTROLSTATION.checkIfHeldWriteAccess} to check write access status.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface to get edit/motion mastership status.
* @memberof API.RWS
* @param {string} type The mastership type, which can be 'edit' or 'motion'.
* @returns {Promise<object>}
*/
this.getMastershipState = async function (type = 'edit') {
try {
let res = await RWS.Network.get(`/rw/mastership/${type}`);
let obj = parseJSON(res.responseText);
return obj['state'][0]['mastership'];
} catch (err) {
return API.rejectWithStatus(`Failed to get ${type} mastership status`, err, {
errorCode: ErrorCode.FailedToGetMastershipStatus,
});
}
};
/**
* Checks if held the mastership.
* @alias checkIfHeldMastership
* @memberof API.RWS
* @param {string} type The mastership type, which can be 'edit' or 'motion'.
* @note SPOC systems (e.g., RobotWare 8): Always returns false. Mastership is not supported. Use {@link API.RWS.CONTROLSTATION.checkIfHeldWriteAccess} instead.
* @note Non-SPOC systems (e.g., RobotWare 7): Returns true if mastership is held by current client.
* @returns {Promise<boolean>}
* @example
* // check if holding edit mastership
* await API.RWS.checkIfHeldMastership()
*/
this.checkIfHeldMastership = async function (type = 'edit') {
try {
const isSpoc = await API.RWS.CONTROLSTATION.isSpocSystem();
if (isSpoc) return false;
let res = await RWS.Network.get(`/rw/mastership/${type}`);
let obj = parseJSON(res.responseText);
let state = obj['state'][0];
return state && state.mastershipheldbyme && state.mastershipheldbyme == 'TRUE';
} catch (err) {
return API.rejectWithStatus(`Failed to check if held ${type} mastership`, err, {
errorCode: ErrorCode.FailedToCheckIfHeldMastership,
});
}
};
function parseResponse(res) {
let items = [];
let obj = parseJSON(res.responseText);
if (typeof obj === 'undefined') return Promise.reject('Could not parse JSON.');
let resources = obj._embedded.resources;
resources.forEach((item) => {
let it = {};
it.name = item._title;
for (let prop in item) {
if (prop.indexOf('_') === -1) {
it[prop] = item[prop];
}
}
items.push(it);
});
return items;
}
/**
* This class provides motion-related interfaces that are not supported by the OmniCore SDK.
* @namespace MOTIONSYSTEM
* @memberof API.RWS
*/
this.MOTIONSYSTEM = new (function () {
/**
*
* @typedef Mechunits
* @prop {string} activationAllowed
* @prop {string} driveModule
* @prop {string} mode // 'Activated', ...
* @prop {string} name
* @memberof API.RWS.MOTIONSYSTEM
*/
/**
*
* @typedef Mechunit
* @prop {string} axes
* @prop {string} axesTotal
* @prop {string} coords
* @prop {string} hasIntegratedUnit
* @prop {string} isIntegratedUnit
* @prop {string} jogMode
* @prop {string} mode
* @prop {string} payload
* @prop {string} status
* @prop {string} task
* @prop {string} tool
* @prop {string} totalPayload
* @prop {string} type
* @prop {string} wobj
* @prop {string} name
* @memberof API.RWS.MOTIONSYSTEM
*/
/**
* Gets details of all the mechanical units.
* @alias getMechunits
* @memberof API.RWS.MOTIONSYSTEM
* @returns {Promise<Mechunits[]>}
* @example
* const mechunits = await API.RWS.MOTIONSYSTEM.getMechunits();
* console.log(mechunits); // Outputs an array of mechanical units
*/
this.getMechunits = async function () {
try {
let res = await RWS.Network.get('/rw/motionsystem/mechunits');
let items = [];
let obj = parseJSON(res.responseText);
if (typeof obj === 'undefined') return Promise.reject('Could not parse the response of getting mechunits');
let resources = obj._embedded.resources;
resources.forEach((item) => {
let it = {};
it.name = item._title;
for (let prop in item) {
if (prop.indexOf('_') === -1) {
it[prop] = item[prop];
}
}
items.push(it);
});
return items;
} catch (err) {
return API.rejectWithStatus('Failed to get mechunits.', err, {errorCode: ErrorCode.FailedToGetMechunits});
}
};
/**
* Gets the details of a mechanical unit.
* @alias getMechunit
* @memberof API.RWS.MOTIONSYSTEM
* @param {string} name Name of the mechanical unit., e.g., 'ROB_1'
* @returns {Promise<Mechunit>}
* @example
* const mechunit = await API.RWS.MOTIONSYSTEM.getMechunit('ROB_1');
* console.log(mechunit); // Outputs details of the specified mechanical unit
*/
this.getMechunit = async function (name = 'ROB_1') {
try {
let res = await RWS.Network.get(`/rw/motionsystem/mechunits/${name}?continue-on-err=1`);
let obj = parseJSON(res.responseText);
let mechunit = {};
mechunit.axes = obj.state[0].axes;
mechunit.axesTotal = obj.state[0]['axes-total'];
mechunit.coords = obj.state[0]['coord-system'];
mechunit.hasIntegratedUnit = obj.state[0]['has-integrated-unit'];
mechunit.isIntegratedUnit = obj.state[0]['is-integrated-unit'];
mechunit.jogMode = obj.state[0]['jog-mode'];
mechunit.mode = obj.state[0]['mode'];
mechunit.payload = obj.state[0]['payload-name'];
mechunit.status = obj.state[0]['status'];
mechunit.task = obj.state[0]['task-name'];
mechunit.tool = obj.state[0]['tool-name'];
mechunit.totalPayload = obj.state[0]['total-payload-name'];
mechunit.type = obj.state[0]['type'];
mechunit.wobj = obj.state[0]['wobj-name'];
mechunit.name = obj.state[0]._title;
return mechunit;
} catch (err) {
return API.rejectWithStatus(`Failed to get mechunit ${name} info`, err, {
errorCode: ErrorCode.FailedToGetMechunitInfo,
});
}
};
/**
* @typedef SetMechunitProps
* @prop {string} [name]
* @prop {string} [tool]
* @prop {string} [wobj]
* @prop {string} [coords]
* @prop {string} [jogMode]
* @prop {string} [mode]
* @prop {string} [payload]
* @prop {string} [totalPayload]
* @memberof API.RWS.MOTIONSYSTEM
*/
/**
* Sets the properties of a mechanical unit.
* @alias setMechunit
* @memberof API.RWS.MOTIONSYSTEM
* @param {SetMechunitProps} props See {@link SetMechunitProps}
* @returns {Promise<any>}
* @example
* await API.RWS.MOTIONSYSTEM.setMechunit({
* name: 'ROB_1',
* tool: 'Tool1',
* wobj: 'WObj1',
* });
*/
this.setMechunit = async function ({
name = 'ROB_1',
tool = '',
wobj = '',
coords = '',
payload = '',
totalPayload = '',
mode = '',
jogMode = '',
} = {}) {
let url = `/rw/motionsystem/mechunits/${name}?continue-on-err=1`;
let body = '';
body += tool ? 'tool=' + tool : '';
body += wobj ? (body ? '&' : '') + 'wobj=' + wobj : '';
body += payload ? (body ? '&' : '') + 'payload=' + payload : '';
body += totalPayload ? (body ? '&' : '') + 'total-payload=' + totalPayload : '';
body += mode ? (body ? '&' : '') + 'mode=' + mode : '';
body += jogMode ? (body ? '&' : '') + 'jog-mode=' + jogMode : '';
body += coords ? (body ? '&' : '') + 'coord-system=' + coords : '';
let res = await rwsPost(url, body, 'Failed to set mechunit.');
return res;
};
/**
* Sets the robot position target for GoTo task.
* @alias setRobotPositionTarget
* @memberof API.RWS.MOTIONSYSTEM
* @param {API.MOTION.RobTarget} r See {@link API.MOTION.RobTarget}
* @returns {Promise<any>}
* @example
* const robTarget = {
* trans: { x: 100, y: 200, z: 300 },
* rot: { q1: 1, q2: 0, q3: 0, q4: 0 },
* robconf: { cf1: 0, cf4: 0, cf6: 0, cfx: 0 },
* extax: { eax_a: 0, eax_b: 0, eax_c: 0, eax_d: 0, eax_e: 0, eax_f: 0 },
* };
* await API.RWS.MOTIONSYSTEM.setRobotPositionTarget(robTarget);
*/
this.setRobotPositionTarget = function (r) {
if (r.trans === undefined || r.rot === undefined || r.robconf === undefined) {
return API.rejectWithStatus("Parameter 'r' is not a robtarget.", {}, {errorCode: ErrorCode.InvalidPrameter});
}
let url = `/rw/motionsystem/position-target`;
let body = `pos-x=${r.trans.x}&pos-y=${r.trans.y}&pos-z=${r.trans.z}&orient-q1=${r.rot.q1}&orient-q2=${
r.rot.q2
}&orient-q3=${r.rot.q3}&orient-q4=${r.rot.q4}&config-j1=${r.robconf.cf1}&config-j4=${r.robconf.cf4}&config-j6=${
r.robconf.cf6
}&config-jx=${r.robconf.cfx}&extjoint-1=${r.extax ? r.extax.eax_a : 9e9}&extjoint-2=${
r.extax ? r.extax.eax_b : 9e9
}&extjoint-3=${r.extax ? r.extax.eax_c : 9e9}&extjoint-4=${r.extax ? r.extax.eax_d : 9e9}&extjoint-5=${
r.extax ? r.extax.eax_e : 9e9
}&extjoint-6=${r.extax ? r.extax.eax_f : 9e9}`;
return rwsPost(url, body, 'Failed to set target robot position for GoTo');
};
/**
* Sets the joint position target for GoTo task.
* @alias setRobotPositionJoint
* @memberof API.RWS.MOTIONSYSTEM
* @param {API.MOTION.JointTarget} j
* @returns {Promise<any>}
* @example
* const jointTarget = {
* robax: { rax_1: 0, rax_2: 0, rax_3: 0, rax_4: 0, rax_5: 0, rax_6: 0 },
* extax: { eax_a: 0, eax_b: 0, eax_c: 0, eax_d: 0, eax_e: 0, eax_f: 0 },
* };
* await API.RWS.MOTIONSYSTEM.setRobotPositionJoint(jointTarget);
*/
this.setRobotPositionJoint = function (j) {
if (j.robax === undefined) return Promise.reject("Parameter 'j' is not a jointtarget.");
let url = `/rw/motionsystem/position-joint`;
let body = `robjoint=${j.robax.rax_1},${j.robax.rax_2},${j.robax.rax_3},${j.robax.rax_4},${j.robax.rax_5},${
j.robax.rax_6
}&extjoint=${j.extax ? j.extax.eax_a : 9e9},${j.extax ? j.extax.eax_b : 9e9},${j.extax ? j.extax.eax_c : 9e9},${
j.extax ? j.extax.eax_d : 9e9
},${j.extax ? j.extax.eax_e : 9e9},${j.extax ? j.extax.eax_f : 9e9}`;
return rwsPost(url, body, 'Failed to set target joint position for GoTo');
};
/**
* Starts jogging. This request shall be sent cyclically during the jogging.
* @alias jog
* @memberof API.RWS.MOTIONSYSTEM
* @param {API.MOTION.JogData} jogdata Jogging speed for the axis.
* @param {number} ccount Counter value, which needs to be obtained using {@link getChangeCount} before start jogging.
* @returns {Promise<any>}
* @example
* const jogData = [10, 0, 0, 0, 0, 0]; // Jogging along axis 1
* const changeCount = await API.RWS.MOTIONSYSTEM.getChangeCount();
* await API.RWS.MOTIONSYSTEM.jog(jogData, changeCount);
*/
this.jog = async function (jogdata, ccount) {
let url = `/rw/motionsystem/jog`;
let body = `axis1=${jogdata[0]}&axis2=${jogdata[1]}&axis3=${jogdata[2]}&axis4=${jogdata[3]}&axis5=${jogdata[4]}&axis6=${jogdata[5]}&ccount=${ccount}`;
return rwsPost(url, body, `Failed to send jog commands.jogdata: ${jogdata.toString()}. ccount:${ccount}.`);
};
/**
* Gets the change count of the motion system.
* @alias getChangeCount
* @memberof API.RWS.MOTIONSYSTEM
* @returns {Promise<number>}
* @example
* const changeCount = await API.RWS.MOTIONSYSTEM.getChangeCount();
* console.log(`Change count: ${changeCount}`);
*/
this.getChangeCount = async function () {
try {
let res = await RWS.Network.get(`/rw/motionsystem?resource=change-count`);
let obj = parseJSON(res.responseText);
return obj['state'][0]['change-count'];
} catch (err) {
return API.rejectWithStatus('Failed to get change count of motion system', err, {
errorCode: ErrorCode.FailedToGetMotionSystemChangeCount,
});
}
};
this.parseRobTarget = function (robTarget) {
let rt = {trans: {}, rot: {}, robconf: {}, extax: {}};
rt.trans.x = parseFloat(robTarget['x']);
rt.trans.y = parseFloat(robTarget['y']);
rt.trans.z = parseFloat(robTarget['z']);
rt.rot.q1 = parseFloat(robTarget['q1']);
rt.rot.q2 = parseFloat(robTarget['q2']);
rt.rot.q3 = parseFloat(robTarget['q3']);
rt.rot.q4 = parseFloat(robTarget['q4']);
rt.robconf.cf1 = parseFloat(robTarget['cf1']);
rt.robconf.cf4 = parseFloat(robTarget['cf4']);
rt.robconf.cf6 = parseFloat(robTarget['cf6']);
rt.robconf.cfx = parseFloat(robTarget['cfx']);
rt.extax.eax_a = parseFloat(robTarget['eax_a']);
rt.extax.eax_b = parseFloat(robTarget['eax_b']);
rt.extax.eax_c = parseFloat(robTarget['eax_c']);
rt.extax.eax_d = parseFloat(robTarget['eax_d']);
rt.extax.eax_e = parseFloat(robTarget['eax_e']);
rt.extax.eax_f = parseFloat(robTarget['eax_f']);
return rt;
};
/**
* @typedef RobTargetProps
* @param {string} [tool]
* @param {string} [wobj]
* @param {string} [coords]
* @param {string} [mechunit]
* @memberof API.RWS.MOTIONSYSTEM
*/
/**
* Gets the current robot position.
* @alias getRobTarget
* @memberof API.RWS.MOTIONSYSTEM
* @param {RobTargetProps} props See {@link RobTargetProps}
* @returns {Promise<API.MOTION.RobTarget>}
* @example
* const robTarget = await API.RWS.MOTIONSYSTEM.getRobTarget('Tool1', 'WObj1');
*/
this.getRobTarget = async function (tool = '', wobj = '', coords = '', mechunit = 'ROB_1') {
try {
let params = '';
params += tool ? '?tool=' + tool : '';
params += wobj ? (params ? '&' : '?') + 'wobj=' + wobj : '';
params += coords ? (params ? '&' : '?') + 'coordinate=' + coords : '';
let res = await RWS.Network.get(`/rw/motionsystem/mechunits/${mechunit}/robtarget${params}`);
let obj = parseJSON(res.responseText);
let rt = this.parseRobTarget(obj['state'][0]);
return rt;
} catch (e) {
return API.rejectWithStatus('Failed to get current robot position', e, {
errorCode: ErrorCode.FailedToGetRobotTarget,
});
}
};
this.parseJointTarget = function (jointTarget, toDegrees = false) {
let jt = {robax: {}, extax: {}};
let factor = 180 / Math.PI;
jt.robax.rax_1 = toDegrees ? parseFloat(jointTarget['rax_1']) * factor : parseFloat(jointTarget['rax_1']);
jt.robax.rax_2 = toDegrees ? parseFloat(jointTarget['rax_2']) * factor : parseFloat(jointTarget['rax_2']);
jt.robax.rax_3 = toDegrees ? parseFloat(jointTarget['rax_3']) * factor : parseFloat(jointTarget['rax_3']);
jt.robax.rax_4 = toDegrees ? parseFloat(jointTarget['rax_4']) * factor : parseFloat(jointTarget['rax_4']);
jt.robax.rax_5 = toDegrees ? parseFloat(jointTarget['rax_5']) * factor : parseFloat(jointTarget['rax_5']);
jt.robax.rax_6 = toDegrees ? parseFloat(jointTarget['rax_6']) * factor : parseFloat(jointTarget['rax_6']);
jt.extax.eax_a = parseFloat(jointTarget['eax_a']);
jt.extax.eax_b = parseFloat(jointTarget['eax_b']);
jt.extax.eax_c = parseFloat(jointTarget['eax_c']);
jt.extax.eax_d = parseFloat(jointTarget['eax_d']);
jt.extax.eax_e = parseFloat(jointTarget['eax_e']);
jt.extax.eax_f = parseFloat(jointTarget['eax_f']);
return jt;
};
/**
* Gets the current joint position.
* @alias getJointTarget
* @memberof API.RWS.MOTIONSYSTEM
* @param {string} mechunit
* @returns {Promise<API.MOTION.JointTarget>}
* @example
* const jointTarget = await API.RWS.MOTIONSYSTEM.getJointTarget('ROB_1');
* console.log(jointTarget); // Outputs the current joint positions
*/
this.getJointTarget = async function (mechunit = 'ROB_1') {
try {
let res = await RWS.Network.get(`/rw/motionsystem/mechunits/${mechunit}/jointtarget`);
let obj = parseJSON(res.responseText);
let jt = this.parseJointTarget(obj['state'][0], false);
return jt;
} catch (e) {
return API.rejectWithStatus('Failed to get current robot joint position', e, {
errorCode: ErrorCode.FailedToGetJointTarget,
});
}
};
/**
*
* @typedef JointsSolutionProps
* @prop {string} [mechUnit]
* @prop {API.MOTION.RobTarget} robTarget
* @prop {API.MOTION.ToolData} [toolData]
*/
/**
* Get all joint solutions
* @alias getAllJointsSolution
* @memberof API.RWS.MOTIONSYSTEM
* @param {JointsSolutionProps} props
* @returns {Promise<any>}
*/
this.getAllJointsSolution = async function ({mechUnit = 'ROB_1', robTarget, toolData}) {
/*
* {
* curr_position* string
* [x,y,z]
*
* curr_ext_joints* string
* [j1,j2,j3,j4,j5,j6]
*
* tool_frame_position* string
* [x, y, z]
*
* curr_orientation* string
* [u0, u1, u2, u3]
*
* tool_frame_orientation* string
* [u0, u1, u2, u3]
*
* robot_fixed_object* string
* TRUE|FALSE
*
* robot_configuration* string
* [quarter_rev_j1, quarter_rev_j4, quarter_rev_j6, quarter_rev_jx]
*
* }
*/
Logger.i(InfoType.RobotOperation, 'Trying to get joint solution for a target position');
const r = robTarget;
const t = toolData
? toolData
: {robhold: true, tframe: {trans: {x: 0, y: 0, z: 0}, rot: {q1: 1, q2: 0, q3: 0, q4: 0}}};
const toMeters = 0.001;
if (r.trans === undefined || r.rot === undefined || r.robconf === undefined)
return API.rejectWithStatus(
"Parameter 'robTarget' is not a valid robtarget value.",
{},
{errorCode: ErrorCode.InvalidPrameter},
);
let url = `/rw/motionsystem/mechunits/${mechUnit}/all-joints-solution`;
let body = `curr_position=[${r.trans.x * toMeters},${r.trans.y * toMeters},${
r.trans.z * toMeters
}]&curr_ext_joints=[${r.extax ? r.extax.eax_a : 9e9},${r.extax ? r.extax.eax_b : 9e9},${
r.extax ? r.extax.eax_c : 9e9
},${r.extax ? r.extax.eax_d : 9e9},${r.extax ? r.extax.eax_e : 9e9},${
r.extax ? r.extax.eax_f : 9e9
}]&tool_frame_position=[${t.tframe.trans.x * toMeters},${t.tframe.trans.y * toMeters},${
t.tframe.trans.z * toMeters
}]&curr_orientation=[${r.rot.q1},${r.rot.q2},${r.rot.q3},${r.rot.q4}]&tool_frame_orientation=[${
t.tframe.rot.q1
},${t.tframe.rot.q2},${t.tframe.rot.q3},${t.tframe.rot.q4}]&robot_fixed_object=${
t.robhold ? 'TRUE' : 'FALSE'
}&robot_configuration=[${r.robconf.cf1},${r.robconf.cf4},${r.robconf.cf6},${r.robconf.cfx}]`;
let res = await rwsPost(url, body, 'Failed to get all joint solutions from Cartesian');
let toDegree = 180 / Math.PI;
let jtArray = res.state.map((jointData) => {
let jt = {robax: {}, extax: {}, robconf: {}};
jt.robax.rax_1 = parseFloat(jointData['robotjoint1']) * toDegree;
jt.robax.rax_2 = parseFloat(jointData['robotjoint2']) * toDegree;
jt.robax.rax_3 = parseFloat(jointData['robotjoint3']) * toDegree;
jt.robax.rax_4 = parseFloat(jointData['robotjoint4']) * toDegree;
jt.robax.rax_5 = parseFloat(jointData['robotjoint5']) * toDegree;
jt.robax.rax_6 = parseFloat(jointData['robotjoint6']) * toDegree;
jt.extax.eax_a = parseFloat(jointData['extjoint1']);
jt.extax.eax_b = parseFloat(jointData['extjoint2']);
jt.extax.eax_c = parseFloat(jointData['extjoint3']);
jt.extax.eax_d = parseFloat(jointData['extjoint4']);
jt.extax.eax_e = parseFloat(jointData['extjoint5']);
jt.extax.eax_f = parseFloat(jointData['extjoint6']);
jt.robconf.cf1 = parseInt(jointData['quarter_rev_j11']);
jt.robconf.cf4 = parseInt(jointData['quarter_rev_j4']);
jt.robconf.cf6 = parseInt(jointData['quarter_rev_j6']);
jt.robconf.cfx = parseInt(jointData['quarter_rev_jx']);
return jt;
});
return jtArray;
};
/**
*
* @typedef JontsFromCartesianProps
* @prop {string} [mechUnit]
* @prop {API.MOTION.RobTarget} robTarget
* @prop {API.MOTION.ToolData} [toolData]
* @prop {API.MOTION.JointTarget} jointTarget
*/
/**
* Get all the joint solutions.
* @alias getJointsFromCartesian
* @memberof API.RWS.MOTIONSYSTEM
* @param {JontsFromCartesianProps} props
* @returns {Promise<any>}
*/
this.getJointsFromCartesian = async function ({mechUnit = 'ROB_1', robTarget, toolData, jointTarget}) {
/*
* {
* curr_position* string
* [x,y,z]
*
* curr_ext_joints* string
* [j1,j2,j3,j4,j5,j6]
*
* tool_frame_position* string
* [x, y, z]
*
* curr_orientation* string
* [u0, u1, u2, u3]
*
* tool_frame_orientation* string
* [u0, u1, u2, u3]
*
* old_rob_joints* string
* [j1,j2,j3,j4,j5,j6]
*
* old_ext_joints* string
* [j1,j2,j3,j4,j5,j6]
*
* robot_fixed_object* string
* TRUE|FALSE
*
* robot_configuration* string
* [quarter_rev_j1, quarter_rev_j4, quarter_rev_j6, quarter_rev_jx]
*
* elog_at_error* string
* TRUE|FALSE
*
* }
*/
Logger.i(InfoType.RobotOperation, 'Trying to get joint solution for a target position');
const r = robTarget;
const j = jointTarget;
const t = toolData
? toolData
: {robhold: true, tframe: {trans: {x: 0, y: 0, z: 0}, rot: {q1: 1, q2: 0, q3: 0, q4: 0}}};
const toRadians = Math.PI / 180;
const toMeters = 0.001;
if (r.trans === undefined || r.rot === undefined || r.robconf === undefined || j.robax === undefined)
return API.rejectWithStatus(
"Parameter 'robTarget' is not a valid robtarget value.",
{},
{errorCode: ErrorCode.InvalidPrameter},
);
let url = `/rw/motionsystem/mechunits/${mechUnit}/joints-from-cartesian`;
let body = `curr_position=[${r.trans.x * toMeters},${r.trans.y * toMeters},${
r.trans.z * toMeters
}]&curr_ext_joints=[${r.extax ? r.extax.eax_a : 9e9},${r.extax ? r.extax.eax_b : 9e9},${
r.extax ? r.extax.eax_c : 9e9
},${r.extax ? r.extax.eax_d : 9e9},${r.extax ? r.extax.eax_e : 9e9},${
r.extax ? r.extax.eax_f : 9e9
}]&tool_frame_position=[${t.tframe.trans.x},${t.tframe.trans.y},${t.tframe.trans.z}]&curr_orientation=[${
r.rot.q1
},${r.rot.q2},${r.rot.q3},${r.rot.q4}]&tool_frame_orientation=[${t.tframe.rot.q1},${t.tframe.rot.q2},${
t.tframe.rot.q3
},${t.tframe.rot.q4}]&old_rob_joints=[${j.robax.rax_1 * toRadians},${j.robax.rax_2 * toRadians},${
j.robax.rax_3 * toRadians
},${j.robax.rax_4 * toRadians},${j.robax.rax_5 * toRadians},${j.robax.rax_6 * toRadians}]&old_ext_joints=[${
j.extax ? j.extax.eax_a : 9e9
},${j.extax ? j.extax.eax_b : 9e9},${j.extax ? j.extax.eax_c : 9e9},${j.extax ? j.extax.eax_d : 9e9},${
j.extax ? j.extax.eax_e : 9e9
},${j.extax ? j.extax.eax_f : 9e9}]&robot_fixed_object=${t.robhold ? 'TRUE' : 'FALSE'}&robot_configuration=[${
r.robconf.cf1
},${r.robconf.cf4},${r.robconf.cf6},${r.robconf.cfx}]&elog_at_error=TRUE`;
let res = await rwsPost(url, body, 'Failed to get joint solution from Cartesian');
let jt = {robax: {}, extax: {}};
let toDegree = 180 / Math.PI;
jt.robax.rax_1 = parseFloat(res.state[0]['robotjoint1']) * toDegree;
jt.robax.rax_2 = parseFloat(res.state[0]['robotjoint2']) * toDegree;
jt.robax.rax_3 = parseFloat(res.state[0]['robotjoint3']) * toDegree;
jt.robax.rax_4 = parseFloat(res.state[0]['robotjoint4']) * toDegree;
jt.robax.rax_5 = parseFloat(res.state[0]['robotjoint5']) * toDegree;
jt.robax.rax_6 = parseFloat(res.state[0]['robotjoint6']) * toDegree;
jt.extax.eax_a = parseFloat(res.state[0]['extjoint1']);
jt.extax.eax_b = parseFloat(res.state[0]['extjoint2']);
jt.extax.eax_c = parseFloat(res.state[0]['extjoint3']);
jt.extax.eax_d = parseFloat(res.state[0]['extjoint4']);
jt.extax.eax_e = parseFloat(res.state[0]['extjoint5']);
jt.extax.eax_f = parseFloat(res.state[0]['extjoint6']);
return jt;
};
/**
* Gets the lead-through status.
* @alias getLeadThroughStatus
* @memberof API.RWS.MOTIONSYSTEM
* @param {string} mechunit
* @returns {Promise<any>}
* @example
* const status = await API.RWS.MOTIONSYSTEM.getLeadThroughStatus('ROB_1');
* console.log(`Lead-through status: ${status}`);
*/
this.getLeadThroughStatus = async function (mechunit = 'ROB_1') {
try {
let ltRes = await RWS.Network.get(`/rw/motionsystem/mechunits/${mechunit}/lead-through`);
let obj = parseJSON(ltRes.responseText);
return obj['state'][0]['status'];
} catch (e) {
return API.rejectWithStatus('Get leadthrough status failed.', e, {
errorCode: ErrorCode.FailedToGetLeadthroughStatus,
});
}
};
/**
* Sets the lead-through status.
* @alias setLeadThroughStatus
* @memberof API.RWS.MOTIONSYSTEM
* @param {string} type API.RWS.LEADTHROUGHSTATUS.Inactive | API.RWS.LEADTHROUGHSTATUS.Active
* @param {string} disableAutoLoadCompensation Being Active if the precision mode is used. This is supported from RW7.6.
* @param {string} mechunit The name of the mechanical unit.
* @returns {Promise<any>}
* @example
* await API.RWS.MOTIONSYSTEM.setLeadThroughStatus(
* API.RWS.LEADTHROUGHSTATUS.Active,
* 'inactive',
* 'ROB_1'
* );
* console.log('Lead-through status set successfully.');
*/
this.setLeadThroughStatus = async function (type, disableAutoLoadCompensation = 'inactive', mechunit = 'ROB_1') {
let data = 'status=' + type;
let systemInfo = await API.RWS.CONTROLLER.getSystemInfo();
if (parseInt(systemInfo['major']) == 7 && parseInt(systemInfo['minor']) >= 6) {
data += '&disableautoloadcompensation=' + disableAutoLoadCompensation;
}
try {
await RWS.Network.post('/rw/motionsystem/mechunits/' + mechunit + '/lead-through', data);
} catch (e) {
return API.rejectWithStatus(`Failed to set leadthrough status to ${type}`, e, {
errorCode: ErrorCode.FailedToSetLeadthrough,
});
}
};
})();
/**
* The API.RWS.RAPID namespace provides RAPID-related interfaces that are not supported by the OmniCore SDK.
* @namespace RAPID
* @memberof API.RWS
*/
this.RAPID = new (function () {
/**
* Loads a module from the HOME directory of the controller into the specified task.
* @note SPOC systems (e.g., RobotWare 8): Write access must be held before loading module. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to acquire write access explicitly.
* @note Non-SPOC systems (e.g., RobotWare 7): Edit mastership is handled internally by this interface.
* @alias loadModule
* @memberof API.RWS.RAPID
* @param {string} path Path to the module file in the HOME directory (including the file extension).
* @param {boolean} [replace] If the value is 'true', existing module in RAPID with the same name will be replaced.
* @param {string} [taskName] The task name.
* @param {boolean} [requestMsh] Edit mastership needs to be requested before loading the module.
* @returns {Promise<any>}
* @example
* let url = `${this.path}/${this.name}${this.extension}`;
* await API.RWS.RAPID.loadModule(url, true, 'T_ROB1');
*/
this.loadModule = async function (path, replace = false, taskName = 'T_ROB1', requestMsh = true) {
const f = async function () {
await RWS.Network.post(`/rw/rapid/tasks/${taskName}/loadmod`, 'modulepath=' + path + '&replace=' + replace);
};
try {
if (requestMsh) {
return await executeWithMastership(f);
} else {
return await f();
}
} catch (e) {
return API.rejectWithStatus(`Failed to load RAPID module at path ${path}`, e, {
errorCode: ErrorCode.FailedToLoadRapidModule,
});
}
};
/**
* Unloads a RAPID module.
* @alias unloadModule
* @memberof API.RWS.RAPID
* @note SPOC systems (e.g., RobotWare 8): Write access must be held before unloading module. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to acquire write access explicitly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface directly as edit mastership is handled internally.
* @param {string} moduleName The module name.
* @param {string} [taskName] The task name.
* @param {boolean} [requestMsh] Edit mastership needs to be requested before unloading the module.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.unloadModule('MyModule', 'T_ROB1');
*/
this.unloadModule = async function (moduleName, taskName = 'T_ROB1', requestMsh = true) {
const f = function () {
return RWS.Network.post(`/rw/rapid/tasks/${taskName}/unloadmod`, 'module=' + moduleName);
};
try {
if (requestMsh) {
return await executeWithMastership(f);
} else {
return await f();
}
} catch (e) {
return API.rejectWithStatus(`Failed to unload RAPID module. Task: ${taskName}. Module: ${moduleName}`, e, {
errorCode: ErrorCode.FailedToUnloadRapidModule,
});
}
};
/**
* Gets the RAPID module text.
* @alias getModuleText
* @memberof API.RWS.RAPID
* @param {*} moduleName The module name.
* @param {*} taskName The task name.
* @returns {Promise<any>} An object containing the structure
* {
* change-count,
* module-length,
* module-text?,
* file-path?,
* _title,
* _type,
* }
* @example
* const moduleText = await API.RWS.RAPID.getModuleText('MyModule', 'T_ROB1');
*/
this.getModuleText = async function (moduleName, taskName = 'T_ROB1') {
try {
let res = await RWS.Network.get(`/rw/rapid/tasks/${taskName}/modules/${moduleName}/text`);
var fixedResponse = res.responseText.replace(/""(.*?)""/g, '"$1"');
let obj = parseJSON(fixedResponse);
if (typeof obj === 'undefined') return Promise.reject('Could not parse JSON.');
if (!obj.state[0]) return Promise.reject('Failed to get module text.');
return obj.state[0];
} catch (e) {
return API.rejectWithStatus(
`Failed to get module text content. Task: ${taskName}. Module: ${moduleName}`,
e,
{errorCode: ErrorCode.FailedToGetRapidModuleContent},
);
}
};
/**
* Sets the content of a RAPID module.
* @alias setModuleText
* @memberof API.RWS.RAPID
* @param {string} moduleName The module name.
* @param {string} text The new content of the module.
* @param {string} [taskName] The task name.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.setModuleText("Module1",`MODULE Module1\r\nPROC proc1()\r\nTPWrite "123";\r\nENDPROC\r\nENDMODULE`), 'T_ROB1');
*/
this.setModuleText = async function (moduleName, text, taskName = 'T_ROB1') {
const url = `/rw/rapid/tasks/${taskName}/modules/${moduleName}/text`;
const body = `text=${encodeURIComponent(text)}`;
return await rwsPost(url, body, `Failed to set content of ${taskName}:${moduleName}.`);
};
/**
* Sets the content of a RAPID module within a specified range.
* @alias setModuleTextInRange
* @memberof API.RWS.RAPID
* @param {string} moduleName The module name.
* @param {string} text The new content to insert.
* @param {number} startRow Start row of the range.
* @param {number} startCol Start column of the range.
* @param {number} endRow End row of the range.
* @param {number} endCol End column of the range.
* @param {string} [taskName] The task name.
* @param {string} [replaceMode] Replace mode, e.g., 'Replace'.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.setModuleTextInRange(
* 'MyModule',
* 'TPErase;',
* 5,
* 1,
* 10,
* 1,
* 'T_ROB1',
* 'Replace'
* );
*/
this.setModuleTextInRange = async function (
moduleName,
text,
startRow,
startCol,
endRow,
endCol,
taskName = 'T_ROB1',
replaceMode = 'Replace',
) {
const url = `/rw/rapid/tasks/${taskName}/modules/${moduleName}/text/range`;
const body = `replace-mode=${replaceMode}&query-mode=Force&startrow=${startRow}&startcol=${startCol}&endrow=${endRow}&endcol=${endCol}&text=${encodeURIComponent(
text,
)}`;
return rwsPost(url, body, `Failed to set content range of ${taskName}:${moduleName}.`);
};
/**
* Moves the program pointer to a specified cursor position.
* @alias movePPToCursor
* @memberof API.RWS.RAPID
* @param {string} moduleName The module name.
* @param {string} taskName The task name.
* @param {string} line Number of the line to which the pointer is desired.
* @param {string} column Number of the column to which the pointer is desired.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.movePPToCursor('MyModule', 'T_ROB1', '10', '5');
*/
this.movePPToCursor = function (moduleName, taskName, line, column) {
if (typeof moduleName !== 'string') return Promise.reject("Parameter 'module' is not a string.");
if (typeof line !== 'string') return Promise.reject("Parameter 'line' is not a string.");
if (typeof column !== 'string') return Promise.reject("Parameter 'column' is not a string.");
let url = `/rw/rapid/tasks/${encodeURIComponent(taskName)}/pcp/cursor?mastership=implicit`;
let body = `module=${encodeURIComponent(moduleName)}&line=${encodeURIComponent(
line,
)}&column=${encodeURIComponent(column)}`;
return rwsPost(
url,
body,
`Failed to set PP to cursor. Task: ${taskName}. Module: ${moduleName}. line:${line}. column: ${column}.`,
);
};
/**
* Sets the program pointer to a specified routine.
* @alias setPPToRoutine
* @memberof API.RWS.RAPID
* @param {string} moduleName The module name.
* @param {string} routineName Routine's name.
* @param {string} [taskName] The task name.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.setPPToRoutine('MyModule', 'MainRoutine', 'T_ROB1');
*/
this.setPPToRoutine = async function (moduleName, routineName, taskName = 'T_ROB1') {
let url = `/rw/rapid/tasks/${taskName}/pcp/routine-from-url`;
let body = `routineurl=/RAPID/${taskName}/${moduleName}/${routineName}&userlevel=false`;
try {
return await rwsPostRequest(url, body);
} catch (error) {
return API.rejectWithStatus(
`Failed to set PP to routine. Task: ${taskName}. Module: ${moduleName}. Routine: ${routineName}.`,
error,
{
errorCode: ErrorCode.FailedToSetPPToRoutine,
},
);
}
};
/**
* Gets the information of program pointers
* @alias getPointersInfo
* @memberof API.RWS.RAPID
* @param {string} [taskName] The task name.
* @returns {Promise<any>}
* @example
* const pointersInfo = await API.RWS.RAPID.getPointersInfo('T_ROB1');
*/
this.getPointersInfo = async function (taskName = 'T_ROB1') {
try {
let res = await RWS.Network.get('/rw/rapid/tasks/' + taskName + '/pcp');
let pcpInfo = JSON.parse(res.responseText)['state'];
return pcpInfo;
} catch (e) {
return API.rejectWithStatus(`Failed to get program/motion pointers information. Task: ${taskName}.`, e, {
errorCode: ErrorCode.FailedToGetPointersInfo,
});
}
};
/**
* Synchronizes the value of persistent variables in a specified module.
* @alias syncPers
* @memberof API.RWS.RAPID
* @param {string} moduleName The module name.
* @param {string} [taskName] The task name.
* @note SPOC systems (e.g., RobotWare 8): This function requires write access. An error will be thrown if write access is not held.
* @note Non-SPOC systems (e.g., RobotWare 7): Use {@link API.RWS.requestMastership} to request edit mastership before calling this API.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.syncPers('MyModule', 'T_ROB1');
*/
this.syncPers = async function (moduleName, taskName = 'T_ROB1') {
try {
let url = `/rw/rapid/tasks/${taskName}/modules/${moduleName}/sync-pers`;
return await rwsPostRequest(url, '');
} catch (e) {
if (e && e.controllerStatus && e.controllerStatus.code === '-1073442809') {
return API.rejectWithStatus(`Failed to sync pers. Task: ${taskName}. Module: ${moduleName}`, e, {
errorCode: ErrorCode.FailedToSyncPersDuringExecution,
});
} else {
throw API.rejectWithStatus(`Failed to sync pers. Task: ${taskName}. Module: ${moduleName}`, e, {
errorCode: ErrorCode.FailedToSyncPers,
});
}
}
};
/**
* Gets the properties of the specified RAPID data type
* @alias getDataTypeProperty
* @memberof API.RWS.RAPID
* @param {string} typeUrl The URL of the data type.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.getDataTypeProperty('robtarget');
*/
this.getDataTypeProperty = async function (typeUrl) {
try {
let res = await RWS.Network.get(`/rw/rapid/symbol/${typeUrl}/properties`);
let typeProp = JSON.parse(res.responseText)['state'][0];
return typeProp;
} catch (e) {
return API.rejectWithStatus(`Failed to get data type property of ${typeUrl}`, e, {
errorCode: ErrorCode.FailedToGetDataTypeProperty,
});
}
};
/**
* Gets the RECORD data list.
* @alias getRecordTypes
* @memberof API.RWS.RAPID
* @param {string} task The task name.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.getRecordTypes()
*/
this.getRecordTypes = function (task = 'T_ROB1', url = 'symbols/search') {
let data = '';
data += '&blockurl=' + `RAPID/${task}`;
data += '&recursive=TRUE'; // True all the way
data += '&symtyp=rec';
data += "®exp = '[^<].*?[^>]'";
return rwsPost('/rw/rapid/' + url, data, `Failed to get RECORD name list of task ${task}`);
};
/**
* Get the data structure of the specified RAPID data type
* @alias getDataTypeStructure
* @memberof API.RWS.RAPID
* @param {string} typeUrl The URL of the data type.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.getDataTypeStructure();
*/
this.getDataTypeStructure = function (blockurl = 'RAPID/robtarget', exturl = 'symbols/search') {
let data = '';
data += '&blockurl=' + blockurl;
data += '&recursive=TRUE'; // True all the way
return rwsPost('/rw/rapid/' + exturl, data, `Failed to get data structure of ${blockurl}`);
};
/**
* Gets the raw value of a RAPID variable.
* @alias getVariableRawValue
* @memberof API.RWS.RAPID
* @param {string} moduleName The module name.
* @param {string} variableName The variable name.
* @param {string} taskName The task name.
* @returns {Promise<string>}
* @example
* await API.RWS.RAPID.getVariableRawValue('TestModule', 'TestVariable', 'T_ROB1');
*/
this.getVariableRawValue = async function (moduleName, variableName, taskName = 'T_ROB1') {
try {
let res = await RWS.Network.get(`/rw/rapid/symbol/RAPID/${taskName}/${moduleName}/${variableName}/data`);
let value = JSON.parse(res.responseText)['state'][0]['value'];
return value;
} catch (e) {
return API.rejectWithStatus(
`Failed to get variable value. Task: ${taskName}. Module: ${moduleName}. Variable: ${variableName}`,
e,
{errorCode: ErrorCode.FailedToGetVariableValue},
);
}
};
/**
* Gets the service routines.
* @alias getServiceRoutines
* @memberof API.RWS.RAPID
* @param {string} exturl External URL for the service routines.
* @param {boolean} allread Indicates whether to include all the readable routines.
* @returns {Promise<any>}
*/
this.getServiceRoutines = async function (exturl, allread) {
try {
let searchedResult = await RWS.Network.get('/rw/rapid/' + exturl + 'allread=' + (allread ? 'TRUE' : 'FALSE'));
return JSON.parse(searchedResult.responseText);
} catch (e) {
return API.rejectWithStatus('Failed to get service routine list', e, {
errorCode: ErrorCode.FailedToGetServiceRoutines,
});
}
};
/**
* Sets the cycle mode for the program execution.
* @alias setCycleMode
* @memberof API.RWS.RAPID
* @param {string} mode Cycle mode, e.g., 'once' or 'forever'.
* @returns {Promise<any>}
* @example
* await API.RWS.RAPID.setCycleMode('forever');
*/
this.setCycleMode = async function (mode = 'once') {
let url = `/rw/rapid/execution/cycle`;
let body = `cycle=${mode}`;
return rwsPost(url, body, `Failed to set cycle mode to ${mode}`, ErrorCode.FailedToSetCycleMode, logModule);
};
/**
* Gets the current cycle mode of the program execution.
* @alias getCycleMode
* @memberof API.RWS.RAPID
* @returns {Promise<string>} The current cycle mode.
* @example
* const cycleMode = await API.RWS.RAPID.getCycleMode();
*/
this.getCycleMode = async function () {
try {
let res = await RWS.Network.get('/rw/rapid/execution');
let executionInfo = JSON.parse(res.responseText)['state'][0];
return executionInfo['cycle'];
} catch (e) {
return API.rejectWithStatus('Failed to get cycle mode', e, {errorCode: ErrorCode.FailedToGetCycleMode});
}
};
/**
* This class represents a monitor on the RAPID task changes.
* @class TaskChangeMonitor
* @memberof API.RWS
* @param {string} taskName The task name to be monitored.
*/
class TaskChangeMonitor {
constructor(task = 'T_ROB1') {
this.url = `/rw/rapid/tasks/${encodeURIComponent(task)}`;
this.resourceString = `/rw/rapid/tasks/${encodeURIComponent(task)};taskchange`;
this.callbacks = [];
}
getTitle() {
return this.url;
}
getResourceString() {
return this.resourceString;
}
addCallbackOnChanged(callback) {
if (typeof callback !== 'function') {
throw new Error('callback is not a valid function');
}
this.callbacks.push(callback);
}
async onchanged(newValue) {
let parsedValue = {};
let taskChangeInfo = {};
taskChangeInfo['changeCount'] = Object.prototype.hasOwnProperty.call(newValue, 'change-count')
? newValue['change-count']
: '';
taskChangeInfo['changeType'] = Object.prototype.hasOwnProperty.call(newValue, 'changetype')
? newValue['changetype']
: '';
taskChangeInfo['moduleName'] = Object.prototype.hasOwnProperty.call(newValue, 'module-name')
? newValue['module-name']
: '';
taskChangeInfo['programName'] = Object.prototype.hasOwnProperty.call(newValue, 'program-name')
? newValue['program-name']
: '';
taskChangeInfo['taskName'] = Object.prototype.hasOwnProperty.call(newValue, 'task-name')
? newValue['task-name']
: '';
parsedValue = taskChangeInfo;
for (let i = 0; i < this.callbacks.length; i++) {
try {
this.callbacks[i](parsedValue);
} catch (error) {
return API.rejectWithStatus(
'Failed to execute specific callback when RAPID task change is triggered',
error,
{errorCode: ErrorCode.FailedToExecuteCallback},
);
}
}
}
subscribe() {
return RWS.Subscriptions.subscribe([this]);
}
unsubscribe() {
return RWS.Subscriptions.unsubscribe([this]);
}
}
/**
* Gets an instance of a API.RAPID.TaskChangeMonitor class
* @alias getTaskChangeMonitor
* @memberof API.RWS.RAPID
* @param {string} taskName - Task name
* @returns {Promise<object>} - API.RAPID.TaskChangeMonitor
* @example
* let taskChangeMonitor = await API.RWS.getTaskChangeMonitor();
* const taskchangeCb = function (data) {
* console.log('RAPID is changed');
* };
* taskChangeMonitor.addCallbackOnChanged(taskchangeCb);
* taskChangeMonitor.subscribe()
*/
this.getTaskChangeMonitor = function (taskName = 'T_ROB1') {
return new TaskChangeMonitor(taskName);
};
})();
/**
* The API.RWS.CFG namespace provides configuration-related interfaces that are not supported by OmniCore SDK.
* @namespace CFG
* @memberof API.RWS
*/
this.CFG = new (function () {
/**
* Deletes an existing entry from the configuraiton database
* @alias deleteConfigInstance
* @memberof API.RWS.CFG
* @param {string} name The instance name
* @param {string} type The instance type
* @param {string} domain The instance domain
* @param {boolean} [requestMsh] Edit mastership needs to be requested before deleting the instance.
* @returns {Promise<any>}
* @example
* // delete the signal configuration instance
* await API.RWS.CFG.deleteConfigInstance("TestDO1", "EIO_SIGNAL", "eio")
*/
this.deleteConfigInstance = async function (name, type, domain, requestMsh = true) {
const f = function () {
return RWS.Network.delete(`/rw/cfg/${domain}/${type}/instances/${name}`);
};
try {
if (requestMsh) {
return await executeWithMastership(f);
} else {
return await f();
}
} catch (e) {
return API.rejectWithStatus(`Failed to delete configuration instance.${domain}-${type}-${name}`, e, {
errorCode: ErrorCode.FailedToDeleteConfigInstance,
});
}
};
})();
/**
* The RWS.CONTROLLER class provides a set of controller-related interfaces that are not supported by the OmniCore SDK.
* It includes methods for retrieving robot types, system information, and managing program execution settings such as speed ratio.
* @namespace CONTROLLER
* @memberof API.RWS
*/
this.CONTROLLER = new (function () {
/**
* Gets the robot type.
* @alias getRobotType
* @memberof API.RWS.CONTROLLER
* @returns {Promise<string>} A promise with a string containing the robot type.
* @example
* const robotType = await API.RWS.CONTROLLER.getRobotType();
* console.log(robotType); // Outputs the robot type
*/
this.getRobotType = async function () {
try {
let robotType = await RWS.Network.get('/rw/system/robottype');
return JSON.parse(robotType.responseText)['state'][0]['robot-type'];
} catch (e) {
return API.rejectWithStatus('Get robot type failed', e, {errorCode: ErrorCode.FailedToGetRobotType});
}
};
/**
* Gets the system information.
* @alias getSystemInfo
* @memberof API.RWS.CONTROLLER
* @returns {Promise<any>}
* @example
* const systemInfo = await API.RWS.CONTROLLER.getSystemInfo();
* console.log(systemInfo);
*/
this.getSystemInfo = async function () {
try {
let systemRes = await RWS.Network.get('/rw/system');
let res = JSON.parse(systemRes.responseText);
let result = {
options: [],
rwversionname: res['state'][0]['rwversionname'],
major: res['state'][0]['major'],
minor: res['state'][0]['minor'],
build: res['state'][0]['build'],
revision: res['state'][0]['revision'],
date: res['state'][0]['date'],
name: res['state'][0]['name'],
sysid: res['state'][0]['sysid'],
};
let opts = res['_embedded']['resources'][0]['options'];
for (const iterator of opts) {
if (iterator['option'] != undefined) {
result.options.push(iterator['option']);
}
}
return result;
} catch (e) {
return API.rejectWithStatus('Failed to get controller system information', e, {
errorCode: ErrorCode.FailedToGetSystemInfo,
});
}
};
/**
* Sets the speed ratio for program execution. The setting requires the editing mastership and only supports the Auto mode.
* @alias setSpeedRatio
* @memberof API.RWS.CONTROLLER
* @param {number} speedRatio The speed ratio number in percentage.
* @returns {Promise<string>}
* @note SPOC systems (e.g., RobotWare 8): This function requires write access. An error will be thrown if write access is not held.
* @note Non-SPOC systems (e.g., RobotWare 7): Use {@link API.RWS.requestMastership} to request edit mastership before calling this API.
* @example
* await API.RWS.CONTROLLER.setSpeedRatio(50); // Sets the speed ratio to 50%
*/
this.setSpeedRatio = async function (speedRatio) {
try {
return RWS.Network.post('/rw/panel/speedratio', 'speed-ratio=' + speedRatio.toString());
} catch (e) {
return API.rejectWithStatus(`Failed to set speed ratio to ${speedRatio}`, e, {
errorCode: ErrorCode.FailedToSetSpeedRatio,
});
}
};
/**
* Gets the speed ratio for program execution.
* @alias getSpeedRatio
* @memberof API.RWS.CONTROLLER
* @returns {Promise<number>}
* @example
* const speedRatio = await API.RWS.CONTROLLER.getSpeedRatio();
* console.log(`Current speed ratio: ${speedRatio}%`);
*/
this.getSpeedRatio = async function () {
try {
let speedRes = await RWS.Network.get('/rw/panel/speedratio');
return parseInt(JSON.parse(speedRes.responseText)['state'][0]['speedratio']);
} catch (e) {
return API.rejectWithStatus('Failed to get speed ratio', e, {errorCode: ErrorCode.FailedToGetSpeedRatio});
}
};
})();
/**
* This class class provides a set of signal-related interfaces that are not supported by the OmniCore SDK.
* @namespace SIGNAL
* @memberof API.RWS
*/
this.SIGNAL = new (function () {
/**
* Modifies the signal value.
* @alias setSignalValue
* @memberof API.RWS.SIGNAL
* @param {string} signalName
* @param {number} value
* @param {string} mode value | pulse | invert | toggle | delay
* @param {number} pulses Number of pulses. This parameter is required to be set if the mode is set to "toggle" or "pulse".
* @param {number} activePulse Active pulse length for pulse/toggle mode, in ms
* @param {number} passivePulse Passive pulse length for pulse/toggle mode, in ms
* @param {number} delay Delay time for delay mode, in ms
* @param {string} deviceType
* @param {string} networkType
* @param {*} attr
* @example
* await API.RWS.SIGNAL.setSignalValue("TestDO1","1");
*/
this.setSignalValue = async function (
signalName,
value,
{
mode = '',
pulses = 1,
activePulse = 1000,
passivePulse = 1000,
delay = 1000,
deviceType = '',
networkType = '',
} = {},
) {
try {
const isPulseParameterRequired = ['pulse', 'toggle'].includes(mode);
const isDelayParameterRequired = ['delay'].includes(mode);
await RWS.Network.post(
'/rw/iosystem/signals/' +
(networkType ? networkType + '/' : '') +
(deviceType ? deviceType + '/' : '') +
signalName +
'/set-value',
'lvalue=' +
value +
(mode ? `&mode=${mode}` : '') +
(isPulseParameterRequired
? `&Pulses=${pulses}&ActivePulse=${activePulse}&PassivePulse=${passivePulse}`
: '') +
(isDelayParameterRequired ? `&Delay=${delay}` : ''),
);
} catch (error) {
throw new Error(ErrorCode.FailedToSetSignalValue, {cause: error});
}
};
/**
* Gets the signal value.
* @alias getSignalValue
* @memberof API.RWS.SIGNAL
* @param {string} signalName The signal name.
* @param {string} deviceType The device type.
* @param {string} networkType The network type.
* @returns {Promise<string>}
* @example
* await API.RWS.SIGNAL.getSignalValue("TestDO1");
*/
this.getSignalValue = async function (signalName, {deviceType = '', networkType = ''} = {}) {
try {
let signalRes = await RWS.Network.get(
'/rw/iosystem/signals/' +
(networkType ? networkType + '/' : '') +
(deviceType ? deviceType + '/' : '') +
signalName +
';state',
);
return JSON.parse(signalRes.responseText).state[0].lvalue;
} catch (error) {
return API.rejectWithStatus(`Failed to get signal ${signalName} value`, error, {
errorCode: ErrorCode.FailedToGetSignalValue,
});
}
};
/**
* Get the signal instance defined by RWS.IO
* @alias getSignalInstance
* @memberof API.RWS.SIGNAL
* @param {string} signalName
* @param {string} network
* @param {string} device
* @returns {Promise<any>}
*/
this.getSignalInstance = async function (signalName, network = '', device = '') {
if (network && device) {
const rwsSignalInstance = RWS.IO.createSignal_internal(network, device, signalName);
await rwsSignalInstance.fetch();
return rwsSignalInstance;
} else {
let signalRes = await RWS.Network.get('/rw/iosystem/signals/' + signalName);
const jsonData = JSON.parse(signalRes.responseText)['_embedded']['resources'][0];
const path = jsonData._title.split('/');
const UNASSIGNED_TAG = '%%UNASSIGNED%%';
let networkName = UNASSIGNED_TAG;
let deviceName = UNASSIGNED_TAG;
let signal = '';
if (path.length === 1) {
signal = path[0];
} else if (path.length === 3) {
networkName = path[0];
deviceName = path[1];
signal = path[2];
}
const rwsSignalInstance = RWS.IO.createSignal_internal(networkName, deviceName, signal);
await rwsSignalInstance.fetch();
return rwsSignalInstance;
}
};
})();
/**
* This class provides a set of UAS-related interfaces that are not supported by the OmniCore SDK.
* @namespace UAS
* @memberof API.RWS
*/
this.UAS = new (function () {
/**
* Get the grants of a specified user.
* @alias getGrantsOfUser
* @memberof API.RWS.UAS
* @param {string} userName The user name.
* @example
* await API.RWS.UAS.getGrantsOfUser("admin")
*/
this.getGrantsOfUser = async function (userName) {
try {
let userGrantsInfoRes = await RWS.Network.get(`/uas/users/${userName}/grants`);
let userGrantsInfo = JSON.parse(userGrantsInfoRes.responseText).state;
let userGrants = [];
userGrantsInfo.forEach((info) => {
userGrants.push(info.grantname);
});
return userGrants;
} catch (e) {
return API.rejectWithStatus(`Failed to get grant list of user ${userName}`, e, {
errorCode: ErrorCode.FailedToGetUserGrants,
});
}
};
})();
/**
* This class provides a set of user-releated interfaces.
* @namespace USER
* @memberof API.RWS
*/
this.USER = new (function () {
const RMMPStatus = {
GRANTED: 'GRANTED',
PENDING: 'PENDING',
REJECTED: 'REJECTED',
};
/**
* Logs out.
* @alias logout
* @memberof API.RWS.USER
* @example
* await API.RWS.USER.logout()
*/
this.logout = async function () {
return await RWS.Network.get('/logout');
};
// TODO: check the name of all user functions
this.poller = 0;
/**
* Gets thw RMMP state.
* @alias getRMMPState
* @memberof API.RWS.USER
* @note SPOC systems (e.g., RobotWare 8): RMMP (Remote Manual Mode Privilege) is not supported. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to request write access directly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface to cancel RMMP request.
* @example
* await API.RWS.USER.getRMMPState()
*/
this.getRMMPState = async function () {
try {
let rmmpStates = await RWS.Network.get('/users/rmmp');
return JSON.parse(rmmpStates.responseText)['state'][0];
} catch (e) {
return API.rejectWithStatus('Failed to get RMMP state', e, {errorCode: ErrorCode.FailedToGetRMMPState});
}
};
/**
* Cancels the RMMP request.
* @alias cancelRMMP
* @memberof API.RWS.USER
* @note SPOC systems (e.g., RobotWare 8): RMMP (Remote Manual Mode Privilege) is not supported. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to request write access directly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface to cancel RMMP request.
* @example
* await API.RWS.USER.cancelRMMP()
*/
this.cancelRMMP = async function () {
clearInterval(this.poller);
return await RWS.Network.post('/users/rmmp/cancel', '');
};
/**
* Requests the RMMP. Release RMMP first if RMMP is held.
* @alias reqRMMP
* @memberof API.RWS.USER
* @note SPOC systems (e.g., RobotWare 8): RMMP (Remote Manual Mode Privilege) is not supported. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to request write access directly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface to request RMMP.
* @example
* await API.RWS.USER.reqRMMP()
*/
this.reqRMMP = async () => {
try {
var resRMMP = await API.RWS.USER.getRMMPState();
if (resRMMP['rmmpheldbyme'] === 'false') {
await API.RWS.USER.requestRMMP();
} else if (resRMMP['rmmpheldbyme'] === 'true' && resRMMP['privilege'] != 'none') {
// TODO: confirm if it is offical way to handle the case where TPU revoke the msh
// The reason why add "privilege" is that it cannot request msh when rmmpheldbyme is true and privilege is 'modify'
await API.RWS.USER.relRMMP();
await API.RWS.USER.requestRMMP();
}
} catch (e) {
return API.rejectWithStatus('Failed to request RMMP', e, {errorCode: ErrorCode.FailedToRequestRMMP});
}
};
/**
* Releases the RMMP.
* @alias relRMMP
* @memberof API.RWS.USER
* @note SPOC systems (e.g., RobotWare 8): RMMP (Remote Manual Mode Privilege) is not supported. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to request write access directly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface to release RMMP.
* @example
* await API.RWS.USER.relRMMP()
*/
this.relRMMP = async () => {
try {
let resRMMP = await API.RWS.USER.getRMMPState();
if (resRMMP['rmmpheldbyme'] === 'false') {
return;
} else if (resRMMP['rmmpheldbyme'] === 'true') {
try {
await API.RWS.USER.cancelRMMP();
} catch (error) {
throw new Error('release RMMP failed');
}
}
} catch (error) {
return API.rejectWithStatus('Failed to release RMMP', error, {errorCode: ErrorCode.FailedToReleaseRMMP});
}
};
/**
* Request RMMP
* @alias requestRMMP
* @memberof API.RWS.USER
* @note SPOC systems (e.g., RobotWare 8): RMMP (Remote Manual Mode Privilege) is not supported. Use {@link API.RWS.CONTROLSTATION.requestWriteAccess} to request write access directly.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface to request RMMP.
* @example
* await API.RWS.USER.requestRMMP()
*/
this.requestRMMP = async (type = 'modify') => {
await RWS.Network.post('/users/rmmp', `privilege=${type}`);
};
/**
* Polls the RMMP state.
* @alias pollRMMPStatus
* @memberof API.RWS.USER
* @note SPOC systems (e.g., RobotWare 8): RMMP (Remote Manual Mode Privilege) is not supported.
* @note Non-SPOC systems (e.g., RobotWare 7): Use this interface to poll RMMP status.
* @example
* await API.RWS.USER.pollRMMPStatus()
*/
this.pollRMMPStatus = async () => {
let rmmpStates = await RWS.Network.get('/users/rmmp/poll');
return JSON.parse(rmmpStates.responseText)['state'][0];
};
this.pollRMMPState = () => {
return new Promise((resolve, reject) => {
let timeout = 500;
clearInterval(this.poller);
let that = this;
this.poller = setInterval(async function () {
try {
var rmmp = await API.RWS.USER.pollRMMPStatus();
} catch (error) {
Logger.e(
logModule,
ErrorCode.FailedToPollRMMP,
`Could not request edit Mastership in manual mode. >>> Failed to poll RMMP status!`,
);
reject('Failed to poll RMMP status!');
return;
}
let rmmpStatus = rmmp.status;
switch (rmmpStatus) {
case RMMPStatus.GRANTED:
clearInterval(that.poller);
resolve(true);
break;
case RMMPStatus.REJECTED:
clearInterval(that.poller);
Logger.e(
logModule,
ErrorCode.FailedToPollRMMP,
`Could not request edit Mastership in manual mode. >>> RMMP rejected by local client!`,
);
reject('RMMP rejected');
break;
case RMMPStatus.PENDING:
timeout -= 1; // 1000ms/1. 200ms/0.2.
if (timeout < 0) {
await API.RWS.USER.relRMMP();
clearInterval(that.poller);
Logger.e(
logModule,
ErrorCode.FailedToPollRMMP,
`Could not request edit Mastership in manual mode. >>> RMMP request pending timeout!`,
);
reject('Pending timeout');
}
break;
default:
}
}, 200);
});
};
})();
/**
* This class provides a set of control station related interfaces that are not supported by the OmniCore SDK.
* @namespace CONTROLSTATION
* @memberof API.RWS
*/
this.CONTROLSTATION = new (function () {
const TPUName = {
FlexPendant: 'FlexPendant',
};
/**
* Returns `true` if the controller is running with RobotWare 8 (SPOC system). Use this API to check system compatibility
* @alias isSpocSystem
* @memberof API.RWS.CONTROLSTATION
* @returns {Promise<boolean>}
* @example
* let bWithSpocSystem = await API.RWS.CONTROLSTATION.isSpocSystem()
*/
this.isSpocSystem = async function () {
let isSpoc = await RWS.isSpocSystem();
return isSpoc;
};
const checkIfSpocSystem = async function () {
let bSpoc = await API.RWS.CONTROLSTATION.isSpocSystem();
if (!bSpoc) {
throw new Error(ErrorCode.UnsupportedSystem, {
cause: 'ControlStation functionality is only available on RobotWare 8 or later.',
});
}
};
/**
* Returns `true` if current session is held by a control station.
* @alias isControlStation
* @memberof API.RWS.CONTROLSTATION
* @returns {Promise<boolean>}
* @example
* let bControlStation = await API.RWS.CONTROLSTATION.isControlStation()
*/
this.isControlStation = async function () {
await checkIfSpocSystem();
let type = await RWS.ControlStation.getType();
return type !== RWS.ControlStation.ControlStationType.none;
};
/**
* Returns `true` if current session is held by a local control station (e.g., FlexPendant).
* @alias isLocalControlStation
* @memberof API.RWS.CONTROLSTATION
* @returns {Promise<boolean>}
* @example
* let bLocal = await API.RWS.CONTROLSTATION.isLocalControlStation()
*/
this.isLocalControlStation = async function () {
await checkIfSpocSystem();
let type = await RWS.ControlStation.getType();
return type == RWS.ControlStation.ControlStationType.local;
};
/**
* Generate a new control station name.
* @alias isLocalControlStation
* @memberof API.CONTROLSTATION
* @returns {Promise<string>}
* @example
* const name = await API.RWS.CONTROLSTATION.generateControlStationName()
*/
this.generateControlStationName = async function () {
return `WebClient_${Math.floor(Math.random() * 1e9)}`;
};
/**
* Generate a new control station ID.
* @alias generateControlStationID
* @memberof API.RWS.CONTROLSTATION
* @returns {Promise<string>}
* @example
* const id = await API.RWS.CONTROLSTATION.generateControlStationID()
*/
this.generateControlStationID = async function () {
return '{' + API.generateUUID() + '}';
};
const checkIfSameControlId = async function (id) {
const ownId = await RWS.ControlStation.getId();
if (id == ownId) {
return true;
} else {
return false;
}
};
/**
* Checks if write access is currently held.
* Throws an error if write access is not held.
* Use this API to verify write access before executing operations that require write access.
* @alias checkIfHeldWriteAccess
* @memberof API.RWS.CONTROLSTATION
* @returns {Promise<void>} - Resolves if write access is held, throws error otherwise
* @example
* await API.RWS.CONTROLSTATION.checkIfHeldWriteAccess()
*/
this.checkIfHeldWriteAccess = async function () {
const isSpoc = await API.RWS.CONTROLSTATION.isSpocSystem();
if (!isSpoc) return;
// check write access status
const status = await RWS.ControlStation.getWriteAccessStatus();
// check if running in FlexPendant / TPU
if (API.isTPU()) {
if (status.status && status.name == TPUName.FlexPendant) {
// write access is held by current client
return true;
}
throw new Error(ErrorCode.WriteAccessNotHeld, {
cause: 'Write access is not held. Please request it through Flexpendant UI.',
});
} else {
if (status.status && (await checkIfSameControlId(status.id))) {
// write access is held by current client
return true;
} else if (status.status) {
// write access is held by other client
throw new Error(ErrorCode.WriteAccessHeldByOtherClient, {
cause: 'Write access is held by other client. Please request it before proceeding.',
});
} else if (!status.externalControlEnabled) {
// remote access is not enabled
throw new Error(ErrorCode.ExternalControlNotEnabled, {
cause: 'Remote Access is not enabled on the controller. Please enable it to proceed.',
});
}
}
throw new Error(ErrorCode.WriteAccessNotHeld, {
cause: 'Write access is not held. Please request it before proceeding.',
});
};
/**
* Requests the write access.
* This interface is only valid for RobotWare 8 or later.
* When the control station is running in TPU / FlexPendant, request write access through the FlexPendant UI.
* When the control station is running in browser, use this interface.
* This interface only supports remote control station for now.
* Throws an error if external control is not enabled or if write access is held by another client.
* @alias requestWriteAccess
* @memberof API.RWS.CONTROLSTATION
* @param {boolean} [autoRegisterControlStation=false] Whether to automatically register as a remote control station if not registered when requesting write access.
* @returns {Promise<object|void>} - Resolves if request succeeds, rejects if failure
* @example
* await API.RWS.CONTROLSTATION.requestWriteAccess()
*/
this.requestWriteAccess = async function (autoRegisterControlStation = false) {
// TODO: support local control station
// check system compatibility
await checkIfSpocSystem();
// check write access status
const status = await RWS.ControlStation.getWriteAccessStatus();
// check if running in FlexPendant / TPU
if (API.isTPU()) {
if (status.status && status.name == TPUName.FlexPendant) {
// write access is held by current client
return;
}
throw new Error(ErrorCode.RequestWriteAccessThroughTPU, {
cause: 'Failed to request write access. Please request it through Flexpendant UI.',
});
} else {
if (status.status && (await checkIfSameControlId(status.id))) {
// write access is held by current client
return;
} else if (status.status) {
// write access is held by other client
throw new Error(ErrorCode.WriteAccessHeldByOtherClient, {
cause: 'Write access is held by other client. Please request it before proceeding.',
});
}
}
// non TPU, no write access held
if (!status.externalControlEnabled) {
throw new Error(ErrorCode.ExternalControlNotEnabled, {
cause: 'Failed to request write access as remote access is not enabled on the controller.',
});
}
try {
await RWS.ControlStation.requestWriteAccess();
} catch (e) {
if (e.controllerStatus && e.controllerStatus.code == '-1073435871') {
//"Operation rejected because the request is not from a Control Station."
if (autoRegisterControlStation) {
try {
const name = await API.RWS.CONTROLSTATION.generateControlStationName();
await RWS.ControlStation.registerAsRemote(name);
} catch (e) {
throw new Error(ErrorCode.FailedToRegisterRemoteControlStation, {
cause: 'Failed to register as remote control station.',
});
}
try {
await RWS.ControlStation.requestWriteAccess();
} catch (e) {
throw new Error(ErrorCode.FailedToRequestWriteAccess, {cause: e});
}
} else {
// don't auto register control station, throw error directly
throw new Error(ErrorCode.NotRegisteredAsControlStation, {
cause: 'Failed to request write access as current client is not registered as a control station.',
});
}
} else {
throw new Error(ErrorCode.FailedToRequestWriteAccess, {cause: e});
}
}
};
/**
* Releases the write access.
* This interface is only valid for RobotWare 8 or later.
* When the control station is running in TPU / FlexPendant, release write access through the FlexPendant UI.
* When the control station is running in browser, use this interface.
* This interface only supports remote control station for now. And it should be called when write access is no longer needed or before logging out.
* @alias releaseWriteAccess
* @memberof API.RWS.CONTROLSTATION
* @returns {Promise<object|void>} - Resolves if release succeeds, rejects if failure
* @example
* await API.RWS.CONTROLSTATION.releaseWriteAccess()
*/
this.releaseWriteAccess = async function () {
// check system compatibility
await checkIfSpocSystem();
// check write access status
const status = await RWS.ControlStation.getWriteAccessStatus();
// check if running in FlexPendant / TPU
if (API.isTPU()) {
if (!(status.status && status.name == TPUName.FlexPendant)) {
return;
}
throw new Error(ErrorCode.ReleaseWriteAccessThroughTPU, {
cause: 'Failed to release write access. Please release it through Flexpendant UI.',
});
} else {
if (!(status.status && (await checkIfSameControlId(status.id)))) {
return;
}
}
try {
await RWS.ControlStation.releaseWriteAccess();
} catch (e) {
if (e.controllerStatus && e.controllerStatus.code == '-1073435871') {
throw new Error(ErrorCode.NotRegisteredAsControlStation, {
cause: 'Failed to release write access as current client is not registered as a control station.',
});
} else {
throw new Error(ErrorCode.FailedToReleaseWriteAccess, {cause: e});
}
}
};
/**
* Ensures motion control is allowed in SPOC systems.
* Checks if motion control is already allowed, and if not, requests it automatically.
* This interface is only valid for RobotWare 8 or later and remote control stations.
* @alias ensureMotionControlAllowed
* @memberof API.RWS.CONTROLSTATION
* @returns {Promise<void>} - Resolves if motion control is ensured to be allowed, rejects if failure
* @example
* await API.RWS.CONTROLSTATION.ensureMotionControlAllowed()
*/
this.ensureMotionControlAllowed = async function () {
await checkIfSpocSystem();
if (API.isTPU()) {
return;
}
try {
const isAllowed = await RWS.ControlStation.isMotionControlAllowed();
if (!isAllowed) {
await RWS.ControlStation.allowMotionControl(true);
}
} catch (e) {
throw new Error(ErrorCode.FailedToAllowMotionControl, {cause: e});
}
};
const subscribedResources = {};
/**
* Enum for monitor resources
*/
this.MonitorResources = {
'write-access-status': 'write-access-status',
'release-appeal-counter': 'release-appeal-counter',
'tpu-safety-protocol-connected': 'tpu-safety-protocol-connected',
'local-is-connected': 'local-is-connected',
};
const subscribeRes = async function (monitor, func) {
if (rws.isSubscriptionBlocked) {
Logger.w(
WarningType.RWSSubscriptionBlocked,
'API.RWS.CONTROLSTATION: Subscription disabled when trying to monitor control station resource',
);
return;
}
monitor.addCallbackOnChanged(func);
await monitor.subscribe(true);
};
/**
* Monitors a control station resource.
* @alias monitorResource
* @memberof API.RWS.CONTROLSTATION
* @param {string} resourceType The type of resource to monitor (from MonitorResources enum).
* @param {function} [callback] The callback function that is called when the resource changes.
* @returns {Promise<void>}
* @example
* API.RWS.CONTROLSTATION.monitorResource(
* API.RWS.CONTROLSTATION.MonitorResources['write-access-status'],
* (status) => {
* console.log('Status:', status);
* }
* );
*/
this.monitorResource = async function (resourceType, callback = null) {
await checkIfSpocSystem();
const mappingKey = Store.generateMappingKey('ControlStation', [resourceType]);
// Validate if resourceType is a valid monitor resource
if (!Object.keys(this.MonitorResources).includes(resourceType)) {
Logger.w(WarningType.RWSSubscriptionBlocked, `API.RWS.CONTROLSTATION: Unknown ${mappingKey}`);
return;
}
if (rws.isFetchControllerBlocked) {
Logger.w(
WarningType.RWSRequestBlocked,
`API.RWS.CONTROLSTATION: HttpRequest disabled when trying to monitor ${mappingKey}`,
);
return;
}
const storeValue = Store.getNamespace(ControlStation).getMapping(mappingKey);
if (storeValue === undefined) {
try {
Store.getNamespace(ControlStation).setMapping(mappingKey, Pending);
let initialValue;
switch (resourceType) {
case 'write-access-status':
initialValue = await RWS.ControlStation.getWriteAccessStatus();
break;
case 'release-appeal-counter':
initialValue = await RWS.ControlStation.getWriteAccessReleaseAppealChangeCounter();
break;
case 'tpu-safety-protocol-connected':
initialValue = await RWS.ControlStation.getTPUSafetyProtocolStatus();
break;
case 'local-is-connected':
initialValue = await RWS.ControlStation.isLocalConnected();
break;
default:
throw new Error(`Unknown resource type: ${resourceType}`);
}
Store.getNamespace(ControlStation).setMapping(mappingKey, initialValue);
typeof callback === 'function' && callback(initialValue);
rws._events.trigger(mappingKey + '_fetch', initialValue);
} catch (e) {
return API.rejectWithStatus(
`Failed to subscribe to control station resource ${resourceType}`,
e,
ErrorCode.FailedToSubscribeControlStation,
);
}
} else if (storeValue === Pending) {
rws._events.once(mappingKey + '_fetch', (value) => {
typeof callback === 'function' && callback(value);
});
} else {
typeof callback === 'function' && callback(storeValue);
}
if (rws.isSubscriptionBlocked) {
Logger.w(
WarningType.RWSSubscriptionBlocked,
`API.CONTROLSTATION: Subscription disabled when trying to monitor control station resource ${mappingKey}`,
);
return;
}
// if the resource is not subscribed yet, subscribe to it
rws._events.on(mappingKey, callback);
if (!subscribedResources[mappingKey]) {
try {
const monitor = RWS.ControlStation.getMonitor(resourceType);
const cbResource = function (data) {
Logger.i(logModule, `Control station resource changed: ${resourceType}`);
Store.getNamespace(ControlStation).setMapping(mappingKey, data);
rws._events.trigger(mappingKey, data);
Logger.i(InfoType.RobotOperation, `Triggered control station resource: ${resourceType}`);
};
subscribeRes(monitor, cbResource.bind(this));
subscribedResources[mappingKey] = true;
Logger.i(InfoType.RobotOperation, `Control station resource subscribed:${mappingKey}`);
} catch (e) {
return API.rejectWithStatus(
`Failed to subscribe to control station resource ${resourceType}`,
e,
ErrorCode.FailedToSubscribeControlStation,
);
}
}
};
/**
* Monitors the write access status of control station. The callback receives an object with `status` (boolean) and `name` (string) properties indicating who holds write access.
* @alias monitorWriteAccessStatus
* @memberof API.RWS.CONTROLSTATION
* @param {function} [callback] The callback function that is called when write access status changes.
* @returns {Promise<void>}
* @example
* API.RWS.CONTROLSTATION.monitorWriteAccessStatus((status) => {
* console.log('Write access status:', status);
* if (status.status) {
* console.log('Write access held by:', status.name);
* }
* });
*/
this.monitorWriteAccessStatus = async function (callback = null) {
return this.monitorResource(this.MonitorResources['write-access-status'], callback);
};
/**
* Monitors the write access release appeal counter. The counter increments when another client requests write access currently held by the current client.
* @alias monitorReleaseAppealCounter
* @memberof API.RWS.CONTROLSTATION
* @param {function} [callback] The callback function that is called when counter changes.
* @returns {Promise<void>}
* @example
* API.RWS.CONTROLSTATION.monitorReleaseAppealCounter((counter) => {
* console.log('Release appeal counter:', counter);
* });
*/
this.monitorReleaseAppealCounter = async function (callback = null) {
return this.monitorResource(this.MonitorResources['release-appeal-counter'], callback);
};
/**
* Monitors the TPU safety protocol connection status. Returns `true` when a local control station with safety protocol is connected.
* @alias monitorTPUSafetyProtocolConnected
* @memberof API.RWS.CONTROLSTATION
* @param {function} [callback] The callback function that is called when connection status changes.
* @returns {Promise<void>}
* @example
* API.RWS.CONTROLSTATION.monitorTPUSafetyProtocolConnected((isConnected) => {
* console.log('TPU safety protocol connected:', isConnected);
* });
*/
this.monitorTPUSafetyProtocolConnected = async function (callback = null) {
return this.monitorResource(this.MonitorResources['tpu-safety-protocol-connected'], callback);
};
/**
* Monitors the local control station connection status. Returns `true` when a local control station is connected to the controller.
* @alias monitorLocalIsConnected
* @memberof API.RWS.CONTROLSTATION
* @param {function} [callback] The callback function that is called when connection status changes.
* @returns {Promise<void>}
* @example
* API.RWS.CONTROLSTATION.monitorLocalIsConnected((isConnected) => {
* console.log('Local control station connected:', isConnected);
* });
*/
this.monitorLocalIsConnected = async function (callback = null) {
return this.monitorResource(this.MonitorResources['local-is-connected'], callback);
};
})();
})();
rws.constructedRWS = true;
};
if (typeof API.constructedRWS === 'undefined') {
factoryApiRws(API);
}
export default API;
export {factoryApiRws};