< Summary

Class:SimMonitor
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Monitors/SimMonitor.cs
Covered lines:74
Uncovered lines:102
Coverable lines:176
Total lines:281
Line coverage:42% (74 of 176)
Covered branches:0
Total branches:0
Covered methods:16
Total methods:26
Method coverage:61.5% (16 of 26)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
SimMonitor()0%110100%
Awake()0%3.473062.5%
Start()0%110100%
OnDestroy()0%2100%
RegisterSimulationStarted()0%3.213071.43%
RegisterSimulationEnded()0%110100%
RegisterNewInterceptor(...)0%110100%
RegisterNewThreat(...)0%110100%
RegisterInterceptorHit(...)0%2100%
RegisterInterceptorMiss(...)0%2100%
RegisterThreatHit(...)0%2100%
RegisterThreatMiss(...)0%2100%
RegisterAgentEvent(...)0%220100%
InitializeSessionDirectory()0%2.082072.73%
InitializeTelemetryLogging()0%2100%
InitializeEventLogging()0%110100%
DestroyLogging()0%9.665042.86%
DestroyTelemetryLogging()0%12300%
MonitorRoutine()0%12300%
RecordTelemetry()0%42600%
ConvertTelemetryBinaryToCsv(...)0%30500%
WriteEventsToFile()0%330100%

File(s)

