Full Code of dgretton/pyhamilton for AI

master 65938d05cc8f cached
387 files
15.2 MB
4.0M tokens
702 symbols
1 requests
Copy disabled (too large) Download .txt
Showing preview only (15,992K chars total). Download the full file to get everything.
Repository: dgretton/pyhamilton
Branch: master
Commit: 65938d05cc8f
Files: 387
Total size: 15.2 MB

Directory structure:
gitextract_3_91ea39/

├── .gitattributes
├── .gitignore
├── .pylintrc
├── .vscode/
│   └── settings.json
├── LICENSE
├── PKG-INFO
├── README.md
├── imgs/
│   ├── README.md
│   └── text
├── pandoc_pdf.sh
├── pyhamilton/
│   ├── __init__.py
│   ├── bin/
│   │   └── Hamilton MPE HSL Driver.msi
│   ├── consumables/
│   │   ├── __init__.py
│   │   └── consumables.py
│   ├── defaultcmds.py
│   ├── defaults/
│   │   └── defaults.json
│   ├── defaults.py
│   ├── devices/
│   │   ├── __init__.py
│   │   ├── centrifuge_wrappers.py
│   │   ├── hhs_wrappers.py
│   │   ├── hig_wrappers.py
│   │   ├── mpe_wrappers.py
│   │   ├── odtc_wrappers.py
│   │   ├── pH_wrappers.py
│   │   └── tec_wrappers.py
│   ├── error_code_descriptions.txt
│   ├── interface.py
│   ├── library/
│   │   ├── ASWStandard/
│   │   │   ├── ASWGlobal/
│   │   │   │   ├── ASWGlobal.hsl
│   │   │   │   └── ASWGlobal.stp
│   │   │   └── TraceLevel/
│   │   │       ├── TraceLevel.chm
│   │   │       ├── TraceLevel.chw
│   │   │       ├── TraceLevel.hsl
│   │   │       └── TraceLevel.stp
│   │   ├── Alpha Numeric Conversion/
│   │   │   ├── Alpha Numeric Conversion.hs_
│   │   │   ├── Alpha Numeric Conversion.hsi
│   │   │   ├── Alpha Numeric Conversion.smt
│   │   │   └── Alpha Numeric Conversion.stp
│   │   ├── DaisyChainedTiltModule/
│   │   │   ├── HSLDaisyChainCommunication.chm
│   │   │   └── HSLDaisyChainedTiltModule.chm
│   │   ├── ErrorSimulator/
│   │   │   ├── ErrorSimulator.hs_
│   │   │   ├── ErrorSimulator.hsi
│   │   │   ├── ErrorSimulator.smt
│   │   │   ├── ErrorSimulator.stp
│   │   │   ├── HSLFilLibEx.chm
│   │   │   ├── HSLFilLibEx.hsl
│   │   │   ├── HSLFilLibEx.stp
│   │   │   ├── MlStarSimCfg.hsl
│   │   │   ├── MlStarSimCfg.stp
│   │   │   ├── PhoenixChecksum_0.reg
│   │   │   ├── StrTokenize.hs_
│   │   │   ├── StrTokenize.hsi
│   │   │   ├── StrTokenize.smt
│   │   │   └── StrTokenize.stp
│   │   ├── HSLAppsLib/
│   │   │   ├── HSLAppsLib.chm
│   │   │   ├── HSLAppsLib.hsl
│   │   │   └── HSLAppsLib.stp
│   │   ├── HSLAppsLib.chm
│   │   ├── HSLAppsLib.chw
│   │   ├── HSLAppsLib.hsl
│   │   ├── HSLAppsLib.stp
│   │   ├── HSLBarcodeReader.hs_
│   │   ├── HSLBarcodeReader.hsl
│   │   ├── HSLBarcodeReader.stp
│   │   ├── HSLBarcodeReaderEnu.chm
│   │   ├── HSLBarcodeReaderStringTableEnu.hs_
│   │   ├── HSLBarcodeReaderStringTableEnu.stp
│   │   ├── HSLDaisyChainCommunication.chm
│   │   ├── HSLDaisyChainCommunication.hsl
│   │   ├── HSLDaisyChainCommunication.stp
│   │   ├── HSLDaisyChainedMediaLine.chm
│   │   ├── HSLDaisyChainedMediaLine.chw
│   │   ├── HSLDaisyChainedMediaLine.hsl
│   │   ├── HSLDaisyChainedMediaLine.stp
│   │   ├── HSLDaisyChainedTiltModule.chm
│   │   ├── HSLDaisyChainedTiltModule.chw
│   │   ├── HSLDaisyChainedTiltModule.hsl
│   │   ├── HSLDaisyChainedTiltModule.stp
│   │   ├── HSLInhecoTEC/
│   │   │   ├── HSLInhecoTECLib.hsl
│   │   │   ├── HSLInhecoTECLib.stp
│   │   │   └── HSLInhecoTECLibEnu.chm
│   │   ├── HSLLabwareStateLib.hsl
│   │   ├── HSLLabwareStateLib.stp
│   │   ├── HSLLabwareStateLibEnu.chm
│   │   ├── HSLLabwareStateLibImpl.hs_
│   │   ├── HSLLabwareStateLibImpl.stp
│   │   ├── HSLLabwareStateLibImplEnu.hs_
│   │   ├── HSLLabwareStateLibImplEnu.stp
│   │   ├── HSLLabwrAccess/
│   │   │   ├── HSLLabwrAccess.chm
│   │   │   ├── HSLLabwrAccess.hsl
│   │   │   └── HSLLabwrAccess.stp
│   │   ├── HSLTrcLib.hsl
│   │   ├── HSLTrcLib.stp
│   │   ├── HSL_LiquidClassLib.hsl
│   │   ├── HSL_LiquidClassLib.stp
│   │   ├── HslHamHeaterShakerLib.Instrument.stp
│   │   ├── HslHamHeaterShakerLib.stp
│   │   ├── HslHamHeaterShakerLibEnu.chw
│   │   ├── HslHamHeaterShakerStringTableEnu.hs_
│   │   ├── HslHamHeaterShakerStringTableEnu.stp
│   │   ├── Labware Properties/
│   │   │   ├── Labware Definition Keys.xlsx
│   │   │   ├── Labware_Property_Query.hs_
│   │   │   ├── Labware_Property_Query.hsi
│   │   │   ├── Labware_Property_Query.smt
│   │   │   ├── Labware_Property_Query.stp
│   │   │   └── Resources/
│   │   │       ├── LPQ_GLOBAL.hsl
│   │   │       └── LPQ_GLOBAL.stp
│   │   ├── SMT/
│   │   │   ├── MLSTARLiquidClassLib.chm
│   │   │   ├── MLSTARLiquidClassLib.hsl
│   │   │   └── MLSTARLiquidClassLib.stp
│   │   ├── SMTs/
│   │   │   ├── lookup.hs_
│   │   │   ├── lookup.hsi
│   │   │   ├── lookup.smt
│   │   │   └── lookup.stp
│   │   ├── STAR Tools/
│   │   │   ├── Channel Tools Test.lay
│   │   │   ├── Channel Tools Test.res
│   │   │   ├── Resources/
│   │   │   │   └── SubMethods/
│   │   │   │       ├── CHANNEL_TOOLS_GLOBAL.hsl
│   │   │   │       ├── CHANNEL_TOOLS_GLOBAL.stp
│   │   │   │       ├── CenterSpot.rck
│   │   │   │       ├── FW_HelperLibrary.hs_
│   │   │   │       ├── FW_HelperLibrary.hsi
│   │   │   │       ├── FW_HelperLibrary.smt
│   │   │   │       ├── FW_HelperLibrary.stp
│   │   │   │       ├── Firmware Pipetting Commands.hs_
│   │   │   │       ├── Firmware Pipetting Commands.hsi
│   │   │   │       ├── Firmware Pipetting Commands.smt
│   │   │   │       ├── Firmware Pipetting Commands.stp
│   │   │   │       ├── HelperLibrary.hs_
│   │   │   │       ├── HelperLibrary.hsi
│   │   │   │       ├── HelperLibrary.smt
│   │   │   │       ├── HelperLibrary.stp
│   │   │   │       ├── Liquid Level Check.hs_
│   │   │   │       ├── Liquid Level Check.hsi
│   │   │   │       ├── Liquid Level Check.smt
│   │   │   │       ├── Liquid Level Check.stp
│   │   │   │       ├── Plate Stack Verify.hs_
│   │   │   │       ├── Plate Stack Verify.hsi
│   │   │   │       ├── Plate Stack Verify.smt
│   │   │   │       ├── Plate Stack Verify.stp
│   │   │   │       ├── STAR Channel Movement Tools.hs_
│   │   │   │       ├── STAR Channel Movement Tools.hsi
│   │   │   │       ├── STAR Channel Movement Tools.smt
│   │   │   │       ├── STAR Channel Movement Tools.stp
│   │   │   │       ├── Split_Wells.hs_
│   │   │   │       ├── Split_Wells.hsi
│   │   │   │       ├── Split_Wells.smt
│   │   │   │       ├── Split_Wells.stp
│   │   │   │       ├── TIP_OFFSET_GLOBAL.hsl
│   │   │   │       ├── TIP_OFFSET_GLOBAL.stp
│   │   │   │       ├── Tip Tool Global Variables.hsl
│   │   │   │       ├── Tip Tool Global Variables.stp
│   │   │   │       ├── TipCounterGlobal.hsl
│   │   │   │       ├── TipCounterGlobal.stp
│   │   │   │       ├── Travel Lanes.hs_
│   │   │   │       ├── Travel Lanes.hsi
│   │   │   │       ├── Travel Lanes.smt
│   │   │   │       ├── Travel Lanes.stp
│   │   │   │       ├── VANTAGE Channel Movement Tools.hs_
│   │   │   │       ├── VANTAGE Channel Movement Tools.hsi
│   │   │   │       ├── VANTAGE Channel Movement Tools.smt
│   │   │   │       ├── VANTAGE Channel Movement Tools.stp
│   │   │   │       └── centerspot.ctr
│   │   │   ├── STAR Channel Tools.hs_
│   │   │   ├── STAR Channel Tools.hsi
│   │   │   ├── STAR Channel Tools.smt
│   │   │   ├── STAR Channel Tools.stp
│   │   │   ├── STAR MPH96 Tools.hs_
│   │   │   ├── STAR MPH96 Tools.hsi
│   │   │   ├── STAR MPH96 Tools.smt
│   │   │   ├── STAR MPH96 Tools.stp
│   │   │   ├── STAR Pipetting Tools.hs_
│   │   │   ├── STAR Pipetting Tools.hsi
│   │   │   ├── STAR Pipetting Tools.smt
│   │   │   ├── STAR Pipetting Tools.stp
│   │   │   ├── STAR Tip Tools.hs_
│   │   │   ├── STAR Tip Tools.hsi
│   │   │   ├── STAR Tip Tools.smt
│   │   │   ├── STAR Tip Tools.stp
│   │   │   ├── STAR Tools Demo.hsl
│   │   │   ├── STAR Tools Demo.med
│   │   │   ├── STAR Tools Demo.stp
│   │   │   ├── STAR Tools Demo.sub
│   │   │   ├── TipSupport.rck
│   │   │   ├── TipSupport.x
│   │   │   └── tipsupport.ctr
│   │   └── SchedulingDev/
│   │       ├── HSLAppsLib.chm
│   │       ├── HSLAppsLib.chw
│   │       ├── HSLAppsLib.hsl
│   │       └── HSLAppsLib.stp
│   ├── liquid_class_db.py
│   ├── liquid_classes.py
│   ├── liquid_handling_wrappers.py
│   ├── ngs/
│   │   ├── __init__.py
│   │   ├── loading/
│   │   │   ├── __init__.py
│   │   │   ├── deck_annotation.py
│   │   │   ├── deck_regions.json
│   │   │   ├── deck_regions_old.json
│   │   │   ├── loading_vis.py
│   │   │   ├── plate_96_render.py
│   │   │   ├── rendering_helpers.py
│   │   │   └── tube_rack_render.py
│   │   ├── protocol.py
│   │   ├── tadm.py
│   │   └── tests/
│   │       ├── PacBio_MultiPlexLibraryPrepDeck_v1.2.lay
│   │       ├── PacBio_MultiPlexLibraryPrepDeck_v1.2.res
│   │       ├── bead_cleanup.py
│   │       ├── cLLD.py
│   │       ├── consumables_tracking.py
│   │       ├── cpac.py
│   │       ├── double_aspirate.py
│   │       ├── get_liquid_class_volume.py
│   │       ├── gripper_move.py
│   │       ├── hhs.py
│   │       ├── loading/
│   │       │   ├── deck_regions.json
│   │       │   └── loading_vis.py
│   │       ├── log/
│   │       │   ├── hamilton.log
│   │       │   ├── main.log
│   │       │   └── robot_json.log
│   │       ├── magnetic_bead_cleanup.py
│   │       ├── mix_plate.py
│   │       ├── multi_dispense.py
│   │       ├── ngs_demo.py
│   │       ├── pip_transfer.py
│   │       ├── set_labware_property.py
│   │       ├── stacking.py
│   │       ├── thermal_cycler/
│   │       │   └── thermal_cycler_with_transport.py
│   │       ├── tip_support.py
│   │       ├── tip_tracker.py
│   │       ├── transfer_96.py
│   │       └── transport.py
│   ├── odtc/
│   │   ├── __init__.py
│   │   └── odtc_protocol.py
│   ├── oemerr.py
│   ├── paths.py
│   ├── pipetting/
│   │   ├── __init__.py
│   │   ├── pipetting.py
│   │   └── trough_manager.py
│   ├── resources/
│   │   ├── __init__.py
│   │   ├── deckresource.py
│   │   ├── enums.py
│   │   └── managed_resources.py
│   ├── run_venus_client.py
│   ├── star-oem/
│   │   ├── HslHamHeaterShakerLib.hs_
│   │   ├── HslHamHeaterShakerLib.hsl
│   │   ├── RunHSLExecutor.dll.config
│   │   ├── RunHSLExecutor.pdb
│   │   ├── STAR_OEM.hsl
│   │   ├── STAR_OEM.lay
│   │   ├── STAR_OEM.res
│   │   ├── STAR_OEM.stp
│   │   ├── STAR_OEM.sub
│   │   ├── STAR_OEM_toolkit.hs_
│   │   ├── STAR_OEM_toolkit.hsi
│   │   ├── STAR_OEM_toolkit.smt
│   │   ├── STAR_OEM_toolkit.stp
│   │   └── VENUS_Method/
│   │       ├── Hamilton pH Module Controller.chw
│   │       ├── Hamilton pH Module Controller.hs_
│   │       ├── Hamilton pH Module Controller.hsi
│   │       ├── Hamilton pH Module Controller.smt
│   │       ├── Hamilton pH Module Controller.stp
│   │       ├── HslHamHeaterShakerLib.chm
│   │       ├── HslHamHeaterShakerLib.chw
│   │       ├── HslHamHeaterShakerLib.hs_
│   │       ├── HslHamHeaterShakerLib.hsl
│   │       ├── STAR_OEM.hsl
│   │       ├── STAR_OEM_HiG.hs_
│   │       ├── STAR_OEM_HiG.hsi
│   │       ├── STAR_OEM_HiG.stp
│   │       ├── STAR_OEM_ODTC.hs_
│   │       ├── STAR_OEM_ODTC.hsi
│   │       ├── STAR_OEM_ODTC.stp
│   │       ├── STAR_OEM_Test.hsl
│   │       ├── STAR_OEM_Test.lay
│   │       ├── STAR_OEM_Test.med
│   │       ├── STAR_OEM_Test.res
│   │       ├── STAR_OEM_Test.stp
│   │       ├── STAR_OEM_Test.sub
│   │       ├── STAR_OEM_noFan.hsl
│   │       ├── STAR_OEM_noFan.med
│   │       ├── STAR_OEM_noFan.stp
│   │       ├── STAR_OEM_noFan.sub
│   │       ├── STAR_OEM_toolkit.hs_
│   │       ├── STAR_OEM_toolkit.hsi
│   │       ├── STAR_OEM_toolkit.smt
│   │       ├── STAR_OEM_toolkit.stp
│   │       ├── STAR_OEM_toolkit_MPE.hs_
│   │       ├── STAR_OEM_toolkit_MPE.hsi
│   │       ├── STAR_OEM_toolkit_MPE.smt
│   │       ├── STAR_OEM_toolkit_MPE.stp
│   │       ├── STAR_OEM_toolkit_centrifuge.hs_
│   │       ├── STAR_OEM_toolkit_centrifuge.hsi
│   │       ├── STAR_OEM_toolkit_centrifuge.smt
│   │       ├── STAR_OEM_toolkit_centrifuge.stp
│   │       ├── STAR_OEM_toolkit_pH.hs_
│   │       ├── STAR_OEM_toolkit_pH.hsi
│   │       ├── STAR_OEM_toolkit_pH.smt
│   │       ├── STAR_OEM_toolkit_pH.stp
│   │       ├── STAR_OEM_wFan.hsl
│   │       ├── STAR_OEM_wFan.med
│   │       ├── STAR_OEM_wFan.stp
│   │       ├── STAR_OEM_wFan.sub
│   │       ├── testWasher.hsl
│   │       ├── testWasher.med
│   │       ├── testWasher.stp
│   │       ├── testWasher.sub
│   │       ├── ~Hx1C7B.hsl
│   │       ├── ~Hx1C7B.sub
│   │       ├── ~Hx1C7B.tmp
│   │       ├── ~Hx1DE8.hsl
│   │       ├── ~Hx1DE8.sub
│   │       ├── ~Hx1DE8.tmp
│   │       ├── ~Hx2A1D.hsl
│   │       ├── ~Hx2A1D.sub
│   │       ├── ~Hx2A1D.tmp
│   │       ├── ~Hx2D5B.hsl
│   │       ├── ~Hx2D5B.sub
│   │       ├── ~Hx2D5B.tmp
│   │       ├── ~Hx305.hsl
│   │       ├── ~Hx305.sub
│   │       ├── ~Hx305.tmp
│   │       ├── ~Hx393D.hsi
│   │       ├── ~Hx393D.tmp
│   │       ├── ~Hx3D8A.hsl
│   │       ├── ~Hx3D8A.sub
│   │       ├── ~Hx3D8A.tmp
│   │       ├── ~Hx4005.hsl
│   │       ├── ~Hx4005.sub
│   │       ├── ~Hx4005.tmp
│   │       ├── ~Hx48F0.hsi
│   │       ├── ~Hx48F0.tmp
│   │       ├── ~Hx5A3E.hsl
│   │       ├── ~Hx5A3E.sub
│   │       ├── ~Hx5FF.hsl
│   │       ├── ~Hx5FF.sub
│   │       ├── ~Hx5FF.tmp
│   │       ├── ~Hx68E5.hsl
│   │       ├── ~Hx68E5.sub
│   │       ├── ~Hx68E5.tmp
│   │       ├── ~Hx7218.hsl
│   │       ├── ~Hx7218.tmp
│   │       ├── ~Hx7406.hsl
│   │       ├── ~Hx7406.sub
│   │       ├── ~Hx7A41.hsi
│   │       ├── ~Hx8511.hsl
│   │       ├── ~Hx8511.sub
│   │       ├── ~Hx8511.tmp
│   │       ├── ~Hx8AF.hsl
│   │       ├── ~Hx8AF.sub
│   │       ├── ~Hx8AF.tmp
│   │       ├── ~HxAB5D.hsl
│   │       ├── ~HxAB5D.stp
│   │       ├── ~HxAB5D.sub
│   │       ├── ~HxB3C9.hsl
│   │       ├── ~HxB3C9.sub
│   │       ├── ~HxB54F.hsi
│   │       ├── ~HxB54F.tmp
│   │       ├── ~HxC302.hsl
│   │       ├── ~HxC302.sub
│   │       ├── ~HxC302.tmp
│   │       ├── ~HxCAAD.hsl
│   │       ├── ~HxCAAD.tmp
│   │       ├── ~HxE052.hsi
│   │       ├── ~HxE052.tmp
│   │       ├── ~HxE82B.hsl
│   │       ├── ~HxE82B.sub
│   │       ├── ~HxE82B.tmp
│   │       ├── ~HxEF83.hsl
│   │       ├── ~HxEF83.sub
│   │       ├── ~HxEF83.tmp
│   │       ├── ~HxFA7A.hsi
│   │       ├── ~HxFA7A.tmp
│   │       └── ~ReAB0E.res
│   ├── templates/
│   │   ├── ai_template/
│   │   │   ├── deck.lay
│   │   │   ├── deck.res
│   │   │   ├── preprompt.py
│   │   │   ├── robot_method.py
│   │   │   └── voice.py
│   │   └── basic_template/
│   │       ├── deck.lay
│   │       ├── deck.res
│   │       └── robot_method.py
│   └── transport/
│       ├── __init__.py
│       └── transport.py
├── pytest.ini
├── requirements.txt
├── setup.cfg
├── setup.py
└── tests/
    ├── __init__.py
    └── interface_tests.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
* text eol=lf
*.lay binary
*.dll* binary
*.pdb binary
*.hsl binary
*.hsi binary
*.hs_ binary
*.sub binary
*.smt binary
*.stp binary
*.chm binary
*.chw binary
*.ico binary
*.med binary
*.res binary
*.png binary
*.bmp binary
*.exe filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text


================================================
FILE: .gitignore
================================================
*.pyc
*__pycache__
*.DS_store
*.swp
pyhamilton/LAY-BACKUP
dist
*.egg-info
.ipynb_checkpoints
build/
dist/
*.egg-info/
**/tadm_report_*.html


================================================
FILE: .pylintrc
================================================
# This Pylint rcfile contains a best-effort configuration to uphold the
# best-practices and style described in the Google Python style guide:
#   https://google.github.io/styleguide/pyguide.html
#
# Its canonical open-source location is:
#   https://google.github.io/styleguide/pylintrc

[MASTER]

# Files or directories to be skipped. They should be base names, not paths.
ignore=third_party

# Files or directories matching the regex patterns are skipped. The regex
# matches against base names, not paths.
ignore-patterns=

# Pickle collected data for later comparisons.
persistent=no

# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=

# Use multiple processes to speed up Pylint.
jobs=4

# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no


[MESSAGES CONTROL]

# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
#enable=

# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=abstract-method,
        apply-builtin,
        arguments-differ,
        attribute-defined-outside-init,
        backtick,
        bad-option-value,
        basestring-builtin,
        buffer-builtin,
        c-extension-no-member,
        consider-using-enumerate,
        cmp-builtin,
        cmp-method,
        coerce-builtin,
        coerce-method,
        delslice-method,
        div-method,
        duplicate-code,
        eq-without-hash,
        execfile-builtin,
        file-builtin,
        filter-builtin-not-iterating,
        fixme,
        getslice-method,
        global-statement,
        hex-method,
        idiv-method,
        implicit-str-concat-in-sequence,
        import-error,
        import-self,
        import-star-module-level,
        inconsistent-return-statements,
        input-builtin,
        intern-builtin,
        invalid-str-codec,
        locally-disabled,
        long-builtin,
        long-suffix,
        map-builtin-not-iterating,
        misplaced-comparison-constant,
        missing-function-docstring,
        metaclass-assignment,
        next-method-called,
        next-method-defined,
        no-absolute-import,
        no-else-break,
        no-else-continue,
        no-else-raise,
        no-else-return,
        no-init,  # added
        no-member,
        no-name-in-module,
        no-self-use,
        nonzero-method,
        oct-method,
        old-division,
        old-ne-operator,
        old-octal-literal,
        old-raise-syntax,
        parameter-unpacking,
        print-statement,
        raising-string,
        range-builtin-not-iterating,
        raw_input-builtin,
        rdiv-method,
        reduce-builtin,
        relative-import,
        reload-builtin,
        round-builtin,
        setslice-method,
        signature-differs,
        standarderror-builtin,
        suppressed-message,
        sys-max-int,
        too-few-public-methods,
        too-many-ancestors,
        too-many-arguments,
        too-many-boolean-expressions,
        too-many-branches,
        too-many-instance-attributes,
        too-many-locals,
        too-many-nested-blocks,
        too-many-public-methods,
        too-many-return-statements,
        too-many-statements,
        trailing-newlines,
        unichr-builtin,
        unicode-builtin,
        unidiomatic-typecheck,
        unnecessary-pass,
        unpacking-in-except,
        useless-else-on-loop,
        useless-object-inheritance,
        useless-suppression,
        using-cmp-argument,
        wrong-import-order,
        xrange-builtin,
        zip-builtin-not-iterating,


[REPORTS]

# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html. You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text

# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]". This option is deprecated
# and it will be removed in Pylint 2.0.
#files-output=no

# Tells whether to display a full report or only the messages
reports=no

# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)

# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=


[BASIC]

# Good variable names which should always be accepted, separated by a comma
good-names=main,_

# Bad variable names which should always be refused, separated by a comma
bad-names=

# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=

# Include a hint for the correct naming format with invalid-name
include-naming-hint=no

# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl

# Regular expression matching correct function names
function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$

# Regular expression matching correct variable names
variable-rgx=^[a-z][a-z0-9_]*$

# Regular expression matching correct constant names
const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$

# Regular expression matching correct attribute names
attr-rgx=^_{0,2}[a-z][a-z0-9_]*$

# Regular expression matching correct argument names
argument-rgx=^[a-z][a-z0-9_]*$

# Regular expression matching correct class attribute names
class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$

# Regular expression matching correct inline iteration names
inlinevar-rgx=^[a-z][a-z0-9_]*$

# Regular expression matching correct class names
class-rgx=^_?[A-Z][a-zA-Z0-9]*$

# Regular expression matching correct module names
module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$

# Regular expression matching correct method names
method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$

# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$

# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=10


[TYPECHECK]

# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager

# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes

# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=

# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local

# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=


[FORMAT]

# Maximum number of characters on a single line.
#max-line-length=80

# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
# lines made too long by directives to pytype.

# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=(?x)(
  ^\s*(\#\ )?<?https?://\S+>?$|
  ^\s*(from\s+\S+\s+)?import\s+.+$)

# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=yes

# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1  : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
#no-space-check=

# Maximum number of lines in a module
max-module-lines=99999

# String used as indentation unit.  The internal Google style guide mandates 2
# spaces.  Google's externaly-published style guide says 4, consistent with
# PEP 8.  Here, we use 2 spaces, for conformity with many open-sourced Google
# projects (like TensorFlow).
indent-string='  '

# Number of spaces of indent required inside a hanging  or continued line.
indent-after-paren=4

# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=


[MISCELLANEOUS]

# List of note tags to take in consideration, separated by a comma.
notes=TODO


[STRING]

# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=yes


[VARIABLES]

# Tells whether we should check for unused import in __init__ files.
init-import=no

# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)

# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=

# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb

# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools


[LOGGING]

# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging,absl.logging,tensorflow.io.logging


[SIMILARITIES]

# Minimum lines number of a similarity.
min-similarity-lines=4

# Ignore comments when computing similarities.
ignore-comments=yes

# Ignore docstrings when computing similarities.
ignore-docstrings=yes

# Ignore imports when computing similarities.
ignore-imports=no


[SPELLING]

# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=

# List of comma separated words that should not be checked.
spelling-ignore-words=

# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=

# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no


[IMPORTS]

# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,
                   TERMIOS,
                   Bastion,
                   rexec,
                   sets

# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=

# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=

# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=

# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=

# Force import order to recognize a module as part of a third party library.
known-third-party=enchant, absl

# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no


[CLASSES]

# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
                      __new__,
                      setUp

# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
                  _fields,
                  _replace,
                  _source,
                  _make

# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls,
                            class_

# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs


[EXCEPTIONS]

# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=StandardError,
                       Exception,
                       BaseException


================================================
FILE: .vscode/settings.json
================================================
{
  "editor.tabSize": 2,
  "files.trimTrailingWhitespace": true,
  "cSpell.words": ["iswap", "pyhamilton", "subresource"],
  "python.linting.pylintEnabled": true,
  "python.testing.pytestArgs": ["pyhamilton"],
  "python.testing.unittestEnabled": false,
  "python.testing.pytestEnabled": true,
  "python.testing.autoTestDiscoverOnSaveEnabled": true,
  "editor.rulers": [100],
  "editor.formatOnSave": true,
  "files.exclude": {
    "**/__pycache__": true
  }
}


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 dgretton

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: PKG-INFO
================================================
Metadata-Version: 2.1
Name: pyhamilton
Version: 1.48
Summary: Python for Hamilton liquid handling robots
Home-page: https://github.com/dgretton/pyhamilton.git
Author: Dana Gretton
Author-email: dgretton@mit.edu
License: MIT
License-File: LICENSE

Forthcoming due to markdown incompatibility


================================================
FILE: README.md
================================================
# PyHamilton

**Python for Hamilton liquid handling robots**

Hamilton software only works on Windows, so the same goes for PyHamilton.

Developed for Hamilton STAR and STARlet on Windows XP, Windows 7, and Windows 10. VANTAGE series supported with plugin. Other robot models and operating systems not supported yet.

