Skip to content

Commit

Permalink
Merge branch 'edge' into PLAT-451-handle-estop-with-plate-reader
Browse files Browse the repository at this point in the history
  • Loading branch information
vegano1 committed Oct 24, 2024
2 parents 3dee541 + a0086cc commit 39fb23c
Show file tree
Hide file tree
Showing 114 changed files with 2,166 additions and 740 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 @@ -148,7 +148,7 @@ jobs:
yarn config set cache-folder ${{ github.workspace }}/.yarn-cache
make setup-js
- name: 'test native(er) packages'
run: make test-js-internal tests="${{}matrix.shell}/src" cov_opts="--coverage=true"
run: make test-js-internal tests="${{matrix.shell}}/src" cov_opts="--coverage=true"
- name: 'Upload coverage report'
uses: 'codecov/codecov-action@v3'
with:
Expand Down
11 changes: 9 additions & 2 deletions api-client/src/runs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export interface GetRunsParams {
}

export interface Runs {
data: RunData[]
data: readonly RunData[]
links: RunsLinks
}

Expand All @@ -128,12 +128,15 @@ export const RUN_ACTION_TYPE_PAUSE: 'pause' = 'pause'
export const RUN_ACTION_TYPE_STOP: 'stop' = 'stop'
export const RUN_ACTION_TYPE_RESUME_FROM_RECOVERY: 'resume-from-recovery' =
'resume-from-recovery'
export const RUN_ACTION_TYPE_RESUME_FROM_RECOVERY_ASSUMING_FALSE_POSITIVE: 'resume-from-recovery-assuming-false-positive' =
'resume-from-recovery-assuming-false-positive'

export type RunActionType =
| typeof RUN_ACTION_TYPE_PLAY
| typeof RUN_ACTION_TYPE_PAUSE
| typeof RUN_ACTION_TYPE_STOP
| typeof RUN_ACTION_TYPE_RESUME_FROM_RECOVERY
| typeof RUN_ACTION_TYPE_RESUME_FROM_RECOVERY_ASSUMING_FALSE_POSITIVE

