Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ runs:
|| sudo apt-get install -y libgirepository1.0-dev
fi
shell: bash
- name: Install BTX OCR system dependency
if: ${{ inputs.extra == 'btx' || inputs.extra == 'all' }}
run: sudo apt-get install -y tesseract-ocr
shell: bash
- name: Set up Python
uses: actions/setup-python@v6
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
extra: ["<none>", serial, usb, ftdi, hid, modbus, opentrons, sila, cytation-microscopy, pico]
extra: ["<none>", serial, usb, ftdi, hid, btx, modbus, opentrons, sila, cytation-microscopy, pico]

name: Tests (${{ matrix.extra }}, py3.12)
runs-on: ${{ matrix.os }}
Expand Down
6 changes: 6 additions & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ LOK = "LOK"
ouput = "ouput"
hegiht = "hegiht"

# BTX Gemini X2 firmware/OCR vocabulary. `scap` is the RSI framebuffer
# command; the misspelled protocol variants are OCR corrections.
scap = "scap"
protocals = "protocals"
protocal = "protocal"

[files]
extend-exclude = [
"*.ipynb"
Expand Down
2 changes: 2 additions & 0 deletions docs/_exts/plr_devices/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class DeviceRegistryError(ValueError):
"centrifuge loader",
"decapper",
"delidder",
"electroporator",
"fan",
"flow cytometer",
"heater shaker",
Expand Down Expand Up @@ -51,6 +52,7 @@ class DeviceRegistryError(ValueError):
"decapping",
"delidding",
"dispensing",
"electroporation",
"flow cytometry",
"fluorescence",
"fluorescence polarization",
Expand Down
15 changes: 15 additions & 0 deletions docs/_static/devices.json
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,21 @@
"manager": "https://discuss.pylabrobot.org/u/rickwierenga",
"oem": "https://www.preciseflexrobots.com/lab-automation-applicable-products#preciseflex400_labproducts"
},
{
"id": "btx-gemini-x2",
"vendor": "BTX",
"name": "Gemini X2",
"kind": "electroporator",
"capabilities": [
"electroporation"
],
"status": "mostly",
"api": "pylabrobot.thermo_fisher.btx.gemini.X2.BTXGeminiX2",
"api_version": "v1",
"code_slug": "thermo_fisher/btx/gemini/X2",
"doc_slug": "thermo_fisher/btx/gemini/X2/hello-world",
"oem": "https://support.btxonline.com/hc/en-us/articles/6215664757907-Gemini-Twin-Wave-Electroporators-Manual-and-Quick-Start-guide"
},
{
"id": "byonoy-absorbance-96",
"vendor": "Byonoy",
Expand Down
37 changes: 37 additions & 0 deletions docs/api/pylabrobot.thermo_fisher.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,40 @@ ALPS Heat Sealers
ThermoScientificALPS5000
ALPS5000Status

BTX Gemini X2
-------------

.. currentmodule:: pylabrobot.thermo_fisher.btx.gemini.X2.gemini_x2

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

BTXGeminiX2

.. currentmodule:: pylabrobot.thermo_fisher.btx.gemini.X2.standard

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

ElectroporationProtocol
ElectroporationPreparationDetails
ElectroporationExecutionDetails
ElectroporationCancellationDetails
ElectroporationLogCapture
ElectroporationCleanup
PreparedElectroporationRun
ElectroporationRunResult
ElectroporationCancellationResult

.. currentmodule:: pylabrobot.thermo_fisher.btx.gemini.X2.ht200

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

BTXHT200
224 changes: 224 additions & 0 deletions docs/user_guide/thermo_fisher/btx/gemini/X2/hello-world.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "intro",
"metadata": {},
"source": [
"# BTX Gemini X2\n",
"\n",
"The BTX Gemini X2 is a twin-waveform electroporator. PyLabRobot controls it through a USB serial connection: protocol and log transfer use the Gemini file-transfer interface, while run-screen actions use GhostTouch on the instrument touchscreen.\n",
"\n",
"See the [installation instructions](installation.md) before connecting the instrument.\n",
"\n",
"Protocol and run-result models are provided by the Gemini X2 package."
]
},
{
"cell_type": "markdown",
"id": "device-card",
"metadata": {},
"source": [
"```{device-card} btx-gemini-x2\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "setup-md",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"Identify the serial port on your control computer and create the device. On macOS this is often `/dev/cu.usbmodem...`; on Windows it is usually a `COM` port."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "setup-code",
"metadata": {},
"outputs": [],
"source": [
"from pylabrobot.thermo_fisher.btx.gemini.X2 import BTXGeminiX2\n",
"\n",
"gemini = BTXGeminiX2(port=\"/dev/cu.usbmodemXXXX\")\n",
"await gemini.setup()"
]
},
{
"cell_type": "markdown",
"id": "info-md",
"metadata": {},
"source": [
"## Device information\n",
"\n",
"The Gemini X2 exposes device identity, the temporary protocol prefix, and plate-handler support."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "info-code",
"metadata": {},
"outputs": [],
"source": [
"info = await gemini.request_device_info()\n",
"print(info[\"model\"], info[\"version\"], info[\"serial_number\"])"
]
},
{
"cell_type": "markdown",
"id": "protocol-md",
"metadata": {},
"source": [
"## Define a protocol\n",
"\n",
"Square-wave protocols use `duration_us`; exponential-decay protocols use `resistance_ohms` and `capacitance_uf`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "protocol-code",
"metadata": {},
"outputs": [],
"source": [
"from pylabrobot.thermo_fisher.btx.gemini.X2 import ElectroporationProtocol\n",
"\n",
"protocol = ElectroporationProtocol(\n",
" protocol_type=\"square\",\n",
" pulse_amplitude_volts=250,\n",
" gap_mm=2.0,\n",
" pulse_count=1,\n",
" pulse_interval_seconds=0.0,\n",
" duration_us=1000,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "prepare-md",
"metadata": {},
"source": [
"## Prepare a temporary run\n",
"\n",
"`prepare_temporary_protocol` writes a temporary `!PLR_...` user protocol, opens it on the Gemini touchscreen, sets plate-handler columns when requested, and leaves the device on the run screen.\n",
"\n",
"When using the HT-200 plate handler, first record its configured pulse count and column adjustment. `plate_columns` also requires an explicit `plate_handler_reset_state`. Use `reset_confirmed` only after manually returning the handler to column 1; use `continue_current_position` only when intentionally continuing from the current handler position."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "prepare-code",
"metadata": {},
"outputs": [],
"source": [
"gemini.plate_handler.configure_manual_state(pulse_count=1, column_adjust=0)\n",
"\n",
"prepared = await gemini.prepare_temporary_protocol(\n",
" protocol=protocol,\n",
" plate_columns=3,\n",
" plate_handler_reset_state=\"reset_confirmed\",\n",
")\n",
"prepared.protocol_name"
]
},
{
"cell_type": "markdown",
"id": "serialize-md",
"metadata": {},
"source": [
"The prepared run can be serialized and passed to a later process. The serialized payload includes the temporary protocol name and baseline log listing used to match the new BTXDATA log after GO."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "serialize-code",
"metadata": {},
"outputs": [],
"source": [
"prepared_payload = prepared.as_dict()\n",
"prepared_payload[\"protocol_name\"]"
]
},
{
"cell_type": "markdown",
"id": "start-md",
"metadata": {},
"source": [
"## Start the prepared run\n",
"\n",
"The next cell presses GO on the prepared run screen and delivers the configured pulse. Confirm that the plate, electrodes, samples, safety cover, and plate-handler state are correct before running it."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "start-code",
"metadata": {},
"outputs": [],
"source": [
"result = await gemini.start_prepared_run(\n",
" prepared_run=prepared,\n",
" home_after=True,\n",
")\n",
"result.log_capture.summary"
]
},
{
"cell_type": "markdown",
"id": "cancel-md",
"metadata": {},
"source": [
"## Cancel before pulse delivery\n",
"\n",
"If a run has been prepared but should not be started, cancel it. This returns the Gemini to a safe screen and deletes the temporary protocol."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cancel-code",
"metadata": {},
"outputs": [],
"source": [
"# cancelled = await gemini.cancel_prepared_run(prepared)\n",
"# cancelled.cleanup.deleted"
]
},
{
"cell_type": "markdown",
"id": "teardown-md",
"metadata": {},
"source": [
"## Teardown"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "teardown-code",
"metadata": {},
"outputs": [],
"source": [
"await gemini.stop()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
12 changes: 12 additions & 0 deletions docs/user_guide/thermo_fisher/btx/gemini/X2/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# BTX Gemini X2 installation

BTX Gemini X2 support uses serial communication and touchscreen screenshot OCR. First, install the
Python dependencies:

```bash
pip install "pylabrobot[btx]"
```

Touchscreen control also requires the external
[Tesseract OCR](https://github.com/tesseract-ocr/tesseract) executable. Install Tesseract for your
operating system and make sure the `tesseract` command is available on `PATH`.
8 changes: 8 additions & 0 deletions docs/user_guide/thermo_fisher/btx/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# BTX

```{toctree}
:maxdepth: 1

gemini/X2/installation
gemini/X2/hello-world
```
1 change: 1 addition & 0 deletions docs/user_guide/thermo_fisher/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
:maxdepth: 1

alps/index
btx/index
```
Loading
Loading