Skip to content

Commit

Permalink
Merge branch 'edge' into tip_tracking_for_alternative_configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
CaseyBatten committed Feb 22, 2024
2 parents 7fe6dc6 + c58db57 commit 0d1c8c7
Show file tree
Hide file tree
Showing 322 changed files with 4,875 additions and 2,227 deletions.
2 changes: 1 addition & 1 deletion .github/actions/python/pypi-deploy/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ runs:
fi
fi
status=0
CI=1 QUIET=1 BUILD_NUMBER=${OT_BUILD} make -C ${{ inputs.project }} clean deploy twine_repository_url=${{ inputs.repository_url }} pypi_username=opentrons pypi_password=${{ inputs.password }} || status=$?
CI=1 QUIET=1 BUILD_NUMBER=${OT_BUILD} make -C ${{ inputs.project }} clean deploy twine_repository_url=${{ inputs.repository_url }} pypi_username=__token__ pypi_password=${{ inputs.password }} || status=$?
if [[ ${status} != 0 ]] && [[ ${{ inputs.repository_url }} =~ "test.pypi.org" ]]; then
echo "upload failures allowed to test pypi"
exit 0
Expand Down
10 changes: 6 additions & 4 deletions .github/actions/python/setup/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ runs:
if: ${{ inputs.python-version != 'false' }}
run: echo "OT_VIRTUALENV_VERSION=${{ inputs.python-version }}" >> $GITHUB_ENV
- shell: bash
run: |
npm install --global [email protected]
$OT_PYTHON -m pip install pipenv==2023.11.15
run: npm install --global [email protected]
- shell: bash
run: $OT_PYTHON -m pip install --upgrade pip
- shell: bash
run: $OT_PYTHON -m pip install --user pipenv==2023.12.1
- shell: bash
run: 'make -C ${{ inputs.project }} setup'
run: 'make -C ${{ inputs.project }} setup || make -C ${{ inputs.project }} setup'
4 changes: 2 additions & 2 deletions .github/workflows/api-test-lint-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,11 @@ jobs:
with:
project: 'api'
repository_url: 'https://test.pypi.org/legacy/'
password: '${{ secrets.OT_TEST_PYPI_PASSWORD }}'
password: '${{ secrets.TEST_PYPI_DEPLOY_TOKEN_OPENTRONS }}'
- if: startsWith(env.OT_TAG, 'v')
name: 'upload to real pypi'
uses: './.github/actions/python/pypi-deploy'
with:
project: 'api'
repository_url: 'https://upload.pypi.org/legacy/'
password: '${{ secrets.OT_PYPI_PASSWORD }}'
password: '${{ secrets.PYPI_DEPLOY_TOKEN_OPENTRONS }}'
Empty file.
4 changes: 2 additions & 2 deletions .github/workflows/shared-data-test-lint-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,14 @@ jobs:
with:
project: 'shared-data/python'
repository_url: 'https://test.pypi.org/legacy/'
password: '${{ secrets.OT_TEST_PYPI_PASSWORD }}'
password: '${{ secrets.TEST_PYPI_DEPLOY_TOKEN_OPENTRONS_SHARED_DATA }}'
- if: startsWith(env.OT_TAG, 'v')
name: 'upload to pypi'
uses: './.github/actions/python/pypi-deploy'
with:
project: 'shared-data/python'
repository_url: 'https://upload.pypi.org/legacy/'
password: '${{ secrets.OT_PYPI_PASSWORD }}'
password: '${{ secrets.PYPI_DEPLOY_TOKEN_OPENTRONS_SHARED_DATA }}'

publish-switch:
runs-on: 'ubuntu-latest'
Expand Down
8 changes: 4 additions & 4 deletions DEV_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ You will need the following tools installed to develop on the Opentrons platform
- curl
- ssh
- Python v3.10
- Node.js v16
- Node.js v18

### macOS

Expand Down Expand Up @@ -82,10 +82,10 @@ Close and re-open your terminal to confirm `nvs` is installed.
nvs --version
```

Now we can use nvs to install Node.js v16 and switch on `auto` mode, which will make sure Node.js v16 is used any time we're in the `opentrons` project directory.
Now we can use nvs to install Node.js v18 and switch on `auto` mode, which will make sure Node.js v18 is used any time we're in the `opentrons` project directory.

```shell
nvs add 16
nvs add 18
nvs auto on
```

Expand Down Expand Up @@ -202,7 +202,7 @@ Once you are inside the repository for the first time, you should do two things:
3. Run `python --version` to confirm your chosen version. If you get the incorrect version and you're using an Apple silicon Mac, try running `eval "$(pyenv init --path)"` and then `pyenv local 3.10.13`. Then check `python --version` again.

```shell
# confirm Node v16
# confirm Node v18
node --version

