aggiunto il rate dei pacchetti inviati dal server

This commit is contained in:
VALLONGOL 2025-10-29 12:27:37 +01:00
parent a2e786b6e3
commit 4995c0743d
4 changed files with 118 additions and 45 deletions

View File

@ -1,52 +1,64 @@
# Copilot Instructions for Target Simulator
## Copilot / AI agent instructions — Target Simulator (concise)
## Project Overview
- **Target Simulator** is a Python desktop application (Tkinter GUI) for simulating radar targets and scenarios, with communication via TFTP and serial protocols.
- Main entry: `target_simulator/__main__.py` launches the GUI (`MainView`).
- Core logic is split into:
- `core/`: simulation engine, communication interfaces, scenario models
- `gui/`: Tkinter windows, frames, and widgets
- `utils/`: logging, config management, TFTP client
This file contains the minimal, repository-specific guidance an AI coding agent needs to be productive.
## Architecture & Data Flow
- **Simulation** runs in a background thread (`SimulationEngine`) and updates target states/scenarios.
- **Communication**: Supports both TFTP and serial, abstracted via `CommunicatorInterface` and implemented in `serial_communicator.py` and `tftp_communicator.py`.
- **Configuration**: Settings and scenarios are stored in `settings.json` (read/write via `ConfigManager`).
- **Logging**: Centralized, routed to both console and GUI (`logger.py`). GUI log widget uses colored levels.
- **Scenarios**: Defined in `settings.json` under `scenarios`, loaded and managed via GUI and core logic.
1) Big picture
- Type: Python desktop app (Tkinter) that simulates radar targets and communicates with hardware/emulators via SFP/TFTP/Serial.
- Major areas:
- `target_simulator/core/` — simulation, command builders, communicator interfaces (CommunicatorInterface is the contract).
- `target_simulator/gui/` — Tkinter windows; `MainView` is the app host and coordinates simulation + communicators.
- `target_simulator/utils/``ConfigManager`, logging helpers (`utils/logger.py`), CSV trace helpers.
## Developer Workflows
- **Run app**: `python -m target_simulator` (from project root)
- **Debug**: Use Python debugger on `__main__.py` or GUI modules
- **Settings**: Edit `settings.json` for global config, connection, and scenario data
- **Logging**: Adjust `LOGGING_CONFIG` in `config.py` for format/colors
2) Key workflows & commands (PowerShell)
- Run app (development):
$env:PYTHONPATH='C:\src\____GitProjects\target_simulator'; python -m target_simulator
- Open REPL with project path:
$env:PYTHONPATH='C:\src\____GitProjects\target_simulator'; python
- Run tests (coverage task available): use the workspace task labeled "pytest coverage report" or run directly:
$env:PYTHONPATH='C:\src\____GitProjects\target_simulator'; pytest --cov=target_simulator.core.models --cov-report=term-missing
## Patterns & Conventions
- **Absolute imports** are used throughout (e.g., `from target_simulator.core...`).
- **PEP8** style, English for code/comments/docstrings
- **One statement per line**; concise, essential comments only
- **Modularization**: If a file grows >1000 lines, split by topic into new modules
- **Scenario/Settings**: All persistent config is managed via `ConfigManager` and stored in `settings.json`
- **GUI**: All Tkinter windows/frames are in `gui/`, main window is `MainView`
- **Logging**: Use `get_logger`, `setup_basic_logging`, and `add_tkinter_handler` for consistent logging
3) Project-specific patterns & conventions
- Absolute imports are used throughout; `__main__.py` inserts the project root into `sys.path` for convenience.
- Persistence: `ConfigManager` stores general settings in `settings.json` and scenarios in `scenarios.json` (atomic write + rotating backups `.bak1`, `.bak2`).
- Logging: central queue-based logging that forwards to console/file/GUI. Use `setup_basic_logging(app, LOGGING_CONFIG)` and `add_tkinter_handler(widget, LOGGING_CONFIG)`; temporary increases via `temporary_log_level(logger, level)`.
- Simulation: `SimulationEngine` runs as a daemon thread and emits GUI updates via a Queue; do not change update semantics lightly — runtime sends `tgtset` without qualifiers while debug GUI may include qualifiers.
- Communicator contract: implement `CommunicatorInterface` (methods like `connect(config)->bool`, `disconnect()`, `send_commands(list)`, property `is_open`). `SFP` communicator supports deferred connect and an optional `_use_json_protocol` flag (checked by engine).
## Integration Points
- **External dependencies**: Tkinter, threading, TFTP/serial (no third-party packages required)
- **Temp files**: Written to `Temp/` for live updates and scenario init logs
- **C++ code**: `BupTFTP.cpp/h` present but not integrated with Python app
4) Important implementation details to preserve
- Debug vs runtime sending: the SFP debug window intentionally sends `tgtset` with state qualifiers (Active/Traceable/Restart) while the live `SimulationEngine` uses `tgtset` without qualifiers to avoid changing lifecycle flags. See `gui/sfp_debug_window.py` and `core/command_builder.py` for the builders.
- Scenarios are saved to a separate `scenarios.json`; `ConfigManager` will avoid overwriting it with empty data and performs atomic replace + rotating backups.
- `MainView._setup_communicator` shows how new communicator types are wired (update this method when adding a protocol).
## Examples
- To add a new communication protocol, implement `CommunicatorInterface` in `core/` and update GUI logic in `main_view.py`.
- To add a new scenario, update `settings.json` and use `ConfigManager` methods.
- To extend logging, add new handlers in `logger.py` and update `config.py` for color/format.
5) Integration points & external artifacts
- Native libs: Tkinter (GUI). No heavy third-party dependencies are required for core features.
- Optional C++ helpers exist in `cpp/` (e.g., `BupTFTP.cpp`) but are not integrated by default.
- Temporary CSV logs and traces live in `Temp/` (filenames configured in `settings.json` `debug` block).
## References
- Main entry: `target_simulator/__main__.py`
- Simulation: `core/simulation_engine.py`
- GUI: `gui/main_view.py`, other `gui/` modules
- Config: `utils/config_manager.py`, `settings.json`
- Logging: `utils/logger.py`, `config.py`
6) Files to inspect for common tasks (quick links)
- App start: `target_simulator/__main__.py`
- Main UI and lifecycle: `target_simulator/gui/main_view.py`
- Simulation loop and sending logic: `target_simulator/core/simulation_engine.py`
- Command builders: `target_simulator/core/command_builder.py`
- Communicators: `target_simulator/core/*.py` (look for `SFP`, `Serial`, `TFTP` implementations)
- Persistence: `target_simulator/utils/config_manager.py` and `settings.json` / `scenarios.json`
- Logging helpers: `target_simulator/utils/logger.py` and `logger_prefs.json`
7) Small examples (copyable snippets)
- Enable module debug at runtime (REPL):
import logging; logging.getLogger('target_simulator.gui.sfp_debug_window').setLevel(logging.DEBUG)
- Start app (PowerShell):
$env:PYTHONPATH='C:\src\____GitProjects\target_simulator'; python -m target_simulator
8) When changing behavior
- If you change how commands are formatted (e.g., `build_tgtset_*`), update both the SimulationEngine logic and the SFP debug sender to keep debug vs runtime semantics clear.
- If you add a new persistent key, prefer storing it under `general` in `settings.json` via `ConfigManager.save_general_settings()`.
9) What the agent should *not* do automatically
- Do not change scenario storage semantics (moving scenarios back into `settings.json`), since `ConfigManager` intentionally separates scenarios and performs backups.
10) If more detail is needed
- Ask which area to expand (GUI wiring, communicator API, command builders, or logging). Provide the specific file and a brief goal and the agent will produce patches and tests.
---
If any section is unclear or missing, please provide feedback to improve these instructions.
If anything here is unclear or you want additional examples (unit tests, small refactors, or a runtime checklist), tell me which sections to expand and I will iterate.

