init commit
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
"""Snapshot and ring-drain helper mixin for the main GUI window."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import time
|
||||
|
||||
from PyQt6.QtWidgets import QFileDialog
|
||||
|
||||
|
||||
class AppWindowSnapshotMixin:
|
||||
"""Saves runtime data snapshots and maintains ring-reader freshness."""
|
||||
|
||||
def _save_snapshot(self) -> None:
|
||||
"""Save runtime snapshot in numpy-directory format."""
|
||||
self._drain_runtime_rings_for_snapshot()
|
||||
|
||||
if not self._raw_history and not self._pre_history and not self._result_history:
|
||||
self._show_error("No runtime data is available for save")
|
||||
return
|
||||
|
||||
try:
|
||||
last_n = int(self._save_count.value())
|
||||
output_root = Path(self._save_path_input.text().strip()).expanduser()
|
||||
snapshot_name = self._save_name_input.text().strip()
|
||||
snapshot_dir, summary = self._store.save_runtime_snapshot_numpy(
|
||||
output_root,
|
||||
snapshot_name,
|
||||
list(self._raw_history),
|
||||
list(self._pre_history),
|
||||
list(self._result_history),
|
||||
last_n,
|
||||
)
|
||||
self._log(
|
||||
f"Saved numpy snapshot: {snapshot_dir} "
|
||||
f"(raw={summary.get('raw_count', 0)}, "
|
||||
f"preprocessed={summary.get('preprocessed_count', 0)}, "
|
||||
f"results={summary.get('result_count', 0)}, "
|
||||
f"mode={summary.get('selection_mode', 'unknown')})"
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
self._show_error(f"Failed to save snapshot: {exc}")
|
||||
|
||||
def _browse_save_path(self) -> None:
|
||||
"""Open directory picker for snapshot output path."""
|
||||
selected = QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
"Select Snapshot Directory",
|
||||
self._save_path_input.text().strip() or str(self._project_root),
|
||||
)
|
||||
if selected:
|
||||
self._save_path_input.setText(selected)
|
||||
|
||||
def _drain_runtime_rings_for_snapshot(self) -> None:
|
||||
"""Drain readers before snapshot to reduce partial-history races."""
|
||||
if self._raw_reader is None and self._pre_reader is None and self._result_reader is None:
|
||||
return
|
||||
try:
|
||||
start_raw_count = len(self._raw_history)
|
||||
start_pre_count = len(self._pre_history)
|
||||
start_result_count = len(self._result_history)
|
||||
deadline = time.monotonic() + 0.8
|
||||
|
||||
while True:
|
||||
progress = False
|
||||
|
||||
if self._raw_reader is not None:
|
||||
for _ in range(self._max_pop_per_snapshot_drain):
|
||||
collection = self._raw_reader.pop_raw_collection()
|
||||
if collection is None:
|
||||
break
|
||||
self._raw_history.append(collection)
|
||||
progress = True
|
||||
|
||||
if self._pre_reader is not None:
|
||||
for _ in range(self._max_pop_per_snapshot_drain):
|
||||
collection = self._pre_reader.pop_preprocessed_collection()
|
||||
if collection is None:
|
||||
break
|
||||
self._pre_history.append(collection)
|
||||
progress = True
|
||||
|
||||
if self._result_reader is not None:
|
||||
for _ in range(self._max_pop_per_snapshot_drain):
|
||||
collection = self._result_reader.pop_result_collection()
|
||||
if collection is None:
|
||||
break
|
||||
self._record_result_history(collection)
|
||||
progress = True
|
||||
|
||||
if progress:
|
||||
continue
|
||||
|
||||
missing_raw = self._raw_reader is not None and len(self._raw_history) == start_raw_count
|
||||
missing_pre = self._pre_reader is not None and len(self._pre_history) == start_pre_count
|
||||
got_results = len(self._result_history) > start_result_count
|
||||
|
||||
if got_results and (missing_raw or missing_pre) and time.monotonic() < deadline:
|
||||
time.sleep(0.01)
|
||||
continue
|
||||
break
|
||||
except Exception as exc: # noqa: BLE001
|
||||
self._log(f"Snapshot drain warning: {exc}")
|
||||
|
||||
def _drop_pending_ring_payloads(self, *, include_results: bool = True) -> None:
|
||||
"""Drop unread payloads from active readers."""
|
||||
dropped_raw = self._raw_reader.drop_all() if self._raw_reader is not None else 0
|
||||
dropped_pre = self._pre_reader.drop_all() if self._pre_reader is not None else 0
|
||||
dropped_results = 0
|
||||
if include_results and self._result_reader is not None:
|
||||
dropped_results = self._result_reader.drop_all()
|
||||
|
||||
if dropped_raw or dropped_pre or dropped_results:
|
||||
self._log(
|
||||
"Single capture ring reset: "
|
||||
f"dropped raw={dropped_raw}, "
|
||||
f"preprocessed={dropped_pre}, "
|
||||
f"results={dropped_results}"
|
||||
)
|
||||
Reference in New Issue
Block a user