Skip to content

Commit 404b989

Browse files
committed
Updated 'machine_config_from_file' function to parse and connstruct instrument machine configs hierarchically; it can load all the machine configs or just the specified one
1 parent 14f42bb commit 404b989

File tree

1 file changed

+68
-21
lines changed

1 file changed

+68
-21
lines changed

src/murfey/util/config.py

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -147,16 +147,72 @@ def validate_software_versions(cls, v: dict[str, Any]) -> dict[str, str]:
147147
return v
148148

149149

150+
@lru_cache(maxsize=1)
150151
def machine_config_from_file(
151-
config_file_path: Path, instrument: str = ""
152+
config_file_path: Path,
153+
instrument_name: str,
152154
) -> dict[str, MachineConfig]:
155+
"""
156+
Loads the machine config YAML file and constructs instrument-specific configs from
157+
a hierarchical set of dictionary key-value pairs. It will populate the keys listed
158+
in the general dictionary, then update the keys specified in the shared instrument
159+
dictionary, before finally updating the keys for that specific instrument.
160+
"""
161+
162+
def _update_nested_values(base: dict[str, Any], new: dict[str, Any]):
163+
"""
164+
Helper function to recursively update nested dictioanry values.
165+
If the old and new values are both dicts, it will add the new keys and values
166+
to the existing dictionary recursively without overwriting entries.
167+
If the old and new values are both lists, it will extend the existing list.
168+
For all other values, it will overwrite the existing value with the new one.
169+
"""
170+
for key, value in new.items():
171+
# If new values are dicts and dict values already exist, do recursive update
172+
if key in base and isinstance(base[key], dict) and isinstance(value, dict):
173+
base[key] = _update_nested_values(base[key], value)
174+
# If new values are lists and a list already exists, extend the list
175+
if key in base and isinstance(base[key], list) and isinstance(value, list):
176+
base[key].extend(value)
177+
# Otherwise, overwrite values as normal
178+
else:
179+
base[key] = value
180+
return base
181+
182+
# Load the dict from the file
153183
with open(config_file_path, "r") as config_stream:
154-
config = yaml.safe_load(config_stream)
155-
return {
156-
i: MachineConfig(**config[i])
157-
for i in config.keys()
158-
if not instrument or i == instrument
159-
}
184+
master_config: dict[str, Any] = yaml.safe_load(config_stream)
185+
186+
# Construct requested machine configs from the YAML file
187+
all_machine_configs: dict[str, MachineConfig] = {}
188+
for i in sorted(master_config.keys()):
189+
# Skip reserved top-level keys
190+
if i in ("general", "clem", "fib", "tem"):
191+
continue
192+
# If instrument name is set, skip irrelevant configs
193+
if instrument_name and i != instrument_name:
194+
continue
195+
print(f"Parsing key {i}")
196+
# Construct instrument config hierarchically
197+
config: dict[str, Any] = {}
198+
199+
# Populate with general values
200+
general_config: dict[str, Any] = master_config.get("general", {})
201+
config = _update_nested_values(config, general_config)
202+
203+
# Populate with shared instrument values
204+
instrument_config: dict[str, Any] = master_config.get(i, {})
205+
instrument_shared_config: dict[str, Any] = master_config.get(
206+
str(instrument_config.get("instrument_type", "")).lower(), {}
207+
)
208+
config = _update_nested_values(config, instrument_shared_config)
209+
210+
# Insert instrument-specific values
211+
config = _update_nested_values(config, instrument_config)
212+
213+
all_machine_configs[i] = MachineConfig(**config)
214+
215+
return all_machine_configs
160216

161217

162218
class Security(BaseModel):
@@ -250,22 +306,13 @@ def get_security_config() -> Security:
250306

251307
@lru_cache(maxsize=1)
252308
def get_machine_config(instrument_name: str = "") -> dict[str, MachineConfig]:
253-
machine_config = {
254-
"": MachineConfig(
255-
acquisition_software=[],
256-
calibrations={},
257-
data_directories=[],
258-
rsync_basepath=Path("dls/tmp"),
259-
murfey_db_credentials="",
260-
default_model="/tmp/weights.h5",
261-
)
262-
}
309+
# Create an empty machine config as a placeholder
310+
machine_configs = {instrument_name: MachineConfig()}
263311
if settings.murfey_machine_configuration:
264-
microscope = instrument_name
265-
machine_config = machine_config_from_file(
266-
Path(settings.murfey_machine_configuration), microscope
312+
machine_configs = machine_config_from_file(
313+
Path(settings.murfey_machine_configuration), instrument_name
267314
)
268-
return machine_config
315+
return machine_configs
269316

270317

271318
def get_extended_machine_config(

0 commit comments

Comments
 (0)