4 Robot configuration

In order for Roboliq to compile your protocol for your robot setup, it needs to know how the robot is configured. This is the most complicated part of Roboliq – it requires advanced technical knowledge or your robot and some programming skill. Once the configuraiton has been specified, future users don’t need to be concerned with it.

So if someone else has configured Roboliq for your robot already, then you should probably just proceed to the next chapter. However, if you need to write the configuration, this chapter is for you.

4.1 Evoware configuration

(If you are not using an Evoware robot, you can skip this section.)

Roboliq supplies a simplified method for configuring Evoware robots. You will need to create a JavaScript file in which you define an EvowareConfigSpec object, and then have it converted to the Roboliq protocol format. Your config file will have the following structure:

const evowareConfigSpec = {
  // Lab name and robot name
  namespace: "YOUR LAB ID",
  name: "YOUR ROBOT ID",
  // Compiler settings
  config: {
    TEMPDIR: "TEMPORARY DIRECTORY FOR MEASUREMENT FILES",
    ROBOLIQ: "COMMAND TO CALL ROBOLIQ'S RUNTIME",
    BROWSER: "PATH TO WEB BROWSER"
  },
  // Bench sites on the robot
  sites: {
    MYSITE1: {evowareCarrier: "CARRIER ID", evowareGrid: MYGRID, evowareSite: MYSITE},
    ...
  },
  // Labware models
  models: {
    MYPLATEMODEL1: {type: "PlateModel", rows: 8, columns: 12, evowareName: "EVOWARE LABWARE NAME"},
    ...
  },
  // List of which sites and labware models can be used together
  siteModelCompatibilities: [
    {
      sites: ["MYSITE1", ...],
      models: ["MYPLATEMODEL1", ...]
    },
    ...
  ],
  // List of the robot's equipment
  equipment: {
    MYEQUIPMENT1: {
      module: "EQUIPMENT1.js",
      params: {
        ...
      }
    }
  },
  // List of which lid types can be stacked on which labware models
  lidStacking: [
    {
      lids: ["lidModel_standard"],
      models: ["MYPLATEMODEL1"]
    }
  ],
  // List of the robot's robotic arms
  romas: [
    {
      description: "roma1",
      // List of sites this ROMA can safely access with which vectors
      safeVectorCliques: [
                { vector: "Narrow", clique: ["MYSITE1", ...] },
                ...
            ]
    },
    ...
  ],
  // Liquid handing arm
  liha: {
    // Available tip models
    tipModels: {
      MYTIPMODEL1000: {programCode: "1000", min: "3ul", max: "950ul", canHandleSeal: false, canHandleCells: true},
      ...
    },
    // List of LIHA syringes (e.g. 8 entries if it has 8 syringes)
    syringes: [
      { tipModelPermanent: "MYTIPMODEL1000" },
      ...
    ],
    // Sites that the LIHA can access
    sites: ["MYSITE1", ...],
    // Specifications for how to wash the tips
    washPrograms: {
      // For Example: Specification for flushing the tips with `programCode == 1000`
      flush_1000: { ... },
      ...
    }
  },
  // Additional user-defined command handlers
  commandHandlers: {
    "MYCOMMAND1": function(params, parsed, data) { ... },
    ...
  },
  // Optional functions to choose among planning alternatives
  planAlternativeChoosers: {
    // For Example: when the `shaker.shakePlate` command has
    // multiple shakers available, you might want to use
    // the one name `MYEQUIPMENT1`
    "shaker.canAgentEquipmentSite": (alternatives) => {
            const l = alternatives.filter(x => x.equipment.endsWith("MYEQUIPMENT1"));
            if (l.length > 0)
                return l[0];
        }
  }
};

const EvowareConfigSpec = require('roboliq-evoware/dist/EvowareConfigSpec.js');
module.exports = EvowareConfigSpec.makeProtocol(evowareConfigSpec);

The details about evowareConfigSpec are in the Evoware API documenation, and an extensive example can be found in roboliq-processor/tests/ourlab.js. This information is probably enough to configure your Evoware robot, and you can skip the rest of this chapter unless you need to setup a different kind of robot.

