S1005403_RisCC/target_simulator/core/models.py
2025-10-02 14:37:38 +02:00

155 lines
4.8 KiB
Python

# target_simulator/core/models.py
"""
Defines the core data models for the application, including Target and Scenario.
This module is responsible for the business logic representation of simulation
entities, completely independent of the GUI or communication layers.
"""
from __future__ import annotations
import json
from dataclasses import dataclass, asdict
from typing import Dict, List, Optional
# Constants for target limitations, derived from the user manual.
MIN_TARGET_ID = 0
MAX_TARGET_ID = 15
@dataclass
class Target:
"""
Represents a single target with its state and kinematic properties.
Attributes reflect the parameters used in the MMI commands like 'tgtinit'.
"""
target_id: int
range_nm: float
azimuth_deg: float
velocity_fps: float
heading_deg: float
altitude_ft: float
active: bool = True
traceable: bool = True
def __post_init__(self):
"""Validate target_id after initialization."""
if not (MIN_TARGET_ID <= self.target_id <= MAX_TARGET_ID):
raise ValueError(
f"Target ID must be between {MIN_TARGET_ID} and {MAX_TARGET_ID}."
)
def to_dict(self) -> Dict:
"""Serializes the Target object to a dictionary."""
return asdict(self)
class Scenario:
@classmethod
def from_dict(cls, data: Dict) -> "Scenario":
"""
Crea uno Scenario da un dizionario.
"""
name = data.get("name", "Loaded Scenario")
scenario = cls(name=name)
for target_data in data.get("targets", []):
try:
target = Target(**target_data)
scenario.add_target(target)
except (TypeError, ValueError) as e:
print(f"Skipping invalid target data: {target_data}. Error: {e}")
return scenario
def to_dict(self) -> Dict:
"""
Serializza lo scenario in un dizionario.
"""
return {
"name": self.name,
"targets": [target.to_dict() for target in self.get_all_targets()]
}
"""
Manages a collection of targets that constitute a simulation scenario.
Provides functionality to add, remove, and retrieve targets, as well as
saving and loading the entire scenario to/from a file.
"""
def __init__(self, name: str = "Untitled Scenario"):
self.name = name
self.targets: Dict[int, Target] = {}
def add_target(self, target: Target) -> None:
"""
Adds a target to the scenario. Overwrites if ID already exists.
Args:
target: The Target object to add.
"""
self.targets[target.target_id] = target
def get_target(self, target_id: int) -> Optional[Target]:
"""
Retrieves a target by its ID.
Args:
target_id: The ID of the target to retrieve.
Returns:
The Target object if found, otherwise None.
"""
return self.targets.get(target_id)
def remove_target(self, target_id: int) -> None:
"""
Removes a target from the scenario by its ID.
Args:
target_id: The ID of the target to remove.
"""
if target_id in self.targets:
del self.targets[target_id]
def get_all_targets(self) -> List[Target]:
"""Returns a list of all targets in the scenario."""
return list(self.targets.values())
def save_to_file(self, filepath: str) -> None:
"""
Saves the current scenario to a JSON file.
Args:
filepath: The path to the file where the scenario will be saved.
"""
scenario_data = {
"name": self.name,
"targets": [target.to_dict() for target in self.get_all_targets()]
}
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(scenario_data, f, indent=4)
@classmethod
def load_from_file(cls, filepath: str) -> Scenario:
"""
Loads a scenario from a JSON file and returns a new Scenario instance.
Args:
filepath: The path to the scenario file to load.
Returns:
A new Scenario object populated with data from the file.
"""
with open(filepath, 'r', encoding='utf-8') as f:
scenario_data = json.load(f)
scenario_name = scenario_data.get("name", "Loaded Scenario")
new_scenario = cls(name=scenario_name)
for target_data in scenario_data.get("targets", []):
try:
target = Target(**target_data)
new_scenario.add_target(target)
except (TypeError, ValueError) as e:
# Log or print a warning about invalid target data in the file
print(f"Skipping invalid target data: {target_data}. Error: {e}")
return new_scenario