View File

@ -46,6 +46,13 @@ class SimulationStateHub:
# Timestamps (monotonic) of recent "real" events for rate computation
# Keep a bounded deque to avoid unbounded memory growth.
self._real_event_timestamps = collections.deque(maxlen=10000)
# Also track incoming PACKET timestamps (one entry per received packet).
# Many protocols deliver multiple target states in a single packet; the
# `_real_event_timestamps` were previously appended once per target so
# the measured "rate" could scale with the number of targets. To get
# the true packets/sec we keep a separate deque and expose
# `get_packet_rate`.
self._real_packet_timestamps = collections.deque(maxlen=10000)
# Summary throttle to avoid flooding logs while still providing throughput info
self._last_real_summary_time = time.monotonic()
self._real_summary_interval_s = 1.0
@ -175,6 +182,38 @@ class SimulationStateHub:
except Exception:
return 0.0
def add_real_packet(self, timestamp: Optional[float] = None):
"""
Record the arrival of a packet (containing potentially many target updates).
Args:
timestamp: optional monotonic timestamp to use (defaults to now).
"""
try:
ts = float(timestamp) if timestamp is not None else time.monotonic()
self._real_packet_timestamps.append(ts)
except Exception:
# silently ignore errors -- non-critical
pass
def get_packet_rate(self, window_seconds: float = 1.0) -> float:
"""
Returns an approximate packets/sec rate based on recorded packet arrival
timestamps over the last `window_seconds`.
"""
try:
now = time.monotonic()
cutoff = now - float(window_seconds)
count = 0
for ts in reversed(self._real_packet_timestamps):
if ts >= cutoff:
count += 1
else:
break
return count / float(window_seconds) if window_seconds > 0 else float(count)
except Exception:
return 0.0
def set_real_heading(
self, target_id: int, heading_deg: float, raw_value: float = None
):

