opentrons 8.2.0a3__py2.py3-none-any.whl → 8.3.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of opentrons might be problematic. Click here for more details.
- opentrons/calibration_storage/deck_configuration.py +3 -3
- opentrons/calibration_storage/file_operators.py +3 -3
- opentrons/calibration_storage/helpers.py +3 -1
- opentrons/calibration_storage/ot2/models/v1.py +16 -29
- opentrons/calibration_storage/ot2/tip_length.py +7 -4
- opentrons/calibration_storage/ot3/models/v1.py +14 -23
- opentrons/cli/analyze.py +18 -6
- opentrons/config/defaults_ot3.py +1 -0
- opentrons/drivers/asyncio/communication/__init__.py +2 -0
- opentrons/drivers/asyncio/communication/errors.py +16 -3
- opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
- opentrons/drivers/command_builder.py +2 -2
- opentrons/drivers/flex_stacker/__init__.py +9 -0
- opentrons/drivers/flex_stacker/abstract.py +89 -0
- opentrons/drivers/flex_stacker/driver.py +260 -0
- opentrons/drivers/flex_stacker/simulator.py +109 -0
- opentrons/drivers/flex_stacker/types.py +138 -0
- opentrons/drivers/heater_shaker/driver.py +18 -3
- opentrons/drivers/temp_deck/driver.py +13 -3
- opentrons/drivers/thermocycler/driver.py +17 -3
- opentrons/execute.py +3 -1
- opentrons/hardware_control/__init__.py +1 -2
- opentrons/hardware_control/api.py +33 -21
- opentrons/hardware_control/backends/flex_protocol.py +17 -7
- opentrons/hardware_control/backends/ot3controller.py +213 -63
- opentrons/hardware_control/backends/ot3simulator.py +18 -9
- opentrons/hardware_control/backends/ot3utils.py +43 -15
- opentrons/hardware_control/dev_types.py +4 -0
- opentrons/hardware_control/emulation/heater_shaker.py +4 -0
- opentrons/hardware_control/emulation/module_server/client.py +1 -1
- opentrons/hardware_control/emulation/module_server/server.py +5 -3
- opentrons/hardware_control/emulation/settings.py +3 -4
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
- opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
- opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
- opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
- opentrons/hardware_control/modules/mod_abc.py +2 -2
- opentrons/hardware_control/motion_utilities.py +68 -0
- opentrons/hardware_control/nozzle_manager.py +39 -41
- opentrons/hardware_control/ot3_calibration.py +1 -1
- opentrons/hardware_control/ot3api.py +78 -31
- opentrons/hardware_control/protocols/gripper_controller.py +3 -0
- opentrons/hardware_control/protocols/hardware_manager.py +5 -1
- opentrons/hardware_control/protocols/liquid_handler.py +22 -1
- opentrons/hardware_control/protocols/motion_controller.py +7 -0
- opentrons/hardware_control/robot_calibration.py +1 -1
- opentrons/hardware_control/types.py +61 -0
- opentrons/legacy_commands/commands.py +37 -0
- opentrons/legacy_commands/types.py +39 -0
- opentrons/protocol_api/__init__.py +20 -1
- opentrons/protocol_api/_liquid.py +24 -49
- opentrons/protocol_api/_liquid_properties.py +754 -0
- opentrons/protocol_api/_types.py +24 -0
- opentrons/protocol_api/core/common.py +2 -0
- opentrons/protocol_api/core/engine/instrument.py +191 -10
- opentrons/protocol_api/core/engine/labware.py +29 -7
- opentrons/protocol_api/core/engine/protocol.py +130 -5
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/well.py +4 -1
- opentrons/protocol_api/core/instrument.py +73 -4
- opentrons/protocol_api/core/labware.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +87 -3
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
- opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +61 -3
- opentrons/protocol_api/core/protocol.py +34 -1
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/instrument_context.py +299 -44
- opentrons/protocol_api/labware.py +248 -9
- opentrons/protocol_api/module_contexts.py +21 -17
- opentrons/protocol_api/protocol_context.py +125 -4
- opentrons/protocol_api/robot_context.py +204 -32
- opentrons/protocol_api/validation.py +262 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/actions.py +2 -3
- opentrons/protocol_engine/clients/sync_client.py +18 -0
- opentrons/protocol_engine/commands/__init__.py +121 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -3
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +20 -6
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -2
- opentrons/protocol_engine/commands/absorbance_reader/read.py +40 -10
- opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
- opentrons/protocol_engine/commands/aspirate.py +103 -53
- opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
- opentrons/protocol_engine/commands/blow_out.py +44 -39
- opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
- opentrons/protocol_engine/commands/command.py +73 -66
- opentrons/protocol_engine/commands/command_unions.py +140 -1
- opentrons/protocol_engine/commands/comment.py +1 -1
- opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
- opentrons/protocol_engine/commands/custom.py +6 -12
- opentrons/protocol_engine/commands/dispense.py +82 -48
- opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
- opentrons/protocol_engine/commands/drop_tip.py +52 -31
- opentrons/protocol_engine/commands/drop_tip_in_place.py +79 -8
- opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
- opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
- opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/home.py +13 -4
- opentrons/protocol_engine/commands/liquid_probe.py +125 -31
- opentrons/protocol_engine/commands/load_labware.py +33 -6
- opentrons/protocol_engine/commands/load_lid.py +146 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
- opentrons/protocol_engine/commands/load_liquid.py +12 -4
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +31 -10
- opentrons/protocol_engine/commands/load_pipette.py +19 -8
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
- opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
- opentrons/protocol_engine/commands/move_labware.py +28 -6
- opentrons/protocol_engine/commands/move_relative.py +35 -25
- opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
- opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
- opentrons/protocol_engine/commands/move_to_well.py +40 -24
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
- opentrons/protocol_engine/commands/pipetting_common.py +169 -87
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
- opentrons/protocol_engine/commands/reload_labware.py +1 -1
- opentrons/protocol_engine/commands/retract_axis.py +1 -1
- opentrons/protocol_engine/commands/robot/__init__.py +69 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
- opentrons/protocol_engine/commands/robot/common.py +18 -0
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
- opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
- opentrons/protocol_engine/commands/robot/move_to.py +94 -0
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
- opentrons/protocol_engine/commands/save_position.py +14 -5
- opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
- opentrons/protocol_engine/commands/set_status_bar.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/touch_tip.py +65 -16
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +5 -2
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +13 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +2 -5
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +4 -2
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +2 -5
- opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
- opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
- opentrons/protocol_engine/errors/__init__.py +12 -0
- opentrons/protocol_engine/errors/error_occurrence.py +19 -20
- opentrons/protocol_engine/errors/exceptions.py +76 -0
- opentrons/protocol_engine/execution/command_executor.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +73 -5
- opentrons/protocol_engine/execution/gantry_mover.py +369 -8
- opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
- opentrons/protocol_engine/execution/movement.py +27 -0
- opentrons/protocol_engine/execution/pipetting.py +5 -1
- opentrons/protocol_engine/execution/tip_handler.py +34 -15
- opentrons/protocol_engine/notes/notes.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +7 -6
- opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
- opentrons/protocol_engine/resources/labware_validation.py +18 -0
- opentrons/protocol_engine/resources/module_data_provider.py +1 -1
- opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
- opentrons/protocol_engine/slot_standardization.py +9 -9
- opentrons/protocol_engine/state/_move_types.py +9 -5
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +25 -61
- opentrons/protocol_engine/state/command_history.py +12 -0
- opentrons/protocol_engine/state/commands.py +22 -14
- opentrons/protocol_engine/state/files.py +10 -12
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/frustum_helpers.py +63 -69
- opentrons/protocol_engine/state/geometry.py +47 -1
- opentrons/protocol_engine/state/labware.py +92 -26
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +16 -4
- opentrons/protocol_engine/state/modules.py +56 -71
- opentrons/protocol_engine/state/motion.py +6 -1
- opentrons/protocol_engine/state/pipettes.py +149 -58
- opentrons/protocol_engine/state/state.py +21 -2
- opentrons/protocol_engine/state/state_summary.py +4 -2
- opentrons/protocol_engine/state/tips.py +11 -44
- opentrons/protocol_engine/state/update_types.py +343 -48
- opentrons/protocol_engine/state/wells.py +19 -11
- opentrons/protocol_engine/types.py +176 -28
- opentrons/protocol_reader/extract_labware_definitions.py +5 -2
- opentrons/protocol_reader/file_format_validator.py +5 -5
- opentrons/protocol_runner/json_file_reader.py +9 -3
- opentrons/protocol_runner/json_translator.py +51 -25
- opentrons/protocol_runner/legacy_command_mapper.py +66 -64
- opentrons/protocol_runner/protocol_runner.py +35 -4
- opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
- opentrons/protocol_runner/run_orchestrator.py +13 -3
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +1 -1
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +56 -0
- opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/instrument.py +1 -1
- opentrons/protocols/api_support/util.py +10 -0
- opentrons/protocols/labware.py +70 -8
- opentrons/protocols/models/json_protocol.py +5 -9
- opentrons/simulate.py +3 -1
- opentrons/types.py +162 -2
- opentrons/util/entrypoint_util.py +2 -5
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
|
@@ -11,6 +11,7 @@ from opentrons.drivers.asyncio.communication import (
|
|
|
11
11
|
SerialConnection,
|
|
12
12
|
AsyncResponseSerialConnection,
|
|
13
13
|
AsyncSerial,
|
|
14
|
+
UnhandledGcode,
|
|
14
15
|
)
|
|
15
16
|
from opentrons.drivers.thermocycler.abstract import AbstractThermocyclerDriver
|
|
16
17
|
from opentrons.drivers.types import Temperature, PlateTemperature, ThermocyclerLidStatus
|
|
@@ -33,6 +34,7 @@ class GCODE(str, Enum):
|
|
|
33
34
|
DEACTIVATE_LID = "M108"
|
|
34
35
|
DEACTIVATE_BLOCK = "M14"
|
|
35
36
|
DEVICE_INFO = "M115"
|
|
37
|
+
GET_RESET_REASON = "M114"
|
|
36
38
|
ENTER_PROGRAMMING = "dfu"
|
|
37
39
|
|
|
38
40
|
|
|
@@ -94,7 +96,7 @@ class ThermocyclerDriverFactory:
|
|
|
94
96
|
name=port,
|
|
95
97
|
ack=TC_GEN2_SERIAL_ACK,
|
|
96
98
|
retry_wait_time_seconds=0.1,
|
|
97
|
-
error_keyword="
|
|
99
|
+
error_keyword="err",
|
|
98
100
|
alarm_keyword="alarm",
|
|
99
101
|
)
|
|
100
102
|
|
|
@@ -292,12 +294,13 @@ class ThermocyclerDriver(AbstractThermocyclerDriver):
|
|
|
292
294
|
|
|
293
295
|
async def get_device_info(self) -> Dict[str, str]:
|
|
294
296
|
"""Send get device info command"""
|
|
295
|
-
|
|
297
|
+
device_info = CommandBuilder(terminator=TC_COMMAND_TERMINATOR).add_gcode(
|
|
296
298
|
gcode=GCODE.DEVICE_INFO
|
|
297
299
|
)
|
|
298
300
|
response = await self._connection.send_command(
|
|
299
|
-
command=
|
|
301
|
+
command=device_info, retries=DEFAULT_COMMAND_RETRIES
|
|
300
302
|
)
|
|
303
|
+
|
|
301
304
|
return utils.parse_device_information(device_info_string=response)
|
|
302
305
|
|
|
303
306
|
async def enter_programming_mode(self) -> None:
|
|
@@ -353,6 +356,17 @@ class ThermocyclerDriverV2(ThermocyclerDriver):
|
|
|
353
356
|
response = await self._connection.send_command(
|
|
354
357
|
command=c, retries=DEFAULT_COMMAND_RETRIES
|
|
355
358
|
)
|
|
359
|
+
|
|
360
|
+
reset_reason = CommandBuilder(terminator=TC_COMMAND_TERMINATOR).add_gcode(
|
|
361
|
+
gcode=GCODE.GET_RESET_REASON
|
|
362
|
+
)
|
|
363
|
+
try:
|
|
364
|
+
await self._connection.send_command(
|
|
365
|
+
command=reset_reason, retries=DEFAULT_COMMAND_RETRIES
|
|
366
|
+
)
|
|
367
|
+
except UnhandledGcode:
|
|
368
|
+
pass
|
|
369
|
+
|
|
356
370
|
return utils.parse_hs_device_information(device_info_string=response)
|
|
357
371
|
|
|
358
372
|
async def enter_programming_mode(self) -> None:
|
opentrons/execute.py
CHANGED
|
@@ -560,7 +560,9 @@ def _create_live_context_pe(
|
|
|
560
560
|
# Non-async would use call_soon_threadsafe(), which makes the waiting harder.
|
|
561
561
|
async def add_all_extra_labware() -> None:
|
|
562
562
|
for labware_definition_dict in extra_labware.values():
|
|
563
|
-
labware_definition = LabwareDefinition.
|
|
563
|
+
labware_definition = LabwareDefinition.model_validate(
|
|
564
|
+
labware_definition_dict
|
|
565
|
+
)
|
|
564
566
|
pe.add_labware_definition(labware_definition)
|
|
565
567
|
|
|
566
568
|
# Add extra_labware to ProtocolEngine, being careful not to modify ProtocolEngine from this
|
|
@@ -38,8 +38,7 @@ OT3HardwareControlAPI = FlexHardwareControlInterface[
|
|
|
38
38
|
]
|
|
39
39
|
HardwareControlAPI = Union[OT2HardwareControlAPI, OT3HardwareControlAPI]
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
ThreadManagedHardware = ThreadManager[HardwareControlAPI] # type: ignore[misc]
|
|
41
|
+
ThreadManagedHardware = ThreadManager[HardwareControlAPI]
|
|
43
42
|
SyncHardwareAPI = SynchronousAdapter[HardwareControlAPI]
|
|
44
43
|
|
|
45
44
|
__all__ = [
|
|
@@ -169,6 +169,16 @@ class API(
|
|
|
169
169
|
def _reset_last_mount(self) -> None:
|
|
170
170
|
self._last_moved_mount = None
|
|
171
171
|
|
|
172
|
+
def get_deck_from_machine(
|
|
173
|
+
self, machine_pos: Dict[Axis, float]
|
|
174
|
+
) -> Dict[Axis, float]:
|
|
175
|
+
return deck_from_machine(
|
|
176
|
+
machine_pos=machine_pos,
|
|
177
|
+
attitude=self._robot_calibration.deck_calibration.attitude,
|
|
178
|
+
offset=top_types.Point(0, 0, 0),
|
|
179
|
+
robot_type=cast(RobotType, "OT-2 Standard"),
|
|
180
|
+
)
|
|
181
|
+
|
|
172
182
|
@classmethod
|
|
173
183
|
async def build_hardware_controller( # noqa: C901
|
|
174
184
|
cls,
|
|
@@ -657,11 +667,8 @@ class API(
|
|
|
657
667
|
async with self._motion_lock:
|
|
658
668
|
if smoothie_gantry:
|
|
659
669
|
smoothie_pos.update(await self._backend.home(smoothie_gantry))
|
|
660
|
-
self._current_position =
|
|
661
|
-
|
|
662
|
-
attitude=self._robot_calibration.deck_calibration.attitude,
|
|
663
|
-
offset=top_types.Point(0, 0, 0),
|
|
664
|
-
robot_type=cast(RobotType, "OT-2 Standard"),
|
|
670
|
+
self._current_position = self.get_deck_from_machine(
|
|
671
|
+
self._axis_map_from_string_map(smoothie_pos)
|
|
665
672
|
)
|
|
666
673
|
for plunger in plungers:
|
|
667
674
|
await self._do_plunger_home(axis=plunger, acquire_lock=False)
|
|
@@ -703,11 +710,8 @@ class API(
|
|
|
703
710
|
async with self._motion_lock:
|
|
704
711
|
if refresh:
|
|
705
712
|
smoothie_pos = await self._backend.update_position()
|
|
706
|
-
self._current_position =
|
|
707
|
-
|
|
708
|
-
attitude=self._robot_calibration.deck_calibration.attitude,
|
|
709
|
-
offset=top_types.Point(0, 0, 0),
|
|
710
|
-
robot_type=cast(RobotType, "OT-2 Standard"),
|
|
713
|
+
self._current_position = self.get_deck_from_machine(
|
|
714
|
+
self._axis_map_from_string_map(smoothie_pos)
|
|
711
715
|
)
|
|
712
716
|
if mount == top_types.Mount.RIGHT:
|
|
713
717
|
offset = top_types.Point(0, 0, 0)
|
|
@@ -774,6 +778,7 @@ class API(
|
|
|
774
778
|
position: Mapping[Axis, float],
|
|
775
779
|
speed: Optional[float] = None,
|
|
776
780
|
max_speeds: Optional[Dict[Axis, float]] = None,
|
|
781
|
+
expect_stalls: bool = False,
|
|
777
782
|
) -> None:
|
|
778
783
|
"""Moves the effectors of the specified axis to the specified position.
|
|
779
784
|
The effector of the x,y axis is the center of the carriage.
|
|
@@ -917,6 +922,16 @@ class API(
|
|
|
917
922
|
async def disengage_axes(self, which: List[Axis]) -> None:
|
|
918
923
|
await self._backend.disengage_axes([ot2_axis_to_string(ax) for ax in which])
|
|
919
924
|
|
|
925
|
+
def axis_is_present(self, axis: Axis) -> bool:
|
|
926
|
+
is_ot2 = axis in Axis.ot2_axes()
|
|
927
|
+
if not is_ot2:
|
|
928
|
+
return False
|
|
929
|
+
if axis in Axis.pipette_axes():
|
|
930
|
+
mount = Axis.to_ot2_mount(axis)
|
|
931
|
+
if self.attached_pipettes.get(mount) is None:
|
|
932
|
+
return False
|
|
933
|
+
return True
|
|
934
|
+
|
|
920
935
|
@ExecutionManagerProvider.wait_for_running
|
|
921
936
|
async def _fast_home(self, axes: Sequence[str], margin: float) -> Dict[str, float]:
|
|
922
937
|
converted_axes = "".join(axes)
|
|
@@ -938,11 +953,8 @@ class API(
|
|
|
938
953
|
|
|
939
954
|
async with self._motion_lock:
|
|
940
955
|
smoothie_pos = await self._fast_home(smoothie_ax, margin)
|
|
941
|
-
self._current_position =
|
|
942
|
-
|
|
943
|
-
attitude=self._robot_calibration.deck_calibration.attitude,
|
|
944
|
-
offset=top_types.Point(0, 0, 0),
|
|
945
|
-
robot_type=cast(RobotType, "OT-2 Standard"),
|
|
956
|
+
self._current_position = self.get_deck_from_machine(
|
|
957
|
+
self._axis_map_from_string_map(smoothie_pos)
|
|
946
958
|
)
|
|
947
959
|
|
|
948
960
|
# Gantry/frame (i.e. not pipette) config API
|
|
@@ -1237,7 +1249,10 @@ class API(
|
|
|
1237
1249
|
await self.prepare_for_aspirate(mount)
|
|
1238
1250
|
|
|
1239
1251
|
async def tip_drop_moves(
|
|
1240
|
-
self,
|
|
1252
|
+
self,
|
|
1253
|
+
mount: top_types.Mount,
|
|
1254
|
+
home_after: bool = True,
|
|
1255
|
+
ignore_plunger: bool = False,
|
|
1241
1256
|
) -> None:
|
|
1242
1257
|
spec, _ = self.plan_check_drop_tip(mount, home_after)
|
|
1243
1258
|
|
|
@@ -1256,11 +1271,8 @@ class API(
|
|
|
1256
1271
|
axes=[ot2_axis_to_string(ax) for ax in move.home_axes],
|
|
1257
1272
|
margin=move.home_after_safety_margin,
|
|
1258
1273
|
)
|
|
1259
|
-
self._current_position =
|
|
1260
|
-
|
|
1261
|
-
attitude=self._robot_calibration.deck_calibration.attitude,
|
|
1262
|
-
offset=top_types.Point(0, 0, 0),
|
|
1263
|
-
robot_type=cast(RobotType, "OT-2 Standard"),
|
|
1274
|
+
self._current_position = self.get_deck_from_machine(
|
|
1275
|
+
self._axis_map_from_string_map(smoothie_pos)
|
|
1264
1276
|
)
|
|
1265
1277
|
|
|
1266
1278
|
for shake in spec.shake_moves:
|
|
@@ -36,10 +36,9 @@ from opentrons.hardware_control.types import (
|
|
|
36
36
|
HepaFanState,
|
|
37
37
|
HepaUVState,
|
|
38
38
|
StatusBarState,
|
|
39
|
+
PipetteSensorResponseQueue,
|
|
39
40
|
)
|
|
40
41
|
from opentrons.hardware_control.module_control import AttachedModulesControl
|
|
41
|
-
from opentrons_hardware.firmware_bindings.constants import SensorId
|
|
42
|
-
from opentrons_hardware.sensors.types import SensorDataType
|
|
43
42
|
from ..dev_types import OT3AttachedInstruments
|
|
44
43
|
from .types import HWStopCondition
|
|
45
44
|
|
|
@@ -60,6 +59,14 @@ class FlexBackend(Protocol):
|
|
|
60
59
|
def grab_pressure(self, channels: int, mount: OT3Mount) -> AsyncIterator[None]:
|
|
61
60
|
...
|
|
62
61
|
|
|
62
|
+
def set_pressure_sensor_available(
|
|
63
|
+
self, pipette_axis: Axis, available: bool
|
|
64
|
+
) -> None:
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
def get_pressure_sensor_available(self, pipette_axis: Axis) -> bool:
|
|
68
|
+
...
|
|
69
|
+
|
|
63
70
|
def update_constraints_for_gantry_load(self, gantry_load: GantryLoad) -> None:
|
|
64
71
|
...
|
|
65
72
|
|
|
@@ -70,7 +77,11 @@ class FlexBackend(Protocol):
|
|
|
70
77
|
...
|
|
71
78
|
|
|
72
79
|
def update_constraints_for_plunger_acceleration(
|
|
73
|
-
self,
|
|
80
|
+
self,
|
|
81
|
+
mount: OT3Mount,
|
|
82
|
+
acceleration: float,
|
|
83
|
+
gantry_load: GantryLoad,
|
|
84
|
+
high_speed_pipette: bool = False,
|
|
74
85
|
) -> None:
|
|
75
86
|
...
|
|
76
87
|
|
|
@@ -154,11 +165,10 @@ class FlexBackend(Protocol):
|
|
|
154
165
|
threshold_pascals: float,
|
|
155
166
|
plunger_impulse_time: float,
|
|
156
167
|
num_baseline_reads: int,
|
|
168
|
+
z_offset_for_plunger_prep: float,
|
|
157
169
|
probe: InstrumentProbeType = InstrumentProbeType.PRIMARY,
|
|
158
170
|
force_both_sensors: bool = False,
|
|
159
|
-
response_queue: Optional[
|
|
160
|
-
asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
|
|
161
|
-
] = None,
|
|
171
|
+
response_queue: Optional[PipetteSensorResponseQueue] = None,
|
|
162
172
|
) -> float:
|
|
163
173
|
...
|
|
164
174
|
|
|
@@ -221,7 +231,7 @@ class FlexBackend(Protocol):
|
|
|
221
231
|
...
|
|
222
232
|
|
|
223
233
|
async def tip_action(
|
|
224
|
-
self, origin:
|
|
234
|
+
self, origin: float, targets: List[Tuple[float, float]]
|
|
225
235
|
) -> None:
|
|
226
236
|
...
|
|
227
237
|
|
|
@@ -104,7 +104,6 @@ from opentrons_hardware.firmware_bindings.constants import (
|
|
|
104
104
|
ErrorCode,
|
|
105
105
|
SensorId,
|
|
106
106
|
)
|
|
107
|
-
from opentrons_hardware.sensors.types import SensorDataType
|
|
108
107
|
from opentrons_hardware.firmware_bindings.messages.message_definitions import (
|
|
109
108
|
StopRequest,
|
|
110
109
|
)
|
|
@@ -142,6 +141,10 @@ from opentrons.hardware_control.types import (
|
|
|
142
141
|
EstopState,
|
|
143
142
|
HardwareEventHandler,
|
|
144
143
|
HardwareEventUnsubscriber,
|
|
144
|
+
PipetteSensorId,
|
|
145
|
+
PipetteSensorType,
|
|
146
|
+
PipetteSensorData,
|
|
147
|
+
PipetteSensorResponseQueue,
|
|
145
148
|
)
|
|
146
149
|
from opentrons.hardware_control.errors import (
|
|
147
150
|
InvalidPipetteName,
|
|
@@ -197,6 +200,7 @@ from opentrons_shared_data.errors.exceptions import (
|
|
|
197
200
|
PipetteLiquidNotFoundError,
|
|
198
201
|
CommunicationError,
|
|
199
202
|
PythonException,
|
|
203
|
+
UnsupportedHardwareCommand,
|
|
200
204
|
)
|
|
201
205
|
|
|
202
206
|
from .subsystem_manager import SubsystemManager
|
|
@@ -211,6 +215,7 @@ from ..types import HepaFanState, HepaUVState, StatusBarState
|
|
|
211
215
|
from .types import HWStopCondition
|
|
212
216
|
from .flex_protocol import FlexBackend
|
|
213
217
|
from .status_bar_state import StatusBarStateController
|
|
218
|
+
from opentrons_hardware.sensors.types import SensorDataType
|
|
214
219
|
|
|
215
220
|
log = logging.getLogger(__name__)
|
|
216
221
|
|
|
@@ -362,6 +367,7 @@ class OT3Controller(FlexBackend):
|
|
|
362
367
|
self._configuration.motion_settings, GantryLoad.LOW_THROUGHPUT
|
|
363
368
|
)
|
|
364
369
|
)
|
|
370
|
+
self._pressure_sensor_available: Dict[NodeId, bool] = {}
|
|
365
371
|
|
|
366
372
|
@asynccontextmanager
|
|
367
373
|
async def restore_system_constraints(self) -> AsyncIterator[None]:
|
|
@@ -380,6 +386,16 @@ class OT3Controller(FlexBackend):
|
|
|
380
386
|
async with grab_pressure(channels, tool, self._messenger):
|
|
381
387
|
yield
|
|
382
388
|
|
|
389
|
+
def set_pressure_sensor_available(
|
|
390
|
+
self, pipette_axis: Axis, available: bool
|
|
391
|
+
) -> None:
|
|
392
|
+
pip_node = axis_to_node(pipette_axis)
|
|
393
|
+
self._pressure_sensor_available[pip_node] = available
|
|
394
|
+
|
|
395
|
+
def get_pressure_sensor_available(self, pipette_axis: Axis) -> bool:
|
|
396
|
+
pip_node = axis_to_node(pipette_axis)
|
|
397
|
+
return self._pressure_sensor_available[pip_node]
|
|
398
|
+
|
|
383
399
|
def update_constraints_for_calibration_with_gantry_load(
|
|
384
400
|
self,
|
|
385
401
|
gantry_load: GantryLoad,
|
|
@@ -399,10 +415,18 @@ class OT3Controller(FlexBackend):
|
|
|
399
415
|
)
|
|
400
416
|
|
|
401
417
|
def update_constraints_for_plunger_acceleration(
|
|
402
|
-
self,
|
|
418
|
+
self,
|
|
419
|
+
mount: OT3Mount,
|
|
420
|
+
acceleration: float,
|
|
421
|
+
gantry_load: GantryLoad,
|
|
422
|
+
high_speed_pipette: bool = False,
|
|
403
423
|
) -> None:
|
|
404
424
|
new_constraints = get_system_constraints_for_plunger_acceleration(
|
|
405
|
-
self._configuration.motion_settings,
|
|
425
|
+
self._configuration.motion_settings,
|
|
426
|
+
gantry_load,
|
|
427
|
+
mount,
|
|
428
|
+
acceleration,
|
|
429
|
+
high_speed_pipette,
|
|
406
430
|
)
|
|
407
431
|
self._move_manager.update_constraints(new_constraints)
|
|
408
432
|
|
|
@@ -597,30 +621,104 @@ class OT3Controller(FlexBackend):
|
|
|
597
621
|
return axis_convert(self._encoder_position, 0.0)
|
|
598
622
|
|
|
599
623
|
def _handle_motor_status_response(
|
|
600
|
-
self,
|
|
601
|
-
response: NodeMap[MotorPositionStatus],
|
|
624
|
+
self, response: NodeMap[MotorPositionStatus], handle_gear_move: bool = False
|
|
602
625
|
) -> None:
|
|
603
626
|
for axis, pos in response.items():
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
(not
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
motor_ok=(pos.motor_ok or motor_ok_latch),
|
|
620
|
-
encoder_ok=pos.encoder_ok,
|
|
627
|
+
if handle_gear_move and axis == NodeId.pipette_left:
|
|
628
|
+
self._gear_motor_position = {axis: pos.motor_position}
|
|
629
|
+
else:
|
|
630
|
+
self._position.update({axis: pos.motor_position})
|
|
631
|
+
self._encoder_position.update({axis: pos.encoder_position})
|
|
632
|
+
# TODO (FPS 6-01-2023): Remove this once the Feature Flag to ignore stall detection is removed.
|
|
633
|
+
# This check will latch the motor status for an axis at "true" if it was ever set to true.
|
|
634
|
+
# To account for the case where a motor axis has its power reset, we also depend on the
|
|
635
|
+
# "encoder_ok" flag staying set (it will only be False if the motor axis has not been
|
|
636
|
+
# homed since a power cycle)
|
|
637
|
+
motor_ok_latch = (
|
|
638
|
+
(not self._feature_flags.stall_detection_enabled)
|
|
639
|
+
and (
|
|
640
|
+
(axis in self._motor_status)
|
|
641
|
+
and self._motor_status[axis].motor_ok
|
|
621
642
|
)
|
|
622
|
-
|
|
643
|
+
and self._motor_status[axis].encoder_ok
|
|
644
|
+
)
|
|
645
|
+
self._motor_status.update(
|
|
646
|
+
{
|
|
647
|
+
axis: MotorStatus(
|
|
648
|
+
motor_ok=(pos.motor_ok or motor_ok_latch),
|
|
649
|
+
encoder_ok=pos.encoder_ok,
|
|
650
|
+
)
|
|
651
|
+
}
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
def _build_move_node_axis_runner(
|
|
655
|
+
self,
|
|
656
|
+
origin: Dict[Axis, float],
|
|
657
|
+
target: Dict[Axis, float],
|
|
658
|
+
speed: float,
|
|
659
|
+
stop_condition: HWStopCondition,
|
|
660
|
+
nodes_in_moves_only: bool,
|
|
661
|
+
) -> Tuple[Optional[MoveGroupRunner], bool]:
|
|
662
|
+
if not target:
|
|
663
|
+
return None, False
|
|
664
|
+
move_target = MoveTarget.build(position=target, max_speed=speed)
|
|
665
|
+
try:
|
|
666
|
+
_, movelist = self._move_manager.plan_motion(
|
|
667
|
+
origin=origin, target_list=[move_target]
|
|
623
668
|
)
|
|
669
|
+
except ZeroLengthMoveError as zme:
|
|
670
|
+
log.debug(f"Not moving because move was zero length {str(zme)}")
|
|
671
|
+
return None, False
|
|
672
|
+
moves = movelist[0]
|
|
673
|
+
log.debug(
|
|
674
|
+
f"move: machine coordinates {target} from origin: machine coordinates {origin} at speed: {speed} requires {moves}"
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
ordered_nodes = self._motor_nodes()
|
|
678
|
+
if nodes_in_moves_only:
|
|
679
|
+
moving_axes = {
|
|
680
|
+
axis_to_node(ax) for move in moves for ax in move.unit_vector.keys()
|
|
681
|
+
}
|
|
682
|
+
ordered_nodes = ordered_nodes.intersection(moving_axes)
|
|
683
|
+
|
|
684
|
+
move_group, _ = create_move_group(
|
|
685
|
+
origin, moves, ordered_nodes, MoveStopCondition[stop_condition.name]
|
|
686
|
+
)
|
|
687
|
+
return (
|
|
688
|
+
MoveGroupRunner(
|
|
689
|
+
move_groups=[move_group],
|
|
690
|
+
ignore_stalls=True
|
|
691
|
+
if not self._feature_flags.stall_detection_enabled
|
|
692
|
+
else False,
|
|
693
|
+
),
|
|
694
|
+
False,
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
def _build_move_gear_axis_runner(
|
|
698
|
+
self,
|
|
699
|
+
possible_q_axis_origin: Optional[float],
|
|
700
|
+
possible_q_axis_target: Optional[float],
|
|
701
|
+
speed: float,
|
|
702
|
+
nodes_in_moves_only: bool,
|
|
703
|
+
) -> Tuple[Optional[MoveGroupRunner], bool]:
|
|
704
|
+
if possible_q_axis_origin is None or possible_q_axis_target is None:
|
|
705
|
+
return None, True
|
|
706
|
+
tip_motor_move_group = self._build_tip_action_group(
|
|
707
|
+
possible_q_axis_origin, [(possible_q_axis_target, speed)]
|
|
708
|
+
)
|
|
709
|
+
if nodes_in_moves_only:
|
|
710
|
+
ordered_nodes = self._motor_nodes()
|
|
711
|
+
|
|
712
|
+
ordered_nodes.intersection({axis_to_node(Axis.Q)})
|
|
713
|
+
return (
|
|
714
|
+
MoveGroupRunner(
|
|
715
|
+
move_groups=[tip_motor_move_group],
|
|
716
|
+
ignore_stalls=True
|
|
717
|
+
if not self._feature_flags.stall_detection_enabled
|
|
718
|
+
else False,
|
|
719
|
+
),
|
|
720
|
+
True,
|
|
721
|
+
)
|
|
624
722
|
|
|
625
723
|
@requires_update
|
|
626
724
|
@requires_estop
|
|
@@ -648,40 +746,52 @@ class OT3Controller(FlexBackend):
|
|
|
648
746
|
Returns:
|
|
649
747
|
None
|
|
650
748
|
"""
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
_, movelist = self._move_manager.plan_motion(
|
|
654
|
-
origin=origin, target_list=[move_target]
|
|
655
|
-
)
|
|
656
|
-
except ZeroLengthMoveError as zme:
|
|
657
|
-
log.debug(f"Not moving because move was zero length {str(zme)}")
|
|
658
|
-
return
|
|
659
|
-
moves = movelist[0]
|
|
660
|
-
log.info(f"move: machine {target} from {origin} requires {moves}")
|
|
749
|
+
possible_q_axis_origin = origin.pop(Axis.Q, None)
|
|
750
|
+
possible_q_axis_target = target.pop(Axis.Q, None)
|
|
661
751
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
752
|
+
maybe_runners = (
|
|
753
|
+
self._build_move_node_axis_runner(
|
|
754
|
+
origin, target, speed, stop_condition, nodes_in_moves_only
|
|
755
|
+
),
|
|
756
|
+
self._build_move_gear_axis_runner(
|
|
757
|
+
possible_q_axis_origin,
|
|
758
|
+
possible_q_axis_target,
|
|
759
|
+
speed,
|
|
760
|
+
nodes_in_moves_only,
|
|
761
|
+
),
|
|
671
762
|
)
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
763
|
+
log.debug(f"The move groups are {maybe_runners}.")
|
|
764
|
+
|
|
765
|
+
gather_moving_nodes = set()
|
|
766
|
+
all_moving_nodes = set()
|
|
767
|
+
for runner, _ in maybe_runners:
|
|
768
|
+
if runner:
|
|
769
|
+
for n in runner.all_nodes():
|
|
770
|
+
gather_moving_nodes.add(n)
|
|
771
|
+
for n in runner.all_moving_nodes():
|
|
772
|
+
all_moving_nodes.add(n)
|
|
773
|
+
|
|
774
|
+
pipettes_moving = moving_pipettes_in_move_group(
|
|
775
|
+
gather_moving_nodes, all_moving_nodes
|
|
678
776
|
)
|
|
679
777
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
778
|
+
async def _runner_coroutine(
|
|
779
|
+
runner: MoveGroupRunner, is_gear_move: bool
|
|
780
|
+
) -> Tuple[Dict[NodeId, MotorPositionStatus], bool]:
|
|
683
781
|
positions = await runner.run(can_messenger=self._messenger)
|
|
684
|
-
|
|
782
|
+
return positions, is_gear_move
|
|
783
|
+
|
|
784
|
+
coros = [
|
|
785
|
+
_runner_coroutine(runner, is_gear_move)
|
|
786
|
+
for runner, is_gear_move in maybe_runners
|
|
787
|
+
if runner
|
|
788
|
+
]
|
|
789
|
+
checked_moving_pipettes = self._pipettes_to_monitor_pressure(pipettes_moving)
|
|
790
|
+
async with self._monitor_overpressure(checked_moving_pipettes):
|
|
791
|
+
all_positions = await asyncio.gather(*coros)
|
|
792
|
+
|
|
793
|
+
for positions, handle_gear_move in all_positions:
|
|
794
|
+
self._handle_motor_status_response(positions, handle_gear_move)
|
|
685
795
|
|
|
686
796
|
def _get_axis_home_distance(self, axis: Axis) -> float:
|
|
687
797
|
if self.check_motor_status([axis]):
|
|
@@ -786,7 +896,8 @@ class OT3Controller(FlexBackend):
|
|
|
786
896
|
moving_pipettes = [
|
|
787
897
|
axis_to_node(ax) for ax in checked_axes if ax in Axis.pipette_axes()
|
|
788
898
|
]
|
|
789
|
-
|
|
899
|
+
checked_moving_pipettes = self._pipettes_to_monitor_pressure(moving_pipettes)
|
|
900
|
+
async with self._monitor_overpressure(checked_moving_pipettes):
|
|
790
901
|
positions = await asyncio.gather(*coros)
|
|
791
902
|
# TODO(CM): default gear motor homing routine to have some acceleration
|
|
792
903
|
if Axis.Q in checked_axes:
|
|
@@ -796,10 +907,14 @@ class OT3Controller(FlexBackend):
|
|
|
796
907
|
Axis.to_kind(Axis.Q)
|
|
797
908
|
],
|
|
798
909
|
)
|
|
910
|
+
|
|
799
911
|
for position in positions:
|
|
800
912
|
self._handle_motor_status_response(position)
|
|
801
913
|
return axis_convert(self._position, 0.0)
|
|
802
914
|
|
|
915
|
+
def _pipettes_to_monitor_pressure(self, pipettes: List[NodeId]) -> List[NodeId]:
|
|
916
|
+
return [pip for pip in pipettes if self._pressure_sensor_available[pip]]
|
|
917
|
+
|
|
803
918
|
def _filter_move_group(self, move_group: MoveGroup) -> MoveGroup:
|
|
804
919
|
new_group: MoveGroup = []
|
|
805
920
|
for step in move_group:
|
|
@@ -840,17 +955,23 @@ class OT3Controller(FlexBackend):
|
|
|
840
955
|
self._gear_motor_position = {}
|
|
841
956
|
raise e
|
|
842
957
|
|
|
843
|
-
|
|
844
|
-
self, origin:
|
|
845
|
-
) ->
|
|
958
|
+
def _build_tip_action_group(
|
|
959
|
+
self, origin: float, targets: List[Tuple[float, float]]
|
|
960
|
+
) -> MoveGroup:
|
|
846
961
|
move_targets = [
|
|
847
|
-
MoveTarget.build(target_pos, speed)
|
|
962
|
+
MoveTarget.build({Axis.Q: target_pos}, speed)
|
|
963
|
+
for target_pos, speed in targets
|
|
848
964
|
]
|
|
849
965
|
_, moves = self._move_manager.plan_motion(
|
|
850
|
-
origin=origin, target_list=move_targets
|
|
966
|
+
origin={Axis.Q: origin}, target_list=move_targets
|
|
851
967
|
)
|
|
852
|
-
move_group = create_tip_action_group(moves[0], [NodeId.pipette_left], "clamp")
|
|
853
968
|
|
|
969
|
+
return create_tip_action_group(moves[0], [NodeId.pipette_left], "clamp")
|
|
970
|
+
|
|
971
|
+
async def tip_action(
|
|
972
|
+
self, origin: float, targets: List[Tuple[float, float]]
|
|
973
|
+
) -> None:
|
|
974
|
+
move_group = self._build_tip_action_group(origin, targets)
|
|
854
975
|
runner = MoveGroupRunner(
|
|
855
976
|
move_groups=[move_group],
|
|
856
977
|
ignore_stalls=True
|
|
@@ -915,10 +1036,12 @@ class OT3Controller(FlexBackend):
|
|
|
915
1036
|
lookup_name = {
|
|
916
1037
|
FirmwarePipetteName.p1000_single: "P1KS",
|
|
917
1038
|
FirmwarePipetteName.p1000_multi: "P1KM",
|
|
1039
|
+
FirmwarePipetteName.p1000_multi_em: "P1KP",
|
|
918
1040
|
FirmwarePipetteName.p50_single: "P50S",
|
|
919
1041
|
FirmwarePipetteName.p50_multi: "P50M",
|
|
920
1042
|
FirmwarePipetteName.p1000_96: "P1KH",
|
|
921
1043
|
FirmwarePipetteName.p50_96: "P50H",
|
|
1044
|
+
FirmwarePipetteName.p200_96: "P2HH",
|
|
922
1045
|
}
|
|
923
1046
|
return lookup_name[pipette_name]
|
|
924
1047
|
|
|
@@ -949,6 +1072,7 @@ class OT3Controller(FlexBackend):
|
|
|
949
1072
|
converted_name.pipette_type,
|
|
950
1073
|
converted_name.pipette_channels,
|
|
951
1074
|
converted_name.pipette_version,
|
|
1075
|
+
converted_name.oem_type,
|
|
952
1076
|
),
|
|
953
1077
|
"id": OT3Controller._combine_serial_number(attached),
|
|
954
1078
|
}
|
|
@@ -1370,14 +1494,39 @@ class OT3Controller(FlexBackend):
|
|
|
1370
1494
|
threshold_pascals: float,
|
|
1371
1495
|
plunger_impulse_time: float,
|
|
1372
1496
|
num_baseline_reads: int,
|
|
1497
|
+
z_offset_for_plunger_prep: float,
|
|
1373
1498
|
probe: InstrumentProbeType = InstrumentProbeType.PRIMARY,
|
|
1374
1499
|
force_both_sensors: bool = False,
|
|
1375
|
-
response_queue: Optional[
|
|
1376
|
-
asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
|
|
1377
|
-
] = None,
|
|
1500
|
+
response_queue: Optional[PipetteSensorResponseQueue] = None,
|
|
1378
1501
|
) -> float:
|
|
1379
1502
|
head_node = axis_to_node(Axis.by_mount(mount))
|
|
1380
1503
|
tool = sensor_node_for_pipette(OT3Mount(mount.value))
|
|
1504
|
+
if tool not in self._pipettes_to_monitor_pressure([tool]):
|
|
1505
|
+
raise UnsupportedHardwareCommand(
|
|
1506
|
+
"Liquid Presence Detection not available on this pipette."
|
|
1507
|
+
)
|
|
1508
|
+
|
|
1509
|
+
if response_queue is None:
|
|
1510
|
+
response_capture: Optional[
|
|
1511
|
+
Callable[[Dict[SensorId, List[SensorDataType]]], None]
|
|
1512
|
+
] = None
|
|
1513
|
+
else:
|
|
1514
|
+
|
|
1515
|
+
def response_capture(data: Dict[SensorId, List[SensorDataType]]) -> None:
|
|
1516
|
+
response_queue.put_nowait(
|
|
1517
|
+
{
|
|
1518
|
+
PipetteSensorId(sensor_id.value): [
|
|
1519
|
+
PipetteSensorData(
|
|
1520
|
+
sensor_type=PipetteSensorType(packet.sensor_type.value),
|
|
1521
|
+
_as_int=packet.to_int,
|
|
1522
|
+
_as_float=packet.to_float(),
|
|
1523
|
+
)
|
|
1524
|
+
for packet in packets
|
|
1525
|
+
]
|
|
1526
|
+
for sensor_id, packets in data.items()
|
|
1527
|
+
}
|
|
1528
|
+
)
|
|
1529
|
+
|
|
1381
1530
|
positions = await liquid_probe(
|
|
1382
1531
|
messenger=self._messenger,
|
|
1383
1532
|
tool=tool,
|
|
@@ -1388,9 +1537,10 @@ class OT3Controller(FlexBackend):
|
|
|
1388
1537
|
threshold_pascals=threshold_pascals,
|
|
1389
1538
|
plunger_impulse_time=plunger_impulse_time,
|
|
1390
1539
|
num_baseline_reads=num_baseline_reads,
|
|
1540
|
+
z_offset_for_plunger_prep=z_offset_for_plunger_prep,
|
|
1391
1541
|
sensor_id=sensor_id_for_instrument(probe),
|
|
1392
1542
|
force_both_sensors=force_both_sensors,
|
|
1393
|
-
|
|
1543
|
+
emplace_data=response_capture,
|
|
1394
1544
|
)
|
|
1395
1545
|
for node, point in positions.items():
|
|
1396
1546
|
self._position.update({node: point.motor_position})
|