< Summary

Class:SimManager
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Managers/SimManager.cs
Covered lines:0
Uncovered lines:244
Coverable lines:244
Total lines:383
Line coverage:0% (0 of 244)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:51
Method coverage:0% (0 of 51)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
SimManager()0%2100%
SimManager()0%2100%
StartSimulation()0%6200%
EndSimulation()0%72800%
PostSimulation()0%6200%
PauseSimulation()0%2100%
ResumeSimulation()0%2100%
QuitSimulation()0%2100%
ResetAndStartSimulation()0%2100%
LoadNewSimulationConfig(...)0%12300%
CreateInterceptor(...)0%42600%
CreateThreat(...)0%42600%
CreateDummyAgent(...)0%2100%
DestroyDummyAgent(...)0%2100%
CreateAgent(...)0%6200%
CreateRandomAgent(...)0%2100%
Awake()0%12300%
Start()0%6200%
FixedUpdate()0%20400%
LateUpdate()0%6200%
InitializeLaunchers()0%20400%
InitializeThreats()0%20400%
LoadSimConfigs(...)0%6200%
SetGameSpeed()0%6200%
SetTimeScale(...)0%2100%
RegisterThreatMiss(...)0%2100%
RegisterThreatTerminated(...)0%2100%
ShouldEndSimulation()0%42600%

File(s)