View File

@ -1056,14 +1056,19 @@ class MainView(tk.Tk):
"""
try:
real_rate = 0.0
packet_rate = 0.0
ppi_rate = 0.0
try:
if self.simulation_hub and hasattr(
self.simulation_hub, "get_real_rate"
):
real_rate = float(self.simulation_hub.get_real_rate(1.0))
# Prefer packet rate if available (packets/sec vs events/sec)
if self.simulation_hub and hasattr(self.simulation_hub, "get_packet_rate"):
packet_rate = float(self.simulation_hub.get_packet_rate(1.0))
except Exception:
real_rate = 0.0
packet_rate = 0.0
try:
if (
@ -1077,6 +1082,14 @@ class MainView(tk.Tk):
if getattr(self, "rate_status_var", None) is not None:
try:
# Show both packet-level rate and per-target event rate so
# users can distinguish network throughput (packets/sec)
# from per-target updates (events/sec).
if packet_rate > 0.0:
self.rate_status_var.set(
f"pkt in: {packet_rate:.1f} pkt/s | ev in: {real_rate:.1f} ev/s | ppi upd: {ppi_rate:.1f} upd/s"
)
else:
self.rate_status_var.set(
f"real in: {real_rate:.1f} ev/s | ppi upd: {ppi_rate:.1f} upd/s"
)

View File

@ -155,6 +155,15 @@ class DebugPayloadRouter:
# to ensure simulated and real data can be correlated.
reception_timestamp = time.monotonic()
# Record a single packet-level arrival timestamp so callers can
# measure packets/sec (instead of per-target events/sec).
try:
if hasattr(self._hub, "add_real_packet"):
self._hub.add_real_packet(reception_timestamp)
except Exception:
# Non-fatal: continue even if packet recording fails
pass
# Add real states for active targets
for target in real_targets:
state_tuple = (