configurazione salvataggi esterni al modulo

This commit is contained in:
VALLONGOL 2026-01-19 14:37:12 +01:00
parent 28f5fda397
commit bef6cf6f08
4 changed files with 327 additions and 7 deletions

View File

@ -49,6 +49,8 @@ class DumpManager:
self._video_fps_map: Dict[str, float] = {}
# Maximum duplicate frames to write when spacing is large (safety cap)
self._max_duplicate_frames = 1000
# Track last produced video file paths per category
self._video_paths: Dict[str, str] = {}
# prune existing files at startup
for cat in list(self._saved.keys()):
@ -112,6 +114,8 @@ class DumpManager:
return False
self._video_writers[category] = writer
self._video_start_times[category] = time.time()
# remember video path for later retrieval
self._video_paths[category] = path
# store configured fps per category
try:
self._video_fps_map[category] = float(fps)
@ -171,6 +175,10 @@ class DumpManager:
self._video_writers.pop(category, None)
self._video_start_times.pop(category, None)
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."""
@ -178,6 +186,38 @@ class DumpManager:
self._saved[cat].append(path)
self._prune_category(cat)
def get_last_saved(self, category: str) -> Optional[str]:
"""Return the last saved preview path for a category, or None."""
try:
cat = category if category in self._saved else 'unknown'
if not self._saved.get(cat):
return None
return self._saved[cat][-1]
except Exception:
return None
def get_last_video_path(self, category: Optional[str] = None) -> Optional[str]:
"""Return the last started video path.
If `category` is provided, return that category's last video path.
Otherwise return the most-recently-started video across categories.
"""
try:
if category:
return self._video_paths.get(category)
# choose latest by _video_start_times
best_cat = None
best_ts = 0.0
for cat, ts in self._video_start_times.items():
if ts and ts > best_ts:
best_ts = ts
best_cat = cat
if best_cat:
return self._video_paths.get(best_cat)
return None
except Exception:
return None
def _prune_category(self, category: str) -> None:
"""Remove oldest files if they exceed the keep limit."""
prefix = f'VideoReceiverSFP_{category}_'

View File

@ -1262,6 +1262,36 @@ class SfpConnectorModule:
except Exception:
pass
# --- API: last saved file paths -------------------------------------
def get_last_saved_mfd(self) -> Optional[str]:
try:
if getattr(self, '_dump_manager', None) is not None:
return self._dump_manager.get_last_saved('mfd')
# fallback: check module-level saved lists
if getattr(self, '_saved_pngs', None):
return self._saved_pngs[-1] if len(self._saved_pngs) else None
except Exception:
pass
return None
def get_last_saved_sar(self) -> Optional[str]:
try:
if getattr(self, '_dump_manager', None) is not None:
return self._dump_manager.get_last_saved('sar')
if getattr(self, '_saved_sar_pngs', None):
return self._saved_sar_pngs[-1] if len(self._saved_sar_pngs) else None
except Exception:
pass
return None
def get_last_video_path(self, category: Optional[str] = None) -> Optional[str]:
try:
if getattr(self, '_dump_manager', None) is not None:
return self._dump_manager.get_last_video_path(category)
except Exception:
pass
return None
def update_mfd_lut(self, lut) -> None:
"""Receive an MFD LUT (numpy array or similar) from the UI and store it.

View File

