diff --git a/docs/user_guide/02_analytical/plate-reading/byonoy.ipynb b/docs/user_guide/02_analytical/plate-reading/byonoy.ipynb index 7ed00febadc..620385c5720 100644 --- a/docs/user_guide/02_analytical/plate-reading/byonoy.ipynb +++ b/docs/user_guide/02_analytical/plate-reading/byonoy.ipynb @@ -1,53 +1,48 @@ { "cells": [ { - "cell_type": "code", - "execution_count": 1, - "id": "6aa99372", + "cell_type": "markdown", + "id": "63d8a7a6-1107-4334-8b73-598aa1ca97c4", "metadata": {}, - "outputs": [], "source": [ - "from byonoy import byonoy_absorbance_adapter, byonoy_absorbance96_base_and_reader\n", - "a = byonoy_absorbance_adapter(name='a')\n", - "b, r = byonoy_absorbance96_base_and_reader(name='b', assign=True)" + "# Byonoy Absorbance 96 Automate\n", + "\n", + "| Summary | Photo |\n", + "|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------|\n", + "| - [OEM Link](https://byonoy.com/absorbance-automate/)
- **Communication Protocol / Hardware**: Serial (HID)/ USB-A
- **Communication Level**: Firmware
- VID:PID 16d0:1199
- Serial: \"BYOMAA00058\"
- | ![quadrants](img/byonoy_absorbance_96_automate.png) |" ] }, { - "cell_type": "code", - "execution_count": 2, - "id": "84949548", + "cell_type": "markdown", + "id": "840adda3-0ea1-4e7c-b0cb-34dd2244de69", "metadata": {}, - "outputs": [], "source": [ - "a.assign_child_resource(b)" + "---\n", + "## Setup Instructions (Physical)\n", + "\n", + "The Byonoy Absorbance 96 Automate is a an absorbance plate reader consisting of...\n", + "1. a `base` containing the liqht source,\n", + "2. a `reader_cap` containing the light detectors, and\n", + "3. a `cap_adapter` representing a simple resource_holder for the `reader_cap`\n", + "\n", + "It requires only one cable connections to be operational:\n", + "1. USB cable (USB-C at `base` end; USB-A at control PC end)" ] }, { - "cell_type": "code", - "execution_count": 3, - "id": "b2e6e986", + "cell_type": "markdown", + "id": "e4aa8066-9eb5-4f8a-8d69-372712bdb3b5", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(ResourceHolder(name=a, location=None, size_x=127.76, size_y=85.59, size_z=14.07, category=resource_holder),\n", - " ByonoyBase(name=b_base, location=Coordinate(-05.120, -05.055, 011.620), size_x=138, size_y=95.7, size_z=27.7, category=None),\n", - " PlateReader(name=b_reader, location=Coordinate(000.000, 000.000, 010.660), size_x=138, size_y=95.7, size_z=0, category=None))" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "a, b, r" + "---\n", + "## Setup Instructions (Programmatic)\n", + "\n", + "If used with a liquid handler, first setup the liquid handler:" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "id": "1fd4d917", "metadata": {}, "outputs": [], @@ -60,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "id": "abde0e65", "metadata": {}, "outputs": [ @@ -80,9 +75,17 @@ "await lh.setup()" ] }, + { + "cell_type": "markdown", + "id": "165bd434-5899-4623-ac67-91d2aad55e7c", + "metadata": {}, + "source": [ + "Then generate a plate definition for the plate you want to read:" + ] + }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "id": "5be9a197", "metadata": {}, "outputs": [ @@ -103,112 +106,249 @@ "lh.deck.assign_child_resource(plate, location=Coordinate(0, 0, 0))" ] }, + { + "cell_type": "markdown", + "id": "bee933e6-b6df-4de7-aad1-40a2f0ba6721", + "metadata": {}, + "source": [ + "Now instantiate the Byonoy absorbance plate reader:" + ] + }, { "cell_type": "code", - "execution_count": 7, - "id": "219becbc", + "execution_count": 4, + "id": "6aa99372", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Picking up resource: ResourcePickup(resource=PlateReader(name=b_reader, location=Coordinate(000.000, 000.000, 010.660), size_x=138, size_y=95.7, size_z=0, category=None), offset=Coordinate(x=0, y=0, z=0), pickup_distance_from_top=0, direction=)\n", - "Dropping resource: ResourceDrop(resource=PlateReader(name=b_reader, location=Coordinate(000.000, 000.000, 010.660), size_x=138, size_y=95.7, size_z=0, category=None), destination=Coordinate(x=200, y=0, z=0), destination_absolute_rotation=Rotation(x=0, y=0, z=0), offset=Coordinate(x=0, y=0, z=0), pickup_distance_from_top=0, pickup_direction=, drop_direction=, rotation=0)\n", - "Resource b_reader was assigned to the liquid handler.\n" + "Resource cap_adapter was assigned to the liquid handler.\n" ] } ], "source": [ - "# move the reader off the base\n", - "await lh.move_resource(r, Coordinate(200, 0, 0))" + "from pylabrobot.plate_reading.byonoy import (\n", + " byonoy_absorbance_adapter,\n", + " byonoy_absorbance96_base_and_reader\n", + ")\n", + "\n", + "cap_adapter = byonoy_absorbance_adapter(name='cap_adapter')\n", + "\n", + "base, reader_cap = byonoy_absorbance96_base_and_reader(name='base', assign=True)\n", + "\n", + "lh.deck.assign_child_resource(cap_adapter, location=Coordinate(400, 0, 0))" ] }, { "cell_type": "code", - "execution_count": 8, - "id": "c7f821fd", + "execution_count": 5, + "id": "a10f9bb9", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Resource a was assigned to the liquid handler.\n" - ] + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "lh.deck.assign_child_resource(a, location=Coordinate(400, 0, 0))" + "await reader_cap.setup()\n", + "\n", + "reader_cap.setup_finished" + ] + }, + { + "cell_type": "raw", + "id": "860c3830-e2a8-4ae3-a4bb-586ae993d849", + "metadata": {}, + "source": [ + "## Test Movement for Plate Reading" + ] + }, + { + "cell_type": "raw", + "id": "32de1568-625b-4114-ae55-df1c03ea9230", + "metadata": {}, + "source": [ + "# move the reader off the base\n", + "await lh.move_resource(reader_cap, Coordinate(200, 0, 0))" + ] + }, + { + "cell_type": "raw", + "id": "4199936d-efd1-423c-9714-20b0ae581e10", + "metadata": { + "scrolled": true + }, + "source": [ + "await lh.move_resource(plate, base.plate_holder)" + ] + }, + { + "cell_type": "raw", + "id": "b11f154e-2025-4092-9a52-fb14af1a1520", + "metadata": {}, + "source": [ + "await lh.move_resource(reader_cap, base.reader_holder)" + ] + }, + { + "cell_type": "raw", + "id": "0b975857-6b26-49c9-947d-db25763e332d", + "metadata": {}, + "source": [ + "adapter.assign_child_resource(base)" ] }, { "cell_type": "code", - "execution_count": 9, - "id": "134cd064", + "execution_count": 6, + "id": "b2e6e986", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Picking up resource: ResourcePickup(resource=Plate(name=plate, size_x=127.6, size_y=85.75, size_z=13.83, location=Coordinate(000.000, 000.000, 000.000)), offset=Coordinate(x=0, y=0, z=0), pickup_distance_from_top=0, direction=)\n", - "Dropping resource: ResourceDrop(resource=Plate(name=plate, size_x=127.6, size_y=85.75, size_z=13.83, location=Coordinate(000.000, 000.000, 000.000)), destination=Coordinate(x=400.0, y=0.0, z=39.32), destination_absolute_rotation=Rotation(x=0, y=0, z=0), offset=Coordinate(x=0, y=0, z=0), pickup_distance_from_top=0, pickup_direction=, drop_direction=, rotation=0)\n", - "Resource plate was unassigned from the liquid handler.\n", - "Resource plate was assigned to the liquid handler.\n" - ] + "data": { + "text/plain": [ + "(ResourceHolder(name='cap_adapter', location=Coordinate(400.000, 000.000, 000.000), size_x=127.76, size_y=85.59, size_z=14.07, category=resource_holder),\n", + " ByonoyBase(name='base_base', location=None, size_x=138, size_y=95.7, size_z=27.7, category=None),\n", + " PlateReader(name='base_reader', location=Coordinate(000.000, 000.000, 010.660), size_x=138, size_y=95.7, size_z=0, category=None))" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "await lh.move_resource(plate, b.plate_holder)" + "cap_adapter, base, reader_cap" ] }, { - "cell_type": "code", - "execution_count": 10, - "id": "867ec7d8", + "cell_type": "markdown", + "id": "1ccafe3d-56c1-405f-b79e-6d4f8930e49d", + "metadata": {}, + "source": [ + "---\n", + "\n", + "## Usage / Machine Features" + ] + }, + { + "cell_type": "markdown", + "id": "30619f34-af58-4a74-b4fd-e2d53033c2de", "metadata": {}, + "source": [ + "### Query Machine Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2254228f-2864-4174-a615-9d1aed119ad5", + "metadata": { + "scrolled": true + }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Picking up resource: ResourcePickup(resource=PlateReader(name=b_reader, location=Coordinate(200.000, 000.000, 000.000), size_x=138, size_y=95.7, size_z=0, category=None), offset=Coordinate(x=0, y=0, z=0), pickup_distance_from_top=0, direction=)\n", - "Dropping resource: ResourceDrop(resource=PlateReader(name=b_reader, location=Coordinate(200.000, 000.000, 000.000), size_x=138, size_y=95.7, size_z=0, category=None), destination=Coordinate(x=394.88, y=-5.055, z=22.28), destination_absolute_rotation=Rotation(x=0, y=0, z=0), offset=Coordinate(x=0, y=0, z=0), pickup_distance_from_top=0, pickup_direction=, drop_direction=, rotation=0)\n", - "Resource b_reader was unassigned from the liquid handler.\n", - "Resource b_reader was assigned to the liquid handler.\n" + "ename": "CancelledError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mCancelledError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[7]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28;01mawait\u001b[39;00m reader_cap.backend.get_available_absorbance_wavelengths()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/GitHub/pylabrobot/pylabrobot/plate_reading/byonoy/byonoy_backend.py:135\u001b[39m, in \u001b[36mByonoyAbsorbance96AutomateBackend.get_available_absorbance_wavelengths\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 134\u001b[39m \u001b[38;5;28;01masync\u001b[39;00m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mget_available_absorbance_wavelengths\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> List[\u001b[38;5;28mfloat\u001b[39m]:\n\u001b[32m--> \u001b[39m\u001b[32m135\u001b[39m available_wavelengths_r = \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m.send_command(\n\u001b[32m 136\u001b[39m report_id=\u001b[32m0x0330\u001b[39m,\n\u001b[32m 137\u001b[39m payload_fmt=\u001b[33m\"\u001b[39m\u001b[33m<30h\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 138\u001b[39m payload=[\u001b[32m0\u001b[39m] * \u001b[32m30\u001b[39m,\n\u001b[32m 139\u001b[39m wait_for_response=\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[32m 140\u001b[39m )\n\u001b[32m 141\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m available_wavelengths_r \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[33m\"\u001b[39m\u001b[33mFailed to get available wavelengths.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 142\u001b[39m \u001b[38;5;66;03m# cut out the first 2 bytes, then read the next 2 bytes as an integer\u001b[39;00m\n\u001b[32m 143\u001b[39m \u001b[38;5;66;03m# 64 - 4 = 60. 60/2 = 30 16 bit integers\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/GitHub/pylabrobot/pylabrobot/plate_reading/byonoy/byonoy_backend.py:80\u001b[39m, in \u001b[36m_ByonoyBase.send_command\u001b[39m\u001b[34m(self, report_id, payload_fmt, payload, wait_for_response)\u001b[39m\n\u001b[32m 77\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m time.time() - t0 > \u001b[32m120\u001b[39m: \u001b[38;5;66;03m# read for 2 minutes max. typical is 1m5s.\u001b[39;00m\n\u001b[32m 78\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTimeoutError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mReading luminescence data timed out after 2 minutes.\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m80\u001b[39m response = \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m.io.read(\u001b[32m64\u001b[39m, timeout=\u001b[32m30\u001b[39m)\n\u001b[32m 81\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(response) == \u001b[32m0\u001b[39m:\n\u001b[32m 82\u001b[39m \u001b[38;5;28;01mcontinue\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/GitHub/pylabrobot/pylabrobot/io/hid.py:109\u001b[39m, in \u001b[36mHID.read\u001b[39m\u001b[34m(self, size, timeout)\u001b[39m\n\u001b[32m 107\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._executor \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 108\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mCall setup() first.\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m109\u001b[39m r = \u001b[38;5;28;01mawait\u001b[39;00m loop.run_in_executor(\u001b[38;5;28mself\u001b[39m._executor, _read)\n\u001b[32m 110\u001b[39m logger.log(LOG_LEVEL_IO, \u001b[33m\"\u001b[39m\u001b[33m[\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[33m] read \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[33m\"\u001b[39m, \u001b[38;5;28mself\u001b[39m._unique_id, r)\n\u001b[32m 111\u001b[39m capturer.record(HIDCommand(device_id=\u001b[38;5;28mself\u001b[39m._unique_id, action=\u001b[33m\"\u001b[39m\u001b[33mread\u001b[39m\u001b[33m\"\u001b[39m, data=r.hex()))\n", + "\u001b[31mCancelledError\u001b[39m: " ] } ], "source": [ - "await lh.move_resource(r, b.reader_holder)" + "await reader_cap.backend.get_available_absorbance_wavelengths()" ] }, { "cell_type": "code", - "execution_count": 11, - "id": "439663bd", + "execution_count": null, + "id": "cb5404f7-56c1-4647-b6cb-32606ac470f3", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "fc15c1b4-be77-4180-a5ce-d8a31480d0d4", + "metadata": {}, + "source": [ + "### Measure Absorbance" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "761a587a-7084-40f8-9820-ef934230fb2f", "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "ResourceHolder(name=b_base_reader_holder, location=Coordinate(000.000, 000.000, 000.000), size_x=138, size_y=95.7, size_z=0, category=resource_holder)" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" + "ename": "TimeoutError", + "evalue": "Reading luminescence data timed out after 2 minutes.", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTimeoutError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28;01mawait\u001b[39;00m reader_cap.backend.read_absorbance(\n\u001b[32m 2\u001b[39m plate = CellVis_96_wellplate_350uL_Fb,\n\u001b[32m 3\u001b[39m wavelength = \u001b[32m600\u001b[39m\n\u001b[32m 4\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/GitHub/pylabrobot/pylabrobot/plate_reading/byonoy/byonoy_backend.py:152\u001b[39m, in \u001b[36mByonoyAbsorbance96AutomateBackend.read_absorbance\u001b[39m\u001b[34m(self, plate, wavelength)\u001b[39m\n\u001b[32m 148\u001b[39m \u001b[38;5;28;01masync\u001b[39;00m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mread_absorbance\u001b[39m(\u001b[38;5;28mself\u001b[39m, plate: Plate, wavelength: \u001b[38;5;28mint\u001b[39m) -> List[List[\u001b[38;5;28mfloat\u001b[39m]]:\n\u001b[32m 149\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Read the absorbance from the plate reader. This should return a list of lists, where the\u001b[39;00m\n\u001b[32m 150\u001b[39m \u001b[33;03m outer list is the columns of the plate and the inner list is the rows of the plate.\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m152\u001b[39m available_wavelengths = \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m.get_available_absorbance_wavelengths()\n\u001b[32m 153\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m wavelength \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m available_wavelengths:\n\u001b[32m 154\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 155\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mWavelength \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mwavelength\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m nm is not supported by this plate reader. \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 156\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mAvailable wavelengths: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mavailable_wavelengths\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m\n\u001b[32m 157\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/GitHub/pylabrobot/pylabrobot/plate_reading/byonoy/byonoy_backend.py:135\u001b[39m, in \u001b[36mByonoyAbsorbance96AutomateBackend.get_available_absorbance_wavelengths\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 134\u001b[39m \u001b[38;5;28;01masync\u001b[39;00m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mget_available_absorbance_wavelengths\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> List[\u001b[38;5;28mfloat\u001b[39m]:\n\u001b[32m--> \u001b[39m\u001b[32m135\u001b[39m available_wavelengths_r = \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m.send_command(\n\u001b[32m 136\u001b[39m report_id=\u001b[32m0x0330\u001b[39m,\n\u001b[32m 137\u001b[39m payload_fmt=\u001b[33m\"\u001b[39m\u001b[33m<30h\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 138\u001b[39m payload=[\u001b[32m0\u001b[39m] * \u001b[32m30\u001b[39m,\n\u001b[32m 139\u001b[39m wait_for_response=\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[32m 140\u001b[39m )\n\u001b[32m 141\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m available_wavelengths_r \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[33m\"\u001b[39m\u001b[33mFailed to get available wavelengths.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 142\u001b[39m \u001b[38;5;66;03m# cut out the first 2 bytes, then read the next 2 bytes as an integer\u001b[39;00m\n\u001b[32m 143\u001b[39m \u001b[38;5;66;03m# 64 - 4 = 60. 60/2 = 30 16 bit integers\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/GitHub/pylabrobot/pylabrobot/plate_reading/byonoy/byonoy_backend.py:78\u001b[39m, in \u001b[36m_ByonoyBase.send_command\u001b[39m\u001b[34m(self, report_id, payload_fmt, payload, wait_for_response)\u001b[39m\n\u001b[32m 76\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m 77\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m time.time() - t0 > \u001b[32m120\u001b[39m: \u001b[38;5;66;03m# read for 2 minutes max. typical is 1m5s.\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m78\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTimeoutError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mReading luminescence data timed out after 2 minutes.\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 80\u001b[39m response = \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m.io.read(\u001b[32m64\u001b[39m, timeout=\u001b[32m30\u001b[39m)\n\u001b[32m 81\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(response) == \u001b[32m0\u001b[39m:\n", + "\u001b[31mTimeoutError\u001b[39m: Reading luminescence data timed out after 2 minutes." + ] } ], "source": [ - "r.parent" + "await reader_cap.backend.read_absorbance(\n", + " plate = CellVis_96_wellplate_350uL_Fb,\n", + " wavelength = 600 # units: nm\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1a33230d-8243-4d21-88e1-4a4eb6cba7c8", + "metadata": {}, + "source": [ + "## Disconnect from Reader" ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "21a72488", + "metadata": {}, + "outputs": [], + "source": [ + "await reader_cap.stop()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62b8732a-8bd7-427d-85c3-ab900f2a48b6", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "env (3.10.15)", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -222,7 +362,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.15" + "version": "3.12.11" } }, "nbformat": 4, diff --git a/docs/user_guide/02_analytical/plate-reading/img/byonoy_absorbance_96_automate.png b/docs/user_guide/02_analytical/plate-reading/img/byonoy_absorbance_96_automate.png new file mode 100644 index 00000000000..dfe4151929b Binary files /dev/null and b/docs/user_guide/02_analytical/plate-reading/img/byonoy_absorbance_96_automate.png differ