{ "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", "`` 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=\"\")`.\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 }