@opentrons/shared-data 0.0.1-alpha.19 → 0.0.1-alpha.21
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.
- package/lib/{bundle.673284981fd97184260c.js → opentrons-shared-data.js} +2 -2
- package/lib/opentrons-shared-data.js.map +1 -0
- package/package.json +1 -1
- package/webpack.config.js +1 -0
- package/js/__tests__/__snapshots__/pipettes.test.ts.snap +0 -5969
- package/js/__tests__/deckSchemas.test.ts +0 -87
- package/js/__tests__/errors.test.js +0 -21
- package/js/__tests__/getAreSlotsAdjacent.test.ts +0 -128
- package/js/__tests__/getWellNamePerMultiTip.test.ts +0 -162
- package/js/__tests__/labwareDefQuirks.test.ts +0 -37
- package/js/__tests__/labwareDefSchemaV1.test.ts +0 -82
- package/js/__tests__/labwareDefSchemaV2.test.ts +0 -298
- package/js/__tests__/moduleAccessors.test.ts +0 -50
- package/js/__tests__/moduleSpecsSchema.test.ts +0 -77
- package/js/__tests__/pipetteSchemaV2.test.ts +0 -107
- package/js/__tests__/pipetteSpecSchemas.test.ts +0 -48
- package/js/__tests__/pipettes.test.ts +0 -58
- package/js/__tests__/protocolSchemaV4.test.ts +0 -137
- package/js/__tests__/protocolSchemaV5.test.ts +0 -137
- package/js/__tests__/protocolSchemaV6.test.ts +0 -132
- package/js/__tests__/protocolSchemaV7.test.ts +0 -64
- package/js/__tests__/protocolValidation.test.ts +0 -83
- package/js/__tests__/sortWells.test.ts +0 -40
- package/js/__tests__/splitWellsOnColumn.test.ts +0 -21
- package/js/__tests__/validateErrors.test.js +0 -34
- package/js/constants.ts +0 -338
- package/js/cypressUtils.ts +0 -35
- package/js/errors.ts +0 -33
- package/js/fixtures.ts +0 -161
- package/js/getLabware.ts +0 -154
- package/js/gripper.ts +0 -29
- package/js/helpers/__tests__/getAdapterName.test.ts +0 -31
- package/js/helpers/__tests__/getDeckDefFromLoadedLabware.test.ts +0 -12
- package/js/helpers/__tests__/getVectorDifference.test.ts +0 -25
- package/js/helpers/__tests__/getVectorSum.test.ts +0 -21
- package/js/helpers/__tests__/labwareInference.test.ts +0 -84
- package/js/helpers/__tests__/orderWells.test.ts +0 -87
- package/js/helpers/__tests__/parseProtocolData.test.ts +0 -165
- package/js/helpers/__tests__/volume.test.ts +0 -108
- package/js/helpers/__tests__/wellSets.test.ts +0 -352
- package/js/helpers/get96Channel384WellPlateWells.ts +0 -44
- package/js/helpers/getAddressableAreasInProtocol.ts +0 -59
- package/js/helpers/getLoadedLabwareDefinitionsByUri.ts +0 -30
- package/js/helpers/getModuleVizDims.ts +0 -118
- package/js/helpers/getOccludedSlotCountForModule.ts +0 -13
- package/js/helpers/getVectorDifference.ts +0 -12
- package/js/helpers/getVectorSum.ts +0 -12
- package/js/helpers/getWellNamePerMultiTip.ts +0 -86
- package/js/helpers/getWellTotalVolume.ts +0 -16
- package/js/helpers/index.ts +0 -347
- package/js/helpers/labwareInference.ts +0 -92
- package/js/helpers/orderWells.ts +0 -69
- package/js/helpers/parseProtocolData.ts +0 -141
- package/js/helpers/volume.ts +0 -35
- package/js/helpers/wellIsRect.ts +0 -6
- package/js/helpers/wellSets.ts +0 -155
- package/js/index.ts +0 -14
- package/js/labwareTools/__tests__/__snapshots__/createIrregularLabware.test.ts.snap +0 -3
- package/js/labwareTools/__tests__/__snapshots__/createLabware.test.ts.snap +0 -3
- package/js/labwareTools/__tests__/createDefaultDisplayName.test.ts +0 -111
- package/js/labwareTools/__tests__/createIrregularLabware.test.ts +0 -226
- package/js/labwareTools/__tests__/createLabware.test.ts +0 -147
- package/js/labwareTools/index.ts +0 -452
- package/js/modules.ts +0 -97
- package/js/pipettes.ts +0 -89
- package/js/protocols.ts +0 -290
- package/js/schema.ts +0 -80
- package/js/scripts/generateDeckLayersFromSVG.js +0 -46
- package/js/titleCase.ts +0 -30
- package/js/types.ts +0 -600
- package/lib/bundle.673284981fd97184260c.js.map +0 -1
- package/python/.coveragerc +0 -4
- package/python/.flake8 +0 -32
- package/python/Config.in +0 -10
- package/python/Makefile +0 -134
- package/python/Pipfile +0 -28
- package/python/Pipfile.lock +0 -645
- package/python/buildroot.mk +0 -30
- package/python/mypy.ini +0 -41
- package/python/opentrons_shared_data/__init__.py +0 -17
- package/python/opentrons_shared_data/_version.py +0 -20
- package/python/opentrons_shared_data/command/__init__.py +0 -52
- package/python/opentrons_shared_data/deck/__init__.py +0 -96
- package/python/opentrons_shared_data/deck/dev_types.py +0 -180
- package/python/opentrons_shared_data/errors/__init__.py +0 -30
- package/python/opentrons_shared_data/errors/categories.py +0 -60
- package/python/opentrons_shared_data/errors/codes.py +0 -108
- package/python/opentrons_shared_data/errors/exceptions.py +0 -915
- package/python/opentrons_shared_data/gripper/__init__.py +0 -63
- package/python/opentrons_shared_data/gripper/constants.py +0 -6
- package/python/opentrons_shared_data/gripper/gripper_definition.py +0 -101
- package/python/opentrons_shared_data/labware/__init__.py +0 -22
- package/python/opentrons_shared_data/labware/constants.py +0 -7
- package/python/opentrons_shared_data/labware/dev_types.py +0 -140
- package/python/opentrons_shared_data/labware/labware_definition.py +0 -328
- package/python/opentrons_shared_data/load.py +0 -57
- package/python/opentrons_shared_data/module/__init__.py +0 -74
- package/python/opentrons_shared_data/module/dev_types.py +0 -141
- package/python/opentrons_shared_data/pipette/__init__.py +0 -107
- package/python/opentrons_shared_data/pipette/dev_types.py +0 -177
- package/python/opentrons_shared_data/pipette/file_operation_helpers.py +0 -37
- package/python/opentrons_shared_data/pipette/load_data.py +0 -261
- package/python/opentrons_shared_data/pipette/model_constants.py +0 -123
- package/python/opentrons_shared_data/pipette/mutable_configurations.py +0 -431
- package/python/opentrons_shared_data/pipette/pipette_definition.py +0 -503
- package/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py +0 -240
- package/python/opentrons_shared_data/pipette/scripts/__init__.py +0 -0
- package/python/opentrons_shared_data/pipette/scripts/build_json_script.py +0 -469
- package/python/opentrons_shared_data/pipette/scripts/update_configuration_files.py +0 -450
- package/python/opentrons_shared_data/pipette/types.py +0 -193
- package/python/opentrons_shared_data/protocol/__init__.py +0 -13
- package/python/opentrons_shared_data/protocol/constants.py +0 -98
- package/python/opentrons_shared_data/protocol/dev_types.py +0 -436
- package/python/opentrons_shared_data/protocol/models/__init__.py +0 -48
- package/python/opentrons_shared_data/protocol/models/protocol_schema_v6.py +0 -145
- package/python/opentrons_shared_data/protocol/models/protocol_schema_v7.py +0 -95
- package/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py +0 -110
- package/python/opentrons_shared_data/protocol/models/shared_models.py +0 -111
- package/python/opentrons_shared_data/py.typed +0 -0
- package/python/opentrons_shared_data/robot/__init__.py +0 -33
- package/python/opentrons_shared_data/robot/dev_types.py +0 -37
- package/python/pytest.ini +0 -2
- package/python/setup.cfg +0 -5
- package/python/setup.py +0 -179
- package/python/tests/__init__.py +0 -1
- package/python/tests/conftest.py +0 -0
- package/python/tests/deck/__init__.py +0 -0
- package/python/tests/deck/test_list_names.py +0 -12
- package/python/tests/deck/test_position.py +0 -113
- package/python/tests/deck/test_typechecks.py +0 -28
- package/python/tests/errors/__init__.py +0 -1
- package/python/tests/errors/test_error_completeness.py +0 -19
- package/python/tests/errors/test_exceptions.py +0 -74
- package/python/tests/gripper/__init__.py +0 -0
- package/python/tests/gripper/test_definition.py +0 -30
- package/python/tests/gripper/test_load.py +0 -16
- package/python/tests/labware/__init__.py +0 -13
- package/python/tests/labware/test_typechecks.py +0 -21
- package/python/tests/module/__init__.py +0 -20
- package/python/tests/module/test_load.py +0 -49
- package/python/tests/module/test_typechecks.py +0 -34
- package/python/tests/pipette/__init__.py +0 -0
- package/python/tests/pipette/test_load_data.py +0 -103
- package/python/tests/pipette/test_mutable_configurations.py +0 -266
- package/python/tests/pipette/test_pipette_definition.py +0 -183
- package/python/tests/pipette/test_pipette_load_name_conversions.py +0 -222
- package/python/tests/pipette/test_typechecks.py +0 -50
- package/python/tests/pipette/test_validate_schema.py +0 -37
- package/python/tests/protocol/__init__.py +0 -15
- package/python/tests/protocol/test_protocol_schema_v6.py +0 -107
- package/python/tests/protocol/test_protocol_schema_v7.py +0 -34
- package/python/tests/protocol/test_protocol_schema_v8.py +0 -20
- package/python/tests/protocol/test_typechecks.py +0 -36
- package/python/tests/robot/__init__.py +0 -0
- package/python/tests/robot/test_typechecks.py +0 -20
- /package/lib/{bundle.673284981fd97184260c.js.LICENSE.txt → opentrons-shared-data.js.LICENSE.txt} +0 -0
|
@@ -1,450 +0,0 @@
|
|
|
1
|
-
"""A script for updating pre-existing V2 Pipette configurations."""
|
|
2
|
-
import os
|
|
3
|
-
import json
|
|
4
|
-
import argparse
|
|
5
|
-
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import List, Dict, Tuple, Any, Iterator, Type
|
|
8
|
-
|
|
9
|
-
from pydantic import BaseModel
|
|
10
|
-
from pydantic.main import ModelMetaclass
|
|
11
|
-
from enum import Enum
|
|
12
|
-
|
|
13
|
-
from opentrons_shared_data import get_shared_data_root
|
|
14
|
-
|
|
15
|
-
from ..pipette_definition import (
|
|
16
|
-
PipetteConfigurations,
|
|
17
|
-
PipetteGeometryDefinition,
|
|
18
|
-
PipettePhysicalPropertiesDefinition,
|
|
19
|
-
PipetteLiquidPropertiesDefinition,
|
|
20
|
-
PipetteModelVersionType,
|
|
21
|
-
SupportedTipsDefinition,
|
|
22
|
-
)
|
|
23
|
-
from ..types import (
|
|
24
|
-
PipetteModelType,
|
|
25
|
-
PipetteChannelType,
|
|
26
|
-
PipetteVersionType,
|
|
27
|
-
PipetteTipType,
|
|
28
|
-
PipetteModelMajorVersion,
|
|
29
|
-
PipetteModelMinorVersion,
|
|
30
|
-
LiquidClasses,
|
|
31
|
-
)
|
|
32
|
-
from ..load_data import _geometry, _physical, _liquid
|
|
33
|
-
from ..pipette_load_name_conversions import convert_pipette_model
|
|
34
|
-
from ..dev_types import PipetteModel
|
|
35
|
-
|
|
36
|
-
"""
|
|
37
|
-
Instructions:
|
|
38
|
-
|
|
39
|
-
To run this script, you must be in `shared-data/python`. To invoke, use the command:
|
|
40
|
-
|
|
41
|
-
`pipenv run python -m opentrons_shared_data.pipette.scripts.update_configuration_files`
|
|
42
|
-
|
|
43
|
-
If you want to update all files, you can simply use the argument `--update_all_models`.
|
|
44
|
-
|
|
45
|
-
Make sure to run `make format-js` afterwards to ensure formatting of the json files
|
|
46
|
-
is good.
|
|
47
|
-
|
|
48
|
-
*Note* If you are adding a brand-new key, you MUST update the pydantic models
|
|
49
|
-
found in `python/pipette/pipette_definition.py` before running this script.
|
|
50
|
-
|
|
51
|
-
*Note* When you are entering in your data, please utilize the exact type. I.e. if it's a
|
|
52
|
-
list, you must input the list like: [1, 2, 3] or if it's a dict, like: {"data": 1}..
|
|
53
|
-
|
|
54
|
-
For now, we do not support updating pipetting functions in this script.
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
ROOT = get_shared_data_root() / "pipette" / "definitions" / "2"
|
|
58
|
-
NOZZLE_LOCATION_CONFIGS = ["nozzle_offset", "nozzle_map"]
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def _change_to_camel_case(c: str) -> str:
|
|
62
|
-
# Tiny helper function to convert to camelCase.
|
|
63
|
-
config_name = c.split("_")
|
|
64
|
-
if len(config_name) == 1:
|
|
65
|
-
return config_name[0]
|
|
66
|
-
return f"{config_name[0]}" + "".join(s.capitalize() for s in config_name[1::])
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def list_configuration_keys() -> Tuple[List[str], Dict[int, str]]:
|
|
70
|
-
"""List out the model keys available to modify at the top level."""
|
|
71
|
-
lookup = {i: v for (i, v) in enumerate(PipetteConfigurations.__fields__)}
|
|
72
|
-
return [
|
|
73
|
-
f"{i}: {v}" for (i, v) in enumerate(PipetteConfigurations.__fields__)
|
|
74
|
-
], lookup
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def list_available_enum(enum_type: Type[Enum]) -> List[str]:
|
|
78
|
-
"""List available pipette models"""
|
|
79
|
-
return [f"{i}: {v}" for (i, v) in enumerate(enum_type)] # type: ignore[var-annotated]
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def handle_subclass_model(
|
|
83
|
-
top_level_configuration: List[str], base_model: BaseModel, is_basemodel: bool
|
|
84
|
-
) -> List[str]:
|
|
85
|
-
"""Handle sub-classed basemodels and update the top level model as necessary."""
|
|
86
|
-
if is_basemodel:
|
|
87
|
-
if base_model.__fields__ == SupportedTipsDefinition.__fields__:
|
|
88
|
-
# pydantic does something weird with the types in ModelFields so
|
|
89
|
-
# we cannot use isinstance checks to confirm if the base model
|
|
90
|
-
# is a supported tips definition
|
|
91
|
-
print(f"choose {PipetteTipType.__name__}:")
|
|
92
|
-
for row in list_available_enum(PipetteTipType):
|
|
93
|
-
print(f"\t{row}")
|
|
94
|
-
tip_type = list(PipetteTipType)[
|
|
95
|
-
int(input("select the tip volume size to modify"))
|
|
96
|
-
]
|
|
97
|
-
top_level_configuration.append(tip_type.name)
|
|
98
|
-
|
|
99
|
-
lookup = {i: v for (i, v) in enumerate(base_model.__fields__)}
|
|
100
|
-
config_list = [f"{i}: {v}" for (i, v) in enumerate(base_model.__fields__)]
|
|
101
|
-
print(f"you selected the basemodel {base_model.__name__}:") # type: ignore[attr-defined]
|
|
102
|
-
for row in config_list:
|
|
103
|
-
print(f"\t{row}")
|
|
104
|
-
|
|
105
|
-
configuration_to_update = lookup[
|
|
106
|
-
int(input("select a specific configuration from above\n"))
|
|
107
|
-
]
|
|
108
|
-
field_type = base_model.__fields__[configuration_to_update].type_
|
|
109
|
-
is_basemodel = isinstance(field_type, ModelMetaclass)
|
|
110
|
-
|
|
111
|
-
top_level_configuration.append(configuration_to_update)
|
|
112
|
-
return handle_subclass_model(top_level_configuration, field_type, is_basemodel)
|
|
113
|
-
else:
|
|
114
|
-
return top_level_configuration
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def check_from_version(version: str) -> str:
|
|
118
|
-
"""Check that the version requested is supported in the system."""
|
|
119
|
-
version_int = [int(v) for v in version.split(".")]
|
|
120
|
-
if version_int[0] not in PipetteModelMajorVersion:
|
|
121
|
-
raise ValueError(f"Major version {version_int[0]} is not supported.")
|
|
122
|
-
if version_int[1] not in PipetteModelMinorVersion:
|
|
123
|
-
raise ValueError(f"Minor version {version_int[1]} is not supported.")
|
|
124
|
-
return version
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def save_data_to_file(
|
|
128
|
-
directorypath: Path,
|
|
129
|
-
file_name: str,
|
|
130
|
-
data: Dict[str, Any],
|
|
131
|
-
) -> None:
|
|
132
|
-
"""
|
|
133
|
-
Function used to save data to a file
|
|
134
|
-
"""
|
|
135
|
-
directorypath.mkdir(parents=True, exist_ok=True)
|
|
136
|
-
filepath = directorypath / f"{file_name}.json"
|
|
137
|
-
with open(filepath, "w") as f:
|
|
138
|
-
json.dump(data, f, indent=2)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def update(
|
|
142
|
-
dict_to_update: Dict[str, Any], iter_of_configs: Iterator[str], value_to_update: Any
|
|
143
|
-
) -> Dict[str, Any]:
|
|
144
|
-
"""
|
|
145
|
-
Recursively update the given dictionary to ensure no data is lost when updating.
|
|
146
|
-
"""
|
|
147
|
-
next_key = next(iter_of_configs, None)
|
|
148
|
-
if next_key and isinstance(dict_to_update.get(next_key), dict):
|
|
149
|
-
dict_to_update[next_key] = update(
|
|
150
|
-
dict_to_update.get(next_key, {}), iter_of_configs, value_to_update
|
|
151
|
-
)
|
|
152
|
-
elif next_key:
|
|
153
|
-
dict_to_update[next_key] = value_to_update
|
|
154
|
-
return dict_to_update
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def build_nozzle_map(
|
|
158
|
-
nozzle_offset: List[float], channels: PipetteChannelType
|
|
159
|
-
) -> Dict[str, List[float]]:
|
|
160
|
-
Y_OFFSET = -9
|
|
161
|
-
X_OFFSET = 9
|
|
162
|
-
if channels == PipetteChannelType.SINGLE_CHANNEL:
|
|
163
|
-
return {"A1": nozzle_offset}
|
|
164
|
-
elif channels == PipetteChannelType.EIGHT_CHANNEL:
|
|
165
|
-
return {
|
|
166
|
-
f"{chr(ord('A') + 1*row)}1": [
|
|
167
|
-
nozzle_offset[0],
|
|
168
|
-
nozzle_offset[1] + Y_OFFSET * row,
|
|
169
|
-
nozzle_offset[2],
|
|
170
|
-
]
|
|
171
|
-
for row in range(8)
|
|
172
|
-
}
|
|
173
|
-
elif channels == PipetteChannelType.NINETY_SIX_CHANNEL:
|
|
174
|
-
return {
|
|
175
|
-
f"{chr(ord('A') + 1*row)}{1 + 1*col}": [
|
|
176
|
-
nozzle_offset[0] + X_OFFSET * col,
|
|
177
|
-
nozzle_offset[1] + Y_OFFSET * row,
|
|
178
|
-
nozzle_offset[2],
|
|
179
|
-
]
|
|
180
|
-
for row in range(8)
|
|
181
|
-
for col in range(12)
|
|
182
|
-
}
|
|
183
|
-
raise ValueError(f"Unsupported channel type {channels}")
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def load_and_update_file_from_config(
|
|
187
|
-
config_to_update: List[str],
|
|
188
|
-
value_to_update: Any,
|
|
189
|
-
model_to_update: PipetteModelVersionType,
|
|
190
|
-
) -> None:
|
|
191
|
-
"""Update the requested config and save to disk.
|
|
192
|
-
|
|
193
|
-
Load the requested config sub type (physical, geometry or liquid). Then
|
|
194
|
-
update the current file and save to disk.
|
|
195
|
-
|
|
196
|
-
"""
|
|
197
|
-
camel_list_to_update = iter([_change_to_camel_case(i) for i in config_to_update])
|
|
198
|
-
|
|
199
|
-
if config_to_update[0] in PipetteGeometryDefinition.__fields__:
|
|
200
|
-
geometry = _geometry(
|
|
201
|
-
model_to_update.pipette_channels,
|
|
202
|
-
model_to_update.pipette_type,
|
|
203
|
-
model_to_update.pipette_version,
|
|
204
|
-
)
|
|
205
|
-
if config_to_update[0] == "nozzle_map":
|
|
206
|
-
nozzle_to_use = (
|
|
207
|
-
value_to_update if value_to_update else geometry["nozzleOffset"]
|
|
208
|
-
)
|
|
209
|
-
geometry["nozzleMap"] = build_nozzle_map(
|
|
210
|
-
nozzle_to_use, model_to_update.pipette_channels
|
|
211
|
-
)
|
|
212
|
-
elif config_to_update[0] == "nozzle_offset":
|
|
213
|
-
geometry["nozzleMap"] = build_nozzle_map(
|
|
214
|
-
value_to_update, model_to_update.pipette_channels
|
|
215
|
-
)
|
|
216
|
-
geometry["nozzleOffset"] = value_to_update
|
|
217
|
-
else:
|
|
218
|
-
geometry = update(geometry, camel_list_to_update, value_to_update)
|
|
219
|
-
PipetteGeometryDefinition.parse_obj(geometry)
|
|
220
|
-
|
|
221
|
-
filepath = (
|
|
222
|
-
ROOT
|
|
223
|
-
/ "geometry"
|
|
224
|
-
/ model_to_update.pipette_channels.name.lower()
|
|
225
|
-
/ model_to_update.pipette_type.value
|
|
226
|
-
)
|
|
227
|
-
save_data_to_file(
|
|
228
|
-
filepath,
|
|
229
|
-
f"{model_to_update.pipette_version.major}_{model_to_update.pipette_version.minor}",
|
|
230
|
-
geometry,
|
|
231
|
-
)
|
|
232
|
-
elif config_to_update[0] in PipettePhysicalPropertiesDefinition.__fields__:
|
|
233
|
-
physical = _physical(
|
|
234
|
-
model_to_update.pipette_channels,
|
|
235
|
-
model_to_update.pipette_type,
|
|
236
|
-
model_to_update.pipette_version,
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
physical = update(physical, camel_list_to_update, value_to_update)
|
|
240
|
-
|
|
241
|
-
PipettePhysicalPropertiesDefinition.parse_obj(physical)
|
|
242
|
-
filepath = (
|
|
243
|
-
ROOT
|
|
244
|
-
/ "general"
|
|
245
|
-
/ model_to_update.pipette_channels.name.lower()
|
|
246
|
-
/ model_to_update.pipette_type.value
|
|
247
|
-
)
|
|
248
|
-
save_data_to_file(
|
|
249
|
-
filepath,
|
|
250
|
-
f"{model_to_update.pipette_version.major}_{model_to_update.pipette_version.minor}",
|
|
251
|
-
physical,
|
|
252
|
-
)
|
|
253
|
-
elif config_to_update[0] == "liquid_properties":
|
|
254
|
-
next(camel_list_to_update)
|
|
255
|
-
liquid = _liquid(
|
|
256
|
-
model_to_update.pipette_channels,
|
|
257
|
-
model_to_update.pipette_type,
|
|
258
|
-
model_to_update.pipette_version,
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
print(
|
|
262
|
-
"Please select what liquid class you wish to update.\n If you want to update all liquid classes then type 'all'.\n"
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
print(f"choose {LiquidClasses.__name__} or type 'all':")
|
|
266
|
-
for row in list_available_enum(LiquidClasses):
|
|
267
|
-
print(f"\t{row}")
|
|
268
|
-
liquid_classes = input("liquid class: ")
|
|
269
|
-
if liquid_classes == "all":
|
|
270
|
-
for c in LiquidClasses:
|
|
271
|
-
liquid = update(
|
|
272
|
-
liquid[c.name.lower()], camel_list_to_update, value_to_update
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
PipetteLiquidPropertiesDefinition.parse_obj(liquid)
|
|
276
|
-
filepath = (
|
|
277
|
-
ROOT
|
|
278
|
-
/ "liquid"
|
|
279
|
-
/ model_to_update.pipette_channels.name.lower()
|
|
280
|
-
/ model_to_update.pipette_type.value
|
|
281
|
-
/ c.name.lower()
|
|
282
|
-
)
|
|
283
|
-
save_data_to_file(
|
|
284
|
-
filepath,
|
|
285
|
-
f"{model_to_update.pipette_version.major}_{model_to_update.pipette_version.minor}",
|
|
286
|
-
liquid,
|
|
287
|
-
)
|
|
288
|
-
else:
|
|
289
|
-
lc = list(LiquidClasses)[int(liquid_classes)]
|
|
290
|
-
liquid = update(
|
|
291
|
-
liquid[lc.name.lower()], camel_list_to_update, value_to_update
|
|
292
|
-
)
|
|
293
|
-
PipetteLiquidPropertiesDefinition.parse_obj(liquid)
|
|
294
|
-
|
|
295
|
-
filepath = (
|
|
296
|
-
ROOT
|
|
297
|
-
/ "liquid"
|
|
298
|
-
/ model_to_update.pipette_channels.name.lower()
|
|
299
|
-
/ model_to_update.pipette_type.value
|
|
300
|
-
/ lc.name.lower()
|
|
301
|
-
)
|
|
302
|
-
save_data_to_file(
|
|
303
|
-
filepath,
|
|
304
|
-
f"{model_to_update.pipette_version.major}_{model_to_update.pipette_version.minor}",
|
|
305
|
-
liquid,
|
|
306
|
-
)
|
|
307
|
-
else:
|
|
308
|
-
raise KeyError(
|
|
309
|
-
f"{config_to_update} is not saved to a file. Check `pipette_definition.py` for more information."
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
def _update_single_model(configuration_to_update: List[str]) -> None:
|
|
314
|
-
"""Helper function to update single model."""
|
|
315
|
-
print(f"choose {PipetteModelType.__name__}:")
|
|
316
|
-
for row in list_available_enum(PipetteModelType):
|
|
317
|
-
print(f"\t{row}")
|
|
318
|
-
model = list(PipetteModelType)[int(input("Please select from above\n"))]
|
|
319
|
-
|
|
320
|
-
print(f"choose {PipetteChannelType.__name__}:")
|
|
321
|
-
for row in list_available_enum(PipetteChannelType):
|
|
322
|
-
print(f"\t{row}")
|
|
323
|
-
channels = list(PipetteChannelType)[int(input("Please select from above\n"))]
|
|
324
|
-
|
|
325
|
-
version = PipetteVersionType.convert_from_float(
|
|
326
|
-
float(check_from_version(input("Please input the version of the model\n")))
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
built_model: PipetteModel = PipetteModel(
|
|
330
|
-
f"{model.name}_{str(channels)}_v{version.major}.{version.minor}"
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
if configuration_to_update[0] == NOZZLE_LOCATION_CONFIGS[1]:
|
|
334
|
-
print(
|
|
335
|
-
"You selected nozzle_map to edit. If you wish to update the nozzle offset, enter it on the next line.\n"
|
|
336
|
-
)
|
|
337
|
-
print("Otherwise, please type 'null' on the next line.\n")
|
|
338
|
-
|
|
339
|
-
value_to_update = json.loads(
|
|
340
|
-
input(
|
|
341
|
-
f"Please select what you would like to update {configuration_to_update[-1]} to for {built_model}\n"
|
|
342
|
-
)
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
model_version = convert_pipette_model(built_model)
|
|
346
|
-
|
|
347
|
-
load_and_update_file_from_config(
|
|
348
|
-
configuration_to_update, value_to_update, model_version
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
def _update_all_models(configuration_to_update: List[str]) -> None:
|
|
353
|
-
paths_to_validate = ROOT / "general"
|
|
354
|
-
_channel_model_str = {
|
|
355
|
-
"single_channel": "single",
|
|
356
|
-
"ninety_six_channel": "96",
|
|
357
|
-
"eight_channel": "multi",
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
for channel_dir in os.listdir(paths_to_validate):
|
|
361
|
-
for model_dir in os.listdir(paths_to_validate / channel_dir):
|
|
362
|
-
for version_file in os.listdir(paths_to_validate / channel_dir / model_dir):
|
|
363
|
-
version_list = version_file.split(".json")[0].split("_")
|
|
364
|
-
built_model: PipetteModel = PipetteModel(
|
|
365
|
-
f"{model_dir}_{_channel_model_str[channel_dir]}_v{version_list[0]}.{version_list[1]}"
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
if configuration_to_update[0] == NOZZLE_LOCATION_CONFIGS[1]:
|
|
369
|
-
print(
|
|
370
|
-
"You selected nozzle_map to edit. If you wish to update the nozzle offset, enter it on the next line.\n"
|
|
371
|
-
)
|
|
372
|
-
print("Otherwise, please type 'null' on the next line.\n")
|
|
373
|
-
|
|
374
|
-
value_to_update = json.loads(
|
|
375
|
-
input(
|
|
376
|
-
f"Please select what you would like to update {configuration_to_update} to for {built_model}\n"
|
|
377
|
-
)
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
model_version = convert_pipette_model(built_model)
|
|
381
|
-
load_and_update_file_from_config(
|
|
382
|
-
configuration_to_update, value_to_update, model_version
|
|
383
|
-
)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
def determine_models_to_update(update_all_models: bool) -> None:
|
|
387
|
-
try:
|
|
388
|
-
while True:
|
|
389
|
-
print(f"choose {PipetteConfigurations.__name__}:")
|
|
390
|
-
config_list, table_lookup = list_configuration_keys()
|
|
391
|
-
for row in config_list:
|
|
392
|
-
print(f"\t{row}")
|
|
393
|
-
|
|
394
|
-
configuration_to_update = [
|
|
395
|
-
table_lookup[int(input("select a configuration from above\n"))]
|
|
396
|
-
]
|
|
397
|
-
|
|
398
|
-
if configuration_to_update[0] == NOZZLE_LOCATION_CONFIGS[0]:
|
|
399
|
-
print(
|
|
400
|
-
f"NOTE: updating the {configuration_to_update[0]} will automatically update the {NOZZLE_LOCATION_CONFIGS[1]}\n"
|
|
401
|
-
)
|
|
402
|
-
|
|
403
|
-
field_type = PipetteConfigurations.__fields__[
|
|
404
|
-
configuration_to_update[0]
|
|
405
|
-
].type_
|
|
406
|
-
is_basemodel = isinstance(field_type, ModelMetaclass)
|
|
407
|
-
|
|
408
|
-
configuration_to_update = handle_subclass_model(
|
|
409
|
-
configuration_to_update, field_type, is_basemodel
|
|
410
|
-
)
|
|
411
|
-
if update_all_models:
|
|
412
|
-
_update_all_models(configuration_to_update)
|
|
413
|
-
else:
|
|
414
|
-
_update_single_model(configuration_to_update)
|
|
415
|
-
except KeyboardInterrupt:
|
|
416
|
-
print("Finished updating! Validate that your files updated successfully.")
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
def main() -> None:
|
|
420
|
-
"""Entry point."""
|
|
421
|
-
parser = argparse.ArgumentParser(
|
|
422
|
-
description="96 channel tip handling testing script."
|
|
423
|
-
)
|
|
424
|
-
parser.add_argument(
|
|
425
|
-
"--update_all_models",
|
|
426
|
-
type=bool,
|
|
427
|
-
help="update all",
|
|
428
|
-
default=False,
|
|
429
|
-
)
|
|
430
|
-
|
|
431
|
-
args = parser.parse_args()
|
|
432
|
-
|
|
433
|
-
determine_models_to_update(args.update_all_models)
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if __name__ == "__main__":
|
|
437
|
-
"""
|
|
438
|
-
A script to automate building a pipette configuration definition.
|
|
439
|
-
|
|
440
|
-
This script can either perform migrations from a v1 -> v2 schema format
|
|
441
|
-
or build a brand new script from scratch.
|
|
442
|
-
|
|
443
|
-
When building a new pipette configuration model, you will either need
|
|
444
|
-
to provide CSVs or use command line inputs.
|
|
445
|
-
|
|
446
|
-
If you choose CSVs you will need one CSV for the general pipette configuration
|
|
447
|
-
data (such as pipette model or number of channels) and one for every tip
|
|
448
|
-
type that this pipette model can support.
|
|
449
|
-
"""
|
|
450
|
-
main()
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import enum
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from typing import Union, Dict, Mapping, Tuple, cast
|
|
4
|
-
from typing_extensions import Literal
|
|
5
|
-
|
|
6
|
-
"""Pipette Definition V2 Types"""
|
|
7
|
-
|
|
8
|
-
# Needed for Int Comparison. Keeping it next to
|
|
9
|
-
# the Literal type for ease of readability
|
|
10
|
-
PipetteModelMajorVersion = [1, 2, 3]
|
|
11
|
-
PipetteModelMinorVersion = [0, 1, 2, 3, 4, 5, 6]
|
|
12
|
-
|
|
13
|
-
# TODO Literals are only good for writing down
|
|
14
|
-
# exact values. Is there a better typing mechanism
|
|
15
|
-
# so we don't need to keep track of versions in two
|
|
16
|
-
# different places?
|
|
17
|
-
PipetteModelMajorVersionType = Literal[1, 2, 3]
|
|
18
|
-
PipetteModelMinorVersionType = Literal[0, 1, 2, 3, 4, 5, 6]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class LiquidClasses(enum.Enum):
|
|
22
|
-
default = enum.auto()
|
|
23
|
-
lowVolumeDefault = enum.auto()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class PipetteTipType(enum.Enum):
|
|
27
|
-
t10 = 10
|
|
28
|
-
t20 = 20
|
|
29
|
-
t50 = 50
|
|
30
|
-
t200 = 200
|
|
31
|
-
t300 = 300
|
|
32
|
-
t1000 = 1000
|
|
33
|
-
|
|
34
|
-
@classmethod
|
|
35
|
-
def check_and_return_type(
|
|
36
|
-
cls, working_volume: int, maximum_volume: int
|
|
37
|
-
) -> "PipetteTipType":
|
|
38
|
-
try:
|
|
39
|
-
return cls(working_volume)
|
|
40
|
-
except ValueError:
|
|
41
|
-
return cls(maximum_volume)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class PipetteChannelType(int, enum.Enum):
|
|
45
|
-
SINGLE_CHANNEL = 1
|
|
46
|
-
EIGHT_CHANNEL = 8
|
|
47
|
-
NINETY_SIX_CHANNEL = 96
|
|
48
|
-
|
|
49
|
-
def __str__(self) -> str:
|
|
50
|
-
if self.value == 96:
|
|
51
|
-
return "96"
|
|
52
|
-
elif self.value == 8:
|
|
53
|
-
return "multi"
|
|
54
|
-
else:
|
|
55
|
-
return "single"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class PipetteModelType(enum.Enum):
|
|
59
|
-
p10 = "p10"
|
|
60
|
-
p20 = "p20"
|
|
61
|
-
p50 = "p50"
|
|
62
|
-
p300 = "p300"
|
|
63
|
-
p1000 = "p1000"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class PipetteGenerationType(enum.Enum):
|
|
67
|
-
GEN1 = "GEN1"
|
|
68
|
-
GEN2 = "GEN2"
|
|
69
|
-
FLEX = "FLEX"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@dataclass(frozen=True)
|
|
73
|
-
class PipetteVersionType:
|
|
74
|
-
major: PipetteModelMajorVersionType
|
|
75
|
-
minor: PipetteModelMinorVersionType
|
|
76
|
-
|
|
77
|
-
@classmethod
|
|
78
|
-
def convert_from_float(cls, version: float) -> "PipetteVersionType":
|
|
79
|
-
major = cast(PipetteModelMajorVersionType, int(version // 1))
|
|
80
|
-
minor = cast(PipetteModelMinorVersionType, int(round((version % 1), 2) * 10))
|
|
81
|
-
return cls(major=major, minor=minor)
|
|
82
|
-
|
|
83
|
-
def __str__(self) -> str:
|
|
84
|
-
if self.major == 1 and self.minor == 0:
|
|
85
|
-
# Maintain the format of V1 pipettes that
|
|
86
|
-
# do not contain a minor version at all.
|
|
87
|
-
return f"{self.major}"
|
|
88
|
-
else:
|
|
89
|
-
return f"{self.major}.{self.minor}"
|
|
90
|
-
|
|
91
|
-
@property
|
|
92
|
-
def as_tuple(
|
|
93
|
-
self,
|
|
94
|
-
) -> Tuple[PipetteModelMajorVersionType, PipetteModelMinorVersionType]:
|
|
95
|
-
return (self.major, self.minor)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"""Mutable Configs Types"""
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class Quirks(enum.Enum):
|
|
102
|
-
pickupTipShake = "pickupTipShake"
|
|
103
|
-
dropTipShake = "dropTipShake"
|
|
104
|
-
doubleDropTip = "doubleDropTip"
|
|
105
|
-
needsUnstick = "needsUnstick"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class AvailableUnits(enum.Enum):
|
|
109
|
-
mm = "mm"
|
|
110
|
-
amps = "amps"
|
|
111
|
-
speed = "mm/s"
|
|
112
|
-
presses = "presses"
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
@dataclass
|
|
116
|
-
class RobotMountConfigs:
|
|
117
|
-
stepsPerMM: float
|
|
118
|
-
homePosition: float
|
|
119
|
-
travelDistance: float
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
@dataclass
|
|
123
|
-
class MutableConfig:
|
|
124
|
-
value: Union[int, float]
|
|
125
|
-
default: Union[int, float]
|
|
126
|
-
units: AvailableUnits
|
|
127
|
-
type: str
|
|
128
|
-
min: float
|
|
129
|
-
max: float
|
|
130
|
-
name: str
|
|
131
|
-
|
|
132
|
-
@classmethod
|
|
133
|
-
def build(
|
|
134
|
-
cls,
|
|
135
|
-
value: Union[int, float],
|
|
136
|
-
default: Union[int, float],
|
|
137
|
-
units: str,
|
|
138
|
-
type: str,
|
|
139
|
-
min: float,
|
|
140
|
-
max: float,
|
|
141
|
-
name: str,
|
|
142
|
-
) -> "MutableConfig":
|
|
143
|
-
if units == "mm/sec":
|
|
144
|
-
units = "mm/s"
|
|
145
|
-
return cls(
|
|
146
|
-
value=value,
|
|
147
|
-
default=default,
|
|
148
|
-
units=AvailableUnits(units),
|
|
149
|
-
type=type,
|
|
150
|
-
min=min,
|
|
151
|
-
max=max,
|
|
152
|
-
name=name,
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
def validate_and_add(self, new_value: Union[int, float]) -> None:
|
|
156
|
-
if new_value < self.min or new_value > self.max:
|
|
157
|
-
raise ValueError(f"{self.name} out of range with {new_value}")
|
|
158
|
-
self.value = new_value
|
|
159
|
-
|
|
160
|
-
def dict_for_encode(self) -> Dict[str, Union[int, float, str]]:
|
|
161
|
-
return {
|
|
162
|
-
"value": self.value,
|
|
163
|
-
"default": self.default,
|
|
164
|
-
"units": self.units.value,
|
|
165
|
-
"type": self.type,
|
|
166
|
-
"min": self.min,
|
|
167
|
-
"max": self.max,
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
@dataclass
|
|
172
|
-
class QuirkConfig:
|
|
173
|
-
value: bool
|
|
174
|
-
name: Quirks
|
|
175
|
-
|
|
176
|
-
@classmethod
|
|
177
|
-
def validate_and_build(cls, name: str, value: bool) -> "QuirkConfig":
|
|
178
|
-
quirk_name = Quirks(name)
|
|
179
|
-
cls.validate(name, value)
|
|
180
|
-
return cls(value=value, name=quirk_name)
|
|
181
|
-
|
|
182
|
-
@classmethod
|
|
183
|
-
def validate(cls, name: str, value: bool) -> None:
|
|
184
|
-
if not isinstance(value, bool):
|
|
185
|
-
raise ValueError(f"{value} is invalid for {name}")
|
|
186
|
-
|
|
187
|
-
def dict_for_encode(self) -> bool:
|
|
188
|
-
return self.value
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
TypeOverrides = Mapping[str, Union[float, bool, None]]
|
|
192
|
-
|
|
193
|
-
OverrideType = Dict[str, Union[Dict[str, QuirkConfig], MutableConfig, str]]
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
opentrons_shared_data.protocol: types and bindings for json protocols
|
|
3
|
-
"""
|
|
4
|
-
import json
|
|
5
|
-
from typing import Any, NewType, Dict
|
|
6
|
-
|
|
7
|
-
from .. import load_shared_data
|
|
8
|
-
|
|
9
|
-
Schema = NewType("Schema", Dict[str, Any])
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def load_schema(version: int) -> "Schema":
|
|
13
|
-
return json.loads(load_shared_data(f"protocol/schemas/{version}.json"))
|