@ -128,7 +128,119 @@ def run_orchestrator():
main_frame.columnconfigure(1, weight=1)
main_frame.rowconfigure(1, weight=1) # The viewer row is the one that grows
# --- Row 0b: Last saved paths (for copy/paste) ---
paths_frame = ttk.Frame(controls_box)
paths_frame.pack(fill='x', expand=False, pady=(6,0))
mfd_img_save_var = tk.BooleanVar(value=bool(getattr(module, '_save_png', False)))
mfd_img_cb = ttk.Checkbutton(paths_frame, text='Last MFD file image', variable=mfd_img_save_var)
mfd_img_cb.grid(row=0, column=0, sticky='w', padx=5)
last_mfd_var = tk.StringVar(value="")
last_mfd_entry = ttk.Entry(paths_frame, textvariable=last_mfd_var, width=80, state='readonly')
last_mfd_entry.grid(row=0, column=1, padx=5, sticky='w')
def _copy_mfd():
try:
root.clipboard_clear()
root.clipboard_append(last_mfd_var.get() or '')
except Exception:
pass
ttk.Button(paths_frame, text='Copy', command=_copy_mfd, width=6).grid(row=0, column=2, padx=4)
sar_img_save_var = tk.BooleanVar(value=bool(getattr(module, '_sar_save_png', False)))
sar_img_cb = ttk.Checkbutton(paths_frame, text='Last SAR file image', variable=sar_img_save_var)
sar_img_cb.grid(row=1, column=0, sticky='w', padx=5)
last_sar_var = tk.StringVar(value="")
last_sar_entry = ttk.Entry(paths_frame, textvariable=last_sar_var, width=80, state='readonly')
last_sar_entry.grid(row=1, column=1, padx=5, sticky='w')
def _copy_sar():
try:
root.clipboard_clear()
root.clipboard_append(last_sar_var.get() or '')
except Exception:
pass
ttk.Button(paths_frame, text='Copy', command=_copy_sar, width=6).grid(row=1, column=2, padx=4)
mfd_vid_save_var = tk.BooleanVar(value=bool(getattr(module, '_record_mfd_video', False)))
mfd_vid_cb = ttk.Checkbutton(paths_frame, text='Last MFD file video', variable=mfd_vid_save_var)
mfd_vid_cb.grid(row=2, column=0, sticky='w', padx=5)
last_mfd_vid_var = tk.StringVar(value="")
last_mfd_vid_entry = ttk.Entry(paths_frame, textvariable=last_mfd_vid_var, width=80, state='readonly')
last_mfd_vid_entry.grid(row=2, column=1, padx=5, sticky='w')
def _copy_mfd_vid():
try:
root.clipboard_clear()
root.clipboard_append(last_mfd_vid_var.get() or '')
except Exception:
pass
ttk.Button(paths_frame, text='Copy', command=_copy_mfd_vid, width=6).grid(row=2, column=2, padx=4)
sar_vid_save_var = tk.BooleanVar(value=bool(getattr(module, '_record_sar_video', False)))
sar_vid_cb = ttk.Checkbutton(paths_frame, text='Last SAR file video', variable=sar_vid_save_var)
sar_vid_cb.grid(row=3, column=0, sticky='w', padx=5)
last_sar_vid_var = tk.StringVar(value="")
last_sar_vid_entry = ttk.Entry(paths_frame, textvariable=last_sar_vid_var, width=80, state='readonly')
last_sar_vid_entry.grid(row=3, column=1, padx=5, sticky='w')
def _copy_sar_vid():
try:
root.clipboard_clear()
root.clipboard_append(last_sar_vid_var.get() or '')
except Exception:
pass
ttk.Button(paths_frame, text='Copy', command=_copy_sar_vid, width=6).grid(row=3, column=2, padx=4)
def _refresh_paths():
try:
m = module.get_last_saved_mfd()
s = module.get_last_saved_sar()
vm = module.get_last_video_path('mfd')
vs = module.get_last_video_path('sar')
try:
last_mfd_var.set(m or '')
except Exception:
pass
try:
last_sar_var.set(s or '')
except Exception:
pass
try:
last_mfd_vid_var.set(vm or '')
except Exception:
pass
try:
last_sar_vid_var.set(vs or '')
except Exception:
pass
except Exception:
pass
ttk.Button(paths_frame, text='Refresh Paths', command=_refresh_paths).grid(row=0, column=3, rowspan=4, padx=8)
# initial refresh
try:
_refresh_paths()
except Exception:
pass
# Periodic automatic refresh of last-saved paths (every 2 seconds)
def _periodic_refresh():
try:
_refresh_paths()
except Exception:
pass
try:
root.after(2000, _periodic_refresh)
except Exception:
pass
try:
root.after(2000, _periodic_refresh)
except Exception:
pass
# Callbacks for Parameters
# Track whether the user has started a 'manual' recording session
user_recording_active_var = tk.BooleanVar(value=False)
def on_mfd_param_changed(param_type, name, value):
try:
if param_type == 'save_png':
@ -208,20 +320,108 @@ def run_orchestrator():
# Button Actions
def on_start_recording():
# Read desired targets from checkboxes and only apply when starting
save_mfd = bool(mfd_img_save_var.get())
save_sar = bool(sar_img_save_var.get())
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 not (save_mfd or save_sar or rec_mfd or rec_sar):
return
# Apply selections to module: image saving and video recording only while 'recording'
try:
module.set_save_png(save_mfd)
except Exception:
pass
try:
module.set_sar_save_png(save_sar)
except Exception:
pass
try:
module.set_record_mfd_video(rec_mfd)
except Exception:
pass
try:
module.set_record_sar_video(rec_sar)
except Exception:
pass
# Mark that user requested recording (even if only image saving)
try:
user_recording_active_var.set(True)
except Exception:
pass
# Lock checkboxes while recording/armed
try:
mfd_img_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
sar_img_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
mfd_vid_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
sar_vid_cb.config(state=tk.DISABLED)
except Exception:
pass
start_rec_btn.config(state=tk.DISABLED)
stop_rec_btn.config(state=tk.NORMAL)
rec_status_var.set("Recording...")
rec_status_label.config(foreground="red")
module.set_record_mfd_video(True)
module.set_record_sar_video(True)
def on_stop_recording():
# Stop all recording/saving targets and unlock checkboxes
try:
module.set_record_mfd_video(False)
except Exception:
pass
try:
module.set_record_sar_video(False)
except Exception:
pass
try:
module.set_save_png(False)
except Exception:
pass
try:
module.set_sar_save_png(False)
except Exception:
pass
try:
mfd_img_cb.config(state=tk.NORMAL)
except Exception:
pass
try:
sar_img_cb.config(state=tk.NORMAL)
except Exception:
pass
try:
mfd_vid_cb.config(state=tk.NORMAL)
except Exception:
pass
try:
sar_vid_cb.config(state=tk.NORMAL)
except Exception:
pass
try:
user_recording_active_var.set(False)
except Exception:
pass
start_rec_btn.config(state=tk.NORMAL)
stop_rec_btn.config(state=tk.DISABLED)
rec_status_var.set("Not Recording")
rec_status_label.config(foreground="gray")
module.set_record_mfd_video(False)
module.set_record_sar_video(False)
start_rec_btn.config(command=on_start_recording)
stop_rec_btn.config(command=on_stop_recording)
@ -229,7 +429,9 @@ def run_orchestrator():
# Periodically update recording status indicator based on module state
def _update_record_status():
try:
armed = bool(getattr(module, '_record_mfd_video', False) or getattr(module, '_record_sar_video', False))
# consider user-initiated recording as 'armed' even if video flags are not set
manual = bool(user_recording_active_var.get())
armed = manual or bool(getattr(module, '_record_mfd_video', False) or getattr(module, '_record_sar_video', False))
active_mfd = bool(getattr(module, '_mfd_video_active', False))
active_sar = bool(getattr(module, '_sar_video_active', False))
if active_mfd or active_sar:
@ -242,16 +444,64 @@ def run_orchestrator():
rec_status_label.config(foreground="red")
start_rec_btn.config(state=tk.DISABLED)
stop_rec_btn.config(state=tk.NORMAL)
try:
mfd_vid_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
sar_vid_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
mfd_img_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
sar_img_cb.config(state=tk.DISABLED)
except Exception:
pass
elif armed:
rec_status_var.set("Armed (waiting for first frame)")
rec_status_label.config(foreground="orange")
start_rec_btn.config(state=tk.DISABLED)
stop_rec_btn.config(state=tk.NORMAL)
try:
mfd_vid_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
sar_vid_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
mfd_img_cb.config(state=tk.DISABLED)
except Exception:
pass
try:
sar_img_cb.config(state=tk.DISABLED)
except Exception:
pass
else:
rec_status_var.set("Not Recording")
rec_status_label.config(foreground="gray")
start_rec_btn.config(state=tk.NORMAL)
stop_rec_btn.config(state=tk.DISABLED)
try:
mfd_vid_cb.config(state=tk.NORMAL)
except Exception:
pass
try:
sar_vid_cb.config(state=tk.NORMAL)
except Exception:
pass
try:
mfd_img_cb.config(state=tk.NORMAL)
except Exception:
pass
try:
sar_img_cb.config(state=tk.NORMAL)
except Exception:
pass
except Exception:
pass
try:

