diff --git a/target_simulator/core/sfp_structures.py b/target_simulator/core/sfp_structures.py index 5619adb..d189d25 100644 --- a/target_simulator/core/sfp_structures.py +++ b/target_simulator/core/sfp_structures.py @@ -208,7 +208,7 @@ class RisScenario(ctypes.Structure): ("longitude", ctypes.c_float), ("ant_nav_az", ctypes.c_float), ("ant_nav_el", ctypes.c_float), - ("spare2", ctypes.c_uint32 * 7), + ("spare2", ctypes.c_uint32 * 8), ] diff --git a/target_simulator/gui/sfp_debug_window.py b/target_simulator/gui/sfp_debug_window.py index 4559380..a42e293 100644 --- a/target_simulator/gui/sfp_debug_window.py +++ b/target_simulator/gui/sfp_debug_window.py @@ -66,6 +66,38 @@ class SfpDebugWindow(tk.Toplevel): self.ip_var = tk.StringVar(value="127.0.0.1") self.port_var = tk.StringVar(value="60002") + # Master mode names (from C++ enum ordering) — used to display readable mode + # NOTE: keep in sync with the C++ enum if it changes + self._master_mode_names = [ + "idle_master_mode", + "int_bit_master_mode", + "gm_master_mode", + "dbs_master_mode", + "rws_master_mode", + "vs_master_mode", + "acm_master_mode", + "tws_master_mode", + "sea_low_master_mode", + "sea_high_master_mode", + "gmti_master_mode", + "bcn_master_mode", + "sam_master_mode", + "ta_master_mode", + "wa_master_mode", + "stt_master_mode", + "dtt_master_mode", + "sstt_master_mode", + "acq_master_mode", + "ftt_master_mode", + "agr_master_mode", + "sar_master_mode", + "invalid_master_mode_", + "xtst_dummy_mode", + "xtst_hw_validation_mode", + "boot_master_mode", + "master_mode_id_cardinality_", + ] + # --- Connection Frame (IP / Port / Connect controls) --- conn_frame = ttk.Frame(self) conn_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=(5, 2)) @@ -611,6 +643,27 @@ class SfpDebugWindow(tk.Toplevel): import json struct = json.loads(payload.decode("utf-8")) if isinstance(payload, (bytes, bytearray)) else payload + # Debug: log keys and a short sample of the payload to help + # diagnose any label/value mismatches coming from the server. + try: + if isinstance(struct, dict): + scenario_preview = struct.get("scenario") + #self.logger.debug("RIS payload keys: %s", list(struct.keys())) + #if isinstance(scenario_preview, dict): + #self.logger.debug("RIS scenario keys: %s", list(scenario_preview.keys())) + targets_preview = struct.get("targets") + if isinstance(targets_preview, list): + sample_keys = [list(t.keys()) for t in targets_preview[:3] if isinstance(t, dict)] + #self.logger.debug("RIS targets sample keys (first 3): %s", sample_keys) + # Also put a concise message in the Raw Log widget for convenience + try: + msg = f"RIS JSON: scenario_keys={len(scenario_preview) if isinstance(scenario_preview, dict) else 0}, targets={len(targets_preview) if isinstance(targets_preview, list) else 0}" + #self._log_to_widget(msg, "DEBUG") + except Exception: + pass + except Exception: + # Never raise from debug logging + pass # scenario table (field, value) for iid in self.scenario_tree.get_children(): self.scenario_tree.delete(iid) @@ -671,6 +724,15 @@ class SfpDebugWindow(tk.Toplevel): except Exception: return str(v) + def try_float(v): + """Attempt to convert value to float; return None on failure.""" + try: + return float(v) + except Exception: + return None + + # collect rows first so we can log the exact label->value mapping + scenario_rows = [] for label, key, unit in order: if key in scenario: val = scenario.get(key) @@ -691,33 +753,38 @@ class SfpDebugWindow(tk.Toplevel): else: # simplified view: show converted value and unit adjacent to number if key in ("platform_azimuth", "true_heading"): - if isinstance(val, (int, float)): - conv = to_deg(val) + fv = try_float(val) + if fv is not None: + conv = to_deg(fv) display_val = fmt_simplified_number(conv, "°", dec_simp) else: display_val = str(val) elif key in ("ant_nav_az", "ant_nav_el"): - if isinstance(val, (int, float)): - conv = to_deg(val) + fv = try_float(val) + if fv is not None: + conv = to_deg(fv) display_val = fmt_simplified_number(conv, "°", dec_simp) else: display_val = str(val) elif key in ("vx", "vy", "vz"): - if isinstance(val, (int, float)): - conv = m_s_to_ft_s(val) + fv = try_float(val) + if fv is not None: + conv = m_s_to_ft_s(fv) display_val = fmt_simplified_number(conv, "ft/s", dec_simp) else: display_val = str(val) elif key == "baro_altitude": - if isinstance(val, (int, float)): - conv = m_to_ft(val) + fv = try_float(val) + if fv is not None: + conv = m_to_ft(fv) display_val = fmt_simplified_number(conv, "ft", dec_simp) else: display_val = str(val) elif key in ("latitude", "longitude"): - if isinstance(val, (int, float)): + fv = try_float(val) + if fv is not None: # show decimal degrees with higher precision per request - display_val = f"{float(val):.{8}f} °" + display_val = f"{fv:.{8}f} °" else: display_val = str(val) elif key == "flags": @@ -726,12 +793,41 @@ class SfpDebugWindow(tk.Toplevel): except Exception: display_val = str(val) elif key == "mode": - display_val = str(int(val)) if isinstance(val, (int, float)) else str(val) + # Map numeric mode to human-friendly short name + try: + midx = int(val) + if 0 <= midx < len(self._master_mode_names): + name = self._master_mode_names[midx] + # strip trailing parts like '_master_mode' or '_mode' or trailing underscores + short = name + for suffix in ("_master_mode", "_mode", "_master_mode_", "_"): + if short.endswith(suffix): + short = short[: -len(suffix)] + # also remove any remaining 'master' parts + short = short.replace("master", "").strip("_") + display_val = short + else: + display_val = str(midx) + except Exception: + display_val = str(val) else: display_val = str(val) - # Show label without unit; unit is appended to the value in simplified view - self.scenario_tree.insert("", tk.END, values=(f"{label}", display_val)) + # collect the pair for logging and insertion + scenario_rows.append((label, display_val)) + + # Log the label->value pairs for diagnosis (concise) + try: + preview = ", ".join(f"{l}={v}" for l, v in scenario_rows) + #self.logger.debug("Scenario label->value: %s", preview) + # also write a shorter message to Raw Log for visibility + #self._log_to_widget(f"Scenario preview: {preview}", "DEBUG") + except Exception: + pass + + # Now insert collected rows into the Treeview + for l, v in scenario_rows: + self.scenario_tree.insert("", tk.END, values=(f"{l}", v)) # targets for iid in self.ris_tree.get_children(): @@ -751,63 +847,82 @@ class SfpDebugWindow(tk.Toplevel): view_mode = self.scenario_view_mode.get() if hasattr(self, "scenario_view_mode") else "simplified" dec_simp = int(self.simplified_decimals.get() if hasattr(self, "simplified_decimals") else 4) for t in targets: - if view_mode == "raw": - # format raw with reasonable precision - try: - heading_raw = float(t.get("heading")) - heading_val = f"{heading_raw:.6f}" - except Exception: - heading_val = str(t.get("heading")) - try: - x_raw = float(t.get("x")) - y_raw = float(t.get("y")) - z_raw = float(t.get("z")) - x_val = f"{x_raw:.6f}" - y_val = f"{y_raw:.6f}" - z_val = f"{z_raw:.6f}" - except Exception: - x_val = t.get("x") - y_val = t.get("y") - z_val = t.get("z") + try: + # normalize index (accept common aliases) + idx = t.get("index") + if idx is None: + idx = t.get("idx") + if idx is None: + idx = t.get("#") - vals = ( - t.get("index"), - t.get("flags"), - heading_val, - x_val, - y_val, - z_val, - ) - else: - # simplified: converted values with units next to number + # normalize flags and format consistently + raw_flags = t.get("flags", t.get("flag", 0)) try: - heading = float(t.get("heading")) - heading_deg = heading * (180.0 / 3.141592653589793) - heading_val = f"{heading_deg:.{dec_simp}f} °" + flags_val = int(raw_flags) + flags_display = f"{flags_val} (0x{flags_val:X})" except Exception: - heading_val = str(t.get("heading")) + flags_display = str(raw_flags) + + if view_mode == "raw": + # format raw with reasonable precision using try_float + hfv = try_float(t.get("heading")) + heading_val = f"{hfv:.6f}" if hfv is not None else str(t.get("heading")) + + xfv = try_float(t.get("x")) + yfv = try_float(t.get("y")) + zfv = try_float(t.get("z")) + x_val = f"{xfv:.6f}" if xfv is not None else t.get("x") + y_val = f"{yfv:.6f}" if yfv is not None else t.get("y") + z_val = f"{zfv:.6f}" if zfv is not None else t.get("z") + + vals = ( + idx, + flags_display, + heading_val, + x_val, + y_val, + z_val, + ) + else: + # simplified: converted values with units next to number + hfv = try_float(t.get("heading")) + if hfv is not None: + heading_deg = hfv * (180.0 / 3.141592653589793) + heading_val = f"{heading_deg:.{dec_simp}f} °" + else: + heading_val = str(t.get("heading")) + + xfv = try_float(t.get("x")) + yfv = try_float(t.get("y")) + zfv = try_float(t.get("z")) + x_val = f"{xfv:.{dec_simp}f} m" if xfv is not None else str(t.get("x")) + y_val = f"{yfv:.{dec_simp}f} m" if yfv is not None else str(t.get("y")) + if zfv is not None: + z_val = f"{(zfv * 3.280839895):.{dec_simp}f} ft" + else: + z_val = str(t.get("z")) + + vals = ( + idx, + flags_display, + heading_val, + x_val, + y_val, + z_val, + ) + + # ensure we always insert a 6-tuple (Treeview expects 6 columns) + if not isinstance(vals, (list, tuple)) or len(vals) != 6: + # fallback: make a safe 6-element tuple + vals = (idx, flags_display, "", "", "", "") + + self.ris_tree.insert("", tk.END, values=vals) + except Exception as _e: + # on malformed target entries, insert a visible placeholder try: - x = float(t.get("x")) - y = float(t.get("y")) - z_m = float(t.get("z")) - x_val = f"{x:.{dec_simp}f} m" - y_val = f"{y:.{dec_simp}f} m" - z_val = f"{(z_m * 3.280839895):.{dec_simp}f} ft" + self.ris_tree.insert("", tk.END, values=(None, None, str(t.get("heading")), str(t.get("x")), str(t.get("y")), str(t.get("z")))) except Exception: - x_val = str(t.get("x")) - y_val = str(t.get("y")) - z_val = str(t.get("z")) - - vals = ( - t.get("index"), - t.get("flags"), - heading_val, - x_val, - y_val, - z_val, - ) - - self.ris_tree.insert("", tk.END, values=vals) + pass except Exception: # ignore malformed JSON for now pass diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..c9f51ea --- /dev/null +++ b/todo.md @@ -0,0 +1,17 @@ +code da fare + +leggere le informazioni dell'antenna del messaggio di stato +leggere la flag per capire se il target è attivo +scomporre il campo flag in bit per avere le informazioni dello stato del target (attivo, tracable) +fare simulazione con moviumento dell'aereo letto da protocollo +visualizzare informaizoni dinamiche dell'areo durante la simulazione +sull'edito, se seleziono una manovra, vederla colorata di un altro colore sulla preview per capire cosa sto toccando. +aggiungere visualizzazione nell'editor su ris_status in questo modo: +1) dati raw, dati come vengono spediti, senza conversioni +2) dati convertiti secondo intefaccia gui del mission computer +3) dati dei target in range/elevation + +la visualizzazione ppi in simulazione se tiene conto della rotazione del ptazimuth dovrebbe ruotare in modo che il cono di scansione dell'antenna si muove +di conseguenza. Immagino che la mappa ppi sia sempre diretta a nord, quindi quando io con l'aereo vado a nord tutto torna +se invece cambio direzione dell'aereo la mappa ruota e quindi ruotano anche le label attorno in modo che siano sempre riferite al muso dell'aereo. +Quindi dovremmo inserire una nuova legenda oltre a quella attuale che indichi che il nord è sempra. per ricordare all'utente che la ppi è verso l'alto. \ No newline at end of file