< Summary

Class:IADS
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/IADS/IADS.cs
Covered lines:0
Uncovered lines:116
Coverable lines:116
Total lines:192
Line coverage:0% (0 of 116)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:20
Method coverage:0% (0 of 20)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
IADS()0%2100%
Awake()0%12300%
Start()0%2100%
OnDestroy()0%6200%
RegisterSimulationStarted()0%2100%
RegisterSimulationEnded()0%6200%
RegisterNewAsset(...)0%6200%
RegisterNewLauncher(...)0%6200%
RegisterNewThreat(...)0%6200%
RegisterMessageReceived(...)0%12300%
HierarchyManager()0%20400%
BuildHierarchy()0%30500%
AssignSubInterceptor(...)0%90900%
ReassignTarget(...)0%20400%

File(s)

/github/workspace/Assets/Scripts/IADS/IADS.cs

#LineLine coverage
 1using System.Collections;
 2using System.Collections.Generic;
 3using System.Linq;
 4using UnityEngine;
 5
 6// The Integrated Air Defense System (IADS) manages the air defense strategy.
 7// It implements the singleton pattern to ensure that only one instance exists.
 8public class IADS : MonoBehaviour, ICommsEndpoint {
 9  // Hierarchy parameters.
 10  private const float _hierarchyUpdatePeriod = 5f;
 11  private const float _coverageFactor = 1f;
 12
 13  // List of assets.
 014  private List<IHierarchical> _assets = new List<IHierarchical>();
 15
 16  // The IADS only manages the launchers at the top level of the interceptor hierarchy.
 017  private List<IHierarchical> _launchers = new List<IHierarchical>();
 18
 19  // Coroutine to perform the maintain the agent hierarchy.
 20  private Coroutine _hierarchyCoroutine;
 21
 22  // List of threats waiting to be incorporated into the hierarchy.
 023  private List<IHierarchical> _newThreats = new List<IHierarchical>();
 24
 025  public static IADS Instance { get; private set; }
 26
 027  public IReadOnlyList<IHierarchical> Assets => _assets.AsReadOnly();
 28
 029  public IReadOnlyList<IHierarchical> Launchers => _launchers.AsReadOnly();
 30
 031  public CommsNode CommsNode { get; private set; }
 32
 033  private void Awake() {
 034    if (Instance != null && Instance != this) {
 035      Destroy(gameObject);
 036      return;
 37    }
 038    Instance = this;
 039  }
 40
 041  private void Start() {
 042    SimManager.Instance.OnSimulationStarted += RegisterSimulationStarted;
 043    SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
 044    SimManager.Instance.OnNewAsset += RegisterNewAsset;
 045    SimManager.Instance.OnNewLauncher += RegisterNewLauncher;
 046    SimManager.Instance.OnNewThreat += RegisterNewThreat;
 47
 48    // Create a communication node for the IADS.
 049    CommsNode = new CommsNode(Configs.AgentType.Iads);
 050    CommsNode.OnReceived += RegisterMessageReceived;
 051    CommsManager.Instance.AddNode(CommsNode);
 052  }
 53
 054  private void OnDestroy() {
 055    if (_hierarchyCoroutine != null) {
 056      StopCoroutine(_hierarchyCoroutine);
 057      _hierarchyCoroutine = null;
 058    }
 059  }
 60
 061  private void RegisterSimulationStarted() {
 062    _hierarchyCoroutine = StartCoroutine(HierarchyManager(_hierarchyUpdatePeriod));
 063  }
 64
 065  private void RegisterSimulationEnded() {
 066    if (_hierarchyCoroutine != null) {
 067      StopCoroutine(_hierarchyCoroutine);
 068      _hierarchyCoroutine = null;
 069    }
 070    _assets.Clear();
 071    _launchers.Clear();
 072    _newThreats.Clear();
 073  }
 74
 075  private void RegisterNewAsset(IInterceptor asset) {
 076    if (asset.HierarchicalAgent != null) {
 077      _assets.Add(asset.HierarchicalAgent);
 078    }
 079  }
 80
 081  private void RegisterNewLauncher(IInterceptor launcher) {
 082    if (launcher.HierarchicalAgent != null) {
 083      launcher.ParentCommsNode = CommsNode;
 084      _launchers.Add(launcher.HierarchicalAgent);
 085    }
 086  }
 87
 088  private void RegisterNewThreat(IThreat threat) {
 089    if (threat.HierarchicalAgent != null) {
 090      _newThreats.Add(threat.HierarchicalAgent);
 091    }
 092  }
 93
 094  private void RegisterMessageReceived(Message message) {
 095    switch (message) {
 96      case AssignTargetRequestMessage request:
 097        AssignSubInterceptor(request.PayloadData.SubInterceptor);
 098        break;
 99      case ReassignTargetRequestMessage request:
 0100        ReassignTarget(request.PayloadData.Target);
 0101        break;
 102      default:
 0103        break;
 104    }
 0105  }
 106
 0107  private IEnumerator HierarchyManager(float period) {
 0108    while (true) {
 0109      if (_newThreats.Count != 0) {
 0110        BuildHierarchy();
 0111      }
 0112      yield return new WaitForSeconds(period);
 0113    }
 114  }
 115
 0116  private void BuildHierarchy() {
 0117    if (_newThreats.Count == 0 || _launchers.Count == 0) {
 0118      return;
 119    }
 120
 121    // TODO(titan): The clustering algorithm should be aware of the capacity of the launcher.
 0122    var swarmClusterer = new KMeansClusterer(Mathf.RoundToInt(_launchers.Count / _coverageFactor));
 0123    List<Cluster> swarms = swarmClusterer.Cluster(_newThreats);
 0124    _newThreats.Clear();
 125
 126    // Assign one swarm to each launcher.
 0127    var swarmToLauncherAssignment =
 128        new MinDistanceAssignment(Assignment.Assignment_EvenAssignment_Assign);
 0129    List<AssignmentItem> swarmToLauncherAssignments =
 130        swarmToLauncherAssignment.Assign(_launchers, swarms);
 0131    void AssignTarget(IHierarchical hierarchical, IHierarchical target) {
 0132      hierarchical.Target = target;
 0133      foreach (var subHierarchical in hierarchical.ActiveSubHierarchicals) {
 0134        AssignTarget(subHierarchical, target);
 0135      }
 0136    }
 0137    foreach (var assignment in swarmToLauncherAssignments) {
 138      // Assign the swarm as the target to the launcher.
 0139      assignment.First.Target = assignment.Second;
 140
 141      // Find the asset closest to each swarm and assign it as the target to all threats within the
 142      // swarm. If there are no assets, the threats will target the launcher.
 143      // TODO(titan): Move threat coordination into a separate module as the IADS should only manage
 144      // the defense strategy.
 0145      var closestAsset =
 0146          _assets.OrderBy(asset => Vector3.Distance(assignment.Second.Position, asset.Position))
 147              .FirstOrDefault();
 0148      AssignTarget(assignment.Second, closestAsset ?? assignment.First);
 0149    }
 0150  }
 151
 0152  private void AssignSubInterceptor(IInterceptor subInterceptor) {
 0153    if (subInterceptor == null || subInterceptor.IsTerminated ||
 0154        subInterceptor.CapacityRemaining <= 0) {
 0155      return;
 156    }
 157
 158    // Pass the sub-interceptor through all the launchers in order of increasing distance between
 159    // the sub-interceptor and the launcher's target.
 0160    var sortedLaunchers =
 0161        Launchers.Where(launcher => launcher.Target != null && !launcher.Target.IsTerminated)
 162            .OrderBy(launcher =>
 0163                         Vector3.Distance(subInterceptor.Position, launcher.Target.Position));
 0164    foreach (var launcher in sortedLaunchers) {
 0165      IHierarchical target = launcher.FindNewTarget(subInterceptor.HierarchicalAgent,
 166                                                    subInterceptor.CapacityRemaining);
 0167      if (target != null && !target.IsTerminated) {
 0168        CommsManager.Instance.SendMessage(
 169            new AssignTargetResponseMessage(CommsNode, subInterceptor.CommsNode, target));
 0170        break;
 171      }
 0172    }
 0173  }
 174
 0175  private void ReassignTarget(IHierarchical target) {
 176    // Assign the closest launcher with non-zero remaining capacity to pursue the target.
 0177    var closestLauncher =
 178        Launchers
 0179            .Select(launcher => new {
 180              Hierarchical = launcher,
 181              Interceptor = (launcher as HierarchicalAgent)?.Agent as IInterceptor,
 182            })
 0183            .Where(launcher => launcher.Interceptor?.CapacityPlannedRemaining > 0)
 0184            .OrderBy(launcher => Vector3.Distance(target.Position, launcher.Hierarchical.Position))
 185            .FirstOrDefault();
 0186    if (closestLauncher == null) {
 0187      return;
 188    }
 0189    CommsManager.Instance.SendMessage(
 190        new ReassignTargetRequestMessage(CommsNode, closestLauncher.Interceptor.CommsNode, target));
 0191  }
 192}