opentrons 8.6.0a1__py3-none-any.whl → 8.6.0a3__py3-none-any.whl

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

Potentially problematic release.


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

opentrons/_version.py CHANGED
@@ -1,14 +1,7 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = [
5
- "__version__",
6
- "__version_tuple__",
7
- "version",
8
- "version_tuple",
9
- "__commit_id__",
10
- "commit_id",
11
- ]
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
12
5
 
13
6
  TYPE_CHECKING = False
14
7
  if TYPE_CHECKING:
@@ -16,19 +9,13 @@ if TYPE_CHECKING:
16
9
  from typing import Union
17
10
 
18
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
- COMMIT_ID = Union[str, None]
20
12
  else:
21
13
  VERSION_TUPLE = object
22
- COMMIT_ID = object
23
14
 
24
15
  version: str
25
16
  __version__: str
26
17
  __version_tuple__: VERSION_TUPLE
27
18
  version_tuple: VERSION_TUPLE
28
- commit_id: COMMIT_ID
29
- __commit_id__: COMMIT_ID
30
19
 
31
- __version__ = version = '8.6.0a1'
32
- __version_tuple__ = version_tuple = (8, 6, 0, 'a1')
33
-
34
- __commit_id__ = commit_id = None
20
+ __version__ = version = '8.6.0a3'
21
+ __version_tuple__ = version_tuple = (8, 6, 0, 'a3')
@@ -263,8 +263,13 @@ class SerialConnection:
263
263
  return
264
264
 
265
265
  lower = response.lower()
266
- res_gcode = response.split()[0]
267
- req_gcode = request.split()[0]
266
+ try:
267
+ res_gcode = response.split()[0]
268
+ req_gcode = request.split()[0]
269
+ except IndexError:
270
+ # this means the response is an empty string or something, which is weird
271
+ # but not a canonical error
272
+ return
268
273
 
269
274
  # Make sure this is not just a normal response that happens to contain the
270
275
  # `err` or `alarm` keyword in the message body by checking the gcode values
