aggiunto il rate dei pacchetti inviati dal server
This commit is contained in:
parent
a2e786b6e3
commit
4995c0743d
96
.github/copilot-instructions.md
vendored
96
.github/copilot-instructions.md
vendored
@ -1,52 +1,64 @@
|
|||||||
# Copilot Instructions for Target Simulator
|
## Copilot / AI agent instructions — Target Simulator (concise)
|
||||||
|
|
||||||
## Project Overview
|
This file contains the minimal, repository-specific guidance an AI coding agent needs to be productive.
|
||||||
- **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
|
|
||||||
|
|
||||||
## Architecture & Data Flow
|
1) Big picture
|
||||||
- **Simulation** runs in a background thread (`SimulationEngine`) and updates target states/scenarios.
|
- Type: Python desktop app (Tkinter) that simulates radar targets and communicates with hardware/emulators via SFP/TFTP/Serial.
|
||||||
- **Communication**: Supports both TFTP and serial, abstracted via `CommunicatorInterface` and implemented in `serial_communicator.py` and `tftp_communicator.py`.
|
- Major areas:
|
||||||
- **Configuration**: Settings and scenarios are stored in `settings.json` (read/write via `ConfigManager`).
|
- `target_simulator/core/` — simulation, command builders, communicator interfaces (CommunicatorInterface is the contract).
|
||||||
- **Logging**: Centralized, routed to both console and GUI (`logger.py`). GUI log widget uses colored levels.
|
- `target_simulator/gui/` — Tkinter windows; `MainView` is the app host and coordinates simulation + communicators.
|
||||||
- **Scenarios**: Defined in `settings.json` under `scenarios`, loaded and managed via GUI and core logic.
|
- `target_simulator/utils/` — `ConfigManager`, logging helpers (`utils/logger.py`), CSV trace helpers.
|
||||||
|
|
||||||
## Developer Workflows
|
2) Key workflows & commands (PowerShell)
|
||||||
- **Run app**: `python -m target_simulator` (from project root)
|
- Run app (development):
|
||||||
- **Debug**: Use Python debugger on `__main__.py` or GUI modules
|
$env:PYTHONPATH='C:\src\____GitProjects\target_simulator'; python -m target_simulator
|
||||||
- **Settings**: Edit `settings.json` for global config, connection, and scenario data
|
- Open REPL with project path:
|
||||||
- **Logging**: Adjust `LOGGING_CONFIG` in `config.py` for format/colors
|
$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
|
3) Project-specific patterns & conventions
|
||||||
- **Absolute imports** are used throughout (e.g., `from target_simulator.core...`).
|
- Absolute imports are used throughout; `__main__.py` inserts the project root into `sys.path` for convenience.
|
||||||
- **PEP8** style, English for code/comments/docstrings
|
- Persistence: `ConfigManager` stores general settings in `settings.json` and scenarios in `scenarios.json` (atomic write + rotating backups `.bak1`, `.bak2`).
|
||||||
- **One statement per line**; concise, essential comments only
|
- 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)`.
|
||||||
- **Modularization**: If a file grows >1000 lines, split by topic into new modules
|
- 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.
|
||||||
- **Scenario/Settings**: All persistent config is managed via `ConfigManager` and stored in `settings.json`
|
- 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).
|
||||||
- **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
|
|
||||||
|
|
||||||
## Integration Points
|
4) Important implementation details to preserve
|
||||||
- **External dependencies**: Tkinter, threading, TFTP/serial (no third-party packages required)
|
- 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.
|
||||||
- **Temp files**: Written to `Temp/` for live updates and scenario init logs
|
- Scenarios are saved to a separate `scenarios.json`; `ConfigManager` will avoid overwriting it with empty data and performs atomic replace + rotating backups.
|
||||||
- **C++ code**: `BupTFTP.cpp/h` present but not integrated with Python app
|
- `MainView._setup_communicator` shows how new communicator types are wired (update this method when adding a protocol).
|
||||||
|
|
||||||
## Examples
|
5) Integration points & external artifacts
|
||||||
- To add a new communication protocol, implement `CommunicatorInterface` in `core/` and update GUI logic in `main_view.py`.
|
- Native libs: Tkinter (GUI). No heavy third-party dependencies are required for core features.
|
||||||
- To add a new scenario, update `settings.json` and use `ConfigManager` methods.
|
- Optional C++ helpers exist in `cpp/` (e.g., `BupTFTP.cpp`) but are not integrated by default.
|
||||||
- To extend logging, add new handlers in `logger.py` and update `config.py` for color/format.
|
- Temporary CSV logs and traces live in `Temp/` (filenames configured in `settings.json` `debug` block).
|
||||||
|
|
||||||
## References
|
6) Files to inspect for common tasks (quick links)
|
||||||
- Main entry: `target_simulator/__main__.py`
|
- App start: `target_simulator/__main__.py`
|
||||||
- Simulation: `core/simulation_engine.py`
|
- Main UI and lifecycle: `target_simulator/gui/main_view.py`
|
||||||
- GUI: `gui/main_view.py`, other `gui/` modules
|
- Simulation loop and sending logic: `target_simulator/core/simulation_engine.py`
|
||||||
- Config: `utils/config_manager.py`, `settings.json`
|
- Command builders: `target_simulator/core/command_builder.py`
|
||||||
- Logging: `utils/logger.py`, `config.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.
|
||||||
@ -46,6 +46,13 @@ class SimulationStateHub:
|
|||||||
# Timestamps (monotonic) of recent "real" events for rate computation
|
# Timestamps (monotonic) of recent "real" events for rate computation
|
||||||
# Keep a bounded deque to avoid unbounded memory growth.
|
# Keep a bounded deque to avoid unbounded memory growth.
|
||||||
self._real_event_timestamps = collections.deque(maxlen=10000)
|
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
|
# Summary throttle to avoid flooding logs while still providing throughput info
|
||||||
self._last_real_summary_time = time.monotonic()
|
self._last_real_summary_time = time.monotonic()
|
||||||
self._real_summary_interval_s = 1.0
|
self._real_summary_interval_s = 1.0
|
||||||
@ -175,6 +182,38 @@ class SimulationStateHub:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return 0.0
|
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(
|
def set_real_heading(
|
||||||
self, target_id: int, heading_deg: float, raw_value: float = None
|
self, target_id: int, heading_deg: float, raw_value: float = None
|
||||||
):
|
):
|
||||||
|
|||||||
@ -1056,14 +1056,19 @@ class MainView(tk.Tk):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
real_rate = 0.0
|
real_rate = 0.0
|
||||||
|
packet_rate = 0.0
|
||||||
ppi_rate = 0.0
|
ppi_rate = 0.0
|
||||||
try:
|
try:
|
||||||
if self.simulation_hub and hasattr(
|
if self.simulation_hub and hasattr(
|
||||||
self.simulation_hub, "get_real_rate"
|
self.simulation_hub, "get_real_rate"
|
||||||
):
|
):
|
||||||
real_rate = float(self.simulation_hub.get_real_rate(1.0))
|
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:
|
except Exception:
|
||||||
real_rate = 0.0
|
real_rate = 0.0
|
||||||
|
packet_rate = 0.0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if (
|
if (
|
||||||
@ -1077,9 +1082,17 @@ class MainView(tk.Tk):
|
|||||||
|
|
||||||
if getattr(self, "rate_status_var", None) is not None:
|
if getattr(self, "rate_status_var", None) is not None:
|
||||||
try:
|
try:
|
||||||
self.rate_status_var.set(
|
# Show both packet-level rate and per-target event rate so
|
||||||
f"real in: {real_rate:.1f} ev/s | ppi upd: {ppi_rate:.1f} upd/s"
|
# 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"
|
||||||
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@ -155,6 +155,15 @@ class DebugPayloadRouter:
|
|||||||
# to ensure simulated and real data can be correlated.
|
# to ensure simulated and real data can be correlated.
|
||||||
reception_timestamp = time.monotonic()
|
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
|
# Add real states for active targets
|
||||||
for target in real_targets:
|
for target in real_targets:
|
||||||
state_tuple = (
|
state_tuple = (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user