opentrons 8.3.2__py2.py3-none-any.whl → 8.4.0__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 (196) hide show
  1. opentrons/calibration_storage/ot2/mark_bad_calibration.py +2 -0
  2. opentrons/calibration_storage/ot2/tip_length.py +6 -6
  3. opentrons/config/advanced_settings.py +9 -11
  4. opentrons/config/feature_flags.py +0 -4
  5. opentrons/config/reset.py +7 -2
  6. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  7. opentrons/drivers/asyncio/communication/async_serial.py +4 -0
  8. opentrons/drivers/asyncio/communication/errors.py +41 -8
  9. opentrons/drivers/asyncio/communication/serial_connection.py +36 -10
  10. opentrons/drivers/flex_stacker/__init__.py +9 -3
  11. opentrons/drivers/flex_stacker/abstract.py +140 -15
  12. opentrons/drivers/flex_stacker/driver.py +593 -47
  13. opentrons/drivers/flex_stacker/errors.py +64 -0
  14. opentrons/drivers/flex_stacker/simulator.py +222 -24
  15. opentrons/drivers/flex_stacker/types.py +211 -15
  16. opentrons/drivers/flex_stacker/utils.py +19 -0
  17. opentrons/execute.py +4 -2
  18. opentrons/hardware_control/api.py +5 -0
  19. opentrons/hardware_control/backends/flex_protocol.py +4 -0
  20. opentrons/hardware_control/backends/ot3controller.py +12 -1
  21. opentrons/hardware_control/backends/ot3simulator.py +3 -0
  22. opentrons/hardware_control/backends/subsystem_manager.py +8 -4
  23. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +10 -6
  24. opentrons/hardware_control/instruments/ot3/pipette_handler.py +59 -6
  25. opentrons/hardware_control/modules/__init__.py +12 -1
  26. opentrons/hardware_control/modules/absorbance_reader.py +11 -9
  27. opentrons/hardware_control/modules/flex_stacker.py +498 -0
  28. opentrons/hardware_control/modules/heater_shaker.py +12 -10
  29. opentrons/hardware_control/modules/magdeck.py +5 -1
  30. opentrons/hardware_control/modules/tempdeck.py +5 -1
  31. opentrons/hardware_control/modules/thermocycler.py +15 -14
  32. opentrons/hardware_control/modules/types.py +191 -1
  33. opentrons/hardware_control/modules/utils.py +3 -0
  34. opentrons/hardware_control/motion_utilities.py +20 -0
  35. opentrons/hardware_control/ot3api.py +145 -15
  36. opentrons/hardware_control/protocols/liquid_handler.py +47 -1
  37. opentrons/hardware_control/types.py +6 -0
  38. opentrons/legacy_commands/commands.py +102 -5
  39. opentrons/legacy_commands/helpers.py +74 -1
  40. opentrons/legacy_commands/types.py +33 -2
  41. opentrons/protocol_api/__init__.py +2 -0
  42. opentrons/protocol_api/_liquid.py +39 -8
  43. opentrons/protocol_api/_liquid_properties.py +20 -19
  44. opentrons/protocol_api/_transfer_liquid_validation.py +91 -0
  45. opentrons/protocol_api/core/common.py +3 -1
  46. opentrons/protocol_api/core/engine/deck_conflict.py +11 -1
  47. opentrons/protocol_api/core/engine/instrument.py +1356 -107
  48. opentrons/protocol_api/core/engine/labware.py +8 -4
  49. opentrons/protocol_api/core/engine/load_labware_params.py +68 -10
  50. opentrons/protocol_api/core/engine/module_core.py +118 -2
  51. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
  52. opentrons/protocol_api/core/engine/protocol.py +253 -11
  53. opentrons/protocol_api/core/engine/stringify.py +19 -8
  54. opentrons/protocol_api/core/engine/transfer_components_executor.py +858 -0
  55. opentrons/protocol_api/core/engine/well.py +73 -5
  56. opentrons/protocol_api/core/instrument.py +71 -21
  57. opentrons/protocol_api/core/labware.py +6 -2
  58. opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
  59. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +76 -49
  60. opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
  61. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
  62. opentrons/protocol_api/core/legacy/legacy_well_core.py +27 -2
  63. opentrons/protocol_api/core/legacy/load_info.py +4 -12
  64. opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
  65. opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +73 -23
  67. opentrons/protocol_api/core/module.py +43 -0
  68. opentrons/protocol_api/core/protocol.py +33 -0
  69. opentrons/protocol_api/core/well.py +23 -2
  70. opentrons/protocol_api/instrument_context.py +454 -150
  71. opentrons/protocol_api/labware.py +98 -50
  72. opentrons/protocol_api/module_contexts.py +140 -0
  73. opentrons/protocol_api/protocol_context.py +163 -19
  74. opentrons/protocol_api/validation.py +51 -41
  75. opentrons/protocol_engine/__init__.py +21 -2
  76. opentrons/protocol_engine/actions/actions.py +5 -5
  77. opentrons/protocol_engine/clients/sync_client.py +6 -0
  78. opentrons/protocol_engine/commands/__init__.py +66 -36
  79. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
  80. opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
  81. opentrons/protocol_engine/commands/aspirate.py +6 -2
  82. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
  83. opentrons/protocol_engine/commands/aspirate_while_tracking.py +210 -0
  84. opentrons/protocol_engine/commands/blow_out.py +2 -0
  85. opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
  86. opentrons/protocol_engine/commands/command_unions.py +102 -33
  87. opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
  88. opentrons/protocol_engine/commands/dispense.py +3 -1
  89. opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
  90. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  91. opentrons/protocol_engine/commands/drop_tip.py +23 -1
  92. opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
  93. opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
  94. opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
  95. opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
  96. opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
  97. opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
  98. opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
  99. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
  100. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
  101. opentrons/protocol_engine/commands/flex_stacker/store.py +291 -0
  102. opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
  103. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  104. opentrons/protocol_engine/commands/liquid_probe.py +27 -13
  105. opentrons/protocol_engine/commands/load_labware.py +42 -39
  106. opentrons/protocol_engine/commands/load_lid.py +21 -13
  107. opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
  108. opentrons/protocol_engine/commands/load_module.py +18 -17
  109. opentrons/protocol_engine/commands/load_pipette.py +3 -0
  110. opentrons/protocol_engine/commands/move_labware.py +139 -20
  111. opentrons/protocol_engine/commands/move_to_well.py +5 -11
  112. opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
  113. opentrons/protocol_engine/commands/pipetting_common.py +159 -8
  114. opentrons/protocol_engine/commands/prepare_to_aspirate.py +15 -5
  115. opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +33 -34
  116. opentrons/protocol_engine/commands/reload_labware.py +6 -19
  117. opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +97 -76
  118. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
  119. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
  120. opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +31 -40
  121. opentrons/protocol_engine/errors/__init__.py +10 -0
  122. opentrons/protocol_engine/errors/exceptions.py +62 -0
  123. opentrons/protocol_engine/execution/equipment.py +123 -106
  124. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  125. opentrons/protocol_engine/execution/pipetting.py +235 -25
  126. opentrons/protocol_engine/execution/tip_handler.py +82 -32
  127. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  128. opentrons/protocol_engine/protocol_engine.py +22 -13
  129. opentrons/protocol_engine/resources/deck_configuration_provider.py +98 -2
  130. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  131. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  132. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  133. opentrons/protocol_engine/slot_standardization.py +11 -23
  134. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  135. opentrons/protocol_engine/state/frustum_helpers.py +36 -14
  136. opentrons/protocol_engine/state/geometry.py +892 -227
  137. opentrons/protocol_engine/state/labware.py +252 -55
  138. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  139. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  140. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  141. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  142. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  143. opentrons/protocol_engine/state/modules.py +210 -67
  144. opentrons/protocol_engine/state/pipettes.py +54 -0
  145. opentrons/protocol_engine/state/state.py +1 -1
  146. opentrons/protocol_engine/state/tips.py +14 -0
  147. opentrons/protocol_engine/state/update_types.py +180 -25
  148. opentrons/protocol_engine/state/wells.py +55 -9
  149. opentrons/protocol_engine/types/__init__.py +300 -0
  150. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  151. opentrons/protocol_engine/types/command_annotations.py +53 -0
  152. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  153. opentrons/protocol_engine/types/execution.py +96 -0
  154. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  155. opentrons/protocol_engine/types/instrument.py +47 -0
  156. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  157. opentrons/protocol_engine/types/labware.py +111 -0
  158. opentrons/protocol_engine/types/labware_movement.py +22 -0
  159. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  160. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  161. opentrons/protocol_engine/types/liquid.py +40 -0
  162. opentrons/protocol_engine/types/liquid_class.py +59 -0
  163. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  164. opentrons/protocol_engine/types/liquid_level_detection.py +131 -0
  165. opentrons/protocol_engine/types/location.py +194 -0
  166. opentrons/protocol_engine/types/module.py +301 -0
  167. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  168. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  169. opentrons/protocol_engine/types/tip.py +18 -0
  170. opentrons/protocol_engine/types/util.py +21 -0
  171. opentrons/protocol_engine/types/well_position.py +124 -0
  172. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  173. opentrons/protocol_reader/file_format_validator.py +5 -3
  174. opentrons/protocol_runner/json_translator.py +4 -2
  175. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  176. opentrons/protocol_runner/run_orchestrator.py +4 -1
  177. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  178. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  179. opentrons/protocols/api_support/definitions.py +1 -1
  180. opentrons/protocols/api_support/instrument.py +16 -3
  181. opentrons/protocols/labware.py +27 -23
  182. opentrons/protocols/models/__init__.py +0 -21
  183. opentrons/simulate.py +4 -2
  184. opentrons/types.py +20 -7
  185. opentrons/util/logging_config.py +94 -25
  186. opentrons/util/logging_queue_handler.py +61 -0
  187. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/METADATA +4 -4
  188. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/RECORD +192 -151
  189. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  190. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  191. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  192. opentrons/protocol_engine/types.py +0 -1311
  193. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/LICENSE +0 -0
  194. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/WHEEL +0 -0
  195. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/entry_points.txt +0 -0
  196. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,64 @@