# set Python version, and confirm
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ setup: setup-js setup-py
# virtual envs using pipenv.
.PHONY: setup-py-toolchain
setup-py-toolchain:
$(OT_PYTHON) -m pip install pipenv==2023.11.15
$(OT_PYTHON) -m pip install --upgrade pip
$(OT_PYTHON) -m pip install pipenv==2023.12.1

# front-end dependecies handled by yarn
.PHONY: setup-js
Expand Down
8 changes: 4 additions & 4 deletions api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ sphinx_build_allow_warnings := $(pipenv) run sphinx-build

ot_project := $(OPENTRONS_PROJECT)
project_rs_default = $(if $(ot_project),$(ot_project),robot-stack)
project_ot3_default = $(if $(ot_project),$(ot_project),ot3)
project_ir_default = $(if $(ot_project),$(ot_project),ot3)

# Find the version of the wheel from git using a helper script. We
# use python here so we can use the same version normalization that will be
# used to create the wheel.
wheel_file = dist/$(call python_get_wheelname,api,$(project_rs_default),opentrons,$(BUILD_NUMBER))

# Find the version of the sdist file from git using a helper script.
sdist_file = dist/$(call python_get_sdistname,api,$(project_ot3_default),opentrons)
sdist_file = dist/$(call python_get_sdistname,api,$(project_rs_default),opentrons)

# Find the branch, sha, version that will be used to update the VERSION.json file
version_file = $(call python_get_git_version,api,$(project_ot3_default),opentrons)
version_file = $(call python_get_git_version,api,$(project_rs_default),opentrons)

# These variables are for simulating python protocols
sim_log_level ?= info
Expand Down Expand Up @@ -100,7 +100,7 @@ wheel:


.PHONY: sdist
sdist: export OPENTRONS_PROJECT=$(project_ot3_default)
sdist: export OPENTRONS_PROJECT=$(project_rs_default)
sdist:
$(clean_sdist_cmd)
$(python) setup.py sdist
Expand Down
2 changes: 1 addition & 1 deletion api/docs/v2/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ For example, if we wanted to transfer liquid from well A1 to well B1 on a plate,
# protocol run function
def run(protocol: protocol_api.ProtocolContext):
# labware
trash = protocol.load_trash_bin("A3")
plate = protocol.load_labware(
"corning_96_wellplate_360ul_flat", location="D1"
)
tiprack = protocol.load_labware(
"opentrons_flex_96_tiprack_200ul", location="D2"
)
trash = protocol.load_trash_bin(location="A3")
# pipettes
left_pipette = protocol.load_instrument(
Expand Down
2 changes: 1 addition & 1 deletion api/docs/v2/new_examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ This code only loads the instruments and labware listed above, and performs no o
load_name="usascientific_12_reservoir_22ml", location="D1"
)
# load trash bin in deck slot A3
trash = protocol.load_trash_bin("A3")
trash = protocol.load_trash_bin(location="A3")
# Put protocol commands here
.. tab:: OT-2
Expand Down
2 changes: 2 additions & 0 deletions api/docs/v2/new_labware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ After you've created your labware, save it as a ``.json`` file and add it to the

If other people need to use your custom labware definition, they must also add it to their Opentrons App.

.. _loading-labware:

***************
Loading Labware
***************
Expand Down
2 changes: 1 addition & 1 deletion api/docs/v2/pipettes/partial_tip_pickup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,4 @@ The major drawback of this configuration, compared to using column 12, is that t

This code first constructs a list of all the wells in row A of the tip rack. Then, when picking up a tip, instead of referencing one of those wells directly, the ``location`` is set to ``row_a.pop()``. This uses the `built-in pop method <https://docs.python.org/3/tutorial/datastructures.html#more-on-lists>`_ to get the last item from the list and remove it from the list. If you keep using this approach to pick up tips, you'll get an error once the tip rack is empty — not from the API, but from Python itself, since you're trying to ``pop`` an item from an empty list.

