opentrons 8.2.0a4__py2.py3-none-any.whl → 8.3.0a1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (229) hide show
  1. opentrons/calibration_storage/deck_configuration.py +3 -3
  2. opentrons/calibration_storage/file_operators.py +3 -3
  3. opentrons/calibration_storage/helpers.py +3 -1
  4. opentrons/calibration_storage/ot2/models/v1.py +16 -29
  5. opentrons/calibration_storage/ot2/tip_length.py +7 -4
  6. opentrons/calibration_storage/ot3/models/v1.py +14 -23
  7. opentrons/cli/analyze.py +18 -6
  8. opentrons/config/defaults_ot3.py +1 -0
  9. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  10. opentrons/drivers/asyncio/communication/errors.py +16 -3
  11. opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
  12. opentrons/drivers/command_builder.py +2 -2
  13. opentrons/drivers/flex_stacker/__init__.py +9 -0
  14. opentrons/drivers/flex_stacker/abstract.py +89 -0
  15. opentrons/drivers/flex_stacker/driver.py +260 -0
  16. opentrons/drivers/flex_stacker/simulator.py +109 -0
  17. opentrons/drivers/flex_stacker/types.py +138 -0
  18. opentrons/drivers/heater_shaker/driver.py +18 -3
  19. opentrons/drivers/temp_deck/driver.py +13 -3
  20. opentrons/drivers/thermocycler/driver.py +17 -3
  21. opentrons/execute.py +3 -1
  22. opentrons/hardware_control/__init__.py +1 -2
  23. opentrons/hardware_control/api.py +28 -20
  24. opentrons/hardware_control/backends/flex_protocol.py +17 -7
  25. opentrons/hardware_control/backends/ot3controller.py +213 -63
  26. opentrons/hardware_control/backends/ot3simulator.py +18 -9
  27. opentrons/hardware_control/backends/ot3utils.py +43 -15
  28. opentrons/hardware_control/dev_types.py +4 -0
  29. opentrons/hardware_control/emulation/heater_shaker.py +4 -0
  30. opentrons/hardware_control/emulation/module_server/client.py +1 -1
  31. opentrons/hardware_control/emulation/module_server/server.py +5 -3
  32. opentrons/hardware_control/emulation/settings.py +3 -4
  33. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
  34. opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
  35. opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
  36. opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
  37. opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
  38. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
  39. opentrons/hardware_control/modules/mod_abc.py +2 -2
  40. opentrons/hardware_control/motion_utilities.py +68 -0
  41. opentrons/hardware_control/nozzle_manager.py +39 -41
  42. opentrons/hardware_control/ot3_calibration.py +1 -1
  43. opentrons/hardware_control/ot3api.py +60 -23
  44. opentrons/hardware_control/protocols/gripper_controller.py +3 -0
  45. opentrons/hardware_control/protocols/hardware_manager.py +5 -1
  46. opentrons/hardware_control/protocols/liquid_handler.py +18 -0
  47. opentrons/hardware_control/protocols/motion_controller.py +6 -0
  48. opentrons/hardware_control/robot_calibration.py +1 -1
  49. opentrons/hardware_control/types.py +61 -0
  50. opentrons/protocol_api/__init__.py +20 -1
  51. opentrons/protocol_api/_liquid.py +24 -49
  52. opentrons/protocol_api/_liquid_properties.py +754 -0
  53. opentrons/protocol_api/_types.py +24 -0
  54. opentrons/protocol_api/core/common.py +2 -0
  55. opentrons/protocol_api/core/engine/instrument.py +82 -10
  56. opentrons/protocol_api/core/engine/labware.py +29 -7
  57. opentrons/protocol_api/core/engine/protocol.py +130 -5
  58. opentrons/protocol_api/core/engine/robot.py +139 -0
  59. opentrons/protocol_api/core/engine/well.py +4 -1
  60. opentrons/protocol_api/core/instrument.py +46 -4
  61. opentrons/protocol_api/core/labware.py +13 -4
  62. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +37 -3
  63. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  64. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  65. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +37 -3
  67. opentrons/protocol_api/core/protocol.py +34 -1
  68. opentrons/protocol_api/core/robot.py +51 -0
  69. opentrons/protocol_api/instrument_context.py +158 -44
  70. opentrons/protocol_api/labware.py +231 -7
  71. opentrons/protocol_api/module_contexts.py +21 -17
  72. opentrons/protocol_api/protocol_context.py +125 -4
  73. opentrons/protocol_api/robot_context.py +204 -32
  74. opentrons/protocol_api/validation.py +262 -3
  75. opentrons/protocol_engine/__init__.py +4 -0
  76. opentrons/protocol_engine/actions/actions.py +2 -3
  77. opentrons/protocol_engine/clients/sync_client.py +18 -0
  78. opentrons/protocol_engine/commands/__init__.py +81 -0
  79. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
  80. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
  81. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
  82. opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
  83. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  84. opentrons/protocol_engine/commands/aspirate.py +103 -53
  85. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  86. opentrons/protocol_engine/commands/blow_out.py +44 -39
  87. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  88. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  89. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  90. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  91. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  92. opentrons/protocol_engine/commands/command.py +73 -66
  93. opentrons/protocol_engine/commands/command_unions.py +101 -1
  94. opentrons/protocol_engine/commands/comment.py +1 -1
  95. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  96. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  97. opentrons/protocol_engine/commands/custom.py +6 -12
  98. opentrons/protocol_engine/commands/dispense.py +82 -48
  99. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  100. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  101. opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
  102. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  103. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  104. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  105. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  106. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  107. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  108. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  109. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  111. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  112. opentrons/protocol_engine/commands/home.py +13 -4
  113. opentrons/protocol_engine/commands/liquid_probe.py +67 -24
  114. opentrons/protocol_engine/commands/load_labware.py +29 -7
  115. opentrons/protocol_engine/commands/load_lid.py +146 -0
  116. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  117. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  118. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  119. opentrons/protocol_engine/commands/load_module.py +31 -10
  120. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  121. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  122. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  123. opentrons/protocol_engine/commands/move_labware.py +19 -6
  124. opentrons/protocol_engine/commands/move_relative.py +35 -25
  125. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  126. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  127. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  128. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  129. opentrons/protocol_engine/commands/movement_common.py +338 -0
  130. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  131. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  132. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  133. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  134. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  135. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  136. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  137. opentrons/protocol_engine/commands/robot/common.py +18 -0
  138. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  139. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  140. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  141. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  142. opentrons/protocol_engine/commands/save_position.py +14 -5
  143. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  144. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  145. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  146. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  147. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  148. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  149. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  150. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  151. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  152. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
  153. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  154. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  155. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  157. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  158. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  159. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
  160. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
  161. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
  162. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
  163. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  164. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  165. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  166. opentrons/protocol_engine/errors/__init__.py +8 -0
  167. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  168. opentrons/protocol_engine/errors/exceptions.py +50 -0
  169. opentrons/protocol_engine/execution/command_executor.py +1 -1
  170. opentrons/protocol_engine/execution/equipment.py +73 -5
  171. opentrons/protocol_engine/execution/gantry_mover.py +364 -8
  172. opentrons/protocol_engine/execution/movement.py +27 -0
  173. opentrons/protocol_engine/execution/pipetting.py +5 -1
  174. opentrons/protocol_engine/execution/tip_handler.py +4 -6
  175. opentrons/protocol_engine/notes/notes.py +1 -1
  176. opentrons/protocol_engine/protocol_engine.py +7 -6
  177. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  178. opentrons/protocol_engine/resources/labware_validation.py +5 -0
  179. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  180. opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
  181. opentrons/protocol_engine/slot_standardization.py +9 -9
  182. opentrons/protocol_engine/state/_move_types.py +9 -5
  183. opentrons/protocol_engine/state/_well_math.py +193 -0
  184. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  185. opentrons/protocol_engine/state/command_history.py +12 -0
  186. opentrons/protocol_engine/state/commands.py +17 -13
  187. opentrons/protocol_engine/state/files.py +10 -12
  188. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  189. opentrons/protocol_engine/state/frustum_helpers.py +57 -32
  190. opentrons/protocol_engine/state/geometry.py +47 -1
  191. opentrons/protocol_engine/state/labware.py +79 -25
  192. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  193. opentrons/protocol_engine/state/liquids.py +16 -4
  194. opentrons/protocol_engine/state/modules.py +52 -70
  195. opentrons/protocol_engine/state/motion.py +6 -1
  196. opentrons/protocol_engine/state/pipettes.py +144 -58
  197. opentrons/protocol_engine/state/state.py +21 -2
  198. opentrons/protocol_engine/state/state_summary.py +4 -2
  199. opentrons/protocol_engine/state/tips.py +11 -44
  200. opentrons/protocol_engine/state/update_types.py +343 -48
  201. opentrons/protocol_engine/state/wells.py +19 -11
  202. opentrons/protocol_engine/types.py +176 -28
  203. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  204. opentrons/protocol_reader/file_format_validator.py +5 -5
  205. opentrons/protocol_runner/json_file_reader.py +9 -3
  206. opentrons/protocol_runner/json_translator.py +51 -25
  207. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  208. opentrons/protocol_runner/protocol_runner.py +35 -4
  209. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  210. opentrons/protocol_runner/run_orchestrator.py +13 -3
  211. opentrons/protocols/advanced_control/common.py +38 -0
  212. opentrons/protocols/advanced_control/mix.py +1 -1
  213. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  214. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  215. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  216. opentrons/protocols/api_support/definitions.py +1 -1
  217. opentrons/protocols/api_support/instrument.py +1 -1
  218. opentrons/protocols/api_support/util.py +10 -0
  219. opentrons/protocols/labware.py +39 -6
  220. opentrons/protocols/models/json_protocol.py +5 -9
  221. opentrons/simulate.py +3 -1
  222. opentrons/types.py +162 -2
  223. opentrons/util/logging_config.py +1 -1
  224. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/METADATA +16 -15
  225. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/RECORD +229 -202
  226. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/WHEEL +1 -1
  227. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/LICENSE +0 -0
  228. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/entry_points.txt +0 -0
  229. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,6 @@
