From 996137708e10d8b5d44f359b322f438fa9376c84 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 8 Sep 2026 19:20:37 -0700 Subject: [PATCH 1/8] Add HighRes MicroServe driver and captured protocol tests --- docs/_static/devices.json | 15 + docs/api/pylabrobot.high_res.rst | 16 + docs/user_guide/high_res/index.md | 2 + .../high_res/micro-serve/hello-world.ipynb | 609 +++++ .../high_res/micro-serve/protocol.md | 134 + pylabrobot/high_res/__init__.py | 20 + pylabrobot/high_res/micro_serve.py | 664 +++++ .../micro_serve_carousel_capture.json | 2435 +++++++++++++++++ .../micro_serve_empty_cycle_capture.json | 1127 ++++++++ .../micro_serve_empty_load_capture.json | 659 +++++ .../high_res/micro_serve_home_capture.json | 257 ++ .../high_res/micro_serve_query_capture.json | 581 ++++ pylabrobot/high_res/micro_serve_tests.py | 674 +++++ 13 files changed, 7193 insertions(+) create mode 100644 docs/user_guide/high_res/micro-serve/hello-world.ipynb create mode 100644 docs/user_guide/high_res/micro-serve/protocol.md create mode 100644 pylabrobot/high_res/micro_serve.py create mode 100644 pylabrobot/high_res/micro_serve_carousel_capture.json create mode 100644 pylabrobot/high_res/micro_serve_empty_cycle_capture.json create mode 100644 pylabrobot/high_res/micro_serve_empty_load_capture.json create mode 100644 pylabrobot/high_res/micro_serve_home_capture.json create mode 100644 pylabrobot/high_res/micro_serve_query_capture.json create mode 100644 pylabrobot/high_res/micro_serve_tests.py diff --git a/docs/_static/devices.json b/docs/_static/devices.json index fb4c4f4fc38..60c62d12051 100644 --- a/docs/_static/devices.json +++ b/docs/_static/devices.json @@ -583,6 +583,21 @@ "manager": "https://discuss.pylabrobot.org/u/rickwierenga", "oem": "https://www.highres.com/lab-instruments/lid-management" }, + { + "id": "highres-microserve", + "vendor": "HighRes Biosolutions", + "name": "MicroServe", + "kind": "storage", + "capabilities": [ + "storage" + ], + "status": "wip", + "api": "pylabrobot.high_res.HighResMicroServe", + "api_version": "v1", + "code_slug": "high_res/micro_serve", + "doc_slug": "high_res/micro-serve/hello-world", + "notes": "Homing, carousel, and empty load/retract verified on firmware 2.7.0.756; plate transfers and barcode scans unverified." + }, { "id": "highres-microspin", "vendor": "HighRes Biosolutions", diff --git a/docs/api/pylabrobot.high_res.rst b/docs/api/pylabrobot.high_res.rst index 7971a6c5443..f822ea93fce 100644 --- a/docs/api/pylabrobot.high_res.rst +++ b/docs/api/pylabrobot.high_res.rst @@ -13,6 +13,22 @@ pylabrobot.high_res package HighResLidValet HighResLidValetError +.. currentmodule:: pylabrobot.high_res.micro_serve + +.. autosummary:: + :toctree: _autosummary + :nosignatures: + + HighResMicroServe + MicroServeStacker + MicroServePlateDimensions + MicroServeStatus + MicroServeCommandStatus + MicroServeMotorInfo + MicroServeUnresolvedPreparation + MicroServeError + MicroServeProtocolError + .. currentmodule:: pylabrobot.high_res.sample_storage .. autosummary:: diff --git a/docs/user_guide/high_res/index.md b/docs/user_guide/high_res/index.md index 27033a36794..84b63872753 100644 --- a/docs/user_guide/high_res/index.md +++ b/docs/user_guide/high_res/index.md @@ -4,5 +4,7 @@ :maxdepth: 1 lid-valet/hello-world +micro-serve/hello-world +micro-serve/protocol sample-storage/index ``` diff --git a/docs/user_guide/high_res/micro-serve/hello-world.ipynb b/docs/user_guide/high_res/micro-serve/hello-world.ipynb new file mode 100644 index 00000000000..58c60f28b4e --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/hello-world.ipynb @@ -0,0 +1,609 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f8d2a60b", + "metadata": {}, + "source": [ + "# HighRes MicroServe\n", + "\n", + "```{device-card} highres-microserve\n", + "```\n", + "\n", + "| Property | Value |\n", + "| --- | --- |\n", + "| Transport | Ethernet, TCP port 1000 |\n", + "| Storage | 14 stackers, indexed 0–13 |\n", + "| Direct connection address | `10.253.253.253` |\n", + "| Verified controller | HRB-2008-10558, firmware 2.7.0.756 |\n", + "| Public length units | millimeters |\n", + "\n", + "**Plate transfers, barcode scanning, and physical fault recovery remain unverified.** Communication, homing, all fourteen carousel positions, and empty receiving-position preparation/retraction were checked on this controller. Review the physical setup before running each motion cell.\n", + "\n", + "This driver prepares the MicroServe for a separate robot to place or pick plates. It does not model plate inventory in PyLabRobot resources. Reported plate counts are the controller's cached estimates.\n", + "\n", + "See [Protocol and validation](protocol.md) for the device-source mapping and verification limits." + ] + }, + { + "cell_type": "markdown", + "id": "4be5b404", + "metadata": {}, + "source": [ + "## Connection and manufacturer documentation\n", + "\n", + "Connect the powered MicroServe's Ethernet port to a dedicated NIC. Configure that NIC with `10.253.253.50/24`, then use `10.253.253.253` as the host. All HighRes controllers share that alternate address, so use it only on a dedicated link. Your normal network connection can remain separate.\n", + "\n", + "The controller serves its [MicroServe API PDF](http://10.253.253.253/support_files/files/MicroServe_API.pdf) through its [Support Files page](http://10.253.253.253/support_files.html). This guide follows revision 756, dated May 29, 2020. The saved network address shown by the web interface can differ from legacy in-memory `settings` values.\n", + "\n", + "The protocol acknowledges a command with an identifier, sends data, then sends a completion with the same command and identifier. The driver handles framing and converts plate dimensions from millimeters to device micrometers." + ] + }, + { + "cell_type": "markdown", + "id": "2d902948", + "metadata": {}, + "source": [ + "## Physical setup\n", + "\n", + "Secure the machine, keep the robot outside its motion area, and inspect the transfer position. Each stacker must contain only one compatible plate geometry. Measure plate height, stacking pitch, and spatula support thickness before moving plates. Resolve an active E-stop through the machine's normal procedure.\n", + "\n", + "## Connect\n", + "\n", + "`setup()` opens the socket without homing, clearing errors, or changing calibration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f40ff8cb", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.high_res import HighResMicroServe, MicroServePlateDimensions\n", + "\n", + "microserve = HighResMicroServe(host=\"10.253.253.253\")\n", + "await microserve.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "7e8f19f5", + "metadata": {}, + "source": [ + "## Identification\n", + "\n", + "Read the controller identification report." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a85419ed", + "metadata": {}, + "outputs": [], + "source": [ + "print(await microserve.request_version())" + ] + }, + { + "cell_type": "markdown", + "id": "bdba7428", + "metadata": {}, + "source": [ + "## Firmware version\n", + "\n", + "Read the machine-readable firmware version." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3133794a", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.request_firmware_version()" + ] + }, + { + "cell_type": "markdown", + "id": "07f7cf16", + "metadata": {}, + "source": [ + "## Status\n", + "\n", + "Inspect homing, selected stacker, loader state, and the plate-detection beam. `plate_sensor_blocked` reports the beam signal; it does not establish plate occupancy at the virtual transfer position. An unknown stacker before homing is `None`.\n", + "\n", + "Use `status.loader_retracted` and `status.loader_extended` to check the loader sensors. The raw `status.loader` field can stay `\"extended\"` after retraction on this firmware." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d93caba2", + "metadata": {}, + "outputs": [], + "source": [ + "status = await microserve.request_status()\n", + "status" + ] + }, + { + "cell_type": "markdown", + "id": "d58b8492", + "metadata": {}, + "source": [ + "## Readiness\n", + "\n", + "This reports readiness to present a plate to the robot. It is false while the loader is retracted, even when the machine is homed and idle. Use `request_status()` to check homing and busy state." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6cdadde", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.is_ready()" + ] + }, + { + "cell_type": "markdown", + "id": "bab2380e", + "metadata": {}, + "source": [ + "## Error log\n", + "\n", + "Read error entries without clearing them. Their numbers identify log entries, not stable fault codes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1074996a", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.request_errors()" + ] + }, + { + "cell_type": "markdown", + "id": "66e9bf60", + "metadata": {}, + "source": [ + "## Settings\n", + "\n", + "Read the current in-memory settings without changing or saving them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb911a0f", + "metadata": {}, + "outputs": [], + "source": [ + "settings = await microserve.request_settings()\n", + "settings[\"PRODUCT_NAME\"]" + ] + }, + { + "cell_type": "markdown", + "id": "77e9d6ca", + "metadata": {}, + "source": [ + "## Current plate geometry\n", + "\n", + "Read all stackers' configured dimensions in millimeters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c7c2344", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.request_dimensions()" + ] + }, + { + "cell_type": "markdown", + "id": "b65ad666", + "metadata": {}, + "source": [ + "## Cached plate counts\n", + "\n", + "These are approximate firmware counts. This query does not move the carousel or physically recount plates." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27bd6959", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.request_plate_counts()" + ] + }, + { + "cell_type": "markdown", + "id": "db2ddd93", + "metadata": {}, + "source": [ + "## Detailed hardware versions\n", + "\n", + "Read firmware checksums and motor-controller versions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9543a43", + "metadata": {}, + "outputs": [], + "source": [ + "print(await microserve.request_detailed_version())" + ] + }, + { + "cell_type": "markdown", + "id": "5f138678", + "metadata": {}, + "source": [ + "## Motor identities\n", + "\n", + "Read the serial number and configured name of each axis controller." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73fc3077", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.request_motor_information()" + ] + }, + { + "cell_type": "markdown", + "id": "3d27d522", + "metadata": {}, + "source": [ + "## Command history\n", + "\n", + "Read the latest three command history entries without repeating any command." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20e409bc", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.request_history(3)" + ] + }, + { + "cell_type": "markdown", + "id": "da5e8b62", + "metadata": {}, + "source": [ + "## Command completion record\n", + "\n", + "Query the last acknowledged command. IDs are only valid until the controller restarts." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d120428c", + "metadata": {}, + "outputs": [], + "source": [ + "command_id = microserve.last_command_id\n", + "assert command_id is not None\n", + "await microserve.request_command_status(command_id)" + ] + }, + { + "cell_type": "markdown", + "id": "82f9cc01", + "metadata": {}, + "source": [ + "## One stacker\n", + "\n", + "Read a single stacker's cached approximate count without measurement motion." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21c443e3", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.stackers[0].request_plate_count()" + ] + }, + { + "cell_type": "markdown", + "id": "128169c6", + "metadata": {}, + "source": [ + "## Home\n", + "\n", + "**Moves hardware.** Clear the robot and transfer position first. Homing is explicit. A repeated call checks status and does not home again when already homed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ae46eef", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.home()" + ] + }, + { + "cell_type": "markdown", + "id": "986766c0", + "metadata": {}, + "source": [ + "## Plate geometry\n", + "\n", + "Use measurements for the plates in this stacker. The values below are examples, not a plate-type recommendation:\n", + "\n", + "- `height`: bottom of plate to top.\n", + "- `stack_height`: bottom-to-bottom distance between adjacent stacked plates.\n", + "- `thickness`: top of plate to the underside of its wells where the spatula supports it.\n", + "\n", + "Each prepare or scan call receives geometry explicitly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a9975ea", + "metadata": {}, + "outputs": [], + "source": [ + "dimensions = MicroServePlateDimensions(height=11.0, stack_height=10.0, thickness=10.0)\n", + "stacker = microserve.stackers[0]" + ] + }, + { + "cell_type": "markdown", + "id": "1e75f102", + "metadata": {}, + "source": [ + "## Set dimensions\n", + "\n", + "Apply geometry for this stacker and verify it by reading it back. Preparation and scan methods also perform this step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a9db34e", + "metadata": {}, + "outputs": [], + "source": [ + "await stacker.set_dimensions(dimensions)" + ] + }, + { + "cell_type": "markdown", + "id": "09d37721", + "metadata": {}, + "source": [ + "## Rotate to a stacker\n", + "\n", + "**Moves hardware.** The loader must be retracted and the transfer position clear." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73b892f0", + "metadata": {}, + "outputs": [], + "source": [ + "await stacker.move_to()" + ] + }, + { + "cell_type": "markdown", + "id": "37c63007", + "metadata": {}, + "source": [ + "## Prepare to load a plate\n", + "\n", + "**Moves hardware.** Present an empty receiving position for the robot. This command does not move the robot. Repeating it in the same verified state does not move the loader again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9933015c", + "metadata": {}, + "outputs": [], + "source": [ + "await stacker.prepare_for_load(dimensions)" + ] + }, + { + "cell_type": "markdown", + "id": "16c9fc2c", + "metadata": {}, + "source": [ + "## Finish loading\n", + "\n", + "Have the robot place the plate at the taught transfer position, then withdraw completely. Only after that physical action, retract the mechanism." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8fb3e090", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.retract()" + ] + }, + { + "cell_type": "markdown", + "id": "09fa4f05", + "metadata": {}, + "source": [ + "## Prepare to unload a plate\n", + "\n", + "**Moves hardware.** Present a plate from this stacker for the robot to pick. The method confirms the command completed and the selected stacker and loader reached the expected state. A repeated preparation owned by this driver does not fetch another plate before retraction, even if the beam signal changes. Actual robot pickup geometry is unverified." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73b87251", + "metadata": {}, + "outputs": [], + "source": [ + "await stacker.prepare_for_unload(dimensions)" + ] + }, + { + "cell_type": "markdown", + "id": "60b9d894", + "metadata": {}, + "source": [ + "## Finish unloading\n", + "\n", + "Have the robot pick the presented plate and withdraw completely before running this cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cabb83aa", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.retract()" + ] + }, + { + "cell_type": "markdown", + "id": "b424b5d6", + "metadata": {}, + "source": [ + "## Scan stacker barcodes\n", + "\n", + "**Moves hardware.** The robot must be clear and the loader retracted. The returned tuple contains the controller's unmodified barcode data lines; barcode formatting has not yet been verified on this machine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ee7310f", + "metadata": {}, + "outputs": [], + "source": [ + "await stacker.scan_barcodes(dimensions)" + ] + }, + { + "cell_type": "markdown", + "id": "87456a6e", + "metadata": {}, + "source": [ + "## Failures and reconnecting\n", + "\n", + "`MicroServeError` preserves the command, identifier, completion status, and diagnostic lines for `ERROR`, `ABORTED`, and `WARNING` replies. All three interrupt the operation.\n", + "\n", + "A timeout, cancelled task, or malformed reply closes the connection. The driver never automatically repeats a command. Closing the socket does not stop motion already executing. Inspect the physical machine before reconnecting with `setup()` and reading status.\n", + "\n", + "An interrupted load/unload is retained in `unresolved_preparation`, blocking further preparation. The beam signal alone cannot identify which transfer completed. If the controller has not rebooted and no other client has moved it, `reconcile_preparation()` reads the original command record and current position to confirm success. Failed or unidentifiable commands stay unresolved. Explicit retraction ends a handoff only after the operator has inspected it and the robot is clear." + ] + }, + { + "cell_type": "markdown", + "id": "21464634", + "metadata": {}, + "source": [ + "## Inspect an interrupted preparation\n", + "\n", + "This is `None` when no load/unload is unresolved." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2eabb666", + "metadata": {}, + "outputs": [], + "source": [ + "microserve.unresolved_preparation" + ] + }, + { + "cell_type": "markdown", + "id": "9ef8e469", + "metadata": {}, + "source": [ + "## Reconcile without moving\n", + "\n", + "Use only after inspecting the machine and confirming it has not rebooted or been moved by another client. This reads the command record and loader state; it is a no-op when nothing is unresolved." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2da550eb", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.reconcile_preparation()" + ] + }, + { + "cell_type": "markdown", + "id": "bd3a1cbc", + "metadata": {}, + "source": [ + "## Disconnect\n", + "\n", + "Close the connection without homing, retracting, aborting, or clearing errors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c878e738", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.stop()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/user_guide/high_res/micro-serve/protocol.md b/docs/user_guide/high_res/micro-serve/protocol.md new file mode 100644 index 00000000000..ffcb03e6a3a --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/protocol.md @@ -0,0 +1,134 @@ +# MicroServe protocol and validation + +The implementation follows documentation served by the MicroServe itself: + +- [MicroServe API, revision 756, May 29, 2020](http://10.253.253.253/support_files/files/MicroServe_API.pdf). +- [Operation notes](http://10.253.253.253/) and the illustrated plate-dimension instructions. +- The `info all` and `help` replies from firmware 2.7.0.756. +- The [REST command catalog](http://10.253.253.253:9095/MicroServe/AvailableCommands) and + [commissioning page](http://10.253.253.253/commissioning.html). + +These links require a direct connection to the controller. The factory's API and firmware help +define the wire commands; no vendor DLL is needed to run the driver. + +## Framing + +TCP port 1000 accepts an ASCII command and arguments separated by spaces, terminated with LF. +API section 4 documents the following exchange, captured here from the controller: + +```text +version +ACK! version 2 +Product Name: MicroServe +Serial Number: HRB-2008-10558 +libcommon Version: 1.1.0.756 +libts7600 Version: 1.0.0.756 +Firmware Version: 2.7.0.756 +Firmware Build: A620467A +OK! version 2 +``` + +The ACK and completion must echo the entire command and the same identifier. Status data also +starts with `OK! status`, followed by comma-separated fields, and is **not** a completion frame. +The driver retains that data until the exact command-and-ID completion arrives. + +The documented completion statuses are `OK`, `ERROR`, `ABORTED`, and `WARNING`. Non-OK completions +raise `MicroServeError`, retaining the command, identifier, and diagnostic lines. The sequential +numbers in `Error : ...` messages are log entry numbers, not stable error codes. + +An incomplete exchange closes the transport. Commands are never automatically replayed. A socket +close does not stop an executing command. The command timeout applies to the entire completion +phase, rather than restarting on each data line. + +## Operation mapping + +| Python operation | Device command | Device documentation | Hardware validation | +| --- | --- | --- | --- | +| `request_status()` | `status` | API §5.23 | Read-only query verified | +| `request_version()` | `version` | API §5.27 | Read-only query verified | +| `request_firmware_version()` | `firmwareversion` | API §5.8 | Read-only query verified | +| `request_detailed_version()` | `detailedversion` | API §5.4 | Read-only query verified | +| `request_motor_information()` | `copleyserial` | Firmware help | Read-only query verified | +| `request_command_status(id)` | `commandstat id` | API §5.3; firmware spelling | Read-only query verified | +| `request_history(count)` | `history count` | Firmware help | Read-only query verified | +| `request_errors(count)` | `errors count` | API §5.7 | Read-only query verified | +| `request_settings(search)` | `settings [search]` | Firmware help | Read-only query verified | +| `is_ready()` | `microserveready` | API §5.16 | False when retracted, true at the empty receiving position | +| `request_plate_counts()` | `getplatecounts` | Firmware help | Read-only query verified | +| `stackers[i].request_plate_count()` | `getplatecounts i` | Firmware help | Read-only query verified | +| `request_dimensions()` | `dimstatus` | API §5.5 | Read-only query verified | +| `home()` | `home` | API §5.11; operation notes for manual mode | Homing verified; manual-mode recovery simulated | +| `stackers[i].move_to()` | `spin i` | API §5.22 | All fourteen positions and repeated targets verified | +| `stackers[i].set_dimensions(d)` | `setstackerdimensions i PH SH PT` | API §5.21 | Existing geometry written and read back at stacker 2 | +| `stackers[i].prepare_for_load(d)` | geometry, then `load i` | API §5.14, §5.21; commissioning page | Empty stacker 2 and repeated preparation verified | +| `stackers[i].prepare_for_unload(d)` | geometry, then `unload i` | API §5.24, §5.21; commissioning page | Simulated only | +| `retract()` | `retract` | API §5.17 | Empty loader and repeated retraction verified using home sensors | +| `stackers[i].scan_barcodes(d)` | geometry, then `readbarcodestacker i` | Firmware help; operation notes | Simulated only; returns raw data lines | +| `reconcile_preparation()` | `commandstat id`, `status` | API §5.3, §5.23 | Query primitives verified; recovery scenario simulated | + +Stacker indices are 0–13. Geometry is provided in millimeters and encoded as whole micrometers in +the order plate height, stack pitch, plate thickness. Height includes the lid when plates are lidded. +Cached counts do not measure the physical stack. `countplates`, `measurestacker`, and barcode scans +perform motion and must not be included in a read-only verification run. + +The public API does not expose relative jogs, toggles, calibration changes, or arbitrary commands. +The firmware also advertises maintenance operations; listing them does not establish a validated +physical sequence or calibrated travel limits for the Python driver. + +## Transfer state + +The operation notes describe a **virtual transfer position**, determined by plate geometry and a +plate-detection beam. `MicroServeStatus.plate_sensor_blocked` is that beam's signal. It is not a +reliable inventory count or a guarantee that a plate occupies the robot's transfer position. +The beam was blocked at the prepared receiving position even with no plates loaded. + +Firmware 2.7.0.756 leaves `loaderStatus Extended` set after successful `retract`, even when both +loader axes report zero, their home sensors are on, and the stacker lock is released. The stale +field persists through later queries and carousel rotation. `MicroServeStatus.loader` preserves +that firmware field. `loader_retracted` checks both home sensors and the released lock; +`loader_extended` requires the extended field, both home sensors off, and the lock engaged. +Motion guards use these sensor checks, and a status missing any required sensor is rejected. + +`is_ready()` reports readiness to present a plate to the robot, as described in API §5.16. It +returned false while the machine was homed, idle, and retracted, then true after preparing the +empty receiving position, and false again after retraction. Use `request_status()` for general +homing and busy state; a false `is_ready()` alone does not indicate a fault. + +The driver records a confirmed load/unload preparation and verifies the selected stacker and +extended loader. Repeating the same preparation before retraction does not move another plate, +even if the beam changes when the robot acts. An already-extended loader without a preparation +owned by this driver is not assumed to represent the requested operation. Plate occupancy at the +robot interface remains the application's responsibility until physical transfer validation. + +After an interrupted preparation, `unresolved_preparation` retains its direction, stacker, and ACK +identifier. Another preparation is blocked. If the controller has not rebooted and another client +has not moved it, `reconcile_preparation()` can confirm the original command completed successfully +and the loader is in the expected position. It does not resend the command. A failed, running, +missing, or mismatched record stays unresolved. Explicit retraction ends the handoff after the +operator has inspected the machine and the robot is clear. + +Only this driver should control the MicroServe during a handoff. Command IDs reset on controller +restart. Reconnecting does not home, clear an abort, or infer success from the beam signal. + +The controller exposes axis jogs, but its documentation does not specify the complete sensor-driven +transfer sequence needed to reconstruct load/unload from those jogs. The driver uses the documented +load/unload operations for that sequence, while managing geometry, command ownership, state checks, +and reconciliation in Python. + +## Verification limits + +Queries and operator-approved empty-machine motion were captured on MicroServe HRB-2008-10558, +firmware 2.7.0.756, on September 8, 2026. Hardware checks covered homing, all fourteen carousel +positions, repeated targets, reconnecting, preparation of empty stacker 2, retraction, and rotation +after retraction. Existing geometry (11 mm height, 10 mm stack pitch, 10 mm thickness) was written +and read back; geometry was unchanged at the end. The controller reported no errors. + +Exact transport exchanges are replayed through `SocketValidator`, including the stale loader flag +that caused the initial retraction check to fail. The captures verify that repeated target, +preparation, and retraction requests do not send additional motion commands. Simulated tests cover +timeout, cancellation, reply mismatch, unresolved preparation, and sensor disagreement. + +**Plate transfer, unloading, barcode scans, and physical fault recovery remain unverified.** Setup +warns about these limits. Empty-machine tests do not establish pickup height with a plate, gripper +clearance, plate compatibility, or E-stop recovery. Those need a separate physical validation with +known plates and robot clearance. diff --git a/pylabrobot/high_res/__init__.py b/pylabrobot/high_res/__init__.py index fc28cb0882f..e2d13e16e99 100644 --- a/pylabrobot/high_res/__init__.py +++ b/pylabrobot/high_res/__init__.py @@ -3,6 +3,17 @@ HighResLidValetError, LidValetState, ) +from pylabrobot.high_res.micro_serve import ( + HighResMicroServe, + MicroServeCommandStatus, + MicroServeError, + MicroServeMotorInfo, + MicroServePlateDimensions, + MicroServeProtocolError, + MicroServeStacker, + MicroServeStatus, + MicroServeUnresolvedPreparation, +) from pylabrobot.high_res.settings import HighResLidValetSettings __all__ = [ @@ -10,4 +21,13 @@ "HighResLidValetError", "HighResLidValetSettings", "LidValetState", + "HighResMicroServe", + "MicroServeCommandStatus", + "MicroServeError", + "MicroServeMotorInfo", + "MicroServePlateDimensions", + "MicroServeProtocolError", + "MicroServeStacker", + "MicroServeStatus", + "MicroServeUnresolvedPreparation", ] diff --git a/pylabrobot/high_res/micro_serve.py b/pylabrobot/high_res/micro_serve.py new file mode 100644 index 00000000000..a4336d7fca0 --- /dev/null +++ b/pylabrobot/high_res/micro_serve.py @@ -0,0 +1,664 @@ +"""HighRes MicroServe control over TCP (MicroServe API, revision 756).""" + +import asyncio +import logging +import math +import re +from dataclasses import dataclass +from typing import Dict, List, Literal, Optional, Tuple + +from pylabrobot.io import Socket + +logger = logging.getLogger(__name__) + + +class MicroServeError(Exception): + """A completed command reported an error, abort, or warning. + + ``lines`` preserves the controller's diagnostic text. Error numbers in that + text are sequential log entry numbers, not stable error codes. + """ + + def __init__(self, command: str, command_id: int, status: str, lines: Tuple[str, ...]) -> None: + """Record the command identity, completion status, and diagnostic lines.""" + self.command = command + self.command_id = command_id + self.status = status + self.lines = lines + super().__init__(f"{status} {command} {command_id}: " + "\n".join(lines)) + + +class MicroServeProtocolError(ConnectionError): + """The reply cannot be associated with the outstanding command.""" + + +@dataclass(frozen=True) +class MicroServePlateDimensions: + """Plate geometry in millimeters. + + ``height`` is bottom to top; ``stack_height`` is the bottom-to-bottom pitch + of stacked plates; ``thickness`` is the distance from the plate top to the + underside of the wells where the spatula supports the plate. + """ + + height: float + stack_height: float + thickness: float + + def __post_init__(self) -> None: + """Reject dimensions that cannot be represented as positive micrometers.""" + for value in (self.height, self.stack_height, self.thickness): + if not math.isfinite(value) or value <= 0 or round(value * 1000) < 1: + raise ValueError("Plate dimensions must be finite and at least 0.001 mm") + if self.thickness > self.height: + raise ValueError("Plate thickness cannot exceed plate height") + + def _wire(self) -> str: + """Encode dimensions in the order and units required by the controller.""" + return " ".join(str(round(v * 1000)) for v in (self.height, self.stack_height, self.thickness)) + + +@dataclass(frozen=True) +class MicroServeStatus: + """Controller state; ``raw`` retains device-native axis positions. + + ``plate_sensor_blocked`` is the detection beam signal, not an inventory or + transfer-position occupancy guarantee. The loader moves plates relative to + this beam to determine their height. + + ``loader`` retains the firmware's loaderStatus field, which can remain + extended after retraction. Use ``loader_retracted`` to check the loader's + home sensors and the released stacker lock. + """ + + homed: bool + stacker: Optional[int] + busy: bool + loader: Literal["extended", "retracted"] + mode: str + plate_sensor_blocked: bool + flags: Dict[str, bool] + raw: str + + @property + def loader_retracted(self) -> bool: + """Both loader axes are at their home sensors and the stacker lock is released.""" + return ( + self.flags["spatulaHome"] and self.flags["effectuatorHome"] and not self.flags["lockSensor"] + ) + + @property + def loader_extended(self) -> bool: + """Firmware reports extension with both axes away from home and the lock engaged.""" + return ( + self.loader == "extended" + and not self.flags["spatulaHome"] + and not self.flags["effectuatorHome"] + and self.flags["lockSensor"] + ) + + +@dataclass(frozen=True) +class MicroServeCommandStatus: + """A command's firmware record, retained until the controller restarts.""" + + command_id: int + state: Literal["ok", "running", "fail", "warning", "aborted"] + command: str + + +@dataclass(frozen=True) +class MicroServeMotorInfo: + """Motor-controller serial number and configured name, read without motion.""" + + serial_number: str + name: str + + +@dataclass(frozen=True) +class MicroServeUnresolvedPreparation: + """A load/unload whose completion or final position could not be confirmed.""" + + direction: Literal["load", "unload"] + stacker: int + command_id: Optional[int] + + +def _parse_status(lines: Tuple[str, ...]) -> MicroServeStatus: + """Parse status data without confusing its OK! prefix with completion.""" + raw = " ".join(lines) + fields = re.search( + r"^OK! status (homed|nothomed), stackerNumber (-?\d+), .*?" + r"machineState (NotBusy|Busy), loaderStatus (Extended|Retracted), mode (\w+)$", + raw, + ) + if fields is None: + raise MicroServeProtocolError(f"Unrecognized MicroServe status: {raw!r}") + flags = {name: state == "ON" for name, state in re.findall(r"(\w+) (ON|OFF)\b", raw)} + if not {"plateSensor", "spatulaHome", "effectuatorHome", "lockSensor"} <= flags.keys(): + raise MicroServeProtocolError(f"Status is missing a loader or plate sensor: {raw!r}") + stacker = int(fields[2]) + if not -1 <= stacker < 14: + raise MicroServeProtocolError(f"Invalid stacker in status: {stacker}") + return MicroServeStatus( + homed=fields[1] == "homed", + stacker=None if stacker == -1 else stacker, + busy=fields[3] == "Busy", + loader="extended" if fields[4] == "Extended" else "retracted", + mode=fields[5].lower(), + plate_sensor_blocked=flags["plateSensor"], + flags=flags, + raw=raw, + ) + + +class HighResMicroServe: + """MicroServe with fourteen zero-based stackers and a shared transfer position. + + Connect to TCP port 1000. On a dedicated Ethernet link the controller also + answers at ``10.253.253.253``. ``setup()`` only opens the connection; homing + and all other motion are explicit operations. Use ``stackers[0]`` through + ``stackers[13]`` to prepare plates for a separate robot to place or pick. + + Only one operation runs at a time. Commands are never retried automatically. + After a timeout, cancellation, or broken reply, the connection is closed: + call ``setup()`` and inspect the machine before deciding how to recover. + Closing a connection does not abort an operation already executing. + + An incomplete load/unload remains in ``unresolved_preparation``. It cannot + be repeated until its result is reconciled or the handoff explicitly ended + by retraction. The plate-detection beam is not used to infer occupancy. + + Communication, homing, all carousel positions, and empty receiving-position + preparation/retraction were checked on firmware 2.7.0.756. Transfers with + plates, barcode scanning, and physical fault recovery remain unverified. + """ + + def __init__( + self, + host: str, + port: int = 1000, + ack_timeout: float = 10.0, + command_timeout: float = 120.0, + scan_timeout: float = 300.0, + ) -> None: + """Configure the connection and timeouts in seconds without opening it.""" + for timeout in (ack_timeout, command_timeout, scan_timeout): + if not math.isfinite(timeout) or timeout <= 0: + raise ValueError("Timeouts must be finite and positive") + self.ack_timeout = ack_timeout + self.command_timeout = command_timeout + self.scan_timeout = scan_timeout + self.io = Socket( + human_readable_device_name="HighRes MicroServe", + host=host, + port=port, + read_timeout=ack_timeout, + write_timeout=ack_timeout, + ) + self._lock = asyncio.Lock() + self._connected = False + self._last_command_id: Optional[int] = None + self._prepared: Optional[Tuple[str, int]] = None + self._unresolved_preparation: Optional[MicroServeUnresolvedPreparation] = None + self.stackers = tuple(MicroServeStacker(self, index) for index in range(14)) + + @property + def last_command_id(self) -> Optional[int]: + """ID from the latest ACK, or None if the latest exchange received no ACK.""" + return self._last_command_id + + @property + def unresolved_preparation(self) -> Optional[MicroServeUnresolvedPreparation]: + """The load/unload that requires reconciliation before another preparation.""" + return self._unresolved_preparation + + async def setup(self) -> None: + """Connect without homing, clearing faults, or changing settings.""" + async with self._lock: + if self._connected: + return + try: + await asyncio.wait_for(self.io.setup(), timeout=self.ack_timeout) + except BaseException: + await self._close() + raise + self._connected = True + logger.info("Connected to MicroServe") + logger.warning( + "MicroServe transfers with plates, barcode scanning, and physical fault recovery " + "are unverified; validate before use" + ) + + async def stop(self) -> None: + """Close the connection without moving the device or clearing its state.""" + async with self._lock: + await self._close() + + async def _close(self) -> None: + """Invalidate the session and close its transport.""" + self._connected = False + self._prepared = None + await self.io.stop() + + async def _command(self, command: str, timeout: Optional[float] = None) -> Tuple[str, ...]: + """Exchange one command while the caller holds the operation lock. + + Completion must echo the entire command and its ACK identifier. In + particular, ``OK! status homed, ...`` is data, not a completion frame. + """ + if not self._connected: + raise RuntimeError("MicroServe is not connected; call setup() first") + if not command or any(c in command for c in "\r\n\x00"): + raise ValueError("A command must be one nonempty ASCII line") + payload = command.encode("ascii") + b"\n" + self._last_command_id = None + completion_timeout = self.command_timeout if timeout is None else timeout + loop = asyncio.get_running_loop() + lines: List[str] = [] + command_id: Optional[int] = None + deadline = loop.time() + self.ack_timeout + size = 0 + try: + await self.io.write(payload, timeout=self.ack_timeout) + while True: + remaining = deadline - loop.time() + if remaining <= 0: + raise TimeoutError(f"Timed out waiting for {command!r} (id {command_id})") + data = await self.io.readline(timeout=remaining) + if not data: + raise ConnectionError(f"MicroServe disconnected during {command!r}") + size += len(data) + if not data.endswith(b"\n") or size > 4 * 1024 * 1024: + raise MicroServeProtocolError("Incomplete or oversized MicroServe reply") + line = data.decode("ascii").rstrip("\r\n") + if not line: + continue + if command_id is None: + ack = re.fullmatch(r"ACK! " + re.escape(command) + r" (\d+)", line) + if ack is None: + raise MicroServeProtocolError(f"Expected ACK for {command!r}, received {line!r}") + command_id = int(ack[1]) + self._last_command_id = command_id + deadline = loop.time() + completion_timeout + continue + frame = re.fullmatch(r"(OK|ERROR|ABORTED|WARNING|ACK)! (.+) (\d+)", line) + if frame is not None: + if frame[2] != command or int(frame[3]) != command_id or frame[1] == "ACK": + raise MicroServeProtocolError(f"Unexpected command frame: {line!r}") + if frame[1] != "OK": + raise MicroServeError(command, command_id, frame[1], tuple(lines)) + return tuple(lines) + lines.append(line) + except MicroServeError: + logger.error("MicroServe command failed: %s", command, exc_info=True) + raise + except BaseException: + # Cancellation or a partial exchange leaves command ownership uncertain. + logger.warning("Closing MicroServe connection after incomplete command %s", command) + try: + await self._close() + except BaseException: + logger.exception("Failed to close invalid MicroServe connection") + raise + + async def _status(self) -> MicroServeStatus: + """Read the full status while the caller holds the operation lock.""" + return _parse_status(await self._command("status")) + + async def request_status(self) -> MicroServeStatus: + """Read homing, carousel, loader, and sensor state without motion.""" + async with self._lock: + return await self._status() + + async def request_version(self) -> str: + """Return the human-readable controller identification report.""" + async with self._lock: + return "\n".join(await self._command("version")) + + async def request_firmware_version(self) -> str: + """Return the machine-readable firmware version.""" + async with self._lock: + return "\n".join(await self._command("firmwareversion")) + + async def request_detailed_version(self) -> str: + """Read firmware checksums and versions of the individual motor controllers.""" + async with self._lock: + return "\n".join(await self._command("detailedversion")) + + async def request_motor_information(self) -> Dict[str, MicroServeMotorInfo]: + """Read the carousel, spatula, effectuator, and barcode motor identities.""" + async with self._lock: + motors = {} + for line in await self._command("copleyserial"): + match = re.fullmatch(r"([^:]+):\s*(\d+)\s*:\s*(.+)", line) + if match is None or match[1].strip() in motors: + raise MicroServeProtocolError(f"Invalid motor identity: {line!r}") + motors[match[1].strip()] = MicroServeMotorInfo(match[2], match[3].strip()) + return motors + + async def _command_status(self, command_id: int) -> MicroServeCommandStatus: + """Read one command record while holding the operation lock.""" + lines = await self._command(f"commandstat {command_id}") + if len(lines) != 1: + raise MicroServeProtocolError(f"Invalid command status: {lines!r}") + match = re.fullmatch(r"\s*(\d+) (OK|RUNNING|FAIL|WARNING|ABORTED)!\s+- (.+)", lines[0]) + if match is None or int(match[1]) != command_id: + raise MicroServeProtocolError(f"Invalid command status: {lines[0]!r}") + states: Dict[str, Literal["ok", "running", "fail", "warning", "aborted"]] = { + "OK": "ok", + "RUNNING": "running", + "FAIL": "fail", + "WARNING": "warning", + "ABORTED": "aborted", + } + return MicroServeCommandStatus(command_id, states[match[2]], match[3]) + + async def request_command_status(self, command_id: int) -> MicroServeCommandStatus: + """Query a command's completion; this does not repeat the command. + + IDs are only meaningful within the current controller boot. Do not use a + saved identifier to infer a result after the MicroServe has restarted. + """ + self._validate_count(command_id, "command_id") + async with self._lock: + return await self._command_status(command_id) + + @staticmethod + def _validate_count(value: int, name: str) -> None: + """Require an actual positive integer for query counts and identifiers.""" + if isinstance(value, bool) or not isinstance(value, int) or value < 1: + raise ValueError(f"{name} must be a positive integer") + + async def request_history(self, count: int = 10) -> Tuple[str, ...]: + """Read recent command history as human-readable lines.""" + self._validate_count(count, "count") + async with self._lock: + return await self._command(f"history {count}") + + async def request_errors(self, count: int = 10) -> Tuple[str, ...]: + """Read up to ``count`` error log entries without clearing them.""" + self._validate_count(count, "count") + async with self._lock: + return await self._command(f"errors {count}") + + async def request_settings(self, search: Optional[str] = None) -> Dict[str, str]: + """Read in-memory settings, optionally filtering names with ``search``. + + Network settings can differ from the web configuration. This query neither + changes settings nor applies defaults from the web settings descriptions. + """ + if search is not None and re.fullmatch(r"[A-Za-z0-9_]+", search) is None: + raise ValueError("search must contain only ASCII letters, numbers, and underscores") + async with self._lock: + lines = await self._command("settings" if search is None else f"settings {search}") + settings = {} + for line in lines: + key, separator, value = line.partition("=") + if not separator or not key.strip(): + raise MicroServeProtocolError(f"Invalid setting: {line!r}") + settings[key.strip()] = value.strip() + return settings + + async def is_ready(self) -> bool: + """Report readiness to present a plate to the robot, not general machine idleness. + + Firmware returns false for an idle retracted loader and true at a prepared + transfer position. The API also describes this flag for E-stop recovery. + """ + async with self._lock: + reply = await self._command("microserveready") + if reply not in (("0",), ("1",)): + raise MicroServeProtocolError(f"Invalid readiness reply: {reply!r}") + return reply == ("1",) + + async def request_plate_counts(self) -> Dict[int, int]: + """Read cached approximate counts; this does not physically count plates.""" + async with self._lock: + counts = {} + for line in await self._command("getplatecounts"): + match = re.fullmatch(r"(\d+): (\d+)", line) + if match is None or int(match[1]) in counts or not 0 <= int(match[1]) < 14: + raise MicroServeProtocolError(f"Invalid plate count: {line!r}") + counts[int(match[1])] = int(match[2]) + if set(counts) != set(range(14)): + raise MicroServeProtocolError("Plate count reply must contain all fourteen stackers") + return counts + + async def _dimensions(self) -> Dict[int, MicroServePlateDimensions]: + """Read dimensions while the caller holds the operation lock.""" + dimensions = {} + for line in await self._command("dimstatus"): + match = re.fullmatch( + r"Stacker (\d+): PlateHeight (\d+), StackHeight (\d+), PlateThickness (\d+)", line + ) + if match is None or int(match[1]) in dimensions or not 0 <= int(match[1]) < 14: + raise MicroServeProtocolError(f"Invalid dimension reply: {line!r}") + dimensions[int(match[1])] = MicroServePlateDimensions( + int(match[2]) / 1000, int(match[3]) / 1000, int(match[4]) / 1000 + ) + if set(dimensions) != set(range(14)): + raise MicroServeProtocolError("Dimension reply must contain all fourteen stackers") + return dimensions + + async def request_dimensions(self) -> Dict[int, MicroServePlateDimensions]: + """Read the configured plate dimensions in millimeters for every stacker.""" + async with self._lock: + return await self._dimensions() + + @staticmethod + def _require_idle(status: MicroServeStatus, homed: bool = True) -> None: + """Reject motion in an unknown, busy, or inappropriate operating state.""" + if status.busy or status.mode != "ready" or (homed and not status.homed): + raise RuntimeError(f"MicroServe is not ready for this operation: {status.raw}") + + def _require_resolved_preparation(self) -> None: + """Block new motion when the previous transfer preparation is uncertain.""" + if self._unresolved_preparation is not None: + raise RuntimeError( + "A transfer preparation is unresolved. Inspect the machine and call " + "reconcile_preparation(), or explicitly retract to end the handoff." + ) + + async def reconcile_preparation(self) -> None: + """Confirm an interrupted preparation using read-only command and status queries. + + Call only if the controller has not rebooted and no other client has moved + it. A successful command record plus the expected extended stacker restores + the prepared state. Failed, running, or unidentifiable commands remain + unresolved. This method never retries a command or moves the machine. + """ + async with self._lock: + pending = self._unresolved_preparation + if pending is None: + return + if pending.command_id is None: + raise RuntimeError("No ACK identifier was received; inspect the machine before recovery") + record = await self._command_status(pending.command_id) + if record.command != f"{pending.direction} {pending.stacker}" or record.state != "ok": + raise RuntimeError(f"Preparation is not confirmed complete: {record}") + status = await self._status() + self._require_idle(status) + if status.stacker != pending.stacker or not status.loader_extended: + raise RuntimeError(f"Prepared transfer position cannot be confirmed: {status.raw}") + self._prepared = (pending.direction, pending.stacker) + self._unresolved_preparation = None + logger.info("Reconciled MicroServe preparation %s", record.command) + + async def home(self) -> None: + """Home the machine if needed, then verify it is homed and idle. + + Remove plates from the transfer position and keep the robot clear first. + """ + async with self._lock: + self._require_resolved_preparation() + status = await self._status() + if status.busy or status.mode not in ("ready", "manual"): + raise RuntimeError(f"MicroServe cannot home in this state: {status.raw}") + if status.homed and status.mode == "ready": + return + if status.plate_sensor_blocked: + raise RuntimeError("Clear the plate-detection beam before homing") + logger.info("Homing MicroServe") + await self._command("home") + self._require_idle(await self._status()) + self._prepared = None + + async def retract(self) -> None: + """Retract the spatula and effectuator, then verify the loader is retracted. + + Call after the robot finishes its transfer and clears the device. Retraction + can move a plate placed by the robot; the caller must finish the handoff first. + Successful retraction ends any recorded or unresolved preparation. + """ + async with self._lock: + status = await self._status() + self._require_idle(status) + if status.loader_retracted: + self._prepared = None + self._unresolved_preparation = None + return + logger.info("Retracting MicroServe loader") + await self._command("retract") + status = await self._status() + self._require_idle(status) + if not status.loader_retracted: + raise RuntimeError(f"Loader did not retract: {status.raw}") + self._prepared = None + self._unresolved_preparation = None + + +class MicroServeStacker: + """One of the fourteen stackers, accessed through ``device.stackers[index]``. + + Stacker indices are zero-based. Preparation methods position a plate or an + empty receiving location for an external robot; they do not operate the robot. + """ + + def __init__(self, device: HighResMicroServe, index: int) -> None: + """Associate this stacker with its controller and zero-based index.""" + if isinstance(index, bool) or not isinstance(index, int) or not 0 <= index < 14: + raise ValueError("Stacker index must be an integer from 0 through 13") + self._device = device + self._index = index + + @property + def index(self) -> int: + """This stacker's fixed zero-based controller index.""" + return self._index + + async def request_plate_count(self) -> int: + """Read this stacker's cached approximate count without physical measurement.""" + async with self._device._lock: + lines = await self._device._command(f"getplatecounts {self.index}") + if len(lines) != 1: + raise MicroServeProtocolError(f"Invalid plate count reply: {lines!r}") + match = re.fullmatch(rf"{self.index}: (\d+)", lines[0]) + if match is None: + raise MicroServeProtocolError(f"Invalid plate count reply: {lines!r}") + return int(match[1]) + + async def _set_dimensions(self, dimensions: MicroServePlateDimensions) -> None: + """Apply and verify geometry while holding the device's operation lock.""" + await self._device._command(f"setstackerdimensions {self.index} {dimensions._wire()}") + actual = (await self._device._dimensions())[self.index] + if actual._wire() != dimensions._wire(): + raise MicroServeProtocolError(f"Stacker {self.index} did not accept the plate dimensions") + + async def set_dimensions(self, dimensions: MicroServePlateDimensions) -> None: + """Set this stacker's plate geometry in millimeters, then read it back.""" + async with self._device._lock: + self._device._require_resolved_preparation() + status = await self._device._status() + self._device._require_idle(status, homed=False) + if not status.loader_retracted: + raise RuntimeError("Retract before changing plate geometry") + await self._set_dimensions(dimensions) + + async def move_to(self) -> None: + """Rotate the retracted carousel to this stacker, if it is not already there.""" + async with self._device._lock: + self._device._require_resolved_preparation() + status = await self._device._status() + self._device._require_idle(status) + if not status.loader_retracted or status.plate_sensor_blocked: + raise RuntimeError("Clear the transfer position and retract before rotating the carousel") + if status.stacker == self.index: + return + logger.info("Rotating MicroServe to stacker %d", self.index) + await self._device._command(f"spin {self.index}") + status = await self._device._status() + self._device._require_idle(status) + if status.stacker != self.index or not status.loader_retracted: + raise RuntimeError(f"Carousel did not reach stacker {self.index}: {status.raw}") + + async def _prepare( + self, direction: Literal["load", "unload"], dimensions: MicroServePlateDimensions + ) -> None: + """Apply geometry and reach the requested transfer state without blind retries.""" + async with self._device._lock: + self._device._require_resolved_preparation() + status = await self._device._status() + self._device._require_idle(status) + at_target = status.stacker == self.index and status.loader_extended + if at_target and self._device._prepared == (direction, self.index): + if (await self._device._dimensions())[self.index]._wire() != dimensions._wire(): + raise RuntimeError("Retract the loader before changing geometry at the transfer position") + return + if status.plate_sensor_blocked or not status.loader_retracted: + raise RuntimeError("Finish the current robot handoff and retract before preparing another") + await self._set_dimensions(dimensions) + logger.info("Preparing MicroServe stacker %d for %s", self.index, direction) + command_id = None + self._device._prepared = None + try: + await self._device._command(f"{direction} {self.index}") + command_id = self._device.last_command_id + status = await self._device._status() + self._device._require_idle(status) + if status.stacker != self.index or not status.loader_extended: + raise RuntimeError(f"Requested transfer state was not reached: {status.raw}") + except BaseException: + self._device._unresolved_preparation = MicroServeUnresolvedPreparation( + direction, + self.index, + command_id if command_id is not None else self._device.last_command_id, + ) + raise + self._device._prepared = (direction, self.index) + + async def prepare_for_load(self, dimensions: MicroServePlateDimensions) -> None: + """Present an empty receiving position for the robot to place a plate. + + Repeating this call in the same verified transfer state does not move the + device again. After placing the plate, clear the robot and call ``retract()``. + """ + await self._prepare("load", dimensions) + + async def prepare_for_unload(self, dimensions: MicroServePlateDimensions) -> None: + """Present a plate from this stacker for the robot to pick. + + A confirmed preparation is not repeated before retraction, even if the + robot has already removed the plate. After the robot clears the device, + call ``retract()`` to end the handoff. + """ + await self._prepare("unload", dimensions) + + async def scan_barcodes(self, dimensions: MicroServePlateDimensions) -> Tuple[str, ...]: + """Move the barcode reader and return the controller's unmodified data lines. + + Barcode output formatting is firmware-dependent. Geometry is applied before + scanning; the robot must be clear and the loader retracted. This operation + moves hardware even though its purpose is reading barcodes. + """ + async with self._device._lock: + self._device._require_resolved_preparation() + status = await self._device._status() + self._device._require_idle(status) + if not status.loader_retracted or status.plate_sensor_blocked: + raise RuntimeError("Clear the transfer position and retract before scanning barcodes") + await self._set_dimensions(dimensions) + logger.info("Scanning MicroServe stacker %d", self.index) + return await self._device._command( + f"readbarcodestacker {self.index}", timeout=self._device.scan_timeout + ) diff --git a/pylabrobot/high_res/micro_serve_carousel_capture.json b/pylabrobot/high_res/micro_serve_carousel_capture.json new file mode 100644 index 00000000000..29073420857 --- /dev/null +++ b/pylabrobot/high_res/micro_serve_carousel_capture.json @@ -0,0 +1,2435 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e2038350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e2038350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732038360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732038360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732038370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732038370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732038380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732038380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d7374617475732038390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d7374617475732038390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732039300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732039300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e20332039310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e20332039310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732039320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732039320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732039330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732039330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732039340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732039340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e747320332039350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "333a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e747320332039350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732039360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732039360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e20342039370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e20342039370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732039380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732039380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732039390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732039390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203130300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203130300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732034203130310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "343a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732034203130310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203130320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203130320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2035203130330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2035203130330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203130340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203130340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203130350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203130350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203130360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203130360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203130370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203130370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203130380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203130380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2036203130390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2036203130390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203131300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220362c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203131300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203131310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220362c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203131310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203131320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220362c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203131320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732036203131330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "363a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732036203131330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203131340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220362c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203131340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2037203131350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2037203131350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203131360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220372c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203131360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203131370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220372c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203131370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203131380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220372c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203131380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732037203131390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "373a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732037203131390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203132300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220372c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203132300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2038203132310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2038203132310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203132320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220382c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203132320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203132330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220382c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203132330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203132340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220382c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203132340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732038203132350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "383a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732038203132350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203132360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220382c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203132360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2039203132370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2039203132370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203132380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220392c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203132380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203132390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220392c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203132390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203133300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220392c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203133300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732039203133310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "393a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732039203133310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203133320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220392c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203133320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e2031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e203130203133330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e203130203133330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203133340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203133340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203133350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203133350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203133360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203133360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e74732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e7473203130203133370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e7473203130203133370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203133380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203133380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e2031310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e203131203133390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e203131203133390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203134300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203134300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203134310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203134310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203134320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203134320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e74732031310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e7473203131203134330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31313a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e7473203131203134330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203134340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203134340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e2031320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e203132203134350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e203132203134350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203134360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203134360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203134370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203134370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203134380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203134380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e74732031320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e7473203132203134390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31323a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e7473203132203134390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203135300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203135300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e2031330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e203133203135310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e203133203135310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203135320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203135320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203135330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203135330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203135340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203135340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e74732031330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e7473203133203135350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31333a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e7473203133203135350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203135360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203135360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2030203135370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2030203135370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203135380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203135380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203135390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203135390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203136300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203136300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732030203136310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732030203136310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203136320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203136320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2031203136330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2031203136330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203136340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203136340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203136350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203136350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203136360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203136360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732031203136370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "313a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732031203136370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203136380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203136380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2032203136390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2032203136390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203137300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203137300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203137310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203137310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203137320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203137320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732032203137330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "323a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732032203137330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d6963726f736572766572656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d6963726f73657276657265616479203137340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d6963726f73657276657265616479203137340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e74730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e7473203137350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "313a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "323a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "333a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "343a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "363a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "373a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "383a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "393a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31313a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31323a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31333a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e7473203137350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203137360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203137360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203137370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203137370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203137380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203137380a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve_empty_cycle_capture.json b/pylabrobot/high_res/micro_serve_empty_cycle_capture.json new file mode 100644 index 00000000000..e282c7c4f84 --- /dev/null +++ b/pylabrobot/high_res/micro_serve_empty_cycle_capture.json @@ -0,0 +1,1127 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203230310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203230310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203230320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203230320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203230330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203230330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203230340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203230340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203230350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203230350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320322031313030302031303030302031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732032203131303030203130303030203130303030203230360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732032203131303030203130303030203130303030203230360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203230370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203230370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203230380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203230380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320322031313030302031303030302031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732032203131303030203130303030203130303030203230390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732032203131303030203130303030203130303030203230390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203231300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203231300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6c6f616420320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206c6f61642032203231310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206c6f61642032203231310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203231320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3939333130332c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203231320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203231330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3939333130332c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203231330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d6963726f736572766572656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d6963726f73657276657265616479203231340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d6963726f73657276657265616479203231340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203231350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3939333130332c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203231350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203231360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203231360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203231370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3939333130332c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203231370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203231380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203231380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203231390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203231390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203232300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203232300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203232310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203232310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d6963726f736572766572656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d6963726f73657276657265616479203232320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d6963726f73657276657265616479203232320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203232330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203232330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2033203232340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2033203232340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203232350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203232350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203232360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203232360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2032203232370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2032203232370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203232380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203232380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203232390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203232390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203233300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203233300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203233310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203233310a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve_empty_load_capture.json b/pylabrobot/high_res/micro_serve_empty_load_capture.json new file mode 100644 index 00000000000..e1a9d7434a9 --- /dev/null +++ b/pylabrobot/high_res/micro_serve_empty_load_capture.json @@ -0,0 +1,659 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203137390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203137390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203138300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203138300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203138310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203138310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d6963726f736572766572656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d6963726f73657276657265616479203138320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d6963726f73657276657265616479203138320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203138330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203138330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320322031313030302031303030302031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732032203131303030203130303030203130303030203138340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732032203131303030203130303030203130303030203138340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203138350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203138350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6c6f616420320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206c6f61642032203138360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206c6f61642032203138360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203138370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734332e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203138370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203138380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734332e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203138380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d6963726f736572766572656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d6963726f73657276657265616479203138390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d6963726f73657276657265616479203138390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203139300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734332e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203139300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203139310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203139310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203139320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734332e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203139320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203139330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734332e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203139330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203139340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203139340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203139350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203139350a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve_home_capture.json b/pylabrobot/high_res/micro_serve_home_capture.json new file mode 100644 index 00000000000..e5e735f7b46 --- /dev/null +++ b/pylabrobot/high_res/micro_serve_home_capture.json @@ -0,0 +1,257 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e2035360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e2035360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732035370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732035370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732035380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732035380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "686f6d650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120686f6d652035390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120686f6d652035390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732036300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732036300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732036310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732036310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f72732031302036320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f72732031302036320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "686973746f727920380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120686973746f727920382036330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020353620287061737365642920202d2076657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020353720287061737365642920202d207374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020353820287061737365642920202d207374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020353920287061737365642920202d20686f6d650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020363020287061737365642920202d207374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020363120287061737365642920202d207374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020363220287061737365642920202d206572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020203633202872756e6e696e6729202d20686973746f727920380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120686973746f727920382036330a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve_query_capture.json b/pylabrobot/high_res/micro_serve_query_capture.json new file mode 100644 index 00000000000..184b8d1d583 --- /dev/null +++ b/pylabrobot/high_res/micro_serve_query_capture.json @@ -0,0 +1,581 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732034320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732034320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e2034330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e2034330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6669726d7761726576657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206669726d7761726576657273696f6e2034340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206669726d7761726576657273696f6e2034340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64657461696c656476657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064657461696c656476657273696f6e2034350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d7761726520436865636b73756d3a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4361726f7573656c3a2046573a20352e35362c204e616d653a20604361726f7573656c207632272c20534e3a203932353139383636320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53706174756c613a2046573a20352e35362c204e616d653a206053706174756c61207632272c20534e3a203933393139343530390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4566666563747561746f723a2046573a20352e35362c204e616d653a20604566666563747561746f72207632272c20534e3a203933353139303530380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "426172636f64653a2046573a20352e35362c204e616d653a2060426172636f6465207632272c20534e3a203932353139383636340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064657461696c656476657273696f6e2034350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636f706c657973657269616c0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636f706c657973657269616c2034360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4361726f7573656c3a20393235313938363632203a204361726f7573656c2076320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53706174756c613a20393339313934353039203a2053706174756c612076320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4566666563747561746f723a20393335313930353038203a204566666563747561746f722076320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "426172636f64653a20393235313938363634203a20426172636f64652076320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636f706c657973657269616c2034360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d7374617475732034370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d7374617475732034370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e74730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732034380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "313a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "323a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "333a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "343a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "363a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "373a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "383a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "393a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31313a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31323a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31333a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732034380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e747320302034390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e747320302034390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d6963726f736572766572656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d6963726f736572766572656164792035300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d6963726f736572766572656164792035300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "73657474696e6773204341524f5553454c5f535441434b45525f434f554e540a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212073657474696e6773204341524f5553454c5f535441434b45525f434f554e542035310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4341524f5553454c5f535441434b45525f434f554e5420202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203d2031340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073657474696e6773204341524f5553454c5f535441434b45525f434f554e542035310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f72732031302035320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f72732031302035320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "686973746f727920330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120686973746f727920332035330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020353120287061737365642920202d2073657474696e6773204341524f5553454c5f535441434b45525f434f554e540a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020353220287061737365642920202d206572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020203533202872756e6e696e6729202d20686973746f727920330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120686973746f727920332035330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636f6d6d616e64737461742034330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636f6d6d616e64737461742034332035340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020203433204f4b2120202d2076657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636f6d6d616e64737461742034332035340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207374617475732035350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f61646572537461747573205265747261637465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207374617475732035350a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve_tests.py b/pylabrobot/high_res/micro_serve_tests.py new file mode 100644 index 00000000000..b6acc235ec5 --- /dev/null +++ b/pylabrobot/high_res/micro_serve_tests.py @@ -0,0 +1,674 @@ +"""MicroServe query/motion captures and simulated failures; no hardware connections.""" + +import asyncio +import unittest +from collections import deque +from pathlib import Path +from typing import Deque, List, Optional, Tuple, Union + +from pylabrobot.high_res.micro_serve import ( + HighResMicroServe, + MicroServeError, + MicroServePlateDimensions, + MicroServeProtocolError, + MicroServeStacker, + _parse_status, +) +from pylabrobot.io.capture import CaptureReader +from pylabrobot.io.socket import SocketValidator + +# Captured from MicroServe HRB-2008-10558, firmware 2.7.0.756. +STATUS = ( + "OK! status nothomed, stackerNumber -1, Flags: carouselHome ON, effectuatorHome ON, " + "spatulaHome ON, barcodeHome ON, plateSensor OFF, button OFF, lockSensor OFF, " + "Positions: carousel 0.000000, effectuator 0.000000, spatula 0.000000, barcode 0.000000, " + "machineState NotBusy, loaderStatus Retracted, mode Ready" +) +VERSION = ( + "Product Name: MicroServe", + "Serial Number: HRB-2008-10558", + "libcommon Version: 1.1.0.756", + "libts7600 Version: 1.0.0.756", + "Firmware Version: 2.7.0.756", + "Firmware Build: A620467A", +) +DIMENSIONS = tuple( + f"Stacker {i}: PlateHeight 11000, StackHeight 10000, PlateThickness 10000" for i in range(14) +) +GEOMETRY = MicroServePlateDimensions(height=11, stack_height=10, thickness=10) + + +def status( + homed: bool = True, stacker: int = 0, extended: bool = False, plate: bool = False +) -> str: + """Build simulated states by modifying fields in the captured status line.""" + return ( + STATUS.replace("nothomed", "homed" if homed else "nothomed") + .replace("stackerNumber -1", f"stackerNumber {stacker}") + .replace("Retracted", "Extended" if extended else "Retracted") + .replace("plateSensor OFF", "plateSensor ON" if plate else "plateSensor OFF") + .replace("spatulaHome ON", "spatulaHome OFF" if extended else "spatulaHome ON") + .replace("effectuatorHome ON", "effectuatorHome OFF" if extended else "effectuatorHome ON") + .replace("lockSensor OFF", "lockSensor ON" if extended else "lockSensor OFF") + ) + + +class FakeSocket: + """Script command/reply pairs and refuse interleaved exchanges.""" + + def __init__(self) -> None: + """Start with no scripted commands or unread replies.""" + self.scripts: Deque[Tuple[str, List[Union[bytes, BaseException]]]] = deque() + self.pending: Deque[Union[bytes, BaseException]] = deque() + self.writes: List[bytes] = [] + self.read_timeouts: List[float] = [] + self.setup_calls = 0 + self.stop_calls = 0 + self.next_id = 1 + + def add(self, command: str, *lines: str, completion: str = "OK") -> None: + """Queue a complete controller reply with a unique command identifier.""" + ident = self.next_id + self.next_id += 1 + self.scripts.append( + ( + command, + [ + (line + "\r\n").encode("ascii") + for line in (f"ACK! {command} {ident}", *lines, f"{completion}! {command} {ident}") + ], + ) + ) + + async def setup(self) -> None: + """Record connection setup.""" + self.setup_calls += 1 + + async def stop(self) -> None: + """Discard unread replies when a connection is invalidated.""" + self.stop_calls += 1 + self.pending.clear() + + async def write(self, data: bytes, timeout: Optional[float] = None) -> None: + """Check framing, command order, and ownership of pending replies.""" + if self.pending: + raise AssertionError("A new command was written before consuming the previous reply") + expected, reply = self.scripts.popleft() + if data != expected.encode("ascii") + b"\n": + raise AssertionError(f"Expected {expected!r} with LF terminator, got {data!r}") + self.writes.append(data) + self.pending.extend(reply) + await asyncio.sleep(0) + + async def readline(self, timeout: float) -> bytes: + """Return one line or a scripted transport failure.""" + self.read_timeouts.append(timeout) + await asyncio.sleep(0) + item = self.pending.popleft() + if isinstance(item, BaseException): + raise item + return item + + +class MicroServeTests(unittest.IsolatedAsyncioTestCase): + """Exercise the public driver through a scripted PyLabRobot transport.""" + + async def asyncSetUp(self) -> None: + """Connect to a fake transport with no implicit commands.""" + self.driver = HighResMicroServe(host="192.0.2.1") + self.io = FakeSocket() + self.driver.io = self.io # type: ignore[assignment] + await self.driver.setup() + + async def test_setup_and_stop_do_not_move_hardware(self) -> None: + """Lifecycle calls are idempotent and do not home or abort the device.""" + await self.driver.setup() + await self.driver.stop() + await self.driver.stop() + self.assertEqual(self.io.setup_calls, 1) + self.assertEqual(self.io.writes, []) + + async def test_captured_status_then_version_consumes_both_completions(self) -> None: + """The status data's OK! prefix must not leave a completion on the stream.""" + self.io.add("status", STATUS) + self.io.add("version", *VERSION) + result = await self.driver.request_status() + self.assertFalse(result.homed) + self.assertIsNone(result.stacker) + self.assertFalse(result.plate_sensor_blocked) + self.assertEqual(result.loader, "retracted") + self.assertTrue(result.flags["carouselHome"]) + self.assertIn("HRB-2008-10558", await self.driver.request_version()) + self.assertFalse(self.io.pending) + + async def test_real_geometry_and_counts(self) -> None: + """Device micrometers become millimeters and all fourteen indices survive.""" + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("getplatecounts", *(f"{i}: 0" for i in range(14))) + self.assertEqual((await self.driver.request_dimensions())[0], GEOMETRY) + self.assertEqual(await self.driver.request_plate_counts(), dict.fromkeys(range(14), 0)) + + async def test_diagnostics_do_not_change_settings(self) -> None: + """Readiness, firmware, settings, and errors use query commands only.""" + self.io.add("microserveready", "0") + self.io.add("firmwareversion", "2.7.0.756") + self.io.add("settings", "IP_ADDRESS = 10.10.1.67", "PRODUCT_NAME = MicroServe") + self.io.add("errors 10") + self.assertFalse(await self.driver.is_ready()) + self.assertEqual(await self.driver.request_firmware_version(), "2.7.0.756") + self.assertEqual((await self.driver.request_settings())["IP_ADDRESS"], "10.10.1.67") + self.assertEqual(await self.driver.request_errors(), ()) + + async def test_device_motor_identity_and_filtered_settings_captures(self) -> None: + """Parse the additional read-only replies captured from the device itself.""" + self.io.add( + "copleyserial", + "Carousel: 925198662 : Carousel v2", + "Spatula: 939194509 : Spatula v2", + "Effectuator: 935190508 : Effectuator v2", + "Barcode: 925198664 : Barcode v2", + ) + self.io.add("detailedversion", "Firmware Version: 2.7.0.756", "Firmware Checksum: A620467A") + self.io.add("settings CAROUSEL_STACKER_COUNT", "CAROUSEL_STACKER_COUNT = 14") + self.io.add("getplatecounts 0", "0: 0") + motors = await self.driver.request_motor_information() + self.assertEqual(motors["Spatula"].serial_number, "939194509") + self.assertEqual(motors["Barcode"].name, "Barcode v2") + self.assertIn("A620467A", await self.driver.request_detailed_version()) + self.assertEqual( + await self.driver.request_settings("CAROUSEL_STACKER_COUNT"), {"CAROUSEL_STACKER_COUNT": "14"} + ) + self.assertEqual(await self.driver.stackers[0].request_plate_count(), 0) + + async def test_command_record_and_history_data_have_their_own_status_tokens(self) -> None: + """Status tokens in a command record are data rather than envelope completions.""" + self.io.add("commandstat 1", " 1 OK! - version") + self.io.add( + "history 3", + " 33 (passed) - detailedversion", + " 34 (passed) - copleyserial", + " 35 (running) - history 3", + ) + result = await self.driver.request_command_status(1) + self.assertEqual((result.command_id, result.state, result.command), (1, "ok", "version")) + self.assertEqual(len(await self.driver.request_history(3)), 3) + + async def test_command_record_rejects_wrong_record_id(self) -> None: + """Matching envelope IDs do not make data about another command acceptable.""" + self.io.add("commandstat 1", " 2 OK! - version") + with self.assertRaises(MicroServeProtocolError): + await self.driver.request_command_status(1) + + async def test_query_arguments_cannot_inject_commands(self) -> None: + """Reject embedded commands and invalid identifiers before writing anything.""" + for search in ("IP_ADDRESS\nreboot", "", "PRODUCT NAME"): + with self.subTest(search=search), self.assertRaises(ValueError): + await self.driver.request_settings(search) + for count in (0, -1, True): + with self.subTest(count=count), self.assertRaises(ValueError): + await self.driver.request_command_status(count) + self.assertEqual(self.io.writes, []) + + async def test_terminal_failures_keep_diagnostics_and_session_alignment(self) -> None: + """Errors, aborts, and warnings are never silently treated as success.""" + for terminal in ("ERROR", "ABORTED", "WARNING"): + with self.subTest(terminal=terminal): + self.io.add("version", "Error 7: (14:03:30) -150: Example fault", completion=terminal) + with self.assertRaises(MicroServeError) as caught: + await self.driver.request_version() + self.assertEqual(caught.exception.status, terminal) + self.assertIn("Example fault", str(caught.exception)) + self.assertGreater(caught.exception.command_id, 0) + self.io.add("version", *VERSION) + await self.driver.request_version() + self.assertEqual(self.io.stop_calls, 0) + + async def test_bad_envelopes_close_connection(self) -> None: + """Wrong commands, identifiers, and duplicate ACKs invalidate the stream.""" + for bad in ( + b"ACK! status 1\n", + b"ACK! version 1\nOK! version 2\n", + b"ACK! version 1\nOK! status 1\n", + b"ACK! version 1\nACK! version 1\n", + ): + with self.subTest(reply=bad): + await self.driver.setup() + self.io.scripts.append(("version", list(bad.splitlines(keepends=True)))) + with self.assertRaises(MicroServeProtocolError): + await self.driver.request_version() + with self.assertRaisesRegex(RuntimeError, "setup"): + await self.driver.request_version() + + async def test_timeout_eof_partial_line_and_cancellation_do_not_retry(self) -> None: + """No uncertain command is replayed; reconnect is an explicit action.""" + for failure in (TimeoutError("stalled"), b"", b"partial", asyncio.CancelledError()): + with self.subTest(failure=failure): + await self.driver.setup() + self.io.scripts.append(("version", [b"ACK! version 1\n", failure])) + with self.assertRaises((TimeoutError, ConnectionError, asyncio.CancelledError)): + await self.driver.request_version() + with self.assertRaises(RuntimeError): + await self.driver.request_version() + self.assertEqual(len(self.io.writes), 4) + + async def test_query_concurrency_serializes_entire_exchanges(self) -> None: + """Concurrent requests cannot consume each other's data or completion.""" + self.io.add("version", *VERSION) + self.io.add("status", STATUS) + version, state = await asyncio.gather( + self.driver.request_version(), self.driver.request_status() + ) + self.assertIn("MicroServe", version) + self.assertFalse(state.homed) + + async def test_home_only_when_needed_and_verify(self) -> None: + """A second home call does not repeat a completed homing sequence.""" + self.io.add("status", STATUS) + self.io.add("home") + self.io.add("status", status()) + self.io.add("status", status()) + await self.driver.home() + await self.driver.home() + self.assertEqual(self.io.writes.count(b"home\n"), 1) + + async def test_home_rejects_blocked_detection_beam(self) -> None: + """A blocked beam prevents homing without implying an inventory state.""" + self.io.add("status", status(homed=False, plate=True)) + with self.assertRaisesRegex(RuntimeError, "detection beam"): + await self.driver.home() + + async def test_home_checks_result(self) -> None: + """An OK envelope does not override an unhomed result.""" + self.io.add("status", STATUS) + self.io.add("home") + self.io.add("status", STATUS) + with self.assertRaises(RuntimeError): + await self.driver.home() + + async def test_home_can_leave_manual_mode_as_described_in_operation_notes(self) -> None: + """Homing restores ready mode even when the previous homed flag remains set.""" + self.io.add("status", status().replace("mode Ready", "mode Manual")) + self.io.add("home") + self.io.add("status", status()) + await self.driver.home() + self.assertIn(b"home\n", self.io.writes) + + async def test_load_and_unload_apply_geometry_then_verify_target(self) -> None: + """Each preparation sends geometry before motion and skips repeated intent.""" + for command, prepare, present in ( + ("load", self.driver.stackers[2].prepare_for_load, False), + ("unload", self.driver.stackers[2].prepare_for_unload, True), + ): + with self.subTest(command=command): + self.io.add("status", status()) + self.io.add("setstackerdimensions 2 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add(f"{command} 2") + self.io.add("status", status(stacker=2, extended=True, plate=present)) + self.io.add("status", status(stacker=2, extended=True, plate=present)) + self.io.add("dimstatus", *DIMENSIONS) + await prepare(GEOMETRY) + await prepare(GEOMETRY) + self.assertEqual(self.io.writes.count(f"{command} 2\n".encode()), 1) + + async def test_prepare_rejects_other_handoff_and_wrong_final_state(self) -> None: + """Never take ownership of another handoff or accept the wrong carousel position.""" + self.io.add("status", status(stacker=1, extended=True, plate=True)) + with self.assertRaisesRegex(RuntimeError, "handoff"): + await self.driver.stackers[2].prepare_for_unload(GEOMETRY) + self.io.add("status", status()) + self.io.add("setstackerdimensions 2 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("unload 2") + self.io.add("status", status(stacker=1, extended=True, plate=False)) + with self.assertRaisesRegex(RuntimeError, "not reached"): + await self.driver.stackers[2].prepare_for_unload(GEOMETRY) + + async def test_dimension_readback_failure_prevents_motion(self) -> None: + """A rejected geometry update must stop the sequence before unload.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 0 13900 11900 11900") + self.io.add("dimstatus", *DIMENSIONS) + with self.assertRaises(MicroServeProtocolError): + await self.driver.stackers[0].prepare_for_unload(MicroServePlateDimensions(13.9, 11.9, 11.9)) + + async def test_uncertain_unload_requires_reconnect_and_does_not_fetch_twice(self) -> None: + """An interrupted unload needs command-record reconciliation, not a beam guess.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 2 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.scripts.append(("unload 2", [b"ACK! unload 2 50\n", TimeoutError("lost completion")])) + with self.assertRaises(TimeoutError): + await self.driver.stackers[2].prepare_for_unload(GEOMETRY) + with self.assertRaisesRegex(RuntimeError, "unresolved"): + await self.driver.stackers[2].prepare_for_unload(GEOMETRY) + pending = self.driver.unresolved_preparation + assert pending is not None + self.assertEqual(pending.command_id, 50) + await self.driver.setup() + self.io.add("commandstat 50", " 50 OK! - unload 2") + self.io.add("status", status(stacker=2, extended=True, plate=False)) + await self.driver.reconcile_preparation() + self.assertIsNone(self.driver.unresolved_preparation) + self.io.add("status", status(stacker=2, extended=True, plate=True)) + self.io.add("dimstatus", *DIMENSIONS) + await self.driver.stackers[2].prepare_for_unload(GEOMETRY) + self.assertEqual(self.io.writes.count(b"unload 2\n"), 1) + + async def test_preparation_sequence_is_atomic_with_concurrent_query(self) -> None: + """A competing caller cannot interleave between geometry and transfer checks.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 2 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("load 2") + self.io.add("status", status(stacker=2, extended=True)) + self.io.add("version", *VERSION) + await asyncio.gather( + self.driver.stackers[2].prepare_for_load(GEOMETRY), self.driver.request_version() + ) + self.assertEqual(self.io.writes[-1], b"version\n") + + async def test_unload_repeat_does_not_infer_occupancy_from_beam(self) -> None: + """A changing beam does not authorize fetching another plate before retraction.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 0 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("unload 0") + self.io.add("status", status(extended=True, plate=False)) + self.io.add("status", status(extended=True, plate=True)) + self.io.add("dimstatus", *DIMENSIONS) + await self.driver.stackers[0].prepare_for_unload(GEOMETRY) + await self.driver.stackers[0].prepare_for_unload(GEOMETRY) + self.assertEqual(self.io.writes.count(b"unload 0\n"), 1) + + async def test_extended_loader_without_owned_preparation_is_not_adopted(self) -> None: + """An extended stacker alone cannot identify an earlier load versus unload.""" + self.io.add("status", status(extended=True, plate=True)) + with self.assertRaisesRegex(RuntimeError, "handoff"): + await self.driver.stackers[0].prepare_for_unload(GEOMETRY) + self.assertEqual(self.io.writes, [b"status\n"]) + + async def test_running_failed_or_mismatched_preparation_stays_unresolved(self) -> None: + """Reconciliation only adopts an exact successful command record.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 0 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.scripts.append(("unload 0", [b"ACK! unload 0 50\n", TimeoutError("lost completion")])) + with self.assertRaises(TimeoutError): + await self.driver.stackers[0].prepare_for_unload(GEOMETRY) + await self.driver.setup() + for record in ("50 RUNNING! - unload 0", "50 FAIL! - unload 0", "50 OK! - load 0"): + self.io.add("commandstat 50", record) + with self.subTest(record=record), self.assertRaisesRegex(RuntimeError, "not confirmed"): + await self.driver.reconcile_preparation() + self.assertIsNotNone(self.driver.unresolved_preparation) + + async def test_preparation_without_ack_cannot_be_reconciled_from_a_stale_id(self) -> None: + """A failed write/ACK cannot borrow the identifier of the geometry query.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 0 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.scripts.append(("load 0", [TimeoutError("no ACK")])) + with self.assertRaises(TimeoutError): + await self.driver.stackers[0].prepare_for_load(GEOMETRY) + pending = self.driver.unresolved_preparation + assert pending is not None + self.assertIsNone(pending.command_id) + await self.driver.setup() + with self.assertRaisesRegex(RuntimeError, "No ACK"): + await self.driver.reconcile_preparation() + + async def test_retract_checks_state_and_is_idempotent(self) -> None: + """Retraction is explicit and not repeated when already retracted.""" + self.io.add("status", status(extended=True)) + self.io.add("retract") + self.io.add("status", status()) + self.io.add("status", status()) + await self.driver.retract() + await self.driver.retract() + self.assertEqual(self.io.writes.count(b"retract\n"), 1) + + async def test_rotation_requires_clear_retracted_loader(self) -> None: + """Carousel motion cannot start during a robot handoff.""" + self.io.add("status", status(extended=True)) + with self.assertRaises(RuntimeError): + await self.driver.stackers[2].move_to() + self.io.add("status", status()) + self.io.add("spin 2") + self.io.add("status", status(stacker=2)) + await self.driver.stackers[2].move_to() + + async def test_rotation_uses_home_sensors_instead_of_stale_loader_field(self) -> None: + """A stale Extended field must not block a physically retracted loader.""" + self.io.add("status", status().replace("loaderStatus Retracted", "loaderStatus Extended")) + self.io.add("spin 2") + self.io.add("status", status(stacker=2)) + await self.driver.stackers[2].move_to() + + async def test_rotation_rejects_retracted_field_with_unsafe_home_or_lock_sensor(self) -> None: + """A Retracted field alone cannot authorize carousel motion.""" + for old, new in ( + ("spatulaHome ON", "spatulaHome OFF"), + ("effectuatorHome ON", "effectuatorHome OFF"), + ("lockSensor OFF", "lockSensor ON"), + ): + with self.subTest(sensor=old): + self.io.add("status", status().replace(old, new)) + with self.assertRaisesRegex(RuntimeError, "retract"): + await self.driver.stackers[2].move_to() + self.assertEqual(self.io.writes, [b"status\n"] * 3) + + async def test_retraction_rejects_ok_reply_when_home_sensor_stays_off(self) -> None: + """The command envelope must agree with the physical home sensors.""" + self.io.add("status", status(extended=True)) + self.io.add("retract") + self.io.add("status", status().replace("spatulaHome ON", "spatulaHome OFF")) + with self.assertRaisesRegex(RuntimeError, "did not retract"): + await self.driver.retract() + + async def test_bad_status_and_incomplete_counts_are_rejected(self) -> None: + """Unknown sensor states and partial inventory are not interpreted as empty.""" + self.io.add("status", STATUS.replace("plateSensor OFF", "plateSensor UNKNOWN")) + with self.assertRaises(MicroServeProtocolError): + await self.driver.request_status() + self.io.add("getplatecounts", "0: 0") + with self.assertRaises(MicroServeProtocolError): + await self.driver.request_plate_counts() + + async def test_barcode_scan_uses_geometry_and_scan_timeout(self) -> None: + """Barcode data remains raw and uses the longer motion timeout.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 0 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("readbarcodestacker 0", '0: "EXAMPLE"') + result = await self.driver.stackers[0].scan_barcodes(GEOMETRY) + self.assertEqual(result, ('0: "EXAMPLE"',)) + self.assertGreater(self.io.read_timeouts[-1], 290) + + +class MicroServeCaptureTests(unittest.IsolatedAsyncioTestCase): + """Replay actual query and motion exchanges without opening a network connection.""" + + async def test_homing_capture(self) -> None: + """Replay the transition from unhomed to homed at stacker zero.""" + driver = HighResMicroServe(host="10.253.253.253") + reader = CaptureReader(str(Path(__file__).with_name("micro_serve_home_capture.json"))) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + self.assertFalse((await driver.request_status()).homed) + await driver.home() + result = await driver.request_status() + self.assertTrue(result.homed) + self.assertTrue(result.loader_retracted) + self.assertEqual(result.stacker, 0) + self.assertEqual(await driver.request_errors(), ()) + self.assertIn(" 59 (passed) - home", await driver.request_history(8)) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_all_carousel_positions_capture(self) -> None: + """Replay all fourteen positions, repeated targets, and reconnection.""" + driver = HighResMicroServe(host="10.253.253.253") + reader = CaptureReader(str(Path(__file__).with_name("micro_serve_carousel_capture.json"))) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + before = await driver.request_status() + await driver.home() + await driver.retract() + dimensions = await driver.request_dimensions() + for index in [*range(3, 14), 0, 1, 2]: + await driver.stackers[index].move_to() + await driver.stackers[index].move_to() + result = await driver.request_status() + self.assertEqual(result.stacker, index) + self.assertTrue(result.loader_retracted) + self.assertFalse(result.busy) + self.assertEqual(await driver.stackers[index].request_plate_count(), 0) + self.assertFalse(await driver.is_ready()) + self.assertEqual(await driver.request_plate_counts(), dict.fromkeys(range(14), 0)) + self.assertEqual(await driver.request_dimensions(), dimensions) + self.assertEqual(await driver.request_errors(), ()) + await driver.stop() + await driver.setup() + self.assertEqual(before, await driver.request_status()) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_empty_load_retract_with_stale_firmware_loader_flag(self) -> None: + """Actual retraction succeeds although firmware leaves loaderStatus Extended.""" + driver = HighResMicroServe(host="10.253.253.253") + reader = CaptureReader(str(Path(__file__).with_name("micro_serve_empty_load_capture.json"))) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + self.assertEqual((await driver.request_status()).stacker, 2) + dimensions = await driver.request_dimensions() + self.assertFalse(await driver.is_ready()) + await driver.stackers[2].prepare_for_load(dimensions[2]) + extended = await driver.request_status() + self.assertTrue(extended.plate_sensor_blocked) + self.assertTrue(await driver.is_ready()) + await driver.stackers[2].prepare_for_load(dimensions[2]) + self.assertEqual(extended, await driver.request_status()) + await driver.retract() + self.assertIsNone(driver._prepared) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_empty_cycle_and_rotation_after_retraction_capture(self) -> None: + """Replay a complete empty handoff and subsequent rotation with a stale loader field.""" + driver = HighResMicroServe(host="10.253.253.253") + reader = CaptureReader(str(Path(__file__).with_name("micro_serve_empty_cycle_capture.json"))) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + before = await driver.request_status() + self.assertEqual(before.loader, "extended") + self.assertTrue(before.loader_retracted) + await driver.retract() + dimensions = await driver.request_dimensions() + await driver.stackers[2].set_dimensions(dimensions[2]) + await driver.stackers[2].prepare_for_load(dimensions[2]) + self.assertTrue((await driver.request_status()).loader_extended) + self.assertTrue(await driver.is_ready()) + await driver.stackers[2].prepare_for_load(dimensions[2]) + await driver.retract() + await driver.retract() + retracted = await driver.request_status() + self.assertEqual(retracted.loader, "extended") + self.assertTrue(retracted.loader_retracted) + self.assertFalse(retracted.loader_extended) + self.assertIsNone(driver._prepared) + self.assertFalse(await driver.is_ready()) + await driver.stackers[3].move_to() + await driver.stackers[2].move_to() + self.assertEqual(before, await driver.request_status()) + self.assertEqual(dimensions, await driver.request_dimensions()) + self.assertEqual(await driver.request_errors(), ()) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_device_native_read_only_capture(self) -> None: + """Exercise all implemented queries without opening a network connection.""" + driver = HighResMicroServe(host="10.253.253.253") + reader = CaptureReader(str(Path(__file__).with_name("micro_serve_query_capture.json"))) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + before = await driver.request_status() + self.assertIn("HRB-2008-10558", await driver.request_version()) + version_id = driver.last_command_id + assert version_id is not None + self.assertEqual(await driver.request_firmware_version(), "2.7.0.756") + self.assertIn("A620467A", await driver.request_detailed_version()) + self.assertEqual( + (await driver.request_motor_information())["Barcode"].serial_number, "925198664" + ) + self.assertEqual(len(await driver.request_dimensions()), 14) + self.assertEqual(len(await driver.request_plate_counts()), 14) + self.assertEqual(await driver.stackers[0].request_plate_count(), 0) + self.assertFalse(await driver.is_ready()) + self.assertEqual( + await driver.request_settings("CAROUSEL_STACKER_COUNT"), {"CAROUSEL_STACKER_COUNT": "14"} + ) + self.assertEqual(await driver.request_errors(), ()) + self.assertEqual(len(await driver.request_history(3)), 3) + record = await driver.request_command_status(version_id) + self.assertEqual((record.state, record.command), ("ok", "version")) + self.assertEqual(before, await driver.request_status()) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + +class MicroServeValidationTests(unittest.TestCase): + """Validate parameters and status variants before hardware communication.""" + + def test_dimensions_reject_nonphysical_values(self) -> None: + """Dimensions must survive conversion to device-native micrometers.""" + for value in (0, -1, float("nan"), float("inf"), 0.0001): + with self.subTest(value=value), self.assertRaises(ValueError): + MicroServePlateDimensions(value, 10, 10) + with self.assertRaises(ValueError): + MicroServePlateDimensions(10, 10, 11) + + def test_indices_and_timeouts(self) -> None: + """Expose exactly fourteen zero-based stackers and finite positive timeouts.""" + device = HighResMicroServe(host="192.0.2.1") + self.assertEqual([stacker.index for stacker in device.stackers], list(range(14))) + for index in (-1, 14, True): + with self.subTest(index=index), self.assertRaises(ValueError): + MicroServeStacker(device, index) + for timeout in (0, -1, float("nan")): + with self.subTest(timeout=timeout), self.assertRaises(ValueError): + HighResMicroServe(host="192.0.2.1", ack_timeout=timeout) + + def test_manual_status_example_and_unknown_machine_state(self) -> None: + """Device-native position annotations do not interfere with state parsing.""" + manual = status(extended=True).replace("effectuator 0.000000", "effectuator -1 (-1 um)") + parsed = _parse_status((manual,)) + self.assertTrue(parsed.homed) + self.assertEqual(parsed.loader, "extended") + with self.assertRaises(MicroServeProtocolError): + _parse_status((STATUS.replace("NotBusy", "Unknown"),)) + + def test_missing_loader_sensors_cannot_authorize_motion(self) -> None: + """Incomplete physical state must fail before a motion method can consume it.""" + for sensor in ("spatulaHome ON", "effectuatorHome ON", "lockSensor OFF"): + with self.subTest(sensor=sensor), self.assertRaises(MicroServeProtocolError): + _parse_status((STATUS.replace(sensor, ""),)) From 0a5cb9c8909cfb748caa96ca0c8a3d51fbf1386c Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 8 Sep 2026 19:39:14 -0700 Subject: [PATCH 2/8] Organize MicroServe package and document state transitions --- docs/api/pylabrobot.high_res.rst | 2 +- docs/user_guide/high_res/index.md | 1 + .../high_res/micro-serve/hello-world.ipynb | 2 +- .../high_res/micro-serve/images/lifecycle.dot | 21 ++++ .../high_res/micro-serve/images/recovery.dot | 37 ++++++ .../micro-serve/images/state-machine.dot | 41 +++++++ .../high_res/micro-serve/protocol.md | 2 + .../high_res/micro-serve/state-machine.md | 115 ++++++++++++++++++ pylabrobot/high_res/micro_serve/__init__.py | 25 ++++ .../high_res/{ => micro_serve}/micro_serve.py | 0 .../high_res/micro_serve/tests/__init__.py | 1 + .../tests/captures/carousel.json} | 0 .../tests/captures/empty_cycle.json} | 0 .../tests/captures/empty_load.json} | 0 .../tests/captures/home.json} | 0 .../tests/captures/query.json} | 0 .../tests}/micro_serve_tests.py | 12 +- 17 files changed, 251 insertions(+), 8 deletions(-) create mode 100644 docs/user_guide/high_res/micro-serve/images/lifecycle.dot create mode 100644 docs/user_guide/high_res/micro-serve/images/recovery.dot create mode 100644 docs/user_guide/high_res/micro-serve/images/state-machine.dot create mode 100644 docs/user_guide/high_res/micro-serve/state-machine.md create mode 100644 pylabrobot/high_res/micro_serve/__init__.py rename pylabrobot/high_res/{ => micro_serve}/micro_serve.py (100%) create mode 100644 pylabrobot/high_res/micro_serve/tests/__init__.py rename pylabrobot/high_res/{micro_serve_carousel_capture.json => micro_serve/tests/captures/carousel.json} (100%) rename pylabrobot/high_res/{micro_serve_empty_cycle_capture.json => micro_serve/tests/captures/empty_cycle.json} (100%) rename pylabrobot/high_res/{micro_serve_empty_load_capture.json => micro_serve/tests/captures/empty_load.json} (100%) rename pylabrobot/high_res/{micro_serve_home_capture.json => micro_serve/tests/captures/home.json} (100%) rename pylabrobot/high_res/{micro_serve_query_capture.json => micro_serve/tests/captures/query.json} (100%) rename pylabrobot/high_res/{ => micro_serve/tests}/micro_serve_tests.py (98%) diff --git a/docs/api/pylabrobot.high_res.rst b/docs/api/pylabrobot.high_res.rst index f822ea93fce..678e8c76299 100644 --- a/docs/api/pylabrobot.high_res.rst +++ b/docs/api/pylabrobot.high_res.rst @@ -13,7 +13,7 @@ pylabrobot.high_res package HighResLidValet HighResLidValetError -.. currentmodule:: pylabrobot.high_res.micro_serve +.. currentmodule:: pylabrobot.high_res.micro_serve.micro_serve .. autosummary:: :toctree: _autosummary diff --git a/docs/user_guide/high_res/index.md b/docs/user_guide/high_res/index.md index 84b63872753..025bcf892f7 100644 --- a/docs/user_guide/high_res/index.md +++ b/docs/user_guide/high_res/index.md @@ -5,6 +5,7 @@ lid-valet/hello-world micro-serve/hello-world +micro-serve/state-machine micro-serve/protocol sample-storage/index ``` diff --git a/docs/user_guide/high_res/micro-serve/hello-world.ipynb b/docs/user_guide/high_res/micro-serve/hello-world.ipynb index 58c60f28b4e..cd099a327c0 100644 --- a/docs/user_guide/high_res/micro-serve/hello-world.ipynb +++ b/docs/user_guide/high_res/micro-serve/hello-world.ipynb @@ -22,7 +22,7 @@ "\n", "This driver prepares the MicroServe for a separate robot to place or pick plates. It does not model plate inventory in PyLabRobot resources. Reported plate counts are the controller's cached estimates.\n", "\n", - "See [Protocol and validation](protocol.md) for the device-source mapping and verification limits." + "See the [state machine](state-machine.md) for workflow and recovery diagrams, and [protocol and validation](protocol.md) for the device-source mapping and verification limits." ] }, { diff --git a/docs/user_guide/high_res/micro-serve/images/lifecycle.dot b/docs/user_guide/high_res/micro-serve/images/lifecycle.dot new file mode 100644 index 00000000000..b04d4c2e4b2 --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/images/lifecycle.dot @@ -0,0 +1,21 @@ +digraph microserve_lifecycle { + graph [bgcolor="#f8fafc", pad=0.25, rankdir=TB, ranksep=0.65, nodesep=0.7]; + node [shape=box, style="rounded,filled", fillcolor="#e0f2fe", color="#0284c7", + fontcolor="#0f172a", fontname="Helvetica", fontsize=14, margin="0.17,0.12"]; + edge [color="#475569", fontcolor="#475569", fontname="Helvetica", fontsize=12, + arrowsize=0.8]; + + disconnected [label="DISCONNECTED"]; + connected [label="CONNECTED\nphysical state must be queried"]; + needs_home [label="NOT HOMED\nor manual mode"]; + homing [label="HOMING", fillcolor="#ede9fe", color="#8b5cf6"]; + homed [label="HOMED + READY MODE\nnot busy", fillcolor="#dcfce7", color="#16a34a"]; + + disconnected -> connected [label="setup()\nopens transport only"]; + connected -> disconnected [label="stop()\ncloses transport only", constraint=false]; + connected -> needs_home [label="request_status()"]; + connected -> homed [label="request_status()\nif already homed and idle"]; + needs_home -> homing [label="home()\nclear beam · not busy\nready or manual mode"]; + homing -> homed [label="home completion +\nstatus confirmation"]; + homed -> homed [label="home() checks status\nwithout another home"]; +} diff --git a/docs/user_guide/high_res/micro-serve/images/recovery.dot b/docs/user_guide/high_res/micro-serve/images/recovery.dot new file mode 100644 index 00000000000..ad11efffc5a --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/images/recovery.dot @@ -0,0 +1,37 @@ +digraph microserve_recovery { + graph [bgcolor="#f8fafc", pad=0.25, rankdir=TB, ranksep=0.65, nodesep=0.6, + outputorder=edgesfirst]; + node [shape=box, style="rounded,filled", fillcolor="#fef3c7", color="#d97706", + fontcolor="#0f172a", fontname="Helvetica", fontsize=14, margin="0.17,0.12"]; + edge [color="#475569", fontcolor="#475569", fontname="Helvetica", fontsize=12, + arrowsize=0.8]; + + preparing [label="PREPARATION IN FLIGHT\nload/unload has been attempted", + fillcolor="#ede9fe", color="#8b5cf6"]; + disconnected [label="DISCONNECTED\nunresolved preparation retained", + fillcolor="#fee2e2", color="#dc2626"]; + pending [label="UNRESOLVED / CONNECTED\nnew preparation and most motion blocked", + penwidth=2]; + checking [label="RECONCILING\nread command record + status", + fillcolor="#e0f2fe", color="#0284c7"]; + retracting [label="EXPLICIT RETRACTION\ninspect machine · clear robot\nhomed · idle · ready mode"]; + prepared [label="OWNED HANDOFF RESTORED\nexpected direction + stacker", + fillcolor="#dcfce7", color="#16a34a"]; + cleared [label="HANDOFF CLEARED\nloader home sensors confirmed", + fillcolor="#dcfce7", color="#16a34a"]; + + preparing -> disconnected [label="timeout / cancellation /\nincomplete exchange", + color="#dc2626", fontcolor="#dc2626"]; + preparing -> pending [label="ERROR / WARNING / ABORTED\nor final state not confirmed", + color="#d97706", fontcolor="#d97706"]; + disconnected -> pending [label="inspect, then setup()\npending record is preserved"]; + pending -> checking [label="reconcile_preparation()\nsame controller boot · no other client moves"]; + checking -> pending [label="missing / running / failed /\nmismatched result", constraint=false, + color="#d97706", fontcolor="#d97706"]; + checking -> prepared [label="exact command succeeded\ntarget + extension confirmed"]; + pending -> retracting [label="retract()\nafter inspection"]; + retracting -> cleared [label="both home sensors ON\nlock released"]; + + {rank=same; checking; retracting;} + {rank=same; prepared; cleared;} +} diff --git a/docs/user_guide/high_res/micro-serve/images/state-machine.dot b/docs/user_guide/high_res/micro-serve/images/state-machine.dot new file mode 100644 index 00000000000..ddbc49e3734 --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/images/state-machine.dot @@ -0,0 +1,41 @@ +digraph microserve_activity { + graph [bgcolor="#f8fafc", pad=0.3, layout=neato, overlap=true, splines=true, + outputorder=edgesfirst]; + node [shape=box, style="rounded,filled", fillcolor="#ede9fe", color="#8b5cf6", + fontcolor="#0f172a", fontname="Helvetica", fontsize=16, pin=true, + margin="0.18,0.13"]; + edge [color="#475569", fontcolor="#475569", fontname="Helvetica", fontsize=12, + arrowsize=0.8, penwidth=1.3]; + + idle [pos="5,8!", label="RETRACTED / IDLE", + penwidth=2, fillcolor="#dcfce7", color="#16a34a"]; + positioning [pos="0.5,10!", label="POSITIONING\nmove_to()"]; + scanning [pos="9.5,10!", label="SCANNING\nscan_barcodes(d)"]; + position_check [pos="2,8.8!", shape=plain, fontsize=12, fontcolor="#475569", + label="target + sensors\nconfirmed"]; + scan_check [pos="8,8.8!", shape=plain, fontsize=12, fontcolor="#475569", + label="inspect status after completion\nconfirm retraction"]; + preparing_load [pos="2,5.2!", label="PREPARING TO LOAD\nprepare_for_load(d)\napply geometry · load(i)"]; + preparing_unload [pos="8,5.2!", label="PREPARING TO UNLOAD\nprepare_for_unload(d)\napply geometry · unload(i)"]; + receiving [pos="2,2.4!", label="READY TO RECEIVE\nowned load handoff", + fillcolor="#dcfce7", color="#16a34a"]; + presenting [pos="8,2.4!", label="READY FOR PICKUP\nowned unload handoff", + fillcolor="#dcfce7", color="#16a34a"]; + retracting [pos="5,0!", label="RETRACTING\nretract() · robot clear\nspatula + effectuator"]; + + idle -> positioning; + positioning -> idle; + idle -> scanning; + scanning -> idle [style=dashed]; + idle -> preparing_load; + idle -> preparing_unload; + preparing_load -> receiving [xlabel="command + target +\nextension confirmed"]; + preparing_unload -> presenting [xlabel="command + target +\nextension confirmed"]; + receiving -> receiving [xlabel="robot places plate\nand withdraws", color="#0369a1", + fontcolor="#0369a1", style=dashed]; + presenting -> presenting [xlabel="robot picks plate\nand withdraws", color="#0369a1", + fontcolor="#0369a1", style=dashed]; + receiving -> retracting; + presenting -> retracting; + retracting -> idle [xlabel="both home sensors ON\nlock released", penwidth=1.6]; +} diff --git a/docs/user_guide/high_res/micro-serve/protocol.md b/docs/user_guide/high_res/micro-serve/protocol.md index ffcb03e6a3a..4f6bacc81b3 100644 --- a/docs/user_guide/high_res/micro-serve/protocol.md +++ b/docs/user_guide/high_res/micro-serve/protocol.md @@ -1,5 +1,7 @@ # MicroServe protocol and validation +See the [state machine](state-machine.md) for connection, plate-handoff, and recovery diagrams. + The implementation follows documentation served by the MicroServe itself: - [MicroServe API, revision 756, May 29, 2020](http://10.253.253.253/support_files/files/MicroServe_API.pdf). diff --git a/docs/user_guide/high_res/micro-serve/state-machine.md b/docs/user_guide/high_res/micro-serve/state-machine.md new file mode 100644 index 00000000000..6ea065c6cf4 --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/state-machine.md @@ -0,0 +1,115 @@ +# MicroServe state machine + +The MicroServe coordinates one carousel, fourteen stackers, and a shared plate-transfer +position. These diagrams show the driver's workflows and the conditions that confirm each +transition. Start with the [hello-world guide](hello-world.ipynb) for connection and plate geometry. + +The phase names below describe workflows; the driver does not expose a VSpin-style `.state` +object. It combines fresh `request_status()` replies with a recorded handoff and, after an +interrupted preparation, `unresolved_preparation`. + +## Activity overview + +```{graphviz} images/state-machine.dot +:layout: neato +:alt: MicroServe workflows share a retracted, idle state. Carousel positioning returns after verification. Load and unload preparation lead to separate owned handoffs; the external robot transfers a plate before explicit retraction returns to idle. +:caption: Purple nodes are operations; green nodes are confirmed resting or handoff conditions. Dashed blue loops are actions by the external robot. Barcode completion returns data without a final position check, so inspect status before further motion. Failure paths are shown in the recovery diagram. +``` + +{download}`Download the activity diagram source ` + +The central state requires a connection, homing, ready mode, no active operation, no unresolved +preparation, and a retracted loader. Rotation, preparation, and scanning also require a clear +plate-detection beam. Each public operation holds the device lock for its entire sequence; +another operation or status query waits for that lock. + +`prepare_for_load(dimensions)` presents a receiving position. `prepare_for_unload(dimensions)` +presents a plate for pickup. Both apply the supplied geometry, run the firmware operation, and +confirm the stacker and loader sensors. Python records the direction and stacker of the resulting +handoff. The separate robot must place or pick the plate and withdraw before `retract()`. + +Repeated requests preserve the intended state: + +- `move_to()` at the selected stacker checks the motion preconditions and sends no `spin` command. +- Repeating the same owned preparation with unchanged geometry sends no additional load/unload. +- `retract()` with both loader home sensors on and the lock released sends no retraction command. + +The robot's action does not clear ownership, and a change in the plate-detection beam does not +authorize another plate fetch. Retraction ends the handoff. An extended loader whose preparation +is not owned by this driver cannot be adopted by calling prepare again. + +## Connection and homing + +```{graphviz} images/lifecycle.dot +:alt: Setup opens a connection without motion. A status query determines whether homing is needed. Explicit home verifies homed and idle status. Stop closes the connection without stopping or retracting the machine. +:caption: Connection and physical state are separate. Homed and ready mode do not establish loader retraction or plate-transfer readiness. Physical motion checks still apply. +``` + +{download}`Download the lifecycle diagram source ` + +`setup()` opens the TCP connection. `home()` is an explicit motion operation that verifies homed, +idle status. A machine already reporting homed and ready mode does not receive another home +command. An unresolved preparation must be handled before homing. + +`stop()` closes the connection and discards confirmed handoff ownership. It does not abort a +running command, retract the loader, or clear an unresolved preparation. Reconnecting does not +recover ownership of a previously confirmed handoff; inspect the machine before proceeding. + +## Read the physical state + +```python +status = await microserve.request_status() +print(status.homed, status.busy, status.mode, status.stacker) +print(status.loader_retracted, status.loader_extended) +print(microserve.unresolved_preparation) +``` + +| Condition | How the driver determines it | +|---|---| +| Homed and idle | `status.homed`, `not status.busy`, and `status.mode == "ready"` | +| Loader retracted | Both loader home sensors on and stacker lock released: `status.loader_retracted` | +| Loader extended | Firmware reports extension, both loader home sensors off, and lock engaged: `status.loader_extended` | +| Readiness to present a plate | `await microserve.is_ready()`; false while retracted, true at the tested receiving position | +| Interrupted load/unload | `microserve.unresolved_preparation` retains direction, stacker, and the ACK identifier when available | +| Plate-detection beam | `status.plate_sensor_blocked`; this does not establish occupancy or plate count | + +Firmware 2.7.0.756 can retain `loaderStatus Extended` after successful retraction. The driver +preserves that field in `status.loader`, but uses the home and lock sensors for motion guards. +Neither `status.loader` nor `is_ready()` alone is a general machine-readiness check. + +## Interrupted preparation and recovery + +```{graphviz} images/recovery.dot +:alt: A failed or interrupted load/unload retains an unresolved preparation. An incomplete exchange closes the connection. After inspection and reconnection, read-only reconciliation can restore the exact successful handoff; explicit retraction can instead clear it after confirming the home sensors. +:caption: No path automatically retries load or unload. Reconnection preserves the pending record. Reconciliation requires the same controller boot and no intervening moves by another client; unsuccessful evidence leaves the preparation unresolved. +``` + +{download}`Download the recovery diagram source ` + +A precondition rejection prevents the requested motion. Geometry is written and checked before +load/unload; a failure during that step does not create an attempted transfer. Once load/unload +has been attempted, a failure or an unconfirmed final state retains an unresolved preparation. + +A timeout, cancellation, or broken exchange closes the connection because completion is uncertain. +A complete `ERROR`, `WARNING`, or `ABORTED` reply raises `MicroServeError` and retains diagnostics; +it does not by itself close the transport. A device operation may continue after disconnection. + +`reconcile_preparation()` reads the original command record and the current physical state. It +only restores ownership when the exact command succeeded and the expected stacker is extended, +homed, idle, and in ready mode. An unknown ACK identifier, unsuccessful record, or mismatched +state leaves the pending preparation in place. There is no automatic resend. + +Alternatively, after inspecting the actual machine and clearing the robot, `retract()` can end +the pending handoff. It requires homed, idle, ready-mode status, and only clears the record after +the home and lock sensors confirm retraction. If those prerequisites are unavailable, resolve the +physical condition before attempting another driver operation. + +These recovery records specifically protect load/unload. Other failed operations still require +inspection and fresh status; the driver does not maintain a general recovery flag for every axis. + +## Hardware verification + +Homing, all fourteen carousel positions, repeated requests, empty receiving-position preparation, +retraction, and subsequent rotation were verified on firmware 2.7.0.756. The diagrams also include +unloading, robot transfers, barcode scanning, and recovery paths that remain unverified on hardware. +See [protocol and validation](protocol.md) for the captures and exact limits. diff --git a/pylabrobot/high_res/micro_serve/__init__.py b/pylabrobot/high_res/micro_serve/__init__.py new file mode 100644 index 00000000000..11ba29eed65 --- /dev/null +++ b/pylabrobot/high_res/micro_serve/__init__.py @@ -0,0 +1,25 @@ +"""HighRes MicroServe controller and stacker operations.""" + +from .micro_serve import ( + HighResMicroServe, + MicroServeCommandStatus, + MicroServeError, + MicroServeMotorInfo, + MicroServePlateDimensions, + MicroServeProtocolError, + MicroServeStacker, + MicroServeStatus, + MicroServeUnresolvedPreparation, +) + +__all__ = [ + "HighResMicroServe", + "MicroServeCommandStatus", + "MicroServeError", + "MicroServeMotorInfo", + "MicroServePlateDimensions", + "MicroServeProtocolError", + "MicroServeStacker", + "MicroServeStatus", + "MicroServeUnresolvedPreparation", +] diff --git a/pylabrobot/high_res/micro_serve.py b/pylabrobot/high_res/micro_serve/micro_serve.py similarity index 100% rename from pylabrobot/high_res/micro_serve.py rename to pylabrobot/high_res/micro_serve/micro_serve.py diff --git a/pylabrobot/high_res/micro_serve/tests/__init__.py b/pylabrobot/high_res/micro_serve/tests/__init__.py new file mode 100644 index 00000000000..5d0d8d9da6a --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/__init__.py @@ -0,0 +1 @@ +"""MicroServe captured transport and simulated failure tests.""" diff --git a/pylabrobot/high_res/micro_serve_carousel_capture.json b/pylabrobot/high_res/micro_serve/tests/captures/carousel.json similarity index 100% rename from pylabrobot/high_res/micro_serve_carousel_capture.json rename to pylabrobot/high_res/micro_serve/tests/captures/carousel.json diff --git a/pylabrobot/high_res/micro_serve_empty_cycle_capture.json b/pylabrobot/high_res/micro_serve/tests/captures/empty_cycle.json similarity index 100% rename from pylabrobot/high_res/micro_serve_empty_cycle_capture.json rename to pylabrobot/high_res/micro_serve/tests/captures/empty_cycle.json diff --git a/pylabrobot/high_res/micro_serve_empty_load_capture.json b/pylabrobot/high_res/micro_serve/tests/captures/empty_load.json similarity index 100% rename from pylabrobot/high_res/micro_serve_empty_load_capture.json rename to pylabrobot/high_res/micro_serve/tests/captures/empty_load.json diff --git a/pylabrobot/high_res/micro_serve_home_capture.json b/pylabrobot/high_res/micro_serve/tests/captures/home.json similarity index 100% rename from pylabrobot/high_res/micro_serve_home_capture.json rename to pylabrobot/high_res/micro_serve/tests/captures/home.json diff --git a/pylabrobot/high_res/micro_serve_query_capture.json b/pylabrobot/high_res/micro_serve/tests/captures/query.json similarity index 100% rename from pylabrobot/high_res/micro_serve_query_capture.json rename to pylabrobot/high_res/micro_serve/tests/captures/query.json diff --git a/pylabrobot/high_res/micro_serve_tests.py b/pylabrobot/high_res/micro_serve/tests/micro_serve_tests.py similarity index 98% rename from pylabrobot/high_res/micro_serve_tests.py rename to pylabrobot/high_res/micro_serve/tests/micro_serve_tests.py index b6acc235ec5..3928c6eac91 100644 --- a/pylabrobot/high_res/micro_serve_tests.py +++ b/pylabrobot/high_res/micro_serve/tests/micro_serve_tests.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import Deque, List, Optional, Tuple, Union -from pylabrobot.high_res.micro_serve import ( +from pylabrobot.high_res.micro_serve.micro_serve import ( HighResMicroServe, MicroServeError, MicroServePlateDimensions, @@ -492,7 +492,7 @@ class MicroServeCaptureTests(unittest.IsolatedAsyncioTestCase): async def test_homing_capture(self) -> None: """Replay the transition from unhomed to homed at stacker zero.""" driver = HighResMicroServe(host="10.253.253.253") - reader = CaptureReader(str(Path(__file__).with_name("micro_serve_home_capture.json"))) + reader = CaptureReader(str(Path(__file__).parent / "captures" / "home.json")) driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) await driver.setup() try: @@ -513,7 +513,7 @@ async def test_homing_capture(self) -> None: async def test_all_carousel_positions_capture(self) -> None: """Replay all fourteen positions, repeated targets, and reconnection.""" driver = HighResMicroServe(host="10.253.253.253") - reader = CaptureReader(str(Path(__file__).with_name("micro_serve_carousel_capture.json"))) + reader = CaptureReader(str(Path(__file__).parent / "captures" / "carousel.json")) driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) await driver.setup() try: @@ -545,7 +545,7 @@ async def test_all_carousel_positions_capture(self) -> None: async def test_empty_load_retract_with_stale_firmware_loader_flag(self) -> None: """Actual retraction succeeds although firmware leaves loaderStatus Extended.""" driver = HighResMicroServe(host="10.253.253.253") - reader = CaptureReader(str(Path(__file__).with_name("micro_serve_empty_load_capture.json"))) + reader = CaptureReader(str(Path(__file__).parent / "captures" / "empty_load.json")) driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) await driver.setup() try: @@ -569,7 +569,7 @@ async def test_empty_load_retract_with_stale_firmware_loader_flag(self) -> None: async def test_empty_cycle_and_rotation_after_retraction_capture(self) -> None: """Replay a complete empty handoff and subsequent rotation with a stale loader field.""" driver = HighResMicroServe(host="10.253.253.253") - reader = CaptureReader(str(Path(__file__).with_name("micro_serve_empty_cycle_capture.json"))) + reader = CaptureReader(str(Path(__file__).parent / "captures" / "empty_cycle.json")) driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) await driver.setup() try: @@ -605,7 +605,7 @@ async def test_empty_cycle_and_rotation_after_retraction_capture(self) -> None: async def test_device_native_read_only_capture(self) -> None: """Exercise all implemented queries without opening a network connection.""" driver = HighResMicroServe(host="10.253.253.253") - reader = CaptureReader(str(Path(__file__).with_name("micro_serve_query_capture.json"))) + reader = CaptureReader(str(Path(__file__).parent / "captures" / "query.json")) driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) await driver.setup() try: From 830980f1fa07395f70e6bbe9296407962f67f198 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 8 Sep 2026 21:46:15 -0700 Subject: [PATCH 3/8] Group MicroServe guides and clarify controller documentation access --- docs/user_guide/high_res/index.md | 4 +--- .../high_res/micro-serve/hello-world.ipynb | 2 +- docs/user_guide/high_res/micro-serve/index.md | 13 ++++++++++ .../high_res/micro-serve/protocol.md | 24 +++++++++++-------- 4 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 docs/user_guide/high_res/micro-serve/index.md diff --git a/docs/user_guide/high_res/index.md b/docs/user_guide/high_res/index.md index 025bcf892f7..314c51b2b56 100644 --- a/docs/user_guide/high_res/index.md +++ b/docs/user_guide/high_res/index.md @@ -4,8 +4,6 @@ :maxdepth: 1 lid-valet/hello-world -micro-serve/hello-world -micro-serve/state-machine -micro-serve/protocol +micro-serve/index sample-storage/index ``` diff --git a/docs/user_guide/high_res/micro-serve/hello-world.ipynb b/docs/user_guide/high_res/micro-serve/hello-world.ipynb index cd099a327c0..8d28f26596f 100644 --- a/docs/user_guide/high_res/micro-serve/hello-world.ipynb +++ b/docs/user_guide/high_res/micro-serve/hello-world.ipynb @@ -34,7 +34,7 @@ "\n", "Connect the powered MicroServe's Ethernet port to a dedicated NIC. Configure that NIC with `10.253.253.50/24`, then use `10.253.253.253` as the host. All HighRes controllers share that alternate address, so use it only on a dedicated link. Your normal network connection can remain separate.\n", "\n", - "The controller serves its [MicroServe API PDF](http://10.253.253.253/support_files/files/MicroServe_API.pdf) through its [Support Files page](http://10.253.253.253/support_files.html). This guide follows revision 756, dated May 29, 2020. The saved network address shown by the web interface can differ from legacy in-memory `settings` values.\n", + "With the controller connected, open its address in a browser and choose **Support Files → MicroServe_API.pdf**. This guide follows revision 756, dated May 29, 2020. See the [protocol reference](protocol.md) for the other documentation available on the controller. The saved network address shown by the web interface can differ from legacy in-memory `settings` values.\n", "\n", "The protocol acknowledges a command with an identifier, sends data, then sends a completion with the same command and identifier. The driver handles framing and converts plate dimensions from millimeters to device micrometers." ] diff --git a/docs/user_guide/high_res/micro-serve/index.md b/docs/user_guide/high_res/micro-serve/index.md new file mode 100644 index 00000000000..86291fa06a0 --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/index.md @@ -0,0 +1,13 @@ +# MicroServe + +The HighRes MicroServe is a fourteen-position carousel plate stacker controlled over Ethernet. +Start with the hello-world guide for connection and operation examples. The state machine explains +plate handoffs and recovery; the protocol reference describes commands and validation limits. + +```{toctree} +:maxdepth: 1 + +hello-world +state-machine +protocol +``` diff --git a/docs/user_guide/high_res/micro-serve/protocol.md b/docs/user_guide/high_res/micro-serve/protocol.md index 4f6bacc81b3..b0809c71e5d 100644 --- a/docs/user_guide/high_res/micro-serve/protocol.md +++ b/docs/user_guide/high_res/micro-serve/protocol.md @@ -2,16 +2,20 @@ See the [state machine](state-machine.md) for connection, plate-handoff, and recovery diagrams. -The implementation follows documentation served by the MicroServe itself: - -- [MicroServe API, revision 756, May 29, 2020](http://10.253.253.253/support_files/files/MicroServe_API.pdf). -- [Operation notes](http://10.253.253.253/) and the illustrated plate-dimension instructions. -- The `info all` and `help` replies from firmware 2.7.0.756. -- The [REST command catalog](http://10.253.253.253:9095/MicroServe/AvailableCommands) and - [commissioning page](http://10.253.253.253/commissioning.html). - -These links require a direct connection to the controller. The factory's API and firmware help -define the wire commands; no vendor DLL is needed to run the driver. +The implementation follows documentation served by the MicroServe itself. To consult these sources, +connect to the controller as described in the [hello-world guide](hello-world.ipynb), then open its +address in a browser. These documents are available on the device and are not hosted by PyLabRobot. + +| Source | Location on the connected controller | +| --- | --- | +| MicroServe API, revision 756, May 29, 2020 | Web interface: Support Files → `MicroServe_API.pdf` (`/support_files/files/MicroServe_API.pdf`, HTTP port 80) | +| Operation notes and illustrated plate dimensions | Web interface home page (`/`, HTTP port 80) | +| Firmware 2.7.0.756 command help | `info all` and `help` over TCP port 1000 | +| REST command catalog | `/MicroServe/AvailableCommands`, HTTP port 9095 | +| Commissioning instructions | `/commissioning.html`, HTTP port 80 | + +The factory's API and firmware help define the wire commands; no vendor DLL is needed to run the +driver. ## Framing From ce2e08326cccc3a898a6a2f0a212ff127ed6ff5b Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Wed, 9 Sep 2026 09:30:55 -0700 Subject: [PATCH 4/8] Expand MicroServe measurement and recovery operations --- docs/_static/devices.json | 2 +- .../high_res/micro-serve/capabilities.md | 113 ++ .../high_res/micro-serve/hello-world.ipynb | 358 +++- .../micro-serve/images/state-machine.dot | 4 +- docs/user_guide/high_res/micro-serve/index.md | 2 + .../high_res/micro-serve/protocol.md | 33 +- .../high_res/micro-serve/state-machine.md | 31 +- .../high_res/micro-serve/validation.md | 75 + .../high_res/micro_serve/micro_serve.py | 303 ++- .../micro_serve/tests/capability_tests.py | 420 ++++ .../captures/barcode_geometry_error.json | 401 ++++ .../tests/captures/diagnostics.json | 1805 +++++++++++++++++ .../tests/captures/manual_laser.json | 713 +++++++ .../tests/captures/measurement_discovery.json | 1073 ++++++++++ .../tests/captures/measurement_empty.json | 299 +++ .../micro_serve/tests/micro_serve_tests.py | 3 +- 16 files changed, 5601 insertions(+), 34 deletions(-) create mode 100644 docs/user_guide/high_res/micro-serve/capabilities.md create mode 100644 docs/user_guide/high_res/micro-serve/validation.md create mode 100644 pylabrobot/high_res/micro_serve/tests/capability_tests.py create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/barcode_geometry_error.json create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/diagnostics.json create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/manual_laser.json create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/measurement_discovery.json create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/measurement_empty.json diff --git a/docs/_static/devices.json b/docs/_static/devices.json index 60c62d12051..e421ed545d7 100644 --- a/docs/_static/devices.json +++ b/docs/_static/devices.json @@ -596,7 +596,7 @@ "api_version": "v1", "code_slug": "high_res/micro_serve", "doc_slug": "high_res/micro-serve/hello-world", - "notes": "Homing, carousel, and empty load/retract verified on firmware 2.7.0.756; plate transfers and barcode scans unverified." + "notes": "Queries, homing, carousel, empty handoff, stack measurement, and manual access verified; plate transfers and successful barcode scans unverified." }, { "id": "highres-microspin", diff --git a/docs/user_guide/high_res/micro-serve/capabilities.md b/docs/user_guide/high_res/micro-serve/capabilities.md new file mode 100644 index 00000000000..8b9a11ba5ce --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/capabilities.md @@ -0,0 +1,113 @@ +# Capability coverage + +This audit covers the 64 TCP command names in firmware 2.7.0.756's `info all` reply and the +additional functions advertised by its REST catalog. Aliases and older commands often implement +the same capability, so command counts alone do not establish the registry's ≥90% threshold for +`full` support. The registry remains **WIP** while physical validation and maintenance coverage +are incomplete. + +## Implemented operations + +Methods below belong to `HighResMicroServe` unless prefixed with `stackers[i]`. +“Simulated” means the framing and control sequence have tests, but the operation has not passed a +physical test on this device. The API PDF's measurement examples also serve as parser fixtures. + +| Firmware command | Python interface | Verification | +| --- | --- | --- | +| `status` | `request_status()` | Hardware capture | +| `varstatus` | `request_variable_status()` | Hardware capture; compact report has no sensors | +| `version` | `request_version()` | Hardware capture | +| `firmwareversion` | `request_firmware_version()` | Hardware capture | +| `detailedversion` | `request_detailed_version()` | Hardware capture | +| `copleyserial` | `request_motor_information()` | Hardware capture | +| `commandstat` | `request_command_status(id)` | Hardware capture; reconciliation scenarios simulated | +| `history` | `request_history(count)` | Hardware capture | +| `errors` | `request_errors(count)` | Hardware capture | +| `settings` | `request_settings(search)` | Hardware capture; read only | +| `info`, `help` | `request_command_catalog()`, `request_command_help(command)` | Hardware capture | +| `limits` | `request_limits()` | Hardware capture; values are not usable as travel limits | +| `queryhomeoffset` | `request_home_offset(address)` | Hardware capture; motor label and native unit retained | +| `getangle` | `request_plate_angle()` | Hardware capture; cached report, unit unspecified | +| `microserveready` | `is_ready()` | Hardware capture; plate-presentation readiness | +| `getplatecounts` | `request_plate_counts()`, `stackers[i].request_plate_count()` | Hardware capture; approximate cached counts | +| `dimstatus` | `request_dimensions()` | Hardware capture | +| `setstackerdimensions` | `stackers[i].set_dimensions(d)` | Existing geometry written and verified on hardware | +| `home` | `home()` | Hardware captures, including return from manual access | +| `spin` | `stackers[i].move_to()` | All fourteen positions verified | +| `load` | `stackers[i].prepare_for_load(d)` | Empty receiving position verified; plate placement unverified | +| `unload` | `stackers[i].prepare_for_unload(d)` | Simulated | +| `unloadangle` | `stackers[i].prepare_for_unload_with_angle(d)` | Simulated; raw angle report has unspecified unit | +| `retract` | `retract()` | Empty loader verified | +| `readbarcodestacker` | `stackers[i].scan_barcodes(d)` | Geometry-error path captured; successful scans unverified | +| `countplates` | `stackers[i].count_plates(d)` | Simulated; moves hardware, then reads the updated count | +| `measurestacker` | `stackers[i].measure_height()` | Empty and single-plate hardware captures; returns millimeters; accuracy not independently checked | +| `calculateplatedimensions` | `calculate_plate_dimensions(count)` | API example and simulated sequence | +| `setplatecount` | `stackers[i].set_plate_count(count)` | Simulated; verified bookkeeping write | +| `manual` | `enter_manual_mode()` | Hardware capture, including repeated owned request; requires retraction before access | +| `clearabort` | `clear_abort()` | Simulated; does not resolve an interrupted handoff | +| `estoprecover` | `recover_from_estop()` | Simulated; physical recovery may move axes | +| `laser` | `set_barcode_laser(enabled)` | Hardware on/on/off command capture; no firmware optical-state readback | + +## Equivalent functions + +These commands do not need separate public wrappers to provide their underlying capability. + +| Firmware command | Existing interface | +| --- | --- | +| `list` | `request_command_catalog()` includes names and parameter descriptions | +| `disconnect` | `stop()` closes the transport | +| `loadl`, `unloadl` | Prepare the explicitly selected `stackers[i]` | +| `measure` | Measure the explicitly selected `stackers[i]` | +| `readbarcodeall` | Iterate the fourteen stackers with each stacker's geometry and collect scan results | +| `setplatedimensions`, `setplateheights`, `setplatethickness` | Set complete geometry per stacker; iterate for a machine-wide update | + +Per-stacker iteration is not an atomic machine-wide operation. Inspect status and retract if a +measurement or scan leaves the loader extended before starting the next stacker. Failed +measurements and scans block subsequent motion until inspected retraction. + +## Unimplemented maintenance and calibration + +These are coverage gaps, not silently excluded capabilities. They require protocol clarification, +calibrated travel bounds, or a dedicated commissioning procedure before implementation. + +| Firmware command | Remaining work | +| --- | --- | +| `jogb`, `jogc`, `joge`, `jogs` | Resolve the live firmware's position units and validate axis interlocks and travel bounds before exposing absolute targets | +| `jogbrel`, `jogcrel`, `jogerel`, `jogsrel` | Provide the corresponding motion capability through absolute targets; public relative jogs would violate the idempotent API guide | +| `homesel` | Resolve conflicting axis-number documentation and verify each axis's homing preconditions | +| `local`, `locall` | Verify directional carousel alignment and how it differs from positioning a selected stacker | +| `measureflag`, `measurestackerwidth` | Establish result format, calibration effects, and safe carousel alignment procedure | +| `beambreakheight` | Establish the returned unit and whether the TCP command applies or only measures the offset | +| `callaser` | Replace relative adjustments with a verified absolute calibration interface | +| `change`, `save`, `revert` | Define typed setting-specific APIs, readback, persistence, and recovery behavior | +| `savecvm` | Establish motor-program persistence and backup behavior | +| `reboot` | Establish reconnect behavior and invalidation of command IDs and handoff records | +| `sleep` | Firmware queue-testing utility; no public wrapper | + +The REST catalog additionally advertises **Download Copley Files** and **Update Beam Break Height**. +Neither has a verified Python implementation. The latter must be distinguished from the TCP +measurement before writing calibration. Firmware installation and network administration pages +are also outside the implemented driver interface. + +### Conflicting controller information + +The TCP `homesel` help assigns 1 to the effectuator and 2 to the spatula. The REST catalog assigns +1 to the spatula and 2 to the effectuator. Read-only `queryhomeoffset` calls report address 1 as +Spatula and address 2 as Effectuator, but Copley addresses do not prove the `homesel` mapping. +The driver exposes the offset query by address and does not guess an axis homing map. + +The `limits` reply contains a negative spatula maximum and zero effectuator maximum on the tested +machine. Those values are preserved for diagnosis and are not accepted as permissible travel. +Jog help describes micrometers for some axes, while the web commissioning controls and live +settings use different scales. Public physical units cannot be established from that help alone. + +The `manual` command clears homing and the selected stacker but can leave mode Ready in the status +reply. The driver checks the acknowledged transition and resulting sensors, and retains ownership +for repeated manual requests. An arbitrary unhomed machine is not assumed to be in manual mode. + +## Completing hardware validation + +See the [hardware validation procedure](validation.md) for the supervised sequence. To justify +`full`, complete that validation, resolve the maintenance gaps above, and review coverage by +distinct capabilities against the registry definition. Documentation and simulated tests alone +do not establish that plate handoffs or fault recovery work on hardware. diff --git a/docs/user_guide/high_res/micro-serve/hello-world.ipynb b/docs/user_guide/high_res/micro-serve/hello-world.ipynb index 8d28f26596f..d30d24a8e48 100644 --- a/docs/user_guide/high_res/micro-serve/hello-world.ipynb +++ b/docs/user_guide/high_res/micro-serve/hello-world.ipynb @@ -18,11 +18,13 @@ "| Verified controller | HRB-2008-10558, firmware 2.7.0.756 |\n", "| Public length units | millimeters |\n", "\n", - "**Plate transfers, barcode scanning, and physical fault recovery remain unverified.** Communication, homing, all fourteen carousel positions, and empty receiving-position preparation/retraction were checked on this controller. Review the physical setup before running each motion cell.\n", + "**Plate transfers, counting, plate-dimension calculation, successful barcode scanning, and physical fault recovery remain unverified.** Communication, homing, all fourteen carousel positions, and empty receiving-position preparation/retraction, stack-height measurement, and manual access were checked on this controller. Laser command exchanges and barcode geometry-error handling were also captured. Review the physical setup before running each motion cell.\n", "\n", "This driver prepares the MicroServe for a separate robot to place or pick plates. It does not model plate inventory in PyLabRobot resources. Reported plate counts are the controller's cached estimates.\n", "\n", - "See the [state machine](state-machine.md) for workflow and recovery diagrams, and [protocol and validation](protocol.md) for the device-source mapping and verification limits." + "See the [state machine](state-machine.md) for workflow and recovery diagrams, and [protocol and validation](protocol.md) for the device-source mapping and verification limits.\n", + "\n", + "The [capability audit](capabilities.md) lists implemented operations, hardware evidence, and maintenance gaps." ] }, { @@ -519,6 +521,354 @@ "await stacker.scan_barcodes(dimensions)" ] }, + { + "cell_type": "markdown", + "id": "dff75f8a", + "metadata": {}, + "source": [ + "## Firmware diagnostics\n", + "\n", + "The compact state, limit, angle, and home-offset reports retain device text. Axis limits are implausible on firmware 2.7.0.756; do not use them for travel bounds. Angle and home-offset units are undocumented." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb6058c5", + "metadata": {}, + "outputs": [], + "source": [ + "print(await microserve.request_variable_status())\n", + "print(await microserve.request_limits())\n", + "print(await microserve.request_plate_angle())\n", + "print(await microserve.request_home_offset(1))" + ] + }, + { + "cell_type": "markdown", + "id": "31ed063b", + "metadata": {}, + "source": [ + "## Firmware command help\n", + "\n", + "Read command descriptions without executing those commands." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eebd588f", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"\\n\".join(await microserve.request_command_help(\"measurestacker\")))\n", + "print(\"\\n\".join(await microserve.request_command_catalog()))" + ] + }, + { + "cell_type": "markdown", + "id": "9bad74fc", + "metadata": {}, + "source": [ + "## Measure stack height\n", + "\n", + "Keep the robot clear. This moves the loader to measure the stack relative to its calibrated beam and returns millimeters. A single upright plate measures its well thickness. Small negative empty-stack readings are retained for calibration diagnosis. The tested firmware leaves the loader extended after measurement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c427a72", + "metadata": {}, + "outputs": [], + "source": [ + "height = await stacker.measure_height()\n", + "print(height)" + ] + }, + { + "cell_type": "markdown", + "id": "7e869d34", + "metadata": {}, + "source": [ + "## Retract after measurement\n", + "\n", + "Inspect the measured stack and clear the robot before retraction. This must precede another stacker operation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66b83c26", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.retract()" + ] + }, + { + "cell_type": "markdown", + "id": "86c9be91", + "metadata": {}, + "source": [ + "## Actively count plates\n", + "\n", + "With correct plate geometry, measure this stacker and read the refreshed approximate count. This operation moves hardware; cached count queries do not. Inspect the loader afterward." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "279788ff", + "metadata": {}, + "outputs": [], + "source": [ + "count = await stacker.count_plates(dimensions)\n", + "print(count)" + ] + }, + { + "cell_type": "markdown", + "id": "4cb26fb7", + "metadata": {}, + "source": [ + "## Retract after counting\n", + "\n", + "Clear the robot and retract before another stacker operation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ffea129c", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.retract()" + ] + }, + { + "cell_type": "markdown", + "id": "0577a36c", + "metadata": {}, + "source": [ + "## Correct a cached count\n", + "\n", + "After physically inspecting an empty stacker, set its bookkeeping count to zero. This does not remove plates or measure occupancy. Supply the observed count for a nonempty stacker." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c56ccfea", + "metadata": {}, + "outputs": [], + "source": [ + "await stacker.set_plate_count(0)" + ] + }, + { + "cell_type": "markdown", + "id": "414827ca", + "metadata": {}, + "source": [ + "## Enter manual mode\n", + "\n", + "Finish any handoff and retract first. With the robot clear, release the carousel for manual access. Repeating the call in verified manual mode does not send another motion command." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc89af0b", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.enter_manual_mode()" + ] + }, + { + "cell_type": "markdown", + "id": "0d4419db", + "metadata": {}, + "source": [ + "## Return from manual access\n", + "\n", + "Finish manual access, remove your hands, and clear the robot before homing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59464d27", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.home()" + ] + }, + { + "cell_type": "markdown", + "id": "d50455d2", + "metadata": {}, + "source": [ + "## Enable the barcode laser\n", + "\n", + "The absolute on/off command does not move the scanner axis. Observe the laser state without looking into its beam; the firmware has no laser-state query." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62374c1a", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.set_barcode_laser(True)" + ] + }, + { + "cell_type": "markdown", + "id": "d29fe35d", + "metadata": {}, + "source": [ + "## Disable the barcode laser\n", + "\n", + "Turn the laser off after inspection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53593c6d", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.set_barcode_laser(False)" + ] + }, + { + "cell_type": "markdown", + "id": "f0cfec2a", + "metadata": {}, + "source": [ + "## Measure plate dimensions\n", + "\n", + "This requires the separate four-stacker arrangement described in the [validation procedure](validation.md): stacker 1 empty, an inverted plate in 2, an upright plate in 3, and at least three plates in 4. Confirm physical labels against indices and clear the robot. Inspect the returned dimensions before using them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43cf3681", + "metadata": {}, + "outputs": [], + "source": [ + "measured_dimensions = await microserve.calculate_plate_dimensions(count=3)\n", + "print(measured_dimensions)" + ] + }, + { + "cell_type": "markdown", + "id": "b3d603d7", + "metadata": {}, + "source": [ + "## Retract after dimension measurement\n", + "\n", + "Inspect the arrangement, clear the robot, and retract before changing stackers or geometry." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "260fb110", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.retract()" + ] + }, + { + "cell_type": "markdown", + "id": "c5bda22f", + "metadata": {}, + "source": [ + "## Prepare an unload with angle reporting\n", + "\n", + "This presents a plate for pickup and returns the firmware angle as text. The angle unit is undocumented. Repeating this preparation queries the cached angle without fetching another plate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6832009", + "metadata": {}, + "outputs": [], + "source": [ + "angle_report = await stacker.prepare_for_unload_with_angle(dimensions)\n", + "print(angle_report)" + ] + }, + { + "cell_type": "markdown", + "id": "efa63803", + "metadata": {}, + "source": [ + "## Finish the angle-reporting handoff\n", + "\n", + "Pick the presented plate and withdraw the robot before retracting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ad982f3", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.retract()" + ] + }, + { + "cell_type": "markdown", + "id": "c6c0263a", + "metadata": {}, + "source": [ + "## Clear an abort\n", + "\n", + "Use only after resolving the abort cause and clearing the robot. This clears a firmware latch; it neither homes nor resolves an uncertain plate handoff." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c70a2c72", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.clear_abort()" + ] + }, + { + "cell_type": "markdown", + "id": "24816990", + "metadata": {}, + "source": [ + "## Recover from an E-stop\n", + "\n", + "Inspect plate support, release the E-stop according to the manufacturer procedure, and clear the robot before this explicit recovery operation. It can move hardware. Do not race firmware automatic recovery. An unresolved handoff stays unresolved." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12327928", + "metadata": {}, + "outputs": [], + "source": [ + "await microserve.recover_from_estop()" + ] + }, { "cell_type": "markdown", "id": "87456a6e", @@ -530,7 +880,9 @@ "\n", "A timeout, cancelled task, or malformed reply closes the connection. The driver never automatically repeats a command. Closing the socket does not stop motion already executing. Inspect the physical machine before reconnecting with `setup()` and reading status.\n", "\n", - "An interrupted load/unload is retained in `unresolved_preparation`, blocking further preparation. The beam signal alone cannot identify which transfer completed. If the controller has not rebooted and no other client has moved it, `reconcile_preparation()` reads the original command record and current position to confirm success. Failed or unidentifiable commands stay unresolved. Explicit retraction ends a handoff only after the operator has inspected it and the robot is clear." + "An interrupted load/unload is retained in `unresolved_preparation`, blocking further preparation. The beam signal alone cannot identify which transfer completed. If the controller has not rebooted and no other client has moved it, `reconcile_preparation()` reads the original command record and current position to confirm success. Failed or unidentifiable commands stay unresolved. Explicit retraction ends a handoff only after the operator has inspected it and the robot is clear.\n", + "\n", + "Failed measurements and barcode scans retain `unresolved_operation`. This also survives reconnection and blocks fresh motion. After inspection, explicit `retract()` clears it only when retraction is confirmed." ] }, { diff --git a/docs/user_guide/high_res/micro-serve/images/state-machine.dot b/docs/user_guide/high_res/micro-serve/images/state-machine.dot index ddbc49e3734..f35ea0b1b3b 100644 --- a/docs/user_guide/high_res/micro-serve/images/state-machine.dot +++ b/docs/user_guide/high_res/micro-serve/images/state-machine.dot @@ -10,11 +10,11 @@ digraph microserve_activity { idle [pos="5,8!", label="RETRACTED / IDLE", penwidth=2, fillcolor="#dcfce7", color="#16a34a"]; positioning [pos="0.5,10!", label="POSITIONING\nmove_to()"]; - scanning [pos="9.5,10!", label="SCANNING\nscan_barcodes(d)"]; + scanning [pos="9.5,10!", label="MEASURING / SCANNING\nmeasure_height() · count_plates(d)\nscan_barcodes(d)"]; position_check [pos="2,8.8!", shape=plain, fontsize=12, fontcolor="#475569", label="target + sensors\nconfirmed"]; scan_check [pos="8,8.8!", shape=plain, fontsize=12, fontcolor="#475569", - label="inspect status after completion\nconfirm retraction"]; + label="idle verified; inspect loader\nretract before next operation"]; preparing_load [pos="2,5.2!", label="PREPARING TO LOAD\nprepare_for_load(d)\napply geometry · load(i)"]; preparing_unload [pos="8,5.2!", label="PREPARING TO UNLOAD\nprepare_for_unload(d)\napply geometry · unload(i)"]; receiving [pos="2,2.4!", label="READY TO RECEIVE\nowned load handoff", diff --git a/docs/user_guide/high_res/micro-serve/index.md b/docs/user_guide/high_res/micro-serve/index.md index 86291fa06a0..5bbb39a1db6 100644 --- a/docs/user_guide/high_res/micro-serve/index.md +++ b/docs/user_guide/high_res/micro-serve/index.md @@ -10,4 +10,6 @@ plate handoffs and recovery; the protocol reference describes commands and valid hello-world state-machine protocol +capabilities +validation ``` diff --git a/docs/user_guide/high_res/micro-serve/protocol.md b/docs/user_guide/high_res/micro-serve/protocol.md index b0809c71e5d..f68bea5ed7c 100644 --- a/docs/user_guide/high_res/micro-serve/protocol.md +++ b/docs/user_guide/high_res/micro-serve/protocol.md @@ -1,6 +1,8 @@ # MicroServe protocol and validation See the [state machine](state-machine.md) for connection, plate-handoff, and recovery diagrams. +The [capability audit](capabilities.md) covers every advertised TCP command and remaining +maintenance gaps; the [hardware procedure](validation.md) describes pending supervised tests. The implementation follows documentation served by the MicroServe itself. To consult these sources, connect to the controller as described in the [hello-world guide](hello-world.ipynb), then open its @@ -77,7 +79,7 @@ the order plate height, stack pitch, plate thickness. Height includes the lid wh Cached counts do not measure the physical stack. `countplates`, `measurestacker`, and barcode scans perform motion and must not be included in a read-only verification run. -The public API does not expose relative jogs, toggles, calibration changes, or arbitrary commands. +The public API does not expose relative jogs, toggles, arbitrary settings changes, or arbitrary commands. The firmware also advertises maintenance operations; listing them does not establish a validated physical sequence or calibrated travel limits for the Python driver. @@ -116,6 +118,20 @@ operator has inspected the machine and the robot is clear. Only this driver should control the MicroServe during a handoff. Command IDs reset on controller restart. Reconnecting does not home, clear an abort, or infer success from the beam signal. +Measurements and barcode scans verify homed, idle, ready-mode status after completion. They do +not promise a retracted loader: `measurestacker` leaves it extended on the tested firmware. +Inspect the result and explicitly retract before another stacker operation. Failed or interrupted +measurements and scans retain `unresolved_operation`, which survives reconnection and blocks new +motion until inspected retraction. Read-only diagnostics remain available. + +`enter_manual_mode()` requires a clear, retracted loader and verifies the manual transition before returning. +On firmware that leaves mode Ready stale, this means an acknowledged manual command followed by +unhomed, unselected, idle, retracted status. That owned transition makes repeated requests idempotent. +`home()` returns from manual access. `clear_abort()` clears the abort latch and checks status; +`recover_from_estop()` invokes firmware recovery only when the machine is not already homed and +ready. Recovery can move axes and never resolves an uncertain plate handoff by itself. Physical +fault transitions and their mode spellings still require validation. + The controller exposes axis jogs, but its documentation does not specify the complete sensor-driven transfer sequence needed to reconstruct load/unload from those jogs. The driver uses the documented load/unload operations for that sequence, while managing geometry, command ownership, state checks, @@ -138,3 +154,18 @@ timeout, cancellation, reply mismatch, unresolved preparation, and sensor disagr warns about these limits. Empty-machine tests do not establish pickup height with a plate, gripper clearance, plate compatibility, or E-stop recovery. Those need a separate physical validation with known plates and robot clearance. + +On September 9, 2026, measurements of stackers 0–4 returned 0.059–0.086 mm and a user-loaded +single plate was located in stacker 5 with a 13.629 mm measurement. Measurements left the loader +extended; explicit retraction was verified between stackers. These readings were not compared +with an independent dimensional measurement. + +A barcode scan with the saved 11/10/10 mm geometry failed with `Invalid plate count, check plate +dimensions`. The driver retained an unresolved operation and the diagnostic reply. That capture +verifies error handling, not successful barcode support. No transfer geometry was inferred from +this failure. + +Manual access, repeated manual requests, return to homed/ready operation, and laser on/on/off +command exchanges were also captured. No additional errors appeared and plate geometry remained +unchanged. There is no laser-state readback, so the command capture does not independently verify +optical output. diff --git a/docs/user_guide/high_res/micro-serve/state-machine.md b/docs/user_guide/high_res/micro-serve/state-machine.md index 6ea065c6cf4..d4139c30541 100644 --- a/docs/user_guide/high_res/micro-serve/state-machine.md +++ b/docs/user_guide/high_res/micro-serve/state-machine.md @@ -6,20 +6,21 @@ transition. Start with the [hello-world guide](hello-world.ipynb) for connection The phase names below describe workflows; the driver does not expose a VSpin-style `.state` object. It combines fresh `request_status()` replies with a recorded handoff and, after an -interrupted preparation, `unresolved_preparation`. +interrupted preparation, `unresolved_preparation`. Failed measurements and barcode scans retain +`unresolved_operation` until inspected retraction. ## Activity overview ```{graphviz} images/state-machine.dot :layout: neato :alt: MicroServe workflows share a retracted, idle state. Carousel positioning returns after verification. Load and unload preparation lead to separate owned handoffs; the external robot transfers a plate before explicit retraction returns to idle. -:caption: Purple nodes are operations; green nodes are confirmed resting or handoff conditions. Dashed blue loops are actions by the external robot. Barcode completion returns data without a final position check, so inspect status before further motion. Failure paths are shown in the recovery diagram. +:caption: Purple nodes are operations; green nodes are confirmed resting or handoff conditions. Dashed blue loops are actions by the external robot. Measurements and scans verify idle status but can leave the loader extended; inspect and retract before returning to the central state. Failure paths are shown in the recovery diagram. ``` {download}`Download the activity diagram source ` The central state requires a connection, homing, ready mode, no active operation, no unresolved -preparation, and a retracted loader. Rotation, preparation, and scanning also require a clear +preparation or measurement, and a retracted loader. Rotation, preparation, and scanning also require a clear plate-detection beam. Each public operation holds the device lock for its entire sequence; another operation or status query waits for that lock. @@ -49,7 +50,14 @@ is not owned by this driver cannot be adopted by calling prepare again. `setup()` opens the TCP connection. `home()` is an explicit motion operation that verifies homed, idle status. A machine already reporting homed and ready mode does not receive another home -command. An unresolved preparation must be handled before homing. +command. An unresolved preparation or measurement must be handled before homing. + +`enter_manual_mode()` releases the carousel only after the loader is retracted and the beam is +clear. Firmware 2.7.0.756 can keep reporting mode Ready after successful manual access. The driver +then requires an acknowledged manual command followed by unhomed, unselected, retracted, idle +status. It records that ownership so a repeated request sends no additional command; unhomed +status alone does not establish manual access. Reconnecting discards that ownership. Finish +manual access and clear the robot before calling `home()` to restore homed, ready operation. `stop()` closes the connection and discards confirmed handoff ownership. It does not abort a running command, retract the loader, or clear an unresolved preparation. Reconnecting does not @@ -62,6 +70,7 @@ status = await microserve.request_status() print(status.homed, status.busy, status.mode, status.stacker) print(status.loader_retracted, status.loader_extended) print(microserve.unresolved_preparation) +print(microserve.unresolved_operation) ``` | Condition | How the driver determines it | @@ -71,6 +80,7 @@ print(microserve.unresolved_preparation) | Loader extended | Firmware reports extension, both loader home sensors off, and lock engaged: `status.loader_extended` | | Readiness to present a plate | `await microserve.is_ready()`; false while retracted, true at the tested receiving position | | Interrupted load/unload | `microserve.unresolved_preparation` retains direction, stacker, and the ACK identifier when available | +| Interrupted measurement or scan | `microserve.unresolved_operation` retains the command until inspected retraction | | Plate-detection beam | `status.plate_sensor_blocked`; this does not establish occupancy or plate count | Firmware 2.7.0.756 can retain `loaderStatus Extended` after successful retraction. The driver @@ -104,12 +114,21 @@ the pending handoff. It requires homed, idle, ready-mode status, and only clears the home and lock sensors confirm retraction. If those prerequisites are unavailable, resolve the physical condition before attempting another driver operation. -These recovery records specifically protect load/unload. Other failed operations still require -inspection and fresh status; the driver does not maintain a general recovery flag for every axis. +Failed measurements and barcode scans have a separate `unresolved_operation` record. They follow +the inspection and retraction path, without command-record reconciliation. Both recovery records +survive reconnects. Other failed operations still require inspection and fresh status; the driver +does not maintain a general recovery flag for every axis. + +`clear_abort()` and `recover_from_estop()` preserve these unresolved records. Resolve the physical +fault first, then inspect and reconcile or retract the original operation. Recovery never +automatically replays a plate transfer. ## Hardware verification Homing, all fourteen carousel positions, repeated requests, empty receiving-position preparation, retraction, and subsequent rotation were verified on firmware 2.7.0.756. The diagrams also include unloading, robot transfers, barcode scanning, and recovery paths that remain unverified on hardware. +Stack-height measurement, retraction after measurement, and manual access followed by homing +were also captured. A barcode geometry-error capture validates the failure path; successful reads +remain unverified. See [protocol and validation](protocol.md) for the captures and exact limits. diff --git a/docs/user_guide/high_res/micro-serve/validation.md b/docs/user_guide/high_res/micro-serve/validation.md new file mode 100644 index 00000000000..ab72c84957d --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/validation.md @@ -0,0 +1,75 @@ +# Hardware validation procedure + +The implemented operations and their evidence are listed in the [capability audit](capabilities.md). +The sequence below is pending physical validation. Run it with an operator present, the robot +clear, known plate geometry, and suitable empty plates. Record the commands and replies with +PyLabRobot's I/O capture facilities. Stop on an unexpected state or measurement; inspect before +choosing recovery, and never automatically repeat an interrupted plate operation. + +## Empty-machine checks + +1. Query identity, status, errors, dimensions, and cached counts. Confirm the intended controller, + a retracted loader, and no plates in the selected stacker or transfer position. Save geometry + and cached counts for comparison after testing. +2. Enter manual mode and verify the carousel is released. Repeat the call and confirm there is + no second `manual` command. Clear the machine, call `home()`, and verify homed/idle/ready. +3. Measure the empty selected stacker three times. Record the actual reply format and convert + micrometers to millimeters. Results should be close to zero within the commissioned tolerance. + Inspect loader sensors after each operation and retract if necessary. A discrepancy requires + calibration investigation, not an automatic offset adjustment. +4. With known geometry, actively count that empty stacker and verify the cached result is zero. + Repeat to check consistency. Verify an unchanged cached-count target sends no bookkeeping write. +5. Command the barcode laser on, on again, then off while observing its state without looking + into the beam. No scanner-axis motion should occur. + +## Plate handoffs, measurement, and barcodes + +Use at least three compatible empty plates with known, readable barcodes. Measure plate height, +stack pitch, and well thickness as described in the hello-world guide. Do not substitute the +11/10/10 mm geometry from the empty-machine captures for the actual plates. + +1. Prepare an empty receiving position using the plate's geometry. Verify extension and selected + stacker. Repeat the preparation before placing a plate and confirm no second load command. +2. The operator places one plate at the documented receiving position and withdraws. Explicitly + retract and inspect that the plate is supported in the stacker. Repeat this handoff to build + the test stack, observing each placement and retraction. +3. Measure and actively count the stack. Compare the result against the physical plate count + and measured dimensions; repeated measurements should agree within the commissioned tolerance. +4. Scan that stacker, record the exact barcode reply format, and compare every returned barcode + against the physical stack. Do not interpret a missing barcode as proof of an empty position. +5. Prepare one plate for unloading, inspect its support and pickup position, then repeat the + preparation and confirm no second plate is fetched. The operator removes the presented plate, + withdraws, and explicitly retracts the loader. +6. Repeat using angle-reporting preparation. Record the raw angle; establish its unit before + using it to command a robot rotation. A repeated preparation must only query the cached angle. +7. Repeat until the test stack is empty. Compare counts, sensors, errors, and geometry against + the expected end state. Verify a final carousel move with the loader retracted. + +## Four-stacker dimension calculation + +This is a separate setup. The firmware procedure specifies stacker 1 empty, one upside-down plate +in stacker 2, one upright plate in stacker 3, and at least three plates in stacker 4. Confirm the +physical labeling against command indices before placing plates. Run `calculate_plate_dimensions` +and compare every returned dimension with independent measurements. Query settings and geometry +before and after to establish firmware calibration side effects; do not save or apply new values +without reviewing them. + +## Fault recovery + +Fault-state spelling, E-stop auto-recovery, and physical plate support during an interruption +remain unverified. Validate them separately, beginning with an empty machine: + +1. Confirm the manufacturer's E-stop procedure and observe the stopped device's status. Check + whether firmware starts recovery automatically when the E-stop is released. +2. Once the cause is resolved and the robot is clear, validate explicit abort clearance or + E-stop recovery as appropriate. Verify homed/idle/ready and actual loader sensor state; + neither call should erase an unresolved plate handoff. +3. Validate a deliberately interrupted measurement with a controlled connection loss. Reconnect + and confirm the uncertainty blocks fresh motion. After inspection, explicit retraction must + clear it without repeating the measurement. +4. Only after empty-machine recovery is established, assess an interrupted loaded handoff with + known plate support. Validate exact-command reconciliation or inspected retraction, ensuring + that no path fetches a second plate. + +Do not induce a jam or collision to test an error handler. Simulated transport failures cover +those software paths while the physical recovery procedure is being established. diff --git a/pylabrobot/high_res/micro_serve/micro_serve.py b/pylabrobot/high_res/micro_serve/micro_serve.py index a4336d7fca0..0ba5a7a9f1a 100644 --- a/pylabrobot/high_res/micro_serve/micro_serve.py +++ b/pylabrobot/high_res/micro_serve/micro_serve.py @@ -119,7 +119,7 @@ class MicroServeMotorInfo: class MicroServeUnresolvedPreparation: """A load/unload whose completion or final position could not be confirmed.""" - direction: Literal["load", "unload"] + direction: Literal["load", "unload", "unloadangle"] stacker: int command_id: Optional[int] @@ -169,9 +169,11 @@ class HighResMicroServe: be repeated until its result is reconciled or the handoff explicitly ended by retraction. The plate-detection beam is not used to infer occupancy. - Communication, homing, all carousel positions, and empty receiving-position - preparation/retraction were checked on firmware 2.7.0.756. Transfers with - plates, barcode scanning, and physical fault recovery remain unverified. + Communication, homing, all carousel positions, empty receiving-position + preparation/retraction, stack-height measurement, manual access, and laser + command exchanges were checked on firmware 2.7.0.756. Transfers with plates, + counting, plate-dimension calculation, successful barcode scanning, and + physical fault recovery remain unverified. """ def __init__( @@ -201,6 +203,8 @@ def __init__( self._last_command_id: Optional[int] = None self._prepared: Optional[Tuple[str, int]] = None self._unresolved_preparation: Optional[MicroServeUnresolvedPreparation] = None + self._unresolved_operation: Optional[str] = None + self._manual_mode = False self.stackers = tuple(MicroServeStacker(self, index) for index in range(14)) @property @@ -213,6 +217,11 @@ def unresolved_preparation(self) -> Optional[MicroServeUnresolvedPreparation]: """The load/unload that requires reconciliation before another preparation.""" return self._unresolved_preparation + @property + def unresolved_operation(self) -> Optional[str]: + """Measurement or barcode command requiring inspection and explicit retraction.""" + return self._unresolved_operation + async def setup(self) -> None: """Connect without homing, clearing faults, or changing settings.""" async with self._lock: @@ -226,8 +235,8 @@ async def setup(self) -> None: self._connected = True logger.info("Connected to MicroServe") logger.warning( - "MicroServe transfers with plates, barcode scanning, and physical fault recovery " - "are unverified; validate before use" + "MicroServe transfers with plates, counting, plate-dimension calculation, successful " + "barcode scanning, and physical fault recovery are unverified; validate before use" ) async def stop(self) -> None: @@ -239,6 +248,7 @@ async def _close(self) -> None: """Invalidate the session and close its transport.""" self._connected = False self._prepared = None + self._manual_mode = False await self.io.stop() async def _command(self, command: str, timeout: Optional[float] = None) -> Tuple[str, ...]: @@ -311,6 +321,51 @@ async def request_status(self) -> MicroServeStatus: async with self._lock: return await self._status() + async def request_variable_status(self) -> str: + """Read the compact firmware state report, which omits sensors and positions.""" + async with self._lock: + return "\n".join(await self._command("varstatus")) + + async def request_limits(self) -> str: + """Read the firmware's native axis-limit report for diagnostics. + + Firmware 2.7.0.756 returns implausible values in this report. Do not use it + to establish permissible travel or convert its values into physical units. + """ + async with self._lock: + return "\n".join(await self._command("limits")) + + async def request_plate_angle(self) -> str: + """Read the last computed plate angle as text; firmware does not document its unit. + + This is a cached measurement, not a live measurement of the current plate. + """ + async with self._lock: + return "\n".join(await self._command("getangle")) + + async def request_home_offset(self, address: int) -> str: + """Read a motor controller's native home-offset report without moving it. + + ``address`` is a Copley address (0–3), not a ``homesel`` axis number. + The report retains the firmware's motor label and undocumented native unit. + """ + if isinstance(address, bool) or not isinstance(address, int) or not 0 <= address <= 3: + raise ValueError("address must be an integer from 0 through 3") + async with self._lock: + return "\n".join(await self._command(f"queryhomeoffset {address}")) + + async def request_command_help(self, command: str) -> Tuple[str, ...]: + """Read firmware help for a command name without executing that command.""" + if re.fullmatch(r"[a-zA-Z]+", command) is None: + raise ValueError("command must contain only ASCII letters") + async with self._lock: + return await self._command(f"help {command}") + + async def request_command_catalog(self) -> Tuple[str, ...]: + """Read user and maintenance command descriptions directly from firmware.""" + async with self._lock: + return await self._command("info all") + async def request_version(self) -> str: """Return the human-readable controller identification report.""" async with self._lock: @@ -446,6 +501,122 @@ async def request_dimensions(self) -> Dict[int, MicroServePlateDimensions]: async with self._lock: return await self._dimensions() + async def set_barcode_laser(self, enabled: bool) -> None: + """Set the barcode laser on or off without moving its axis. + + The controller acknowledges the requested state but exposes no laser-state + readback. Observe the scanner to verify it; do not look into its beam. + """ + if not isinstance(enabled, bool): + raise ValueError("enabled must be a boolean") + async with self._lock: + self._require_idle(await self._status(), homed=False) + logger.info("Setting MicroServe barcode laser %s", "on" if enabled else "off") + await self._command(f"laser {'on' if enabled else 'off'}") + + async def enter_manual_mode(self) -> None: + """Retract the axes and release the carousel for manual access. + + Finish any robot handoff and retract the loader first. Keep the robot clear. + Call ``home()`` to return to automatic operation after manual access. + Firmware can retain mode Ready after manual access. In that case an + acknowledged manual command plus unhomed/unselected status establishes + ownership; an arbitrary unhomed machine is not assumed to be released. + """ + async with self._lock: + self._require_resolved_preparation() + status = await self._status() + if status.busy or status.mode not in ("ready", "manual"): + raise RuntimeError(f"MicroServe cannot enter manual mode: {status.raw}") + if not status.loader_retracted or status.plate_sensor_blocked: + raise RuntimeError("Clear the transfer position and retract before manual access") + if status.mode == "manual" or ( + self._manual_mode and not status.homed and status.stacker is None + ): + return + logger.info("Releasing MicroServe carousel for manual access") + self._manual_mode = False + await self._command("manual") + status = await self._status() + manual_state = status.mode == "manual" or ( + status.mode == "ready" and not status.homed and status.stacker is None + ) + if status.busy or not manual_state or not status.loader_retracted: + raise RuntimeError(f"Manual mode was not confirmed: {status.raw}") + self._prepared = None + self._manual_mode = True + + async def clear_abort(self) -> None: + """Clear the firmware abort latch after the operator resolves its cause. + + This does not home or reconcile an interrupted handoff. It can enable later + motion, so inspect the machine and clear the robot before calling it. + """ + async with self._lock: + status = await self._status() + if status.busy: + raise RuntimeError("Wait for the running command before clearing an abort") + logger.info("Clearing MicroServe abort latch") + await self._command("clearabort") + status = await self._status() + if status.busy or status.mode not in ("ready", "manual"): + raise RuntimeError(f"Abort clearance was not confirmed: {status.raw}") + + async def recover_from_estop(self) -> None: + """Run firmware E-stop recovery after inspection and release of the E-stop. + + Recovery can move hardware. Check plate support and robot clearance first. + A homed, idle, ready machine needs no recovery. An unresolved handoff stays + unresolved; inspect and retract it explicitly before another preparation. + """ + async with self._lock: + status = await self._status() + if status.busy: + raise RuntimeError("Wait for firmware recovery or the running command to finish") + if status.mode == "ready" and status.homed: + return + if status.mode in ("ready", "manual"): + raise RuntimeError("Use home() to leave manual mode or home an unhomed machine") + logger.info("Recovering MicroServe from E-stop") + self._prepared = None + await self._command("estoprecover") + self._require_idle(await self._status()) + + async def calculate_plate_dimensions(self, count: int) -> MicroServePlateDimensions: + """Measure plate geometry using the manufacturer's four-stacker procedure. + + Firmware instructions specify stacker 1 empty, one upside-down plate in + stacker 2, one upright plate in stacker 3, and ``count`` plates (at least + three) in stacker 4. Confirm this arrangement and clear the robot first. + This moves the carousel and loader. Returned dimensions are in millimeters; + inspect them before supplying them to a transfer or barcode operation. + """ + self._validate_count(count, "count") + if count < 3: + raise ValueError("count must be at least three") + async with self._lock: + self._require_resolved_preparation() + status = await self._status() + self._require_idle(status) + if not status.loader_retracted or status.plate_sensor_blocked: + raise RuntimeError("Clear the transfer position and retract before measuring dimensions") + logger.info("Measuring MicroServe plate dimensions with %d stacked plates", count) + lines = await self._measurement(f"calculateplatedimensions {count}") + values = {} + for line in lines: + match = re.fullmatch(r"(Plate Height|Plate Thickness|Stack Height):\s*(\d+)", line.strip()) + if match is not None: + if match[1] in values: + raise MicroServeProtocolError(f"Duplicate dimension in measurement: {lines!r}") + values[match[1]] = int(match[2]) / 1000 + if set(values) != {"Plate Height", "Plate Thickness", "Stack Height"}: + raise MicroServeProtocolError(f"Incomplete plate-dimension measurement: {lines!r}") + return MicroServePlateDimensions( + height=values["Plate Height"], + stack_height=values["Stack Height"], + thickness=values["Plate Thickness"], + ) + @staticmethod def _require_idle(status: MicroServeStatus, homed: bool = True) -> None: """Reject motion in an unknown, busy, or inappropriate operating state.""" @@ -454,12 +625,32 @@ def _require_idle(status: MicroServeStatus, homed: bool = True) -> None: def _require_resolved_preparation(self) -> None: """Block new motion when the previous transfer preparation is uncertain.""" + if self._unresolved_operation is not None: + raise RuntimeError( + f"Operation {self._unresolved_operation!r} is unresolved. Inspect the machine, " + "then explicitly retract before another motion." + ) if self._unresolved_preparation is not None: raise RuntimeError( "A transfer preparation is unresolved. Inspect the machine and call " "reconcile_preparation(), or explicitly retract to end the handoff." ) + async def _measurement(self, command: str) -> Tuple[str, ...]: + """Run a measurement with final-state verification while holding the lock. + + Interrupted or failed motion stays unresolved across reconnects. Retraction + after inspection ends that uncertainty; the command is never replayed. + """ + self._prepared = None + try: + lines = await self._command(command, timeout=self.scan_timeout) + self._require_idle(await self._status()) + return lines + except BaseException: + self._unresolved_operation = command + raise + async def reconcile_preparation(self) -> None: """Confirm an interrupted preparation using read-only command and status queries. @@ -496,10 +687,12 @@ async def home(self) -> None: if status.busy or status.mode not in ("ready", "manual"): raise RuntimeError(f"MicroServe cannot home in this state: {status.raw}") if status.homed and status.mode == "ready": + self._manual_mode = False return if status.plate_sensor_blocked: raise RuntimeError("Clear the plate-detection beam before homing") logger.info("Homing MicroServe") + self._manual_mode = False await self._command("home") self._require_idle(await self._status()) self._prepared = None @@ -517,6 +710,7 @@ async def retract(self) -> None: if status.loader_retracted: self._prepared = None self._unresolved_preparation = None + self._unresolved_operation = None return logger.info("Retracting MicroServe loader") await self._command("retract") @@ -526,6 +720,7 @@ async def retract(self) -> None: raise RuntimeError(f"Loader did not retract: {status.raw}") self._prepared = None self._unresolved_preparation = None + self._unresolved_operation = None class MicroServeStacker: @@ -550,13 +745,73 @@ def index(self) -> int: async def request_plate_count(self) -> int: """Read this stacker's cached approximate count without physical measurement.""" async with self._device._lock: - lines = await self._device._command(f"getplatecounts {self.index}") - if len(lines) != 1: - raise MicroServeProtocolError(f"Invalid plate count reply: {lines!r}") - match = re.fullmatch(rf"{self.index}: (\d+)", lines[0]) - if match is None: - raise MicroServeProtocolError(f"Invalid plate count reply: {lines!r}") - return int(match[1]) + return await self._plate_count() + + async def _plate_count(self) -> int: + """Read this stacker's cached count while holding the controller lock.""" + lines = await self._device._command(f"getplatecounts {self.index}") + if len(lines) != 1: + raise MicroServeProtocolError(f"Invalid plate count reply: {lines!r}") + match = re.fullmatch(rf"{self.index}: (\d+)", lines[0]) + if match is None: + raise MicroServeProtocolError(f"Invalid plate count reply: {lines!r}") + return int(match[1]) + + async def set_plate_count(self, count: int) -> None: + """Set and verify the cached count after manually inspecting this stacker. + + This changes bookkeeping only; it neither measures nor moves plates. + """ + if isinstance(count, bool) or not isinstance(count, int) or count < 0: + raise ValueError("count must be a nonnegative integer") + async with self._device._lock: + self._device._require_resolved_preparation() + self._device._require_idle(await self._device._status(), homed=False) + if await self._plate_count() == count: + return + logger.info("Setting MicroServe stacker %d cached plate count to %d", self.index, count) + await self._device._command(f"setplatecount {self.index} {count}") + if await self._plate_count() != count: + raise MicroServeProtocolError(f"Stacker {self.index} did not accept the plate count") + + async def count_plates(self, dimensions: MicroServePlateDimensions) -> int: + """Physically measure this stacker and return its updated approximate count. + + This moves the carousel and loader. The result depends on plate geometry + and calibration; it is not a barcode inventory. Keep the robot clear. + """ + async with self._device._lock: + await self._require_measurement_position() + await self._set_dimensions(dimensions) + logger.info("Counting plates in MicroServe stacker %d", self.index) + await self._device._measurement(f"countplates {self.index}") + return await self._plate_count() + + async def _require_measurement_position(self) -> None: + """Require a resolved handoff and a clear, retracted loader before measurement.""" + self._device._require_resolved_preparation() + status = await self._device._status() + self._device._require_idle(status) + if not status.loader_retracted or status.plate_sensor_blocked: + raise RuntimeError("Clear the transfer position and retract before measuring a stacker") + + async def measure_height(self) -> float: + """Move the loader to measure this stacker's contents; return millimeters. + + The measurement is relative to the calibrated detection beam. Small + negative results on an empty stacker are retained for calibration diagnosis. + Keep the robot clear. No plate dimensions are changed by the driver. + """ + async with self._device._lock: + await self._require_measurement_position() + logger.info("Measuring MicroServe stacker %d height", self.index) + lines = await self._device._measurement(f"measurestacker {self.index}") + if len(lines) != 1 or re.fullmatch(r"[+-]?\d+(?:\.\d+)?", lines[0].strip()) is None: + raise MicroServeProtocolError(f"Invalid stack-height measurement: {lines!r}") + result = float(lines[0]) / 1000 + if not math.isfinite(result): + raise MicroServeProtocolError(f"Nonfinite stack-height measurement: {lines!r}") + return result async def _set_dimensions(self, dimensions: MicroServePlateDimensions) -> None: """Apply and verify geometry while holding the device's operation lock.""" @@ -593,8 +848,8 @@ async def move_to(self) -> None: raise RuntimeError(f"Carousel did not reach stacker {self.index}: {status.raw}") async def _prepare( - self, direction: Literal["load", "unload"], dimensions: MicroServePlateDimensions - ) -> None: + self, direction: Literal["load", "unload", "unloadangle"], dimensions: MicroServePlateDimensions + ) -> Tuple[str, ...]: """Apply geometry and reach the requested transfer state without blind retries.""" async with self._device._lock: self._device._require_resolved_preparation() @@ -604,7 +859,7 @@ async def _prepare( if at_target and self._device._prepared == (direction, self.index): if (await self._device._dimensions())[self.index]._wire() != dimensions._wire(): raise RuntimeError("Retract the loader before changing geometry at the transfer position") - return + return await self._device._command("getangle") if direction == "unloadangle" else () if status.plate_sensor_blocked or not status.loader_retracted: raise RuntimeError("Finish the current robot handoff and retract before preparing another") await self._set_dimensions(dimensions) @@ -612,7 +867,7 @@ async def _prepare( command_id = None self._device._prepared = None try: - await self._device._command(f"{direction} {self.index}") + lines = await self._device._command(f"{direction} {self.index}") command_id = self._device.last_command_id status = await self._device._status() self._device._require_idle(status) @@ -626,6 +881,7 @@ async def _prepare( ) raise self._device._prepared = (direction, self.index) + return lines async def prepare_for_load(self, dimensions: MicroServePlateDimensions) -> None: """Present an empty receiving position for the robot to place a plate. @@ -644,6 +900,15 @@ async def prepare_for_unload(self, dimensions: MicroServePlateDimensions) -> Non """ await self._prepare("unload", dimensions) + async def prepare_for_unload_with_angle(self, dimensions: MicroServePlateDimensions) -> str: + """Present a plate for picking and return the firmware's angle report as text. + + The firmware documentation does not define the angle unit. Do not assume + degrees or radians. Repeating this owned preparation queries the cached + angle without moving another plate. Retract after the robot clears the nest. + """ + return "\n".join(await self._prepare("unloadangle", dimensions)) + async def scan_barcodes(self, dimensions: MicroServePlateDimensions) -> Tuple[str, ...]: """Move the barcode reader and return the controller's unmodified data lines. @@ -659,6 +924,4 @@ async def scan_barcodes(self, dimensions: MicroServePlateDimensions) -> Tuple[st raise RuntimeError("Clear the transfer position and retract before scanning barcodes") await self._set_dimensions(dimensions) logger.info("Scanning MicroServe stacker %d", self.index) - return await self._device._command( - f"readbarcodestacker {self.index}", timeout=self._device.scan_timeout - ) + return await self._device._measurement(f"readbarcodestacker {self.index}") diff --git a/pylabrobot/high_res/micro_serve/tests/capability_tests.py b/pylabrobot/high_res/micro_serve/tests/capability_tests.py new file mode 100644 index 00000000000..c66e226f13b --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/capability_tests.py @@ -0,0 +1,420 @@ +"""Diagnostic captures and simulated measurement, manual-mode, and recovery exchanges.""" + +import asyncio +import unittest +from pathlib import Path + +from pylabrobot.high_res.micro_serve import ( + HighResMicroServe, + MicroServeError, + MicroServePlateDimensions, + MicroServeProtocolError, + MicroServeUnresolvedPreparation, +) +from pylabrobot.high_res.micro_serve.tests.micro_serve_tests import ( + DIMENSIONS, + GEOMETRY, + FakeSocket, + status, +) +from pylabrobot.io.capture import CaptureReader +from pylabrobot.io.socket import SocketValidator + + +class MicroServeCapabilityTests(unittest.IsolatedAsyncioTestCase): + """Verify the public operations through a serialized simulated transport.""" + + async def asyncSetUp(self) -> None: + """Connect without opening a hardware socket.""" + self.driver = HighResMicroServe("192.0.2.1") + self.io = FakeSocket() + self.driver.io = self.io # type: ignore[assignment] + await self.driver.setup() + + async def test_native_diagnostics_retain_device_units_and_implausible_limits(self) -> None: + """Reports captured from firmware 2.7.0.756 must not become guessed travel limits.""" + limits = "Spatula -2030043136, Effectuator 0, Barcode 1082617856, Carousel 0, 1080688640" + variables = ( + "OK! status homed, stackerNumber 2, machineState NotBusy, loaderStatus Extended, mode Ready" + ) + self.io.add("limits", limits) + self.io.add("varstatus", variables) + self.io.add("getangle", "0.0000") + self.io.add("queryhomeoffset 1", "Spatula: 0.517") + self.io.add("help estoprecover", "estoprecover - Recover from an EStop.") + self.io.add("info all", "clearabort, ca - Clear abort state.") + self.assertEqual(await self.driver.request_limits(), limits) + self.assertEqual(await self.driver.request_variable_status(), variables) + self.assertEqual(await self.driver.request_plate_angle(), "0.0000") + self.assertEqual(await self.driver.request_home_offset(1), "Spatula: 0.517") + self.assertIn("EStop", (await self.driver.request_command_help("estoprecover"))[0]) + self.assertIn("clearabort", (await self.driver.request_command_catalog())[0]) + + async def test_invalid_arguments_never_reach_transport(self) -> None: + """Reject command injection, ambiguous booleans, and invalid calibration counts.""" + for name in ("home\nmanual", "home 0", "", "é"): + with self.subTest(name=name), self.assertRaises(ValueError): + await self.driver.request_command_help(name) + for value in (-1, 4, True, 0.5): + with self.subTest(address=value), self.assertRaises(ValueError): + await self.driver.request_home_offset(value) # type: ignore[arg-type] + for value in (-1, True, 0.5): + with self.subTest(count=value), self.assertRaises(ValueError): + await self.driver.stackers[0].set_plate_count(value) # type: ignore[arg-type] + for value in (0, 1, 2, True): + with self.subTest(calibration_count=value), self.assertRaises(ValueError): + await self.driver.calculate_plate_dimensions(value) + with self.assertRaises(ValueError): + await self.driver.set_barcode_laser(1) # type: ignore[arg-type] + self.assertEqual(self.io.writes, []) + + async def test_cached_count_setter_verifies_and_skips_repeated_target(self) -> None: + """A bookkeeping correction is acknowledged and read back, including zero.""" + self.io.add("status", status()) + self.io.add("getplatecounts 2", "2: 3") + self.io.add("setplatecount 2 0") + self.io.add("getplatecounts 2", "2: 0") + self.io.add("status", status()) + self.io.add("getplatecounts 2", "2: 0") + await self.driver.stackers[2].set_plate_count(0) + await self.driver.stackers[2].set_plate_count(0) + self.assertEqual(self.io.writes.count(b"setplatecount 2 0\n"), 1) + + async def test_rejected_cached_count_is_not_reported_as_success(self) -> None: + """An OK envelope does not replace setting readback.""" + self.io.add("status", status()) + self.io.add("getplatecounts 0", "0: 0") + self.io.add("setplatecount 0 2") + self.io.add("getplatecounts 0", "0: 0") + with self.assertRaises(MicroServeProtocolError): + await self.driver.stackers[0].set_plate_count(2) + + async def test_counting_applies_geometry_and_returns_refreshed_count(self) -> None: + """An active count must run before reading the count cache, under one lock.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 2 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("countplates 2") + self.io.add("status", status(stacker=2)) + self.io.add("getplatecounts 2", "2: 3") + self.io.add("getangle", "0.0000") + result, _ = await asyncio.gather( + self.driver.stackers[2].count_plates(GEOMETRY), self.driver.request_plate_angle() + ) + self.assertEqual(result, 3) + self.assertEqual(self.io.writes[-1], b"getangle\n") + + async def test_height_measurement_converts_microns_and_retains_negative_offset(self) -> None: + """The operation-notes calibration workflow needs small negative empty readings.""" + for reply, expected in (("25000", 25), ("-125.5", -0.1255)): + with self.subTest(reply=reply): + self.io.add("status", status()) + self.io.add("measurestacker 2", reply) + self.io.add("status", status(stacker=2)) + self.assertEqual(await self.driver.stackers[2].measure_height(), expected) + self.assertFalse(any(b"dimensions" in command for command in self.io.writes)) + + async def test_height_measurement_rejects_missing_or_non_numeric_results(self) -> None: + """Do not mistake diagnostics, nonfinite values, or multiple readings for a height.""" + for lines in ((), ("nan",), ("100", "200"), ("Error 1",)): + with self.subTest(lines=lines): + self.io.add("status", status()) + self.io.add("measurestacker 0", *lines) + self.io.add("status", status()) + with self.assertRaises(MicroServeProtocolError): + await self.driver.stackers[0].measure_height() + + async def test_measurements_refuse_unsafe_initial_states(self) -> None: + """No measurement starts while unhomed, busy, extended, or beam-blocked.""" + for initial in ( + status(homed=False), + status().replace("NotBusy", "Busy"), + status(extended=True), + status(plate=True), + ): + with self.subTest(initial=initial): + self.io.add("status", initial) + with self.assertRaises(RuntimeError): + await self.driver.stackers[0].measure_height() + self.assertEqual(self.io.writes, [b"status\n"] * 4) + + async def test_interrupted_measurement_survives_reconnect_until_inspected_retraction( + self, + ) -> None: + """An uncertain measurement must not permit a later carousel move after reconnect.""" + self.io.add("status", status()) + self.io.scripts.append( + ("measurestacker 2", [b"ACK! measurestacker 2 50\n", TimeoutError("lost reply")]) + ) + with self.assertRaises(TimeoutError): + await self.driver.stackers[2].measure_height() + await self.driver.setup() + self.assertEqual(self.driver.unresolved_operation, "measurestacker 2") + with self.assertRaisesRegex(RuntimeError, "unresolved"): + await self.driver.stackers[3].move_to() + with self.assertRaisesRegex(RuntimeError, "unresolved"): + await self.driver.home() + self.io.add("status", status(stacker=2)) + await self.driver.retract() + self.assertIsNone(self.driver.unresolved_operation) + self.assertEqual(self.io.writes.count(b"measurestacker 2\n"), 1) + + async def test_scan_failure_and_non_idle_final_status_remain_unresolved(self) -> None: + """A completion failure or busy final state cannot be accepted as a completed scan.""" + for completion in ("ERROR", "OK"): + with self.subTest(completion=completion): + self.io.add("status", status()) + self.io.add("setstackerdimensions 0 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("readbarcodestacker 0", completion=completion) + if completion == "OK": + self.io.add("status", status().replace("NotBusy", "Busy")) + with self.assertRaises((MicroServeError, RuntimeError)): + await self.driver.stackers[0].scan_barcodes(GEOMETRY) + self.assertEqual(self.driver.unresolved_operation, "readbarcodestacker 0") + self.io.add("status", status()) + await self.driver.retract() + + async def test_dimension_calculation_uses_documented_pdf_example(self) -> None: + """Parse the API revision 756 example into PLR millimeters.""" + self.io.add("status", status()) + self.io.add( + "calculateplatedimensions 3", + "SPATULA_BEAM_BREAK_HEIGHT: 600", + "Plate Height: 60000", + "Plate Thickness: 50000", + "Stack Height: 55000", + ) + self.io.add("status", status(stacker=4)) + self.assertEqual( + await self.driver.calculate_plate_dimensions(3), MicroServePlateDimensions(60, 55, 50) + ) + + async def test_manual_mode_is_verified_and_repeated_access_does_not_move(self) -> None: + """Manual access requires a retracted loader and leaves homing explicit.""" + manual = status().replace("mode Ready", "mode Manual") + self.io.add("status", status()) + self.io.add("manual") + self.io.add("status", manual) + self.io.add("status", manual) + await self.driver.enter_manual_mode() + await self.driver.enter_manual_mode() + self.assertEqual(self.io.writes.count(b"manual\n"), 1) + self.assertNotIn(b"home\n", self.io.writes) + self.io.add("status", status(extended=True)) + with self.assertRaisesRegex(RuntimeError, "retract"): + await self.driver.enter_manual_mode() + + async def test_manual_mode_rejects_incorrect_final_state(self) -> None: + """The firmware ACK cannot establish that the carousel is released.""" + self.io.add("status", status()) + self.io.add("manual") + self.io.add("status", status()) + with self.assertRaisesRegex(RuntimeError, "not confirmed"): + await self.driver.enter_manual_mode() + + async def test_stale_ready_mode_requires_owned_manual_command(self) -> None: + """Unhomed and unselected status alone does not prove the carousel was released.""" + manual = status(homed=False, stacker=-1) + self.io.add("status", manual) + self.io.add("manual") + self.io.add("status", manual) + self.io.add("status", manual) + await self.driver.enter_manual_mode() + await self.driver.enter_manual_mode() + self.assertEqual(self.io.writes.count(b"manual\n"), 1) + await self.driver.stop() + await self.driver.setup() + self.io.add("status", manual) + self.io.add("manual") + self.io.add("status", manual) + await self.driver.enter_manual_mode() + self.assertEqual(self.io.writes.count(b"manual\n"), 2) + + async def test_recovery_keeps_unresolved_handoff_and_skips_already_ready_machine(self) -> None: + """Recovery must neither replay unload nor silently restore its ownership.""" + pending = MicroServeUnresolvedPreparation("unload", 2, 50) + self.driver._unresolved_preparation = pending + # Fault mode spelling is simulated; physical fault transitions are unverified. + fault = status(homed=False).replace("mode Ready", "mode Estop") + self.io.add("status", fault) + self.io.add("estoprecover") + self.io.add("status", status()) + self.io.add("status", status()) + await self.driver.recover_from_estop() + await self.driver.recover_from_estop() + self.assertEqual(self.driver.unresolved_preparation, pending) + self.assertEqual(self.io.writes.count(b"estoprecover\n"), 1) + + async def test_recovery_refuses_busy_manual_and_normally_unhomed_states(self) -> None: + """Recovery cannot race an executing command or substitute for explicit homing.""" + for initial in ( + status().replace("NotBusy", "Busy"), + status().replace("mode Ready", "mode Manual"), + status(homed=False), + ): + self.io.add("status", initial) + with self.assertRaises(RuntimeError): + await self.driver.recover_from_estop() + self.assertEqual(self.io.writes, [b"status\n"] * 3) + + async def test_clear_abort_preserves_uncertain_transfer_and_checks_status(self) -> None: + """Clearing a fault latch must not resolve plate ownership or trigger homing.""" + pending = MicroServeUnresolvedPreparation("load", 2, 50) + self.driver._unresolved_preparation = pending + self.io.add("status", status().replace("mode Ready", "mode Aborted")) + self.io.add("clearabort") + self.io.add("status", status(homed=False)) + await self.driver.clear_abort() + self.assertEqual(self.driver.unresolved_preparation, pending) + self.assertNotIn(b"home\n", self.io.writes) + self.io.add("status", status().replace("NotBusy", "Busy")) + with self.assertRaises(RuntimeError): + await self.driver.clear_abort() + + async def test_laser_uses_absolute_state_without_axis_motion(self) -> None: + """Enabling twice remains an on request, not a toggle.""" + for enabled in (True, True, False): + self.io.add("status", status()) + self.io.add(f"laser {'on' if enabled else 'off'}") + await self.driver.set_barcode_laser(enabled) + self.assertEqual(self.io.writes.count(b"laser on\n"), 2) + self.assertEqual(self.io.writes.count(b"laser off\n"), 1) + + async def test_angle_preparation_repeat_queries_angle_without_unloading_again(self) -> None: + """An angle request cannot fetch a second plate after an owned preparation.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 2 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("unloadangle 2", "0.1013") + self.io.add("status", status(stacker=2, extended=True)) + self.io.add("status", status(stacker=2, extended=True)) + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("getangle", "0.1013") + self.assertEqual( + await self.driver.stackers[2].prepare_for_unload_with_angle(GEOMETRY), "0.1013" + ) + self.assertEqual( + await self.driver.stackers[2].prepare_for_unload_with_angle(GEOMETRY), "0.1013" + ) + self.assertEqual(self.io.writes.count(b"unloadangle 2\n"), 1) + + +class MicroServeDiagnosticCaptureTests(unittest.IsolatedAsyncioTestCase): + """Replay the complete read-only diagnostic session recorded on the controller.""" + + async def test_diagnostic_capture(self) -> None: + """Queries preserve the physical status and consume every captured reply.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "diagnostics.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + before = await driver.request_status() + self.assertIn("stackerNumber 2", await driver.request_variable_status()) + self.assertIn("Spatula -2030043136", await driver.request_limits()) + self.assertEqual(await driver.request_plate_angle(), "0.0000") + for address, expected in enumerate( + ("Carousel: -24.389", "Spatula: 0.517", "Effectuator: 0.813", "Barcode: 4.804") + ): + self.assertEqual(await driver.request_home_offset(address), expected) + self.assertIn("microns", (await driver.request_command_help("measurestacker"))[0]) + self.assertIn("homesel, hs", "\n".join(await driver.request_command_catalog())) + self.assertEqual(before, await driver.request_status()) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_empty_measurement_leaves_loader_extended(self) -> None: + """Replay the measured empty-stack offset and require explicit retraction afterward.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "measurement_empty.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + self.assertTrue((await driver.request_status()).loader_retracted) + self.assertEqual((await driver.request_dimensions())[0], GEOMETRY) + self.assertEqual(await driver.stackers[0].measure_height(), 0.069) + result = await driver.request_status() + self.assertTrue(result.loader_extended) + self.assertTrue(result.plate_sensor_blocked) + self.assertIsNone(driver.unresolved_operation) + self.assertEqual(await driver.request_errors(), ()) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_plate_discovery_measurements_and_retractions(self) -> None: + """Replay discovery of the single loaded stacker without changing geometry.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "measurement_discovery.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + await driver.retract() + for index, expected in enumerate((0.086, 0.064, 0.059, 0.059, 13.629), start=1): + self.assertEqual(await driver.stackers[index].measure_height(), expected) + measured = await driver.request_status() + self.assertEqual(measured.stacker, index) + self.assertTrue(measured.loader_extended) + await driver.retract() + self.assertTrue((await driver.request_status()).loader_retracted) + self.assertEqual(await driver.request_errors(), ()) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_actual_barcode_geometry_error_preserves_unresolved_operation(self) -> None: + """Invalid geometry is a firmware failure, not an empty or successful barcode scan.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "barcode_geometry_error.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + self.assertTrue((await driver.request_status()).loader_retracted) + geometry = (await driver.request_dimensions())[5] + with self.assertRaisesRegex(MicroServeError, "Invalid plate count"): + await driver.stackers[5].scan_barcodes(geometry) + self.assertEqual(driver.unresolved_operation, "readbarcodestacker 5") + self.assertTrue((await driver.request_status()).loader_extended) + self.assertIn("Invalid plate count", (await driver.request_errors())[0]) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_manual_home_and_laser_capture(self) -> None: + """Replay stale-mode manual ownership, explicit home, and absolute laser commands.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "manual_laser.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + self.assertTrue((await driver.request_status()).loader_retracted) + geometry = await driver.request_dimensions() + errors = await driver.request_errors() + await driver.enter_manual_mode() + manual = await driver.request_status() + self.assertFalse(manual.homed) + self.assertIsNone(manual.stacker) + self.assertEqual(manual.mode, "ready") + await driver.enter_manual_mode() + self.assertTrue((await driver.request_status()).loader_retracted) + await driver.home() + self.assertTrue((await driver.request_status()).homed) + for enabled in (True, True, False): + await driver.set_barcode_laser(enabled) + self.assertTrue((await driver.request_status()).loader_retracted) + self.assertEqual(await driver.request_errors(), errors) + self.assertEqual(await driver.request_dimensions(), geometry) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() diff --git a/pylabrobot/high_res/micro_serve/tests/captures/barcode_geometry_error.json b/pylabrobot/high_res/micro_serve/tests/captures/barcode_geometry_error.json new file mode 100644 index 00000000000..9f2d4d2b9bc --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/barcode_geometry_error.json @@ -0,0 +1,401 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203331350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203331350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203331360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203331360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203331370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203331370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203331380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203331380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031313030302031303030302031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203131303030203130303030203130303030203331390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203131303030203130303030203130303030203331390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203332300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203332300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "72656164626172636f6465737461636b657220350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072656164626172636f6465737461636b65722035203332310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4552524f52212072656164626172636f6465737461636b65722035203332310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203332320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932383839342c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203332320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203332330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203332330a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve/tests/captures/diagnostics.json b/pylabrobot/high_res/micro_serve/tests/captures/diagnostics.json new file mode 100644 index 00000000000..74828fec7c7 --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/diagnostics.json @@ -0,0 +1,1805 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203235310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203235310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7661727374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120766172737461747573203235320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120766172737461747573203235320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6c696d6974730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206c696d697473203235330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53706174756c61202d323033303034333133362c20204566666563747561746f7220302c2020426172636f646520313038323631373835362c20204361726f7573656c20302c20313038303638383634300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206c696d697473203235330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574616e676c650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574616e676c65203235340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "302e303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574616e676c65203235340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7175657279686f6d656f666673657420300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207175657279686f6d656f66667365742030203235350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4361726f7573656c3a202d32342e3338390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207175657279686f6d656f66667365742030203235350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7175657279686f6d656f666673657420310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207175657279686f6d656f66667365742031203235360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53706174756c613a20302e3531370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207175657279686f6d656f66667365742031203235360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7175657279686f6d656f666673657420320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207175657279686f6d656f66667365742032203235370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4566666563747561746f723a20302e3831330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207175657279686f6d656f66667365742032203235370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7175657279686f6d656f666673657420330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207175657279686f6d656f66667365742033203235380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "426172636f64653a20342e3830340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207175657279686f6d656f66667365742033203235380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "68656c70206d656173757265737461636b65720a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212068656c70206d656173757265737461636b6572203235390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6d656173757265737461636b65722c206d73202d204d6561737572652074686520686569676874206f66207468652073656c656374656420737461636b6572277320636f6e74656e74732c2072657475726e20726573756c7420696e206d6963726f6e732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732074686520737461636b65722077686f73652068656967687420746f206d6561737572652e2052616e676520302d31332e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212068656c70206d656173757265737461636b6572203235390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "696e666f20616c6c0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120696e666f20616c6c203236300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6368616e67652c206368202d204368616e67652074686520696e2d6d656d6f72792076616c7565206f6620612073657474696e67732066696c652073657474696e672e202054686973206368616e6765206973206c6f73740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7768656e20746865207365727665722072657374617274732e200a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732832202d203135293a203c53455454494e473e203c6e65772076616c75653e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c53455454494e473e20697320746865206e616d65206f66207468652073657474696e672066726f6d207468652073657474696e67732066696c6520746f206368616e67652e2020410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020206c697374206f662074686573652063616e20626520666f756e642062792069737375696e6720746865202773657474696e67732720636f6d6d616e642e203c6e65772076616c75653e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020206973207468652076616c756520746f206368616e676520697420746f2e20204e6f746520746861742076616c756573207769746820656d626564646564207370616365732063616e6e6f740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020202062652061737369676e656420696e2074686973207761792e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "636c65617261626f72742c206361202d20436c6561722061626f72742073746174652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "636f6d6d616e64737461742c206373746174202d204765747320737461747573206f66206120636f6d6d616e642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c636f6d6d616e642069643e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020200a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "636f706c657973657269616c2020202d204765742073657269616c206e756d62657220616e64206e616d6520737472696e672066726f6d206d6f746f7220636f6e74726f6c6c6572732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "64657461696c656476657273696f6e2020202d2052657475726e2076657273696f6e2064657461696c732061626f757420616c6c2068617264776172652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "646973636f6e6e6563742c2064202d20436c6f7365207468652063757272656e7420636c69656e74277320636f6e6e656374696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6572726f72732c2065202d20446973706c61792074686520746f70203130206572726f7273206f6e20746865206572726f7220737461636b2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732830202d2031293a205b3c6e3e5d0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020204f7074696f6e616c20706172616d65746572203c6e3e2073706563696669657320746865206d6178206e756d626572206f66206572726f727320746f20646973706c61792e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6669726d7761726576657273696f6e2c20667776657273696f6e202d20446973706c617920746865206669726d776172652076657273696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "68656c702020202d20446973706c6179732074686520706172616d6574657220696e666f726d6174696f6e20666f72206120737065636966696320636f6d6d616e642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c636f6d6d616e643e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020205768657265203c636f6d6d616e643e20697320746865206e616d65206f662074686520636f6d6d616e6420746f207669657720696e666f726d6174696f6e2061626f75742e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "686973746f72792c206869202d20446973706c617920746865206c61737420313020636f6d6d616e64732073656e7420746f20746865207365727665722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732830202d2031293a205b3c6e3e5d0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020204f7074696f6e616c20706172616d65746572203c6e3e2073706563696669657320746865206d6178206e756d626572206f6620686973746f727920636f6d6d616e647320746f0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020646973706c61792e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "696e666f2c203f3f202d20446973706c61797320746865206c697374206f66207573657220636f6d6d616e6473207769746820706172616d6574657220696e666f726d6174696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732830202d2031293a205b616c6c5d0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020202049662027616c6c27206973207370656369666965642c206d61696e74656e616e636520636f6d6d616e64732077696c6c20626520696e636c756465642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c617365722c206c61202d205475726e206f6e2074686520626172636f6465206c61736572206f6e206f72206f66662e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c6f6e7c6f66663e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020206f6e206f72206f66660a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6973742c203f202d20446973706c61797320746865206c697374206f66207573657220636f6d6d616e647320746861742074686520736572766572207265636f676e697a65732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732830202d2031293a205b616c6c5d0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020202049662027616c6c27206973207370656369666965642c206d61696e74656e616e636520636f6d6d616e64732077696c6c20626520696e636c756465642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7175657279686f6d656f66667365742c2071686f202d20446973706c61792074686520686f6d65206f666673657420666f722074686520676976656e206d6f746f7220616464726573730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c616464726573733e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c616464726573733e20737065636966696573207468652061646472657373206f662074686520617869732c20696520636f706c657920616464726573730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7265626f6f742020202d205265626f6f7420746865206465766963652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7265766572742020202d205265766572742074686520696e2d6d656d6f72792076616c756573206f66207468652073657474696e677320746f207468652073746f7265642076616c7565732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "736176652020202d20536176652074686520696e2d6d656d6f72792076616c7565206f66207468652073657474696e677320746f207065726d616e656e74206d656d6f72792e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7361766563766d2020202d2053617665207468652043564d2070726f6772616d20666f7220616c6c20436f706c657920636f6e74726f6c6c6572730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "73657474696e67732c207373202d204c697374207468652073657474696e67732066696c652073657474696e677320617320746865792061726520696e206d656d6f72792e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732830202d2031293a205b3c73656172636820737472696e673e5d0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020204966203c73656172636820737472696e673e20697320737065636966696564206f6e6c7920636f6d6d616e6473207468617420636f6e7461696e2074686520737472696e67206172652073686f776e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "736c6565702020202d20536c656570657220636f6d6d616e6420666f722074657374696e672074686520636f6d6d616e642071756575652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c733e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c733e20697320746865206e756d626572206f66207365636f6e647320746f20736c65657020666f722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "76657273696f6e2c2076202d2052657475726e2074686520736f6674776172652076657273696f6e207265706f72742e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6265616d627265616b6865696768742c20626268202d2043616c63756c61746520746865206265616d20627265616b206865696768742e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732074686520737461636b657220746f2075736520647572696e67207468652063616c63756c6174696f6e2c20302d31332e20205468697320706f736974696f6e206d75737420636f6e7461696e206e6f20706c617465732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "63616c63756c617465706c61746564696d656e73696f6e732c20637064202d2043616c63756c617465207468652064696d656e73696f6e7320666f72206120676976656e20706c61746520747970652e20204d616b652073757265207468617420737461636b6572203120697320656d7074792e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020202020202020506c616365203120706c6174652075707369646520646f776e20696e20737461636b657220322e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020202020202020506c616365203120706c617465207269676874207369646520757020696e20737461636b657220332e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020202020202020506c616365203c636f756e743e20706c6174657320696e20737461636b657220342e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c636f756e743e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c636f756e743e20697320746865206e756d626572206f6620706c6174657320696e20737461636b6572203420286d7573742062652033206f722067726561746572292e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "63616c6c617365722020202d204368616e676520746865206c617365722073746172742073746f7020616e64206379636c6520746865206c61736572206f66662f6f6e2e204d616b65207375726520746f202273617665220a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732832293a203c6c6566742061646a7573746d656e743e203c72696768742061646a7573746d656e743e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c6c6566742061646a7573746d656e743e2070657263656e7420746f206368616e67652063757272656e742073657474696e6720627920666f72206c6566742065646765206f66206c617365720a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c72696768742061646a7573746d656e743e2070657263656e7420746f206368616e67652063757272656e742073657474696e6720627920666f722072696768742065646765206f66206c617365720a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "636f756e74706c617465732020202d20436f756e742074686520706c6174657320696e203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e20746f20636f756e742074686520706c6174657320696e2c20302d31332e202049662027616c6c2720697320676976656e20666f72203c737461636b65723e20616c6c20737461636b6572732077696c6c20626520636f756e7465642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "64696d7374617475732c206473202d2052657475726e732074686520706c6174652064696d656e73696f6e7320627920737461636b65722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6573746f707265636f7665722020202d205265636f7665722066726f6d20616e204553746f702e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "676574616e676c652020202d2052657475726e7320746865206c61737420636f6d707574656420706c61746520616e676c652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "676574706c617465636f756e74732020202d20446973706c61792074686520617070726f78696d61746520636f756e74206f6620706c6174657320696e206561636820737461636b65720a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732830202d2031293a205b3c737461636b65723e5d0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f7074696f6e616c205b3c737461636b65723e5d2069732074686520737461636b6572206e756d62657220746f207265706f72742074686520636f756e74206f662e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "686f6d652c2068202d2053746172742064657669636520686f6d696e672073657175656e63652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "686f6d6573656c2c206873202d20486f6d6520612073696e676c652061786973206f6e6c792e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c617869733e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c617869733e20697320616e20696e74656765722066726f6d203020746f20332c2077686572652030203d206361726f7573656c2c2031203d206566666563747561746f722c2032203d0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020202073706174756c612c2033203d20626172636f64650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6a6f67622c206a62202d204a6f672074686520626172636f64652072656164657220746f20616e206162736f6c75746520706f736974696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c706f736974696f6e3e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c706f736974696f6e3e20697320746865206162736f6c75746520706f736974696f6e20696e206d6963726f6e732066726f6d2074686520626f74746f6d20746f206d6f766520746f2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6a6f676272656c2c207262202d204a6f6720626172636f646520612072656c61746976652064697374616e63652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c64697374616e63653e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c64697374616e63653e20697320746865206e756d626572206f66206d6963726f6e7320746f206a6f6720746865207265616465722062792e2020557020697320706f7369746976652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6a6f67632c206a63202d204a6f6720746865206361726f7573656c20746f20616e206162736f6c75746520706f736974696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c706f736974696f6e3e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c706f736974696f6e3e20697320746865206162736f6c75746520706f736974696f6e20746f206d6f766520746865206361726f7573656c20746f0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6a6f676372656c2c207263202d204a6f6720746865206361726f7573656c20627920612072656c61746976652064697374616e63652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c64697374616e63653e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c64697374616e63653e20697320746865206e756d626572206f66207374657070657220636f756e747320746f206a6f6720746865206361726f7573656c2062792e20506f7369746976652069730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020636c6f636b776973650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6a6f67652c206a65202d204a6f6720746865206566666563747561746f7220746f20616e206162736f6c75746520706f736974696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c706f736974696f6e3e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c706f736974696f6e3e20697320746865206162736f6c75746520706f736974696f6e20696e206d6963726f6e732066726f6d2074686520626f74746f6d20746f206d6f766520746f2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6a6f676572656c2c207265202d204a6f67206566666563747561746f72206120676976656e2064697374616e63652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c64697374616e63653e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c64697374616e63653e206973207468652072656c61746976652064697374616e636520746f206a6f6720746865206566666563747561746f722062790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6a6f67732c206a73202d204a6f67207468652073706174756c6120746f20616e206162736f6c75746520706f736974696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c706f736974696f6e3e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c706f736974696f6e3e20697320746865206162736f6c75746520706f736974696f6e20746f206d6f766520746f2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6a6f677372656c2c207273202d204a6f672073706174756c61206120676976656e2064697374616e63652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c64697374616e63653e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c64697374616e63653e20697320746865206e756d626572206f66206d6963726f6e7320746f206a6f67207468652073706174756c612062792e2020557020697320706f7369746976652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c696d6974732c206c73202d2052657475726e7320746865206d6178696d756d206178697320636f756e742076616c7565732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6f61642c206c202d20506c616365206120676976656e20737461636b657220696e746f20746865206c6f616420706f736974696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732061206e756d62657220696e207468652072616e67652030202d203133207468617420696e6469636174657320776869636820737461636b657220746f206c6f61642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6f61646c2c206c6c202d204c6f6164207468652063757272656e7420737461636b65722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6f63616c2c206c68202d20486f6d6520746865206361726f7573656c20746f2074686520737461636b65722066726f6d20746865206e6567617469766520646972656374696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6f63616c6c2c206c6f202d20486f6d6520746865206361726f7573656c20746f2074686520737461636b65722066726f6d2074686520706f73697469766520646972656374696f6e2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6d616e75616c2c206d202d2053746f702065766572797468696e672c20726574726163742065766572797468696e672c2072656c6561736520746865206361726f7573656c2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6d6561737572652c206d65202d204d6561737572652074686520686569676874206f66207468652063757272656e7420737461636b6572277320636f6e74656e74732c2072657475726e20726573756c7420696e206d6963726f6e732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6d656173757265666c61672020202d204d656173757265207468652073697a65206f6620746865206e65787420666c61672e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6d656173757265737461636b657277696474682020202d204d6561737572652074686520706f736974696f6e206f6620616c6c20666c6167732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6d656173757265737461636b65722c206d73202d204d6561737572652074686520686569676874206f66207468652073656c656374656420737461636b6572277320636f6e74656e74732c2072657475726e20726573756c7420696e206d6963726f6e732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732074686520737461636b65722077686f73652068656967687420746f206d6561737572652e2052616e676520302d31332e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6d6963726f736572766572656164792c206d737265616479202d2052657475726e203120696620746865204d6963726f53657276652f4e616e6f536572766520697320726561647920746f2070726573656e74206120706c61746520746f2074686520726f626f742c20616e642030206f74686572776973650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "72656164626172636f6465616c6c2c206261202d205265616420616c6c20626172636f64657320696e20746865206d616368696e652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "72656164626172636f6465737461636b65722c206273202d205265616420616c6c20626172636f64657320696e206120737461636b65722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732074686520737461636b20746f207265616420626172636f6465732066726f6d2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "726574726163742c207274202d2052657472616374207468652073706174756c6120616e64206566666563747561746f7220746f20746865697220686f6d6520706f736974696f6e732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "736574706c617465636f756e742020202d205365742074686520706c61746520636f756e7420666f722074686520676976656e20737461636b65722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732832293a203c737461636b65723e203c636f756e743e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7365742074686520706c61746520636f756e74206f66203c737461636b65723e2c20302d31332c20746f203c636f756e743e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "736574706c61746564696d656e73696f6e732c20737064202d20536574732074686520706c617465206865696768742c20737461636b206865696768742c20616e6420706c61746520746869636b6e65737320666f7220616c6c20737461636b6572732e205468697320636f6d6d616e6420636f6d62696e65732074686520736574706c6174656865696768747320616e6420736574706c617465746869636b6e6573732066756e6374696f6e732c207768696368206172652072657461696e656420666f72206261636b7761726420636f6d7061746962696c6974792077697468206f6c64657220647269766572732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732833293a203c706c617465206865696768743e203c737461636b206865696768743e203c706c61746520746869636b6e6573733e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c706c617465206865696768743e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520626f74746f6d206f66206120706c61746520746f2069747320746f702e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c737461636b206865696768743e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520626f74746f6d206f66206120706c61746520746f207468650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020626f74746f6d206f6620746865206e65787420706c617465207768656e20746865792061726520737461636b65642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c706c61746520746869636b6e6573733e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520746f70206f66206120706c61746520746f207468650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020626f74746f6d2073757266616365206f66206974732077656c6c732077686572652074686520706c617465207265737473206f6e207468652073706174756c61207768656e2069740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020202069732061742074686520626f74746f6d206f66206120737461636b2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "736574706c617465686569676874732c20737068202d205365742074686520706c6174652068656967687420616e6420737461636b206865696768742076616c75657320666f7220616c6c20737461636b6572732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732832293a203c706c617465206865696768743e203c737461636b206865696768743e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c706c617465206865696768743e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520626f74746f6d206f66206120706c61746520746f2069747320746f702e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c737461636b206865696768743e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520626f74746f6d206f66206120706c61746520746f207468650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020626f74746f6d206f6620746865206e65787420706c617465207768656e20746865792061726520737461636b65642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "736574706c617465746869636b6e6573732c20737074202d20536574732074686520706c617465206865696768742c20737461636b206865696768742c20616e6420706c61746520746869636b6e65737320666f7220612073656c656374656420737461636b65722e20546869732069732075736566756c20696e20616e20656e7669726f6e6d656e742077697468206d756c7469706c6520706c61746520747970657320696e2073696d756c74616e656f7573207573652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c706c61746520746869636b6e6573733e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c706c61746520746869636b6e6573733e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520746f70206f66206120706c61746520746f20746865200a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020626f74746f6d2073757266616365206f66206974732077656c6c73207768657265207468652073706174756c61207265737473207768656e206974732074686520626f74746f6d0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020706c617465206f6e2074686520737461636b2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "736574737461636b657264696d656e73696f6e732c20737364202d20536574732074686520706c617465206865696768742c20737461636b206865696768742c20616e6420706c61746520746869636b6e65737320666f7220612073656c656374656420737461636b65722e20546869732069732075736566756c20696e20616e20656e7669726f6e6d656e742077697468206d756c7469706c6520706c61746520747970657320696e2073696d756c74616e656f7573207573652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732834293a203c737461636b65723e203c706c617465206865696768743e203c737461636b206865696768743e203c706c61746520746869636b6e6573733e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732074686520737461636b6572206e756d6265722077686f736520706c617465206865696768747320746f2061646a7573742e2052616e67653a20302d31332e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c706c617465206865696768743e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520626f74746f6d206f66206120706c61746520746f2069747320746f702e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c737461636b206865696768743e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520626f74746f6d206f66206120706c61746520746f207468650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020626f74746f6d206f6620746865206e65787420706c617465207768656e20746865792061726520737461636b65642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "20202020203c706c61746520746869636b6e6573733e206973207468652064697374616e636520696e206d6963726f6e732066726f6d2074686520746f70206f66206120706c61746520746f20746865200a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020626f74746f6d2073757266616365206f66206974732077656c6c732077686572652074686520706c617465207265737473206f6e207468652073706174756c61207768656e206974200a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "202020202069732061742074686520626f74746f6d206f66206120737461636b2e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7370696e2c207370202d205370696e20746865206361726f7573656c20746f206120676976656e20737461636b65722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732061206e756d62657220696e207468652072616e67652030202d203133207468617420696e6469636174657320746f20776869636820737461636b657220746f20726f746174652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7374617475732c2073202d2052657475726e732074686520737461747573206f6620746865206465766963652e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "756e6c6f61642c2075202d20556e6c6f6164206120706c6174652066726f6d206120676976656e20737461636b65722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732061206e756d62657220696e207468652072616e67652030202d203133207468617420696e6469636174657320776869636820737461636b657220746f20756e6c6f61642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "756e6c6f6164616e676c652c207561202d20556e6c6f6164206120706c6174652066726f6d206120676976656e20737461636b65722c20616e64207265706f727420616e676c65206f66667365742e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020506172616d65746572732831293a203c737461636b65723e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "3c737461636b65723e2069732061206e756d62657220696e207468652072616e67652030202d203133207468617420696e6469636174657320776869636820737461636b657220746f20756e6c6f61642e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "756e6c6f61646c2c20756c202d20556e6c6f6164206120706c6174652066726f6d207468652063757272656e7420737461636b65722e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "7661727374617475732c207673202d2052657475726e7320746865206d616368696e65207374617465207661726961626c65732e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "2020202020284e6f20706172616d6574657273290a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120696e666f20616c6c203236300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203236310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203236310a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve/tests/captures/manual_laser.json b/pylabrobot/high_res/micro_serve/tests/captures/manual_laser.json new file mode 100644 index 00000000000..cd54ff38a74 --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/manual_laser.json @@ -0,0 +1,713 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203334300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203334300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203334310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c202d322e3133393432392c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203334310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203334320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203334320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203334330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203334330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203334340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c202d322e3133393432392c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203334340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d616e75616c0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d616e75616c203334350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d616e75616c203334350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203334360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c202d322e3133393432392c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203334360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203334370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c202d322e3133393432392c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203334370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203334380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c202d322e3133393432392c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203334380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203334390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c202d322e3133393432392c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203334390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203335300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573206e6f74686f6d65642c20737461636b65724e756d626572202d312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c202d322e3133393432392c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203335300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "686f6d650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120686f6d65203335310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120686f6d65203335310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203335320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203335320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203335330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203335330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203335340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203335340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6c61736572206f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206c61736572206f6e203335350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206c61736572206f6e203335350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203335360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203335360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6c61736572206f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206c61736572206f6e203335370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206c61736572206f6e203335370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203335380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203335380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6c61736572206f66660a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206c61736572206f6666203335390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206c61736572206f6666203335390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203336300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203336300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203336310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203336310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203336320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203336320a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve/tests/captures/measurement_discovery.json b/pylabrobot/high_res/micro_serve/tests/captures/measurement_discovery.json new file mode 100644 index 00000000000..8f7c1f3f34a --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/measurement_discovery.json @@ -0,0 +1,1073 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203237300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203237300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203237310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3438363338392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203237310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203237320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203237320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203237330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203237330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203237340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203237340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d656173757265737461636b657220310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d656173757265737461636b65722031203237350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "38360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d656173757265737461636b65722031203237350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203237360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3436383530362c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203237360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203237370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3436383530362c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203237370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203237380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3436383530362c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203237380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203237390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203237390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203238300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203238300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203238310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203238310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203238320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220312c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203238320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d656173757265737461636b657220320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d656173757265737461636b65722032203238330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "36340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d656173757265737461636b65722032203238330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203238340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439303834352c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203238340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203238350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439303834352c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203238350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203238360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439303834352c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203238360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203238370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203238370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203238380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203238380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203238390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203238390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203239300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203239300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d656173757265737461636b657220330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d656173757265737461636b65722033203239310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "35390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d656173757265737461636b65722033203239310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203239320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439353732382c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203239320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203239330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439353732382c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203239330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203239340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439353732382c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203239340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203239350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203239350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203239360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203239360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203239370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203239370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203239380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220332c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203239380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d656173757265737461636b657220340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d656173757265737461636b65722034203239390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "35390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d656173757265737461636b65722034203239390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203330300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439353732382c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203330300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203330310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439353732382c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203330310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203330320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439353732382c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203330320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203330330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203330330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203330340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203330340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203330350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203330350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203330360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220342c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203330360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d656173757265737461636b657220350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d656173757265737461636b65722035203330370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "31333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d656173757265737461636b65722035203330370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203330380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932353635392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203330380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203330390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932353635392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203330390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203331300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932353635392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203331300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203331310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203331310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203331320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203331320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203331330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203331330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203331340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203331340a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve/tests/captures/measurement_empty.json b/pylabrobot/high_res/micro_serve/tests/captures/measurement_empty.json new file mode 100644 index 00000000000..46f99621750 --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/measurement_empty.json @@ -0,0 +1,299 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203236320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203236320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203236330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203236330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203236340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203236340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203236350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203236350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6d656173757265737461636b657220300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206d656173757265737461636b65722030203236360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "36390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206d656173757265737461636b65722030203236360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203236370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3438363338392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203236370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203236380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3438363338392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203236380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203236390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203236390a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve/tests/micro_serve_tests.py b/pylabrobot/high_res/micro_serve/tests/micro_serve_tests.py index 3928c6eac91..664b23a9e1c 100644 --- a/pylabrobot/high_res/micro_serve/tests/micro_serve_tests.py +++ b/pylabrobot/high_res/micro_serve/tests/micro_serve_tests.py @@ -481,9 +481,10 @@ async def test_barcode_scan_uses_geometry_and_scan_timeout(self) -> None: self.io.add("setstackerdimensions 0 11000 10000 10000") self.io.add("dimstatus", *DIMENSIONS) self.io.add("readbarcodestacker 0", '0: "EXAMPLE"') + self.io.add("status", status()) result = await self.driver.stackers[0].scan_barcodes(GEOMETRY) self.assertEqual(result, ('0: "EXAMPLE"',)) - self.assertGreater(self.io.read_timeouts[-1], 290) + self.assertGreater(max(self.io.read_timeouts), 290) class MicroServeCaptureTests(unittest.IsolatedAsyncioTestCase): From feb898df216c5cb2468b79309a9be6b9a7c5b6df Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Wed, 9 Sep 2026 11:29:39 -0700 Subject: [PATCH 5/8] Validate MicroServe barcode scans and active plate counts --- docs/_static/devices.json | 2 +- .../high_res/micro-serve/capabilities.md | 6 +- .../high_res/micro-serve/hello-world.ipynb | 2 +- .../high_res/micro-serve/protocol.md | 17 +- .../high_res/micro-serve/state-machine.md | 7 +- .../high_res/micro_serve/micro_serve.py | 11 +- .../micro_serve/tests/capability_tests.py | 65 + .../tests/captures/empty_scan_count.json | 1265 +++++++++++++++ .../captures/single_plate_scan_count.json | 1445 +++++++++++++++++ 9 files changed, 2805 insertions(+), 15 deletions(-) create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/empty_scan_count.json create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/single_plate_scan_count.json diff --git a/docs/_static/devices.json b/docs/_static/devices.json index e421ed545d7..41431bbab56 100644 --- a/docs/_static/devices.json +++ b/docs/_static/devices.json @@ -596,7 +596,7 @@ "api_version": "v1", "code_slug": "high_res/micro_serve", "doc_slug": "high_res/micro-serve/hello-world", - "notes": "Queries, homing, carousel, empty handoff, stack measurement, and manual access verified; plate transfers and successful barcode scans unverified." + "notes": "Queries, homing, carousel, empty handoff, measurement, manual access, and single-plate barcode/count verified; loaded handoffs and multi-plate scans unverified." }, { "id": "highres-microspin", diff --git a/docs/user_guide/high_res/micro-serve/capabilities.md b/docs/user_guide/high_res/micro-serve/capabilities.md index 8b9a11ba5ce..34014b0c12f 100644 --- a/docs/user_guide/high_res/micro-serve/capabilities.md +++ b/docs/user_guide/high_res/micro-serve/capabilities.md @@ -38,11 +38,11 @@ physical test on this device. The API PDF's measurement examples also serve as p | `unload` | `stackers[i].prepare_for_unload(d)` | Simulated | | `unloadangle` | `stackers[i].prepare_for_unload_with_angle(d)` | Simulated; raw angle report has unspecified unit | | `retract` | `retract()` | Empty loader verified | -| `readbarcodestacker` | `stackers[i].scan_barcodes(d)` | Geometry-error path captured; successful scans unverified | -| `countplates` | `stackers[i].count_plates(d)` | Simulated; moves hardware, then reads the updated count | +| `readbarcodestacker` | `stackers[i].scan_barcodes(d)` | Empty and single-plate scans and geometry-error path captured; multi-plate scans unverified | +| `countplates` | `stackers[i].count_plates(d)` | Empty and repeated single-plate counts captured; multi-plate counting unverified | | `measurestacker` | `stackers[i].measure_height()` | Empty and single-plate hardware captures; returns millimeters; accuracy not independently checked | | `calculateplatedimensions` | `calculate_plate_dimensions(count)` | API example and simulated sequence | -| `setplatecount` | `stackers[i].set_plate_count(count)` | Simulated; verified bookkeeping write | +| `setplatecount` | `stackers[i].set_plate_count(count)` | Hardware capture of 1 → 0 → 1 and repeated target; verified readback | | `manual` | `enter_manual_mode()` | Hardware capture, including repeated owned request; requires retraction before access | | `clearabort` | `clear_abort()` | Simulated; does not resolve an interrupted handoff | | `estoprecover` | `recover_from_estop()` | Simulated; physical recovery may move axes | diff --git a/docs/user_guide/high_res/micro-serve/hello-world.ipynb b/docs/user_guide/high_res/micro-serve/hello-world.ipynb index d30d24a8e48..3a864255df8 100644 --- a/docs/user_guide/high_res/micro-serve/hello-world.ipynb +++ b/docs/user_guide/high_res/micro-serve/hello-world.ipynb @@ -18,7 +18,7 @@ "| Verified controller | HRB-2008-10558, firmware 2.7.0.756 |\n", "| Public length units | millimeters |\n", "\n", - "**Plate transfers, counting, plate-dimension calculation, successful barcode scanning, and physical fault recovery remain unverified.** Communication, homing, all fourteen carousel positions, and empty receiving-position preparation/retraction, stack-height measurement, and manual access were checked on this controller. Laser command exchanges and barcode geometry-error handling were also captured. Review the physical setup before running each motion cell.\n", + "**Plate transfers, multi-plate counting/scanning, plate-dimension calculation, and physical fault recovery remain unverified.** Communication, homing, all fourteen carousel positions, and empty receiving-position preparation/retraction, stack-height measurement, and manual access were checked on this controller. Laser command exchanges, single-plate barcode scanning, repeated active counts, and barcode geometry-error handling were also captured. Review the physical setup before running each motion cell.\n", "\n", "This driver prepares the MicroServe for a separate robot to place or pick plates. It does not model plate inventory in PyLabRobot resources. Reported plate counts are the controller's cached estimates.\n", "\n", diff --git a/docs/user_guide/high_res/micro-serve/protocol.md b/docs/user_guide/high_res/micro-serve/protocol.md index f68bea5ed7c..232c9fda8b2 100644 --- a/docs/user_guide/high_res/micro-serve/protocol.md +++ b/docs/user_guide/high_res/micro-serve/protocol.md @@ -71,7 +71,7 @@ phase, rather than restarting on each data line. | `stackers[i].prepare_for_load(d)` | geometry, then `load i` | API §5.14, §5.21; commissioning page | Empty stacker 2 and repeated preparation verified | | `stackers[i].prepare_for_unload(d)` | geometry, then `unload i` | API §5.24, §5.21; commissioning page | Simulated only | | `retract()` | `retract` | API §5.17 | Empty loader and repeated retraction verified using home sensors | -| `stackers[i].scan_barcodes(d)` | geometry, then `readbarcodestacker i` | Firmware help; operation notes | Simulated only; returns raw data lines | +| `stackers[i].scan_barcodes(d)` | geometry, then `readbarcodestacker i` | Firmware help; operation notes | Single-plate scan verified; returns raw data lines | | `reconcile_preparation()` | `commandstat id`, `status` | API §5.3, §5.23 | Query primitives verified; recovery scenario simulated | Stacker indices are 0–13. Geometry is provided in millimeters and encoded as whole micrometers in @@ -150,7 +150,7 @@ that caused the initial retraction check to fail. The captures verify that repea preparation, and retraction requests do not send additional motion commands. Simulated tests cover timeout, cancellation, reply mismatch, unresolved preparation, and sensor disagreement. -**Plate transfer, unloading, barcode scans, and physical fault recovery remain unverified.** Setup +**Plate transfer, unloading, multi-plate counting/scanning, and physical fault recovery remain unverified.** Setup warns about these limits. Empty-machine tests do not establish pickup height with a plate, gripper clearance, plate compatibility, or E-stop recovery. Those need a separate physical validation with known plates and robot clearance. @@ -169,3 +169,16 @@ Manual access, repeated manual requests, return to homed/ready operation, and la command exchanges were also captured. No additional errors appeared and plate geometry remained unchanged. There is no laser-state readback, so the command capture does not independently verify optical output. + +An operator-approved single-plate test used the measured 13.629 mm thickness with provisional +height and pitch also set to 13.629 mm. It returned `BARCODES! Count: 1, "codex"`, followed by +two active counts of one. Scanning and counting left the loader extended; explicit retraction +was verified between operations. The saved 11/10/10 mm geometry was restored and no additional +errors appeared. This test validates one-plate scanning/counting, not transfer geometry or +multi-plate stack pitch. Do not use these provisional dimensions as a plate definition. + +An empty-stacker scan returned `BARCODES! Count: 0`, and active counting returned zero. +The cached count for the loaded stacker was explicitly changed 1 → 0 → 1, read back after each +write, and restored to one; a repeated target skipped the write. These checks left geometry +unchanged and added no errors. Barcode methods preserve these complete data lines, including +the `BARCODES! Count:` prefix; an empty-stack report is not an empty Python tuple. diff --git a/docs/user_guide/high_res/micro-serve/state-machine.md b/docs/user_guide/high_res/micro-serve/state-machine.md index d4139c30541..f687f58155d 100644 --- a/docs/user_guide/high_res/micro-serve/state-machine.md +++ b/docs/user_guide/high_res/micro-serve/state-machine.md @@ -127,8 +127,9 @@ automatically replays a plate transfer. Homing, all fourteen carousel positions, repeated requests, empty receiving-position preparation, retraction, and subsequent rotation were verified on firmware 2.7.0.756. The diagrams also include -unloading, robot transfers, barcode scanning, and recovery paths that remain unverified on hardware. +unloading, robot transfers, multi-plate scanning, and recovery paths that remain unverified on hardware. Stack-height measurement, retraction after measurement, and manual access followed by homing -were also captured. A barcode geometry-error capture validates the failure path; successful reads -remain unverified. +were also captured. Single-plate barcode scanning and repeated active counts passed with temporary +geometry, followed by retraction and restoration of the saved dimensions. A separate barcode +geometry-error capture validates the failure path. See [protocol and validation](protocol.md) for the captures and exact limits. diff --git a/pylabrobot/high_res/micro_serve/micro_serve.py b/pylabrobot/high_res/micro_serve/micro_serve.py index 0ba5a7a9f1a..50ce8d8a7e1 100644 --- a/pylabrobot/high_res/micro_serve/micro_serve.py +++ b/pylabrobot/high_res/micro_serve/micro_serve.py @@ -171,9 +171,10 @@ class HighResMicroServe: Communication, homing, all carousel positions, empty receiving-position preparation/retraction, stack-height measurement, manual access, and laser - command exchanges were checked on firmware 2.7.0.756. Transfers with plates, - counting, plate-dimension calculation, successful barcode scanning, and - physical fault recovery remain unverified. + command exchanges were checked on firmware 2.7.0.756. A single-plate barcode + scan and repeated active counts also passed. Transfers with plates, + multi-plate counting/scanning, plate-dimension calculation, and physical + fault recovery remain unverified. """ def __init__( @@ -235,8 +236,8 @@ async def setup(self) -> None: self._connected = True logger.info("Connected to MicroServe") logger.warning( - "MicroServe transfers with plates, counting, plate-dimension calculation, successful " - "barcode scanning, and physical fault recovery are unverified; validate before use" + "MicroServe transfers with plates, multi-plate counting/scanning, plate-dimension " + "calculation, and physical fault recovery are unverified; validate before use" ) async def stop(self) -> None: diff --git a/pylabrobot/high_res/micro_serve/tests/capability_tests.py b/pylabrobot/high_res/micro_serve/tests/capability_tests.py index c66e226f13b..a6941535b39 100644 --- a/pylabrobot/high_res/micro_serve/tests/capability_tests.py +++ b/pylabrobot/high_res/micro_serve/tests/capability_tests.py @@ -418,3 +418,68 @@ async def test_manual_home_and_laser_capture(self) -> None: reader.next_command() finally: await driver.stop() + + async def test_single_plate_scan_and_repeated_count_capture(self) -> None: + """Replay barcode discovery, two physical counts, and restoration of saved geometry.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "single_plate_scan_count.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + self.assertTrue((await driver.request_status()).loader_retracted) + saved = (await driver.request_dimensions())[5] + errors = await driver.request_errors() + single_plate = MicroServePlateDimensions(13.629, 13.629, 13.629) + self.assertEqual( + await driver.stackers[5].scan_barcodes(single_plate), ('BARCODES! Count: 1, "codex"',) + ) + self.assertTrue((await driver.request_status()).loader_extended) + await driver.retract() + for _ in range(2): + self.assertEqual(await driver.stackers[5].count_plates(single_plate), 1) + self.assertTrue((await driver.request_status()).loader_extended) + await driver.retract() + self.assertTrue((await driver.request_status()).loader_retracted) + await driver.retract() + await driver.stackers[5].set_dimensions(saved) + self.assertEqual((await driver.request_dimensions())[5], saved) + self.assertTrue((await driver.request_status()).loader_retracted) + self.assertEqual(await driver.request_errors(), errors) + self.assertIsNone(driver.unresolved_operation) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_empty_scan_count_and_cached_count_write_capture(self) -> None: + """Replay zero-count scan output and bookkeeping writes without changing geometry.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "empty_scan_count.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + self.assertTrue((await driver.request_status()).loader_retracted) + geometry = await driver.request_dimensions() + errors = await driver.request_errors() + self.assertEqual(await driver.stackers[5].request_plate_count(), 1) + await driver.stackers[5].set_plate_count(0) + await driver.stackers[5].set_plate_count(1) + await driver.stackers[5].set_plate_count(1) + self.assertEqual(await driver.stackers[0].scan_barcodes(geometry[0]), ("BARCODES! Count: 0",)) + self.assertTrue((await driver.request_status()).loader_extended) + await driver.retract() + self.assertEqual(await driver.stackers[0].count_plates(geometry[0]), 0) + await driver.retract() + await driver.stackers[5].move_to() + final = await driver.request_status() + self.assertEqual(final.stacker, 5) + self.assertTrue(final.loader_retracted) + self.assertEqual(await driver.request_dimensions(), geometry) + self.assertEqual(await driver.stackers[5].request_plate_count(), 1) + self.assertEqual(await driver.request_errors(), errors) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() diff --git a/pylabrobot/high_res/micro_serve/tests/captures/empty_scan_count.json b/pylabrobot/high_res/micro_serve/tests/captures/empty_scan_count.json new file mode 100644 index 00000000000..dfeb10eece1 --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/empty_scan_count.json @@ -0,0 +1,1265 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203430340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203430340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203430350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203430350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203430360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203430360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203430370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203430370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203430380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203430380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203430390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203430390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203431300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203431300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574706c617465636f756e74203520300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574706c617465636f756e7420352030203431310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574706c617465636f756e7420352030203431310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203431320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203431320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203431330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203431330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203431340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203431340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574706c617465636f756e74203520310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574706c617465636f756e7420352031203431350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574706c617465636f756e7420352031203431350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203431360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203431360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203431370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203431370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203431380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203431380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203431390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203431390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320302031313030302031303030302031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732030203131303030203130303030203130303030203432300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732030203131303030203130303030203130303030203432300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203432310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203432310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "72656164626172636f6465737461636b657220300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072656164626172636f6465737461636b65722030203432320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "424152434f4445532120436f756e743a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072656164626172636f6465737461636b65722030203432320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203432330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439333238362c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203432330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203432340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439333238362c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203432340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203432350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439333238362c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203432350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203432360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203432360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203432370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203432370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203432380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203432380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320302031313030302031303030302031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732030203131303030203130303030203130303030203432390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732030203131303030203130303030203130303030203432390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203433300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203433300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636f756e74706c6174657320300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636f756e74706c617465732030203433310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636f756e74706c617465732030203433310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203433320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439353732382c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203433320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732030203433330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "303a20300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732030203433330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203433340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203733372e3439353732382c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203433340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203433350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203433350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203433360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203433360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203433370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203433370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7370696e20350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21207370696e2035203433380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21207370696e2035203433380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203433390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203433390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203434300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203434300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203434310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203434310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203434320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203434320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203434330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203434330a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve/tests/captures/single_plate_scan_count.json b/pylabrobot/high_res/micro_serve/tests/captures/single_plate_scan_count.json new file mode 100644 index 00000000000..e41a1567211 --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/single_plate_scan_count.json @@ -0,0 +1,1445 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203336330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203336330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203336340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203336340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203336350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203336350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203336360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203336360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203336370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220302c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20322e3039393235342c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203336370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203336380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203336380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203336390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203336390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "72656164626172636f6465737461636b657220350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072656164626172636f6465737461636b65722035203337300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "424152434f4445532120436f756e743a20312c2022636f646578220a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072656164626172636f6465737461636b65722035203337300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203337310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3839313930372c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203337310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203337320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3839313930372c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203337320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203337330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3839313930372c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203337330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203337340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203337340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203337350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203337350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203337360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203337360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203337370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203337370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203337380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203337380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636f756e74706c6174657320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636f756e74706c617465732035203337390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636f756e74706c617465732035203337390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203338300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932343830352c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203338300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203338310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203338310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203338320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932343830352c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203338320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203338330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932343830352c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203338330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203338340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203338340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203338350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203338350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203338360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203338360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203338370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203338370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203338380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203338380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636f756e74706c6174657320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636f756e74706c617465732035203338390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636f756e74706c617465732035203338390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203339300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932313537302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203339300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203339310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203339310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203339320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932313537302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203339320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203339330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932313537302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203339330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203339340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203339340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203339350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203339350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203339360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203339360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203339370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203339370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203339380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203339380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031313030302031303030302031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203131303030203130303030203130303030203339390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203131303030203130303030203130303030203339390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203430300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203430300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203430310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203430310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203430320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203430320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203430330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203430330a" + } + ] +} \ No newline at end of file From 1a712436dbedc48552db6f710add68ee04195689 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Wed, 9 Sep 2026 11:46:06 -0700 Subject: [PATCH 6/8] Preserve MicroServe angle reports and validate loaded positioning --- docs/_static/devices.json | 2 +- .../high_res/micro-serve/capabilities.md | 19 +- .../high_res/micro-serve/hello-world.ipynb | 4 +- .../high_res/micro-serve/protocol.md | 29 +- .../high_res/micro-serve/state-machine.md | 6 +- .../high_res/micro-serve/validation.md | 6 +- .../high_res/micro_serve/micro_serve.py | 38 +- .../micro_serve/tests/capability_tests.py | 102 +- .../tests/captures/loaded_cycles.json | 3083 +++++++++++++++++ .../tests/captures/recovery_guards.json | 539 +++ 10 files changed, 3795 insertions(+), 33 deletions(-) create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/loaded_cycles.json create mode 100644 pylabrobot/high_res/micro_serve/tests/captures/recovery_guards.json diff --git a/docs/_static/devices.json b/docs/_static/devices.json index 41431bbab56..cc070fe282b 100644 --- a/docs/_static/devices.json +++ b/docs/_static/devices.json @@ -596,7 +596,7 @@ "api_version": "v1", "code_slug": "high_res/micro_serve", "doc_slug": "high_res/micro-serve/hello-world", - "notes": "Queries, homing, carousel, empty handoff, measurement, manual access, and single-plate barcode/count verified; loaded handoffs and multi-plate scans unverified." + "notes": "Single-plate preparation/retraction, barcode/count, measurement, and basic motion verified; external handoffs, multi-plate scans, and fault recovery unverified." }, { "id": "highres-microspin", diff --git a/docs/user_guide/high_res/micro-serve/capabilities.md b/docs/user_guide/high_res/micro-serve/capabilities.md index 34014b0c12f..74ac473b916 100644 --- a/docs/user_guide/high_res/micro-serve/capabilities.md +++ b/docs/user_guide/high_res/micro-serve/capabilities.md @@ -34,9 +34,9 @@ physical test on this device. The API PDF's measurement examples also serve as p | `setstackerdimensions` | `stackers[i].set_dimensions(d)` | Existing geometry written and verified on hardware | | `home` | `home()` | Hardware captures, including return from manual access | | `spin` | `stackers[i].move_to()` | All fourteen positions verified | -| `load` | `stackers[i].prepare_for_load(d)` | Empty receiving position verified; plate placement unverified | -| `unload` | `stackers[i].prepare_for_unload(d)` | Simulated | -| `unloadangle` | `stackers[i].prepare_for_unload_with_angle(d)` | Simulated; raw angle report has unspecified unit | +| `load` | `stackers[i].prepare_for_load(d)` | Empty and single-plate preparation/retraction captured; external placement unverified | +| `unload` | `stackers[i].prepare_for_unload(d)` | Single-plate preparation/retraction captured; external pickup unverified | +| `unloadangle` | `stackers[i].prepare_for_unload_with_angle(d)` | Single-plate preparation/retraction captured; controller supplied no angle report, returned as `None` | | `retract` | `retract()` | Empty loader verified | | `readbarcodestacker` | `stackers[i].scan_barcodes(d)` | Empty and single-plate scans and geometry-error path captured; multi-plate scans unverified | | `countplates` | `stackers[i].count_plates(d)` | Empty and repeated single-plate counts captured; multi-plate counting unverified | @@ -44,8 +44,8 @@ physical test on this device. The API PDF's measurement examples also serve as p | `calculateplatedimensions` | `calculate_plate_dimensions(count)` | API example and simulated sequence | | `setplatecount` | `stackers[i].set_plate_count(count)` | Hardware capture of 1 → 0 → 1 and repeated target; verified readback | | `manual` | `enter_manual_mode()` | Hardware capture, including repeated owned request; requires retraction before access | -| `clearabort` | `clear_abort()` | Simulated; does not resolve an interrupted handoff | -| `estoprecover` | `recover_from_estop()` | Simulated; physical recovery may move axes | +| `clearabort` | `clear_abort()` | Command acknowledgements captured with no active fault; actual abort recovery unverified | +| `estoprecover` | `recover_from_estop()` | Already-ready no-op captured; physical recovery remains simulated and may move axes | | `laser` | `set_barcode_laser(enabled)` | Hardware on/on/off command capture; no firmware optical-state readback | ## Equivalent functions @@ -105,6 +105,15 @@ The `manual` command clears homing and the selected stacker but can leave mode R reply. The driver checks the acknowledged transition and resulting sensors, and retains ownership for repeated manual requests. An arbitrary unhomed machine is not assumed to be in manual mode. +The documented `unloadangle` example includes a numeric reply, but the tested controller completed +the operation without supplying one. The method returns `None` in this case. A separate +`getangle` query returned `0.0000`; its freshness and unit are unverified, so it is not substituted +for the missing report. Repeated owned preparations preserve the original result. + +A scan of stacker 5 completed with stacker 12 selected and the loader retracted. Scanning does +not guarantee that the scanned stacker remains at the transfer position. Subsequent motion uses +fresh status and an explicit target rather than assuming the scan's final position. + ## Completing hardware validation See the [hardware validation procedure](validation.md) for the supervised sequence. To justify diff --git a/docs/user_guide/high_res/micro-serve/hello-world.ipynb b/docs/user_guide/high_res/micro-serve/hello-world.ipynb index 3a864255df8..8ad6f267fd2 100644 --- a/docs/user_guide/high_res/micro-serve/hello-world.ipynb +++ b/docs/user_guide/high_res/micro-serve/hello-world.ipynb @@ -18,7 +18,7 @@ "| Verified controller | HRB-2008-10558, firmware 2.7.0.756 |\n", "| Public length units | millimeters |\n", "\n", - "**Plate transfers, multi-plate counting/scanning, plate-dimension calculation, and physical fault recovery remain unverified.** Communication, homing, all fourteen carousel positions, and empty receiving-position preparation/retraction, stack-height measurement, and manual access were checked on this controller. Laser command exchanges, single-plate barcode scanning, repeated active counts, and barcode geometry-error handling were also captured. Review the physical setup before running each motion cell.\n", + "**External plate pickup/placement, multi-plate counting/scanning, plate-dimension calculation, and physical fault recovery remain unverified.** Communication, homing, all fourteen carousel positions, and empty receiving-position preparation/retraction, stack-height measurement, and manual access were checked on this controller. Laser command exchanges, single-plate barcode scanning, repeated active counts, loaded preparation/retraction, and barcode geometry-error handling were also captured. Review the physical setup before running each motion cell.\n", "\n", "This driver prepares the MicroServe for a separate robot to place or pick plates. It does not model plate inventory in PyLabRobot resources. Reported plate counts are the controller's cached estimates.\n", "\n", @@ -795,7 +795,7 @@ "source": [ "## Prepare an unload with angle reporting\n", "\n", - "This presents a plate for pickup and returns the firmware angle as text. The angle unit is undocumented. Repeating this preparation queries the cached angle without fetching another plate." + "This presents a plate for pickup and returns the firmware angle report as text, or `None` when the controller supplies no angle (as observed on firmware 2.7.0.756). The angle unit is undocumented. Repeating this preparation preserves the original result without fetching another plate or substituting a cached angle." ] }, { diff --git a/docs/user_guide/high_res/micro-serve/protocol.md b/docs/user_guide/high_res/micro-serve/protocol.md index 232c9fda8b2..b234ff9c67c 100644 --- a/docs/user_guide/high_res/micro-serve/protocol.md +++ b/docs/user_guide/high_res/micro-serve/protocol.md @@ -68,9 +68,9 @@ phase, rather than restarting on each data line. | `home()` | `home` | API §5.11; operation notes for manual mode | Homing verified; manual-mode recovery simulated | | `stackers[i].move_to()` | `spin i` | API §5.22 | All fourteen positions and repeated targets verified | | `stackers[i].set_dimensions(d)` | `setstackerdimensions i PH SH PT` | API §5.21 | Existing geometry written and read back at stacker 2 | -| `stackers[i].prepare_for_load(d)` | geometry, then `load i` | API §5.14, §5.21; commissioning page | Empty stacker 2 and repeated preparation verified | -| `stackers[i].prepare_for_unload(d)` | geometry, then `unload i` | API §5.24, §5.21; commissioning page | Simulated only | -| `retract()` | `retract` | API §5.17 | Empty loader and repeated retraction verified using home sensors | +| `stackers[i].prepare_for_load(d)` | geometry, then `load i` | API §5.14, §5.21; commissioning page | Empty and single-plate preparation/retraction verified; no external placement | +| `stackers[i].prepare_for_unload(d)` | geometry, then `unload i` | API §5.24, §5.21; commissioning page | Single-plate preparation/retraction verified; no external pickup | +| `retract()` | `retract` | API §5.17 | Empty and single-plate loader retraction verified using home sensors | | `stackers[i].scan_barcodes(d)` | geometry, then `readbarcodestacker i` | Firmware help; operation notes | Single-plate scan verified; returns raw data lines | | `reconcile_preparation()` | `commandstat id`, `status` | API §5.3, §5.23 | Query primitives verified; recovery scenario simulated | @@ -150,7 +150,7 @@ that caused the initial retraction check to fail. The captures verify that repea preparation, and retraction requests do not send additional motion commands. Simulated tests cover timeout, cancellation, reply mismatch, unresolved preparation, and sensor disagreement. -**Plate transfer, unloading, multi-plate counting/scanning, and physical fault recovery remain unverified.** Setup +**External plate pickup/placement, multi-plate counting/scanning, and physical fault recovery remain unverified.** Setup warns about these limits. Empty-machine tests do not establish pickup height with a plate, gripper clearance, plate compatibility, or E-stop recovery. Those need a separate physical validation with known plates and robot clearance. @@ -182,3 +182,24 @@ The cached count for the loaded stacker was explicitly changed 1 → 0 → 1, re write, and restored to one; a repeated target skipped the write. These checks left geometry unchanged and added no errors. Barcode methods preserve these complete data lines, including the `BARCODES! Count:` prefix; an empty-stack report is not an empty Python tuple. + +Two `recover_from_estop()` calls on an already homed, idle, ready machine were confirmed to send +only status queries. Two `clear_abort()` calls with no active fault received successful +acknowledgements. Status, geometry, and the error log were unchanged. These checks validate the +ready-state guard and command exchange, not recovery from a physical E-stop or active abort. + +Single-plate `unload`, `unloadangle`, and `load` preparation/retraction cycles were captured with +the approved temporary geometry. Repeated preparations sent no additional transfer command, each +cycle retained an active count of one, and the barcode remained `codex`. Nobody removed or added +a plate. The saved geometry was restored afterward. These cycles validate loaded positioning and +retraction, not an external robot handoff or independent plate dimensions. + +`unloadangle` supplied no data before its successful completion frame. The Python method returns +`None` for a missing report and preserves that result on repeated requests. A separately queried +cached angle is not evidence that this operation measured an angle. Reconciliation can restore +the handoff but cannot recover a missing original angle report. + +The final barcode scan selected stacker 12 while scanning stacker 5. Its completion confirmed +idle, ready status with the loader retracted, so this is not rejected as a transfer-target error. +Callers must query current status or explicitly move to a stacker after scanning. A separate, +guarded move returned the carousel to stacker 5, with the laser commanded off and no new errors. diff --git a/docs/user_guide/high_res/micro-serve/state-machine.md b/docs/user_guide/high_res/micro-serve/state-machine.md index f687f58155d..358a4492812 100644 --- a/docs/user_guide/high_res/micro-serve/state-machine.md +++ b/docs/user_guide/high_res/micro-serve/state-machine.md @@ -127,9 +127,13 @@ automatically replays a plate transfer. Homing, all fourteen carousel positions, repeated requests, empty receiving-position preparation, retraction, and subsequent rotation were verified on firmware 2.7.0.756. The diagrams also include -unloading, robot transfers, multi-plate scanning, and recovery paths that remain unverified on hardware. +external robot transfers, multi-plate scanning, and recovery paths that remain unverified on hardware. Stack-height measurement, retraction after measurement, and manual access followed by homing were also captured. Single-plate barcode scanning and repeated active counts passed with temporary geometry, followed by retraction and restoration of the saved dimensions. A separate barcode geometry-error capture validates the failure path. +Loaded load/unload/angle-variant preparation and retraction also passed without external pickup or +placement. The angle variant returned no report on this controller; `None` remains `None` on repeat. +Scanning can finish with a different carousel stacker selected, so subsequent operations use fresh +status and explicit targets. See [protocol and validation](protocol.md) for the captures and exact limits. diff --git a/docs/user_guide/high_res/micro-serve/validation.md b/docs/user_guide/high_res/micro-serve/validation.md index ab72c84957d..a0cc2ed9dc5 100644 --- a/docs/user_guide/high_res/micro-serve/validation.md +++ b/docs/user_guide/high_res/micro-serve/validation.md @@ -40,8 +40,10 @@ stack pitch, and well thickness as described in the hello-world guide. Do not su 5. Prepare one plate for unloading, inspect its support and pickup position, then repeat the preparation and confirm no second plate is fetched. The operator removes the presented plate, withdraws, and explicitly retracts the loader. -6. Repeat using angle-reporting preparation. Record the raw angle; establish its unit before - using it to command a robot rotation. A repeated preparation must only query the cached angle. +6. Repeat using angle-reporting preparation. Record the raw angle if provided; establish its unit + before using it to command a robot rotation. The tested controller supplies no angle, returned + as `None`. A repeated preparation must preserve the original report without fetching another + plate or substituting a potentially stale cached angle. 7. Repeat until the test stack is empty. Compare counts, sensors, errors, and geometry against the expected end state. Verify a final carousel move with the loader retracted. diff --git a/pylabrobot/high_res/micro_serve/micro_serve.py b/pylabrobot/high_res/micro_serve/micro_serve.py index 50ce8d8a7e1..21871d2e5c4 100644 --- a/pylabrobot/high_res/micro_serve/micro_serve.py +++ b/pylabrobot/high_res/micro_serve/micro_serve.py @@ -171,10 +171,10 @@ class HighResMicroServe: Communication, homing, all carousel positions, empty receiving-position preparation/retraction, stack-height measurement, manual access, and laser - command exchanges were checked on firmware 2.7.0.756. A single-plate barcode - scan and repeated active counts also passed. Transfers with plates, - multi-plate counting/scanning, plate-dimension calculation, and physical - fault recovery remain unverified. + command exchanges were checked on firmware 2.7.0.756. Single-plate barcode + scans, repeated active counts, and loaded preparation/retraction also passed. + External plate pickup/placement, multi-plate counting/scanning, plate-dimension + calculation, and physical fault recovery remain unverified. """ def __init__( @@ -202,7 +202,7 @@ def __init__( self._lock = asyncio.Lock() self._connected = False self._last_command_id: Optional[int] = None - self._prepared: Optional[Tuple[str, int]] = None + self._prepared: Optional[Tuple[str, int, Tuple[str, ...]]] = None self._unresolved_preparation: Optional[MicroServeUnresolvedPreparation] = None self._unresolved_operation: Optional[str] = None self._manual_mode = False @@ -236,7 +236,7 @@ async def setup(self) -> None: self._connected = True logger.info("Connected to MicroServe") logger.warning( - "MicroServe transfers with plates, multi-plate counting/scanning, plate-dimension " + "MicroServe external plate pickup/placement, multi-plate counting/scanning, plate-dimension " "calculation, and physical fault recovery are unverified; validate before use" ) @@ -673,7 +673,7 @@ async def reconcile_preparation(self) -> None: self._require_idle(status) if status.stacker != pending.stacker or not status.loader_extended: raise RuntimeError(f"Prepared transfer position cannot be confirmed: {status.raw}") - self._prepared = (pending.direction, pending.stacker) + self._prepared = (pending.direction, pending.stacker, ()) self._unresolved_preparation = None logger.info("Reconciled MicroServe preparation %s", record.command) @@ -857,10 +857,11 @@ async def _prepare( status = await self._device._status() self._device._require_idle(status) at_target = status.stacker == self.index and status.loader_extended - if at_target and self._device._prepared == (direction, self.index): + prepared = self._device._prepared + if at_target and prepared is not None and prepared[:2] == (direction, self.index): if (await self._device._dimensions())[self.index]._wire() != dimensions._wire(): raise RuntimeError("Retract the loader before changing geometry at the transfer position") - return await self._device._command("getangle") if direction == "unloadangle" else () + return prepared[2] if status.plate_sensor_blocked or not status.loader_retracted: raise RuntimeError("Finish the current robot handoff and retract before preparing another") await self._set_dimensions(dimensions) @@ -881,7 +882,7 @@ async def _prepare( command_id if command_id is not None else self._device.last_command_id, ) raise - self._device._prepared = (direction, self.index) + self._device._prepared = (direction, self.index, lines) return lines async def prepare_for_load(self, dimensions: MicroServePlateDimensions) -> None: @@ -901,21 +902,28 @@ async def prepare_for_unload(self, dimensions: MicroServePlateDimensions) -> Non """ await self._prepare("unload", dimensions) - async def prepare_for_unload_with_angle(self, dimensions: MicroServePlateDimensions) -> str: + async def prepare_for_unload_with_angle( + self, dimensions: MicroServePlateDimensions + ) -> Optional[str]: """Present a plate for picking and return the firmware's angle report as text. The firmware documentation does not define the angle unit. Do not assume - degrees or radians. Repeating this owned preparation queries the cached - angle without moving another plate. Retract after the robot clears the nest. + degrees or radians. Return None when the command supplies no angle report, + as observed on firmware 2.7.0.756. Repeated requests retain the original + report without moving another plate or substituting a potentially stale + ``getangle`` value. A reconciled preparation has no recovered angle report. + Retract after the robot clears the nest. """ - return "\n".join(await self._prepare("unloadangle", dimensions)) + return "\n".join(await self._prepare("unloadangle", dimensions)) or None async def scan_barcodes(self, dimensions: MicroServePlateDimensions) -> Tuple[str, ...]: """Move the barcode reader and return the controller's unmodified data lines. Barcode output formatting is firmware-dependent. Geometry is applied before scanning; the robot must be clear and the loader retracted. This operation - moves hardware even though its purpose is reading barcodes. + moves hardware even though its purpose is reading barcodes. Inspect status + afterward: the loader can be extended and the selected carousel stacker can + differ from the scanned index. """ async with self._device._lock: self._device._require_resolved_preparation() diff --git a/pylabrobot/high_res/micro_serve/tests/capability_tests.py b/pylabrobot/high_res/micro_serve/tests/capability_tests.py index a6941535b39..b970187a465 100644 --- a/pylabrobot/high_res/micro_serve/tests/capability_tests.py +++ b/pylabrobot/high_res/micro_serve/tests/capability_tests.py @@ -281,8 +281,8 @@ async def test_laser_uses_absolute_state_without_axis_motion(self) -> None: self.assertEqual(self.io.writes.count(b"laser on\n"), 2) self.assertEqual(self.io.writes.count(b"laser off\n"), 1) - async def test_angle_preparation_repeat_queries_angle_without_unloading_again(self) -> None: - """An angle request cannot fetch a second plate after an owned preparation.""" + async def test_angle_preparation_repeat_preserves_original_report(self) -> None: + """A repeated request neither fetches a second plate nor substitutes a cached angle.""" self.io.add("status", status()) self.io.add("setstackerdimensions 2 11000 10000 10000") self.io.add("dimstatus", *DIMENSIONS) @@ -290,7 +290,6 @@ async def test_angle_preparation_repeat_queries_angle_without_unloading_again(se self.io.add("status", status(stacker=2, extended=True)) self.io.add("status", status(stacker=2, extended=True)) self.io.add("dimstatus", *DIMENSIONS) - self.io.add("getangle", "0.1013") self.assertEqual( await self.driver.stackers[2].prepare_for_unload_with_angle(GEOMETRY), "0.1013" ) @@ -298,6 +297,31 @@ async def test_angle_preparation_repeat_queries_angle_without_unloading_again(se await self.driver.stackers[2].prepare_for_unload_with_angle(GEOMETRY), "0.1013" ) self.assertEqual(self.io.writes.count(b"unloadangle 2\n"), 1) + self.assertNotIn(b"getangle\n", self.io.writes) + + async def test_missing_angle_report_stays_missing_on_repeat(self) -> None: + """Firmware can successfully present a plate without returning an angle value.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 2 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add("unloadangle 2") + self.io.add("status", status(stacker=2, extended=True)) + self.io.add("status", status(stacker=2, extended=True)) + self.io.add("dimstatus", *DIMENSIONS) + self.assertIsNone(await self.driver.stackers[2].prepare_for_unload_with_angle(GEOMETRY)) + self.assertIsNone(await self.driver.stackers[2].prepare_for_unload_with_angle(GEOMETRY)) + self.assertNotIn(b"getangle\n", self.io.writes) + + async def test_reconciliation_does_not_invent_an_angle_report(self) -> None: + """A command record establishes completion but cannot reconstruct missing angle data.""" + self.driver._unresolved_preparation = MicroServeUnresolvedPreparation("unloadangle", 2, 50) + self.io.add("commandstat 50", " 50 OK! - unloadangle 2") + self.io.add("status", status(stacker=2, extended=True)) + await self.driver.reconcile_preparation() + self.io.add("status", status(stacker=2, extended=True)) + self.io.add("dimstatus", *DIMENSIONS) + self.assertIsNone(await self.driver.stackers[2].prepare_for_unload_with_angle(GEOMETRY)) + self.assertNotIn(b"getangle\n", self.io.writes) class MicroServeDiagnosticCaptureTests(unittest.IsolatedAsyncioTestCase): @@ -483,3 +507,75 @@ async def test_empty_scan_count_and_cached_count_write_capture(self) -> None: reader.next_command() finally: await driver.stop() + + async def test_ready_state_recovery_guards_capture(self) -> None: + """An already-ready recovery request sends no recovery motion command.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "recovery_guards.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + before = await driver.request_status() + geometry = await driver.request_dimensions() + errors = await driver.request_errors() + await driver.recover_from_estop() + await driver.recover_from_estop() + await driver.clear_abort() + await driver.clear_abort() + self.assertEqual(await driver.request_status(), before) + self.assertEqual(await driver.request_dimensions(), geometry) + self.assertEqual(await driver.request_errors(), errors) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() + + async def test_loaded_preparation_cycles_preserve_the_single_plate(self) -> None: + """Replay loaded preparation/retraction, missing angle data, and scan repositioning.""" + driver = HighResMicroServe("10.253.253.253") + reader = CaptureReader(str(Path(__file__).parent / "captures" / "loaded_cycles.json")) + driver.io = SocketValidator(reader, "HighRes MicroServe", host="10.253.253.253", port=1000) + await driver.setup() + try: + self.assertIn("HRB-2008-10558", await driver.request_version()) + self.assertTrue((await driver.request_status()).loader_retracted) + saved = (await driver.request_dimensions())[5] + errors = await driver.request_errors() + dimensions = MicroServePlateDimensions(13.629, 13.629, 13.629) + stacker = driver.stackers[5] + barcode = await stacker.scan_barcodes(dimensions) + self.assertEqual(barcode, ('BARCODES! Count: 1, "codex"',)) + await driver.retract() + for name, prepare in ( + ("unload", stacker.prepare_for_unload), + ("unloadangle", stacker.prepare_for_unload_with_angle), + ("load", stacker.prepare_for_load), + ): + with self.subTest(operation=name): + self.assertIsNone(await prepare(dimensions)) + prepared = await driver.request_status() + self.assertTrue(prepared.loader_extended) + self.assertEqual(prepared.stacker, 5) + self.assertIsNone(await prepare(dimensions)) + if name == "unloadangle": + # This separate cached query does not establish a fresh angle report. + self.assertEqual(await driver.request_plate_angle(), "0.0000") + await driver.retract() + self.assertTrue((await driver.request_status()).loader_retracted) + self.assertEqual(await stacker.count_plates(dimensions), 1) + await driver.retract() + self.assertEqual(await stacker.scan_barcodes(dimensions), barcode) + await driver.retract() + await stacker.set_dimensions(saved) + self.assertEqual((await driver.request_dimensions())[5], saved) + final = await driver.request_status() + self.assertEqual(final.stacker, 12) + self.assertTrue(final.loader_retracted) + self.assertEqual(await driver.request_errors(), errors) + self.assertIsNone(driver.unresolved_preparation) + self.assertIsNone(driver.unresolved_operation) + with self.assertRaises(IndexError): + reader.next_command() + finally: + await driver.stop() diff --git a/pylabrobot/high_res/micro_serve/tests/captures/loaded_cycles.json b/pylabrobot/high_res/micro_serve/tests/captures/loaded_cycles.json new file mode 100644 index 00000000000..fcfd1d781c1 --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/loaded_cycles.json @@ -0,0 +1,3083 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203435390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203435390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203436300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203436300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203436310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203436310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203436320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203436320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203436330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203436330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203436340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203436340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203436350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203436350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "72656164626172636f6465737461636b657220350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072656164626172636f6465737461636b65722035203436360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "424152434f4445532120436f756e743a20312c2022636f646578220a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072656164626172636f6465737461636b65722035203436360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203436370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203436370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203436380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203436380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203436390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203436390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203437300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203437300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203437310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203437310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "756e6c6f616420350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120756e6c6f61642035203437320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120756e6c6f61642035203437320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203437330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3035333635302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203437330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203437340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3035333635302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203437340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203437350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3035333635302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203437350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203437360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203437360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203437370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3035333635302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203437370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203437380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203437380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203437390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203437390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203438300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203438300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203438310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203438310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203438320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203438320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203438330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203438330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636f756e74706c6174657320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636f756e74706c617465732035203438340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636f756e74706c617465732035203438340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203438350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932383130312c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203438350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203438360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203438360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203438370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932383130312c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203438370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203438380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203438380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203438390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203438390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203439300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203439300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203439310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203439310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203439320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203439320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "756e6c6f6164616e676c6520350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120756e6c6f6164616e676c652035203439330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120756e6c6f6164616e676c652035203439330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203439340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3036343230392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203439340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203439350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3036343230392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203439350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203439360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3036343230392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203439360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203439370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203439370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574616e676c650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574616e676c65203439380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "302e303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574616e676c65203439380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203439390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203734322e3036343230392c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203439390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203530300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203530300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203530310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203530310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203530320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203530320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203530330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203530330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203530340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203530340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203530350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203530350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636f756e74706c6174657320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636f756e74706c617465732035203530360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636f756e74706c617465732035203530360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203530370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932383130312c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203530370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203530380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203530380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203530390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932383130312c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203530390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203531300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203531300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203531310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203531310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203531320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203531320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203531330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203531330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203531340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203531340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6c6f616420350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206c6f61642035203531350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206c6f61642035203531350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203531360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203732382e3432313338372c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203531360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203531370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203732382e3432313338372c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203531370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203531380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203732382e3432313338372c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203531380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203531390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203531390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203532300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722032302e3030303030302c2073706174756c61203732382e3432313338372c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203532300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203532310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203532310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203532320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203532320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203532330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203532330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203532340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203532340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203532350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203532350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203532360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203532360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636f756e74706c6174657320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636f756e74706c617465732035203532370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636f756e74706c617465732035203532370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203532380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932313537302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203532380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "676574706c617465636f756e747320350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120676574706c617465636f756e74732035203532390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "353a20310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120676574706c617465636f756e74732035203532390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203533300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f46462c2073706174756c61486f6d65204f46462c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f4e2c20627574746f6e204f46462c206c6f636b53656e736f72204f4e2c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f722031312e3030303030302c2073706174756c61203732332e3932313537302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203533300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "726574726163740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072657472616374203533310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072657472616374203533310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203533320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203533320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203533330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f4e2c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203533330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031333632392031333632392031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203533340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203133363239203133363239203133363239203533340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203533350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031333632392c20537461636b4865696768742031333632392c20506c617465546869636b6e6573732031333632390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203533350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "72656164626172636f6465737461636b657220350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212072656164626172636f6465737461636b65722035203533360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "424152434f4445532120436f756e743a20312c2022636f646578220a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212072656164626172636f6465737461636b65722035203533360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203533370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203533370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203533380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203533380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203533390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203533390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "736574737461636b657264696d656e73696f6e7320352031313030302031303030302031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120736574737461636b657264696d656e73696f6e732035203131303030203130303030203130303030203534300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120736574737461636b657264696d656e73696f6e732035203131303030203130303030203130303030203534300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203534310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203534310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203534320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203534320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203534330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d6265722031322c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203534330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203534340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203534340a" + } + ] +} \ No newline at end of file diff --git a/pylabrobot/high_res/micro_serve/tests/captures/recovery_guards.json b/pylabrobot/high_res/micro_serve/tests/captures/recovery_guards.json new file mode 100644 index 00000000000..a0d940551fc --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/captures/recovery_guards.json @@ -0,0 +1,539 @@ +{ + "version": "0.2.2", + "commands": [ + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "76657273696f6e0a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212076657273696f6e203434340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "50726f64756374204e616d653a204d6963726f53657276650a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "53657269616c204e756d6265723a204852422d323030382d31303535380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c6962636f6d6d6f6e2056657273696f6e3a20202020312e312e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "6c69627473373630302056657273696f6e3a20202020312e302e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d776172652056657273696f6e3a2020202020322e372e302e3735360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4669726d77617265204275696c643a2041363230343637410a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212076657273696f6e203434340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203434350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203434350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203434360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203434360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203434370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203434370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203434380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203434380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203434390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203434390a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203435300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203435300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636c65617261626f72740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636c65617261626f7274203435310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636c65617261626f7274203435310a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203435320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203435320a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203435330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203435330a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "636c65617261626f72740a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120636c65617261626f7274203435340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120636c65617261626f7274203435340a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203435350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203435350a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b2120737461747573203435360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212073746174757320686f6d65642c20737461636b65724e756d62657220352c20466c6167733a206361726f7573656c486f6d65204f4e2c206566666563747561746f72486f6d65204f4e2c2073706174756c61486f6d65204f4e2c20626172636f6465486f6d65204f46462c20706c61746553656e736f72204f46462c20627574746f6e204f46462c206c6f636b53656e736f72204f46462c20506f736974696f6e733a206361726f7573656c20302e3030303030302c206566666563747561746f7220302e3030303030302c2073706174756c6120302e3030303030302c20626172636f646520302e3030303030302c206d616368696e655374617465204e6f74427573792c206c6f6164657253746174757320457874656e6465642c206d6f64652052656164790a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b2120737461747573203435360a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "64696d7374617475730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b212064696d737461747573203435370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220343a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220353a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220363a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220373a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220383a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b657220393a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031303a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031313a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031323a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "537461636b65722031333a20506c6174654865696768742031313030302c20537461636b4865696768742031303030302c20506c617465546869636b6e6573732031303030300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b212064696d737461747573203435370a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "write", + "data": "6572726f72732031300a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "41434b21206572726f7273203130203435380a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4572726f7220313a202831363a30333a303229203332313a20496e76616c696420706c61746520636f756e742c20636865636b20706c6174652064696d656e73696f6e730a" + }, + { + "module": "socket", + "device_id": "10.253.253.253:1000", + "action": "readline", + "data": "4f4b21206572726f7273203130203435380a" + } + ] +} \ No newline at end of file From f5e5d165827035b01be48f0724ff9670b23b2d62 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Thu, 10 Sep 2026 10:19:01 -0700 Subject: [PATCH 7/8] Model MicroServe stackers as PLR resource stacks --- .../high_res/micro-serve/hello-world.ipynb | 35 ++- docs/user_guide/high_res/micro-serve/index.md | 1 + .../high_res/micro-serve/inventory.md | 169 ++++++++++ .../high_res/micro-serve/state-machine.md | 15 + .../high_res/micro_serve/micro_serve.py | 192 +++++++++++- .../micro_serve/tests/resource_tests.py | 296 ++++++++++++++++++ 6 files changed, 697 insertions(+), 11 deletions(-) create mode 100644 docs/user_guide/high_res/micro-serve/inventory.md create mode 100644 pylabrobot/high_res/micro_serve/tests/resource_tests.py diff --git a/docs/user_guide/high_res/micro-serve/hello-world.ipynb b/docs/user_guide/high_res/micro-serve/hello-world.ipynb index 8ad6f267fd2..34f2aeae05a 100644 --- a/docs/user_guide/high_res/micro-serve/hello-world.ipynb +++ b/docs/user_guide/high_res/micro-serve/hello-world.ipynb @@ -334,6 +334,29 @@ "await microserve.stackers[0].request_plate_count()" ] }, + { + "cell_type": "markdown", + "id": "295c49b2", + "metadata": {}, + "source": [ + "## Resource stacks and plate inventory\n", + "\n", + "Each `microserve.stackers[i]` is a PLR `ResourceStack` containing the expected `Plate` objects, in bottom-to-top order. No inventory is inferred from firmware counts or barcodes. An empty list here means no resources have been recorded, not necessarily an empty physical stack.\n", + "\n", + "See [plate inventory](inventory.md) to register known plates, derive dimensions, save inventories, and confirm external transfers. Resource coordinates do not supply calibrated robot pickup positions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5f8c662", + "metadata": {}, + "outputs": [], + "source": [ + "stacker = microserve.stackers[0]\n", + "stacker.children" + ] + }, { "cell_type": "markdown", "id": "128169c6", @@ -448,7 +471,9 @@ "source": [ "## Finish loading\n", "\n", - "Have the robot place the plate at the taught transfer position, then withdraw completely. Only after that physical action, retract the mechanism." + "Have the robot place the plate at the taught transfer position, then withdraw completely. Only after that physical action, retract the mechanism.\n", + "\n", + "If tracking inventory, call `await stacker.confirm_plate_loaded(plate)` with the actual placed `Plate` before this retraction cell. Preparation alone does not add a plate." ] }, { @@ -488,7 +513,9 @@ "source": [ "## Finish unloading\n", "\n", - "Have the robot pick the presented plate and withdraw completely before running this cell." + "Have the robot pick the presented plate and withdraw completely before running this cell.\n", + "\n", + "If tracking inventory, call `await stacker.confirm_plate_unloaded(plate)` with the actual picked `Plate` before this retraction cell. Preparation alone does not remove a plate." ] }, { @@ -816,7 +843,9 @@ "source": [ "## Finish the angle-reporting handoff\n", "\n", - "Pick the presented plate and withdraw the robot before retracting." + "Pick the presented plate and withdraw the robot before retracting.\n", + "\n", + "If tracking inventory, call `await stacker.confirm_plate_unloaded(plate)` with the actual picked `Plate` before this retraction cell. Preparation alone does not remove a plate." ] }, { diff --git a/docs/user_guide/high_res/micro-serve/index.md b/docs/user_guide/high_res/micro-serve/index.md index 5bbb39a1db6..f3679753d75 100644 --- a/docs/user_guide/high_res/micro-serve/index.md +++ b/docs/user_guide/high_res/micro-serve/index.md @@ -8,6 +8,7 @@ plate handoffs and recovery; the protocol reference describes commands and valid :maxdepth: 1 hello-world +inventory state-machine protocol capabilities diff --git a/docs/user_guide/high_res/micro-serve/inventory.md b/docs/user_guide/high_res/micro-serve/inventory.md new file mode 100644 index 00000000000..b5de23db0f3 --- /dev/null +++ b/docs/user_guide/high_res/micro-serve/inventory.md @@ -0,0 +1,169 @@ +# Plate inventory + +Each entry in `microserve.stackers` is a `MicroServeStacker`, a PLR +`ResourceStack` with `direction="z"`. It retains the stacker's hardware methods +and holds actual `Plate` resources. The fourteen entries remain indexed 0–13. + +Inventory is the expected arrangement supplied by your workflow. An empty +`children` list means no plates have been recorded; it does not prove that the +physical stacker is empty. Firmware counts, height measurements, and barcode +scans never manufacture plate definitions or change resource ownership. + +## Record the starting arrangement + +Use plate definitions that match the physical labware, including lid and stacking +pitch. In this example, `bottom_plate` and `top_plate` are existing `Plate` objects +for the two plates you have inspected in stacker 5: + +```python +stacker = microserve.stackers[5] +stacker.assign_child_resource(bottom_plate) +stacker.assign_child_resource(top_plate) + +assert stacker.get_top_item() is top_plate +print(stacker.children) # bottom to top +print(stacker.get_size_z()) +``` + +Assignment records inventory only. It sends no command, moves no hardware, and +does not set the firmware's cached count. Only the top plate can be removed. +Repeating assignment of the same top plate leaves its position unchanged. +Each physical stack must contain one compatible plate geometry; resource +assignment does not validate physical capacity or compatibility. + +You can also construct detached stacks with initial inventories. Like PLR's +`ResourceStack`, the constructor takes `resources` **top to bottom**: + +```python +from pylabrobot.high_res import HighResMicroServe, MicroServeStacker + +stackers = [ + MicroServeStacker(index=i, name=f"carousel_stacker_{i}") + for i in range(14) +] +# Pass detached stacks in index order. This does not open the connection. +microserve = HighResMicroServe(host="10.253.253.253", stackers=stackers) +``` + +For example, a detached stack with two plates can be constructed as +`MicroServeStacker(index=5, resources=[top_plate, bottom_plate])`. +Use distinct names when placing several devices' stacks in a resource tree. +`HighResMicroServe(name="carousel_a", ...)` prefixes the default stacker names. + +## Geometry and transfer coordinates + +For a bare plate with a known `stacking_z_height`, derive the device's height and +stacking pitch while supplying the measured spatula-support thickness: + +```python +from pylabrobot.high_res import MicroServePlateDimensions + +dimensions = MicroServePlateDimensions.from_plate( + top_plate, thickness=measured_support_thickness +) +``` + +The support thickness is the distance from the top to the underside of the wells, +not the plate's overall height. If pitch is unknown, supply `stack_height` +explicitly. Lidded plates always require an explicit measured pitch for that +plate/lid combination; the helper includes the lid in overall height. + +Stack resource locations describe storage geometry. They are **not calibrated +robot pickup coordinates**. The MicroServe presents plates at a shared, moving +transfer position. Teach the robot's handoff separately before moving a robot to +it. This driver does not supply a calibrated transfer `PlateHolder`. + +## Confirm a pickup + +Remember the expected top plate before preparing it: + +```python +plate = stacker.get_top_item() +await stacker.prepare_for_unload(dimensions) +``` + +Have the robot pick that plate and withdraw. Only after successful pickup: + +```python +await stacker.confirm_plate_unloaded(plate) +``` + +If the plate is still assigned to the stack in PLR, confirmation detaches it. +If the robot already reassigned it to its destination, confirmation preserves +that assignment. The lower plates must match the inventory recorded at preparation. +An unexpected inventory change raises an error for inspection. + +After the robot clears the mechanism: + +```python +await microserve.retract() +``` + +The angle-reporting preparation uses the same confirmation method. Preparation +and retraction alone never remove a plate from inventory. + +## Confirm a placement + +Prepare the receiving position with the incoming plate's validated dimensions: + +```python +await stacker.prepare_for_load(dimensions) +``` + +Have the robot place `incoming_plate` and withdraw. After successful placement: + +```python +await stacker.confirm_plate_loaded(incoming_plate) +``` + +Confirmation assigns that plate to the top, or accepts the robot's existing +assignment if it matches the expected inventory. Then retract: + +```python +await microserve.retract() +``` + +Confirmations are assertions of a completed external action, not sensor queries +or robot commands. Call them before retraction while this driver owns the +preparation. Repeating confirmation of the same plate during that handoff does +nothing; confirming a different plate is rejected. Direct resource assignment +remains available for inspected manual inventory corrections. + +## Failures and saved inventories + +A failed or interrupted preparation leaves inventory unchanged. Reconnecting +does not infer a pickup or placement. After inspecting the device, successful +`reconcile_preparation()` restores preparation ownership and permits explicit +confirmation of the actual external action. Retraction can instead end the +handoff without changing inventory. If the robot's result is uncertain, inspect +and reconcile the physical plates and resource tree before continuing. + +You can save the expected inventory without saving a connection or handoff: + +```python +import json +from pathlib import Path + +path = Path("microserve-inventory.json") +path.write_text(json.dumps([stack.serialize() for stack in microserve.stackers])) +``` + +Restore and bind it to a controller without opening a connection: + +```python +restored = [ + MicroServeStacker.deserialize(data) + for data in json.loads(path.read_text()) +] +microserve = HighResMicroServe(host="10.253.253.253", stackers=restored) +``` + +Restored stacks are detached until passed to the device constructor. Their +hardware methods fail while detached. Serialization preserves plate resources, +stacker indices, names, metadata, and child locations; it excludes sockets and +all preparation/recovery ownership. Check saved inventory against the actual +machine before use. It is not a checkpoint for resuming an interrupted transfer. + +These inventory rules are tested with simulated transfers, including multiple +plates, serialization, and interrupted preparation. Actual robot handoffs and +multi-plate ordering remain unverified on hardware; see [validation](validation.md). diff --git a/docs/user_guide/high_res/micro-serve/state-machine.md b/docs/user_guide/high_res/micro-serve/state-machine.md index 358a4492812..8978ee783c0 100644 --- a/docs/user_guide/high_res/micro-serve/state-machine.md +++ b/docs/user_guide/high_res/micro-serve/state-machine.md @@ -39,6 +39,21 @@ The robot's action does not clear ownership, and a change in the plate-detection authorize another plate fetch. Retraction ends the handoff. An extended loader whose preparation is not owned by this driver cannot be adopted by calling prepare again. +## Resource inventory during a handoff + +Each stacker is a vertical PLR `ResourceStack`. Its expected plate inventory is +independent of the motion state above. Preparation snapshots the current plate +order and leaves it unchanged. After a successful external pickup or placement, +`confirm_plate_unloaded(plate)` or `confirm_plate_loaded(plate)` records that one +transfer. Repeats for the same plate preserve inventory; a second plate in the +same handoff is rejected. Confirm before retracting. + +If the robot already updated the resource tree, confirmation checks the expected +result and preserves the robot's assignment. Retraction, counts, scans, and +reconnects never infer an inventory change. An interrupted preparation retains +its original expected order for confirmation after successful reconciliation. +See [plate inventory](inventory.md) for initialization, geometry, and recovery. + ## Connection and homing ```{graphviz} images/lifecycle.dot diff --git a/pylabrobot/high_res/micro_serve/micro_serve.py b/pylabrobot/high_res/micro_serve/micro_serve.py index 21871d2e5c4..4a98785c4b2 100644 --- a/pylabrobot/high_res/micro_serve/micro_serve.py +++ b/pylabrobot/high_res/micro_serve/micro_serve.py @@ -5,9 +5,10 @@ import math import re from dataclasses import dataclass -from typing import Dict, List, Literal, Optional, Tuple +from typing import Any, Dict, List, Literal, Mapping, Optional, Sequence, Tuple, cast from pylabrobot.io import Socket +from pylabrobot.resources import Coordinate, Plate, Resource, ResourceStack logger = logging.getLogger(__name__) @@ -57,6 +58,26 @@ def _wire(self) -> str: """Encode dimensions in the order and units required by the controller.""" return " ".join(str(round(v * 1000)) for v in (self.height, self.stack_height, self.thickness)) + @classmethod + def from_plate( + cls, plate: Plate, *, thickness: float, stack_height: Optional[float] = None + ) -> "MicroServePlateDimensions": + """Use a plate's height and pitch, with an explicitly measured support thickness. + + Supply ``stack_height`` for lidded plates or when ``stacking_z_height`` is + unknown. The pitch must describe the actual combination of plate and lid. + """ + height = plate.get_size_z() + if plate.lid is not None: + height += plate.lid.get_size_z() - plate.lid.nesting_z_height + if stack_height is None: + raise ValueError("Supply the measured stacking pitch for lidded plates") + if stack_height is None: + stack_height = plate.stacking_z_height + if stack_height is None: + raise ValueError("Supply stack_height or define plate.stacking_z_height") + return cls(height=height, stack_height=stack_height, thickness=thickness) + @dataclass(frozen=True) class MicroServeStatus: @@ -184,6 +205,9 @@ def __init__( ack_timeout: float = 10.0, command_timeout: float = 120.0, scan_timeout: float = 300.0, + *, + name: str = "microserve", + stackers: Optional[Sequence["MicroServeStacker"]] = None, ) -> None: """Configure the connection and timeouts in seconds without opening it.""" for timeout in (ack_timeout, command_timeout, scan_timeout): @@ -206,7 +230,26 @@ def __init__( self._unresolved_preparation: Optional[MicroServeUnresolvedPreparation] = None self._unresolved_operation: Optional[str] = None self._manual_mode = False - self.stackers = tuple(MicroServeStacker(self, index) for index in range(14)) + self._transfer_inventory: Tuple[Resource, ...] = () + self._confirmed_transfer: Optional[Tuple[str, int, Plate]] = None + if stackers is None: + self.stackers = tuple( + MicroServeStacker(self, index, name=f"{name}_stacker_{index}") for index in range(14) + ) + else: + configured = tuple(stackers) + if len(configured) != 14 or any( + not isinstance(stack, MicroServeStacker) or stack.index != index + for index, stack in enumerate(configured) + ): + raise ValueError("Supply fourteen MicroServeStackers ordered by index 0 through 13") + if any(stack._controller is not None for stack in configured): + raise ValueError("Supplied stackers must be detached from a controller") + if len({stack.name for stack in configured}) != 14: + raise ValueError("Stacker names must be unique") + self.stackers = configured + for stack in self.stackers: + stack._controller = self @property def last_command_id(self) -> Optional[int]: @@ -724,19 +767,148 @@ async def retract(self) -> None: self._unresolved_operation = None -class MicroServeStacker: +class MicroServeStacker(ResourceStack): """One of the fourteen stackers, accessed through ``device.stackers[index]``. Stacker indices are zero-based. Preparation methods position a plate or an empty receiving location for an external robot; they do not operate the robot. + + Children represent the expected inventory, bottom to top. Constructor + ``resources`` are supplied top to bottom, following ``ResourceStack``. Counts, + scans, preparation, and retraction never infer or change this inventory. + Resource coordinates describe the stack, not a calibrated robot handoff. + Each physical stack must use one compatible plate geometry; resource assignment + does not certify plate compatibility or the carousel's physical capacity. """ - def __init__(self, device: HighResMicroServe, index: int) -> None: - """Associate this stacker with its controller and zero-based index.""" + def __init__( + self, + device: Optional[HighResMicroServe] = None, + index: int = 0, + *, + name: Optional[str] = None, + resources: Optional[List[Resource]] = None, + metadata: Optional[Mapping[str, Any]] = None, + category: str = "resource_group", + model: Optional[str] = None, + ) -> None: + """Create a vertical plate stack, optionally bound to a controller. + + Deserialized stacks are detached. Pass all fourteen as ``stackers`` to + ``HighResMicroServe`` to bind them without opening a hardware connection. + """ if isinstance(index, bool) or not isinstance(index, int) or not 0 <= index < 14: raise ValueError("Stacker index must be an integer from 0 through 13") - self._device = device + self._controller = device self._index = index + super().__init__( + name=name or f"microserve_stacker_{index}", + direction="z", + resources=resources, + metadata=metadata, + ) + self.category = category + self.model = model + + @property + def _device(self) -> HighResMicroServe: + """Require a live controller binding for hardware operations.""" + if self._controller is None: + raise RuntimeError("Attach this stacker through HighResMicroServe(stackers=...) first") + return self._controller + + def serialize(self) -> dict: + """Save inventory and index without sockets, controller ownership, or handoff state.""" + data = super().serialize() + for key in ("size_x", "size_y", "size_z"): + data.pop(key) + data["index"] = self.index + return data + + def check_can_drop_resource_here(self, resource: Resource, *, reassign: bool = True) -> None: + """Accept plates only; physical compatibility still requires validated geometry.""" + if not isinstance(resource, Plate): + raise TypeError("MicroServe stackers can contain only Plate resources") + + def assign_child_resource( + self, resource: Resource, location: Optional[Coordinate] = None, reassign: bool = True + ) -> None: + """Record a plate on top, without moving hardware or changing firmware counts.""" + self.check_can_drop_resource_here(resource, reassign=reassign) + if resource.parent is self and reassign and self.get_top_item() is resource: + return + super().assign_child_resource(resource, location=location, reassign=reassign) + + def unassign_child_resource(self, resource: Resource) -> None: + """Remove the expected top plate from inventory, without moving hardware.""" + if not self.children or self.children[-1] is not resource: + raise ValueError("Only the top plate can be removed from a MicroServe stacker") + super().unassign_child_resource(resource) + + def get_top_item(self) -> Plate: + """Return the expected accessible plate, raising ValueError for an empty stack.""" + return cast(Plate, super().get_top_item()) + + async def confirm_plate_loaded(self, plate: Plate) -> None: + """Record a successful external placement during this stacker's prepared load. + + Call only after the robot or operator confirms placement. This performs no + hardware operation. Repeating with the same plate records it only once. + A robot that already assigned the plate to this stack is also supported. + """ + await self._confirm_transfer("load", plate) + + async def confirm_plate_unloaded(self, plate: Plate) -> None: + """Record a successful external pickup during this stacker's prepared unload. + + Call only after confirmed pickup. The plate is detached if still on this + stack; an assignment already made by the robot to its destination is kept. + This performs no hardware operation and never removes a second plate on repeat. + """ + await self._confirm_transfer("unload", plate) + + async def _confirm_transfer(self, direction: Literal["load", "unload"], plate: Plate) -> None: + """Commit one explicitly confirmed plate transfer under the controller lock.""" + if not isinstance(plate, Plate): + raise TypeError("A transfer requires a Plate resource") + device = self._device + async with device._lock: + device._require_resolved_preparation() + prepared = device._prepared + allowed = ("load",) if direction == "load" else ("unload", "unloadangle") + if prepared is None or prepared[0] not in allowed or prepared[1] != self.index: + raise RuntimeError("Confirm the transfer before retracting its owned preparation") + confirmed = device._confirmed_transfer + if confirmed is not None: + if confirmed[0] != direction or confirmed[1] != self.index or confirmed[2] is not plate: + raise RuntimeError("A plate transfer has already been confirmed for this preparation") + before = device._transfer_inventory + if direction == "load": + if any(item is plate for item in before): + raise ValueError("The plate was already in this stack before preparation") + after = (*before, plate) + else: + if not before or before[-1] is not plate: + raise ValueError("The plate must be the top plate recorded before preparation") + after = before[:-1] + current = tuple(self.children) + unchanged = len(current) == len(before) and all(a is b for a, b in zip(current, before)) + transferred = len(current) == len(after) and all(a is b for a, b in zip(current, after)) + if confirmed is not None: + if not transferred: + raise RuntimeError("Inventory changed after confirmation; inspect before continuing") + return + if not unchanged and not transferred: + raise RuntimeError("Inventory changed during the handoff; reconcile it after inspection") + if unchanged: + if direction == "load": + self.assign_child_resource(plate) + else: + self.unassign_child_resource(plate) + device._confirmed_transfer = (direction, self.index, plate) + logger.info( + "Confirmed MicroServe stacker %d %s of plate %s", self.index, direction, plate.name + ) @property def index(self) -> int: @@ -867,6 +1039,8 @@ async def _prepare( await self._set_dimensions(dimensions) logger.info("Preparing MicroServe stacker %d for %s", self.index, direction) command_id = None + self._device._transfer_inventory = tuple(self.children) + self._device._confirmed_transfer = None self._device._prepared = None try: lines = await self._device._command(f"{direction} {self.index}") @@ -889,7 +1063,8 @@ async def prepare_for_load(self, dimensions: MicroServePlateDimensions) -> None: """Present an empty receiving position for the robot to place a plate. Repeating this call in the same verified transfer state does not move the - device again. After placing the plate, clear the robot and call ``retract()``. + device again. After confirmed placement, use ``confirm_plate_loaded(plate)`` + to update inventory, clear the robot, and call ``retract()``. """ await self._prepare("load", dimensions) @@ -898,7 +1073,8 @@ async def prepare_for_unload(self, dimensions: MicroServePlateDimensions) -> Non A confirmed preparation is not repeated before retraction, even if the robot has already removed the plate. After the robot clears the device, - call ``retract()`` to end the handoff. + use ``confirm_plate_unloaded(plate)`` to update inventory and call ``retract()`` + to end the handoff. Preparation itself never removes a resource. """ await self._prepare("unload", dimensions) diff --git a/pylabrobot/high_res/micro_serve/tests/resource_tests.py b/pylabrobot/high_res/micro_serve/tests/resource_tests.py new file mode 100644 index 00000000000..e9520196934 --- /dev/null +++ b/pylabrobot/high_res/micro_serve/tests/resource_tests.py @@ -0,0 +1,296 @@ +"""Resource inventories and confirmed transfers, without hardware connections.""" + +import asyncio +import json +import unittest + +from pylabrobot.high_res.micro_serve import ( + HighResMicroServe, + MicroServePlateDimensions, + MicroServeStacker, +) +from pylabrobot.high_res.micro_serve.tests.micro_serve_tests import ( + DIMENSIONS, + GEOMETRY, + FakeSocket, + status, +) +from pylabrobot.resources import Coordinate, Lid, Plate, Resource, ResourceStack + + +def plate(name: str) -> Plate: + """Create a plate with known height and nesting pitch for inventory tests.""" + return Plate(name, size_x=127.76, size_y=85.48, size_z=11, stacking_z_height=10, ordered_items={}) + + +class MicroServeResourceTests(unittest.TestCase): + """Check resource ordering, geometry, serialization, and controller binding.""" + + def test_stack_is_a_resource_stack_with_top_access_and_nesting(self) -> None: + """Only the upper plate can leave; children and locations follow PLR conventions.""" + top, bottom = plate("top"), plate("bottom") + stack = MicroServeStacker(index=5, resources=[top, bottom]) + self.assertIsInstance(stack, ResourceStack) + self.assertEqual(stack.direction, "z") + self.assertEqual(stack.children, [bottom, top]) + self.assertIs(stack.get_top_item(), top) + self.assertEqual(top.location, Coordinate(0, 0, 10)) + self.assertEqual(stack.get_size_z(), 21) + with self.assertRaises(ValueError): + stack.unassign_child_resource(bottom) + stack.assign_child_resource(top) + self.assertEqual(len(stack.children), 2) + stack.unassign_child_resource(top) + self.assertIsNone(top.parent) + self.assertIs(stack.get_top_item(), bottom) + stack.unassign_child_resource(bottom) + with self.assertRaises(ValueError): + stack.get_top_item() + with self.assertRaises(ValueError): + stack.unassign_child_resource(bottom) + + def test_nonplates_are_rejected(self) -> None: + """A resource tree cannot silently contain arbitrary labware in the stack.""" + stack = MicroServeStacker() + resource = Resource("box", 1, 1, 1) + with self.assertRaises(TypeError): + stack.assign_child_resource(resource) + with self.assertRaises(TypeError): + stack.check_can_drop_resource_here(resource) + self.assertIsNone(resource.parent) + + def test_serialized_inventory_restores_detached_and_can_bind(self) -> None: + """JSON round trips plate identity, ordering, index, metadata, and placement, not IO.""" + device = HighResMicroServe("192.0.2.1", name="carousel") + stack = device.stackers[5] + stack.metadata["inventory_source"] = "operator" + stack.assign_child_resource(plate("bottom")) + stack.assign_child_resource(plate("top")) + root = Resource("deck", 1000, 1000, 0) + root.assign_child_resource(stack, Coordinate(100, 200, 0)) + restored_root = Resource.deserialize(json.loads(json.dumps(root.serialize()))) + restored_stack = restored_root.children[0] + self.assertIsInstance(restored_stack, MicroServeStacker) + self.assertEqual(restored_stack.serialize(), stack.serialize()) + restored = [ + MicroServeStacker.deserialize(json.loads(json.dumps(s.serialize()))) for s in device.stackers + ] + self.assertTrue(all(isinstance(s, MicroServeStacker) for s in restored)) + rebound = HighResMicroServe("192.0.2.2", stackers=restored) + self.assertEqual(rebound.stackers[5].index, 5) + self.assertEqual(rebound.stackers[5].get_top_item().name, "top") + self.assertEqual(rebound.stackers[5].get_size_z(), 21) + self.assertEqual(rebound.stackers[5].metadata, {"inventory_source": "operator"}) + with self.assertRaises(ValueError): + HighResMicroServe("192.0.2.3", stackers=rebound.stackers) + + def test_invalid_bindings_do_not_partially_attach(self) -> None: + """Incorrect indices or duplicated names fail before any controller is attached.""" + stacks = [MicroServeStacker(index=i) for i in range(14)] + with self.assertRaises(ValueError): + HighResMicroServe("192.0.2.1", stackers=stacks[:-1]) + with self.assertRaises(ValueError): + HighResMicroServe("192.0.2.1", stackers=list(reversed(stacks))) + stacks[13] = MicroServeStacker(index=13, name=stacks[12].name) + with self.assertRaises(ValueError): + HighResMicroServe("192.0.2.1", stackers=stacks) + stacks[13] = MicroServeStacker(index=13, name="last") + HighResMicroServe("192.0.2.1", stackers=stacks) + + def test_plate_geometry_requires_known_pitch_and_support_thickness(self) -> None: + """Plate height and pitch map to device units without guessing unknown geometry.""" + item = plate("plate") + self.assertEqual(MicroServePlateDimensions.from_plate(item, thickness=10), GEOMETRY) + item.stacking_z_height = None + with self.assertRaises(ValueError): + MicroServePlateDimensions.from_plate(item, thickness=10) + self.assertEqual( + MicroServePlateDimensions.from_plate(item, thickness=10, stack_height=10), GEOMETRY + ) + item.assign_child_resource(Lid("lid", 127.76, 85.48, 4, nesting_z_height=2)) + with self.assertRaises(ValueError): + MicroServePlateDimensions.from_plate(item, thickness=10) + self.assertEqual( + MicroServePlateDimensions.from_plate(item, thickness=10, stack_height=13), + MicroServePlateDimensions(height=13, stack_height=13, thickness=10), + ) + + +class MicroServeInventoryTransferTests(unittest.IsolatedAsyncioTestCase): + """Inventory changes follow explicit successful robot handoffs, not firmware motion.""" + + async def asyncSetUp(self) -> None: + """Create a scripted controller and a two-plate inventory.""" + self.device = HighResMicroServe("192.0.2.1") + self.io = FakeSocket() + self.device.io = self.io # type: ignore[assignment] + await self.device.setup() + self.stack = self.device.stackers[0] + self.bottom, self.top = plate("bottom"), plate("top") + self.stack.assign_child_resource(self.bottom) + self.stack.assign_child_resource(self.top) + + def script_prepare(self, direction: str) -> None: + """Queue geometry confirmation and an acknowledged preparation.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 0 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.add(f"{direction} 0") + self.io.add("status", status(extended=True)) + + async def test_detached_hardware_methods_fail_without_io(self) -> None: + """Deserializing or constructing a resource does not provide hardware access.""" + with self.assertRaisesRegex(RuntimeError, "Attach this stacker"): + await MicroServeStacker().request_plate_count() + + async def test_preparation_and_retraction_preserve_inventory(self) -> None: + """Presenting and returning a plate does not imply an external pickup.""" + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + self.assertIs(self.stack.get_top_item(), self.top) + self.io.add("status", status(extended=True)) + self.io.add("retract") + self.io.add("status", status()) + await self.device.retract() + self.assertEqual(self.stack.children, [self.bottom, self.top]) + with self.assertRaises(RuntimeError): + await self.stack.confirm_plate_unloaded(self.top) + + async def test_confirmed_unload_removes_only_the_named_top_once(self) -> None: + """Repeating a confirmation cannot accidentally consume the next plate.""" + self.script_prepare("unloadangle") + await self.stack.prepare_for_unload_with_angle(GEOMETRY) + writes = list(self.io.writes) + await asyncio.gather( + self.stack.confirm_plate_unloaded(self.top), self.stack.confirm_plate_unloaded(self.top) + ) + self.assertEqual(self.stack.children, [self.bottom]) + self.assertIsNone(self.top.parent) + with self.assertRaises(RuntimeError): + await self.stack.confirm_plate_unloaded(self.bottom) + self.assertEqual(self.io.writes, writes) + + async def test_robot_resource_assignment_is_preserved(self) -> None: + """Confirmation accepts the robot's successful move without detaching its destination.""" + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + destination = Resource("destination", 200, 100, 20) + destination.assign_child_resource(self.top, Coordinate.zero()) + await self.stack.confirm_plate_unloaded(self.top) + self.assertIs(self.top.parent, destination) + self.assertEqual(self.stack.children, [self.bottom]) + + async def test_confirmed_load_adds_plate_once(self) -> None: + """A successful placement becomes the top plate with the correct nesting height.""" + incoming = plate("incoming") + self.script_prepare("load") + await self.stack.prepare_for_load(GEOMETRY) + self.assertEqual(len(self.stack.children), 2) + await self.stack.confirm_plate_loaded(incoming) + await self.stack.confirm_plate_loaded(incoming) + self.assertIs(self.stack.get_top_item(), incoming) + self.assertEqual(self.stack.get_size_z(), 31) + with self.assertRaises(RuntimeError): + await self.stack.confirm_plate_loaded(plate("another")) + + async def test_robot_load_assignment_is_not_duplicated(self) -> None: + """PLR robot assignment can precede confirmation without adding a second child.""" + incoming = plate("incoming") + self.script_prepare("load") + await self.stack.prepare_for_load(GEOMETRY) + self.stack.assign_child_resource(incoming) + await self.stack.confirm_plate_loaded(incoming) + self.assertEqual(len(self.stack.children), 3) + + async def test_repeated_preparation_keeps_the_original_inventory(self) -> None: + """A repeated unload after pickup cannot change which plate was handed off.""" + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + await self.stack.confirm_plate_unloaded(self.top) + self.io.add("status", status(extended=True)) + self.io.add("dimstatus", *DIMENSIONS) + await self.stack.prepare_for_unload(GEOMETRY) + await self.stack.confirm_plate_unloaded(self.top) + self.assertEqual(self.stack.children, [self.bottom]) + self.assertEqual(self.io.writes.count(b"unload 0\n"), 1) + + async def test_next_handoff_can_transfer_another_plate(self) -> None: + """Retraction and a fresh preparation establish a separate transfer.""" + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + await self.stack.confirm_plate_unloaded(self.top) + self.io.add("status", status(extended=True)) + self.io.add("retract") + self.io.add("status", status()) + await self.device.retract() + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + await self.stack.confirm_plate_unloaded(self.bottom) + self.assertEqual(self.stack.children, []) + + async def test_changed_inventory_after_confirmation_is_rejected(self) -> None: + """A repeat does not silently repair a subsequent inventory mutation.""" + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + await self.stack.confirm_plate_unloaded(self.top) + self.stack.assign_child_resource(self.top) + with self.assertRaisesRegex(RuntimeError, "Inventory changed after confirmation"): + await self.stack.confirm_plate_unloaded(self.top) + self.assertIs(self.stack.get_top_item(), self.top) + + async def test_failed_robot_pickup_and_disconnect_leave_inventory_unchanged(self) -> None: + """Without successful external pickup, ending the connection never removes a plate.""" + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + await self.device.stop() + await self.device.setup() + with self.assertRaises(RuntimeError): + await self.stack.confirm_plate_unloaded(self.top) + self.assertEqual(self.stack.children, [self.bottom, self.top]) + + async def test_confirmation_rejects_wrong_plate_direction_and_stacker(self) -> None: + """Only the owned handoff and the original top plate can be confirmed.""" + with self.assertRaises(RuntimeError): + await self.stack.confirm_plate_unloaded(self.top) + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + with self.assertRaises(ValueError): + await self.stack.confirm_plate_unloaded(self.bottom) + with self.assertRaises(RuntimeError): + await self.stack.confirm_plate_loaded(plate("incoming")) + with self.assertRaises(RuntimeError): + await self.device.stackers[1].confirm_plate_unloaded(self.top) + self.assertEqual(self.stack.children, [self.bottom, self.top]) + + async def test_unexpected_inventory_change_requires_inspection(self) -> None: + """Another inventory mutation cannot be mistaken for the expected transfer.""" + self.script_prepare("unload") + await self.stack.prepare_for_unload(GEOMETRY) + self.stack.assign_child_resource(plate("unexpected")) + with self.assertRaisesRegex(RuntimeError, "Inventory changed"): + await self.stack.confirm_plate_unloaded(self.top) + self.assertEqual(len(self.stack.children), 3) + + async def test_timeout_preserves_inventory_until_reconciled_confirmation(self) -> None: + """An uncertain preparation retains its original inventory across reconnects.""" + self.io.add("status", status()) + self.io.add("setstackerdimensions 0 11000 10000 10000") + self.io.add("dimstatus", *DIMENSIONS) + self.io.scripts.append(("unload 0", [b"ACK! unload 0 4\r\n", TimeoutError()])) + with self.assertRaises(TimeoutError): + await self.stack.prepare_for_unload(GEOMETRY) + self.assertEqual(self.stack.children, [self.bottom, self.top]) + with self.assertRaises(RuntimeError): + await self.stack.confirm_plate_unloaded(self.top) + await self.device.setup() + self.io.add("commandstat 4", " 4 OK! - unload 0") + self.io.add("status", status(extended=True)) + await self.device.reconcile_preparation() + await self.stack.confirm_plate_unloaded(self.top) + self.assertEqual(self.stack.children, [self.bottom]) + + async def test_counts_do_not_create_or_remove_resources(self) -> None: + """Firmware counts and identity-bearing PLR inventories remain distinct.""" + self.io.add("getplatecounts 0", "0: 17") + self.assertEqual(await self.stack.request_plate_count(), 17) + self.assertEqual(self.stack.children, [self.bottom, self.top]) From f472ef851b534135ce45629413ca76536960f9df Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Thu, 10 Sep 2026 10:21:53 -0700 Subject: [PATCH 8/8] Preserve standard resource fields in MicroServe serialization --- pylabrobot/high_res/micro_serve/micro_serve.py | 9 +++++++-- pylabrobot/high_res/micro_serve/tests/resource_tests.py | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pylabrobot/high_res/micro_serve/micro_serve.py b/pylabrobot/high_res/micro_serve/micro_serve.py index 4a98785c4b2..11e51765450 100644 --- a/pylabrobot/high_res/micro_serve/micro_serve.py +++ b/pylabrobot/high_res/micro_serve/micro_serve.py @@ -791,14 +791,21 @@ def __init__( metadata: Optional[Mapping[str, Any]] = None, category: str = "resource_group", model: Optional[str] = None, + size_x: float = 0, + size_y: float = 0, + size_z: float = 0, ) -> None: """Create a vertical plate stack, optionally bound to a controller. Deserialized stacks are detached. Pass all fourteen as ``stackers`` to ``HighResMicroServe`` to bind them without opening a hardware connection. + Size fields support PLR resource deserialization and must be zero; actual + stack dimensions are calculated from its children by ``ResourceStack``. """ if isinstance(index, bool) or not isinstance(index, int) or not 0 <= index < 14: raise ValueError("Stacker index must be an integer from 0 through 13") + if (size_x, size_y, size_z) != (0, 0, 0): + raise ValueError("Stack sizes are calculated from plates; fixed size fields must be zero") self._controller = device self._index = index super().__init__( @@ -820,8 +827,6 @@ def _device(self) -> HighResMicroServe: def serialize(self) -> dict: """Save inventory and index without sockets, controller ownership, or handoff state.""" data = super().serialize() - for key in ("size_x", "size_y", "size_z"): - data.pop(key) data["index"] = self.index return data diff --git a/pylabrobot/high_res/micro_serve/tests/resource_tests.py b/pylabrobot/high_res/micro_serve/tests/resource_tests.py index e9520196934..80350ef5a68 100644 --- a/pylabrobot/high_res/micro_serve/tests/resource_tests.py +++ b/pylabrobot/high_res/micro_serve/tests/resource_tests.py @@ -66,6 +66,10 @@ def test_serialized_inventory_restores_detached_and_can_bind(self) -> None: stack.metadata["inventory_source"] = "operator" stack.assign_child_resource(plate("bottom")) stack.assign_child_resource(plate("top")) + self.assertEqual( + {key: stack.serialize()[key] for key in ("size_x", "size_y", "size_z")}, + {"size_x": 0, "size_y": 0, "size_z": 0}, + ) root = Resource("deck", 1000, 1000, 0) root.assign_child_resource(stack, Coordinate(100, 200, 0)) restored_root = Resource.deserialize(json.loads(json.dumps(root.serialize())))