Source: commands/transporter.js

/**
 * Roboliq: Automation for liquid-handling robots
 * @copyright 2017, ETH Zurich, Ellis Whitehead
 * @license GPL-3.0
 */

/**
 * Handles transporter instructions and supplies some additional control functions
 * for moving the ROMAs.
 * @module
 */

import _ from 'lodash';
import commandHelper from 'roboliq-processor/dist/commandHelper.js';

function getMoveRomaHomeLine(romaIndex) {
	return getRomaMoveLine(romaIndex, 2);
}

/**
 * Move a ROMA
 * @param  {number} romaIndex - index of roma
 * @param  {number} action - 0=open gripper, 1=close gripper, 2=move home, 3=move relative,
 * @return {string} string representation of ROMA command
 */
function getRomaMoveLine(romaIndex, action) {
	const x = {
		action,
		gripperDistance: 80,
		force: 0,
		dx: 0,
		dy: 0,
		dz: 0,
		speed: 150,
		maximumSpeed: 1,
		romaIndex
	};
	return `ROMA(${x.action},${x.gripperDistance},${x.force},${x.dx},${x.dy},${x.dz},${x.speed},${x.maximumSpeed},${x.romaIndex});`;
}

/**
 * Handle the `transporter._moveLidFromContainerToSite` instruction.
 *
 * @param  {object} params - original paramters
 * @param  {object} parsed - parsed parameters
 * @param  {object} data - protocol data
 * @return {array} an array of objects that describe output, effects, and table effects
 */
export function _moveLidFromContainerToSite(params, parsed, data) {
	// console.log("_moveLidFromContainerToSite: "+JSON.stringify(parsed, null, '\t'));
	const params2 = {
		agent: params.agent,
		equipment: params.equipment,
		program: params.program,
		object: params.container,
		destination: parsed.value.container.location
	};
	// console.log("params2: "+JSON.stringify(params2, null, '\t'));
	const parsed2 = commandHelper.parseParams(params2, data, data.protocol.schemas["transporter._movePlate"]);
	const lidHandling = {
		lid: parsed.objectName.object,
		action: "remove",
		location: parsed.objectName.destination,
		destination: parsed.objectName.destination
	};
	return _movePlate(params2, parsed2, data, lidHandling);
}

/**
 * Handle the `transporter._moveLidFromSiteToContainer` instruction.
 *
 * @param  {object} params - original paramters
 * @param  {object} parsed - parsed parameters
 * @param  {object} data - protocol data
 * @return {array} an array of objects that describe output, effects, and table effects
 */
export function _moveLidFromSiteToContainer(params, parsed, data) {
	// console.log("_moveLidFromContainerToSite: "+JSON.stringify(parsed, null, '\t'));
	const params2 = {
		agent: params.agent,
		equipment: params.equipment,
		program: params.program,
		object: params.container,
		destination: parsed.value.container.location
	};
	// console.log("params2: "+JSON.stringify(params2, null, '\t'));
	const parsed2 = commandHelper.parseParams(params2, data, data.protocol.schemas["transporter._movePlate"]);
	const lidHandling = {
		lid: parsed.objectName.object,
		action: "cover",
		location: parsed.objectName.origin,
		destination: parsed.objectName.container
	};
	return _movePlate(params2, parsed2, data, lidHandling);
}

/**
 * Handle the `transporter._movePlate` instruction.
 *
 * @param {object} params - original paramters
 * @param {object} parsed - parsed parameters
 * @param {object} data - protocol data
 * @param {object} [lidHandling0] - an optional option to define lid handling - this is only used by the `_moveLidFromContainerToSite` and `_moveLidFromSiteToContainer` handlers.
 * @param {string} [lidHandling0.lid] - name of the lid
 * @param {string} [lidHandling0.action] - should either be "remove" or "cover"
 * @param {string} [lidHandling0.location] - the site where the lid should be moved from or to
 * @param {string} [lidHandling0.destination] - the site where the lid should be after the transfer
 * @return {array} an array of objects that describe output, effects, and table effects
 */