export interface RunAction {
id: string
Expand Down Expand Up @@ -175,7 +178,11 @@ export type RunError = RunCommandError
* Error Policy
*/

export type IfMatchType = 'ignoreAndContinue' | 'failRun' | 'waitForRecovery'
export type IfMatchType =
| 'assumeFalsePositiveAndContinue'
| 'ignoreAndContinue'
| 'failRun'
| 'waitForRecovery'

export interface ErrorRecoveryPolicy {
policyRules: Array<{
Expand Down
29 changes: 14 additions & 15 deletions api/docs/v2/new_examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ Opentrons electronic pipettes can do some things that a human cannot do with a p
location=3)
p300 = protocol.load_instrument(
instrument_name="p300_single",
mount="right",
mount="left",
tip_racks=[tiprack_1])
p300.pick_up_tip()
Expand Down Expand Up @@ -442,13 +442,13 @@ This protocol dispenses diluent to all wells of a Corning 96-well plate. Next, i
source = reservoir.wells()[i]
row = plate.rows()[i]
# transfer 30 µL of source to first well in column
pipette.transfer(30, source, row[0], mix_after=(3, 25))
# transfer 30 µL of source to first well in column
pipette.transfer(30, source, row[0], mix_after=(3, 25))
# dilute the sample down the column
pipette.transfer(
30, row[:11], row[1:],
mix_after=(3, 25))
# dilute the sample down the column
pipette.transfer(
30, row[:11], row[1:],
mix_after=(3, 25))
.. tab:: OT-2

Expand All @@ -474,7 +474,7 @@ This protocol dispenses diluent to all wells of a Corning 96-well plate. Next, i
location=4)
p300 = protocol.load_instrument(
instrument_name="p300_single",
mount="right",
mount="left",
tip_racks=[tiprack_1, tiprack_2])
# Dispense diluent
p300.distribute(50, reservoir["A12"], plate.wells())
Expand All @@ -483,16 +483,15 @@ This protocol dispenses diluent to all wells of a Corning 96-well plate. Next, i
for i in range(8):
# save the source well and destination column to variables
source = reservoir.wells()[i]
source = reservoir.wells()[i]
row = plate.rows()[i]
# transfer 30 µL of source to first well in column
p300.transfer(30, source, row[0], mix_after=(3, 25))
# transfer 30 µL of source to first well in column
p300.transfer(30, source, row[0], mix_after=(3, 25))
# dilute the sample down the column
p300.transfer(
30, row[:11], row[1:],
mix_after=(3, 25))
# dilute the sample down the column
p300.transfer(
30, row[:11], row[1:],
mix_after=(3, 25))
Notice here how the code sample loops through the rows and uses slicing to distribute the diluent. For information about these features, see the Loops and Air Gaps examples above. See also, the :ref:`tutorial-commands` section of the Tutorial.

Expand Down
5 changes: 0 additions & 5 deletions api/src/opentrons/hardware_control/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1189,11 +1189,6 @@ async def tip_pickup_moves(

await self.retract(mount, spec.retract_target)

def cache_tip(self, mount: top_types.Mount, tip_length: float) -> None:
instrument = self.get_pipette(mount)
instrument.add_tip(tip_length=tip_length)
instrument.set_current_volume(0)

async def pick_up_tip(
self,
mount: top_types.Mount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,14 @@ def add_tip(self, mount: MountType, tip_length: float) -> None:
f"attach tip called while tip already attached to {instr}"
)

def cache_tip(self, mount: MountType, tip_length: float) -> None:
instrument = self.get_pipette(mount)
if instrument.has_tip:
# instrument.add_tip() would raise an AssertionError if we tried to overwrite an existing tip.
instrument.remove_tip()
instrument.add_tip(tip_length=tip_length)
instrument.set_current_volume(0)

def remove_tip(self, mount: MountType) -> None:
instr = self._attached_instruments[mount]
attached = self.attached_instruments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,14 @@ def add_tip(self, mount: OT3Mount, tip_length: float) -> None:
"attach tip called while tip already attached to {instr}"
)

def cache_tip(self, mount: OT3Mount, tip_length: float) -> None:
instrument = self.get_pipette(mount)
if instrument.has_tip:
# instrument.add_tip() would raise an AssertionError if we tried to overwrite an existing tip.
instrument.remove_tip()
instrument.add_tip(tip_length=tip_length)
instrument.set_current_volume(0)

def remove_tip(self, mount: OT3Mount) -> None:
instr = self._attached_instruments[mount]
attached = self.attached_instruments
Expand Down
14 changes: 5 additions & 9 deletions api/src/opentrons/hardware_control/ot3api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2236,15 +2236,6 @@ async def _tip_motor_action(
)
await self.home_gear_motors()

def cache_tip(
self, mount: Union[top_types.Mount, OT3Mount], tip_length: float
) -> None:
realmount = OT3Mount.from_mount(mount)
instrument = self._pipette_handler.get_pipette(realmount)

instrument.add_tip(tip_length=tip_length)
instrument.set_current_volume(0)

async def pick_up_tip(
self,
mount: Union[top_types.Mount, OT3Mount],
Expand Down Expand Up @@ -2613,6 +2604,11 @@ def add_tip(
) -> None:
self._pipette_handler.add_tip(OT3Mount.from_mount(mount), tip_length)

def cache_tip(
self, mount: Union[top_types.Mount, OT3Mount], tip_length: float
) -> None:
self._pipette_handler.cache_tip(OT3Mount.from_mount(mount), tip_length)

def remove_tip(self, mount: Union[top_types.Mount, OT3Mount]) -> None:
self._pipette_handler.remove_tip(OT3Mount.from_mount(mount))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,24 @@ def get_instrument_max_height(
"""
...

# todo(mm, 2024-10-17): Consider deleting this in favor of cache_tip(), which is
# the same except for `assert`s, if we can do so without breaking anything.
# todo(mm, 2024-10-17): Consider deleting this in favor of cache_tip()
# if we can do so without breaking anything.
def add_tip(self, mount: MountArgType, tip_length: float) -> None:
"""Inform the hardware that a tip is now attached to a pipette.
If a tip is already attached, this no-ops.
This changes the critical point of the pipette to make sure that
the end of the tip is what moves around, and allows liquid handling.
"""
...

def cache_tip(self, mount: MountArgType, tip_length: float) -> None:
"""Inform the hardware that a tip is now attached to a pipette.
This is like `add_tip()`, except that if a tip is already attached,
this replaces it instead of no-opping.
"""
...

def remove_tip(self, mount: MountArgType) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ async def _drop_tip(self) -> None:
try:
if self._state_store.labware.get_fixed_trash_id() == FIXED_TRASH_ID:
# OT-2 and Flex 2.15 protocols will default to the Fixed Trash Labware
await self._tip_handler.add_tip(pipette_id=pipette_id, tip=tip)
await self._tip_handler.cache_tip(
pipette_id=pipette_id, tip=tip
)
await self._movement_handler.move_to_well(
pipette_id=pipette_id,
labware_id=FIXED_TRASH_ID,
Expand All @@ -90,7 +92,9 @@ async def _drop_tip(self) -> None:
)
elif self._state_store.config.robot_type == "OT-2 Standard":
# API 2.16 and above OT2 protocols use addressable areas
await self._tip_handler.add_tip(pipette_id=pipette_id, tip=tip)
await self._tip_handler.cache_tip(
pipette_id=pipette_id, tip=tip
)
await self._movement_handler.move_to_addressable_area(
pipette_id=pipette_id,
addressable_area_name="fixedTrash",
Expand Down
34 changes: 13 additions & 21 deletions api/src/opentrons/protocol_engine/execution/tip_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ async def drop_tip(self, pipette_id: str, home_after: Optional[bool]) -> None:
TipAttachedError
"""

async def add_tip(self, pipette_id: str, tip: TipGeometry) -> None:
async def cache_tip(self, pipette_id: str, tip: TipGeometry) -> None:
"""Tell the Hardware API that a tip is attached."""

async def get_tip_presence(self, pipette_id: str) -> TipPresenceStatus:
Expand Down Expand Up @@ -234,31 +234,23 @@ async def pick_up_tip(
labware_definition=self._state_view.labware.get_definition(labware_id),
nominal_fallback=nominal_tip_geometry.length,
)
tip_geometry = TipGeometry(
length=actual_tip_length,
diameter=nominal_tip_geometry.diameter,
volume=nominal_tip_geometry.volume,
)

await self._hardware_api.tip_pickup_moves(
mount=hw_mount, presses=None, increment=None
)
# Allow TipNotAttachedError to propagate.
await self.verify_tip_presence(pipette_id, TipPresenceStatus.PRESENT)

self._hardware_api.cache_tip(hw_mount, actual_tip_length)
await self._hardware_api.prepare_for_aspirate(hw_mount)

self._hardware_api.set_current_tiprack_diameter(
mount=hw_mount,
tiprack_diameter=nominal_tip_geometry.diameter,
)
await self.cache_tip(pipette_id, tip_geometry)

self._hardware_api.set_working_volume(
mount=hw_mount,
tip_volume=nominal_tip_geometry.volume,
)
await self._hardware_api.prepare_for_aspirate(hw_mount)

return TipGeometry(
length=actual_tip_length,
diameter=nominal_tip_geometry.diameter,
volume=nominal_tip_geometry.volume,
)
return tip_geometry

async def drop_tip(self, pipette_id: str, home_after: Optional[bool]) -> None:
"""See documentation on abstract base class."""
Expand All @@ -279,11 +271,11 @@ async def drop_tip(self, pipette_id: str, home_after: Optional[bool]) -> None:
self._hardware_api.remove_tip(hw_mount)
self._hardware_api.set_current_tiprack_diameter(hw_mount, 0)

async def add_tip(self, pipette_id: str, tip: TipGeometry) -> None:
async def cache_tip(self, pipette_id: str, tip: TipGeometry) -> None:
"""See documentation on abstract base class."""
hw_mount = self._state_view.pipettes.get_mount(pipette_id).to_hw_mount()

self._hardware_api.add_tip(mount=hw_mount, tip_length=tip.length)
self._hardware_api.cache_tip(mount=hw_mount, tip_length=tip.length)

self._hardware_api.set_current_tiprack_diameter(
mount=hw_mount,
Expand Down Expand Up @@ -422,12 +414,12 @@ async def drop_tip(
expected_has_tip=True,
)

async def add_tip(self, pipette_id: str, tip: TipGeometry) -> None:
async def cache_tip(self, pipette_id: str, tip: TipGeometry) -> None:
"""Add a tip using a virtual pipette.
This should not be called when using virtual pipettes.
"""
assert False, "TipHandler.add_tip should not be used with virtual pipettes"
assert False, "TipHandler.cache_tip should not be used with virtual pipettes"

async def verify_tip_presence(
self,
Expand Down
8 changes: 6 additions & 2 deletions api/src/opentrons/util/logging_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

from opentrons.config import CONFIG, ARCHITECTURE, SystemArchitecture

from opentrons_hardware.sensors import SENSOR_LOG_NAME
if ARCHITECTURE is SystemArchitecture.BUILDROOT:
from opentrons_hardware.sensors import SENSOR_LOG_NAME
else:
# we don't use the sensor log on ot2 or host
SENSOR_LOG_NAME = "unused"


def _host_config(level_value: int) -> Dict[str, Any]:
Expand Down Expand Up @@ -125,7 +129,7 @@ def _buildroot_config(level_value: int) -> Dict[str, Any]:
},
"sensor": {
"class": "logging.handlers.RotatingFileHandler",
"formatter": "basic",
"formatter": "message_only",
"filename": sensor_log_filename,
"maxBytes": 1000000,
"level": logging.DEBUG,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ async def test_hardware_stopping_sequence_no_tip_drop(
decoy.verify(await hardware_api.stop(home_after=False), times=1)

decoy.verify(
await mock_tip_handler.add_tip(
await mock_tip_handler.cache_tip(
pipette_id="pipette-id",
tip=TipGeometry(length=1.0, volume=2.0, diameter=3.0),
),
Expand All @@ -181,7 +181,7 @@ async def test_hardware_stopping_sequence_no_pipette(
)

decoy.when(
await mock_tip_handler.add_tip(
await mock_tip_handler.cache_tip(
pipette_id="pipette-id",
tip=TipGeometry(length=1.0, volume=2.0, diameter=3.0),
),
Expand Down Expand Up @@ -271,7 +271,7 @@ async def test_hardware_stopping_sequence_with_fixed_trash(
await movement.home(
axes=[MotorAxis.X, MotorAxis.Y, MotorAxis.LEFT_Z, MotorAxis.RIGHT_Z]
),
await mock_tip_handler.add_tip(
await mock_tip_handler.cache_tip(
pipette_id="pipette-id",
tip=TipGeometry(length=1.0, volume=2.0, diameter=3.0),
),
Expand Down Expand Up @@ -320,7 +320,7 @@ async def test_hardware_stopping_sequence_with_OT2_addressable_area(
await movement.home(
axes=[MotorAxis.X, MotorAxis.Y, MotorAxis.LEFT_Z, MotorAxis.RIGHT_Z]
),
await mock_tip_handler.add_tip(
await mock_tip_handler.cache_tip(
pipette_id="pipette-id",
tip=TipGeometry(length=1.0, volume=2.0, diameter=3.0),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,10 @@ async def test_add_tip(
MountType.LEFT
)

await subject.add_tip(pipette_id="pipette-id", tip=tip)
await subject.cache_tip(pipette_id="pipette-id", tip=tip)

decoy.verify(
mock_hardware_api.add_tip(mount=Mount.LEFT, tip_length=50),
mock_hardware_api.cache_tip(mount=Mount.LEFT, tip_length=50),
mock_hardware_api.set_current_tiprack_diameter(
mount=Mount.LEFT,
tiprack_diameter=5,
Expand Down
2 changes: 1 addition & 1 deletion app/src/assets/localization/en/pipette_wizard_flows.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"install_probe": "Take the calibration probe from its storage location. Ensure its collar is unlocked. Push the pipette ejector up and press the probe firmly onto the <bold>{{location}}</bold> pipette nozzle. Twist the collar to lock the probe. Test that the probe is secure by gently pulling it back and forth.",
"loose_detach": "Loosen screws and detach ",
"move_gantry_to_front": "Move gantry to front",
"must_detach_mounting_plate": "You must detach the mounting plate and reattach the z-axis carraige before using other pipettes. We do not recommend exiting this process before completion.",
"must_detach_mounting_plate": "You must detach the mounting plate and reattach the z-axis carriage before using other pipettes. We do not recommend exiting this process before completion.",
"name_and_volume_detected": "{{name}} Pipette Detected",
"next": "next",
"ninety_six_channel": "{{ninetySix}} pipette",
Expand Down
Loading

0 comments on commit 39fb23c

Please sign in to comment.