Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api, robot-server, app): Handle the Plate Reader lid while held by the gripper when the Estop is pressed. #16574

Merged
merged 27 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
369bb40
save
vegano1 Oct 18, 2024
b7b989f
Merge branch 'edge' into PLAT-451-handle-estop-with-plate-reader
vegano1 Oct 18, 2024
d42b4ba
save
vegano1 Oct 18, 2024
893da7a
working place labware command
vegano1 Oct 19, 2024
2fcce8e
feat(api): add unsafe/placeLabware command to place labware the gripp…
vegano1 Oct 19, 2024
0e201e0
feat(robot-server): add placeLabwareState and estopEngaged to runs/cu…
vegano1 Oct 20, 2024
3ae5e2f
clean up
vegano1 Oct 21, 2024
f0b4766
format
vegano1 Oct 21, 2024
a35161b
api, always move the plate reader lid to its dock
vegano1 Oct 22, 2024
c7e8264
feat(app, shared-data): add usePlacePlateReaderLid to EstopPressedMod…
vegano1 Oct 22, 2024
2a7a4b4
save
vegano1 Oct 22, 2024
2cb3765
conditionally call unsafe/placeLabware command based on the runs/curr…
vegano1 Oct 22, 2024
837fe9a
simplify usePlacePlateReaderLid hook
vegano1 Oct 23, 2024
74b02cc
Merge branch 'edge' into PLAT-451-handle-estop-with-plate-reader
vegano1 Oct 23, 2024
6861d3d
fix bad edge merge
vegano1 Oct 23, 2024
6c4de3e
move EstopTakeover component below maintenanceRunTakeover so we dont …
vegano1 Oct 23, 2024
de39cbe
refactor estopPressed
vegano1 Oct 24, 2024
3dee541
clean up
vegano1 Oct 24, 2024
39fb23c
Merge branch 'edge' into PLAT-451-handle-estop-with-plate-reader
vegano1 Oct 24, 2024
ddc24cb
default the filename arg of absorbance read command to None
vegano1 Oct 24, 2024
d9f9175
format
vegano1 Oct 28, 2024
337b515
add unit tests
vegano1 Oct 29, 2024
6fe1b96
format
vegano1 Oct 29, 2024
2da1b7a
Merge branch 'edge' into PLAT-451-handle-estop-with-plate-reader
vegano1 Oct 29, 2024
2023531
cleanup
vegano1 Oct 29, 2024
8c96485
format
vegano1 Oct 29, 2024
8cf7a54
formatr
vegano1 Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api-client/src/runs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
RunTimeCommand,
RunTimeParameter,
NozzleLayoutConfig,
OnDeckLabwareLocation,
} from '@opentrons/shared-data'
import type { ResourceLink, ErrorDetails } from '../types'
export * from './commands/types'
Expand Down Expand Up @@ -117,7 +118,9 @@ export interface Runs {
}

export interface RunCurrentStateData {
estopEngaged: boolean
activeNozzleLayouts: Record<string, NozzleLayoutValues> // keyed by pipetteId
placeLabwareState?: PlaceLabwareState
}

export const RUN_ACTION_TYPE_PLAY: 'play' = 'play'
Expand Down Expand Up @@ -209,3 +212,9 @@ export interface NozzleLayoutValues {
activeNozzles: string[]
config: NozzleLayoutConfig
}