Please post on [labautomation.io](https://labautomation.io/) if you have any questions, comments, issues, or feedback! You can also email stefanmgolas@gmail.com for troubleshooting help.


**Disclaimer:** PyHamilton is not officially endorsed or supported by the Hamilton Company. Please direct any questions to the above email address, and not to Hamilton Company. 

## Example usage
```python
if __name__ == "__main__":

    from pyhamilton import HamiltonInterface, INITIALIZE
    with HamiltonInterface() as ham_int:
    
        ham_int.wait_on_response(ham_int.send_command(INITIALIZE))
```

## Guides

Here is a protocol repository with guides about how to do things like magnetic bead washes and thermal cycling. These are all NGS protocols, but you can use the same steps in many other experiments.

[NGS Protocol Library](https://github.com/stefangolas/ngs-protocols)

## Documentation

[Available online](https://dgretton.github.io/pyhamilton-docs/).

## Tutorial Video
https://www.youtube.com/watch?v=G92neaVfvyw

## Installation

1. **Install and test the standard Hamilton software suite for your system.** We no longer host the link here, please contact Hamilton for a copy of the Venus software if you don't have one already
2. **Install [Python <=3.13.](https://www.python.org/downloads/windows/)** Make sure to check the box that asks if you want to add Python to your path variable.
3. **Make sure git is installed.** https://git-scm.com/download/win
4. **Make sure you have .NET framework 4.0 or higher installed.** https://www.microsoft.com/en-us/download/details.aspx?id=17851
5. [**Install Microsoft Access database engine**](https://www.microsoft.com/en-us/download/details.aspx?id=54920). This must have the same bit number (32-bit or 64-bit) as your Python version, and the rest of your Microsoft Office applications.
6. **Update your pip and setuptools.**
    ```
    > python -m pip install --upgrade pip
    > pip install --upgrade setuptools
    ```
7. **Install pyhamilton.**
   
   ```
   git clone https://github.com/dgretton/pyhamilton
   cd pyhamilton
   pip install -e .
   ```
   Now changes you make to the cloned repo will be reflected in your package install. You can test new code this way and then push it to a fork. 
    
8. **Run the pyhamilton autoconfig tool from the command line.** 
This will automatically execute all the installers in `pyhamilton/bin` and will copy all the files in `pyhamilton\library` to `C:/Program Files (x86)/HAMILTON/Library`. You are welcome to forgo this command and perform the steps manually if you are concerned about file overwriting.

    ```
    pyhamilton-configure
    ``` 

    Press accept to proceed with the bundled installers.
    
9. **Test your PyHamilton installation** </br>
The easiest way to test your PyHamilton installation is by running the following in your terminal

    ```
    mkdir new-project
    cd new-project
    pyhamilton-new-project
    py robot_method.py
    ```

10. **Run.** If you have other Python versions installed, always run pyhamilton with `py yourmethod.py` (the bundled Python launcher, which interprets shebangs) or `python3 yourmethod.py`



## Installation Troubleshooting
1. If you encounter an error relating to HxFan (i.e., your robot does not have a fan), open pyhamilton/star-oem/VENUS_Method/STAR_OEM_Test.med, navigate to the "HxFan" grouping, and delete all commands under this grouping.

2. If you would like to test your PyHamilton installation on a computer not connected to a Hamilton robot, use `HamiltonInterface(simulate=True)` to open your interface inside your robot script. 

3. If your initialization hangs (such as on initial_error_example.py), try these steps:
    </br>a. Make sure you don't have any other program running which is communicating with the robot e.g. Venus run control
    </br>b. Make sure the .dlls referenced in ```__init__.py``` are unblocked. See [this StackOverflow thread](https://stackoverflow.com/questions/28840880/pythonnet-filenotfoundexception-unable-to-find-assembly) for more details.

4. If you get an error like `pyhamilton-configure is not recognized as an internal or external command, operable program or batch file,` make sure the directory containing your Python interpreter is in your `path` environment variable.

## Applications

- [A high-throughput platform for feedback-controlled directed evolution](https://www.biorxiv.org/content/10.1101/2020.04.01.021022v1), _preprint_

- [Flexible open-source automation for robotic bioengineering](https://www.biorxiv.org/content/10.1101/2020.04.14.041368v1), _preprint_


_Developed for the Sculpting Evolution Group at the MIT Media Lab_


================================================
FILE: imgs/README.md
================================================
# PyHamilton Reference Guide
Author: Stefan Golas _(Contact stefanmgolas@gmail.com)_

## Contents
- Intro
- Installation
- Your First PyHamilton Method
- Breaking it Down
- How PyHamilton Works
- Expanding The API
## Intro
PyHamilton is an open-source Python interface for programming Hamilton liquid-handling robots. PyHamilton is designed to be accessible while affording unlimited flexibility to the developer. We believe that an open-source community driven framework will accelerate discovery and enable a new generation of biological workflows.

 ## Installation

1. **Install and test the standard Hamilton software suite for your system.**
2. **Install 32-bit python <=3.9**, preferably using the executable installer at https://www.python.org/downloads/release/python-390/. Python 3.10+ is known to cause an installation issue with some required pythonnet modules.
3. **Make sure git is installed.** https://git-scm.com/download/win
4. **Make sure you have .NET framework 4.0 or higher installed.** https://www.microsoft.com/en-us/download/details.aspx?id=17851
5. **Update your pip and setuptools.**
    ```
    > python -m pip install --upgrade pip
    > pip install --upgrade setuptools
    ```
6. **Install pyhamilton.**
   
    ```
    pip install pyhamilton
    ```
    
7. **Run the pyhamilton autoconfig tool from the command line.** 

    ```
    pyhamilton-config
    ``` 

    Press accept to proceed with the bundled installers.

## Your First PyHamilton Method

Here is how to write your first PyHamilton method.

First, create a new directory called `my-project`. Then,  open the Hamilton Method Editor and create a new Layout file. Add 5 96-tip tip carriers named "tips_1", "tips_2", etc. Then add 5 96-well plates named "plate_1", "plate_2", etc. <br>
![Deck layout](https://raw.githubusercontent.com/dgretton/pyhamilton/master/imgs/decklay.png) 
_deck.lay_

Next, create a file named `robot_method.py` in your preferred text editor. Inside this file, type 

``` 
from pyhamilton import (HamiltonInterface, LayoutManager, ResourceType,  Plate96, Tip96, initialize, tip_pick_up, tip_eject, aspirate, dispense, tip_pick_up_96, tip_eject_96, aspirate_96, dispense_96, oemerr, , move_plate)
 ```


```
my-project
│   deck.lay
│   robot_method.py 
```
_Project directory structure_ 

In `robot_method.py`, 

<br>
<br>
<br>

```
my-project
│   README.md
│   file001.txt    
│
└───folder1
│   │   file011.txt
```


================================================
FILE: imgs/text
================================================



================================================
FILE: pandoc_pdf.sh
================================================
 pandoc --from=markdown+abbreviations+tex_math_single_backslash  \
           --pdf-engine=xelatex --variable=mainfont:"DejaVu Sans"   \
           --toc --toc-depth=4 --output=../pyhamilton-docs/pyhamilton-doc.pdf  \
           build/pdf-intermediate.md



================================================
FILE: pyhamilton/__init__.py
================================================
"""
Pyhamilton
"""
import os
import shutil
from os.path import dirname, join, abspath
PACKAGE_PATH = abspath(dirname(__file__))
LAY_BACKUP_DIR = join(PACKAGE_PATH, 'LAY-BACKUP')
if not os.path.exists(LAY_BACKUP_DIR):
    os.mkdir(LAY_BACKUP_DIR)
OEM_STAR_PATH = join(PACKAGE_PATH, 'star-oem')
if not (os.path.exists(OEM_STAR_PATH)
		and os.path.exists(os.path.join(OEM_STAR_PATH, 'RunHSLExecutor.dll'))
		and os.path.exists(os.path.join(OEM_STAR_PATH, 'HSLHttp.dll'))):
    raise FileNotFoundError('pyhamilton requires .../site-packages/pyhamilton/STAR-OEM, distributed separately.')
OEM_LAY_PATH = join(OEM_STAR_PATH, 'VENUS_Method', 'STAR_OEM_Test.lay')
OEM_HSL_PATH = join(OEM_STAR_PATH, 'VENUS_Method', 'STAR_OEM_noFan.hsl')
OEM_RUN_EXE_PATH = 'C:\\Program Files (x86)\\HAMILTON\\Bin\\HxRun.exe'

from .interface import *
from .oemerr import *
from .liquid_handling_wrappers import *
from .devices import *
from .resources import *
from .liquid_class_db import *
from .consumables import *
from .ngs import *
from .liquid_classes import *



this_file_dir = os.path.dirname(os.path.abspath(__file__))
PACKAGE_DIR = os.path.abspath(os.path.join(this_file_dir))
LIBRARY_DIR = os.path.join(PACKAGE_DIR, 'library')
TEMPLATE_DIR = os.path.join(PACKAGE_DIR, 'templates/basic_template')
AI_TEMPLATE_DIR = os.path.join(PACKAGE_DIR, 'templates/ai_template')
EXE_DIR = os.path.join(PACKAGE_DIR, 'bin')

exe_http = os.path.join(PACKAGE_DIR, 'bin', 'Hamilton HSLHttp Library Installer Version 2.7.exe')
exe_json = os.path.join(PACKAGE_DIR, 'bin', 'HSLJson Library v2.0.1 Installer.exe')
exe_pH = os.path.join(PACKAGE_DIR, 'bin', 'Hamilton pH Module v2.2.exe')
exe_mpe = os.path.join(PACKAGE_DIR, 'bin', 'Hamilton MPE HSL Driver.msi')



def full_paths_list(directory_abs_path):
    list_files = os.listdir(directory_abs_path)
    list_file_paths = [directory_abs_path + '\\' + file for file in list_files]
    return list_file_paths

def recursive_copy(source_dir, target_dir):
    source_list = full_paths_list(source_dir)
    for file in source_list:
        if os.path.isfile(file):
            target_file = os.path.join(target_dir, os.path.basename(file))
            if not os.path.exists(target_file):
                shutil.copy(file, target_file)
        if os.path.isdir(file):
            target_subdir = os.path.join(target_dir, os.path.basename(file))
            if not os.path.exists(target_subdir):
                os.mkdir(target_subdir)
            recursive_copy(file, target_subdir)


def autoconfig():
    input("""\n This tool automatically configures your PyHamilton installation by copying library files from pyhamilton/library
into C:/Program Files (x86)/HAMILTON/Library. It is recommended you back up your Hamilton installation
folder in the rare event of  a file overwrite. Press enter to continue, or press ctrl+c to cancel the
installation process.""")
    for filename in os.listdir(EXE_DIR):
        file_path = os.path.join(EXE_DIR, filename)
        os.startfile(file_path)
    
    hamilton_lib_dir = os.path.abspath('C:/Program Files (x86)/HAMILTON/Library')
    print("Copying files to Hamilton library")
    print(LIBRARY_DIR)
    
    recursive_copy(LIBRARY_DIR, hamilton_lib_dir)        
    print("Configuration completed")

    user_home = os.path.expanduser("~")
    config_dir = os.path.join(user_home, ".pyhamilton")
    os.makedirs(config_dir, exist_ok=True)

    source_defaults_path = os.path.join(PACKAGE_DIR, "defaults", "defaults.json")
    target_defaults_path = os.path.join(config_dir, "defaults.json")

    if not os.path.exists(source_defaults_path):
        print(f"ERROR: Could not find source defaults file at: {source_defaults_path}")
    else:
        shutil.copyfile(source_defaults_path, target_defaults_path)
        print(f"Copied default config to: {target_defaults_path}")

    print("Configuration completed")




def create_project():
    current_dir = os.path.abspath(os.getcwd())
    print("Creating project template")
    recursive_copy(TEMPLATE_DIR, current_dir)

def create_ai_project():
    current_dir = os.path.abspath(os.getcwd())
    print("Creating AI assistant project template")
    recursive_copy(AI_TEMPLATE_DIR, current_dir)

================================================
FILE: pyhamilton/consumables/__init__.py
================================================
from .consumables import (ReagentTrackedFalconCarrier24, ReagentTrackedPlate96, ReagentTrackedReservoir60mL, 
                          ReagentTrackedEppiCarrier32, ReagentTrackedBulkPlate, ReagentTrackedPlate24, tracked_volume_aspirate_96,
                          tracked_volume_aspirate, generate_reagent_summary, generate_tip_use_summary)

================================================
FILE: pyhamilton/consumables/consumables.py
================================================
from ..interface import HamiltonInterface
from ..resources import DeckResource, ResourceType,layout_item, LayoutManager
from ..resources import BulkReagentPlate, FalconCarrier24, EppiCarrier32, Reservoir60mL, Plate24, Plate96
from pathlib import Path
import json

class VolumeConsumptionTracker:
    def __init__(self, num_positions):
        self.initial_volumes = {k:v for k,v in [(idx, 0) for idx in range(num_positions)]}
        self.volumes = self.initial_volumes.copy()

    def aspirate_volume(self, well_index, volume):
        self.volumes[well_index] += volume

class TrackedContainer:
    """Base class for any tracked container type with volume bookkeeping."""

    def __init__(self):
        pass

    def aspirate_volume(self, well_index, volume):
        """Subtract volume from one or more wells. 
        Must be overridden in subclasses."""
        raise NotImplementedError(
            f"{self.__class__.__name__} must implement aspirate_volume()"
        )

class TrackedReagentVessel(TrackedContainer):
    def __init__(self, *args, **kwargs):
        self.reagent_map = {}
   
    def assign_reagent_map(self, reagent_name: str, positions: list[int]) -> list[tuple[TrackedContainer, int]]:
        '''
        Assigns reagent positions for a specific reagent and returns the tuple of (container, position) for each position
        so we can pass the output to aspirate functions.
        '''
        self.reagent_map[reagent_name] = positions
        return self.reagent_positions(reagent_name)
    

    
    def reset_volumes(self):
        """
        Resets all volume trackers in the container to 0.
        """
        self.volumes = {pos: 0 for pos in self.volumes}

    def calculate_required_reagent_volume(self, reagent_name: str):
        # Use the negative volume from the tracker to determine reagent consumption for this tracked resource
        # return -self.volumes[self.reagent_map[reagent_name]]
        # Above won't work because self.reagent_map[reagent_name] is a list of positions, we want to do a list comprehension

        # Right now this only handles single-well reagents but should be extended to handle reagents distributed across
        # Multiple wells
        return -sum(self.volumes[pos] for pos in self.reagent_map[reagent_name]) + self.dead_volume

    def all_required_reagent_volumes(self):
        return {self.layout_name(): {reagent: self.calculate_required_reagent_volume(reagent) for reagent in self.reagent_map}}

    def reagent_positions(self, reagent_name):
        # List comprehension of form [(self, pos) for pos in self.reagent_map[reagent_name]]
        return [(self, pos) for pos in self.reagent_map[reagent_name]]
    

class ReagentTrackedPlate96(Plate96, TrackedReagentVessel):
    def __init__(self, *args, **kwargs):
        Plate96.__init__(self, *args, **kwargs)
        TrackedReagentVessel.__init__(self)
        self.volumes = {k:v for k,v in [(idx, 0) for idx in range(96)]}
        self.dead_volume = kwargs.get('dead_volume', 10) # uL


    def aspirate_volume(self, well_index, volume):
        self.volumes[well_index] -= volume
    
class ReagentTrackedBulkPlate(BulkReagentPlate, TrackedReagentVessel):
    def __init__(self, *args, **kwargs):
        Plate96.__init__(self, *args, **kwargs)
        TrackedReagentVessel.__init__(self)
        self.volumes = {0: 0}
        self.dead_volume = 100

    # The plate is a single container, so we subtract all volumes from one element in the tracker. This overrides the base method.
    def aspirate_volume(self, well_index, volume):
        self.volumes[0] -= volume
    
    # We return the plate object itself so we can pass it to 96 channel commands. This overrides the base method
    def assign_reagent_map(self, reagent_name, positions):
        self.reagent_map[reagent_name] = positions
        return self
    
    def calculate_required_reagent_volume(self, reagent_name):
        # Use the negative volume from the tracker to determine reagent consumption for this tracked resource
        return -self.volumes[0] + self.dead_volume

class ReagentTrackedPlate24(Plate24, TrackedReagentVessel):
    def __init__(self, *args, **kwargs):
        Plate24.__init__(self, *args, **kwargs)
        TrackedReagentVessel.__init__(self)
        self.volumes = {k:v for k,v in [(idx, 0) for idx in range(24)]}
        self.dead_volume = kwargs.get('dead_volume', 20) # uL


    def aspirate_volume(self, well_index, volume):
        self.volumes[well_index] -= volume

class ReagentTrackedReservoir60mL(Reservoir60mL, TrackedReagentVessel):
    def __init__(self, *args, **kwargs):
        Reservoir60mL.__init__(self, *args, **kwargs)
        TrackedReagentVessel.__init__(self)
        self.volumes = {0: 0}
        self.dead_volume = kwargs.get('dead_volume', 200) # uL

    # The 60mL trough is a single well, so we subtract all volumes from one element in the tracker. This overrides the base method.
    def aspirate_volume(self, well_index, volume):
        self.volumes[0] -= volume

    def calculate_required_reagent_volume(self, reagent_name):
        # Use the negative volume from the tracker to determine reagent consumption for this tracked resource
        return -self.volumes[0] + self.dead_volume


    def height_to_volume(self, height):
        pass

class ReagentTrackedFalconCarrier24(FalconCarrier24, TrackedReagentVessel):
    def __init__(self, *args, **kwargs):
        FalconCarrier24.__init__(self, *args, **kwargs)
        TrackedReagentVessel.__init__(self)
        self.volumes = {k:v for k,v in [(idx, 0) for idx in range(24)]}
        self.dead_volume = kwargs.get('dead_volume', 10) # uL


    def aspirate_volume(self, well_index, volume):
        self.volumes[well_index] -= volume

class ReagentTrackedEppiCarrier32(EppiCarrier32, TrackedReagentVessel):
    def __init__(self, *args, **kwargs):
        EppiCarrier32.__init__(self, *args, **kwargs)
        TrackedReagentVessel.__init__(self)
        self.volumes = {k:v for k,v in [(idx, 0) for idx in range(32)]}
        self.dead_volume = kwargs.get('dead_volume', 10) # uL


    def aspirate_volume(self, well_index, volume):
        self.volumes[well_index] -= volume


# Helper function to get the class name of an object
def get_class_name(obj):
    """Returns the name of the object's class as a string."""
    return type(obj).__name__

def generate_reagent_summary(tracked_vessels: list, units_default: str = "uL", output_file: str = None):
    """
    Generates a summary of reagent consumption from a list of tracked vessels.
    Now also includes vessels with custom labels even if they have no reagent consumption.
    """
    summary = {"units_default": units_default}
    
    for vessel in tracked_vessels:
        vessel_name = vessel.layout_name()

        if isinstance(vessel, TrackedReagentVessel):
            vessel_data = vessel.all_required_reagent_volumes()[vessel_name]
        else:
            vessel_data = {}

        # Check if vessel has a custom label
        has_custom_label = hasattr(vessel, 'custom_label') and vessel.custom_label is not None
        
        # Skip vessels with no reagents AND no custom label
        if not has_custom_label and (not vessel_data or all(vol <= 0 for vol in vessel_data.values())):
            continue
        
        summary[vessel_name] = {
            "class_name": get_class_name(vessel),
            "positions": {}
        }
        
        # Add custom label if present
        if has_custom_label:
            summary[vessel_name]["custom_label"] = vessel.custom_label
        
        # For each reagent in this vessel, find which positions it occupies
        for reagent_name, total_volume in vessel_data.items():
            if total_volume <= 0:
                continue
                
            # Get the positions where this reagent is located
            reagent_positions = vessel.reagent_map.get(reagent_name, [])
            
            for pos in reagent_positions:
                # Get the volume consumed from this specific position
                pos_volume = -vessel.volumes.get(pos, 0) if vessel.volumes.get(pos, 0) < 0 else 0
                
                if pos_volume > 0:
                    summary[vessel_name]["positions"][pos] = {
                        "reagent": reagent_name,
                        "volume": pos_volume,
                        "unit": "uL"
                    }
    
    # Write to JSON file if output_file is specified
    if output_file:
        Path(output_file).parent.mkdir(parents=True, exist_ok=True)
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(summary, f, indent=2, ensure_ascii=False)
        print(f"Reagent summary written to {output_file}")
    
    return summary


def generate_tip_use_summary(tracked_tips_list, output_file=None):
    """
    Generate tip consumption summary data from TrackedTips objects.
    
    Parameters
    ----------
    tracked_tips_list : list[TrackedTips]
        List of TrackedTips objects to analyze
        
    Returns
    -------
    dict
        Summary data structure with consumption details for LoadingVis
    """
    from datetime import datetime
    
    report_data = {
        "report_generated": datetime.now().isoformat(),
        "tip_trackers": {},
        "summary": {
            "total_trackers": len(tracked_tips_list),
            "total_tips_available": 0,
            "total_tips_consumed": 0,
            "total_tips_capacity": 0,
            "overall_consumption_rate": 0.0
        }
    }
    
    for i, tracker in enumerate(tracked_tips_list, 1):
        # Calculate consumption metrics for this tracker
        total_tips = tracker.total_tips()
        remaining_tips = tracker.count_remaining()
        consumed_tips = total_tips - remaining_tips
        consumption_rate = (consumed_tips / total_tips * 100) if total_tips > 0 else 0
        
        # Update summary totals
        report_data["summary"]["total_tips_capacity"] += total_tips
        report_data["summary"]["total_tips_available"] += remaining_tips
        report_data["summary"]["total_tips_consumed"] += consumed_tips
        
        # Analyze by rack
        rack_details = []
        rack_start_idx = 0
        
        for rack in tracker.tip_racks:
            rack_total = rack._num_items
            rack_occupied = sum(1 for j in range(rack_total) 
                              if tracker.is_occupied(rack_start_idx + j))
            rack_consumed = rack_total - rack_occupied
            rack_consumption_rate = (rack_consumed / rack_total * 100) if rack_total > 0 else 0
            
            rack_info = {
                "name": rack.layout_name(),
                "total_tips": rack_total,
                "tips_consumed": rack_consumed,
                "tips_remaining": rack_occupied,
                "consumption_rate": round(rack_consumption_rate, 1)
            }
            rack_details.append(rack_info)
            rack_start_idx += rack_total
        
        # Store tracker data
        tracker_data = {
            "tracker_id": tracker.tracker_id,
            "volume_capacity": tracker.volume_capacity,
            "total_tips": total_tips,
            "tips_consumed": consumed_tips,
            "tips_remaining": remaining_tips,
            "consumption_rate": round(consumption_rate, 1),
            "num_racks": len(tracker.tip_racks),
            "racks": rack_details
        }
        report_data["tip_trackers"][f"tracker_{i}"] = tracker_data
    
    # Calculate overall summary
    total_capacity = report_data["summary"]["total_tips_capacity"]
    total_consumed = report_data["summary"]["total_tips_consumed"]
    if total_capacity > 0:
        overall_rate = (total_consumed / total_capacity * 100)
        report_data["summary"]["overall_consumption_rate"] = round(overall_rate, 1)

    if output_file:
        with open(output_file, "w") as f:
            json.dump(report_data, f)

    return report_data


def tracked_volume_aspirate(ham_int: HamiltonInterface, plate_poss: list[tuple[TrackedContainer, int]], vols: list, **kwargs):
    response = ham_int.aspirate(plate_poss, vols, **kwargs)
    filtered_pairs = [(pos, vol) for pos, vol in zip(plate_poss, vols) if pos is not None]
    plate_poss, vols = zip(*filtered_pairs) if filtered_pairs else ([], [])
    
    try:
        for (plate, well_index), vol in zip(plate_poss, vols):
            plate.aspirate_volume(well_index, vol)
    except AttributeError:
        return response
    
    return response

def tracked_volume_aspirate_96(ham_int: HamiltonInterface, plate: TrackedContainer, vol: int, **kwargs):
    ham_int.aspirate_96(plate, vol, **kwargs)
    
    try: # In case the plate doesn't implement aspirate_volume, we just skip it
        for well_idx in range(96):
            plate.aspirate_volume(well_idx, vol)
    except AttributeError:
        return

================================================
FILE: pyhamilton/defaultcmds.py
================================================
"""
Built-in commands, definitions of their parameters, and defaults.
"""

_channel_patt_16 = '1'*8 + '0'*8
_channel_patt_96 = '1'*96

_fan_port = 6
# on module load, scan COM ports to see if the usual fan COM number (6) has been reassigned by the OS
try:
    import serial.tools.list_ports
    for port in serial.tools.list_ports.comports():
        port_parse = str(port).split(' ')
        if 'Isolated' in port_parse and 'RS-485' in port_parse:
            _fan_port = int(port_parse[0][-1])
except Exception:
    pass

from .defaults import defaults

cfg = defaults()


defaults_by_cmd = { # 'field':None indicates field is required when assembling command

    'initialize':('INITIALIZE', {
        'initializeAlways':0
    }),

    'channelTipPickUp':('PICKUP', {
        'tipSequence':'', # (string) leave empty if you are going to provide specific labwarePositions below
        'labwarePositions':'', # (string) leave empty if you are going to provide a sequence name above.'LabwareId1, positionId1; LabwareId2,positionId2; ....'
        'channelVariable':_channel_patt_16, # (string)  channel pattern e.g. '11110000'
        'sequenceCounting':0, # (integer) 0=don´t autoincrement,  1=Autoincrement
        'channelUse':1 # (integer) 1=use all sequence positions (no empty wells), 2=keep channel pattern
    }),

    'channelTipEject':('EJECT', {
        'wasteSequence':'', # (string) leave empty if you are going to provide specific labware-positions below or ejecting to default waste
        'labwarePositions':'', # (string) leave empty if you are going to provide a sequence name above.'LabwareId1, positionId1; LabwareId2,positionId2; ....'
        'channelVariable':_channel_patt_16, # (string) channel pattern e.g. "11110000"
        'sequenceCounting':0, # (integer) 0=don´t autoincrement,  1=Autoincrement.  Value omitted if ejecting to default waste
        'channelUse':1, # (integer) 1=use all sequence positions (no empty wells), 2=keep channel pattern
        'useDefaultWaste':0, # (integer) 0=eject to custom waste sequence,  1=Use default waste
        'xDisplacement':0.0,
        'yDisplacement':0.0,
        'zDisplacement':0.0
    }),

    'channelAspirate':('ASPIRATE', {
        'aspirateSequence':'', # (string) leave empty if you are going to provide specific labware-positions below
        'labwarePositions':'', # (string) leave empty if you are going to provide a sequence name above. 'LabwareId1, positionId1; LabwareId2,positionId2; ....'
        'volumes':None, # (float or string) enter a single value used for all channels or enter an array of values for each channel like [10.0,15.5,11.2]
        'channelVariable':_channel_patt_16, # (string) channel pattern e.g. "11110000"
        'liquidClass':None, # (string)
        'sequenceCounting':0, # (integer) 0=don´t autoincrement,  1=Autoincrement
        'channelUse':1, # (integer) 1=use all sequence positions (no empty wells), 2=keep channel pattern
        'aspirateMode':0, # (integer) 0=Normal Aspiration, 1=Consecutive (don´t aspirate blowout), 2=Aspirate all 
        'capacitiveLLD':0, # (integer) 0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From labware definition
        'pressureLLD':0, # (integer) 0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From liquid class definition
        'liquidFollowing':0, # (integer) 0=Off , 1=On
        'submergeDepth':2.0, # (float) mm of immersion below liquid´s surface to start aspiration when using LLD
        'liquidHeight':1.0, # (float) mm above container´s bottom to start aspiration when not using LLD
        'maxLLdDifference':0.0, # (float) max mm height different between cLLD and pLLD detected liquid levels
        'mixCycles':0, # (integer) number of mixing cycles (1 cycle = 1 asp + 1 disp)
        'mixPosition':0.0, # (float) additional immersion mm below aspiration position to start mixing
        'mixVolume':0.0, # (float) mix volume
        'xDisplacement':0.0,
        'yDisplacement':0.0,
        'zDisplacement':0.0,
        'airTransportRetractDist':10.0, # (float) mm to move up in Z after finishing the aspiration at a fixed height before aspirating 'transport air'
        'touchOff':0, # (integer) 0=Off , 1=On
        'aspPosAboveTouch':0.0 # (float)  mm to move up in Z after touch off detects the bottom before aspirating liquid
    }),

    'channelDispense':('DISPENSE', {
        'dispenseSequence':'', # (string) leave empty if you are going to provide specific labware-positions below
        'labwarePositions':'', # (string) leave empty if you are going to provide a sequence name above. 'LabwareId1, positionId1; LabwareId2,positionId2; ....'
        'volumes':None, # (float or string) enter a single value used for all channels or enter an array of values for each channel like [10.0,15.5,11.2]
        'channelVariable':_channel_patt_16, # (string) channel pattern e.g. "11110000"
        'liquidClass':None, # (string)
        'sequenceCounting':0, # (integer) 0=don´t autoincrement,  1=Autoincrement
        'channelUse':1, # (integer) 1=use all sequence positions (no empty wells), 2=keep channel pattern
        'dispenseMode':8, # (integer) 0=Jet Part, 1=Jet Empty, 2=Surface Part, 3=Surface Empty, 4=Jet Drain tip, 8=From liquid class, 9=Blowout tip
        'capacitiveLLD':0, # (integer) 0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From labware definition
        'liquidFollowing':0, # (integer) 0=Off , 1=On
        'submergeDepth':2.0, # (float) mm of immersion below liquid´s surface to start dispense when using LLD
        'liquidHeight':1.0, # (float) mm above container´s bottom to start dispense when not using LLD
        'mixCycles':0, # (integer) number of mixing cycles (1 cycle = 1 asp + 1 disp)
        'mixPosition':0.0, # (float) additional immersion mm below dispense position to start mixing
        'mixVolume':0.0, # (float) mix volume
        'xDisplacement':0.0,
        'yDisplacement':0.0,
        'zDisplacement':0.0,
        'airTransportRetractDist':10.0, # (float) mm to move up in Z after finishing the dispense at a fixed height before aspirating 'transport air'
        'touchOff':0, # (integer) 0=Off , 1=On
        'dispPositionAboveTouch':0.0, # (float) mm to move up in Z after touch off detects the bottom, before dispense
        'zMoveAfterStep':0, # (integer) 0=normal, 1=Minimized (Attention!!! this depends on labware clearance height, can crash). 
        'sideTouch':0 # (integer) 0=Off , 1=On
    }),

    'mph96TipPickUp':('PICKUP96', {
        'tipSequence':'', # (string) leave empty if you are going to provide specific labware-positions below
        'labwarePositions':'', # (string) leave empty if you are going to provide a sequence name above. 'LabwareId1, positionId1; LabwareId2,positionId2; ....' Must contain 96 values
        'channelVariable':_channel_patt_96, # (string) channel Variable e.g. "11110000...." . Must contain 96 values
        'sequenceCounting':0, # (integer) 0=don´t autoincrement,  1=Autoincrement
        'reducedPatternMode':0 # (integer) 0=All (not reduced), 1=One channel, 2=One row  3=One column
    }),

    'mph96TipEject':('EJECT96', {
        'wasteSequence':'', # (string) leave empty if you are going to provide specific labware-positions below or ejecting to default waste
        'labwarePositions':'', # (string) leave empty if you are going to provide a sequence name above. 'LabwareId1, positionId1; LabwareId2,positionId2; ....'
        'channelVariable':_channel_patt_96, # (string) channel Variable e.g. "11110000...." . Must contain 96 values
        'sequenceCounting':0, # (integer)  0=don´t autoincrement,  1=Autoincrement.  Value omitted if ejecting to default waste
        'tipEjectToKnownPosition':0 # (integer) 0=Eject to specified sequence position,  1=Eject on tip pick up position, 2=Eject on default waste
    }),

    'mph96Aspirate':('ASPIRATE96', {
        'aspirateSequence':'', # (string) leave empty if you are going to provide specific labware-positions below
        'labwarePositions':'', # (string) leave empty if you are going to provide a sequence name above. LabwareId1, positionId1; LabwareId2,positionId2; ....
        'aspirateVolume':None, # (float)  single volume used for all channels in the head. There´s no individual control of each channel volume in multi-probe heads.
        'channelVariable':_channel_patt_96, # (string) channel Variable e.g. "11110000...." . Must contain 96 values
        'liquidClass':None, # (string)
        'sequenceCounting':0, # (integer)  0=don´t autoincrement,  1=Autoincrement
        'aspirateMode':0, # (integer) 0=Normal Aspiration, 1=Consecutive (don´t aspirate blowout), 2=Aspirate all 
        'capacitiveLLD':0, # (integer) 0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From labware definition
        'liquidFollowing':0, # (integer) 0=Off , 1=On
        'submergeDepth':2.0, # (float) mm of immersion below liquid´s surface to start aspiration when using LLD
        'liquidHeight':1.0, # (float) mm above container´s bottom to start aspiration when not using LLD
        'mixCycles':0, # (integer) number of mixing cycles (1 cycle = 1 asp + 1 disp)
        'mixPosition':0.0, # (float) additional immersion mm below aspiration position to start mixing
        'mixVolume':0.0, # (float) mix volume
        'airTransportRetractDist':10.0 # (float) mm to move up in Z after finishing the aspiration at a fixed height before aspirating 'transport air'
    }),

    'mph96Dispense':('DISPENSE96', {
        'dispenseSequence':'', # (string) leave empty if you are going to provide specific labware-positions below
        'labwarePositions':'', # (string) leave empty if you are going to provide a sequence name above. LabwareId1, positionId1; LabwareId2,positionId2; ....
        'dispenseVolume':None, # (float) single volume used for all channels in the head. There´s no individual control of each channel volume in multi-probe heads.
        'channelVariable':_channel_patt_96, # (string) channel Variable e.g. "11110000...." . Must contain 96 values
        'liquidClass':None, # (string) 
        'sequenceCounting':0, # (integer)  0=don´t autoincrement,  1=Autoincrement
        'dispenseMode':8, # (integer) 0=Jet Part, 1=Jet Empty, 2=Surface Part, 3=Surface Empty,4=Jet Drain tip, 8=From liquid class, 9=Blowout tip
        'capacitiveLLD':0, # (integer) 0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From labware definition
        'liquidFollowing':0, # (integer)  0=Off , 1=On
        'submergeDepth':2.0, # (float) mm of immersion below liquid´s surface to start dispense when using LLD
        'liquidHeight':1.0, # (float) mm above container´s bottom to start dispense when not using LLD
        'mixCycles':0, # (integer)  number of mixing cycles (1 cycle = 1 asp + 1 disp)
        'mixPosition':0.0, # (float)  additional immersion mm below dispense position to start mixing
        'mixVolume':0.0, # (float)  mix volume
        'airTransportRetractDist':10.0, # (float) mm to move up in Z after finishing the dispense at a fixed height before aspirating 'transport air'
        'zMoveAfterStep':0, # (integer) 0=normal, 1=Minimized (Attention!!! this depends on labware clearance height, can crash). 
        'sideTouch':0 # (integer) 0=Off , 1=On
    }),

    'iSwapGet':('ISWAP_GET', {
        'plateSequence':'', # leave empty if you are going to provide specific plate labware-position below
        'plateLabwarePositions':'', # leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 
        'lidSequence':'', # leave empty if you don´t use lid or if you are going to provide specific plate labware-positions below or ejecting to default waste
        'lidLabwarePositions':'', # leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 
        'toolSequence':'', # sequence name of the iSWAP. leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1;
        'sequenceCounting':0, # (integer) 0=don´t autoincrement plate sequence,  1=Autoincrement
        'movementType':0, # (integer) 0=To carrier, 1=Complex movement
        'transportMode':0, # (integer) 0=Plate only, 1=Lid only ,2=Plate with lid
        'gripForce':4, # (integer) 2 (minimum) ... 9 (maximum)
        'inverseGrip':0, # (integer) 0=Off, 1=On
        'collisionControl':0, # (integer) 0=Off, 1=On
        'gripMode':1, # (integer) 0=Small side, 1=Large side
        'retractDistance':0.0, # (float) retract distance [mm] (only used if 'movement type' is set to 'complex movement')
        'liftUpHeight':20.0, # (float) lift-up distance [mm] (only used if 'movement type' is set to 'complex movement')
        'gripWidth':123.7, # (float) grip width when closed [mm]
        'tolerance':2.0, # (float) tolerance [mm]
        'gripHeight':3.0, # (float) height to grip above bottom of labware [mm]
        'widthBefore':130.0, # (float) grip width when opened before grip [mm]
        'labwareOrientation':1
    }),

    'iSwapPlace':('ISWAP_PLACE', {
        'plateSequence':'', # leave empty if you are going to provide specific plate labware-position below
        'plateLabwarePositions':'', # leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 
        'lidSequence':'', # leave empty if you don´t use lid or if you are going to provide specific plate labware-positions below or ejecting to default waste
        'lidLabwarePositions':'', # leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 
        'toolSequence':'', # sequence name of the iSWAP. leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1;
        'sequenceCounting':0, # (integer) 0=don´t autoincrement plate sequence,  1=Autoincrement
        'movementType':0, # (integer) 0=To carrier, 1=Complex movement
        'transportMode':0, # (integer) 0=Plate only, 1=Lid only ,2=Plate with lid
        'collisionControl':0, # (integer) 0=Off, 1=On
        'retractDistance':0.0, # (float) retract distance [mm] (only used if 'movement type' is set to 'complex movement')
        'liftUpHeight':20.0, # (float) lift-up distance [mm] (only used if 'movement type' is set to 'complex movement')
        'labwareOrientation':1
    }),
    'iSwapMove':('ISWAP_MOVE',{
        'plateSequence':'',
        'plateLabwarePositions':'',
        'collisionControl':0,
        'gripMode':1
    }),

    'HxFanSet':('HEPA', {
        'deviceNumber':_fan_port, # (integer) COM port number of fan
        'persistant':1, # (integer) 0=don´t keep fan running after method exits, 1=keep settings after method exits
        'fanSpeed':None, # (float) set percent of maximum fan speed
        'simulate':0 #(integer) 0=normal mode, 1=use HxFan simulation mode
    }),

    'CORE96WashEmpty':('WASH96_EMPTY', {
        'refillAfterEmpty':0, # (integer) 0=Don't refill, 1=Refill both chambers, 2=Refill chamber 1 only, 3=Refill chamber 2 only
        'chamber1WashLiquid':0, # (integer) 0=Liquid 1 (red container), 1=liquid 2 (blue container)
        'chamber1LiquidChange':0, # (integer) 0=No, 1=Yes TODO: What does this mean?
        'chamber2WashLiquid':0, # (integer) 0=Liquid 1 (red container), 1=liquid 2 (blue container)
        'chamber2LiquidChange':0, # (integer) 0=No, 1=Yes TODO: What does this mean?
    }),

    'gripGet':('GRIP_GET', {
        'plateSequence':'', # leave empty if you are going to provide specific plate labware-position below
        'plateLabwarePositions':'', # leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 
        'lidSequence':'', # leave empty if you don´t use lid or if you are going to provide specific plate labware-positions below or ejecting to default waste
        'lidLabwarePositions':'', # leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 
        'toolSequence': cfg.core_gripper_sequence, # sequence name of the CO-RE Gripper
        'gripForce':3, # (integer) 0-9, from lowest to highest
        'gripperToolChannel':8, # specifies the higher of two consecutive integers representing the CO-RE gripper channels.
        'sequenceCounting':0, # (integer) 0=don´t autoincrement plate sequence,  1=Autoincrement
        'gripWidth':75, # (float) mm
        'gripHeight':3.0, # (float) mm
        'widthBefore':90, # (float) mm width before gripping
        'gripSpeed':5.0, # (float) mm/s. Must be supplied
        'zSpeed':50.0, # (float) mm/s. Must be supplied
        'transportMode':0, # (integer) 0=Plate only, 1=Lid only ,2=Plate with lid
        'checkPlate':0 # (integer) 
    }),

    'gripMove':('GRIP_MOVE', {
        'plateSequence':'', # leave empty if you are going to provide specific plate labware-position below
        'xAcceleration':4, # (integer) 1-5 from slowest to fastest, where 4 is default
        'plateLabwarePositions':'', # leave empty if you don´t use lid or if you are going to provide specific plate labware-positions below or ejecting to default waste
        'xDisplacement':0.0,
        'yDisplacement':0.0,
        'zDisplacement':0.0,

    }),

    'gripPlace':('GRIP_PLACE', {
        'plateSequence':'', # leave empty if you are going to provide specific plate labware-position below
        'plateLabwarePositions':'', # leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 
        'lidSequence':'', # leave empty if you don´t use lid or if you are going to provide specific plate labware-positions below or ejecting to default waste
        'lidLabwarePositions':'', # leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 
        'toolSequence':cfg.core_gripper_sequence, # sequence name of the iSWAP. leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1;
        'sequenceCounting':0, # (integer) 0=don´t autoincrement plate sequence,  1=Autoincrement
        'movementType':0, # (integer) 0=To carrier, 1=Complex movement
        'transportMode':0, # (integer) 0=Plate only, 1=Lid only ,2=Plate with lid
        'ejectToolWhenFinish':1, # (integer) 0=Off, 1=On
        'zSpeed':100.0, # (float) mm/s
        'platePressOnDistance':0.0, # (float) lift-up distance [mm] (only used if 'movement type' is set to 'complex movement'),
        'xAcceleration':4  # (integer) 1-5 from slowest to fastest, where 4 is default
    }),
    'moveSequence':('MOVE_SEQ',{

        'inputSequence':'',
        'xDisplacement':'',
        'yDisplacement':'',
        'zDisplacement':'',
    }),
    'copyLiquidClass':('COPY_LIQ_CLASS',{
        'TemplateLiquidClass':'',
        'NewLiquidClass':'',
    }),
    'setAspirateParam':('SET_ASP_PARAM',{
        'LiquidClass':'',
        'Parameter':'',
        'Value':'',
    }),
    'setDispenseParam':('SET_DISP_PARAM',{
        'LiquidClass':'',
        'Parameter':'',
        'Value':'',
    }),
    'setTipType':('SET_TIP_TYPE',{
        'LiquidClass':'',
        'TipType':'', # int from the TipType enum
    }),
    'setCorrectionCurve':('SET_CORR_CURVE',{
        'LiquidClass':'',
        'NominalArray':'',
        'CorrectedArray':'',
    }),
    'setDispenseMode':('SET_DISP_MODE',{
        'LiquidClass':'',
        'DispenseMode':'', # int from the DispenseMode enum
    }),
    'setLabwareProperty':('SET_LABWARE_PROPERTY',{
        'LabwareID':'',
        'PropertyName':'',
        'PropertyValue':'',
    }),
    'TEC_Initialize':('TEC_INIT', {

        'ControllerID':'', # (integer)
        'SimulationMode':'', # 0=False, 1=True; 
    }),
    'TEC_StartTempControl':('TEC_START', {

        'ControllerID':'', # (integer)
        'DeviceID':'', # (integer); 
    }),

    'TEC_SetTarget':('TEC_SET_TARGET', {
        'ControllerID':'', # (integer)
        'DeviceID':'', # (integer); 
        'TargetTemperature':'', # (float); 
    }),
    
    'TEC_GetTemperature':('TEC_GET_TEMPERATURE', {

        'ControllerID':'', # (integer)
        'DeviceID':'', # (integer); 
        'Selector':'', # (integer); 
    }),
    'TEC_StopTemperatureControl':('TEC_STOP', {
        'ControllerID':'', # (integer)
        'DeviceID':'', # (integer); 
    }),
    'TEC_Terminate':('TEC_TERMINATE', {

        'StopAllDevices':'', # 0=False, 1=True
    }),
    'TiltModule_Initialize':('TILT_INIT', {

        'ModuleName':'', # (string)
        'Comport':'', # (integer)
        'TraceLevel':'', # (integer)
        'Simulate': '' # 0=False, 1=True
    }),
    'TiltModule_MoveToPosition':('TILT_MOVE', {

        'ModuleName':'', # (string)
        'Angle':'' # (integer)
    }),
    'FirmwareCommand':('FIRMWARECOMMAND', {

        'FirmwareCommandList':[], # list elements as {FirmwareCommand:'', FirmwareParameter:''} 
    }),
    'BarcodeReader_Initialize':('BC_INITIALIZE',{

        'ComPort':'' # (string)
    }),
    'BarcodeReader_Read':('BC_READ',{

    }),

    'loadCarrier':('LOAD_CARRIER', {
        'carrierName': '', # (string) name of the carrier to load
        'barcodeFileName': '', # (string) name of the barcode file to load
        'barcodeReadPositions': '', # (string) path to the barcode file
    }),
    'unloadCarrier':('UNLOAD_CARRIER', {
        'carrierName': '', # (string) name of the carrier to unload
    }), 
    'pH_Initialize':('PH_INIT',{
        'Comport' : '' , # (int)
        'SimulationMode' : '' # (boolean)
    }),
    'pH_Request_Battery':('PH_REQ_BTRY',{
        'ModuleID' : '' , # (int)
    }),
    'pH_measure':('PH_MEASURE',{
        'ModuleID' : '' , # (int)
        'Temperature' : '' , # (float)
        'probePattern' : '' , # (string)
    }),
    'pH_Measure_Dynamically':('PH_MEASURE_DYN',{
        'ModuleID' : '' , # (int)
        'Temperature' : '' , # (float)
        'Precision' : '' , # (float)
        'Timeout' : '' , # (int)
        'probePattern' : '' , # (string)
    }),
    'pH_Request_Calibration':('PH_REQ_CALIBRATION',{
        'ModuleID' : '' , # (int)
        'ProbeNumber' : '' , # (int)
    }),
    'pH_Request_Probe_Data':('PH_REQ_PROBE_DATA',{
        'ModuleID' : '' , # (int)
    }),
    'pH_Request_Technical_Data':('PH_REQ_TECH_DATA',{
        'ModuleID' : '' , # (int)
        'HardwareNumber' : '' , # (int)
    }),
    'pH_Calibrate':('PH_CALIBRATE',{
        'ModuleID' : '' , # (int)
        'CalibrationLevel' : '' , # (int)
        'CalibrationValue' : '' , # (flt)
        'CalibrationTemperature' : '' , # (flt)
        'probePattern' : '' , # (str)
    }),
    'pH_Calibrate_Dynamically':('PH_CALIBRATE_DYN',{
        'ModuleID' : '' , # (int)
        'Variance' : '' , # (int)
        'Timeout' : '' , # (int)
        'CalibrationLevel' : '' , # (int)
        'CalibrationValue' : '' , # (flt)
        'CalibrationTemperature' : '' , # (flt)
        'probePattern' : '' , # (str)
    }),
    
    'pH_Sleep':('PH_SLEEP',{
        'ModuleID' : '' , # (int)
    }),
    'pH_Terminate':('PH_TERM',{
        'ModuleID' : '' , # (int)
    }),
    'pH_Wakeup':('PH_WAKEUP',{
        'ModuleID' : '' , # (int)
    }),
    
    'pH_Washer_Initialize':('PH_WASHER_INIT',{
        'Comport' : '' , # (int)
        'SimulationMode' : '' , # (bln)
    }),
    'pH_Washer_Set_Trace':('PH_WASHER_SET_TRC',{
        'ModuleID' : '' , # (int)
        'TraceLevel' : '' , # (int)
    }),
    'pH_Washer_Wash':('PH_WASHER_WASH',{
        'ModuleID' : '' , # (int)
        'CycleNumber' : '' , # (int)
    }),
    'pH_Washer_Terminate':('PH_WASHER_TERM',{
        'ModuleID' : '' , # (int)
    }),
    'pH_Dryer_Initialize':('PH_DRYER_INIT',{
        'Comport' : '' , # (int)
        'SimulationMode' : '' , # (bln)
    }),
    'pH_Dryer_Set_Trace':('PH_DRYER_SET_TRC',{
        'ModuleID' : '' , # (int)
        'TraceLevel' : '' , # (int)
    }),
    'pH_Start_Drying':('PH_DRYER_START',{
        'ModuleID' : '' , # (int)
    }),
    'pH_Stop_Drying':('PH_DRYER_STOP',{
        'ModuleID' : '' , # (int)
    }),
    'pH_Dryer_Terminate':('PH_DRYER_TERM',{
        'ModuleID' : '' , # (int)
    }),
    
    'HHS_BeginMonitoring':('HHS_BEGIN_MONITORING',{
        'deviceNumber' : '' , # (int)
        'shakingToleranceRange' : '' , # (int)
        'sampleInterval' : '' , # (int)
        'action' : '' , # (int)
    }),
    'HHS_CreateStarDevice':('HHS_CREATE_STAR_DEVICE',{
        'starDevice': '', # (str)
        'usedNode': '' # (int [1,2])
    }),
    'HHS_CreateUSBDevice':('HHS_CREATE_USB_DEVICE',{
        'usedNode': '' # (int [1,8])
    }),
    'HHS_EndMonitoring':('HHS_END_MONITORING', {
        'deviceNumber': '' # (int)
    }),
    'HHS_GetFirmwareVersion':('HHS_GET_FIRMWARE_VERSION', {
        'deviceNumber': '' # (int)
    }),
    'HHS_GetSerialNumber':('HHS_GET_SERIAL_NUM', {
        'deviceNumber': '' # (int)
    }),
    'HHS_GetShakerParameter':('HHS_GET_SHAKER_PARAM',{
        'deviceNumber': '' # (int)
    }),
    'HHS_GetShakerSpeed':('HHS_GET_SHAKER_SPEED',{
        'deviceNumber': '' # (int)
    }),
    'HHS_GetTempParameter':('HHS_GET_TEMP_PARAM', {
        'deviceNumber': '' # (int)
    }),
    'HHS_GetTemperature':('HHS_GET_TEMP', {
        'deviceNumber': '' # (int)
    }),
    'HHS_GetTemperatureState':('HHS_GET_TEMP_STATE', {
        'deviceNumber': '' # (int)
    }),
    'HHS_SendFirmwareCommand':('HHS_SEND_FIRMWARE_CMD', {
        'deviceNumber': '', # (int)
        'command': '', # (str)
        'parameter': '' # (str)
    }),
    'HHS_SetPlateLock':('HHS_SET_PLATE_LOCK', {
        'deviceNumber': '', #(int)
        'plateLock': '' #(int_bool)
    }),
    'HHS_SetShakerParameter':('HHS_SET_SHAKER_PARAM', {
        'deviceNumber': '', # (int)
        'shakingDirection': '', # (int_bool)
        'shakingAccRamp': '' # (int [630, 12500])
    }),
    'HHS_SetSimulation':('HHS_SET_SIMULATION', {
        'simulate': '' # (int_bool)
    }),
    'HHS_SetTempParameter':('HHS_SET_TEMP_PARAM', {
        'deviceNumber': '', #(int)
        'startTimeout': '', # (int [1, 2500])
        'toleranceRange': '', # (float [0.0, 105.0])
        'securityRange': '' #(float [0.0, 100.0])
    }),
    'HHS_SetUSBTrace':('HHS_SET_USB_TRC', {
        'trace': '' # (int_bool)
    }),
    'HHS_StartAllShaker':('HHS_START_ALL_SHAKER', {
        'shakingSpeed': '', #(int [30, 2500])
    }),
    'HHS_StartAllShakerTimed':('HHS_START_ALL_SHAKER_TIMED', {
        'shakingSpeed': '', # (int [30, 2500])
        'shakingTime': '' # (int [1, 30000])
    }),
    'HHS_StartShaker':('HHS_START_SHAKER', {
        'deviceNumber': '', #(int)
        'shakingSpeed': '' # (int [30, 2500])
    }),
    'HHS_StartShakerTimed':('HHS_START_SHAKER_TIMED', {
        'deviceNumber': '', # (int)
        'shakingSpeed': '', # (int [30, 2500])
        'shakingTime': '' #(int [1, 30000])
    }),
    'HHS_StartTempCtrl':('HHS_START_TEMP_CTRL', {
        'deviceNumber': '', # (int)
        'temperature': '', # (float [0.0, 105.0])
        'waitForTempReached': '' #(int_bool)
    }),
    'HHS_StopAllShaker':('HHS_STOP_ALL_SHAKER', {
    }),
    'HHS_StopShaker':('HHS_STOP_SHAKER', {
        'deviceNumber': '' # (int)
    }),
    'HHS_StopTempCtrl':('HHS_STOP_TEMP_CTRL', {
        'deviceNumber': '', #(int)
    }),
    'HHS_Terminate':('HHS_TERMINATE', {
    }),
    'HHS_WaitForShaker':('HHS_WAIT_FOR_SHAKER', {
        'deviceNumber': '' # (int)
    }),
    'HHS_WaitForTempCtrl':('HHS_WAIT_FOR_TEMP_CTRL', {
        'deviceNumber': '' #(int)
    }),
    'ODTC_Abort':('ODTC_ABORT', {
        'DeviceID':'', # (integer)
        'LockID':'', # (integer); 
    }),
    'ODTC_Connect':('ODTC_CONNECT', {
        'LocalIP':'', # (string)
        'DeviceIP':'', # (string)
        'DevicePort':'', # (string)
        'SimulationMode':'', # (boolean)
        
    }),
    'ODTC_Initialize':('ODTC_INIT', {
        'DeviceID':'', # (integer)
        'LockID':'', # (string); 
    }),
    'ODTC_CloseDoor':('ODTC_CLOSE', {
        'DeviceID':'', # (integer)
        'LockID':'', # (string); 
    }),
    'ODTC_DownloadProtocol':('ODTC_PRTCL', {
        'DeviceID':'', # (integer)
        'LockID':'', # (string); 
        'ProtocolFile':'',
    }),
    'ODTC_EvaluateError':('ODTC_EVAL', {
        'DeviceID':'', # (integer)
        'LockID':0, # (string); 
    }),
    'ODTC_ExecuteMethod':('ODTC_EXCT', {
        'DeviceID':'', # (integer)
        'LockID':0, # (integer);
        'MethodName':'', # (string)
        'Priority':'', # (integer); 
    }),
    'ODTC_GetStatus':('ODTC_STATUS', {
        'DeviceID':'', # (integer)
    }),
    'ODTC_OpenDoor':('ODTC_OPEN', {
        'DeviceID':'', # (integer)
        'LockID':0, # (integer);
    }),
    'ODTC_ReadActualTemperature':('ODTC_READ', {
        'DeviceID':'', # (integer)
        'LockID':0, # (integer);
    }),
    'ODTC_Reset':('ODTC_RESET', {
        'DeviceID':'', # (integer)
        'LockID':0, # (integer);
        'SimulationMode': '', # (boolean)
        'TimeToWait': '', # 0=False, 1=True
        'strDeviceID': '', # (string)
        'PMSID': '', # (string)
    }),
    'ODTC_StopMethod':('ODTC_STOP', {
        'DeviceID':'', # (integer)
        'LockID':0, # (integer);
    }),
    'ODTC_Terminate':('ODTC_TERM', {
        'DeviceID':'', # (integer)
    }),
    'Centrifuge_Initialize':('CENT_INIT', {
        'Label':'', # (str)
        'NodeName':'', # (str)
        'SimulationMode':'', # (bln)
        'AlwaysInitialize':'', # (bln)

    }),
    'Centrifuge_Centrifuge':('CENT_CENT', {
        'Label':'', # (str)
        'CloseCoverAtEnd':'', # (bln)
        'PresentPosition':'', # (int)
        'Direction':'', # (int)
        'ArraySpeed':'', # (str)
        'ArrayDuration':'', # (str)
        'ArrayAcceleration':'', # (str)
        'Deceleration':'', # (int)
    }),
    'Centrifuge_Open':('CENT_OPEN', {
        'Label':'', # (str)
    }),
    'Centrifuge_Close':('CENT_CLOSE', {
        'Label':'', # (str)
    }),
    'Centrifuge_Stop':('CENT_STOP', {
        'Label':'', # (str)
        'Deceleration':'', # (int)
    }),
    'Centrifuge_Terminate':('CENT_TERM', {
        'Label':'', # (str)
    }),
    'Centrifuge_Start':('CENT_START', {
        'Label':'', # (str)
        'Direction':'', # (int)
        'Speed':'', # (int)
        'Deceleration':'', # (int)
        'MaxRunTime':'', # (int)
    }),
    'Centrifuge_GetStatus':('CENT_STATUS', {
        'Label':'', # (str)
    }),
    'HiG_Connect':('HIG_CONNECT', {
        'DeviceID':'', # (str)
        'AdapterDeviceID':'', # (str)
        'SimulationMode':'', # (bln)
    }),
    'HiG_Disconnect':('HIG_DISCONNECT', {
    }),
    'HiG_Home':('HIG_HOME', {
    }),
    'HiG_Spin':('HIG_SPIN', {
        'RotationalGs':'', # (flt)
        'AccelPercent':'', # (flt)
        'DecelPercent':'', # (flt)
        'TimeSeconds':'', # (flt)
    }),
    'HiG_SpinAndWait':('HIG_SPINWAIT', {
        'RotationalGs':'', # (flt)
        'AccelPercent':'', # (flt)
        'DecelPercent':'', # (flt)
        'TimeSeconds':'', # (flt)
    }),
    'HiG_OpenShield':('HIG_OPEN', {
        'BucketIndex':'', # (int)
    }),
    'HiG_CloseShield':('HIG_CLOSE', {
    }),
    'HiG_IsSpinning':('HIG_SPINNING', {
    }),
    'HiG_AbortSpin':('HIG_ABORT', {
    }),
    'MPE2_ConnectIP':('MPE2_IP', {
        'InstrumentName':'', # (str)
        'PortNumber':'', # (str)
        'SimulationMode':'', # (bln)
        'Options':'', # (int)
    }),
    'MPE2_ConnectCOM':('MPE2_COM', {
        'ComPort':'', # (str)
        'BaudRate':'', # (str)
        'SimulationMode':'', # (bln)
        'Options':'', # (int)
    }),
    'MPE2_ClampFilterPlate':('MPE2_CLAMP', {
        'DeviceID':'', # (int)
    }),
    'MPE2_CollectionPlatePlaced':('MPE2_COL_PLACED', {
        'DeviceID':'', # (int)
        'CollectionPlateHeight':'', # (flt)
        'OffsetFromNozzles':'', # (flt)

    }),
    'MPE2_CollectionPlateRemoved':('MPE2_COL_REMOVED', {
        'DeviceID':'', # (int)
    }),
    'MPE2_Disconnect':('MPE2_DISCONNECT', {
        'DeviceID':'', # (int)
    }),
    'MPE2_Initialize':('MPE2_INIT', {
        'DeviceID':'', # (int)
    }),
    'MPE2_InitializeWithParams':('MPE2_INIT_PARAMS', {
        'DeviceID':'', # (int)
        'Smart':'', # (bln)
        'WasteContainerID':'', # (int)
        'VacuumRunTime':'', # (int)
        'DisableVacuumCheck':'', # (bln)
    }),
    'MPE2_FilterPlatePlaced':('MPE2_FIL_PLACED', {
        'DeviceID':'', # (int)
        'FilterHeight':'', # (flt)
        'NozzleHeight':'', # (flt)

    }),
    'MPE2_FilterPlateRemoved':('MPE2_FIL_REMOVED', {
        'DeviceID':'', # (int)
    }),
    'MPE2_ProcessFilterToCollectionPlate':('MPE2_FIL_TO_COL', {
        'DeviceID':'', # (int)
        'ControlPoints':'', # (str)
        'ReturnPlateToIntegrationArea':'', # (bln)
    }),
    'MPE2_ProcessFilterToWasteContainer':('MPE2_FIL_TO_WASTE', {
        'DeviceID':'', # (int)
        'ControlPoints':'', # (str)
        'ReturnPlateToIntegrationArea':'', # (bln)
        'WasteContainerID':'', # (int)
        'DisableVacuumCheck':'', # (bln)
    }),
    'MPE2_RetrieveFilterPlate':('MPE2_RETRIEVE_FIL', {
        'DeviceID':'', # (int)
    }),
    'MPE2_StartMPEVacuum':('MPE2_START_VAC', {
        'DeviceID':'', # (int)
        'WasteContainerID':'', # (int)
        'DisableVacuumCheck':'', # (bln)
    }),
    'MPE2_StopVacuum':('MPE2_STOP_VAC', {
        'DeviceID':'', # (int)
    }),
    'MPE2_GetVacuumStatus':('MPE2_GET_VAC', {
        'DeviceID':'', # (int)
    }),
    'MPE2_GetPressureReadings':('MPE2_GET_PRESS', {
        'DeviceID':'', # (int)
    }),
    'MPE2_Dispense':('MPE2_DISPENSE', {
        'DeviceID':'', # (int)
        'SourceID':'', # (int)
        'WellVolume':'', # (int)
        'FlowRateAspirate':'', # (int)
        'FlowRateDispense':'', # (int)
        'NeedleOffset':'', # (int)
    }),
    'MPE2_Prime':('MPE2_PRIME', {
        'DeviceID':'', # (int)
        'SourceID':'', # (int)
        'WellVolume':'', # (flt)
        'FlowRate':'', # (flt)
        'WasteContainerID':'', # (int)
    }),
    'MPE2_Flush':('MPE2_FLUSH', {
        'DeviceID':'', # (int)
        'WellVolume':'', # (flt)
        'FlowRate':'', # (flt)
        'WasteContainerID':'', # (int)
    }),
    'MPE2_Evaporate':('MPE2_EVAP', {
        'DeviceID':'', # (int)
        'PlateHeight':'', # (flt)
        'NeedleOffset':'', # (flt)
        'WellDepth':'', # (flt)
        'EvaporatorTravelDistance':'', # (flt)
        'EvaporateTime':'', # (flt)
    }),
    'MPE2_EvaporateWithRate':('MPE2_EVAP_RATE', {
        'DeviceID':'', # (int)
        'PlateHeight':'', # (flt)
        'NeedleOffset':'', # (flt)
        'WellDepth':'', # (flt)
        'EvaporatorTravelDistance':'', # (flt)
        'EvaporateTime':'', # (flt)
        'FollowRate':'', # (flt)
    }),
    'MPE2_EvaporateEnd':('MPE2_EVAP_END', {
        'DeviceID':'', # (int)
        'Timeout':'', # (int)
    }),
    'MPE2_GetTemperatureRange':('MPE2_TEMP_RANGE', {
        'DeviceID':'', # (int)
    }),
    'MPE2_GetHeaterStatus':('MPE2_HEATER_STATUS', {
        'DeviceID':'', # (int)
        'Reset':'', # (bln)
    }),
    'MPE2_GetHeaterRange':('MPE2_TEMP_RANGE', {
        'DeviceID':'', # (int)
        'Reset':'', # (bln)
    }),
    'MPE2_GetSourceConfiguration':('MPE2_GET_SOURCE_CONFIG', {
        'DeviceID':'', # (int)
    }),
    'MPE2_SetSourceConfiguration':('MPE2_SET_SOURCE_CONFIG', {
        'DeviceID':'', # (int)
    }),
    'MPE2_StartContainerCalibration':('MPE2_START_CAL', {
        'DeviceID':'', # (int)
        'SourceID':'', # (int)
        'Volume':'', # (flt)
    }),
    'MPE2_GetContainerCalibration':('MPE2_GET_CAL', {
        'DeviceID':'', # (int)
        'SourceID':'', # (int)
        'Volume':'', # (flt)
    }),
    'MPE2_MeasureEmptyContainer':('MPE2_MEAS_EMPTY', {
        'DeviceID':'', # (int)
        'SourceID':'', # (int)
    }),
    'MPE2_MeasureFullContainer':('MPE2_MEAS_FULL', {
        'DeviceID':'', # (int)
        'SourceID':'', # (int)
    }),
    'MPE2_SaveContainerCalibration':('MPE2_SAVE_CAL', {
        'DeviceID':'', # (int)
        'SourceID':'', # (int)
    }),
    'pH_Controller_Initialize':('PHC_INIT', {
        'PortNumber':'', # (int)
    }),
    'pH_Controller_Terminate':('PHC_TERM', {
        'ModuleID':'', # (int)
    }),
    'pH_Controller_Calibrate':('PHC_CAL', {
        'ModuleID':'', # (int)
        'seqModule':'', # (str)
        'seqCalibration1':'', # (str)
        'seqCalibration2':'', # (str)
        'seqReference':'', # (str)
        'MeasureTime':'', # (int)
        'CalibrationTime':'', # (int)
        'MeasureHeight':'', # (flt)
        'CalibrationValue1':'', # (flt)
        'CalibrationValue2':'', # (flt)
        'CalibrationValueRef':'', # (flt)
        'TempSoln1':'', # (flt)
        'TempSoln2':'', # (flt)
        'TempSolnRef':'', # (flt)
        'CalibrateDynamically':'', # (bln)
    }),
    'pH_Controller_MeasureCycle':('PHC_MEASURE_CYCLE', {
        'ModuleID':'', # (int)
        'seqMeasurement':'', # (str)
        'MeasurePositions':'', # (str)
        'MeasureHeight':'', # (flt)
        'ProbePattern':'', # (str)
        'MeasureTime':'', # (int)
        'Temperature':'', # (flt)
    }),
    'pH_Controller_SetParameters':('PHC_SET_PARAMS', {
        'ModuleID':'', # (int)
        'seqGripper':'', # (str)
        'seqWashPosition':'', # (flt)
        'seqDryPosition':'', # (str)
        'TransportChannel':'', # (int)
        'WashCycles':'', # (int)
        'DryCycles':'', # (int)
        'DryTime':'', # (int)
    }),

    'pH_Controller_Dry':('PHC_DRY', {
        'ModuleID':'', # (int)
    }),
    'pH_Controller_Wash':('PHC_WASH', {
        'ModuleID':'', # (int)
    }),
    'pH_Controller_Pickup':('PHC_PICKUP', {
        'ModuleID':'', # (int)
        'seqModule':'', # (str)

    }),
    'pH_Controller_Park':('PHC_PARK', {
        'ModuleID':'', # (int)
        'seqModule':'', # (str)
    }),
    'pH_Controller_LoadLastConfig':('PHC_LOAD', {
    }),
    
    'pH_Controller_SaveLastConfig':('PHC_SAVE', {
        'BluetoothPort':'', # (int)
        'NumWashCycles':'', # (int)
        'NumDryCycles':'', # (int)
        'DryTime':'', # (int)
    }),

}


"""All of the command names supported out of the box, mapped to their default params.

On module load, defaults_by_cmd is parsed into `HamiltonCmdTemplate`s, which are injected into the global package namespace under the first element of the values of this dict (strings in all caps). This is so that they can be imported directly from `pyhamilton` as module-level variables, while avoiding circular imports.

Example:

```
from pyhamilton import INITIALIZE
```


INITIALIZE

- initializeAlways (int)

    0=only initialize components not already initialized, 1=always reinitialize all robot components

    Default: 0 





PICKUP

- tipSequence (string)

    leave empty if you are going to provide specific labwarePositions below

    Default: ''

- labwarePositions (string)

    leave empty if you are going to provide a sequence name above.'LabwareId1, positionId1; LabwareId2,positionId2; ....'

    Default: ''

- channelVariable (string)

    channel pattern e.g. '11110000'

    Default: _channel_patt_16

- sequenceCounting (integer)

    0=don´t autoincrement,  1=Autoincrement

    Default: 0

- channelUse (integer)

    1=use all sequence positions (no empty wells), 2=keep channel pattern

    Default: 1



EJECT

- wasteSequence (string)

    leave empty if you are going to provide specific labware-positions below or ejecting to default waste

    Default: ''

- labwarePositions (string)

    leave empty if you are going to provide a sequence name above.'LabwareId1, positionId1; LabwareId2,positionId2; ....'

    Default: ''

- channelVariable (string)

    channel pattern e.g. "11110000"

    Default: _channel_patt_16

- sequenceCounting (integer)

    0=don´t autoincrement,  1=Autoincrement.  Value omitted if ejecting to default waste

    Default: 0

- channelUse (integer)

    1=use all sequence positions (no empty wells), 2=keep channel pattern

    Default: 1

- useDefaultWaste (integer)

    0=eject to custom waste sequence,  1=Use default waste

    Default: 0





ASPIRATE

- aspirateSequence (string)

    leave empty if you are going to provide specific labware-positions below

    Default: ''

- labwarePositions (string)

    leave empty if you are going to provide a sequence name above. 'LabwareId1, positionId1; LabwareId2,positionId2; ....'

    Default: ''

- volumes (float or string)

    enter a single value used for all channels or enter an array of values for each channel like [10.0,15.5,11.2]

    Default: None

- channelVariable (string)

    channel pattern e.g. "11110000"

    Default: _channel_patt_16

- liquidClass (string)

    Default: None

- sequenceCounting (integer)

    0=don´t autoincrement,  1=Autoincrement

    Default: 0

- channelUse (integer)

    1=use all sequence positions (no empty wells), 2=keep channel pattern

    Default: 1

- aspirateMode (integer)

    0=Normal Aspiration, 1=Consecutive (don´t aspirate blowout), 2=Aspirate all 

    Default: 0

- capacitiveLLD (integer)

    0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From labware definition

    Default: 0

- pressureLLD (integer)

    0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From liquid class definition

    Default: 0

- liquidFollowing (integer)

    0=Off , 1=On

    Default: 0

- submergeDepth (float)

    mm of immersion below liquid´s surface to start aspiration when using LLD

    Default: 2.0

- liquidHeight (float)

    mm above container´s bottom to start aspiration when not using LLD

    Default: 1.0

- maxLLdDifference (float)

    max mm height different between cLLD and pLLD detected liquid levels

    Default: 0.0

- mixCycles (integer)

    number of mixing cycles (1 cycle = 1 asp + 1 disp)

    Default: 0

- mixPosition (float)

    additional immersion mm below aspiration position to start mixing

    Default: 0.0

- mixVolume (float)

    mix volume

    Default: 0.0

- airTransportRetractDist (float)

    mm to move up in Z after finishing the aspiration at a fixed height before aspirating 'transport air'

    Default: 10.0

- touchOff (integer)

    0=Off , 1=On

    Default: 0

- aspPosAboveTouch (float)

    mm to move up in Z after touch off detects the bottom before aspirating liquid

    Default: 0.0





DISPENSE

- dispenseSequence (string)

    leave empty if you are going to provide specific labware-positions below

    Default: ''

- labwarePositions (string)

    leave empty if you are going to provide a sequence name above. 'LabwareId1, positionId1; LabwareId2,positionId2; ....'

    Default: ''

- volumes (float or string)

    enter a single value used for all channels or enter an array of values for each channel like [10.0,15.5,11.2]

    Default: None

- channelVariable (string)

    channel pattern e.g. "11110000"

    Default: _channel_patt_16

- liquidClass (string)

    Default: None

- sequenceCounting (integer)

    0=don´t autoincrement,  1=Autoincrement

    Default: 0

- channelUse (integer)

    1=use all sequence positions (no empty wells), 2=keep channel pattern

    Default: 1

- dispenseMode (integer)

    0=Jet Part, 1=Jet Empty, 2=Surface Part, 3=Surface Empty, 4=Jet Drain tip, 8=From liquid class, 9=Blowout tip

    Default: 8

- capacitiveLLD (integer)

    0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From labware definition

    Default: 0

- liquidFollowing (integer)

    0=Off , 1=On

    Default: 0

- submergeDepth (float)

    mm of immersion below liquid´s surface to start dispense when using LLD

    Default: 2.0



- liquidHeight (float)

    mm above container´s bottom to start dispense when not using LLD

    Default: 1.0

- mixCycles (integer)

    number of mixing cycles (1 cycle = 1 asp + 1 disp)

    Default: 0

- mixPosition (float)

    additional immersion mm below dispense position to start mixing

    Default: 0.0

- mixVolume (float)

    mix volume

    Default: 0.0

- airTransportRetractDist (float)

    mm to move up in Z after finishing the dispense at a fixed height before aspirating 'transport air'

    Default: 10.0

- touchOff (integer)

    0=Off , 1=On

    Default: 0

- dispPositionAboveTouch (float)

    mm to move up in Z after touch off detects the bottom, before dispense

    Default: 0.0

- zMoveAfterStep (integer)

    0=normal, 1=Minimized (Attention!!! this depends on labware clearance height, can crash). 

    Default: 0

- sideTouch (integer)

    0=Off , 1=On

    Default: 0





PICKUP96

- tipSequence (string)

    leave empty if you are going to provide specific labware-positions below

    Default: ''

- labwarePositions (string)

    leave empty if you are going to provide a sequence name above. 'LabwareId1, positionId1; LabwareId2,positionId2; ....' Must contain 96 values

    Default: ''

- channelVariable (string)

    channel Variable e.g. "11110000...." . Must contain 96 values

    Default: _channel_patt_96

- sequenceCounting (integer)

    0=don´t autoincrement,  1=Autoincrement

    Default: 0

- reducedPatternMode (integer)

    0=All (not reduced), 1=One channel, 2=One row  3=One column

    Default: 0





EJECT96

- wasteSequence (string)

    leave empty if you are going to provide specific labware-positions below or ejecting to default waste

    Default: ''

- labwarePositions (string)

    leave empty if you are going to provide a sequence name above. 'LabwareId1, positionId1; LabwareId2,positionId2; ....'

    Default: ''

- channelVariable (string)

    channel Variable e.g. "11110000...." . Must contain 96 values

    Default: _channel_patt_96

- sequenceCounting (integer)

    0=don´t autoincrement,  1=Autoincrement.  Value omitted if ejecting to default waste

    Default: 0

- tipEjectToKnownPosition (integer)

    0=Eject to specified sequence position,  1=Eject on tip pick up position, 2=Eject on default waste

    Default: 0





ASPIRATE96

- aspirateSequence (string)

    leave empty if you are going to provide specific labware-positions below

    Default: ''

- labwarePositions (string)

    leave empty if you are going to provide a sequence name above. LabwareId1, positionId1; LabwareId2,positionId2; ....

    Default: ''

- aspirateVolume (float)

    single volume used for all channels in the head. There´s no individual control of each channel volume in multi-probe heads.

    Default: None

- channelVariable (string)

    channel Variable e.g. "11110000...." . Must contain 96 values

    Default: _channel_patt_96

- liquidClass (string)

    Default: None

- sequenceCounting (integer)

    0=don´t autoincrement,  1=Autoincrement

    Default: 0

- aspirateMode (integer)

    0=Normal Aspiration, 1=Consecutive (don´t aspirate blowout), 2=Aspirate all 

    Default: 0

- capacitiveLLD (integer)

    0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From labware definition

    Default: 0

- liquidFollowing (integer)

    0=Off , 1=On

    Default: 0

- submergeDepth (float)

    mm of immersion below liquid´s surface to start aspiration when using LLD

    Default: 2.0

- liquidHeight (float)

    mm above container´s bottom to start aspiration when not using LLD

    Default: 1.0

- mixCycles (integer)

    number of mixing cycles (1 cycle = 1 asp + 1 disp)

    Default: 0

- mixPosition (float)

    additional immersion mm below aspiration position to start mixing

    Default: 0.0

- mixVolume (float)

    mix volume

    Default: 0.0

- airTransportRetractDist (float)

    mm to move up in Z after finishing the aspiration at a fixed height before aspirating 'transport air'

    Default: 10.0





DISPENSE96

- dispenseSequence (string)

    leave empty if you are going to provide specific labware-positions below

    Default: ''

- labwarePositions (string)

    leave empty if you are going to provide a sequence name above. LabwareId1, positionId1; LabwareId2,positionId2; ....

    Default: ''

- dispenseVolume (float)

    single volume used for all channels in the head. There´s no individual control of each channel volume in multi-probe heads.

    Default: None

- channelVariable (string)

    channel Variable e.g. "11110000...." . Must contain 96 values

    Default: _channel_patt_96

- liquidClass (string)

    Default: None

- sequenceCounting (integer)

    0=don´t autoincrement,  1=Autoincrement

    Default: 0

- dispenseMode (integer)

    0=Jet Part, 1=Jet Empty, 2=Surface Part, 3=Surface Empty,4=Jet Drain tip, 8=From liquid class, 9=Blowout tip

    Default: 8

- capacitiveLLD (integer)

    0=Off, 1=Max, 2=High, 3=Mid, 4=Low, 5=From labware definition

    Default: 0

- liquidFollowing (integer)

    0=Off , 1=On

    Default: 0

- submergeDepth (float)

    mm of immersion below liquid´s surface to start dispense when using LLD

    Default: 2.0

- liquidHeight (float)

    mm above container´s bottom to start dispense when not using LLD

    Default: 1.0

- mixCycles (integer)

    number of mixing cycles (1 cycle = 1 asp + 1 disp)

    Default: 0

- mixPosition (float)

    additional immersion mm below dispense position to start mixing

    Default: 0.0

- mixVolume (float)

    mix volume

    Default: 0.0

- airTransportRetractDist (float)

    mm to move up in Z after finishing the dispense at a fixed height before aspirating 'transport air'

    Default: 10.0

- zMoveAfterStep (integer)

    0=normal, 1=Minimized (Attention!!! this depends on labware clearance height, can crash). 

    Default: 0

- sideTouch (integer)

    0=Off , 1=On

    Default: 0





ISWAP_GET

plateSequence

    leave empty if you are going to provide specific plate labware-position below

    Default:''

- plateLabwarePositions (string)

    leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 

    Default: ''

- lidSequence (string)

    leave empty if you don´t use lid or if you are going to provide specific plate labware-positions below or ejecting to default waste

    Default: ''

- lidLabwarePositions (string)

    leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 

    Default: ''

- toolSequence (string)

    sequence name of the iSWAP. leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1;

    Default: ''

- sequenceCounting (integer)

    0=don´t autoincrement plate sequence,  1=Autoincrement

    Default: 0

- movementType (integer)

    0=To carrier, 1=Complex movement

    Default: 0

- transportMode (integer)

    0=Plate only, 1=Lid only ,2=Plate with lid

    Default: 0

- gripForce (integer)

    2 (minimum) ... 9 (maximum)

    Default: 4

- inverseGrip (integer)

    0=Off, 1=On

    Default: 0

- collisionControl (integer)

    0=Off, 1=On

    Default: 0

- gripMode (integer)

    0=Small side, 1=Large side

    Default: 1

- retractDistance (float)

    retract distance [mm] (only used if 'movement type' is set to 'complex movement')

    Default: 0.0

- liftUpHeight (float)

    lift-up distance [mm] (only used if 'movement type' is set to 'complex movement')

    Default: 20.0

- gripWidth (float)

    grip width when closed [mm]

    Default: 123.7

- tolerance (float)

    tolerance [mm]

    Default: 2.0

- gripHeight (float)

    height to grip above bottom of labware [mm]

    Default: 3.0

- widthBefore (float)

    grip width when opened before grip [mm]

    Default: 130.0





ISWAP_PLACE

- plateSequence (string)

    leave empty if you are going to provide specific plate labware-position below

    Default: ''

- plateLabwarePositions (string)

    leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 

    Default: ''

- lidSequence (string)

    leave empty if you don´t use lid or if you are going to provide specific plate labware-positions below or ejecting to default waste

    Default: ''

- lidLabwarePositions (string)

    leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1; 

    Default: ''

- toolSequence (string)

    sequence name of the iSWAP. leave empty if you are going to provide a plate sequence name above. LabwareId1, positionId1;

    Default: ''

- sequenceCounting (integer)

    0=don´t autoincrement plate sequence,  1=Autoincrement

    Default: 0

- movementType (integer)

    0=To carrier, 1=Complex movement

    Default: 0

- transportMode (integer)

    0=Plate only, 1=Lid only ,2=Plate with lid

    Default: 0

- collisionControl (integer)

    0=Off, 1=On

    Default: 0

- retractDistance (float)

    retract distance [mm] (only used if 'movement type' is set to 'complex movement')

    Default: 0.0

- liftUpHeight (float)

    lift-up distance [mm] (only used if 'movement type' is set to 'complex movement')

    Default: 20.0





HEPA

- deviceNumber (integer)

    COM port number of fan

    Default: _fan_port

- persistant (integer)

    0=don´t keep fan running after method exits, 1=keep settings after method exits

    Default: 1

- fanSpeed (float)

    set percent of maximum fan speed

    Default: None

- simulate (integer)

    0=normal mode, 1=use HxFan simulation mode

    Default: 0 





WASH96_EMPTY

- refillAfterEmpty (integer)

    0=Don't refill, 1=Refill both chambers, 2=Refill chamber 1 only, 3=Refill chamber 2 only

    Default: 0

- chamber1WashLiquid (integer)

    0=Liquid 1 (red container), 1=liquid 2 (blue container)

    Default: 0

- chamber1LiquidChange (integer)

    0=No, 1=Yes TODO: What does this mean?

    Default: 0

- chamber2WashLiquid (integer)

    0=Liquid 1 (red container), 1=liquid 2 (blue container)

    Default: 0

- chamber2LiquidChange (integer)

    0=No, 1=Yes TODO: What does this mean?

    Default: 0


"""



================================================
FILE: pyhamilton/defaults/defaults.json
================================================
{
  "robot_type": "STAR",
  "core_gripper_sequence": "COREGripTool_OnWaste_1000ul_0001",
   "liquids_database": "C:\\Program Files (x86)\\Hamilton\\Config\\ML_STARLiquids.mdb"
}


================================================
FILE: pyhamilton/defaults.py
================================================
from __future__ import annotations
import json
from dataclasses import dataclass, asdict, replace
from pathlib import Path
from typing import Any, Final

# ───────────────────────────── File location ─────────────────────────────
_DOTDIR: Final[Path] = Path.home() / ".pyhamilton"
_DOTDIR.mkdir(exist_ok=True)
_DEFAULTS_PATH: Final[Path] = _DOTDIR / "defaults.json"


# ───────────────────────────── Settings dataclass ─────────────────────────
@dataclass(slots=True, frozen=True)
class Defaults:
    """
    Persistent user-wide defaults for PyHamilton configuration.
    Automatically loaded from ~/.pyhamilton/defaults.json if available.
    """
    robot_type: str = "STAR"
    core_gripper_sequence: list[str] = ()
    liquids_database: str = "C:\\Program Files (x86)\\Hamilton\\Config\\ML_STARLiquids.mdb"

    # (internal) pointer to source file for debugging
    _source_file: Path | None = None


# ───────────────────────────── Internal load logic ────────────────────────
def _read_file() -> dict[str, Any]:
    if not _DEFAULTS_PATH.exists():
        return {}
    try:
        with _DEFAULTS_PATH.open("r", encoding="utf-8") as f:
            return json.load(f) or {}
    except (json.JSONDecodeError, OSError):
        return {}


# cache the singleton
_defaults_singleton: Defaults | None = None


def defaults(**overrides) -> Defaults:
    """
    Get the current default settings.
    You can optionally pass overrides to get a copy with modified fields.
    """
    global _defaults_singleton
    if _defaults_singleton is None:
        raw = _read_file()
        _defaults_singleton = Defaults(**raw, _source_file=_DEFAULTS_PATH)

    if overrides:
        return replace(_defaults_singleton, **overrides)
    return _defaults_singleton


# ───────────────────────────── Save and reload utilities ──────────────────
def save(new_defaults: Defaults | None = None) -> None:
    """
    Save the provided Defaults object (or current one) to disk.
    """
    obj = new_defaults or defaults()
    data = asdict(obj)
    data.pop("_source_file", None)
    with _DEFAULTS_PATH.open("w", encoding="utf-8") as f:
        json.dump(data, f, indent=2)


def reload() -> Defaults:
    """
    Force a reload of the defaults from disk, replacing the cached singleton.
    """
    global _defaults_singleton
    _defaults_singleton = None
    return defaults()


================================================
FILE: pyhamilton/devices/__init__.py
================================================
from .centrifuge_wrappers import *
from .hhs_wrappers import *
from .hig_wrappers import *
from .mpe_wrappers import *
from .odtc_wrappers import *
from .tec_wrappers import *
from .pH_wrappers import *

================================================
FILE: pyhamilton/devices/centrifuge_wrappers.py
================================================
# -*- coding: utf-8 -*-
"""
Created on Wed Oct  5 07:52:56 2022

@author: stefa
"""

import sys, os, time, logging, importlib
from threading import Thread

from ..interface import HamiltonInterface

from ..interface import (CENT_INIT, CENT_STATUS, CENT_CENT)


def centrifuge_initialize(ham, label, node_name, simulate, always_init):
    cmd = ham.send_command(CENT_INIT, Label = label, NodeName = node_name,
                           SimulationMode = simulate, AlwaysInitialize = always_init)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=300)


def centrifuge_get_drive_status(ham, label):
    return_fields = ['step-return2', 'step-return3', 'step-return4']
    cmd = ham.send_command(CENT_STATUS, Label = label)
    outputs = ham.wait_on_response(cmd, raise_first_exception=True, timeout=300, return_data = return_fields)
    return outputs


def centrifuge_set_run(ham, label, array_speed, array_acceleration,
                   array_duration, deceleration, close_cover, 
                   direction, present_position):
    
    if not all([201 < speed < 4200 for speed in array_speed]):
        raise ValueError('Speed must be between 201 and 4200 rpm')
    
    if not all([1000 < acc < 6500 for acc in array_acceleration]):
        raise ValueError('Acceleration must be between 1000 and 6500 rpm^2')

    if not all([0 < dur < 2700 for dur in array_duration]):
        raise ValueError('Duration must be greater than 2700 seconds')
        
    if not 1000 < deceleration < 6500:
        raise ValueError('Deceleration must be between 1000 and 6500')
        
    array_acceleration = ','.join(map(str, array_acceleration))
    array_duration = ','.join(map(str, array_duration))
    array_speed = ','.join(map(str, array_speed))

    
    cmd = ham.send_command(CENT_CENT, Label = label, ArraySpeed = array_speed, 
                           ArrayAcceleration = array_acceleration, ArrayDuration = array_duration,
                           Deceleration = deceleration, CloseCoverAtEnd = close_cover, 
                           Direction = direction, PresentPosition = present_position)
    
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=300)








================================================
FILE: pyhamilton/devices/hhs_wrappers.py
================================================
import sys, os, time, logging, importlib
from threading import Thread

from ..interface import HamiltonInterface

from ..interface import (HHS_BEGIN_MONITORING, HHS_CREATE_STAR_DEVICE, HHS_CREATE_USB_DEVICE,
    HHS_END_MONITORING, HHS_GET_FIRMWARE_VERSION, HHS_GET_SERIAL_NUM, HHS_GET_SHAKER_PARAM, HHS_GET_SHAKER_SPEED,
    HHS_GET_TEMP_PARAM, HHS_GET_TEMP, HHS_GET_TEMP_STATE, HHS_SEND_FIRMWARE_CMD, HHS_SET_PLATE_LOCK,
    HHS_SET_SHAKER_PARAM, HHS_SET_SIMULATION, HHS_SET_TEMP_PARAM, HHS_SET_USB_TRC, HHS_START_ALL_SHAKER,
    HHS_START_ALL_SHAKER_TIMED, HHS_START_SHAKER, HHS_START_SHAKER_TIMED, HHS_START_TEMP_CTRL, HHS_STOP_ALL_SHAKER,
    HHS_STOP_SHAKER, HHS_STOP_TEMP_CTRL, HHS_TERMINATE, HHS_WAIT_FOR_SHAKER, HHS_WAIT_FOR_TEMP_CTRL)

from ..resources import layout_item, Plate96, LayoutManager

std_timeout = 30

def hhs_begin_monitoring(ham, device_number, tolerance_range, interval, action):
    cmd = ham.send_command(HHS_BEGIN_MONITORING, deviceNumber = device_number, \
            shakingToleranceRange = tolerance_range, sampleInterval = interval, action = action)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)


def hhs_create_star_device(ham, star_device='ML_STAR', used_node=1):
    return_field = ['step-return2']
    cmd = ham.send_command(HHS_CREATE_STAR_DEVICE, starDevice=star_device, usedNode=used_node)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    device_number = response.return_data[0]
    return device_number


def hhs_create_usb_device(ham, used_node):
    cmd = ham.send_command(HHS_CREATE_USB_DEVICE, usedNode = used_node)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=['step-return2'])
    device_number = response.return_data[0]
    return device_number

def hhs_end_monitoring(ham, device_number):
    cmd = ham.send_command(HHS_END_MONITORING, deviceNumber = device_number)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=['step-return2'])
    monitor_result = response.return_data[0]
    return monitor_result

def hhs_get_firmware_version(ham, device_number):
    cmd = ham.send_command(HHS_GET_FIRMWARE_VERSION, deviceNumber = device_number)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=['step-return2'])
    monitor_result = response.return_data[0]
    return monitor_result

def hhs_get_serial_num(ham, device_number):
    cmd = ham.send_command(HHS_GET_SERIAL_NUM, deviceNumber = device_number)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=['step-return2'])
    serial_number = response.return_data[0]
    return serial_number

def hhs_get_shaker_param(ham, device_number):
    return_fields = ['step-return2', 'step-return3']
    cmd = ham.send_command(HHS_GET_SHAKER_PARAM, deviceNumber = device_number)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_fields)
    params = response.return_data[0:1]
    return params

def hhs_get_shaker_speed(ham, device_number):
    cmd = ham.send_command(HHS_GET_SHAKER_SPEED, deviceNumber = device_number)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=['step-return2'])
    shaker_speed = response.return_data[0]
    return shaker_speed

