Tool Driver Development
This guide outlines the process of developing and integrating new tool drivers into the Galago platform. Tool drivers are essential components that enable communication between the Galago controller and laboratory instruments.
Table of Contents
- Overview
- Tool Drivers
- Development Process
- Creating the Proto Interfaces
- Implementing the Driver
- Testing the Driver
- Integrating with the Controller
- Best Practices
- Troubleshooting
Overview
We use gRPC to communicate between tool servers and the controller. Each tool has its own instance of a gRPC server running on a dedicated port. The controller and Python both reference the <workcell_config>.json
file to match a tool ID to the port it runs on.
Tool Drivers
Manufacturer | Instrument Name | Category |
---|---|---|
BioTek | Cytation | Plate Reader |
Opentrons | Opentrons2 | Liquid Handler |
Brooks | PF400 | Dispenser |
Liconic | Liconic | Incubator |
Cognex | Dataman70 | Barcode Reader |
Molecular Devices | Spectramax | Plate Reader |
QInstruments | Bioshake | Shaker |
BioNex | HIG Centrifuge | Centrifuge |
Agilent | Bravo | Liquid Handler |
Agilent | VCode | Barcode Reader |
Agilent | Plateloc | Plate Sealer |
Azenta | XPeel | Plate Peeler |
Thermo Fisher Scientific | ALPS3000 | Plate Sealer |
Custom | Toolbox | Utility Tool |
Hamilton | Hamilton Star/Star Plus | Liquid Handler |
Development Process
Creating the Proto Interfaces
We first need to define the methods and attributes that the top-level driver commands will execute. The data is structured in the form of 'Messages'.
These files are located under interfaces/tools
.
Here is an example proto for a basic bioshake driver:
syntax = 'proto3';
package com.science.foundry.tools.grpc_interfaces.bioshake;
message Command {
oneof command {
Grip grip = 1;
Ungrip ungrip = 2;
Home home = 3;
StartShake start_shake = 4;
StopShake stop_shake = 5;
Reset reset = 6;
WaitForShakeToFinish wait_for_shake_to_finish = 7;
}
message Grip {}
message Ungrip {}
message Home {}
message StartShake {
int32 speed = 1;
int32 duration = 2;
}
message StopShake {}
message Reset {}
message WaitForShakeToFinish {
int32 timeout = 1;
}
}
message Config {
string com_port = 1;
string tool_id = 2;
}
Implementing the Driver
After defining the proto interface, you need to implement the actual driver that will communicate with the laboratory instrument. The driver should be implemented in Python and follow the structure below:
from concurrent import futures
import grpc
import time
import logging
from com.science.foundry.tools.grpc_interfaces.bioshake import bioshake_pb2
from com.science.foundry.tools.grpc_interfaces.bioshake import bioshake_pb2_grpc
class BioshakeDriver(bioshake_pb2_grpc.BioshakeServicer):
def __init__(self, config):
self.com_port = config.com_port
self.tool_id = config.tool_id
# Initialize connection to the device
def Execute(self, request, context):
command_type = request.WhichOneof("command")
if command_type == "grip":
# Implement grip functionality
return bioshake_pb2.CommandResponse()
elif command_type == "ungrip":
# Implement ungrip functionality
return bioshake_pb2.CommandResponse()
# Implement other commands...
return bioshake_pb2.CommandResponse(error="Unknown command")
Testing the Driver
Before integrating with the controller, it's important to test your driver to ensure it communicates correctly with the laboratory instrument. Create a test script that:
- Initializes the driver with a test configuration
- Executes various commands
- Verifies the responses
import grpc
import time
from com.science.foundry.tools.grpc_interfaces.bioshake import bioshake_pb2
from com.science.foundry.tools.grpc_interfaces.bioshake import bioshake_pb2_grpc
def run_test():
# Create a gRPC channel
channel = grpc.insecure_channel('localhost:50051')
# Create a stub (client)
stub = bioshake_pb2_grpc.BioshakeStub(channel)
# Test grip command
grip_request = bioshake_pb2.Command(grip=bioshake_pb2.Command.Grip())
response = stub.Execute(grip_request)
print(f"Grip response: {response}")
# Test shake command
shake_request = bioshake_pb2.Command(
start_shake=bioshake_pb2.Command.StartShake(speed=500, duration=10)
)
response = stub.Execute(shake_request)
print(f"Shake response: {response}")
# Add more tests as needed
if __name__ == '__main__':
run_test()
Integrating with the Controller
To integrate your driver with the Galago controller, you need to:
- Add your driver to the appropriate directory in the codebase
- Update the workcell configuration file to include your tool
- Register the tool with the controller
Example workcell configuration entry:
{
"tools": [
{
"id": "bioshake_1",
"type": "bioshake",
"port": 50051,
"config": {
"com_port": "COM3",
"tool_id": "bioshake_1"
}
}
]
}
Best Practices
Error Handling
- Implement comprehensive error handling
- Return descriptive error messages
- Log errors for debugging
Troubleshooting
Issue | Possible Cause | Solution |
---|---|---|
Connection failures | Incorrect port or address | Verify connection settings in the configuration file |
Command timeouts | Device not responding | Check physical connections and device power |
Protocol errors | Mismatched proto definitions | Ensure proto files are up-to-date and properly compiled |
Tip: When developing a new driver, start with a simple implementation that supports basic functionality, then gradually add more complex features.
Note: Always test your driver thoroughly with the actual hardware before deploying it in a production environment.