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.
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
@@ -0,0 +1,754 @@
1
+ from dataclasses import dataclass
2
+ from numpy import interp
3
+ from typing import Optional, Dict, Sequence, Tuple, List
4
+
5
+ from opentrons_shared_data.liquid_classes.liquid_class_definition import (
6
+ AspirateProperties as SharedDataAspirateProperties,
7
+ SingleDispenseProperties as SharedDataSingleDispenseProperties,
8
+ MultiDispenseProperties as SharedDataMultiDispenseProperties,
9
+ DelayProperties as SharedDataDelayProperties,
10
+ DelayParams as SharedDataDelayParams,
11
+ TouchTipProperties as SharedDataTouchTipProperties,
12
+ LiquidClassTouchTipParams as SharedDataTouchTipParams,
13
+ MixProperties as SharedDataMixProperties,
14
+ MixParams as SharedDataMixParams,
15
+ BlowoutProperties as SharedDataBlowoutProperties,
16
+ BlowoutParams as SharedDataBlowoutParams,
17
+ ByTipTypeSetting as SharedByTipTypeSetting,
18
+ Submerge as SharedDataSubmerge,
19
+ RetractAspirate as SharedDataRetractAspirate,
20
+ RetractDispense as SharedDataRetractDispense,
21
+ BlowoutLocation,
22
+ PositionReference,
23
+ Coordinate,
24
+ )
25
+
26
+ from . import validation
27
+
28
+
29
+ class LiquidHandlingPropertyByVolume:
30
+ def __init__(self, by_volume_property: Sequence[Tuple[float, float]]) -> None:
31
+ self._properties_by_volume: Dict[float, float] = {
32
+ float(volume): value for volume, value in by_volume_property
33
+ }
34
+ # Volumes need to be sorted for proper interpolation of non-defined volumes, and the
35
+ # corresponding values need to be in the same order for them to be interpolated correctly
36
+ self._sorted_volumes: Tuple[float, ...] = ()
37
+ self._sorted_values: Tuple[float, ...] = ()
38
+ self._sort_volume_and_values()
39
+
40
+ def as_dict(self) -> Dict[float, float]:
41
+ """Get a dictionary representation of all set volumes and values along with the default."""
42
+ return self._properties_by_volume
43
+
44
+ def as_list_of_tuples(self) -> List[Tuple[float, float]]:
45
+ """Get as list of tuples."""
46
+ return list(self._properties_by_volume.items())
47
+
48
+ def get_for_volume(self, volume: float) -> float:
49
+ """Get a value by volume for this property. Volumes not defined will be interpolated between set volumes."""
50
+ validated_volume = validation.ensure_positive_float(volume)
51
+ if len(self._properties_by_volume) == 0:
52
+ raise ValueError(
53
+ "No properties found for any volumes. Cannot interpolate for the given volume."
54
+ )
55
+ try:
56
+ return self._properties_by_volume[validated_volume]
57
+ except KeyError:
58
+ # If volume is not defined in dictionary, do a piecewise interpolation with existing sorted values
59
+ return float(
60
+ interp(validated_volume, self._sorted_volumes, self._sorted_values)
61
+ )
62
+
63
+ def set_for_volume(self, volume: float, value: float) -> None:
64
+ """Add a new volume and value for the property for the interpolation curve."""
65
+ validated_volume = validation.ensure_positive_float(volume)
66
+ self._properties_by_volume[validated_volume] = value
67
+ self._sort_volume_and_values()
68
+
69
+ def delete_for_volume(self, volume: float) -> None:
70
+ """Remove an existing volume and value from the property."""
71
+ try:
72
+ del self._properties_by_volume[volume]
73
+ except KeyError:
74
+ raise KeyError(f"No value set for volume {volume} uL")
75
+ self._sort_volume_and_values()
76
+
77
+ def _sort_volume_and_values(self) -> None:
78
+ """Sort volume in increasing order along with corresponding values in matching order."""
79
+ self._sorted_volumes, self._sorted_values = (
80
+ zip(*sorted(self._properties_by_volume.items()))
81
+ if len(self._properties_by_volume) > 0
82
+ else [(), ()]
83
+ )
84
+
85
+
86
+ @dataclass
87
+ class DelayProperties:
88
+
89
+ _enabled: bool
90
+ _duration: Optional[float]
91
+
92
+ @property
93
+ def enabled(self) -> bool:
94
+ return self._enabled
95
+
96
+ @enabled.setter
97
+ def enabled(self, enable: bool) -> None:
98
+ validated_enable = validation.ensure_boolean(enable)
99
+ if validated_enable and self._duration is None:
100
+ raise ValueError("duration must be set before enabling delay.")
101
+ self._enabled = validated_enable
102
+
103
+ @property
104
+ def duration(self) -> Optional[float]:
105
+ return self._duration
106
+
107
+ @duration.setter
108
+ def duration(self, new_duration: float) -> None:
109
+ validated_duration = validation.ensure_positive_float(new_duration)
110
+ self._duration = validated_duration
111
+
112
+ def as_shared_data_model(self) -> SharedDataDelayProperties:
113
+ return SharedDataDelayProperties(
114
+ enable=self._enabled,
115
+ params=SharedDataDelayParams(duration=self.duration)
116
+ if self.duration is not None
117
+ else None,
118
+ )
119
+
120
+
121
+ @dataclass
122
+ class TouchTipProperties:
123
+
124
+ _enabled: bool
125
+ _z_offset: Optional[float]
126
+ _mm_to_edge: Optional[float]
127
+ _speed: Optional[float]
128
+
129
+ @property
130
+ def enabled(self) -> bool:
131
+ return self._enabled
132
+
133
+ @enabled.setter
134
+ def enabled(self, enable: bool) -> None:
135
+ validated_enable = validation.ensure_boolean(enable)
136
+ if validated_enable and (
137
+ self._z_offset is None or self._mm_to_edge is None or self._speed is None
138
+ ):
139
+ raise ValueError(
140
+ "z_offset, mm_to_edge and speed must be set before enabling touch tip."
141
+ )
142
+ self._enabled = validated_enable
143
+
144
+ @property
145
+ def z_offset(self) -> Optional[float]:
146
+ return self._z_offset
147
+
148
+ @z_offset.setter
149
+ def z_offset(self, new_offset: float) -> None:
150
+ validated_offset = validation.ensure_float(new_offset)
151
+ self._z_offset = validated_offset
152
+
153
+ @property
154
+ def mm_to_edge(self) -> Optional[float]:
155
+ return self._mm_to_edge
156
+
157
+ @mm_to_edge.setter
158
+ def mm_to_edge(self, new_mm: float) -> None:
159
+ validated_mm = validation.ensure_float(new_mm)
160
+ self._z_offset = validated_mm
161
+
162
+ @property
163
+ def speed(self) -> Optional[float]:
164
+ return self._speed
165
+
166
+ @speed.setter
167
+ def speed(self, new_speed: float) -> None:
168
+ validated_speed = validation.ensure_positive_float(new_speed)
169
+ self._speed = validated_speed
170
+
171
+ def _get_shared_data_params(self) -> Optional[SharedDataTouchTipParams]:
172
+ """Get the touch tip params in schema v1 shape."""
173
+ if (
174
+ self._z_offset is not None
175
+ and self._mm_to_edge is not None
176
+ and self._speed is not None
177
+ ):
178
+ return SharedDataTouchTipParams(
179
+ zOffset=self._z_offset,
180
+ mmToEdge=self._mm_to_edge,
181
+ speed=self._speed,
182
+ )
183
+ else:
184
+ return None
185
+
186
+ def as_shared_data_model(self) -> SharedDataTouchTipProperties:
187
+ return SharedDataTouchTipProperties(
188
+ enable=self._enabled,
189
+ params=self._get_shared_data_params(),
190
+ )
191
+
192
+
193
+ @dataclass
194
+ class MixProperties:
195
+
196
+ _enabled: bool
197
+ _repetitions: Optional[int]
198
+ _volume: Optional[float]
199
+
200
+ @property
201
+ def enabled(self) -> bool:
202
+ return self._enabled
203
+
204
+ @enabled.setter
205
+ def enabled(self, enable: bool) -> None:
206
+ validated_enable = validation.ensure_boolean(enable)
207
+ if validated_enable and (self._repetitions is None or self._volume is None):
208
+ raise ValueError("repetitions and volume must be set before enabling mix.")
209
+ self._enabled = validated_enable
210
+
211
+ @property
212
+ def repetitions(self) -> Optional[int]:
213
+ return self._repetitions
214
+
215
+ @repetitions.setter
216
+ def repetitions(self, new_repetitions: int) -> None:
217
+ validated_repetitions = validation.ensure_positive_int(new_repetitions)
218
+ self._repetitions = validated_repetitions
219
+
220
+ @property
221
+ def volume(self) -> Optional[float]:
222
+ return self._volume
223
+
224
+ @volume.setter
225
+ def volume(self, new_volume: float) -> None:
226
+ validated_volume = validation.ensure_positive_float(new_volume)
227
+ self._volume = validated_volume
228
+
229
+ def _get_shared_data_params(self) -> Optional[SharedDataMixParams]:
230
+ """Get the mix params in schema v1 shape."""
231
+ if self._repetitions is not None and self._volume is not None:
232
+ return SharedDataMixParams(
233
+ repetitions=self._repetitions,
234
+ volume=self._volume,
235
+ )
236
+ else:
237
+ return None
238
+
239
+ def as_shared_data_model(self) -> SharedDataMixProperties:
240
+ return SharedDataMixProperties(
241
+ enable=self._enabled,
242
+ params=self._get_shared_data_params(),
243
+ )
244
+
245
+
246
+ @dataclass
247
+ class BlowoutProperties:
248
+
249
+ _enabled: bool
250
+ _location: Optional[BlowoutLocation]
251
+ _flow_rate: Optional[float]
252
+
253
+ @property
254
+ def enabled(self) -> bool:
255
+ return self._enabled
256
+
257
+ @enabled.setter
258
+ def enabled(self, enable: bool) -> None:
259
+ validated_enable = validation.ensure_boolean(enable)
260
+ if validated_enable and (self._location is None or self._flow_rate is None):
261
+ raise ValueError(
262
+ "location and flow_rate must be set before enabling blowout."
263
+ )
264
+ self._enabled = validated_enable
265
+
266
+ @property
267
+ def location(self) -> Optional[BlowoutLocation]:
268
+ return self._location
269
+
270
+ @location.setter
271
+ def location(self, new_location: str) -> None:
272
+ self._location = BlowoutLocation(new_location)
273
+
274
+ @property
275
+ def flow_rate(self) -> Optional[float]:
276
+ return self._flow_rate
277
+
278
+ @flow_rate.setter
279
+ def flow_rate(self, new_flow_rate: float) -> None:
280
+ validated_flow_rate = validation.ensure_positive_float(new_flow_rate)
281
+ self._flow_rate = validated_flow_rate
282
+
283
+ def _get_shared_data_params(self) -> Optional[SharedDataBlowoutParams]:
284
+ """Get the mix params in schema v1 shape."""
285
+ if self._location is not None and self._flow_rate is not None:
286
+ return SharedDataBlowoutParams(
287
+ location=self._location,
288
+ flowRate=self._flow_rate,
289
+ )
290
+ else:
291
+ return None
292
+
293
+ def as_shared_data_model(self) -> SharedDataBlowoutProperties:
294
+ return SharedDataBlowoutProperties(
295
+ enable=self._enabled,
296
+ params=self._get_shared_data_params(),
297
+ )
298
+
299
+
300
+ @dataclass
301
+ class SubmergeRetractCommon:
302
+
303
+ _position_reference: PositionReference
304
+ _offset: Coordinate
305
+ _speed: float
306
+ _delay: DelayProperties
307
+
308
+ @property
309
+ def position_reference(self) -> PositionReference:
310
+ return self._position_reference
311
+
312
+ @position_reference.setter
313
+ def position_reference(self, new_position: str) -> None:
314
+ self._position_reference = PositionReference(new_position)
315
+
316
+ @property
317
+ def offset(self) -> Coordinate:
318
+ return self._offset
319
+
320
+ @offset.setter
321
+ def offset(self, new_offset: Sequence[float]) -> None:
322
+ x, y, z = validation.validate_coordinates(new_offset)
323
+ self._offset = Coordinate(x=x, y=y, z=z)
324
+
325
+ @property
326
+ def speed(self) -> float:
327
+ return self._speed
328
+
329
+ @speed.setter
330
+ def speed(self, new_speed: float) -> None:
331
+ validated_speed = validation.ensure_positive_float(new_speed)
332
+ self._speed = validated_speed
333
+
334
+ @property
335
+ def delay(self) -> DelayProperties:
336
+ return self._delay
337
+
338
+
339
+ @dataclass
340
+ class Submerge(SubmergeRetractCommon):
341
+ ...
342
+
343
+ def as_shared_data_model(self) -> SharedDataSubmerge:
344
+ return SharedDataSubmerge(
345
+ positionReference=self._position_reference,
346
+ offset=self._offset,
347
+ speed=self._speed,
348
+ delay=self._delay.as_shared_data_model(),
349
+ )
350
+
351
+
352
+ @dataclass
353
+ class RetractAspirate(SubmergeRetractCommon):
354
+
355
+ _air_gap_by_volume: LiquidHandlingPropertyByVolume
356
+ _touch_tip: TouchTipProperties
357
+
358
+ @property
359
+ def air_gap_by_volume(self) -> LiquidHandlingPropertyByVolume:
360
+ return self._air_gap_by_volume
361
+
362
+ @property
363
+ def touch_tip(self) -> TouchTipProperties:
364
+ return self._touch_tip
365
+
366
+ def as_shared_data_model(self) -> SharedDataRetractAspirate:
367
+ return SharedDataRetractAspirate(
368
+ positionReference=self._position_reference,
369
+ offset=self._offset,
370
+ speed=self._speed,
371
+ airGapByVolume=self._air_gap_by_volume.as_list_of_tuples(),
372
+ touchTip=self._touch_tip.as_shared_data_model(),
373
+ delay=self._delay.as_shared_data_model(),
374
+ )
375
+
376
+
377
+ @dataclass
378
+ class RetractDispense(SubmergeRetractCommon):
379
+
380
+ _air_gap_by_volume: LiquidHandlingPropertyByVolume
381
+ _touch_tip: TouchTipProperties
382
+ _blowout: BlowoutProperties
383
+
384
+ @property
385
+ def air_gap_by_volume(self) -> LiquidHandlingPropertyByVolume:
386
+ return self._air_gap_by_volume
387
+
388
+ @property
389
+ def touch_tip(self) -> TouchTipProperties:
390
+ return self._touch_tip
391
+
392
+ @property
393
+ def blowout(self) -> BlowoutProperties:
394
+ return self._blowout
395
+
396
+ def as_shared_data_model(self) -> SharedDataRetractDispense:
397
+ return SharedDataRetractDispense(
398
+ positionReference=self._position_reference,
399
+ offset=self._offset,
400
+ speed=self._speed,
401
+ airGapByVolume=self._air_gap_by_volume.as_list_of_tuples(),
402
+ blowout=self._blowout.as_shared_data_model(),
403
+ touchTip=self._touch_tip.as_shared_data_model(),
404
+ delay=self._delay.as_shared_data_model(),
405
+ )
406
+
407
+
408
+ @dataclass
409
+ class BaseLiquidHandlingProperties:
410
+
411
+ _submerge: Submerge
412
+ _position_reference: PositionReference
413
+ _offset: Coordinate
414
+ _flow_rate_by_volume: LiquidHandlingPropertyByVolume
415
+ _correction_by_volume: LiquidHandlingPropertyByVolume
416
+ _delay: DelayProperties
417
+
418
+ @property
419
+ def submerge(self) -> Submerge:
420
+ return self._submerge
421
+
422
+ @property
423
+ def position_reference(self) -> PositionReference:
424
+ return self._position_reference
425
+
426
+ @position_reference.setter
427
+ def position_reference(self, new_position: str) -> None:
428
+ self._position_reference = PositionReference(new_position)
429
+
430
+ @property
431
+ def offset(self) -> Coordinate:
432
+ return self._offset
433
+
434
+ @offset.setter
435
+ def offset(self, new_offset: Sequence[float]) -> None:
436
+ x, y, z = validation.validate_coordinates(new_offset)
437
+ self._offset = Coordinate(x=x, y=y, z=z)
438
+
439
+ @property
440
+ def flow_rate_by_volume(self) -> LiquidHandlingPropertyByVolume:
441
+ return self._flow_rate_by_volume
442
+
443
+ @property
444
+ def correction_by_volume(self) -> LiquidHandlingPropertyByVolume:
445
+ return self._correction_by_volume
446
+
447
+ @property
448
+ def delay(self) -> DelayProperties:
449
+ return self._delay
450
+
451
+
452
+ @dataclass
453
+ class AspirateProperties(BaseLiquidHandlingProperties):
454
+
455
+ _retract: RetractAspirate
456
+ _pre_wet: bool
457
+ _mix: MixProperties
458
+
459
+ @property
460
+ def pre_wet(self) -> bool:
461
+ return self._pre_wet
462
+
463
+ @pre_wet.setter
464
+ def pre_wet(self, new_setting: bool) -> None:
465
+ validated_setting = validation.ensure_boolean(new_setting)
466
+ self._pre_wet = validated_setting
467
+
468
+ @property
469
+ def retract(self) -> RetractAspirate:
470
+ return self._retract
471
+
472
+ @property
473
+ def mix(self) -> MixProperties:
474
+ return self._mix
475
+
476
+ def as_shared_data_model(self) -> SharedDataAspirateProperties:
477
+ return SharedDataAspirateProperties(
478
+ submerge=self._submerge.as_shared_data_model(),
479
+ retract=self._retract.as_shared_data_model(),
480
+ positionReference=self._position_reference,
481
+ offset=self._offset,
482
+ flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
483
+ preWet=self._pre_wet,
484
+ mix=self._mix.as_shared_data_model(),
485
+ delay=self._delay.as_shared_data_model(),
486
+ correctionByVolume=self._correction_by_volume.as_list_of_tuples(),
487
+ )
488
+
489
+
490
+ @dataclass
491
+ class SingleDispenseProperties(BaseLiquidHandlingProperties):
492
+
493
+ _retract: RetractDispense
494
+ _push_out_by_volume: LiquidHandlingPropertyByVolume
495
+ _mix: MixProperties
496
+
497
+ @property
498
+ def push_out_by_volume(self) -> LiquidHandlingPropertyByVolume:
499
+ return self._push_out_by_volume
500
+
501
+ @property
502
+ def retract(self) -> RetractDispense:
503
+ return self._retract
504
+
505
+ @property
506
+ def mix(self) -> MixProperties:
507
+ return self._mix
508
+
509
+ def as_shared_data_model(self) -> SharedDataSingleDispenseProperties:
510
+ return SharedDataSingleDispenseProperties(
511
+ submerge=self._submerge.as_shared_data_model(),
512
+ retract=self._retract.as_shared_data_model(),
513
+ positionReference=self._position_reference,
514
+ offset=self._offset,
515
+ flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
516
+ mix=self._mix.as_shared_data_model(),
517
+ pushOutByVolume=self._push_out_by_volume.as_list_of_tuples(),
518
+ delay=self._delay.as_shared_data_model(),
519
+ correctionByVolume=self._correction_by_volume.as_list_of_tuples(),
520
+ )
521
+
522
+
523
+ @dataclass
524
+ class MultiDispenseProperties(BaseLiquidHandlingProperties):
525
+
526
+ _retract: RetractDispense
527
+ _conditioning_by_volume: LiquidHandlingPropertyByVolume
528
+ _disposal_by_volume: LiquidHandlingPropertyByVolume
529
+
530
+ @property
531
+ def retract(self) -> RetractDispense:
532
+ return self._retract
533
+
534
+ @property
535
+ def conditioning_by_volume(self) -> LiquidHandlingPropertyByVolume:
536
+ return self._conditioning_by_volume
537
+
538
+ @property
539
+ def disposal_by_volume(self) -> LiquidHandlingPropertyByVolume:
540
+ return self._disposal_by_volume
541
+
542
+ def as_shared_data_model(self) -> SharedDataMultiDispenseProperties:
543
+ return SharedDataMultiDispenseProperties(
544
+ submerge=self._submerge.as_shared_data_model(),
545
+ retract=self._retract.as_shared_data_model(),
546
+ positionReference=self._position_reference,
547
+ offset=self._offset,
548
+ flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
549
+ conditioningByVolume=self._conditioning_by_volume.as_list_of_tuples(),
550
+ disposalByVolume=self._disposal_by_volume.as_list_of_tuples(),
551
+ delay=self._delay.as_shared_data_model(),
552
+ correctionByVolume=self._correction_by_volume.as_list_of_tuples(),
553
+ )
554
+
555
+
556
+ @dataclass
557
+ class TransferProperties:
558
+ _aspirate: AspirateProperties
559
+ _dispense: SingleDispenseProperties
560
+ _multi_dispense: Optional[MultiDispenseProperties]
561
+
562
+ @property
563
+ def aspirate(self) -> AspirateProperties:
564
+ """Aspirate properties."""
565
+ return self._aspirate
566
+
567
+ @property
568
+ def dispense(self) -> SingleDispenseProperties:
569
+ """Single dispense properties."""
570
+ return self._dispense
571
+
572
+ @property
573
+ def multi_dispense(self) -> Optional[MultiDispenseProperties]:
574
+ """Multi dispense properties."""
575
+ return self._multi_dispense
576
+
577
+
578
+ def _build_delay_properties(
579
+ delay_properties: SharedDataDelayProperties,
580
+ ) -> DelayProperties:
581
+ if delay_properties.params is not None:
582
+ duration = delay_properties.params.duration
583
+ else:
584
+ duration = None
585
+ return DelayProperties(_enabled=delay_properties.enable, _duration=duration)
586
+
587
+
588
+ def _build_touch_tip_properties(
589
+ touch_tip_properties: SharedDataTouchTipProperties,
590
+ ) -> TouchTipProperties:
591
+ if touch_tip_properties.params is not None:
592
+ z_offset = touch_tip_properties.params.zOffset
593
+ mm_to_edge = touch_tip_properties.params.mmToEdge
594
+ speed = touch_tip_properties.params.speed
595
+ else:
596
+ z_offset = None
597
+ mm_to_edge = None
598
+ speed = None
599
+ return TouchTipProperties(
600
+ _enabled=touch_tip_properties.enable,
601
+ _z_offset=z_offset,
602
+ _mm_to_edge=mm_to_edge,
603
+ _speed=speed,
604
+ )
605
+
606
+
607
+ def _build_mix_properties(
608
+ mix_properties: SharedDataMixProperties,
609
+ ) -> MixProperties:
610
+ if mix_properties.params is not None:
611
+ repetitions = mix_properties.params.repetitions
612
+ volume = mix_properties.params.volume
613
+ else:
614
+ repetitions = None
615
+ volume = None
616
+ return MixProperties(
617
+ _enabled=mix_properties.enable, _repetitions=repetitions, _volume=volume
618
+ )
619
+
620
+
621
+ def _build_blowout_properties(
622
+ blowout_properties: SharedDataBlowoutProperties,
623
+ ) -> BlowoutProperties:
624
+ if blowout_properties.params is not None:
625
+ location = blowout_properties.params.location
626
+ flow_rate = blowout_properties.params.flowRate
627
+ else:
628
+ location = None
629
+ flow_rate = None
630
+ return BlowoutProperties(
631
+ _enabled=blowout_properties.enable, _location=location, _flow_rate=flow_rate
632
+ )
633
+
634
+
635
+ def _build_submerge(
636
+ submerge_properties: SharedDataSubmerge,
637
+ ) -> Submerge:
638
+ return Submerge(
639
+ _position_reference=submerge_properties.positionReference,
640
+ _offset=submerge_properties.offset,
641
+ _speed=submerge_properties.speed,
642
+ _delay=_build_delay_properties(submerge_properties.delay),
643
+ )
644
+
645
+
646
+ def _build_retract_aspirate(
647
+ retract_aspirate: SharedDataRetractAspirate,
648
+ ) -> RetractAspirate:
649
+ return RetractAspirate(
650
+ _position_reference=retract_aspirate.positionReference,
651
+ _offset=retract_aspirate.offset,
652
+ _speed=retract_aspirate.speed,
653
+ _air_gap_by_volume=LiquidHandlingPropertyByVolume(
654
+ retract_aspirate.airGapByVolume
655
+ ),
656
+ _touch_tip=_build_touch_tip_properties(retract_aspirate.touchTip),
657
+ _delay=_build_delay_properties(retract_aspirate.delay),
658
+ )
659
+
660
+
661
+ def _build_retract_dispense(
662
+ retract_dispense: SharedDataRetractDispense,
663
+ ) -> RetractDispense:
664
+ return RetractDispense(
665
+ _position_reference=retract_dispense.positionReference,
666
+ _offset=retract_dispense.offset,
667
+ _speed=retract_dispense.speed,
668
+ _air_gap_by_volume=LiquidHandlingPropertyByVolume(
669
+ retract_dispense.airGapByVolume
670
+ ),
671
+ _blowout=_build_blowout_properties(retract_dispense.blowout),
672
+ _touch_tip=_build_touch_tip_properties(retract_dispense.touchTip),
673
+ _delay=_build_delay_properties(retract_dispense.delay),
674
+ )
675
+
676
+
677
+ def build_aspirate_properties(
678
+ aspirate_properties: SharedDataAspirateProperties,
679
+ ) -> AspirateProperties:
680
+ return AspirateProperties(
681
+ _submerge=_build_submerge(aspirate_properties.submerge),
682
+ _retract=_build_retract_aspirate(aspirate_properties.retract),
683
+ _position_reference=aspirate_properties.positionReference,
684
+ _offset=aspirate_properties.offset,
685
+ _flow_rate_by_volume=LiquidHandlingPropertyByVolume(
686
+ aspirate_properties.flowRateByVolume
687
+ ),
688
+ _correction_by_volume=LiquidHandlingPropertyByVolume(
689
+ aspirate_properties.correctionByVolume
690
+ ),
691
+ _pre_wet=aspirate_properties.preWet,
692
+ _mix=_build_mix_properties(aspirate_properties.mix),
693
+ _delay=_build_delay_properties(aspirate_properties.delay),
694
+ )
695
+
696
+
697
+ def build_single_dispense_properties(
698
+ single_dispense_properties: SharedDataSingleDispenseProperties,
699
+ ) -> SingleDispenseProperties:
700
+ return SingleDispenseProperties(
701
+ _submerge=_build_submerge(single_dispense_properties.submerge),
702
+ _retract=_build_retract_dispense(single_dispense_properties.retract),
703
+ _position_reference=single_dispense_properties.positionReference,
704
+ _offset=single_dispense_properties.offset,
705
+ _flow_rate_by_volume=LiquidHandlingPropertyByVolume(
706
+ single_dispense_properties.flowRateByVolume
707
+ ),
708
+ _correction_by_volume=LiquidHandlingPropertyByVolume(
709
+ single_dispense_properties.correctionByVolume
710
+ ),
711
+ _mix=_build_mix_properties(single_dispense_properties.mix),
712
+ _push_out_by_volume=LiquidHandlingPropertyByVolume(
713
+ single_dispense_properties.pushOutByVolume
714
+ ),
715
+ _delay=_build_delay_properties(single_dispense_properties.delay),
716
+ )
717
+
718
+
719
+ def build_multi_dispense_properties(
720
+ multi_dispense_properties: Optional[SharedDataMultiDispenseProperties],
721
+ ) -> Optional[MultiDispenseProperties]:
722
+ if multi_dispense_properties is None:
723
+ return None
724
+ return MultiDispenseProperties(
725
+ _submerge=_build_submerge(multi_dispense_properties.submerge),
726
+ _retract=_build_retract_dispense(multi_dispense_properties.retract),
727
+ _position_reference=multi_dispense_properties.positionReference,
728
+ _offset=multi_dispense_properties.offset,
729
+ _flow_rate_by_volume=LiquidHandlingPropertyByVolume(
730
+ multi_dispense_properties.flowRateByVolume
731
+ ),
732
+ _correction_by_volume=LiquidHandlingPropertyByVolume(
733
+ multi_dispense_properties.correctionByVolume
734
+ ),
735
+ _conditioning_by_volume=LiquidHandlingPropertyByVolume(
736
+ multi_dispense_properties.conditioningByVolume
737
+ ),
738
+ _disposal_by_volume=LiquidHandlingPropertyByVolume(
739
+ multi_dispense_properties.disposalByVolume
740
+ ),
741
+ _delay=_build_delay_properties(multi_dispense_properties.delay),
742
+ )
743
+
744
+
745
+ def build_transfer_properties(
746
+ by_tip_type_setting: SharedByTipTypeSetting,
747
+ ) -> TransferProperties:
748
+ return TransferProperties(
749
+ _aspirate=build_aspirate_properties(by_tip_type_setting.aspirate),
750
+ _dispense=build_single_dispense_properties(by_tip_type_setting.singleDispense),
751
+ _multi_dispense=build_multi_dispense_properties(
752
+ by_tip_type_setting.multiDispense
753
+ ),
754
+ )