def hhs_get_temp_param(ham, device_number):
    return_fields = ['step-return2', 'step-return3', 'step-return4']
    cmd = ham.send_command(HHS_GET_TEMP_PARAM, deviceNumber = device_number)
    data = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_fields)
    '''***Check STAR_OEM_noFan to verify step return 4'''
    return data

def hhs_get_temp(ham, device_number):
    cmd = ham.send_command(HHS_GET_TEMP, deviceNumber = device_number)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=['step-return2'])
    temp = response.return_data[0]
    return temp

def hhs_get_temp_state(ham, device_number):
    cmd = ham.send_command(HHS_GET_TEMP_STATE, deviceNumber = device_number)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=['step-return2'])
    temp_state = response
    return temp_state

def hhs_set_simulation(ham, simulate):
    cmd = ham.send_command(HHS_SET_SIMULATION, simulate=simulate)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)






def hhs_send_firmware_cmd(ham, device_number, command, parameter):
    '''*** ValueError: Assert valid command "HHS_SendFirmwareCommand" failed: command name "TA" does not match
        Probably need to get example commands from Hamilton'''
    cmd = ham.send_command(HHS_SEND_FIRMWARE_CMD, deviceNumber=device_number, command=command, parameter=parameter)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)



def hhs_set_plate_lock(ham, device_number, plate_lock):
    cmd = ham.send_command(HHS_SET_PLATE_LOCK, deviceNumber=device_number, plateLock=plate_lock)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_set_shaker_param(ham, device_number, shaking_direction, shaking_acc_ramp):
    cmd = ham.send_command(HHS_SET_SHAKER_PARAM, deviceNumber=device_number, shakingDirection=shaking_direction, \
            shakingAccRamp=shaking_acc_ramp)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)