@@ -0,0 +1,274 @@
1
+ """Module Firmware update script."""
2
+ import argparse
3
+ import asyncio
4
+ from glob import glob
5
+ import os
6
+ import re
7
+ import subprocess
8
+ import sys
9
+ from typing import Dict, Final, List, Optional
10
+
11
+ from opentrons.drivers.rpi_drivers import usb
12
+ from opentrons.hardware_control.module_control import MODULE_PORT_REGEX
13
+ from opentrons.hardware_control import modules
14
+ from opentrons.hardware_control.modules.mod_abc import AbstractModule
15
+ from opentrons.hardware_control.modules.update import update_firmware
16
+ from opentrons.hardware_control.types import BoardRevision
17
+
18
+
19
+ # Constants for checking if module is back online
20
+ ONLINE_RETRIES = 3
21
+ DELAY_S = 5
22
+
23
+
24
+ MODULES: Final[Dict[str, str]] = {
25
+ "temp-deck": "tempdeck",
26
+ "mag-deck": "magdeck",
27
+ "thermocycler": "thermocycler",
28
+ "heater-shaker": "heatershaker",
29
+ "absorbance-reader": "absorbancereader",
30
+ "flex-stacker": "flexstacker",
31
+ }
32
+
33
+
34
+ def parse_version(filepath: str) -> str:
35
+ """Parse the version string from the filename."""
36
+ _, ext = os.path.splitext(os.path.basename(filepath))
37
+ ext_pattern = re.escape(ext.lstrip("."))
38
+ pattern = rf"@(v\d+(?:\.\d+)*)\.{ext_pattern}"
39
+ match = re.search(pattern, os.path.basename(filepath))
40
+ return match.group(1) if match else ""
41
+
42
+
43
+ def scan_connected_modules() -> List[modules.ModuleAtPort]:
44
+ """Scan for connected modules and return list of
45
+ tuples of serial ports and device names
46
+ """
47
+ discovered_modules = []
48
+ devices = glob("/dev/ot_module*")
49
+ for port in devices:
50
+ symlink_port = port.split("dev/")[1]
51
+ module_at_port = get_module_at_port(symlink_port)
52
+ if module_at_port:
53
+ discovered_modules.append(module_at_port)
54
+ return discovered_modules
55
+
56
+
57
+ def get_module_at_port(port: str) -> Optional[modules.ModuleAtPort]:
58
+ """Given a port, returns either a ModuleAtPort
59
+ if it is a recognized module, or None if not recognized.
60
+ """
61
+ match = MODULE_PORT_REGEX.search(port)
62
+ if match:
63
+ name = match.group(1).lower()
64
+ if name not in modules.MODULE_TYPE_BY_NAME:
65
+ print(f"Unexpected module connected: {name} on {port}")
66
+ return None
67
+ return modules.ModuleAtPort(port=f"/dev/{port}", name=name)
68
+ return None
69
+
70
+
71
+ async def build_module(
72
+ mod: modules.ModuleAtPort, loop: asyncio.AbstractEventLoop
73
+ ) -> Optional[AbstractModule]:
74
+ try:
75
+ # Get the device path and
76
+ port = subprocess.check_output(["readlink", "-f", mod.port]).decode().strip()
77
+ # remove the symlink for the device, so its freed by the robot-server
78
+ print(f"Removing symlink {mod.port}")
79
+ subprocess.check_call(["unlink", mod.port])
80
+ # wwait some time to let the device teardown
81
+ await asyncio.sleep(2)
82
+ # create an instance of the module using the device path
83
+ return await modules.build(
84
+ port=port,
85
+ usb_port=mod.usb_port,
86
+ type=modules.MODULE_TYPE_BY_NAME[mod.name],
87
+ simulating=False,
88
+ hw_control_loop=loop,
89
+ )
90
+ except Exception:
91
+ return None
92
+
93
+
94
+ async def teardown_module(module: AbstractModule) -> None:
95
+ """Tearsdown the module so it can be used again by the robot-server.."""
96
+ name = module.name()
97
+ serial = module.device_info["serial"]
98
+ port_name = module.usb_port.name
99
+ command = f"echo '{port_name}' | tee /sys/bus/usb/drivers/usb"
100
+ print(f"Removing module: {name} {serial}")
101
+ try:
102
+ # stop the poller and disconnect serial
103
+ await module._poller.stop() # type: ignore
104
+ await module._driver.disconnect() # type: ignore
105
+ # unbind the device from usb and re-bind to simulate unplug
106
+ subprocess.run(f"{command}/unbind", shell=True, capture_output=True)
107
+ await asyncio.sleep(2)
108
+ subprocess.run(f"{command}/bind", shell=True, capture_output=True)
109
+ except Exception:
110
+ pass
111
+
112
+
113
+ def enable_udev_rules(enable: bool) -> None:
114
+ """Enable/Disable creation of opentrons modules by the hardware controller.
115
+
116
+ This is done so the module is not automatically picked up by the server
117
+ while we are updating it.
118
+ """
119
+ rule = "95-opentrons-modules.rules"
120
+ original = f"/etc/udev/rules.d/{rule}"
121
+ destination = f"/var/lib/{rule}"
122
+ src = original if not enable else destination
123
+ dst = destination if not enable else original
124
+ msg = "Disabl" if not enable else "Enabl"
125
+ if not os.path.exists(src):
126
+ sys.exit(f"ERROR: Rule file not found: {src}")
127
+
128
+ try:
129
+ print(f"{msg}ing udev: Moving rule {src} -> {dst}")
130
+ subprocess.check_call(["mount", "-o", "remount,rw", "/"])
131
+ subprocess.check_call(["mv", src, dst])
132
+ except Exception as e:
133
+ sys.exit(
134
+ f"ERROR: Could not {msg}e udev rule: {rule}\n{e}",
135
+ )
136
+ finally:
137
+ subprocess.check_call(["mount", "-o", "remount,ro", "/"])
138
+ subprocess.check_call(["udevadm", "control", "--reload-rules"])
139
+
140
+
141
+ def check_dev_exist(port: str) -> bool:
142
+ """True if the device with the given port exists in /dev."""
143
+ try:
144
+ return subprocess.run(["ls", port], capture_output=True).returncode == 0
145
+ except Exception:
146
+ return False
147
+
148
+
149
+ async def main(args: argparse.Namespace) -> None: # noqa: C901
150
+ """Entry point for script."""
151
+ mod_name = MODULES[args.module]
152
+ target_version = parse_version(args.file)
153
+ if not os.path.exists(args.file):
154
+ sys.exit(f"Invalid filepath: {args.file}")
155
+ if not target_version:
156
+ sys.exit(f"Target version could not be parsed from file: {args.file}")
157
+
158
+ print("Setting up...")
159
+ loop = asyncio.get_running_loop()
160
+ usb_bus = usb.USBBus(BoardRevision.FLEX_B2) # todo: get this from the robot
161
+ print(f"Searching for {mod_name} modules in /dev/")
162
+ mods = scan_connected_modules()
163
+ mods = usb_bus.match_virtual_ports(mods) # type: ignore
164
+ if not mods:
165
+ print("No modules found")
166
+ return
167
+
168
+ # Disable udev rules so modules aren't re-created by the server when they update
169
+ teardown_modules: List[AbstractModule] = []
170
+ enable_udev_rules(False)
171
+ print("\n------------------------------------------")
172
+ for mod in mods:
173
+ if mod_name not in mod.name:
174
+ continue
175
+
176
+ # Create an instance of the opentrons module
177
+ print(f"Found mod: {mod.name} at {mod.port}")
178
+ module = await build_module(mod, loop)
179
+ if module is None:
180
+ continue
181
+
182
+ name = module.name()
183
+ version = module.device_info["version"]
184
+ serial = module.device_info["serial"]
185
+ model = module.device_info["model"]
186
+ teardown_modules.append(module)
187
+ print(f"Created module: {module.name()} {model} {serial} {version}")
188
+
189
+ # Check that the update file is for this module
190
+ file_prefix = module.firmware_prefix()
191
+ if file_prefix not in args.file:
192
+ print(f"ERROR: Target module does not match file: {mod_name} {args.file}")
193
+ continue
194
+
195
+ # Check if the module is one we care about
196
+ if args.serial and serial not in args.serial:
197
+ continue
198
+
199
+ # Check if the module needs an update
200
+ if version == target_version and not args.force:
201
+ print(f"Module {name} {serial} is up-to-date.")
202
+ continue
203
+
204
+ print(f"Updating {name} {model} {serial}: {version} -> {target_version}")
205
+ await update_firmware(module, args.file)
206
+
207
+ # wait for the device to come online
208
+ for retry in range(ONLINE_RETRIES):
209
+ if retry >= ONLINE_RETRIES:
210
+ print(f"Module {serial} failed to come back online.")
211
+ break
212
+
213
+ print(f"Checking if {name} at {module.port} is online...")
214
+ await asyncio.sleep(DELAY_S)
215
+ if not check_dev_exist(module.port):
216
+ print("Not online")
217
+ continue
218
+
219
+ # re-open serial connection
220
+ if not await module._driver.is_connected(): # type: ignore
221
+ await module._driver.connect() # type: ignore
222
+
223
+ # refresh the device info
224
+ print(f"Device {module.port} is back online, refreshing device info.")
225
+ device_info = (await module._driver.get_device_info()).to_dict() # type: ignore
226
+ success = device_info["version"] == target_version
227
+ msg = "updated successfully!" if success else "failed to update"
228
+ print(f"Device {name} {serial} {msg}")
229
+ break
230
+
231
+ print("------------------------------------------\n")
232
+ print("Tearing down")
233
+ # Enable udev rules and teardown the module so they can be pick-up by the robot-server
234
+ enable_udev_rules(True)
235
+ for module in teardown_modules:
236
+ await teardown_module(module)
237
+ print("Done")
238
+
239
+
240
+ if __name__ == "__main__":
241
+ parser = argparse.ArgumentParser(description="Module FW Update.")
242
+ parser.add_argument(
243
+ "--module",
244
+ help="The module target to be updated.",
245
+ type=str,
246
+ required=True,
247
+ choices=MODULES.keys(),
248
+ )
249
+ parser.add_argument(
250
+ "--file",
251
+ help="""Path to binary file containing the FW executable"""
252
+ """Must have format `module-name@vx.x.x.bin/hex`, ex, flex-stacker@v0.0.1.bin""",
253
+ type=str,
254
+ required=True,
255
+ )
256
+ parser.add_argument(
257
+ "--serial",
258
+ help="The specific serial numbers of the devices to update.",
259
+ type=str,
260
+ nargs="+",
261
+ )
262
+ parser.add_argument(
263
+ "--force",
264
+ help="Force install the update, even if the versions are the same.",
265
+ action="store_true",
266
+ default=False,
267
+ )
268
+ args = parser.parse_args()
269
+ try:
270
+ asyncio.run(main(args))
271
+ except Exception as e:
272
+ print("ERROR: Unhandled Exception: ", e)
273
+ # Re-enable udev rules
274
+ enable_udev_rules(True)
@@ -24,6 +24,7 @@ from opentrons.protocol_engine.types import (
24
24
  ABSMeasureMode,
25
25
  StackerFillEmptyStrategy,
26
26
  StackerStoredLabwareGroup,
27
+ StackerLabwareMovementStrategy,
27
28
  )
