/** * Roboliq: Automation for liquid-handling robots * @copyright 2017, ETH Zurich, Ellis Whitehead * @license GPL-3.0 */ /** * Centrifuge commands module (see {@tutorial Commands#centrifuge} or [command specification](tutorial-Commands.html#centrifuge)). * * THIS SECTION IS FOR TESTING ONLY: * {@link loadEvowareCarrierData} * {@link module:commandHelper} * {@link module:commands/centrifuge} * END OF SECTION * * See {@link roboliq#Protocol}. * @module commands/centrifuge * @return {Protocol} * @version v1 */ var _ = require('lodash'); var jmespath = require('jmespath'); import yaml from 'yamljs'; var commandHelper = require('../commandHelper.js'); var expect = require('../expect.js'); var misc = require('../misc.js'); /** * Create predicates for objects of type = "Centrifuge" * @static */ var objectToPredicateConverters = { "Centrifuge": function(name, object) { return [{ "isCentrifuge": { "equipment": name } }]; }, }; function centrifuge2(params, parsed, data) { var llpl = require('../HTN/llpl.js').create(); llpl.initializeDatabase(data.predicates); //console.log(JSON.stringify(parsed, null, '\t')) var agent = parsed.objectName.agent || "?agent"; var equipment = parsed.objectName.equipment || "?equipment"; var object1 = parsed.value.object1; var object2 = parsed.value.object2; var query0 = { "centrifuge.canAgentEquipmentModelSite1Site2": { "agent": "?agent", "equipment": "?equipment", "model": "?model", "site1": "?site1", "site2": "?site2" } }; var query = _.merge({}, query0, {"centrifuge.canAgentEquipmentModelSite1Site2": { "agent": parsed.objectName.agent, "equipment": parsed.objectName.equipment, "model": object1.model, "site1": parsed.objectName.site1, "site2": parsed.objectName.site2 }} ); var resultList = llpl.query(query); var alternatives = jmespath.search(resultList, '[]."centrifuge.canAgentEquipmentModelSite1Site2"'); if (_.isEmpty(resultList)) { var resultList2 = llpl.query(query0); if (_.isEmpty(resultList2)) { return { errors: ["missing equipment data (please add predicates `centrifuge.canAgentEquipmentModelSite1Site`)"] }; } else { return { errors: ["missing equipment configuration for " + JSON.stringify(query)] }; } } // Pick a plan let chosen = undefined; if (data.planAlternativeChoosers.hasOwnProperty("centrifuge.canAgentEquipmentModelSite1Site2")) { chosen = data.planAlternativeChoosers["centrifuge.canAgentEquipmentModelSite1Site2"](alternatives, data); // console.log({chosen}) } const params2 = chosen || alternatives[0]; const destination1 = (parsed.value.destinationAfter1 === "stay") ? params2.site1 : _.isUndefined(parsed.objectName.destinationAfter1) ? object1.location : parsed.objectName.destinationAfter1; const destination2 = (parsed.value.destinationAfter2 === "stay") ? params2.site2 : _.isUndefined(parsed.objectName.destinationAfter2) ? object2.location : parsed.objectName.destinationAfter2; var expansion = [ (object1.location === params2.site1) ? null : [ { command: "equipment.openSite", agent: params2.agent, equipment: params2.equipment, site: params2.site1 }, { "command": "transporter.movePlate", "object": parsed.objectName.object1, "destination": params2.site1 } ], (object2.location === params2.site2) ? null : [ { command: "equipment.openSite", agent: params2.agent, equipment: params2.equipment, site: params2.site2 }, { "command": "transporter.movePlate", "object": parsed.objectName.object2, "destination": params2.site2 } ], { command: "equipment.close", agent: params2.agent, equipment: params2.equipment }, { command: ["equipment.run", params2.agent, params2.equipment].join('|'), agent: params2.agent, equipment: params2.equipment, program: parsed.objectName.program || parsed.value.program }, // Move object1 back (destination1 === params2.site1) ? null : [ { command: "equipment.openSite", agent: params2.agent, equipment: params2.equipment, site: params2.site1 }, { "command": "transporter.movePlate", "object": parsed.objectName.object1, "destination": destination1 } ], // Move object2 back (destination2 === params2.site2) ? null : [ { command: "equipment.openSite", agent: params2.agent, equipment: params2.equipment, site: params2.site2 }, { "command": "transporter.movePlate", "object": parsed.objectName.object2, "destination": destination2 } ], // Close the centrifuge (destination1 === params2.site1 && destination2 === params2.site2) ? null : { command: "equipment.close", agent: params2.agent, equipment: params2.equipment }, ]; const warnings = (object1.model != object2.model) ? ["[W#CENT0001] object1 and object2 are of different labware models; this may be problematic for centrifugation."] : []; //console.log("centrifuge2 expansion:") //console.log(JSON.stringify(expansion, null, '\t')) return { expansion, alternatives, warnings }; } function insertPlates2(params, parsed, data) { // console.log("insertPlates2: "+JSON.stringify(parsed, null, '\t')); var llpl = require('../HTN/llpl.js').create(); llpl.initializeDatabase(data.predicates); if (!parsed.value.object1 && !parsed.value.object2) { // do nothing return {}; } var agent = parsed.objectName.agent || "?agent"; var equipment = parsed.objectName.equipment || "?equipment"; var object1 = parsed.value.object1; var object2 = parsed.value.object2; const model = (object1) ? object1.model : object2.model; var query0 = { "centrifuge.canAgentEquipmentModelSite1Site2": { "agent": "?agent", "equipment": "?equipment", "model": "?model", "site1": "?site1", "site2": "?site2" } }; var query = _.merge({}, query0, {"centrifuge.canAgentEquipmentModelSite1Site2": { "agent": parsed.objectName.agent, "equipment": parsed.objectName.equipment, "model": model, "site1": parsed.objectName.site1, "site2": parsed.objectName.site2 }} ); var resultList = llpl.query(query); var alternatives = jmespath.search(resultList, '[]."centrifuge.canAgentEquipmentModelSite1Site2"'); if (_.isEmpty(resultList)) { var resultList2 = llpl.query(query0); if (_.isEmpty(resultList2)) { return { errors: ["missing equipment data (please add predicates `centrifuge.canAgentEquipmentModelSite1Site`)"] }; } else { return { errors: ["missing equipment configuration for " + JSON.stringify(query)] }; } } // Pick a plan let chosen = undefined; if (data.planAlternativeChoosers.hasOwnProperty("centrifuge.canAgentEquipmentModelSite1Site2")) { chosen = data.planAlternativeChoosers["centrifuge.canAgentEquipmentModelSite1Site2"](alternatives, data); // console.log({chosen}) } const params2 = chosen || alternatives[0]; var expansion = [ (!object1 || object1.location === params2.site1) ? null : [ { command: "equipment.openSite", agent: params2.agent, equipment: params2.equipment, site: params2.site1 }, { "command": "transporter.movePlate", "object": parsed.objectName.object1, "destination": params2.site1 } ], (!object2 || object2.location === params2.site2) ? null : [ { command: "equipment.openSite", agent: params2.agent, equipment: params2.equipment, site: params2.site2 }, { "command": "transporter.movePlate", "object": parsed.objectName.object2, "destination": params2.site2 } ], ]; const warnings = (parsed.input.object1Model && parsed.input.object2Model && parsed.input.object1Model != parsed.input.object2Model) ? ["[W#CENT0001] object1 and object2 are of different labware models; this may be problematic for centrifugation."] : []; return { expansion, alternatives, warnings }; } insertPlates2.inputSpec = { agent: "?agent", equipment: "?equipment", object1: "?object1", object2: "?object2", object1Model: "?object1*model", object2Model: "?object2*model", site1: "?site1", site2: "?site2" }; /** * Handlers for {@link centrifuge} commands. * @static */ var commandHandlers = { "centrifuge.centrifuge2": centrifuge2, "centrifuge.insertPlates2": insertPlates2 }; module.exports = { roboliq: "v1", objectToPredicateConverters, schemas: yaml.load(__dirname+"/../schemas/centrifuge.yaml"), commandHandlers };