Skip to main content

Lab Equipment Automation Package

Project description

Control.lab.ly

Lab Equipment Automation Package

Description

User-friendly package that enables flexible automation an reconfigurable setups for high-throughput experimentation and machine learning.

Package Structure

  1. Compound
  2. Control
  3. Make
  4. Measure
  5. Move
  6. Transfer
  7. View

Device support

  • Make
    • (QInstruments) BioShake Orbital Shaker
    • (Arduino-based devices)
      • Multi-channel LED array
      • Multi-channel spin-coater
      • Peltier device
  • Measure
    • (Keithley)
      • SMU 2450 Source Measure Unit Instrument
      • DAQ 6510 Data Acquisition and Multimeter System
    • (PiezoRobotics) Dynamic Mechanical Analyser (DMA)
    • (Sentron) SI series pH meters
    • (Arduino-based device)
      • Precision mass balance
      • Load cell
  • Move
    • (Creality) Ender-3
    • (Dobot)
      • M1 Pro
      • MG400
    • (Arduino-based device) gantry robot running on GRBL
  • Transfer
    • (Dobot) Gripper attachments
    • (Sartorius) rLINE® dispensing modules
    • (TriContinent) C Series syringe pumps
    • (Arduino-based device) Peristaltic pump and syringe system
  • View
    • (FLIR) AX8 thermal imaging camera
    • (General) Web cameras

Installation

Control.lab.ly can be found on PyPI and can be easily installed with pip install.

$ pip install control-lab-ly

Basic Usage

Simple start-up guide for basic usage of the package.

Import desired class

from controllably.Move.Cartesian import Ender
mover = Ender(...)
mover.safeMoveTo((x,y,z))

View documentation

Use the built-in guide to read the documentation for the package.

from controllably import guide_me
guide_me()

Screenshot of the documentation guide

Alternatively, details for each class / module / package can be explored by using the help function.

help(Ender)

For basic usage, this is all you need to know. Check the documentation for more details on each class and function.


Advanced Usage

For more advanced uses, Control.lab.ly provides a host of tools to streamline the development of lab equipment automation. This includes setting up configuration files and adding plugin drivers.

Import package

import controllably as lab

Optionally, you can set the safety policy for the session. This feature allows the user to prevent collisions before each movement is made. The safety policy has to be assigned before importing any of the Mover objects.

lab.set_safety('high')  # Notifies; Pauses for input before every move action
lab.set_safety('low')   # Notifies; Waits for countdown before every move action
lab.set_safety(None)    # Carries out movement actions without delay

# Import control-lab-ly classes only after setting the safety policy

Contents

  1. Setups
  2. Decks
  3. Addresses
  4. Plugins

1. Creating a new setup

Create a /configs/MySetup folder that holds the configuration files for the setup, which includes config.yaml and layout.json.

lab.create_setup(setup_name = "MySetup")

1.1 config.yaml

This file stores the configuration and calibration values for your devices.

MyDevice:                                       # device name
  module: Move                                  # top-level category
  class: Cartesian.Ender                        # device class
  settings:
    port: COM1                                  # serial port address
    setting_A: {'tuple': [300,0,200]}           # use keys to define the type of iterable
    setting_B: {'array': [[0,1,0],[-1,0,0]]}    # only tuple and np.array supported

Each device configuration starts with the device name, then the following parameters:
module: top-level category (such as Make, Measure, Move,Transfer, View)
class: point to specific subclass using dot notation
settings: various initialisation settings
See the guide for more details on these parameters

Compound devices are similarly configured.

MyCompoundDevice:                         # compound device name
  module: Compound
  class: LiquidMover.LiquidMoverSetup
  settings:                               # settings for your compound device
    setting_C: True
    component_config:                     # nest component configuration settings here
      MyFirstDevice:                      # component device name
        module: Transfer
        class: Liquid.SyringeAssembly
        settings:                         # settings for your component device
          port: COM22
          setting_D: 2                    
      MySecondDevice:                     # component device name
        module: Mover
        class: Jointed.Dobot.M1Pro
        settings:                         # settings for your compound device
          ip_address: '192.0.0.1'

The configuration values for the component devices are nested under the component_config setting of the compound device.