1
+ """Stacker-specific error codes and exceptions."""
2
+
3
+ from opentrons.drivers.asyncio.communication.errors import (
4
+ BaseErrorCode,
5
+ ErrorResponse,
6
+ UnhandledGcode,
7
+ )
8
+
9
+
10
+ class EStopTriggered(ErrorResponse):
11
+ """Raised when the estop is triggered during a move."""
12
+
13
+ def __init__(self, port: str, response: str, command: str) -> None:
14
+ super().__init__(port, response, command)
15
+
16
+
17
+ class MotorStallDetected(ErrorResponse):
18
+ """Raised when a motor stall is detected."""
19
+
20
+ def __init__(self, port: str, response: str, command: str) -> None:
21
+ super().__init__(port, response, command)
22
+
23
+
24
+ class MotorQueueFull(ErrorResponse):
25
+ """Raised when the motor command queue is full."""
26
+
27
+ def __init__(self, port: str, response: str, command: str) -> None:
28
+ super().__init__(port, response, command)
29
+
30
+
31
+ class UnexpectedLimitSwitch(ErrorResponse):
32
+ """Raised when an unexpected limit switch is triggered."""
33
+
34
+ def __init__(self, port: str, response: str, command: str) -> None:
35
+ super().__init__(port, response, command)
36
+
37
+
38
+ class MotorBusy(ErrorResponse):
39
+ """Raised when a motor is busy."""
40
+
41
+ # TODO: differentiate between motors
42
+ def __init__(self, port: str, response: str, command: str) -> None:
43
+ super().__init__(port, response, command)
44
+
45
+
46
+ class StopRequested(ErrorResponse):
47
+ """Raised when a stop is requested during a movement."""
48
+
49
+ def __init__(self, port: str, response: str, command: str) -> None:
50
+ super().__init__(port, response, command)
51
+
52
+
53
+ class StackerErrorCodes(BaseErrorCode):
54
+ """Stacker-specific error codes."""
55
+
56
+ UNHANDLED_GCODE = ("ERR003", UnhandledGcode)
57
+ ESTOP_TRIGGERED = ("ERR006", EStopTriggered)
58
+ MOTOR_STALL_DETECTED = ("ERR403", MotorStallDetected)
59
+ MOTOR_QUEUE_FULL = ("ERR404", MotorQueueFull)
60
+ UNEXPECTED_LIMIT_SWITCH = ("ERR405", UnexpectedLimitSwitch)
61
+ X_MOTOR_BUSY = ("ERR501", MotorBusy)
62
+ Z_MOTOR_BUSY = ("ERR502", MotorBusy)
63
+ L_MOTOR_BUSY = ("ERR503", MotorBusy)
64
+ STOP_REQUESTED = ("ERR504", StopRequested)
@@ -1,9 +1,16 @@
1
- from typing import Optional
1
+ from typing import List, Optional, Dict
2
2
 
