Skip to content

Commit e95a8cd

Browse files
authored
Merge branch 'main' into json-merge
2 parents 5437eb9 + 2f2e467 commit e95a8cd

98 files changed

Lines changed: 10594 additions & 1058 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
repos:
22
- repo: local
33
hooks:
4-
- id: make-format
5-
name: Run format
6-
entry: make format
4+
- id: ruff-format
5+
name: ruff format
6+
entry: ruff format
77
language: system
8+
types: [python]
9+
files: ^pylabrobot/.*$
10+
11+
- id: ruff-check
12+
name: ruff check
13+
entry: ruff check --fix
14+
language: system
15+
types: [python]
816
files: ^pylabrobot/.*$
917

1018
- repo: https://github.com/crate-ci/typos

Makefile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,18 @@ clean-docs:
1919
rm -rf docs/jupyter_execute
2020
rm -rf docs/user_guide/jupyter_execute
2121

22+
TRACKED_PY = $(shell git ls-files 'pylabrobot/*.py' 'pylabrobot/*.ipynb')
23+
2224
lint:
23-
$(BIN)python -m ruff check pylabrobot
25+
$(BIN)python -m ruff check $(TRACKED_PY)
2426

2527
format:
26-
$(BIN)python -m ruff format pylabrobot
27-
$(BIN)python -m ruff check --fix pylabrobot --select I
28+
$(BIN)python -m ruff format $(TRACKED_PY)
29+
$(BIN)python -m ruff check --fix $(TRACKED_PY) --select I
2830

2931
format-check:
30-
$(BIN)python -m ruff format --check pylabrobot
31-
$(BIN)python -m ruff check pylabrobot --select I
32+
$(BIN)python -m ruff format --check $(TRACKED_PY)
33+
$(BIN)python -m ruff check $(TRACKED_PY) --select I
3234

3335
test:
3436
$(BIN)python -m pytest -s -v

docs/user_guide/00_liquid-handling/_liquid-handling.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Examples:
1919
hamilton-prep/_hamilton-prep
2020
opentrons-ot2/hello-world
2121
tecan-evo/_tecan-evo
22+
plate-washing/plate-washing
2223
pumps/_pumps
2324
moving-channels-around
2425
tutorial_tip_inventory_consolidation
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# BioTek EL406\n",
8+
"\n",
9+
"The BioTek EL406 plate washer is controlled by the {class}`~pylabrobot.plate_washing.biotek.el406.BioTekEL406Backend` class. It communicates via FTDI USB serial.\n",
10+
"\n",
11+
"The EL406 has three fluid delivery subsystems — manifold, syringe pump, and peristaltic pump — plus an integrated plate shaker. All operations require a {class}`~pylabrobot.resources.Plate` object to configure plate-specific parameters automatically."
12+
]
13+
},
14+
{
15+
"cell_type": "markdown",
16+
"metadata": {},
17+
"source": [
18+
"## Setup"
19+
]
20+
},
21+
{
22+
"cell_type": "code",
23+
"execution_count": null,
24+
"metadata": {},
25+
"outputs": [],
26+
"source": [
27+
"from pylabrobot.plate_washing.biotek.el406 import ExperimentalBioTekEL406Backend\n",
28+
"from pylabrobot.resources import Cor_96_wellplate_360ul_Fb\n",
29+
"\n",
30+
"backend = ExperimentalBioTekEL406Backend() # ExperimentalBioTekEL406Backend(device_id=\"YOUR_FTDI_ID_HERE\")\n",
31+
"await backend.setup()\n",
32+
"\n",
33+
"plate = Cor_96_wellplate_360ul_Fb(name=\"plate\")"
34+
]
35+
},
36+
{
37+
"cell_type": "markdown",
38+
"metadata": {},
39+
"source": [
40+
"## Manifold\n",
41+
"\n",
42+
"The wash manifold is the primary fluid system. Prime the lines before use to fill tubing with buffer."
43+
]
44+
},
45+
{
46+
"cell_type": "code",
47+
"execution_count": null,
48+
"metadata": {},
49+
"outputs": [],
50+
"source": [
51+
"await backend.manifold_prime(plate, volume=10000, buffer=\"A\") # 10 mL"
52+
]
53+
},
54+
{
55+
"cell_type": "markdown",
56+
"metadata": {},
57+
"source": [
58+
"Dispense and aspirate individually, or use `manifold_wash` for repeated dispense-aspirate cycles."
59+
]
60+
},
61+
{
62+
"cell_type": "code",
63+
"execution_count": null,
64+
"metadata": {},
65+
"outputs": [],
66+
"source": [
67+
"await backend.manifold_dispense(plate, volume=200, buffer=\"A\")"
68+
]
69+
},
70+
{
71+
"cell_type": "code",
72+
"execution_count": null,
73+
"metadata": {},
74+
"outputs": [],
75+
"source": [
76+
"await backend.manifold_aspirate(plate)"
77+
]
78+
},
79+
{
80+
"cell_type": "code",
81+
"execution_count": null,
82+
"metadata": {},
83+
"outputs": [],
84+
"source": [
85+
"await backend.manifold_wash(plate, cycles=3, buffer=\"A\")"
86+
]
87+
},
88+
{
89+
"cell_type": "markdown",
90+
"metadata": {},
91+
"source": [
92+
"`manifold_wash` supports many options: shake/soak between cycles, secondary aspirate, bottom wash, and per-cycle pre-dispense."
93+
]
94+
},
95+
{
96+
"cell_type": "code",
97+
"execution_count": null,
98+
"metadata": {},
99+
"outputs": [],
100+
"source": [
101+
"await backend.manifold_wash(\n",
102+
" plate,\n",
103+
" cycles=3,\n",
104+
" buffer=\"A\",\n",
105+
" dispense_volume=300,\n",
106+
" soak_duration=10,\n",
107+
" shake_duration=5,\n",
108+
" shake_intensity=\"Medium\",\n",
109+
")"
110+
]
111+
},
112+
{
113+
"cell_type": "markdown",
114+
"metadata": {},
115+
"source": [
116+
"Run an auto-clean cycle to flush the manifold lines."
117+
]
118+
},
119+
{
120+
"cell_type": "code",
121+
"execution_count": null,
122+
"metadata": {},
123+
"outputs": [],
124+
"source": [
125+
"await backend.manifold_auto_clean(plate, buffer=\"A\", duration=60)"
126+
]
127+
},
128+
{
129+
"cell_type": "markdown",
130+
"metadata": {},
131+
"source": [
132+
"## Syringe pump\n",
133+
"\n",
134+
"The syringe pump provides precise low-volume dispensing."
135+
]
136+
},
137+
{
138+
"cell_type": "code",
139+
"execution_count": null,
140+
"metadata": {},
141+
"outputs": [],
142+
"source": [
143+
"await backend.syringe_prime(plate, syringe=\"A\", volume=5000, flow_rate=5, refills=2)"
144+
]
145+
},
146+
{
147+
"cell_type": "code",
148+
"execution_count": null,
149+
"metadata": {},
150+
"outputs": [],
151+
"source": [
152+
"await backend.syringe_dispense(plate, volume=50, syringe=\"A\")"
153+
]
154+
},
155+
{
156+
"cell_type": "markdown",
157+
"metadata": {},
158+
"source": [
159+
"## Peristaltic pump"
160+
]
161+
},
162+
{
163+
"cell_type": "code",
164+
"execution_count": null,
165+
"metadata": {},
166+
"outputs": [],
167+
"source": [
168+
"await backend.peristaltic_prime(plate, volume=300, flow_rate=\"High\")"
169+
]
170+
},
171+
{
172+
"cell_type": "code",
173+
"execution_count": null,
174+
"metadata": {},
175+
"outputs": [],
176+
"source": [
177+
"await backend.peristaltic_dispense(plate, volume=100, flow_rate=\"High\")"
178+
]
179+
},
180+
{
181+
"cell_type": "code",
182+
"execution_count": null,
183+
"metadata": {},
184+
"outputs": [],
185+
"source": [
186+
"await backend.peristaltic_purge(plate, volume=300, flow_rate=\"High\")"
187+
]
188+
},
189+
{
190+
"cell_type": "markdown",
191+
"metadata": {},
192+
"source": [
193+
"## Shaking"
194+
]
195+
},
196+
{
197+
"cell_type": "code",
198+
"execution_count": null,
199+
"metadata": {},
200+
"outputs": [],
201+
"source": [
202+
"await backend.shake(plate, duration=30, intensity=\"Medium\")"
203+
]
204+
},
205+
{
206+
"cell_type": "markdown",
207+
"metadata": {},
208+
"source": [
209+
"## Batching\n",
210+
"\n",
211+
"Each step command automatically starts and cleans up a batch. To group multiple steps into a single batch (avoiding repeated start/cleanup overhead), use the `batch()` context manager."
212+
]
213+
},
214+
{
215+
"cell_type": "code",
216+
"execution_count": null,
217+
"metadata": {},
218+
"outputs": [],
219+
"source": [
220+
"async with backend.batch(plate):\n",
221+
" await backend.manifold_prime(plate, volume=10000, buffer=\"A\")\n",
222+
" await backend.manifold_wash(plate, cycles=3, buffer=\"A\")\n",
223+
" await backend.manifold_aspirate(plate)"
224+
]
225+
},
226+
{
227+
"cell_type": "markdown",
228+
"metadata": {},
229+
"source": [
230+
"## Queries"
231+
]
232+
},
233+
{
234+
"cell_type": "code",
235+
"execution_count": null,
236+
"metadata": {},
237+
"outputs": [],
238+
"source": [
239+
"await backend.request_serial_number()"
240+
]
241+
},
242+
{
243+
"cell_type": "code",
244+
"execution_count": null,
245+
"metadata": {},
246+
"outputs": [],
247+
"source": [
248+
"await backend.request_washer_manifold()"
249+
]
250+
},
251+
{
252+
"cell_type": "code",
253+
"execution_count": null,
254+
"metadata": {},
255+
"outputs": [],
256+
"source": [
257+
"await backend.request_instrument_settings()"
258+
]
259+
},
260+
{
261+
"cell_type": "markdown",
262+
"metadata": {},
263+
"source": [
264+
"## Teardown"
265+
]
266+
},
267+
{
268+
"cell_type": "code",
269+
"execution_count": null,
270+
"metadata": {},
271+
"outputs": [],
272+
"source": [
273+
"await backend.stop()"
274+
]
275+
}
276+
],
277+
"metadata": {
278+
"kernelspec": {
279+
"display_name": "env",
280+
"language": "python",
281+
"name": "python3"
282+
},
283+
"language_info": {
284+
"codemirror_mode": {
285+
"name": "ipython",
286+
"version": 3
287+
},
288+
"file_extension": ".py",
289+
"mimetype": "text/x-python",
290+
"name": "python",
291+
"nbconvert_exporter": "python",
292+
"pygments_lexer": "ipython3",
293+
"version": "3.9.25"
294+
}
295+
},
296+
"nbformat": 4,
297+
"nbformat_minor": 4
298+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Plate Washing
2+
3+
Plate washers automate the process of dispensing and aspirating wash buffers from microplates. They are commonly used in ELISA, cell-based assays, and other workflows that require repeated wash cycles.
4+
5+
## Supported Plate Washers
6+
7+
- BioTek EL406
8+
9+
```{toctree}
10+
:maxdepth: 1
11+
:hidden:
12+
13+
BioTek EL406 <biotek-el406>
14+
```

docs/user_guide/machines.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ tr > td:nth-child(5) { width: 15%; }
5959
| Cole Parmer | Masterflex L/S 07522-20 07522-30 07551-20 07551-30 07575-30 07575-40 | Full | [PLR](https://docs.pylabrobot.org/user_guide/00_liquid-handling/pumps/cole-parmer-masterflex.html) / [OEM](https://www.masterflex.nl/assets/uploads/2017/09/07551-xx.pdf) |
6060
| Agrowtek | Pump Array | Full | [OEM](https://www.agrowtek.com/index.php/products/dosing_systems/dosing-pumps/agrowdose-adi-digital-persitaltic-dosing-pumps-detail) |
6161

62+
### Plate Washers
63+
64+
| Manufacturer | Machine | PLR-Support | Links |
65+
|--------------|---------|-------------|--------|
66+
| Agilent (BioTek) | EL406 | Mostly | [PLR](https://docs.pylabrobot.org/user_guide/00_liquid-handling/plate-washing/biotek-el406.html) / [OEM](https://www.agilent.com/en/product/microplate-instrumentation/microplate-washers-dispensers/biotek-el406-washer-dispenser-795212) |
67+
6268
---
6369

6470
## Material Handling

0 commit comments

Comments
 (0)