1
1
  """Utilities for calculating motion correctly."""
2
+ from logging import getLogger
3
+
2
4
  from functools import lru_cache
3
5
  from typing import Callable, Dict, Union, Optional, cast
4
6
  from collections import OrderedDict
@@ -11,6 +13,7 @@ from opentrons.util import linal
11
13
 
12
14
  from .types import Axis, OT3Mount
13
15
 
16
+ log = getLogger(__name__)
14
17
 
15
18
  # TODO: The offset_for_mount function should be defined with an overload
16
19
  # set, as with other functions in this module. Unfortunately, mypy < 0.920
@@ -36,6 +39,19 @@ from .types import Axis, OT3Mount
36
39
  # ) -> Point:
37
40
  # ...
38
41
 
42
+ EMPTY_ORDERED_DICT = OrderedDict(
43
+ (
44
+ (Axis.X, 0.0),
45
+ (Axis.Y, 0.0),
46
+ (Axis.Z_L, 0.0),
47
+ (Axis.Z_R, 0.0),
48
+ (Axis.Z_G, 0.0),
49
+ (Axis.P_L, 0.0),
50
+ (Axis.P_R, 0.0),
51
+ (Axis.Q, 0.0),
52
+ )
53
+ )
54
+
39
55
 
40
56
  @lru_cache(4)
