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.

Files changed (147) hide show
  1. opentrons/_version.py +2 -2
  2. opentrons/drivers/asyncio/communication/serial_connection.py +55 -129
  3. opentrons/drivers/flex_stacker/driver.py +6 -1
  4. opentrons/drivers/heater_shaker/abstract.py +0 -5
  5. opentrons/drivers/heater_shaker/driver.py +0 -10
  6. opentrons/drivers/heater_shaker/simulator.py +0 -4
  7. opentrons/drivers/thermocycler/abstract.py +0 -6
  8. opentrons/drivers/thermocycler/driver.py +10 -61
  9. opentrons/drivers/thermocycler/simulator.py +0 -6
  10. opentrons/hardware_control/api.py +5 -24
  11. opentrons/hardware_control/backends/controller.py +2 -8
  12. opentrons/hardware_control/backends/flex_protocol.py +1 -0
  13. opentrons/hardware_control/backends/ot3controller.py +3 -3
  14. opentrons/hardware_control/backends/ot3simulator.py +2 -2
  15. opentrons/hardware_control/backends/simulator.py +1 -2
  16. opentrons/hardware_control/backends/subsystem_manager.py +2 -5
  17. opentrons/hardware_control/emulation/abstract_emulator.py +4 -6
  18. opentrons/hardware_control/emulation/connection_handler.py +5 -8
  19. opentrons/hardware_control/emulation/heater_shaker.py +3 -12
  20. opentrons/hardware_control/emulation/settings.py +1 -1
  21. opentrons/hardware_control/emulation/thermocycler.py +15 -67
  22. opentrons/hardware_control/module_control.py +8 -82
  23. opentrons/hardware_control/modules/__init__.py +0 -3
  24. opentrons/hardware_control/modules/absorbance_reader.py +4 -11
  25. opentrons/hardware_control/modules/flex_stacker.py +9 -38
  26. opentrons/hardware_control/modules/heater_shaker.py +5 -42
  27. opentrons/hardware_control/modules/magdeck.py +4 -8
  28. opentrons/hardware_control/modules/mod_abc.py +5 -13
  29. opentrons/hardware_control/modules/tempdeck.py +5 -25
  30. opentrons/hardware_control/modules/thermocycler.py +11 -68
  31. opentrons/hardware_control/modules/types.py +1 -20
  32. opentrons/hardware_control/modules/utils.py +4 -11
  33. opentrons/hardware_control/nozzle_manager.py +0 -3
  34. opentrons/hardware_control/ot3api.py +7 -26
  35. opentrons/hardware_control/poller.py +8 -22
  36. opentrons/hardware_control/protocols/gripper_controller.py +1 -0
  37. opentrons/hardware_control/scripts/update_module_fw.py +0 -5
  38. opentrons/hardware_control/types.py +2 -31
  39. opentrons/legacy_commands/module_commands.py +0 -23
  40. opentrons/legacy_commands/protocol_commands.py +0 -20
  41. opentrons/legacy_commands/types.py +0 -80
  42. opentrons/motion_planning/deck_conflict.py +12 -17
  43. opentrons/motion_planning/waypoints.py +29 -15
  44. opentrons/protocol_api/__init__.py +1 -5
  45. opentrons/protocol_api/_types.py +1 -6
  46. opentrons/protocol_api/core/common.py +1 -3
  47. opentrons/protocol_api/core/engine/_default_labware_versions.py +11 -32
  48. opentrons/protocol_api/core/engine/labware.py +1 -8
  49. opentrons/protocol_api/core/engine/module_core.py +8 -75
  50. opentrons/protocol_api/core/engine/protocol.py +1 -18
  51. opentrons/protocol_api/core/engine/well.py +0 -8
  52. opentrons/protocol_api/core/legacy/legacy_module_core.py +4 -24
  53. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -11
  54. opentrons/protocol_api/core/legacy/legacy_well_core.py +0 -4
  55. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +2 -14
  56. opentrons/protocol_api/core/module.py +4 -37
  57. opentrons/protocol_api/core/protocol.py +2 -11
  58. opentrons/protocol_api/core/well.py +0 -4
  59. opentrons/protocol_api/labware.py +0 -5
  60. opentrons/protocol_api/module_contexts.py +11 -117
  61. opentrons/protocol_api/protocol_context.py +4 -26
  62. opentrons/protocol_api/robot_context.py +21 -38
  63. opentrons/protocol_api/validation.py +1 -6
  64. opentrons/protocol_engine/actions/__init__.py +2 -4
  65. opentrons/protocol_engine/actions/actions.py +9 -22
  66. opentrons/protocol_engine/clients/sync_client.py +7 -42
  67. opentrons/protocol_engine/commands/__init__.py +0 -42
  68. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +15 -2
  69. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +15 -2
  70. opentrons/protocol_engine/commands/aspirate.py +0 -1
  71. opentrons/protocol_engine/commands/command.py +0 -1
  72. opentrons/protocol_engine/commands/command_unions.py +0 -49
  73. opentrons/protocol_engine/commands/dispense.py +0 -1
  74. opentrons/protocol_engine/commands/drop_tip.py +8 -32
  75. opentrons/protocol_engine/commands/heater_shaker/__init__.py +0 -14
  76. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +4 -5
  77. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +5 -31
  78. opentrons/protocol_engine/commands/movement_common.py +0 -2
  79. opentrons/protocol_engine/commands/pick_up_tip.py +11 -21
  80. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +7 -38
  81. opentrons/protocol_engine/commands/thermocycler/__init__.py +0 -16
  82. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +0 -6
  83. opentrons/protocol_engine/commands/thermocycler/run_profile.py +0 -8
  84. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +6 -40
  85. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +5 -29
  86. opentrons/protocol_engine/commands/touch_tip.py +1 -1
  87. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +22 -6
  88. opentrons/protocol_engine/errors/__init__.py +0 -4
  89. opentrons/protocol_engine/errors/exceptions.py +0 -55
  90. opentrons/protocol_engine/execution/__init__.py +0 -2
  91. opentrons/protocol_engine/execution/command_executor.py +0 -8
  92. opentrons/protocol_engine/execution/create_queue_worker.py +1 -5
  93. opentrons/protocol_engine/execution/labware_movement.py +21 -10
  94. opentrons/protocol_engine/execution/movement.py +0 -2
  95. opentrons/protocol_engine/execution/queue_worker.py +0 -4
  96. opentrons/protocol_engine/execution/run_control.py +0 -8
  97. opentrons/protocol_engine/protocol_engine.py +34 -75
  98. opentrons/protocol_engine/resources/__init__.py +0 -2
  99. opentrons/protocol_engine/resources/deck_configuration_provider.py +0 -7
  100. opentrons/protocol_engine/resources/labware_validation.py +6 -10
  101. opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
  102. opentrons/protocol_engine/state/_well_math.py +18 -60
  103. opentrons/protocol_engine/state/addressable_areas.py +0 -2
  104. opentrons/protocol_engine/state/commands.py +11 -14
  105. opentrons/protocol_engine/state/geometry.py +374 -213
  106. opentrons/protocol_engine/state/labware.py +102 -52
  107. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +0 -37
  108. opentrons/protocol_engine/state/modules.py +8 -21
  109. opentrons/protocol_engine/state/motion.py +0 -44
  110. opentrons/protocol_engine/state/state.py +0 -14
  111. opentrons/protocol_engine/state/state_summary.py +0 -2
  112. opentrons/protocol_engine/state/tips.py +258 -177
  113. opentrons/protocol_engine/state/update_types.py +9 -16
  114. opentrons/protocol_engine/types/__init__.py +3 -9
  115. opentrons/protocol_engine/types/deck_configuration.py +1 -5
  116. opentrons/protocol_engine/types/instrument.py +1 -8
  117. opentrons/protocol_engine/types/labware.py +13 -1
  118. opentrons/protocol_engine/types/module.py +0 -10
  119. opentrons/protocol_engine/types/tip.py +0 -9
  120. opentrons/protocol_runner/create_simulating_orchestrator.py +2 -29
  121. opentrons/protocol_runner/run_orchestrator.py +2 -18
  122. opentrons/protocols/api_support/definitions.py +1 -1
  123. opentrons/protocols/api_support/types.py +1 -2
  124. opentrons/simulate.py +15 -48
  125. opentrons/system/camera.py +1 -1
  126. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/METADATA +4 -4
  127. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/RECORD +130 -146
  128. opentrons/protocol_api/core/engine/tasks.py +0 -48
  129. opentrons/protocol_api/core/legacy/tasks.py +0 -19
  130. opentrons/protocol_api/core/legacy_simulator/tasks.py +0 -19
  131. opentrons/protocol_api/core/tasks.py +0 -31
  132. opentrons/protocol_api/tasks.py +0 -48
  133. opentrons/protocol_engine/commands/create_timer.py +0 -83
  134. opentrons/protocol_engine/commands/heater_shaker/common.py +0 -20
  135. opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +0 -136
  136. opentrons/protocol_engine/commands/set_tip_state.py +0 -97
  137. opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +0 -191
  138. opentrons/protocol_engine/commands/wait_for_tasks.py +0 -98
  139. opentrons/protocol_engine/execution/task_handler.py +0 -157
  140. opentrons/protocol_engine/resources/concurrency_provider.py +0 -27
  141. opentrons/protocol_engine/state/labware_origin_math/errors.py +0 -94
  142. opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +0 -1331
  143. opentrons/protocol_engine/state/tasks.py +0 -139
  144. opentrons/protocol_engine/types/tasks.py +0 -38
  145. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/WHEEL +0 -0
  146. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.dist-info}/entry_points.txt +0 -0
  147. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a8.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.0a7'
