opentrons 8.7.0a7__py3-none-any.whl → 8.7.0a8__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/_version.py +2 -2
- opentrons/drivers/asyncio/communication/serial_connection.py +55 -129
- opentrons/drivers/flex_stacker/driver.py +6 -1
- opentrons/drivers/heater_shaker/abstract.py +0 -5
- opentrons/drivers/heater_shaker/driver.py +0 -10
- opentrons/drivers/heater_shaker/simulator.py +0 -4
- opentrons/drivers/thermocycler/abstract.py +0 -6
- opentrons/drivers/thermocycler/driver.py +10 -61
- opentrons/drivers/thermocycler/simulator.py +0 -6
- opentrons/hardware_control/api.py +5 -24
- opentrons/hardware_control/backends/controller.py +2 -8
- opentrons/hardware_control/backends/flex_protocol.py +1 -0
- opentrons/hardware_control/backends/ot3controller.py +3 -3
- opentrons/hardware_control/backends/ot3simulator.py +2 -2
- opentrons/hardware_control/backends/simulator.py +1 -2
- opentrons/hardware_control/backends/subsystem_manager.py +2 -5
- opentrons/hardware_control/emulation/abstract_emulator.py +4 -6
- opentrons/hardware_control/emulation/connection_handler.py +5 -8
- opentrons/hardware_control/emulation/heater_shaker.py +3 -12
- opentrons/hardware_control/emulation/settings.py +1 -1
- opentrons/hardware_control/emulation/thermocycler.py +15 -67
- opentrons/hardware_control/module_control.py +8 -82
- opentrons/hardware_control/modules/__init__.py +0 -3
- opentrons/hardware_control/modules/absorbance_reader.py +4 -11
- opentrons/hardware_control/modules/flex_stacker.py +9 -38
- opentrons/hardware_control/modules/heater_shaker.py +5 -42
- opentrons/hardware_control/modules/magdeck.py +4 -8
- opentrons/hardware_control/modules/mod_abc.py +5 -13
- opentrons/hardware_control/modules/tempdeck.py +5 -25
- opentrons/hardware_control/modules/thermocycler.py +11 -68
- opentrons/hardware_control/modules/types.py +1 -20
- opentrons/hardware_control/modules/utils.py +4 -11
- opentrons/hardware_control/nozzle_manager.py +0 -3
- opentrons/hardware_control/ot3api.py +7 -26
- opentrons/hardware_control/poller.py +8 -22
- opentrons/hardware_control/protocols/gripper_controller.py +1 -0
- opentrons/hardware_control/scripts/update_module_fw.py +0 -5
- opentrons/hardware_control/types.py +2 -31
- opentrons/legacy_commands/module_commands.py +0 -23
- opentrons/legacy_commands/protocol_commands.py +0 -20
- opentrons/legacy_commands/types.py +0 -80
- opentrons/motion_planning/deck_conflict.py +12 -17
- opentrons/motion_planning/waypoints.py +29 -15
- opentrons/protocol_api/__init__.py +1 -5
- opentrons/protocol_api/_types.py +1 -6
- opentrons/protocol_api/core/common.py +1 -3
- opentrons/protocol_api/core/engine/_default_labware_versions.py +11 -32
- opentrons/protocol_api/core/engine/labware.py +1 -8
- opentrons/protocol_api/core/engine/module_core.py +8 -75
- opentrons/protocol_api/core/engine/protocol.py +1 -18
- opentrons/protocol_api/core/engine/well.py +0 -8
- opentrons/protocol_api/core/legacy/legacy_module_core.py +4 -24
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -11
- opentrons/protocol_api/core/legacy/legacy_well_core.py +0 -4
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +2 -14
- opentrons/protocol_api/core/module.py +4 -37
- opentrons/protocol_api/core/protocol.py +2 -11
- opentrons/protocol_api/core/well.py +0 -4
- opentrons/protocol_api/labware.py +0 -5
- opentrons/protocol_api/module_contexts.py +11 -117
- opentrons/protocol_api/protocol_context.py +4 -26
- opentrons/protocol_api/robot_context.py +21 -38
- opentrons/protocol_api/validation.py +1 -6
- opentrons/protocol_engine/actions/__init__.py +2 -4
- opentrons/protocol_engine/actions/actions.py +9 -22
- opentrons/protocol_engine/clients/sync_client.py +7 -42
- opentrons/protocol_engine/commands/__init__.py +0 -42
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +15 -2
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +15 -2
- opentrons/protocol_engine/commands/aspirate.py +0 -1
- opentrons/protocol_engine/commands/command.py +0 -1
- opentrons/protocol_engine/commands/command_unions.py +0 -49
- opentrons/protocol_engine/commands/dispense.py +0 -1
- opentrons/protocol_engine/commands/drop_tip.py +8 -32
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +0 -14
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +4 -5
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +5 -31
- opentrons/protocol_engine/commands/movement_common.py +0 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +11 -21
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +7 -38
- opentrons/protocol_engine/commands/thermocycler/__init__.py +0 -16
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +0 -6
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +0 -8
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +6 -40
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +5 -29
- opentrons/protocol_engine/commands/touch_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +22 -6
- opentrons/protocol_engine/errors/__init__.py +0 -4
- opentrons/protocol_engine/errors/exceptions.py +0 -55
- opentrons/protocol_engine/execution/__init__.py +0 -2
- opentrons/protocol_engine/execution/command_executor.py +0 -8
- opentrons/protocol_engine/execution/create_queue_worker.py +1 -5
- opentrons/protocol_engine/execution/labware_movement.py +21 -10
- opentrons/protocol_engine/execution/movement.py +0 -2
- opentrons/protocol_engine/execution/queue_worker.py +0 -4
- opentrons/protocol_engine/execution/run_control.py +0 -8
- opentrons/protocol_engine/protocol_engine.py +34 -75
- opentrons/protocol_engine/resources/__init__.py +0 -2
- opentrons/protocol_engine/resources/deck_configuration_provider.py +0 -7
- opentrons/protocol_engine/resources/labware_validation.py +6 -10
- opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
- opentrons/protocol_engine/state/_well_math.py +18 -60
- opentrons/protocol_engine/state/addressable_areas.py +0 -2
- opentrons/protocol_engine/state/commands.py +11 -14
- opentrons/protocol_engine/state/geometry.py +374 -213
- opentrons/protocol_engine/state/labware.py +102 -52
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +0 -37
- opentrons/protocol_engine/state/modules.py +8 -21
- opentrons/protocol_engine/state/motion.py +0 -44
- opentrons/protocol_engine/state/state.py +0 -14
- opentrons/protocol_engine/state/state_summary.py +0 -2
- opentrons/protocol_engine/state/tips.py +258 -177
- opentrons/protocol_engine/state/update_types.py +9 -16
- opentrons/protocol_engine/types/__init__.py +3 -9
- opentrons/protocol_engine/types/deck_configuration.py +1 -5
- opentrons/protocol_engine/types/instrument.py +1 -8
- opentrons/protocol_engine/types/labware.py +13 -1
- opentrons/protocol_engine/types/module.py +0 -10
- opentrons/protocol_engine/types/tip.py +0 -9
- opentrons/protocol_runner/create_simulating_orchestrator.py +2 -29
- opentrons/protocol_runner/run_orchestrator.py +2 -18
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +1 -2
- opentrons/simulate.py +15 -48
- opentrons/system/camera.py +1 -1
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/RECORD +130 -146
- opentrons/protocol_api/core/engine/tasks.py +0 -48
- opentrons/protocol_api/core/legacy/tasks.py +0 -19
- opentrons/protocol_api/core/legacy_simulator/tasks.py +0 -19
- opentrons/protocol_api/core/tasks.py +0 -31
- opentrons/protocol_api/tasks.py +0 -48
- opentrons/protocol_engine/commands/create_timer.py +0 -83
- opentrons/protocol_engine/commands/heater_shaker/common.py +0 -20
- opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +0 -136
- opentrons/protocol_engine/commands/set_tip_state.py +0 -97
- opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +0 -191
- opentrons/protocol_engine/commands/wait_for_tasks.py +0 -98
- opentrons/protocol_engine/execution/task_handler.py +0 -157
- opentrons/protocol_engine/resources/concurrency_provider.py +0 -27
- opentrons/protocol_engine/state/labware_origin_math/errors.py +0 -94
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +0 -1331
- opentrons/protocol_engine/state/tasks.py +0 -139
- opentrons/protocol_engine/types/tasks.py +0 -38
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/licenses/LICENSE +0 -0
|
@@ -414,8 +414,7 @@ class Simulator:
|
|
|
414
414
|
|
|
415
415
|
@ensure_yield
|
|
416
416
|
async def clean_up(self) -> None:
|
|
417
|
-
|
|
418
|
-
await self._module_controls.clean_up()
|
|
417
|
+
pass
|
|
419
418
|
|
|
420
419
|
@ensure_yield
|
|
421
420
|
async def configure_mount(
|
|
@@ -91,8 +91,6 @@ class SubsystemManager:
|
|
|
91
91
|
self._present_tools = tools.types.ToolSummary(
|
|
92
92
|
left=None, right=None, gripper=None
|
|
93
93
|
)
|
|
94
|
-
# This is intended to be an internal variable but is modified in unit tests to avoid long timeouts
|
|
95
|
-
self._check_device_update_timeout = 10.0
|
|
96
94
|
|
|
97
95
|
@property
|
|
98
96
|
def ok(self) -> bool:
|
|
@@ -185,12 +183,11 @@ class SubsystemManager:
|
|
|
185
183
|
return self._tool_task_state is True
|
|
186
184
|
|
|
187
185
|
async def _check_devices_after_update(
|
|
188
|
-
self, devices: Set[SubSystem], timeout_sec:
|
|
186
|
+
self, devices: Set[SubSystem], timeout_sec: float = 10.0
|
|
189
187
|
) -> None:
|
|
190
188
|
try:
|
|
191
189
|
await asyncio.wait_for(
|
|
192
|
-
self._do_check_devices_after_update(devices),
|
|
193
|
-
timeout=timeout_sec or self._check_device_update_timeout,
|
|
190
|
+
self._do_check_devices_after_update(devices), timeout=timeout_sec
|
|
194
191
|
)
|
|
195
192
|
except asyncio.TimeoutError:
|
|
196
193
|
raise RuntimeError("Device failed to come back after firmware update")
|
|
@@ -10,14 +10,12 @@ class AbstractEmulator(ABC):
|
|
|
10
10
|
"""Handle a command and return a response."""
|
|
11
11
|
...
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
@staticmethod
|
|
14
|
+
def get_terminator() -> bytes:
|
|
14
15
|
"""Get the command terminator for messages coming from PI."""
|
|
15
16
|
return b"\r\n\r\n"
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
@staticmethod
|
|
19
|
+
def get_ack() -> bytes:
|
|
18
20
|
"""Get the command ack send to the PI."""
|
|
19
21
|
return b"ok\r\nok\r\n"
|
|
20
|
-
|
|
21
|
-
def get_autoack(self) -> bool:
|
|
22
|
-
"""Should this system automatically acknowledge messages?"""
|
|
23
|
-
return True
|
|
@@ -27,15 +27,12 @@ class ConnectionHandler:
|
|
|
27
27
|
try:
|
|
28
28
|
response = self._emulator.handle(line.decode().strip())
|
|
29
29
|
if response:
|
|
30
|
-
|
|
31
|
-
logger.debug(
|
|
32
|
-
writer.write(
|
|
30
|
+
response = f"{response}\r\n"
|
|
31
|
+
logger.debug("%s Sending: %s", emulator_name, response)
|
|
32
|
+
writer.write(response.encode())
|
|
33
33
|
except Exception as e:
|
|
34
34
|
logger.exception("%s exception", emulator_name)
|
|
35
|
-
writer.write(
|
|
36
|
-
f"Error: {str(e)} ".encode() + self._emulator.get_terminator()
|
|
37
|
-
)
|
|
35
|
+
writer.write(f"Error: {str(e)}\r\n".encode())
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
writer.write(self._emulator.get_ack())
|
|
37
|
+
writer.write(self._emulator.get_ack())
|
|
41
38
|
await writer.drain()
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
The purpose is to provide a fake backend that responds to GCODE commands.
|
|
4
4
|
"""
|
|
5
|
-
|
|
6
5
|
import logging
|
|
7
6
|
from time import sleep
|
|
8
7
|
from typing import (
|
|
@@ -51,7 +50,6 @@ class HeaterShakerEmulator(AbstractEmulator):
|
|
|
51
50
|
GCODE.CLOSE_LABWARE_LATCH.value: self._close_labware_latch,
|
|
52
51
|
GCODE.GET_LABWARE_LATCH_STATE.value: self._get_labware_latch_state,
|
|
53
52
|
GCODE.DEACTIVATE_HEATER.value: self._deactivate_heater,
|
|
54
|
-
GCODE.GET_ERROR_STATE.value: self._get_error_state,
|
|
55
53
|
}
|
|
56
54
|
self.reset()
|
|
57
55
|
|
|
@@ -62,6 +60,7 @@ class HeaterShakerEmulator(AbstractEmulator):
|
|
|
62
60
|
return None if not joined else joined
|
|
63
61
|
|
|
64
62
|
def reset(self) -> None:
|
|
63
|
+
|
|
65
64
|
self._temperature = Temperature(
|
|
66
65
|
per_tick=self._settings.temperature.degrees_per_tick,
|
|
67
66
|
current=self._settings.temperature.starting,
|
|
@@ -146,14 +145,6 @@ class HeaterShakerEmulator(AbstractEmulator):
|
|
|
146
145
|
self._temperature.deactivate(TEMPERATURE_ROOM)
|
|
147
146
|
return "M106"
|
|
148
147
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def get_terminator(self) -> bytes:
|
|
148
|
+
@staticmethod
|
|
149
|
+
def get_terminator() -> bytes:
|
|
153
150
|
return b"\n"
|
|
154
|
-
|
|
155
|
-
def get_ack(self) -> bytes:
|
|
156
|
-
return HS_ACK.encode()
|
|
157
|
-
|
|
158
|
-
def get_autoack(self) -> bool:
|
|
159
|
-
return False
|
|
@@ -90,7 +90,7 @@ class Settings(BaseSettings):
|
|
|
90
90
|
)
|
|
91
91
|
thermocycler: ThermocyclerSettings = ThermocyclerSettings(
|
|
92
92
|
serial_number="thermocycler_emulator",
|
|
93
|
-
model="
|
|
93
|
+
model="v02",
|
|
94
94
|
version="v1.1.0",
|
|
95
95
|
lid_temperature=TemperatureModelSettings(),
|
|
96
96
|
plate_temperature=TemperatureModelSettings(),
|
|
@@ -5,15 +5,8 @@ The purpose is to provide a fake backend that responds to GCODE commands.
|
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
7
|
from typing import Optional
|
|
8
|
-
from opentrons.drivers.thermocycler.driver import
|
|
9
|
-
GCODE,
|
|
10
|
-
TC_GEN2_ACK,
|
|
11
|
-
TC_GEN2_SERIAL_ACK,
|
|
12
|
-
TC_ACK as TC_GEN1_ACK,
|
|
13
|
-
SERIAL_ACK as TC_GEN1_SERIAL_ACK,
|
|
14
|
-
)
|
|
8
|
+
from opentrons.drivers.thermocycler.driver import GCODE
|
|
15
9
|
from opentrons.drivers.types import ThermocyclerLidStatus
|
|
16
|
-
from opentrons.hardware_control.modules.types import ThermocyclerModuleModel
|
|
17
10
|
from opentrons.hardware_control.emulation.parser import Parser, Command
|
|
18
11
|
from opentrons.hardware_control.emulation.settings import ThermocyclerSettings
|
|
19
12
|
|
|
@@ -36,31 +29,13 @@ class ThermocyclerEmulator(AbstractEmulator):
|
|
|
36
29
|
def __init__(self, parser: Parser, settings: ThermocyclerSettings) -> None:
|
|
37
30
|
self._parser = parser
|
|
38
31
|
self._settings = settings
|
|
39
|
-
# I hate this. These modules do not return anything like this for their actual versions
|
|
40
|
-
# (gen2 returns "Opentrons-thermocycler-gen2" for instance) and this is not what any of
|
|
41
|
-
# the settings anywhere use.
|
|
42
|
-
self._model = (
|
|
43
|
-
ThermocyclerModuleModel.THERMOCYCLER_V1
|
|
44
|
-
if settings.model in ["thermocyclerModuleV1", "v1", "v01"]
|
|
45
|
-
else ThermocyclerModuleModel.THERMOCYCLER_V2
|
|
46
|
-
)
|
|
47
|
-
self._terminator = (
|
|
48
|
-
TC_GEN1_SERIAL_ACK
|
|
49
|
-
if self._model is ThermocyclerModuleModel.THERMOCYCLER_V1
|
|
50
|
-
else TC_GEN2_SERIAL_ACK
|
|
51
|
-
)
|
|
52
|
-
self._ack = (
|
|
53
|
-
TC_GEN1_ACK
|
|
54
|
-
if self._model is ThermocyclerModuleModel.THERMOCYCLER_V1
|
|
55
|
-
else TC_GEN2_ACK
|
|
56
|
-
)
|
|
57
32
|
self.reset()
|
|
58
33
|
|
|
59
34
|
def handle(self, line: str) -> Optional[str]:
|
|
60
35
|
"""Handle a line"""
|
|
61
36
|
results = (self._handle(c) for c in self._parser.parse(line))
|
|
62
|
-
joined = " ".join(
|
|
63
|
-
return
|
|
37
|
+
joined = " ".join(r for r in results if r)
|
|
38
|
+
return None if not joined else joined
|
|
64
39
|
|
|
65
40
|
def reset(self) -> None:
|
|
66
41
|
self._lid_temperature = Temperature(
|
|
@@ -75,12 +50,6 @@ class ThermocyclerEmulator(AbstractEmulator):
|
|
|
75
50
|
self.plate_volume = util.OptionalValue[float]()
|
|
76
51
|
self.plate_ramp_rate = util.OptionalValue[float]()
|
|
77
52
|
|
|
78
|
-
def _pref(self, command: Command) -> str:
|
|
79
|
-
if self._model is ThermocyclerModuleModel.THERMOCYCLER_V1:
|
|
80
|
-
return ""
|
|
81
|
-
else:
|
|
82
|
-
return f"{command.gcode} "
|
|
83
|
-
|
|
84
53
|
def _handle(self, command: Command) -> Optional[str]: # noqa: C901
|
|
85
54
|
"""
|
|
86
55
|
Handle a command.
|
|
@@ -93,7 +62,7 @@ class ThermocyclerEmulator(AbstractEmulator):
|
|
|
93
62
|
elif command.gcode == GCODE.CLOSE_LID:
|
|
94
63
|
self.lid_status = ThermocyclerLidStatus.CLOSED
|
|
95
64
|
elif command.gcode == GCODE.GET_LID_STATUS:
|
|
96
|
-
return
|
|
65
|
+
return f"Lid:{self.lid_status}"
|
|
97
66
|
elif command.gcode == GCODE.SET_LID_TEMP:
|
|
98
67
|
temperature = command.params["S"]
|
|
99
68
|
assert isinstance(
|
|
@@ -107,7 +76,7 @@ class ThermocyclerEmulator(AbstractEmulator):
|
|
|
107
76
|
f"H:none Total_H:none"
|
|
108
77
|
)
|
|
109
78
|
self._lid_temperature.tick()
|
|
110
|
-
return
|
|
79
|
+
return res
|
|
111
80
|
elif command.gcode == GCODE.EDIT_PID_PARAMS:
|
|
112
81
|
pass
|
|
113
82
|
elif command.gcode == GCODE.SET_PLATE_TEMP:
|
|
@@ -136,7 +105,7 @@ class ThermocyclerEmulator(AbstractEmulator):
|
|
|
136
105
|
f"Total_H:{plate_total_hold_time} "
|
|
137
106
|
)
|
|
138
107
|
self._plate_temperature.tick()
|
|
139
|
-
return
|
|
108
|
+
return res
|
|
140
109
|
elif command.gcode == GCODE.SET_RAMP_RATE:
|
|
141
110
|
self.plate_ramp_rate.val = command.params["S"]
|
|
142
111
|
elif command.gcode == GCODE.DEACTIVATE_ALL:
|
|
@@ -147,34 +116,13 @@ class ThermocyclerEmulator(AbstractEmulator):
|
|
|
147
116
|
elif command.gcode == GCODE.DEACTIVATE_BLOCK:
|
|
148
117
|
self._plate_temperature.deactivate(temperature=util.TEMPERATURE_ROOM)
|
|
149
118
|
elif command.gcode == GCODE.DEVICE_INFO:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
f"version:{self._settings.version}"
|
|
157
|
-
)
|
|
158
|
-
else:
|
|
159
|
-
return (
|
|
160
|
-
command.gcode
|
|
161
|
-
+ " "
|
|
162
|
-
+ (
|
|
163
|
-
f"FW:{self._settings.version} "
|
|
164
|
-
f"HW:{self._settings.model} "
|
|
165
|
-
f"SerialNo:{self._settings.serial_number}"
|
|
166
|
-
)
|
|
167
|
-
)
|
|
168
|
-
elif command.gcode == GCODE.GET_ERROR_STATE:
|
|
169
|
-
if self._model is ThermocyclerModuleModel.THERMOCYCLER_V2:
|
|
170
|
-
return self._pref(command) + self._ack + self._pref(command)
|
|
171
|
-
return self._pref(command)
|
|
172
|
-
|
|
173
|
-
def get_terminator(self) -> bytes:
|
|
174
|
-
return self._terminator.encode()
|
|
175
|
-
|
|
176
|
-
def get_ack(self) -> bytes:
|
|
177
|
-
return self._ack.encode()
|
|
119
|
+
return (
|
|
120
|
+
f"serial:{self._settings.serial_number} "
|
|
121
|
+
f"model:{self._settings.model} "
|
|
122
|
+
f"version:{self._settings.version}"
|
|
123
|
+
)
|
|
124
|
+
return None
|
|
178
125
|
|
|
179
|
-
|
|
180
|
-
|
|
126
|
+
@staticmethod
|
|
127
|
+
def get_terminator() -> bytes:
|
|
128
|
+
return b"\r\n"
|
|
@@ -2,11 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
|
-
from typing import TYPE_CHECKING, List, Optional, Union
|
|
5
|
+
from typing import TYPE_CHECKING, List, Optional, Union
|
|
6
6
|
from glob import glob
|
|
7
7
|
|
|
8
|
-
from opentrons_shared_data.errors.exceptions import EnumeratedError
|
|
9
|
-
|
|
10
8
|
from opentrons.config import IS_ROBOT, IS_LINUX
|
|
11
9
|
from opentrons.drivers.rpi_drivers import types, interfaces, usb, usb_simulator
|
|
12
10
|
from opentrons.hardware_control.emulation.module_server.helpers import (
|
|
@@ -21,16 +19,8 @@ from opentrons.hardware_control.modules.module_calibration import (
|
|
|
21
19
|
from opentrons.hardware_control.modules.types import ModuleAtPort, ModuleType
|
|
22
20
|
from opentrons.hardware_control.modules import SimulatingModuleAtPort
|
|
23
21
|
|
|
24
|
-
|
|
25
22
|
from opentrons.types import Point
|
|
26
|
-
from .types import
|
|
27
|
-
AionotifyEvent,
|
|
28
|
-
BoardRevision,
|
|
29
|
-
OT3Mount,
|
|
30
|
-
StatusBarUpdateEvent,
|
|
31
|
-
HardwareEvent,
|
|
32
|
-
AsynchronousModuleErrorNotification,
|
|
33
|
-
)
|
|
23
|
+
from .types import AionotifyEvent, BoardRevision, OT3Mount, StatusBarUpdateEvent
|
|
34
24
|
from . import modules
|
|
35
25
|
|
|
36
26
|
if TYPE_CHECKING:
|
|
@@ -61,21 +51,10 @@ class AttachedModulesControl:
|
|
|
61
51
|
self,
|
|
62
52
|
api: Union["API", "OT3API"],
|
|
63
53
|
usb: interfaces.USBDriverInterface,
|
|
64
|
-
event_callback: Callable[[HardwareEvent], None],
|
|
65
54
|
) -> None:
|
|
66
55
|
self._available_modules: List[modules.AbstractModule] = []
|
|
67
56
|
self._api = api
|
|
68
57
|
self._usb = usb
|
|
69
|
-
self._event_callback = event_callback
|
|
70
|
-
if not IS_ROBOT and not api.is_simulator:
|
|
71
|
-
# Start task that registers emulated modules.
|
|
72
|
-
self._emulation_listen_task: asyncio.Task[
|
|
73
|
-
None
|
|
74
|
-
] | None = api.loop.create_task(
|
|
75
|
-
listen_module_connection(self.register_modules)
|
|
76
|
-
)
|
|
77
|
-
else:
|
|
78
|
-
self._emulation_listen_task = None
|
|
79
58
|
|
|
80
59
|
def subscribe_to_api_event(self, module: modules.AbstractModule) -> None:
|
|
81
60
|
self._api.add_status_bar_listener(module.event_listener)
|
|
@@ -85,20 +64,22 @@ class AttachedModulesControl:
|
|
|
85
64
|
cls,
|
|
86
65
|
api_instance: Union["API", "OT3API"],
|
|
87
66
|
board_revision: BoardRevision,
|
|
88
|
-
event_callback: Callable[[HardwareEvent], None],
|
|
89
67
|
) -> AttachedModulesControl:
|
|
90
68
|
usb_instance = (
|
|
91
69
|
usb.USBBus(board_revision)
|
|
92
70
|
if not api_instance.is_simulator and IS_ROBOT
|
|
93
71
|
else usb_simulator.USBBusSimulator()
|
|
94
72
|
)
|
|
95
|
-
mc_instance = cls(
|
|
96
|
-
api=api_instance, usb=usb_instance, event_callback=event_callback
|
|
97
|
-
)
|
|
73
|
+
mc_instance = cls(api=api_instance, usb=usb_instance)
|
|
98
74
|
|
|
99
75
|
if not api_instance.is_simulator:
|
|
100
76
|
# Do an initial scan of modules.
|
|
101
77
|
await mc_instance.register_modules(mc_instance.scan())
|
|
78
|
+
if not IS_ROBOT:
|
|
79
|
+
# Start task that registers emulated modules.
|
|
80
|
+
api_instance.loop.create_task(
|
|
81
|
+
listen_module_connection(mc_instance.register_modules)
|
|
82
|
+
)
|
|
102
83
|
|
|
103
84
|
return mc_instance
|
|
104
85
|
|
|
@@ -106,37 +87,6 @@ class AttachedModulesControl:
|
|
|
106
87
|
def available_modules(self) -> List[modules.AbstractModule]:
|
|
107
88
|
return self._available_modules
|
|
108
89
|
|
|
109
|
-
async def clean_up(self) -> None:
|
|
110
|
-
"""Clean up all registered modules and emulator scanning tasks (if any)."""
|
|
111
|
-
for module in self._available_modules:
|
|
112
|
-
await module.cleanup()
|
|
113
|
-
if self._emulation_listen_task is not None:
|
|
114
|
-
self._emulation_listen_task.cancel("cleanup")
|
|
115
|
-
try:
|
|
116
|
-
await self._emulation_listen_task
|
|
117
|
-
except asyncio.CancelledError:
|
|
118
|
-
pass
|
|
119
|
-
except Exception:
|
|
120
|
-
log.exception("Exception cleaning up emulation listen task")
|
|
121
|
-
finally:
|
|
122
|
-
self._emulation_listen_task = None
|
|
123
|
-
|
|
124
|
-
async def register_simulated_module(
|
|
125
|
-
self,
|
|
126
|
-
simulated_usb_port: types.USBPort,
|
|
127
|
-
type: modules.ModuleType,
|
|
128
|
-
sim_model: str,
|
|
129
|
-
) -> modules.AbstractModule:
|
|
130
|
-
"""Register a simulated module."""
|
|
131
|
-
module = await self.build_module(
|
|
132
|
-
"", simulated_usb_port, type, sim_model, sim_serial_number=None
|
|
133
|
-
)
|
|
134
|
-
self._available_modules.append(module)
|
|
135
|
-
self._available_modules = sorted(
|
|
136
|
-
self._available_modules, key=modules.AbstractModule.sort_key
|
|
137
|
-
)
|
|
138
|
-
return module
|
|
139
|
-
|
|
140
90
|
async def build_module(
|
|
141
91
|
self,
|
|
142
92
|
port: str,
|
|
@@ -155,7 +105,6 @@ class AttachedModulesControl:
|
|
|
155
105
|
sim_model=sim_model,
|
|
156
106
|
sim_serial_number=sim_serial_number,
|
|
157
107
|
disconnected_callback=self._disconnected_callback,
|
|
158
|
-
error_callback=self._async_error_callback,
|
|
159
108
|
)
|
|
160
109
|
last_event = StatusBarUpdateEvent(
|
|
161
110
|
self._api.get_status_bar_state(), self._api.get_status_bar_enabled()
|
|
@@ -172,29 +121,6 @@ class AttachedModulesControl:
|
|
|
172
121
|
self._api.loop,
|
|
173
122
|
)
|
|
174
123
|
|
|
175
|
-
def _async_error_callback(
|
|
176
|
-
self,
|
|
177
|
-
exc: Exception,
|
|
178
|
-
model: str,
|
|
179
|
-
port: str,
|
|
180
|
-
serial: str | None,
|
|
181
|
-
) -> None:
|
|
182
|
-
"""Used by the module to indicate it saw an error from its data poller."""
|
|
183
|
-
try:
|
|
184
|
-
self._api.loop.call_soon(
|
|
185
|
-
self._event_callback,
|
|
186
|
-
AsynchronousModuleErrorNotification(
|
|
187
|
-
exception=EnumeratedError.ensure(exc),
|
|
188
|
-
module_serial=serial,
|
|
189
|
-
module_model=modules.module_model_from_string(model),
|
|
190
|
-
port=port,
|
|
191
|
-
),
|
|
192
|
-
)
|
|
193
|
-
except Exception:
|
|
194
|
-
log.exception(
|
|
195
|
-
f"Async error callback for module {model} {serial} at {port} for exc {exc} failed"
|
|
196
|
-
)
|
|
197
|
-
|
|
198
124
|
async def unregister_modules(
|
|
199
125
|
self,
|
|
200
126
|
mods_at_ports: Union[
|
|
@@ -27,9 +27,7 @@ from .types import (
|
|
|
27
27
|
LiveData,
|
|
28
28
|
ModuleData,
|
|
29
29
|
ModuleDataValidator,
|
|
30
|
-
module_model_from_string,
|
|
31
30
|
)
|
|
32
|
-
|
|
33
31
|
from .errors import (
|
|
34
32
|
UpdateError,
|
|
35
33
|
AbsorbanceReaderDisconnectedError,
|
|
@@ -68,5 +66,4 @@ __all__ = [
|
|
|
68
66
|
"FlexStackerStatus",
|
|
69
67
|
"PlatformState",
|
|
70
68
|
"StackerAxisState",
|
|
71
|
-
"module_model_from_string",
|
|
72
69
|
]
|
|
@@ -21,7 +21,6 @@ from opentrons.hardware_control.poller import Poller, Reader
|
|
|
21
21
|
from opentrons.hardware_control.modules import mod_abc
|
|
22
22
|
from opentrons.hardware_control.modules.types import (
|
|
23
23
|
ModuleDisconnectedCallback,
|
|
24
|
-
ModuleErrorCallback,
|
|
25
24
|
ModuleType,
|
|
26
25
|
AbsorbanceReaderStatus,
|
|
27
26
|
LiveData,
|
|
@@ -105,13 +104,12 @@ class AbsorbanceReader(mod_abc.AbstractModule):
|
|
|
105
104
|
port: str,
|
|
106
105
|
usb_port: USBPort,
|
|
107
106
|
hw_control_loop: asyncio.AbstractEventLoop,
|
|
108
|
-
execution_manager: ExecutionManager,
|
|
109
|
-
disconnected_callback: ModuleDisconnectedCallback,
|
|
110
|
-
error_callback: ModuleErrorCallback,
|
|
107
|
+
execution_manager: Optional[ExecutionManager] = None,
|
|
111
108
|
poll_interval_seconds: Optional[float] = None,
|
|
112
109
|
simulating: bool = False,
|
|
113
110
|
sim_model: Optional[str] = None,
|
|
114
111
|
sim_serial_number: Optional[str] = None,
|
|
112
|
+
disconnected_callback: ModuleDisconnectedCallback = None,
|
|
115
113
|
) -> "AbsorbanceReader":
|
|
116
114
|
"""
|
|
117
115
|
Build and connect to an AbsorbanceReader
|
|
@@ -154,7 +152,6 @@ class AbsorbanceReader(mod_abc.AbstractModule):
|
|
|
154
152
|
hw_control_loop=hw_control_loop,
|
|
155
153
|
execution_manager=execution_manager,
|
|
156
154
|
disconnected_callback=disconnected_callback,
|
|
157
|
-
error_callback=error_callback,
|
|
158
155
|
)
|
|
159
156
|
|
|
160
157
|
try:
|
|
@@ -173,9 +170,8 @@ class AbsorbanceReader(mod_abc.AbstractModule):
|
|
|
173
170
|
poller: Poller,
|
|
174
171
|
device_info: Mapping[str, str],
|
|
175
172
|
hw_control_loop: asyncio.AbstractEventLoop,
|
|
176
|
-
execution_manager: ExecutionManager,
|
|
177
|
-
disconnected_callback: ModuleDisconnectedCallback,
|
|
178
|
-
error_callback: ModuleErrorCallback,
|
|
173
|
+
execution_manager: Optional[ExecutionManager] = None,
|
|
174
|
+
disconnected_callback: ModuleDisconnectedCallback = None,
|
|
179
175
|
) -> None:
|
|
180
176
|
"""
|
|
181
177
|
Constructor
|
|
@@ -197,7 +193,6 @@ class AbsorbanceReader(mod_abc.AbstractModule):
|
|
|
197
193
|
hw_control_loop=hw_control_loop,
|
|
198
194
|
execution_manager=execution_manager,
|
|
199
195
|
disconnected_callback=disconnected_callback,
|
|
200
|
-
error_callback=error_callback,
|
|
201
196
|
)
|
|
202
197
|
self._device_info = device_info
|
|
203
198
|
self._reader = reader
|
|
@@ -376,5 +371,3 @@ class AbsorbanceReader(mod_abc.AbstractModule):
|
|
|
376
371
|
self._error = str(error)
|
|
377
372
|
if isinstance(error, AbsorbanceReaderDisconnectedError):
|
|
378
373
|
self.disconnected_callback()
|
|
379
|
-
else:
|
|
380
|
-
self.error_callback(error)
|
|
@@ -44,7 +44,6 @@ from opentrons.hardware_control.modules.types import (
|
|
|
44
44
|
FlexStackerStatus,
|
|
45
45
|
HopperDoorState,
|
|
46
46
|
LatchState,
|
|
47
|
-
ModuleErrorCallback,
|
|
48
47
|
ModuleDisconnectedCallback,
|
|
49
48
|
ModuleType,
|
|
50
49
|
PlatformState,
|
|
@@ -216,13 +215,12 @@ class FlexStacker(mod_abc.AbstractModule):
|
|
|
216
215
|
port: str,
|
|
217
216
|
usb_port: USBPort,
|
|
218
217
|
hw_control_loop: asyncio.AbstractEventLoop,
|
|
219
|
-
execution_manager: ExecutionManager,
|
|
220
|
-
|
|
221
|
-
error_callback: ModuleErrorCallback,
|
|
222
|
-
poll_interval_seconds: float | None = None,
|
|
218
|
+
execution_manager: Optional[ExecutionManager] = None,
|
|
219
|
+
poll_interval_seconds: Optional[float] = None,
|
|
223
220
|
simulating: bool = False,
|
|
224
221
|
sim_model: Optional[str] = None,
|
|
225
222
|
sim_serial_number: Optional[str] = None,
|
|
223
|
+
disconnected_callback: ModuleDisconnectedCallback = None,
|
|
226
224
|
) -> "FlexStacker":
|
|
227
225
|
"""
|
|
228
226
|
Build a FlexStacker
|
|
@@ -261,9 +259,11 @@ class FlexStacker(mod_abc.AbstractModule):
|
|
|
261
259
|
hw_control_loop=hw_control_loop,
|
|
262
260
|
execution_manager=execution_manager,
|
|
263
261
|
disconnected_callback=disconnected_callback,
|
|
264
|
-
error_callback=error_callback,
|
|
265
262
|
)
|
|
266
263
|
|
|
264
|
+
# Set initialized callback
|
|
265
|
+
reader.set_initialized_callback(module._initialized_callback)
|
|
266
|
+
|
|
267
267
|
# Enable stallguard
|
|
268
268
|
for axis, config in STALLGUARD_CONFIG.items():
|
|
269
269
|
await driver.set_stallguard_threshold(
|
|
@@ -286,9 +286,8 @@ class FlexStacker(mod_abc.AbstractModule):
|
|
|
286
286
|
poller: Poller,
|
|
287
287
|
device_info: Mapping[str, str],
|
|
288
288
|
hw_control_loop: asyncio.AbstractEventLoop,
|
|
289
|
-
execution_manager: ExecutionManager,
|
|
290
|
-
disconnected_callback: ModuleDisconnectedCallback,
|
|
291
|
-
error_callback: ModuleErrorCallback,
|
|
289
|
+
execution_manager: Optional[ExecutionManager] = None,
|
|
290
|
+
disconnected_callback: ModuleDisconnectedCallback = None,
|
|
292
291
|
):
|
|
293
292
|
super().__init__(
|
|
294
293
|
port=port,
|
|
@@ -296,7 +295,6 @@ class FlexStacker(mod_abc.AbstractModule):
|
|
|
296
295
|
hw_control_loop=hw_control_loop,
|
|
297
296
|
execution_manager=execution_manager,
|
|
298
297
|
disconnected_callback=disconnected_callback,
|
|
299
|
-
error_callback=error_callback,
|
|
300
298
|
)
|
|
301
299
|
self._device_info = device_info
|
|
302
300
|
self._driver = driver
|
|
@@ -306,20 +304,12 @@ class FlexStacker(mod_abc.AbstractModule):
|
|
|
306
304
|
self._stacker_status = FlexStackerStatus.IDLE
|
|
307
305
|
self._last_status_bar_event: Optional[StatusBarUpdateEvent] = None
|
|
308
306
|
self._should_identify = False
|
|
309
|
-
# Set initialized callback
|
|
310
|
-
self._unsubscribe_init = reader.set_initialized_callback(
|
|
311
|
-
self._initialized_callback
|
|
312
|
-
)
|
|
313
|
-
self._unsubscribe_error = reader.set_error_callback(self._async_error_callback)
|
|
314
307
|
|
|
315
308
|
async def _initialized_callback(self) -> None:
|
|
316
309
|
"""Called by the reader once the module is initialized."""
|
|
317
310
|
if self._last_status_bar_event:
|
|
318
311
|
await self._handle_status_bar_event(self._last_status_bar_event)
|
|
319
312
|
|
|
320
|
-
def _async_error_callback(self, exception: Exception) -> None:
|
|
321
|
-
self.error_callback(exception)
|
|
322
|
-
|
|
323
313
|
async def cleanup(self) -> None:
|
|
324
314
|
"""Stop the poller task"""
|
|
325
315
|
await self._poller.stop()
|
|
@@ -874,27 +864,10 @@ class FlexStackerReader(Reader):
|
|
|
874
864
|
self.installation_detected = False
|
|
875
865
|
self._refresh_state = False
|
|
876
866
|
self._initialized_callback: Optional[Callable[[], Awaitable[None]]] = None
|
|
877
|
-
self._error_callback: Optional[Callable[[Exception], None]] = None
|
|
878
867
|
|
|
879
|
-
def set_initialized_callback(
|
|
880
|
-
self, callback: Callable[[], Awaitable[None]]
|
|
881
|
-
) -> Callable[[], None]:
|
|
868
|
+
def set_initialized_callback(self, callback: Callable[[], Awaitable[None]]) -> None:
|
|
882
869
|
"""Sets the callback used when done initializing the module."""
|
|
883
870
|
self._initialized_callback = callback
|
|
884
|
-
return self._remove_init_callback
|
|
885
|
-
|
|
886
|
-
def _remove_init_callback(self) -> None:
|
|
887
|
-
self._initialized_callback = None
|
|
888
|
-
|
|
889
|
-
def set_error_callback(
|
|
890
|
-
self, error_callback: Callable[[Exception], None]
|
|
891
|
-
) -> Callable[[], None]:
|
|
892
|
-
"""Register a handler for asynchronous hardware errors."""
|
|
893
|
-
self._error_callback = error_callback
|
|
894
|
-
return self._remove_error_callback
|
|
895
|
-
|
|
896
|
-
def _remove_error_callback(self) -> None:
|
|
897
|
-
self._error_callback = None
|
|
898
871
|
|
|
899
872
|
async def read(self) -> None:
|
|
900
873
|
await self.get_door_closed()
|
|
@@ -969,8 +942,6 @@ class FlexStackerReader(Reader):
|
|
|
969
942
|
if exception is None:
|
|
970
943
|
self.error = None
|
|
971
944
|
else:
|
|
972
|
-
if self._error_callback:
|
|
973
|
-
self._error_callback(exception)
|
|
974
945
|
try:
|
|
975
946
|
self.error = str(exception.args[0])
|
|
976
947
|
except Exception:
|