41
57
  def offset_for_mount(
@@ -68,6 +84,7 @@ def target_position_from_absolute(
68
84
  )
69
85
  primary_cp = get_critical_point(mount)
70
86
  primary_z = Axis.by_mount(mount)
87
+
71
88
  target_position = OrderedDict(
72
89
  (
73
90
  (Axis.X, abs_position.x - offset.x - primary_cp.x),
@@ -97,6 +114,57 @@ def target_position_from_relative(
97
114
  return target_position
98
115
 
99
116
 
117
+ def target_axis_map_from_absolute(
118
+ primary_mount: Union[OT3Mount, Mount],
119
+ axis_map: Dict[Axis, float],
120
+ get_critical_point: Callable[[Union[Mount, OT3Mount]], Point],
121
+ left_mount_offset: Point,
122
+ right_mount_offset: Point,
123
+ gripper_mount_offset: Optional[Point] = None,
124
+ ) -> "OrderedDict[Axis, float]":
125
+ """Create an absolute target position for all specified machine axes."""
126
+ keys_for_target_position = list(axis_map.keys())
127
+
128
+ offset = offset_for_mount(
129
+ primary_mount, left_mount_offset, right_mount_offset, gripper_mount_offset
130
+ )
131
+ primary_cp = get_critical_point(primary_mount)
132
+ primary_z = Axis.by_mount(primary_mount)
133
+ target_position = OrderedDict()
134
+
135
+ if Axis.X in keys_for_target_position:
136
+ target_position[Axis.X] = axis_map[Axis.X] - offset.x - primary_cp.x
137
+ if Axis.Y in keys_for_target_position:
138
+ target_position[Axis.Y] = axis_map[Axis.Y] - offset.y - primary_cp.y
139
+ if primary_z in keys_for_target_position:
140
+ # Since this function is intended to be used in conjunction with `API.move_axes`
141
+ # we must leave out the carriage offset subtraction from the target position as
142
+ # `move_axes` already does this calculation.
143
+ target_position[primary_z] = axis_map[primary_z] - primary_cp.z
144
+
145
+ target_position.update(
146
+ {ax: val for ax, val in axis_map.items() if ax not in Axis.gantry_axes()}
147
+ )
148
+ return target_position
149
+
150
+
151
+ def target_axis_map_from_relative(
152
+ axis_map: Dict[Axis, float],
153
+ current_position: Dict[Axis, float],
154
+ ) -> "OrderedDict[Axis, float]":
155
+ """Create a target position for all specified machine axes."""
156
+ target_position = OrderedDict(
157
+ (
158
+ (ax, current_position[ax] + axis_map[ax])
159
+ for ax in EMPTY_ORDERED_DICT.keys()
160
+ if ax in axis_map.keys()
161
+ )
162
+ )
163
+ log.info(f"Current position {current_position} and axis map delta {axis_map}")
164
+ log.info(f"Relative move target {target_position}")
165
+ return target_position
166
+
167
+
100
168
  def target_position_from_plunger(
101
169
  mount: Union[Mount, OT3Mount],
102
170
  delta: float,
@@ -1,11 +1,13 @@
1
1
  from typing import Dict, List, Optional, Any, Sequence, Iterator, Tuple, cast
2
2
  from dataclasses import dataclass
3
3
  from collections import OrderedDict
4
- from enum import Enum
5
4
  from itertools import chain
6
5
 
7
6
  from opentrons.hardware_control.types import CriticalPoint
8
- from opentrons.types import Point
7
+ from opentrons.types import (
8
+ Point,
9
+ NozzleConfigurationType,
10
+ )
9
11
  from opentrons_shared_data.pipette.pipette_definition import (
10
12
  PipetteGeometryDefinition,
11
13
  PipetteRowDefinition,
@@ -41,43 +43,6 @@ def _row_col_indices_for_nozzle(
41
43
  )
42
44
 
43
45
 
44
- class NozzleConfigurationType(Enum):
45
- """
46
- Nozzle Configuration Type.
47
-
48
- Represents the current nozzle
49
- configuration stored in NozzleMap
50
- """
51
-
52
- COLUMN = "COLUMN"
53
- ROW = "ROW"
54
- SINGLE = "SINGLE"
55
- FULL = "FULL"
56
- SUBRECT = "SUBRECT"
57
-
58
- @classmethod
59
- def determine_nozzle_configuration(
60
- cls,
61
- physical_rows: "OrderedDict[str, List[str]]",
62
- current_rows: "OrderedDict[str, List[str]]",
63
- physical_cols: "OrderedDict[str, List[str]]",
64
- current_cols: "OrderedDict[str, List[str]]",
65
- ) -> "NozzleConfigurationType":
66
- """
67
- Determine the nozzle configuration based on the starting and
68
- ending nozzle.
69
- """
70
- if physical_rows == current_rows and physical_cols == current_cols:
71
- return NozzleConfigurationType.FULL
72
- if len(current_rows) == 1 and len(current_cols) == 1:
73
- return NozzleConfigurationType.SINGLE
74
- if len(current_rows) == 1:
75
- return NozzleConfigurationType.ROW
76
- if len(current_cols) == 1:
77
- return NozzleConfigurationType.COLUMN
78
- return NozzleConfigurationType.SUBRECT
79
-
80
-
81
46
  @dataclass
82
47
  class NozzleMap:
83
48
  """
@@ -113,6 +78,28 @@ class NozzleMap:
113
78
  full_instrument_rows: Dict[str, List[str]]
114
79
  #: A map of all the rows of an instrument
115
80
 
81
+ @classmethod
82
+ def determine_nozzle_configuration(
83
+ cls,
84
+ physical_rows: "OrderedDict[str, List[str]]",
85
+ current_rows: "OrderedDict[str, List[str]]",
86
+ physical_cols: "OrderedDict[str, List[str]]",
87
+ current_cols: "OrderedDict[str, List[str]]",
88
+ ) -> "NozzleConfigurationType":
89
+ """
90
+ Determine the nozzle configuration based on the starting and
91
+ ending nozzle.
92
+ """
93
+ if physical_rows == current_rows and physical_cols == current_cols:
94
+ return NozzleConfigurationType.FULL
95
+ if len(current_rows) == 1 and len(current_cols) == 1:
96
+ return NozzleConfigurationType.SINGLE
97
+ if len(current_rows) == 1:
98
+ return NozzleConfigurationType.ROW
99
+ if len(current_cols) == 1:
100
+ return NozzleConfigurationType.COLUMN
101
+ return NozzleConfigurationType.SUBRECT
102
+
116
103
  def __str__(self) -> str:
117
104
  return f"back_left_nozzle: {self.back_left} front_right_nozzle: {self.front_right} configuration: {self.configuration}"
118
105
 
@@ -216,6 +203,16 @@ class NozzleMap:
216
203
  """The total number of active nozzles in the configuration, and thus the number of tips that will be picked up."""
217
204
  return len(self.map_store)
218
205
 
206
+ @property
207
+ def physical_nozzle_count(self) -> int:
208
+ """The number of physical nozzles, regardless of configuration."""
209
+ return len(self.full_instrument_map_store)
210
+
211
+ @property
212
+ def active_nozzles(self) -> list[str]:
213
+ """An unstructured list of all nozzles active in the configuration."""
214
+ return list(self.map_store.keys())
215
+
219
216
  @classmethod
220
217
  def build( # noqa: C901
221
218
  cls,
@@ -274,7 +271,7 @@ class NozzleMap:
274
271
  )
275
272
 
276
273
  if (
277
- NozzleConfigurationType.determine_nozzle_configuration(
274
+ cls.determine_nozzle_configuration(
278
275
  physical_rows, rows, physical_columns, columns
279
276
  )
280
277
  != NozzleConfigurationType.FULL
@@ -289,6 +286,7 @@ class NozzleMap:
289
286
  if valid_nozzle_maps.maps[map_key] == list(map_store.keys()):
290
287
  validated_map_key = map_key
291
288
  break
289
+
292
290
  if validated_map_key is None:
293
291
  raise IncompatibleNozzleConfiguration(
294
292
  "Attempted Nozzle Configuration does not match any approved map layout for the current pipette."
@@ -302,7 +300,7 @@ class NozzleMap:
302
300
  full_instrument_map_store=physical_nozzles,
303
301
  full_instrument_rows=physical_rows,
304
302
  columns=columns,
305
- configuration=NozzleConfigurationType.determine_nozzle_configuration(
303
+ configuration=cls.determine_nozzle_configuration(
306
304
  physical_rows, rows, physical_columns, columns
307
305
  ),
308
306
  )
@@ -968,7 +968,7 @@ def load_attitude_matrix(to_default: bool = True) -> DeckCalibration:
968
968
  return DeckCalibration(
969
969
  attitude=apply_machine_transform(calibration_data.attitude),
970
970
  source=calibration_data.source,
971
- status=types.CalibrationStatus(**calibration_data.status.dict()),
971
+ status=types.CalibrationStatus(**calibration_data.status.model_dump()),
972
972
  belt_attitude=calibration_data.attitude,
973
973
  last_modified=calibration_data.lastModified,
974
974
  pipette_calibrated_with=calibration_data.pipetteCalibratedWith,
@@ -32,6 +32,7 @@ from opentrons_shared_data.pipette.types import (
32
32
  )
33
33
  from opentrons_shared_data.pipette import (
34
34
  pipette_load_name_conversions as pipette_load_name,
35
+ pipette_definition,
35
36
  )
36
37
  from opentrons_shared_data.robot.types import RobotType
37
38
 
@@ -45,7 +46,6 @@ from opentrons.config.types import (
45
46
  LiquidProbeSettings,
46
47
  )
47
48
  from opentrons.drivers.rpi_drivers.types import USBPort, PortGroup
48
- from opentrons.hardware_control.nozzle_manager import NozzleConfigurationType
49
49
  from opentrons_shared_data.errors.exceptions import (
50
50
  EnumeratedError,
51
51
  PythonException,
@@ -98,6 +98,7 @@ from .types import (
98
98
  EstopState,
99
99
  HardwareFeatureFlags,
100
100
  FailedTipStateCheck,
101
+ PipetteSensorResponseQueue,
101
102
  )
102
103
  from .errors import (
103
104
  UpdateOngoingError,
@@ -143,8 +144,6 @@ from .backends.types import HWStopCondition
143
144
  from .backends.flex_protocol import FlexBackend
144
145
  from .backends.ot3simulator import OT3Simulator
145
146
  from .backends.errors import SubsystemUpdating
146
- from opentrons_hardware.firmware_bindings.constants import SensorId
147
- from opentrons_hardware.sensors.types import SensorDataType
148
147
 
149
148
  mod_log = logging.getLogger(__name__)
150
149
 
@@ -299,8 +298,11 @@ class OT3API(
299
298
  async def set_system_constraints_for_plunger_acceleration(
300
299
  self, mount: OT3Mount, acceleration: float
301
300
  ) -> None:
301
+ high_speed_pipette = self._pipette_handler.get_pipette(
302
+ mount
303
+ ).is_high_speed_pipette()
302
304
  self._backend.update_constraints_for_plunger_acceleration(
303
- mount, acceleration, self._gantry_load
305
+ mount, acceleration, self._gantry_load, high_speed_pipette
304
306
  )
305
307
 
306
308
  @contextlib.asynccontextmanager
@@ -353,7 +355,9 @@ class OT3API(
353
355
  def _reset_last_mount(self) -> None:
354
356
  self._last_moved_mount = None
355
357
 
356
- def _deck_from_machine(self, machine_pos: Dict[Axis, float]) -> Dict[Axis, float]:
358
+ def get_deck_from_machine(
359
+ self, machine_pos: Dict[Axis, float]
360
+ ) -> Dict[Axis, float]:
357
361
  return deck_from_machine(
358
362
  machine_pos=machine_pos,
359
363
  attitude=self._robot_calibration.deck_calibration.attitude,
@@ -633,10 +637,31 @@ class OT3API(
633
637
  self._feature_flags.use_old_aspiration_functions,
634
638
  )
635
639
  self._pipette_handler.hardware_instruments[mount] = p
640
+
641
+ if config is not None:
642
+ self._set_pressure_sensor_available(mount, instrument_config=config)
643
+
636
644
  # TODO (lc 12-5-2022) Properly support backwards compatibility
637
645
  # when applicable
638
646
  return skipped
639
647
 
648
+ def get_pressure_sensor_available(self, mount: OT3Mount) -> bool:
649
+ pip_axis = Axis.of_main_tool_actuator(mount)
650
+ return self._backend.get_pressure_sensor_available(pip_axis)
651
+
652
+ def _set_pressure_sensor_available(
653
+ self,
654
+ mount: OT3Mount,
655
+ instrument_config: pipette_definition.PipetteConfigurations,
656
+ ) -> None:
657
+ pressure_sensor_available = (
658
+ "pressure" in instrument_config.available_sensors.sensors
659
+ )
660
+ pip_axis = Axis.of_main_tool_actuator(mount)
661
+ self._backend.set_pressure_sensor_available(
662
+ pipette_axis=pip_axis, available=pressure_sensor_available
663
+ )
664
+
640
665
  async def cache_gripper(self, instrument_data: AttachedGripper) -> bool:
641
666
  """Set up gripper based on scanned information."""
642
667
  grip_cal = load_gripper_calibration_offset(instrument_data.get("id"))
@@ -775,6 +800,8 @@ class OT3API(
775
800
  """
776
801
  Function to update motor estimation for a set of axes
777
802
  """
803
+ await self._backend.update_motor_status()
804
+
778
805
  if axes is None:
779
806
  axes = [ax for ax in Axis]
780
807
 
@@ -943,8 +970,8 @@ class OT3API(
943
970
  ):
944
971
  # move toward home until a safe distance
945
972
  await self._backend.tip_action(
946
- origin={Axis.Q: current_pos_float},
947
- targets=[({Axis.Q: self._config.safe_home_distance}, 400)],
973
+ origin=current_pos_float,
974
+ targets=[(self._config.safe_home_distance, 400)],
948
975
  )
949
976
 
950
977
  # update current position
@@ -1021,14 +1048,14 @@ class OT3API(
1021
1048
 
1022
1049
  async def _cache_current_position(self) -> Dict[Axis, float]:
1023
1050
  """Cache current position from backend and return in absolute deck coords."""
1024
- self._current_position = self._deck_from_machine(
1051
+ self._current_position = self.get_deck_from_machine(
1025
1052
  await self._backend.update_position()
1026
1053
  )
1027
1054
  return self._current_position
1028
1055
 
1029
1056
  async def _cache_encoder_position(self) -> Dict[Axis, float]:
1030
1057
  """Cache encoder position from backend and return in absolute deck coords."""
1031
- self._encoder_position = self._deck_from_machine(
1058
+ self._encoder_position = self.get_deck_from_machine(
1032
1059
  await self._backend.update_encoder_position()
1033
1060
  )
1034
1061
  if self.has_gripper():
@@ -1228,7 +1255,9 @@ class OT3API(
1228
1255
  message=f"{axis} is not present", detail={"axis": str(axis)}
1229
1256
  )
1230
1257
 
1258
+ self._log.info(f"Attempting to move {position} with speed {speed}.")
1231
1259
  if not self._backend.check_encoder_status(list(position.keys())):
1260
+ self._log.info("Calling home in move_axes")
1232
1261
  await self.home()
1233
1262
  self._assert_motor_ok(list(position.keys()))
1234
1263
 
@@ -1439,6 +1468,10 @@ class OT3API(
1439
1468
  check_motion_bounds(to_check, target_position, bounds, check_bounds)
1440
1469
  self._log.info(f"Move: deck {target_position} becomes machine {machine_pos}")
1441
1470
  origin = await self._backend.update_position()
1471
+
1472
+ if self._gantry_load == GantryLoad.HIGH_THROUGHPUT:
1473
+ origin[Axis.Q] = self._backend.gear_motor_position or 0.0
1474
+
1442
1475
  async with contextlib.AsyncExitStack() as stack:
1443
1476
  if acquire_lock:
1444
1477
  await stack.enter_async_context(self._motion_lock)
@@ -1640,7 +1673,12 @@ class OT3API(
1640
1673
  await self._backend.disengage_axes(which)
1641
1674
 
1642
1675
  async def engage_axes(self, which: List[Axis]) -> None:
1643
- await self._backend.engage_axes(which)
1676
+ await self._backend.engage_axes(
1677
+ [axis for axis in which if self._backend.axis_is_present(axis)]
1678
+ )
1679
+
1680
+ def axis_is_present(self, axis: Axis) -> bool:
1681
+ return self._backend.axis_is_present(axis)
1644
1682
 
1645
1683
  async def get_limit_switches(self) -> Dict[Axis, bool]:
1646
1684
  res = await self._backend.get_limit_switches()
@@ -1826,7 +1864,7 @@ class OT3API(
1826
1864
  if (
1827
1865
  self.gantry_load == GantryLoad.HIGH_THROUGHPUT
1828
1866
  and instrument.nozzle_manager.current_configuration.configuration
1829
- == NozzleConfigurationType.FULL
1867
+ == top_types.NozzleConfigurationType.FULL
1830
1868
  ):
1831
1869
  spec = self._pipette_handler.plan_ht_pick_up_tip(
1832
1870
  instrument.nozzle_manager.current_configuration.tip_count
@@ -2156,8 +2194,8 @@ class OT3API(
2156
2194
  # only move tip motors if they are not already below the sensor
2157
2195
  if tip_motor_pos_float < tip_presence_check_target:
2158
2196
  await self._backend.tip_action(
2159
- origin={Axis.Q: tip_motor_pos_float},
2160
- targets=[({Axis.Q: tip_presence_check_target}, 400)],
2197
+ origin=tip_motor_pos_float,
2198
+ targets=[(tip_presence_check_target, 400)],
2161
2199
  )
2162
2200
  try:
2163
2201
  yield
@@ -2228,11 +2266,11 @@ class OT3API(
2228
2266
  gear_origin_float = self._backend.gear_motor_position or 0.0
2229
2267
 
2230
2268
  move_targets = [
2231
- ({Axis.Q: move_segment.distance}, move_segment.speed or 400)
2269
+ (move_segment.distance, move_segment.speed or 400)
2232
2270
  for move_segment in pipette_spec
2233
2271
  ]
2234
2272
  await self._backend.tip_action(
2235
- origin={Axis.Q: gear_origin_float}, targets=move_targets
2273
+ origin=gear_origin_float, targets=move_targets
2236
2274
  )
2237
2275
  await self.home_gear_motors()
2238
2276
 
@@ -2557,7 +2595,7 @@ class OT3API(
2557
2595
  mount: Union[top_types.Mount, OT3Mount],
2558
2596
  critical_point: Optional[CriticalPoint] = None,
2559
2597
  ) -> float:
2560
- carriage_pos = self._deck_from_machine(self._backend.home_position())
2598
+ carriage_pos = self.get_deck_from_machine(self._backend.home_position())
2561
2599
  pos_at_home = self._effector_pos_from_carriage_pos(
2562
2600
  OT3Mount.from_mount(mount), carriage_pos, critical_point
2563
2601
  )
@@ -2639,10 +2677,9 @@ class OT3API(
2639
2677
  probe_settings: LiquidProbeSettings,
2640
2678
  probe: InstrumentProbeType,
2641
2679
  p_travel: float,
2680
+ z_offset_for_plunger_prep: float,
2642
2681
  force_both_sensors: bool = False,
2643
- response_queue: Optional[
2644
- asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
2645
- ] = None,
2682
+ response_queue: Optional[PipetteSensorResponseQueue] = None,
2646
2683
  ) -> float:
2647
2684
  plunger_direction = -1 if probe_settings.aspirate_while_sensing else 1
2648
2685
  end_z = await self._backend.liquid_probe(
@@ -2653,13 +2690,14 @@ class OT3API(
2653
2690
  probe_settings.sensor_threshold_pascals,
2654
2691
  probe_settings.plunger_impulse_time,
2655
2692
  probe_settings.samples_for_baselining,
2693
+ z_offset_for_plunger_prep,
2656
2694
  probe=probe,
2657
2695
  force_both_sensors=force_both_sensors,
2658
2696
  response_queue=response_queue,
2659
2697
  )
2660
2698
  machine_pos = await self._backend.update_position()
2661
2699
  machine_pos[Axis.by_mount(mount)] = end_z
2662
- deck_end_z = self._deck_from_machine(machine_pos)[Axis.by_mount(mount)]
2700
+ deck_end_z = self.get_deck_from_machine(machine_pos)[Axis.by_mount(mount)]
2663
2701
  offset = offset_for_mount(
2664
2702
  mount,
2665
2703
  top_types.Point(*self._config.left_mount_offset),
@@ -2676,9 +2714,7 @@ class OT3API(
2676
2714
  probe_settings: Optional[LiquidProbeSettings] = None,
2677
2715
  probe: Optional[InstrumentProbeType] = None,
2678
2716
  force_both_sensors: bool = False,
2679
- response_queue: Optional[
2680
- asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
2681
- ] = None,
2717
+ response_queue: Optional[PipetteSensorResponseQueue] = None,
2682
2718
  ) -> float:
2683
2719
  """Search for and return liquid level height.
2684
2720
 
@@ -2804,6 +2840,7 @@ class OT3API(
2804
2840
  probe_settings,
2805
2841
  checked_probe,
2806
2842
  plunger_travel_mm + sensor_baseline_plunger_move_mm,
2843
+ z_offset_for_plunger_prep,
2807
2844
  force_both_sensors,
2808
2845
  response_queue,
2809
2846
  )
@@ -14,6 +14,9 @@ class GripperController(Protocol):
14
14
  ) -> None:
15
15
  ...
16
16
 
17
+ async def home_gripper_jaw(self) -> None:
18
+ ...
19
+
17
20
  async def ungrip(self, force_newtons: Optional[float] = None) -> None:
18
21
  """Release gripped object.
19
22
 
@@ -1,7 +1,7 @@
1
1
  from typing import Dict, Optional
2
2
  from typing_extensions import Protocol
3
3
 
4
- from ..types import SubSystem, SubSystemState
4
+ from ..types import SubSystem, SubSystemState, Axis
5
5
 
6
6
 
7
7
  class HardwareManager(Protocol):
@@ -45,3 +45,7 @@ class HardwareManager(Protocol):
45
45
  async def get_serial_number(self) -> Optional[str]:
46
46
  """Get the robot serial number, if provisioned. If not provisioned, will be None."""
47
47
  ...
48
+
49
+ def axis_is_present(self, axis: Axis) -> bool:
50
+ """Get whether a motor axis is present on the machine."""
51
+ ...
@@ -1,6 +1,8 @@
1
1
  from typing import Optional
2
2
  from typing_extensions import Protocol
3
3
 
4
+ from opentrons.types import Point
5
+ from opentrons.hardware_control.types import CriticalPoint
4
6
  from .types import MountArgType, CalibrationType, ConfigType
5
7
 
6
8
  from .instrument_configurer import InstrumentConfigurer
@@ -16,6 +18,22 @@ class LiquidHandler(
16
18
  Calibratable[CalibrationType],
17
19
  Protocol[CalibrationType, MountArgType, ConfigType],
18
20
  ):
21
+ def critical_point_for(
22
+ self,
23
+ mount: MountArgType,
24
+ cp_override: Optional[CriticalPoint] = None,
25
+ ) -> Point:
26
+ """
27
+ Determine the current critical point for the specified mount.
28
+
29
+ :param mount: A robot mount that the instrument is on.
30
+ :param cp_override: The critical point override to use.
31
+
32
+ If no critical point override is specified, the robot defaults to nozzle location `A1` or the mount critical point.
33
+ :return: Point.
34
+ """
35
+ ...
36
+
19
37
  async def update_nozzle_configuration_for_mount(
20
38
  self,
21
39
  mount: MountArgType,
@@ -9,6 +9,12 @@ from .types import MountArgType
9
9
  class MotionController(Protocol[MountArgType]):
10
10
  """Protocol specifying fundamental motion controls."""
11
11
 
12
+ def get_deck_from_machine(
13
+ self, machine_pos: Dict[Axis, float]
14
+ ) -> Dict[Axis, float]:
15
+ """Convert machine coordinates to deck coordinates."""
16
+ ...
17
+
12
18
  async def halt(self, disengage_before_stopping: bool = False) -> None:
13
19
  """Immediately stop motion.
14
20
 
@@ -154,7 +154,7 @@ def load_attitude_matrix() -> DeckCalibration:
154
154
  return DeckCalibration(
155
155
  attitude=calibration_data.attitude,
156
156
  source=calibration_data.source,
157
- status=types.CalibrationStatus(**calibration_data.status.dict()),
157
+ status=types.CalibrationStatus(**calibration_data.status.model_dump()),
158
158
  last_modified=calibration_data.last_modified,
159
159
  pipette_calibrated_with=calibration_data.pipette_calibrated_with,
160
160
  tiprack=calibration_data.tiprack,
@@ -1,3 +1,4 @@
1
+ from asyncio import Queue
1
2
  import enum
2
3
  import logging
3
4
  from dataclasses import dataclass
@@ -712,3 +713,63 @@ class FailedTipStateCheck(RuntimeError):
712
713
  super().__init__(
713
714
  f"Expected tip state {expected_state}, but received {actual_state}."
714
715
  )
716
+
717
+
718
+ @enum.unique
719
+ class PipetteSensorId(int, enum.Enum):
720
+ """Sensor IDs available.
721
+
722
+ Not to be confused with SensorType. This is the ID value that separate
723
+ two or more of the same type of sensor within a system.
724
+
725
+ Note that this is a copy of an enum defined in opentrons_hardware.firmware_bindings.constants. That version
726
+ is authoritative; this version is here because this data is exposed above the hardware control layer and
727
+ therefore needs a typing source here so that we don't create a dependency on the internal hardware package.
728
+ """
729
+
730
+ S0 = 0x0
731
+ S1 = 0x1
732
+ UNUSED = 0x2
733
+ BOTH = 0x3
734
+
735
+
736
+ @enum.unique
737
+ class PipetteSensorType(int, enum.Enum):
738
+ """Sensor types available.
739
+
740
+ Note that this is a copy of an enum defined in opentrons_hardware.firmware_bindings.constants. That version
741
+ is authoritative; this version is here because this data is exposed above the hardware control layer and
742
+ therefore needs a typing source here so that we don't create a dependency on the internal hardware package.
743
+ """
744
+
745
+ tip = 0x00
746
+ capacitive = 0x01
747
+ environment = 0x02
748
+ pressure = 0x03
749
+ pressure_temperature = 0x04
750
+ humidity = 0x05
751
+ temperature = 0x06
752
+
753
+
754
+ @dataclass(frozen=True)
755
+ class PipetteSensorData:
756
+ """Sensor data from a monitored sensor.
757
+
758
+ Note that this is a copy of an enum defined in opentrons_hardware.firmware_bindings.constants. That version
759
+ is authoritative; this version is here because this data is exposed above the hardware control layer and
760
+ therefore needs a typing source here so that we don't create a dependency on the internal hardware package.
761
+ """
762
+
763
+ sensor_type: PipetteSensorType
764
+ _as_int: int
765
+ _as_float: float
766
+
767
+ def to_float(self) -> float:
768
+ return self._as_float
769
+
770
+ @property
771
+ def to_int(self) -> int:
772
+ return self._as_int
773
+
774
+
775
+ PipetteSensorResponseQueue = Queue[Dict[PipetteSensorId, List[PipetteSensorData]]]
@@ -30,7 +30,16 @@ from .module_contexts import (
30
30
  )
31
31
  from .disposal_locations import TrashBin, WasteChute
32
32
  from ._liquid import Liquid, LiquidClass
33
- from ._types import OFF_DECK
33
+ from ._types import (
34
+ OFF_DECK,
35
+ PLUNGER_BLOWOUT,
36
+ PLUNGER_TOP,
37
+ PLUNGER_BOTTOM,
38
+ PLUNGER_DROPTIP,
39
+ ASPIRATE_ACTION,
40
+ DISPENSE_ACTION,
41
+ BLOWOUT_ACTION,
42
+ )
34
43
  from ._nozzle_layout import (
35
44
  COLUMN,
36
45
  PARTIAL_COLUMN,
@@ -69,12 +78,22 @@ __all__ = [
69
78
  "Liquid",
70
79
  "LiquidClass",
71
80
  "Parameters",
81
+ # Partial Tip types
72
82
  "COLUMN",
73
83
  "PARTIAL_COLUMN",
74
84
  "SINGLE",
75
85
  "ROW",
76
86
  "ALL",
87
+ # Deck location types
77
88
  "OFF_DECK",
89
+ # Pipette plunger types
90
+ "PLUNGER_BLOWOUT",
91
+ "PLUNGER_TOP",
92
+ "PLUNGER_BOTTOM",
93
+ "PLUNGER_DROPTIP",
94
+ "ASPIRATE_ACTION",
95
+ "DISPENSE_ACTION",
96
+ "BLOWOUT_ACTION",
78
97
  "RuntimeParameterRequiredError",
79
98
  "CSVParameter",
80
99
  # For internal Opentrons use only: