/** * 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 []; } }