/** * Roboliq: Automation for liquid-handling robots * @copyright 2017, ETH Zurich, Ellis Whitehead * @license GPL-3.0 */ /** * Namespace for the ``transporter`` commands. * @namespace transporter * @version v1 */ /** * Transporter commands module. * @module commands/transporter * @return {Protocol} * @version v1 */ const _ = require('lodash'); import yaml from 'yamljs'; const commandHelper = require('../commandHelper.js'); const expect = require('../expect.js'); const misc = require('../misc.js'); /** * Create predicates for objects of type = "Transporter" * @static */ var objectToPredicateConverters = { "Transporter": function(name, data) { return [{ "isTransporter": { "equipment": name } }]; }, }; /** * Transport a lid from a container to a destination site. */ function moveLidFromContainerToSite(params, parsed, data) { // console.log("transporter.moveLidFromContainerToSite:"); console.log(JSON.stringify(parsed, null, '\t')) if (parsed.input.containerObject.type != "Plate") { expect.throw({paramName: "object"}, "expected lid to be on a plate; instead lid's location is "+parsed.input.container); } const transporterLogic = require('./transporterLogic.json'); const keys = ["null", "one"]; const movePlateParams = makeMoveLidFromContainerToSiteParams(parsed); const shop = require('../HTN/shop.js'); const llpl = require('../HTN/llpl.js').create(); llpl.initializeDatabase(data.predicates); let input0 = data.predicates; let plan; let errorLog = ""; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const method = {method: makeMoveLidFromContainerToSiteMethod(parsed, movePlateParams, i)}; input0 = input0.concat(_.values(transporterLogic[key])); input0 = input0.concat([method]); if (transporterLogic.hasOwnProperty(key)) { llpl.addToDatabase(transporterLogic[key]); } llpl.addToDatabase([method]); // const fs = require('fs'); // fs.writeFileSync("a.json", JSON.stringify(llpl.database, null, '\t')); const queryResultsAll = queryMovePlateMethod(llpl, method.method, i); // If we didn't find a path for this method: if (queryResultsAll.length === 0) { //const queryResultOne = llpl.query({stackable: {below: "?below", above: "ourlab.model.lidModel_384_square"}}); //console.log("queryResultOne: " +JSON.stringify(queryResultOne)) const text = debugMovePlateMethod(llpl, method.method, queryResultsAll, i); errorLog += text; } // If we did find a path: else { // console.log(`${queryResultsAll.length} path(s) found, e.g.: ${JSON.stringify(queryResultsAll[0])}`); const tasks = { "tasks": { "ordered": [{ moveLidFromContainerToSite: movePlateParams }] } }; const input = input0.concat([tasks]); var planner = shop.makePlanner(input); plan = planner.plan(); //console.log(key, plan) if (!_.isEmpty(plan)) { //console.log("plan found for "+key) //console.log(planner.ppPlan(plan)); } else { console.log("apparently unable to open some site or something") } break; } } if (_.isEmpty(plan)) { console.log(errorLog); //console.log("transporter.movePlate: "+JSON.stringify(parsed, null, '\t')) return {errors: [`unable to find a transportation path for lid ${parsed.input.object} on ${parsed.input.container} from ${parsed.input.origin} to ${parsed.input.destination}`]}; } var tasks = planner.listAndOrderTasks(plan, true); //console.log("Tasks:") //console.log(JSON.stringify(tasks, null, ' ')); var cmdList = _(tasks).map(function(task) { return _(task).map(function(taskParams, taskName) { return (data.planHandlers.hasOwnProperty(taskName)) ? data.planHandlers[taskName](taskParams, params, data) : []; }).flatten().value(); }).flatten().value(); // console.log("cmdList: "+JSON.stringify(cmdList, null, ' ')); // Create the expansion object var expansion = {}; var i = 1; _.forEach(cmdList, function(cmd) { expansion[i.toString()] = cmd; i += 1; }); // Create the effets object var effects = {}; effects[`${parsed.objectName.object}.location`] = parsed.objectName.destination; return { expansion: expansion, effects: effects }; } moveLidFromContainerToSite.inputSpec = { lid: "object", lidModel: "object*model", container: "object*location", containerObject: "object*location*", model: "?object*location*model", origin: "?object*location*location", destination: "destination" }; /** * Take the parsed parameters passed to `transporter.moveLidFromContainerToSite` * and create the parameter list for the task `moveLidFromContainerToSite` in `makeMoveLidFromContainerToSiteMethod()`. * If any of the following parameters are not specified, then they will also * be a part of the created parameters: agent, equipment, program */ function makeMoveLidFromContainerToSiteParams(parsed) { // console.log("makeMoveLidFromContainerToSiteParams: "+JSON.stringify(parsed)) return _.pickBy({ agent: (parsed.objectName.agent) ? "?agent" : undefined, equipment: (parsed.objectName.equipment) ? "?equipment" : undefined, program: (parsed.objectName.program || parsed.value.program) ? "?program" : undefined, lid: "?lid", container: "?container", destination: "?destination" }); } function makeMoveLidFromContainerToSiteMethod(parsed, moveLidParams, n) { // console.log("makeMoveLidFromContainToSiteMethod: "+JSON.stringify(parsed, null, '\t')); const {lid, lidModel, container, model, origin, destination} = parsed.input; function makeArray(name, value) { return _.map(_.range(n), i => (_.isUndefined(value)) ? name+(i+1) : value); } const agents = makeArray("?agent", parsed.objectName.agent); const equipments = makeArray("?equipment", parsed.objectName.equipment); const programs = makeArray("?program", parsed.objectName.program || parsed.value.program) //const origin = parsed.value.object.location; // if (n === 0) { const name = "moveLidFromContainerToSite-0"; return { "description": `${name}: transport lid from container to destination in ${n} step(s)`, "task": {"moveLidFromContainerToSite": moveLidParams}, "preconditions": [ {"location": {"labware": lid, "site": destination}} ], "subtasks": {"ordered": [ {"print": {"text": name}} ]} }; } else if (n === 1) { const name = "moveLidFromContainerToSite-1"; return { "description": `${name}: transport plate from origin to destination in ${n} step(s)`, "task": {"moveLidFromContainerToSite": moveLidParams}, "preconditions": [ {"model": {"labware": lid, "model": lidModel}}, // TODO: Superfluous, but maybe check anyway {"location": {"labware": lid, "site": container}}, //{"labwareHasLid": {"labware": container}}, {"model": {"labware": container, "model": model}}, {"location": {"labware": container, "site": origin}}, {"siteIsOpen": {"site": origin}}, {"siteIsOpen": {"site": destination}}, {"siteModel": {"site": origin, "siteModel": "?originModel"}}, {"siteModel": {"site": destination, "siteModel": "?destinationModel"}}, {"stackable": {"below": "?destinationModel", "above": lidModel}}, {"siteIsClear": {"site": destination}}, {"siteCliqueSite": {"siteClique": "?siteClique1", "site": origin}}, {"siteCliqueSite": {"siteClique": "?siteClique1", "site": destination}}, {"transporter.canAgentEquipmentProgramSites": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], "siteClique": "?siteClique1"}} ], "subtasks": {"ordered": [ {"print": {"text": name}}, {"transporter._moveLidFromContainerToSite": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], lid, lidModel, container, model, origin, "originModel": "?originModel", destination, "destinationModel": "?destinationModel"}} ]} }; } assert(false); } /** * Transport a lid from an origin site to a container. */ function moveLidFromSiteToContainer(params, parsed, data) { // console.log("transporter.moveLidFromSiteToContainer:"); console.log(JSON.stringify(parsed, null, '\t')); if (parsed.input.originType != "Site") { expect.throw({paramName: "object"}, "expected lid to be on a site; instead lid's location is "+parsed.input.origin); } const transporterLogic = require('./transporterLogic.json'); const keys = ["null", "one"]; const movePlateParams = makeMoveLidFromSiteToContainerParams(parsed); const shop = require('../HTN/shop.js'); const llpl = require('../HTN/llpl.js').create(); llpl.initializeDatabase(data.predicates); let input0 = data.predicates; let plan; let errorLog = ""; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const method = {method: makeMoveLidFromSiteToContainerMethod(parsed, movePlateParams, i, llpl)}; input0 = input0.concat(_.values(transporterLogic[key])); input0 = input0.concat([method]); if (transporterLogic.hasOwnProperty(key)) { llpl.addToDatabase(transporterLogic[key]); } llpl.addToDatabase([method]); // const fs = require('fs'); // fs.writeFileSync("a.json", JSON.stringify(llpl.database, null, '\t')); const queryResultsAll = queryMovePlateMethod(llpl, method.method, i); // If we didn't find a path for this method: if (queryResultsAll.length === 0) { //const queryResultOne = llpl.query({stackable: {below: "?below", above: "ourlab.model.lidModel_384_square"}}); //console.log("queryResultOne: " +JSON.stringify(queryResultOne)) const text = debugMovePlateMethod(llpl, method.method, queryResultsAll, i); errorLog += text; } // If we did find a path: else { // console.log(`${queryResultsAll.length} path(s) found, e.g.: ${JSON.stringify(queryResultsAll[0])}`); const tasks = { "tasks": { "ordered": [{ moveLidFromSiteToContainer: movePlateParams }] } }; const input = input0.concat([tasks]); var planner = shop.makePlanner(input); plan = planner.plan(); //console.log(key, plan) if (!_.isEmpty(plan)) { //console.log("plan found for "+key) //console.log(planner.ppPlan(plan)); } else { console.log("apparently unable to open some site or something") } break; } } if (_.isEmpty(plan)) { console.log(errorLog); const x = _.pickBy({agent: parsed.objectName.agent, equipment: parsed.objectName.equipment, program: parsed.objectName.program || parsed.value.program, model: parsed.value.object.model, origin: parsed.objectName["object.location"], destination: parsed.objectName.destination}); //console.log("transporter.movePlate: "+JSON.stringify(parsed, null, '\t')) return {errors: ["unable to find a transportation path for lid `"+parsed.objectName.object+"` from `"+parsed.objectName.origin+"` to `"+parsed.objectName.container+"` at `"+parsed.value.container.location+"`", JSON.stringify(x)]}; } var tasks = planner.listAndOrderTasks(plan, true); //console.log("Tasks:") //console.log(JSON.stringify(tasks, null, ' ')); var cmdList = _(tasks).map(function(task) { return _(task).map(function(taskParams, taskName) { return (data.planHandlers.hasOwnProperty(taskName)) ? data.planHandlers[taskName](taskParams, params, data) : []; }).flatten().value(); }).flatten().value(); // console.log("cmdList: "+JSON.stringify(cmdList, null, ' ')); // Create the expansion object var expansion = {}; var i = 1; _.forEach(cmdList, function(cmd) { expansion[i.toString()] = cmd; i += 1; }); // Create the effets object var effects = {}; effects[`${parsed.objectName.object}.location`] = parsed.objectName.container; // console.log("expansion: "+JSON.stringify(expansion, null, '\t')) // console.log("effects: "+JSON.stringify(effects, null, '\t')) return { expansion: expansion, effects: effects }; } moveLidFromSiteToContainer.inputSpec = { origin: "object*location", originType: "object*location*type", lid: "object", lidModel: "object*model", container: "container", model: "container*model", destination: "container*location" }; /** * Take the parsed parameters passed to `transporter.moveLidFromSiteToContainer` * and create the parameter list for the task `moveLidFromSiteToContainer` in `makeMoveLidFromSiteToContainerMethod()`. * If any of the following parameters are not specified, then they will also * be a part of the created parameters: agent, equipment, program */ function makeMoveLidFromSiteToContainerParams(parsed) { // console.log("makeMoveLidFromSiteToContainerParams: "+JSON.stringify(parsed)) return _.pickBy({ agent: (parsed.objectName.agent) ? "?agent" : undefined, equipment: (parsed.objectName.equipment) ? "?equipment" : undefined, program: (parsed.objectName.program || parsed.value.program) ? "?program" : undefined, lid: "?lid", origin: "?origin", container: "?container" }); } function makeMoveLidFromSiteToContainerMethod(parsed, moveLidParams, n, llpl) { // console.log("makeMoveLidFromContainToSiteMethod: "+JSON.stringify(parsed, null, '\t')); const {origin, lid, lidModel, container, model, destination} = parsed.input; // console.log({lid, lidModel, container, model, origin, destination}) const originQuery = llpl.query({"siteModel": {"site": origin, "siteModel": "?originModel"}}); const destinationQuery = llpl.query({"siteModel": {"site": destination, "siteModel": "?destinationModel"}}); // console.log("originQuery: "+JSON.stringify(originQuery, null, '\t')) // console.log("destinationQuery: "+JSON.stringify(destinationQuery, null, '\t')) const originModel = originQuery[0].siteModel.siteModel; const destinationModel = destinationQuery[0].siteModel.siteModel; // console.log("labwareHasNoLid?: "+JSON.stringify(llpl.query({"labwareHasNoLid": {"site": container}}))) function makeArray(name, value) { return _.map(_.range(n), i => (_.isUndefined(value)) ? name+(i+1) : value); } const agents = makeArray("?agent", parsed.objectName.agent); const equipments = makeArray("?equipment", parsed.objectName.equipment); const programs = makeArray("?program", parsed.objectName.program || parsed.value.program) if (n === 0) { const name = "moveLidFromSiteToContainer-0"; return { "description": `${name}: transport lid from container to destination in ${n} step(s)`, "task": {"moveLidFromSiteToContainer": moveLidParams}, "preconditions": [ {"location": {"labware": lid, "labware": destination}} ], "subtasks": {"ordered": [ {"print": {"text": name}} ]} }; } else if (n === 1) { const name = "moveLidFromSiteToContainer-1"; return { "description": `${name}: transport plate from origin to destination in ${n} step(s)`, "task": {"moveLidFromSiteToContainer": moveLidParams}, "preconditions": [ // {"model": {"labware": lid, "model": lidModel}}, // TODO: Superfluous, but maybe check anyway {"location": {"labware": lid, "site": origin}}, {"model": {"labware": container, "model": model}}, {"location": {"labware": container, "site": destination}}, {"siteIsOpen": {"site": origin}}, {"siteIsOpen": {"site": destination}}, {"siteModel": {"site": origin, "siteModel": originModel}}, {"siteModel": {"site": destination, "siteModel": destinationModel}}, {"stackable": {"below": model, "above": lidModel}}, {"labwareHasNoLid": {"labware": container}}, {"siteCliqueSite": {"siteClique": "?siteClique1", "site": origin}}, {"siteCliqueSite": {"siteClique": "?siteClique1", "site": destination}}, {"transporter.canAgentEquipmentProgramSites": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], "siteClique": "?siteClique1"}} ], "subtasks": {"ordered": [ {"print": {"text": name}}, {"transporter._moveLidFromSiteToContainer": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], lid, lidModel, container, model, origin, "originModel": originModel, destination, "destinationModel": destinationModel}} ]} }; } assert(false); } function makeMovePlateParams(parsed) { return _.merge({}, { agent: (parsed.objectName.agent) ? "?agent" : undefined, equipment: (parsed.objectName.equipment) ? "?equipment" : undefined, program: (parsed.objectName.program || parsed.value.program) ? "?program" : undefined, labware: "?labware", destination: "?destination" }); } function makeMovePlateMethod(parsed, movePlateParams, n) { //console.log("makeMovePlateMethod: "+JSON.stringify(parsed, null, '\t')); function makeArray(name, value) { return _.map(_.range(n), i => (_.isUndefined(value)) ? name+(i+1) : value); } const labware = parsed.objectName.object; const model = parsed.value.object.model; const origin = parsed.value.object.location; const destination = parsed.objectName.destination; const agents = makeArray("?agent", parsed.objectName.agent); const equipments = makeArray("?equipment", parsed.objectName.equipment); const programs = makeArray("?program", parsed.objectName.program || parsed.value.program) //const origin = parsed.value.object.location; // if (n === 0) { const name = "movePlate-0"; return { "description": `${name}: transport plate from origin to destination in ${n} step(s)`, "task": {"movePlate": movePlateParams}, "preconditions": [ {"location": {"labware": labware, "site": destination}} ], "subtasks": {"ordered": [ {"print": {"text": name}} ]} }; } else if (n === 1) { const name = "movePlate-1"; return { "description": `${name}: transport plate from origin to destination in ${n} step(s)`, "task": {"movePlate": movePlateParams}, "preconditions": [ {"model": {"labware": labware, "model": model}}, // TODO: Superfluous, but maybe check anyway {"location": {"labware": labware, "site": origin}}, // TODO: Superfluous, but maybe check anyway {"siteModel": {"site": origin, "siteModel": "?originModel"}}, {"siteModel": {"site": destination, "siteModel": "?destinationModel"}}, {"stackable": {"below": "?destinationModel", "above": model}}, {"siteIsClear": {"site": destination}}, {"siteCliqueSite": {"siteClique": "?siteClique1", "site": origin}}, {"siteCliqueSite": {"siteClique": "?siteClique1", "site": destination}}, {"transporter.canAgentEquipmentProgramSites": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], "siteClique": "?siteClique1"}} ], "subtasks": {"ordered": [ {"print": {"text": name}}, {"openAndMovePlate": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], "labware": labware, "model": model, "origin": origin, "originModel": "?originModel", "destination": destination, "destinationModel": "?destinationModel"}} ]} }; } else if (n === 2) { const name = "movePlate-2"; return { "description": `${name}: transport plate from origin to destination in ${n} step(s)`, "task": {"movePlate": movePlateParams}, "preconditions": [ {"model": {"labware": labware, "model": model}}, // TODO: can handle this in programatically {"location": {"labware": labware, "site": origin}}, // TODO: can handle this in programatically {"siteModel": {"site": origin, "siteModel": "?originModel"}}, {"siteModel": {"site": destination, "siteModel": "?destinationModel"}}, {"stackable": {"below": "?destinationModel", "above": model}}, {"siteIsClear": {"site": destination}}, // TODO: Check this programmatically instead of via logic {"siteCliqueSite": {"siteClique": "?siteClique1", "site": origin}}, {"siteCliqueSite": {"siteClique": "?siteClique2", "site": destination}}, {"siteCliqueSite": {"siteClique": "?siteClique1", "site": "?site2"}}, {"siteCliqueSite": {"siteClique": "?siteClique2", "site": "?site2"}}, {"not": {"same": {"thing1": "?site2", "thing2": origin}}}, {"not": {"same": {"thing1": "?site2", "thing2": destination}}}, {"transporter.canAgentEquipmentProgramSites": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], "siteClique": "?siteClique1"}}, {"transporter.canAgentEquipmentProgramSites": {"agent": agents[1], "equipment": equipments[1], "program": programs[1], "siteClique": "?siteClique2"}}, {"siteModel": {"site": "?site2", "siteModel": "?site2Model"}}, {"stackable": {"below": "?site2Model", "above": model}}, {"siteIsClear": {"site": "?site2"}} ], "subtasks": {"ordered": [ {"print": {"text": name}}, {"openAndMovePlate": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], "labware": labware, "model": model, "origin": origin, "originModel": "?originModel", "destination": "?site2", "destinationModel": "?site2Model"}}, {"openAndMovePlate": {"agent": agents[1], "equipment": equipments[1], "program": programs[1], "labware": labware, "model": model, "origin": "?site2", "originModel": "?site2Model", "destination": destination, "destinationModel": "?destinationModel"}} ]} }; } else if (n === 3) { const name = "movePlate-3"; return { "description": `${name}: transport plate from origin to destination in ${n} step(s)`, "task": {"movePlate": movePlateParams}, "preconditions": [ {"model": {"labware": labware, "model": model}}, // TODO: can handle this in programatically {"location": {"labware": labware, "site": origin}}, // TODO: can handle this in programatically {"siteModel": {"site": origin, "siteModel": "?originModel"}}, {"siteModel": {"site": destination, "siteModel": "?destinationModel"}}, {"stackable": {"below": "?destinationModel", "above": model}}, {"siteIsClear": {"site": destination}}, // TODO: Check this programmatically instead of via logic {"siteCliqueSite": {"siteClique": "?siteClique1", "site": origin}}, {"siteCliqueSite": {"siteClique": "?siteClique3", "site": destination}}, {"siteCliqueSite": {"siteClique": "?siteClique1", "site": "?site2"}}, {"siteCliqueSite": {"siteClique": "?siteClique2", "site": "?site2"}}, {"siteCliqueSite": {"siteClique": "?siteClique2", "site": "?site3"}}, {"siteCliqueSite": {"siteClique": "?siteClique3", "site": "?site3"}}, {"not": {"same": {"thing1": "?site2", "thing2": origin}}}, {"not": {"same": {"thing1": "?site2", "thing2": destination}}}, {"not": {"same": {"thing1": "?site2", "thing2": "?site3"}}}, {"not": {"same": {"thing1": "?site3", "thing2": origin}}}, {"not": {"same": {"thing1": "?site3", "thing2": destination}}}, {"transporter.canAgentEquipmentProgramSites": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], "siteClique": "?siteClique1"}}, {"transporter.canAgentEquipmentProgramSites": {"agent": agents[1], "equipment": equipments[1], "program": programs[1], "siteClique": "?siteClique2"}}, {"transporter.canAgentEquipmentProgramSites": {"agent": agents[2], "equipment": equipments[2], "program": programs[2], "siteClique": "?siteClique3"}}, {"siteModel": {"site": "?site2", "siteModel": "?site2Model"}}, {"siteModel": {"site": "?site3", "siteModel": "?site3Model"}}, {"stackable": {"below": "?site2Model", "above": model}}, {"stackable": {"below": "?site3Model", "above": model}}, {"siteIsClear": {"site": "?site2"}}, {"siteIsClear": {"site": "?site3"}} ], "subtasks": {"ordered": [ {"print": {"text": name}}, {"openAndMovePlate": {"agent": agents[0], "equipment": equipments[0], "program": programs[0], "labware": labware, "model": model, "origin": origin, "originModel": "?originModel", "destination": "?site2", "destinationModel": "?site2Model"}}, {"openAndMovePlate": {"agent": agents[1], "equipment": equipments[1], "program": programs[1], "labware": labware, "model": model, "origin": "?site2", "originModel": "?site2Model", "destination": "?site3", "destinationModel": "?site3Model"}}, {"openAndMovePlate": {"agent": agents[2], "equipment": equipments[2], "program": programs[2], "labware": labware, "model": model, "origin": "?site3", "originModel": "?site3Model", "destination": destination, "destinationModel": "?destinationModel"}} ]} }; } assert(false); } function queryMovePlateMethod(llpl, method, n) { //console.log("originId: "+originId) const criteria = method.preconditions; const queryAll = {"and": criteria}; const queryResultsAll = llpl.query(queryAll); return queryResultsAll; } function debugMovePlateMethod(llpl, method, queryResultsAll, n) { const lines = []; const criteria = method.preconditions; //console.log(queryResultsAll.length); if (queryResultsAll.length === 0) { lines.push(`\nmovePlate-${n} failed:`); // console.log("debug: "+criteria); // let failures = 0; _.forEach(criteria, criterion => { // console.log({criterion}) const queryOne = {"and": [criterion]}; const queryResultOne = llpl.query(queryOne); //console.log("queryResultOne: "+JSON.stringify(queryResultOne)); if (queryResultOne.length === 0) { // failures++; lines.push("FAILED: "+JSON.stringify(criterion)); } else { //console.log("queryResults:\n"+JSON.stringify(queryResultOne, null, '\t')); } }); } else { console.log("found paths: "+queryResultsAll) } return lines.join("\n"); } /** * Handlers for {@link transporter} commands. * @static */ var commandHandlers = { /** * Transport a lid from labware to site or from site to labware. * * Handler should return `effects` with the lid's new location * and set or remove labware's `hasLid` property. */ "transporter._moveLidFromContainerToSite": function(params, parsed, data) { return {effects: { //[`${parsed.objectName.container}.hasLid`]: false, [`${parsed.objectName.object}.location`]: parsed.objectName.destination }}; }, /** * Transport a lid from labware to site or from site to labware. * * Handler should return `effects` with the lid's new location * and set or remove labware's `hasLid` property. */ "transporter._moveLidFromSiteToContainer": function(params, parsed, data) { return {effects: { //[`${parsed.objectName.container}.hasLid`]: false, [`${parsed.objectName.object}.location`]: parsed.objectName.container }}; }, /** * Transport a plate to a destination. * * Handler should return `effects` with the plate's new location. */ "transporter._movePlate": function(params, parsed, data) { return {effects: { [`${parsed.objectName.object}.location`]: parsed.objectName.destination }}; }, "transporter.moveLidFromContainerToSite": moveLidFromContainerToSite, "transporter.moveLidFromSiteToContainer": moveLidFromSiteToContainer, /** * Transport a plate to a destination. */ "transporter.movePlate": function(params, parsed, data) { //console.log("transporter.movePlate("+JSON.stringify(params)+")") const transporterLogic = require('./transporterLogic.json'); const keys = ["null", "one", "two", "three"]; const movePlateParams = makeMovePlateParams(parsed); const shop = require('../HTN/shop.js'); const llpl = require('../HTN/llpl.js').create(); llpl.initializeDatabase(data.predicates); let input0 = data.predicates; let plan; let errorLog = ""; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const method = {method: makeMovePlateMethod(parsed, movePlateParams, i)}; input0 = input0.concat(_.values(transporterLogic[key])); input0 = input0.concat([method]); if (transporterLogic.hasOwnProperty(key)) { llpl.addToDatabase(transporterLogic[key]); } llpl.addToDatabase([method]); // const fs = require('fs'); // fs.writeFileSync("a.json", JSON.stringify(llpl.database, null, '\t')); const queryResultsAll = queryMovePlateMethod(llpl, method.method, i); // If we didn't find a path for this method: if (queryResultsAll.length === 0) { const text = debugMovePlateMethod(llpl, method.method, queryResultsAll, i); errorLog += text; } // If we did find a path: else { // console.log(`${queryResultsAll.length} path(s) found, e.g.: ${JSON.stringify(queryResultsAll[0])}`); const tasks = { "tasks": { "ordered": [{ movePlate: movePlateParams }] } }; const input = input0.concat([tasks]); var planner = shop.makePlanner(input); plan = planner.plan(); //console.log(key, plan) if (!_.isEmpty(plan)) { //console.log("plan found for "+key) //console.log(planner.ppPlan(plan)); } else { console.log("apparently unable to open some site or something") } break; } } /* const transporterPredicates = _(transporterLogic).values().map(x => _.values(x)).flatten().value(); var input0 = [].concat(data.predicates, transporterPredicates, [tasksOrdered]); var input = input0; //console.log(JSON.stringify(input, null, '\t')); var shop = require('../HTN/shop.js'); var planner = shop.makePlanner(input); var plan = planner.plan(); //console.log("plan:\n"+JSON.stringify(plan, null, ' ')); var x = planner.ppPlan(plan); console.log(x); */ if (_.isEmpty(plan)) { console.log(errorLog); /* var agentId = params.agent || "?agent"; var modelId = parsed.value.object.model || "?model"; var originId = parsed.value.object.location || "?site"; debug_movePlate_null(input0, agentId, parsed.objectName.object, modelId, originId, parsed.objectName.destination); debug_movePlate_one(input0, agentId, parsed.objectName.object, modelId, originId, parsed.objectName.destination); for (let i = 0; i <= 3; i++) { const method = makeMovePlateMethod(parsed, movePlateParams, i); debugMovePlateMethod() */ } if (_.isEmpty(plan)) { const x = _.merge({}, {agent: parsed.objectName.agent, equipment: parsed.objectName.equipment, program: parsed.objectName.program || parsed.value.program, model: parsed.value.object.model, origin: parsed.value.object.location, destination: parsed.objectName.destination}); //console.log("transporter.movePlate: "+JSON.stringify(parsed, null, '\t')) return {errors: ["unable to find a transportation path for `"+parsed.objectName.object+"` from `"+misc.findObjectsValue(parsed.objectName.object+".location", data.objects)+"` to `"+parsed.objectName.destination+"`", JSON.stringify(x)]}; } var tasks = planner.listAndOrderTasks(plan, true); // console.log("Tasks:") // console.log(JSON.stringify(tasks, null, ' ')); var cmdList = _(tasks).map(function(task) { return _(task).map(function(taskParams, taskName) { return (data.planHandlers.hasOwnProperty(taskName)) ? data.planHandlers[taskName](taskParams, params, data) : []; }).flatten().value(); }).flatten().value(); // console.log("cmdList:") // console.log(JSON.stringify(cmdList, null, ' ')); // Create the expansion object var expansion = {}; var i = 1; _.forEach(cmdList, function(cmd) { expansion[i.toString()] = cmd; i += 1; }); // console.log({expansion}) // Create the effets object var effects = {}; effects[`${parsed.objectName.object}.location`] = parsed.objectName.destination; return { expansion: expansion, effects: effects }; }, "transporter.doThenRestoreLocation": function(params, parsed, data) { //console.log("transporter.doThenRestoreLocation("+JSON.stringify(parsed, null, '\t')+")"); const expansion = _.cloneDeep(_.values(parsed.value.steps)); // console.log("objects: "+JSON.stringify(parsed.value.objects)) for (let i = 0; i < parsed.value.objects.length; i++) { const labwareName = parsed.objectName[`objects.${i}`]; const command = _.merge({}, { command: "transporter.movePlate", agent: parsed.objectName.agent, equipment: parsed.objectName.equipment, program: parsed.value.program, object: labwareName, destination: parsed.value.objects[i].location }); expansion.push(command); } return { expansion }; } }; /** * Plan handler to allow other modules to use `transporter._movePlate` as a * planning action. * @static */ var planHandlers = { "transporter._movePlate": function(params, parentParams, data) { return [{ command: "transporter._movePlate", agent: params.agent, equipment: params.equipment, program: params.program, object: params.labware, destination: params.destination }]; }, "transporter._moveLidFromContainerToSite": function(params, parentParams, data) { return [{ command: "transporter._moveLidFromContainerToSite", agent: params.agent, equipment: params.equipment, program: params.program, object: params.lid, container: params.container, destination: params.destination }]; }, "transporter._moveLidFromSiteToContainer": function(params, parentParams, data) { return [{ command: "transporter._moveLidFromSiteToContainer", agent: params.agent, equipment: params.equipment, program: params.program, object: params.lid, origin: params.origin, container: params.container }]; }, }; module.exports = { roboliq: "v1", objectToPredicateConverters: objectToPredicateConverters, schemas: yaml.load(__dirname+'/../schemas/transporter.yaml'), commandHandlers: commandHandlers, planHandlers: planHandlers };