def hhs_set_temp_param(ham, device_number, start_timeout, tolerance_range, security_range):
    cmd = ham.send_command(HHS_SET_TEMP_PARAM, deviceNumber=device_number, startTimeout=start_timeout, \
            toleranceRange=tolerance_range, securityRange=security_range)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_set_usb_trace(ham, trace):
    cmd = ham.send_command(HHS_SET_USB_TRC, trace=trace)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)



def hhs_start_all_shaker(ham, shaking_speed):
    '''*** trace: complete with error: node not initialized'''
    cmd = ham.send_command(HHS_START_ALL_SHAKER, shakingSpeed=shaking_speed)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)


def hhs_start_all_shaker_timed(ham, shaking_speed, shaking_time):
    '''*** trace: complete with error: node not initialized'''
    cmd = ham.send_command(HHS_START_ALL_SHAKER_TIMED, shakingSpeed=shaking_speed, shakingTime=shaking_time)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_start_shaker(ham, device_number, shaking_speed):
    cmd = ham.send_command(HHS_START_SHAKER, deviceNumber=device_number, shakingSpeed=shaking_speed)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_start_shaker_timed(ham, device_number, shaking_speed, shaking_time):
    cmd = ham.send_command(HHS_START_SHAKER_TIMED, deviceNumber=device_number, shakingSpeed=shaking_speed, \
            shakingTime=shaking_time)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_start_temp_ctrl(ham, device_number, temperature, wait_for_temp_reached):
    cmd = ham.send_command(HHS_START_TEMP_CTRL, deviceNumber=device_number, temperature=temperature, \
            waitForTempReached=wait_for_temp_reached)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)



def hhs_stop_all_shakers(ham):
    '''*** trace: complete with error: node not initialized'''
    cmd = ham.send_command(HHS_STOP_ALL_SHAKER)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_stop_shaker(ham, device_number):
    cmd = ham.send_command(HHS_STOP_SHAKER, deviceNumber=device_number)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_stop_temp_ctrl(ham, device_number):
    cmd = ham.send_command(HHS_STOP_TEMP_CTRL, deviceNumber=device_number)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_terminate(ham):
    cmd = ham.send_command(HHS_TERMINATE)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_wait_for_shaker(ham, device_number):
    cmd = ham.send_command(HHS_WAIT_FOR_SHAKER, deviceNumber=device_number)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

def hhs_wait_for_temp_ctrl(ham, device_number):
    cmd = ham.send_command(HHS_WAIT_FOR_TEMP_CTRL, deviceNumber=device_number)
    ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)

class HHS:
    """Helper class for Hamilton Heater Shaker devices."""
    def __init__(self, node, sequence, lmgr: LayoutManager):
        self.node = node
        self._sequence = sequence
        self.lmgr = lmgr
        self.resource = layout_item(lmgr, Plate96, sequence)
    
    def layout_name(self):
        return self._sequence


================================================
FILE: pyhamilton/devices/hig_wrappers.py
================================================
# -*- coding: utf-8 -*-
"""
Created on Fri Feb 10 00:01:53 2023

@author: stefa
"""

import sys, os, time, logging, importlib
from threading import Thread

from ..interface import HamiltonInterface

from ..interface import (HIG_CONNECT, HIG_DISCONNECT, HIG_HOME, HIG_SPIN,
                        HIG_SPINWAIT, HIG_OPEN, HIG_CLOSE, HIG_SPINNING,
                        HIG_ABORT)

std_timeout = 5


def hig_connect(ham, device_id, adapter_device_id, simulation_mode):
    return_field = ['step-return2']
    cmd = ham.send_command(HIG_CONNECT, DeviceID=device_id, AdapterDeviceID = adapter_device_id, SimulationMode = simulation_mode)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    result = response.return_data[0]
    return result

def hig_disconnect(ham):
    cmd = ham.send_command(HIG_DISCONNECT)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)
    return response

def hig_home(ham):
    cmd = ham.send_command(HIG_HOME)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)
    result = response.return_data[0]
    return result

def hig_spin(ham, Gs, acceleration_pct, deceleration_pct, time):
    cmd = ham.send_command(HIG_SPIN, RotationalGs = Gs, AccelPercent = acceleration_pct, 
                                    DecelPercent = deceleration_pct)
    response = ham.wait_on_response(cmd, raise_first_exception=True, 
                                    timeout = time + std_timeout)
    return response

def hig_spin_and_wait(ham, Gs, acceleration, deceleration, time):
    cmd = ham.send_command(HIG_SPINWAIT)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)
    return response

def hig_open_shield(ham, bucket_index):
    cmd = ham.send_command(HIG_OPEN, BucketIndex = bucket_index)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)
    return response

def hig_close_shield(ham):
    cmd = ham.send_command(HIG_CLOSE)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)
    return response

def hig_is_spinning(ham):
    return_field = ['step-return2']
    cmd = ham.send_command(HIG_SPINNING)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    result = response.return_data[0]
    return result

def hig_home(ham):
    cmd = ham.send_command(HIG_ABORT)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout)
    return response

================================================
FILE: pyhamilton/devices/mpe_wrappers.py
================================================
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 12 18:56:58 2023

@author: stefa
"""

import sys, os, time, logging, importlib
from threading import Thread

from functools import partial

from ..interface import HamiltonInterface

from ..interface import (MPE2_IP, MPE2_COM, MPE2_CLAMP, MPE2_COL_PLACED,
                        MPE2_COL_REMOVED, MPE2_DISCONNECT, MPE2_INIT,
                        MPE2_INIT_PARAMS, MPE2_DISCONNECT, MPE2_FIL_PLACED,
                        MPE2_FIL_REMOVED, MPE2_FIL_TO_COL, MPE2_FIL_TO_WASTE,
                        MPE2_FLUSH,MPE2_EVAP, MPE2_EVAP_END, MPE2_EVAP_RATE, 
                        MPE2_PRIME, MPE2_HEATER_STATUS, MPE2_TEMP_RANGE, MPE2_GET_VAC,
                        MPE2_MEAS_EMPTY, MPE2_MEAS_FULL, MPE2_DISPENSE,
                        MPE2_GET_PRESS, MPE2_RETRIEVE_FIL, MPE2_START_VAC, MPE2_STOP_VAC)


def mpe2_connect_ip(ham, instrument_name, port_number, simulation_mode, options = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_IP, InstrumentName=instrument_name, PortNumber=port_number, SimulationMode=simulation_mode, Options=options)
    response = ham.wait_on_response(cmd, raise_first_exception=True, return_data=return_field)
    result = response.return_data[0]
    return result

def mpe2_connect_com(ham, com_port, baud_rate, simulation_mode, options = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_COM, ComPort=com_port, BaudRate=baud_rate, SimulationMode=simulation_mode, Options=options)
    response = ham.wait_on_response(cmd, raise_first_exception=True, return_data=return_field)
    return response

def mpe2_clamp_filter_plate(ham, device_id):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_CLAMP, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, return_data=return_field)
    return response

def mpe2_collection_plate_placed(ham, device_id, collection_plate_height, offset_from_nozzles):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_COL_PLACED, DeviceID=device_id, CollectionPlateHeight=collection_plate_height, OffsetFromNozzles=offset_from_nozzles)
    response = ham.wait_on_response(cmd, raise_first_exception=True, return_data=return_field)
    return response

def mpe2_collection_plate_removed(ham, device_id):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_COL_REMOVED, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, return_data=return_field)
    return response

def mpe2_disconnect(ham, device_id):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_DISCONNECT, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, return_data=return_field)
    return response

def mpe2_initialize(ham, device_id):
    cmd = ham.send_command(MPE2_INIT, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_initialize_with_params(ham, device_id, smart, waste_container_id, vacuum_run_time, disable_vacuum_check):
    cmd = ham.send_command(MPE2_INIT_PARAMS, DeviceID=device_id, Smart=smart, WasteContainerID=waste_container_id, VacuumRunTime=vacuum_run_time, DisableVacuumCheck=disable_vacuum_check)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_filter_plate_placed(ham, device_id, filter_height, nozzle_height):
    cmd = ham.send_command(MPE2_FIL_PLACED, DeviceID=device_id, FilterHeight=filter_height, NozzleHeight=nozzle_height)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_filter_plate_removed(ham, device_id):
    cmd = ham.send_command(MPE2_FIL_REMOVED, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_process_filter_to_collection_plate(ham, device_id, control_points, return_plate_to_integration_area=''):
    cmd = ham.send_command(MPE2_FIL_TO_COL, DeviceID=device_id, ControlPoints=control_points, ReturnPlateToIntegrationArea=return_plate_to_integration_area)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_process_filter_to_waste_container(ham, device_id, control_points, return_plate_to_integration_area='', waste_container_id='', disable_vacuum_check=''):
    cmd = ham.send_command(MPE2_FIL_TO_WASTE, DeviceID=device_id, ControlPoints=control_points, ReturnPlateToIntegrationArea=return_plate_to_integration_area, WasteContainerID=waste_container_id, DisableVacuumCheck=disable_vacuum_check)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_retrieve_filter_plate(ham, device_id):
    cmd = ham.send_command(MPE2_RETRIEVE_FIL, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_start_mpe_vacuum(ham, device_id, waste_container_id='', disable_vacuum_check=''):
    cmd = ham.send_command(MPE2_START_VAC, DeviceID=device_id, WasteContainerID=waste_container_id, DisableVacuumCheck=disable_vacuum_check)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_stop_vacuum(ham, device_id):
    cmd = ham.send_command(MPE2_STOP_VAC, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_get_vacuum_status(ham, device_id):
    cmd = ham.send_command(MPE2_GET_VAC, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_get_pressure_readings(ham, device_id):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_GET_PRESS, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, return_data=return_field)
    return response

def mpe2_dispense(ham, device_id, source_id, well_volume, flow_rate_aspirate, flow_rate_dispense, needle_offset):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_DISPENSE, DeviceID=device_id, SourceID=source_id, WellVolume=well_volume, FlowRateAspirate=flow_rate_aspirate, FlowRateDispense=flow_rate_dispense, NeedleOffset=needle_offset)
    response = ham.wait_on_response(cmd, raise_first_exception=True,  return_data=return_field)
    return response

def mpe2_prime(ham, device_id, source_id, well_volume, flow_rate, waste_container_id):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_PRIME, DeviceID=device_id, SourceID=source_id, WellVolume=well_volume, FlowRate=flow_rate, WasteContainerID=waste_container_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True,  return_data=return_field)
    return response

def mpe2_flush(ham, device_id, well_volume, flow_rate, waste_container_id):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_FLUSH, DeviceID=device_id, WellVolume=well_volume, FlowRate=flow_rate, WasteContainerID=waste_container_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, return_data=return_field)
    return response

def mpe2_evaporate(ham, device_id, plate_height, needle_offset, well_depth, evaporator_travel_distance, evaporate_time):
    return_field = ['step-return2']
    cmd = ham.send_command(MPE2_EVAP, DeviceID=device_id, PlateHeight=plate_height, NeedleOffset=needle_offset, WellDepth=well_depth, EvaporatorTravelDistance=evaporator_travel_distance, EvaporateTime=evaporate_time)
    response = ham.wait_on_response(cmd, raise_first_exception=True,  return_data=return_field)
    return response


def mpe2_evaporate_with_rate(ham, device_id, plate_height, needle_offset, well_depth, evaporator_travel_distance, evaporate_time, follow_rate):
    cmd = ham.send_command(MPE2_EVAP_RATE, DeviceID=device_id, PlateHeight=plate_height, NeedleOffset=needle_offset, WellDepth=well_depth, EvaporatorTravelDistance=evaporator_travel_distance, EvaporateTime=evaporate_time, FollowRate=follow_rate)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_evaporate_end(ham, device_id, timeout):
    cmd = ham.send_command(MPE2_EVAP_END, DeviceID=device_id, Timeout=timeout)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_get_temperature_range(ham, device_id):
    cmd = ham.send_command(MPE2_TEMP_RANGE, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_get_heater_status(ham, device_id, reset):
    cmd = ham.send_command(MPE2_HEATER_STATUS, DeviceID=device_id, Reset=reset)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response

def mpe2_get_heater_range(ham, device_id, reset):
    cmd = ham.send_command(MPE2_TEMP_RANGE, DeviceID=device_id, Reset=reset)
    response = ham.wait_on_response(cmd, raise_first_exception=True)
    return response





================================================
FILE: pyhamilton/devices/odtc_wrappers.py
================================================
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 23 23:25:49 2023

@author: stefa
"""

import sys, os, time, logging, importlib
from threading import Thread
from dataclasses import dataclass

from ..interface import HamiltonInterface, HamiltonResponse

from ..interface import (ODTC_ABORT, ODTC_CONNECT, ODTC_INIT, ODTC_CLOSE, 
                        ODTC_PRTCL, ODTC_EVAL, ODTC_EXCT, ODTC_STATUS, 
                        ODTC_OPEN, ODTC_READ, ODTC_RESET, ODTC_STOP, ODTC_TERM)

std_timeout = 60


def odtc_abort(ham, device_id, lock_id):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_ABORT, DeviceID=device_id, LockID=lock_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_connect(ham, simulation_mode, local_ip, device_ip, device_port = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_CONNECT, LocalIP=local_ip, DeviceIP=device_ip, DevicePort=device_port, SimulationMode=simulation_mode)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    if response == 0 and not ham.simulating:
        raise RuntimeError("Failed to connect to ODTC device")
    if ham.simulating:
        return 1  # Simulated device ID
    else:
        device_id = int(response.return_data[0])
    return device_id

def odtc_initialize(ham, device_id, lock_id = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_INIT, DeviceID=device_id, LockID=lock_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_close_door(ham, device_id, lock_id = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_CLOSE, DeviceID=device_id, LockID=lock_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_download_protocol(ham, device_id, protocol_file, lock_id = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_PRTCL, DeviceID=device_id, LockID=lock_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_evaluate_error(ham, device_id, lock_id = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_EVAL, DeviceID=device_id, LockID=lock_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_execute_protocol(ham, device_id, method_name, simulating, priority=1, lock_id = ''):
    
    if not 0 < priority < 10001:
        raise ValueError("Date provided can't be in the past")
    
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_EXCT, DeviceID=device_id, LockID=lock_id, MethodName=method_name, Priority=priority)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)

    @dataclass
    class ODTCExecuteResponse:
        duration: float     
        resultID: int
        raw: HamiltonResponse  # keep the raw object if callers need extras

    if simulating:
        return ODTCExecuteResponse(duration=0.0, resultID=0, raw=response)
    else:
        return ODTCExecuteResponse(duration=response.return_data[0], resultID=response.return_data[1], raw=response)

def odtc_get_status(ham, device_id, simulating):
    print("Checking ODTC status...")
    print(simulating)

    return_fields = ['step-return2', 'step-return3', 'step-return4', 'step-return5',
                     'step-return6', 'step-return7', 'step-return8']
    cmd = ham.send_command(ODTC_STATUS, DeviceID=device_id)
    
    @dataclass
    class ODTCStatusResponse:
        '''
        'startup', 'resetting', 'standby', 'idle', 'busy', 'paused', 'errorhandling', 
        'inerror', 'asynchpaused', 'pauserequested', 'processing', 'responsewaiting'
        '''
        state: str
        raw: HamiltonResponse # keep the raw object if callers need extras

    if ham.simulating or simulating:
        print("Simulating ODTC status as 'idle'")
        return ODTCStatusResponse(state='idle', raw=None)
    
    else:
        response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_fields)
    
        result = ODTCStatusResponse(
            state=response.return_data[0],
            raw=response
        )
        return result

def odtc_open_door(ham, device_id, lock_id = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_OPEN, DeviceID=device_id, LockID=lock_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_read_actual_temperature(ham, device_id, lock_id = ''):
    return_fields = ['step-return2', 'step-return3']
    cmd = ham.send_command(ODTC_READ, DeviceID=device_id, LockID=lock_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_fields)
    if ham.simulate:
        return ['Simulation_mode_placeholder']*len(return_fields)
    else:
        result = response.return_data
        return result

def odtc_reset(ham, device_id, simulation_mode, timeout, str_device_id = '', pms_id = '', lock_id = ''):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_RESET, DeviceID=device_id, LockID=lock_id, SimulationMode=simulation_mode, TimeToWait=timeout, strDeviceID=str_device_id, PMSID=pms_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_stop_method(ham, device_id, lock_id):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_STOP, DeviceID=device_id, LockID=lock_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_terminate(ham, device_id):
    return_field = ['step-return2']
    cmd = ham.send_command(ODTC_TERM, DeviceID=device_id)
    response = ham.wait_on_response(cmd, raise_first_exception=True, timeout=std_timeout, return_data=return_field)
    return response

def odtc_wait_for_idle(ham, device_id, simulating, check_interval=5, max_wait=3000):
    '''
    Waits until the ODTC device is in 'idle' state.
    
    Parameters:
        ham (HamiltonInterface): The Hamilton interface instance.
        device_id (int): The ID of the ODTC device.
        check_interval (int): Time in seconds between status checks.
        max_wait (int): Maximum time in seconds to wait before raising a TimeoutError.
    
    Raises:
        TimeoutError: If the device does not reach 'idle' state within max_wait time.
    '''
    if simulating:
        return
    
    start_time = time.time()
    while True:
        status = odtc_get_status(ham, device_id, simulating)
        if status.state == 'idle':
            return
        elif time.time() - start_time > max_wait:
            raise TimeoutError(f"ODTC device {device_id} did not reach 'idle' state within {max_wait} seconds.")
        time.sleep(check_interval)



================================================
FILE: pyhamilton/devices/pH_wrappers.py
================================================
# -*- coding: utf-8 -*-
"""
Created on Sun Oct  2 15:40:58 2022

@author: stefa
"""
import sys, os, time, logging, importlib
from threading import Thread

from ..interface import HamiltonInterface

from ..interface import (PH_INIT, PH_REQ_BTRY, PH_MEASURE, PH_MEASURE_DYN, PH_REQ_CALIBRATION, PH_REQ_PROBE_DATA,
                        PH_REQ_TECH_DATA, PH_CALIBRATE, PH_CALIBRATE_DYN, PH_TERM, PH_SLEEP, PH_WAKEUP, PH_WASHER_INIT,
                        PH_WASHER_WASH, PH_WASHER_TERM, PH_DRYER_INIT, PH_DRYER_START, PH_DRYER_STOP, PH_DRYER_TERM,
                        PHC_WASH, PHC_DRY, PHC_LOAD, PHC_SAVE)

from ..interface import (PHC_INIT, PHC_SET_PARAMS, PHC_PICKUP, PHC_PARK, PHC_CAL, PHC_MEASURE_CYCLE)
from ..liquid_handling_wrappers import compound_pos_str


DEFAULT_WAIT_ON_RESPONSE_TIMEOUT = 300  # seconds



### Controller functions ###

def ph_controller_initialize(ham, port_number, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PHC_INIT, PortNumber = port_number)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data=['step-return2'])
    return int(response.moduleID)

def ph_controller_parameters(ham, module_id, seq_gripper, seq_wash, seq_dry, transport_channels,wash_cycles,dry_cycles,dry_time,
                             raise_first_exception=True,
                             wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    
    cmd = ham.send_command(PHC_SET_PARAMS,
                           ModuleID = module_id,
                           seqGripper = seq_gripper,
                           seqWashPosition = seq_wash,
                           seqDryPosition = seq_dry,
                           TransportChannel = transport_channels,
                           WashCycles = wash_cycles,
                           DryCycles = dry_cycles,
                           DryTime = dry_time)
    
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)


def ph_controller_pickup(ham, module_id, seq_module, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PHC_PICKUP, ModuleID = module_id, seqModule = seq_module)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)
    
def ph_controller_park(ham, module_id, seq_module, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PHC_PARK, ModuleID = module_id, seqModule = seq_module)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)


def ph_controller_calibrate(ham, module_id, seq_module, seq_solution_1,seq_solution_2,seq_reference,
                           measure_time,calibration_time,measure_height, pH_solution_1, pH_solution_2,
                           pH_reference,temp_solution_1, temp_solution_2, temp_solution_ref, calibrate_dynamically,
                           raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    
    cmd = ham.send_command(PHC_CAL, ModuleID = module_id, seqModule = seq_module, seqCalibration1 = seq_solution_1, 
                           seqCalibration2 = seq_solution_2, seqReference = seq_reference, MeasureTime = measure_time,
                           CalibrationTime = calibration_time, MeasureHeight = measure_height, 
                           CalibrationValue1 = pH_solution_1, CalibrationValue2 = pH_solution_2, 
                           CalibrationValueRef = pH_reference, TempSoln1 = temp_solution_1, TempSoln2 = temp_solution_2,
                           TempSolnRef = temp_solution_ref, CalibrateDynamically = calibrate_dynamically)
    
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)


def ph_controller_measure_cycle(ham, module_id, pos, measure_height, probe_pattern, measure_time,temperature,
                             raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    
    return_fields = ['step-return2']
    labware_pos = compound_pos_str(pos)
    cmd = ham.send_command(PHC_MEASURE_CYCLE,
                           ModuleID = module_id,
                           MeasurePositions = labware_pos,
                           MeasureHeight = measure_height,
                           ProbePattern = probe_pattern,
                           MeasureTime = measure_time,
                           Temperature = temperature)
    
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, 
                                    timeout=wait_on_response_timeout, return_data = return_fields)
    
    pH_values = response.return_data[0].split(';')
    pH_values = [float(pH) for pH in pH_values]
    return pH_values

def ph_controller_wash(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PHC_WASH, ModuleID = module_id)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)

def ph_controller_dry(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PHC_DRY, ModuleID = module_id)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)

def ph_controller_loadconfig(ham, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    return_fields = ['step-return2', 'step-return3', 'step-return4', 'step-return5']
    cmd = ham.send_command(PHC_LOAD)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, 
                                    timeout=wait_on_response_timeout, return_data = return_fields)
    return response

def ph_controller_saveconfig(ham, bluetooth_port, num_wash_cycles, num_dry_cycles, dry_time, 
                             raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    
    cmd = ham.send_command(PHC_SAVE, BluetoothPort = bluetooth_port, NumWashCycles = num_wash_cycles,
                           NumDryCycles = num_dry_cycles, DryTime = dry_time)
    
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, 
                                    timeout=wait_on_response_timeout)
    return response

### Low-level functions ###

def ph_initialize(ham, comport, simulate, asynch=False, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_INIT, Comport = comport, SimulationMode = simulate)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data=['step-return2'])
    return int(response.moduleID)

def ph_req_battery_data(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    return_fields = ['step-return2', 'step-return3', 'step-return4', 'step-return5']
    cmd = ham.send_command(PH_REQ_BTRY, ModuleID = module_id)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data = return_fields)
    return response.return_data

def ph_measure(ham, module_id, temperature, probePattern, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    return_fields = ['step-return2', 'step-return3', 'step-return4', 'step-return5']
    cmd = ham.send_command(PH_MEASURE, ModuleID = module_id, Temperature = temperature, probePattern = probePattern)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data = return_fields)
    return response.return_data

def ph_measure_dynamic(ham, module_id, temperature, precision, timeout, probePattern, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    return_fields = ['step-return2', 'step-return3', 'step-return4', 'step-return5']
    cmd = ham.send_command(PH_MEASURE_DYN, ModuleID = module_id, Temperature = temperature,
                           Precision = precision, Timeout = timeout, probePattern = probePattern)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data = return_fields)
    return response.return_data

def ph_request_calibration(ham, module_id, probe_number, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    return_fields = ['step-return2', 'step-return3', 'step-return4', 'step-return5',
                     'step-return6', 'step-return7', 'step-return8', 'step-return9'
                     ]
    cmd = ham.send_command(PH_REQ_CALIBRATION, ModuleID = module_id, ProbeNumber = probe_number)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data = return_fields)
    return response.return_data


def ph_request_probe_data(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    return_fields = ['step-return2', 'step-return3', 'step-return4', 'step-return5',
                     'step-return6'
                     ]
    cmd = ham.send_command(PH_REQ_PROBE_DATA, ModuleID = module_id)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data = return_fields)
    return response.return_data

def ph_request_technical_data(ham, module_id, hardware_number, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    return_fields = ['step-return2', 'step-return3', 'step-return4', 'step-return5']
    cmd = ham.send_command(PH_REQ_TECH_DATA, ModuleID = module_id, HardwareNumber = hardware_number)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data = return_fields)
    return response.return_data

def ph_calibrate(ham, module_id, cal_level, cal_value, cal_temperature, probe_pattern, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_CALIBRATE, ModuleID = module_id, CalibrationLevel = cal_level,
                           CalibrationValue = cal_value, CalibrationTemperature=cal_temperature,
                           probePattern = probe_pattern)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)
    return response

def ph_calibrate_dynamically(ham, module_id, variance, timeout, cal_level, cal_value, cal_temperature, probe_pattern, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_CALIBRATE_DYN, ModuleID = module_id, Variance = variance, Timeout = timeout,
                           CalibrationLevel = cal_level, CalibrationValue = cal_value,
                           CalibrationTemperature=cal_temperature, probePattern = probe_pattern)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)
    return response


def ph_wakeup(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_WAKEUP, ModuleID = module_id)
    return ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)

def ph_sleep(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_SLEEP, ModuleID = module_id)
    return ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)

def ph_washer_initialize(ham, comport, simulate, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_WASHER_INIT, Comport = comport, SimulationMode = simulate)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data=['step-return2'])
    return int(response.moduleID)

def ph_washer_wash(ham, module_id, cycle_num, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_WASHER_WASH, ModuleID = module_id, CycleNumber = cycle_num)
    return ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)

def ph_washer_terminate(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_WASHER_TERM, ModuleID = module_id)
    return ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)

def ph_dryer_initialize(ham, comport, simulate, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_DRYER_INIT, Comport = comport, SimulationMode = simulate)
    response = ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout, return_data=['step-return2'])
    return int(response.moduleID)

def ph_dryer_start(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_DRYER_START, ModuleID = module_id)
    return ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)

def ph_dryer_stop(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_DRYER_STOP, ModuleID = module_id)
    return ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)

def ph_dryer_terminate(ham, module_id, raise_first_exception=True, wait_on_response_timeout=DEFAULT_WAIT_ON_RESPONSE_TIMEOUT):
    cmd = ham.send_command(PH_DRYER_TERM, ModuleID = module_id)
    return ham.wait_on_response(cmd, raise_first_exception=raise_first_exception, timeout=wait_on_response_timeout)



================================================
FILE: pyhamilton/devices/tec_wrappers.py
================================================
# -*- coding: utf-8 -*-
"""
Created on Tue May 30 21:44:03 2023

@author: stefa
"""
from ..interface import TEC_INIT, TEC_START, TEC_SET_TARGET, TEC_STOP, TEC_TERMINATE, TEC_GET_TEMPERATURE, HamiltonInterface
import logging

def initialize_tec(ham, controller_id, simulating):
    logging.info('Initializing TEC/CPAC ' + str(controller_id) )
    cid = ham.send_command(TEC_INIT, ControllerID=controller_id, SimulationMode=simulating)
    ham.wait_on_response(cid, raise_first_exception=True, timeout=120)

def set_temperature_target_tec(ham, target_temp, controller_id, device_id):
    logging.info('Set target temperature ' + str(controller_id) +' '+ str(device_id)+' to '+str(target_temp)+' degrees C')
    cid = ham.send_command(TEC_SET_TARGET, TargetTemperature=target_temp, ControllerID=controller_id, DeviceID=device_id)
    ham.wait_on_response(cid, raise_first_exception=True, timeout=120)

def get_temperature_tec(ham:HamiltonInterface, controller_id, device_id, selector = 1):
    logging.info('Getting temperature ' + str(controller_id) +' '+ str(device_id))
    cid = ham.send_command(TEC_GET_TEMPERATURE, ControllerID=controller_id, DeviceID=device_id, Selector=selector)
    response = ham.wait_on_response(cid, raise_first_exception=True, timeout=120, return_data=['step-return2'])
    return response

def start_temperature_control_tec(ham, controller_id, device_id):
    logging.info('Starting temperature control '+str(controller_id)+' '+str(device_id))
    cid=ham.send_command(TEC_START, ControllerID=controller_id, DeviceID=device_id)
    ham.wait_on_response(cid, raise_first_exception=True, timeout=120)

def stop_temperature_control_tec(ham, controller_id, device_id):
    logging.info('Ending temperature control '+str(controller_id)+' '+str(device_id))
    cid=ham.send_command(TEC_STOP, ControllerID=controller_id, DeviceID=device_id)
    ham.wait_on_response(cid, raise_first_exception=True, timeout=120)

def terminate_tec(ham, stop_all_devices):
    logging.info('Terminating TEC/ CPAC')
    cid=ham.send_command(TEC_TERMINATE, StopAllDevices=stop_all_devices)
    ham.wait_on_response(cid, raise_first_exception=True, timeout=120)

# CPAC has the exact same API as TEC
initialize_cpac = initialize_tec
set_temperature_target_cpac = set_temperature_target_tec
get_temperature_cpac = get_temperature_tec
start_temperature_control_cpac = start_temperature_control_tec
stop_temperature_control_cpac = stop_temperature_control_tec
terminate_cpac = terminate_tec

================================================
FILE: pyhamilton/error_code_descriptions.txt
================================================
Main Error Enumeration
ID
 Error
 Description
 
 0
 No error
 -
 
 1
 Syntax Error
 There is a wrong set of parameters or parameter ranges.
 
 2
 Hardware Error
 Steps lost on one or more hardware components, or component not initialized or not functioning.
 
 3
 Not Executed Error
 There was an error in previous part command.
 
 4
 Clot Error
 Blood clot detected.
 
 5
 Barcode Error
 Barcode could not be read or is missing.
 
 6
 Insufficient Liquid Error
 Not enough liquid available.
 
 7
 Tip Present Error
 A tip has already been picked up.
 
 8
 No Tip Error
 Tip is missing or not picked up.
 
 9
 No Carrier Error
 No carrier present for loading.
 
 10
 Execution Error
 A step or a part of a step could not be processed.
 
 11
 Pressure LLD Error
 A dispense with pressure liquid level detection is not allowed.
 
 12
 Calibrate Error
 No capacitive signal detected during carrier calibration procedure.
 
 13
 Unload Error
 Not possible to unload the carrier due to occupied loading tray position.
 
 14
 Pressure LLD Error
 Pressure liquid level detection in a consecutive aspiration is not allowed.
 
 15
 Parameter Error
 Dispense in jet mode with pressure liquid level detection is not allowed.
 
 16
 Cover Open Error
 Cover not closed or can not be locked.
 
 17
 Improper Aspiration Error

Improper Dispense Error
 The pressure-based aspiration / dispensation control reported an error ( not enough liquid ).
 
 18
 Wash Liquid Error
 Waste full or no more wash liquid available.
 
 19
 Temperature Error
 Incubator temperature out of range.
 
 20
 TADM overshot
 Overshot of limits during aspirate or dispense.

Note:

On aspirate this error is returned as main error 17.

On dispense this error is returned as main error 4.
 
 21
 Labware Error
 Labware not available.
 
 22
 Labware Gripped Error
 Labware already gripped.
 
 23
 Labware Lost Error
 Labware lost during transport.
 
 24
 Illegal target plate position
 Cannot place plate, plate was gripped in a wrong direction.
 
 25
 Illegal Intervention Error
 Cover was opened or a carrier was removed manually.
 
 26
 TADM undershot
 Undershot of limits during aspirate or dispense.

Note:

On aspirate this error is returned as main error 4.

On dispense this error is returned as main error 17.
 
 27
 Position Error
 The position is out of range.
 
28
 Unexpected cLLD Error
 The cLLD detected a liquid level above start height of liquid level search.
 
29
 Area already occupied
 Instrument region already reserved.
 
30
 Impossible to occupy area
 A region on the instrument cannot be reserved.
 
31
 Anti drop control error
 Anti drop controlling out of tolerance.
 
32
 Decapper error
 Decapper lock error while screw / unscrew a cap by twister channels.
 
33
 Decapper handling error
 Decapper station error while lock / unlock a cap.
 
99
 Slave Error
 Slave error.
 
 100
 Wrong Carrier Error
 Wrong carrier barcode detected.
 
 101
 No Carrier Barcode Error
 Carrier barcode could not be read or is missing.
 
 102
 Liquid Level Error
 Liquid surface not detected.

This error is created from main / slave error 06/70, 06/73 and 06/87.
 
 103
 Not Detected Error
 Carrier not detected at deck end position.
 
 104
 Not Aspirated Error
 Dispense volume exceeds the aspirated volume.

This error is created from main / slave error 02/54.
 
105
 Improper Dispensation Error
 The dispensed volume is out of tolerance (may only occur for Nano Pipettor Dispense steps). 

This error is created from main / slave error 02/52 and 02/54.
 
106
 No Labware Error
 The labware to be loaded was not detected by autoload module.

Note:

May only occur on a Reload Carrier step if the labware property 'MlStarCarPosAreRecognizable' is set to 1.
 
107
 Unexpected Labware Error
 The labware contains unexpected barcode ( may only occur on a Reload Carrier step ).
 
108
 Wrong Labware Error
 The labware to be reloaded contains wrong barcode ( may only occur on a Reload Carrier step ).
 
109
 Barcode Mask Error
 The barcode read doesn't match the barcode mask defined.
 
110
 Barcode Not Unique Error
 The barcode read is not unique. Previously loaded labware with same barcode was loaded without unique barcode check.
 
