Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
d01b19b
Add v1b1 Micronic rack reading support
alexjamesgodfrey Apr 3, 2026
cf37a31
Add rack ID scan helper to rack reader capability
alexjamesgodfrey Apr 24, 2026
c049a5f
Test rack ID scan helper
alexjamesgodfrey Apr 24, 2026
a92a4bf
Test Micronic rack ID only scan surface
alexjamesgodfrey Apr 24, 2026
cad63e4
Document Micronic rack reading capability usage
alexjamesgodfrey Apr 24, 2026
061f5b9
Document rack ID only scan capability
alexjamesgodfrey Apr 24, 2026
96e56f6
Update Micronic driver setup for current v1b1
alexjamesgodfrey Apr 24, 2026
c1864a3
Split Micronic single-tube scanning into barcode_scanning
alexjamesgodfrey Apr 24, 2026
4ece53d
Adapt Micronic split to updated PR branch
alexjamesgodfrey Apr 24, 2026
915bba5
Expose Micronic rack barcode-only scans
alexjamesgodfrey Apr 24, 2026
ca7fd33
fix: consistency with micronic io and v1b1
alexjamesgodfrey Apr 24, 2026
c2d8e0d
Refactor rack_id scan into one-shot backend method
alexjamesgodfrey Apr 24, 2026
9124d9d
Format Micronic+rack-reading files and tighten driver type
alexjamesgodfrey Apr 24, 2026
18b7f92
Add direct Micronic rack reader driver
alexjamesgodfrey May 6, 2026
4815cd9
Remove bundled Micronic scanner helper
alexjamesgodfrey May 6, 2026
a6c65e2
Tighten Micronic direct driver review findings
alexjamesgodfrey May 6, 2026
de86626
Guard Micronic direct scan state transitions
alexjamesgodfrey May 6, 2026
7e0e82a
Simplify Micronic direct rack reader
alexjamesgodfrey May 6, 2026
e5cbad5
Use PLR Serial for Micronic rack IDs
alexjamesgodfrey May 7, 2026
0297703
Collapse Micronic driver abstraction
alexjamesgodfrey May 7, 2026
30416f8
Simplify Micronic public names
alexjamesgodfrey May 7, 2026
4aa0e21
Update rack-reading docs for Micronic names
alexjamesgodfrey May 7, 2026
cd78625
Remove Alakascan defaults from Micronic docs
alexjamesgodfrey May 7, 2026
69cb080
Mock rack-reader backend in tests instead of hand-rolled fakes
rickwierenga May 15, 2026
c8f90f6
Make rack-reader state Micronic-specific
rickwierenga May 15, 2026
acc7d9e
Drop min_wells from Micronic driver in favor of rack.num_items
rickwierenga May 15, 2026
b4b5fd3
Collapse rack-reading error hierarchy to MicronicError
rickwierenga May 15, 2026
4ceb0a8
Drop date and time fields from RackScanResult
rickwierenga May 15, 2026
73ed69e
Type RackScanEntry.status as Literal["OK", "NOREAD"]
rickwierenga May 15, 2026
b11bf2f
Log decode method instead of carrying it on RackScanEntry
rickwierenga May 15, 2026
a9cdcae
Use PLR position convention (A1) instead of zero-padded A01
rickwierenga May 15, 2026
cdaa760
Drop MicronicDriver.get_rack_id
rickwierenga May 15, 2026
48ff245
Convert rack-reading docs from md to notebook
rickwierenga May 15, 2026
f8f3b42
Use chatterbox in rack-reading docs and convert Micronic index to not…
rickwierenga May 15, 2026
481a541
Refactor Micronic scanner acquisition into Scanner classes
rickwierenga May 15, 2026
6dedfe4
Remove rack_id_command subprocess escape hatch from Micronic driver
rickwierenga May 15, 2026
b73cab3
Remove rack_id_override from Micronic driver
rickwierenga May 15, 2026
96f9853
MicronicDriver owns Serial and reads the rack ID on the main loop
rickwierenga May 15, 2026
75b0b49
Drop stale scan_command and image_input references from Micronic doc
rickwierenga May 15, 2026
8f3cba3
Revert "Drop stale scan_command and image_input references from Micro…
rickwierenga May 15, 2026
b52b41a
Remove the Micronic state machine
rickwierenga May 15, 2026
f14c909
Move Micronic notebook under code_reader/ and use index.md vendor pat…
rickwierenga May 15, 2026
4c8bc87
Reject concurrent Micronic rack scans
rickwierenga May 15, 2026
b136f04
Drop unused default_timeout / default_poll_interval on MicronicCodeRe…
rickwierenga May 15, 2026
1368134
Address Micronic rack reader review notes
alexjamesgodfrey May 20, 2026
94e2f94
Align Micronic reader with v1b1 layering
alexjamesgodfrey May 20, 2026
8ac1882
Keep Micronic scan locked through timeout cleanup
alexjamesgodfrey May 21, 2026
c837ef4
remove unused files
rickwierenga Aug 17, 2026
c2e0a4b
Clean up Micronic rack reader diff
rickwierenga Aug 17, 2026
85b998c
Refactor Micronic code reader integration
rickwierenga Aug 17, 2026
2058fe4
Merge remote-tracking branch 'origin/main' into micronic-rack-reader-…
rickwierenga Aug 18, 2026
d681e61
Refine Micronic code reader integration
rickwierenga Aug 18, 2026
0ed217c
Allow partial Micronic rack scans
rickwierenga Aug 18, 2026
9324177
Document Micronic RD235 model
rickwierenga Aug 19, 2026
c9ad73f
Fix timeout tests on Python 3.9
rickwierenga Aug 19, 2026
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
15 changes: 15 additions & 0 deletions docs/_static/devices.json
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,21 @@
"manager": "https://discuss.pylabrobot.org/u/rickwierenga",
"oem": "https://www.mt.com/us/en/home/products/Industrial_Weighing_Solutions/high-precision-weigh-sensors/weigh-module-wxs205sdu-15-11121008.html"
},
{
"id": "micronic-code-reader",
"vendor": "Micronic",
"name": "RD235",
"kind": "barcode scanner",
"capabilities": [
"barcode reading"
],
"status": "full",
"api": "pylabrobot.micronic.MicronicRD235",
"api_version": "v1",
"code_slug": "micronic/code_reader",
"doc_slug": "micronic/code_reader/hello-world",
"oem": "https://www.micronic.com/products/code-reader/"
},
{
"id": "molecular-devices-imagexpress-micro",
"vendor": "Molecular Devices",
Expand Down
44 changes: 44 additions & 0 deletions docs/api/pylabrobot.micronic.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.. currentmodule:: pylabrobot.micronic

pylabrobot.micronic package
===========================

Direct Micronic RD235 code-reader integration.

Code reader
-----------

.. currentmodule:: pylabrobot.micronic.code_reader.driver

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

MicronicRD235
RackScanEntry
RackScanResult

.. currentmodule:: pylabrobot.micronic.code_reader.errors

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

MicronicError


Scanners
--------

.. currentmodule:: pylabrobot.micronic.code_reader.scanner

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

Scanner
TwainScanner
SaneScanner
1 change: 1 addition & 0 deletions docs/api/pylabrobot.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Manufacturers
pylabrobot.kbioscience
pylabrobot.kbiosystems
pylabrobot.mettler_toledo
pylabrobot.micronic
pylabrobot.molecular_devices
pylabrobot.qinstruments
pylabrobot.sartorius
Expand Down
1 change: 1 addition & 0 deletions docs/user_guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ inheco/index
kbioscience/index
kbiosystems/index
mettler_toledo/index
micronic/index
molecular_devices/index
qinstruments/index
sartorius/index
Expand Down
221 changes: 221 additions & 0 deletions docs/user_guide/micronic/code_reader/hello-world.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "micronic-intro",
"metadata": {},
"source": [
"# Micronic RD235 Code Reader\n",
"\n",
"The Micronic RD235 Code Reader scans the side barcode of an 8x12 tube rack, acquires an image of\n",
"the rack, and decodes the 96 tube DataMatrix codes locally. It does not require Micronic Code\n",
"Reader or IO Monitor.\n",
"\n",
"| Property | Value |\n",
"|---|---|\n",
"| Model | RD235 |\n",
"| Rack-ID communication | Serial trigger and response |\n",
"| Serial settings | 9600 baud, 7 data bits, even parity, 1 stop bit |\n",
"| Rack image | TWAIN helper on Windows, SANE `scanimage` on Linux, or a custom `Scanner` |\n",
"| Rack layout | 8 rows by 12 columns |\n",
"| Tube barcode | Ten-digit DataMatrix |\n",
"| Rack barcode | Code 128 |\n",
"\n",
"[OEM link](https://www.micronic.com/products/code-reader/)"
]
},
{
"cell_type": "markdown",
"id": "device-card",
"metadata": {},
"source": [
"```{device-card} micronic-code-reader\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "micronic-communication",
"metadata": {},
"source": [
"## How it talks\n",
"\n",
"The side reader uses `pylabrobot.io.Serial`. A rack-ID scan clears pending input, sends\n",
"`<t>` followed by CRLF, reads through the response terminator, and returns the first sequence\n",
"of at least six digits. It returns `NOREAD` when the response contains no rack identifier.\n",
"\n",
"Rack-image acquisition runs through PyLabRobot's command-line transport. `TwainScanner` calls\n",
"an operator-installed TWAIN helper; `SaneScanner` calls `scanimage`. PyLabRobot then performs\n",
"grid fitting and DataMatrix decoding locally."
]
},
{
"cell_type": "markdown",
"id": "micronic-physical-setup",
"metadata": {},
"source": [
"## Physical setup\n",
"\n",
"1. Connect the rack scanner to the computer and install its operating-system driver.\n",
"2. Connect the side rack-ID reader and identify its serial port.\n",
"3. On Windows, install the local TWAIN helper and note its path and TWAIN source name. On\n",
" Linux, install SANE and confirm that `scanimage --list-devices` lists the scanner.\n",
"4. Install the serial and decoding dependencies: `pylabrobot[serial]`, `pillow`,\n",
" `opencv-python-headless`, `numpy`, and `zxing-cpp`.\n",
"5. Place an 8x12 Micronic tube rack in the scanner with the orientation expected by the\n",
" instrument."
]
},
{
"cell_type": "markdown",
"id": "micronic-connect",
"metadata": {},
"source": [
"## Connect\n",
"\n",
"Choose the scanner implementation for the host. This example uses a Windows TWAIN helper. On\n",
"Linux, replace it with `SaneScanner(sane_device=\"<device from scanimage --list-devices>\")`.\n",
"`setup()` resolves the scanner executable, creates the image directory, and opens the side\n",
"reader's serial port."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "micronic-connect-code",
"metadata": {},
"outputs": [],
"source": [
"from pylabrobot.micronic import MicronicRD235, TwainScanner\n",
"\n",
"reader = MicronicRD235(\n",
" scanner=TwainScanner(\n",
" twain_scanner_path=r\"C:\\Tools\\twain_scan.exe\",\n",
" twain_source=\"AVA6PlusG\",\n",
" ),\n",
" serial_port=\"COM4\",\n",
" image_dir=r\"C:\\ProgramData\\PyLabRobot\\micronic-images\",\n",
")\n",
"await reader.setup()"
]
},
{
"cell_type": "markdown",
"id": "micronic-rack-id",
"metadata": {},
"source": [
"## Read the rack ID\n",
"\n",
"`scan_rack_id()` triggers only the side barcode reader. It returns the decoded identifier or\n",
"`NOREAD`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "micronic-rack-id-code",
"metadata": {},
"outputs": [],
"source": [
"rack_id = await reader.scan_rack_id(timeout=5.0)\n",
"print(rack_id)"
]
},
{
"cell_type": "markdown",
"id": "micronic-rack-resource",
"metadata": {},
"source": [
"## Represent the rack\n",
"\n",
"`scan_rack()` accepts a `TubeRack` with exactly 8 rows and 12 columns. Define the rack layout\n",
"before scanning it."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "micronic-rack-resource-code",
"metadata": {},
"outputs": [],
"source": [
"from pylabrobot.resources import ResourceHolder, TubeRack, create_ordered_items_2d\n",
"\n",
"rack = TubeRack(\n",
" name=\"micronic_96_tube_rack\",\n",
" size_x=85.0,\n",
" size_y=127.0,\n",
" size_z=20.0,\n",
" ordered_items=create_ordered_items_2d(\n",
" ResourceHolder,\n",
" num_items_x=12,\n",
" num_items_y=8,\n",
" dx=0,\n",
" dy=0,\n",
" dz=0,\n",
" item_dx=9.0,\n",
" item_dy=9.0,\n",
" size_x=9.0,\n",
" size_y=9.0,\n",
" size_z=20.0,\n",
" ),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "micronic-scan-rack",
"metadata": {},
"source": [
"## Scan the rack\n",
"\n",
"`scan_rack()` reads the side rack barcode, acquires the rack image, and returns one entry for\n",
"each tube position. A position reports `NOREAD` when no tube barcode was decoded."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "micronic-scan-rack-code",
"metadata": {},
"outputs": [],
"source": [
"result = await reader.scan_rack(rack=rack, timeout=90.0)\n",
"print(result.rack_id)\n",
"print(len([entry for entry in result.entries if entry.status == \"OK\"]))"
]
},
{
"cell_type": "markdown",
"id": "micronic-disconnect",
"metadata": {},
"source": [
"## Disconnect\n",
"\n",
"`stop()` closes the side reader's serial connection."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "micronic-disconnect-code",
"metadata": {},
"outputs": [],
"source": [
"await reader.stop()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
7 changes: 7 additions & 0 deletions docs/user_guide/micronic/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Micronic

```{toctree}
:maxdepth: 1

code_reader/hello-world
```
9 changes: 9 additions & 0 deletions pylabrobot/micronic/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pylabrobot.micronic.code_reader import (
MicronicError,
MicronicRD235,
RackScanEntry,
RackScanResult,
SaneScanner,
Scanner,
TwainScanner,
)
7 changes: 7 additions & 0 deletions pylabrobot/micronic/code_reader/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from pylabrobot.micronic.code_reader.driver import (
MicronicRD235,
RackScanEntry,
RackScanResult,
)
from pylabrobot.micronic.code_reader.errors import MicronicError
from pylabrobot.micronic.code_reader.scanner import SaneScanner, Scanner, TwainScanner
Loading
Loading