View File

@ -7,7 +7,7 @@
- [ ] VRSFP: aggiungere la possibilità di modificare la configurazione rutime, com e poter aggiungere la possibilità di salvare le immagini invece che il video o altro: creare una funzione setConfig che permetta di modificare runtime la configurazione del modulo. La stessa informazioni come formato deve essere la stessa che viene passata in fase di inizializzazione. Visto che la configurazione possa essere modificata anche in altre parti sarebbe utile anche a vere una funzione che restituisca il dizionario della configurazione come get_config().
- [x] VRSFP: poter aggiungere le funzioni di start salvataggio video e stop salvataggio video come api
- [ ] VRSFP: poter impostare runtime il nome e la posizione del file immagine o video prodotto
- [ ] VRSFP: avere una funzione che restituisce nome e path dell'ultimo file salvato per mfd, sar e video
- [x] VRSFP: avere una funzione che restituisce nome e path dell'ultimo file salvato per mfd, sar e video
- [x] VRSFP: aggiungere un log stile modulo aggiuntivo e collegarlo al modulo che poi sarà comune a tutti i moduli
- [x] VRSFP: aggiungere un pannello al test_orchestator dove poter aggiungere i controlli per testare le funzionalità di registrazione ondemand ecc
- [x] VRSFP: poter impostare a priori la dimensione in pixel della finestra mfd e della finestra sar. Per la finestra mfd, se viene indicato "0, 0 " viene usata la dimensione dell'immagine che viene spedita. se è diversa da 0,0 viene ridmensionata la finestra. Invece per il sar la dimensione è sempre quella specificata e deve sempre essere diversa da 0,0.