opentrons 8.3.0a0__py2.py3-none-any.whl → 8.3.0a2__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.
- 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/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 +28 -20
- opentrons/hardware_control/backends/flex_protocol.py +4 -6
- opentrons/hardware_control/backends/ot3controller.py +177 -59
- opentrons/hardware_control/backends/ot3simulator.py +10 -8
- opentrons/hardware_control/backends/ot3utils.py +3 -13
- opentrons/hardware_control/dev_types.py +2 -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 +9 -21
- 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 +13 -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 +34 -22
- 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 +18 -0
- opentrons/hardware_control/protocols/motion_controller.py +6 -0
- opentrons/hardware_control/robot_calibration.py +1 -1
- opentrons/hardware_control/types.py +61 -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 +67 -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 +42 -4
- opentrons/protocol_api/core/labware.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +34 -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 +34 -3
- opentrons/protocol_api/core/protocol.py +34 -1
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/instrument_context.py +145 -43
- opentrons/protocol_api/labware.py +231 -7
- 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 +261 -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 +81 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
- opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
- 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 +101 -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 +13 -3
- 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 +60 -25
- opentrons/protocol_engine/commands/load_labware.py +29 -7
- 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 +19 -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 +8 -2
- 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 +4 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
- 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 +8 -0
- opentrons/protocol_engine/errors/error_occurrence.py +19 -20
- opentrons/protocol_engine/errors/exceptions.py +50 -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 +364 -8
- opentrons/protocol_engine/execution/movement.py +27 -0
- opentrons/protocol_engine/execution/pipetting.py +5 -1
- opentrons/protocol_engine/execution/tip_handler.py +4 -6
- 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 +5 -0
- opentrons/protocol_engine/resources/module_data_provider.py +1 -1
- opentrons/protocol_engine/resources/pipette_data_provider.py +12 -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 +17 -13
- opentrons/protocol_engine/state/files.py +10 -12
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/frustum_helpers.py +57 -32
- opentrons/protocol_engine/state/geometry.py +47 -1
- opentrons/protocol_engine/state/labware.py +79 -25
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +16 -4
- opentrons/protocol_engine/state/modules.py +52 -70
- opentrons/protocol_engine/state/motion.py +6 -1
- opentrons/protocol_engine/state/pipettes.py +135 -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.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/METADATA +16 -15
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/RECORD +229 -202
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/WHEEL +1 -1
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/LICENSE +0 -0
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/entry_points.txt +0 -0
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.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)
|
|
@@ -917,6 +921,16 @@ class API(
|
|
|
917
921
|
async def disengage_axes(self, which: List[Axis]) -> None:
|
|
918
922
|
await self._backend.disengage_axes([ot2_axis_to_string(ax) for ax in which])
|
|
919
923
|
|
|
924
|
+
def axis_is_present(self, axis: Axis) -> bool:
|
|
925
|
+
is_ot2 = axis in Axis.ot2_axes()
|
|
926
|
+
if not is_ot2:
|
|
927
|
+
return False
|
|
928
|
+
if axis in Axis.pipette_axes():
|
|
929
|
+
mount = Axis.to_ot2_mount(axis)
|
|
930
|
+
if self.attached_pipettes.get(mount) is None:
|
|
931
|
+
return False
|
|
932
|
+
return True
|
|
933
|
+
|
|
920
934
|
@ExecutionManagerProvider.wait_for_running
|
|
921
935
|
async def _fast_home(self, axes: Sequence[str], margin: float) -> Dict[str, float]:
|
|
922
936
|
converted_axes = "".join(axes)
|
|
@@ -938,11 +952,8 @@ class API(
|
|
|
938
952
|
|
|
939
953
|
async with self._motion_lock:
|
|
940
954
|
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"),
|
|
955
|
+
self._current_position = self.get_deck_from_machine(
|
|
956
|
+
self._axis_map_from_string_map(smoothie_pos)
|
|
946
957
|
)
|
|
947
958
|
|
|
948
959
|
# Gantry/frame (i.e. not pipette) config API
|
|
@@ -1256,11 +1267,8 @@ class API(
|
|
|
1256
1267
|
axes=[ot2_axis_to_string(ax) for ax in move.home_axes],
|
|
1257
1268
|
margin=move.home_after_safety_margin,
|
|
1258
1269
|
)
|
|
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"),
|
|
1270
|
+
self._current_position = self.get_deck_from_machine(
|
|
1271
|
+
self._axis_map_from_string_map(smoothie_pos)
|
|
1264
1272
|
)
|
|
1265
1273
|
|
|
1266
1274
|
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
|
|
|
@@ -166,11 +165,10 @@ class FlexBackend(Protocol):
|
|
|
166
165
|
threshold_pascals: float,
|
|
167
166
|
plunger_impulse_time: float,
|
|
168
167
|
num_baseline_reads: int,
|
|
168
|
+
z_offset_for_plunger_prep: float,
|
|
169
169
|
probe: InstrumentProbeType = InstrumentProbeType.PRIMARY,
|
|
170
170
|
force_both_sensors: bool = False,
|
|
171
|
-
response_queue: Optional[
|
|
172
|
-
asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
|
|
173
|
-
] = None,
|
|
171
|
+
response_queue: Optional[PipetteSensorResponseQueue] = None,
|
|
174
172
|
) -> float:
|
|
175
173
|
...
|
|
176
174
|
|
|
@@ -233,7 +231,7 @@ class FlexBackend(Protocol):
|
|
|
233
231
|
...
|
|
234
232
|
|
|
235
233
|
async def tip_action(
|
|
236
|
-
self, origin:
|
|
234
|
+
self, origin: float, targets: List[Tuple[float, float]]
|
|
237
235
|
) -> None:
|
|
238
236
|
...
|
|
239
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,
|
|
@@ -212,6 +215,7 @@ from ..types import HepaFanState, HepaUVState, StatusBarState
|
|
|
212
215
|
from .types import HWStopCondition
|
|
213
216
|
from .flex_protocol import FlexBackend
|
|
214
217
|
from .status_bar_state import StatusBarStateController
|
|
218
|
+
from opentrons_hardware.sensors.types import SensorDataType
|
|
215
219
|
|
|
216
220
|
log = logging.getLogger(__name__)
|
|
217
221
|
|
|
@@ -617,30 +621,104 @@ class OT3Controller(FlexBackend):
|
|
|
617
621
|
return axis_convert(self._encoder_position, 0.0)
|
|
618
622
|
|
|
619
623
|
def _handle_motor_status_response(
|
|
620
|
-
self,
|
|
621
|
-
response: NodeMap[MotorPositionStatus],
|
|
624
|
+
self, response: NodeMap[MotorPositionStatus], handle_gear_move: bool = False
|
|
622
625
|
) -> None:
|
|
623
626
|
for axis, pos in response.items():
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
(not
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
motor_ok=(pos.motor_ok or motor_ok_latch),
|
|
640
|
-
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
|
|
641
642
|
)
|
|
642
|
-
|
|
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]
|
|
643
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
|
+
)
|
|
644
722
|
|
|
645
723
|
@requires_update
|
|
646
724
|
@requires_estop
|
|
@@ -668,41 +746,52 @@ class OT3Controller(FlexBackend):
|
|
|
668
746
|
Returns:
|
|
669
747
|
None
|
|
670
748
|
"""
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
_, movelist = self._move_manager.plan_motion(
|
|
674
|
-
origin=origin, target_list=[move_target]
|
|
675
|
-
)
|
|
676
|
-
except ZeroLengthMoveError as zme:
|
|
677
|
-
log.debug(f"Not moving because move was zero length {str(zme)}")
|
|
678
|
-
return
|
|
679
|
-
moves = movelist[0]
|
|
680
|
-
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)
|
|
681
751
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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
|
+
),
|
|
691
762
|
)
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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
|
|
698
776
|
)
|
|
699
777
|
|
|
700
|
-
|
|
778
|
+
async def _runner_coroutine(
|
|
779
|
+
runner: MoveGroupRunner, is_gear_move: bool
|
|
780
|
+
) -> Tuple[Dict[NodeId, MotorPositionStatus], bool]:
|
|
781
|
+
positions = await runner.run(can_messenger=self._messenger)
|
|
782
|
+
return positions, is_gear_move
|
|
701
783
|
|
|
784
|
+
coros = [
|
|
785
|
+
_runner_coroutine(runner, is_gear_move)
|
|
786
|
+
for runner, is_gear_move in maybe_runners
|
|
787
|
+
if runner
|
|
788
|
+
]
|
|
702
789
|
checked_moving_pipettes = self._pipettes_to_monitor_pressure(pipettes_moving)
|
|
703
790
|
async with self._monitor_overpressure(checked_moving_pipettes):
|
|
704
|
-
|
|
705
|
-
|
|
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)
|
|
706
795
|
|
|
707
796
|
def _get_axis_home_distance(self, axis: Axis) -> float:
|
|
708
797
|
if self.check_motor_status([axis]):
|
|
@@ -818,6 +907,7 @@ class OT3Controller(FlexBackend):
|
|
|
818
907
|
Axis.to_kind(Axis.Q)
|
|
819
908
|
],
|
|
820
909
|
)
|
|
910
|
+
|
|
821
911
|
for position in positions:
|
|
822
912
|
self._handle_motor_status_response(position)
|
|
823
913
|
return axis_convert(self._position, 0.0)
|
|
@@ -865,17 +955,23 @@ class OT3Controller(FlexBackend):
|
|
|
865
955
|
self._gear_motor_position = {}
|
|
866
956
|
raise e
|
|
867
957
|
|
|
868
|
-
|
|
869
|
-
self, origin:
|
|
870
|
-
) ->
|
|
958
|
+
def _build_tip_action_group(
|
|
959
|
+
self, origin: float, targets: List[Tuple[float, float]]
|
|
960
|
+
) -> MoveGroup:
|
|
871
961
|
move_targets = [
|
|
872
|
-
MoveTarget.build(target_pos, speed)
|
|
962
|
+
MoveTarget.build({Axis.Q: target_pos}, speed)
|
|
963
|
+
for target_pos, speed in targets
|
|
873
964
|
]
|
|
874
965
|
_, moves = self._move_manager.plan_motion(
|
|
875
|
-
origin=origin, target_list=move_targets
|
|
966
|
+
origin={Axis.Q: origin}, target_list=move_targets
|
|
876
967
|
)
|
|
877
|
-
move_group = create_tip_action_group(moves[0], [NodeId.pipette_left], "clamp")
|
|
878
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)
|
|
879
975
|
runner = MoveGroupRunner(
|
|
880
976
|
move_groups=[move_group],
|
|
881
977
|
ignore_stalls=True
|
|
@@ -945,6 +1041,7 @@ class OT3Controller(FlexBackend):
|
|
|
945
1041
|
FirmwarePipetteName.p50_multi: "P50M",
|
|
946
1042
|
FirmwarePipetteName.p1000_96: "P1KH",
|
|
947
1043
|
FirmwarePipetteName.p50_96: "P50H",
|
|
1044
|
+
FirmwarePipetteName.p200_96: "P2HH",
|
|
948
1045
|
}
|
|
949
1046
|
return lookup_name[pipette_name]
|
|
950
1047
|
|
|
@@ -1397,11 +1494,10 @@ class OT3Controller(FlexBackend):
|
|
|
1397
1494
|
threshold_pascals: float,
|
|
1398
1495
|
plunger_impulse_time: float,
|
|
1399
1496
|
num_baseline_reads: int,
|
|
1497
|
+
z_offset_for_plunger_prep: float,
|
|
1400
1498
|
probe: InstrumentProbeType = InstrumentProbeType.PRIMARY,
|
|
1401
1499
|
force_both_sensors: bool = False,
|
|
1402
|
-
response_queue: Optional[
|
|
1403
|
-
asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
|
|
1404
|
-
] = None,
|
|
1500
|
+
response_queue: Optional[PipetteSensorResponseQueue] = None,
|
|
1405
1501
|
) -> float:
|
|
1406
1502
|
head_node = axis_to_node(Axis.by_mount(mount))
|
|
1407
1503
|
tool = sensor_node_for_pipette(OT3Mount(mount.value))
|
|
@@ -1410,6 +1506,27 @@ class OT3Controller(FlexBackend):
|
|
|
1410
1506
|
"Liquid Presence Detection not available on this pipette."
|
|
1411
1507
|
)
|
|
1412
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
|
+
|
|
1413
1530
|
positions = await liquid_probe(
|
|
1414
1531
|
messenger=self._messenger,
|
|
1415
1532
|
tool=tool,
|
|
@@ -1420,9 +1537,10 @@ class OT3Controller(FlexBackend):
|
|
|
1420
1537
|
threshold_pascals=threshold_pascals,
|
|
1421
1538
|
plunger_impulse_time=plunger_impulse_time,
|
|
1422
1539
|
num_baseline_reads=num_baseline_reads,
|
|
1540
|
+
z_offset_for_plunger_prep=z_offset_for_plunger_prep,
|
|
1423
1541
|
sensor_id=sensor_id_for_instrument(probe),
|
|
1424
1542
|
force_both_sensors=force_both_sensors,
|
|
1425
|
-
|
|
1543
|
+
emplace_data=response_capture,
|
|
1426
1544
|
)
|
|
1427
1545
|
for node, point in positions.items():
|
|
1428
1546
|
self._position.update({node: point.motor_position})
|
|
@@ -45,6 +45,7 @@ from opentrons.hardware_control.types import (
|
|
|
45
45
|
EstopPhysicalStatus,
|
|
46
46
|
HardwareEventHandler,
|
|
47
47
|
HardwareEventUnsubscriber,
|
|
48
|
+
PipetteSensorResponseQueue,
|
|
48
49
|
)
|
|
49
50
|
|
|
50
51
|
from opentrons_shared_data.pipette.types import PipetteName, PipetteModel
|
|
@@ -62,9 +63,9 @@ from opentrons.hardware_control.dev_types import (
|
|
|
62
63
|
)
|
|
63
64
|
from opentrons.util.async_helpers import ensure_yield
|
|
64
65
|
from .types import HWStopCondition
|
|
65
|
-
from .flex_protocol import
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
from .flex_protocol import (
|
|
67
|
+
FlexBackend,
|
|
68
|
+
)
|
|
68
69
|
|
|
69
70
|
log = logging.getLogger(__name__)
|
|
70
71
|
|
|
@@ -352,11 +353,10 @@ class OT3Simulator(FlexBackend):
|
|
|
352
353
|
threshold_pascals: float,
|
|
353
354
|
plunger_impulse_time: float,
|
|
354
355
|
num_baseline_reads: int,
|
|
356
|
+
z_offset_for_plunger_prep: float,
|
|
355
357
|
probe: InstrumentProbeType = InstrumentProbeType.PRIMARY,
|
|
356
358
|
force_both_sensors: bool = False,
|
|
357
|
-
response_queue: Optional[
|
|
358
|
-
asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
|
|
359
|
-
] = None,
|
|
359
|
+
response_queue: Optional[PipetteSensorResponseQueue] = None,
|
|
360
360
|
) -> float:
|
|
361
361
|
z_axis = Axis.by_mount(mount)
|
|
362
362
|
pos = self._position
|
|
@@ -443,10 +443,12 @@ class OT3Simulator(FlexBackend):
|
|
|
443
443
|
return self._sim_jaw_state
|
|
444
444
|
|
|
445
445
|
async def tip_action(
|
|
446
|
-
self, origin:
|
|
446
|
+
self, origin: float, targets: List[Tuple[float, float]]
|
|
447
447
|
) -> None:
|
|
448
448
|
self._gear_motor_position.update(
|
|
449
|
-
coalesce_move_segments(
|
|
449
|
+
coalesce_move_segments(
|
|
450
|
+
{Axis.Q: origin}, [{Axis.Q: target[0]} for target in targets]
|
|
451
|
+
)
|
|
450
452
|
)
|
|
451
453
|
await asyncio.sleep(0)
|
|
452
454
|
|
|
@@ -536,10 +536,10 @@ def create_gripper_jaw_hold_group(encoder_position_um: int) -> MoveGroup:
|
|
|
536
536
|
return move_group
|
|
537
537
|
|
|
538
538
|
|
|
539
|
-
def moving_pipettes_in_move_group(
|
|
539
|
+
def moving_pipettes_in_move_group(
|
|
540
|
+
all_nodes: Set[NodeId], moving_nodes: Set[NodeId]
|
|
541
|
+
) -> List[NodeId]:
|
|
540
542
|
"""Utility function to get which pipette nodes are moving either in z or their plunger."""
|
|
541
|
-
all_nodes = [node for step in group for node, _ in step.items()]
|
|
542
|
-
moving_nodes = moving_axes_in_move_group(group)
|
|
543
543
|
pipettes_moving: List[NodeId] = [
|
|
544
544
|
k for k in moving_nodes if k in [NodeId.pipette_left, NodeId.pipette_right]
|
|
545
545
|
]
|
|
@@ -550,16 +550,6 @@ def moving_pipettes_in_move_group(group: MoveGroup) -> List[NodeId]:
|
|
|
550
550
|
return pipettes_moving
|
|
551
551
|
|
|
552
552
|
|
|
553
|
-
def moving_axes_in_move_group(group: MoveGroup) -> Set[NodeId]:
|
|
554
|
-
"""Utility function to get only the moving nodes in a move group."""
|
|
555
|
-
ret: Set[NodeId] = set()
|
|
556
|
-
for step in group:
|
|
557
|
-
for node, node_step in step.items():
|
|
558
|
-
if node_step.is_moving_step():
|
|
559
|
-
ret.add(node)
|
|
560
|
-
return ret
|
|
561
|
-
|
|
562
|
-
|
|
563
553
|
AxisMapPayload = TypeVar("AxisMapPayload")
|
|
564
554
|
|
|
565
555
|
|
|
@@ -101,6 +101,8 @@ class PipetteDict(InstrumentDict):
|
|
|
101
101
|
pipette_bounding_box_offsets: PipetteBoundingBoxOffsetDefinition
|
|
102
102
|
current_nozzle_map: NozzleMap
|
|
103
103
|
lld_settings: Optional[Dict[str, Dict[str, float]]]
|
|
104
|
+
plunger_positions: Dict[str, float]
|
|
105
|
+
shaft_ul_per_mm: float
|
|
104
106
|
available_sensors: AvailableSensorDefinition
|
|
105
107
|
|
|
106
108
|
|
|
@@ -45,6 +45,7 @@ class HeaterShakerEmulator(AbstractEmulator):
|
|
|
45
45
|
GCODE.HOME.value: self._home,
|
|
46
46
|
GCODE.ENTER_BOOTLOADER.value: self._enter_bootloader,
|
|
47
47
|
GCODE.GET_VERSION.value: self._get_version,
|
|
48
|
+
GCODE.GET_RESET_REASON.value: self._get_reset_reason,
|
|
48
49
|
GCODE.OPEN_LABWARE_LATCH.value: self._open_labware_latch,
|
|
49
50
|
GCODE.CLOSE_LABWARE_LATCH.value: self._close_labware_latch,
|
|
50
51
|
GCODE.GET_LABWARE_LATCH_STATE.value: self._get_labware_latch_state,
|
|
@@ -126,6 +127,9 @@ class HeaterShakerEmulator(AbstractEmulator):
|
|
|
126
127
|
f"SerialNo:{self._settings.serial_number}"
|
|
127
128
|
)
|
|
128
129
|
|
|
130
|
+
def _get_reset_reason(self, command: Command) -> str:
|
|
131
|
+
return "M114 Last Reset Reason: 01"
|
|
132
|
+
|
|
129
133
|
def _open_labware_latch(self, command: Command) -> str:
|
|
130
134
|
self._latch_status = HeaterShakerLabwareLatchStatus.IDLE_OPEN
|
|
131
135
|
return "M242"
|
|
@@ -66,7 +66,7 @@ class ModuleStatusClient:
|
|
|
66
66
|
"""Read a message from the module server."""
|
|
67
67
|
try:
|
|
68
68
|
b = await self._reader.readuntil(MessageDelimiter)
|
|
69
|
-
m: Message = Message.
|
|
69
|
+
m: Message = Message.model_validate_json(b)
|
|
70
70
|
return m
|
|
71
71
|
except LimitOverrunError as e:
|
|
72
72
|
raise ModuleServerClientError(str(e))
|