/github/workspace/Assets/Scripts/Monitors/SimMonitor.cs

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using System.IO;
 5using System.Linq;
 6using UnityEngine;
 7
 8public class SimMonitor : MonoBehaviour {
 9  private static class EventTypes {
 10    public const string NewInterceptor = "NEW_INTERCEPTOR";
 11    public const string NewThreat = "NEW_THREAT";
 12    public const string InterceptorHit = "INTERCEPTOR_HIT";
 13    public const string InterceptorMiss = "INTERCEPTOR_MISS";
 14    public const string ThreatHit = "THREAT_HIT";
 15    public const string ThreatMiss = "THREAT_MISS";
 16  }
 17
 18  [Serializable]
 19  private class EventRecord {
 20    public float Time;
 21    public string EventType;
 22    public string AgentType;
 23    public string AgentID;
 24    public float PositionX;
 25    public float PositionY;
 26    public float PositionZ;
 27  }
 28
 29  // Telemetry update period in seconds.
 30  private const float _updatePeriod = 0.1f;  // 10 Hz
 31
 232  public static SimMonitor Instance { get; private set; }
 33
 2634  public string Timestamp { get; private set; } = "";
 35
 36  private Coroutine _monitorRoutine;
 37
 38  private string _sessionDirectory;
 39
 40  private string _telemetryBinPath;
 41  private FileStream _telemetryFileStream;
 42  private BinaryWriter _telemetryBinaryWriter;
 43
 44  private string _eventLogPath;
 45  [SerializeField]
 46  private List<EventRecord> _eventLog;
 47
 248  private bool _isLoggingDestroyed = false;
 49
 150  private void Awake() {
 151    if (Instance != null && Instance != this) {
 052      Destroy(gameObject);
 053      return;
 54    }
 155    Instance = this;
 156    DontDestroyOnLoad(gameObject);
 157  }
 58
 159  private void Start() {
 160    SimManager.Instance.OnSimulationStarted += RegisterSimulationStarted;
 161    SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
 162    SimManager.Instance.OnNewThreat += RegisterNewThreat;
 163    SimManager.Instance.OnNewInterceptor += RegisterNewInterceptor;
 164  }
 65
 066  private void OnDestroy() {
 067    DestroyLogging();
 068  }
 69
 1270  private void RegisterSimulationStarted() {
 1271    _isLoggingDestroyed = false;
 1272    Timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
 1273    InitializeSessionDirectory();
 1274    if (SimManager.Instance.SimulatorConfig.EnableTelemetryLogging) {
 075      InitializeTelemetryLogging();
 076      _monitorRoutine = StartCoroutine(MonitorRoutine());
 077    }
 2478    if (SimManager.Instance.SimulatorConfig.EnableEventLogging) {
 1279      InitializeEventLogging();
 1280    }
 1281  }
 82
 1183  private void RegisterSimulationEnded() {
 1184    DestroyLogging();
 1185  }
 86
 1487  private void RegisterNewInterceptor(IInterceptor interceptor) {
 1488    RegisterAgentEvent(interceptor, EventTypes.NewInterceptor);
 1489    interceptor.OnHit += RegisterInterceptorHit;
 1490    interceptor.OnMiss += RegisterInterceptorMiss;
 1491  }
 92
 95593  private void RegisterNewThreat(IThreat threat) {
 95594    RegisterAgentEvent(threat, EventTypes.NewThreat);
 95595    threat.OnHit += RegisterThreatHit;
 95596    threat.OnMiss += RegisterThreatMiss;
 95597  }
 98
 099  private void RegisterInterceptorHit(IInterceptor interceptor) {
 0100    RegisterAgentEvent(interceptor, EventTypes.InterceptorHit);
 0101  }
 102
 0103  private void RegisterInterceptorMiss(IInterceptor interceptor) {
 0104    RegisterAgentEvent(interceptor, EventTypes.InterceptorMiss);
 0105  }
 106
 0107  private void RegisterThreatHit(IThreat threat) {
 0108    RegisterAgentEvent(threat, EventTypes.ThreatHit);
 0109  }
 110
 0111  private void RegisterThreatMiss(IThreat threat) {
 0112    RegisterAgentEvent(threat, EventTypes.ThreatMiss);
 0113  }
 114
 969115  private void RegisterAgentEvent(IAgent agent, string eventType) {
 1938116    if (SimManager.Instance.SimulatorConfig.EnableEventLogging) {
 969117      float time = SimManager.Instance.ElapsedTime;
 969118      Vector3 position = agent.Position;
 969119      var record = new EventRecord {
 120        Time = time,
 121        EventType = eventType,
 122        AgentType = agent.StaticConfig.AgentType.ToString(),
 123        AgentID = agent.gameObject.name,
 124        PositionX = position.x,
 125        PositionY = position.y,
 126        PositionZ = position.z,
 127      };
 969128      _eventLog.Add(record);
 969129    }
 969130  }
 131
 12132  private void InitializeSessionDirectory() {
 12133    if (RunManager.Instance.HasRunConfig()) {
 0134      _sessionDirectory =
 135          Path.Combine(Application.persistentDataPath, "Logs",
 136                       $"{RunManager.Instance.RunConfig.Name}_{SimManager.Instance.Timestamp}",
 137                       $"run_{RunManager.Instance.RunIndex + 1}_seed_{RunManager.Instance.Seed}");
 12138    } else {
 12139      _sessionDirectory = Path.Combine(Application.persistentDataPath, "Logs",
 140                                       $"run_{SimManager.Instance.Timestamp}");
 12141    }
 12142    Directory.CreateDirectory(_sessionDirectory);
 12143    Debug.Log($"Monitoring simulation logs to {_sessionDirectory}.");
 12144  }
 145
 0146  private void InitializeTelemetryLogging() {
 0147    string telemetryFile = $"sim_telemetry_{Timestamp}.bin";
 0148    _telemetryBinPath = Path.Combine(_sessionDirectory, telemetryFile);
 0149    _telemetryFileStream =
 150        new FileStream(_telemetryBinPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
 0151    _telemetryBinaryWriter = new BinaryWriter(_telemetryFileStream);
 0152    Debug.Log($"Telemetry file initialized successfully: {telemetryFile}.");
 0153  }
 154
 12155  private void InitializeEventLogging() {
 12156    string eventLog = $"sim_events_{Timestamp}.csv";
 12157    _eventLogPath = Path.Combine(_sessionDirectory, eventLog);
 12158    _eventLog = new List<EventRecord>();
 12159    Debug.Log($"Event log initialized successfully: {eventLog}.");
 12160  }
 161
 11162  private void DestroyLogging() {
 11163    if (_isLoggingDestroyed) {
 0164      return;
 165    }
 11166    _isLoggingDestroyed = true;
 167
 11168    if (SimManager.Instance.SimulatorConfig.EnableTelemetryLogging) {
 0169      if (_monitorRoutine != null) {
 0170        StopCoroutine(_monitorRoutine);
 0171        _monitorRoutine = null;
 0172      }
 0173      RecordTelemetry();
 0174      DestroyTelemetryLogging();
 0175      ConvertTelemetryBinaryToCsv(_telemetryBinPath,
 176                                  Path.ChangeExtension(_telemetryBinPath, ".csv"));
 0177    }
 22178    if (SimManager.Instance.SimulatorConfig.EnableEventLogging) {
 11179      WriteEventsToFile();
 11180    }
 11181  }
 182
 0183  private void DestroyTelemetryLogging() {
 0184    if (_telemetryBinaryWriter != null) {
 0185      _telemetryBinaryWriter.Flush();
 0186      _telemetryBinaryWriter.Close();
 0187      _telemetryBinaryWriter = null;
 0188    }
 189
 0190    if (_telemetryFileStream != null) {
 0191      _telemetryFileStream.Close();
 0192      _telemetryFileStream = null;
 0193    }
 0194  }
 195
 0196  private IEnumerator MonitorRoutine() {
 0197    while (true) {
 0198      RecordTelemetry();
 0199      yield return new WaitForSeconds(_updatePeriod);
 0200    }
 201  }
 202
 0203  private void RecordTelemetry() {
 0204    float time = SimManager.Instance.ElapsedTime;
 0205    List<IAgent> agents = SimManager.Instance.Agents.Where(agent => !agent.IsTerminated).ToList();
 0206    if (_telemetryBinaryWriter == null) {
 0207      Debug.LogWarning("Telemetry binary writer is null.");
 0208      return;
 209    }
 0210    foreach (var agent in agents) {
 0211      if (!agent.gameObject.activeInHierarchy) {
 0212        continue;
 213      }
 214
 0215      Vector3 position = agent.Position;
 0216      if (position == Vector3.zero) {
 0217        continue;
 218      }
 0219      Vector3 velocity = agent.Velocity;
 220
 221      // Write telemetry data directly to the binary file.
 0222      _telemetryBinaryWriter.Write(time);
 0223      _telemetryBinaryWriter.Write((int)agent.StaticConfig.AgentType);
 0224      _telemetryBinaryWriter.Write(agent.gameObject.name);
 0225      _telemetryBinaryWriter.Write(position.x);
 0226      _telemetryBinaryWriter.Write(position.y);
 0227      _telemetryBinaryWriter.Write(position.z);
 0228      _telemetryBinaryWriter.Write(velocity.x);
 0229      _telemetryBinaryWriter.Write(velocity.y);
 0230      _telemetryBinaryWriter.Write(velocity.z);
 0231    }
 0232  }
 233
 0234  private void ConvertTelemetryBinaryToCsv(string binaryFilePath, string csvFilePath) {
 0235    try {
 0236      using var fs =
 237          new FileStream(binaryFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
 0238      using var reader = new BinaryReader(fs);
 0239      using var writer = new StreamWriter(csvFilePath, false);
 0240      {
 241        // Write the CSV header.
 0242        writer.WriteLine(
 243            "Time,AgentType,AgentID,PositionX,PositionY,PositionZ,VelocityX,VelocityY,VelocityZ");
 244
 0245        while (reader.BaseStream.Position != reader.BaseStream.Length) {
 0246          float time = reader.ReadSingle();
 0247          var agentType = (Configs.AgentType)reader.ReadInt32();
 0248          string agentID = reader.ReadString();
 0249          float positionX = reader.ReadSingle();
 0250          float positionY = reader.ReadSingle();
 0251          float positionZ = reader.ReadSingle();
 0252          float velocityX = reader.ReadSingle();
 0253          float velocityY = reader.ReadSingle();
 0254          float velocityZ = reader.ReadSingle();
 255
 256          // Write the data to CSV.
 0257          writer.WriteLine($"{time:F2},{agentType.ToString()},{agentID}," +
 258                           $"{positionX:F2},{positionY:F2},{positionZ:F2}," +
 259                           $"{velocityX:F2},{velocityY:F2},{velocityZ:F2}");
 0260        }
 0261        Debug.Log($"Telemetry CSV file converted successfully: {csvFilePath}.");
 0262      }
 0263    } catch (IOException e) {
 0264      Debug.LogWarning(
 265          $"An IO error occurred while converting binary telemetry file to CSV format: {e.Message}.");
 0266    }
 0267  }
 268
 11269  private void WriteEventsToFile() {
 22270    using (var writer = new StreamWriter(_eventLogPath, append: false)) {
 271      // Write the CSV header.
 11272      writer.WriteLine("Time,Event,AgentType,AgentID,PositionX,PositionY,PositionZ");
 273
 2916274      foreach (var record in _eventLog) {
 961275        writer.WriteLine(
 276            $"{record.Time:F2},{record.EventType},{record.AgentType},{record.AgentID}," +
 277            $"{record.PositionX:F2},{record.PositionY:F2},{record.PositionZ:F2}");
 961278      }
 11279    }
 11280  }
 281}