opentrons 8.6.0a1__py3-none-any.whl → 8.6.0a2__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 +3 -16
- opentrons/hardware_control/scripts/update_module_fw.py +274 -0
- opentrons/protocol_engine/state/commands.py +7 -1
- {opentrons-8.6.0a1.dist-info → opentrons-8.6.0a2.dist-info}/METADATA +4 -4
- {opentrons-8.6.0a1.dist-info → opentrons-8.6.0a2.dist-info}/RECORD +8 -7
- {opentrons-8.6.0a1.dist-info → opentrons-8.6.0a2.dist-info}/WHEEL +0 -0
- {opentrons-8.6.0a1.dist-info → opentrons-8.6.0a2.dist-info}/entry_points.txt +0 -0
- {opentrons-8.6.0a1.dist-info → opentrons-8.6.0a2.dist-info}/licenses/LICENSE +0 -0
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.
|
|
32
|
-
__version_tuple__ = version_tuple = (8, 6, 0, '
|
|
33
|
-
|
|
34
|
-
__commit_id__ = commit_id = None
|
|
20
|
+
__version__ = version = '8.6.0a2'
|
|
21
|
+
__version_tuple__ = version_tuple = (8, 6, 0, 'a2')
|
|
@@ -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)
|
|
@@ -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
|
-
|
|
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,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opentrons
|
|
3
|
-
Version: 8.6.
|
|
3
|
+
Version: 8.6.0a2
|
|
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.
|
|
27
|
+
Requires-Dist: opentrons-shared-data==8.6.0a2
|
|
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.
|
|
35
|
+
Requires-Dist: opentrons-hardware[flex]==8.6.0a2; extra == 'flex-hardware'
|
|
36
36
|
Provides-Extra: ot2-hardware
|
|
37
|
-
Requires-Dist: opentrons-hardware==8.6.
|
|
37
|
+
Requires-Dist: opentrons-hardware==8.6.0a2; extra == 'ot2-hardware'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
opentrons/__init__.py,sha256=TQ_Ca_zzAM3iLzAysWKkFkQHG8-imihxDPQbLCYrf-E,4533
|
|
2
|
-
opentrons/_version.py,sha256=
|
|
2
|
+
opentrons/_version.py,sha256=GNMqhyBM4WyqEn46p7PGtrzujTlw5TeW__t1rYiBsMc,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
|
|
@@ -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
|
|
@@ -456,7 +457,7 @@ 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=
|
|
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
|
|
@@ -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.
|
|
597
|
-
opentrons-8.6.
|
|
598
|
-
opentrons-8.6.
|
|
599
|
-
opentrons-8.6.
|
|
600
|
-
opentrons-8.6.
|
|
597
|
+
opentrons-8.6.0a2.dist-info/METADATA,sha256=cYFFLWaqACc2IODIfrDWwlHmMo3gwbsx_Tda1r2hX9Q,1607
|
|
598
|
+
opentrons-8.6.0a2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
599
|
+
opentrons-8.6.0a2.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
|
|
600
|
+
opentrons-8.6.0a2.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
601
|
+
opentrons-8.6.0a2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|