Lastly, you can define shortcuts (or nicknames) to quickly access the components of compound devices.

SHORTCUTS:
  First: 'MyCompoundDevice.MyFirstDevice'
  Second: 'MyCompoundDevice.MySecondDevice'

A different serial port address or camera index may be used by different machines for the same device.
See Section 3 to find out how to manage the different addresses used by different machines.

1.2 layout.json

This file stores the layout configuration of your physical workspace, also known as a Deck.
See Section 2 on how to load this information into the setup.

Optional: if your setup does not involve moving objects around in a pre-defined workspace, a layout configuration may not be required

This package uses the same Labware files as those provided by Opentrons, which can be found here, and custom Labware files can be created here. Labware files are JSON files that specifies the external and internal dimensions of a Labware block / object.

{
  "reference_points":{
    "1": [11.1,22.2,33.3],
    "2": [44.4,55.5,66.6],
    "3": [77.7,88.8,99.9]
  },
  "slots":{
    "1": {
      "name": "MyLabware01",
      "exclusion_height": -1,
      "filepath": "MyREPO/.../MyLabware01.json"
    },
    "2": {
      "name": "MyLabware02",
      "exclusion_height": 0,
      "filepath": "MyREPO/.../MyLabware02.json"
    },
    "3": {
      "name": "MyLabware03",
      "exclusion_height": 10,
      "filepath": "MyREPO/.../MyLabware03.json"
    }
  }
}

In reference_points, the bottom-left coordinates of each slot on the deck are defined. Slots are positions where Labware blocks may be placed.

In slots, the name of the Labware and the filepath to the JSON file containing Labware details are defined. The filepath starts with the name of the repository's base folder.

The exclusion_height is the height (in mm) above the dimensions of the Labware block to steer clear from when performing movement actions. Values less than 0 means the Labware is not avoided.

(Note: Labware avoidance only applies to final coordinates (i.e. destination). Does not guarantee collision avoidance when using point-to-point move actions. Use safeMoveTo() instead.)

1.3 Load setup

The initialisation of the setup occurs when importing setup from configs.MySetup. With setup, you can access all the devices that you have defined in Section 1.1.

### main.py ###
# Add repository folder to sys.path
from pathlib import Path
import sys
REPO = 'MyREPO'
ROOT = str(Path().absolute()).split(REPO)[0]
sys.path.append(f'{ROOT}{REPO}')

# Import the initialised setup
from configs.MySetup import setup
setup.MyDevice
setup.First

2. Managing a deck

Optional: if your setup does not involve moving items around in a pre-defined workspace, a Deck may not be required

2.1 Loading a deck

To load a Deck from the layout file, use the loadDeck() function of a Mover object (or its subclasses).

from configs.MySetup import setup, LAYOUT_FILE
setup.Mover.loadDeck(LAYOUT_FILE)

LAYOUT_FILE contains the details that has been defined in layout.json (see Section 1.2)

2.2 Loading a Labware

To load a Labware onto the deck, use the loadLabware() method of the Deck object.

setup.Mover.deck.loadLabware(...)

This package uses the same Labware files as those provided by Opentrons, which can be found here, and custom Labware files can be created here. Labware files are JSON files that specifies the external and internal dimensions of a Labware block / object.

3. Managing project addresses

A /configs folder will have been created in the base folder of your project repository to store all configuration related files from which the package will read from, in Section 1. A template of registry.yaml has also been added to the /configs folder to manage the machine-specific addresses of your connected devices (e.g. serial port and camera index).

### registry.yaml ###
'012345678901234':              # insert your machine's 15-digit ID here
    cam_index:                  # camera index of the connected imaging devices
      __cam_01__: 1             # keep the leading and trailing double underscores
      __cam_02__: 0
    port:                       # addresses of serial ports
      __MyDevice__: COM1        # keep the leading and trailing double underscores
      __MyFirstDevice__: COM22

Use the Helper.get_node() function to get the 15-digit unique identifier of your machine
Use the Helper.get_port() function to get the serial port addresses of your connect devices

lab.Helper.get_node()           # Get your machine's ID (15-digit)
lab.Helper.get_ports()          # Get the port addresses of connected devices

Afterwards, change the value for the serial port address in the config.yaml file to match the registry.