4.2 Components

A configuration is a JavaScript object with the following properties:

  • roboliq: specifies the version of Roboliq.
  • schemas: JSON Schema defintitions for all object and commands.
  • objects: specifies the objects provided by the robot.
  • commandHandlers: specifies the functions that handle protocol commands, for example by generating low-level commands from high-level commands.
  • predicates: specifies the logical predicates used by Roboliq’s A.I. to figure out how to compile some high-level commands to low-level commands for this configuration.
  • objectToPredicateConverters: specifies functions that generate logical predicates based from objects.
  • planAlternativeChoosers: specifies functions that can choose a specific plan among various alternative plans
  • planHandlers: specifies the functions that transform logical tasks into commands.

The first four properties are easy for the average JavaScript programmer to understand: they are straight-forward JSON values or JavaScript functions. In contrast, the last four properties (predicates, objectToPredicateConverters, planAlternativeChoosers, and planHandlers) rely on concepts from Artificial Intelligence, which will require more effort to grasp.

4.2.1 roboliq

As with protocols, the first property of a configuration should be roboliq: v1.

4.2.2 schemas

For every command and object type, Roboliq requires a schema that defines its properties. By convention, object types begin with an upper-case letter, and commands begin with a lower-case letter. The schemas are written in a standardized format, which you can learn more about at JSON Schema.

Normally, the schemas required for your robot should be automatically provided by your chosen backend and the equipment you select. The schemas property is a hash map: its keys are the command names and object type names; its values are the respective JSON schemas. You will not need to write schemas unless you are creating new commands or objects types for Roboliq. An example schema is provided below in the commandHandlers section.

4.2.3 objects

The objects property of a configuration is the same as describe for protocols in the Quick Start chapter. However, robot configurations usually contain complex objects, such as measurement devices, whereas protocols usually only define fairly simple labware. Furthermore, the configuration may contain information that’s required for the backend compiler. The object schemas are described in the documentation for standard Roboliq objects and for Evoware objects.

Namespaces. Because a robot “contains” its devices, sites, and permanent labware, we use Namespace types to build nested objects. For example, if our lab is named “bsse”, we have a robot named “bert”, and it has a site named “P1”, this could be encoded as follows:

objects:
  bsse:
    type: Namespace
    bert:
      type: Namespace
      site:
        type: Namespace
        P1:
          type: Site
          ...

We can then reference the site in the protocol as bsse.bert.site.P1.

4.2.4 commandHandlers

A command handler is a JavaScript function that processes command parameters and returns information to Roboliq about the command’s effects, sub-commands, and user messages.

Command handlers are supplied by various modules. Roboliq’s command modules are in the subdirectory roboliq-processor/src/commands, and Evoware’s command modules are in the subdirectory roboliq-evoware/src/commands. The API documentation contains information about the available commands.

If you want to write your own command handler, you can add it to commandHandlers as a property. The property name should be the name of the command, and the value should be the command handler function. For example, let’s make a simplistic function called my.hello that tells an Evoware robot to say “Hello, YOURNAME!”. First we’ll defined the schema for the new command:

schemas: {
  "my.hello": {
    description: "Say hello to someone",
    properties: {
      name: {
        description: "Name to say hello to",
        type: "string"
      }
    },
    required: ["name"]
  }
}

This schema lets Roboliq know that there’s a command named my.hello.

commandHandlers: {
  "my.hello": function(params, parsed, data) {
    return {
      expansion: [
                {
                    command: "evoware._userPrompt",
                    text: "Hello, "+parsed.value.name",
                }
            ]
    };
  }
}

4.3 Logic components

Roboliq uses an Articifial Intelligence method called Hierarchical Task Network (HTN) Planning. In particular, it uses a SHOP2 implementation written by Warren Sack in JavaScript.

4.3.1 predicates