28
29
  from opentrons.types import DeckSlotName
29
30
  from opentrons.protocol_engine.clients import SyncClient as ProtocolEngineClient
@@ -747,6 +748,7 @@ class FlexStackerCore(ModuleCore, AbstractFlexStackerCore[LabwareCore]):
747
748
  self._engine_client.execute_command(
748
749
  cmd.flex_stacker.StoreParams(
749
750
  moduleId=self.module_id,
751
+ strategy=StackerLabwareMovementStrategy.AUTOMATIC,
750
752
  )
751
753
  )
752
754
 
@@ -41,6 +41,7 @@ from ...types import (
41
41
  InStackerHopperLocation,
42
42
  StackerStoredLabwareGroup,
43
43
  ModuleLocation,
44
+ StackerLabwareMovementStrategy,
44
45
  )
45
46
 
46
47
  if TYPE_CHECKING:
@@ -59,6 +60,13 @@ class StoreParams(BaseModel):
59
60
  ...,
60
61
  description="Unique ID of the flex stacker.",
61
62
  )
63
+ strategy: StackerLabwareMovementStrategy = Field(
64
+ ...,
65
+ description=(
66
+ "If manual, indicates that labware has been moved to the hopper "
67
+ "manually by the user, as required in error recovery."
68
+ ),
69
+ )
62
70
 
