aggiunto il salvataggio dei dati del debug delle latenze nei messaggi di sync in csv
This commit is contained in:
parent
3052757f43
commit
5908e72ae3
6
logger_prefs.json
Normal file
6
logger_prefs.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"saved_levels": {
|
||||||
|
"target_simulator.core.sfp_transport": "INFO"
|
||||||
|
},
|
||||||
|
"last_selected": "target_simulator.core.sfp_transport"
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
"scan_limit": 60,
|
"scan_limit": 60,
|
||||||
"max_range": 100,
|
"max_range": 100,
|
||||||
"geometry": "1492x992+230+258",
|
"geometry": "1492x992+230+258",
|
||||||
"last_selected_scenario": "scenario2",
|
"last_selected_scenario": "scenario_dritto",
|
||||||
"connection": {
|
"connection": {
|
||||||
"target": {
|
"target": {
|
||||||
"type": "sfp",
|
"type": "sfp",
|
||||||
|
|||||||
@ -127,10 +127,6 @@ def build_display_data(
|
|||||||
t_scenario = scenario.get_target(tid)
|
t_scenario = scenario.get_target(tid)
|
||||||
if t_scenario is not None:
|
if t_scenario is not None:
|
||||||
sim_target.active = bool(getattr(t_scenario, "active", True))
|
sim_target.active = bool(getattr(t_scenario, "active", True))
|
||||||
if logger and not sim_target.active:
|
|
||||||
logger.debug(
|
|
||||||
f"PPI Adapter: Target {tid} is INACTIVE (from scenario fallback)"
|
|
||||||
)
|
|
||||||
|
|
||||||
simulated_targets_for_ppi.append(sim_target)
|
simulated_targets_for_ppi.append(sim_target)
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,12 @@ import tkinter as tk
|
|||||||
from tkinter import ttk, messagebox
|
from tkinter import ttk, messagebox
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
import os
|
||||||
|
import csv
|
||||||
|
import threading
|
||||||
from typing import Dict, Optional, List
|
from typing import Dict, Optional, List
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from target_simulator.core.sfp_communicator import SFPCommunicator
|
from target_simulator.core.sfp_communicator import SFPCommunicator
|
||||||
from target_simulator.gui.payload_router import DebugPayloadRouter
|
from target_simulator.gui.payload_router import DebugPayloadRouter
|
||||||
@ -51,6 +55,15 @@ class SyncToolWindow(tk.Toplevel):
|
|||||||
# Statistiche
|
# Statistiche
|
||||||
self.latency_values: List[float] = []
|
self.latency_values: List[float] = []
|
||||||
|
|
||||||
|
# === Sistema di salvataggio CSV (asincrono e non invasivo) ===
|
||||||
|
self.csv_enabled = False
|
||||||
|
self.csv_filepath: Optional[str] = None
|
||||||
|
self.csv_buffer: deque = deque(maxlen=10000) # Buffer grande per evitare perdite
|
||||||
|
self.csv_lock = threading.Lock()
|
||||||
|
self.csv_writer_thread: Optional[threading.Thread] = None
|
||||||
|
self.csv_stop_event = threading.Event()
|
||||||
|
self.csv_session_start: Optional[float] = None
|
||||||
|
|
||||||
self._create_widgets()
|
self._create_widgets()
|
||||||
|
|
||||||
self.protocol("WM_DELETE_WINDOW", self._on_close)
|
self.protocol("WM_DELETE_WINDOW", self._on_close)
|
||||||
@ -96,6 +109,25 @@ class SyncToolWindow(tk.Toplevel):
|
|||||||
side=tk.LEFT
|
side=tk.LEFT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Riga 3: Salvataggio CSV
|
||||||
|
row3 = ttk.Frame(controls_frame)
|
||||||
|
row3.pack(fill=tk.X, pady=2)
|
||||||
|
|
||||||
|
self.csv_check_var = tk.BooleanVar(value=False)
|
||||||
|
self.csv_checkbox = ttk.Checkbutton(
|
||||||
|
row3,
|
||||||
|
text="Save to CSV",
|
||||||
|
variable=self.csv_check_var,
|
||||||
|
command=self._toggle_csv_save,
|
||||||
|
)
|
||||||
|
self.csv_checkbox.pack(side=tk.LEFT, padx=(0, 10))
|
||||||
|
|
||||||
|
self.csv_status_var = tk.StringVar(value="Not saving")
|
||||||
|
self.csv_status_label = tk.Label(
|
||||||
|
row3, textvariable=self.csv_status_var, foreground="gray"
|
||||||
|
)
|
||||||
|
self.csv_status_label.pack(side=tk.LEFT)
|
||||||
|
|
||||||
# === STATISTICHE ===
|
# === STATISTICHE ===
|
||||||
stats_frame = ttk.LabelFrame(main_frame, text="Statistics", padding=5)
|
stats_frame = ttk.LabelFrame(main_frame, text="Statistics", padding=5)
|
||||||
stats_frame.pack(fill=tk.X, pady=(0, 10))
|
stats_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
@ -320,6 +352,14 @@ class SyncToolWindow(tk.Toplevel):
|
|||||||
if len(self.latency_values) > 200:
|
if len(self.latency_values) > 200:
|
||||||
self.latency_values.pop(0)
|
self.latency_values.pop(0)
|
||||||
|
|
||||||
|
# Salva su CSV se abilitato (operazione velocissima, non blocca)
|
||||||
|
if self.csv_enabled and self.csv_session_start is not None:
|
||||||
|
elapsed_s = reception_time - self.csv_session_start
|
||||||
|
server_timetag = result.get("server_timetag", 0)
|
||||||
|
self._add_sample_to_csv_buffer(
|
||||||
|
elapsed_s, cookie, rtt_ms, latency_ms, server_timetag
|
||||||
|
)
|
||||||
|
|
||||||
# Aggiorna UI
|
# Aggiorna UI
|
||||||
timestamp_str = time.strftime("%H:%M:%S")
|
timestamp_str = time.strftime("%H:%M:%S")
|
||||||
|
|
||||||
@ -343,10 +383,157 @@ class SyncToolWindow(tk.Toplevel):
|
|||||||
# Continua il polling
|
# Continua il polling
|
||||||
self.after(100, self._process_sync_queue)
|
self.after(100, self._process_sync_queue)
|
||||||
|
|
||||||
|
def _toggle_csv_save(self):
|
||||||
|
"""Attiva/disattiva il salvataggio CSV asincrono."""
|
||||||
|
if self.csv_check_var.get():
|
||||||
|
# Avvia salvataggio
|
||||||
|
self._start_csv_save()
|
||||||
|
else:
|
||||||
|
# Ferma salvataggio
|
||||||
|
self._stop_csv_save()
|
||||||
|
|
||||||
|
def _start_csv_save(self):
|
||||||
|
"""Avvia il salvataggio CSV in background."""
|
||||||
|
if self.csv_enabled:
|
||||||
|
return # Già attivo
|
||||||
|
|
||||||
|
# Crea nome file con timestamp
|
||||||
|
project_root = os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), "..", "..")
|
||||||
|
)
|
||||||
|
temp_dir = os.path.join(project_root, "Temp")
|
||||||
|
os.makedirs(temp_dir, exist_ok=True)
|
||||||
|
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
self.csv_filepath = os.path.join(temp_dir, f"sync_latency_{timestamp}.csv")
|
||||||
|
self.csv_session_start = time.monotonic()
|
||||||
|
|
||||||
|
# Inizializza il file CSV con header
|
||||||
|
try:
|
||||||
|
with open(self.csv_filepath, "w", newline="") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerow(
|
||||||
|
[
|
||||||
|
"elapsed_s",
|
||||||
|
"timestamp",
|
||||||
|
"cookie",
|
||||||
|
"rtt_ms",
|
||||||
|
"latency_ms",
|
||||||
|
"server_timetag",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.csv_enabled = True
|
||||||
|
self.csv_stop_event.clear()
|
||||||
|
|
||||||
|
# Avvia thread di scrittura
|
||||||
|
self.csv_writer_thread = threading.Thread(
|
||||||
|
target=self._csv_writer_loop, daemon=True, name="CSVWriterThread"
|
||||||
|
)
|
||||||
|
self.csv_writer_thread.start()
|
||||||
|
|
||||||
|
filename = os.path.basename(self.csv_filepath)
|
||||||
|
self.csv_status_var.set(f"Saving to {filename}")
|
||||||
|
self.csv_status_label.config(foreground="green")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror(
|
||||||
|
"CSV Error", f"Failed to create CSV file: {e}", parent=self
|
||||||
|
)
|
||||||
|
self.csv_check_var.set(False)
|
||||||
|
self.csv_enabled = False
|
||||||
|
|
||||||
|
def _stop_csv_save(self):
|
||||||
|
"""Ferma il salvataggio CSV."""
|
||||||
|
if not self.csv_enabled:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.csv_enabled = False
|
||||||
|
self.csv_stop_event.set()
|
||||||
|
|
||||||
|
# Attendi che il thread finisca di scrivere i dati rimanenti
|
||||||
|
if self.csv_writer_thread and self.csv_writer_thread.is_alive():
|
||||||
|
self.csv_writer_thread.join(timeout=2.0)
|
||||||
|
|
||||||
|
self.csv_status_var.set("Not saving")
|
||||||
|
self.csv_status_label.config(foreground="gray")
|
||||||
|
|
||||||
|
if self.csv_filepath:
|
||||||
|
filename = os.path.basename(self.csv_filepath)
|
||||||
|
messagebox.showinfo(
|
||||||
|
"CSV Saved",
|
||||||
|
f"Latency data saved to:\n{filename}",
|
||||||
|
parent=self,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _csv_writer_loop(self):
|
||||||
|
"""Thread worker che scrive i dati bufferizzati su disco periodicamente."""
|
||||||
|
while not self.csv_stop_event.is_set():
|
||||||
|
try:
|
||||||
|
# Attendi un po' prima di scrivere (batch writing)
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
# Estrai tutti i dati dal buffer
|
||||||
|
rows_to_write = []
|
||||||
|
with self.csv_lock:
|
||||||
|
while len(self.csv_buffer) > 0:
|
||||||
|
rows_to_write.append(self.csv_buffer.popleft())
|
||||||
|
|
||||||
|
# Scrivi su file (fuori dal lock per non bloccare)
|
||||||
|
if rows_to_write and self.csv_filepath:
|
||||||
|
try:
|
||||||
|
with open(self.csv_filepath, "a", newline="") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerows(rows_to_write)
|
||||||
|
except Exception:
|
||||||
|
pass # Ignora errori di scrittura per non bloccare
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Flush finale dei dati rimanenti
|
||||||
|
rows_to_write = []
|
||||||
|
with self.csv_lock:
|
||||||
|
while len(self.csv_buffer) > 0:
|
||||||
|
rows_to_write.append(self.csv_buffer.popleft())
|
||||||
|
|
||||||
|
if rows_to_write and self.csv_filepath:
|
||||||
|
try:
|
||||||
|
with open(self.csv_filepath, "a", newline="") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerows(rows_to_write)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _add_sample_to_csv_buffer(
|
||||||
|
self, elapsed_s: float, cookie: int, rtt_ms: float, latency_ms: float, server_timetag: int
|
||||||
|
):
|
||||||
|
"""Aggiunge un campione al buffer CSV (operazione velocissima, non blocca)."""
|
||||||
|
if not self.csv_enabled:
|
||||||
|
return
|
||||||
|
|
||||||
|
timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||||
|
row = [
|
||||||
|
f"{elapsed_s:.3f}",
|
||||||
|
timestamp_str,
|
||||||
|
cookie,
|
||||||
|
f"{rtt_ms:.3f}",
|
||||||
|
f"{latency_ms:.3f}",
|
||||||
|
server_timetag,
|
||||||
|
]
|
||||||
|
|
||||||
|
with self.csv_lock:
|
||||||
|
self.csv_buffer.append(row)
|
||||||
|
|
||||||
def _on_close(self):
|
def _on_close(self):
|
||||||
"""Gestisce la chiusura della finestra."""
|
"""Gestisce la chiusura della finestra."""
|
||||||
if self.periodic_active:
|
if self.periodic_active:
|
||||||
self.periodic_active = False
|
self.periodic_active = False
|
||||||
if self.periodic_after_id:
|
if self.periodic_after_id:
|
||||||
self.after_cancel(self.periodic_after_id)
|
self.after_cancel(self.periodic_after_id)
|
||||||
|
|
||||||
|
# Ferma il salvataggio CSV se attivo
|
||||||
|
if self.csv_enabled:
|
||||||
|
self.csv_check_var.set(False)
|
||||||
|
self._stop_csv_save()
|
||||||
|
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user