export function _movePlate(params, parsed, data, lidHandling0) {
	// console.log("_movePlate: "+JSON.stringify(parsed, null, '\t'));
	// romaIndex: "(@equipment).evowareRoma: integer"
	const values = commandHelper.lookupPaths({
		romaIndex: ["@equipment", "evowareRoma"],
		programName: ["@program"],
		plateModelName: [["@object", "model"], "evowareName"],
		plateOrigName: ["@object", "location"],
		plateOrigCarrierName: [["@object", "location"], "evowareCarrier"],
		plateOrig: [["@object", "location"]],
		plateOrigGrid: [["@object", "location"], "evowareGrid"],
		plateOrigSite: [["@object", "location"], "evowareSite"],
		plateDest: [["@destination"]],
		plateDestCarrierName: ["@destination", "evowareCarrier"],
		plateDestGrid: ["@destination", "evowareGrid"],
		plateDestSite: ["@destination", "evowareSite"],
	}, params, data);

	// Lid handling parameters
	const lidHandling = {
		enabled: false,
		grid: "",
		carrierName: "",
		site: "(Not defined)"
	};
	if (!_.isEmpty(lidHandling0)) {
		lidHandling.enabled = true;
		lidHandling.removeAtSource = (lidHandling0.action == "remove");
		lidHandling.grid = commandHelper.lookupPath(["@location", "evowareGrid"], lidHandling0, data)
		lidHandling.carrierName = commandHelper.lookupPath(["@location", "evowareCarrier"], lidHandling0, data)
		lidHandling.site = commandHelper.lookupPath(["@location", "evowareSite"], lidHandling0, data)
	}

	const plateDestName = parsed.objectName.destination;

	// It may be that multiple sites are defined which are actually the same physical location.
	// We can supress transporter commands between the logical sites by checking whether the sames have the same siteIdUnique.
	// console.log({plateOrig: values.plateOrig, plateDest: values.plateDest})
	if (values.plateOrig.siteIdUnique && values.plateOrig.siteIdUnique === values.plateDest.siteIdUnique) {
		return [{
			tableEffects: [
				[[values.plateOrigCarrierName, values.plateOrigGrid, values.plateOrigSite], {label: _.last(values.plateOrigName.split('.')), labwareModelName: values.plateModelName}],
				[[values.plateDestCarrierName, values.plateDestGrid, values.plateDestSite], {label: _.last(plateDestName.split('.')), labwareModelName: values.plateModelName}],
			]
		}];
	}

	const romaIndexPrev = _.get(data.objects, ["EVOWARE", "romaIndexPrev"], values.romaIndex);
	const bMoveBackToHome = parsed.value.evowareMoveBackToHome || false; // 1 = move back to home position
	values.moveBackToHome = (bMoveBackToHome) ? 1 : 0;
	//console.log(JSON.stringify(values, null, '\t'))
	const l = [
		`"${values.plateOrigGrid}"`,
		`"${values.plateDestGrid}"`,
		values.moveBackToHome,
		(lidHandling.enabled) ? 1 : 0,
		0, // speed: 0 = maximum, 1 = taught in vector dialog
		values.romaIndex,
		(lidHandling.removeAtSource) ? 1 : 0,
		`"${lidHandling.grid}"`,
		`"${values.plateModelName}"`,
		`"${values.programName}"`,
		'""',
		'""',
		`"${values.plateOrigCarrierName}"`,
		`"${lidHandling.carrierName}"`,
		`"${values.plateDestCarrierName}"`,
		`"${values.plateOrigSite}"`,
		`"${lidHandling.site}"`,
		`"${values.plateDestSite}"`
	];
	const line = `Transfer_Rack(${l.join(",")});`;
	//println(s"line: $line")
	//val let = JsonUtils.makeSimpleObject(x.`object`+".location", JsString(plateDestName))

	const plateName = parsed.objectName.object;

	const items = [];

	if (values.romaIndex !== romaIndexPrev) {
		items.push({
			line: getMoveRomaHomeLine(romaIndexPrev)
		});
	}

	items.push({
		line,
		effects:  _.fromPairs(_.compact([
			[`${plateName}.location`, plateDestName],
			(lidHandling.enabled) ? [`${lidHandling0.lid}.location`, lidHandling0.destination] : undefined,
			[`EVOWARE.romaIndexPrev`, values.romaIndex]]
		)),
		tableEffects: [
			[[values.plateOrigCarrierName, values.plateOrigGrid, values.plateOrigSite], {label: _.last(values.plateOrigName.split('.')), labwareModelName: values.plateModelName}],
			[[values.plateDestCarrierName, values.plateDestGrid, values.plateDestSite], {label: _.last(plateDestName.split('.')), labwareModelName: values.plateModelName}],
		]
	});

	return items;
}

/**
 * Move the last-moved ROMA back to its home position.
 *
 * @param  {object} data - protocol data
 * @return {array} an array of objects that describe output, effects, and table effects
 */
export function moveLastRomaHome(data) {
	const romaIndexPrev = _.get(data.objects, ["EVOWARE", "romaIndexPrev"]);
	if (romaIndexPrev) {
		return [{
			line: getMoveRomaHomeLine(romaIndexPrev)
		}];
	}
	else {
		return [];
	}
}