63
71
 
64
72
  class StoreResult(BaseModel):
@@ -201,42 +209,47 @@ class StoreImpl(AbstractCommandImpl[StoreParams, _ExecuteReturn]):
201
209
  stacker_hw = self._equipment.get_module_hardware_api(stacker_state.module_id)
202
210
 
203
211
  state_update = update_types.StateUpdate()
204
- try:
205
- if stacker_hw is not None:
206
- stacker_hw.set_stacker_identify(True)
212
+ if stacker_hw is not None:
213
+ stacker_hw.set_stacker_identify(True)
214
+
215
+ if (
216
+ params.strategy is StackerLabwareMovementStrategy.AUTOMATIC
217
+ and stacker_hw is not None
218
+ ):
219
+ try:
207
220
  await stacker_hw.store_labware(
208
221
  labware_height=stacker_state.get_pool_height_minus_overlap()
209
222
  )
210
- except FlexStackerStallError as e:
211
- return DefinedErrorData(
212
- public=FlexStackerStallOrCollisionError(
213
- id=self._model_utils.generate_id(),
214
- createdAt=self._model_utils.get_timestamp(),
215
- wrappedErrors=[
216
- ErrorOccurrence.from_failed(
217
- id=self._model_utils.generate_id(),
218
- createdAt=self._model_utils.get_timestamp(),
219
- error=e,
220
- )
221
- ],
222
- errorInfo={"labwareId": primary_id},
223
- ),
224
- )
225
- except FlexStackerShuttleMissingError as e:
226
- return DefinedErrorData(
227
- public=FlexStackerShuttleError(
228
- id=self._model_utils.generate_id(),
229
- createdAt=self._model_utils.get_timestamp(),
230
- wrappedErrors=[
231
- ErrorOccurrence.from_failed(
232
- id=self._model_utils.generate_id(),
233
- createdAt=self._model_utils.get_timestamp(),
234
- error=e,
235
- )
236
- ],
237
- errorInfo={"labwareId": primary_id},
238
- ),
239
- )
223
+ except FlexStackerStallError as e:
224
+ return DefinedErrorData(
225
+ public=FlexStackerStallOrCollisionError(
226
+ id=self._model_utils.generate_id(),
227
+ createdAt=self._model_utils.get_timestamp(),
228
+ wrappedErrors=[
229
+ ErrorOccurrence.from_failed(
230
+ id=self._model_utils.generate_id(),
231
+ createdAt=self._model_utils.get_timestamp(),
232
+ error=e,
233
+ )
234
+ ],
235
+ errorInfo={"labwareId": primary_id},
236
+ ),
237
+ )
238
+ except FlexStackerShuttleMissingError as e:
239
+ return DefinedErrorData(
240
+ public=FlexStackerShuttleError(
241
+ id=self._model_utils.generate_id(),
242
+ createdAt=self._model_utils.get_timestamp(),
243
+ wrappedErrors=[
244
+ ErrorOccurrence.from_failed(
245
+ id=self._model_utils.generate_id(),
246
+ createdAt=self._model_utils.get_timestamp(),
247
+ error=e,
248
+ )
249
+ ],
250
+ errorInfo={"labwareId": primary_id},
251
+ ),
252
+ )
240
253
 
241
254
  id_list = [
242
255
  id for id in (primary_id, maybe_adapter_id, maybe_lid_id) if id is not None
@@ -380,7 +380,13 @@ class CommandStore(HasState[CommandState], HandlesActions):
380
380
  prev_entry.command.intent in (CommandIntent.PROTOCOL, None)
381
381
  and action.type == ErrorRecoveryType.WAIT_FOR_RECOVERY
382
382
  ):