export interface PlaceLabwareState {
labwareId: string
location: OnDeckLabwareLocation
shouldPlaceDown: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,8 @@ def usb_port(self) -> USBPort:
return self._usb_port

async def deactivate(self, must_be_running: bool = True) -> None:
"""Deactivate the module.

Contains an override to the `wait_for_is_running` step in cases where the
module must be deactivated regardless of context."""
await self._poller.stop()
await self._driver.disconnect()
"""Deactivate the module."""
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean when we deactivate the module now?

Copy link
Contributor Author

@vegano1 vegano1 Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the context of modules, the deactivate function means we stop whatever target action we are doing, such as heating, shaking, etc. We don't have any actions other than reading for the plate reader, but these happen relatively quickly, so we don't have to worry about them. The problem here was that I thought deactivate meant stop and disconnect the module, which is not correct as deactivate gets called when the estop is pressed. In the case of the plate reader it would disconnect and reconnect when the estop was pressed. This change fixes that behavior by removing the stop and disconnect to the cleanup method.


async def wait_for_is_running(self) -> None:
if not self.is_simulated:
Expand Down Expand Up @@ -336,7 +332,8 @@ async def cleanup(self) -> None:
Clean up, i.e. stop pollers, disconnect serial, etc in preparation for
object destruction.
"""
await self.deactivate()
await self._poller.stop()
await self._driver.disconnect()

async def set_sample_wavelength(
self,
Expand Down
2 changes: 2 additions & 0 deletions api/src/opentrons/motion_planning/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
MINIMUM_Z_MARGIN,
get_waypoints,
get_gripper_labware_movement_waypoints,
get_gripper_labware_placement_waypoints,
)

from .types import Waypoint, MoveType
Expand All @@ -27,4 +28,5 @@
"ArcOutOfBoundsError",
"get_waypoints",
"get_gripper_labware_movement_waypoints",
"get_gripper_labware_placement_waypoints",
]
32 changes: 32 additions & 0 deletions api/src/opentrons/motion_planning/waypoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,35 @@ def get_gripper_labware_movement_waypoints(
)
)
return waypoints_with_jaw_status


def get_gripper_labware_placement_waypoints(
to_labware_center: Point,
gripper_home_z: float,
drop_offset: Optional[Point],
) -> List[GripperMovementWaypointsWithJawStatus]:
"""Get waypoints for placing labware using a gripper."""
drop_offset = drop_offset or Point()

drop_location = to_labware_center + Point(
drop_offset.x, drop_offset.y, drop_offset.z
)

post_drop_home_pos = Point(drop_location.x, drop_location.y, gripper_home_z)

return [
GripperMovementWaypointsWithJawStatus(
position=Point(drop_location.x, drop_location.y, gripper_home_z),
jaw_open=False,
dropping=False,
),
GripperMovementWaypointsWithJawStatus(
position=drop_location, jaw_open=False, dropping=False
),
# Gripper ungrips here
GripperMovementWaypointsWithJawStatus(
position=post_drop_home_pos,
jaw_open=True,
dropping=True,
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ def initialize(
)
self._initialized_value = wavelengths

def read(self, filename: Optional[str]) -> Dict[int, Dict[str, float]]:
def read(self, filename: Optional[str] = None) -> Dict[int, Dict[str, float]]:
"""Initiate a read on the Absorbance Reader, and return the results. During Analysis, this will return a measurement of zero for all wells."""
wavelengths = self._engine_client.state.modules.get_absorbance_reader_substate(
self.module_id
Expand Down
35 changes: 20 additions & 15 deletions api/src/opentrons/protocol_api/core/engine/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,10 @@ def load_module(

# When the protocol engine is created, we add Module Lids as part of the deck fixed labware
# If a valid module exists in the deck config. For analysis, we add the labware here since
# deck fixed labware is not created under the same conditions.
if self._engine_client.state.config.use_virtual_modules:
self._load_virtual_module_lid(module_core)
# deck fixed labware is not created under the same conditions. We also need to inject the Module
# lids when the module isnt already on the deck config, like when adding a new
# module during a protocol setup.
self._load_virtual_module_lid(module_core)

self._module_cores_by_id[module_core.module_id] = module_core

Expand All @@ -461,20 +462,24 @@ def _load_virtual_module_lid(
self, module_core: Union[ModuleCore, NonConnectedModuleCore]
) -> None:
if isinstance(module_core, AbsorbanceReaderCore):
lid = self._engine_client.execute_command_without_recovery(
cmd.LoadLabwareParams(
loadName="opentrons_flex_lid_absorbance_plate_reader_module",
location=ModuleLocation(moduleId=module_core.module_id),
namespace="opentrons",
version=1,
displayName="Absorbance Reader Lid",
)
substate = self._engine_client.state.modules.get_absorbance_reader_substate(
module_core.module_id
)
if substate.lid_id is None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, should provide coverage for the current run setup bug.

lid = self._engine_client.execute_command_without_recovery(
cmd.LoadLabwareParams(
loadName="opentrons_flex_lid_absorbance_plate_reader_module",
location=ModuleLocation(moduleId=module_core.module_id),
namespace="opentrons",
version=1,
displayName="Absorbance Reader Lid",
)
)

self._engine_client.add_absorbance_reader_lid(
module_id=module_core.module_id,
lid_id=lid.labwareId,
)
self._engine_client.add_absorbance_reader_lid(
module_id=module_core.module_id,
lid_id=lid.labwareId,
)

def _create_non_connected_module_core(
self, load_module_result: LoadModuleResult
Expand Down
2 changes: 1 addition & 1 deletion api/src/opentrons/protocol_api/core/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def initialize(
"""Initialize the Absorbance Reader by taking zero reading."""

@abstractmethod
def read(self, filename: Optional[str]) -> Dict[int, Dict[str, float]]:
def read(self, filename: Optional[str] = None) -> Dict[int, Dict[str, float]]:
"""Get an absorbance reading from the Absorbance Reader."""

@abstractmethod
Expand Down
4 changes: 3 additions & 1 deletion api/src/opentrons/protocol_api/module_contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,9 @@ def initialize(
)

@requires_version(2, 21)
def read(self, export_filename: Optional[str]) -> Dict[int, Dict[str, float]]:
def read(
self, export_filename: Optional[str] = None
) -> Dict[int, Dict[str, float]]:
"""Initiate read on the Absorbance Reader.

Returns a dictionary of wavelengths to dictionary of values ordered by well name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@
unsafe.UpdatePositionEstimators,
unsafe.UnsafeEngageAxes,
unsafe.UnsafeUngripLabware,
unsafe.UnsafePlaceLabware,
],
Field(discriminator="commandType"),
]
Expand Down Expand Up @@ -469,6 +470,7 @@
unsafe.UpdatePositionEstimatorsParams,
unsafe.UnsafeEngageAxesParams,
unsafe.UnsafeUngripLabwareParams,
unsafe.UnsafePlaceLabwareParams,
]