### config.yaml ###
MyDevice:
  module: Move
  class: Cartesian.Ender
  settings:
    port: __MyDevice__          # serial port address

4. Using plugins

Optional: if drivers for your hardware components are already included, plugins may not be required

User-defined plugins can be easily written and integrated into Control.lab.ly. All available classes and functions can be found in lab.modules.

lab.guide_me()                              # Use guide to view imported objects
lab.modules.at.Make.Something.Good.MyClass  # Access the class 

4.1 Registering a Class or Function

You can import the class and register the object using the Factory.register() function.

from my_module import MyClass
lab.Factory.register(MyClass, "Make.Something.Good")

4.2 Registering a Python module

Alternatively, you can automatically register all Classes and Functions in a Python module just by importing it.

  1. Declare a __where__ global variable to indicate where to register the module
  2. At the end of the .py file, import and call the include_this_module() function
### my_module.py ###
__where__ = "Make.Something.Good"               # Where to register this module to

def MyClass:                                    # Main body of code goes here
  ...

def my_function:
  ...

from controllably import include_this_module    # Registers only the Classes and Functions
include_this_module()                           # defined above in this .py file

The Classes and Functions in the module will be registered when you import the module.

### main.py ###
from my_module import MyClass, my_function

Dependencies

  • Markdown (>=3.3.4)
  • numpy (>=1.19.5)
  • opencv-python (>=4.5.4.58)
  • pandas (>=1.2.4)
  • pyModbusTCP (>=0.2.0)
  • pyserial (>=3.5)
  • PySimpleGUI (>=4.60.5)
  • PyVISA (>=1.12.0)
  • PyVISA-py (>=0.7)
  • PyYAML (>=6.0)
  • tkhtmlview (>=0.2.0)

Contributors

@kylejeanlewis
@mat-fox
@Quijanove
@AniketChitre

How to Contribute

Issues and feature requests are welcome!

License

This project is distributed under the MIT License.


Change Log

Version 1.3.1

Feature enhancements, bug fixes and patches. First released 19 Feb 2024.

Added

  • implementation of TriContinent.pullback()
  • new Well properties and option in return list of wells by rows instead of columns

Changed

  • fix bugs in Peltier (setTemperature() and getTemperature())
  • fix bugs in Ender (setTemperature() and getTemperature())
  • fix bug in Keithley.setFunction()
  • generalise IV_Scan to take either currents or voltages

Version 1.3.0

Feature enhancements, bug fixes and patches. First released 19 Feb 2024.

Added

  • added check for poor physical connection with PiezoRoboticsDevice
  • Keithley
    • added new subclasses of KeithleyDevice: DAQ6510 and SMU2450
    • added way to read and save model name of KeithleyDevice
    • added new Keithley program for DAQ to scan multiple channels
    • new methods clearErrors(), setDisplay(), setFunction()

Changed

  • changed the way travel times are calculated for Mover tools, so that they reflect the actual physical travel times more accurately
  • changed ability to delay initialisation of TriContinent pumps until it is in a more convenient location
  • fixed few bugs with SentronProbe tool

Removed

  • removed old archived files

Version 1.2.0

Feature enhancements, bug fixes and patches. First released 22 Aug 2023.

Added

  • ForceClampSetup class
  • LoadCell class
  • Balance class (subclass of LoadCell)

Changed

  • update LEDArray to delay timing loop by 0.1s
  • fix bug with initialising PiezoRoboticsDevice
  • update getTemperature() across multiple classes to standardise output
  • Mover class
    • speed-related attributes and properties
    • add method to calculate travel time based on target speed, acceleration and deceleration
    • modify how speeds and max speeds interact with move() and safeMoveTo()
  • Cartesian class
    • setSpeed() and setSpeedFraction()
    • get max speed settings from device upon connecting
    • change calculation of movement wait times using device speed and acceleration
  • Primitiv class
    • change the class name to Grbl and Primitiv as a subclass name to retain compatibility
    • overload moveTo() and _query() methods to use jogging mode
    • modify the sequence of commands to halt movement
    • implement getAcceleration(), getCoordinates(), getMaxSpeed()
    • clear errors and setting feed rate upon establishing connection
  • Ender class
    • change the class name to Marlin and Ender as a subclass name to retain compatibility
    • added method to immediately stop movement
    • implement getAcceleration(), getCoordinates(), getMaxSpeed()
    • separate methods for setSpeed() (absolute speed in mm/s) and setSpeedFraction() (proportional speed to max speed)
  • Dobot class
    • added stop() method
  • Flir AX8 class
    • added invertPalette() method
    • added data parsing methods _decode_from_modbus() and _encode_to_modbus()
  • KeithleyDevice() class
    • added ip_address property
    • added options for _read() method
    • added readline() method
    • implement disconnect() method
  • fix bug with Keithley programs using device.run() instead of device.start()

