{ "cells": [ { "cell_type": "markdown", "id": "5a699051", "metadata": {}, "source": [ "# KingFisher Presto\n", "\n", "```{device-card} thermo-fisher-kingfisher-presto\n", "```\n", "\n", "| Property | Value |\n", "|---|---|\n", "| Manufacturer | Thermo Fisher Scientific |\n", "| Instrument | KingFisher Presto magnetic particle processor |\n", "| Connection | USB HID (`pylabrobot[hid]`) |\n", "| API | `KingFisherPresto` |\n", "| Units | Seconds, degrees Celsius, microliters |\n", "\n", "```{warning}\n", "This driver and the BDZ writer have not been verified on hardware or against manufacturer-generated BDZ fixtures. The interface specification and original examples referenced during development are unavailable. Report tested firmware and operations before removing this warning.\n", "```\n", "\n", "The instrument executes stored protocols and reports events. PLR handles connection, file transfer, explicit nest positioning, and operator acknowledgements. A successful start command means the instrument accepted the command; a terminal event reports the outcome.\n", "\n", "See the manufacturer's [Momentum integration guide](https://apps.thermofisher.com/apps/lahr/LA_Online_Help_Resource/en-us/Content/Topics/Drivers/Extraction-Purification/DRVR%20KingFisher%20Presto.htm) for integration context. It does not replace the missing wire-protocol specification." ] }, { "cell_type": "markdown", "id": "26a8d954", "metadata": {}, "source": [ "## Physical setup\n", "\n", "Connect the Presto to its power supply and the computer by USB. Install the correct magnetic head, heater block, plates and tip comb for your protocol according to the instrument manual. Keep the turntable clear before moving it. Install the `hid` optional dependency and the operating system's HID library; on Linux, grant your user access to the USB HID device.\n", "\n", "Nests are numbered 1 and 2. A nest can face the loading station or the processing head. PLR initially treats both positions as unknown." ] }, { "cell_type": "markdown", "id": "7afb4b6f", "metadata": {}, "source": [ "## Connect\n", "\n", "Import the device directly. Supply `serial_number=\"...\"` if more than one Presto is connected. `setup()` reads identity and status without moving the turntable or clearing an instrument fault." ] }, { "cell_type": "code", "execution_count": null, "id": "604471ee", "metadata": {}, "outputs": [], "source": [ "from pylabrobot.thermo_fisher.kingfisher.presto import KingFisherPresto\n", "\n", "presto = KingFisherPresto(timeout=30)\n", "await presto.setup()\n", "presto.instrument, presto.version, presto.serial" ] }, { "cell_type": "markdown", "id": "12be4956", "metadata": {}, "source": [ "## Inspect status\n", "\n", "`status` is `Idle`, `Busy`, or `In error`. An existing run remains under instrument control after a connection is opened." ] }, { "cell_type": "code", "execution_count": null, "id": "381b6340", "metadata": {}, "outputs": [], "source": [ "status = await presto.get_status()\n", "status" ] }, { "cell_type": "markdown", "id": "095700ab", "metadata": {}, "source": [ "## Move a nest to loading\n", "\n", "This moves nest 1 to the loading station. The call waits for rotation completion. Repeating the same target does not swap nests." ] }, { "cell_type": "code", "execution_count": null, "id": "31a6afe8", "metadata": {}, "outputs": [], "source": [ "await presto.rotate(position=1, location=\"loading\")\n", "presto.get_turntable_state()" ] }, { "cell_type": "markdown", "id": "a7320239", "metadata": {}, "source": [ "## Place the plate, then move it to processing\n", "\n", "Place the protocol's plate in nest 1 before running the next cell. `load_plate(position=1)` means move that specific nest to processing. It does not detect or assign labware in a PLR resource tree." ] }, { "cell_type": "code", "execution_count": null, "id": "17dffdaf", "metadata": {}, "outputs": [], "source": [ "await presto.load_plate(position=1)" ] }, { "cell_type": "markdown", "id": "9b717a4e", "metadata": {}, "source": [ "## List stored protocols\n", "\n", "Inspect the names and memory utilization. Choose a protocol appropriate for the installed consumables before starting a run." ] }, { "cell_type": "code", "execution_count": null, "id": "cfe59aff", "metadata": {}, "outputs": [], "source": [ "names, memory_used = await presto.list_protocols()\n", "names, memory_used" ] }, { "cell_type": "markdown", "id": "2fcbce27", "metadata": {}, "source": [ "## Inspect protocol steps\n", "\n", "Replace the example name with an existing stored protocol. Durations are returned in seconds; the returned tip and step names can be used for single-step execution." ] }, { "cell_type": "code", "execution_count": null, "id": "77eddd33", "metadata": {}, "outputs": [], "source": [ "protocol_name = \"Instrument Test Protocol\"\n", "duration = await presto.get_protocol_duration(protocol_name)\n", "duration" ] }, { "cell_type": "markdown", "id": "e3f9f25b", "metadata": {}, "source": [ "## Download a protocol\n", "\n", "Downloads return the original bytes so unsupported BindIt features are preserved. The structured `read_bdz()` codec covers a subset of Mix protocols and rejects unsupported step types; keep original files for lossless transfer." ] }, { "cell_type": "code", "execution_count": null, "id": "a7161fdd", "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "original_bdz = await presto.download_protocol(protocol_name)\n", "Path(\"presto-protocol-backup.bdz\").write_bytes(original_bdz)" ] }, { "cell_type": "markdown", "id": "285069de", "metadata": {}, "source": [ "## Build a Mix protocol\n", "\n", "The shared codec lives under `kingfisher.bdz`. This creates a protocol in memory; it does not move hardware. Review durations, temperatures, plate definitions, and tip-comb settings before using generated files. The codec has synthetic regression tests but no manufacturer fixtures." ] }, { "cell_type": "code", "execution_count": null, "id": "4efe9fac", "metadata": {}, "outputs": [], "source": [ "from pylabrobot.thermo_fisher.kingfisher.bdz import (\n", " BdzProtocol, MixStep, ProtocolTip, read_bdz, write_bdz,\n", ")\n", "\n", "example = BdzProtocol(\n", " name=\"plr-example-mix\",\n", " tips=[ProtocolTip(name=\"Tip1\", steps=[MixStep(name=\"Mix\", shake_duration=30)])],\n", ")\n", "example_bdz = write_bdz(example)\n", "read_bdz(example_bdz)" ] }, { "cell_type": "markdown", "id": "4ecc3790", "metadata": {}, "source": [ "## Upload a protocol\n", "\n", "Uploading overwrites a stored protocol with the same name. This accepts original BDZ bytes or a `BdzProtocol`; it rejects Duo files before sending them to the Presto. This cell uploads without starting execution." ] }, { "cell_type": "code", "execution_count": null, "id": "3d91e048", "metadata": {}, "outputs": [], "source": [ "await presto.upload_protocol(\"plr-example-mix\", example_bdz)" ] }, { "cell_type": "markdown", "id": "0be46f0f", "metadata": {}, "source": [ "## Start a protocol\n", "\n", "Ensure the instrument and consumables match the selected protocol. This starts a full run. It returns when the command is accepted; consume events to follow progress and handle plate changes." ] }, { "cell_type": "code", "execution_count": null, "id": "9102f09c", "metadata": {}, "outputs": [], "source": [ "await presto.start_protocol(protocol_name)" ] }, { "cell_type": "markdown", "id": "505a8b0c", "metadata": {}, "source": [ "## Read the next event\n", "\n", "Run this cell repeatedly to inspect events. Progress events remain visible. `Ready`, `Aborted`, and `Error` are terminal; inspect Error details before clearing a fault. When reconnecting to an existing run, use `next_event(attach=True)` once to check current status first." ] }, { "cell_type": "code", "execution_count": null, "id": "058f6cfe", "metadata": {}, "outputs": [], "source": [ "event = await presto.next_event()\n", "event.name, event.requires_acknowledgement, event.details.attrib" ] }, { "cell_type": "markdown", "id": "164270c7", "metadata": {}, "source": [ "## Complete the requested action, then acknowledge\n", "\n", "For `LoadPlate`, `RemovePlate`, `ChangePlate`, or `Pause`, perform the requested physical action before running this cell. Never automatically acknowledge these events in a loop. Faults use the separate `error_acknowledge()` operation." ] }, { "cell_type": "code", "execution_count": null, "id": "d2f4974c", "metadata": {}, "outputs": [], "source": [ "if event.requires_acknowledgement:\n", " await presto.acknowledge()" ] }, { "cell_type": "markdown", "id": "df1cba2d", "metadata": {}, "source": [ "## Inspect remaining time\n", "\n", "Run this while a protocol is active. Both values use seconds; a missing time-to-pause is `None`." ] }, { "cell_type": "code", "execution_count": null, "id": "7d6006c0", "metadata": {}, "outputs": [], "source": [ "await presto.get_protocol_time_left()" ] }, { "cell_type": "markdown", "id": "7b78533d", "metadata": {}, "source": [ "## Request execution to stop\n", "\n", "This requests Stop and leaves the connection open. Continue reading events to observe the terminal outcome." ] }, { "cell_type": "code", "execution_count": null, "id": "160f8632", "metadata": {}, "outputs": [], "source": [ "await presto.stop_protocol()" ] }, { "cell_type": "markdown", "id": "30d7004c", "metadata": {}, "source": [ "## Execute a single named step\n", "\n", "Use a tip and step returned by `get_protocol_duration()`. Run only when the instrument is ready and the required plate and tip comb are in place. Both arguments are required for a single step; do not run this cell during another run." ] }, { "cell_type": "code", "execution_count": null, "id": "01323957", "metadata": {}, "outputs": [], "source": [ "step = duration.steps[0]\n", "await presto.start_protocol(protocol_name, tip=step.tip, step=step.name)" ] }, { "cell_type": "markdown", "id": "d5fa87db", "metadata": {}, "source": [ "## Acknowledge a fault\n", "\n", "Inspect `get_status()` and resolve the reported condition at the instrument before explicitly acknowledging the fault." ] }, { "cell_type": "code", "execution_count": null, "id": "f132d60d", "metadata": {}, "outputs": [], "source": [ "await presto.error_acknowledge()" ] }, { "cell_type": "markdown", "id": "f83dd8df", "metadata": {}, "source": [ "## Abort\n", "\n", "Use only when aborting an active operation is intended. Abort uses a HID feature report followed by the abort payload. The driver invalidates the connection afterwards so late responses cannot complete a later command. Close and reopen the connection before further operations; inspect status and physical state before resuming." ] }, { "cell_type": "code", "execution_count": null, "id": "e5f03067", "metadata": {}, "outputs": [], "source": [ "await presto.abort()" ] }, { "cell_type": "markdown", "id": "de7e3759", "metadata": {}, "source": [ "## Reconnect after abort or a command timeout\n", "\n", "A timeout or cancellation has an uncertain instrument outcome; it does not imply the physical operation stopped. Reconnecting clears local response/event state and reads instrument status without moving hardware." ] }, { "cell_type": "code", "execution_count": null, "id": "f7a97954", "metadata": {}, "outputs": [], "source": [ "await presto.stop()\n", "await presto.setup()\n", "await presto.get_status()" ] }, { "cell_type": "markdown", "id": "04b4f736", "metadata": {}, "source": [ "## Remove a stored protocol\n", "\n", "Delete the example only after its execution has finished and it is no longer needed." ] }, { "cell_type": "code", "execution_count": null, "id": "0ec0175f", "metadata": {}, "outputs": [], "source": [ "await presto.remove_protocol(\"plr-example-mix\")" ] }, { "cell_type": "markdown", "id": "25149785", "metadata": {}, "source": [ "## Disconnect\n", "\n", "`stop()` closes the connection. It does not stop an instrument protocol; use `stop_protocol()` when that is intended. The device also supports `async with KingFisherPresto() as presto:` for automatic connection cleanup." ] }, { "cell_type": "code", "execution_count": null, "id": "2f490e37", "metadata": {}, "outputs": [], "source": [ "await presto.stop()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }