Files

122 lines
5.7 KiB
Python

"""Manual dataset export and paused deletion of retained acquisitions."""
from __future__ import annotations
from collections.abc import Callable
from pathlib import Path
from python_app.models.gui_profile_schema import ProfileWriter
from python_app.gui.controllers.pipeline_controller import PipelineController
from python_app.gui.controllers.sections.views import PrimaryView, DataView
from python_app.models.run_config_schema import RunConfigModel
from python_app.storage.npz.store import NpzStore
class SnapshotController:
"""Save stage histories and hold deletion ownership through final redraw."""
def __init__(
self, pipeline: PipelineController, store: NpzStore, write_profile: ProfileWriter,
*, destination: Callable[[RunConfigModel], tuple[Path, str, str]],
build_config: Callable[[], RunConfigModel], processing_settings: dict[int, dict],
allowed: Callable[[bool], bool], log: Callable[[str], None],
error: Callable[[str, Exception], None],
) -> None:
self.pipeline, self.store = pipeline, store
self._write_profile, self._log, self._error = write_profile, log, error
self._destination, self._build_config = destination, build_config
self._settings, self._allowed = processing_settings, allowed
def bind_actions(self, primary: PrimaryView, data: DataView) -> None:
primary.clear.clicked.connect(lambda: self.delete_history(remove_last=False))
data.remove_last.clicked.connect(lambda: self.delete_history(remove_last=True))
data.save.clicked.connect(lambda: self.save())
data.save_json.clicked.connect(lambda: self.save(json_export=True))
data.record.clicked.connect(lambda: self.start_recording(data.record_count.value()))
def start_recording(self, count: int) -> None:
if not self._allowed(False):
return
try:
config = self.pipeline.active_config if self.pipeline.running else None
self.pipeline.recording.start(
*self._destination(config or self._build_config()), count,
running=self.pipeline.running, start_acquisition=self.pipeline.start,
)
except Exception as exc:
self._error("Failed to start disk recording", exc)
def save(self, *, json_export: bool = False) -> None:
if not self._allowed(False):
return
action = "VNA history JSON" if json_export else "snapshot"
try:
active = self.pipeline.active_config
root, name, prefix = self._destination(active or self._build_config())
self.pipeline.drain_snapshot()
if self.pipeline.closing or self.pipeline.active_config is not active:
return
raw = list(self.pipeline.history.raw)
preprocessed = list(self.pipeline.history.preprocessed)
results = list(self.pipeline.history.results)
if not (raw or preprocessed or results):
raise ValueError("No runtime data is available for save")
if json_export:
paths = self.store.save_runtime_vna_history_json_batch(
root, name, raw, preprocessed, results,
primary_stage="preprocessed", name_prefix=prefix,
)
directory = paths[0].parent
profile_path = directory / "vna_config_profile.json"
else:
directory = self.store.save_runtime_snapshot_numpy(
root, name, raw, preprocessed, results,
name_prefix=prefix, processing_settings=self._settings,
)
profile_path = directory / "config_profile.json"
try:
self._write_profile(profile_path, allow_overwrite=json_export, dataset=True)
except Exception as exc:
self._error(
f"{action} data was saved in {directory}, but the adjacent config profile "
f"could not be written: {profile_path}", exc,
)
return
self._log(
f"Saved {action}: {directory} "
f"(raw={len(raw)}, preprocessed={len(preprocessed)}, results={len(results)})"
)
except Exception as exc:
self._error(f"Failed to save {action}", exc)
def delete_history(self, *, remove_last: bool) -> None:
if not self._allowed(True):
return
action = "remove last runtime measurement" if remove_last else "clear runtime history"
try:
with self.pipeline.pause() as operation:
if not operation.valid:
return
self.pipeline.discard_replay()
dropped_raw, dropped_pre, dropped_results = self.pipeline.drop_pending()
self.pipeline.history.delete(remove_last=remove_last)
try:
self.pipeline.publish_live()
finally:
self.pipeline.plots.clear()
self.pipeline.settle_deleted_results()
if not operation.valid:
return
self.pipeline.history_changed()
self.pipeline.plots.redraw()
# Removing the newest sweep brings an older one, maybe never replayed, into a stopped B-scan.
self.pipeline.schedule_replay()
self._log(
f"{action}: dropped raw={dropped_raw}, "
f"preprocessed={dropped_pre}, results={dropped_results}"
)
except InterruptedError:
self.pipeline.operation_cancelled()
except Exception as exc:
self._error(f"Failed to {action}", exc)