Additionally, you can't access the rightmost columns in labware in column 3, since they are beyond the movement limit of the pipette. The exact number of inaccessible columns varies by labware type. Any well that is within 29 mm of the right edge of the slot may be inaccessible in a column 12 configuration. Call ``configure_nozzle_layout()`` again to switch to a column 1 layout if you need to pipette in that area.
Additionally, you can't access the rightmost columns in labware in column 3, since they are beyond the movement limit of the pipette. The exact number of inaccessible columns varies by labware type. Any well that is within 29 mm of the right edge of the slot may be inaccessible in a column 1 configuration. Call ``configure_nozzle_layout()`` again to switch to a column 12 layout if you need to pipette in that area.
5 changes: 0 additions & 5 deletions api/docs/v2/versioning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,6 @@ This table lists the correspondence between Protocol API versions and robot soft
Changes in API Versions
=======================

Version 2.17
------------

- :py:meth:`.dispense` will now raise an error if you try to dispense more than is available.

Version 2.16
------------

Expand Down
6 changes: 4 additions & 2 deletions api/src/opentrons/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from opentrons.protocols.api_support.deck_type import (
guess_from_global_config as guess_deck_type_from_global_config,
should_load_fixed_trash,
should_load_fixed_trash_for_python_protocol,
should_load_fixed_trash_labware_for_python_protocol,
)
from opentrons.protocols.api_support.types import APIVersion
from opentrons.protocols.execution import execute as execute_apiv2
Expand Down Expand Up @@ -540,7 +540,9 @@ def _create_live_context_pe(
config=_get_protocol_engine_config(),
drop_tips_after_run=False,
post_run_hardware_state=PostRunHardwareState.STAY_ENGAGED_IN_PLACE,
load_fixed_trash=should_load_fixed_trash_for_python_protocol(api_version),
load_fixed_trash=should_load_fixed_trash_labware_for_python_protocol(
api_version
),
)
)

Expand Down
18 changes: 17 additions & 1 deletion api/src/opentrons/hardware_control/backends/flex_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
EstopState,
HardwareEventHandler,
HardwareEventUnsubscriber,
HepaFanState,
HepaUVState,
StatusBarState,
)
from opentrons.hardware_control.module_control import AttachedModulesControl
from ..dev_types import OT3AttachedInstruments
from ..types import StatusBarState
from .types import HWStopCondition

Cls = TypeVar("Cls")
Expand Down Expand Up @@ -417,3 +419,17 @@ def check_gripper_position_within_bounds(
hard_limit_upper: float,
) -> None:
...

async def set_hepa_fan_state(self, fan_on: bool, duty_cycle: int) -> bool:
"""Sets the state and duty cycle of the Hepa/UV module."""
...

async def get_hepa_fan_state(self) -> Optional[HepaFanState]:
...

async def set_hepa_uv_state(self, light_on: bool, uv_duration_s: int) -> bool:
"""Sets the state and duration (seconds) of the UV light for the Hepa/UV module."""
...

async def get_hepa_uv_state(self) -> Optional[HepaUVState]:
...
37 changes: 36 additions & 1 deletion api/src/opentrons/hardware_control/backends/ot3controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@
from opentrons_hardware.hardware_control.gripper_settings import (
get_gripper_jaw_state,
)
from opentrons_hardware.hardware_control.hepa_uv_settings import (
set_hepa_fan_state as set_hepa_fan_state_fw,
get_hepa_fan_state as get_hepa_fan_state_fw,
set_hepa_uv_state as set_hepa_uv_state_fw,
get_hepa_uv_state as get_hepa_uv_state_fw,
)

from opentrons_hardware.drivers.gpio import OT3GPIO, RemoteOT3GPIO
from opentrons_shared_data.pipette.dev_types import PipetteName
Expand All @@ -193,7 +199,7 @@
AttachedGripper,
OT3AttachedInstruments,
)
from ..types import StatusBarState
from ..types import HepaFanState, HepaUVState, StatusBarState

from .types import HWStopCondition
from .flex_protocol import FlexBackend
Expand Down Expand Up @@ -1570,3 +1576,32 @@ def check_gripper_position_within_bounds(
"actual-jaw-width": current_gripper_position,
},
)

async def set_hepa_fan_state(self, fan_on: bool, duty_cycle: int) -> bool:
return await set_hepa_fan_state_fw(self._messenger, fan_on, duty_cycle)

async def get_hepa_fan_state(self) -> Optional[HepaFanState]:
res = await get_hepa_fan_state_fw(self._messenger)
return (
HepaFanState(
fan_on=res.fan_on,
duty_cycle=res.duty_cycle,
)
if res
else None
)

async def set_hepa_uv_state(self, light_on: bool, uv_duration_s: int) -> bool:
return await set_hepa_uv_state_fw(self._messenger, light_on, uv_duration_s)

