opentrons 8.2.0a4__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 +36 -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 +1 -1
- 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 +52 -70
- 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.0a4.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from .types import (
|
|
4
|
+
StackerAxis,
|
|
5
|
+
PlatformStatus,
|
|
6
|
+
Direction,
|
|
7
|
+
MoveParams,
|
|
8
|
+
StackerInfo,
|
|
9
|
+
LEDColor,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AbstractStackerDriver(Protocol):
|
|
14
|
+
"""Protocol for the Stacker driver."""
|
|
15
|
+
|
|
16
|
+
async def connect(self) -> None:
|
|
17
|
+
"""Connect to stacker."""
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
async def disconnect(self) -> None:
|
|
21
|
+
"""Disconnect from stacker."""
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
async def is_connected(self) -> bool:
|
|
25
|
+
"""Check connection to stacker."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
async def update_firmware(self, firmware_file_path: str) -> None:
|
|
29
|
+
"""Updates the firmware on the device."""
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
async def get_device_info(self) -> StackerInfo:
|
|
33
|
+
"""Get Device Info."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
async def set_serial_number(self, sn: str) -> bool:
|
|
37
|
+
"""Set Serial Number."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
async def stop_motors(self) -> bool:
|
|
41
|
+
"""Stop all motor movement."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool:
|
|
45
|
+
"""Get limit switch status.
|
|
46
|
+
|
|
47
|
+
:return: True if limit switch is triggered, False otherwise
|
|
48
|
+
"""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
async def get_platform_sensor(self, direction: Direction) -> bool:
|
|
52
|
+
"""Get platform sensor status.
|
|
53
|
+
|
|
54
|
+
:return: True if platform is present, False otherwise
|
|
55
|
+
"""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
async def get_platform_status(self) -> PlatformStatus:
|
|
59
|
+
"""Get platform status."""
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
async def get_hopper_door_closed(self) -> bool:
|
|
63
|
+
"""Get whether or not door is closed.
|
|
64
|
+
|
|
65
|
+
:return: True if door is closed, False otherwise
|
|
66
|
+
"""
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
async def move_in_mm(
|
|
70
|
+
self, axis: StackerAxis, distance: float, params: MoveParams | None = None
|
|
71
|
+
) -> bool:
|
|
72
|
+
"""Move axis."""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
async def move_to_limit_switch(
|
|
76
|
+
self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None
|
|
77
|
+
) -> bool:
|
|
78
|
+
"""Move until limit switch is triggered."""
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
async def home_axis(self, axis: StackerAxis, direction: Direction) -> bool:
|
|
82
|
+
"""Home axis."""
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
async def set_led(
|
|
86
|
+
self, power: float, color: LEDColor | None = None, external: bool | None = None
|
|
87
|
+
) -> bool:
|
|
88
|
+
"""Set LED color of status bar."""
|
|
89
|
+
...
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import re
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from opentrons.drivers.command_builder import CommandBuilder
|
|
6
|
+
from opentrons.drivers.asyncio.communication import AsyncResponseSerialConnection
|
|
7
|
+
|
|
8
|
+
from .abstract import AbstractStackerDriver
|
|
9
|
+
from .types import (
|
|
10
|
+
GCODE,
|
|
11
|
+
StackerAxis,
|
|
12
|
+
PlatformStatus,
|
|
13
|
+
Direction,
|
|
14
|
+
StackerInfo,
|
|
15
|
+
HardwareRevision,
|
|
16
|
+
MoveParams,
|
|
17
|
+
LimitSwitchStatus,
|
|
18
|
+
LEDColor,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
FS_BAUDRATE = 115200
|
|
23
|
+
DEFAULT_FS_TIMEOUT = 40
|
|
24
|
+
FS_ACK = "OK\n"
|
|
25
|
+
FS_ERROR_KEYWORD = "err"
|
|
26
|
+
FS_ASYNC_ERROR_ACK = "async"
|
|
27
|
+
DEFAULT_COMMAND_RETRIES = 0
|
|
28
|
+
GCODE_ROUNDING_PRECISION = 2
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class FlexStackerDriver(AbstractStackerDriver):
|
|
32
|
+
"""FLEX Stacker driver."""
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def parse_device_info(cls, response: str) -> StackerInfo:
|
|
36
|
+
"""Parse stacker info."""
|
|
37
|
+
# TODO: Validate serial number format once established
|
|
38
|
+
_RE = re.compile(
|
|
39
|
+
f"^{GCODE.DEVICE_INFO} FW:(?P<fw>\\S+) HW:Opentrons-flex-stacker-(?P<hw>\\S+) SerialNo:(?P<sn>\\S+)$"
|
|
40
|
+
)
|
|
41
|
+
m = _RE.match(response)
|
|
42
|
+
if not m:
|
|
43
|
+
raise ValueError(f"Incorrect Response for device info: {response}")
|
|
44
|
+
return StackerInfo(
|
|
45
|
+
m.group("fw"), HardwareRevision(m.group("hw")), m.group("sn")
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def parse_limit_switch_status(cls, response: str) -> LimitSwitchStatus:
|
|
50
|
+
"""Parse limit switch statuses."""
|
|
51
|
+
field_names = LimitSwitchStatus.get_fields()
|
|
52
|
+
pattern = r"\s".join([rf"{name}:(?P<{name}>\d)" for name in field_names])
|
|
53
|
+
_RE = re.compile(f"^{GCODE.GET_LIMIT_SWITCH} {pattern}$")
|
|
54
|
+
m = _RE.match(response)
|
|
55
|
+
if not m:
|
|
56
|
+
raise ValueError(f"Incorrect Response for limit switch status: {response}")
|
|
57
|
+
return LimitSwitchStatus(*(bool(int(m.group(name))) for name in field_names))
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def parse_platform_sensor_status(cls, response: str) -> PlatformStatus:
|
|
61
|
+
"""Parse platform statuses."""
|
|
62
|
+
field_names = PlatformStatus.get_fields()
|
|
63
|
+
pattern = r"\s".join([rf"{name}:(?P<{name}>\d)" for name in field_names])
|
|
64
|
+
_RE = re.compile(f"^{GCODE.GET_PLATFORM_SENSOR} {pattern}$")
|
|
65
|
+
m = _RE.match(response)
|
|
66
|
+
if not m:
|
|
67
|
+
raise ValueError(f"Incorrect Response for platform status: {response}")
|
|
68
|
+
return PlatformStatus(*(bool(int(m.group(name))) for name in field_names))
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def parse_door_closed(cls, response: str) -> bool:
|
|
72
|
+
"""Parse door closed."""
|
|
73
|
+
_RE = re.compile(r"^M122 D:(\d)$")
|
|
74
|
+
match = _RE.match(response)
|
|
75
|
+
if not match:
|
|
76
|
+
raise ValueError(f"Incorrect Response for door closed: {response}")
|
|
77
|
+
return bool(int(match.group(1)))
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def append_move_params(
|
|
81
|
+
cls, command: CommandBuilder, params: MoveParams | None
|
|
82
|
+
) -> CommandBuilder:
|
|
83
|
+
"""Append move params."""
|
|
84
|
+
if params is not None:
|
|
85
|
+
if params.max_speed is not None:
|
|
86
|
+
command.add_float("V", params.max_speed, GCODE_ROUNDING_PRECISION)
|
|
87
|
+
if params.acceleration is not None:
|
|
88
|
+
command.add_float("A", params.acceleration, GCODE_ROUNDING_PRECISION)
|
|
89
|
+
if params.max_speed_discont is not None:
|
|
90
|
+
command.add_float(
|
|
91
|
+
"D", params.max_speed_discont, GCODE_ROUNDING_PRECISION
|
|
92
|
+
)
|
|
93
|
+
return command
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
async def create(
|
|
97
|
+
cls, port: str, loop: Optional[asyncio.AbstractEventLoop]
|
|
98
|
+
) -> "FlexStackerDriver":
|
|
99
|
+
"""Create a FLEX Stacker driver."""
|
|
100
|
+
connection = await AsyncResponseSerialConnection.create(
|
|
101
|
+
port=port,
|
|
102
|
+
baud_rate=FS_BAUDRATE,
|
|
103
|
+
timeout=DEFAULT_FS_TIMEOUT,
|
|
104
|
+
number_of_retries=DEFAULT_COMMAND_RETRIES,
|
|
105
|
+
ack=FS_ACK,
|
|
106
|
+
loop=loop,
|
|
107
|
+
error_keyword=FS_ERROR_KEYWORD,
|
|
108
|
+
async_error_ack=FS_ASYNC_ERROR_ACK,
|
|
109
|
+
)
|
|
110
|
+
return cls(connection)
|
|
111
|
+
|
|
112
|
+
def __init__(self, connection: AsyncResponseSerialConnection) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Constructor
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
connection: Connection to the FLEX Stacker
|
|
118
|
+
"""
|
|
119
|
+
self._connection = connection
|
|
120
|
+
|
|
121
|
+
async def connect(self) -> None:
|
|
122
|
+
"""Connect to stacker."""
|
|
123
|
+
await self._connection.open()
|
|
124
|
+
|
|
125
|
+
async def disconnect(self) -> None:
|
|
126
|
+
"""Disconnect from stacker."""
|
|
127
|
+
await self._connection.close()
|
|
128
|
+
|
|
129
|
+
async def is_connected(self) -> bool:
|
|
130
|
+
"""Check connection to stacker."""
|
|
131
|
+
return await self._connection.is_open()
|
|
132
|
+
|
|
133
|
+
async def get_device_info(self) -> StackerInfo:
|
|
134
|
+
"""Get Device Info."""
|
|
135
|
+
response = await self._connection.send_command(
|
|
136
|
+
GCODE.DEVICE_INFO.build_command()
|
|
137
|
+
)
|
|
138
|
+
await self._connection.send_command(GCODE.GET_RESET_REASON.build_command())
|
|
139
|
+
return self.parse_device_info(response)
|
|
140
|
+
|
|
141
|
+
async def set_serial_number(self, sn: str) -> bool:
|
|
142
|
+
"""Set Serial Number."""
|
|
143
|
+
# TODO: validate the serial number format
|
|
144
|
+
resp = await self._connection.send_command(
|
|
145
|
+
GCODE.SET_SERIAL_NUMBER.build_command().add_element(sn)
|
|
146
|
+
)
|
|
147
|
+
if not re.match(rf"^{GCODE.SET_SERIAL_NUMBER}$", resp):
|
|
148
|
+
raise ValueError(f"Incorrect Response for set serial number: {resp}")
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
async def stop_motors(self) -> bool:
|
|
152
|
+
"""Stop all motor movement."""
|
|
153
|
+
resp = await self._connection.send_command(GCODE.STOP_MOTORS.build_command())
|
|
154
|
+
if not re.match(rf"^{GCODE.STOP_MOTORS}$", resp):
|
|
155
|
+
raise ValueError(f"Incorrect Response for stop motors: {resp}")
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool:
|
|
159
|
+
"""Get limit switch status.
|
|
160
|
+
|
|
161
|
+
:return: True if limit switch is triggered, False otherwise
|
|
162
|
+
"""
|
|
163
|
+
response = await self.get_limit_switches_status()
|
|
164
|
+
return response.get(axis, direction)
|
|
165
|
+
|
|
166
|
+
async def get_limit_switches_status(self) -> LimitSwitchStatus:
|
|
167
|
+
"""Get limit switch statuses for all axes."""
|
|
168
|
+
response = await self._connection.send_command(
|
|
169
|
+
GCODE.GET_LIMIT_SWITCH.build_command()
|
|
170
|
+
)
|
|
171
|
+
return self.parse_limit_switch_status(response)
|
|
172
|
+
|
|
173
|
+
async def get_platform_sensor(self, direction: Direction) -> bool:
|
|
174
|
+
"""Get platform sensor at one direction."""
|
|
175
|
+
response = await self.get_platform_status()
|
|
176
|
+
return response.get(direction)
|
|
177
|
+
|
|
178
|
+
async def get_platform_status(self) -> PlatformStatus:
|
|
179
|
+
"""Get platform sensor status.
|
|
180
|
+
|
|
181
|
+
:return: True if platform is detected, False otherwise
|
|
182
|
+
"""
|
|
183
|
+
response = await self._connection.send_command(
|
|
184
|
+
GCODE.GET_PLATFORM_SENSOR.build_command()
|
|
185
|
+
)
|
|
186
|
+
return self.parse_platform_sensor_status(response)
|
|
187
|
+
|
|
188
|
+
async def get_hopper_door_closed(self) -> bool:
|
|
189
|
+
"""Get whether or not door is closed.
|
|
190
|
+
|
|
191
|
+
:return: True if door is closed, False otherwise
|
|
192
|
+
"""
|
|
193
|
+
response = await self._connection.send_command(
|
|
194
|
+
GCODE.GET_DOOR_SWITCH.build_command()
|
|
195
|
+
)
|
|
196
|
+
return self.parse_door_closed(response)
|
|
197
|
+
|
|
198
|
+
async def move_in_mm(
|
|
199
|
+
self, axis: StackerAxis, distance: float, params: MoveParams | None = None
|
|
200
|
+
) -> bool:
|
|
201
|
+
"""Move axis."""
|
|
202
|
+
command = self.append_move_params(
|
|
203
|
+
GCODE.MOVE_TO.build_command().add_float(
|
|
204
|
+
axis.name, distance, GCODE_ROUNDING_PRECISION
|
|
205
|
+
),
|
|
206
|
+
params,
|
|
207
|
+
)
|
|
208
|
+
resp = await self._connection.send_command(command)
|
|
209
|
+
if not re.match(rf"^{GCODE.MOVE_TO}$", resp):
|
|
210
|
+
raise ValueError(f"Incorrect Response for move to: {resp}")
|
|
211
|
+
return True
|
|
212
|
+
|
|
213
|
+
async def move_to_limit_switch(
|
|
214
|
+
self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None
|
|
215
|
+
) -> bool:
|
|
216
|
+
"""Move until limit switch is triggered."""
|
|
217
|
+
command = self.append_move_params(
|
|
218
|
+
GCODE.MOVE_TO_SWITCH.build_command().add_int(axis.name, direction.value),
|
|
219
|
+
params,
|
|
220
|
+
)
|
|
221
|
+
resp = await self._connection.send_command(command)
|
|
222
|
+
if not re.match(rf"^{GCODE.MOVE_TO_SWITCH}$", resp):
|
|
223
|
+
raise ValueError(f"Incorrect Response for move to switch: {resp}")
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
async def home_axis(self, axis: StackerAxis, direction: Direction) -> bool:
|
|
227
|
+
"""Home axis."""
|
|
228
|
+
resp = await self._connection.send_command(
|
|
229
|
+
GCODE.HOME_AXIS.build_command().add_int(axis.name, direction.value)
|
|
230
|
+
)
|
|
231
|
+
if not re.match(rf"^{GCODE.HOME_AXIS}$", resp):
|
|
232
|
+
raise ValueError(f"Incorrect Response for home axis: {resp}")
|
|
233
|
+
return True
|
|
234
|
+
|
|
235
|
+
async def set_led(
|
|
236
|
+
self, power: float, color: LEDColor | None = None, external: bool | None = None
|
|
237
|
+
) -> bool:
|
|
238
|
+
"""Set LED color.
|
|
239
|
+
|
|
240
|
+
:param power: Power of the LED (0-1.0), 0 is off, 1 is full power
|
|
241
|
+
:param color: Color of the LED
|
|
242
|
+
:param external: True if external LED, False if internal LED
|
|
243
|
+
"""
|
|
244
|
+
power = max(0, min(power, 1.0))
|
|
245
|
+
command = GCODE.SET_LED.build_command().add_float(
|
|
246
|
+
"P", power, GCODE_ROUNDING_PRECISION
|
|
247
|
+
)
|
|
248
|
+
if color is not None:
|
|
249
|
+
command.add_int("C", color.value)
|
|
250
|
+
if external is not None:
|
|
251
|
+
command.add_int("E", external)
|
|
252
|
+
resp = await self._connection.send_command(command)
|
|
253
|
+
if not re.match(rf"^{GCODE.SET_LED}$", resp):
|
|
254
|
+
raise ValueError(f"Incorrect Response for set led: {resp}")
|
|
255
|
+
return True
|
|
256
|
+
|
|
257
|
+
async def update_firmware(self, firmware_file_path: str) -> None:
|
|
258
|
+
"""Updates the firmware on the device."""
|
|
259
|
+
# TODO: Implement firmware update
|
|
260
|
+
pass
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from opentrons.util.async_helpers import ensure_yield
|
|
4
|
+
|
|
5
|
+
from .abstract import AbstractStackerDriver
|
|
6
|
+
from .types import (
|
|
7
|
+
StackerAxis,
|
|
8
|
+
PlatformStatus,
|
|
9
|
+
Direction,
|
|
10
|
+
StackerInfo,
|
|
11
|
+
HardwareRevision,
|
|
12
|
+
MoveParams,
|
|
13
|
+
LimitSwitchStatus,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SimulatingDriver(AbstractStackerDriver):
|
|
18
|
+
"""FLEX Stacker driver simulator."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, serial_number: Optional[str] = None) -> None:
|
|
21
|
+
self._sn = serial_number or "dummySerialFS"
|
|
22
|
+
self._limit_switch_status = LimitSwitchStatus(False, False, False, False, False)
|
|
23
|
+
self._platform_sensor_status = PlatformStatus(False, False)
|
|
24
|
+
self._door_closed = True
|
|
25
|
+
|
|
26
|
+
def set_limit_switch(self, status: LimitSwitchStatus) -> bool:
|
|
27
|
+
self._limit_switch_status = status
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
def set_platform_sensor(self, status: PlatformStatus) -> bool:
|
|
31
|
+
self._platform_sensor_status = status
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
def set_door_closed(self, door_closed: bool) -> bool:
|
|
35
|
+
self._door_closed = door_closed
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
@ensure_yield
|
|
39
|
+
async def connect(self) -> None:
|
|
40
|
+
"""Connect to stacker."""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
@ensure_yield
|
|
44
|
+
async def disconnect(self) -> None:
|
|
45
|
+
"""Disconnect from stacker."""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
@ensure_yield
|
|
49
|
+
async def is_connected(self) -> bool:
|
|
50
|
+
"""Check connection to stacker."""
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
@ensure_yield
|
|
54
|
+
async def get_device_info(self) -> StackerInfo:
|
|
55
|
+
"""Get Device Info."""
|
|
56
|
+
return StackerInfo(fw="stacker-fw", hw=HardwareRevision.EVT, sn=self._sn)
|
|
57
|
+
|
|
58
|
+
@ensure_yield
|
|
59
|
+
async def set_serial_number(self, sn: str) -> bool:
|
|
60
|
+
"""Set Serial Number."""
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
@ensure_yield
|
|
64
|
+
async def stop_motor(self) -> bool:
|
|
65
|
+
"""Stop motor movement."""
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
@ensure_yield
|
|
69
|
+
async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool:
|
|
70
|
+
"""Get limit switch status.
|
|
71
|
+
|
|
72
|
+
:return: True if limit switch is triggered, False otherwise
|
|
73
|
+
"""
|
|
74
|
+
return self._limit_switch_status.get(axis, direction)
|
|
75
|
+
|
|
76
|
+
@ensure_yield
|
|
77
|
+
async def get_limit_switches_status(self) -> LimitSwitchStatus:
|
|
78
|
+
"""Get limit switch statuses for all axes."""
|
|
79
|
+
return self._limit_switch_status
|
|
80
|
+
|
|
81
|
+
@ensure_yield
|
|
82
|
+
async def get_platform_sensor_status(self) -> PlatformStatus:
|
|
83
|
+
"""Get platform sensor status.
|
|
84
|
+
|
|
85
|
+
:return: True if platform is detected, False otherwise
|
|
86
|
+
"""
|
|
87
|
+
return self._platform_sensor_status
|
|
88
|
+
|
|
89
|
+
@ensure_yield
|
|
90
|
+
async def get_hopper_door_closed(self) -> bool:
|
|
91
|
+
"""Get whether or not door is closed.
|
|
92
|
+
|
|
93
|
+
:return: True if door is closed, False otherwise
|
|
94
|
+
"""
|
|
95
|
+
return self._door_closed
|
|
96
|
+
|
|
97
|
+
@ensure_yield
|
|
98
|
+
async def move_in_mm(
|
|
99
|
+
self, axis: StackerAxis, distance: float, params: MoveParams | None = None
|
|
100
|
+
) -> bool:
|
|
101
|
+
"""Move axis."""
|
|
102
|
+
return True
|
|
103
|
+
|
|
104
|
+
@ensure_yield
|
|
105
|
+
async def move_to_limit_switch(
|
|
106
|
+
self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None
|
|
107
|
+
) -> bool:
|
|
108
|
+
"""Move until limit switch is triggered."""
|
|
109
|
+
return True
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from dataclasses import dataclass, fields
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from opentrons.drivers.command_builder import CommandBuilder
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GCODE(str, Enum):
|
|
9
|
+
|
|
10
|
+
MOVE_TO = "G0"
|
|
11
|
+
MOVE_TO_SWITCH = "G5"
|
|
12
|
+
HOME_AXIS = "G28"
|
|
13
|
+
STOP_MOTORS = "M0"
|
|
14
|
+
GET_RESET_REASON = "M114"
|
|
15
|
+
DEVICE_INFO = "M115"
|
|
16
|
+
GET_LIMIT_SWITCH = "M119"
|
|
17
|
+
SET_LED = "M200"
|
|
18
|
+
GET_PLATFORM_SENSOR = "M121"
|
|
19
|
+
GET_DOOR_SWITCH = "M122"
|
|
20
|
+
SET_SERIAL_NUMBER = "M996"
|
|
21
|
+
ENTER_BOOTLOADER = "dfu"
|
|
22
|
+
|
|
23
|
+
def build_command(self) -> CommandBuilder:
|
|
24
|
+
"""Build command."""
|
|
25
|
+
return CommandBuilder().add_gcode(self)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
STACKER_VID = 0x483
|
|
29
|
+
STACKER_PID = 0xEF24
|
|
30
|
+
STACKER_FREQ = 115200
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class HardwareRevision(Enum):
|
|
34
|
+
"""Hardware Revision."""
|
|
35
|
+
|
|
36
|
+
NFF = "nff"
|
|
37
|
+
EVT = "a1"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class StackerInfo:
|
|
42
|
+
"""Stacker Info."""
|
|
43
|
+
|
|
44
|
+
fw: str
|
|
45
|
+
hw: HardwareRevision
|
|
46
|
+
sn: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class StackerAxis(Enum):
|
|
50
|
+
"""Stacker Axis."""
|
|
51
|
+
|
|
52
|
+
X = "X"
|
|
53
|
+
Z = "Z"
|
|
54
|
+
L = "L"
|
|
55
|
+
|
|
56
|
+
def __str__(self) -> str:
|
|
57
|
+
"""Name."""
|
|
58
|
+
return self.name
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class LEDColor(Enum):
|
|
62
|
+
"""Stacker LED Color."""
|
|
63
|
+
|
|
64
|
+
WHITE = 0
|
|
65
|
+
RED = 1
|
|
66
|
+
GREEN = 2
|
|
67
|
+
BLUE = 3
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Direction(Enum):
|
|
71
|
+
"""Direction."""
|
|
72
|
+
|
|
73
|
+
RETRACT = 0 # negative
|
|
74
|
+
EXTENT = 1 # positive
|
|
75
|
+
|
|
76
|
+
def __str__(self) -> str:
|
|
77
|
+
"""Convert to tag for clear logging."""
|
|
78
|
+
return "negative" if self == Direction.RETRACT else "positive"
|
|
79
|
+
|
|
80
|
+
def opposite(self) -> "Direction":
|
|
81
|
+
"""Get opposite direction."""
|
|
82
|
+
return Direction.EXTENT if self == Direction.RETRACT else Direction.RETRACT
|
|
83
|
+
|
|
84
|
+
def distance(self, distance: float) -> float:
|
|
85
|
+
"""Get signed distance, where retract direction is negative."""
|
|
86
|
+
return distance * -1 if self == Direction.RETRACT else distance
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
class LimitSwitchStatus:
|
|
91
|
+
"""Stacker Limit Switch Statuses."""
|
|
92
|
+
|
|
93
|
+
XE: bool
|
|
94
|
+
XR: bool
|
|
95
|
+
ZE: bool
|
|
96
|
+
ZR: bool
|
|
97
|
+
LR: bool
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def get_fields(cls) -> List[str]:
|
|
101
|
+
"""Get fields."""
|
|
102
|
+
return [f.name for f in fields(cls)]
|
|
103
|
+
|
|
104
|
+
def get(self, axis: StackerAxis, direction: Direction) -> bool:
|
|
105
|
+
"""Get limit switch status."""
|
|
106
|
+
if axis == StackerAxis.X:
|
|
107
|
+
return self.XE if direction == Direction.EXTENT else self.XR
|
|
108
|
+
if axis == StackerAxis.Z:
|
|
109
|
+
return self.ZE if direction == Direction.EXTENT else self.ZR
|
|
110
|
+
if direction == Direction.EXTENT:
|
|
111
|
+
raise ValueError("Latch does not have extent limit switch")
|
|
112
|
+
return self.LR
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class PlatformStatus:
|
|
117
|
+
"""Stacker Platform Statuses."""
|
|
118
|
+
|
|
119
|
+
E: bool
|
|
120
|
+
R: bool
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def get_fields(cls) -> List[str]:
|
|
124
|
+
"""Get fields."""
|
|
125
|
+
return [f.name for f in fields(cls)]
|
|
126
|
+
|
|
127
|
+
def get(self, direction: Direction) -> bool:
|
|
128
|
+
"""Get platform status."""
|
|
129
|
+
return self.E if direction == Direction.EXTENT else self.R
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass
|
|
133
|
+
class MoveParams:
|
|
134
|
+
"""Move Parameters."""
|
|
135
|
+
|
|
136
|
+
max_speed: float | None = None
|
|
137
|
+
acceleration: float | None = None
|
|
138
|
+
max_speed_discont: float | None = None
|
|
@@ -6,7 +6,10 @@ import asyncio
|
|
|
6
6
|
from typing import Optional, Dict
|
|
7
7
|
from opentrons.drivers import utils
|
|
8
8
|
from opentrons.drivers.command_builder import CommandBuilder
|
|
9
|
-
from opentrons.drivers.asyncio.communication import
|
|
9
|
+
from opentrons.drivers.asyncio.communication import (
|
|
10
|
+
AsyncResponseSerialConnection,
|
|
11
|
+
UnhandledGcode,
|
|
12
|
+
)
|
|
10
13
|
from opentrons.drivers.heater_shaker.abstract import AbstractHeaterShakerDriver
|
|
11
14
|
from opentrons.drivers.types import Temperature, RPM, HeaterShakerLabwareLatchStatus
|
|
12
15
|
|
|
@@ -23,6 +26,7 @@ class GCODE(str, Enum):
|
|
|
23
26
|
CLOSE_LABWARE_LATCH = "M243"
|
|
24
27
|
GET_LABWARE_LATCH_STATE = "M241"
|
|
25
28
|
DEACTIVATE_HEATER = "M106"
|
|
29
|
+
GET_RESET_REASON = "M114"
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
HS_BAUDRATE = 115200
|
|
@@ -166,12 +170,23 @@ class HeaterShakerDriver(AbstractHeaterShakerDriver):
|
|
|
166
170
|
|
|
167
171
|
async def get_device_info(self) -> Dict[str, str]:
|
|
168
172
|
"""Send get-device-info command"""
|
|
169
|
-
|
|
173
|
+
device_info = CommandBuilder(terminator=HS_COMMAND_TERMINATOR).add_gcode(
|
|
170
174
|
gcode=GCODE.GET_VERSION
|
|
171
175
|
)
|
|
172
176
|
response = await self._connection.send_command(
|
|
173
|
-
command=
|
|
177
|
+
command=device_info, retries=DEFAULT_COMMAND_RETRIES
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
reset_reason = CommandBuilder(terminator=HS_COMMAND_TERMINATOR).add_gcode(
|
|
181
|
+
gcode=GCODE.GET_RESET_REASON
|
|
174
182
|
)
|
|
183
|
+
try:
|
|
184
|
+
await self._connection.send_command(
|
|
185
|
+
command=reset_reason, retries=DEFAULT_COMMAND_RETRIES
|
|
186
|
+
)
|
|
187
|
+
except UnhandledGcode:
|
|
188
|
+
pass
|
|
189
|
+
|
|
175
190
|
return utils.parse_hs_device_information(device_info_string=response)
|
|
176
191
|
|
|
177
192
|
async def enter_programming_mode(self) -> None:
|
|
@@ -17,7 +17,7 @@ from enum import Enum
|
|
|
17
17
|
from opentrons.drivers import utils
|
|
18
18
|
from opentrons.drivers.types import Temperature
|
|
19
19
|
from opentrons.drivers.command_builder import CommandBuilder
|
|
20
|
-
from opentrons.drivers.asyncio.communication import SerialConnection
|
|
20
|
+
from opentrons.drivers.asyncio.communication import SerialConnection, UnhandledGcode
|
|
21
21
|
from opentrons.drivers.temp_deck.abstract import AbstractTempDeckDriver
|
|
22
22
|
|
|
23
23
|
log = logging.getLogger(__name__)
|
|
@@ -31,6 +31,7 @@ class GCODE(str, Enum):
|
|
|
31
31
|
GET_TEMP = "M105"
|
|
32
32
|
SET_TEMP = "M104"
|
|
33
33
|
DEVICE_INFO = "M115"
|
|
34
|
+
GET_RESET_REASON = "M114"
|
|
34
35
|
DISENGAGE = "M18"
|
|
35
36
|
PROGRAMMING_MODE = "dfu"
|
|
36
37
|
|
|
@@ -154,10 +155,19 @@ class TempDeckDriver(AbstractTempDeckDriver):
|
|
|
154
155
|
Example input from Temp-Deck's serial response:
|
|
155
156
|
"serial:aa11bb22 model:aa11bb22 version:aa11bb22"
|
|
156
157
|
"""
|
|
157
|
-
|
|
158
|
+
device_info = CommandBuilder(terminator=TEMP_DECK_COMMAND_TERMINATOR).add_gcode(
|
|
158
159
|
gcode=GCODE.DEVICE_INFO
|
|
159
160
|
)
|
|
160
|
-
response = await self._send_command(command=
|
|
161
|
+
response = await self._send_command(command=device_info)
|
|
162
|
+
|
|
163
|
+
reset_reason = CommandBuilder(
|
|
164
|
+
terminator=TEMP_DECK_COMMAND_TERMINATOR
|
|
165
|
+
).add_gcode(gcode=GCODE.GET_RESET_REASON)
|
|
166
|
+
try:
|
|
167
|
+
await self._send_command(command=reset_reason)
|
|
168
|
+
except UnhandledGcode:
|
|
169
|
+
pass
|
|
170
|
+
|
|
161
171
|
return utils.parse_device_information(device_info_string=response)
|
|
162
172
|
|
|
163
173
|
async def enter_programming_mode(self) -> None:
|