32
- __version_tuple__ = version_tuple = (8, 7, 0, 'a7')
31
+ __version__ = version = '8.7.0a8'
32
+ __version_tuple__ = version_tuple = (8, 7, 0, 'a8')
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 Type, Literal, AsyncIterator
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 | None,
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 | None = None,
46
+ name: Optional[str] = None,
47
47
  retry_wait_time_seconds: float = 0.1,
48
- loop: asyncio.AbstractEventLoop | None = None,
49
- error_keyword: str | None = None,
50
- alarm_keyword: str | None = None,
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 | None = None
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 | None = None
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 | None = None,
354
+ name: Optional[str] = None,
355
355
  retry_wait_time_seconds: float = 0.1,
356
- loop: asyncio.AbstractEventLoop | None = None,
357
- error_keyword: str | None = None,
358
- alarm_keyword: str | None = None,
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 | None = None,
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 send_multiack_command(
464
+ async def send_command(
465
465
  self,
466
466
  command: CommandBuilder,
467
- retries: int = 0,
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 or self._number_of_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 = 0, timeout: float | None = None
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, retries=retries or self._number_of_retries
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 _send_data_multiack(
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(s).
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
- This function will detect async error messages if they were sent before it
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
- This function will always try and consume all the acknowledgements specified for
611
- its command if it sends the command, even if an async error happens in between.
521
+ Raises: SerialException
522
+ """
523
+ data_encode = data.encode()
612
524
 
613
- This should all work together to make sure that there aren't any leftover acks
614
- after the function ends, which could lead to the read/write mechanics getting out
615
- of sync.
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
- Returns: The command responses
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
- Raises: SerialException from an error ack to this command or an async error.
620
- """
621
- retries = retries or self._number_of_retries
622
- responses: list[str] = []
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
- resp = await self._connection.send_command(command)
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:
@@ -74,8 +74,3 @@ class AbstractHeaterShakerDriver(ABC):
74
74
  async def enter_programming_mode(self) -> None:
75
75
  """Reboot into programming mode"""
76
76
  ...
77
-
78
- @abstractmethod
79
- async def get_error_state(self) -> None:
80
- """Raise if the module is in an error state."""
81
- ...
@@ -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
- )
@@ -92,7 +92,3 @@ class SimulatingDriver(AbstractHeaterShakerDriver):
92
92
  @ensure_yield
93
93
  async def enter_programming_mode(self) -> None:
94
94
  pass
95
-
96
- @ensure_yield
97
- async def get_error_state(self) -> None:
98
- return
@@ -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, TypeVar, Generic
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 | ThermocyclerDriverV2:
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
- _ConnectionKind = TypeVar(
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: _ConnectionKind,
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: _ConnectionKind = 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 set_plate_temperature(
361
- self,
362
- temp: float,
363
- hold_time: Optional[float] = None,
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.register_simulated_module(
1334
- simulated_usb_port=USBPort(
1335
- name="", port_number=1, port_group=PortGroup.MAIN
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: # noqa: C901
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:
@@ -451,6 +451,7 @@ class FlexBackend(Protocol):
451
451
  max_allowed_grip_error: float,
452
452
  hard_limit_lower: float,
453
453
  hard_limit_upper: float,
454
+ disable_geometry_grip_check: bool = False,
454
455
  ) -> None:
455
456
  ...
456
457
 
@@ -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
- if hasattr(self, "_module_controls") and self._module_controls is not None:
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.