Skip to content

Commit

Permalink
Merge branch 'edge' into feat_pd-compound-commands-wastechute
Browse files Browse the repository at this point in the history
  • Loading branch information
ncdiehl11 committed Nov 16, 2023
2 parents 2ff50b7 + 780a62f commit 1c99853
Show file tree
Hide file tree
Showing 50 changed files with 912 additions and 1,419 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/app-test-build-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ jobs:
echo "Configuring project, bucket, and folder for ot3"
echo "project=ot3" >> $GITHUB_OUTPUT
echo "bucket=${{env._APP_DEPLOY_BUCKET_OT3}}" >> $GITHUB_OUTPUT
echo "folder=${{env._APP_DEPLOY_BUCKET_OT3}}" >> $GITHUB_OUTPUT
echo "folder=${{env._APP_DEPLOY_FOLDER_OT3}}" >> $GITHUB_OUTPUT
fi
- uses: 'actions/checkout@v3'
with:
Expand Down
47 changes: 6 additions & 41 deletions api/release-notes-internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,17 @@ For more details about this release, please see the full [technical change log][

---

# Internal Release 0.14.0
# Internal Release 1.0.0

## New Stuff In This Release

- Return tip heights and some other pipette behaviors are now properly executed based on the kind of tip being used
- Release Flex robot software builds are now cryptographically signed. If you run a release build, you can only install other properly signed release builds. Note that if the robot was previously on a non-release build this won't latch; remove the update server config file at ``/var/lib/otupdate/config.json`` to go back to signed builds only.
- Error handling has been overhauled; all errors now display with an error code for easier reporting. Many of those error codes are the 4000 catchall still but this will improve over time.
- If there's an error during the post-run cleanup steps, where the robot homes and drops tips, the run should no longer get stuck in a permanent "finishing" state. It should get marked as failed.
- Further updates to Flex motion control parameters from hardware testing for both gantry and plunger speeds and acceleration
- Pipette overpressure detection is now integrated.
- All instrument flows should now show errors if they occur instead of skipping a step
- Fixes to several incorrect status displays in ODD (i.e. protocols skipping the full-color outcome splash)
- Robot can now handle json protocol v7
- Support for PVT (v1.1) grippers
- Update progress should get displayed after restart for firmware updates
- Removed `use_pick_up_location_lpc_offset` and `use_drop_location_lpc_offset` from `protocol_context.move_labware` arguments. So they should be removed from any protocols that used them. This change also requires resetting the protocol run database on the robot.
- Added 'contextual' gripper offsets to deck, labware and module definitions. So, any labware movement offsets that were previously being specified in the protocol should now be removed or adjusted or they will get added twice.
- Fixed an issue where the robot wasn't actually checking for updates; you will now correctly get prompted to update your robot from the settings tab of the ODD when an update is available, including during onboarding
- You can update the robot by putting a system update (ot3-system.zip) on a flash drive and plugging it in the front USB port, then going to robot settings
- Support for 96-channel pipettes in protocols
- Early provisional support for deck configuration and trash chutes in protocols


## Big Things That Don't Work Yet So Don't Report Bugs About Them

### Robot Control
- Pipette pressure sensing for liquid-level sensing purposes
- Labware pick up failure with gripper
- E-stop integrated handling especially with modules

## Big Things That Do Work Please Do Report Bugs About Them
### Robot Control
- Protocol behavior
- Labware movement between slots/modules, both manual and with gripper, from python protocols
- Labware drop/gripper crash errors, but they're very insensitive
- Pipette and gripper automated offset calibration
- Network connectivity and discoverability
- Firmware update for all devices
- Cancelling a protocol run. We're even more sure we fixed this so definitely tell us if it's not.
- USB connectivity
- Stall detection firing basically ever unless you clearly ran into something

### ODD
- Protocol execution including end-of-protocol screen
- Protocol run monitoring
- Attach and calibrate
- Network connection management, including viewing IP addresses and connecting to wifi networks
- Automatic updates of robot software when new internal releases are created
- Chrome remote devtools - if you enable them and then use Chrome to go to robotip:9223 you'll get devtools
- After a while, the ODD should go into idle; if you touch it, it will come back online



- Pipette partial tip pickup is present but not fully validated or developed yet. Partial tip pickup on 96 channel pipettes will not use correct motion parameters; using the front channel of a pipette in partial tip pickup does not work.
6 changes: 6 additions & 0 deletions api/src/opentrons/cli/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,16 @@ class AnalyzeResults(BaseModel):
See robot-server's analysis models for field documentation.
"""

# We want to unify this local analysis model with the one that robot-server returns.
# Until that happens, we need to keep these fields in sync manually.

# Fields that are currently unique to this local analysis module, missing from robot-server:
createdAt: datetime
files: List[ProtocolFile]
config: Union[JsonConfig, PythonConfig]
metadata: Dict[str, Any]

# Fields that should match robot-server:
robotType: RobotType
commands: List[Command]
labware: List[LoadedLabware]
Expand Down
6 changes: 6 additions & 0 deletions api/src/opentrons/protocol_api/core/engine/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

from opentrons_shared_data.pipette.dev_types import PipetteNameType
from opentrons.protocol_api._nozzle_layout import NozzleLayout
from opentrons.hardware_control.nozzle_manager import NozzleConfigurationType

from ..instrument import AbstractInstrument
from .well import WellCore
Expand Down Expand Up @@ -574,6 +575,11 @@ def get_dispense_flow_rate(self, rate: float = 1.0) -> float:
def get_blow_out_flow_rate(self, rate: float = 1.0) -> float:
return self._blow_out_flow_rate * rate

def get_nozzle_configuration(self) -> NozzleConfigurationType:
return self._engine_client.state.pipettes.get_nozzle_layout_type(
self._pipette_id
)

def set_flow_rate(
self,
aspirate: Optional[float] = None,
Expand Down
8 changes: 7 additions & 1 deletion api/src/opentrons/protocol_engine/commands/drop_tip.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,14 @@ async def execute(self, params: DropTipParams) -> DropTipResult:
else:
well_location = params.wellLocation

is_partially_configured = self._state_view.pipettes.get_is_partially_configured(
pipette_id=pipette_id
)
tip_drop_location = self._state_view.geometry.get_checked_tip_drop_location(
pipette_id=pipette_id, labware_id=labware_id, well_location=well_location
pipette_id=pipette_id,
labware_id=labware_id,
well_location=well_location,
partially_configured=is_partially_configured,
)

position = await self._movement_handler.move_to_well(
Expand Down
135 changes: 63 additions & 72 deletions api/src/opentrons/protocol_engine/execution/tip_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,47 @@ async def add_tip(self, pipette_id: str, tip: TipGeometry) -> None:
"""Tell the Hardware API that a tip is attached."""


async def _available_for_nozzle_layout(
channels: int,
style: str,
primary_nozzle: Optional[str],
front_right_nozzle: Optional[str],
) -> Dict[str, str]:
"""Check nozzle layout is compatible with the pipette.
Returns:
A dict of nozzles used to configure the pipette.
"""
if channels == 1:
raise CommandPreconditionViolated(
message=f"Cannot configure nozzle layout with a {channels} channel pipette."
)
if style == "EMPTY":
return {}
if style == "ROW" and channels == 8:
raise CommandParameterLimitViolated(
command_name="configure_nozzle_layout",
parameter_name="RowNozzleLayout",
limit_statement="RowNozzleLayout is incompatible with {channels} channel pipettes.",
actual_value=str(primary_nozzle),
)
if not primary_nozzle:
return {"primary_nozzle": "A1"}
if style == "SINGLE":
return {"primary_nozzle": primary_nozzle}
if not front_right_nozzle:
return {
"primary_nozzle": primary_nozzle,
"front_right_nozzle": PRIMARY_NOZZLE_TO_ENDING_NOZZLE_MAP[primary_nozzle][
style
],
}
return {
"primary_nozzle": primary_nozzle,
"front_right_nozzle": front_right_nozzle,
}


class HardwareTipHandler(TipHandler):
"""Pick up and drop tips, using the Hardware API."""

Expand All @@ -72,9 +113,9 @@ def __init__(
hardware_api: HardwareControlAPI,
labware_data_provider: Optional[LabwareDataProvider] = None,
) -> None:
self._state_view = state_view
self._hardware_api = hardware_api
self._labware_data_provider = labware_data_provider or LabwareDataProvider()
self._state_view = state_view

async def available_for_nozzle_layout(
self,
Expand All @@ -83,40 +124,15 @@ async def available_for_nozzle_layout(
primary_nozzle: Optional[str] = None,
front_right_nozzle: Optional[str] = None,
) -> Dict[str, str]:
"""Check nozzle layout is compatible with the pipette."""
"""Returns configuration for nozzle layout to pass to configure_nozzle_layout."""
if self._state_view.pipettes.get_attached_tip(pipette_id):
raise CommandPreconditionViolated(
message=f"Cannot configure nozzle layout of {str(self)} while it has tips attached."
)
channels = self._state_view.pipettes.get_channels(pipette_id)
if channels == 1:
raise CommandPreconditionViolated(
message=f"Cannot configure nozzle layout with a {channels} channel pipette."
)
if style == "EMPTY":
return {}
if style == "ROW" and channels == 8:
raise CommandParameterLimitViolated(
command_name="configure_nozzle_layout",
parameter_name="RowNozzleLayout",
limit_statement="RowNozzleLayout is incompatible with {channels} channel pipettes.",
actual_value=str(primary_nozzle),
)
if not primary_nozzle:
return {"primary_nozzle": "A1"}
if style == "SINGLE":
return {"primary_nozzle": primary_nozzle}
if not front_right_nozzle:
return {
"primary_nozzle": primary_nozzle,
"front_right_nozzle": PRIMARY_NOZZLE_TO_ENDING_NOZZLE_MAP[
primary_nozzle
][style],
}
return {
"primary_nozzle": primary_nozzle,
"front_right_nozzle": front_right_nozzle,
}
return await _available_for_nozzle_layout(
channels, style, primary_nozzle, front_right_nozzle
)

async def pick_up_tip(
self,
Expand Down Expand Up @@ -196,48 +212,6 @@ class VirtualTipHandler(TipHandler):
def __init__(self, state_view: StateView) -> None:
self._state_view = state_view

async def available_for_nozzle_layout(
self,
pipette_id: str,
style: str,
primary_nozzle: Optional[str] = None,
front_right_nozzle: Optional[str] = None,
) -> Dict[str, str]:
"""Check nozzle layout is compatible with the pipette."""
if self._state_view.pipettes.get_attached_tip(pipette_id):
raise CommandPreconditionViolated(
message=f"Cannot configure nozzle layout of {str(self)} while it has tips attached."
)
channels = self._state_view.pipettes.get_channels(pipette_id)
if channels == 1:
raise CommandPreconditionViolated(
message=f"Cannot configure nozzle layout with a {channels} channel pipette."
)
if style == "EMPTY":
return {}
if style == "ROW" and channels == 8:
raise CommandParameterLimitViolated(
command_name="configure_nozzle_layout",
parameter_name="RowNozzleLayout",
limit_statement="RowNozzleLayout is incompatible with {channels} channel pipettes.",
actual_value=str(primary_nozzle),
)
if not primary_nozzle:
return {"primary_nozzle": "A1"}
if style == "SINGLE":
return {"primary_nozzle": primary_nozzle}
if not front_right_nozzle:
return {
"primary_nozzle": primary_nozzle,
"front_right_nozzle": PRIMARY_NOZZLE_TO_ENDING_NOZZLE_MAP[
primary_nozzle
][style],
}
return {
"primary_nozzle": primary_nozzle,
"front_right_nozzle": front_right_nozzle,
}

async def pick_up_tip(
self,
pipette_id: str,
Expand All @@ -262,6 +236,23 @@ async def pick_up_tip(

return nominal_tip_geometry

async def available_for_nozzle_layout(
self,
pipette_id: str,
style: str,
primary_nozzle: Optional[str] = None,
front_right_nozzle: Optional[str] = None,
) -> Dict[str, str]:
"""Returns configuration for nozzle layout to pass to configure_nozzle_layout."""
if self._state_view.pipettes.get_attached_tip(pipette_id):
raise CommandPreconditionViolated(
message=f"Cannot configure nozzle layout of {str(self)} while it has tips attached."
)
channels = self._state_view.pipettes.get_channels(pipette_id)
return await _available_for_nozzle_layout(
channels, style, primary_nozzle, front_right_nozzle
)

async def drop_tip(
self,
pipette_id: str,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ def configure_virtual_pipette_nozzle_layout(
pipette_model_string
)
new_nozzle_manager = NozzleConfigurationManager.build_from_config(config)
if back_left_nozzle and front_right_nozzle and starting_nozzle:
if back_left_nozzle and front_right_nozzle:
new_nozzle_manager.update_nozzle_configuration(
back_left_nozzle, front_right_nozzle, starting_nozzle
)
self._nozzle_manager_layout_by_id[pipette_id] = new_nozzle_manager
elif back_left_nozzle and front_right_nozzle and starting_nozzle:
elif back_left_nozzle and front_right_nozzle:
# Need to make sure that we pass all the right nozzles here.
self._nozzle_manager_layout_by_id[pipette_id].update_nozzle_configuration(
back_left_nozzle, front_right_nozzle, starting_nozzle
Expand Down
8 changes: 8 additions & 0 deletions api/src/opentrons/protocol_engine/state/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,20 @@ def get_checked_tip_drop_location(
pipette_id: str,
labware_id: str,
well_location: DropTipWellLocation,
partially_configured: bool = False,
) -> WellLocation:
"""Get tip drop location given labware and hardware pipette.
This makes sure that the well location has an appropriate origin & offset
if one is not already set previously.
"""
if (
self._labware.get_definition(labware_id).parameters.isTiprack
and partially_configured
):
raise errors.UnexpectedProtocolError(
"Cannot return tip to a tiprack while the pipette is configured for partial tip."
)
if well_location.origin != DropTipWellOrigin.DEFAULT:
return WellLocation(
origin=WellOrigin(well_location.origin.value),
Expand Down
4 changes: 4 additions & 0 deletions api/src/opentrons/protocol_engine/state/pipettes.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,7 @@ def get_nozzle_layout_type(self, pipette_id: str) -> NozzleConfigurationType:
return nozzle_map_for_pipette.configuration
else:
return NozzleConfigurationType.FULL

def get_is_partially_configured(self, pipette_id: str) -> bool:
"""Determine if the provided pipette is partially configured."""
return self.get_nozzle_layout_type(pipette_id) != NozzleConfigurationType.FULL
14 changes: 13 additions & 1 deletion api/tests/opentrons/protocol_engine/commands/test_drop_tip.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,16 @@ async def test_drop_tip_implementation(
homeAfter=True,
)

decoy.when(
mock_state_view.pipettes.get_is_partially_configured(pipette_id="abc")
).then_return(False)

decoy.when(
mock_state_view.geometry.get_checked_tip_drop_location(
pipette_id="abc",
labware_id="123",
well_location=DropTipWellLocation(offset=WellOffset(x=1, y=2, z=3)),
partially_configured=False,
)
).then_return(WellLocation(offset=WellOffset(x=4, y=5, z=6)))

Expand Down Expand Up @@ -142,9 +147,16 @@ async def test_drop_tip_with_alternating_locations(
)
).then_return(drop_location)

decoy.when(
mock_state_view.pipettes.get_is_partially_configured(pipette_id="abc")
).then_return(False)

decoy.when(
mock_state_view.geometry.get_checked_tip_drop_location(
pipette_id="abc", labware_id="123", well_location=drop_location
pipette_id="abc",
labware_id="123",
well_location=drop_location,
partially_configured=False,
)
).then_return(WellLocation(offset=WellOffset(x=4, y=5, z=6)))

Expand Down
Loading

0 comments on commit 1c99853

Please sign in to comment.