3
+ from opentrons.drivers.flex_stacker.driver import NUMBER_OF_BINS
3
4
  from opentrons.util.async_helpers import ensure_yield
4
5
 
5
- from .abstract import AbstractStackerDriver
6
+ from .abstract import AbstractFlexStackerDriver
6
7
  from .types import (
8
+ ActiveRange,
9
+ LEDColor,
10
+ LEDPattern,
11
+ MeasurementKind,
12
+ MoveResult,
13
+ SpadMapID,
7
14
  StackerAxis,
8
15
  PlatformStatus,
9
16
  Direction,
@@ -11,10 +18,18 @@ from .types import (
11
18
  HardwareRevision,
12
19
  MoveParams,
13
20
  LimitSwitchStatus,
21
+ StallGuardParams,
22
+ TOFConfiguration,
23
+ TOFMeasurement,
24
+ TOFMeasurementResult,
25
+ TOFSensor,
26
+ TOFSensorMode,
27
+ TOFSensorState,
28
+ TOFSensorStatus,
14
29
  )
15
30
 
16
31
 
17
- class SimulatingDriver(AbstractStackerDriver):
32
+ class SimulatingDriver(AbstractFlexStackerDriver):
18
33
  """FLEX Stacker driver simulator."""
19
34
 
20
35
  def __init__(self, serial_number: Optional[str] = None) -> None:
@@ -22,33 +37,51 @@ class SimulatingDriver(AbstractStackerDriver):
22
37
  self._limit_switch_status = LimitSwitchStatus(False, False, False, False, False)
23
38
  self._platform_sensor_status = PlatformStatus(False, False)
24
39
  self._door_closed = True
40
+ self._install_detected = True
41
+ self._connected = True
42
+ self._stallgard_threshold = {
43
+ a: StallGuardParams(a, False, 0) for a in StackerAxis
44
+ }
45
+ self._motor_registers: Dict[StackerAxis, Dict[int, int]] = {
46
+ a: {} for a in StackerAxis
47
+ }
48
+ self._tof_registers: Dict[TOFSensor, Dict[int, int]] = {
49
+ a: {} for a in TOFSensor
50
+ }
51
+ self._tof_sensor_status: Dict[TOFSensor, TOFSensorStatus] = {
52
+ s: TOFSensorStatus(s, TOFSensorState.IDLE, TOFSensorMode.MEASURE, True)
53
+ for s in TOFSensor
54
+ }
55
+ self._tof_sensor_configuration: Dict[TOFSensor, TOFConfiguration] = {
56
+ s: TOFConfiguration(
57
+ s, SpadMapID.SPAD_MAP_ID_14, ActiveRange.SHORT_RANGE, 4000, 500, True
58
+ )
59
+ for s in TOFSensor
60
+ }
25
61
 
26
- def set_limit_switch(self, status: LimitSwitchStatus) -> bool:
62
+ def set_limit_switch(self, status: LimitSwitchStatus) -> None:
27
63
  self._limit_switch_status = status
28
- return True
29
64
 
30
- def set_platform_sensor(self, status: PlatformStatus) -> bool:
65
+ def set_platform_sensor(self, status: PlatformStatus) -> None:
31
66
  self._platform_sensor_status = status
32
- return True
33
67
 
34
- def set_door_closed(self, door_closed: bool) -> bool:
68
+ def set_door_closed(self, door_closed: bool) -> None:
35
69
  self._door_closed = door_closed
36
- return True
37
70
 
38
71
  @ensure_yield
39
72
  async def connect(self) -> None:
40
73
  """Connect to stacker."""
41
- pass
74
+ self._connected = True
42
75
 
43
76
  @ensure_yield
44
77
  async def disconnect(self) -> None:
45
78
  """Disconnect from stacker."""
46
- pass
79
+ self._connected = False
47
80
 
48
81
  @ensure_yield
49
82
  async def is_connected(self) -> bool:
50
83
  """Check connection to stacker."""
51
- return True
84
+ return self._connected
52
85
 
53
86
  @ensure_yield
54
87
  async def get_device_info(self) -> StackerInfo:
@@ -56,14 +89,139 @@ class SimulatingDriver(AbstractStackerDriver):
56
89
  return StackerInfo(fw="stacker-fw", hw=HardwareRevision.EVT, sn=self._sn)
57
90
 
58
91
  @ensure_yield
59
- async def set_serial_number(self, sn: str) -> bool:
92
+ async def set_serial_number(self, sn: str) -> None:
60
93
  """Set Serial Number."""
61
- return True
94
+ self._sn = sn
95
+
96
+ @ensure_yield
97
+ async def enable_motors(self, axis: List[StackerAxis]) -> None:
98
+ """Enables the axis motor if present, disables it otherwise."""
99
+ pass
100
+
101
+ @ensure_yield
102
+ async def stop_motors(self) -> None:
103
+ """Stop all motor movement."""
104
+ pass
105
+
106
+ @ensure_yield
107
+ async def set_run_current(self, axis: StackerAxis, current: float) -> None:
108
+ """Set axis peak run current in amps."""
109
+ pass
110
+
111
+ @ensure_yield
112
+ async def set_ihold_current(self, axis: StackerAxis, current: float) -> None:
113
+ """Set axis hold current in amps."""
114
+ pass
115
+
116
+ @ensure_yield
117
+ async def set_stallguard_threshold(
118
+ self, axis: StackerAxis, enable: bool, threshold: int
119
+ ) -> None:
120
+ """Enables and sets the stallguard threshold for the given axis motor."""
121
+ self._stallgard_threshold[axis] = StallGuardParams(axis, enable, threshold)
122
+
123
+ @ensure_yield
124
+ async def enable_tof_sensor(self, sensor: TOFSensor, enable: bool) -> None:
125
+ """Enable or disable the TOF sensor."""
126
+ state = TOFSensorState.IDLE if enable else TOFSensorState.DISABLED
127
+ self._tof_sensor_status[sensor].state = state
128
+ self._tof_sensor_status[sensor].ok = enable
129
+
130
+ @ensure_yield
131
+ async def manage_tof_measurement(
132
+ self,
133
+ sensor: TOFSensor,
134
+ kind: MeasurementKind = MeasurementKind.HISTOGRAM,
135
+ start: bool = True,
136
+ ) -> TOFMeasurement:
137
+ """Start or stop Measurements from the TOF sensor."""
138
+ return TOFMeasurement(
139
+ sensor=sensor,
140
+ kind=kind,
141
+ cancelled=not start,
142
+ # Each histogram frame is 135 bytes and there are 30 frames.
143
+ # (3b header + 4b sub-header + 128 data) * 30 frames = 3840b.
144
+ # The firmware sends 0 when the measurement is cancelled.
145
+ total_bytes=3840 if start else 0,
146
+ )
147
+
148
+ @ensure_yield
149
+ async def get_tof_histogram(self, sensor: TOFSensor) -> TOFMeasurementResult:
150
+ """Get the full histogram measurement from the TOF sensor."""
151
+ return TOFMeasurementResult(
152
+ sensor=sensor,
153
+ kind=MeasurementKind.HISTOGRAM,
154
+ bins={c: [b for b in range(NUMBER_OF_BINS)] for c in range(10)},
155
+ )
156
+
157
+ async def set_tof_configuration(
158
+ self,
159
+ sensor: TOFSensor,
160
+ spad_map_id: SpadMapID,
161
+ active_range: Optional[ActiveRange] = None,
162
+ kilo_iterations: Optional[int] = None,
163
+ report_period_ms: Optional[int] = None,
164
+ histogram_dump: Optional[bool] = None,
165
+ ) -> None:
166
+ """Set the configuration of the TOF sensor.
167
+
168
+ :param sensor: The TOF sensor to configure.
169
+ :param spad_map_id: The pre-defined SPAD map which sets the fov and focus area (14 default).
170
+ :active_range: The operating mode Short-range high-accuracy (default) or long range.
171
+ :kilo_iterations: The Measurement iterations times 1024 (4000 default).
172
+ :report_period_ms: The reporting period before each measurement (500 default).
173
+ :histogram_dump: Enables/Disables histogram measurements (True default).
174
+ :return: None
175
+ """
176
+ config = self._tof_sensor_configuration[sensor]
177
+ config.spad_map_id = spad_map_id
178
+ config.active_range = active_range or config.active_range
179
+ config.kilo_iterations = kilo_iterations or config.kilo_iterations
180
+ config.report_period_ms = report_period_ms or config.report_period_ms
181
+ config.histogram_dump = histogram_dump or config.histogram_dump
182
+
183
+ async def get_tof_configuration(self, sensor: TOFSensor) -> TOFConfiguration:
184
+ """Get the configuration of the TOF sensor."""
185
+ return self._tof_sensor_configuration[sensor]
186
+
187
+ @ensure_yield
188
+ async def set_motor_driver_register(
189
+ self, axis: StackerAxis, reg: int, value: int
190
+ ) -> None:
191
+ """Set the register of the given motor axis driver to the given value."""
192
+ self._motor_registers[axis].update({reg: value})
193
+
194
+ @ensure_yield
195
+ async def get_motor_driver_register(self, axis: StackerAxis, reg: int) -> int:
196
+ """Gets the register value of the given motor axis driver."""
197
+ return self._motor_registers[axis].get(reg, 0)
198
+
199
+ @ensure_yield
200
+ async def set_tof_driver_register(
201
+ self, sensor: TOFSensor, reg: int, value: int
202
+ ) -> None:
203
+ """Set the register of the given tof sensor driver to the given value."""
204
+ self._tof_registers[sensor].update({reg: value})
205
+
206
+ @ensure_yield
207
+ async def get_tof_driver_register(self, sensor: TOFSensor, reg: int) -> int:
208
+ """Gets the register value of the given tof sensor driver."""
209
+ return self._tof_registers[sensor].get(reg, 0)
62
210
 
63
211
  @ensure_yield
64
- async def stop_motor(self) -> bool:
65
- """Stop motor movement."""
66
- return True
212
+ async def get_tof_sensor_status(self, sensor: TOFSensor) -> TOFSensorStatus:
213
+ """Get the status of the tof sensor."""
214
+ return self._tof_sensor_status[sensor]
215
+
216
+ @ensure_yield
217
+ async def get_motion_params(self, axis: StackerAxis) -> MoveParams:
218
+ """Get the motion parameters used by the given axis motor."""
219
+ return MoveParams(1, 1, 1)
220
+
221
+ @ensure_yield
222
+ async def get_stallguard_threshold(self, axis: StackerAxis) -> StallGuardParams:
223
+ """Get the stallguard parameters by the given axis motor."""
224
+ return self._stallgard_threshold[axis]
67
225
 
68
226
  @ensure_yield
69
227
  async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool:
@@ -79,11 +237,16 @@ class SimulatingDriver(AbstractStackerDriver):
79
237
  return self._limit_switch_status
80
238
 
81
239
  @ensure_yield
82
- async def get_platform_sensor_status(self) -> PlatformStatus:
240
+ async def get_platform_sensor(self, direction: Direction) -> bool:
83
241
  """Get platform sensor status.
84
242
 
85
- :return: True if platform is detected, False otherwise
243
+ :return: True if platform is present, False otherwise
86
244
  """
245
+ return self._platform_sensor_status.get(direction)
246
+
247
+ @ensure_yield
248
+ async def get_platform_status(self) -> PlatformStatus:
249
+ """Get platform status."""
87
250
  return self._platform_sensor_status
88
251
 
89
252
  @ensure_yield
@@ -94,16 +257,51 @@ class SimulatingDriver(AbstractStackerDriver):
94
257
  """
95
258
  return self._door_closed
96
259
 
260
+ @ensure_yield
261
+ async def get_installation_detected(self) -> bool:
262
+ """Get whether or not installation is detected.
263
+
264
+ :return: True if installation is detected, False otherwise
265
+ """
266
+ return self._install_detected
267
+
97
268
  @ensure_yield
98
269
  async def move_in_mm(
99
270
  self, axis: StackerAxis, distance: float, params: MoveParams | None = None
100
- ) -> bool:
101
- """Move axis."""
102
- return True
271
+ ) -> MoveResult:
272
+ """Move axis by the given distance in mm."""
273
+ return MoveResult.NO_ERROR
103
274
 
104
275
  @ensure_yield
105
276
  async def move_to_limit_switch(
106
277
  self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None
107
- ) -> bool:
278
+ ) -> MoveResult:
108
279
  """Move until limit switch is triggered."""
109
- return True
280
+ return MoveResult.NO_ERROR
281
+
282
+ @ensure_yield
283
+ async def home_axis(self, axis: StackerAxis, direction: Direction) -> MoveResult:
284
+ """Home axis."""
285
+ return MoveResult.NO_ERROR
286
+
287
+ @ensure_yield
288
+ async def set_led(
289
+ self,
290
+ power: float,
291
+ color: Optional[LEDColor] = None,
292
+ external: Optional[bool] = None,
293
+ pattern: Optional[LEDPattern] = None,
294
+ duration: Optional[int] = None,
295
+ reps: Optional[int] = None,
296
+ ) -> None:
297
+ """Set LED Status bar color and pattern."""
298
+ pass
299
+
300
+ @ensure_yield
301
+ async def enter_programming_mode(self) -> None:
302
+ """Reboot into programming mode"""
303
+ pass
304
+
305
+ def reset_serial_buffers(self) -> None:
306
+ """Reset the input and output serial buffers."""
307
+ pass
@@ -1,6 +1,6 @@
1
1
  from enum import Enum
2
2
  from dataclasses import dataclass, fields
3
- from typing import List
3
+ from typing import List, Dict, Optional
4
4
 
5
5
  from opentrons.drivers.command_builder import CommandBuilder
6
6
 
@@ -11,13 +11,30 @@ class GCODE(str, Enum):
11
11
  MOVE_TO_SWITCH = "G5"
12
12
  HOME_AXIS = "G28"
13
13
  STOP_MOTORS = "M0"
14
+ ENABLE_MOTORS = "M17"
14
15
  GET_RESET_REASON = "M114"
15
16
  DEVICE_INFO = "M115"
16
17
  GET_LIMIT_SWITCH = "M119"
17
- SET_LED = "M200"
18
+ GET_MOVE_PARAMS = "M120"
18
19
  GET_PLATFORM_SENSOR = "M121"
19
20
  GET_DOOR_SWITCH = "M122"
21
+ GET_INSTALL_DETECTED = "M123"
22
+ GET_STALLGUARD_THRESHOLD = "M911"
23
+ GET_MOTOR_DRIVER_REGISTER = "M920"
24
+ GET_TOF_SENSOR_STATUS = "M215"
25
+ GET_TOF_DRIVER_REGISTER = "M222"
26
+ GET_TOF_MEASUREMENT = "M226"
27
+ ENABLE_TOF_SENSOR = "M224"
28
+ MANAGE_TOF_MEASUREMENT = "M225"
29
+ SET_TOF_CONFIGURATION = "M227"
30
+ GET_TOF_CONFIGURATION = "M228"
31
+ SET_LED = "M200"
20
32
  SET_SERIAL_NUMBER = "M996"
33
+ SET_RUN_CURRENT = "M906"
34
+ SET_IHOLD_CURRENT = "M907"
35
+ SET_STALLGUARD = "M910"
36
+ SET_MOTOR_DRIVER_REGISTER = "M921"
37
+ SET_TOF_DRIVER_REGISTER = "M223"
21
38
  ENTER_BOOTLOADER = "dfu"
22
39
 
23
40
  def build_command(self) -> CommandBuilder:
@@ -35,6 +52,7 @@ class HardwareRevision(Enum):
35
52
 
36
53
  NFF = "nff"
37
54
  EVT = "a1"
55
+ DVT = "b1"
38
56
 
39
57
 
40
58
  @dataclass
@@ -44,18 +62,31 @@ class StackerInfo:
44
62
  fw: str
45
63
  hw: HardwareRevision
46
64
  sn: str
65
+ rr: int = 0
66
+
67
+ def to_dict(self) -> Dict[str, str]:
68
+ """Build command."""
69
+ return {
70
+ "serial": self.sn,
71
+ "version": self.fw,
72
+ "model": self.hw.value,
73
+ "reset_reason": str(self.rr),
74
+ }
47
75
 
48
76
 
49
- class StackerAxis(Enum):
77
+ class StackerAxis(str, Enum):
50
78
  """Stacker Axis."""
51
79
 
52
80
  X = "X"
53
81
  Z = "Z"
54
82
  L = "L"
55
83
 
56
- def __str__(self) -> str:
57
- """Name."""
58
- return self.name
84
+
85
+ class TOFSensor(str, Enum):
86
+ """Stacker TOF sensor."""
87
+
88
+ X = "X"
89
+ Z = "Z"
59
90
 
60
91
 
61
92
  class LEDColor(Enum):
@@ -65,13 +96,23 @@ class LEDColor(Enum):
65
96
  RED = 1
66
97
  GREEN = 2
67
98
  BLUE = 3
99
+ YELLOW = 4
100
+
101
+
102
+ class LEDPattern(Enum):
103
+ """Stacker LED Pattern."""
104
+
105
+ STATIC = 0
106
+ FLASH = 1
107
+ PULSE = 2
108
+ CONFIRM = 3
68
109
 
69
110
 
70
111
  class Direction(Enum):
71
112
  """Direction."""
72
113
 
73
114
  RETRACT = 0 # negative
74
- EXTENT = 1 # positive
115
+ EXTEND = 1 # positive
75
116
 
76
117
  def __str__(self) -> str:
77
118
  """Convert to tag for clear logging."""
@@ -79,13 +120,31 @@ class Direction(Enum):
79
120
 
80
121
  def opposite(self) -> "Direction":
81
122
  """Get opposite direction."""
82
- return Direction.EXTENT if self == Direction.RETRACT else Direction.RETRACT
123
+ return Direction.EXTEND if self == Direction.RETRACT else Direction.RETRACT
83
124
 
84
125
  def distance(self, distance: float) -> float:
85
126
  """Get signed distance, where retract direction is negative."""
86
127
  return distance * -1 if self == Direction.RETRACT else distance
87
128
 
88
129
 
130
+ class TOFSensorState(Enum):
131
+ """TOF Sensor state."""
132
+
133
+ DISABLED = 0
134
+ INITIALIZING = 1
135
+ IDLE = 2
136
+ MEASURING = 3
137
+ ERROR = 4
138
+
139
+
140
+ class TOFSensorMode(Enum):
141
+ """The mode the sensor is in."""
142
+
143
+ UNKNOWN = 0
144
+ MEASURE = 0x03
145
+ BOOTLOADER = 0x80
146
+
147
+
89
148
  @dataclass
90
149
  class LimitSwitchStatus:
91
150
  """Stacker Limit Switch Statuses."""
@@ -104,10 +163,10 @@ class LimitSwitchStatus:
104
163
  def get(self, axis: StackerAxis, direction: Direction) -> bool:
105
164
  """Get limit switch status."""
106
165
  if axis == StackerAxis.X:
107
- return self.XE if direction == Direction.EXTENT else self.XR
166
+ return self.XE if direction == Direction.EXTEND else self.XR
108
167
  if axis == StackerAxis.Z:
109
- return self.ZE if direction == Direction.EXTENT else self.ZR
110
- if direction == Direction.EXTENT:
168
+ return self.ZE if direction == Direction.EXTEND else self.ZR
169
+ if direction == Direction.EXTEND:
111
170
  raise ValueError("Latch does not have extent limit switch")
112
171
  return self.LR
113
172
 
@@ -126,13 +185,150 @@ class PlatformStatus:
126
185
 
127
186
  def get(self, direction: Direction) -> bool:
128
187
  """Get platform status."""
129
- return self.E if direction == Direction.EXTENT else self.R
188
+ return self.E if direction == Direction.EXTEND else self.R
189
+
190
+ def to_dict(self) -> Dict[str, bool]:
191
+ """Dict of the data."""
192
+ return {
193
+ "extent": self.E,
194
+ "retract": self.R,
195
+ }
196
+
197
+
198
+ @dataclass
199
+ class TOFSensorStatus:
200
+ """Stacker TOF sensor status."""
201
+
202
+ sensor: TOFSensor
203
+ state: TOFSensorState
204
+ mode: TOFSensorMode
205
+ ok: bool
130
206
 
131
207
 
132
208
  @dataclass
133
209
  class MoveParams:
134
210
  """Move Parameters."""
135
211
 
136
- max_speed: float | None = None
137
- acceleration: float | None = None
138
- max_speed_discont: float | None = None
212
+ max_speed: float
213
+ acceleration: float
214
+ max_speed_discont: float
215
+
216
+ @classmethod
217
+ def get_fields(cls) -> List[str]:
218
+ """Get parsing fields."""
219
+ return ["V", "A", "D"]
220
+
221
+ def update(
222
+ self,
223
+ max_speed: Optional[float] = None,
224
+ acceleration: Optional[float] = None,
225
+ max_speed_discont: Optional[float] = None,
226
+ ) -> "MoveParams":
227
+ """Update the move parameters and return a new object."""
228
+ return MoveParams(
229
+ max_speed=max_speed if max_speed is not None else self.max_speed,
230
+ acceleration=acceleration
231
+ if acceleration is not None
232
+ else self.acceleration,
233
+ max_speed_discont=max_speed_discont
234
+ if max_speed_discont is not None
235
+ else self.max_speed_discont,
236
+ )
237
+
238
+
239
+ @dataclass
240
+ class AxisParams:
241
+ """Axis Parameters."""
242
+
243
+ run_current: float
244
+ hold_current: float
245
+ move_params: MoveParams
246
+
247
+
248
+ @dataclass
249
+ class StallGuardParams:
250
+ """StallGuard Parameters."""
251
+
252
+ axis: StackerAxis
253
+ enabled: bool
254
+ threshold: int
255
+
256
+
257
+ class MoveResult(str, Enum):
258
+ """The result of a move command."""
259
+
260
+ NO_ERROR = "ok"
261
+ STALL_ERROR = "stall"
262
+ UNKNOWN_ERROR = "unknown"
263
+
264
+
265
+ class MeasurementKind(Enum):
266
+ """The kind of measurement to request."""
267
+
268
+ HISTOGRAM = 0
269
+
270
+
271
+ class SpadMapID(Enum):
272
+ """The spad map id for the TOF sensor."""
273
+
274
+ SPAD_MAP_ID_1 = 1
275
+ # 3x3 macro 1 mode 33°x47° FoV off center
276
+ SPAD_MAP_ID_2 = 2
277
+ # 3x3 macro 2 mode 33°x47° FoV
278
+ SPAD_MAP_ID_3 = 3
279
+ # 3x3 wide mode 41°x52° FoV
280
+ SPAD_MAP_ID_6 = 6
281
+ # 3x3 mode 33°x32° FoV, checkerboard
282
+ SPAD_MAP_ID_11 = 11
283
+ # 3x3 mode 33°x32° FoV, inverted checkerboard
284
+ SPAD_MAP_ID_12 = 12
285
+ # User defined mode, single measurement mode
286
+ SPAD_MAP_ID_14 = 14
287
+
288
+
289
+ class ActiveRange(Enum):
290
+ """The active range for the TOF sensor."""
291
+
292
+ NOT_SUPPORTED = 0
293
+ SHORT_RANGE = 0x6E
294
+ LONG_RANGE = 0x6F
295
+
296
+
297
+ @dataclass
298
+ class TOFMeasurement:
299
+ """The start measurement data."""
300
+
301
+ sensor: TOFSensor
302
+ kind: MeasurementKind
303
+ cancelled: bool
304
+ total_bytes: int
305
+
306
+
307
+ @dataclass
308
+ class TOFMeasurementFrame:
309
+ """Stacker TOF measurement frame."""
310
+
311
+ sensor: TOFSensor
312
+ frame_id: int
313
+ data: bytes
314
+
315
+
316
+ @dataclass
317
+ class TOFMeasurementResult:
318
+ """Stacker TOF measurement result."""
319
+
320
+ sensor: TOFSensor
321
+ kind: MeasurementKind
322
+ bins: Dict[int, List[int]]
323
+
324
+
325
+ @dataclass
326
+ class TOFConfiguration:
327
+ """Stacker TOF configuration."""
328
+
329
+ sensor: TOFSensor
330
+ spad_map_id: SpadMapID
331
+ active_range: Optional[ActiveRange]
332
+ kilo_iterations: Optional[int]
333
+ report_period_ms: Optional[int]
334
+ histogram_dump: Optional[bool]