A predicate defines a “true statement”. For example, the following predicate is named sealer.canAgentEquipmentProgramModelSite1, and it lets Roboliq know that the robot agent ourlab.mario.evoware can use the sealer ourlab.mario.sealer with the labware model ourlab.model.plateModel_96_round_transparent_nunc at site ourlab.mario.site.ROBOSEAL with the internal program file PerkinElmer_weiss.bcf.

yaml "sealer.canAgentEquipmentProgramModelSite": "agent": "ourlab.mario.evoware" "equipment": "ourlab.mario.sealer" "program": "C:\\HJBioanalytikGmbH\\...\\PerkinElmer_weiss.bcf", "model": "ourlab.model.plateModel_96_round_transparent_nunc", "site": "ourlab.mario.site.ROBOSEAL"yaml

The general form for predicates is:

predicateName:
  object1: value1
  object2: value2
  ...

The values are usually object names, and predicates often define true relationships among objects. All of the predicates together form a database of true statements about the “world” in which the protocol will run. They are used by certain command handlers to automatically figure out valid operations without the user needing to specify the low-level details. (Currently, the end-user documentation does not contain details about which predicates are used by which commands, but it can be found in the command handler source code.)

Here is an example excerpt of using the predicate database to find all valid sealers from the sealer.sealPlate command handler:

        const predicates = [
            {"sealer.canAgentEquipmentProgramModelSite": {
                "agent": parsed.objectName.agent,
                "equipment": parsed.objectName.equipment,
                "program": parsed.objectName.program,
                "model": model,
                "site": parsed.objectName.site
            }}
        ];
        const [chosen, alternatives] = commandHelper.queryLogic(data, predicates, "sealer.canAgentEquipmentProgramModelSite");

The function commandHelper.queryLogic() will find all solutions the predicates array, filling in the missing values as necessary. If more than one alternative solution is present, it will choose one of them. The solution can either by chosen by a appropriate function planAlternativeChoosers, or else Roboliq simply picks the first item in the alternatives list.

4.3.2 objectToPredicateConverters

Roboliq’s AI needs predicates describing the available objects. These are generated dynamically by the functions supplied in objectToPredicateConverters. It is a map from an object type to a function that accepts a named object and returns an array of predicates. For example, here is Roboliq’s converter for Plate objects:

objectToPredicateConverters: {
  Plate: function(name, object) {
        const predicates = [
            { "isLabware": { "labware": name } },
            { "isPlate": { "labware": name } },
            { "model": { "labware": name, "model": object.model } },
            { "location": { "labware": name, "site": object.location } }
        ];
        if (object.sealed) {
            predicates.push({ "plateIsSealed": { "labware": name } });
        }
        return predicates;
    },
    ...
}

This converter creates between 4 and 6 predicates for every plate object: isLabware lets the AI know that the plate is a kind of labware, isPlate says that the plate is a plate, model says which labware model the plate has, and location says where the plate is located. Furthermore, plateIsSealed will be present if-and-only-if the plate is sealed.

You’ll only need to create additional object-to-predicate converters if you want to extend Roboliq’s object types, or perhaps if you create an advanced command that required additional logic.

4.3.3 planHandlers

Plan handlers are functions that convert from a planning action to a Roboliq command (usually a low-level command). It is a map from an action name to a function that accepts that action parameters and returns an array of commands. The function also accepts the parameters of the parent command (the one that generated the plan), and the protocol data, in case those are needed to compute the new command.

An example use-case is using the transporter.movePlate command to move a plate into a closed centrifuge: the planning algorithm will include an action to open the centrifuge first, and then the transporter.movePlate command will call the appropriate function in planHandlers to create the required sub-command.

4.4 Conclusion

Configuring a robot can be complicated. First of all, it requires a lot of detailed knowledge about your robot. Secondly, it involves a lot of interdependencies. For example, in order to support a new labware model, you’ll need to add the model to the models list (easy), but you also need to update the list of site/model compatibilities, the list of safe transport vectors, and perhaps the list of which models accept lids or can be stacked on top of each other.

It’s certainly do-able, but you’re likely to encounter some frustrations if you need to trouble-shoot why you get compiler errors when trying to use your new model, for example.