async def get_hepa_uv_state(self) -> Optional[HepaUVState]:
res = await get_hepa_uv_state_fw(self._messenger)
return (
HepaUVState(
light_on=res.uv_light_on,
uv_duration_s=res.uv_duration_s,
remaining_time_s=res.remaining_time_s,
)
if res
else None
)
14 changes: 14 additions & 0 deletions api/src/opentrons/hardware_control/backends/ot3simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from opentrons.hardware_control.types import (
BoardRevision,
Axis,
HepaFanState,
HepaUVState,
OT3Mount,
OT3AxisMap,
CurrentConfig,
Expand Down Expand Up @@ -803,3 +805,15 @@ def check_gripper_position_within_bounds(
# This is a (pretty bad) simulation of the gripper actually gripping something,
# but it should work.
self._encoder_position[Axis.G] = (hard_limit_upper - jaw_width) / 2

async def set_hepa_fan_state(self, fan_on: bool, duty_cycle: int) -> bool:
return False

async def get_hepa_fan_state(self) -> Optional[HepaFanState]:
return None

async def set_hepa_uv_state(self, light_on: bool, timeout_s: int) -> bool:
return False

async def get_hepa_uv_state(self) -> Optional[HepaUVState]:
return None
5 changes: 5 additions & 0 deletions api/src/opentrons/hardware_control/backends/ot3utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,13 @@ def motor_nodes(devices: Set[FirmwareTarget]) -> Set[NodeId]:
NodeId.head_bootloader,
NodeId.gripper_bootloader,
}
hepa_uv_nodes = {
NodeId.hepa_uv,
NodeId.hepa_uv_bootloader,
}
# remove any bootloader nodes
motor_nodes -= bootloader_nodes
motor_nodes -= hepa_uv_nodes
# filter out usb nodes
return {NodeId(target) for target in motor_nodes if target in NodeId}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ def plan_check_pick_up_tip( # type: ignore[no-untyped-def]
if instrument.has_tip:
raise UnexpectedTipAttachError("pick_up_tip", instrument.name, mount.name)
self._ihp_log.debug(f"Picking up tip on {mount.name}")

tip_count = instrument.nozzle_manager.current_configuration.tip_count
if presses is None or presses < 0:
checked_presses = instrument.pick_up_configurations.press_fit.presses
else:
Expand All @@ -782,17 +782,20 @@ def plan_check_pick_up_tip( # type: ignore[no-untyped-def]
else:
check_incr = increment

pick_up_speed = instrument.pick_up_configurations.press_fit.speed
pick_up_speed = instrument.pick_up_configurations.press_fit.speed_by_tip_count[
tip_count
]
pick_up_distance = (
instrument.pick_up_configurations.press_fit.distance_by_tip_count[tip_count]
)

def build_presses() -> Iterator[Tuple[float, float]]:
# Press the nozzle into the tip <presses> number of times,
# moving further by <increment> mm after each press
for i in range(checked_presses):
# move nozzle down into the tip
press_dist = (
-1.0 * instrument.pick_up_configurations.press_fit.distance
+ -1.0 * check_incr * i
)

press_dist = -1.0 * pick_up_distance + -1.0 * check_incr * i
# move nozzle back up
backup_dist = -press_dist
yield (press_dist, backup_dist)
Expand All @@ -814,7 +817,7 @@ def add_tip_to_instr() -> None:
Axis.by_mount(
mount
): instrument.pick_up_configurations.press_fit.current_by_tip_count[
instrument.nozzle_manager.current_configuration.tip_count
tip_count
]
},
speed=pick_up_speed,
Expand All @@ -824,9 +827,7 @@ def add_tip_to_instr() -> None:
for press_dist, backup_dist in build_presses()
],
shake_off_list=self._build_pickup_shakes(instrument),
retract_target=instrument.pick_up_configurations.press_fit.distance
+ check_incr * checked_presses
+ 2,
retract_target=pick_up_distance + check_incr * checked_presses + 2,
),
add_tip_to_instr,
)
Expand Down Expand Up @@ -855,9 +856,7 @@ def add_tip_to_instr() -> None:
for press_dist, backup_dist in build_presses()
],
shake_off_list=self._build_pickup_shakes(instrument),
retract_target=instrument.pick_up_configurations.press_fit.distance
+ check_incr * checked_presses
+ 2,
retract_target=pick_up_distance + check_incr * checked_presses + 2,
),
add_tip_to_instr,
)
Expand Down
Loading

0 comments on commit 0d1c8c7

Please sign in to comment.