diff --git a/.github/workflows/tests_stationA_prot1.yml b/.github/workflows/tests_stationA_prot1.yml new file mode 100644 index 0000000..e6097d1 --- /dev/null +++ b/.github/workflows/tests_stationA_prot1.yml @@ -0,0 +1,36 @@ +name: Station A prot1 tests +# This workflow is triggered on pushes and PRs to the repository. +# It runs the pipeline with several tests to check if everything works +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + parameters: [ "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot1_comb1_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot1_comb2_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot1_comb3_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot1_comb4_pars.json" + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r ${GITHUB_WORKSPACE}/requirements.txt + mkdir ~/.opentrons + cp ${GITHUB_WORKSPACE}/custom_defaults/* ~/.opentrons + - name: Create test script + run: | + echo "testing" + python ${GITHUB_WORKSPACE}/tests/run_tests.py \ + -j ${{ matrix.parameters }} \ + -t ${GITHUB_WORKSPACE}/protocols/S3/stationA_protocol1_buffer_S3.ot2.apiv2.py \ + -o /tmp/tmp_test.py + - name: Simulate script + run: | + opentrons_simulate -L ${GITHUB_WORKSPACE}/labware /tmp/tmp_test.py diff --git a/.github/workflows/tests_stationA_prot2.yml b/.github/workflows/tests_stationA_prot2.yml new file mode 100644 index 0000000..75e29ad --- /dev/null +++ b/.github/workflows/tests_stationA_prot2.yml @@ -0,0 +1,39 @@ +name: Station A prot2 tests +# This workflow is triggered on pushes and PRs to the repository. +# It runs the pipeline with several tests to check if everything works +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + parameters: [ "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot2_comb1_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot2_comb2_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot2_comb3_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot2_comb4_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot2_comb5_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot2_comb6_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot2_comb7_pars.json" + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r ${GITHUB_WORKSPACE}/requirements.txt + mkdir ~/.opentrons + cp ${GITHUB_WORKSPACE}/custom_defaults/* ~/.opentrons + - name: Create test script + run: | + echo "testing" + python ${GITHUB_WORKSPACE}/tests/run_tests.py \ + -j ${{ matrix.parameters }} \ + -t ${GITHUB_WORKSPACE}/protocols/S3/stationA_protocol2_beads_S3.ot2.apiv2.py \ + -o /tmp/tmp_test.py + - name: Simulate script + run: | + opentrons_simulate -L ${GITHUB_WORKSPACE}/labware /tmp/tmp_test.py diff --git a/.github/workflows/tests_stationA_prot3.yml b/.github/workflows/tests_stationA_prot3.yml new file mode 100644 index 0000000..734de79 --- /dev/null +++ b/.github/workflows/tests_stationA_prot3.yml @@ -0,0 +1,40 @@ +name: Station A prot3 tests +# This workflow is triggered on pushes and PRs to the repository. +# It runs the pipeline with several tests to check if everything works +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + parameters: [ "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot3_comb1_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot3_comb2_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot3_comb3_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot3_comb4_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot3_comb5_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot3_comb6_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot3_comb7_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationA_prot3_comb8_pars.json" + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r ${GITHUB_WORKSPACE}/requirements.txt + mkdir ~/.opentrons + cp ${GITHUB_WORKSPACE}/custom_defaults/* ~/.opentrons + - name: Create test script + run: | + echo "testing" + python ${GITHUB_WORKSPACE}/tests/run_tests.py \ + -j ${{ matrix.parameters }} \ + -t ${GITHUB_WORKSPACE}/protocols/S3/stationA_protocol3_lysates_S3.ot2.apiv2.py \ + -o /tmp/tmp_test.py + - name: Simulate script + run: | + opentrons_simulate -L ${GITHUB_WORKSPACE}/labware /tmp/tmp_test.py diff --git a/.github/workflows/tests_stationB_prot1.yml b/.github/workflows/tests_stationB_prot1.yml new file mode 100644 index 0000000..ef1be65 --- /dev/null +++ b/.github/workflows/tests_stationB_prot1.yml @@ -0,0 +1,38 @@ +name: Station B prot1 tests +# This workflow is triggered on pushes and PRs to the repository. +# It runs the pipeline with several tests to check if everything works +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + # Removed comb1,3 and 5 due to a bug in the api, tall deep wells fail on simulation + parameters: [ "${GITHUB_WORKSPACE}/tests/par_tests/stationB_prot1_comb2_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationB_prot1_comb4_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationB_prot1_comb6_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationB_prot1_comb7_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationB_prot1_comb8_pars.json" + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r ${GITHUB_WORKSPACE}/requirements.txt + mkdir ~/.opentrons + cp ${GITHUB_WORKSPACE}/custom_defaults/* ~/.opentrons + - name: Create test script + run: | + echo "testing" + python ${GITHUB_WORKSPACE}/tests/run_tests.py \ + -j ${{ matrix.parameters }} \ + -t ${GITHUB_WORKSPACE}/protocols/S3/stationB_protocol1_extraction_S3.ot2.apiv2.py \ + -o /tmp/tmp_test.py + - name: Simulate script + run: | + opentrons_simulate -L ${GITHUB_WORKSPACE}/labware /tmp/tmp_test.py diff --git a/.github/workflows/tests_stationC_prot1.yml b/.github/workflows/tests_stationC_prot1.yml new file mode 100644 index 0000000..0e06d32 --- /dev/null +++ b/.github/workflows/tests_stationC_prot1.yml @@ -0,0 +1,42 @@ +name: Station C prot1 tests +# This workflow is triggered on pushes and PRs to the repository. +# It runs the pipeline with several tests to check if everything works +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + parameters: [ "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb1_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb2_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb3_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb4_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb5_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb6_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb7_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb8_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb9_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot1_comb10_pars.json" + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r ${GITHUB_WORKSPACE}/requirements.txt + mkdir ~/.opentrons + cp ${GITHUB_WORKSPACE}/custom_defaults/* ~/.opentrons + - name: Create test script + run: | + echo "testing" + python ${GITHUB_WORKSPACE}/tests/run_tests.py \ + -j ${{ matrix.parameters }} \ + -t ${GITHUB_WORKSPACE}/protocols/S3/stationC_protocol1_pcr_S3.ot2.apiv2.py \ + -o /tmp/tmp_test.py + - name: Simulate script + run: | + #opentrons_simulate -L ${GITHUB_WORKSPACE}/labware /tmp/tmp_test.py diff --git a/.github/workflows/tests_stationC_prot2.yml b/.github/workflows/tests_stationC_prot2.yml new file mode 100644 index 0000000..db3c00f --- /dev/null +++ b/.github/workflows/tests_stationC_prot2.yml @@ -0,0 +1,35 @@ +name: Station C prot2 tests +# This workflow is triggered on pushes and PRs to the repository. +# It runs the pipeline with several tests to check if everything works +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + parameters: [ "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot2_comb1_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot2_comb2_pars.json", + "${GITHUB_WORKSPACE}/tests/par_tests/stationC_prot2_comb3_pars.json" + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r ${GITHUB_WORKSPACE}/requirements.txt + mkdir ~/.opentrons + cp ${GITHUB_WORKSPACE}/custom_defaults/* ~/.opentrons + - name: Create test script + run: | + echo "testing" + python ${GITHUB_WORKSPACE}/tests/run_tests.py \ + -j ${{ matrix.parameters }} \ + -t ${GITHUB_WORKSPACE}/protocols/S3/stationC_protocol2_pcrmulti_S3.ot2.apiv2.py \ + -o /tmp/tmp_test.py + - name: Simulate script + run: | + #opentrons_simulate -L ${GITHUB_WORKSPACE}/labware /tmp/tmp_test.py diff --git a/.gitignore b/.gitignore index b6e4761..6894ebf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ *.py[cod] *$py.class +virtualenv # C extensions *.so diff --git a/CHANGELOG b/CHANGELOG index 96783aa..f652a83 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,33 @@ +## Version 1.7 + +New features: +- Added json sending using request. Requests python module is installed in all robots. +- Automatic tests added using github actions for all protocols (except C station due to a bug in opentrons_simulate v.3.17.0 ) +- New protocol for using multi p20 pipette in station C for transferring extracts. +- Now there are two pauses in B station for removing trash tips when > 48 samples. One after mix y another after remove_supernatant. +- Added tip-reuse option for Station B protocol 1 (extraction). +- Added labware definition for biotix tip rack 300 ul (needs testing). +- Added labware definition for biotix tip rack 1000 ul (needs testing). +- Added labware ecogen deepwell to protocols A (this was missing in last release). + +Protocol modification: +- For MM3 distribution in station C protocol 1 tip is changed every three columns. +- Once mastermix is mixed in station C protocol 1 tip is changed. +- Only one p20 rack tips in station C protocol 1 in slot 6. + +Bug fixes: +- Fixed unset variable in dispense_beads protocol 1 station B. 2ab984b + +## Version 1.6 +Hotfix: +- Fix bug in hommogenize function in C protocol, used pip instead of +p300. + +## Version 1.5 +Hotfix: +- Removed comments where MAGNET_HEIGHT variable that were bothering +opentronsweb + ## Version 1.4 Documentation: - Added user manual in spanish and english. diff --git a/configuration_scripts/S3/rename_robots/S3-C3 b/configuration_scripts/S3/rename_robots/S3-C3 new file mode 100644 index 0000000..68811f9 --- /dev/null +++ b/configuration_scripts/S3/rename_robots/S3-C3 @@ -0,0 +1,18 @@ +NEW_SERIAL_NUMBER = "S3-C3" + +from opentrons import robot +import os + +robot.comment(f"Setting serial number to {NEW_SERIAL_NUMBER}.") + +if not robot.is_simulating(): + with open("/var/serial", "w") as serial_number_file: + serial_number_file.write(NEW_SERIAL_NUMBER + "\n") + with open("/etc/machine-info", "w") as serial_number_file: + serial_number_file.write(f"DEPLOYMENT=production\nPRETTY_HOSTNAME={NEW_SERIAL_NUMBER}\n") + with open("/etc/hostname", "w") as serial_number_file: + serial_number_file.write(NEW_SERIAL_NUMBER + "\n") + + os.sync() + + robot.comment("Done.") diff --git a/configuration_scripts/S3/static_ips/S3-C3_static_ip.py b/configuration_scripts/S3/static_ips/S3-C3_static_ip.py new file mode 100644 index 0000000..b4aa342 --- /dev/null +++ b/configuration_scripts/S3/static_ips/S3-C3_static_ip.py @@ -0,0 +1,33 @@ +STATIC_IP = "169.254.100.100/16" + +keyfile_contents = f"""\ +# This file was placed here by Opentrons Support to work around suspected issues with mDNS. +# Normally, IP addresses are assigned dynamically by the "wired-linklocal" or "wired" connection. +# This overrides both of those to set a known, static IP address. +[connection] +id=support-team-wired-static-ip +type=ethernet +autoconnect-priority=20 +interface-name=eth0 +permissions= +[ethernet] +cloned-mac-address=permanent +mac-address-blacklist= +[ipv4] +dns-search= +method=manual +addresses={STATIC_IP} +""" + +from opentrons import robot +import os + +robot.comment(f"Run this protocol to permanently set the wired IP address of your OT-2 to {STATIC_IP}.") + +if not robot.is_simulating(): + with open("/var/lib/NetworkManager/system-connections/support-team-wired-static-ip", "w") as keyfile: + keyfile.write(keyfile_contents) + os.sync() + robot.comment("Done.") + +robot.comment("Restart your OT-2 to apply the changes.") diff --git a/custom_defaults/deck_calibration.json b/custom_defaults/deck_calibration.json new file mode 100644 index 0000000..dd812aa --- /dev/null +++ b/custom_defaults/deck_calibration.json @@ -0,0 +1,28 @@ +{ + "gantry_calibration": [ + [ + 1.0003, + -0.0032, + 0.0, + -0.5771 + ], + [ + -0.0008, + 1.0004, + 0.0, + 3.5125 + ], + [ + 0.0, + 0.0, + 1.0, + -24.801 + ], + [ + 0.0, + -0.0, + 0.0, + 1.0 + ] + ] +} \ No newline at end of file diff --git a/custom_defaults/robot_settings.json b/custom_defaults/robot_settings.json new file mode 100644 index 0000000..7b70cff --- /dev/null +++ b/custom_defaults/robot_settings.json @@ -0,0 +1,123 @@ +{ + "acceleration": { + "A": 1500, + "B": 200, + "C": 200, + "X": 3000, + "Y": 2000, + "Z": 1500 + }, + "default_current": { + "A": 0.8, + "B": 0.05, + "C": 0.05, + "X": 1.25, + "Y": 1.25, + "Z": 0.8 + }, + "default_max_speed": { + "A": 125, + "B": 40, + "C": 40, + "X": 600, + "Y": 400, + "Z": 125 + }, + "default_pipette_configs": { + "homePosition": 220, + "maxTravel": 30, + "stepsPerMM": 768 + }, + "gantry_steps_per_mm": { + "A": 400, + "X": 80.0, + "Y": 80.0, + "Z": 400 + }, + "high_current": { + "A": 0.8, + "B": 0.5, + "C": 0.5, + "X": 1.25, + "Y": 1.25, + "Z": 0.8 + }, + "instrument_offset": { + "left": { + "multi": [ + -1.287455687855413, + -0.23561554671431395, + -2.2330000000000183 + ], + "single": [ + -1.7886079041154517, + 0.32086276627364896, + -1.6440000000000197 + ] + }, + "right": { + "multi": [ + 0.4953178806141523, + 1.0615003371297007, + -2.561000000000007 + ], + "single": [ + 0.2815833732571491, + 1.9860622896941322, + -2.5860000000000127 + ] + } + }, + "log_level": "INFO", + "low_current": { + "A": 0.1, + "B": 0.05, + "C": 0.05, + "X": 0.3, + "Y": 0.3, + "Z": 0.1 + }, + "mount_offset": [ + -34, + 0, + 0 + ], + "name": "Ada Lovelace", + "serial_speed": 115200, + "steps_per_mm": "M92 X80.00 Y80.00 Z400 A400 B768 C768", + "tip_length": { + "Pipette": 51.7, + "p10_multi_v1": 33.745000000000005, + "p10_single_v1": 34.55000000000001, + "p300_multi_v1.5": 51.91000000000001, + "p300_single_v1.4": 51.44800000000002, + "p300_single_v1.5": 51.59800000000001 + }, + "tip_probe": { + "bounce_distance": 5.0, + "center": [ + 293.4843253085927, + 301.00501031231147, + 73.38999999999999 + ], + "dimensions": [ + 35.0, + 40.0, + 79.3 + ], + "switch_clearance": 15, + "switch_offset": [ + 2.0, + 5.0, + 5.0 + ], + "z_clearance": { + "crossover": 35, + "deck": 5.0, + "normal": 5.0, + "start": 20 + } + }, + "version": 3, + "z_retract_distance": 2 +} \ No newline at end of file diff --git a/doc/S3/03_ethernet_connection.md b/doc/S3/03_ethernet_connection.md index 499cae5..8eb9690 100644 --- a/doc/S3/03_ethernet_connection.md +++ b/doc/S3/03_ethernet_connection.md @@ -21,7 +21,7 @@ dns-search= method=auto ``` -This configuration will prepare your robots to get a dynamic IP address that must be set to fixed one by reserving it in your network DHCP server. If you wanted to set a manual address, change the `[ipv4]` field as we did in the [wifi setup file](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/doc/S3/01_OT2_installation.md): +This configuration will prepare your robots to get a dynamic IP address that must be set to fixed one by reserving it in your network DHCP server. If you wanted to set a manual address, change the `[ipv4]` field as we did in the [wifi setup file](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/doc/S3/01_OT2_installation.md): ``` [ipv4] @@ -35,7 +35,7 @@ method=manual 3) The easiest way to tidy up the cable without drilling or cutting is by passing it through the left corner of the plastic cover, where there is already an aperture to pass the other cables. Then, take the cable down using the magnetic cable management hooks for the USB cables of the modules and take it out of the box through the bottom left hole of the rear panel. -![Ethernet cable configuration](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/ethernet_cable_configuration.jpg?raw=true) +![Ethernet cable configuration](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/ethernet_cable_configuration.jpg?raw=true) 4) Plug the cable and restart the robot. Now you should be able to find the robot in your wired network. Use the DHCP server to reserve a fix IP for the robot IPs. diff --git a/experiments/20200528-stationB_protocol1_extraction_release1.5/stationB_protocol1_extraction_S3.ot2.apiv2.py b/experiments/20200528-stationB_protocol1_extraction_release1.5/stationB_protocol1_extraction_S3.ot2.apiv2.py new file mode 100644 index 0000000..202d0f6 --- /dev/null +++ b/experiments/20200528-stationB_protocol1_extraction_release1.5/stationB_protocol1_extraction_S3.ot2.apiv2.py @@ -0,0 +1,615 @@ +from opentrons import protocol_api +from opentrons.types import Point +from opentrons.drivers.rpi_drivers import gpio +import time +import math +import os +import subprocess +import sys +import json +from datetime import datetime +custom_modules_path = "/var/user-packages/usr/lib/python3.7/site-packages" +if custom_modules_path not in sys.path: + sys.path.append(custom_modules_path) +import requests + +# metadata +metadata = { + 'protocolName': 'S3 Station B Protocol 1 extraction Version 2', + 'author': 'Nick Sara Miguel ', + 'source': 'Custom Protocol Request', + 'apiLevel': '2.3' +} + +""" +REAGENT SETUP: +- slot 7 12-channel reservoir: + - elution buffer: channel 1 + - magnetic beads: channel 2 + - bead buffer: channels 3-5 + - wash 1: channels 6-7 + - wash 2: channels 8-9 + - wash 3: channels 10-11 +- slot 11 single-channel reservoir: + - empty reservoir for liquid waste (supernatant removals) +""" + +# Parameters to adapt the protocol +# Warning writing any Parameters below this line. +# It will be deleted if opentronsWeb is used. + +NUM_SAMPLES = 24 +REAGENT_LABWARE = 'nest 12 reservoir plate' +MAGPLATE_LABWARE = 'nest deep generic well plate' +WASTE_LABWARE = 'nest 1 reservoir plate' +ELUTION_LABWARE = 'opentrons aluminum nest plate' +DISPENSE_BEADS = False +REUSE_TIPS = True +LANGUAGE = 'esp' +RESET_TIPCOUNT = True +PROTOCOL_ID = "0000-AA" +URL = 'localhost' +# End Parameters to adapt the protocol + +## global vars +## initialize robot object +robot = None +# default var for drop tip switching +switch = True +# initialize tip_log dictionary +tip_log = {} +tip_log['count'] = {} +tip_log['tips'] = {} +tip_log['max'] = {} + +""" +NUM_SAMPLES is the number of samples, must be an integer number + +REAGENT_LABWARE must be one of the following: + nest 12 reservoir plate + +MAGPLATE_LABWARE must be one of the following: + opentrons deep generic well plate + nest deep generic well plate + vwr deep generic well plate + ecogen deep generic well plate + +WASTE labware + nest 1 reservoir plate + +ELUTION_LABWARE + opentrons aluminum biorad plate + opentrons aluminum nest plate +""" + +# Calculated variables +if MAGPLATE_LABWARE == 'nest deep generic well plate': + MAGNET_HEIGHT = 22 +elif MAGPLATE_LABWARE == 'vwr deep generic well plate': + MAGNET_HEIGHT = 22 +elif MAGPLATE_LABWARE == 'ecogen deep generic well plate': + MAGNET_HEIGHT = 21 +else: + MAGNET_HEIGHT = 22 + +# Config variables +ACTION = "StationB-protocol1-extraction" + +# Constants +REAGENT_LW_DICT = { + 'nest 12 reservoir plate': 'nest_12_reservoir_15ml' +} + +MAGPLATE_LW_DICT = { + 'opentrons deep generic well plate': 'usascientific_96_wellplate_2.4ml_deep', + 'nest deep generic well plate': 'nest_96_deepwellplate_2000ul', + 'ecogen deep generic well plate': 'ecogen_96_deepwellplate_2000ul', + 'vwr deep generic well plate': 'vwr_96_deepwellplate_2000ul' +} + +WASTE_LW_DICT = { + # Radius of each possible tube + 'nest 1 reservoir plate': 'nest_1_reservoir_195ml' +} + +ELUTION_LW_DICT = { + 'opentrons aluminum biorad plate': 'opentrons_96_aluminumblock_biorad_wellplate_200ul', + 'opentrons aluminum nest plate': 'opentrons_96_aluminumblock_nest_wellplate_100ul' + +} + +LANGUAGE_DICT = { + 'esp': 'esp', + 'eng': 'eng' +} + +if LANGUAGE_DICT[LANGUAGE] == 'eng': + VOICE_FILES_DICT = { + 'start': './data/sounds/started_process.mp3', + 'finish': './data/sounds/finished_process.mp3', + 'close_door': './data/sounds/close_door.mp3', + 'replace_tipracks': './data/sounds/replace_tipracks.mp3', + 'empty_trash': './data/sounds/empty_trash.mp3' + } +elif LANGUAGE_DICT[LANGUAGE] == 'esp': + VOICE_FILES_DICT = { + 'start': './data/sounds/started_process_esp.mp3', + 'finish': './data/sounds/finished_process_esp.mp3', + 'close_door': './data/sounds/close_door_esp.mp3', + 'replace_tipracks': './data/sounds/replace_tipracks_esp.mp3', + 'empty_trash': './data/sounds/empty_trash_esp.mp3' + } + +# Function definitions + +def write_to_error_log (info, reason): + date = datetime.now().strftime("%Y_%m_%d") + folder_date = os.path.join('/data', date) + time_now = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + json_file = time_now + '.json' + folder_file_name = os.path.join(folder_date, json_file) + folder_error_log = os.path.join(folder_date,'error.log') + if not os.path.exists(folder_date): + try: + os.makedirs(folder_date) + except: + return + try: + # Create a new file for dumping json data + with open (folder_file_name , 'w') as fh: + json.dump(info, fh, indent=4) + # Append status reason code to the log + with open(folder_error_log, 'a') as fh: + fh.write( time_now + ' Unable to accept the requests get error : '+ reason + '\n') + except: + return + +def run_info(start,end,parameters = dict()): + info = {} + hostname = subprocess.run( + ['hostname'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ).stdout.decode('utf-8') + + info["RobotID"] = hostname + info["executedAction"] = ACTION + info["ProtocolID"] = PROTOCOL_ID + info["StartRunTime"] = start + info["FinishRunTime"] = end + info["parameters"] = parameters + + headers = {'Content-type': 'application/json'} + url_https = 'https://' + URL + url_http = 'http://' + URL + try: + r = requests.post(url_https, data=json.dumps(info), headers=headers) + except: + try: + r = requests.post(url_http, data=json.dumps(info), headers=headers) + except: + write_to_error_log(info, 'Server communication error') + return + if r.status_code > 201 : + write_to_error_log(info, str(r.status_code)) + +def check_door(): + return gpio.read_window_switches() + +def confirm_door_is_closed(): + if not robot.is_simulating(): + #Check if door is opened + if check_door() == False: + #Set light color to red and pause + gpio.set_button_light(1,0,0) + robot.pause() + voice_notification('close_door') + time.sleep(5) + confirm_door_is_closed() + else: + #Set light color to green + gpio.set_button_light(0,1,0) + +def start_run(): + voice_notification('start') + gpio.set_button_light(0,1,0) + now = datetime.now() + # dd/mm/YY H:M:S + start_time = now.strftime("%Y/%m/%d %H:%M:%S") + return start_time + +def finish_run(): + voice_notification('finish') + #Set light color to blue + gpio.set_button_light(0,0,1) + now = datetime.now() + # dd/mm/YY H:M:S + finish_time = now.strftime("%Y/%m/%d %H:%M:%S") + return finish_time + +def voice_notification(action): + if not robot.is_simulating(): + fname = VOICE_FILES_DICT[action] + if os.path.isfile(fname) is True: + subprocess.run( + ['mpg123', fname], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + else: + robot.comment(f"Sound file does not exist. Call the technician") + +def reset_tipcount(file_path = '/data/B/tip_log.json'): + if os.path.isfile(file_path): + os.remove(file_path) + +def retrieve_tip_info(pip,tipracks,file_path = '/data/B/tip_log.json'): + global tip_log + if not tip_log['count'] or pip not in tip_log['count']: + tip_log['count'][pip] = 0 + if not robot.is_simulating(): + if os.path.isfile(file_path): + with open(file_path) as json_file: + data = json.load(json_file) + if 'P1000' in str(pip): + tip_log['count'][pip] = data['tips1000'] + elif 'P300' in str(pip): + tip_log['count'][pip] = data['tips300'] + elif 'P20' in str(pip): + tip_log['count'][pip] = data['tips20'] + + if "8-Channel" in str(pip): + tip_log['tips'][pip] = [tip for rack in tipracks for tip in rack.rows()[0]] + else: + tip_log['tips'][pip] = [tip for rack in tipracks for tip in rack.wells()] + + tip_log['max'][pip] = len(tip_log['tips'][pip]) + + return tip_log + +def save_tip_info(file_path = '/data/B/tip_log.json'): + data = {} + if not robot.is_simulating(): + if os.path.isfile(file_path): + os.rename(file_path,file_path + ".bak") + for pip in tip_log['count']: + if "P1000" in str(pip): + data['tips1000'] = tip_log['count'][pip] + elif "P300" in str(pip): + data['tips300'] = tip_log['count'][pip] + elif "P20" in str(pip): + data['tips20'] = tip_log['count'][pip] + + with open(file_path, 'a+') as outfile: + json.dump(data, outfile) + +def pick_up(pip,tiprack): + if tip_log['count'][pip] == tip_log['max'][pip]: + voice_notification('replace_tipracks') + robot.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ +resuming.') + confirm_door_is_closed() + pip.reset_tipracks() + tip_log['count'][pip] = 0 + pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]]) + tip_log['count'][pip] += 1 + +def drop(pip): + global switch + if "8-Channel" not in str(pip): + side = 1 if switch else -1 + drop_loc = robot.loaded_labwares[12].wells()[0].top().move(Point(x=side*20)) + pip.drop_tip(drop_loc,home_after=False) + switch = not switch + else: + drop_loc = robot.loaded_labwares[12].wells()[0].top().move(Point(x=20)) + pip.drop_tip(drop_loc,home_after=False) + +def mix_beads(reps, dests, pip, tiprack): + ## Dispense beads to deep well plate. + for i, m in enumerate(dests): + if not pip.hw_pipette['has_tip']: + pick_up(pip,tiprack) + dispense_default_speed = pip.flow_rate.dispense + pip.flow_rate.dispense = 600 + pip.mix(reps, 200, m.bottom(5)) + pip.flow_rate.dispense = dispense_default_speed + # PENDING TO FIX THIS blow_out + # pip.blow_out(m.top(-2)) + pip.aspirate(20, m.top(-2)) + drop(pip) + +def dispense_beads(reps,sources,dests,pip,tiprack): + ## Mix beads prior to dispensing. + pick_up(pip,tiprack) + for s in sources: + pip.mix(reps, 200, s.bottom(20)) + + ## Dispense beads to deep well plate. + for i, m in enumerate(dests): + if not pip.hw_pipette['has_tip']: + pick_up(pip,tiprack) + pip.transfer(200, dests[i//3], m.bottom(5), new_tip='never', air_gap=20) + pip.blow_out(m.top(-2)) + drop(pip) + pick_up(pip,tiprack) + pip.transfer(200, dests[i//3], m.bottom(5), new_tip='never', air_gap=20) + mix_beads(reps, dests, pip, tiprack) + +def remove_supernatant(sources,waste,pip,tiprack): + for i, m in enumerate(sources): + loc = m.bottom(1) + pick_up(pip,tiprack) + pip.transfer(800, loc, waste, air_gap=100, new_tip='never') + pip.blow_out(waste) + drop(pip) + +def wash_reuse(wash_sets,dests,waste,magdeck,pip,tiprack,tipreuse): + wash_num = 0 + for wash_set in wash_sets: + # transfer wash + pick_up(pip,tiprack) + for i, m in enumerate(dests): + magdeck.disengage() + wash_chan = wash_set[i//6] + pip.transfer( + 200, wash_chan.bottom(2), m.top(), new_tip='never', air_gap=20) + + # mix beads with wash + tips_loc = 0 + for i, m in enumerate(dests): + + if wash_num != 0: + if tips_loc == 0: + drop(pip) + pip.pick_up_tip(tipreuse[0].rows()[0][tips_loc]) + else: + if tips_loc != 0: + pick_up(pip,tiprack) + + dispense_default_speed = pip.flow_rate.dispense + pip.flow_rate.dispense = 1500 + pip.mix(7, 200, m.bottom(2)) + pip.flow_rate.dispense = dispense_default_speed + if wash_num != 0: + pip.return_tip(home_after=False) + else: + pip.drop_tip(tipreuse[0].rows()[0][tips_loc], home_after=False) + tips_loc += 1 + + magdeck.engage(height_from_base=MAGNET_HEIGHT) + # robot.delay(seconds=75, msg='Incubating on magnet for 75 seconds.') + + wash_num += 1 + + # remove supernatant + tips_loc = 0 + for i, m in enumerate(dests): + pip.pick_up_tip(tipreuse[0].rows()[0][tips_loc]) + aspire_default_speed = pip.flow_rate.aspirate + pip.flow_rate.aspirate = 75 + asp_loc = m.bottom(1.5) + pip.transfer(200, asp_loc, waste, new_tip='never', air_gap=20) + pip.flow_rate.aspirate = aspire_default_speed + pip.blow_out(waste) + if wash_num != 3: + pip.return_tip(home_after=False) + else: + pip.drop_tip(home_after=False) + tips_loc += 1 + +def wash(wash_sets,dests,waste,magdeck,pip,tiprack): + for wash_set in wash_sets: + for i, m in enumerate(dests): + # transfer and mix wash with beads + magdeck.disengage() + wash_chan = wash_set[i//6] + pick_up(pip,tiprack) + pip.transfer( + 200, wash_chan.bottom(2), m.center(), new_tip='never', air_gap=20) + # Mix heigh has to be really close to bottom, it was 5 now reduced to 2, maybe should be 1? + dispense_default_speed = pip.flow_rate.dispense + pip.flow_rate.dispense = 1500 + pip.mix(7, 200, m.bottom(2)) + pip.flow_rate.dispense = dispense_default_speed + + magdeck.engage(height_from_base=MAGNET_HEIGHT) + # robot.delay(seconds=75, msg='Incubating on magnet for 75 seconds.') + + # remove supernatant + aspire_default_speed = pip.flow_rate.aspirate + pip.flow_rate.aspirate = 75 + asp_loc = m.bottom(1.5) + pip.transfer(200, asp_loc, waste, new_tip='never', air_gap=20) + pip.flow_rate.aspirate = aspire_default_speed + pip.blow_out(waste) + drop(pip) + +def elute_samples(sources,dests,buffer,magdeck,pip,tipracks): + ## dispense buffer + for i, m in enumerate(sources): + pick_up(pip,tipracks) + dispense_default_speed = pip.flow_rate.dispense + pip.flow_rate.dispense = 1500 + pip.transfer( + 50, buffer.bottom(2), m.bottom(1), new_tip='never', air_gap=10) + pip.mix(20, 200, m.bottom(1)) + pip.flow_rate.dispense = dispense_default_speed + drop(pip) + + ## Incubation steps + # robot.delay(minutes=5, msg='Incubating off magnet for 5 minutes.') + magdeck.engage(height_from_base=MAGNET_HEIGHT) + # robot.delay(seconds=120, msg='Incubating on magnet for 120 seconds.') + + aspire_default_speed = pip.flow_rate.aspirate + pip.flow_rate.aspirate = 50 + ## Dispense elutes in pcr plate. + for i, (m, e) in enumerate(zip(sources, dests)): + # tranfser and mix elution buffer with beads + asp_loc = m.bottom(1.5) + pick_up(pip,tipracks) + # transfer elution to new plate + pip.transfer(50, asp_loc, e, new_tip='never', air_gap=10) + pip.blow_out(e.top(-2)) + drop(pip) + pip.flow_rate.aspirate = aspire_default_speed + +def run(ctx: protocol_api.ProtocolContext): + global robot + robot = ctx + + text = str(sys.path) + robot.comment(text) + + # check if tipcount is being reset + if RESET_TIPCOUNT: + reset_tipcount() + + # confirm door is close + robot.comment(f"Please, close the door") + confirm_door_is_closed() + + # Begin run + start_time = start_run() + + # load labware and modules + ## ELUTION LABWARE + if ELUTION_LABWARE not in ELUTION_LW_DICT: + raise Exception('Invalid ELUTION_LABWARE. Must be one of the \ + following:\nopentrons aluminum biorad plate\nopentrons aluminum nest plate') + + elution_plate = robot.load_labware( + ELUTION_LW_DICT[ELUTION_LABWARE], '1', + 'elution plate') + + ## MAGNETIC PLATE LABWARE + magdeck = robot.load_module('magdeck', '10') + magdeck.disengage() + + if MAGPLATE_LABWARE not in MAGPLATE_LW_DICT: + raise Exception('Invalid MAGPLATE_LABWARE. Must be one of the \ +following:\nopentrons deep generic well plate\nnest deep generic well plate\nvwr deep generic well plate') + + magplate = magdeck.load_labware(MAGPLATE_LW_DICT[MAGPLATE_LABWARE]) + + ## WASTE LABWARE + if WASTE_LABWARE not in WASTE_LW_DICT: + raise Exception('Invalid WASTE_LABWARE. Must be one of the \ + following:\nnest 1 reservoir plate') + + waste = robot.load_labware( + WASTE_LW_DICT[WASTE_LABWARE], '11', 'waste reservoir').wells()[0].top(-10) + + ## REAGENT RESERVOIR + if REAGENT_LABWARE not in REAGENT_LW_DICT: + raise Exception('Invalid REAGENT_LABWARE. Must be one of the \ + following:\nnest 12 reservoir plate') + + reagent_res = robot.load_labware( + REAGENT_LW_DICT[REAGENT_LABWARE], '4', 'reagent reservoir') + + ## TIPS + # using standard tip definition despite actually using filter tips + # so that the tips can accommodate ~220µl per transfer for efficiency + tips300 = [ + robot.load_labware( + 'opentrons_96_tiprack_300ul', slot, '200µl filter tiprack') + for slot in ['2', '3', '5', '6', '9'] + ] + tipsreuse = [ + robot.load_labware( + 'opentrons_96_tiprack_300ul', slot, '200µl filter tiprack') + for slot in ['7'] + ] + tips1000 = [ + robot.load_labware('opentrons_96_filtertiprack_1000ul', slot, + '1000µl filter tiprack') + for slot in ['8'] + ] + + # reagents and samples + num_cols = math.ceil(NUM_SAMPLES/8) + mag_samples_m = magplate.rows()[0][:num_cols] + mag_samples_s = magplate.wells()[:NUM_SAMPLES] + elution_samples_m = elution_plate.rows()[0][:num_cols] + elution_buffer = reagent_res.wells()[0] + bead_buffer = reagent_res.wells()[1:5] + wash_sets = [reagent_res.wells()[i:i+2] for i in [5, 7, 9]] + + # pipettes + m300 = robot.load_instrument('p300_multi_gen2', 'left', tip_racks=tips300) + p1000 = robot.load_instrument('p1000_single_gen2', 'right', + tip_racks=tips1000) + + ## retrieve tip_log + retrieve_tip_info(p1000,tips1000) + retrieve_tip_info(m300,tips300) + + m300.flow_rate.aspirate = 150 + m300.flow_rate.dispense = 300 + m300.flow_rate.blow_out = 300 + p1000.flow_rate.aspirate = 100 + p1000.flow_rate.dispense = 1000 + p1000.flow_rate.blow_out = 1000 + + if(DISPENSE_BEADS): + # premix, transfer, and mix magnetic beads with sample + ## bead dests depending on number of samples + bead_dests = bead_buffer[:math.ceil(num_cols/4)] + dispense_beads(7,bead_dests,mag_samples_m,m300,tips300) + else: + # Mix bead + mix_beads(7, mag_samples_m,m300,tips300) + + # incubate off the magnet + # robot.delay(minutes=10, msg='Incubating off magnet for 10 minutes.') + + ## First incubate on magnet. + magdeck.engage(height_from_base=MAGNET_HEIGHT) + # robot.delay(minutes=7, msg='Incubating on magnet for 7 minutes.') + + # remove supernatant with P1000 + remove_supernatant(mag_samples_s,waste,p1000,tips1000) + + # empty trash + if NUM_SAMPLES > 24: + voice_notification('empty_trash') + robot.pause(f"Please, empty trash") + confirm_door_is_closed() + + # 3x washes + if REUSE_TIPS == True: + wash_reuse(wash_sets,mag_samples_m,waste,magdeck,m300,tips300,tipsreuse) + else: + wash(wash_sets,mag_samples_m,waste,magdeck,m300,tips300) + + # empty trash + if NUM_SAMPLES > 72: + voice_notification('empty_trash') + robot.pause(f"Please, empty trash") + confirm_door_is_closed() + + # elute samples + magdeck.disengage() + elute_samples(mag_samples_m,elution_samples_m,elution_buffer,magdeck,m300,tips300) + + # track final used tip + save_tip_info() + + magdeck.disengage() + + finish_time = finish_run() + + par = { + "NUM_SAMPLES" : NUM_SAMPLES, + "REAGENT_LABWARE" : REAGENT_LABWARE, + "MAGPLATE_LABWARE" : MAGPLATE_LABWARE, + "WASTE_LABWARE" : WASTE_LABWARE, + "ELUTION_LABWARE" : ELUTION_LABWARE, + "DISPENSE_BEADS" : DISPENSE_BEADS, + "LANGUAGE" : LANGUAGE, + "RESET_TIPCOUNT" : RESET_TIPCOUNT + } + + run_info(start_time, finish_time, par) diff --git "a/labware/Biotix 96 Filter Tip Rack 1000 \302\265L.json" "b/labware/Biotix 96 Filter Tip Rack 1000 \302\265L.json" new file mode 100644 index 0000000..696adc6 --- /dev/null +++ "b/labware/Biotix 96 Filter Tip Rack 1000 \302\265L.json" @@ -0,0 +1,1128 @@ +{ + "ordering": [ + [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1" + ], + [ + "A2", + "B2", + "C2", + "D2", + "E2", + "F2", + "G2", + "H2" + ], + [ + "A3", + "B3", + "C3", + "D3", + "E3", + "F3", + "G3", + "H3" + ], + [ + "A4", + "B4", + "C4", + "D4", + "E4", + "F4", + "G4", + "H4" + ], + [ + "A5", + "B5", + "C5", + "D5", + "E5", + "F5", + "G5", + "H5" + ], + [ + "A6", + "B6", + "C6", + "D6", + "E6", + "F6", + "G6", + "H6" + ], + [ + "A7", + "B7", + "C7", + "D7", + "E7", + "F7", + "G7", + "H7" + ], + [ + "A8", + "B8", + "C8", + "D8", + "E8", + "F8", + "G8", + "H8" + ], + [ + "A9", + "B9", + "C9", + "D9", + "E9", + "F9", + "G9", + "H9" + ], + [ + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10" + ], + [ + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11" + ], + [ + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ] + ], + "brand": { + "brand": "Biotix", + "brandId": [ + "M-0300-9FC" + ], + "links": [ + "https://biotix.com/products/utip-for-universal-pipettes/1000-%ce%bcl-racked-filtered-sterilized/" + ] + }, + "metadata": { + "displayName": "Biotix 96 Filter Tip Rack 1000 µL", + "displayCategory": "tipRack", + "displayVolumeUnits": "µL", + "tags": [] + }, + "dimensions": { + "xDimension": 127.76, + "yDimension": 85.47, + "zDimension": 100.97 + }, + "wells": { + "A1": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 14.4, + "y": 74.27, + "z": 19.22 + }, + "B1": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 14.4, + "y": 65.27, + "z": 19.22 + }, + "C1": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 14.4, + "y": 56.27, + "z": 19.22 + }, + "D1": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 14.4, + "y": 47.27, + "z": 19.22 + }, + "E1": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 14.4, + "y": 38.27, + "z": 19.22 + }, + "F1": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 14.4, + "y": 29.27, + "z": 19.22 + }, + "G1": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 14.4, + "y": 20.27, + "z": 19.22 + }, + "H1": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 14.4, + "y": 11.27, + "z": 19.22 + }, + "A2": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 23.4, + "y": 74.27, + "z": 19.22 + }, + "B2": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 23.4, + "y": 65.27, + "z": 19.22 + }, + "C2": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 23.4, + "y": 56.27, + "z": 19.22 + }, + "D2": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 23.4, + "y": 47.27, + "z": 19.22 + }, + "E2": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 23.4, + "y": 38.27, + "z": 19.22 + }, + "F2": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 23.4, + "y": 29.27, + "z": 19.22 + }, + "G2": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 23.4, + "y": 20.27, + "z": 19.22 + }, + "H2": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 23.4, + "y": 11.27, + "z": 19.22 + }, + "A3": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 32.4, + "y": 74.27, + "z": 19.22 + }, + "B3": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 32.4, + "y": 65.27, + "z": 19.22 + }, + "C3": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 32.4, + "y": 56.27, + "z": 19.22 + }, + "D3": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 32.4, + "y": 47.27, + "z": 19.22 + }, + "E3": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 32.4, + "y": 38.27, + "z": 19.22 + }, + "F3": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 32.4, + "y": 29.27, + "z": 19.22 + }, + "G3": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 32.4, + "y": 20.27, + "z": 19.22 + }, + "H3": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 32.4, + "y": 11.27, + "z": 19.22 + }, + "A4": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 41.4, + "y": 74.27, + "z": 19.22 + }, + "B4": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 41.4, + "y": 65.27, + "z": 19.22 + }, + "C4": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 41.4, + "y": 56.27, + "z": 19.22 + }, + "D4": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 41.4, + "y": 47.27, + "z": 19.22 + }, + "E4": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 41.4, + "y": 38.27, + "z": 19.22 + }, + "F4": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 41.4, + "y": 29.27, + "z": 19.22 + }, + "G4": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 41.4, + "y": 20.27, + "z": 19.22 + }, + "H4": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 41.4, + "y": 11.27, + "z": 19.22 + }, + "A5": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 50.4, + "y": 74.27, + "z": 19.22 + }, + "B5": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 50.4, + "y": 65.27, + "z": 19.22 + }, + "C5": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 50.4, + "y": 56.27, + "z": 19.22 + }, + "D5": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 50.4, + "y": 47.27, + "z": 19.22 + }, + "E5": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 50.4, + "y": 38.27, + "z": 19.22 + }, + "F5": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 50.4, + "y": 29.27, + "z": 19.22 + }, + "G5": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 50.4, + "y": 20.27, + "z": 19.22 + }, + "H5": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 50.4, + "y": 11.27, + "z": 19.22 + }, + "A6": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 59.4, + "y": 74.27, + "z": 19.22 + }, + "B6": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 59.4, + "y": 65.27, + "z": 19.22 + }, + "C6": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 59.4, + "y": 56.27, + "z": 19.22 + }, + "D6": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 59.4, + "y": 47.27, + "z": 19.22 + }, + "E6": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 59.4, + "y": 38.27, + "z": 19.22 + }, + "F6": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 59.4, + "y": 29.27, + "z": 19.22 + }, + "G6": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 59.4, + "y": 20.27, + "z": 19.22 + }, + "H6": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 59.4, + "y": 11.27, + "z": 19.22 + }, + "A7": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 68.4, + "y": 74.27, + "z": 19.22 + }, + "B7": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 68.4, + "y": 65.27, + "z": 19.22 + }, + "C7": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 68.4, + "y": 56.27, + "z": 19.22 + }, + "D7": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 68.4, + "y": 47.27, + "z": 19.22 + }, + "E7": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 68.4, + "y": 38.27, + "z": 19.22 + }, + "F7": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 68.4, + "y": 29.27, + "z": 19.22 + }, + "G7": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 68.4, + "y": 20.27, + "z": 19.22 + }, + "H7": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 68.4, + "y": 11.27, + "z": 19.22 + }, + "A8": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 77.4, + "y": 74.27, + "z": 19.22 + }, + "B8": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 77.4, + "y": 65.27, + "z": 19.22 + }, + "C8": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 77.4, + "y": 56.27, + "z": 19.22 + }, + "D8": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 77.4, + "y": 47.27, + "z": 19.22 + }, + "E8": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 77.4, + "y": 38.27, + "z": 19.22 + }, + "F8": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 77.4, + "y": 29.27, + "z": 19.22 + }, + "G8": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 77.4, + "y": 20.27, + "z": 19.22 + }, + "H8": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 77.4, + "y": 11.27, + "z": 19.22 + }, + "A9": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 86.4, + "y": 74.27, + "z": 19.22 + }, + "B9": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 86.4, + "y": 65.27, + "z": 19.22 + }, + "C9": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 86.4, + "y": 56.27, + "z": 19.22 + }, + "D9": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 86.4, + "y": 47.27, + "z": 19.22 + }, + "E9": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 86.4, + "y": 38.27, + "z": 19.22 + }, + "F9": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 86.4, + "y": 29.27, + "z": 19.22 + }, + "G9": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 86.4, + "y": 20.27, + "z": 19.22 + }, + "H9": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 86.4, + "y": 11.27, + "z": 19.22 + }, + "A10": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 95.4, + "y": 74.27, + "z": 19.22 + }, + "B10": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 95.4, + "y": 65.27, + "z": 19.22 + }, + "C10": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 95.4, + "y": 56.27, + "z": 19.22 + }, + "D10": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 95.4, + "y": 47.27, + "z": 19.22 + }, + "E10": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 95.4, + "y": 38.27, + "z": 19.22 + }, + "F10": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 95.4, + "y": 29.27, + "z": 19.22 + }, + "G10": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 95.4, + "y": 20.27, + "z": 19.22 + }, + "H10": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 95.4, + "y": 11.27, + "z": 19.22 + }, + "A11": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 104.4, + "y": 74.27, + "z": 19.22 + }, + "B11": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 104.4, + "y": 65.27, + "z": 19.22 + }, + "C11": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 104.4, + "y": 56.27, + "z": 19.22 + }, + "D11": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 104.4, + "y": 47.27, + "z": 19.22 + }, + "E11": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 104.4, + "y": 38.27, + "z": 19.22 + }, + "F11": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 104.4, + "y": 29.27, + "z": 19.22 + }, + "G11": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 104.4, + "y": 20.27, + "z": 19.22 + }, + "H11": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 104.4, + "y": 11.27, + "z": 19.22 + }, + "A12": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 113.4, + "y": 74.27, + "z": 19.22 + }, + "B12": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 113.4, + "y": 65.27, + "z": 19.22 + }, + "C12": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 113.4, + "y": 56.27, + "z": 19.22 + }, + "D12": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 113.4, + "y": 47.27, + "z": 19.22 + }, + "E12": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 113.4, + "y": 38.27, + "z": 19.22 + }, + "F12": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 113.4, + "y": 29.27, + "z": 19.22 + }, + "G12": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 113.4, + "y": 20.27, + "z": 19.22 + }, + "H12": { + "depth": 81.75, + "shape": "circular", + "diameter": 7.96, + "totalLiquidVolume": 1000, + "x": 113.4, + "y": 11.27, + "z": 19.22 + } + }, + "groups": [ + { + "metadata": {}, + "wells": [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1", + "A2", + "B2", + "C2", + "D2", + "E2", + "F2", + "G2", + "H2", + "A3", + "B3", + "C3", + "D3", + "E3", + "F3", + "G3", + "H3", + "A4", + "B4", + "C4", + "D4", + "E4", + "F4", + "G4", + "H4", + "A5", + "B5", + "C5", + "D5", + "E5", + "F5", + "G5", + "H5", + "A6", + "B6", + "C6", + "D6", + "E6", + "F6", + "G6", + "H6", + "A7", + "B7", + "C7", + "D7", + "E7", + "F7", + "G7", + "H7", + "A8", + "B8", + "C8", + "D8", + "E8", + "F8", + "G8", + "H8", + "A9", + "B9", + "C9", + "D9", + "E9", + "F9", + "G9", + "H9", + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10", + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11", + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ] + } + ], + "parameters": { + "format": "96Standard", + "isTiprack": true, + "tipLength": 81.75, + "isMagneticModuleCompatible": false, + "loadName": "biotix_96_tiprack_1000ul" + }, + "namespace": "custom_beta", + "version": 1, + "schemaVersion": 2, + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } +} diff --git "a/labware/Biotix 96 Filter Tip Rack 200 \302\265L.json" "b/labware/Biotix 96 Filter Tip Rack 200 \302\265L.json" new file mode 100644 index 0000000..c511b0b --- /dev/null +++ "b/labware/Biotix 96 Filter Tip Rack 200 \302\265L.json" @@ -0,0 +1,1128 @@ +{ + "ordering": [ + [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1" + ], + [ + "A2", + "B2", + "C2", + "D2", + "E2", + "F2", + "G2", + "H2" + ], + [ + "A3", + "B3", + "C3", + "D3", + "E3", + "F3", + "G3", + "H3" + ], + [ + "A4", + "B4", + "C4", + "D4", + "E4", + "F4", + "G4", + "H4" + ], + [ + "A5", + "B5", + "C5", + "D5", + "E5", + "F5", + "G5", + "H5" + ], + [ + "A6", + "B6", + "C6", + "D6", + "E6", + "F6", + "G6", + "H6" + ], + [ + "A7", + "B7", + "C7", + "D7", + "E7", + "F7", + "G7", + "H7" + ], + [ + "A8", + "B8", + "C8", + "D8", + "E8", + "F8", + "G8", + "H8" + ], + [ + "A9", + "B9", + "C9", + "D9", + "E9", + "F9", + "G9", + "H9" + ], + [ + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10" + ], + [ + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11" + ], + [ + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ] + ], + "brand": { + "brand": "Biotix", + "brandId": [ + "M-0300-9FC" + ], + "links": [ + "https://biotix.com/products/utip-for-universal-pipettes/200-%CE%BCl-racked-filtered-sterilized/" + ] + }, + "metadata": { + "displayName": "Biotix 96 Filter Tip Rack 200 µL", + "displayCategory": "tipRack", + "displayVolumeUnits": "µL", + "tags": [] + }, + "dimensions": { + "xDimension": 127.76, + "yDimension": 85.47, + "zDimension": 63 + }, + "wells": { + "A1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 14.4, + "y": 74.27, + "z": 3.25 + }, + "B1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 14.4, + "y": 65.27, + "z": 3.25 + }, + "C1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 14.4, + "y": 56.27, + "z": 3.25 + }, + "D1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 14.4, + "y": 47.27, + "z": 3.25 + }, + "E1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 14.4, + "y": 38.27, + "z": 3.25 + }, + "F1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 14.4, + "y": 29.27, + "z": 3.25 + }, + "G1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 14.4, + "y": 20.27, + "z": 3.25 + }, + "H1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 14.4, + "y": 11.27, + "z": 3.25 + }, + "A2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 23.4, + "y": 74.27, + "z": 3.25 + }, + "B2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 23.4, + "y": 65.27, + "z": 3.25 + }, + "C2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 23.4, + "y": 56.27, + "z": 3.25 + }, + "D2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 23.4, + "y": 47.27, + "z": 3.25 + }, + "E2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 23.4, + "y": 38.27, + "z": 3.25 + }, + "F2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 23.4, + "y": 29.27, + "z": 3.25 + }, + "G2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 23.4, + "y": 20.27, + "z": 3.25 + }, + "H2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 23.4, + "y": 11.27, + "z": 3.25 + }, + "A3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 32.4, + "y": 74.27, + "z": 3.25 + }, + "B3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 32.4, + "y": 65.27, + "z": 3.25 + }, + "C3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 32.4, + "y": 56.27, + "z": 3.25 + }, + "D3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 32.4, + "y": 47.27, + "z": 3.25 + }, + "E3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 32.4, + "y": 38.27, + "z": 3.25 + }, + "F3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 32.4, + "y": 29.27, + "z": 3.25 + }, + "G3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 32.4, + "y": 20.27, + "z": 3.25 + }, + "H3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 32.4, + "y": 11.27, + "z": 3.25 + }, + "A4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 41.4, + "y": 74.27, + "z": 3.25 + }, + "B4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 41.4, + "y": 65.27, + "z": 3.25 + }, + "C4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 41.4, + "y": 56.27, + "z": 3.25 + }, + "D4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 41.4, + "y": 47.27, + "z": 3.25 + }, + "E4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 41.4, + "y": 38.27, + "z": 3.25 + }, + "F4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 41.4, + "y": 29.27, + "z": 3.25 + }, + "G4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 41.4, + "y": 20.27, + "z": 3.25 + }, + "H4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 41.4, + "y": 11.27, + "z": 3.25 + }, + "A5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 50.4, + "y": 74.27, + "z": 3.25 + }, + "B5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 50.4, + "y": 65.27, + "z": 3.25 + }, + "C5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 50.4, + "y": 56.27, + "z": 3.25 + }, + "D5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 50.4, + "y": 47.27, + "z": 3.25 + }, + "E5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 50.4, + "y": 38.27, + "z": 3.25 + }, + "F5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 50.4, + "y": 29.27, + "z": 3.25 + }, + "G5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 50.4, + "y": 20.27, + "z": 3.25 + }, + "H5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 50.4, + "y": 11.27, + "z": 3.25 + }, + "A6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 59.4, + "y": 74.27, + "z": 3.25 + }, + "B6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 59.4, + "y": 65.27, + "z": 3.25 + }, + "C6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 59.4, + "y": 56.27, + "z": 3.25 + }, + "D6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 59.4, + "y": 47.27, + "z": 3.25 + }, + "E6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 59.4, + "y": 38.27, + "z": 3.25 + }, + "F6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 59.4, + "y": 29.27, + "z": 3.25 + }, + "G6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 59.4, + "y": 20.27, + "z": 3.25 + }, + "H6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 59.4, + "y": 11.27, + "z": 3.25 + }, + "A7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 68.4, + "y": 74.27, + "z": 3.25 + }, + "B7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 68.4, + "y": 65.27, + "z": 3.25 + }, + "C7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 68.4, + "y": 56.27, + "z": 3.25 + }, + "D7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 68.4, + "y": 47.27, + "z": 3.25 + }, + "E7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 68.4, + "y": 38.27, + "z": 3.25 + }, + "F7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 68.4, + "y": 29.27, + "z": 3.25 + }, + "G7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 68.4, + "y": 20.27, + "z": 3.25 + }, + "H7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 68.4, + "y": 11.27, + "z": 3.25 + }, + "A8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 77.4, + "y": 74.27, + "z": 3.25 + }, + "B8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 77.4, + "y": 65.27, + "z": 3.25 + }, + "C8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 77.4, + "y": 56.27, + "z": 3.25 + }, + "D8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 77.4, + "y": 47.27, + "z": 3.25 + }, + "E8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 77.4, + "y": 38.27, + "z": 3.25 + }, + "F8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 77.4, + "y": 29.27, + "z": 3.25 + }, + "G8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 77.4, + "y": 20.27, + "z": 3.25 + }, + "H8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 77.4, + "y": 11.27, + "z": 3.25 + }, + "A9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 86.4, + "y": 74.27, + "z": 3.25 + }, + "B9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 86.4, + "y": 65.27, + "z": 3.25 + }, + "C9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 86.4, + "y": 56.27, + "z": 3.25 + }, + "D9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 86.4, + "y": 47.27, + "z": 3.25 + }, + "E9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 86.4, + "y": 38.27, + "z": 3.25 + }, + "F9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 86.4, + "y": 29.27, + "z": 3.25 + }, + "G9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 86.4, + "y": 20.27, + "z": 3.25 + }, + "H9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 86.4, + "y": 11.27, + "z": 3.25 + }, + "A10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 95.4, + "y": 74.27, + "z": 3.25 + }, + "B10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 95.4, + "y": 65.27, + "z": 3.25 + }, + "C10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 95.4, + "y": 56.27, + "z": 3.25 + }, + "D10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 95.4, + "y": 47.27, + "z": 3.25 + }, + "E10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 95.4, + "y": 38.27, + "z": 3.25 + }, + "F10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 95.4, + "y": 29.27, + "z": 3.25 + }, + "G10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 95.4, + "y": 20.27, + "z": 3.25 + }, + "H10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 95.4, + "y": 11.27, + "z": 3.25 + }, + "A11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 104.4, + "y": 74.27, + "z": 3.25 + }, + "B11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 104.4, + "y": 65.27, + "z": 3.25 + }, + "C11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 104.4, + "y": 56.27, + "z": 3.25 + }, + "D11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 104.4, + "y": 47.27, + "z": 3.25 + }, + "E11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 104.4, + "y": 38.27, + "z": 3.25 + }, + "F11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 104.4, + "y": 29.27, + "z": 3.25 + }, + "G11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 104.4, + "y": 20.27, + "z": 3.25 + }, + "H11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 104.4, + "y": 11.27, + "z": 3.25 + }, + "A12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 113.4, + "y": 74.27, + "z": 3.25 + }, + "B12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 113.4, + "y": 65.27, + "z": 3.25 + }, + "C12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 113.4, + "y": 56.27, + "z": 3.25 + }, + "D12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 113.4, + "y": 47.27, + "z": 3.25 + }, + "E12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 113.4, + "y": 38.27, + "z": 3.25 + }, + "F12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 113.4, + "y": 29.27, + "z": 3.25 + }, + "G12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 113.4, + "y": 20.27, + "z": 3.25 + }, + "H12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 200, + "x": 113.4, + "y": 11.27, + "z": 3.25 + } + }, + "groups": [ + { + "metadata": {}, + "wells": [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1", + "A2", + "B2", + "C2", + "D2", + "E2", + "F2", + "G2", + "H2", + "A3", + "B3", + "C3", + "D3", + "E3", + "F3", + "G3", + "H3", + "A4", + "B4", + "C4", + "D4", + "E4", + "F4", + "G4", + "H4", + "A5", + "B5", + "C5", + "D5", + "E5", + "F5", + "G5", + "H5", + "A6", + "B6", + "C6", + "D6", + "E6", + "F6", + "G6", + "H6", + "A7", + "B7", + "C7", + "D7", + "E7", + "F7", + "G7", + "H7", + "A8", + "B8", + "C8", + "D8", + "E8", + "F8", + "G8", + "H8", + "A9", + "B9", + "C9", + "D9", + "E9", + "F9", + "G9", + "H9", + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10", + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11", + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ] + } + ], + "parameters": { + "format": "96Standard", + "isTiprack": true, + "tipLength": 59.75, + "isMagneticModuleCompatible": false, + "loadName": "biotix_96_tiprack_200ul_flat" + }, + "namespace": "custom_beta", + "version": 1, + "schemaVersion": 2, + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } +} diff --git "a/labware/Biotix 96 Filter Tip Rack 300 \302\265L.json" "b/labware/Biotix 96 Filter Tip Rack 300 \302\265L.json" new file mode 100644 index 0000000..87bb32d --- /dev/null +++ "b/labware/Biotix 96 Filter Tip Rack 300 \302\265L.json" @@ -0,0 +1,1128 @@ +{ + "ordering": [ + [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1" + ], + [ + "A2", + "B2", + "C2", + "D2", + "E2", + "F2", + "G2", + "H2" + ], + [ + "A3", + "B3", + "C3", + "D3", + "E3", + "F3", + "G3", + "H3" + ], + [ + "A4", + "B4", + "C4", + "D4", + "E4", + "F4", + "G4", + "H4" + ], + [ + "A5", + "B5", + "C5", + "D5", + "E5", + "F5", + "G5", + "H5" + ], + [ + "A6", + "B6", + "C6", + "D6", + "E6", + "F6", + "G6", + "H6" + ], + [ + "A7", + "B7", + "C7", + "D7", + "E7", + "F7", + "G7", + "H7" + ], + [ + "A8", + "B8", + "C8", + "D8", + "E8", + "F8", + "G8", + "H8" + ], + [ + "A9", + "B9", + "C9", + "D9", + "E9", + "F9", + "G9", + "H9" + ], + [ + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10" + ], + [ + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11" + ], + [ + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ] + ], + "brand": { + "brand": "Biotix", + "brandId": [ + "M-0300-9FC" + ], + "links": [ + "https://biotix.com/products/utip-for-universal-pipettes/300-%CE%BCl-racked-filtered-sterilized/" + ] + }, + "metadata": { + "displayName": "Biotix 96 Filter Tip Rack 300 µL", + "displayCategory": "tipRack", + "displayVolumeUnits": "µL", + "tags": [] + }, + "dimensions": { + "xDimension": 127.76, + "yDimension": 85.47, + "zDimension": 63 + }, + "wells": { + "A1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 14.4, + "y": 74.27, + "z": 3.25 + }, + "B1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 14.4, + "y": 65.27, + "z": 3.25 + }, + "C1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 14.4, + "y": 56.27, + "z": 3.25 + }, + "D1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 14.4, + "y": 47.27, + "z": 3.25 + }, + "E1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 14.4, + "y": 38.27, + "z": 3.25 + }, + "F1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 14.4, + "y": 29.27, + "z": 3.25 + }, + "G1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 14.4, + "y": 20.27, + "z": 3.25 + }, + "H1": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 14.4, + "y": 11.27, + "z": 3.25 + }, + "A2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 23.4, + "y": 74.27, + "z": 3.25 + }, + "B2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 23.4, + "y": 65.27, + "z": 3.25 + }, + "C2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 23.4, + "y": 56.27, + "z": 3.25 + }, + "D2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 23.4, + "y": 47.27, + "z": 3.25 + }, + "E2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 23.4, + "y": 38.27, + "z": 3.25 + }, + "F2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 23.4, + "y": 29.27, + "z": 3.25 + }, + "G2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 23.4, + "y": 20.27, + "z": 3.25 + }, + "H2": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 23.4, + "y": 11.27, + "z": 3.25 + }, + "A3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 32.4, + "y": 74.27, + "z": 3.25 + }, + "B3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 32.4, + "y": 65.27, + "z": 3.25 + }, + "C3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 32.4, + "y": 56.27, + "z": 3.25 + }, + "D3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 32.4, + "y": 47.27, + "z": 3.25 + }, + "E3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 32.4, + "y": 38.27, + "z": 3.25 + }, + "F3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 32.4, + "y": 29.27, + "z": 3.25 + }, + "G3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 32.4, + "y": 20.27, + "z": 3.25 + }, + "H3": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 32.4, + "y": 11.27, + "z": 3.25 + }, + "A4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 41.4, + "y": 74.27, + "z": 3.25 + }, + "B4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 41.4, + "y": 65.27, + "z": 3.25 + }, + "C4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 41.4, + "y": 56.27, + "z": 3.25 + }, + "D4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 41.4, + "y": 47.27, + "z": 3.25 + }, + "E4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 41.4, + "y": 38.27, + "z": 3.25 + }, + "F4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 41.4, + "y": 29.27, + "z": 3.25 + }, + "G4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 41.4, + "y": 20.27, + "z": 3.25 + }, + "H4": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 41.4, + "y": 11.27, + "z": 3.25 + }, + "A5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 50.4, + "y": 74.27, + "z": 3.25 + }, + "B5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 50.4, + "y": 65.27, + "z": 3.25 + }, + "C5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 50.4, + "y": 56.27, + "z": 3.25 + }, + "D5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 50.4, + "y": 47.27, + "z": 3.25 + }, + "E5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 50.4, + "y": 38.27, + "z": 3.25 + }, + "F5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 50.4, + "y": 29.27, + "z": 3.25 + }, + "G5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 50.4, + "y": 20.27, + "z": 3.25 + }, + "H5": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 50.4, + "y": 11.27, + "z": 3.25 + }, + "A6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 59.4, + "y": 74.27, + "z": 3.25 + }, + "B6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 59.4, + "y": 65.27, + "z": 3.25 + }, + "C6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 59.4, + "y": 56.27, + "z": 3.25 + }, + "D6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 59.4, + "y": 47.27, + "z": 3.25 + }, + "E6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 59.4, + "y": 38.27, + "z": 3.25 + }, + "F6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 59.4, + "y": 29.27, + "z": 3.25 + }, + "G6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 59.4, + "y": 20.27, + "z": 3.25 + }, + "H6": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 59.4, + "y": 11.27, + "z": 3.25 + }, + "A7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 68.4, + "y": 74.27, + "z": 3.25 + }, + "B7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 68.4, + "y": 65.27, + "z": 3.25 + }, + "C7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 68.4, + "y": 56.27, + "z": 3.25 + }, + "D7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 68.4, + "y": 47.27, + "z": 3.25 + }, + "E7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 68.4, + "y": 38.27, + "z": 3.25 + }, + "F7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 68.4, + "y": 29.27, + "z": 3.25 + }, + "G7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 68.4, + "y": 20.27, + "z": 3.25 + }, + "H7": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 68.4, + "y": 11.27, + "z": 3.25 + }, + "A8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 77.4, + "y": 74.27, + "z": 3.25 + }, + "B8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 77.4, + "y": 65.27, + "z": 3.25 + }, + "C8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 77.4, + "y": 56.27, + "z": 3.25 + }, + "D8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 77.4, + "y": 47.27, + "z": 3.25 + }, + "E8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 77.4, + "y": 38.27, + "z": 3.25 + }, + "F8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 77.4, + "y": 29.27, + "z": 3.25 + }, + "G8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 77.4, + "y": 20.27, + "z": 3.25 + }, + "H8": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 77.4, + "y": 11.27, + "z": 3.25 + }, + "A9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 86.4, + "y": 74.27, + "z": 3.25 + }, + "B9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 86.4, + "y": 65.27, + "z": 3.25 + }, + "C9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 86.4, + "y": 56.27, + "z": 3.25 + }, + "D9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 86.4, + "y": 47.27, + "z": 3.25 + }, + "E9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 86.4, + "y": 38.27, + "z": 3.25 + }, + "F9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 86.4, + "y": 29.27, + "z": 3.25 + }, + "G9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 86.4, + "y": 20.27, + "z": 3.25 + }, + "H9": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 86.4, + "y": 11.27, + "z": 3.25 + }, + "A10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 95.4, + "y": 74.27, + "z": 3.25 + }, + "B10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 95.4, + "y": 65.27, + "z": 3.25 + }, + "C10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 95.4, + "y": 56.27, + "z": 3.25 + }, + "D10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 95.4, + "y": 47.27, + "z": 3.25 + }, + "E10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 95.4, + "y": 38.27, + "z": 3.25 + }, + "F10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 95.4, + "y": 29.27, + "z": 3.25 + }, + "G10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 95.4, + "y": 20.27, + "z": 3.25 + }, + "H10": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 95.4, + "y": 11.27, + "z": 3.25 + }, + "A11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 104.4, + "y": 74.27, + "z": 3.25 + }, + "B11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 104.4, + "y": 65.27, + "z": 3.25 + }, + "C11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 104.4, + "y": 56.27, + "z": 3.25 + }, + "D11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 104.4, + "y": 47.27, + "z": 3.25 + }, + "E11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 104.4, + "y": 38.27, + "z": 3.25 + }, + "F11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 104.4, + "y": 29.27, + "z": 3.25 + }, + "G11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 104.4, + "y": 20.27, + "z": 3.25 + }, + "H11": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 104.4, + "y": 11.27, + "z": 3.25 + }, + "A12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 113.4, + "y": 74.27, + "z": 3.25 + }, + "B12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 113.4, + "y": 65.27, + "z": 3.25 + }, + "C12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 113.4, + "y": 56.27, + "z": 3.25 + }, + "D12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 113.4, + "y": 47.27, + "z": 3.25 + }, + "E12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 113.4, + "y": 38.27, + "z": 3.25 + }, + "F12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 113.4, + "y": 29.27, + "z": 3.25 + }, + "G12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 113.4, + "y": 20.27, + "z": 3.25 + }, + "H12": { + "depth": 59.75, + "shape": "circular", + "diameter": 5.9, + "totalLiquidVolume": 300, + "x": 113.4, + "y": 11.27, + "z": 3.25 + } + }, + "groups": [ + { + "metadata": {}, + "wells": [ + "A1", + "B1", + "C1", + "D1", + "E1", + "F1", + "G1", + "H1", + "A2", + "B2", + "C2", + "D2", + "E2", + "F2", + "G2", + "H2", + "A3", + "B3", + "C3", + "D3", + "E3", + "F3", + "G3", + "H3", + "A4", + "B4", + "C4", + "D4", + "E4", + "F4", + "G4", + "H4", + "A5", + "B5", + "C5", + "D5", + "E5", + "F5", + "G5", + "H5", + "A6", + "B6", + "C6", + "D6", + "E6", + "F6", + "G6", + "H6", + "A7", + "B7", + "C7", + "D7", + "E7", + "F7", + "G7", + "H7", + "A8", + "B8", + "C8", + "D8", + "E8", + "F8", + "G8", + "H8", + "A9", + "B9", + "C9", + "D9", + "E9", + "F9", + "G9", + "H9", + "A10", + "B10", + "C10", + "D10", + "E10", + "F10", + "G10", + "H10", + "A11", + "B11", + "C11", + "D11", + "E11", + "F11", + "G11", + "H11", + "A12", + "B12", + "C12", + "D12", + "E12", + "F12", + "G12", + "H12" + ] + } + ], + "parameters": { + "format": "96Standard", + "isTiprack": true, + "tipLength": 59.75, + "isMagneticModuleCompatible": false, + "loadName": "biotix_96_tiprack_300ul_flat" + }, + "namespace": "custom_beta", + "version": 1, + "schemaVersion": 2, + "cornerOffsetFromSlot": { + "x": 0, + "y": 0, + "z": 0 + } +} diff --git a/manuals/manual_eng.md b/manuals/manual_eng.md index 7b5dca2..60aba47 100644 --- a/manuals/manual_eng.md +++ b/manuals/manual_eng.md @@ -14,11 +14,11 @@ The robots are distributed in 2 different locations: - **Extraction Room**: Located in Orientación Diagnóstica, here are robots **A1, A2, B1, B2, B3 and B4** in the same room with a PC to control them. -![extraction_room_setup.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/extraction_room_setup.jpg?raw=true) +![extraction_room_setup.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/extraction_room_setup.jpg?raw=true) - **PCR prep Room**: Inside Virus Respiratorios we find robots **C1 y C2** paired with another PC for their control. -![pcrprep_room_setup.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/pcrprep_room_setup.jpg?raw=true) +![pcrprep_room_setup.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/pcrprep_room_setup.jpg?raw=true) ## Powering on the robots @@ -26,18 +26,18 @@ Before starting the robot, please make sure there are no tips attached to any pi To power on the robot, press the power button located at the rear of the left lateral panel, just above where the power cable is plugged. -![robot_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/robot_power_button.jpg?raw=true) +![robot_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/robot_power_button.jpg?raw=true) The robot will produce some mechanical noises and it si possible the arem moves home several times. The front led button will blink blue during the startup process. Once the noise is over and the arm stops moving, the front led will glow blue permanently signalling the robot is ready to take orders. -![robot_front_led.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/robot_front_led.jpg?raw=true) +![robot_front_led.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/robot_front_led.jpg?raw=true) Finally, for robot configurations **B** and **C** we will have to power on the magdeck and tempdeck, respectively. Press the power button at the rear of each module, next to wher its power cord is plugged. A white light will glow and once the module stops making noises it will be ready to use. -![tempdeck_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/tempdeck_power_button.jpg?raw=true) -![magdeck_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/magdeck_power_button.jpg?raw=true) +![tempdeck_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/tempdeck_power_button.jpg?raw=true) +![magdeck_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/magdeck_power_button.jpg?raw=true) **Note**: The unit **A2** has no blue led in the front button, so it will not show that the robot is booting up neither it is ready to be used. As a consecuence, we will need to check its status through the app as we will explain later. @@ -45,8 +45,8 @@ Finally, for robot configurations **B** and **C** we will have to power on the m **It smells like something is burning or I see smoke**: Quickly power off the robot or module where the smoke comes from by pushing the power button at the rear of the left side panel. Check that electric plugs are correctly plugged matching the marks inside the male conector with the marks in the female one. If one of the pins has become black or everything was already correctly connected, do not power off the robot and contact support. -![power_connector_frontal_male.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/power_connector_frontal_male.jpg?raw=true) -![power_connector_frontal_female.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/power_connector_frontal_female.jpg?raw=true) +![power_connector_frontal_male.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/power_connector_frontal_male.jpg?raw=true) +![power_connector_frontal_female.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/power_connector_frontal_female.jpg?raw=true) **I forgot to remove the tip from the pippete before powering on and ...**: Quickly power off the robot by pushing the power button at the rear of the left side panel. Once it has stopped, clean any liquid that can have been split and contact support to evaluate potential damage or discalibration caused by the colition. @@ -58,29 +58,29 @@ The PCs located in the romms of the robots have a common user logging which gran Once you are logged in, look for the icon of Opentrons app and open it. -![opentrons_app_icon.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_icon.jpg?raw=true) +![opentrons_app_icon.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_icon.jpg?raw=true) The app starts in developper mode, which provides some extra features we need for the correct execution of our protocols, but makes the interface a less user-friendly. Close the debug mode of the app and we are ready to go on. Close any update mesagges, too. Each update needs to be tested before installing, as they will also update the robots and can modify their behaviour. -![close_debug_mode.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/close_debug_mode.jpg?raw=true) +![close_debug_mode.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/close_debug_mode.jpg?raw=true) The app interface is divided in 3 vertical sections: -![opentrons_app_mainwindow.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_mainwindow.jpg?raw=true) +![opentrons_app_mainwindow.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_mainwindow.jpg?raw=true) - Left you have a menu to navigate between the windows robot, protocol, calibration and run. -![opentrons_app_leftmenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_leftmenu.jpg?raw=true) +![opentrons_app_leftmenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_leftmenu.jpg?raw=true) - In the middle panel we have the orders we can execute in the robot from the active window. -![opentrons_app_middlemenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_middlemenu.jpg?raw=true) +![opentrons_app_middlemenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_middlemenu.jpg?raw=true) - To the right we have the largest panel, which shows information for the user and different configuration and interative options. As the previous panel, it contents depen on the active window. -![opentrons_app_rightmenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_rightmenu.jpg?raw=true) +![opentrons_app_rightmenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_rightmenu.jpg?raw=true) ## Connecting to a robot @@ -90,15 +90,15 @@ Click on the first option displayed in the left menu inside the app: `Robot`. Th **Note**: Type **C** robots does not have lights. -![opentrons_app_mainrobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_mainrobot.jpg?raw=true) +![opentrons_app_mainrobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_mainrobot.jpg?raw=true) In order to operate a robot, we need to connect to it. You can do it either by activating the little slider button at the right of its name in the middle panel or by clicking on the `Connect``button in the right panel after selecting the robot in the middle one. - ![opentrons_app_connecttorobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_connecttorobot.jpg?raw=true) + ![opentrons_app_connecttorobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_connecttorobot.jpg?raw=true) A green banner at the top will confirm you are sucessfully connecte to a robot and its name. - ![opentrons_app_connectedrobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_connectedrobot.jpg?raw=true) + ![opentrons_app_connectedrobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_connectedrobot.jpg?raw=true) **Note**: You can only be connected to one robot at the same time. All the robots will continue executing their given orders after disconnected, but any configuration done in a robot might get lost if disconnected before starting the run, so it is recommended that in case of disconnetion you start configurating your experiment again. @@ -110,7 +110,7 @@ In order to operate a robot, we need to connect to it. You can do it either by a Click on the second option of the left menu: `Protocol`. In this window you can see which protocol is already loaded in the robot from the previous run in the right panel. -![opentrons_app_mainprotocol.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_mainprotocol.jpg?raw=true) +![opentrons_app_mainprotocol.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_mainprotocol.jpg?raw=true) To load a new protocol, drag and drop the corresponding `.py` file in the middle panel or click on the button `Open` in the same panel and look for the desired file. @@ -128,7 +128,7 @@ In the right panel we can check tne name of the loaded protocol, version, author The third option of the menu on the left, `Calibrate`, allows us to adjust the arm position and its movement inside the hood so the robot can do it preciselly and match the labware. -![opentrons_app_maincalibrate.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_maincalibrate.jpg?raw=true) +![opentrons_app_maincalibrate.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_maincalibrate.jpg?raw=true) **Protocol calibration is only needed when:** - it is the first time this protocol is being executed in this robot @@ -147,7 +147,7 @@ Calibration must be started with an ampty hood (but modules, as they have to rem Calibration will start with the pipettes. It will guide you step by step to remove the trash for the tips and attach a new tip to the pipettes, and the arm will move to touch the sensors usually coveretd by the trash in order to check the movement and poitioingn of the tips. Once both pipettes have been calibrated, carefully put back the trash in its place to cever the sensors. -![opentrons_calibration_metalpins.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_calibration_metalpins.jpg?raw=true) +![opentrons_calibration_metalpins.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_calibration_metalpins.jpg?raw=true) **Note**: Sensors are tiny delicate metal pins. Be very careful when removing or setting the trash, or they could be damaged. @@ -159,11 +159,11 @@ Calibration will start with the pipettes. It will guide you step by step to remo Once pipette calibration is complete and you start calibrating the first labware, the tipracks, the app will show a layout of how to set up modules (if they are required) and labware inside the hood. Follow the instructions to set up the labware as shown, but do not put liquids yet, only empty labware. Follow the instructions of the section `Colocar el labware en la cabina` in this manual to do it right. For the moment, we are only calibrating and liquids would become a hazard, potentially splitting and contaminating both labware and deck. -![opentrons_calibration_labwarelayout.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_calibration_labwarelayout.jpg?raw=true) +![opentrons_calibration_labwarelayout.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_calibration_labwarelayout.jpg?raw=true) Now lets move to calibrate tipracks. The robot will move the corresponding pipette above the top-left tip of each tiprack,and using the contols in the app you must move the lower end of the pipette to the height of the top end of the tip and centered into it. When it is in the right position, it will try to attach the tip and if it achives it the tiprack will be calibrated. If it fails to do it, repeat the process until it is done correctly. -![opentrons_calibration_controls.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_calibration_controls.jpg?raw=true) +![opentrons_calibration_controls.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_calibration_controls.jpg?raw=true) **Note**: The 1000µl pipette can shake several times when picking up a new tip. This behaviour does not depend on the robot but the app, and can be disabled inside the app in the window `Robot`, in the section `PIPETTES & MODULES` of the middle panel. Click on `SETTINGS` next to the pipette and there is a checkbox saying `shaking` that can be disabled. @@ -185,11 +185,11 @@ In order to set up the labware into the hood, follow the instructions provided b - If possible, put the tubes and boxes with their lid on, and open them only before starting the run. - When building an Opentrons' rack, make sure the marked corner of the stand matches with the marked corner of the rack. That marked corner will go on the top-left of the deck slot. -![opentrons_labware_opentronsrack.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_labware_opentronsrack.jpg?raw=true) +![opentrons_labware_opentronsrack.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_labware_opentronsrack.jpg?raw=true) - When putting a piece of labware in a deck slot, always put first the bottom-right corner into the metal hooks. Then the piece should easily fall into position. -![opentrons_labware_deckslot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_labware_deckslot.jpg?raw=true) +![opentrons_labware_deckslot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_labware_deckslot.jpg?raw=true) - Make sure the tubes are in the right position in their racks. - Set up all the tip boxes as if the protocol was going to be executed for the maximum number of samples and steps. @@ -202,7 +202,7 @@ The last option of the left panel menu, `Run`, allow us to give the execution or This order starts the tun of the uploaded protocol. Make sure that the loaded protocol is the right one, that you are connected to the correct robot, that the labware is setup and without lids on, that reuired modules are on and pipettes with no tips attached before clicking on the button. - ![opentrons_run_start.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_start.jpg?raw=true) + ![opentrons_run_start.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_start.jpg?raw=true) - `PAUSE`: @@ -210,13 +210,13 @@ This order starts the tun of the uploaded protocol. Make sure that the loaded pr **Note**: In case the robot finds a recuperable execution error, like door is open, tips are over or trash is full, the execution will pause as if the button `PAUSE` was cliked on. This status is notified to the user both verbally and via text in the log. Once the problem is fixed, click on `RESUME` to resume the run. - ![opentrons_run_pause.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_pause.jpg?raw=true) + ![opentrons_run_pause.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_pause.jpg?raw=true) - `RESUME`: Resume execution after pausing. - ![opentrons_run_pauserestart.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_pauserestart.jpg?raw=true) + ![opentrons_run_pauserestart.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_pauserestart.jpg?raw=true) - `CANCEL RUN`: @@ -224,13 +224,13 @@ Abort run in progress. This order requires confirmation and all progress will be **Note**: After cancelling a run, the robot will erase all information about the run, so it is reommended to refill al tips before running anything again. -![opentrons_run_cancelrunconfirmation.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_cancelrunconfirmation.jpg?raw=true) +![opentrons_run_cancelrunconfirmation.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_cancelrunconfirmation.jpg?raw=true) - `RESET RUN`: Afeter finishing a run the robot will stop. The top baner colour and message will show if the run ended successfully (green), if it is paused (yellow) or got aborted or critical error (red). In order to run the same protocol again it is not needed to upload the same protocol file, but just click on this button and the robot will prepare everything for a new run using the same protocol file. -![opentrons_run_resetrun.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_resetrun.jpg?raw=true) +![opentrons_run_resetrun.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_resetrun.jpg?raw=true) A log of the process will be displayed at all times in the right panel of this window. While running, the log will scroll down and marking in blue the current step , in grey the already completed steps and in black the remaining ones. diff --git a/manuals/manual_esp.md b/manuals/manual_esp.md index 78bd4cb..17d6fc2 100644 --- a/manuals/manual_esp.md +++ b/manuals/manual_esp.md @@ -14,11 +14,11 @@ Los robots se encuentran localizados en 2 laboratorios diferentes, distinguiendo - **Sala de extracción**: Situada en Orientación Diagnóstica, encontramos en una misma sala los robots **A1, A2, B1, B2, B3 y B4** junto con un PC para su operación. -![extraction_room_setup.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/extraction_room_setup.jpg?raw=true) +![extraction_room_setup.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/extraction_room_setup.jpg?raw=true) - **Sala de preparación de PCR**: En Virus Respiratorios se encuentran lo robots **C1 y C2** junto a un otro PC para su operación. -![pcrprep_room_setup.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/pcrprep_room_setup.jpg?raw=true) +![pcrprep_room_setup.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/pcrprep_room_setup.jpg?raw=true) ## Encender los robots @@ -26,18 +26,18 @@ Antes de enceder el robot, asegúrate de que no hay puntas en las pipetas ni obj Para encender los robots, utiliza el botón de encendido que se encuentra al fondo del panel lateral izquierdo, justo sobre la toma de corriente del robot. -![robot_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/robot_power_button.jpg?raw=true) +![robot_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/robot_power_button.jpg?raw=true) El robot emitirá unos sonidos de engranajes y es posible que el brazo se mueva hasta la posición de inicio unas cuantas veces. El botón frontal del aparato parpadeará en color azul durante el proceso. Una vez se paren los ruidos y el brazo del robot esté detenido, el led del botón frontal pasará a brillar en color azul de forma fija indicando que el robot está listo para ser utilizado. -![robot_front_led.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/robot_front_led.jpg?raw=true) +![robot_front_led.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/robot_front_led.jpg?raw=true) Finalmente, en el caso de los robots tipo **B** y **C** tendremos que encender los módulos magnético y de temperatura respectivamente. Para ello, presiona le botón de encendido de cada módulo situado en la parte trasera del mismo. Cuando la luz trasera se encienda y el módulo deje de emitir sonidos estará listo para usarse. -![tempdeck_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/tempdeck_power_button.jpg?raw=true) -![magdeck_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/magdeck_power_button.jpg?raw=true) +![tempdeck_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/tempdeck_power_button.jpg?raw=true) +![magdeck_power_button.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/magdeck_power_button.jpg?raw=true) **Nota**: En el robot **A2** el led del botón frontal no funciona en color azul, por lo que no nos indicará que está en proceso de arranque con parpadeos ni que está listo para su uso brillando fijo en color azul, deberemos comprobarlo a través de la aplicación como explicaremos más adelante. @@ -45,8 +45,8 @@ Finalmente, en el caso de los robots tipo **B** y **C** tendremos que encender l **Huele a quemado o veo humo**: Apaga rápidamente el robot o módulo del que procede el olor/humo pulsando rápidamente el botón de apagado del robot al fondo del panel lateral izquierdo. Comprueba que los conectores de corriente están correctamente conectados haciendo coincidir la parte plana del conector macho con la parte plana del hembra y lo mismo con la muesca interna. Si observas que alguno de los pines se ha vuelto negro o estaba bien conectado, no vuelvas a encender el robot y llama a soporte. -![power_connector_frontal_male.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/power_connector_frontal_male.jpg?raw=true) -![power_connector_frontal_female.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/power_connector_frontal_female.jpg?raw=true) +![power_connector_frontal_male.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/power_connector_frontal_male.jpg?raw=true) +![power_connector_frontal_female.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/power_connector_frontal_female.jpg?raw=true) **Se me ha olvidado quitar la punta de la pipeta antes de encenderlo y ...**: Apaga el robot de nuevo pulsando rápidamente el botón de apagado del robot al fondo del panel lateral izquierdo. Una vez se haya detenido, recoge cualquier líquido que pueda haberse derramado y pide soperte para poder evaluar posibles daños o desajustes producisdos por las colisiones. @@ -58,29 +58,29 @@ Los PCs instalados en las mismas salas que los robots tienen un usuario común q Una vez en el escritorio del equipo, busca el logo de la aplicación Opentrons y ábrela. -![opentrons_app_icon.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_icon.jpg?raw=true) +![opentrons_app_icon.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_icon.jpg?raw=true) La aplicación se abre en modo desarrollador, ya que de otra forma no tendríamos acceso a algunas características necesarias para el correcto funcionamiento de nustros robots, pero a cambio produce efectos en la interfaz que la pueden hacer menos amigable. Cierra el modo debug de la aplicación y podemos continuar. Cierra también las peticiones de actualización. Cada actualización debe ser testada a fondo antes de ser instalada, ya que también actualizará los robots y puede modificar su funcionamiento. -![close_debug_mode.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/close_debug_mode.jpg?raw=true) +![close_debug_mode.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/close_debug_mode.jpg?raw=true) La ventana de la aplicación se divide en 3 secciones verticales: -![opentrons_app_mainwindow.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_mainwindow.jpg?raw=true) +![opentrons_app_mainwindow.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_mainwindow.jpg?raw=true) - A la izquierda del todo tenemos un menú que nos permite cambiar entre las ventanas de robot, protocolo, calibración y ejecución. -![opentrons_app_leftmenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_leftmenu.jpg?raw=true) +![opentrons_app_leftmenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_leftmenu.jpg?raw=true) - En el panel del medio tenemos las posibles órdenes que podemos dar al robot desde la ventana en la que nos encontramos. -![opentrons_app_middlemenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_middlemenu.jpg?raw=true) +![opentrons_app_middlemenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_middlemenu.jpg?raw=true) - En el panel de la derecha, el más grande de los tres, se presenta la información para el usuario así como opciones de configuración e interacción con el robot. Como la anterior, depende de la ventana en la que nos encontremos el contenido de este panel variará. -![opentrons_app_rightmenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_rightmenu.jpg?raw=true) +![opentrons_app_rightmenu.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_rightmenu.jpg?raw=true) ## Conectar un robot @@ -90,15 +90,15 @@ En la aplicación hacemos click en la primera opción del menú lateral de la iz **Nota**: Los robots tipo **C** no tienen luces internas. -![opentrons_app_mainrobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_mainrobot.jpg?raw=true) +![opentrons_app_mainrobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_mainrobot.jpg?raw=true) Para poder operar el robot, tendemos que dar la orden de conectarnos a él. Para ello, haz click en la pequeña palanca a la dereha del nombre del robot en el panel de en medio o en el botón `Connect` en el panel de la derecha tras haber seleccionado el robot en el del medio. - ![opentrons_app_connecttorobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_connecttorobot.jpg?raw=true) + ![opentrons_app_connecttorobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_connecttorobot.jpg?raw=true) Un baner verde en la parte superior del tercer panel nos confirmará a qué robot nos hemos conectado con éxito. - ![opentrons_app_connectedrobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_connectedrobot.jpg?raw=true) + ![opentrons_app_connectedrobot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_connectedrobot.jpg?raw=true) **Nota**: Solo puedes estar conectado a un robot al mismo tiempo. Los demás seguirán ejecutando sus órdenes aunque te desconectes de ellos, pero cualquier configuración hecha en un robot corre el riesgo de perderse si te desconectas antes de ejecutar una orden, por lo que es recomendable que en caso de desconexión vuelvas a empezar la configuración de tu protocolo desde el principio. @@ -110,7 +110,7 @@ Para poder operar el robot, tendemos que dar la orden de conectarnos a él. Para Esta es la segunda opción del panel de la izquierda: `Protocol`. Cuando accedas a esta ventana, en el panel de la derecha verás el protocolo que se encuentra actualmente cargado en el robot de la última ejecución. -![opentrons_app_mainprotocol.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_mainprotocol.jpg?raw=true) +![opentrons_app_mainprotocol.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_mainprotocol.jpg?raw=true) Si quieres cargar un nuevo protocolo, tan solo busca el archivo `.py` correspondiente y arrástralo al panel del medio o haz click en el botón `Open` del mismo panel y selecciona el archivo deseado. @@ -130,7 +130,7 @@ En el panel de la derecha podemos ver el nombre del protocolo, versión, autores En la tercera opción del menú del panel de la izquierda, `Calibrate`, podremos ajustar la posición del brazo del robot y sus movimientos para que se mueva por la cabina correctamente y reconozca el labware al milímetro. -![opentrons_app_maincalibrate.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_app_maincalibrate.jpg?raw=true) +![opentrons_app_maincalibrate.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_app_maincalibrate.jpg?raw=true) **Solo es necesario calibrar un protocolo si:** - es la primera vez que se ejecuta ese protocolo en este robot @@ -149,7 +149,7 @@ Hay que iniciar la calibración con la cabina vacía (a exepción de los módulo Al iniciar la calibración se empezará por las pipetas. Te pedirá paso a paso que quites el cubo de basura de las puntas y pongas una punta en las pipetas, y él solo tocará unos sensores normalemente ocultos bajo el cubo para comprobar el movimiento y posicionamiento de las puntas. Una vez termine el proceso para ambas pipetas, vuelve a poner cuidadosamente el cubo para cubrir los sonsores. -![opentrons_calibration_metalpins.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_calibration_metalpins.jpg?raw=true) +![opentrons_calibration_metalpins.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_calibration_metalpins.jpg?raw=true) **Nota**: Los sensores son unas plaquitas de metal muy delicadas y sensibles. Ten mucho cuidado al quitar y poner el cubo de basura o podrías dañarlos. @@ -161,11 +161,11 @@ Al iniciar la calibración se empezará por las pipetas. Te pedirá paso a paso Una vez acabes de calibrar las pipetas y pases al primer labware, las puntas, te aparecerá una plantilla con la colocación en el deck de los módulos (si los hubiera) y del labware. Es hora de colocarlo todo tal y como se indica en la plantilla, pero todo vacío. Sigue las instrucciones de la sección `Colocar el labware en la cabina` de este manual para hacerlo correctamente. Solo estamos calibrando por el momento y si hubiera líquidos se podría salpicar, manchar o contaminar el labware y la cabina. -![opentrons_calibration_labwarelayout.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_calibration_labwarelayout.jpg?raw=true) +![opentrons_calibration_labwarelayout.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_calibration_labwarelayout.jpg?raw=true) Ahora vamos a comenzar a calibrar las cajas de puntas. El robot desplazará la pipeta correspondiente sobre la punta superior izquierda de cada caja, y usando los controles de la aplicación debe colocarse la pipeta centrada sobre la punta y a ras de su parte superior (a la altura donde acaba la punta debe comenzar la pipeta). Cuando estemos en posición probaremos a coger la punta con la pipeta y si la ha cogido correctamente ese tiprack estará calibrado. Si falla habrá que repetir el proceso hasta que lo haga correctamente. -![opentrons_calibration_controls.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_calibration_controls.jpg?raw=true) +![opentrons_calibration_controls.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_calibration_controls.jpg?raw=true) **Nota**: La pipeta monocanal de 1000µl puede hacer sacudidas extrañas al recoger la punta. Esto es un comportamiento que no depende del robot sino de la aplicación, y puede desactivarse dentro de la aplicacción en la ventana `Robot`, en la sección `PIPETTES & MODULES` del panel central. Selecciona el botón `SETTINGS` correspondiente a la pipeta y habrá un checkbox con la opción de `shaking` que se puede desactivar. @@ -187,11 +187,11 @@ Para colocar el labware dentro de la cabina, sigue las instrucciones que te han - Coloca los botes y cajas tapados siempre que sea posible, y ábrelos solo en el momento antes de la ejecución. - Cuando montes un rack de Opentrons, asegúrate que la esquina marcada del rack coincide con la marca en el soporte. Esa esquina corresponderá con la esquina superior izquierda de la casilla en el deck. -![opentrons_labware_opentronsrack.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_labware_opentronsrack.jpg?raw=true) +![opentrons_labware_opentronsrack.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_labware_opentronsrack.jpg?raw=true) - Para colocar una pieza de labware en su casilla, asegurate de que primero colocas la esquina inferior derecha bien encajada en las pestañas de sujección. El resto debería encajar solo al soltarlo suavemente. -![opentrons_labware_deckslot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_labware_deckslot.jpg?raw=true) +![opentrons_labware_deckslot.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_labware_deckslot.jpg?raw=true) - Asegurate de que colocas los tubos correspondientes en sus posiciones correctas. - Coloca todas las cajas de puntas como si el protocolo se fuera a ejecutar para el máximo de muestras y pasos. @@ -204,7 +204,7 @@ La última opción del menú de la izquierda, `Run`, nos permite darle la orden Esta orden iniciará la ejecución del protocolo cargado. Asegúrate de que el protocolo cargado es el adecuado, de que estás operando el robot correcto, que el labware está colocado y destapado, los módulos requeridos encendidos y las pipetas sin puntas puestas antes de iniciar la ejecución. -![opentrons_run_start.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_start.jpg?raw=true) +![opentrons_run_start.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_start.jpg?raw=true) - Pausa (`PAUSE`): @@ -212,13 +212,13 @@ Detiene temporalmente la ejecución del protocolo. Puede tardar unos segundos en **Nota**: En caso de que el robot encuentre un error subsanable, como por ejemplo apertura de la puerta, falta de puntas o basura llena, detendrá la ejecución como si el botón pausa (`PAUSE`) se hubiera pulsado. Esta situación se notifica al operador tanto por texto en el log como con un mensaje sonoro. Una vez el problema ha sido resuelto, pulsando el botón reanudar (`RESUME`) retomará la ejecución. -![opentrons_run_pause.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_pause.jpg?raw=true) +![opentrons_run_pause.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_pause.jpg?raw=true) - Reanudar (`RESUME`): Retoma la ejecución tras una parada. -![opentrons_run_pauserestart.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_pauserestart.jpg?raw=true) +![opentrons_run_pauserestart.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_pauserestart.jpg?raw=true) - Cancelar ejecución (`CANCEL RUN`): @@ -226,13 +226,13 @@ Aborta la ejecución del protocolo. Esta orden requiere confirmación y no es re **Nota**: Tras una cancelación, el robot borrará toda información sobre la ejecución fallida, por lo que es recomendable recargar todas las puntas antes de volver a ejecutar. -![opentrons_run_cancelrunconfirmation.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_cancelrunconfirmation.jpg?raw=true) +![opentrons_run_cancelrunconfirmation.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_cancelrunconfirmation.jpg?raw=true) - Resetear protocolo (`RESET RUN`): Después de terminar una ejecución el robot se detendrá. El color del baner superior y su mensaje nos indicarán si terminó con éxito (verde), quedo pausada (amarillo) o fue abortada por el usuario o un error irrecuperable (rojo). Para ejecutar de nuevo el mismo protocolo no es necesario volver a subir el protocolo, basta con pulsar este botón y preparará el robot para una nueva ejecución del mismo protocolo. -![opentrons_run_resetrun.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/master/img/opentrons_run_resetrun.jpg?raw=true) +![opentrons_run_resetrun.jpg](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/img/opentrons_run_resetrun.jpg?raw=true) En todo momento, en el panel de la derecha de esta ventana veremos un log de los pasos del protocolo. Cuando esté en ejecución, el log irá acanzando y resaltará en azul la orden que se encuentra realizando el robot en ese momento, en gris las ya realizadas y en negro las que quedan para terminar la ejecución. diff --git a/ot2-modifications/MagdeckV1_ElevatorFrame/OT2 Magdeck V1 Elevator Frame.stl b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2 Magdeck V1 Elevator Frame.stl new file mode 100644 index 0000000..669e46b Binary files /dev/null and b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2 Magdeck V1 Elevator Frame.stl differ diff --git a/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_3D.jpg b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_3D.jpg new file mode 100644 index 0000000..4c8ea1e Binary files /dev/null and b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_3D.jpg differ diff --git a/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_onmagdeck.jpg b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_onmagdeck.jpg new file mode 100644 index 0000000..841a5d9 Binary files /dev/null and b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_onmagdeck.jpg differ diff --git a/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_picture.jpg b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_picture.jpg new file mode 100644 index 0000000..a3682df Binary files /dev/null and b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_picture.jpg differ diff --git a/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_withdeepwellplate.jpg b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_withdeepwellplate.jpg new file mode 100644 index 0000000..2709c62 Binary files /dev/null and b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_withdeepwellplate.jpg differ diff --git a/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagedckV1_ElevatorFrame_template.jpg b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagedckV1_ElevatorFrame_template.jpg new file mode 100644 index 0000000..05ad020 Binary files /dev/null and b/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagedckV1_ElevatorFrame_template.jpg differ diff --git a/ot2-modifications/MagdeckV1_ElevatorFrame/Readme.md b/ot2-modifications/MagdeckV1_ElevatorFrame/Readme.md new file mode 100644 index 0000000..505a8ff --- /dev/null +++ b/ot2-modifications/MagdeckV1_ElevatorFrame/Readme.md @@ -0,0 +1,19 @@ +## OT2 Magdeck V1 Elevator Frame + +The magnetic module V1 is too strong and attracts magnetic beads even when disengaged. But the V2 version is too weak, so we need to fix tinker around to fix this. + +We have created a 3D printable elevator frame designed to raise the plate above the magnetic field of the magnetic module when disengaged. + +The printable file is [OT2 Magdeck V1 Elevator Frame](https://www.tinkercad.com/things/9f8oLel816s-ot2-magdeck-v1-elevator-frame), you can find the .stl in this same folder, and can be also found in Tinkercad. It is licensed under Atribution 3.0 (CC-BY 3.0). + +![Real picture](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_picture.jpg?raw=true) + +![With deepwell plate](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_withdeepwellplate.jpg?raw=true) + +![On magdeck](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_onmagdeck.jpg?raw=true) + +![3D](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagdeckV1_ElevatorFrame_3D.jpg?raw=true) + +In case you want the original template, here it is: + +![Template](https://github.com/BU-ISCIII/opentrons_covid19/blob/develop/ot2-modifications/MagdeckV1_ElevatorFrame/OT2_MagedckV1_ElevatorFrame_template.jpg?raw=true) diff --git a/protocols/S3/stationA_protocol1_buffer_S3.ot2.apiv2.py b/protocols/S3/stationA_protocol1_buffer_S3.ot2.apiv2.py index 36936c4..c963191 100644 --- a/protocols/S3/stationA_protocol1_buffer_S3.ot2.apiv2.py +++ b/protocols/S3/stationA_protocol1_buffer_S3.ot2.apiv2.py @@ -4,10 +4,14 @@ import time import math import os +import sys import subprocess import json from datetime import datetime - +custom_modules_path = "/var/user-packages/usr/lib/python3.7/site-packages" +if custom_modules_path not in sys.path: + sys.path.append(custom_modules_path) +import requests # Metadata metadata = { @@ -28,10 +32,11 @@ VOLUME_BUFFER = 300 LANGUAGE = 'esp' RESET_TIPCOUNT = False - +PROTOCOL_ID = "0000-AA" +URL = 'localhost' # End Parameters to adapt the protocol ACTION = "StationA-protocol1-buffer" -PROTOCOL_ID = "0000-AA" + ## global vars ## initialize robot object @@ -97,6 +102,29 @@ } # Function definitions + +def write_to_error_log (info, reason): + date = datetime.now().strftime("%Y_%m_%d") + folder_date = os.path.join('/data/logs', date) + time_now = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + json_file = time_now + '.json' + folder_file_name = os.path.join(folder_date, json_file) + folder_error_log = os.path.join(folder_date,'error.log') + if not os.path.exists(folder_date): + try: + os.makedirs(folder_date) + except: + return + try: + # Create a new file for dumping json data + with open (folder_file_name , 'w') as fh: + json.dump(info, fh, indent=4) + # Append status reason code to the log + with open(folder_error_log, 'a') as fh: + fh.write( time_now + ' Unable to accept the requests get error : '+ reason + '\n') + except: + return + def run_info(start, end, parameters = dict()): info = {} hostname = subprocess.run( @@ -111,9 +139,20 @@ def run_info(start, end, parameters = dict()): info["StartRunTime"] = start info["FinishRunTime"] = end info["parameters"] = parameters - # write json to file. This is going to be an api post. - #with open('run.json', 'w') as fp: - #json.dump(info, fp,indent=4) + + headers = {'Content-type': 'application/json'} + url_https = 'https://' + URL + url_http = 'http://' + URL + try: + r = requests.post(url_https, data=json.dumps(info), headers=headers) + except: + try: + r = requests.post(url_http, data=json.dumps(info), headers=headers) + except: + write_to_error_log(info, 'Server communication error') + return + if r.status_code > 201 : + write_to_error_log(info, str(r.status_code)) def check_door(): diff --git a/protocols/S3/stationA_protocol2_beads_S3.ot2.apiv2.py b/protocols/S3/stationA_protocol2_beads_S3.ot2.apiv2.py index 94ed5c0..e65256f 100644 --- a/protocols/S3/stationA_protocol2_beads_S3.ot2.apiv2.py +++ b/protocols/S3/stationA_protocol2_beads_S3.ot2.apiv2.py @@ -4,9 +4,14 @@ import time import math import os +import sys import subprocess import json from datetime import datetime +custom_modules_path = "/var/user-packages/usr/lib/python3.7/site-packages" +if custom_modules_path not in sys.path: + sys.path.append(custom_modules_path) +import requests # metadata @@ -27,10 +32,11 @@ DILUTE_BEADS = True LANGUAGE = 'esp' RESET_TIPCOUNT = False - +PROTOCOL_ID = "0000-AA" +URL = 'localhost' # End Parameters to adapt the protocol ACTION = "StationA-protocol2-beads" -PROTOCOL_ID = "0000-AA" + ## global vars ## initialize robot object robot = None @@ -46,23 +52,25 @@ NUM_SAMPLES is the number of samples, must be an integer number BEADS_LABWARE must be one of the following: - opentrons plastic 50 ml tubes + opentrons plastic 50ml tubes opentrons plastic 30ml tubes PLATE_LABWARE must be one of the following: opentrons deep generic well plate nest deep generic well plate vwr deep generic well plate + ecogen deep generic well plate """ BD_LW_DICT = { - 'opentrons plastic 50 ml tubes': 'opentrons_6_tuberack_falcon_50ml_conical', + 'opentrons plastic 50ml tubes': 'opentrons_6_tuberack_falcon_50ml_conical', 'opentrons plastic 30ml tubes': 'opentrons_6_tuberack_generic_30ml_conical' } PL_LW_DICT = { 'opentrons deep generic well plate': 'usascientific_96_wellplate_2.4ml_deep', 'nest deep generic well plate': 'nest_96_deepwellplate_2000ul', + 'ecogen deep generic well plate': 'ecogen_96_deepwellplate_2000ul', 'vwr deep generic well plate': 'vwr_96_deepwellplate_2000ul' } @@ -89,6 +97,28 @@ } # Function definitions +def write_to_error_log (info, reason): + date = datetime.now().strftime("%Y_%m_%d") + folder_date = os.path.join('/data/logs', date) + time_now = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + json_file = time_now + '.json' + folder_file_name = os.path.join(folder_date, json_file) + folder_error_log = os.path.join(folder_date,'error.log') + if not os.path.exists(folder_date): + try: + os.makedirs(folder_date) + except: + return + try: + # Create a new file for dumping json data + with open (folder_file_name , 'w') as fh: + json.dump(info, fh, indent=4) + # Append status reason code to the log + with open(folder_error_log, 'a') as fh: + fh.write( time_now + ' Unable to accept the requests get error : '+ reason + '\n') + except: + return + def run_info(start, end, parameters = dict()): info = {} hostname = subprocess.run( @@ -103,9 +133,21 @@ def run_info(start, end, parameters = dict()): info["StartRunTime"] = start info["FinishRunTime"] = end info["parameters"] = parameters - # write json to file. This is going to be an api post. - #with open('run.json', 'w') as fp: - #json.dump(info, fp,indent=4) + + headers = {'Content-type': 'application/json'} + url_https = 'https://' + URL + url_http = 'http://' + URL + try: + r = requests.post(url_https, data=json.dumps(info), headers=headers) + except: + try: + r = requests.post(url_http, data=json.dumps(info), headers=headers) + except: + write_to_error_log(info, 'Server communication error') + return + if r.status_code > 201 : + write_to_error_log(info, str(r.status_code)) + def check_door(): return gpio.read_window_switches() @@ -244,10 +286,10 @@ def transfer_beads(beads_tube, dests, pip,tiprack): for i in range(len(split_ind)-1)] + [dests[split_ind[-1]:]] # pick_up(pip,tiprack) # Mix bead tubes prior to dispensing - pip.flow_rate.aspirate = 800 - pip.flow_rate.dispense = 8000 + pip.flow_rate.aspirate = 1000 + pip.flow_rate.dispense = 10000 # pip.mix(12,800,beads_tube.bottom(15)) - for i in range(12): + for i in range(15): pip.aspirate(800, beads_tube.bottom(30)) pip.dispense(800, beads_tube.bottom(2)) pip.flow_rate.aspirate = 100 @@ -287,8 +329,8 @@ def run(ctx: protocol_api.ProtocolContext): # check source (elution) labware type if BEADS_LABWARE not in BD_LW_DICT: - raise Exception('Invalid BF_LABWARE. Must be one of the \ -following:\nopentrons plastic 50ml tubes') + raise Exception('Invalid BD_LABWARE. Must be one of the \ +following:\nopentrons plastic 50ml tubes\nopentrons plastic 30ml tubes') # load mastermix labware beads_rack = robot.load_labware( diff --git a/protocols/S3/stationA_protocol3_lysates_S3.ot2.apiv2.py b/protocols/S3/stationA_protocol3_lysates_S3.ot2.apiv2.py index e09c48b..78eccda 100644 --- a/protocols/S3/stationA_protocol3_lysates_S3.ot2.apiv2.py +++ b/protocols/S3/stationA_protocol3_lysates_S3.ot2.apiv2.py @@ -4,9 +4,14 @@ import time import math import os +import sys import subprocess import json from datetime import datetime +custom_modules_path = "/var/user-packages/usr/lib/python3.7/site-packages" +if custom_modules_path not in sys.path: + sys.path.append(custom_modules_path) +import requests # metadata @@ -29,10 +34,10 @@ BEADS = False LANGUAGE = 'esp' RESET_TIPCOUNT = False - +PROTOCOL_ID = "0000-AA" +URL = 'localhost' # End Parameters to adapt the protocol ACTION = "StationA-protocol3-lysates" -PROTOCOL_ID = "0000-AA" ## global vars ## initialize robot object @@ -58,6 +63,7 @@ opentrons deep generic well plate nest deep generic well plate vwr deep generic well plate + ecogen deep generic well plate """ LY_LW_DICT = { @@ -67,6 +73,7 @@ PL_LW_DICT = { 'opentrons deep generic well plate': 'usascientific_96_wellplate_2.4ml_deep', 'nest deep generic well plate': 'nest_96_deepwellplate_2000ul', + 'ecogen deep generic well plate': 'ecogen_96_deepwellplate_2000ul', 'vwr deep generic well plate': 'vwr_96_deepwellplate_2000ul' } @@ -98,6 +105,29 @@ } # Function definitions + +def write_to_error_log (info, reason): + date = datetime.now().strftime("%Y_%m_%d") + folder_date = os.path.join('/data/logs', date) + time_now = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + json_file = time_now + '.json' + folder_file_name = os.path.join(folder_date, json_file) + folder_error_log = os.path.join(folder_date,'error.log') + if not os.path.exists(folder_date): + try: + os.makedirs(folder_date) + except: + return + try: + # Create a new file for dumping json data + with open (folder_file_name , 'w') as fh: + json.dump(info, fh, indent=4) + # Append status reason code to the log + with open(folder_error_log, 'a') as fh: + fh.write( time_now + ' Unable to accept the requests get error : '+ reason + '\n') + except: + return + def run_info(start, end, parameters = dict()): info = {} hostname = subprocess.run( @@ -112,9 +142,21 @@ def run_info(start, end, parameters = dict()): info["StartRunTime"] = start info["FinishRunTime"] = end info["parameters"] = parameters - # write json to file. This is going to be an api post. - #with open('run.json', 'w') as fp: - #json.dump(info, fp,indent=4) + + headers = {'Content-type': 'application/json'} + url_https = 'https://' + URL + url_http = 'http://' + URL + try: + r = requests.post(url_https, data=json.dumps(info), headers=headers) + except: + try: + r = requests.post(url_http, data=json.dumps(info), headers=headers) + except: + write_to_error_log(info, 'Server communication error') + return + if r.status_code > 201 : + write_to_error_log(info, str(r.status_code)) + def check_door(): return gpio.read_window_switches() @@ -318,13 +360,13 @@ def run(ctx: protocol_api.ProtocolContext): finish_time = finish_run() - par = { - "NUM_SAMPLES" : NUM_SAMPLES, - "LYSATE_LABWARE" : LYSATE_LABWARE, - "PLATE_LABWARE" : PLATE_LABWARE, - "VOLUME_LYSATE" : VOLUME_LYSATE, - "BEADS" : BEADS, - "LANGUAGE" : LANGUAGE, - "RESET_TIPCOUNT" : RESET_TIPCOUNT - } - run_info(start_time, finish_time, par) + parameters = { + "NUM_SAMPLES" : NUM_SAMPLES, + "LYSATE_LABWARE" : LYSATE_LABWARE, + "PLATE_LABWARE" : PLATE_LABWARE, + "VOLUME_LYSATE" : VOLUME_LYSATE, + "BEADS" : BEADS, + "LANGUAGE" : LANGUAGE, + "RESET_TIPCOUNT" : RESET_TIPCOUNT} + + run_info(start_time, finish_time, parameters) diff --git a/protocols/S3/stationB_protocol1_extraction_S3.ot2.apiv2.py b/protocols/S3/stationB_protocol1_extraction_S3.ot2.apiv2.py index 66bd6f6..ccf2467 100644 --- a/protocols/S3/stationB_protocol1_extraction_S3.ot2.apiv2.py +++ b/protocols/S3/stationB_protocol1_extraction_S3.ot2.apiv2.py @@ -4,10 +4,14 @@ import time import math import os +import sys import subprocess import json from datetime import datetime - +custom_modules_path = "/var/user-packages/usr/lib/python3.7/site-packages" +if custom_modules_path not in sys.path: + sys.path.append(custom_modules_path) +import requests # metadata metadata = { @@ -40,9 +44,11 @@ WASTE_LABWARE = 'nest 1 reservoir plate' ELUTION_LABWARE = 'opentrons aluminum nest plate' DISPENSE_BEADS = False +REUSE_TIPS = True LANGUAGE = 'esp' RESET_TIPCOUNT = False - +PROTOCOL_ID = "0000-AA" +URL = 'localhost' # End Parameters to adapt the protocol ## global vars @@ -88,7 +94,6 @@ # Config variables ACTION = "StationB-protocol1-extraction" -PROTOCOL_ID = "0000-AA" # Constants REAGENT_LW_DICT = { @@ -136,6 +141,29 @@ } # Function definitions + +def write_to_error_log (info, reason): + date = datetime.now().strftime("%Y_%m_%d") + folder_date = os.path.join('/data/logs', date) + time_now = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + json_file = time_now + '.json' + folder_file_name = os.path.join(folder_date, json_file) + folder_error_log = os.path.join(folder_date,'error.log') + if not os.path.exists(folder_date): + try: + os.makedirs(folder_date) + except: + return + try: + # Create a new file for dumping json data + with open (folder_file_name , 'w') as fh: + json.dump(info, fh, indent=4) + # Append status reason code to the log + with open(folder_error_log, 'a') as fh: + fh.write( time_now + ' Unable to accept the requests get error : '+ reason + '\n') + except: + return + def run_info(start,end,parameters = dict()): info = {} hostname = subprocess.run( @@ -150,9 +178,20 @@ def run_info(start,end,parameters = dict()): info["StartRunTime"] = start info["FinishRunTime"] = end info["parameters"] = parameters - # write json to file. This is going to be an api post. - #with open('run.json', 'w') as fp: - #json.dump(info, fp,indent=4) + + headers = {'Content-type': 'application/json'} + url_https = 'https://' + URL + url_http = 'http://' + URL + try: + r = requests.post(url_https, data=json.dumps(info), headers=headers) + except: + try: + r = requests.post(url_http, data=json.dumps(info), headers=headers) + except: + write_to_error_log(info, 'Server communication error') + return + if r.status_code > 201 : + write_to_error_log(info, str(r.status_code)) def check_door(): return gpio.read_window_switches() @@ -280,7 +319,7 @@ def mix_beads(reps, dests, pip, tiprack): pip.aspirate(20, m.top(-2)) drop(pip) -def dispense_beads(sources,dests,pip,tiprack): +def dispense_beads(reps,sources,dests,pip,tiprack): ## Mix beads prior to dispensing. pick_up(pip,tiprack) for s in sources: @@ -295,7 +334,7 @@ def dispense_beads(sources,dests,pip,tiprack): drop(pip) pick_up(pip,tiprack) pip.transfer(200, dests[i//3], m.bottom(5), new_tip='never', air_gap=20) - mix_beads(7, dests, pip, tiprack) + mix_beads(reps, dests, pip, tiprack) def remove_supernatant(sources,waste,pip,tiprack): for i, m in enumerate(sources): @@ -305,15 +344,66 @@ def remove_supernatant(sources,waste,pip,tiprack): pip.blow_out(waste) drop(pip) +def wash_reuse(wash_sets,dests,waste,magdeck,pip,tiprack,tipreuse): + wash_num = 0 + for wash_set in wash_sets: + # transfer wash + pick_up(pip,tiprack) + for i, m in enumerate(dests): + magdeck.disengage() + wash_chan = wash_set[i//6] + pip.transfer( + 200, wash_chan.bottom(2), m.top(), new_tip='never', air_gap=20) + + # mix beads with wash + tips_loc = 0 + for i, m in enumerate(dests): + + if wash_num != 0: + if tips_loc == 0: + drop(pip) + pip.pick_up_tip(tipreuse[0].rows()[0][tips_loc]) + else: + if tips_loc != 0: + pick_up(pip,tiprack) + + dispense_default_speed = pip.flow_rate.dispense + pip.flow_rate.dispense = 1500 + pip.mix(7, 200, m.bottom(2)) + pip.flow_rate.dispense = dispense_default_speed + if wash_num != 0: + pip.return_tip(home_after=False) + else: + pip.drop_tip(tipreuse[0].rows()[0][tips_loc], home_after=False) + tips_loc += 1 + + magdeck.engage(height_from_base=MAGNET_HEIGHT) + robot.delay(seconds=75, msg='Incubating on magnet for 75 seconds.') + + wash_num += 1 + + # remove supernatant + tips_loc = 0 + for i, m in enumerate(dests): + pip.pick_up_tip(tipreuse[0].rows()[0][tips_loc]) + aspire_default_speed = pip.flow_rate.aspirate + pip.flow_rate.aspirate = 75 + asp_loc = m.bottom(1.5) + pip.transfer(200, asp_loc, waste, new_tip='never', air_gap=20) + pip.flow_rate.aspirate = aspire_default_speed + pip.blow_out(waste) + if wash_num != 3: + pip.return_tip(home_after=False) + else: + pip.drop_tip(home_after=False) + tips_loc += 1 + def wash(wash_sets,dests,waste,magdeck,pip,tiprack): for wash_set in wash_sets: for i, m in enumerate(dests): # transfer and mix wash with beads magdeck.disengage() wash_chan = wash_set[i//6] - side = 1 if i % 2 == 0 else -1 - asp_loc = m.bottom(1.3) - disp_loc = m.bottom(5) pick_up(pip,tiprack) pip.transfer( 200, wash_chan.bottom(2), m.center(), new_tip='never', air_gap=20) @@ -357,8 +447,6 @@ def elute_samples(sources,dests,buffer,magdeck,pip,tipracks): ## Dispense elutes in pcr plate. for i, (m, e) in enumerate(zip(sources, dests)): # tranfser and mix elution buffer with beads - # side = 1 if i % 2 == 0 else -1 - # asp_loc = m.bottom(5).move(Point(x=-1*side*2)) asp_loc = m.bottom(1.5) pick_up(pip,tipracks) # transfer elution to new plate @@ -416,7 +504,7 @@ def run(ctx: protocol_api.ProtocolContext): following:\nnest 12 reservoir plate') reagent_res = robot.load_labware( - REAGENT_LW_DICT[REAGENT_LABWARE], '7', 'reagent reservoir') + REAGENT_LW_DICT[REAGENT_LABWARE], '4', 'reagent reservoir') ## TIPS # using standard tip definition despite actually using filter tips @@ -424,7 +512,12 @@ def run(ctx: protocol_api.ProtocolContext): tips300 = [ robot.load_labware( 'opentrons_96_tiprack_300ul', slot, '200µl filter tiprack') - for slot in ['2', '3', '5', '6', '9','4'] + for slot in ['2', '3', '5', '6', '9'] + ] + tipsreuse = [ + robot.load_labware( + 'opentrons_96_tiprack_300ul', slot, '200µl filter tiprack') + for slot in ['7'] ] tips1000 = [ robot.load_labware('opentrons_96_filtertiprack_1000ul', slot, @@ -457,14 +550,11 @@ def run(ctx: protocol_api.ProtocolContext): p1000.flow_rate.dispense = 1000 p1000.flow_rate.blow_out = 1000 - # start with magdeck off - magdeck.disengage() - if(DISPENSE_BEADS): # premix, transfer, and mix magnetic beads with sample ## bead dests depending on number of samples bead_dests = bead_buffer[:math.ceil(num_cols/4)] - dispense_beads(bead_dests,mag_samples_m,m300,tips300) + dispense_beads(7,bead_dests,mag_samples_m,m300,tips300) else: # Mix bead mix_beads(7, mag_samples_m,m300,tips300) @@ -476,6 +566,12 @@ def run(ctx: protocol_api.ProtocolContext): magdeck.engage(height_from_base=MAGNET_HEIGHT) robot.delay(minutes=7, msg='Incubating on magnet for 7 minutes.') + # empty trash + if NUM_SAMPLES > 48: + voice_notification('empty_trash') + robot.pause(f"Please, empty trash") + confirm_door_is_closed() + # remove supernatant with P1000 remove_supernatant(mag_samples_s,waste,p1000,tips1000) @@ -486,7 +582,10 @@ def run(ctx: protocol_api.ProtocolContext): confirm_door_is_closed() # 3x washes - wash(wash_sets,mag_samples_m,waste,magdeck,m300,tips300) + if REUSE_TIPS == True: + wash_reuse(wash_sets,mag_samples_m,waste,magdeck,m300,tips300,tipsreuse) + else: + wash(wash_sets,mag_samples_m,waste,magdeck,m300,tips300) # elute samples magdeck.disengage() diff --git a/protocols/S3/stationC_protocol1_pcr_S3.ot2.apiv2.py b/protocols/S3/stationC_protocol1_pcr_S3.ot2.apiv2.py index bc3dbf9..c9e1442 100644 --- a/protocols/S3/stationC_protocol1_pcr_S3.ot2.apiv2.py +++ b/protocols/S3/stationC_protocol1_pcr_S3.ot2.apiv2.py @@ -4,9 +4,14 @@ import time import math import os +import sys import subprocess import json from datetime import datetime +custom_modules_path = "/var/user-packages/usr/lib/python3.7/site-packages" +if custom_modules_path not in sys.path: + sys.path.append(custom_modules_path) +import requests # Metadata metadata = { @@ -32,10 +37,10 @@ TRANSFER_SAMPLES = True LANGUAGE = 'esp' RESET_TIPCOUNT = False - +PROTOCOL_ID = "0000-AA" +URL = 'localhost' # End Parameters to adapt the protocol ACTION = "StationC-protocol1-pcr" -PROTOCOL_ID = "0000-AA" ## global vars ## initialize robot object @@ -53,7 +58,7 @@ MM_LABWARE must be one of the following: opentrons plastic block - pentrons aluminum block + opentrons aluminum block covidwarriors aluminum block MMTUBE_LABWARE must be one of the following: @@ -164,7 +169,31 @@ # Function definitions -# Function definitions + + +def write_to_error_log (info, reason): + date = datetime.now().strftime("%Y_%m_%d") + folder_date = os.path.join('/data/logs', date) + time_now = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + json_file = time_now + '.json' + folder_file_name = os.path.join(folder_date, json_file) + folder_error_log = os.path.join(folder_date,'error.log') + if not os.path.exists(folder_date): + try: + os.makedirs(folder_date) + except: + return + try: + # Create a new file for dumping json data + with open (folder_file_name , 'w') as fh: + json.dump(info, fh, indent=4) + # Append status reason code to the log + with open(folder_error_log, 'a') as fh: + fh.write( time_now + ' Unable to accept the requests get error : '+ reason + '\n') + except: + return + + def run_info(start,end,parameters = dict()): info = {} hostname = subprocess.run( @@ -179,9 +208,21 @@ def run_info(start,end,parameters = dict()): info["StartRunTime"] = start info["FinishRunTime"] = end info["parameters"] = parameters - # write json to file. This is going to be an api post. - #with open('run.json', 'w') as fp: - #json.dump(info, fp,indent=4) + + headers = {'Content-type': 'application/json'} + url_https = 'https://' + URL + url_http = 'http://' + URL + try: + r = requests.post(url_https, data=json.dumps(info), headers=headers) + except: + try: + r = requests.post(url_http, data=json.dumps(info), headers=headers) + except: + write_to_error_log(info, 'Server communication error') + return + if r.status_code > 201 : + write_to_error_log(info, str(r.status_code)) + def check_door(): return gpio.read_window_switches() @@ -349,8 +390,7 @@ def homogenize_mm(mm_tube, pip, tiprack, times=5): pip.dispense(200, mm_tube.bottom(volume_height)) # blow out before dropping tip pip.blow_out(mm_tube.top(-2)) - if VOLUME_MMIX < 20: - drop(pip) + drop(pip) def prepare_mastermix(mm_rack, p300, p20,tiprack300,tiprack20): # setup mastermix coordinates @@ -417,6 +457,7 @@ def transfer_mastermix(mm_tube, dests, p300, p20, tiprack300, tiprack20): # get initial fluid height to avoid overflowing mm when aspiring mm_volume = VOLUME_MMIX * NUM_SAMPLES volume_height = get_mm_height(mm_volume) + dest_count = 0 for set in dest_sets: # check height and if it is low enought, aim for the bottom if volume_height < 5: @@ -430,7 +471,12 @@ def transfer_mastermix(mm_tube, dests, p300, p20, tiprack300, tiprack20): pip.distribute(VOLUME_MMIX, disp_loc, [d.bottom(2) for d in set], air_gap=1, disposal_volume=0, new_tip='never') pip.blow_out(disp_loc) - drop(pip) + dest_count += 1 + if (dest_count % 3 == 0) and pip == p20: + drop(pip) + pick_up(pip,tiprack) + if pip.hw_pipette['has_tip']: + drop(pip) def transfer_samples(sources, dests, pip,tiprack): # height for aspiration has to be different depending if you ar useing tubes or wells @@ -454,7 +500,6 @@ def transfer_samples(sources, dests, pip,tiprack): # RUN PROTOCOL def run(ctx: protocol_api.ProtocolContext): global robot - global tip_log # Set robot as global var robot = ctx @@ -471,10 +516,7 @@ def run(ctx: protocol_api.ProtocolContext): start_time = start_run() # define tips - tips20 = [ - robot.load_labware('opentrons_96_filtertiprack_20ul', slot) - for slot in ['6', '9', '8', '7'] - ] + tips20 = [robot.load_labware('opentrons_96_filtertiprack_20ul', '6')] tips300 = [robot.load_labware('opentrons_96_filtertiprack_200ul', '3')] # define pipettes @@ -548,7 +590,9 @@ def run(ctx: protocol_api.ProtocolContext): if TRANSFER_MASTERMIX: transfer_mastermix(mm_tube, dests, p300, p20, tips300, tips20) if TRANSFER_SAMPLES: - robot.pause(f"Please, check that all wells have received the right ammount of mastermix") + robot.comment(f"Please, check that all wells have received the right ammount of mastermix") + if not robot.is_simulating(): + robot.pause() # transfer samples to corresponding locations if TRANSFER_SAMPLES: diff --git a/protocols/S3/stationC_protocol2_pcrmulti_S3.ot2.apiv2.py b/protocols/S3/stationC_protocol2_pcrmulti_S3.ot2.apiv2.py new file mode 100644 index 0000000..7450d09 --- /dev/null +++ b/protocols/S3/stationC_protocol2_pcrmulti_S3.ot2.apiv2.py @@ -0,0 +1,456 @@ +from opentrons import protocol_api +from opentrons.types import Point +from opentrons.drivers.rpi_drivers import gpio +import time +import math +import os +import sys +import subprocess +import json +from datetime import datetime +custom_modules_path = "/var/user-packages/usr/lib/python3.7/site-packages" +if custom_modules_path not in sys.path: + sys.path.append(custom_modules_path) +import requests + + +# Metadata +metadata = { + 'protocolName': 'S3 Station C Protocol 2 pcrmulti Version 1', + 'author': 'Nick , Sara , Miguel ', + 'source': 'Custom Protocol Request', + 'apiLevel': '2.3' +} + +# Parameters to adapt the protocol +# Warning writing any Parameters below this line. +# It will be deleted if opentronsWeb is used. + +NUM_SAMPLES = 96 +PCR_LABWARE = 'opentrons aluminum nest plate' +MM_LABWARE = 'opentrons aluminum block' +ELUTION_LABWARE = 'opentrons aluminum nest plate' +VOLUME_ELUTION = 7 +LANGUAGE = 'esp' +RESET_TIPCOUNT = False +PROTOCOL_ID = "0000-AA" +URL = "localhost" + +# End Parameters to adapt the protocol +ACTION = "StationC-protocol2-pcrmulti" + +## global vars +## initialize robot object +robot = None +# default var for drop tip switching +switch = True +# initialize tip_log dictionary +tip_log = {} +tip_log['count'] = {} +tip_log['tips'] = {} +tip_log['max'] = {} + +""" +NUM_SAMPLES is the number of samples, must be an integer number + +MM_LABWARE must be one of the following: + opentrons plastic block + pentrons aluminum block + covidwarriors aluminum block + +PCR_LABWARE must be one of the following: + opentrons aluminum biorad plate + opentrons aluminum nest plate + opentrons aluminum strip short + covidwarriors aluminum biorad plate + covidwarriors aluminum biorad strip short + +ELUTION_LABWARE must be one of the following: + opentrons plastic 2ml tubes + opentrons plastic 1.5ml tubes + opentrons aluminum 2ml tubes + opentrons aluminum 1.5ml tubes + covidwarriors aluminum 2ml tubes + covidwarriors aluminum 1.5ml tubes + opentrons aluminum biorad plate + opentrons aluminum nest plate + covidwarriors aluminum biorad plate + opentrons aluminum strip alpha + opentrons aluminum strip short + covidwarriors aluminum biorad strip alpha + covidwarriors aluminum biorad strip short + +""" + +# Constants +MM_LW_DICT = { + 'opentrons plastic block': 'opentrons_24_tuberack_generic_2ml_screwcap', + 'opentrons aluminum block': 'opentrons_24_aluminumblock_generic_2ml_screwcap', + 'covidwarriors aluminum block': 'covidwarriors_aluminumblock_24_screwcap_2000ul' +} + +PCR_LW_DICT = { + 'opentrons aluminum biorad plate': 'opentrons_96_aluminumblock_biorad_wellplate_200ul', + 'opentrons aluminum nest plate': 'opentrons_96_aluminumblock_nest_wellplate_100ul', + 'opentrons aluminum strip short': 'opentrons_aluminumblock_96_pcrstrips_100ul', + 'covidwarriors aluminum biorad plate': 'covidwarriors_aluminumblock_96_bioradwellplate_200ul', + 'covidwarriors aluminum biorad strip short': 'covidwarriors_aluminumblock_96_bioradwellplate_pcrstrips_100ul' +} + +EL_LW_DICT = { + # PCR plate + 'opentrons aluminum biorad plate': 'opentrons_96_aluminumblock_biorad_wellplate_200ul', + 'opentrons aluminum nest plate': 'opentrons_96_aluminumblock_nest_wellplate_100ul', + 'covidwarriors aluminum biorad plate': 'covidwarriors_aluminumblock_96_bioradwellplate_200ul', + # Strips + #'large strips': 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', + #'short strips': 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', + 'opentrons aluminum strip alpha': 'opentrons_aluminumblock_96_pcrstripsalpha_200ul', + 'opentrons aluminum strip short': 'opentrons_aluminumblock_96_pcrstrips_100ul', + 'covidwarriors aluminum biorad strip alpha': 'covidwarriors_aluminumblock_96_bioradwellplate_pcrstripsalpha_200ul', + 'covidwarriors aluminum biorad strip short': 'covidwarriors_aluminumblock_96_bioradwellplate_pcrstrips_100ul' +} + +LANGUAGE_DICT = { + 'esp': 'esp', + 'eng': 'eng' +} + +if LANGUAGE_DICT[LANGUAGE] == 'eng': + VOICE_FILES_DICT = { + 'start': './data/sounds/started_process.mp3', + 'finish': './data/sounds/finished_process.mp3', + 'close_door': './data/sounds/close_door.mp3', + 'replace_tipracks': './data/sounds/replace_tipracks.mp3', + 'empty_trash': './data/sounds/empty_trash.mp3' + } +elif LANGUAGE_DICT[LANGUAGE] == 'esp': + VOICE_FILES_DICT = { + 'start': './data/sounds/started_process_esp.mp3', + 'finish': './data/sounds/finished_process_esp.mp3', + 'close_door': './data/sounds/close_door_esp.mp3', + 'replace_tipracks': './data/sounds/replace_tipracks_esp.mp3', + 'empty_trash': './data/sounds/empty_trash_esp.mp3' + } + + +# Function definitions + +def write_to_error_log (info, reason): + date = datetime.now().strftime("%Y_%m_%d") + folder_date = os.path.join('/data/logs', date) + time_now = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") + json_file = time_now + '.json' + folder_file_name = os.path.join(folder_date, json_file) + folder_error_log = os.path.join(folder_date,'error.log') + if not os.path.exists(folder_date): + try: + os.makedirs(folder_date) + except: + return + try: + with open (folder_file_name , 'w') as fh: + json.dump(info, fh, indent=4) + with open(folder_error_log, 'a') as fh: + fh.write( time_now + ' Unable to accept the requests get error : '+ reason + '\n') + except: + return + +def run_info(start,end,parameters = dict()): + info = {} + hostname = subprocess.run( + ['hostname'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ).stdout.decode('utf-8') + + info["RobotID"] = hostname + info["executedAction"] = ACTION + info["ProtocolID"] = PROTOCOL_ID + info["StartRunTime"] = start + info["FinishRunTime"] = end + info["parameters"] = parameters + + headers = {'Content-type': 'application/json'} + url_https = 'https://' + URL + url_http = 'http://' + URL + try: + r = requests.post(url_https, data=json.dumps(info), headers=headers) + except: + try: + r = requests.post(url_http, data=json.dumps(info), headers=headers) + except: + write_to_error_log(info, 'Server communication error') + return + if r.status_code > 201 : + write_to_error_log(info, str(r.status_code)) + +def check_door(): + return gpio.read_window_switches() + +def confirm_door_is_closed(): + if not robot.is_simulating(): + #Check if door is opened + if check_door() == False: + #Set light color to red and pause + gpio.set_button_light(1,0,0) + robot.pause() + voice_notification('close_door') + time.sleep(5) + confirm_door_is_closed() + else: + #Set light color to green + gpio.set_button_light(0,1,0) + +def start_run(): + voice_notification('start') + gpio.set_button_light(0,1,0) + now = datetime.now() + # dd/mm/YY H:M:S + start_time = now.strftime("%Y/%m/%d %H:%M:%S") + return start_time + +def finish_run(): + voice_notification('finish') + #Set light color to blue + gpio.set_button_light(0,0,1) + now = datetime.now() + # dd/mm/YY H:M:S + finish_time = now.strftime("%Y/%m/%d %H:%M:%S") + return finish_time + +def voice_notification(action): + if not robot.is_simulating(): + fname = VOICE_FILES_DICT[action] + if os.path.isfile(fname) is True: + subprocess.run( + ['mpg123', fname], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + else: + robot.comment(f"Sound file does not exist. Call the technician") + +def reset_tipcount(file_path = '/data/C/tip_log.json'): + if os.path.isfile(file_path): + os.remove(file_path) + +def retrieve_tip_info(pip,tipracks,file_path = '/data/C/tip_log.json'): + global tip_log + if not tip_log['count'] or pip not in tip_log['count']: + tip_log['count'][pip] = 0 + if not robot.is_simulating(): + if os.path.isfile(file_path): + with open(file_path) as json_file: + data = json.load(json_file) + if 'P1000' in str(pip): + tip_log['count'][pip] = data['tips1000'] + elif 'P300' in str(pip): + tip_log['count'][pip] = data['tips300'] + elif 'P20' in str(pip): + tip_log['count'][pip] = data['tips20'] + + if "8-Channel" in str(pip): + tip_log['tips'][pip] = [tip for rack in tipracks for tip in rack.rows()[0]] + else: + tip_log['tips'][pip] = [tip for rack in tipracks for tip in rack.wells()] + + tip_log['max'][pip] = len(tip_log['tips'][pip]) + + return tip_log + +def save_tip_info(file_path = '/data/C/tip_log.json'): + data = {} + if not robot.is_simulating(): + if os.path.isfile(file_path): + os.rename(file_path,file_path + ".bak") + for pip in tip_log['count']: + if "P1000" in str(pip): + data['tips1000'] = tip_log['count'][pip] + elif "P300" in str(pip): + data['tips300'] = tip_log['count'][pip] + elif "P20" in str(pip): + data['tips20'] = tip_log['count'][pip] + + with open(file_path, 'a+') as outfile: + json.dump(data, outfile) + +def pick_up(pip,tiprack): + if tip_log['count'][pip] == tip_log['max'][pip]: + voice_notification('replace_tipracks') + robot.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ +resuming.') + confirm_door_is_closed() + pip.reset_tipracks() + tip_log['count'][pip] = 0 + pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]]) + tip_log['count'][pip] += 1 + +def drop(pip): + global switch + if "8-Channel" not in str(pip): + side = 1 if switch else -1 + drop_loc = robot.loaded_labwares[12].wells()[0].top().move(Point(x=side*20)) + pip.drop_tip(drop_loc,home_after=False) + switch = not switch + else: + drop_loc = robot.loaded_labwares[12].wells()[0].top().move(Point(x=20)) + pip.drop_tip(drop_loc,home_after=False) + +def get_source_dest_coordinates(source_racks, pcr_plate): + # Keep floor + num_cols = math.floor(NUM_SAMPLES/8) + # Keep the remaining samples + num_samples_m = int(num_cols*8) + + if 'strip' in ELUTION_LABWARE: + if NUM_SAMPLES > 8: + sources_m = [ + col + for i, rack in enumerate(source_racks) + for col in [ + rack.columns()[c] if i < 2 else rack.columns()[c+1] + for c in [0, 5, 10] + ] + ][:num_cols] + + sources_s = [ + well + for i, rack in enumerate(source_racks) + for col in [ + rack.columns()[c] if i < 2 else rack.columns()[c+1] + for c in [0, 5, 10] + ] + for well in col + ][num_samples_m:NUM_SAMPLES] + + elif 'plate' in ELUTION_LABWARE: + sources_m = source_racks.rows()[0][:num_cols] + sources_s = source_racks.wells()[num_samples_m:NUM_SAMPLES] + + dests_m = pcr_plate.rows()[0][:num_cols] + dests_s = pcr_plate.wells()[num_samples_m:NUM_SAMPLES] + return sources_m, sources_s, dests_m, dests_s + +def transfer_samples(sources, dests, pip,tiprack): + # height for aspiration has to be different depending if you ar using tubes or wells + if 'strip' in ELUTION_LABWARE or 'plate' in ELUTION_LABWARE: + height = 1.5 + else: + height = 1 + # transfer + for s, d in zip(sources, dests): + # Skip for negative control, position NUM_SAMPLES-2 + #if s == sources[NUM_SAMPLES-2]: + # continue + + pick_up(pip,tiprack) + pip.transfer(VOLUME_ELUTION, s.bottom(height), d.bottom(2), air_gap=2, new_tip='never') + #p20.mix(1, 10, d.bottom(2)) + #p20.blow_out(d.top(-2)) + pip.aspirate(1, d.top(-2)) + drop(pip) + +# RUN PROTOCOL +def run(ctx: protocol_api.ProtocolContext): + global robot + + # Set robot as global var + robot = ctx + + # check if tipcount is being reset + if RESET_TIPCOUNT: + reset_tipcount() + + # confirm door is closed + robot.comment(f"Please, close the door") + confirm_door_is_closed() + + # Begin run + start_time = start_run() + + # define tips + tips20 = [ + robot.load_labware('opentrons_96_filtertiprack_20ul', 6) + ] + tipsm20 = [ + robot.load_labware('opentrons_96_filtertiprack_20ul', 3) + ] + + if MM_LABWARE not in MM_LW_DICT: + raise Exception('Invalid MM_LABWARE. Must be one of the following:\n' + '\n'.join(list(MM_LW_DICT.keys()))) + + + # load mastermix labware + mm_rack = robot.load_labware( + MM_LW_DICT[MM_LABWARE], '11', + MM_LABWARE) + + + # define pipettes + p20 = robot.load_instrument('p20_single_gen2', 'right', tip_racks=tips20) + m20 = robot.load_instrument('p20_multi_gen2', 'left', tip_racks=tipsm20) + + ## retrieve tip_log + retrieve_tip_info(p20,tips20) + retrieve_tip_info(m20,tipsm20) + + # tempdeck module + tempdeck = robot.load_module('tempdeck', '10') + tempdeck.set_temperature(4) + + # check pcr plate + if PCR_LABWARE not in PCR_LW_DICT: + raise Exception('Invalid PCR_LABWARE. Must be one of the following:\n' + '\n'.join(list(PCR_LW_DICT.keys()))) + + + # load pcr plate + pcr_plate = tempdeck.load_labware( + PCR_LW_DICT[PCR_LABWARE], 'PCR plate') + + # check source (elution) labware type + if ELUTION_LABWARE not in EL_LW_DICT: + raise Exception('Invalid ELUTION_LABWARE. Must be one of the following:\n' + '\n'.join(list(EL_LW_DICT.keys()))) + + # load elution labware + if 'plate' in ELUTION_LABWARE: + source_racks = robot.load_labware( + EL_LW_DICT[ELUTION_LABWARE], '1', + 'RNA elution labware') + else: + source_racks = [ + robot.load_labware(EL_LW_DICT[ELUTION_LABWARE], slot, + 'RNA elution labware ' + str(i+1)) + for i, slot in enumerate(['4', '1', '5', '2']) + ] + + # setup sample sources and destinations + sources_m, sources_s, dests_m, dests_s = get_source_dest_coordinates(source_racks, pcr_plate) + + # transfer samples to corresponding locations + if NUM_SAMPLES > 8: + transfer_samples(sources_m,dests_m,m20,tipsm20) + transfer_samples(sources_s, dests_s, p20,tips20) + + # transfer negative control to position NUM_SAMPLES-2 + pick_up(p20, tips20) + dests = pcr_plate.wells()[:NUM_SAMPLES] + p20.transfer(VOLUME_ELUTION, mm_rack.wells()[4].bottom(1), dests[NUM_SAMPLES-2].bottom(2), air_gap=2, new_tip='never') + drop(p20) + + # track final used tip + save_tip_info() + + finish_time = finish_run() + + par = { + "NUM_SAMPLES" : NUM_SAMPLES, + "MM_LABWARE" : MM_LABWARE, + "PCR_LABWARE" : PCR_LABWARE, + "ELUTION_LABWARE" : ELUTION_LABWARE, + "VOLUME_ELUTION" : VOLUME_ELUTION, + "LANGUAGE" : LANGUAGE, + "RESET_TIPCOUNT" : RESET_TIPCOUNT + } + + run_info(start_time, finish_time, par) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a58b43c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +opentrons==3.17.0 +requests diff --git a/tests/par_tests/stationA_prot1_comb1_pars.json b/tests/par_tests/stationA_prot1_comb1_pars.json new file mode 100644 index 0000000..ac43b70 --- /dev/null +++ b/tests/par_tests/stationA_prot1_comb1_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BUFFER_LABWARE" : "opentrons plastic 30ml tubes", + "DESTINATION_LABWARE" : "opentrons plastic 2ml tubes", + "DEST_TUBE" : "2ml tubes", + "VOLUME_BUFFER" : 300, + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-test" , + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot1_comb2_pars.json b/tests/par_tests/stationA_prot1_comb2_pars.json new file mode 100644 index 0000000..08fc2a1 --- /dev/null +++ b/tests/par_tests/stationA_prot1_comb2_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BUFFER_LABWARE" : "opentrons plastic 30ml tubes", + "DESTINATION_LABWARE" : "opentrons plastic 2ml tubes", + "DEST_TUBE" : "2ml tubes", + "VOLUME_BUFFER" : 300, + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "True", + "PROTOCOL_ID" : "0000-test", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot1_comb3_pars.json b/tests/par_tests/stationA_prot1_comb3_pars.json new file mode 100644 index 0000000..5f2c441 --- /dev/null +++ b/tests/par_tests/stationA_prot1_comb3_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BUFFER_LABWARE" : "opentrons plastic 50ml tubes", + "DESTINATION_LABWARE" : "opentrons plastic 2ml tubes", + "DEST_TUBE" : "2ml tubes", + "VOLUME_BUFFER" : 300, + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-test" , + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot1_comb4_pars.json b/tests/par_tests/stationA_prot1_comb4_pars.json new file mode 100644 index 0000000..d4fa155 --- /dev/null +++ b/tests/par_tests/stationA_prot1_comb4_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BUFFER_LABWARE" : "opentrons plastic 50ml tubes", + "DESTINATION_LABWARE" : "opentrons plastic 2ml tubes", + "DEST_TUBE" : "2ml tubes", + "VOLUME_BUFFER" : 300, + "LANGUAGE" : "eng", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-test", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot2_comb1_pars.json b/tests/par_tests/stationA_prot2_comb1_pars.json new file mode 100644 index 0000000..7727459 --- /dev/null +++ b/tests/par_tests/stationA_prot2_comb1_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BEADS_LABWARE" : "opentrons plastic 30ml tubes", + "PLATE_LABWARE" : "nest deep generic well plate", + "VOLUME_BEADS" : 410, + "DILUTE_BEADS" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot2_comb2_pars.json b/tests/par_tests/stationA_prot2_comb2_pars.json new file mode 100644 index 0000000..297fd73 --- /dev/null +++ b/tests/par_tests/stationA_prot2_comb2_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BEADS_LABWARE" : "opentrons plastic 50ml tubes", + "PLATE_LABWARE" : "nest deep generic well plate", + "VOLUME_BEADS" : 410, + "DILUTE_BEADS" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot2_comb3_pars.json b/tests/par_tests/stationA_prot2_comb3_pars.json new file mode 100644 index 0000000..0d8cda0 --- /dev/null +++ b/tests/par_tests/stationA_prot2_comb3_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BEADS_LABWARE" : "opentrons plastic 30ml tubes", + "PLATE_LABWARE" : "opentrons deep generic well plate", + "VOLUME_BEADS" : 410, + "DILUTE_BEADS" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot2_comb4_pars.json b/tests/par_tests/stationA_prot2_comb4_pars.json new file mode 100644 index 0000000..24375ad --- /dev/null +++ b/tests/par_tests/stationA_prot2_comb4_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BEADS_LABWARE" : "opentrons plastic 30ml tubes", + "PLATE_LABWARE" : "vwr deep generic well plate", + "VOLUME_BEADS" : 410, + "DILUTE_BEADS" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot2_comb5_pars.json b/tests/par_tests/stationA_prot2_comb5_pars.json new file mode 100644 index 0000000..6fd7bd6 --- /dev/null +++ b/tests/par_tests/stationA_prot2_comb5_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BEADS_LABWARE" : "opentrons plastic 30ml tubes", + "PLATE_LABWARE" : "ecogen deep generic well plate", + "VOLUME_BEADS" : 410, + "DILUTE_BEADS" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot2_comb6_pars.json b/tests/par_tests/stationA_prot2_comb6_pars.json new file mode 100644 index 0000000..c18c633 --- /dev/null +++ b/tests/par_tests/stationA_prot2_comb6_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BEADS_LABWARE" : "opentrons plastic 30ml tubes", + "PLATE_LABWARE" : "nest deep generic well plate", + "VOLUME_BEADS" : 410, + "DILUTE_BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot2_comb7_pars.json b/tests/par_tests/stationA_prot2_comb7_pars.json new file mode 100644 index 0000000..b446dbd --- /dev/null +++ b/tests/par_tests/stationA_prot2_comb7_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "BEADS_LABWARE" : "opentrons plastic 30ml tubes", + "PLATE_LABWARE" : "nest deep generic well plate", + "VOLUME_BEADS" : 410, + "DILUTE_BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "True", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot3_comb1_pars.json b/tests/par_tests/stationA_prot3_comb1_pars.json new file mode 100644 index 0000000..18c758a --- /dev/null +++ b/tests/par_tests/stationA_prot3_comb1_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "LYSATE_LABWARE" : "opentrons plastic 2ml tubes", + "PLATE_LABWARE" : "nest deep generic well plate", + "VOLUME_LYSATE" : 400, + "BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot3_comb2_pars.json b/tests/par_tests/stationA_prot3_comb2_pars.json new file mode 100644 index 0000000..dfd73af --- /dev/null +++ b/tests/par_tests/stationA_prot3_comb2_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "LYSATE_LABWARE" : "opentrons plastic 2ml tubes", + "PLATE_LABWARE" : "opentrons deep generic well plate", + "VOLUME_LYSATE" : 400, + "BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot3_comb3_pars.json b/tests/par_tests/stationA_prot3_comb3_pars.json new file mode 100644 index 0000000..18c758a --- /dev/null +++ b/tests/par_tests/stationA_prot3_comb3_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "LYSATE_LABWARE" : "opentrons plastic 2ml tubes", + "PLATE_LABWARE" : "nest deep generic well plate", + "VOLUME_LYSATE" : 400, + "BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot3_comb4_pars.json b/tests/par_tests/stationA_prot3_comb4_pars.json new file mode 100644 index 0000000..dc7ef23 --- /dev/null +++ b/tests/par_tests/stationA_prot3_comb4_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "LYSATE_LABWARE" : "opentrons plastic 2ml tubes", + "PLATE_LABWARE" : "vwr deep generic well plate", + "VOLUME_LYSATE" : 400, + "BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot3_comb5_pars.json b/tests/par_tests/stationA_prot3_comb5_pars.json new file mode 100644 index 0000000..fd05de6 --- /dev/null +++ b/tests/par_tests/stationA_prot3_comb5_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "LYSATE_LABWARE" : "opentrons plastic 2ml tubes", + "PLATE_LABWARE" : "ecogen deep generic well plate", + "VOLUME_LYSATE" : 400, + "BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot3_comb6_pars.json b/tests/par_tests/stationA_prot3_comb6_pars.json new file mode 100644 index 0000000..813897b --- /dev/null +++ b/tests/par_tests/stationA_prot3_comb6_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "LYSATE_LABWARE" : "opentrons plastic 2ml tubes", + "PLATE_LABWARE" : "ecogen deep generic well plate", + "VOLUME_LYSATE" : 400, + "BEADS" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot3_comb7_pars.json b/tests/par_tests/stationA_prot3_comb7_pars.json new file mode 100644 index 0000000..d5d2482 --- /dev/null +++ b/tests/par_tests/stationA_prot3_comb7_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "LYSATE_LABWARE" : "opentrons plastic 2ml tubes", + "PLATE_LABWARE" : "ecogen deep generic well plate", + "VOLUME_LYSATE" : 400, + "BEADS" : "True", + "LANGUAGE" : "eng", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationA_prot3_comb8_pars.json b/tests/par_tests/stationA_prot3_comb8_pars.json new file mode 100644 index 0000000..b27dfaa --- /dev/null +++ b/tests/par_tests/stationA_prot3_comb8_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "LYSATE_LABWARE" : "opentrons plastic 2ml tubes", + "PLATE_LABWARE" : "ecogen deep generic well plate", + "VOLUME_LYSATE" : 400, + "BEADS" : "True", + "LANGUAGE" : "eng", + "RESET_TIPCOUNT" : "True", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb1_pars.json b/tests/par_tests/stationB_prot1_comb1_pars.json new file mode 100644 index 0000000..3d48031 --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb1_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "opentrons deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "DISPENSE_BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "REUSE_TIPS" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb2_pars.json b/tests/par_tests/stationB_prot1_comb2_pars.json new file mode 100644 index 0000000..83ac862 --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb2_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "nest deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "DISPENSE_BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "REUSE_TIPS" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb3_pars.json b/tests/par_tests/stationB_prot1_comb3_pars.json new file mode 100644 index 0000000..67da44e --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb3_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "vwr deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "DISPENSE_BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "REUSE_TIPS" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb4_pars.json b/tests/par_tests/stationB_prot1_comb4_pars.json new file mode 100644 index 0000000..573dcee --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb4_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "nest deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum biorad plate", + "DISPENSE_BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "REUSE_TIPS" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb5_pars.json b/tests/par_tests/stationB_prot1_comb5_pars.json new file mode 100644 index 0000000..29a8d64 --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb5_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "ecogen deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum biorad plate", + "DISPENSE_BEADS" : "False", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "REUSE_TIPS" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb6_pars.json b/tests/par_tests/stationB_prot1_comb6_pars.json new file mode 100644 index 0000000..5052ab0 --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb6_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "nest deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum biorad plate", + "DISPENSE_BEADS" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "REUSE_TIPS" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb7_pars.json b/tests/par_tests/stationB_prot1_comb7_pars.json new file mode 100644 index 0000000..3d5d2b6 --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb7_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "nest deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum biorad plate", + "DISPENSE_BEADS" : "True", + "LANGUAGE" : "eng", + "RESET_TIPCOUNT" : "False", + "REUSE_TIPS" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb8_pars.json b/tests/par_tests/stationB_prot1_comb8_pars.json new file mode 100644 index 0000000..91a30b3 --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb8_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "nest deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum biorad plate", + "DISPENSE_BEADS" : "True", + "LANGUAGE" : "eng", + "RESET_TIPCOUNT" : "True", + "REUSE_TIPS" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationB_prot1_comb9_pars.json b/tests/par_tests/stationB_prot1_comb9_pars.json new file mode 100644 index 0000000..8e14404 --- /dev/null +++ b/tests/par_tests/stationB_prot1_comb9_pars.json @@ -0,0 +1,13 @@ +{ + "NUM_SAMPLES" : 96, + "REAGENT_LABWARE" : "nest 12 reservoir plate", + "MAGPLATE_LABWARE" : "nest deep generic well plate", + "WASTE_LABWARE" : "nest 1 reservoir plate", + "ELUTION_LABWARE" : "opentrons aluminum biorad plate", + "DISPENSE_BEADS" : "True", + "LANGUAGE" : "eng", + "RESET_TIPCOUNT" : "True", + "REUSE_TIPS" : "True", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb10_pars.json b/tests/par_tests/stationC_prot1_comb10_pars.json new file mode 100644 index 0000000..9afe45c --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb10_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "covidwarriors aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum nest plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "False", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb1_pars.json b/tests/par_tests/stationC_prot1_comb1_pars.json new file mode 100644 index 0000000..5d60fb1 --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb1_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "opentrons aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum nest plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb2_pars.json b/tests/par_tests/stationC_prot1_comb2_pars.json new file mode 100644 index 0000000..60ebf37 --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb2_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "opentrons plastic block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum nest plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb3_pars.json b/tests/par_tests/stationC_prot1_comb3_pars.json new file mode 100644 index 0000000..0c7b524 --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb3_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "covidwarriors aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum nest plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb4_pars.json b/tests/par_tests/stationC_prot1_comb4_pars.json new file mode 100644 index 0000000..0e5b641 --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb4_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "covidwarriors aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum biorad plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb5_pars.json b/tests/par_tests/stationC_prot1_comb5_pars.json new file mode 100644 index 0000000..f08bc89 --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb5_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "covidwarriors aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum strip short", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb6_pars.json b/tests/par_tests/stationC_prot1_comb6_pars.json new file mode 100644 index 0000000..497a855 --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb6_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "covidwarriors aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum nest plate", + "ELUTION_LABWARE" : "opentrons plastic 2ml tubes", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb7_pars.json b/tests/par_tests/stationC_prot1_comb7_pars.json new file mode 100644 index 0000000..16a3571 --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb7_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "covidwarriors aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum nest plate", + "ELUTION_LABWARE" : "covidwarriors aluminum 1.5ml tubes", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb8_pars.json b/tests/par_tests/stationC_prot1_comb8_pars.json new file mode 100644 index 0000000..c5a9917 --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb8_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "covidwarriors aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum nest plate", + "ELUTION_LABWARE" : "opentrons aluminum strip alpha", + "PREPARE_MASTERMIX" : "False", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot1_comb9_pars.json b/tests/par_tests/stationC_prot1_comb9_pars.json new file mode 100644 index 0000000..025904e --- /dev/null +++ b/tests/par_tests/stationC_prot1_comb9_pars.json @@ -0,0 +1,16 @@ +{ + "NUM_SAMPLES" : 96, + "MM_LABWARE" : "covidwarriors aluminum block", + "MMTUBE_LABWARE" : "2ml tubes", + "PCR_LABWARE" : "opentrons aluminum nest plate", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "PREPARE_MASTERMIX" : "True", + "MM_TYPE" : "MM1", + "VOLUME_ELUTION" : 7, + "TRANSFER_MASTERMIX" : "True", + "TRANSFER_SAMPLES" : "True", + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot2_comb1_pars.json b/tests/par_tests/stationC_prot2_comb1_pars.json new file mode 100644 index 0000000..e1b043d --- /dev/null +++ b/tests/par_tests/stationC_prot2_comb1_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "PCR_LABWARE" : "opentrons aluminum nest plate", + "MM_LABWARE" : "opentrons aluminum block", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "VOLUME_ELUTION" : 7, + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot2_comb2_pars.json b/tests/par_tests/stationC_prot2_comb2_pars.json new file mode 100644 index 0000000..9075038 --- /dev/null +++ b/tests/par_tests/stationC_prot2_comb2_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "PCR_LABWARE" : "opentrons aluminum strip short", + "MM_LABWARE" : "opentrons aluminum block", + "ELUTION_LABWARE" : "opentrons aluminum nest plate", + "VOLUME_ELUTION" : 7, + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/par_tests/stationC_prot2_comb3_pars.json b/tests/par_tests/stationC_prot2_comb3_pars.json new file mode 100644 index 0000000..9a9b3ee --- /dev/null +++ b/tests/par_tests/stationC_prot2_comb3_pars.json @@ -0,0 +1,11 @@ +{ + "NUM_SAMPLES" : 96, + "PCR_LABWARE" : "opentrons aluminum strip short", + "MM_LABWARE" : "opentrons aluminum block", + "ELUTION_LABWARE" : "opentrons aluminum strip alpha", + "VOLUME_ELUTION" : 7, + "LANGUAGE" : "esp", + "RESET_TIPCOUNT" : "False", + "PROTOCOL_ID" : "0000-AA", + "URL" : "localhost" +} diff --git a/tests/run_tests.py b/tests/run_tests.py new file mode 100644 index 0000000..105f080 --- /dev/null +++ b/tests/run_tests.py @@ -0,0 +1,106 @@ +#!/usr/bin/python +import json +import os +import re +import argparse + +# Constants +OPENROBOTS_DELIMITATION_PARAMETERS_TAGS = ['# Parameters to adapt the protocol', + '# End Parameters to adapt the protocol'] + +def get_arguments(): + parser = argparse.ArgumentParser(prog = 'run_tests.py', description= 'run opentrons_covid repo tests') + + parser.add_argument('-j', '--json', dest="json", metavar="json info file", type=str, required=True, help='REQUIRED. json file with parameters.') + parser.add_argument('-t', '--template', dest="template", metavar="json info file", type=str, required=True, help='REQUIRED. Protocol template.') + parser.add_argument('-o', '--output', type=str, required=False, help='Output file to save results') + + arguments = parser.parse_args() + + return arguments + +def add_parameters_in_file (in_file, out_file, parameters): + ''' + Description: + The function will get protocol template file and add the parameters to create an output file + that is stored in OPENROBOTS_OUTPUT_DIRECTORYthe Labware information used in the form to create the files + Input: + in_file # template file + out_file # output file name + parameters # dictionnary with the information to include in the file + Constans: + OPENROBOTS_DELIMITATION_PARAMETERS_TAGS + Return: + form_data + ''' + if not os.path.exists(in_file): + return 'Protocol Template does not exists' + + with open (in_file, 'r') as in_fh: + found_start = False + delimitation_end_found = False + parameters_added = True + with open(out_file, 'w') as out_fh: + for line in in_fh: + parameter_section = re.search(rf'^{OPENROBOTS_DELIMITATION_PARAMETERS_TAGS[0]}', line) + end_parameter_section = re.search(rf'^{OPENROBOTS_DELIMITATION_PARAMETERS_TAGS[1]}', line) + if not found_start: + out_fh.write(line) + if parameter_section : + found_start = True + + for key in sorted(parameters): + type_of_data = get_type_of_data(parameters[key]) + if type_of_data == 'boolean' or type_of_data == 'integer': + out_fh.write(key + ' = '+ str(parameters[key]) + '\n') + else: + out_fh.write(key + ' = \''+ parameters[key]+ '\'\n') + parameters_added = True + continue + if end_parameter_section : + out_fh.write(line) + found_start = False + delimitation_end_found = True + + if parameters_added and delimitation_end_found : + return 'True' + return 'Unable to write the parameters in the protocol file' + +def get_type_of_data (data): + ''' + Description: + The function get always as input a string class. + By trying to convert the input data to int or bolealn it will decide the type of data + If not possible to conver it returns string + Return: + type_of_data + ''' + boolean_values = ['True', 'False', 'None'] + if data in boolean_values : + return 'boolean' + try: + integer = int(data) + return 'integer' + except: + return 'string' + +def main(): + ## get args + args = get_arguments() + print(args) + + ## load json + with open(args.json, 'r') as file: + try: + parameters=json.load(file) + except ValueError as e: + print("json decoding has failed") + print(e) + + add_parameters_in_file(args.template,args.output,parameters) + +if __name__ == '__main__': + try: + main() + except Exception as e: + print(e)