/github/workspace/Assets/Scripts/Managers/SimManager.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using UnityEngine;
 5
 6// The simulation manager handles the creation of all agents.
 7// It implements the singleton pattern to ensure that only one instance exists.
 8public class SimManager : MonoBehaviour {
 9  // Simulation events.
 10  public delegate void SimulationEventHandler();
 11  public event SimulationEventHandler OnSimulationStarted;
 12  public event SimulationEventHandler OnSimulationEnded;
 13
 14  // Interceptor events.
 15  public delegate void NewInterceptorEventHandler(IInterceptor interceptor);
 16  public event NewInterceptorEventHandler OnNewInterceptor;
 17  public delegate void NewLauncherEventHandler(IInterceptor launcher);
 18  public event NewLauncherEventHandler OnNewLauncher;
 19
 20  // Threat events.
 21  public delegate void NewThreatEventHandler(IThreat threat);
 22  public event NewThreatEventHandler OnNewThreat;
 23
 24  // Default simulation configuration file.
 25  private const string _defaultSimulationConfigFile = "7_quadcopters.pbtxt";
 26
 27  // Default simulator configuration file.
 28  private const string _defaultSimulatorConfigFile = "simulator.pbtxt";
 29
 30  // Map from the agent type to the prefab class name.
 31  // The prefab class must exist in the Resources/Prefabs directory.
 032  private static readonly Dictionary<Configs.AgentType, string> _agentTypePrefabMap = new() {
 33    { Configs.AgentType.Vessel, "Vessel" },
 34    { Configs.AgentType.ShoreBattery, "ShoreBattery" },
 35    { Configs.AgentType.CarrierInterceptor, "CarrierInterceptor" },
 36    { Configs.AgentType.MissileInterceptor, "MissileInterceptor" },
 37    { Configs.AgentType.FixedWingThreat, "FixedWingThreat" },
 38    { Configs.AgentType.RotaryWingThreat, "RotaryWingThreat" },
 39  };
 40
 041  public static SimManager Instance { get; private set; }
 42
 43  // Simulation configuration.
 044  public Configs.SimulationConfig SimulationConfig { get; set; }
 45
 46  // Simulator configuration.
 047  public Configs.SimulatorConfig SimulatorConfig { get; set; }
 48
 49  // Simulation time.
 050  public float ElapsedTime { get; private set; } = 0f;
 051  public bool IsPaused { get; private set; } = false;
 52
 53  // If true, the simulation is currently running.
 054  public bool IsRunning { get; private set; } = false;
 55
 56  // If true, automatically restart the simulation.
 057  public bool AutoRestartOnEnd { get; set; } = true;
 58
 059  public string Timestamp { get; private set; } = "";
 60
 61  // Lists of all agents in the simulation.
 062  private List<IAgent> _interceptors = new List<IAgent>();
 063  private List<IAgent> _threats = new List<IAgent>();
 064  private List<IAgent> _dummyAgents = new List<IAgent>();
 65
 066  public IReadOnlyList<IAgent> Interceptors => _interceptors.AsReadOnly();
 067  public IReadOnlyList<IAgent> Threats => _threats.AsReadOnly();
 068  public IReadOnlyList<IAgent> Agents => Interceptors.Concat(Threats).ToList().AsReadOnly();
 69
 70  // Interceptor and threat costs.
 071  public float CostLaunchedInterceptors { get; private set; } = 0f;
 072  public float CostDestroyedThreats { get; private set; } = 0f;
 73
 74  // Track the number of interceptors and threats spawned and terminated.
 075  private int _numInterceptorsSpawned = 0;
 076  private int _numThreatsSpawned = 0;
 077  private int _numThreatsTerminated = 0;
 78
 079  public void StartSimulation() {
 080    IsRunning = true;
 081    OnSimulationStarted?.Invoke();
 082    Debug.Log("Simulation started.");
 083    UIManager.Instance.LogActionMessage("[SIM] Simulation started.");
 84
 085    InitializeLaunchers();
 086    InitializeThreats();
 087  }
 88
 089  public void EndSimulation() {
 090    IsRunning = false;
 091    OnSimulationEnded?.Invoke();
 092    Debug.Log("Simulation ended.");
 093    UIManager.Instance.LogActionMessage("[SIM] Simulation ended.");
 94
 95    // Clear existing interceptors and threats.
 096    foreach (var interceptor in _interceptors) {
 097      if (interceptor as MonoBehaviour != null) {
 098        Destroy(interceptor.gameObject);
 099      }
 0100    }
 0101    foreach (var threat in _threats) {
 0102      if (threat as MonoBehaviour != null) {
 0103        Destroy(threat.gameObject);
 0104      }
 0105    }
 0106    foreach (var dummyAgent in _dummyAgents) {
 0107      if (dummyAgent as MonoBehaviour != null) {
 0108        Destroy(dummyAgent.gameObject);
 0109      }
 0110    }
 111
 0112    _interceptors.Clear();
 0113    _threats.Clear();
 0114    _dummyAgents.Clear();
 0115  }
 116
 0117  public void PostSimulation() {
 0118    if (AutoRestartOnEnd) {
 0119      ResetAndStartSimulation();
 0120    }
 0121  }
 122
 0123  public void PauseSimulation() {
 0124    IsPaused = true;
 0125    SetGameSpeed();
 0126  }
 127
 0128  public void ResumeSimulation() {
 0129    IsPaused = false;
 0130    SetGameSpeed();
 0131  }
 132
 0133  public void QuitSimulation() {
 0134    Application.Quit();
 0135  }
 136
 0137  public void ResetAndStartSimulation() {
 0138    ElapsedTime = 0f;
 0139    CostLaunchedInterceptors = 0f;
 0140    CostDestroyedThreats = 0f;
 141
 0142    _numInterceptorsSpawned = 0;
 0143    _numThreatsSpawned = 0;
 0144    _numThreatsTerminated = 0;
 145
 0146    StartSimulation();
 0147  }
 148
 0149  public void LoadNewSimulationConfig(string simulationConfigFile) {
 0150    if (IsRunning) {
 0151      EndSimulation();
 0152    }
 0153    LoadSimConfigs(simulationConfigFile);
 0154    SetGameSpeed();
 155
 0156    if (SimulationConfig != null) {
 0157      Debug.Log($"Loaded new simulation configuration: {simulationConfigFile}.");
 0158      ResetAndStartSimulation();
 0159    } else {
 0160      Debug.LogError($"Failed to load simulation configuration: {simulationConfigFile}.");
 0161    }
 0162  }
 163
 164  // Create an interceptor based on the provided configuration.
 0165  public IInterceptor CreateInterceptor(Configs.AgentConfig config, Simulation.State initialState) {
 0166    if (config == null) {
 0167      return null;
 168    }
 169
 170    // Load the static configuration.
 0171    Configs.StaticConfig staticConfig = ConfigLoader.LoadStaticConfig(config.ConfigFile);
 0172    if (staticConfig == null) {
 0173      return null;
 174    }
 175
 0176    GameObject interceptorObject = null;
 0177    if (_agentTypePrefabMap.TryGetValue(staticConfig.AgentType, out var prefab)) {
 0178      interceptorObject = CreateAgent(config, initialState, prefab);
 0179    }
 0180    if (interceptorObject == null) {
 0181      return null;
 182    }
 183
 0184    IInterceptor interceptor = interceptorObject.GetComponent<IInterceptor>();
 0185    interceptor.HierarchicalAgent = new HierarchicalAgent(interceptor);
 0186    interceptor.StaticConfig = staticConfig;
 0187    interceptor.OnTerminated += (interceptor) => _interceptors.Remove(interceptor);
 0188    _interceptors.Add(interceptor);
 0189    ++_numInterceptorsSpawned;
 190
 191    // Assign a unique and simple ID.
 0192    interceptorObject.name = $"{staticConfig.Name}_Interceptor_{_numInterceptorsSpawned}";
 193
 194    // Add the interceptor's unit cost to the total cost.
 0195    CostLaunchedInterceptors += staticConfig.Cost;
 196
 0197    OnNewInterceptor?.Invoke(interceptor);
 0198    return interceptor;
 0199  }
 200
 201  // Create a threat based on the provided configuration.
 202  // Returns the created threat instance, or null if creation failed.
 0203  public IThreat CreateThreat(Configs.AgentConfig config) {
 0204    if (config == null) {
 0205      return null;
 206    }
 207
 208    // Load the static configuration.
 0209    Configs.StaticConfig staticConfig = ConfigLoader.LoadStaticConfig(config.ConfigFile);
 0210    if (staticConfig == null) {
 0211      return null;
 212    }
 213
 0214    GameObject threatObject = null;
 0215    if (_agentTypePrefabMap.TryGetValue(staticConfig.AgentType, out var prefab)) {
 0216      threatObject = CreateRandomAgent(config, prefab);
 0217    }
 0218    if (threatObject == null) {
 0219      return null;
 220    }
 221
 0222    IThreat threat = threatObject.GetComponent<IThreat>();
 0223    threat.HierarchicalAgent = new HierarchicalAgent(threat);
 0224    threat.StaticConfig = staticConfig;
 0225    threat.OnMiss += RegisterThreatMiss;
 0226    threat.OnTerminated += RegisterThreatTerminated;
 0227    _threats.Add(threat);
 0228    ++_numThreatsSpawned;
 229
 230    // Assign a unique name.
 0231    threatObject.name = $"{staticConfig.Name}_Threat_{_numThreatsSpawned}";
 232
 0233    OnNewThreat?.Invoke(threat);
 0234    return threat;
 0235  }
 236
 0237  public IAgent CreateDummyAgent(in Vector3 position, in Vector3 velocity) {
 0238    GameObject dummyAgentPrefab = Resources.Load<GameObject>($"Prefabs/DummyAgent");
 0239    GameObject dummyAgentObject = Instantiate(dummyAgentPrefab, position, Quaternion.identity);
 0240    var dummyAgent = dummyAgentObject.GetComponent<IAgent>();
 0241    dummyAgent.Velocity = velocity;
 0242    _dummyAgents.Add(dummyAgent);
 0243    dummyAgent.OnTerminated += (agent) => _dummyAgents.Remove(agent);
 0244    return dummyAgent;
 0245  }
 246
 0247  public void DestroyDummyAgent(IAgent dummyAgent) {
 0248    dummyAgent.Terminate();
 0249  }
 250
 251  // Create an agent based on the provided configuration and prefab name.
 252  private GameObject CreateAgent(Configs.AgentConfig config, Simulation.State initialState,
 0253                                 string prefabName) {
 0254    GameObject prefab = Resources.Load<GameObject>($"Prefabs/{prefabName}");
 0255    if (prefab == null) {
 0256      Debug.LogError($"Prefab {prefabName} not found in Resources/Prefabs directory.");
 0257      return null;
 258    }
 259
 0260    GameObject agentObject =
 261        Instantiate(prefab, Coordinates3.FromProto(initialState.Position), Quaternion.identity);
 0262    IAgent agent = agentObject.GetComponent<IAgent>();
 0263    agent.AgentConfig = config;
 0264    Vector3 velocity = Coordinates3.FromProto(initialState.Velocity);
 0265    agent.Velocity = velocity;
 266
 267    // Set the rotation to face the initial velocity.
 0268    Quaternion targetRotation = Quaternion.LookRotation(velocity, Vector3.up);
 0269    agentObject.transform.rotation = targetRotation;
 0270    return agentObject;
 0271  }
 272
 273  // Create a random agent based on the provided configuration and prefab name.
 0274  private GameObject CreateRandomAgent(Configs.AgentConfig config, string prefabName) {
 275    // Randomize the position and the velocity.
 0276    Vector3 positionNoise = Utilities.GenerateRandomNoise(config.StandardDeviation.Position);
 0277    Vector3 velocityNoise = Utilities.GenerateRandomNoise(config.StandardDeviation.Velocity);
 0278    var initialState = new Simulation.State() {
 279      Position = Coordinates3.ToProto(Coordinates3.FromProto(config.InitialState.Position) +
 280                                      positionNoise),
 281      Velocity = Coordinates3.ToProto(Coordinates3.FromProto(config.InitialState.Velocity) +
 282                                      velocityNoise),
 283    };
 0284    return CreateAgent(config, initialState, prefabName);
 0285  }
 286
 0287  private void Awake() {
 0288    if (Instance != null && Instance != this) {
 0289      Destroy(gameObject);
 0290      return;
 291    }
 0292    Instance = this;
 0293    DontDestroyOnLoad(gameObject);
 294
 0295    LoadSimConfigs(_defaultSimulationConfigFile);
 0296    Timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
 0297  }
 298
 0299  private void Start() {
 0300    IsPaused = false;
 0301    if (!RunManager.Instance.HasRunConfig()) {
 0302      StartSimulation();
 0303      ResumeSimulation();
 0304    }
 0305  }
 306
 0307  private void FixedUpdate() {
 0308    if (IsRunning && !IsPaused && ElapsedTime < SimulationConfig.EndTime) {
 0309      ElapsedTime += Time.fixedDeltaTime;
 0310    }
 0311  }
 312
 0313  private void LateUpdate() {
 0314    if (ShouldEndSimulation()) {
 0315      EndSimulation();
 0316      PostSimulation();
 0317    }
 0318  }
 319
 0320  private void InitializeLaunchers() {
 0321    foreach (var swarmConfig in SimulationConfig.InterceptorSwarmConfigs) {
 0322      IInterceptor launcher =
 323          CreateInterceptor(swarmConfig.AgentConfig, swarmConfig.AgentConfig.InitialState);
 0324      OnNewLauncher?.Invoke(launcher);
 0325    }
 0326  }
 327
 0328  private void InitializeThreats() {
 0329    foreach (var swarmConfig in SimulationConfig.ThreatSwarmConfigs) {
 0330      for (int i = 0; i < swarmConfig.NumAgents; ++i) {
 0331        CreateThreat(swarmConfig.AgentConfig);
 0332      }
 0333    }
 0334  }
 335
 0336  private void LoadSimConfigs(string simulationConfigFile) {
 0337    SimulatorConfig = ConfigLoader.LoadSimulatorConfig(_defaultSimulatorConfigFile);
 338    // If a run configuration is provided, enable telemetry logging and event logging.
 0339    if (RunManager.Instance.HasRunConfig()) {
 0340      SimulatorConfig.EnableTelemetryLogging = true;
 0341      SimulatorConfig.EnableEventLogging = true;
 0342    }
 0343    SimulationConfig = ConfigLoader.LoadSimulationConfig(simulationConfigFile);
 0344  }
 345
 0346  private void SetGameSpeed() {
 0347    if (IsPaused) {
 0348      Time.fixedDeltaTime = 0;
 0349      SetTimeScale(timeScale: 0);
 0350    } else {
 0351      Time.fixedDeltaTime = 1.0f / SimulatorConfig.PhysicsUpdateRate;
 0352      SetTimeScale(SimulationConfig.TimeScale);
 0353    }
 0354  }
 355
 0356  private void SetTimeScale(float timeScale) {
 0357    Time.timeScale = timeScale;
 358    // Time.fixedDeltaTime is derived from the simulator configuration.
 0359    Time.maximumDeltaTime = Time.fixedDeltaTime * 3;
 0360  }
 361
 0362  private void RegisterThreatMiss(IThreat threat) {
 0363    CostDestroyedThreats += threat.StaticConfig.Cost;
 0364  }
 365
 0366  private void RegisterThreatTerminated(IAgent threat) {
 0367    _threats.Remove(threat);
 0368    ++_numThreatsTerminated;
 0369  }
 370
 0371  private bool ShouldEndSimulation() {
 0372    if (IsRunning && ElapsedTime >= SimulationConfig.EndTime) {
 0373      return true;
 374    }
 375    // The simulation can be ended before the actual end time if there is a run configuration and
 376    // all spawned threats have been terminated.
 0377    if (RunManager.Instance.HasRunConfig() && _numThreatsSpawned > 0 &&
 0378        _numThreatsTerminated >= _numThreatsSpawned) {
 0379      return true;
 380    }
 0381    return false;
 0382  }
 383}