@opentrons/shared-data 0.0.1-alpha.20 → 0.1.0-alpha.0
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/Makefile +1 -1
- package/lib/opentrons-shared-data.js +1 -1
- package/lib/opentrons-shared-data.js.map +1 -1
- package/package.json +1 -1
- 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/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
|
@@ -1,431 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import json
|
|
3
|
-
import re
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Optional, List, Dict, Any, cast
|
|
6
|
-
|
|
7
|
-
from .pipette_definition import PipetteConfigurations, PipetteModelVersionType
|
|
8
|
-
from .model_constants import (
|
|
9
|
-
MUTABLE_CONFIGS_V1,
|
|
10
|
-
VALID_QUIRKS,
|
|
11
|
-
_MAP_KEY_TO_V2,
|
|
12
|
-
_MIN_MAX_LOOKUP,
|
|
13
|
-
_TYPE_LOOKUP,
|
|
14
|
-
_UNITS_LOOKUP,
|
|
15
|
-
)
|
|
16
|
-
from .load_data import load_definition, load_serial_lookup_table
|
|
17
|
-
from .types import (
|
|
18
|
-
MutableConfig,
|
|
19
|
-
Quirks,
|
|
20
|
-
QuirkConfig,
|
|
21
|
-
TypeOverrides,
|
|
22
|
-
OverrideType,
|
|
23
|
-
LiquidClasses,
|
|
24
|
-
)
|
|
25
|
-
from .pipette_load_name_conversions import (
|
|
26
|
-
convert_pipette_model,
|
|
27
|
-
convert_to_pipette_name_type,
|
|
28
|
-
)
|
|
29
|
-
from .file_operation_helpers import (
|
|
30
|
-
MutableConfigurationEncoder,
|
|
31
|
-
MutableConfigurationDecoder,
|
|
32
|
-
)
|
|
33
|
-
from .dev_types import PipetteModel, PipetteName
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
log = logging.getLogger(__name__)
|
|
37
|
-
|
|
38
|
-
PIPETTE_SERIAL_MODEL_LOOKUP = load_serial_lookup_table()
|
|
39
|
-
SERIAL_STUB_REGEX = re.compile(r"P[0-9]{1,3}[KSMHV]{1,2}V[0-9]{2}")
|
|
40
|
-
LIQUID_CLASS = LiquidClasses.default
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def _edit_non_quirk(
|
|
44
|
-
mutable_config_key: str, new_mutable_value: MutableConfig, base_dict: Dict[str, Any]
|
|
45
|
-
) -> None:
|
|
46
|
-
def _do_edit_non_quirk(
|
|
47
|
-
new_value: MutableConfig, existing: Dict[Any, Any], keypath: List[Any]
|
|
48
|
-
) -> None:
|
|
49
|
-
thiskey: Any = keypath[0]
|
|
50
|
-
if thiskey in [lc.name for lc in LiquidClasses]:
|
|
51
|
-
thiskey = LiquidClasses[thiskey]
|
|
52
|
-
if len(keypath) > 1:
|
|
53
|
-
restkeys = keypath[1:]
|
|
54
|
-
if thiskey == "##EACHTIP##":
|
|
55
|
-
for key in existing.keys():
|
|
56
|
-
_do_edit_non_quirk(new_value, existing[key], restkeys)
|
|
57
|
-
else:
|
|
58
|
-
_do_edit_non_quirk(new_value, existing[thiskey], restkeys)
|
|
59
|
-
else:
|
|
60
|
-
# This was the last key
|
|
61
|
-
if thiskey == "##EACHTIP##":
|
|
62
|
-
for key in existing.keys():
|
|
63
|
-
existing[key] = new_value.value
|
|
64
|
-
else:
|
|
65
|
-
existing[thiskey] = new_value.value
|
|
66
|
-
|
|
67
|
-
new_names = _MAP_KEY_TO_V2[mutable_config_key]
|
|
68
|
-
_do_edit_non_quirk(new_mutable_value, base_dict, new_names)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def _migrate_to_v2_configurations(
|
|
72
|
-
base_configurations: PipetteConfigurations,
|
|
73
|
-
v1_mutable_configs: OverrideType,
|
|
74
|
-
) -> PipetteConfigurations:
|
|
75
|
-
"""Helper function to migration V1 Configs to the V2 format.
|
|
76
|
-
|
|
77
|
-
Given an input of v1 mutable configs, look up the equivalent keyed
|
|
78
|
-
value of that configuration."""
|
|
79
|
-
quirks_list = []
|
|
80
|
-
dict_of_base_model = base_configurations.dict(by_alias=True)
|
|
81
|
-
for c, v in v1_mutable_configs.items():
|
|
82
|
-
if isinstance(v, str):
|
|
83
|
-
# ignore the saved model
|
|
84
|
-
continue
|
|
85
|
-
if isinstance(v, bool):
|
|
86
|
-
# ignore any accidental top level quirks.
|
|
87
|
-
continue
|
|
88
|
-
if c == "quirks" and isinstance(v, dict):
|
|
89
|
-
quirks_list.extend([b.name for b in v.values() if b.value])
|
|
90
|
-
elif isinstance(v, MutableConfig):
|
|
91
|
-
_edit_non_quirk(c, v, dict_of_base_model)
|
|
92
|
-
|
|
93
|
-
dict_of_base_model["quirks"] = list(
|
|
94
|
-
set(dict_of_base_model["quirks"]).union(set(quirks_list))
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
# re-serialization is not great for this nested enum so we need
|
|
98
|
-
# to perform this workaround.
|
|
99
|
-
dict_of_base_model["liquid_properties"][LIQUID_CLASS]["supportedTips"] = {
|
|
100
|
-
k.name: v
|
|
101
|
-
for k, v in dict_of_base_model["liquid_properties"][LIQUID_CLASS][
|
|
102
|
-
"supportedTips"
|
|
103
|
-
].items()
|
|
104
|
-
}
|
|
105
|
-
dict_of_base_model["liquid_properties"] = {
|
|
106
|
-
k.name: v for k, v in dict_of_base_model["liquid_properties"].items()
|
|
107
|
-
}
|
|
108
|
-
dict_of_base_model["plungerPositionsConfigurations"] = {
|
|
109
|
-
k.name: v
|
|
110
|
-
for k, v in dict_of_base_model["plungerPositionsConfigurations"].items()
|
|
111
|
-
}
|
|
112
|
-
return PipetteConfigurations.parse_obj(dict_of_base_model)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def _load_available_overrides(
|
|
116
|
-
pipette_serial_number: str, pipette_override_path: Path
|
|
117
|
-
) -> OverrideType:
|
|
118
|
-
"""Load the available overrides from disk."""
|
|
119
|
-
pipette_override = pipette_override_path / f"{pipette_serial_number}.json"
|
|
120
|
-
try:
|
|
121
|
-
with open(pipette_override, "r") as f:
|
|
122
|
-
return json.load(f, cls=MutableConfigurationDecoder)
|
|
123
|
-
except json.JSONDecodeError as e:
|
|
124
|
-
log.warning(f"pipette override for {pipette_serial_number} is corrupt: {e}")
|
|
125
|
-
pipette_override.unlink()
|
|
126
|
-
raise FileNotFoundError()
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def _list_all_mutable_configs(
|
|
130
|
-
on_disk_overrides: OverrideType, base_configurations: Dict[str, Any]
|
|
131
|
-
) -> OverrideType:
|
|
132
|
-
"""Get all available mutable configurations"""
|
|
133
|
-
all_mutable_configs = set([*MUTABLE_CONFIGS_V1, *VALID_QUIRKS]) - set(["quirks"])
|
|
134
|
-
on_disk_configs = set(
|
|
135
|
-
[
|
|
136
|
-
v.name
|
|
137
|
-
for v in on_disk_overrides.values()
|
|
138
|
-
if isinstance(v, MutableConfig) or isinstance(v, QuirkConfig)
|
|
139
|
-
]
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
missing_configurations = all_mutable_configs.difference(on_disk_configs)
|
|
143
|
-
|
|
144
|
-
default_configurations: OverrideType = {"quirks": {}}
|
|
145
|
-
for c in missing_configurations:
|
|
146
|
-
if c in VALID_QUIRKS:
|
|
147
|
-
if Quirks(c) in base_configurations["quirks"]:
|
|
148
|
-
default_configurations["quirks"][c] = QuirkConfig.validate_and_build( # type: ignore
|
|
149
|
-
c, True
|
|
150
|
-
)
|
|
151
|
-
else:
|
|
152
|
-
default_configurations[c] = _find_default(c, base_configurations)
|
|
153
|
-
default_configurations.update(on_disk_overrides)
|
|
154
|
-
return default_configurations
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def _get_default_value_for(config: Dict[str, Any], keypath: List[str]) -> Any:
|
|
158
|
-
def _do_get_default_value_for(
|
|
159
|
-
remaining_config: Dict[Any, Any], keypath: List[str]
|
|
160
|
-
) -> Any:
|
|
161
|
-
first: Any = keypath[0]
|
|
162
|
-
if first in [lc.name for lc in LiquidClasses]:
|
|
163
|
-
first = LiquidClasses[first]
|
|
164
|
-
if len(keypath) > 1:
|
|
165
|
-
rest = keypath[1:]
|
|
166
|
-
if first == "##EACHTIP##":
|
|
167
|
-
tip_list = list(remaining_config.keys())
|
|
168
|
-
tip_list.sort(key=lambda o: o.value)
|
|
169
|
-
return _do_get_default_value_for(remaining_config[tip_list[-1]], rest)
|
|
170
|
-
else:
|
|
171
|
-
return _do_get_default_value_for(remaining_config[first], rest)
|
|
172
|
-
else:
|
|
173
|
-
if first == "###EACHTIP##":
|
|
174
|
-
tip_list = list(remaining_config.keys())
|
|
175
|
-
tip_list.sort(key=lambda o: o.value)
|
|
176
|
-
return remaining_config[tip_list[-1]]
|
|
177
|
-
elif first == "currentByTipCount":
|
|
178
|
-
# return the value for the most tips at a time
|
|
179
|
-
cbt = remaining_config[first]
|
|
180
|
-
return cbt[next(reversed(sorted(cbt.keys())))]
|
|
181
|
-
else:
|
|
182
|
-
return remaining_config[first]
|
|
183
|
-
|
|
184
|
-
return _do_get_default_value_for(config, keypath)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def _find_default(name: str, configs: Dict[str, Any]) -> MutableConfig:
|
|
188
|
-
"""Find the default value from the configs and return it as a mutable config."""
|
|
189
|
-
keypath = _MAP_KEY_TO_V2[name]
|
|
190
|
-
nested_name = keypath[-1]
|
|
191
|
-
|
|
192
|
-
if name == "pickUpCurrent":
|
|
193
|
-
min_max_dict = _MIN_MAX_LOOKUP["current"]
|
|
194
|
-
type_lookup = _TYPE_LOOKUP["current"]
|
|
195
|
-
units_lookup = _UNITS_LOOKUP["current"]
|
|
196
|
-
else:
|
|
197
|
-
min_max_dict = _MIN_MAX_LOOKUP[nested_name]
|
|
198
|
-
type_lookup = _TYPE_LOOKUP[nested_name]
|
|
199
|
-
units_lookup = _UNITS_LOOKUP[nested_name]
|
|
200
|
-
default_value = _get_default_value_for(configs, keypath)
|
|
201
|
-
return MutableConfig(
|
|
202
|
-
value=default_value,
|
|
203
|
-
default=default_value,
|
|
204
|
-
min=min_max_dict["min"],
|
|
205
|
-
max=min_max_dict["max"],
|
|
206
|
-
type=type_lookup,
|
|
207
|
-
units=units_lookup,
|
|
208
|
-
name=name,
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def known_pipettes(pipette_override_path: Path) -> List[str]:
|
|
213
|
-
"""List pipette IDs for which we have known overrides"""
|
|
214
|
-
return [
|
|
215
|
-
fi.stem
|
|
216
|
-
for fi in pipette_override_path.iterdir()
|
|
217
|
-
if fi.is_file() and ".json" in fi.suffixes
|
|
218
|
-
]
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
def _load_full_mutable_configs(
|
|
222
|
-
pipette_model: PipetteModelVersionType, overrides: OverrideType
|
|
223
|
-
) -> OverrideType:
|
|
224
|
-
base_configs = load_definition(
|
|
225
|
-
pipette_model.pipette_type,
|
|
226
|
-
pipette_model.pipette_channels,
|
|
227
|
-
pipette_model.pipette_version,
|
|
228
|
-
)
|
|
229
|
-
base_configs_dict = base_configs.dict(by_alias=True)
|
|
230
|
-
full_mutable_configs = _list_all_mutable_configs(overrides, base_configs_dict)
|
|
231
|
-
|
|
232
|
-
if not full_mutable_configs.get("name"):
|
|
233
|
-
full_mutable_configs["name"] = str(
|
|
234
|
-
convert_to_pipette_name_type(cast(PipetteName, str(pipette_model)))
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
if not full_mutable_configs.get("model"):
|
|
238
|
-
full_mutable_configs["model"] = str(pipette_model)
|
|
239
|
-
|
|
240
|
-
return full_mutable_configs
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
def list_mutable_configs(
|
|
244
|
-
pipette_serial_number: str, pipette_override_path: Path
|
|
245
|
-
) -> OverrideType:
|
|
246
|
-
"""
|
|
247
|
-
Returns dict of mutable configs only.
|
|
248
|
-
"""
|
|
249
|
-
|
|
250
|
-
mutable_configs: OverrideType = {}
|
|
251
|
-
|
|
252
|
-
try:
|
|
253
|
-
mutable_configs = _load_available_overrides(
|
|
254
|
-
pipette_serial_number, pipette_override_path
|
|
255
|
-
)
|
|
256
|
-
except FileNotFoundError:
|
|
257
|
-
log.info(f"Pipette id {pipette_serial_number} not found")
|
|
258
|
-
# This is mimicing behavior from the original file
|
|
259
|
-
# which returned an empty dict if no on disk value was found.
|
|
260
|
-
return mutable_configs
|
|
261
|
-
|
|
262
|
-
serial_key_match = SERIAL_STUB_REGEX.match(pipette_serial_number)
|
|
263
|
-
|
|
264
|
-
if serial_key_match:
|
|
265
|
-
serial_key = serial_key_match.group(0)
|
|
266
|
-
else:
|
|
267
|
-
serial_key = ""
|
|
268
|
-
pipette_model = convert_pipette_model(
|
|
269
|
-
cast(PipetteModel, PIPETTE_SERIAL_MODEL_LOOKUP[serial_key])
|
|
270
|
-
)
|
|
271
|
-
return _load_full_mutable_configs(pipette_model, mutable_configs)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def list_mutable_configs_with_defaults(
|
|
275
|
-
pipette_model: PipetteModelVersionType,
|
|
276
|
-
pipette_serial_number: Optional[str],
|
|
277
|
-
pipette_override_path: Path,
|
|
278
|
-
) -> OverrideType:
|
|
279
|
-
"""
|
|
280
|
-
Returns dict of mutable configs only, with their defaults.
|
|
281
|
-
"""
|
|
282
|
-
mutable_configs: OverrideType = {}
|
|
283
|
-
if pipette_serial_number:
|
|
284
|
-
try:
|
|
285
|
-
mutable_configs = _load_available_overrides(
|
|
286
|
-
pipette_serial_number, pipette_override_path
|
|
287
|
-
)
|
|
288
|
-
except FileNotFoundError:
|
|
289
|
-
pass
|
|
290
|
-
return _load_full_mutable_configs(pipette_model, mutable_configs)
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
def load_with_mutable_configurations(
|
|
294
|
-
pipette_model: PipetteModelVersionType,
|
|
295
|
-
pipette_override_path: Path,
|
|
296
|
-
pipette_serial_number: Optional[str] = None,
|
|
297
|
-
) -> PipetteConfigurations:
|
|
298
|
-
"""
|
|
299
|
-
Load pipette config data with any overrides available.
|
|
300
|
-
|
|
301
|
-
This function reads from disk each time, so changes to the overrides
|
|
302
|
-
will be picked up in subsequent calls.
|
|
303
|
-
|
|
304
|
-
:param str pipette_model: The pipette model name (i.e. "p10_single_v1.3")
|
|
305
|
-
for which to load configuration
|
|
306
|
-
:param pipette_serial_number: An (optional) unique ID for the pipette to locate
|
|
307
|
-
config overrides. If the ID is not specified, the system
|
|
308
|
-
assumes this is a simulated pipette and does not
|
|
309
|
-
save settings. If the ID is specified but no overrides
|
|
310
|
-
corresponding to the ID are found, the system creates a
|
|
311
|
-
new overrides file for it.
|
|
312
|
-
:type pipette_serial_number: str or None
|
|
313
|
-
:raises KeyError: if ``pipette_model`` is not in the top-level keys of
|
|
314
|
-
the pipetteModelSpecs.json file (and therefore not in
|
|
315
|
-
:py:attr:`configs`)
|
|
316
|
-
|
|
317
|
-
:returns PipetteConfig: The configuration, loaded and checked
|
|
318
|
-
"""
|
|
319
|
-
base_configurations = load_definition(
|
|
320
|
-
pipette_model.pipette_type,
|
|
321
|
-
pipette_model.pipette_channels,
|
|
322
|
-
pipette_model.pipette_version,
|
|
323
|
-
)
|
|
324
|
-
# Load overrides if we have a pipette id
|
|
325
|
-
if pipette_serial_number:
|
|
326
|
-
try:
|
|
327
|
-
override = _load_available_overrides(
|
|
328
|
-
pipette_serial_number, pipette_override_path
|
|
329
|
-
)
|
|
330
|
-
except FileNotFoundError:
|
|
331
|
-
pass
|
|
332
|
-
else:
|
|
333
|
-
base_configurations = _migrate_to_v2_configurations(
|
|
334
|
-
base_configurations, override
|
|
335
|
-
)
|
|
336
|
-
# the ulPerMm functions are structured in pipetteModelSpecs.json as
|
|
337
|
-
# a list sorted from oldest to newest. That means the latest functions
|
|
338
|
-
# are always the last element and, as of right now, the older ones are
|
|
339
|
-
# the first element (for models that only have one function, the first
|
|
340
|
-
# and last elements are the same, which is fine). If we add more in the
|
|
341
|
-
# future, we’ll have to change this code to select items more
|
|
342
|
-
# intelligently
|
|
343
|
-
# TODO (lc 06/27) Handle versioned ul per mm
|
|
344
|
-
|
|
345
|
-
return base_configurations
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
def _add_new_overrides_to_existing( # noqa: C901
|
|
349
|
-
base_configs_dict: Dict[str, Any],
|
|
350
|
-
existing_overrides: OverrideType,
|
|
351
|
-
overrides: TypeOverrides,
|
|
352
|
-
) -> OverrideType:
|
|
353
|
-
"""Helper function to add new overrides to the existing ones"""
|
|
354
|
-
# FIXME remove the validation here for the file save and rely
|
|
355
|
-
# on the validation in the robot server. We unfortunately have
|
|
356
|
-
# to keep the validation here until we decide to fully wipe/migrate
|
|
357
|
-
# files saved on disk because some of them have unexpected
|
|
358
|
-
# data entries.
|
|
359
|
-
if not existing_overrides.get("quirks"):
|
|
360
|
-
existing_overrides["quirks"] = {}
|
|
361
|
-
for key, value in overrides.items():
|
|
362
|
-
# If an existing override is saved as null from endpoint, remove from
|
|
363
|
-
# overrides file
|
|
364
|
-
if value is None and existing_overrides.get(key):
|
|
365
|
-
del existing_overrides[key]
|
|
366
|
-
elif isinstance(value, bool):
|
|
367
|
-
if key in VALID_QUIRKS:
|
|
368
|
-
existing_overrides["quirks"][key] = QuirkConfig.validate_and_build( # type: ignore
|
|
369
|
-
key, value
|
|
370
|
-
)
|
|
371
|
-
elif key not in MUTABLE_CONFIGS_V1:
|
|
372
|
-
# Unfortunately, some of the files got corrupted,
|
|
373
|
-
# so we have to check that the key doesn't exist
|
|
374
|
-
# in mutable configs before throwing an error.
|
|
375
|
-
raise ValueError(
|
|
376
|
-
f"{value} is invalid for {key} or {key} is not a supported quirk."
|
|
377
|
-
)
|
|
378
|
-
elif existing_overrides.get(key):
|
|
379
|
-
del existing_overrides[key]
|
|
380
|
-
elif value:
|
|
381
|
-
if existing_overrides.get(key):
|
|
382
|
-
existing_overrides[key].validate_and_add(value) # type: ignore
|
|
383
|
-
else:
|
|
384
|
-
new_mutable_config = _find_default(key, base_configs_dict)
|
|
385
|
-
new_mutable_config.validate_and_add(value)
|
|
386
|
-
existing_overrides[key] = new_mutable_config
|
|
387
|
-
return existing_overrides
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
def save_overrides(
|
|
391
|
-
pipette_serial_number: str, overrides: TypeOverrides, pipette_override_path: Path
|
|
392
|
-
) -> None:
|
|
393
|
-
"""
|
|
394
|
-
Save overrides for the pipette.
|
|
395
|
-
|
|
396
|
-
:param pipette_id: The pipette id
|
|
397
|
-
:param overrides: The incoming values
|
|
398
|
-
:return: None
|
|
399
|
-
"""
|
|
400
|
-
# need to load defaults
|
|
401
|
-
serial_key_match = SERIAL_STUB_REGEX.match(pipette_serial_number)
|
|
402
|
-
if serial_key_match:
|
|
403
|
-
serial_key = serial_key_match.group(0)
|
|
404
|
-
else:
|
|
405
|
-
serial_key = ""
|
|
406
|
-
pipette_model = convert_pipette_model(
|
|
407
|
-
cast(PipetteModel, PIPETTE_SERIAL_MODEL_LOOKUP[serial_key])
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
base_configs = load_definition(
|
|
411
|
-
pipette_model.pipette_type,
|
|
412
|
-
pipette_model.pipette_channels,
|
|
413
|
-
pipette_model.pipette_version,
|
|
414
|
-
)
|
|
415
|
-
base_configs_dict = base_configs.dict(by_alias=True)
|
|
416
|
-
try:
|
|
417
|
-
existing_overrides = _load_available_overrides(
|
|
418
|
-
pipette_serial_number, pipette_override_path
|
|
419
|
-
)
|
|
420
|
-
except FileNotFoundError:
|
|
421
|
-
existing_overrides = {"quirks": {}}
|
|
422
|
-
|
|
423
|
-
updated_overrides = _add_new_overrides_to_existing(
|
|
424
|
-
base_configs_dict, existing_overrides, overrides
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
if not updated_overrides.get("model"):
|
|
428
|
-
updated_overrides["model"] = str(pipette_model)
|
|
429
|
-
|
|
430
|
-
with open(pipette_override_path / f"{pipette_serial_number}.json", "w") as file:
|
|
431
|
-
json.dump(updated_overrides, file, cls=MutableConfigurationEncoder)
|