111
 Barcode Already Used Error
 The barcode read is already loaded as unique barcode ( it's not possible to load the same barcode twice ).
 
112
 Kit Lot Expired Error
 Kit Lot expired.
 
113
 Delimiter Error
 Barcode contains character which is used as delimiter in result string.
 


================================================
FILE: pyhamilton/interface.py
================================================
import sys
import time, json, signal, os, requests, string, logging, subprocess
from dataclasses import dataclass, field
from enum import auto, Enum, unique
from parse import parse
from waiter import wait, suppress
from http import server
from threading import Thread
from multiprocessing import Process
from pyhamilton import OEM_RUN_EXE_PATH, OEM_HSL_PATH
from .oemerr import * #TODO: specify
from .defaultcmds import defaults_by_cmd
from .liquid_class_db import get_liquid_class_volume, get_liquid_class_dispense_mode

def invert_columns(pos_str: str, sep: str = ';') -> str:
    parts = pos_str.split(sep)
    wells_per_col = 8
    num_cols = 12

    if len(parts) != wells_per_col * num_cols:
        raise ValueError(f"Expected {wells_per_col * num_cols} entries, got {len(parts)}")

    cols = [parts[i*wells_per_col:(i+1)*wells_per_col] for i in range(num_cols)]
    inverted = cols[::-1]
    return sep.join(item for col in inverted for item in col)

class HamiltonCmdTemplate:
    """
    Formatter object to create valid `pyhamilton` command dicts.

    Use of this class to assemble JSON pyhamilton commands enables keyword access to command attributes, which cuts down on string literals. It also helps to fail malformed commands early, before they are sent.

    Several default `HamiltonCmdTemplate`s are defined in `pyhamilton.defaultcmds`, such as `INITIALIZE`, `ASPIRATE`, and `DISPENSE`. Casual users will most likely never need to manually instantiate a HamiltonCmdTemplate.
    """

    @staticmethod
    def unique_id():
        """Return a "uniqe" hexadecimal string (`'0x...'`) based on time of call."""
        return hex(int((time.time()%3600e4)*1e6))

    def __init__(self, cmd_name, params_list):
        """
        Creates a `HamiltonCmdTemplate` with a command name and required parameters.

        The command name must be one of the command names accepted by the
        `pyhamilton` interpreter and a list of expected parameters for this command.

        Args:
          cmd_name (str): One of the set of string literals recognized as command names
            by the `pyhamilton` interpreter, e.g. `'mph96Dispense'`. See `pyhamilton.defaultcmds` for examples.
          params_list (list): exact list of string parameters that must have associated
            values for the command to be valid, other than those that are always present
            (`'command'` and `'id'`)
        """
        self.cmd_name = cmd_name
        self.params_list = params_list
        if cmd_name in defaults_by_cmd:
            const_name, default_dict = defaults_by_cmd[cmd_name]
            self.defaults = {k:v for k, v in default_dict.items() if v is not None}
        else:
            self.defaults = {}

    def assemble_cmd(self, *args, **kwargs):
        """
        Use keyword args to assemble this command. Default values auto-filled.

        Args:
          kwargs (dict): map of any parameters (str) to values that should be different
            from the defaults supplied for this command in `pyhamilton.defaultcmds`
        """
        if args:
            raise ValueError('assemble_cmd can only take keyword arguments.')
        assembled_cmd = {'command':self.cmd_name, 'id':HamiltonCmdTemplate.unique_id()}
        assembled_cmd.update(self.defaults)
        assembled_cmd.update(kwargs)
        self.assert_valid_cmd(assembled_cmd)
        return assembled_cmd

    def assert_valid_cmd(self, cmd_dict):
        """Validate a finished command. Do nothing if it is valid.

        `ValueError` will be raised if the supplied command did not have all required
        parameters for this command, as well as values for keys `'id'` and `'command'`, which
        are always required.

        Args:
          cmd_dict (dict): A fully assembled `pyhamilton` command

        Raises:
          ValueError: The command dict is not ready to send. Specifics of mismatch
            summarized in exception description.
        """
        prefix = 'Assert valid command "' + self.cmd_name + '" failed: '
        if 'id' not in cmd_dict:
            raise ValueError(prefix + 'no key "id"')
        if 'command' not in cmd_dict:
            raise ValueError(prefix + 'no key "command"')
        if cmd_dict['command'] != self.cmd_name:
            raise ValueError(prefix + 'command name "' + cmd_dict['command'] + '" does not match')
        needs = set(['command', 'id'])
        needs.update(self.params_list)
        givens = set(cmd_dict.keys())
        if givens != needs:
            prints = [prefix + 'template parameter keys (left) do not match given keys (right)\n']
            q_mark = ' (?)  '
            l_col_space = 4
            r_col_space = max((len(key) for key in needs)) + len(q_mark) + 1
            needs_l = sorted(list(needs))
            givens_l = sorted(list(givens))
            while needs_l or givens_l:
                if needs_l:
                    lval = needs_l.pop(0)
                    if lval not in givens:
                        lval = q_mark + lval
                else:
                    lval = ''
                if givens_l:
                    rval = givens_l.pop(0)
                    if rval not in needs:
                        rval = q_mark + rval
                else:
                    rval = ''
                prints.append(' '*l_col_space + lval + ' '*(r_col_space - len(lval)) + rval)
            raise ValueError('\n'.join(prints))

_builtin_templates_by_cmd = {}

for cmd in defaults_by_cmd:
    const_name, default_dict = defaults_by_cmd[cmd]
    const_template = HamiltonCmdTemplate(cmd, list(default_dict.keys()))
    globals()[const_name] = const_template
    _builtin_templates_by_cmd[cmd] = const_template

def labware_pos_str(labware, idx):
    return labware.layout_name() + ', ' + labware.position_id(idx)


def _make_new_hamilton_serv_handler(resp_indexing_fn):
    """Make HTTP request handler to aggregate responses according to an index function."""


    

class HamiltonServerHandler(server.BaseHTTPRequestHandler):
    _send_queue = []
    indexed_responses = {}
    MAX_QUEUED_RESPONSES = 1000
    
    @classmethod
    def set_indexing_fn(cls, fn):
        cls.indexing_fn = fn


    @staticmethod
    def send_str(cmd_str):
        if not isinstance(cmd_str, b''.__class__):
            if isinstance(cmd_str, ''.__class__):
                cmd_str = cmd_str.encode()
            else:
                raise ValueError('send_command can only send strings, not ' + str(cmd_str))
        HamiltonServerHandler._send_queue.append(cmd_str)

    @staticmethod
    def has_queued_cmds():
        return bool(HamiltonServerHandler._send_queue)

    @staticmethod
    def pop_response(idx):
        ir = HamiltonServerHandler.indexed_responses
        return None if idx not in ir else ir.pop(idx).decode()

    def _set_headers(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/HTML')
        self.end_headers()

    def do_GET(self):
        sq = HamiltonServerHandler._send_queue
        response_to_send = sq.pop(0) if sq else b''
        self._set_headers()
        self.wfile.write(response_to_send)

    def do_HEAD(self):
        self._set_headers()

    def do_POST(self):
        content_len = int(self.headers.get('content-length', 0))
        post_body = self.rfile.read(content_len)
        self._set_headers()
        self.wfile.write(b'<html><body><h1>POST!</h1></body></html>')
        ir = HamiltonServerHandler.indexed_responses
        index = HamiltonServerHandler.indexing_fn(post_body)
        if index is None:
            return
        ir[index] = post_body

    def log_message(self, *args, **kwargs):
        pass


def run_hamilton_process():
    print("RUNNING HAMILTON PROCESS")
    """Start the interpreter in a separate python process.

    Starts the pyhamilton interpreter, which is an HSL file to be passed to the
    RunHSLExecutor.exe executable from Hamilton. This should always be done in a
    separate python process using the subprocess module, not a Thread.
    """
    import clr
    from pyhamilton import OEM_STAR_PATH, OEM_HSL_PATH
    clr.AddReference(os.path.join(OEM_STAR_PATH, 'RunHSLExecutor'))
    clr.AddReference(os.path.join(OEM_STAR_PATH, 'HSLHttp'))
    try:
        from RunHSLExecutor import Class1
    except ModuleNotFoundError:
        raise RuntimeError('RunHSLExecutor DLLs successfully located, but an internal '
                           'error prevented import as a CLR module. You might be '
                           'missing the standard Hamilton software suite HSL '
                           'executables, their DLLs may not be registered with Windows, '
                           'or they may not be located in the expected system '
                           'directory.')
    C = Class1()
    C.StartMethod(OEM_HSL_PATH)
    try:
        while True:
            pass # Send external signal to end process
    except:
        pass

@unique
class HamiltonResponseStatus(Enum):
    """
    List of global Hamilton response status

    Inheritance
    -----------
        Enum

    Attributes
    ----------
    FAILED : enum.auto
        command failure is reported or parsing error has been detected
    SUCCESS : enum.auto
        command and parsing successful
    UNKNOWN : enum.auto
        indecisive state
    """
    FAILED = auto()
    SUCCESS = auto()
    UNKNOWN = auto()

@dataclass
class HamiltonResponse:
    """
    A class to represent the Venus server response

    Attributes
    ----------
    status: HamiltonResponseStatus
        Response status (failed, success or unknown)
    return_data: list | str
        Extracted values from specific field response
    moduleID: str
        ID of module from "step-return2" field
    parsed_return: any
        Represent "step-return1" field value
    raw: any
        Original server response

    Methods
    -------
    _compute_status()
        Compute the status based on step-return1 field from raw response

    _return_data()
        Contain values of requested fields

    _moduleID()
        Return Module ID (step-return2)

    _parse_return():
        Parse values from "step-return1" field

    digest(fields)
        Populate this object's attributes

    raise_first_exception()
        Evaluate which exception to raise


    Raises
    ------
        HamiltonStepError: Errors in steps executed by VENUS software
        HamiltonReturnParseError: Server response parsing failed
        InvalidErrCodeError: Unknown server response error code

    """
    status: HamiltonResponseStatus = HamiltonResponseStatus.UNKNOWN
    return_data: list = field(default_factory=list)
    moduleID: str = ""
    parsed_return: any = None
    raw: any = None

    def _compute_status(self):
        is_unknown = 'step-return1' not in self.raw
        if is_unknown:
            return HamiltonResponseStatus.UNKNOWN

        response = json.loads(self.raw)['step-return1']

        is_success = response == 1 or           \
            (
                isinstance(response, str) and   \
                len(response) == 1 and          \
                response[0] == '1'
            ) or                                \
            (
                isinstance(response, str) and   \
                len(response) > 1 and           \
                response[0] == '0'
            )
        is_failed = response == 0 or            \
            (
                isinstance(response, str) and   \
                len(response) == 1 and          \
                response[0] != '1'
            ) or                                \
            (
                isinstance(response, str) and   \
                len(response.strip()) > 1 and   \
                response.strip()[0] != '0'
            )

        if is_failed:
            return HamiltonResponseStatus.FAILED

        if is_success:
            return HamiltonResponseStatus.SUCCESS

        return HamiltonResponseStatus.UNKNOWN

    def _return_data(self, fields):
        response = json.loads(self.raw)
        if not fields or (isinstance(fields, str) and fields not in response):
            return []
        if isinstance(fields, str) and fields in response:
                return [response[fields]]

        return [response[field] for field in fields if field in response]

    def _moduleID(self):
        moduleID_field_name = "step-return2"
        if moduleID_field_name not in self.raw:
            return ""
        response = json.loads(self.raw)
        return str(response[moduleID_field_name])

    def _parse_return(self):
        return_field = "step-return1"
        field_names = [
            "{numField:d}",
            "{mainErrField:d}",
            "{slaveErr:d}",
            "{recoveryBtnId:d}",
            "{stepData}",
            "{labwareName:w}",
            "{labwarePos}"
        ]
        if return_field not in self.raw:
            return None

        response = json.loads(self.raw)[return_field]
        block_available = isinstance(response, str) and '[' in response and ',' in response
        if not block_available:
            return None

        blocks = [r for r in response.split('[')[1:]]
        if not blocks:
            return None

        parsed = [
            parse(
                ",".join(field_names[:block.count(',') + 1]),
                ",".join([' ' if item == '' else item for item in block.split(',') ])) for block in blocks
        ]
        if not parsed:
            return None

        if all([p is None for p in parsed]):
            return None
        return [p.named for p in parsed if p]

    def digest(self, fields=None):
        self.status = self._compute_status()
        self.return_data = self._return_data(fields=fields)
        self.moduleID = self._moduleID()
        self.parsed_return = self._parse_return()

    def raise_first_exception(self):
        isSuccessStatus = self.status == HamiltonResponseStatus.SUCCESS and '[' not in self.raw
        if isSuccessStatus:
            return
        
        isHamiltonStepError = self.status == HamiltonResponseStatus.FAILED and '[' not in self.raw
        if isHamiltonStepError:
            raise HamiltonStepError('Hamilton step did not execute correctly; no error code given. ( response: ' + self.raw + ' )')

        isHamiltonReturnParseError = '[' in self.raw and (self.parsed_return is None or self.parsed_return == [])
        if isHamiltonReturnParseError:
            raise HamiltonReturnParseError(self.raw)

        reportedErrorCodes = [p['mainErrField'] for p in self.parsed_return if p['mainErrField'] != 0]
        isSuccessStatus = self.status == HamiltonResponseStatus.SUCCESS and len(reportedErrorCodes) == 0
        if isSuccessStatus:
            return

        isFailedStatusNoReportedErrorCode = self.status == HamiltonResponseStatus.FAILED and len(reportedErrorCodes) == 0
        if isFailedStatusNoReportedErrorCode:
            raise HamiltonReturnParseError('Hamilton step did not execute correctly; no error code found. ( response: ' + self.raw + ' )')

        firstErrorCode = reportedErrorCodes[0]
        isFirstExceptionKnown = firstErrorCode in HAMILTON_ERROR_MAP
        if isFirstExceptionKnown:
            raise HAMILTON_ERROR_MAP[firstErrorCode]()
        
        if self.status == HamiltonResponseStatus.FAILED:
            raise InvalidErrCodeError(f'Unknown error code: {firstErrorCode}')

        if self.status == HamiltonResponseStatus.SUCCESS:
            raise HamiltonReturnParseError('Inconsistency: Venus returns SUCCESS while error code {firstErrorCode} found! ( response: ' + self.raw + ' )')

@dataclass
class DispenseResult:
    liquidHeights: float
    liquidVolumes: float
    raw: HamiltonResponse # keep the raw object if callers need extras

@dataclass
class AspirateResult:
    liquidHeights: float        
    liquidVolumes: float
    raw: HamiltonResponse # keep the raw object if callers need extras


class HamiltonServerThread(Thread):
    """Private threaded local HTTP server with graceful shutdown flag."""

    def __init__(self, address, port):
        super().__init__()
        self.daemon = True  # CRITICAL: Make this a daemon thread
        self.server_address = (address, port)
        self.should_continue = True
        self.exited = False

        def index_on_resp_id(response_str):
            try:
                response = json.loads(response_str)
                if 'id' in response:
                    return response['id']
            except json.decoder.JSONDecodeError:
                pass
            return None

        HamiltonServerHandler.indexing_fn = index_on_resp_id
        self.httpd = None

    def run(self):
        self.exited = False
        try:
            self.httpd = server.HTTPServer(self.server_address, HamiltonServerHandler)
            # Set a short timeout so we don't block forever
            self.httpd.timeout = 0.5
            
            while self.should_continue:
                try:
                    self.httpd.handle_request()
                except OSError:
                    # Socket was closed, exit gracefully
                    break
                    
        except Exception as e:
            print(f"Server thread exception: {e}")
        finally:
            if self.httpd:
                try:
                    self.httpd.server_close()
                except:
                    pass
            self.exited = True
            print("Server thread run() method completed")

    def disconnect(self):
        """Simple disconnect without calling shutdown() to avoid deadlocks"""
        self.should_continue = False
        # Don't call httpd.shutdown() here - it can cause deadlocks

    def has_exited(self):
        return self.exited


class HamiltonInterface:
    """Main class to automatically set up and tear down an interface to a Hamilton robot.

    HamiltonInterface is the primary class offered by this module. It creates a Hamilton
    HSL background process running the `pyhamilton` interpreter, along with a `localhost`
    connection to act as a bridge. It is recommended to create a `HamiltonInterface` using
    a `with:` block to ensure proper startup and shutdown of its async components, even if
    exceptions are raised. It may be used with explicit `start()` and `stop()` calls.

      Typical usage:

      ```
      with HamiltonInterface() as ham_int:
          cmd_id = ham_int.send_command(INITIALIZE)
          ...
          response = ham_int.wait_on_response(cmd_id)
          ...
      ```
    """

    known_templates = _builtin_templates_by_cmd
    default_port = 3221
    default_address = '127.0.0.1' # localhost
    _global_server_thread = None


    def __init__(self, address=None, port=None, simulating = False, debug=False, windowed = False, server_mode = False, persistent = False, **kwargs):
        if 'simulate' in kwargs:
            raise Exception("The simulate keyword argument is deprecated in favor of windowed. Please use windowed = True")
        self.address = HamiltonInterface.default_address if address is None else address
        self.port = HamiltonInterface.default_port if port is None else port
        self.windowed = windowed
        self.simulating = simulating
        self.server_mode = server_mode
        self.persistent = persistent
        self.debug = debug
        self.server_thread = None
        self.oem_process = None
        self.active = False
        self.logger = None
        self.log_queue = []
        self.json_logger = JSONLogger()


        if self.__class__._global_server_thread is not None and \
           self.__class__._global_server_thread.is_alive():
            print("Reusing existing server thread")
            self.server_thread = self.__class__._global_server_thread
        else:
            print("Starting a new server thread")
            self.server_thread = HamiltonServerThread(self.address, self.port)
            self.server_thread.start()
            # Store this new thread as the global server thread
            self.__class__._global_server_thread = self.server_thread


    def _open(self):
        # This is your logic to check if the HSL application is up or needs to be started
        if self.windowed:
                # Only start HSL if it's not responding
            try:
                    # Attempt the ping
                self.active = True
                
                print("Sending ping to check if interface is open")
                response_id = self.send_command(command='ping', id=HamiltonCmdTemplate.unique_id())
                self.wait_on_response(response_id, timeout=5)
                
                print("Interface already open")
                return
            except HamiltonTimeoutError:
                print("Opening HSL application")
                subprocess.Popen([OEM_RUN_EXE_PATH, OEM_HSL_PATH])
                self.active = True
                return


    def start(self):
        """Starts the extra processes, threads, and servers for the Hamilton connection.

        Launches: 1) the pyhamilton interpreter using the Hamilton Run Control
        executable, either in the background for normal use, or in the foreground with a
        GUI for simulation; 2) a local HTTP server to ferry messages between the python
        module and the interpreter.

        When used with a `with:` block, called automatically upon entering the block.
        """

        if self.active:
            return
        self.log('starting a Hamilton interface')
        if self.windowed:
            self._open()
            #subprocess.Popen([OEM_RUN_EXE_PATH, OEM_HSL_PATH])
            self.log('started the oem application for simulation')
        elif self.simulating:
            self.active=True
            self.log('running in simulation mode')
        elif self.server_mode:
            current_directory = os.path.dirname(os.path.abspath(__file__))
            server_script_path = os.path.join(current_directory, 'run_venus_client.py')
            python_32bit_path = os.getenv('PYTHON_32BIT_PATH')
            print("SERVER PATHS")
            print(python_32bit_path)
            if not python_32bit_path:
                raise Exception("Please set your PYTHON_32BIT_PATH variable in order to use server mode.")
            print(server_script_path)
            self.server_process = subprocess.Popen([python_32bit_path, server_script_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        else:
            self.oem_process = Process(target=run_hamilton_process, args=())
            self.oem_process.start()
            self.log('started the oem process')
        #self.server_thread = HamiltonInterface.HamiltonServerThread(self.address, self.port)
        #self.server_thread.start()
        print("started the server thread")
        self.log('started the server thread')
        #self.active = True

    def stop(self):
        """Stop this HamiltonInterface and clean up associated async processes."""

        if not self.active:
            return
        
        try:
            if self.windowed or self.simulating or self.server_mode:
                self.log('sending end run command to simulator')
                try:
                    print("Sending end command")
                    self.wait_on_response(self.send_command(command='end', id=hex(0)), timeout=1.5)
                    print("End command sent")
                except HamiltonTimeoutError:
                    pass
            else:
                for i in range(2):
                    try:
                        os.kill(self.oem_process.pid, signal.SIGTERM)
                        self.log('sent sigterm to oem process')
                        self.oem_process.join()
                        self.log('oem process exited')
                        break
                    except PermissionError:
                        self.log('permission denied, trying again...', 'warn')
                        time.sleep(2)
                else:
                    self.log('Could not kill oem process, moving on with shutdown', 'warn')
        finally:
            print("Stopping server thread")
            self.active = False
            
            # Don't call disconnect() - it can cause deadlocks
            # Instead, just signal the thread to stop and force close the server
            self.server_thread.should_continue = False
            
            if hasattr(self.server_thread, 'httpd') and self.server_thread.httpd:
                print("Force closing HTTP server")
                try:
                    # Close the server socket immediately without waiting
                    self.server_thread.httpd.server_close()
                    self.log('HTTP server socket closed')
                except Exception as e:
                    print(f"Error closing server: {e}")
            
            print("Joining server thread with timeout")
            # Give the thread a very short time to exit gracefully
            self.server_thread.join(timeout=1.0)
            
            if self.server_thread.is_alive():
                print("Server thread still alive, this is expected in some cases")
                self.log('server thread did not exit within timeout (this may be normal)', 'info')
                # Don't try to force kill - just let it be a daemon thread
            else:
                print("Server thread exited successfully")
                self.log('server thread exited successfully')

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, exc_type, exc_value, tb):
        if exc_type is not None:
            # There was an error, always stop
            self.stop()
        elif not self.persistent:
            # Normal exit, but not persistent → stop
            self.stop()
        # else: persistent and normal exit → keep running

        # Returning False means exceptions propagate normally
        return False

    def is_open(self):
        """Return `True` if the HamiltonInterface has been started and not stopped."""
        return self.active

    def send_command(self, template=None, block_until_sent=False, *args, **cmd_dict): # returns unique id of command
        """Add a command templated after HamiltonCmdTemplate to the server send queue.

        Args:
          template (HamiltonCmdTemplate): Optional; a template to provide default
            arguments not specified in `cmd_dict`.
          block_until_sent (bool): Optional; if `True`, wait for all queued messages,
            including this one, to get picked up by the local server and sent across
            the HTTP connection, before returning. Default is False.
          cmd_dict (dict): keyword arguments to be forwarded to `template` when building
            the command, overriding its defaults. If `template` not given, cmd_dict must
            either have a 'command' key with value matching one of the command names in
            `defaultcmds` and might be missing an 'id' key, or itself be a fully formed
            and correct pyhamilton command with its own 'id' key.

        Returns:
          unique id (str) of the command that can be used to index it later, either
            newly generated or same as originally present in cmd_dict.
        """
        if not self.is_open():
            self.log_and_raise(RuntimeError('Cannot send a command from a closed HamiltonInterface'))
        if template is None:
            if 'command' not in cmd_dict:
                self.log_and_raise(ValueError('Command dicts from HamiltonInterface must have a \'command\' key'))
            cmd_name = cmd_dict['command']
            if cmd_name in HamiltonInterface.known_templates:
                # raises if this is a known command but some fields in cmd_dict are invalid
                send_cmd_dict = HamiltonInterface.known_templates[cmd_name].assemble_cmd(**cmd_dict)
            else:
                send_cmd_dict = cmd_dict
        else:
            send_cmd_dict = template.assemble_cmd(**cmd_dict)
        if 'id' not in send_cmd_dict:
            self.log_and_raise(ValueError("Command dicts sent from HamiltonInterface must have a unique id with key 'id'"))
        if not self.simulating:
            HamiltonServerHandler.send_str(json.dumps(send_cmd_dict))
        else:
            self.json_logger.log(str(send_cmd_dict))
        if block_until_sent:
            self._block_until_sq_clear()
        return send_cmd_dict['id']

    def wait_on_response(self, id, timeout=60, raise_first_exception=False, return_data=None):
        """Wait and do not return until the response for the specified id comes back.

        When the command corresponding to `id` regards multiple distinct pipette channels
        or devices, responses may contain encoded errors that might be different for
        different channels or devices. For this reason, the default behavior of
        `wait_on_response` is to not raise exceptions, but to delegate handling
        exceptions to the caller. For convenience, this method can optionally raise the
        first exception it encounters, often a useful behavior for succinct scripted
        commands that regard only one device, when raise_first_exception is `True`.

        Args:
          id (str): The unique id of a previously sent command
          timeout (float): Optional; maximum time in seconds to wait before raising
            `HamiltonTimeoutError`. Default is 60 seconds.
          raise_first_exception: Optional; if True, may raise if there is an error
            encoded in the response. Default is False.
          return_data(list | str): Optional
            field(s) value to extract (e.g: "step-result1")

        Returns:
          HamiltonResponse

        Raises:
          `HamiltonTimeoutError`: after `timeout` seconds elapse with no response, if
          `timeout` was specified.
        """
        if self.simulating:
            return
        
        delays = 1  # sec
        server_response = None
        for _ in wait(delays=delays, timeout=timeout):
            server_response = HamiltonServerHandler.pop_response(id)
            if server_response is not None:
                break
            
        if server_response is None:
            self.log_and_raise(HamiltonTimeoutError('Timed out after ' + str(timeout) + ' sec while waiting for response id ' + str(id)))
        
        if self.debug:
            print(server_response)
        
        return self.parse_response(server_response, raise_first_exception, return_data)

    def parse_response(self, server_response:str, raise_first_exception:bool=False, return_data:"list|str"=None):
        """Parse the server response and return parsed response of type HamiltonResponse.

        Args:
          server_response (str): Venus server response
          raise_first_exception (bool): Optional; forwarded to `wait_on_response`.
            Default is `False`.
          return_data: field(s) value to extract (e.g: "step-result1")

        Returns:
          HamiltonResponse

        """

        hamiltonResponse = HamiltonResponse(raw=server_response)
        hamiltonResponse.digest(fields=return_data)
        if raise_first_exception:
            hamiltonResponse.raise_first_exception()

        return hamiltonResponse

    def _block_until_sq_clear(self):
        while HamiltonServerHandler.has_queued_cmds():
            print(HamiltonServerHandler._send_queue)

            pass

    def set_log_dir(self, log_dir):
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.INFO)
        hdlr = logging.FileHandler(log_dir)
        formatter = logging.Formatter('[%(asctime)s] %(name)s %(levelname)s %(message)s')
        hdlr.setFormatter(formatter)
        self.logger.addHandler(hdlr)
        self._dump_log_queue()

    def log(self, msg, msg_type='info'):
        self.log_queue.append((msg, msg_type))
        self._dump_log_queue()

    def _dump_log_queue(self):
        if self.logger is None:
            return
        log_actions = {'error':self.logger.error,
                      'warn':self.logger.warn,
                      'debug':self.logger.debug,
                      'info':self.logger.info,
                      'critical':self.logger.critical}
        while self.log_queue:
            msg, msg_type = self.log_queue.pop(0)
            log_actions.get(msg_type.lower(), self.logger.info)(msg) # prints if no log path set

    def log_and_raise(self, err):
        self.log(repr(err), 'error')
        raise err

    @staticmethod
    def _channel_var(pos_tuples):
        """Create channel pattern string for commands"""
        ch_var = ['0']*16
        for i, pos_tup in enumerate(pos_tuples):
            if pos_tup is not None:
                ch_var[i] = '1'
        return ''.join(ch_var)

    @staticmethod
    def _compound_pos_str(pos_tuples):
        """Create position string for commands"""
        present_pos_tups = [pt for pt in pos_tuples if pt is not None]
        return ';'.join((pt[0].layout_name() + ', ' + pt[0].position_id(pt[1]) 
                        for pt in present_pos_tups))

    @staticmethod
    def _compound_pos_str_96(labware96):
        """Create position string for 96-well commands"""
        return ';'.join((labware96.layout_name() + ', ' + labware96.position_id(idx) 
                        for idx in range(96)))

    @staticmethod
    def _assert_parallel_nones(list1, list2):
        """Verify two lists have None values in the same positions"""
        if not (len(list1) == len(list2) and 
                all([(i1 is None) == (i2 is None) for i1, i2 in zip(list1, list2)])):
            raise ValueError('Lists must have parallel None entries')

    def initialize(self, **more_options):
        """Initialize the Hamilton robot with optional parameters.

        Args:
            **more_options: Additional command options to pass to the initialize command.
        """
        self.log('initialize: Initializing Hamilton robot with options ' + str(more_options))
        response = self.wait_on_response(
            self.send_command(
                INITIALIZE,
                **more_options
            ),
            timeout=300,
            raise_first_exception=True,
            return_data=['step-return2', 'step-return3']
        )
        return response
    
    def aspirate(self, pos_tuples, vols, **more_options) -> AspirateResult:
        """Aspirate liquid from specified positions.
        
        Args:
            pos_tuples: List of (labware, idx) tuples specifying positions
            vols: List of volumes to aspirate
            **more_options: Additional command options

        Returns
        -------
        AspirateResult:
            .liquidHeights liquid heights after aspirate
            .liquidVolumes liquid volumes after aspirate
            .raw           raw HamiltonResponse object
        """
        self.log('aspirate: Aspirate volumes ' + str(vols) + ' from positions [' +
                '; '.join((labware_pos_str(*pt) if pt else '(skip)' for pt in pos_tuples)) +
                (']' if not more_options else '] with extra options ' + str(more_options)))

        if len(pos_tuples) > 8:
            raise ValueError('Can only aspirate with 8 channels at a time')
            
        self._assert_parallel_nones(pos_tuples, vols)
            
        if 'liquidClass' not in more_options:
            raise ValueError('Must specify a liquidClass for aspirate commands')

        if more_options.get('capacitiveLLD', 0) not in (0, 5):
            dispense_mode = get_liquid_class_dispense_mode(more_options['liquidClass'])
            if 'Surface' not in dispense_mode:
                raise ValueError('cLLD can only be used with Surface dispense modes')
        
        response = self.wait_on_response(
            self.send_command(
                ASPIRATE,
                channelVariable=self._channel_var(pos_tuples),
                labwarePositions=self._compound_pos_str(pos_tuples), 
                volumes=[v for v in vols if v is not None],
                **more_options
            ),
            raise_first_exception=True,
            return_data=['step-return2', 'step-return3']
        )

        if self.simulating:
            # In simulation mode, we don't get liquid heights and volumes
            res = AspirateResult(
                liquidHeights=[2.0] * len(pos_tuples),
                liquidVolumes=[10.0] * len(pos_tuples),
                raw=response
            )
            return res
        
        else:
            res = AspirateResult(
                liquidHeights=[float(x) for x in response.return_data[0].split(';')],
                liquidVolumes=[float(x) for x in response.return_data[1].split(';')],
                raw=response
            )
            return res


    def dispense(self, pos_tuples, vols, **more_options) -> DispenseResult:
        """Dispense liquid into specified positions.
        
        Args:
            pos_tuples: List of (labware, idx) tuples specifying positions
            vols: List of volumes to dispense
            **more_options: Additional command options
        
        """
        self.log('dispense: Dispense volumes ' + str(vols) + ' into positions [' +
                '; '.join((labware_pos_str(*pt) if pt else '(skip)' for pt in pos_tuples)) +
                (']' if not more_options else '] with extra options ' + str(more_options)))

        if len(pos_tuples) > 8:
            raise ValueError('Can only dispense with 8 channels at a time')
            
        self._assert_parallel_nones(pos_tuples, vols)
            
        if 'liquidClass' not in more_options:
            more_options['liquidClass'] = 'HighVolumeFilter_Water_DispenseJet_Empty_with_transport_vol'

        if more_options.get('capacitiveLLD', 0) not in (0, 5):
            dispense_mode = get_liquid_class_dispense_mode(more_options['liquidClass'])
            if 'Surface' not in dispense_mode:
                raise ValueError('cLLD can only be used with Surface dispense modes')


        response = self.wait_on_response(
            self.send_command(
                DISPENSE,
                channelVariable=self._channel_var(pos_tuples),
                labwarePositions=self._compound_pos_str(pos_tuples),
                volumes=[v for v in vols if v is not None],
                **more_options
            ),
            raise_first_exception=True,
            return_data=['step-return2', 'step-return3']
        )


        if self.simulating:
            # In simulation mode, we don't get liquid heights and volumes
            res = DispenseResult(
                liquidHeights=[2.0] * len(pos_tuples),
                liquidVolumes=[10.0] * len(pos_tuples),
                raw=response
            )
            return res
        
        else:
            res = DispenseResult(
                liquidHeights=[float(x) for x in response.return_data[0].split(';')],
                liquidVolumes=[float(x) for x in response.return_data[1].split(';')],
                raw=response
            )
            return res



    def tip_pick_up(self, pos_tuples, **more_options):
        """Pick up tips from specified positions.
        
        Args:
            pos_tuples: List of (labware, idx) tuples specifying tip positions
            **more_options: Additional command options
        """
        self.log('tip_pick_up: Pick up tips at ' + '; '.join((labware_pos_str(*pt) if pt else '(skip)' 
                for pt in pos_tuples)) + ('' if not more_options else ' with extra options ' + str(more_options)))

        if len(pos_tuples) > 8:
            raise ValueError('Can only pick up 8 tips at a time')

        self.wait_on_response(
            self.send_command(
                PICKUP,
                labwarePositions=self._compound_pos_str(pos_tuples),
                channelVariable=self._channel_var(pos_tuples),
                **more_options
            ), 
            raise_first_exception=True
        )

    def tip_eject(self, pos_tuples=None, **more_options):
        """Eject tips to specified positions or default waste.
        
        Args:
            pos_tuples: Optional list of (labware, idx) tuples specifying tip positions.
                       If None, eject to default waste.
            **more_options: Additional command options
        """
        if pos_tuples is None:
            self.log('tip_eject: Eject tips to default waste' + 
                    ('' if not more_options else ' with extra options ' + str(more_options)))
            more_options['useDefaultWaste'] = 1
            from .resources.deckresource import Tip96
            dummy = Tip96('')
            pos_tuples = [(dummy, 0)] * 8
        else:
            self.log('tip_eject: Eject tips to ' + '; '.join((labware_pos_str(*pt) if pt else '(skip)' 
                    for pt in pos_tuples)) + ('' if not more_options else ' with extra options ' + str(more_options)))

        if len(pos_tuples) > 8:
            raise ValueError('Can only eject up to 8 tips')

        self.wait_on_response(
            self.send_command(
                EJECT,
                labwarePositions=self._compound_pos_str(pos_tuples),
                channelVariable=self._channel_var(pos_tuples),
                **more_options
            ),
            raise_first_exception=True
        )

    def tip_pick_up_96(self, tip96, **more_options):
        """Pick up tips from a 96-well tip rack.
        
        Args:
            tip96: 96-well tip rack labware
            **more_options: Additional command options
        """
        self.log('tip_pick_up_96: Pick up tips at ' + tip96.layout_name() +
                ('' if not more_options else ' with extra options ' + str(more_options)))
        self.wait_on_response(
            self.send_command(
                PICKUP96,
                labwarePositions=self._compound_pos_str_96(tip96),
                **more_options
            ),
            raise_first_exception=True
        )

    def tip_eject_96(self, tip96=None, **more_options):
        """Eject tips to a 96-well tip rack or default waste.
        
        Args:
            tip96: Optional 96-well tip rack labware. If None, eject to default waste.
            **more_options: Additional command options
        """
        self.log('tip_eject_96: Eject tips to ' + (tip96.layout_name() if tip96 else 'default waste') +
                ('' if not more_options else ' with extra options ' + str(more_options)))

        if tip96 is None:
            labware_poss = ''
            more_options.update({'tipEjectToKnownPosition': 2})  # 2 is default waste
        else:
            labware_poss = self._compound_pos_str_96(tip96)

        self.wait_on_response(
            self.send_command(
                EJECT96,
                labwarePositions=labware_poss,
                **more_options
            ),
            raise_first_exception=True
        )


    def aspirate_96(self, plate96, vol, **more_options):
        """Aspirate liquid from a 96-well plate.
        
        Args:
            plate96: 96-well plate labware
            vol: Volume to aspirate
            **more_options: Additional command options
        """
        self.log('aspirate_96: Aspirate volume ' + str(vol) + ' from ' + plate96.layout_name() +
                ('' if not more_options else ' with extra options ' + str(more_options)))

        if 'liquidClass' not in more_options:
            raise ValueError('Must specify a liquidClass for aspirate commands')

        if more_options.get('capacitiveLLD', 0) not in (0, 5):
            dispense_mode = get_liquid_class_dispense_mode(more_options['liquidClass'])
            if 'Surface' not in dispense_mode:
                raise ValueError('cLLD can only be used with Surface dispense modes')

        self.wait_on_response(
            self.send_command(
                ASPIRATE96,
                labwarePositions=self._compound_pos_str_96(plate96),
                aspirateVolume=vol,
                **more_options
            ),
            raise_first_exception=True
        )

    def tip_pick_up_mph_columns(self, tip_96, num_columns_from_left, **more_options):
        """Pick up tips from a 96-well tip rack in a multi-channel fashion.

        Args:
            tip_96: 96-well tip rack labware
            num_columns: Number of columns to pick up
            **more_options: Additional command options
        """
        self.log('tip_pick_up_mph_columns: Pick up tips at ' + tip_96.layout_name() +
                ('' if not more_options else ' with extra options ' + str(more_options)))
        num_columns_from_right = 12 - num_columns_from_left + 1 # Convert to right-side pickup
        channelVariable = '1'*num_columns_from_right*8 + '0'*(12-num_columns_from_right)*8
        positions = self._compound_pos_str_96(tip_96)
        flipped_positions = invert_columns(positions) # Sequences have to be inverted for right-side pickup
        self.wait_on_response(
            self.send_command(
                PICKUP96,
                labwarePositions=flipped_positions,
                channelVariable=channelVariable,
                reducedPatternMode=3,  # (integer) 0=All (not reduced), 1=One channel, 2=One row  3=One column
                **more_options
            ),
            raise_first_exception=True
        )

    def dispense_96(self, plate96, vol, **more_options):
        """Dispense liquid into a 96-well plate.
        
        Args:
            plate96: 96-well plate labware
            vol: Volume to dispense
            **more_options: Additional command options
        """
        self.log('dispense_96: Dispense volume ' + str(vol) + ' into ' + plate96.layout_name() +
                ('' if not more_options else ' with extra options ' + str(more_options)))

        if 'liquidClass' not in more_options:
            more_options['liquidClass'] = 'HighVolumeFilter_Water_DispenseJet_Empty_with_transport_vol'

        if more_options.get('capacitiveLLD', 0) not in (0, 5):
            dispense_mode = get_liquid_class_dispense_mode(more_options['liquidClass'])
            if 'Surface' not in dispense_mode:
                raise ValueError('cLL
Download .txt
gitextract_3_91ea39/

├── .gitattributes
├── .gitignore
├── .pylintrc
├── .vscode/
│   └── settings.json
├── LICENSE
├── PKG-INFO
├── README.md
├── imgs/
│   ├── README.md
│   └── text
├── pandoc_pdf.sh
├── pyhamilton/
│   ├── __init__.py
│   ├── bin/
│   │   └── Hamilton MPE HSL Driver.msi
│   ├── consumables/
│   │   ├── __init__.py
│   │   └── consumables.py
│   ├── defaultcmds.py
│   ├── defaults/
│   │   └── defaults.json
│   ├── defaults.py
│   ├── devices/
│   │   ├── __init__.py
│   │   ├── centrifuge_wrappers.py
│   │   ├── hhs_wrappers.py
│   │   ├── hig_wrappers.py
│   │   ├── mpe_wrappers.py
│   │   ├── odtc_wrappers.py
│   │   ├── pH_wrappers.py
│   │   └── tec_wrappers.py
│   ├── error_code_descriptions.txt
│   ├── interface.py
│   ├── library/
│   │   ├── ASWStandard/
│   │   │   ├── ASWGlobal/
│   │   │   │   ├── ASWGlobal.hsl
│   │   │   │   └── ASWGlobal.stp
│   │   │   └── TraceLevel/
│   │   │       ├── TraceLevel.chm
│   │   │       ├── TraceLevel.chw
│   │   │       ├── TraceLevel.hsl
│   │   │       └── TraceLevel.stp
│   │   ├── Alpha Numeric Conversion/
│   │   │   ├── Alpha Numeric Conversion.hs_
│   │   │   ├── Alpha Numeric Conversion.hsi
│   │   │   ├── Alpha Numeric Conversion.smt
│   │   │   └── Alpha Numeric Conversion.stp
│   │   ├── DaisyChainedTiltModule/
│   │   │   ├── HSLDaisyChainCommunication.chm
│   │   │   └── HSLDaisyChainedTiltModule.chm
│   │   ├── ErrorSimulator/
│   │   │   ├── ErrorSimulator.hs_
│   │   │   ├── ErrorSimulator.hsi
│   │   │   ├── ErrorSimulator.smt
│   │   │   ├── ErrorSimulator.stp
│   │   │   ├── HSLFilLibEx.chm
│   │   │   ├── HSLFilLibEx.hsl
│   │   │   ├── HSLFilLibEx.stp
│   │   │   ├── MlStarSimCfg.hsl
│   │   │   ├── MlStarSimCfg.stp
│   │   │   ├── PhoenixChecksum_0.reg
│   │   │   ├── StrTokenize.hs_
│   │   │   ├── StrTokenize.hsi
│   │   │   ├── StrTokenize.smt
│   │   │   └── StrTokenize.stp
│   │   ├── HSLAppsLib/
│   │   │   ├── HSLAppsLib.chm
│   │   │   ├── HSLAppsLib.hsl
│   │   │   └── HSLAppsLib.stp
│   │   ├── HSLAppsLib.chm
│   │   ├── HSLAppsLib.chw
│   │   ├── HSLAppsLib.hsl
│   │   ├── HSLAppsLib.stp
│   │   ├── HSLBarcodeReader.hs_
│   │   ├── HSLBarcodeReader.hsl
│   │   ├── HSLBarcodeReader.stp
│   │   ├── HSLBarcodeReaderEnu.chm
│   │   ├── HSLBarcodeReaderStringTableEnu.hs_
│   │   ├── HSLBarcodeReaderStringTableEnu.stp
│   │   ├── HSLDaisyChainCommunication.chm
│   │   ├── HSLDaisyChainCommunication.hsl
│   │   ├── HSLDaisyChainCommunication.stp
│   │   ├── HSLDaisyChainedMediaLine.chm
│   │   ├── HSLDaisyChainedMediaLine.chw
│   │   ├── HSLDaisyChainedMediaLine.hsl
│   │   ├── HSLDaisyChainedMediaLine.stp
│   │   ├── HSLDaisyChainedTiltModule.chm
│   │   ├── HSLDaisyChainedTiltModule.chw
│   │   ├── HSLDaisyChainedTiltModule.hsl
│   │   ├── HSLDaisyChainedTiltModule.stp
│   │   ├── HSLInhecoTEC/
│   │   │   ├── HSLInhecoTECLib.hsl
│   │   │   ├── HSLInhecoTECLib.stp
│   │   │   └── HSLInhecoTECLibEnu.chm
│   │   ├── HSLLabwareStateLib.hsl
│   │   ├── HSLLabwareStateLib.stp
│   │   ├── HSLLabwareStateLibEnu.chm
│   │   ├── HSLLabwareStateLibImpl.hs_
│   │   ├── HSLLabwareStateLibImpl.stp
│   │   ├── HSLLabwareStateLibImplEnu.hs_
│   │   ├── HSLLabwareStateLibImplEnu.stp
│   │   ├── HSLLabwrAccess/
│   │   │   ├── HSLLabwrAccess.chm
│   │   │   ├── HSLLabwrAccess.hsl
│   │   │   └── HSLLabwrAccess.stp
│   │   ├── HSLTrcLib.hsl
│   │   ├── HSLTrcLib.stp
│   │   ├── HSL_LiquidClassLib.hsl
│   │   ├── HSL_LiquidClassLib.stp
│   │   ├── HslHamHeaterShakerLib.Instrument.stp
│   │   ├── HslHamHeaterShakerLib.stp
│   │   ├── HslHamHeaterShakerLibEnu.chw
│   │   ├── HslHamHeaterShakerStringTableEnu.hs_
│   │   ├── HslHamHeaterShakerStringTableEnu.stp
│   │   ├── Labware Properties/
│   │   │   ├── Labware Definition Keys.xlsx
│   │   │   ├── Labware_Property_Query.hs_
│   │   │   ├── Labware_Property_Query.hsi
│   │   │   ├── Labware_Property_Query.smt
│   │   │   ├── Labware_Property_Query.stp
│   │   │   └── Resources/
│   │   │       ├── LPQ_GLOBAL.hsl
│   │   │       └── LPQ_GLOBAL.stp
│   │   ├── SMT/
│   │   │   ├── MLSTARLiquidClassLib.chm
│   │   │   ├── MLSTARLiquidClassLib.hsl
│   │   │   └── MLSTARLiquidClassLib.stp
│   │   ├── SMTs/
│   │   │   ├── lookup.hs_
│   │   │   ├── lookup.hsi
│   │   │   ├── lookup.smt
│   │   │   └── lookup.stp
│   │   ├── STAR Tools/
│   │   │   ├── Channel Tools Test.lay
│   │   │   ├── Channel Tools Test.res
│   │   │   ├── Resources/
│   │   │   │   └── SubMethods/
│   │   │   │       ├── CHANNEL_TOOLS_GLOBAL.hsl
│   │   │   │       ├── CHANNEL_TOOLS_GLOBAL.stp
│   │   │   │       ├── CenterSpot.rck
│   │   │   │       ├── FW_HelperLibrary.hs_
│   │   │   │       ├── FW_HelperLibrary.hsi
│   │   │   │       ├── FW_HelperLibrary.smt
│   │   │   │       ├── FW_HelperLibrary.stp
│   │   │   │       ├── Firmware Pipetting Commands.hs_
│   │   │   │       ├── Firmware Pipetting Commands.hsi
│   │   │   │       ├── Firmware Pipetting Commands.smt
│   │   │   │       ├── Firmware Pipetting Commands.stp
│   │   │   │       ├── HelperLibrary.hs_
│   │   │   │       ├── HelperLibrary.hsi
│   │   │   │       ├── HelperLibrary.smt
│   │   │   │       ├── HelperLibrary.stp
│   │   │   │       ├── Liquid Level Check.hs_
│   │   │   │       ├── Liquid Level Check.hsi
│   │   │   │       ├── Liquid Level Check.smt
│   │   │   │       ├── Liquid Level Check.stp
│   │   │   │       ├── Plate Stack Verify.hs_
│   │   │   │       ├── Plate Stack Verify.hsi
│   │   │   │       ├── Plate Stack Verify.smt
│   │   │   │       ├── Plate Stack Verify.stp
│   │   │   │       ├── STAR Channel Movement Tools.hs_
│   │   │   │       ├── STAR Channel Movement Tools.hsi
│   │   │   │       ├── STAR Channel Movement Tools.smt
│   │   │   │       ├── STAR Channel Movement Tools.stp
│   │   │   │       ├── Split_Wells.hs_
│   │   │   │       ├── Split_Wells.hsi
│   │   │   │       ├── Split_Wells.smt
│   │   │   │       ├── Split_Wells.stp
│   │   │   │       ├── TIP_OFFSET_GLOBAL.hsl
│   │   │   │       ├── TIP_OFFSET_GLOBAL.stp
│   │   │   │       ├── Tip Tool Global Variables.hsl
│   │   │   │       ├── Tip Tool Global Variables.stp
│   │   │   │       ├── TipCounterGlobal.hsl
│   │   │   │       ├── TipCounterGlobal.stp
│   │   │   │       ├── Travel Lanes.hs_
│   │   │   │       ├── Travel Lanes.hsi
│   │   │   │       ├── Travel Lanes.smt
│   │   │   │       ├── Travel Lanes.stp
│   │   │   │       ├── VANTAGE Channel Movement Tools.hs_
│   │   │   │       ├── VANTAGE Channel Movement Tools.hsi
│   │   │   │       ├── VANTAGE Channel Movement Tools.smt
│   │   │   │       ├── VANTAGE Channel Movement Tools.stp
│   │   │   │       └── centerspot.ctr
│   │   │   ├── STAR Channel Tools.hs_
│   │   │   ├── STAR Channel Tools.hsi
│   │   │   ├── STAR Channel Tools.smt
│   │   │   ├── STAR Channel Tools.stp
│   │   │   ├── STAR MPH96 Tools.hs_
│   │   │   ├── STAR MPH96 Tools.hsi
│   │   │   ├── STAR MPH96 Tools.smt
│   │   │   ├── STAR MPH96 Tools.stp
│   │   │   ├── STAR Pipetting Tools.hs_
│   │   │   ├── STAR Pipetting Tools.hsi
│   │   │   ├── STAR Pipetting Tools.smt
│   │   │   ├── STAR Pipetting Tools.stp
│   │   │   ├── STAR Tip Tools.hs_
│   │   │   ├── STAR Tip Tools.hsi
│   │   │   ├── STAR Tip Tools.smt
│   │   │   ├── STAR Tip Tools.stp
│   │   │   ├── STAR Tools Demo.hsl
│   │   │   ├── STAR Tools Demo.med
│   │   │   ├── STAR Tools Demo.stp
│   │   │   ├── STAR Tools Demo.sub
│   │   │   ├── TipSupport.rck
│   │   │   ├── TipSupport.x
│   │   │   └── tipsupport.ctr
│   │   └── SchedulingDev/
│   │       ├── HSLAppsLib.chm
│   │       ├── HSLAppsLib.chw
│   │       ├── HSLAppsLib.hsl
│   │       └── HSLAppsLib.stp
│   ├── liquid_class_db.py
│   ├── liquid_classes.py
│   ├── liquid_handling_wrappers.py
│   ├── ngs/
│   │   ├── __init__.py
│   │   ├── loading/
│   │   │   ├── __init__.py
│   │   │   ├── deck_annotation.py
│   │   │   ├── deck_regions.json
│   │   │   ├── deck_regions_old.json
│   │   │   ├── loading_vis.py
│   │   │   ├── plate_96_render.py
│   │   │   ├── rendering_helpers.py
│   │   │   └── tube_rack_render.py
│   │   ├── protocol.py
│   │   ├── tadm.py
│   │   └── tests/
│   │       ├── PacBio_MultiPlexLibraryPrepDeck_v1.2.lay
│   │       ├── PacBio_MultiPlexLibraryPrepDeck_v1.2.res
│   │       ├── bead_cleanup.py
│   │       ├── cLLD.py
│   │       ├── consumables_tracking.py
│   │       ├── cpac.py
│   │       ├── double_aspirate.py
│   │       ├── get_liquid_class_volume.py
│   │       ├── gripper_move.py
│   │       ├── hhs.py
│   │       ├── loading/
│   │       │   ├── deck_regions.json
│   │       │   └── loading_vis.py
│   │       ├── log/
│   │       │   ├── hamilton.log
│   │       │   ├── main.log
│   │       │   └── robot_json.log
│   │       ├── magnetic_bead_cleanup.py
│   │       ├── mix_plate.py
│   │       ├── multi_dispense.py
│   │       ├── ngs_demo.py
│   │       ├── pip_transfer.py
│   │       ├── set_labware_property.py
│   │       ├── stacking.py
│   │       ├── thermal_cycler/
│   │       │   └── thermal_cycler_with_transport.py
│   │       ├── tip_support.py
│   │       ├── tip_tracker.py
│   │       ├── transfer_96.py
│   │       └── transport.py
│   ├── odtc/
│   │   ├── __init__.py
│   │   └── odtc_protocol.py
│   ├── oemerr.py
│   ├── paths.py
│   ├── pipetting/
│   │   ├── __init__.py
│   │   ├── pipetting.py
│   │   └── trough_manager.py
│   ├── resources/
│   │   ├── __init__.py
│   │   ├── deckresource.py
│   │   ├── enums.py
│   │   └── managed_resources.py
│   ├── run_venus_client.py
│   ├── star-oem/
│   │   ├── HslHamHeaterShakerLib.hs_
│   │   ├── HslHamHeaterShakerLib.hsl
│   │   ├── RunHSLExecutor.dll.config
│   │   ├── RunHSLExecutor.pdb
│   │   ├── STAR_OEM.hsl
│   │   ├── STAR_OEM.lay
│   │   ├── STAR_OEM.res
│   │   ├── STAR_OEM.stp
│   │   ├── STAR_OEM.sub
│   │   ├── STAR_OEM_toolkit.hs_
│   │   ├── STAR_OEM_toolkit.hsi
│   │   ├── STAR_OEM_toolkit.smt
│   │   ├── STAR_OEM_toolkit.stp
│   │   └── VENUS_Method/
│   │       ├── Hamilton pH Module Controller.chw
│   │       ├── Hamilton pH Module Controller.hs_
│   │       ├── Hamilton pH Module Controller.hsi
│   │       ├── Hamilton pH Module Controller.smt
│   │       ├── Hamilton pH Module Controller.stp
│   │       ├── HslHamHeaterShakerLib.chm
│   │       ├── HslHamHeaterShakerLib.chw
│   │       ├── HslHamHeaterShakerLib.hs_
│   │       ├── HslHamHeaterShakerLib.hsl
│   │       ├── STAR_OEM.hsl
│   │       ├── STAR_OEM_HiG.hs_
│   │       ├── STAR_OEM_HiG.hsi
│   │       ├── STAR_OEM_HiG.stp
│   │       ├── STAR_OEM_ODTC.hs_
│   │       ├── STAR_OEM_ODTC.hsi
│   │       ├── STAR_OEM_ODTC.stp
│   │       ├── STAR_OEM_Test.hsl
│   │       ├── STAR_OEM_Test.lay
│   │       ├── STAR_OEM_Test.med
│   │       ├── STAR_OEM_Test.res
│   │       ├── STAR_OEM_Test.stp
│   │       ├── STAR_OEM_Test.sub
│   │       ├── STAR_OEM_noFan.hsl
│   │       ├── STAR_OEM_noFan.med
│   │       ├── STAR_OEM_noFan.stp
│   │       ├── STAR_OEM_noFan.sub
│   │       ├── STAR_OEM_toolkit.hs_
│   │       ├── STAR_OEM_toolkit.hsi
│   │       ├── STAR_OEM_toolkit.smt
│   │       ├── STAR_OEM_toolkit.stp
│   │       ├── STAR_OEM_toolkit_MPE.hs_
│   │       ├── STAR_OEM_toolkit_MPE.hsi
│   │       ├── STAR_OEM_toolkit_MPE.smt
│   │       ├── STAR_OEM_toolkit_MPE.stp
│   │       ├── STAR_OEM_toolkit_centrifuge.hs_
│   │       ├── STAR_OEM_toolkit_centrifuge.hsi
│   │       ├── STAR_OEM_toolkit_centrifuge.smt
│   │       ├── STAR_OEM_toolkit_centrifuge.stp
│   │       ├── STAR_OEM_toolkit_pH.hs_
│   │       ├── STAR_OEM_toolkit_pH.hsi
│   │       ├── STAR_OEM_toolkit_pH.smt
│   │       ├── STAR_OEM_toolkit_pH.stp
│   │       ├── STAR_OEM_wFan.hsl
│   │       ├── STAR_OEM_wFan.med
│   │       ├── STAR_OEM_wFan.stp
│   │       ├── STAR_OEM_wFan.sub
│   │       ├── testWasher.hsl
│   │       ├── testWasher.med
│   │       ├── testWasher.stp
│   │       ├── testWasher.sub
│   │       ├── ~Hx1C7B.hsl
│   │       ├── ~Hx1C7B.sub
│   │       ├── ~Hx1C7B.tmp
│   │       ├── ~Hx1DE8.hsl
│   │       ├── ~Hx1DE8.sub
│   │       ├── ~Hx1DE8.tmp
│   │       ├── ~Hx2A1D.hsl
│   │       ├── ~Hx2A1D.sub
│   │       ├── ~Hx2A1D.tmp
│   │       ├── ~Hx2D5B.hsl
│   │       ├── ~Hx2D5B.sub
│   │       ├── ~Hx2D5B.tmp
│   │       ├── ~Hx305.hsl
│   │       ├── ~Hx305.sub
│   │       ├── ~Hx305.tmp
│   │       ├── ~Hx393D.hsi
│   │       ├── ~Hx393D.tmp
│   │       ├── ~Hx3D8A.hsl
│   │       ├── ~Hx3D8A.sub
│   │       ├── ~Hx3D8A.tmp
│   │       ├── ~Hx4005.hsl
│   │       ├── ~Hx4005.sub
│   │       ├── ~Hx4005.tmp
│   │       ├── ~Hx48F0.hsi
│   │       ├── ~Hx48F0.tmp
│   │       ├── ~Hx5A3E.hsl
│   │       ├── ~Hx5A3E.sub
│   │       ├── ~Hx5FF.hsl
│   │       ├── ~Hx5FF.sub
│   │       ├── ~Hx5FF.tmp
│   │       ├── ~Hx68E5.hsl
│   │       ├── ~Hx68E5.sub
│   │       ├── ~Hx68E5.tmp
│   │       ├── ~Hx7218.hsl
│   │       ├── ~Hx7218.tmp
│   │       ├── ~Hx7406.hsl
│   │       ├── ~Hx7406.sub
│   │       ├── ~Hx7A41.hsi
│   │       ├── ~Hx8511.hsl
│   │       ├── ~Hx8511.sub
│   │       ├── ~Hx8511.tmp
│   │       ├── ~Hx8AF.hsl
│   │       ├── ~Hx8AF.sub
│   │       ├── ~Hx8AF.tmp
│   │       ├── ~HxAB5D.hsl
│   │       ├── ~HxAB5D.stp
│   │       ├── ~HxAB5D.sub
│   │       ├── ~HxB3C9.hsl
│   │       ├── ~HxB3C9.sub
│   │       ├── ~HxB54F.hsi
│   │       ├── ~HxB54F.tmp
│   │       ├── ~HxC302.hsl
│   │       ├── ~HxC302.sub
│   │       ├── ~HxC302.tmp
│   │       ├── ~HxCAAD.hsl
│   │       ├── ~HxCAAD.tmp
│   │       ├── ~HxE052.hsi
│   │       ├── ~HxE052.tmp
│   │       ├── ~HxE82B.hsl
│   │       ├── ~HxE82B.sub
│   │       ├── ~HxE82B.tmp
│   │       ├── ~HxEF83.hsl
│   │       ├── ~HxEF83.sub
│   │       ├── ~HxEF83.tmp
│   │       ├── ~HxFA7A.hsi
│   │       ├── ~HxFA7A.tmp
│   │       └── ~ReAB0E.res
│   ├── templates/
│   │   ├── ai_template/
│   │   │   ├── deck.lay
│   │   │   ├── deck.res
│   │   │   ├── preprompt.py
│   │   │   ├── robot_method.py
│   │   │   └── voice.py
│   │   └── basic_template/
│   │       ├── deck.lay
│   │       ├── deck.res
│   │       └── robot_method.py
│   └── transport/
│       ├── __init__.py
│       └── transport.py
├── pytest.ini
├── requirements.txt
├── setup.cfg
├── setup.py
└── tests/
    ├── __init__.py
    └── interface_tests.py
Download .txt
SYMBOL INDEX (702 symbols across 38 files)

FILE: pyhamilton/__init__.py
  function full_paths_list (line 46) | def full_paths_list(directory_abs_path):
  function recursive_copy (line 51) | def recursive_copy(source_dir, target_dir):
  function autoconfig (line 65) | def autoconfig():
  function create_project (line 99) | def create_project():
  function create_ai_project (line 104) | def create_ai_project():

FILE: pyhamilton/consumables/consumables.py
  class VolumeConsumptionTracker (line 7) | class VolumeConsumptionTracker:
    method __init__ (line 8) | def __init__(self, num_positions):
    method aspirate_volume (line 12) | def aspirate_volume(self, well_index, volume):
  class TrackedContainer (line 15) | class TrackedContainer:
    method __init__ (line 18) | def __init__(self):
    method aspirate_volume (line 21) | def aspirate_volume(self, well_index, volume):
  class TrackedReagentVessel (line 28) | class TrackedReagentVessel(TrackedContainer):
    method __init__ (line 29) | def __init__(self, *args, **kwargs):
    method assign_reagent_map (line 32) | def assign_reagent_map(self, reagent_name: str, positions: list[int]) ...
    method reset_volumes (line 42) | def reset_volumes(self):
    method calculate_required_reagent_volume (line 48) | def calculate_required_reagent_volume(self, reagent_name: str):
    method all_required_reagent_volumes (line 57) | def all_required_reagent_volumes(self):
    method reagent_positions (line 60) | def reagent_positions(self, reagent_name):
  class ReagentTrackedPlate96 (line 65) | class ReagentTrackedPlate96(Plate96, TrackedReagentVessel):
    method __init__ (line 66) | def __init__(self, *args, **kwargs):
    method aspirate_volume (line 73) | def aspirate_volume(self, well_index, volume):
  class ReagentTrackedBulkPlate (line 76) | class ReagentTrackedBulkPlate(BulkReagentPlate, TrackedReagentVessel):
    method __init__ (line 77) | def __init__(self, *args, **kwargs):
    method aspirate_volume (line 84) | def aspirate_volume(self, well_index, volume):
    method assign_reagent_map (line 88) | def assign_reagent_map(self, reagent_name, positions):
    method calculate_required_reagent_volume (line 92) | def calculate_required_reagent_volume(self, reagent_name):
  class ReagentTrackedPlate24 (line 96) | class ReagentTrackedPlate24(Plate24, TrackedReagentVessel):
    method __init__ (line 97) | def __init__(self, *args, **kwargs):
    method aspirate_volume (line 104) | def aspirate_volume(self, well_index, volume):
  class ReagentTrackedReservoir60mL (line 107) | class ReagentTrackedReservoir60mL(Reservoir60mL, TrackedReagentVessel):
    method __init__ (line 108) | def __init__(self, *args, **kwargs):
    method aspirate_volume (line 115) | def aspirate_volume(self, well_index, volume):
    method calculate_required_reagent_volume (line 118) | def calculate_required_reagent_volume(self, reagent_name):
    method height_to_volume (line 123) | def height_to_volume(self, height):
  class ReagentTrackedFalconCarrier24 (line 126) | class ReagentTrackedFalconCarrier24(FalconCarrier24, TrackedReagentVessel):
    method __init__ (line 127) | def __init__(self, *args, **kwargs):
    method aspirate_volume (line 134) | def aspirate_volume(self, well_index, volume):
  class ReagentTrackedEppiCarrier32 (line 137) | class ReagentTrackedEppiCarrier32(EppiCarrier32, TrackedReagentVessel):
    method __init__ (line 138) | def __init__(self, *args, **kwargs):
    method aspirate_volume (line 145) | def aspirate_volume(self, well_index, volume):
  function get_class_name (line 150) | def get_class_name(obj):
  function generate_reagent_summary (line 154) | def generate_reagent_summary(tracked_vessels: list, units_default: str =...
  function generate_tip_use_summary (line 214) | def generate_tip_use_summary(tracked_tips_list, output_file=None):
  function tracked_volume_aspirate (line 302) | def tracked_volume_aspirate(ham_int: HamiltonInterface, plate_poss: list...
  function tracked_volume_aspirate_96 (line 315) | def tracked_volume_aspirate_96(ham_int: HamiltonInterface, plate: Tracke...

FILE: pyhamilton/defaults.py
  class Defaults (line 15) | class Defaults:
  function _read_file (line 29) | def _read_file() -> dict[str, Any]:
  function defaults (line 43) | def defaults(**overrides) -> Defaults:
  function save (line 59) | def save(new_defaults: Defaults | None = None) -> None:
  function reload (line 70) | def reload() -> Defaults:

FILE: pyhamilton/devices/centrifuge_wrappers.py
  function centrifuge_initialize (line 16) | def centrifuge_initialize(ham, label, node_name, simulate, always_init):
  function centrifuge_get_drive_status (line 22) | def centrifuge_get_drive_status(ham, label):
  function centrifuge_set_run (line 29) | def centrifuge_set_run(ham, label, array_speed, array_acceleration,

FILE: pyhamilton/devices/hhs_wrappers.py
  function hhs_begin_monitoring (line 17) | def hhs_begin_monitoring(ham, device_number, tolerance_range, interval, ...
  function hhs_create_star_device (line 23) | def hhs_create_star_device(ham, star_device='ML_STAR', used_node=1):
  function hhs_create_usb_device (line 31) | def hhs_create_usb_device(ham, used_node):
  function hhs_end_monitoring (line 37) | def hhs_end_monitoring(ham, device_number):
  function hhs_get_firmware_version (line 43) | def hhs_get_firmware_version(ham, device_number):
  function hhs_get_serial_num (line 49) | def hhs_get_serial_num(ham, device_number):
  function hhs_get_shaker_param (line 55) | def hhs_get_shaker_param(ham, device_number):
  function hhs_get_shaker_speed (line 62) | def hhs_get_shaker_speed(ham, device_number):
  function hhs_get_temp_param (line 68) | def hhs_get_temp_param(ham, device_number):
  function hhs_get_temp (line 75) | def hhs_get_temp(ham, device_number):
  function hhs_get_temp_state (line 81) | def hhs_get_temp_state(ham, device_number):
  function hhs_set_simulation (line 87) | def hhs_set_simulation(ham, simulate):
  function hhs_send_firmware_cmd (line 96) | def hhs_send_firmware_cmd(ham, device_number, command, parameter):
  function hhs_set_plate_lock (line 104) | def hhs_set_plate_lock(ham, device_number, plate_lock):
  function hhs_set_shaker_param (line 108) | def hhs_set_shaker_param(ham, device_number, shaking_direction, shaking_...
  function hhs_set_temp_param (line 114) | def hhs_set_temp_param(ham, device_number, start_timeout, tolerance_rang...
  function hhs_set_usb_trace (line 119) | def hhs_set_usb_trace(ham, trace):
  function hhs_start_all_shaker (line 125) | def hhs_start_all_shaker(ham, shaking_speed):
  function hhs_start_all_shaker_timed (line 131) | def hhs_start_all_shaker_timed(ham, shaking_speed, shaking_time):
  function hhs_start_shaker (line 136) | def hhs_start_shaker(ham, device_number, shaking_speed):
  function hhs_start_shaker_timed (line 140) | def hhs_start_shaker_timed(ham, device_number, shaking_speed, shaking_ti...
  function hhs_start_temp_ctrl (line 145) | def hhs_start_temp_ctrl(ham, device_number, temperature, wait_for_temp_r...
  function hhs_stop_all_shakers (line 152) | def hhs_stop_all_shakers(ham):
  function hhs_stop_shaker (line 157) | def hhs_stop_shaker(ham, device_number):
  function hhs_stop_temp_ctrl (line 161) | def hhs_stop_temp_ctrl(ham, device_number):
  function hhs_terminate (line 165) | def hhs_terminate(ham):
  function hhs_wait_for_shaker (line 169) | def hhs_wait_for_shaker(ham, device_number):
  function hhs_wait_for_temp_ctrl (line 173) | def hhs_wait_for_temp_ctrl(ham, device_number):
  class HHS (line 177) | class HHS:
    method __init__ (line 179) | def __init__(self, node, sequence, lmgr: LayoutManager):
    method layout_name (line 185) | def layout_name(self):

FILE: pyhamilton/devices/hig_wrappers.py
  function hig_connect (line 20) | def hig_connect(ham, device_id, adapter_device_id, simulation_mode):
  function hig_disconnect (line 27) | def hig_disconnect(ham):
  function hig_home (line 32) | def hig_home(ham):
  function hig_spin (line 38) | def hig_spin(ham, Gs, acceleration_pct, deceleration_pct, time):
  function hig_spin_and_wait (line 45) | def hig_spin_and_wait(ham, Gs, acceleration, deceleration, time):
  function hig_open_shield (line 50) | def hig_open_shield(ham, bucket_index):
  function hig_close_shield (line 55) | def hig_close_shield(ham):
  function hig_is_spinning (line 60) | def hig_is_spinning(ham):
  function hig_home (line 67) | def hig_home(ham):

FILE: pyhamilton/devices/mpe_wrappers.py
  function mpe2_connect_ip (line 25) | def mpe2_connect_ip(ham, instrument_name, port_number, simulation_mode, ...
  function mpe2_connect_com (line 32) | def mpe2_connect_com(ham, com_port, baud_rate, simulation_mode, options ...
  function mpe2_clamp_filter_plate (line 38) | def mpe2_clamp_filter_plate(ham, device_id):
  function mpe2_collection_plate_placed (line 44) | def mpe2_collection_plate_placed(ham, device_id, collection_plate_height...
  function mpe2_collection_plate_removed (line 50) | def mpe2_collection_plate_removed(ham, device_id):
  function mpe2_disconnect (line 56) | def mpe2_disconnect(ham, device_id):
  function mpe2_initialize (line 62) | def mpe2_initialize(ham, device_id):
  function mpe2_initialize_with_params (line 67) | def mpe2_initialize_with_params(ham, device_id, smart, waste_container_i...
  function mpe2_filter_plate_placed (line 72) | def mpe2_filter_plate_placed(ham, device_id, filter_height, nozzle_height):
  function mpe2_filter_plate_removed (line 77) | def mpe2_filter_plate_removed(ham, device_id):
  function mpe2_process_filter_to_collection_plate (line 82) | def mpe2_process_filter_to_collection_plate(ham, device_id, control_poin...
  function mpe2_process_filter_to_waste_container (line 87) | def mpe2_process_filter_to_waste_container(ham, device_id, control_point...
  function mpe2_retrieve_filter_plate (line 92) | def mpe2_retrieve_filter_plate(ham, device_id):
  function mpe2_start_mpe_vacuum (line 97) | def mpe2_start_mpe_vacuum(ham, device_id, waste_container_id='', disable...
  function mpe2_stop_vacuum (line 102) | def mpe2_stop_vacuum(ham, device_id):
  function mpe2_get_vacuum_status (line 107) | def mpe2_get_vacuum_status(ham, device_id):
  function mpe2_get_pressure_readings (line 112) | def mpe2_get_pressure_readings(ham, device_id):
  function mpe2_dispense (line 118) | def mpe2_dispense(ham, device_id, source_id, well_volume, flow_rate_aspi...
  function mpe2_prime (line 124) | def mpe2_prime(ham, device_id, source_id, well_volume, flow_rate, waste_...
  function mpe2_flush (line 130) | def mpe2_flush(ham, device_id, well_volume, flow_rate, waste_container_id):
  function mpe2_evaporate (line 136) | def mpe2_evaporate(ham, device_id, plate_height, needle_offset, well_dep...
  function mpe2_evaporate_with_rate (line 143) | def mpe2_evaporate_with_rate(ham, device_id, plate_height, needle_offset...
  function mpe2_evaporate_end (line 148) | def mpe2_evaporate_end(ham, device_id, timeout):
  function mpe2_get_temperature_range (line 153) | def mpe2_get_temperature_range(ham, device_id):
  function mpe2_get_heater_status (line 158) | def mpe2_get_heater_status(ham, device_id, reset):
  function mpe2_get_heater_range (line 163) | def mpe2_get_heater_range(ham, device_id, reset):

FILE: pyhamilton/devices/odtc_wrappers.py
  function odtc_abort (line 21) | def odtc_abort(ham, device_id, lock_id):
  function odtc_connect (line 27) | def odtc_connect(ham, simulation_mode, local_ip, device_ip, device_port ...
  function odtc_initialize (line 39) | def odtc_initialize(ham, device_id, lock_id = ''):
  function odtc_close_door (line 45) | def odtc_close_door(ham, device_id, lock_id = ''):
  function odtc_download_protocol (line 51) | def odtc_download_protocol(ham, device_id, protocol_file, lock_id = ''):
  function odtc_evaluate_error (line 57) | def odtc_evaluate_error(ham, device_id, lock_id = ''):
  function odtc_execute_protocol (line 63) | def odtc_execute_protocol(ham, device_id, method_name, simulating, prior...
  function odtc_get_status (line 83) | def odtc_get_status(ham, device_id, simulating):
  function odtc_open_door (line 113) | def odtc_open_door(ham, device_id, lock_id = ''):
  function odtc_read_actual_temperature (line 119) | def odtc_read_actual_temperature(ham, device_id, lock_id = ''):
  function odtc_reset (line 129) | def odtc_reset(ham, device_id, simulation_mode, timeout, str_device_id =...
  function odtc_stop_method (line 135) | def odtc_stop_method(ham, device_id, lock_id):
  function odtc_terminate (line 141) | def odtc_terminate(ham, device_id):
  function odtc_wait_for_idle (line 147) | def odtc_wait_for_idle(ham, device_id, simulating, check_interval=5, max...

FILE: pyhamilton/devices/pH_wrappers.py
  function ph_controller_initialize (line 27) | def ph_controller_initialize(ham, port_number, raise_first_exception=Tru...
  function ph_controller_parameters (line 32) | def ph_controller_parameters(ham, module_id, seq_gripper, seq_wash, seq_...
  function ph_controller_pickup (line 49) | def ph_controller_pickup(ham, module_id, seq_module, raise_first_excepti...
  function ph_controller_park (line 53) | def ph_controller_park(ham, module_id, seq_module, raise_first_exception...
  function ph_controller_calibrate (line 58) | def ph_controller_calibrate(ham, module_id, seq_module, seq_solution_1,s...
  function ph_controller_measure_cycle (line 73) | def ph_controller_measure_cycle(ham, module_id, pos, measure_height, pro...
  function ph_controller_wash (line 93) | def ph_controller_wash(ham, module_id, raise_first_exception=True, wait_...
  function ph_controller_dry (line 97) | def ph_controller_dry(ham, module_id, raise_first_exception=True, wait_o...
  function ph_controller_loadconfig (line 101) | def ph_controller_loadconfig(ham, raise_first_exception=True, wait_on_re...
  function ph_controller_saveconfig (line 108) | def ph_controller_saveconfig(ham, bluetooth_port, num_wash_cycles, num_d...
  function ph_initialize (line 120) | def ph_initialize(ham, comport, simulate, asynch=False, raise_first_exce...
  function ph_req_battery_data (line 125) | def ph_req_battery_data(ham, module_id, raise_first_exception=True, wait...
  function ph_measure (line 131) | def ph_measure(ham, module_id, temperature, probePattern, raise_first_ex...
  function ph_measure_dynamic (line 137) | def ph_measure_dynamic(ham, module_id, temperature, precision, timeout, ...
  function ph_request_calibration (line 144) | def ph_request_calibration(ham, module_id, probe_number, raise_first_exc...
  function ph_request_probe_data (line 153) | def ph_request_probe_data(ham, module_id, raise_first_exception=True, wa...
  function ph_request_technical_data (line 161) | def ph_request_technical_data(ham, module_id, hardware_number, raise_fir...
  function ph_calibrate (line 167) | def ph_calibrate(ham, module_id, cal_level, cal_value, cal_temperature, ...
  function ph_calibrate_dynamically (line 174) | def ph_calibrate_dynamically(ham, module_id, variance, timeout, cal_leve...
  function ph_wakeup (line 182) | def ph_wakeup(ham, module_id, raise_first_exception=True, wait_on_respon...
  function ph_sleep (line 186) | def ph_sleep(ham, module_id, raise_first_exception=True, wait_on_respons...
  function ph_washer_initialize (line 190) | def ph_washer_initialize(ham, comport, simulate, raise_first_exception=T...
  function ph_washer_wash (line 195) | def ph_washer_wash(ham, module_id, cycle_num, raise_first_exception=True...
  function ph_washer_terminate (line 199) | def ph_washer_terminate(ham, module_id, raise_first_exception=True, wait...
  function ph_dryer_initialize (line 203) | def ph_dryer_initialize(ham, comport, simulate, raise_first_exception=Tr...
  function ph_dryer_start (line 208) | def ph_dryer_start(ham, module_id, raise_first_exception=True, wait_on_r...
  function ph_dryer_stop (line 212) | def ph_dryer_stop(ham, module_id, raise_first_exception=True, wait_on_re...
  function ph_dryer_terminate (line 216) | def ph_dryer_terminate(ham, module_id, raise_first_exception=True, wait_...

FILE: pyhamilton/devices/tec_wrappers.py
  function initialize_tec (line 10) | def initialize_tec(ham, controller_id, simulating):
  function set_temperature_target_tec (line 15) | def set_temperature_target_tec(ham, target_temp, controller_id, device_id):
  function get_temperature_tec (line 20) | def get_temperature_tec(ham:HamiltonInterface, controller_id, device_id,...
  function start_temperature_control_tec (line 26) | def start_temperature_control_tec(ham, controller_id, device_id):
  function stop_temperature_control_tec (line 31) | def stop_temperature_control_tec(ham, controller_id, device_id):
  function terminate_tec (line 36) | def terminate_tec(ham, stop_all_devices):

FILE: pyhamilton/interface.py
  function invert_columns (line 15) | def invert_columns(pos_str: str, sep: str = ';') -> str:
  class HamiltonCmdTemplate (line 27) | class HamiltonCmdTemplate:
    method unique_id (line 37) | def unique_id():
    method __init__ (line 41) | def __init__(self, cmd_name, params_list):
    method assemble_cmd (line 63) | def assemble_cmd(self, *args, **kwargs):
    method assert_valid_cmd (line 79) | def assert_valid_cmd(self, cmd_dict):
  function labware_pos_str (line 134) | def labware_pos_str(labware, idx):
  function _make_new_hamilton_serv_handler (line 138) | def _make_new_hamilton_serv_handler(resp_indexing_fn):
  class HamiltonServerHandler (line 144) | class HamiltonServerHandler(server.BaseHTTPRequestHandler):
    method set_indexing_fn (line 150) | def set_indexing_fn(cls, fn):
    method send_str (line 155) | def send_str(cmd_str):
    method has_queued_cmds (line 164) | def has_queued_cmds():
    method pop_response (line 168) | def pop_response(idx):
    method _set_headers (line 172) | def _set_headers(self):
    method do_GET (line 177) | def do_GET(self):
    method do_HEAD (line 183) | def do_HEAD(self):
    method do_POST (line 186) | def do_POST(self):
    method log_message (line 197) | def log_message(self, *args, **kwargs):
  function run_hamilton_process (line 201) | def run_hamilton_process():
  class HamiltonResponseStatus (line 231) | class HamiltonResponseStatus(Enum):
  class HamiltonResponse (line 253) | class HamiltonResponse:
    method _compute_status (line 304) | def _compute_status(self):
    method _return_data (line 342) | def _return_data(self, fields):
    method _moduleID (line 351) | def _moduleID(self):
    method _parse_return (line 358) | def _parse_return(self):
    method digest (line 393) | def digest(self, fields=None):
    method raise_first_exception (line 399) | def raise_first_exception(self):
  class DispenseResult (line 433) | class DispenseResult:
  class AspirateResult (line 439) | class AspirateResult:
  class HamiltonServerThread (line 445) | class HamiltonServerThread(Thread):
    method __init__ (line 448) | def __init__(self, address, port):
    method run (line 467) | def run(self):
    method disconnect (line 492) | def disconnect(self):
    method has_exited (line 497) | def has_exited(self):
  class HamiltonInterface (line 501) | class HamiltonInterface:
    method __init__ (line 527) | def __init__(self, address=None, port=None, simulating = False, debug=...
    method _open (line 557) | def _open(self):
    method start (line 578) | def start(self):
    method stop (line 619) | def stop(self):
    method __enter__ (line 676) | def __enter__(self):
    method __exit__ (line 680) | def __exit__(self, exc_type, exc_value, tb):
    method is_open (line 692) | def is_open(self):
    method send_command (line 696) | def send_command(self, template=None, block_until_sent=False, *args, *...
    method wait_on_response (line 738) | def wait_on_response(self, id, timeout=60, raise_first_exception=False...
    method parse_response (line 783) | def parse_response(self, server_response:str, raise_first_exception:bo...
    method _block_until_sq_clear (line 804) | def _block_until_sq_clear(self):
    method set_log_dir (line 810) | def set_log_dir(self, log_dir):
    method log (line 819) | def log(self, msg, msg_type='info'):
    method _dump_log_queue (line 823) | def _dump_log_queue(self):
    method log_and_raise (line 835) | def log_and_raise(self, err):
    method _channel_var (line 840) | def _channel_var(pos_tuples):
    method _compound_pos_str (line 849) | def _compound_pos_str(pos_tuples):
    method _compound_pos_str_96 (line 856) | def _compound_pos_str_96(labware96):
    method _assert_parallel_nones (line 862) | def _assert_parallel_nones(list1, list2):
    method initialize (line 868) | def initialize(self, **more_options):
    method aspirate (line 886) | def aspirate(self, pos_tuples, vols, **more_options) -> AspirateResult:
    method dispense (line 948) | def dispense(self, pos_tuples, vols, **more_options) -> DispenseResult:
    method tip_pick_up (line 1007) | def tip_pick_up(self, pos_tuples, **more_options):
    method tip_eject (line 1030) | def tip_eject(self, pos_tuples=None, **more_options):
    method tip_pick_up_96 (line 1062) | def tip_pick_up_96(self, tip96, **more_options):
    method tip_eject_96 (line 1080) | def tip_eject_96(self, tip96=None, **more_options):
    method aspirate_96 (line 1106) | def aspirate_96(self, plate96, vol, **more_options):
    method tip_pick_up_mph_columns (line 1135) | def tip_pick_up_mph_columns(self, tip_96, num_columns_from_left, **mor...
    method dispense_96 (line 1160) | def dispense_96(self, plate96, vol, **more_options):
    method aspirate_384_quadrant (line 1191) | def aspirate_384_quadrant(self, plate384, quadrant, vol, **more_options):
    method dispense_384_quadrant (line 1216) | def dispense_384_quadrant(self, plate384, quadrant, vol, **more_options):
    method set_labware_property (line 1241) | def set_labware_property(self, labware_id, property_name, property_val...
    method _compound_pos_str_384_quad (line 1262) | def _compound_pos_str_384_quad(plate384, quadrant):
    method move_plate (line 1272) | def move_plate(self, source_plate, target_plate, CmplxGetDict=None, Cm...
    method move_by_seq (line 1335) | def move_by_seq(self, source_plate_seq, target_plate_seq, CmplxGetDict...
    method get_plate_gripper_seq (line 1400) | def get_plate_gripper_seq(self, source_plate_seq, gripHeight, gripWidt...
    method move_plate_gripper_seq (line 1424) | def move_plate_gripper_seq(self, dest_plate_seq, **more_options):
    method place_plate_gripper_seq (line 1435) | def place_plate_gripper_seq(self, dest_plate_seq, tool_sequence, **mor...
    method move_plate_gripper (line 1447) | def move_plate_gripper(self, source_poss, dest_poss, **more_options):
    method move_sequence (line 1459) | def move_sequence(self, sequence, xDisplacement=0, yDisplacement=0, zD...
    method load_carrier (line 1475) | def load_carrier(self, carrier_name, **more_options):
    method unload_carrier (line 1514) | def unload_carrier(self, carrier_name, **more_options):
  class JSONLogger (line 1525) | class JSONLogger:
    method __init__ (line 1526) | def __init__(self):
    method log (line 1530) | def log(self, message):
    method set_log_dir (line 1533) | def set_log_dir(self, log_dir):

FILE: pyhamilton/liquid_class_db.py
  class DispenseMode (line 12) | class DispenseMode(Enum):
    method to_code (line 25) | def to_code(self) -> int:
    method from_code (line 38) | def from_code(code: int) -> 'DispenseMode':
    method from_string (line 54) | def from_string(cls, identifier: str) -> 'DispenseMode':
  function _check_access_dialect (line 63) | def _check_access_dialect() -> None:
  function _build_engine (line 71) | def _build_engine(mdb_path: str):
  function load_liquid_classes (line 80) | def load_liquid_classes():
  function _get_liquid_class_data (line 134) | def _get_liquid_class_data(liquid_class_name: str, columns: Union[str, L...
  function check_liquid_class_exists (line 169) | def check_liquid_class_exists(liquid_class_name: str) -> bool:
  function liquid_class_has_parameter (line 192) | def liquid_class_has_parameter(liquid_class_name: str, parameter: str, v...
  function get_liquid_class_column_details (line 218) | def get_liquid_class_column_details() -> List[Dict[str, Any]]:
  function get_liquid_class_columns (line 232) | def get_liquid_class_columns() -> List[str]:
  function get_liquid_class_columns_with_types (line 241) | def get_liquid_class_columns_with_types() -> Dict[str, str]:
  function print_table_schema (line 250) | def print_table_schema():
  function get_all_table_names (line 277) | def get_all_table_names() -> List[str]:
  function get_liquid_class_dispense_mode (line 290) | def get_liquid_class_dispense_mode(liquid_class_name: str) -> str:
  function get_liquid_class_volume (line 306) | def get_liquid_class_volume(liquid_class_name: str, nominal=False) -> int:
  function get_liquid_class_parameter (line 336) | def get_liquid_class_parameter(liquid_class_name: str, parameter_name: s...
  function create_correction_curve (line 354) | def create_correction_curve(data: Tuple[float, ...]) -> collections.Orde...
  function unpack_doubles_dynamic (line 367) | def unpack_doubles_dynamic(byte_string: bytes) -> tuple:
  function export_liquid_classes_to_csv (line 377) | def export_liquid_classes_to_csv(directory="./liquid_class_data", filena...

FILE: pyhamilton/liquid_classes.py
  class AspirateParameter (line 11) | class AspirateParameter(Enum):
  class DispenseParameter (line 21) | class DispenseParameter(Enum):
  function copy_liquid_class (line 54) | def copy_liquid_class(ham_int, template_liquid_class: str, new_liquid_cl...
  function set_aspirate_parameter (line 62) | def set_aspirate_parameter(ham_int, liquid_class: str, parameter: Aspira...
  function set_dispense_parameter (line 71) | def set_dispense_parameter(ham_int, liquid_class: str, parameter: Dispen...
  function set_tip_type (line 80) | def set_tip_type(ham_int, liquid_class: str, tip_type: int):
  function set_dispense_mode (line 87) | def set_dispense_mode(ham_int, liquid_class: str, dispense_mode: int):
  function set_correction_curve (line 95) | def set_correction_curve(ham_int, liquid_class: str, nominal_array: list...
  function validate_liquid_class_definitions (line 105) | def validate_liquid_class_definitions(definitions: list):
  function create_liquid_class_from_dict (line 160) | def create_liquid_class_from_dict(
  function create_liquid_class_from_json (line 260) | def create_liquid_class_from_json(

FILE: pyhamilton/liquid_handling_wrappers.py
  function labware_pos_str (line 27) | def labware_pos_str(labware, idx):
  function compound_pos_str (line 30) | def compound_pos_str(pos_tuples):
  function compound_pos_str_96 (line 34) | def compound_pos_str_96(labware96):
  function cells_384_to_1536 (line 37) | def cells_384_to_1536(well, idx):
  function cells_96_to_384 (line 40) | def cells_96_to_384(well, idx):
  function wells_384_to_96 (line 43) | def wells_384_to_96(x):
  function get_cells_from_position_384 (line 48) | def get_cells_from_position_384(well):
  function get_cells_from_position_96 (line 51) | def get_cells_from_position_96(well):
  function get_384w_quadrant (line 54) | def get_384w_quadrant(quadrant):
  function compound_pos_str_384_quad (line 57) | def compound_pos_str_384_quad(labware384, quadrant):
  function initialize (line 61) | def initialize(ham, asynch=False):
  function hepa_on (line 68) | def hepa_on(ham, speed=15, asynch=False, **more_options):
  function wash_empty_refill (line 76) | def wash_empty_refill(ham, asynch=False, **more_options):
  function move_plate (line 85) | def move_plate(ham, source_plate, target_plate, CmplxGetDict=None, Cmplx...
  function move_by_seq (line 89) | def move_by_seq(ham, source_plate_seq, target_plate_seq, CmplxGetDict=No...
  function channel_var (line 93) | def channel_var(pos_tuples):
  function tip_pick_up (line 100) | def tip_pick_up(ham_int, pos_tuples, **more_options):
  function tip_eject (line 104) | def tip_eject(ham_int, pos_tuples=None, **more_options):
  function assert_parallel_nones (line 108) | def assert_parallel_nones(list1, list2):
  class TimerHandle (line 114) | class TimerHandle:
    method __init__ (line 115) | def __init__(self, seconds):
    method wait (line 121) | def wait(self, skip=False):
  function start_timer (line 127) | def start_timer(seconds):
  function aspirate (line 134) | def aspirate(ham_int, pos_tuples, vols, **more_options):
  function dispense (line 138) | def dispense(ham_int, pos_tuples, vols, **more_options):
  function tip_pick_up_96 (line 143) | def tip_pick_up_96(ham_int, tip96, **more_options):
  function tip_eject_96 (line 147) | def tip_eject_96(ham_int, tip96=None, **more_options):
  function aspirate_96 (line 151) | def aspirate_96(ham_int, plate96, vol, **more_options):
  function dispense_96 (line 155) | def dispense_96(ham_int, plate96, vol, **more_options):
  function aspirate_384_quadrant (line 159) | def aspirate_384_quadrant(ham_int, plate384, quadrant, vol, **more_optio...
  function dispense_384_quadrant (line 163) | def dispense_384_quadrant(ham_int, plate384, quadrant, vol, **more_optio...
  function copy_liquid_class (line 189) | def copy_liquid_class(ham_int, template_liquid_class, new_liquid_class):
  function set_aspirate_parameter (line 193) | def set_aspirate_parameter(ham_int, LiquidClass, Parameter, Value):
  function set_dispense_parameter (line 198) | def set_dispense_parameter(ham_int, LiquidClass, Parameter, Value):
  function set_tip_type (line 203) | def set_tip_type(ham_int, LiquidClass, TipType):
  function set_correction_curve (line 207) | def set_correction_curve(ham_int, LiquidClass, NominalArray, CorrectedAr...
  function move_sequence (line 211) | def move_sequence(ham_int, sequence, xDisplacement=0, yDisplacement=0, z...
  function tilt_module_initialize (line 215) | def tilt_module_initialize(ham_int, module_name, comport, trace_level, s...
  function tilt_module_move (line 222) | def tilt_module_move(ham_int, module_name, angle):
  function get_plate_gripper_seq (line 226) | def get_plate_gripper_seq(ham, source_plate_seq, gripHeight, gripWidth, ...
  function move_plate_gripper_seq (line 230) | def move_plate_gripper_seq(ham, dest_plate_seq, **more_options):
  function place_plate_gripper_seq (line 234) | def place_plate_gripper_seq(ham, dest_plate_seq, tool_sequence, **more_o...
  function move_plate_gripper (line 238) | def move_plate_gripper(ham, dest_poss, **more_options):
  function move_plate_using_gripper (line 243) | def move_plate_using_gripper(ham_int: HamiltonInterface, source: str, de...
  function tracked_tip_pick_up (line 252) | def tracked_tip_pick_up(ham_int: HamiltonInterface, tips_tracker: Tracke...
  function tracked_tip_eject (line 270) | def tracked_tip_eject(ham_int: HamiltonInterface, tips_tracker: TrackedT...
  function tracked_tip_pick_up_96 (line 281) | def tracked_tip_pick_up_96(ham_int: HamiltonInterface, tips_tracker: Tra...
  function tip_support_pickup_columns (line 293) | def tip_support_pickup_columns(ham_int: HamiltonInterface, tips:TrackedT...
  class StderrLogger (line 301) | class StderrLogger:
    method __init__ (line 302) | def __init__(self, level):
    method write (line 306) | def write(self, message):
  function add_stderr_logging (line 311) | def add_stderr_logging(logger_name=None):
  function normal_logging (line 315) | def normal_logging(ham_int, method_local_dir):
  function run_async (line 334) | def run_async(funcs):
  function run_async_dict (line 347) | def run_async_dict(func):
  function yield_in_chunks (line 357) | def yield_in_chunks(sliceable, n):
  function log_banner (line 365) | def log_banner(banner_text):

FILE: pyhamilton/ngs/loading/deck_annotation.py
  function load_image_pil (line 41) | def load_image_pil(path: str):
  function load_regions_from_json (line 47) | def load_regions_from_json(path: str):
  function save_regions_to_json (line 70) | def save_regions_to_json(path: str, regions_list: list, image_path_str: ...
  function draw_all_elements (line 92) | def draw_all_elements(canvas):
  function get_closest_point_index (line 116) | def get_closest_point_index(mouse_pos, points, thresh=5):
  function on_mouse_down (line 123) | def on_mouse_down(event):
  function on_mouse_right_click (line 150) | def on_mouse_right_click(event):
  function on_mouse_up (line 160) | def on_mouse_up(event):
  function on_mouse_move (line 165) | def on_mouse_move(event):
  function on_key_press (line 170) | def on_key_press(event, root):
  function save_json (line 176) | def save_json():
  function update_treeview (line 186) | def update_treeview():
  function on_treeview_click (line 198) | def on_treeview_click(event):
  function edit_name (line 221) | def edit_name(item_id):
  function set_name (line 237) | def set_name(item_id):
  function edit_color (line 249) | def edit_color(item_id):
  function edit_resource_type (line 257) | def edit_resource_type(item_id):
  function set_resource_type (line 274) | def set_resource_type(item_id):
  function delete_selected_region (line 286) | def delete_selected_region():
  function main (line 296) | def main():

FILE: pyhamilton/ngs/loading/loading_vis.py
  class Region (line 49) | class Region:
    method from_dict (line 57) | def from_dict(cls, name: str, d: Dict) -> "Region":
  class ReagentVessel (line 91) | class ReagentVessel:
    method __init__ (line 92) | def __init__(self, name: str):
    method layout_name (line 95) | def layout_name(self):
    method __init__ (line 721) | def __init__(self, name):
    method layout_name (line 724) | def layout_name(self):
  class LoadingVis (line 101) | class LoadingVis:
    method __init__ (line 111) | def __init__(
    method _shift_regions (line 237) | def _shift_regions(self, offset: Tuple[int, int]) -> None:
    method _crop_to_regions (line 248) | def _crop_to_regions(self, margin: int = 12) -> None:
    method _auto_trim_uniform_borders (line 266) | def _auto_trim_uniform_borders(self, tol: int = 3, max_crop: int = 400...
    method _apply_alias_rules (line 299) | def _apply_alias_rules(self, region_name) -> str:
    method _create_vessels_from_data (line 307) | def _create_vessels_from_data(self) -> List[DeckResource]:
    method _load_reagent_map (line 322) | def _load_reagent_map(self, reagent_data: Union[str, Path, Dict]) -> N...
    method render (line 380) | def render(
    method show (line 512) | def show(
    method save (line 539) | def save(self, img_bgr: np.ndarray, out_path: Union[str, Path]) -> None:
    method missing_for (line 544) | def missing_for(self, vessels: List[DeckResource]) -> List[str]:
    method has_tube_rack_data (line 553) | def has_tube_rack_data(self) -> bool:
    method _cleanup_cv_windows (line 558) | def _cleanup_cv_windows(self, deck_window_name, plate_enabled):
    method show_dialogues (line 573) | def show_dialogues(
    method ShowDialogues (line 705) | def ShowDialogues(self, *args, **kwargs):
  class ReagentVessel (line 720) | class ReagentVessel:
    method __init__ (line 92) | def __init__(self, name: str):
    method layout_name (line 95) | def layout_name(self):
    method __init__ (line 721) | def __init__(self, name):
    method layout_name (line 724) | def layout_name(self):

FILE: pyhamilton/ngs/loading/plate_96_render.py
  function render_plate_96 (line 27) | def render_plate_96(
  function _draw_text_simple (line 197) | def _draw_text_simple(canvas: np.ndarray, text: str, pos: Tuple[int, int...

FILE: pyhamilton/ngs/loading/rendering_helpers.py
  function draw_text_pillow (line 28) | def draw_text_pillow(img_bgr: np.ndarray, text: str, org: Tuple[int, int],
  function _resolve_font_path (line 76) | def _resolve_font_path(pref: Optional[str] = ARIAL_TTF) -> Optional[str]:
  function _measure_text_pillow (line 84) | def _measure_text_pillow(text: str, font_path: Optional[str], px: Union[...
  function _rescale_image (line 108) | def _rescale_image(img: np.ndarray, scale: float) -> np.ndarray:
  function _ascii_label (line 118) | def _ascii_label(s: str) -> str:
  function _label_from_info_dict (line 125) | def _label_from_info_dict(info: Dict, *, unit_default: str, fallback: st...
  function _name_to_color_bgr (line 139) | def _name_to_color_bgr(name: str) -> Tuple[int, int, int]:
  function _norm_key (line 148) | def _norm_key(k: str) -> str:
  function _ensure_bgr (line 151) | def _ensure_bgr(img: np.ndarray) -> np.ndarray:
  function _draw_transparent_rect (line 161) | def _draw_transparent_rect(
  function _draw_transparent_polygon (line 174) | def _draw_transparent_polygon(
  function _rect_xyxy (line 195) | def _rect_xyxy(p1, p2) -> Tuple[int, int, int, int]:
  function _rect_overlap_area (line 201) | def _rect_overlap_area(a, b) -> int:
  function _intersects_any (line 208) | def _intersects_any(r, rects) -> bool:
  function _fan_offsets (line 211) | def _fan_offsets(max_shift: int, step: int):
  function _fmt_volume (line 220) | def _fmt_volume(vol: Any, unit_default: str) -> str:
  function _preferred_side_for_key (line 230) | def _preferred_side_for_key(key_norm: str) -> str:
  function _best_label_box_outside (line 249) | def _best_label_box_outside(
  function _get_polygon_min_area_rect_center_and_bbox (line 420) | def _get_polygon_min_area_rect_center_and_bbox(points: List[Tuple[int, i...
  function _min_distance_to_polygon (line 453) | def _min_distance_to_polygon(rect_xyxy: Tuple[int, int, int, int], poly_...
  function _find_poly_edge_x (line 499) | def _find_poly_edge_x(poly_points: List[Tuple[int, int]], y_min: int, y_...

FILE: pyhamilton/ngs/loading/tube_rack_render.py
  class TubeRackRenderer (line 41) | class TubeRackRenderer:
    method render_tube_rack_screen (line 43) | def render_tube_rack_screen(
    method show_tkinter_modal (line 202) | def show_tkinter_modal(

FILE: pyhamilton/ngs/protocol.py
  class Protocol (line 8) | class Protocol:
    method __init__ (line 10) | def __init__(self):
    method prompt_step_selection (line 24) | def prompt_step_selection(self):
    method reset_tracked_resources (line 334) | def reset_tracked_resources(self):
    method run_selected_steps (line 356) | def run_selected_steps(self, steps: list, simulation: bool = True, win...
    method end (line 377) | def end(self):
    method run_protocol (line 394) | def run_protocol(self, simulating=False, output_file="reagent_summary....
    method show_loading_dialogues (line 410) | def show_loading_dialogues(self, parent=None):

FILE: pyhamilton/ngs/tadm.py
  class USBTraceParser (line 13) | class USBTraceParser:
    method __init__ (line 17) | def __init__(self, debug: bool = False):
    method parse_file (line 20) | def parse_file(self, filename: str | Path):
  class LiquidHandlerCommand (line 63) | class LiquidHandlerCommand:
  class Association (line 77) | class Association:
  class TraceParser (line 85) | class TraceParser:
    method __init__ (line 86) | def __init__(self, debug=False):
    method parse_liquid_handler_trace (line 91) | def parse_liquid_handler_trace(self, content: str):
    method extract_channel_info (line 152) | def extract_channel_info(self, line: str):
    method associate_commands (line 173) | def associate_commands(self) -> List[Association]:
  function extract_clean_command (line 206) | def extract_clean_command(trace_line: str) -> str:
  function format_channel_info (line 230) | def format_channel_info(lh_cmd: LiquidHandlerCommand) -> str:
  function generate_json_report (line 241) | def generate_json_report(associations: List, output_file: str):
  function get_last_usb_data_block (line 307) | def get_last_usb_data_block() -> Optional[dict]:
  function generate_tadm_report_with_json (line 342) | def generate_tadm_report_with_json(html_output="tadm_report.html", json_...
  function generate_html_report (line 385) | def generate_html_report(associations: list, output_file: str):
  function usb_block_plot_base64 (line 485) | def usb_block_plot_base64(usb_block: dict) -> str:
  function find_most_recent_trace_file (line 502) | def find_most_recent_trace_file(directory: Path, pattern: str) -> Option...
  function generate_tadm_report (line 520) | def generate_tadm_report(output = "tadm_report.html"):

FILE: pyhamilton/ngs/tests/bead_cleanup.py
  function estimate_tip_consumption (line 103) | def estimate_tip_consumption(num_samples: int):
  function initialize (line 112) | def initialize(simulation=True):
  function BeadCleanup (line 124) | def BeadCleanup(num_samples, sample_volume, starting_from=0):

FILE: pyhamilton/ngs/tests/hhs.py
  class HHS (line 29) | class HHS:
    method __init__ (line 31) | def __init__(self, node, sequence):
    method layout_name (line 35) | def layout_name(self):
    method node (line 38) | def node(self):
  function initialize_hhs (line 49) | def initialize_hhs(simulation):

FILE: pyhamilton/ngs/tests/magnetic_bead_cleanup.py
  function magnetic_bead_cleanup (line 15) | def magnetic_bead_cleanup():
  class HHS (line 78) | class HHS:
    method __init__ (line 80) | def __init__(self, node, layout_name):
  function initialize_hhs (line 93) | def initialize_hhs(ham_int, simulating):

FILE: pyhamilton/ngs/tests/multi_dispense.py
  function condense_volumes (line 29) | def condense_volumes(lst, max_volume):

FILE: pyhamilton/ngs/tests/thermal_cycler/thermal_cycler_with_transport.py
  function thermal_cycle_with_plate_movement (line 15) | def thermal_cycle_with_plate_movement(ham_int, source_plate_sequence, od...

FILE: pyhamilton/odtc/odtc_protocol.py
  class ThermalCyclerProtocol (line 4) | class ThermalCyclerProtocol:
    method __init__ (line 8) | def __init__(self, creator="stefan", variant=960000, plate_type=0, flu...
    method add_step (line 28) | def add_step(self, plateau_temp, plateau_time, slope=1, overshoot_temp...
    method add_pcr_cycle (line 49) | def add_pcr_cycle(self, denaturation_temp, denaturation_time, annealin...
    method add_final_extension (line 65) | def add_final_extension(self, temp, time):
    method set_pid_parameters (line 69) | def set_pid_parameters(self, pid_number, p_heating, p_cooling, i_heati...
    method set_pre_method (line 79) | def set_pre_method(self, block_temp=25, lid_temp=110):
    method generate_xml (line 85) | def generate_xml(self, filename="protocol.xml"):

FILE: pyhamilton/oemerr.py
  class HamiltonError (line 3) | class HamiltonError(Exception):
  class HamiltonDeckResourceError (line 13) | class HamiltonDeckResourceError(HamiltonError):
  class ResourceUnavailableError (line 19) | class ResourceUnavailableError(HamiltonDeckResourceError):
  class HamiltonInterfaceError (line 29) | class HamiltonInterfaceError(HamiltonError):
  class HamiltonTimeoutError (line 35) | class HamiltonTimeoutError(HamiltonInterfaceError):
  class InvalidErrCodeError (line 41) | class InvalidErrCodeError(HamiltonInterfaceError):
  class HamiltonReturnParseError (line 47) | class HamiltonReturnParseError(HamiltonInterfaceError):
  class HamiltonStepError (line 57) | class HamiltonStepError(HamiltonError):
  class HamiltonSyntaxError (line 63) | class HamiltonSyntaxError(HamiltonStepError):
  class HardwareError (line 69) | class HardwareError(HamiltonStepError):
  class NotExecutedError (line 75) | class NotExecutedError(HamiltonStepError):
  class ClotError (line 81) | class ClotError(HamiltonStepError):
  class BarcodeError (line 87) | class BarcodeError(HamiltonStepError):
  class InsufficientLiquidError (line 93) | class InsufficientLiquidError(HamiltonStepError):
  class TipPresentError (line 99) | class TipPresentError(HamiltonStepError):
  class NoTipError (line 105) | class NoTipError(HamiltonStepError):
  class NoCarrierError (line 111) | class NoCarrierError(HamiltonStepError):
  class ExecutionError (line 117) | class ExecutionError(HamiltonStepError):
  class PressureLLDError (line 123) | class PressureLLDError(HamiltonStepError):
  class CalibrateError (line 129) | class CalibrateError(HamiltonStepError):
  class UnloadError (line 135) | class UnloadError(HamiltonStepError):
  class PressureLLDError (line 141) | class PressureLLDError(HamiltonStepError):
  class ParameterError (line 147) | class ParameterError(HamiltonStepError):
  class CoverOpenError (line 153) | class CoverOpenError(HamiltonStepError):
  class ImproperAspirationOrDispenseError (line 159) | class ImproperAspirationOrDispenseError(HamiltonStepError):
  class WashLiquidError (line 165) | class WashLiquidError(HamiltonStepError):
  class TemperatureError (line 171) | class TemperatureError(HamiltonStepError):
  class TADMOvershotError (line 177) | class TADMOvershotError(HamiltonStepError):
  class LabwareError (line 189) | class LabwareError(HamiltonStepError):
  class LabwareGrippedError (line 195) | class LabwareGrippedError(HamiltonStepError):
  class LabwareLostError (line 201) | class LabwareLostError(HamiltonStepError):
  class IllegalTargetPlatePositionError (line 207) | class IllegalTargetPlatePositionError(HamiltonStepError):
  class IllegalInterventionError (line 213) | class IllegalInterventionError(HamiltonStepError):
  class TADMUndershotError (line 219) | class TADMUndershotError(HamiltonStepError):
  class PositionError (line 231) | class PositionError(HamiltonStepError):
  class UnexpectedcLLDError (line 237) | class UnexpectedcLLDError(HamiltonStepError):
  class AreaAlreadyOccupiedError (line 243) | class AreaAlreadyOccupiedError(HamiltonStepError):
  class ImpossibleToOccupyAreaError (line 249) | class ImpossibleToOccupyAreaError(HamiltonStepError):
  class AntiDropControlError (line 255) | class AntiDropControlError(HamiltonStepError):
  class DecapperError (line 261) | class DecapperError(HamiltonStepError):
  class DecapperHandlingError (line 267) | class DecapperHandlingError(HamiltonStepError):
  class SlaveError (line 273) | class SlaveError(HamiltonStepError):
  class WrongCarrierError (line 279) | class WrongCarrierError(HamiltonStepError):
  class NoCarrierBarcodeError (line 285) | class NoCarrierBarcodeError(HamiltonStepError):
  class LiquidLevelError (line 291) | class LiquidLevelError(HamiltonStepError):
  class NotDetectedError (line 299) | class NotDetectedError(HamiltonStepError):
  class NotAspiratedError (line 305) | class NotAspiratedError(HamiltonStepError):
  class ImproperDispensationError (line 313) | class ImproperDispensationError(HamiltonStepError):
  class NoLabwareError (line 321) | class NoLabwareError(HamiltonStepError):
  class UnexpectedLabwareError (line 331) | class UnexpectedLabwareError(HamiltonStepError):
  class WrongLabwareError (line 337) | class WrongLabwareError(HamiltonStepError):
  class BarcodeMaskError (line 343) | class BarcodeMaskError(HamiltonStepError):
  class BarcodeNotUniqueError (line 349) | class BarcodeNotUniqueError(HamiltonStepError):
  class BarcodeAlreadyUsedError (line 355) | class BarcodeAlreadyUsedError(HamiltonStepError):
  class KitLotExpiredError (line 361) | class KitLotExpiredError(HamiltonStepError):
  class DelimiterError (line 367) | class DelimiterError(HamiltonStepError):

FILE: pyhamilton/pipetting/pipetting.py
  function prewet_tips (line 11) | def prewet_tips(ham_int, tip_type, dispense_mode):
  function condense_volumes (line 14) | def condense_volumes(lst, vol, max_volume):
  function get_fitting_dispense_positions (line 19) | def get_fitting_dispense_positions(asp_vols, disp_vols, disp_pos):
  function build_dispense_batches (line 34) | def build_dispense_batches(aspiration_volumes, all_dispense_positions, a...
  function distribute_positions_to_channel_ops (line 70) | def distribute_positions_to_channel_ops(positions_to_distribute, referen...
  function print_transfers (line 111) | def print_transfers(source_wells, destination_wells, vols):
  function batch_columnwise_positions (line 125) | def batch_columnwise_positions(positions):
  function split_volumes_by_max_volume (line 131) | def split_volumes_by_max_volume(volumes, max_volume):
  function set_parallel_nones (line 148) | def set_parallel_nones(positions, reference):
  function pip_transfer (line 159) | def pip_transfer(ham_int: HamiltonInterface, tips: List[Tuple[DeckResour...
  function pip_pool (line 233) | def pip_pool(ham_int: HamiltonInterface, tips: List[Tuple[DeckResource, ...
  function shear_plate_96 (line 306) | def shear_plate_96(ham_int: HamiltonInterface, tips:List[Tuple[DeckResou...
  function mix_plate (line 325) | def mix_plate(ham_int: HamiltonInterface, tips:List[Tuple[DeckResource, ...
  function multi_dispense (line 346) | def multi_dispense(ham_int: HamiltonInterface, tips:List[Tuple[DeckResou...
  function multi_aspirate (line 415) | def multi_aspirate(ham_int: HamiltonInterface, tips:List[Tuple[DeckResou...
  function mph_tip_pickup_support (line 463) | def mph_tip_pickup_support(ham_int: HamiltonInterface, tips: TrackedTips...
  function transfer_96 (line 474) | def transfer_96(ham_int: HamiltonInterface, tips:List[Tuple[DeckResource...
  function double_aspirate_supernatant_96 (line 501) | def double_aspirate_supernatant_96(ham_int: HamiltonInterface, tips: Tra...
  function ethanol_wash (line 535) | def ethanol_wash(ham_int: HamiltonInterface, tips: TrackedTips, tip_supp...
  function pip_mix (line 558) | def pip_mix(ham_int: HamiltonInterface, tips: TrackedTips, positions_to_...
  function aspirate_all_for_stamp (line 594) | def aspirate_all_for_stamp(ham_int, tips, source_plate, volume, liquid_c...
  function aspirate_all (line 606) | def aspirate_all(ham_int, tips, source_plate, volume, liquid_class,

FILE: pyhamilton/pipetting/trough_manager.py
  function check_volumes_in_troughs (line 4) | def check_volumes_in_troughs(ham_int: HamiltonInterface, aspiration_posi...
  function select_trough (line 19) | def select_trough(ham_int: HamiltonInterface, aspiration_positions, volu...
  function prompt_insufficient_volume (line 26) | def prompt_insufficient_volume(ham_int, troughs, volume):
  function accumulate_residual_volume (line 29) | def accumulate_residual_volume(ham_int, troughs, volumes):
  function manage_multiple_troughs (line 38) | def manage_multiple_troughs(ham_int, aspiration_positions, volume, liqui...

FILE: pyhamilton/resources/deckresource.py
  class ResourceType (line 13) | class ResourceType:
    method __init__ (line 48) | def __init__(self, resource_class, *args):
  class LayoutManager (line 60) | class LayoutManager:
    method get_manager (line 77) | def get_manager(checksum):
    method initial_printable (line 88) | def initial_printable(line, start=0):
    method layline_objid (line 97) | def layline_objid(line):
    method layline_first_field (line 111) | def layline_first_field(line):
    method field_starts_with (line 115) | def field_starts_with(field, prefix):
    method name_from_line (line 122) | def name_from_line(line):
    method line_has_prefixed_name (line 129) | def line_has_prefixed_name(prefix):
    method _read_layfile_lines (line 135) | def _read_layfile_lines(layfile_path):
    method _layfile_checksum (line 153) | def _layfile_checksum(layfile_path):
    method layfiles_equal (line 158) | def layfiles_equal(lay_path_1, lay_path_2):
    method __init__ (line 161) | def __init__(self, layfile_path, install=True):
    method assign_unused_resource (line 171) | def assign_unused_resource(self, restype, order_key=None, reverse=Fals...
    method verify_position_ids (line 218) | def verify_position_ids(self, resource):
    method _find_resource_line (line 279) | def _find_resource_line(self, resource_name):
    method _extract_position_ids_from_line (line 315) | def _extract_position_ids_from_line(line, obj_id):
  class ResourceIterItem (line 340) | class ResourceIterItem:
    method __init__ (line 342) | def __init__(self, resource, index):
  class Tip (line 348) | class Tip(ResourceIterItem):
  class Vessel (line 352) | class Vessel(ResourceIterItem):
    method record_removal (line 357) | def record_removal(self, ml, dest=None):
    method record_addition (line 362) | def record_addition(self, ml, source):
    method current_volume (line 367) | def current_volume(self):
  class DeckResource (line 371) | class DeckResource:
    class align (line 373) | class align:
    class types (line 377) | class types:
    method __init__ (line 380) | def __init__(self, layout_name):
    method _alignment_delta (line 383) | def _alignment_delta(self, int_start, int_end):
    method _assert_idx_in_range (line 386) | def _assert_idx_in_range(self, idx_or_vessel):
    method layout_name (line 394) | def layout_name(self):
    method position_id (line 402) | def position_id(self, idx):
    method alignment_delta (line 423) | def alignment_delta(self, start, end):
    method __iter__ (line 439) | def __iter__(self):
    method assign_label (line 443) | def assign_label(self, label: str):
  class Standard96 (line 450) | class Standard96(DeckResource):
    method well_coords (line 454) | def well_coords(self, idx):
    method _alignment_delta (line 458) | def _alignment_delta(self, start, end):
    method position_id (line 465) | def position_id(self, idx):
  class Tip96 (line 470) | class Tip96(Standard96):
    method __init__ (line 472) | def __init__(self, layout_name):
    method position_id (line 478) | def position_id(self, idx): # tips use 1-indexed int ids descending co...
  function resource_list_with_prefix (line 484) | def resource_list_with_prefix(layout_manager:LayoutManager, prefix:str, ...
  class BulkReagentPlate (line 495) | class BulkReagentPlate(Standard96):
    method __init__ (line 497) | def __init__(self, layout_name):
    method position_id (line 503) | def position_id(self, idx):
  class Waste96 (line 508) | class Waste96(BulkReagentPlate):
    method __init__ (line 510) | def __init__(self, layout_name):
  class Plate96 (line 513) | class Plate96(Standard96):
    method __init__ (line 515) | def __init__(self, layout_name):
  class Plate24 (line 522) | class Plate24(DeckResource):
    method __init__ (line 524) | def __init__(self, layout_name):
    method well_coords (line 530) | def well_coords(self, idx):
    method _alignment_delta (line 534) | def _alignment_delta(self, start, end):
    method position_id (line 540) | def position_id(self, idx):
  class Plate12 (line 545) | class Plate12(DeckResource):
    method __init__ (line 547) | def __init__(self, layout_name):
    method well_coords (line 553) | def well_coords(self, idx):
    method _alignment_delta (line 557) | def _alignment_delta(self, start, end):
    method position_id (line 563) | def position_id(self, idx):
  class Plate6 (line 567) | class Plate6(DeckResource):
    method __init__ (line 569) | def __init__(self, layout_name):
    method well_coords (line 575) | def well_coords(self, idx):
    method _alignment_delta (line 579) | def _alignment_delta(self, start, end):
    method position_id (line 585) | def position_id(self, idx):
  class Plate384 (line 590) | class Plate384(DeckResource):
    method __init__ (line 592) | def __init__(self, layout_name):
    method well_coords (line 598) | def well_coords(self, idx):
    method _alignment_delta (line 602) | def _alignment_delta(self, start, end):
    method position_id (line 608) | def position_id(self, idx):
  class Plate1536 (line 612) | class Plate1536(DeckResource):
    method __init__ (line 614) | def __init__(self, layout_name):
    method well_coords (line 620) | def well_coords(self, idx):
    method _alignment_delta (line 624) | def _alignment_delta(self, start, end):
    method position_id (line 630) | def position_id(self, idx):
  class Reservoir60mL (line 635) | class Reservoir60mL(DeckResource):
    method __init__ (line 637) | def __init__(self, layout_name):
    method well_coords (line 643) | def well_coords(self, idx):
    method _alignment_delta (line 647) | def _alignment_delta(self, start, end):
    method position_id (line 653) | def position_id(self, idx):
  class LVKBalanceVial (line 656) | class LVKBalanceVial(DeckResource):
    method __init__ (line 658) | def __init__(self, layout_name):
    method well_coords (line 664) | def well_coords(self, idx):
    method _alignment_delta (line 668) | def _alignment_delta(self, start, end):
    method position_id (line 674) | def position_id(self, idx):
  class EppiCarrier32 (line 677) | class EppiCarrier32(DeckResource):
    method __init__ (line 679) | def __init__(self, layout_name):
    method well_coords (line 686) | def well_coords(self, idx):
    method _alignment_delta (line 690) | def _alignment_delta(self, start, end):
    method position_id (line 696) | def position_id(self, idx):
  class FalconCarrier24 (line 700) | class FalconCarrier24(DeckResource):
    method __init__ (line 702) | def __init__(self, layout_name):
    method well_coords (line 709) | def well_coords(self, idx):
    method _alignment_delta (line 713) | def _alignment_delta(self, start, end):
    method position_id (line 719) | def position_id(self, idx):
  class Lid (line 722) | class Lid(DeckResource):
    method __init__ (line 724) | def __init__(self, layout_name):
  function layout_item (line 727) | def layout_item(lmgr, item_class, item_name):

FILE: pyhamilton/resources/enums.py
  class TipType (line 5) | class TipType(IntEnum):
    method volume (line 39) | def volume(self) -> int:
    method from_volume (line 74) | def from_volume(cls, volume: int) -> "TipType":
    method has_filter (line 88) | def has_filter(self) -> bool:
    method is_needle (line 101) | def is_needle(self) -> bool:
    method is_384_compatible (line 114) | def is_384_compatible(self) -> bool:

FILE: pyhamilton/resources/managed_resources.py
  function _get_conn (line 31) | def _get_conn():
  function _ensure_table (line 41) | def _ensure_table() -> None:
  class TrackedTips (line 56) | class TrackedTips:
    method __init__ (line 65) | def __init__(self,
    method from_prefix (line 98) | def from_prefix(cls,
    method mark_occupied (line 117) | def mark_occupied(self, index: int) -> None:
    method mark_unoccupied (line 122) | def mark_unoccupied(self, index: int) -> None:
    method is_occupied (line 127) | def is_occupied(self, index: int) -> bool:
    method count_remaining (line 130) | def count_remaining(self) -> int:
    method total_tips (line 133) | def total_tips(self) -> int:
    method fetch_next (line 136) | def fetch_next(self, n: int) -> List[Tuple[DeckResource, int]]:
    method fetch_rack (line 166) | def fetch_rack(self) -> Optional[DeckResource]:
    method fetch_rack_with_min_columns (line 182) | def fetch_rack_with_min_columns(self, min_columns: int) -> Optional[Tu...
    method reset_all (line 228) | def reset_all(self) -> None:
    method replace_tips (line 248) | def replace_tips(self, positions: List[Tuple[DeckResource, int]]) -> N...
    method fill_rack_from_occupancy_map (line 286) | def fill_rack_from_occupancy_map(self, rack: DeckResource, occupancy_m...
    method _hydrate_from_db (line 352) | def _hydrate_from_db(self) -> bool:
    method _update_row (line 375) | def _update_row(self, position_idx: int, occupied: bool) -> None:
    method _flush_entire_state (line 386) | def _flush_entire_state(self) -> None:
  function _get_stacked_conn (line 401) | def _get_stacked_conn():
  function _ensure_stacked_table (line 408) | def _ensure_stacked_table() -> None:
  class StackedResources (line 426) | class StackedResources:
    method __init__ (line 432) | def __init__(self,
    method from_prefix (line 462) | def from_prefix(cls,
    method get_stacked (line 477) | def get_stacked(self) -> List[str]:
    method count (line 481) | def count(self) -> int:
    method fetch_next (line 485) | def fetch_next(self) -> str:
    method put_back (line 499) | def put_back(self) -> str:
    method reset_all (line 521) | def reset_all(self) -> None:
    method _hydrate_from_db (line 548) | def _hydrate_from_db(self) -> None:
    method _update_row (line 565) | def _update_row(self, rname: str, *, available: bool) -> None:
    method _flush_entire_state (line 574) | def _flush_entire_state(self, conn) -> None:
  class TipSupportTracker (line 582) | class TipSupportTracker:
    method __init__ (line 587) | def __init__(self, resource):
    method _update_rack_in_tracker (line 594) | def _update_rack_in_tracker(self, rack, tip_occupancies, tip_tracker, ...
    method remove_rack (line 601) | def remove_rack(self, rack=None):
    method has_available_tips (line 607) | def has_available_tips(self, num_tips: int) -> bool:
    method _rightmost_indices_for_n_columns (line 610) | def _rightmost_indices_for_n_columns(self, n: int):
    method fetch_n_columns (line 620) | def fetch_n_columns(self, ham_int: HamiltonInterface, n: int, tip_trac...
    method tip_support_add_rack (line 650) | def tip_support_add_rack(self, ham_int: HamiltonInterface, tracked_tip...

FILE: pyhamilton/templates/ai_template/preprompt.py
  function complete (line 80) | def complete(prompt):

FILE: pyhamilton/templates/ai_template/robot_method.py
  function assist (line 18) | def assist(prompt, safe = False):

FILE: pyhamilton/templates/ai_template/voice.py
  function voice_to_text (line 11) | def voice_to_text():

FILE: pyhamilton/transport/transport.py
  class GrippedResource (line 13) | class GrippedResource(str, Enum):
    method __str__ (line 21) | def __str__(self) -> str:  # pragma: no‑cover – makes reprs nicer when...
    method parse (line 25) | def parse(cls, s: Union[str, "GrippedResource"]) -> "GrippedResource":
  class GripDirection (line 36) | class GripDirection(IntEnum):
  class GripperParams (line 47) | class GripperParams:
    method _complex_dict (line 66) | def _complex_dict(self, orientation: int) -> Dict[str, float]:
    method as_iswap_call (line 76) | def as_iswap_call(self) -> Tuple[int, Dict[str, float], Dict[str, floa...
  function get_core_gripper_params (line 149) | def get_core_gripper_params(
  function get_gripper_params (line 172) | def get_gripper_params(
  function transport_resource (line 205) | def transport_resource(

FILE: tests/interface_tests.py
  class Test_HamiltonInterface (line 217) | class Test_HamiltonInterface:
    method test_parse_response_status_when_ (line 274) | def test_parse_response_status_when_(
    method test_parse_response_return_data_when_ (line 346) | def test_parse_response_return_data_when_(
    method test_parse_response_parsed_result_when_ (line 390) | def test_parse_response_parsed_result_when_(
    method test_parse_response_raise_exception_when_ (line 440) | def test_parse_response_raise_exception_when_(
Copy disabled (too large) Download .json
Condensed preview — 387 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (16,949K chars).
[
  {
    "path": ".gitattributes",
    "chars": 307,
    "preview": "* text eol=lf\n*.lay binary\n*.dll* binary\n*.pdb binary\n*.hsl binary\n*.hsi binary\n*.hs_ binary\n*.sub binary\n*.smt binary\n*"
  },
  {
    "path": ".gitignore",
    "chars": 140,
    "preview": "*.pyc\n*__pycache__\n*.DS_store\n*.swp\npyhamilton/LAY-BACKUP\ndist\n*.egg-info\n.ipynb_checkpoints\nbuild/\ndist/\n*.egg-info/\n**"
  },
  {
    "path": ".pylintrc",
    "chars": 14587,
    "preview": "# This Pylint rcfile contains a best-effort configuration to uphold the\n# best-practices and style described in the Goog"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 460,
    "preview": "{\n  \"editor.tabSize\": 2,\n  \"files.trimTrailingWhitespace\": true,\n  \"cSpell.words\": [\"iswap\", \"pyhamilton\", \"subresource\""
  },
  {
    "path": "LICENSE",
    "chars": 1065,
    "preview": "MIT License\n\nCopyright (c) 2018 dgretton\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
  },
  {
    "path": "PKG-INFO",
    "chars": 291,
    "preview": "Metadata-Version: 2.1\nName: pyhamilton\nVersion: 1.48\nSummary: Python for Hamilton liquid handling robots\nHome-page: http"
  },
  {
    "path": "README.md",
    "chars": 4987,
    "preview": "# PyHamilton\n\n**Python for Hamilton liquid handling robots**\n\nHamilton software only works on Windows, so the same goes "
  },
  {
    "path": "imgs/README.md",
    "chars": 2431,
    "preview": "# PyHamilton Reference Guide\nAuthor: Stefan Golas _(Contact stefanmgolas@gmail.com)_\n\n## Contents\n- Intro\n- Installation"
  },
  {
    "path": "imgs/text",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "pandoc_pdf.sh",
    "chars": 256,
    "preview": " pandoc --from=markdown+abbreviations+tex_math_single_backslash  \\\n           --pdf-engine=xelatex --variable=mainfont:\""
  },
  {
    "path": "pyhamilton/__init__.py",
    "chars": 4201,
    "preview": "\"\"\"\nPyhamilton\n\"\"\"\nimport os\nimport shutil\nfrom os.path import dirname, join, abspath\nPACKAGE_PATH = abspath(dirname(__f"
  },
  {
    "path": "pyhamilton/consumables/__init__.py",
    "chars": 343,
    "preview": "from .consumables import (ReagentTrackedFalconCarrier24, ReagentTrackedPlate96, ReagentTrackedReservoir60mL, \n          "
  },
  {
    "path": "pyhamilton/consumables/consumables.py",
    "chars": 12933,
    "preview": "from ..interface import HamiltonInterface\nfrom ..resources import DeckResource, ResourceType,layout_item, LayoutManager\n"
  },
  {
    "path": "pyhamilton/defaultcmds.py",
    "chars": 53718,
    "preview": "\"\"\"\nBuilt-in commands, definitions of their parameters, and defaults.\n\"\"\"\n\n_channel_patt_16 = '1'*8 + '0'*8\n_channel_pat"
  },
  {
    "path": "pyhamilton/defaults/defaults.json",
    "chars": 178,
    "preview": "{\n  \"robot_type\": \"STAR\",\n  \"core_gripper_sequence\": \"COREGripTool_OnWaste_1000ul_0001\",\n   \"liquids_database\": \"C:\\\\Pro"
  },
  {
    "path": "pyhamilton/defaults.py",
    "chars": 2368,
    "preview": "from __future__ import annotations\nimport json\nfrom dataclasses import dataclass, asdict, replace\nfrom pathlib import Pa"
  },
  {
    "path": "pyhamilton/devices/__init__.py",
    "chars": 202,
    "preview": "from .centrifuge_wrappers import *\nfrom .hhs_wrappers import *\nfrom .hig_wrappers import *\nfrom .mpe_wrappers import *\nf"
  },
  {
    "path": "pyhamilton/devices/centrifuge_wrappers.py",
    "chars": 2203,
    "preview": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Wed Oct  5 07:52:56 2022\n\n@author: stefa\n\"\"\"\n\nimport sys, os, time, logging, impo"
  },
  {
    "path": "pyhamilton/devices/hhs_wrappers.py",
    "chars": 9085,
    "preview": "import sys, os, time, logging, importlib\nfrom threading import Thread\n\nfrom ..interface import HamiltonInterface\n\nfrom ."
  },
  {
    "path": "pyhamilton/devices/hig_wrappers.py",
    "chars": 2621,
    "preview": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Fri Feb 10 00:01:53 2023\n\n@author: stefa\n\"\"\"\n\nimport sys, os, time, logging, impo"
  },
  {
    "path": "pyhamilton/devices/mpe_wrappers.py",
    "chars": 8964,
    "preview": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Sun Feb 12 18:56:58 2023\n\n@author: stefa\n\"\"\"\n\nimport sys, os, time, logging, impo"
  },
  {
    "path": "pyhamilton/devices/odtc_wrappers.py",
    "chars": 7298,
    "preview": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Mon Jan 23 23:25:49 2023\n\n@author: stefa\n\"\"\"\n\nimport sys, os, time, logging, impo"
  },
  {
    "path": "pyhamilton/devices/pH_wrappers.py",
    "chars": 14576,
    "preview": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Sun Oct  2 15:40:58 2022\n\n@author: stefa\n\"\"\"\nimport sys, os, time, logging, impor"
  },
  {
    "path": "pyhamilton/devices/tec_wrappers.py",
    "chars": 2487,
    "preview": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Tue May 30 21:44:03 2023\n\n@author: stefa\n\"\"\"\nfrom ..interface import TEC_INIT, TE"
  },
  {
    "path": "pyhamilton/error_code_descriptions.txt",
    "chars": 4484,
    "preview": "Main Error Enumeration\nID\n Error\n Description\n \n 0\n No error\n -\n \n 1\n Syntax Error\n There is a wrong set of parameters o"
  },
  {
    "path": "pyhamilton/interface.py",
    "chars": 62205,
    "preview": "import sys\nimport time, json, signal, os, requests, string, logging, subprocess\nfrom dataclasses import dataclass, field"
  },
  {
    "path": "pyhamilton/library/ASWStandard/ASWGlobal/ASWGlobal.hsl",
    "chars": 1003,
    "preview": "// This library contains the most needed constants of HSL used in SMT and MED.\r\n// Copyright (C) by HAMILTON Bonaduz AG,"
  },
  {
    "path": "pyhamilton/library/ASWStandard/TraceLevel/TraceLevel.hsl",
    "chars": 52497,
    "preview": "///////////////////////////////////////////////////////////////////////////////////////////////////////\r\n// Copyright (C"
  },
  {
    "path": "pyhamilton/library/Alpha Numeric Conversion/Alpha Numeric Conversion.hs_",
    "chars": 934,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 8/15/2017 8:46:29 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/Alpha Numeric Conversion/Alpha Numeric Conversion.hsi",
    "chars": 20255,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"HSLUtilLib.hsl\"\r\n#incl"
  },
  {
    "path": "pyhamilton/library/ErrorSimulator/ErrorSimulator.hs_",
    "chars": 1628,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 7/16/2012 3:01:49 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/ErrorSimulator/ErrorSimulator.hsi",
    "chars": 84961,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"ErrorSimulator\\\\MlStarSimCfg.hsl\"\r\n#include \"HSL"
  },
  {
    "path": "pyhamilton/library/ErrorSimulator/HSLFilLibEx.hsl",
    "chars": 2967,
    "preview": "// This is an extension of the HSL File Library Extension.\r\n// Copyright (C) by HAMILTON Bonaduz AG, CH-7402 Bonaduz.\r\n/"
  },
  {
    "path": "pyhamilton/library/ErrorSimulator/MlStarSimCfg.hsl",
    "chars": 2160,
    "preview": "#pragma once\r\n\r\n\r\nnamespace MlStarSimCfg\r\n{\r\n\tstatic const variable dataDefName       (\"Simulator\");\r\n\tstatic const vari"
  },
  {
    "path": "pyhamilton/library/ErrorSimulator/StrTokenize.hs_",
    "chars": 530,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 03.10.2008 08:27:45\r\n\r\n#pragma o"
  },
  {
    "path": "pyhamilton/library/ErrorSimulator/StrTokenize.hsi",
    "chars": 3340,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"HSLTrcLib.hsl\"\r\n/* {{ "
  },
  {
    "path": "pyhamilton/library/HSLAppsLib/HSLAppsLib.hsl",
    "chars": 30063,
    "preview": "//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\\r\n//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*"
  },
  {
    "path": "pyhamilton/library/HSLAppsLib.hsl",
    "chars": 37179,
    "preview": "//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\\r\n//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*"
  },
  {
    "path": "pyhamilton/library/HSLBarcodeReader.hs_",
    "chars": 21847,
    "preview": "// ======================================================================\r\n// This is a part of the HSL BarcodeReader Li"
  },
  {
    "path": "pyhamilton/library/HSLBarcodeReader.hsl",
    "chars": 1670,
    "preview": "// ======================================================================\r\n// This is a part of the HSL File Library.\r\n/"
  },
  {
    "path": "pyhamilton/library/HSLBarcodeReaderStringTableEnu.hs_",
    "chars": 3061,
    "preview": "// ======================================================================\r\n// This is the language dependant part of the"
  },
  {
    "path": "pyhamilton/library/HSLDaisyChainCommunication.hsl",
    "chars": 24630,
    "preview": "// ======================================================================\r\n// Interface of the Daisy Chained communicati"
  },
  {
    "path": "pyhamilton/library/HSLDaisyChainedMediaLine.hsl",
    "chars": 69054,
    "preview": "// ======================================================================\r\n// Interface of the Daisy Chained Media Line "
  },
  {
    "path": "pyhamilton/library/HSLDaisyChainedTiltModule.hsl",
    "chars": 37778,
    "preview": "// ===========================================================================\r\n// Interface of the daisy chained Tilt-M"
  },
  {
    "path": "pyhamilton/library/HSLInhecoTEC/HSLInhecoTECLib.hsl",
    "chars": 99190,
    "preview": "// ===========================================================================\r\n// Library for use with Inheco Single an"
  },
  {
    "path": "pyhamilton/library/HSLLabwareStateLib.hsl",
    "chars": 18455,
    "preview": "// ======================================================================\r\n// This is a part of the HSL Labware State Li"
  },
  {
    "path": "pyhamilton/library/HSLLabwareStateLibImpl.hs_",
    "chars": 77325,
    "preview": "// ======================================================================\r\n// This is a part of the HSL Labware State Li"
  },
  {
    "path": "pyhamilton/library/HSLLabwareStateLibImplEnu.hs_",
    "chars": 6471,
    "preview": "// ======================================================================\r\n// This is a part of the HSL Labware State Li"
  },
  {
    "path": "pyhamilton/library/HSLLabwrAccess/HSLLabwrAccess.hsl",
    "chars": 118579,
    "preview": "//--------------------------------------------------------------------------------------------------------\r\n// Copyright"
  },
  {
    "path": "pyhamilton/library/HSLTrcLib.hsl",
    "chars": 7719,
    "preview": "// This is a part of the HSL Trace Library.\r\n// Copyright (C) by HAMILTON Bonaduz AG, CH-7402 Bonaduz.\r\n// All rights re"
  },
  {
    "path": "pyhamilton/library/HSL_LiquidClassLib.hsl",
    "chars": 35175,
    "preview": "// ====================================================================.\r\n// Copyright (C) by HAMILTON Reno, USA.\r\n// Al"
  },
  {
    "path": "pyhamilton/library/HslHamHeaterShakerStringTableEnu.hs_",
    "chars": 8181,
    "preview": "// ======================================================================\r\n// This is the language dependant part of the"
  },
  {
    "path": "pyhamilton/library/Labware Properties/Labware_Property_Query.hs_",
    "chars": 8016,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 7/23/2021 11:08:24 PM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/library/Labware Properties/Labware_Property_Query.hsi",
    "chars": 298423,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma global\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"HSLMthLib.hsl\"\r\n#inc"
  },
  {
    "path": "pyhamilton/library/Labware Properties/Resources/LPQ_GLOBAL.hsl",
    "chars": 804,
    "preview": "#pragma global\r\n\r\n#ifndef __LPQ_GLOBAL_hsl__\r\n#define __LPQ_GLOBAL__ 1\r\n\r\nnamespace LPQ\r\n{\r\n   function InitializeVariab"
  },
  {
    "path": "pyhamilton/library/SMT/MLSTARLiquidClassLib.hsl",
    "chars": 23147,
    "preview": "// ======================================================================\r\n// This is a part of the HSL File Library.\r\n/"
  },
  {
    "path": "pyhamilton/library/SMTs/lookup.hs_",
    "chars": 473,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 1/16/2017 10:36:57 PM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/library/SMTs/lookup.hsi",
    "chars": 1893,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLUtilLib.hsl\"\r\n/* {{ 2 \"LibraryInsertLine\" \"\" "
  },
  {
    "path": "pyhamilton/library/STAR Tools/Channel Tools Test.res",
    "chars": 370,
    "preview": "#pragma once\r\nglobal resource Res_ML_STAR(1, 0xff0000, Translate(\"ML_STAR\"));\r\n\r\n\r\nfunction Res_ML_STAR_map(variable uni"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/CHANNEL_TOOLS_GLOBAL.hsl",
    "chars": 1120,
    "preview": "#pragma global\r\n\r\n#ifndef __CHANNEL_TOOLS_GLOBAL_hsl__\r\n#define __CHANNEL_TOOLS_GLOBAL__ 1\r\n\r\n\r\nnamespace SPLIT_WELLS_GL"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/FW_HelperLibrary.hs_",
    "chars": 3505,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 10/23/2020 1:02:26 PM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/FW_HelperLibrary.hsi",
    "chars": 132638,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLMthLib.hsl\"\r\n#include \"HSLStrLib.hsl\"\r\n#inclu"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Firmware Pipetting Commands.hs_",
    "chars": 6430,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 7/29/2021 11:19:22 AM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Firmware Pipetting Commands.hsi",
    "chars": 282839,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"STAR Tools\\\\Resources\\"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/HelperLibrary.hs_",
    "chars": 6666,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 4/20/2022 4:45:49 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/HelperLibrary.hsi",
    "chars": 172178,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"Alpha Numeric Conversi"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Liquid Level Check.hs_",
    "chars": 1210,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 9/23/2019 10:30:09 AM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Liquid Level Check.hsi",
    "chars": 66617,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"HSLExtensions\\\\File.hs"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Plate Stack Verify.hs_",
    "chars": 881,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 12/17/2020 3:41:53 PM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Plate Stack Verify.hsi",
    "chars": 104683,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLDevLib.hsl\"\r\n#include \"HSLSeqLib.hsl\"\r\n#inclu"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/STAR Channel Movement Tools.hs_",
    "chars": 3259,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 10/6/2021 2:58:30 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/STAR Channel Movement Tools.hsi",
    "chars": 152915,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLMthLib.hsl\"\r\n#include \"HSLStrLib.hsl\"\r\n#inclu"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Split_Wells.hs_",
    "chars": 1388,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 9/26/2019 9:05:44 AM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Split_Wells.hsi",
    "chars": 50530,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"STAR Tools\\\\Resources\\\\SubMethods\\\\CHANNEL_TOOLS"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/TIP_OFFSET_GLOBAL.hsl",
    "chars": 1003,
    "preview": "#pragma global\r\n\r\n#ifndef __TIP_OFFSET_GLOBAL_hsl__\r\n#define __TIP_OFFSET_GLOBAL_hsl__ 1\r\n\r\nnamespace TIP_OFFSET_GLOBAL\r"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Tip Tool Global Variables.hsl",
    "chars": 481,
    "preview": "#pragma global\r\n\r\n#ifndef __Tip_Tool_Global_Variables_hsl__\r\n#define __Tip_Tool_Global_Variables_hsl__ 1\r\n\r\nnamespace TT"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/TipCounterGlobal.hsl",
    "chars": 7342,
    "preview": "#pragma global\r\n\r\n#ifndef __TipCounterGlobal_hsl__\r\n#define __TipCounterGlobal_hsl__ 1\r\n\r\nstatic variable initializedLib"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Travel Lanes.hs_",
    "chars": 1148,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 3/26/2020 2:50:35 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/Travel Lanes.hsi",
    "chars": 50844,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLDevLib.hsl\"\r\n#include \"STAR Tools\\\\Resources\\"
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/VANTAGE Channel Movement Tools.hs_",
    "chars": 3077,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 10/6/2021 3:00:03 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/STAR Tools/Resources/SubMethods/VANTAGE Channel Movement Tools.hsi",
    "chars": 177374,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLDevLib.hsl\"\r\n#include \"HSLMthLib.hsl\"\r\n#inclu"
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR Channel Tools.hs_",
    "chars": 6881,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 12/13/2021 11:05:43 AM\r\n\r\n#pragm"
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR Channel Tools.hsi",
    "chars": 442850,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"STAR Tools\\\\Resources\\\\SubMethods\\\\Plate Stack V"
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR MPH96 Tools.hs_",
    "chars": 2041,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 9/28/2021 1:31:16 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR MPH96 Tools.hsi",
    "chars": 290299,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"STAR Tools\\\\STAR Tip T"
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR Pipetting Tools.hs_",
    "chars": 4545,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 12/9/2021 11:07:24 AM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR Pipetting Tools.hsi",
    "chars": 477052,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"HSLSeqLib.hsl\"\r\n#inclu"
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR Tip Tools.hs_",
    "chars": 3435,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 4/21/2022 2:36:43 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR Tip Tools.hsi",
    "chars": 319904,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLSeqLib.hsl\"\r\n#include \"HSLStrLib.hsl\"\r\n#inclu"
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR Tools Demo.hsl",
    "chars": 30231,
    "preview": " namespace _Method { #include \"STAR Tools\\\\STAR Tip Tools.hs_\" } \r\n namespace _Method { #include \"STAR Tools\\\\STAR MPH96"
  },
  {
    "path": "pyhamilton/library/STAR Tools/STAR Tools Demo.sub",
    "chars": 359,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nfunction OnAbort(  ) void ;\r\n// }} \"\"\r\n// {{{ 5 \"OnAbort\" \"Begin\"\r\nfunction O"
  },
  {
    "path": "pyhamilton/library/STAR Tools/TipSupport.x",
    "chars": 553859,
    "preview": "xof 0303txt 0032\n\nFrame Root {\n  FrameTransformMatrix {\n     1.000000, 0.000000, 0.000000, 0.000000,\n     0.000000,-0.00"
  },
  {
    "path": "pyhamilton/library/STAR Tools/tipsupport.ctr",
    "chars": 687,
    "preview": "HxCfgFile,3;\n\nConfigIsValid,Y;\n\nDataDef,CONTAINER,3,default,\n{\n1.DX, \"0\",\n1.DY, \"0\",\n1.DZ, \"6.8\",\n1.EqnOfVol, \"h*36.3168"
  },
  {
    "path": "pyhamilton/library/SchedulingDev/HSLAppsLib.hsl",
    "chars": 37179,
    "preview": "//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\\r\n//*\\\\//*\\\\//*\\\\//*\\\\//*\\\\//*"
  },
  {
    "path": "pyhamilton/liquid_class_db.py",
    "chars": 15329,
    "preview": "from importlib import util\nfrom urllib.parse import quote_plus\nfrom sqlalchemy import create_engine, text, inspect\nfrom "
  },
  {
    "path": "pyhamilton/liquid_classes.py",
    "chars": 11711,
    "preview": "from enum import Enum, IntEnum\nimport json\nfrom typing import Dict, Any, Optional, Union\n\nfrom .interface import (COPY_L"
  },
  {
    "path": "pyhamilton/liquid_handling_wrappers.py",
    "chars": 15045,
    "preview": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Wed Mar  9 12:01:01 2022\n\n@author: stefa\n\"\"\"\n\nimport sys, os, time, logging, impo"
  },
  {
    "path": "pyhamilton/ngs/__init__.py",
    "chars": 158,
    "preview": "from .protocol import Protocol\nfrom .loading import LoadingVis\nfrom .tadm import generate_tadm_report, generate_tadm_rep"
  },
  {
    "path": "pyhamilton/ngs/loading/__init__.py",
    "chars": 35,
    "preview": "from .loading_vis import LoadingVis"
  },
  {
    "path": "pyhamilton/ngs/loading/deck_annotation.py",
    "chars": 13963,
    "preview": "import numpy as np\nimport json\nimport sys\nimport os\nimport tkinter as tk\nfrom tkinter import filedialog, colorchooser, t"
  },
  {
    "path": "pyhamilton/ngs/loading/deck_regions.json",
    "chars": 4297,
    "preview": "{\n    \"image_path\": \"deck.png\",\n    \"image_dimensions\": {\n        \"width\": 1098,\n        \"height\": 527\n    },\n    \"rgt_c"
  },
  {
    "path": "pyhamilton/ngs/loading/deck_regions_old.json",
    "chars": 3766,
    "preview": "{\n  \"image_path\": \".\\\\deck.png\",\n  \"image_dimensions\": {\n    \"width\": 1098,\n    \"height\": 527\n  },\n  \"regions\": {\n    \"r"
  },
  {
    "path": "pyhamilton/ngs/loading/loading_vis.py",
    "chars": 28110,
    "preview": "# loading_vis.py\nfrom __future__ import annotations\nimport os\nimport unicodedata\nimport json\nfrom dataclasses import dat"
  },
  {
    "path": "pyhamilton/ngs/loading/plate_96_render.py",
    "chars": 6872,
    "preview": "from __future__ import annotations\nimport os\nimport unicodedata\nimport json\nfrom dataclasses import dataclass\nfrom pathl"
  },
  {
    "path": "pyhamilton/ngs/loading/rendering_helpers.py",
    "chars": 18839,
    "preview": "from __future__ import annotations\nimport os\nimport unicodedata\nimport json\nfrom dataclasses import dataclass\nfrom pathl"
  },
  {
    "path": "pyhamilton/ngs/loading/tube_rack_render.py",
    "chars": 10020,
    "preview": "from __future__ import annotations\nimport os\nimport unicodedata\nimport json\nfrom dataclasses import dataclass\nfrom pathl"
  },
  {
    "path": "pyhamilton/ngs/protocol.py",
    "chars": 19037,
    "preview": "from ..consumables import generate_reagent_summary, generate_tip_use_summary\nfrom ..ngs.loading import LoadingVis\nfrom ."
  },
  {
    "path": "pyhamilton/ngs/tadm.py",
    "chars": 20154,
    "preview": "import re\nfrom datetime import datetime\nfrom dataclasses import dataclass\nfrom typing import List, Optional\nfrom collect"
  },
  {
    "path": "pyhamilton/ngs/tests/PacBio_MultiPlexLibraryPrepDeck_v1.2.res",
    "chars": 376,
    "preview": "#pragma once\r\nglobal resource Res_ML_STAR(1, 0xff0000, Translate(\"ML_STAR\"));\r\n\r\n\r\nfunction Res_ML_STAR_map(variable uni"
  },
  {
    "path": "pyhamilton/ngs/tests/bead_cleanup.py",
    "chars": 6188,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Plate96, Tip96, hhs_set_simulation, move_plate_using_gripper, "
  },
  {
    "path": "pyhamilton/ngs/tests/cLLD.py",
    "chars": 1883,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, ResourceType, Plate24, Plate96, Tip96, resource_list_with_pref"
  },
  {
    "path": "pyhamilton/ngs/tests/consumables_tracking.py",
    "chars": 1127,
    "preview": "from pyhamilton import DeckResource, layout_item, LayoutManager, HamiltonInterface, Plate96, Reservoir60mL, FalconCarrie"
  },
  {
    "path": "pyhamilton/ngs/tests/cpac.py",
    "chars": 618,
    "preview": "from pyhamilton import (HamiltonInterface, initialize_cpac, set_temperature_target_cpac, start_temperature_control_cpac,"
  },
  {
    "path": "pyhamilton/ngs/tests/double_aspirate.py",
    "chars": 1627,
    "preview": "from pyhamilton import HamiltonInterface, LayoutManager, Plate96, Tip96, TrackedTips, layout_item, Waste96\nfrom pyhamilt"
  },
  {
    "path": "pyhamilton/ngs/tests/get_liquid_class_volume.py",
    "chars": 2542,
    "preview": "from pyhamilton import (get_liquid_class_volume, get_liquid_class_columns, \n                        get_liquid_class_dis"
  },
  {
    "path": "pyhamilton/ngs/tests/gripper_move.py",
    "chars": 462,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, ResourceType, Plate24, Plate96, Tip96, \n                      "
  },
  {
    "path": "pyhamilton/ngs/tests/hhs.py",
    "chars": 2411,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Reservoir60mL, TrackedTips, StackedResources, Tip96, Plate96, "
  },
  {
    "path": "pyhamilton/ngs/tests/loading/deck_regions.json",
    "chars": 3632,
    "preview": "{\n  \"image_path\": \".\\\\deck.png\",\n  \"image_dimensions\": {\n    \"width\": 1152,\n    \"height\": 648\n  },\n  \"regions\": {\n    \"R"
  },
  {
    "path": "pyhamilton/ngs/tests/loading/loading_vis.py",
    "chars": 973,
    "preview": "# loading_vis.py\n\nimport json\nfrom dataclasses import dataclass\nfrom pathlib import Path\nfrom typing import Dict, List, "
  },
  {
    "path": "pyhamilton/ngs/tests/log/hamilton.log",
    "chars": 722370,
    "preview": "[2025-07-27 23:03:16,526] pyhamilton.interface INFO starting a Hamilton interface\n[2025-07-27 23:03:16,526] pyhamilton.i"
  },
  {
    "path": "pyhamilton/ngs/tests/log/main.log",
    "chars": 2030340,
    "preview": "[2025-07-27 23:03:16,524] root INFO ####################################################################################"
  },
  {
    "path": "pyhamilton/ngs/tests/log/robot_json.log",
    "chars": 831347,
    "preview": "{'command': 'channelTipPickUp', 'id': '0x17592e501c35', 'tipSequence': '', 'labwarePositions': 'TIP_50uLF_L_0001, 1', 'c"
  },
  {
    "path": "pyhamilton/ngs/tests/magnetic_bead_cleanup.py",
    "chars": 10135,
    "preview": "\nimport os\nfrom pyhamilton import (HamiltonInterface, hhs_create_usb_device, hhs_set_simulation, normal_logging, odtc_ex"
  },
  {
    "path": "pyhamilton/ngs/tests/mix_plate.py",
    "chars": 1198,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Reservoir60mL, TrackedTips, StackedResources, Tip96, Plate96, "
  },
  {
    "path": "pyhamilton/ngs/tests/multi_dispense.py",
    "chars": 5393,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Reservoir60mL, TrackedTips, StackedResources, Tip96, Plate96, "
  },
  {
    "path": "pyhamilton/ngs/tests/ngs_demo.py",
    "chars": 1064,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Reservoir60mL, TrackedTips, StackedResources, Tip96, Plate96, "
  },
  {
    "path": "pyhamilton/ngs/tests/pip_transfer.py",
    "chars": 1653,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Reservoir60mL, TrackedTips, StackedResources, Tip96, Plate96, "
  },
  {
    "path": "pyhamilton/ngs/tests/set_labware_property.py",
    "chars": 234,
    "preview": "from pyhamilton import HamiltonInterface, TipType\n\nwith HamiltonInterface(simulating=False, windowed=True) as ham_int:\n "
  },
  {
    "path": "pyhamilton/ngs/tests/stacking.py",
    "chars": 1027,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Tip96, TrackedTips, resource_list_with_prefix, \n              "
  },
  {
    "path": "pyhamilton/ngs/tests/thermal_cycler/thermal_cycler_with_transport.py",
    "chars": 4584,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Plate96, Tip96, hhs_set_simulation, move_plate_using_gripper, "
  },
  {
    "path": "pyhamilton/ngs/tests/tip_support.py",
    "chars": 888,
    "preview": "from pyhamilton import HamiltonInterface, LayoutManager, Tip96, layout_item, Plate96, TrackedTips, tip_support_pickup_co"
  },
  {
    "path": "pyhamilton/ngs/tests/tip_tracker.py",
    "chars": 847,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Tip96, TrackedTips, resource_list_with_prefix, \n              "
  },
  {
    "path": "pyhamilton/ngs/tests/transfer_96.py",
    "chars": 1254,
    "preview": "from pyhamilton import (HamiltonInterface, LayoutManager, Reservoir60mL, TrackedTips, StackedResources, Tip96, Plate96, "
  },
  {
    "path": "pyhamilton/ngs/tests/transport.py",
    "chars": 642,
    "preview": "from pyhamilton import HamiltonInterface, LayoutManager, Reservoir60mL, Tip96, layout_item, Plate96\nfrom pyhamilton_adva"
  },
  {
    "path": "pyhamilton/odtc/__init__.py",
    "chars": 48,
    "preview": "from .odtc_protocol import ThermalCyclerProtocol"
  },
  {
    "path": "pyhamilton/odtc/odtc_protocol.py",
    "chars": 6093,
    "preview": "import xml.etree.ElementTree as ET\nfrom datetime import datetime\n\nclass ThermalCyclerProtocol:\n    \"\"\"\n    A class to pr"
  },
  {
    "path": "pyhamilton/oemerr.py",
    "chars": 9717,
    "preview": "\"\"\"`pyhamilton`-specific exception definitions.\n\"\"\"\nclass HamiltonError(Exception):\n    \"\"\"\n    Exceptions raised in pac"
  },
  {
    "path": "pyhamilton/paths.py",
    "chars": 751,
    "preview": "import os\nfrom pyhamilton import PACKAGE_PATH\n\nLAY_BACKUP_DIR = os.path.join(PACKAGE_PATH, 'LAY-BACKUP')\nif not os.path."
  },
  {
    "path": "pyhamilton/pipetting/__init__.py",
    "chars": 390,
    "preview": "from .pipetting import (pip_transfer, shear_plate_96, mix_plate, multi_dispense, transfer_96, \n                        d"
  },
  {
    "path": "pyhamilton/pipetting/pipetting.py",
    "chars": 31161,
    "preview": "from .trough_manager import manage_multiple_troughs\nfrom ..consumables import tracked_volume_aspirate, tracked_volume_as"
  },
  {
    "path": "pyhamilton/pipetting/trough_manager.py",
    "chars": 2311,
    "preview": "from pyhamilton import HamiltonInterface\nfrom itertools import groupby\n\ndef check_volumes_in_troughs(ham_int: HamiltonIn"
  },
  {
    "path": "pyhamilton/resources/__init__.py",
    "chars": 81,
    "preview": "from .enums import *\nfrom .managed_resources import *\nfrom .deckresource import *"
  },
  {
    "path": "pyhamilton/resources/deckresource.py",
    "chars": 29678,
    "preview": "\"\"\"\nCouplings to Hamilton deck layouts.\n\nModule `pyhamilton.deckresource` provides convenience classes and methods for i"
  },
  {
    "path": "pyhamilton/resources/enums.py",
    "chars": 3725,
    "preview": "from enum import IntEnum\nfrom dataclasses import dataclass\n\n\nclass TipType(IntEnum):\n    \"\"\"Hamilton tip types mapped to"
  },
  {
    "path": "pyhamilton/resources/managed_resources.py",
    "chars": 27200,
    "preview": "# Fully self‑contained implementation of TrackedTips + StackedResources\n# with on‑disk persistence (SQLite in ~/.pyhamil"
  },
  {
    "path": "pyhamilton/run_venus_client.py",
    "chars": 493,
    "preview": "from pyhamilton.interface import run_hamilton_process\nfrom multiprocessing import Process\nimport time\nimport os\nimport s"
  },
  {
    "path": "pyhamilton/star-oem/HslHamHeaterShakerLib.hs_",
    "chars": 173880,
    "preview": "// ======================================================================\r\n// This is a part of the HSLHamHeaterShaker L"
  },
  {
    "path": "pyhamilton/star-oem/HslHamHeaterShakerLib.hsl",
    "chars": 7603,
    "preview": "// ======================================================================\r\n// This is a part of the HSLHamHeaterShaker L"
  },
  {
    "path": "pyhamilton/star-oem/RunHSLExecutor.dll.config",
    "chars": 859,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<configuration>\r\n  <startup useLegacyV2RuntimeActivationPolicy=\"true\">\r\n    <sup"
  },
  {
    "path": "pyhamilton/star-oem/STAR_OEM.hsl",
    "chars": 64729,
    "preview": "#include \"STAR_OEM.res\"\r\nglobal device HxFan (\"STAR_OEM.lay\", \"HxFan\", hslTrue);\r\nglobal device ML_STAR (\"STAR_OEM.lay\","
  },
  {
    "path": "pyhamilton/star-oem/STAR_OEM.res",
    "chars": 3816,
    "preview": "#pragma once\r\nglobal resource Res_Cyt6000_6wp(1, 0xff0000, Translate(\"Cyt6000_6wp\"));\r\nglobal resource Res_CytC24_96wp(1"
  },
  {
    "path": "pyhamilton/star-oem/STAR_OEM.sub",
    "chars": 12442,
    "preview": "function OnAbort(  ) void ;\r\nfunction SendTextMessageToServer( variable str ) void ;\r\nprivate function ArrayToString( va"
  },
  {
    "path": "pyhamilton/star-oem/STAR_OEM_toolkit.hs_",
    "chars": 6759,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 5/14/2023 1:36:46 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/star-oem/STAR_OEM_toolkit.hsi",
    "chars": 65266,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLErrLib.hsl\"\r\n#include \"HSLMlStarStepReturnLib"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/Hamilton pH Module Controller.hs_",
    "chars": 3433,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 2/27/2023 12:46:19 PM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/Hamilton pH Module Controller.hsi",
    "chars": 140817,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLStrLib.hsl\"\r\n#include \"HSLDevLib.hsl\"\r\n#inclu"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/HslHamHeaterShakerLib.hs_",
    "chars": 173880,
    "preview": "// ======================================================================\r\n// This is a part of the HSLHamHeaterShaker L"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/HslHamHeaterShakerLib.hsl",
    "chars": 7603,
    "preview": "// ======================================================================\r\n// This is a part of the HSLHamHeaterShaker L"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM.hsl",
    "chars": 71010,
    "preview": "namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\nnamespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\nnamespac"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_HiG.hs_",
    "chars": 1435,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 2/10/2023 12:53:51 AM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_HiG.hsi",
    "chars": 15905,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"Hamilton Centrifuge\\\\Hamilton Centrifuge.hsl\"\r\n#"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_ODTC.hs_",
    "chars": 3440,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 1/28/2023 1:23:56 AM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_ODTC.hsi",
    "chars": 24238,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"Hamilton pH Module\\\\Hamilton pH Station Dryer Mo"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_Test.hsl",
    "chars": 146797,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_Test.res",
    "chars": 848,
    "preview": "#pragma once\r\nglobal resource Res_mlstar(1, 0xff0000, Translate(\"mlstar\"));\r\nglobal resource Res_HxFan(1, 0xff0000, Tran"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_Test.sub",
    "chars": 24853,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_noFan.hsl",
    "chars": 375715,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_noFan.sub",
    "chars": 34067,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_toolkit.hs_",
    "chars": 7511,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 9/17/2025 6:52:03 PM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_toolkit.hsi",
    "chars": 76223,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLErrLib.hsl\"\r\n#include \"HSLMlStarStepReturnLib"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_toolkit_MPE.hs_",
    "chars": 6416,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 3/20/2023 9:12:05 AM\r\n\r\n#pragma "
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_toolkit_MPE.hsi",
    "chars": 61910,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"Hamilton MPE2\\\\HSLMPELib.hsl\"\r\n#include \"HSLErrL"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_toolkit_centrifuge.hs_",
    "chars": 1856,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 2/10/2023 12:16:54 AM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_toolkit_centrifuge.hsi",
    "chars": 12857,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"Hamilton Centrifuge\\\\Hamilton Centrifuge.hsl\"\r\n#"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_toolkit_pH.hs_",
    "chars": 5463,
    "preview": "\r\n// this ALWAYS GENERATED file contains the sub-method library header\r\n// Generated at 10/28/2022 8:30:08 PM\r\n\r\n#pragma"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_toolkit_pH.hsi",
    "chars": 45914,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"Hamilton pH Module\\\\Hamilton pH Module.hsl\"\r\n#in"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_wFan.hsl",
    "chars": 170495,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/STAR_OEM_wFan.sub",
    "chars": 24943,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/testWasher.hsl",
    "chars": 2152,
    "preview": "#include \"Demo Methods\\\\BMG Reader Test.res\"\r\nglobal device ML_STAR (\"Demo Methods\\\\BMG Reader Test.lay\", \"ML_STAR\", hsl"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/testWasher.sub",
    "chars": 361,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nfunction OnAbort(  ) void ;\r\n// }} \"\"\r\n// {{{ 5 \"OnAbort\" \"Begin\"\r\nfunction O"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx1C7B.hsl",
    "chars": 249197,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx1C7B.sub",
    "chars": 28756,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx1C7B.tmp",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx1DE8.hsl",
    "chars": 334614,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx1DE8.sub",
    "chars": 30979,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx1DE8.tmp",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx2A1D.hsl",
    "chars": 339745,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx2A1D.sub",
    "chars": 30977,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx2A1D.tmp",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx2D5B.hsl",
    "chars": 222964,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx2D5B.sub",
    "chars": 28756,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx2D5B.tmp",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx305.hsl",
    "chars": 334198,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx305.sub",
    "chars": 30212,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx305.tmp",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx393D.hsi",
    "chars": 74027,
    "preview": "// {{{ 2 \"IncludeGuard\" \"PragmaOnce\"\r\n#pragma once\r\n// }} \"\"\r\n#include \"HSLErrLib.hsl\"\r\n#include \"HSLMlStarStepReturnLib"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx393D.tmp",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx3D8A.hsl",
    "chars": 253259,
    "preview": " namespace _Method { #include \"HSLHttp\\\\HSLHttp.hsl\" } \r\n namespace _Method { #include \"HSLJson\\\\HSLJson.hsl\" } \r\n names"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx3D8A.sub",
    "chars": 28756,
    "preview": "// {{{ 2 \"SubmethodForwardDeclaration\" \"\"\r\nprivate function ArrayToString( variable & i_array[], variable & o_str ) void"
  },
  {
    "path": "pyhamilton/star-oem/VENUS_Method/~Hx3D8A.tmp",
    "chars": 0,
    "preview": ""
  }
]

// ... and 187 more files (download for full content)

About this extraction

This page contains the full source code of the dgretton/pyhamilton GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 387 files (15.2 MB), approximately 4.0M tokens, and a symbol index with 702 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!