opentrons 8.7.0a7__py3-none-any.whl → 8.7.0a9__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 +61 -122
- 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.0a9.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.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.0a9.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/licenses/LICENSE +0 -0
opentrons/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '8.7.
|
|
32
|
-
__version_tuple__ = version_tuple = (8, 7, 0, '
|
|
31
|
+
__version__ = version = '8.7.0a9'
|
|
32
|
+
__version_tuple__ = version_tuple = (8, 7, 0, 'a9')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Optional, List, Type
|
|
6
6
|
|
|
7
7
|
from opentrons.drivers.command_builder import CommandBuilder
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ class SerialConnection:
|
|
|
25
25
|
port: str,
|
|
26
26
|
baud_rate: int,
|
|
27
27
|
timeout: float,
|
|
28
|
-
loop: asyncio.AbstractEventLoop
|
|
28
|
+
loop: Optional[asyncio.AbstractEventLoop],
|
|
29
29
|
reset_buffer_before_write: bool,
|
|
30
30
|
) -> AsyncSerial:
|
|
31
31
|
return await AsyncSerial.create(
|
|
@@ -43,11 +43,11 @@ class SerialConnection:
|
|
|
43
43
|
baud_rate: int,
|
|
44
44
|
timeout: float,
|
|
45
45
|
ack: str,
|
|
46
|
-
name: str
|
|
46
|
+
name: Optional[str] = None,
|
|
47
47
|
retry_wait_time_seconds: float = 0.1,
|
|
48
|
-
loop: asyncio.AbstractEventLoop
|
|
49
|
-
error_keyword: str
|
|
50
|
-
alarm_keyword: str
|
|
48
|
+
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
49
|
+
error_keyword: Optional[str] = None,
|
|
50
|
+
alarm_keyword: Optional[str] = None,
|
|
51
51
|
reset_buffer_before_write: bool = False,
|
|
52
52
|
error_codes: Type[BaseErrorCode] = DefaultErrorCodes,
|
|
53
53
|
) -> "SerialConnection":
|
|
@@ -133,7 +133,7 @@ class SerialConnection:
|
|
|
133
133
|
self._error_codes = error_codes
|
|
134
134
|
|
|
135
135
|
async def send_command(
|
|
136
|
-
self, command: CommandBuilder, retries: int = 0, timeout: float
|
|
136
|
+
self, command: CommandBuilder, retries: int = 0, timeout: Optional[float] = None
|
|
137
137
|
) -> str:
|
|
138
138
|
"""
|
|
139
139
|
Send a command and return the response.
|
|
@@ -165,7 +165,7 @@ class SerialConnection:
|
|
|
165
165
|
await self._serial.write(data=encoded_command)
|
|
166
166
|
|
|
167
167
|
async def send_data(
|
|
168
|
-
self, data: str, retries: int = 0, timeout: float
|
|
168
|
+
self, data: str, retries: int = 0, timeout: Optional[float] = None
|
|
169
169
|
) -> str:
|
|
170
170
|
"""
|
|
171
171
|
Send data and return the response.
|
|
@@ -184,7 +184,7 @@ class SerialConnection:
|
|
|
184
184
|
):
|
|
185
185
|
return await self._send_data(data=data, retries=retries)
|
|
186
186
|
|
|
187
|
-
async def _send_data(self, data: str, retries: int) -> str:
|
|
187
|
+
async def _send_data(self, data: str, retries: int = 0) -> str:
|
|
188
188
|
"""
|
|
189
189
|
Send data and return the response.
|
|
190
190
|
|
|
@@ -351,14 +351,14 @@ class AsyncResponseSerialConnection(SerialConnection):
|
|
|
351
351
|
baud_rate: int,
|
|
352
352
|
timeout: float,
|
|
353
353
|
ack: str,
|
|
354
|
-
name: str
|
|
354
|
+
name: Optional[str] = None,
|
|
355
355
|
retry_wait_time_seconds: float = 0.1,
|
|
356
|
-
loop: asyncio.AbstractEventLoop
|
|
357
|
-
error_keyword: str
|
|
358
|
-
alarm_keyword: str
|
|
356
|
+
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
357
|
+
error_keyword: Optional[str] = None,
|
|
358
|
+
alarm_keyword: Optional[str] = None,
|
|
359
359
|
reset_buffer_before_write: bool = False,
|
|
360
360
|
error_codes: Type[BaseErrorCode] = DefaultErrorCodes,
|
|
361
|
-
async_error_ack: str
|
|
361
|
+
async_error_ack: Optional[str] = None,
|
|
362
362
|
number_of_retries: int = 0,
|
|
363
363
|
) -> AsyncResponseSerialConnection:
|
|
364
364
|
"""
|
|
@@ -461,29 +461,11 @@ class AsyncResponseSerialConnection(SerialConnection):
|
|
|
461
461
|
self._alarm_keyword = alarm_keyword.lower()
|
|
462
462
|
self._async_error_ack = async_error_ack.lower()
|
|
463
463
|
|
|
464
|
-
async def
|
|
464
|
+
async def send_command(
|
|
465
465
|
self,
|
|
466
466
|
command: CommandBuilder,
|
|
467
|
-
retries: int =
|
|
467
|
+
retries: int | None = None,
|
|
468
468
|
timeout: float | None = None,
|
|
469
|
-
acks: int = 1,
|
|
470
|
-
) -> list[str]:
|
|
471
|
-
"""Send a command and return the responses.
|
|
472
|
-
|
|
473
|
-
Some commands result in multiple responses; collate them and return them all.
|
|
474
|
-
|
|
475
|
-
Args:
|
|
476
|
-
command: A command builder.
|
|
477
|
-
retries: number of times to retry in case of timeout
|
|
478
|
-
timeout: optional override of default timeout in seconds
|
|
479
|
-
acks: the number of acks to expect
|
|
480
|
-
"""
|
|
481
|
-
return await self.send_data_multiack(
|
|
482
|
-
data=command.build(), retries=retries, timeout=timeout, acks=acks
|
|
483
|
-
)
|
|
484
|
-
|
|
485
|
-
async def send_command(
|
|
486
|
-
self, command: CommandBuilder, retries: int = 0, timeout: float | None = None
|
|
487
469
|
) -> str:
|
|
488
470
|
"""
|
|
489
471
|
Send a command and return the response.
|
|
@@ -499,23 +481,12 @@ class AsyncResponseSerialConnection(SerialConnection):
|
|
|
499
481
|
"""
|
|
500
482
|
return await self.send_data(
|
|
501
483
|
data=command.build(),
|
|
502
|
-
retries=retries
|
|
484
|
+
retries=retries if retries is not None else self._number_of_retries,
|
|
503
485
|
timeout=timeout,
|
|
504
486
|
)
|
|
505
487
|
|
|
506
|
-
async def send_data_multiack(
|
|
507
|
-
self, data: str, retries: int = 0, timeout: float | None = None, acks: int = 1
|
|
508
|
-
) -> list[str]:
|
|
509
|
-
"""Send data and return all responses."""
|
|
510
|
-
async with super().send_data_lock, self._serial.timeout_override(
|
|
511
|
-
"timeout", timeout
|
|
512
|
-
):
|
|
513
|
-
return await self._send_data_multiack(
|
|
514
|
-
data=data, retries=retries or self._number_of_retries, acks=acks
|
|
515
|
-
)
|
|
516
|
-
|
|
517
488
|
async def send_data(
|
|
518
|
-
self, data: str, retries: int =
|
|
489
|
+
self, data: str, retries: int | None = None, timeout: float | None = None
|
|
519
490
|
) -> str:
|
|
520
491
|
"""
|
|
521
492
|
Send data and return the response.
|
|
@@ -533,102 +504,57 @@ class AsyncResponseSerialConnection(SerialConnection):
|
|
|
533
504
|
"timeout", timeout
|
|
534
505
|
):
|
|
535
506
|
return await self._send_data(
|
|
536
|
-
data=data,
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
async def _consume_responses(
|
|
540
|
-
self, acks: int
|
|
541
|
-
) -> AsyncIterator[tuple[Literal["response", "error", "empty-unknown"], bytes]]:
|
|
542
|
-
while acks > 0:
|
|
543
|
-
data = await self._serial.read_until(match=self._ack)
|
|
544
|
-
log.debug(f"{self._name}: Read <- {data!r}")
|
|
545
|
-
if self._async_error_ack.encode() in data:
|
|
546
|
-
yield "error", data
|
|
547
|
-
elif self._ack in data:
|
|
548
|
-
yield "response", data
|
|
549
|
-
acks -= 1
|
|
550
|
-
else:
|
|
551
|
-
# A read timeout, end
|
|
552
|
-
yield "empty-unknown", data
|
|
553
|
-
|
|
554
|
-
async def _send_one_retry(self, data: str, acks: int) -> list[str]:
|
|
555
|
-
data_encode = data.encode("utf-8")
|
|
556
|
-
log.debug(f"{self._name}: Write -> {data_encode!r}")
|
|
557
|
-
await self._serial.write(data=data_encode)
|
|
558
|
-
|
|
559
|
-
command_acks: list[bytes] = []
|
|
560
|
-
async_errors: list[bytes] = []
|
|
561
|
-
# consume responses before raising so we don't raise and orphan
|
|
562
|
-
# a response in the buffer
|
|
563
|
-
async for response_type, response in self._consume_responses(acks):
|
|
564
|
-
if response_type == "error":
|
|
565
|
-
async_errors.append(response)
|
|
566
|
-
elif response_type == "response":
|
|
567
|
-
command_acks.append(response)
|
|
568
|
-
else:
|
|
569
|
-
break
|
|
570
|
-
|
|
571
|
-
for async_error in async_errors:
|
|
572
|
-
# Remove ack from response
|
|
573
|
-
ackless_response = async_error.replace(self._ack, b"")
|
|
574
|
-
str_response = self.process_raw_response(
|
|
575
|
-
command=data, response=ackless_response.decode()
|
|
576
|
-
)
|
|
577
|
-
self.raise_on_error(response=str_response, request=data)
|
|
578
|
-
|
|
579
|
-
ackless_responses: list[str] = []
|
|
580
|
-
for command_ack in command_acks:
|
|
581
|
-
# Remove ack from response
|
|
582
|
-
ackless_response = command_ack.replace(self._ack, b"")
|
|
583
|
-
str_response = self.process_raw_response(
|
|
584
|
-
command=data, response=ackless_response.decode()
|
|
507
|
+
data=data,
|
|
508
|
+
retries=retries if retries is not None else self._number_of_retries,
|
|
585
509
|
)
|
|
586
|
-
self.raise_on_error(response=str_response, request=data)
|
|
587
|
-
ackless_responses.append(str_response)
|
|
588
|
-
return ackless_responses
|
|
589
510
|
|
|
590
|
-
async def
|
|
591
|
-
self, data: str, retries: int, acks: int
|
|
592
|
-
) -> list[str]:
|
|
511
|
+
async def _send_data(self, data: str, retries: int = 0) -> str:
|
|
593
512
|
"""
|
|
594
|
-
Send data and return the response
|
|
513
|
+
Send data and return the response.
|
|
595
514
|
|
|
596
515
|
Args:
|
|
597
516
|
data: The data to send.
|
|
598
517
|
retries: number of times to retry in case of timeout
|
|
599
|
-
acks: The number of expected command responses
|
|
600
|
-
|
|
601
|
-
This function retries (resends the command) up to (retries) times, and waits
|
|
602
|
-
for (acks) responses. It also listens for async errors. These are an older
|
|
603
|
-
mechanism where at the moment an error occurs, some modules will send a message
|
|
604
|
-
like async error ERR:202:whatever
|
|
605
518
|
|
|
606
|
-
|
|
607
|
-
sent the command or if they are sent before the final ack for the command is
|
|
608
|
-
sent. It will not catch async errors otherwise.
|
|
519
|
+
Returns: The command response
|
|
609
520
|
|
|
610
|
-
|
|
611
|
-
|
|
521
|
+
Raises: SerialException
|
|
522
|
+
"""
|
|
523
|
+
data_encode = data.encode()
|
|
612
524
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
525
|
+
for retry in range(retries + 1):
|
|
526
|
+
log.debug(f"{self._name}: Write -> {data_encode!r}")
|
|
527
|
+
await self._serial.write(data=data_encode)
|
|
616
528
|
|
|
617
|
-
|
|
529
|
+
response: List[bytes] = []
|
|
530
|
+
response.append(await self._serial.read_until(match=self._ack))
|
|
531
|
+
log.debug(f"{self._name}: Read <- {response[-1]!r}")
|
|
532
|
+
|
|
533
|
+
while self._async_error_ack.encode() in response[-1].lower():
|
|
534
|
+
# check for multiple a priori async errors
|
|
535
|
+
response.append(await self._serial.read_until(match=self._ack))
|
|
536
|
+
log.debug(f"{self._name}: Read <- {response[-1]!r}")
|
|
537
|
+
|
|
538
|
+
for r in response:
|
|
539
|
+
if self._async_error_ack.encode() in r:
|
|
540
|
+
# Remove ack from response
|
|
541
|
+
ackless_response = r.replace(self._ack, b"")
|
|
542
|
+
str_response = self.process_raw_response(
|
|
543
|
+
command=data, response=ackless_response.decode()
|
|
544
|
+
)
|
|
545
|
+
self.raise_on_error(response=str_response, request=data)
|
|
618
546
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
547
|
+
if self._ack in response[-1]:
|
|
548
|
+
# Remove ack from response
|
|
549
|
+
ackless_response = response[-1].replace(self._ack, b"")
|
|
550
|
+
str_response = self.process_raw_response(
|
|
551
|
+
command=data, response=ackless_response.decode()
|
|
552
|
+
)
|
|
553
|
+
self.raise_on_error(response=str_response, request=data)
|
|
554
|
+
return str_response
|
|
623
555
|
|
|
624
|
-
for retry in range(retries + 1):
|
|
625
|
-
responses = await self._send_one_retry(data, acks)
|
|
626
|
-
if responses:
|
|
627
|
-
return responses
|
|
628
556
|
log.info(f"{self._name}: retry number {retry}/{retries}")
|
|
557
|
+
|
|
629
558
|
await self.on_retry()
|
|
630
559
|
|
|
631
560
|
raise NoResponse(port=self._port, command=data)
|
|
632
|
-
|
|
633
|
-
async def _send_data(self, data: str, retries: int) -> str:
|
|
634
|
-
return (await self._send_data_multiack(data, retries, 1))[0]
|
|
@@ -461,7 +461,12 @@ class FlexStackerDriver(AbstractFlexStackerDriver):
|
|
|
461
461
|
command = GCODE.GET_TOF_MEASUREMENT.build_command().add_element(sensor.name)
|
|
462
462
|
if resend:
|
|
463
463
|
command.add_element("R")
|
|
464
|
-
|
|
464
|
+
|
|
465
|
+
# Note: We DONT want to auto resend the request if it fails, because the
|
|
466
|
+
# firmware will send the next frame id instead of the current one missed.
|
|
467
|
+
# So lets set `retries=0` so we only send the frame once and we can
|
|
468
|
+
# use the retry mechanism of the `get_tof_histogram` method instead.
|
|
469
|
+
resp = await self._connection.send_command(command, retries=0)
|
|
465
470
|
return self.parse_get_tof_measurement(resp)
|
|
466
471
|
|
|
467
472
|
async def get_tof_histogram(self, sensor: TOFSensor) -> TOFMeasurementResult:
|
|
@@ -27,7 +27,6 @@ class GCODE(str, Enum):
|
|
|
27
27
|
GET_LABWARE_LATCH_STATE = "M241"
|
|
28
28
|
DEACTIVATE_HEATER = "M106"
|
|
29
29
|
GET_RESET_REASON = "M114"
|
|
30
|
-
GET_ERROR_STATE = "M411"
|
|
31
30
|
|
|
32
31
|
|
|
33
32
|
HS_BAUDRATE = 115200
|
|
@@ -203,12 +202,3 @@ class HeaterShakerDriver(AbstractHeaterShakerDriver):
|
|
|
203
202
|
gcode=GCODE.DEACTIVATE_HEATER
|
|
204
203
|
)
|
|
205
204
|
await self._connection.send_command(command=c, retries=DEFAULT_COMMAND_RETRIES)
|
|
206
|
-
|
|
207
|
-
async def get_error_state(self) -> None:
|
|
208
|
-
"""Raise if the module is in an error state."""
|
|
209
|
-
await self._connection.send_multiack_command(
|
|
210
|
-
command=CommandBuilder(terminator=HS_COMMAND_TERMINATOR).add_gcode(
|
|
211
|
-
gcode=GCODE.GET_ERROR_STATE
|
|
212
|
-
),
|
|
213
|
-
acks=2,
|
|
214
|
-
)
|
|
@@ -55,7 +55,6 @@ class AbstractThermocyclerDriver(ABC):
|
|
|
55
55
|
temp: float,
|
|
56
56
|
hold_time: Optional[float] = None,
|
|
57
57
|
volume: Optional[float] = None,
|
|
58
|
-
ramp_rate: Optional[float] = None,
|
|
59
58
|
) -> None:
|
|
60
59
|
"""Send set plate temperature command"""
|
|
61
60
|
...
|
|
@@ -98,8 +97,3 @@ class AbstractThermocyclerDriver(ABC):
|
|
|
98
97
|
async def jog_lid(self, angle: float) -> None:
|
|
99
98
|
"""Send the Jog Lid command."""
|
|
100
99
|
...
|
|
101
|
-
|
|
102
|
-
@abstractmethod
|
|
103
|
-
async def get_error_state(self) -> None:
|
|
104
|
-
"""Raise if the thermocycler is in an error state."""
|
|
105
|
-
...
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
from enum import Enum
|
|
6
|
-
from typing import Optional, Dict, Union
|
|
6
|
+
from typing import Optional, Dict, Union
|
|
7
7
|
|
|
8
8
|
from opentrons.drivers import utils
|
|
9
9
|
from opentrons.drivers.command_builder import CommandBuilder
|
|
@@ -36,7 +36,6 @@ class GCODE(str, Enum):
|
|
|
36
36
|
DEVICE_INFO = "M115"
|
|
37
37
|
GET_RESET_REASON = "M114"
|
|
38
38
|
ENTER_PROGRAMMING = "dfu"
|
|
39
|
-
GET_ERROR_STATE = "M411"
|
|
40
39
|
|
|
41
40
|
|
|
42
41
|
LID_TARGET_DEFAULT = 105 # Degree celsius (floats)
|
|
@@ -74,7 +73,7 @@ class ThermocyclerDriverFactory:
|
|
|
74
73
|
@staticmethod
|
|
75
74
|
async def create(
|
|
76
75
|
port: str, loop: Optional[asyncio.AbstractEventLoop]
|
|
77
|
-
) -> ThermocyclerDriver
|
|
76
|
+
) -> ThermocyclerDriver:
|
|
78
77
|
"""
|
|
79
78
|
Create a thermocycler driver.
|
|
80
79
|
|
|
@@ -149,15 +148,10 @@ class ThermocyclerDriverFactory:
|
|
|
149
148
|
return response.startswith(GCODE.DEVICE_INFO)
|
|
150
149
|
|
|
151
150
|
|
|
152
|
-
|
|
153
|
-
"_ConnectionKind", SerialConnection, AsyncResponseSerialConnection
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
class _BaseThermocyclerDriver(AbstractThermocyclerDriver, Generic[_ConnectionKind]):
|
|
151
|
+
class ThermocyclerDriver(AbstractThermocyclerDriver):
|
|
158
152
|
def __init__(
|
|
159
153
|
self,
|
|
160
|
-
connection:
|
|
154
|
+
connection: SerialKind,
|
|
161
155
|
) -> None:
|
|
162
156
|
"""
|
|
163
157
|
Constructor
|
|
@@ -165,7 +159,7 @@ class _BaseThermocyclerDriver(AbstractThermocyclerDriver, Generic[_ConnectionKin
|
|
|
165
159
|
Args:
|
|
166
160
|
connection: SerialConnection to the thermocycler
|
|
167
161
|
"""
|
|
168
|
-
self._connection
|
|
162
|
+
self._connection = connection
|
|
169
163
|
|
|
170
164
|
async def connect(self) -> None:
|
|
171
165
|
"""Connect to thermocycler"""
|
|
@@ -232,7 +226,6 @@ class _BaseThermocyclerDriver(AbstractThermocyclerDriver, Generic[_ConnectionKin
|
|
|
232
226
|
temp: float,
|
|
233
227
|
hold_time: Optional[float] = None,
|
|
234
228
|
volume: Optional[float] = None,
|
|
235
|
-
ramp_rate: Optional[float] = None,
|
|
236
229
|
) -> None:
|
|
237
230
|
"""Send set plate temperature command"""
|
|
238
231
|
temp = min(BLOCK_TARGET_MAX, max(BLOCK_TARGET_MIN, temp))
|
|
@@ -335,15 +328,8 @@ class _BaseThermocyclerDriver(AbstractThermocyclerDriver, Generic[_ConnectionKin
|
|
|
335
328
|
"Gen1 Thermocyclers do not support the Jog Lid command."
|
|
336
329
|
)
|
|
337
330
|
|
|
338
|
-
async def get_error_state(self) -> None:
|
|
339
|
-
"""For the gen1, do nothing."""
|
|
340
|
-
pass
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
ThermocyclerDriver = _BaseThermocyclerDriver[SerialConnection]
|
|
344
331
|
|
|
345
|
-
|
|
346
|
-
class ThermocyclerDriverV2(_BaseThermocyclerDriver[AsyncResponseSerialConnection]):
|
|
332
|
+
class ThermocyclerDriverV2(ThermocyclerDriver):
|
|
347
333
|
"""
|
|
348
334
|
This driver is for Thermocycler model Gen2.
|
|
349
335
|
"""
|
|
@@ -357,38 +343,10 @@ class ThermocyclerDriverV2(_BaseThermocyclerDriver[AsyncResponseSerialConnection
|
|
|
357
343
|
"""
|
|
358
344
|
super().__init__(connection)
|
|
359
345
|
|
|
360
|
-
async def
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
volume: Optional[float] = None,
|
|
365
|
-
ramp_rate: Optional[float] = None,
|
|
366
|
-
) -> None:
|
|
367
|
-
"""Send set plate temperature command"""
|
|
368
|
-
temp = min(BLOCK_TARGET_MAX, max(BLOCK_TARGET_MIN, temp))
|
|
369
|
-
|
|
370
|
-
c = (
|
|
371
|
-
CommandBuilder(terminator=TC_COMMAND_TERMINATOR)
|
|
372
|
-
.add_gcode(gcode=GCODE.SET_PLATE_TEMP)
|
|
373
|
-
.add_float(
|
|
374
|
-
prefix="S", value=temp, precision=utils.TC_GCODE_ROUNDING_PRECISION
|
|
375
|
-
)
|
|
376
|
-
)
|
|
377
|
-
if hold_time is not None:
|
|
378
|
-
c = c.add_float(
|
|
379
|
-
prefix="H", value=hold_time, precision=utils.TC_GCODE_ROUNDING_PRECISION
|
|
380
|
-
)
|
|
381
|
-
if volume is not None:
|
|
382
|
-
c = c.add_float(
|
|
383
|
-
prefix="V", value=volume, precision=utils.TC_GCODE_ROUNDING_PRECISION
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
if ramp_rate is not None:
|
|
387
|
-
c = c.add_float(
|
|
388
|
-
prefix="R", value=ramp_rate, precision=utils.TC_GCODE_ROUNDING_PRECISION
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
await self._connection.send_command(command=c, retries=DEFAULT_COMMAND_RETRIES)
|
|
346
|
+
async def set_ramp_rate(self, ramp_rate: float) -> None:
|
|
347
|
+
"""Send a set ramp rate command"""
|
|
348
|
+
# This command is fully unsupported on TC Gen2
|
|
349
|
+
return None
|
|
392
350
|
|
|
393
351
|
async def get_device_info(self) -> Dict[str, str]:
|
|
394
352
|
"""Send get device info command"""
|
|
@@ -435,12 +393,3 @@ class ThermocyclerDriverV2(_BaseThermocyclerDriver[AsyncResponseSerialConnection
|
|
|
435
393
|
.add_element("O")
|
|
436
394
|
)
|
|
437
395
|
await self._connection.send_command(command=c, retries=1)
|
|
438
|
-
|
|
439
|
-
async def get_error_state(self) -> None:
|
|
440
|
-
"""Raise an error if the thermocycler is stuck in an error state."""
|
|
441
|
-
await self._connection.send_multiack_command(
|
|
442
|
-
command=CommandBuilder(terminator=TC_COMMAND_TERMINATOR).add_gcode(
|
|
443
|
-
gcode=GCODE.GET_ERROR_STATE
|
|
444
|
-
),
|
|
445
|
-
acks=2,
|
|
446
|
-
)
|
|
@@ -67,12 +67,10 @@ class SimulatingDriver(AbstractThermocyclerDriver):
|
|
|
67
67
|
temp: float,
|
|
68
68
|
hold_time: Optional[float] = None,
|
|
69
69
|
volume: Optional[float] = None,
|
|
70
|
-
ramp_rate: Optional[float] = None,
|
|
71
70
|
) -> None:
|
|
72
71
|
self._plate_temperature.target = temp
|
|
73
72
|
self._plate_temperature.current = temp
|
|
74
73
|
self._plate_temperature.hold = 0
|
|
75
|
-
self._ramp_rate = ramp_rate
|
|
76
74
|
|
|
77
75
|
@ensure_yield
|
|
78
76
|
async def get_plate_temperature(self) -> PlateTemperature:
|
|
@@ -126,7 +124,3 @@ class SimulatingDriver(AbstractThermocyclerDriver):
|
|
|
126
124
|
if angle < 0
|
|
127
125
|
else ThermocyclerLidStatus.OPEN
|
|
128
126
|
)
|
|
129
|
-
|
|
130
|
-
@ensure_yield
|
|
131
|
-
async def get_error_state(self) -> None:
|
|
132
|
-
return
|
|
@@ -44,7 +44,6 @@ from .execution_manager import ExecutionManagerProvider
|
|
|
44
44
|
from .pause_manager import PauseManager
|
|
45
45
|
from .module_control import AttachedModulesControl
|
|
46
46
|
from .types import (
|
|
47
|
-
AsynchronousModuleErrorNotification,
|
|
48
47
|
Axis,
|
|
49
48
|
CriticalPoint,
|
|
50
49
|
DoorState,
|
|
@@ -52,7 +51,6 @@ from .types import (
|
|
|
52
51
|
ErrorMessageNotification,
|
|
53
52
|
HardwareEventHandler,
|
|
54
53
|
HardwareAction,
|
|
55
|
-
HardwareEvent,
|
|
56
54
|
MotionChecks,
|
|
57
55
|
PauseType,
|
|
58
56
|
StatusBarState,
|
|
@@ -169,18 +167,6 @@ class API(
|
|
|
169
167
|
except Exception:
|
|
170
168
|
mod_log.exception("Errored during door state event callback")
|
|
171
169
|
|
|
172
|
-
def _send_module_notification(self, event: HardwareEvent) -> None:
|
|
173
|
-
if not isinstance(event, AsynchronousModuleErrorNotification):
|
|
174
|
-
return
|
|
175
|
-
mod_log.info(
|
|
176
|
-
f"Forwarding module event {event.event} for {event.module_model} {event.module_serial} at {event.port}"
|
|
177
|
-
)
|
|
178
|
-
for cb in self._callbacks:
|
|
179
|
-
try:
|
|
180
|
-
cb(event)
|
|
181
|
-
except Exception:
|
|
182
|
-
mod_log.exception("Errored during module asynchronous callback")
|
|
183
|
-
|
|
184
170
|
def _reset_last_mount(self) -> None:
|
|
185
171
|
self._last_moved_mount = None
|
|
186
172
|
|
|
@@ -261,9 +247,7 @@ class API(
|
|
|
261
247
|
)
|
|
262
248
|
await api_instance.cache_instruments()
|
|
263
249
|
module_controls = await AttachedModulesControl.build(
|
|
264
|
-
api_instance,
|
|
265
|
-
board_revision=backend.board_revision,
|
|
266
|
-
event_callback=api_instance._send_module_notification,
|
|
250
|
+
api_instance, board_revision=backend.board_revision
|
|
267
251
|
)
|
|
268
252
|
backend.module_controls = module_controls
|
|
269
253
|
checked_loop.create_task(backend.watch(loop=checked_loop))
|
|
@@ -322,9 +306,7 @@ class API(
|
|
|
322
306
|
)
|
|
323
307
|
await api_instance.cache_instruments()
|
|
324
308
|
module_controls = await AttachedModulesControl.build(
|
|
325
|
-
api_instance,
|
|
326
|
-
board_revision=backend.board_revision,
|
|
327
|
-
event_callback=api_instance._send_module_notification,
|
|
309
|
+
api_instance, board_revision=backend.board_revision
|
|
328
310
|
)
|
|
329
311
|
backend.module_controls = module_controls
|
|
330
312
|
await backend.watch()
|
|
@@ -1330,10 +1312,9 @@ class API(
|
|
|
1330
1312
|
self.is_simulator
|
|
1331
1313
|
), "Cannot build simulating module from non-simulating hardware control API"
|
|
1332
1314
|
|
|
1333
|
-
return await self._backend.module_controls.
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
),
|
|
1315
|
+
return await self._backend.module_controls.build_module(
|
|
1316
|
+
port="",
|
|
1317
|
+
usb_port=USBPort(name="", port_number=1, port_group=PortGroup.MAIN),
|
|
1337
1318
|
type=modules.ModuleType.from_model(model),
|
|
1338
1319
|
sim_model=model.value,
|
|
1339
1320
|
)
|
|
@@ -360,19 +360,13 @@ class Controller:
|
|
|
360
360
|
"""Run a probe and return the new position dict"""
|
|
361
361
|
return await self._smoothie_driver.probe_axis(axis, distance)
|
|
362
362
|
|
|
363
|
-
async def clean_up(self) -> None:
|
|
363
|
+
async def clean_up(self) -> None:
|
|
364
364
|
try:
|
|
365
365
|
loop = asyncio.get_event_loop()
|
|
366
366
|
except RuntimeError:
|
|
367
367
|
return
|
|
368
|
-
if hasattr(self, "_module_controls") and self._module_controls is not None:
|
|
369
|
-
await self._module_controls.clean_up()
|
|
370
368
|
if hasattr(self, "_event_watcher"):
|
|
371
|
-
if (
|
|
372
|
-
loop.is_running()
|
|
373
|
-
and self._event_watcher
|
|
374
|
-
and not self._event_watcher.closed
|
|
375
|
-
):
|
|
369
|
+
if loop.is_running() and self._event_watcher:
|
|
376
370
|
self._event_watcher.close()
|
|
377
371
|
if hasattr(self, "gpio_chardev"):
|
|
378
372
|
try:
|
|
@@ -1398,9 +1398,6 @@ class OT3Controller(FlexBackend):
|
|
|
1398
1398
|
except RuntimeError:
|
|
1399
1399
|
return
|
|
1400
1400
|
|
|
1401
|
-
if hasattr(self, "_module_controls") and self._module_controls is not None:
|
|
1402
|
-
await self._module_controls.clean_up()
|
|
1403
|
-
|
|
1404
1401
|
if hasattr(self, "_event_watcher"):
|
|
1405
1402
|
if (
|
|
1406
1403
|
loop.is_running()
|
|
@@ -1766,6 +1763,7 @@ class OT3Controller(FlexBackend):
|
|
|
1766
1763
|
max_allowed_grip_error: float,
|
|
1767
1764
|
hard_limit_lower: float,
|
|
1768
1765
|
hard_limit_upper: float,
|
|
1766
|
+
disable_geometry_grip_check: bool = False,
|
|
1769
1767
|
) -> None:
|
|
1770
1768
|
"""
|
|
1771
1769
|
Check if the gripper is at the expected location.
|
|
@@ -1808,6 +1806,7 @@ class OT3Controller(FlexBackend):
|
|
|
1808
1806
|
if (
|
|
1809
1807
|
current_gripper_position - expected_gripper_position_min
|
|
1810
1808
|
< -max_allowed_grip_error
|
|
1809
|
+
and not disable_geometry_grip_check
|
|
1811
1810
|
):
|
|
1812
1811
|
raise FailedGripperPickupError(
|
|
1813
1812
|
message="Failed to grip: jaws closed too far",
|
|
@@ -1821,6 +1820,7 @@ class OT3Controller(FlexBackend):
|
|
|
1821
1820
|
if (
|
|
1822
1821
|
current_gripper_position - expected_gripper_position_max
|
|
1823
1822
|
> max_allowed_grip_error
|
|
1823
|
+
and not disable_geometry_grip_check
|
|
1824
1824
|
):
|
|
1825
1825
|
raise FailedGripperPickupError(
|
|
1826
1826
|
message="Failed to grip: jaws could not close far enough",
|
|
@@ -728,8 +728,7 @@ class OT3Simulator(FlexBackend):
|
|
|
728
728
|
@ensure_yield
|
|
729
729
|
async def clean_up(self) -> None:
|
|
730
730
|
"""Clean up."""
|
|
731
|
-
|
|
732
|
-
await self._module_controls.clean_up()
|
|
731
|
+
pass
|
|
733
732
|
|
|
734
733
|
@staticmethod
|
|
735
734
|
def _get_home_position() -> Dict[Axis, float]:
|
|
@@ -849,6 +848,7 @@ class OT3Simulator(FlexBackend):
|
|
|
849
848
|
max_allowed_grip_error: float,
|
|
850
849
|
hard_limit_lower: float,
|
|
851
850
|
hard_limit_upper: float,
|
|
851
|
+
disable_geometry_grip_check: bool = False,
|
|
852
852
|
) -> None:
|
|
853
853
|
# This is a (pretty bad) simulation of the gripper actually gripping something,
|
|
854
854
|
# but it should work.
|