diff --git a/VideoReceiverSFP/core/config.py b/VideoReceiverSFP/core/config.py index 7a4e7b8..8ff9492 100644 --- a/VideoReceiverSFP/core/config.py +++ b/VideoReceiverSFP/core/config.py @@ -18,7 +18,11 @@ SAVE_RAW_UNKNOWN = True # Flags to also save decoded previews for quick inspection # MFD: save BMP; SAR/UNKNOWN: save BMP by default for lossless grayscale SAVE_BMP_MFD = False -SAVE_BMP_SAR = True +# By default do not automatically save decoded SAR previews; leave saving +# under user control via the UI checkboxes. Previously this was True and +# caused SAR previews to be written even when the user only enabled video +# recording. Set to False to avoid unexpected saves. +SAVE_BMP_SAR = False SAVE_BMP_UNKNOWN = True # Video recording defaults diff --git a/VideoReceiverSFP/core/dump_manager.py b/VideoReceiverSFP/core/dump_manager.py index 696b528..e69d9c4 100644 --- a/VideoReceiverSFP/core/dump_manager.py +++ b/VideoReceiverSFP/core/dump_manager.py @@ -172,13 +172,12 @@ class DumpManager: except Exception: logger.exception("Error closing video writer") finally: + # Remove the active writer and runtime timestamp but keep + # the recorded video's start time/path so the UI can show + # the last produced file even after recording stopped. self._video_writers.pop(category, None) - self._video_start_times.pop(category, None) + # keep _video_start_times[category] and _video_paths[category] self._last_frame_timestamps.pop(category, None) - try: - self._video_paths.pop(category, None) - except Exception: - pass def _enqueue(self, path: str, category: str) -> None: """Track saved file and trigger pruning.""" diff --git a/VideoReceiverSFP/core/payload_dispatcher.py b/VideoReceiverSFP/core/payload_dispatcher.py index 05772e5..ca7c636 100644 --- a/VideoReceiverSFP/core/payload_dispatcher.py +++ b/VideoReceiverSFP/core/payload_dispatcher.py @@ -168,19 +168,27 @@ def dispatch_frame(module: Any, frame: Any, leader: Optional[Any]) -> None: pass # generic callbacks and saving + try: + is_mfd = (image_type_id == getattr(module, 'IMAGE_TYPE_MFD', 1)) + except Exception: + is_mfd = False + for cb in list(getattr(module, '_callbacks', [])): try: - if getattr(module, '_save_png', False) and Image is not None and hasattr(frame, 'save'): + # Only the MFD generic save is controlled by _save_png. SAR has its + # own _sar_save_png flag handled earlier. Save MFD previews under + # the 'mfd' category so get_last_saved_mfd() can find them. + if is_mfd and getattr(module, '_save_png', False) and Image is not None and hasattr(frame, 'save'): try: if getattr(module, '_dump_manager', None) is not None: - saved = module._dump_manager.save_preview_from_pil(frame, category='frame', fmt='png') + saved = module._dump_manager.save_preview_from_pil(frame, category='mfd', fmt='png') if saved: - logging.getLogger().info("VideoReceiverSFP: saved frame to %s", saved) + logging.getLogger().info("VideoReceiverSFP: saved MFD preview to %s", saved) else: ts = time.strftime('%Y%m%d_%H%M%S') + (f"_{int(time.time() * 1000) % 1000:03d}") dumps_dir = getattr(module, '_dumps_dir', os.path.join(os.getcwd(), 'dumps')) os.makedirs(dumps_dir, exist_ok=True) - pngpath = os.path.join(dumps_dir, f'VideoReceiverSFP_frame_{ts}.png') + pngpath = os.path.join(dumps_dir, f'VideoReceiverSFP_mfd_{ts}.png') frame.save(pngpath) module._saved_pngs.append(pngpath) while len(module._saved_pngs) > module._dump_keep: @@ -190,7 +198,7 @@ def dispatch_frame(module: Any, frame: Any, leader: Optional[Any]) -> None: except Exception: pass except Exception: - logging.getLogger().exception('VideoReceiverSFP: failed to save preview png') + logging.getLogger().exception('VideoReceiverSFP: failed to save MFD preview png') try: cb(frame) except Exception: diff --git a/VideoReceiverSFP/core/test_orchestrator.py b/VideoReceiverSFP/core/test_orchestrator.py index 552e78d..066be8d 100644 --- a/VideoReceiverSFP/core/test_orchestrator.py +++ b/VideoReceiverSFP/core/test_orchestrator.py @@ -12,6 +12,7 @@ import logging import threading import tkinter as tk from tkinter import ttk +from tkinter import messagebox from .sfp_module import SfpConnectorModule @@ -326,8 +327,12 @@ def run_orchestrator(): rec_mfd = bool(mfd_vid_save_var.get()) rec_sar = bool(sar_vid_save_var.get()) - # If nothing selected, do nothing (per user request) + # If nothing selected, inform the user and abort if not (save_mfd or save_sar or rec_mfd or rec_sar): + try: + messagebox.showwarning("No selection", "Please select at least one save or record option before starting.") + except Exception: + pass return # Apply selections to module: image saving and video recording only while 'recording'