383
- self._state.queue_status = QueueStatus.AWAITING_RECOVERY
383
+ if (
384
+ self._state.queue_status == QueueStatus.PAUSED
385
+ or self._state.is_door_blocking
386
+ ):
387
+ self._state.queue_status = QueueStatus.AWAITING_RECOVERY_PAUSED
388
+ else:
389
+ self._state.queue_status = QueueStatus.AWAITING_RECOVERY
384
390
  self._state.recovery_target = _RecoveryTargetInfo(
385
391
  command_id=action.command_id,
386
392
  state_update_if_false_positive=state_update_if_false_positive,
@@ -1099,7 +1099,9 @@ class GeometryView:
1099
1099
 
1100
1100
  middle_slot_fixture = (
1101
1101
  self._addressable_areas.get_fixture_by_deck_slot_name(
1102
- DeckSlotName.SLOT_C2
1102
+ DeckSlotName.SLOT_C2.to_equivalent_for_robot_type(
1103
+ self._config.robot_type
1104
+ )
1103
1105
  )
1104
1106
  )
1105
1107
  if middle_slot_fixture is None:
@@ -68,6 +68,7 @@ from .module import (
68
68
  ModuleOffsetData,
69
69
  StackerFillEmptyStrategy,
70
70
  StackerStoredLabwareGroup,
71
+ StackerLabwareMovementStrategy,
71
72
  )
72
73
  from .location import (
73
74
  DeckSlotLocation,
@@ -212,6 +213,7 @@ __all__ = [
212
213
  "ModuleOffsetData",
213
214
  "StackerFillEmptyStrategy",
214
215
  "StackerStoredLabwareGroup",
216
+ "StackerLabwareMovementStrategy",
215
217
  # Locations of things on deck
216
218
  "DeckSlotLocation",
217
219
  "StagingSlotLocation",
@@ -276,6 +276,13 @@ class StackerFillEmptyStrategy(str, Enum):
276
276
  LOGICAL = "logical"
277
277
 
278
278
 
279
+ class StackerLabwareMovementStrategy(str, Enum):
280
+ """Strategy to retrieve or store labware."""
281
+
282
+ AUTOMATIC = "automatic"
283
+ MANUAL = "manual"
284
+
285
+
279
286
  class StackerStoredLabwareGroup(BaseModel):
280
287
  """Represents one group of labware stored in a stacker hopper."""
281
288
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opentrons
3
- Version: 8.6.0a1
3
+ Version: 8.6.0a3
4
4
  Summary: The Opentrons API is a simple framework designed to make writing automated biology lab protocols easy.
5
5
  Project-URL: opentrons.com, https://www.opentrons.com
6
6
  Project-URL: Source Code On Github, https://github.com/Opentrons/opentrons/tree/edge/api
@@ -24,7 +24,7 @@ Requires-Dist: click<9,>=8.0.0
24
24
  Requires-Dist: importlib-metadata>=1.0; python_version < '3.8'
25
25
  Requires-Dist: jsonschema<4.18.0,>=3.0.1
26
26
  Requires-Dist: numpy<2,>=1.20.0
27
- Requires-Dist: opentrons-shared-data==8.6.0a1
27
+ Requires-Dist: opentrons-shared-data==8.6.0a3
28
28
  Requires-Dist: packaging>=21.0
29
29
  Requires-Dist: pydantic-settings<3,>=2
30
30
  Requires-Dist: pydantic<3,>=2.0.0
@@ -32,6 +32,6 @@ Requires-Dist: pyserial>=3.5
32
32
  Requires-Dist: pyusb==1.2.1
33
33
  Requires-Dist: typing-extensions<5,>=4.0.0
34
34
  Provides-Extra: flex-hardware
35
- Requires-Dist: opentrons-hardware[flex]==8.6.0a1; extra == 'flex-hardware'
35
+ Requires-Dist: opentrons-hardware[flex]==8.6.0a3; extra == 'flex-hardware'
36
36
  Provides-Extra: ot2-hardware
37
- Requires-Dist: opentrons-hardware==8.6.0a1; extra == 'ot2-hardware'
37
+ Requires-Dist: opentrons-hardware==8.6.0a3; extra == 'ot2-hardware'
@@ -1,5 +1,5 @@
1
1
  opentrons/__init__.py,sha256=TQ_Ca_zzAM3iLzAysWKkFkQHG8-imihxDPQbLCYrf-E,4533
2
- opentrons/_version.py,sha256=TKZI6EwgFGSALxTwZXT3sPWvwD0HNFHjfuboucfB5rI,712
2
+ opentrons/_version.py,sha256=xD_T0KGoygCgIVCg4T4APgS97uLDmrAh1HwkwJPEeNk,519
3
3
  opentrons/execute.py,sha256=Y88qICDiHWQjU0L4Ou7DI5OXXu7zZcdkUvNUYmZqIfc,29282
4
4
  opentrons/legacy_broker.py,sha256=XnuEBBlrHCThc31RFW2UR0tGqctqWZ-CZ9vSC4L9whU,1553
5
5
  opentrons/ordered_set.py,sha256=g-SB3qA14yxHu9zjGyc2wC7d2TUCBE6fKZlHAtbPzI8,4082
@@ -53,7 +53,7 @@ opentrons/drivers/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
53
53
  opentrons/drivers/asyncio/communication/__init__.py,sha256=b-rd_I0ecbexGm6b9T91JLfFUrCyui9V1N1j-fzy0SQ,523
54
54
  opentrons/drivers/asyncio/communication/async_serial.py,sha256=oL92Uh3IGP3IEcP2814YaT4_uHbwjLPRVZtkoID0LmQ,5311
55
55
  opentrons/drivers/asyncio/communication/errors.py,sha256=-4pNGVKE83VUPNt1UTBLDzKtty3LxAhUNp-9yLENqyw,2678
56
- opentrons/drivers/asyncio/communication/serial_connection.py,sha256=fsu3MG7B9VhTk8TbJ_1NtlVdrwArQTNG2MnNGG4KZX0,18683
56
+ opentrons/drivers/asyncio/communication/serial_connection.py,sha256=cWcISWe0FfoZj63oYVQD-KkwBojzMTVrzJ64-Gd1u90,18876
57
57
  opentrons/drivers/flex_stacker/__init__.py,sha256=LiM0onRlgC-JfFBd0QseQU0-3WuuIxa7GNFj7Douiq8,351
58
58
  opentrons/drivers/flex_stacker/abstract.py,sha256=50xrkTC5qI_BsvlBGpdBLwF3GVi7HhKVSgloKwcrzNA,6744
59
59
  opentrons/drivers/flex_stacker/driver.py,sha256=TEy2yz5fU-vSmuLjHoZqi8fYmvdrmhoWYCMhx7ldZsg,31342
@@ -206,6 +206,7 @@ opentrons/hardware_control/scripts/ot3gripper,sha256=vK6wdD7MOMBURhpFDSzU_eZUfPc
206
206
  opentrons/hardware_control/scripts/ot3repl,sha256=arOMCJCqhT0jrpVaYJeS2oNNMG_jLfZwKT6_YeSL-bU,359
207
207
  opentrons/hardware_control/scripts/repl.py,sha256=RojtHjYV6sa6O4SeNEgs5SvnAK0imQK_XqoLQTKlzWU,5982
208
208
  opentrons/hardware_control/scripts/tc_control.py,sha256=V6hOzoRXL3xqIUEz8Raldd45aO2JgN5m5Hr08c1G8Ko,2741
209
+ opentrons/hardware_control/scripts/update_module_fw.py,sha256=FqX4y5_Ghs-uY1_KjbQZpVu8be9XR91MJy1owGt3LLc,9795
209
210
  opentrons/legacy_commands/__init__.py,sha256=erkaz7hc2iHsTtjpFDWrR1V5n47it3U1qxD2zL9CkuE,63
210
211
  opentrons/legacy_commands/commands.py,sha256=lgImZ0Y3gZrMKDVioaOXWqa6mMJCNKDa8p-lQhTghWk,14530
211
212
  opentrons/legacy_commands/helpers.py,sha256=Bc7mjK6V7b4h472NCx_qSwD0ojd_DM7mPg18tjo1DIQ,5228
@@ -256,7 +257,7 @@ opentrons/protocol_api/core/engine/exceptions.py,sha256=aZgNrmYEeuPZm21nX_KZYtvy
256
257
  opentrons/protocol_api/core/engine/instrument.py,sha256=lDrCqOZm7ceu1E50EYiSh7qTDMci5cgw4bM-AvLyMjE,98890
257
258
  opentrons/protocol_api/core/engine/labware.py,sha256=-2oygShyRZo1-R0UOoBtXniFOXfc3cSdGM-qa_l9wh8,8610
258
259
  opentrons/protocol_api/core/engine/load_labware_params.py,sha256=CxSbCBXVXHtBszP-3Ko8hsQTjvvogKRo0CCzCmMfIic,3003
259
- opentrons/protocol_api/core/engine/module_core.py,sha256=fofL9aM1u_sXgkhV589GDNUvMlIWZ9FqQLfNqedUz_E,39434
260
+ opentrons/protocol_api/core/engine/module_core.py,sha256=qxxcqpH-A4Zil9hHDxnvh4H8JuPvK6-sgkODU5YdjZQ,39537
260
261
  opentrons/protocol_api/core/engine/overlap_versions.py,sha256=PyGvQtQUg1wzNtkuGZtxwXm019PoIjq7em2JiWaxbXc,675
261
262
  opentrons/protocol_api/core/engine/pipette_movement_conflict.py,sha256=x54RIYtkL1rdzvkSp2JgusLY3EEvY8bX3OwipeVKdsE,15511
262
263
  opentrons/protocol_api/core/engine/point_calculations.py,sha256=C2eF0fvJQGMqQv3DzNhc1-m8HTAXTyTsHPJEPrEUEmo,2502
@@ -368,7 +369,7 @@ opentrons/protocol_engine/commands/flex_stacker/empty.py,sha256=1R0AwQtLn_wfuFH0
368
369
  opentrons/protocol_engine/commands/flex_stacker/fill.py,sha256=3qhAQxJfaSp4EAFGIUotkrJVuOvdFrwzmkMCa2v6khA,10866
369
370
  opentrons/protocol_engine/commands/flex_stacker/retrieve.py,sha256=kh9Lh-FW-RrXmV4RS8txmTYPfPDKhQhjW0UXr8k0vhc,12763
370
371
  opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py,sha256=5VdqCcBdIHmpWqIJN3rCKeZ6IKwfxAdQTXMTD-KL-5w,12484
371
- opentrons/protocol_engine/commands/flex_stacker/store.py,sha256=WwUKAJKgrvz0mD1nKJ-bIn-IKHpjCHNp1539ik52Vpk,12231
372
+ opentrons/protocol_engine/commands/flex_stacker/store.py,sha256=8iS77NRj9CVp12S9uVfCJTl-Q0iaJkCQDcOJWZnSeI0,12770
372
373
  opentrons/protocol_engine/commands/heater_shaker/__init__.py,sha256=ImAPrYSUvP8tI7obvoHmrJbjwLldgGNTnFYRgfXj8hI,2757
373
374
  opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py,sha256=Q7sqFtzUD8wclRLL2PLWjnClIeLtJsiMCobStvzoJKc,2847
374
375
  opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py,sha256=UYeGrTmnGtfw22p0agefI2ZnpukKlIgFcmJv9v58Xnc,2755
@@ -456,11 +457,11 @@ opentrons/protocol_engine/state/_move_types.py,sha256=STLssWsXMY92F_asAQrixv10A6
456
457
  opentrons/protocol_engine/state/_well_math.py,sha256=hppbOs4G6yQ6wgvKQWNhOs2zdYA_MwFW7qKgg2bGyx0,9714
457
458
  opentrons/protocol_engine/state/addressable_areas.py,sha256=3tjP7EedkpD5_tjTXa2n0PlQRRf1ttLrh_hOl9k0ThI,28811
458
459
  opentrons/protocol_engine/state/command_history.py,sha256=cz3Nllk045OpK6r9m4ByZr2hcCYo4pTy-2uoIZtV5XE,12241
459
- opentrons/protocol_engine/state/commands.py,sha256=YNPfx5PRyogc-bUzkq7sktwSCmTzssslnzYn3nMYR9g,45367
460
+ opentrons/protocol_engine/state/commands.py,sha256=y5WE2pKmnMalgHFHEiBnBurO2TZ9wVPDMpS8Y9wMyrs,45612
460
461
  opentrons/protocol_engine/state/config.py,sha256=7jSGxC6Vqj1eA8fqZ2I3zjlxVXg8pxvcBYMztRIx9Mg,1515
461
462
  opentrons/protocol_engine/state/files.py,sha256=w8xxxg8HY0RqKKEGSfHWfrjV54Gb02O3dwtisJ-9j8E,1753
462
463
  opentrons/protocol_engine/state/fluid_stack.py,sha256=uwkf0qYk1UX5iU52xmk-e3yLPK8OG-TtMCcBqrkVFpM,5932
463
- opentrons/protocol_engine/state/geometry.py,sha256=O-P6xUHh1UvaCaz1PxjiozsneYRviw3vxUtON-osMPk,101534
464
+ opentrons/protocol_engine/state/geometry.py,sha256=WmS1hg7F4Ng3vn7HQS91DIWPvuT3nKgBE69E-0IGha4,101642
464
465
  opentrons/protocol_engine/state/inner_well_math_utils.py,sha256=UhemsPpcuKwVc-iGXI2-v--miOGNunAnAVznJTVADlQ,20598
465
466
  opentrons/protocol_engine/state/labware.py,sha256=CRY84JCj9Y31aA-QWgqboql_PRD7QFZh1ja6Iboo0Wg,61308
466
467
  opentrons/protocol_engine/state/liquid_classes.py,sha256=u_z75UYdiFAKG0yB3mr1il4T3qaS0Sotq8sL7KLODP8,2990
@@ -481,7 +482,7 @@ opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py,sha2
481
482
  opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py,sha256=IJ5zpufz5WSRbJqHOAi-WroDxpsRZz-GvwznIL4v7VQ,2468
482
483
  opentrons/protocol_engine/state/module_substates/temperature_module_substate.py,sha256=w9h6EBM1YY8SeUOlUz5-nW1Zoyce8-zua8Z6mX4sDNg,2310
483
484
  opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py,sha256=fLt2jMsbnfe8Q25vAjloxLBGdx8sotqM34VxbwfegpE,5167
484
- opentrons/protocol_engine/types/__init__.py,sha256=T5Tt7O-g9VY3GYqaq1AfrIe6plErR_N19SpbZ2SlYfo,8270
485
+ opentrons/protocol_engine/types/__init__.py,sha256=8nOYquZPSvQAqkCz9Jfev86SmQ1Tou-AL5RMMvSz07o,8344
485
486
  opentrons/protocol_engine/types/automatic_tip_selection.py,sha256=I_B3iWei1Sl7F7IrMKqOn4S12heZXRnfKvtCTUXIMyM,1118
486
487
  opentrons/protocol_engine/types/command_annotations.py,sha256=5A4k_R_4A2_nGl0K85SKwNlnKA09fUhEIe_mdU55yro,1843
487
488
  opentrons/protocol_engine/types/deck_configuration.py,sha256=3dhkk3Z_PrJvqb26brkEdlyzMAGih_UopoEzu9k6SRk,2422
@@ -498,7 +499,7 @@ opentrons/protocol_engine/types/liquid_class.py,sha256=SF5WS3s38S87efUqawRGSIYqj
498
499
  opentrons/protocol_engine/types/liquid_handling.py,sha256=Xx1GihrNRJJdJJA5zIwWvIYNydbSXAHjSUAliF18Iu0,319
499
500
  opentrons/protocol_engine/types/liquid_level_detection.py,sha256=hdSztrZcexko7p4lEkC8YuKlepv3j6ovKhMeQZuUHcg,6348
500
501
  opentrons/protocol_engine/types/location.py,sha256=qIYBs86RO1ws2aXStmdx0CqEVNF9enlj-ACknf75nSs,5707
501
- opentrons/protocol_engine/types/module.py,sha256=DzDQ9WMWwGH0pP3i6-7bAQgrJ4GnCjlporguo1qwieU,9578
502
+ opentrons/protocol_engine/types/module.py,sha256=EDfRqa1Wb1siZ8vt7zQVCjCRcTiGoOjqzrgySdxm-zw,9729
502
503
  opentrons/protocol_engine/types/partial_tip_configuration.py,sha256=4RMtHOAX-dgpXWA737tthj_izTBnhKphBcA24LAKmhI,2760
503
504
  opentrons/protocol_engine/types/run_time_parameters.py,sha256=5gH4GmGK7e3OkSClftZT6VA4wXELIvEMgpmQ__waceU,4468
504
505
  opentrons/protocol_engine/types/tip.py,sha256=qfjnCPG7RAlTGS80SAQB8rGwtFW3yRWYj7DpYy0oDow,389
@@ -593,8 +594,8 @@ opentrons/util/linal.py,sha256=IlKAP9HkNBBgULeSf4YVwSKHdx9jnCjSr7nvDvlRALg,5753
593
594
  opentrons/util/logging_config.py,sha256=7et4YYuQdWdq_e50U-8vFS_QyNBRgdnqPGAQJm8qrIo,9954
594
595
  opentrons/util/logging_queue_handler.py,sha256=ZsSJwy-oV8DXwpYiZisQ1PbYwmK2cOslD46AcyJ1E4I,2484
595
596
  opentrons/util/performance_helpers.py,sha256=ew7H8XD20iS6-2TJAzbQeyzStZkkE6PzHt_Adx3wbZQ,5172
596
- opentrons-8.6.0a1.dist-info/METADATA,sha256=6KTuHgy1y5dweiTc8EtwQOvxQuJ_OB93FLPuNR9kU2A,1607
597
- opentrons-8.6.0a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
598
- opentrons-8.6.0a1.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
599
- opentrons-8.6.0a1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
600
- opentrons-8.6.0a1.dist-info/RECORD,,
597
+ opentrons-8.6.0a3.dist-info/METADATA,sha256=2OzHrnc75fEw1gcKzSg87PJzcTgAY75e8BhN5PijOwI,1607
598
+ opentrons-8.6.0a3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
599
+ opentrons-8.6.0a3.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
600
+ opentrons-8.6.0a3.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
601
+ opentrons-8.6.0a3.dist-info/RECORD,,