CommandType = Union[
Expand Down Expand Up @@ -544,6 +546,7 @@
unsafe.UpdatePositionEstimatorsCommandType,
unsafe.UnsafeEngageAxesCommandType,
unsafe.UnsafeUngripLabwareCommandType,
unsafe.UnsafePlaceLabwareCommandType,
]

CommandCreate = Annotated[
Expand Down Expand Up @@ -620,6 +623,7 @@
unsafe.UpdatePositionEstimatorsCreate,
unsafe.UnsafeEngageAxesCreate,
unsafe.UnsafeUngripLabwareCreate,
unsafe.UnsafePlaceLabwareCreate,
],
Field(discriminator="commandType"),
]
Expand Down Expand Up @@ -697,6 +701,7 @@
unsafe.UpdatePositionEstimatorsResult,
unsafe.UnsafeEngageAxesResult,
unsafe.UnsafeUngripLabwareResult,
unsafe.UnsafePlaceLabwareResult,
]


Expand Down
15 changes: 15 additions & 0 deletions api/src/opentrons/protocol_engine/commands/unsafe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@
)


from .unsafe_place_labware import (
UnsafePlaceLabwareCommandType,
UnsafePlaceLabwareParams,
UnsafePlaceLabwareResult,
UnsafePlaceLabware,
UnsafePlaceLabwareCreate,
)


__all__ = [
# Unsafe blow-out-in-place command models
"UnsafeBlowOutInPlaceCommandType",
Expand Down Expand Up @@ -71,4 +80,10 @@
"UnsafeUngripLabwareResult",
"UnsafeUngripLabware",
"UnsafeUngripLabwareCreate",
# Unsafe place labware
"UnsafePlaceLabwareCommandType",
"UnsafePlaceLabwareParams",
"UnsafePlaceLabwareResult",
"UnsafePlaceLabware",
"UnsafePlaceLabwareCreate",
]
Loading
Loading