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
- Compound
- Control
- Make
- Measure
- Move
- Transfer
- 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
- (Keithley)
- 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()
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. 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
, thename
of the Labware and thefilepath
to the JSON file containing Labware details are defined. The filepath starts with the name of the repository's base folder.
Theexclusion_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. UsesafeMoveTo()
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 inlayout.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 theHelper.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.
- Declare a
__where__
global variable to indicate where to register the module- 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.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
andSMU2450
- 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()
- added new subclasses of
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
classLoadCell
classBalance
class (subclass ofLoadCell
)
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()
andsafeMoveTo()
Cartesian
classsetSpeed()
andsetSpeedFraction()
- 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
andPrimitiv
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
- change the class name to
Ender
class- change the class name to
Marlin
andEnder
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) andsetSpeedFraction()
(proportional speed to max speed)
- change the class name to
Dobot
class- added
stop()
method
- added
- Flir
AX8
class- added
invertPalette()
method - added data parsing methods
_decode_from_modbus()
and_encode_to_modbus()
- added
KeithleyDevice()
class- added
ip_address
property - added options for
_read()
method - added
readline()
method - implement
disconnect()
method
- added
- fix bug with Keithley programs using
device.run()
instead ofdevice.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 offlushInput()
forpyserial.Serial
objects - print the actual string sent to Serial devices
- update methods in
Deck
,Labware
, andWell
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
- modify
setFlag()
to print kwargs instead of raising error if assigned values are not boolean - use
safe_height
(if defined) instead of z-coordinate of home insafeMoveTo()
- added
getSettings()
method
- modify
- update
Gantry
class- read multiple flines in
_query()
- check that commands end with newline before sending to device
- fix bug with changing speeds
- read multiple flines in
- update
Ender
- added
getTemperature()
,holdTemperature()
,isAtTemperature()
methods - modified
setTemperature()
to use Marlin code to wait for temperature
- added
- update
Primitiv
class- add
getStatus()
andstop()
methods - add
_get_settings()
method
- add
- fix bug in
M1Pro.setHandedness()
- update
Sartorius
classtip_inset_mm
now an instance attribute with initialisation parameters- set
tip_on
flag to False when performingeject()
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
- added
- GUI
Guide
- documentation guideMakerPanel
- daptive GUI controls forMaker
objects (#87)
Changed
M1Pro
- fix issue with changing handedness (#86)
Peltier
- rename
getTemperatures()
togetTemperature()
- rename
isReady()
toisAtTemperature()
- rename
set_point
toset_temperature
- rename
Ender
- rename
set_point
toset_temperature
- rename
TriContinent
- rename
step_limit
tolimits
- rename
- Refactor and reorganize
GUI
code - Refactor code in
helper
andfactory
- Updated documentation
Removed
Analyse
sub-package removedControl.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
- 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 ofpd.DataFrame.append()
, which is going ot be deprecated (#63) - Fixed endless loop for when
MassBalance
tries tozero()
while recording data (#60)
- Updated to the use
- 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()
totime.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 attributeflags
- 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
andTriContinentEnsemble
(#25)- single actions such as
empty
,fill
,initialise
, move actions, set speeds and valves, and wait - compound actions such as
aspirate
,dispense
, andprime
- single actions such as
0.0.3
- Added safety measures for movement actions (#24)
- In
Deck
, added exclusion zones when reading thelayout.json
file and new methodis_excluded()
to check if target coordinate is within the exclusion zone - In
Mover
, updateisFeasible()
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)
- In
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 ofCompound
devices - New
load_deck()
function to loadDeck
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for control_lab_ly-1.3.0b0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7d31f888c57fbcb5fe1cd5b7c3c4c1b0de57b9de635e05a4442e13fd24d4c4ef |
|
MD5 | 4184b96cab13a9c95a6c504df7de0309 |
|
BLAKE2b-256 | b3f9bfc51936dcbd3fa679e6c49e373cdee8c0a72160e61685d000944d662a17 |