Metadata-Version: 2.4
Name: opentron_glulac_readout_module
Version: 3.0.0
Summary: A TU Vienna university project, building a system that allow plugging a Jobst SIX Biotransmitter into the Opentron OT2 automated liquid handler system and then automates measurements with a custom printed labware
Author-email: Michael Höller <pypi.capably513@passmail.net>
License-Expression: MIT
Project-URL: Homepage, https://www.cellchipgroup.org/
Keywords: tuvienna,opentron,cellchip
Requires-Python: ==3.12.*
Description-Content-Type: text/markdown
License-File: LICENCE
Requires-Dist: pyserial>=3.5
Requires-Dist: opentrons<=8.8.2
Dynamic: license-file

# opentron_glulac_readout_module

A Python library for reading amperometric sensor data from the **Jobst Six Biotransmitter** when connected to an **Opentrons OT-2** automatic liquid handler (ALH).

---

## Table of Contents

- [Overview](#overview)
- [Installation](#installation)
- [Modules](#modules)
  - [opentrons_photo_module](#opentrons_photo_module)
  - [opentron_glulac_readout](#opentron_glulac_readout)
- [Usage](#usage)
  - [Quick Start](#quick-start)
  - [Typical Protocol Flow](#typical-protocol-flow)
- [API Reference](#api-reference)
  - [SensorHandlerModule](#sensorhandlermodule)
  - [opentrons_photo_module](#opentrons_photo_module-1)
- [Simulation](#simulation)

---

## Overview

This library bridges the Opentrons OT-2 liquid handler with the Jobst Six Biotransmitter, enabling automated amperometric measurements within OT-2 protocols.

**Key features:**

- Continuous background sensor readout written incrementally to CSV — data is preserved even if the protocol is interrupted.
- Built-in wash cycles with configurable buffer sources and waste destinations.
- Replicate measurement support with consistent UUID grouping for downstream analysis.
- Integrated camera capture via the OT-2's built-in video device.
- Simulation-safe: all functions degrade gracefully when run in Opentrons simulation mode.

---

## Installation

> **Requirement:** Python 3.12 — the Opentrons library only supports this version.

### 1. Create and activate a virtual environment

```bash
python3.12 -m venv .venv
source .venv/bin/activate
```

### 2. Install the package

```bash
pip install opentron_glulac_readout_module
```

---

## Modules

| Module | Purpose |
|---|---|
| `opentrons_photo_module` | Capture images using the OT-2's built-in camera |
| `opentron_glulac_readout` | Manage sensor readout, measurement cycles, and wash steps |

---

## Usage

### Quick Start

```python
from opentron_glulac_readout_module import SensorHandlerModule
from opentrons import protocol_api

# ... opentron definitions ...
def run(protocol: protocol_api.ProtocolContext):
  
  # custom manufactured measurement holder for the sensor that can be placed within the OT-2 
  glulac = protocol.load_labware(...) 
  pipette300 = protocol.load_instrument(...)
  
  with SensorHandlerModule(
      protocol=protocol,
      measuring_well=glulac["A1"],
      pipette=pipette300,
  ) as sensor_handler:
  
      sensor_handler.add_trash_volume(trash_well)
      sensor_handler.add_buffer_volume_for_washing(buffer_well)
      sensor_handler.validate_pipette_clearance_and_sensor()
  
      csv_path = sensor_handler.start_measurement()
  
      sensor_handler.measure(source_well=plate["A1"])
```

### Typical Protocol Flow

The following order must be respected when setting up a measurement session:

```
1. Create SensorHandlerModule (via context manager)
2. add_trash_volume()               — register waste wells
3. add_buffer_volume_for_washing()  — register buffer wells
4. validate_pipette_clearance_and_sensor()  — optional, but must precede start_measurement()
5. start_measurement()              — opens CSV and starts background readout
6. measure() / measure_replicate()  — one call per sample
```

> **Note:** `validate_pipette_clearance_and_sensor()` cannot be called after `start_measurement()` — doing so raises a `RuntimeError`.

---

## API Reference

### SensorHandlerModule

The central class for managing sensor readout. Must be used as a **context manager** to ensure background threads and CSV writers are properly started and stopped.

```python
with SensorHandlerModule(
    protocol=protocol,
    measuring_well=glulac["A1"],
    pipette=pipette300,
) as sensor_handler:
    ...
```

---

#### `add_trash_volume(well, ...)`

Registers a well as a waste destination for discarded liquid.

Multiple trash wells can be registered; they are filled in registration order as capacity is consumed. Must be called before `start_measurement()`.

> Not required if only `start_measurement()` is used with no wash steps.

---

#### `add_buffer_volume_for_washing(well, ...)`

Registers a well as a buffer source for wash steps.

Multiple buffer wells can be registered; they are drawn from in registration order as volume is consumed. Must be called before `start_measurement()`.

> Not required if only `start_measurement()` is used with no wash steps.

---

#### `validate_pipette_clearance_and_sensor(...)`

Moves the pipette through all configured positions so the operator can visually confirm there are no crash risks before the protocol runs.

Optionally verifies that:
- The sensor produces a valid readout.
- The camera module is functional.

Each individual check can be disabled if only a subset of positions need to be verified.

**Constraints:**
- Must be called before `start_measurement()`.
- Calling it after `start_measurement()` raises a `RuntimeError`.

---

#### `start_measurement() -> str`

Opens the output CSV file and starts continuous background sensor readout.

The CSV is written incrementally (one row per sensor reading), so data is preserved even if the protocol is interrupted mid-run.

**Returns:** The absolute path of the CSV file being written to.

**Must be called after:**
- `add_buffer_volume_for_washing()` / `add_trash_volume()` (if used)
- `validate_pipette_clearance_and_sensor()` (if used)

---

#### `measure(source_well, ...)`

Performs a single complete measurement cycle:

1. Aspirates sample from `source_well` and dispenses it into the measuring well.
2. Waits for the sensor signal to stabilize (slope detection followed by rolling standard deviation threshold).
3. Washes the measuring well with buffer for `SensorSettings.wash_iterations` cycles, waiting for signal stability after each wash.

The background CSV is annotated with `is_measuring_sample` and `is_washing` flags on each row, enabling precise time-windowing in downstream analysis.

Each call generates a unique `sample_uuid` that groups all CSV rows for the measurement (including wash rows).

---

#### `measure_replicate(source_well, ...)`

Runs a replicate measurement series on the sample.

The number of replicates defaults to `3` and can be configured via `SensorSettings.replicate_iterations`.

All repetitions share the same `sample_uuid` for grouping in downstream analysis and are stamped with an incrementing `replicate_count` column (starting at `1`).

This is equivalent to calling `measure()` multiple times manually, but guarantees consistent UUID grouping and replicate bookkeeping.

---

### opentrons_photo_module

Photography functions do **not** require a `SensorHandlerModule` instance.

---

#### `take_picture(directory, filename, ...)`

Captures a single JPEG frame using the OT-2's built-in video device (`/dev/video0`) via FFmpeg.

- The output directory is created automatically if it does not exist.
- In **simulation mode**, no image is captured; a comment is logged to the protocol indicating where the photo would have been saved.

---

## Simulation

To test whether a protocol will run correctly without executing it on real hardware, use the `opentrons_simulate` CLI from within your virtual environment:

```bash
opentrons_simulate -L /Local_Path/toLabwareFolder protocol_name.py
```

> The `-L` flag points to your custom labware directory. Using the same path as the Opentrons App reduces the risk of labware definition mismatches.
> You find this Location by going to the Opentrons App, press on preferences (bottom left), Advanced 'Additional Custom Labware Source Folder'. Here you can see the current folder, fill this one here