Removed

  • Thermal class
  • removed dependency on imutils package

Versions 1.1.2 & 1.1.1

Bug fixes and patches. First released 12 Jul 2023.

Added

  • import Device classes in init files to view documentation
  • added library for GRBL status and error codes
  • add update_root_direcctory() function to Helper

Changed

  • fix bug with adding new rows into Dataframes
  • use reset_input_buffer() instead of flushInput() for pyserial.Serial objects
  • print the actual string sent to Serial devices
  • update methods in Deck, Labware, and Well to camelCase
  • update Deck.isExcluded() to apply strict inequalities when determining out-of-range coordinates
  • update LiquidMover to insert a portion of tip into rack before ejecting
  • update Spinner
    • fix bug with sending commands
    • added _query() method
    • pass verbosity to individual spinners
  • verbosity of Measure objects pass through to devices
  • update PiezoRoboticsDevice
    • initialize upon connection
    • raise errors when encountering them
  • update Mover
    • modifysetFlag() to print kwargs instead of raising error if assigned values are not boolean
    • use safe_height (if defined) instead of z-coordinate of home in safeMoveTo()
    • added getSettings() method
  • update Gantry class
    • read multiple flines in _query()
    • check that commands end with newline before sending to device
    • fix bug with changing speeds
  • update Ender
    • added getTemperature(), holdTemperature(), isAtTemperature() methods
    • modified setTemperature() to use Marlin code to wait for temperature
  • update Primitiv class
    • add getStatus() and stop() methods
    • add _get_settings() method
  • fix bug in M1Pro.setHandedness()
  • update Sartorius class
    • tip_inset_mm now an instance attribute with initialisation parameters
    • set tip_on flag to False when performing eject()

Version 1.1.0

Bug fixes and feature enhancements. First released 15 Jun 2023.

Added

  • ForceSensor - DIY force sensor (#55)
  • BioShake - orbital shaker from QInstruments (#56)
  • SentronProbe - pH meter probe from Sentron (#75)
  • Maker
    • added execute() abstract method and implemented in subclasses
  • GUI
    • Guide - documentation guide
    • MakerPanel - daptive GUI controls for Maker objects (#87)

Changed

  • M1Pro
    • fix issue with changing handedness (#86)
  • Peltier
    • rename getTemperatures() to getTemperature()
    • rename isReady() to isAtTemperature()
    • rename set_point to set_temperature
  • Ender
    • rename set_point to set_temperature
  • TriContinent
    • rename step_limit to limits
  • Refactor and reorganize GUI code
  • Refactor code in helper and factory
  • Updated documentation

Removed

  • Analyse sub-package removed
  • Control.Schedule sub-package removed
  • Unnecessary commented-out blocks of code

Version 1.0.1

Bug fixes and minor feature enhancements. First released 08 May 2023.

Added

  • LiquidMover
    • Added LiquidMover.touchTip() method to touch the pipette tip against the walls of the vessel to remove excess liquid on the outside of the tip (#62)
    • Added option to indicate the position of the first available pipette tip in LiquidMover (#61)
  • Added adaptive GUI controls for Liquid objects (#70)
  • Added option to indicate which digital IO channel to use with Dobot attachments (#53)

Changed

  • MassBalance
    • Updated to the use pd.concat() instead of pd.DataFrame.append(), which is going ot be deprecated (#63)
    • Fixed endless loop for when MassBalance tries to zero() while recording data (#60)
  • Changed the Image class and methods into functions within a module (#54)
  • Fixed the tool offset of pipette when pipette tip is attached, and accounts for the length of pipette that enters the pipette tip (#64)
  • Changed to using more precise time interval measurements by moving from time.time() to time.perf_counter() (#68)
  • Fixed discrepancy in aspirate and dispense speed for Sartorius (#73) and let speed return to a global default value (#72)
  • Updated documentation

Version 1.0.0

Major overhaul in package structure. Standardisation of methods and consolidation of common methods. First released 12 Apr 2023.

Added

  • Usage of Abstract Base Classes (ABCs) to define a base class with abstract methods that needs to be implemented through sub-classing (#31)
  • Usage of Protocols to provide an interface between different classes of objects (#30)
  • Usage of Dataclasses to store complex data
  • Usage of decorators to modify methods
  • Introduced different functions to parse the program docstring and find program parameters

Changed

  • Standardised methods and consolidated common methods
  • Added type hints (#28)
  • Moved Dobot attachments from Mover to Transfer.Substrate
  • Split GUI Panels into individual files
  • Split Dobot arms into individual files
  • Split functions/methods in misc.py into individual files.
  • Changed _flags to a public attribute flags
  • Update documentation (#27, #28, #29)

Removed

  • Unnecessary commented-out blocks of code

Version 0.0.x

(0.0.4.x) Introduced control for Peltier device and TriContinent Series C syringe pumps. First released 10 Mar 2023.
(0.0.3.x) Minor changes to movement robot safety and pipette control. Introduced control for LED array. First released 08 Mar 2023.
(0.0.2.x) Updates in setting up configuration files. First released 24 Feb 2023.
(0.0.1.x) First release of Control.lab.ly distributed on 23 Feb 2023.
(0.0.0.x) Pre-release packaging checks

Added

0.0.4

  • Added control for Peltier (#23)
    • set and get temperatures
    • hold temperatures for desired duration
    • checks if target temperature has been reached by checking power level lower than a threshold or time passed over a predefined duration, once the temperature is within tolerance
    • ability to record temperatures and timestamps
  • Added control for TriContinent and TriContinentEnsemble (#25)
    • single actions such as empty, fill, initialise, move actions, set speeds and valves, and wait
    • compound actions such as aspirate, dispense, and prime

0.0.3

  • Added safety measures for movement actions (#24)
    • In Deck, added exclusion zones when reading the layout.json file and new method is_excluded() to check if target coordinate is within the exclusion zone
    • In Mover, update isFeasible() method to check if target coordinates violates the deck's exclusion zone
    • New function set_safety() defines safety modes when starting a new session to pause for input (in "high" safety setting) and to wait for safety countdown (in "low" safety setting)
  • Make.Light.LEDArray for controlling LEDs in the photo-reactor, as well as timing the LED "on" durations (#35)

0.0.2.2

  • Added import of CompoundSetup class

0.0.2

  • Deck.at() method for directly referencing slots using either index numbers or names
  • New CompoundSetup class for common methods of Compound devices
  • New load_deck() function to load Deck after initialisation

0.0.1

  • Make
    • Multi-channel spin-coater [Arduino]
  • Measure
    • (Keithley) 2450 Source Measure Unit (SMU) Instrument
    • (PiezoRobotics) Dynamic Mechanical Analyser (DMA)
    • Precision mass balance [Arduino]
  • Move
    • (Creality) Ender-3
    • (Dobot) M1 Pro
    • (Dobot) MG400
    • Primitiv [Arduino]
  • Transfer
    • (Sartorius) rLINE® dispensing modules
    • Peristaltic pump and syringe system [Arduino]
  • View
    • (FLIR) AX8 thermal imaging camera - full functionality in development
    • Web cameras [General]
  • misc
    • Helper class for most common actions
    • create_configs: make new directory for configuration files
    • create_setup: make new directory for specific setup-related files
    • load_setup: initialise setup on import during runtime

Changed

0.0.4

  • Update documentation

0.0.3.1

  • Update documentation

0.0.3

  • Sartorius
    • made the blowout/home optional for the dispense method upon emptying the pipette
  • Update documentation

0.0.2.1

  • Changed template files for lab.create_setup()

0.0.2

  • Update documentation

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

control-lab-ly-1.3.1.tar.gz (530.6 kB view hashes)

Uploaded Source

Built Distribution

control_lab_ly-1.3.1-py3-none-any.whl (380.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page