SensorMonitoringConsoleSystem.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. using Content.Server.Atmos.Monitor.Components;
  2. using Content.Server.Atmos.Monitor.Systems;
  3. using Content.Server.Atmos.Piping.Binary.Components;
  4. using Content.Server.Atmos.Piping.Components;
  5. using Content.Server.Atmos.Piping.Unary.Components;
  6. using Content.Server.DeviceNetwork;
  7. using Content.Server.DeviceNetwork.Components;
  8. using Content.Server.DeviceNetwork.Systems;
  9. using Content.Server.Power.Generation.Teg;
  10. using Content.Shared.Atmos.Monitor;
  11. using Content.Shared.Atmos.Piping.Binary.Components;
  12. using Content.Shared.Atmos.Piping.Unary.Components;
  13. using Content.Shared.DeviceNetwork;
  14. using Content.Shared.DeviceNetwork.Components;
  15. using Content.Shared.DeviceNetwork.Systems;
  16. using Content.Shared.SensorMonitoring;
  17. using Robust.Server.GameObjects;
  18. using Robust.Shared.Timing;
  19. using Robust.Shared.Utility;
  20. namespace Content.Server.SensorMonitoring;
  21. public sealed partial class SensorMonitoringConsoleSystem : EntitySystem
  22. {
  23. // TODO: THIS THING IS HEAVILY WIP AND NOT READY FOR GENERAL USE BY PLAYERS.
  24. // Some of the issues, off the top of my head:
  25. // Way too huge network load when opened
  26. // UI doesn't update properly in cases like adding new streams/devices
  27. // Deleting connected devices causes exceptions
  28. // UI sucks. need a way to make basic dashboards like Grafana, and save them.
  29. private EntityQuery<DeviceNetworkComponent> _deviceNetworkQuery;
  30. [Dependency] private readonly IGameTiming _gameTiming = default!;
  31. [Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default!;
  32. [Dependency] private readonly UserInterfaceSystem _userInterface = default!;
  33. public override void Initialize()
  34. {
  35. base.Initialize();
  36. InitUI();
  37. UpdatesBefore.Add(typeof(UserInterfaceSystem));
  38. SubscribeLocalEvent<SensorMonitoringConsoleComponent, DeviceListUpdateEvent>(DeviceListUpdated);
  39. SubscribeLocalEvent<SensorMonitoringConsoleComponent, ComponentStartup>(ConsoleStartup);
  40. SubscribeLocalEvent<SensorMonitoringConsoleComponent, DeviceNetworkPacketEvent>(DevicePacketReceived);
  41. SubscribeLocalEvent<SensorMonitoringConsoleComponent, AtmosDeviceUpdateEvent>(AtmosUpdate);
  42. _deviceNetworkQuery = GetEntityQuery<DeviceNetworkComponent>();
  43. }
  44. public override void Update(float frameTime)
  45. {
  46. var consoles = EntityQueryEnumerator<SensorMonitoringConsoleComponent>();
  47. while (consoles.MoveNext(out var entityUid, out var comp))
  48. {
  49. UpdateConsole(entityUid, comp);
  50. }
  51. }
  52. private void UpdateConsole(EntityUid uid, SensorMonitoringConsoleComponent comp)
  53. {
  54. var minTime = _gameTiming.CurTime - comp.RetentionTime;
  55. SensorUpdate(uid, comp);
  56. foreach (var data in comp.Sensors.Values)
  57. {
  58. // Cull old data.
  59. foreach (var stream in data.Streams.Values)
  60. {
  61. while (stream.Samples.TryPeek(out var sample) && sample.Time < minTime)
  62. {
  63. stream.Samples.Dequeue();
  64. }
  65. }
  66. }
  67. UpdateConsoleUI(uid, comp);
  68. }
  69. private void ConsoleStartup(EntityUid uid, SensorMonitoringConsoleComponent component, ComponentStartup args)
  70. {
  71. if (TryComp(uid, out DeviceListComponent? network))
  72. UpdateDevices(uid, component, network.Devices, Array.Empty<EntityUid>());
  73. }
  74. private void DeviceListUpdated(
  75. EntityUid uid,
  76. SensorMonitoringConsoleComponent component,
  77. DeviceListUpdateEvent args)
  78. {
  79. UpdateDevices(uid, component, args.Devices, args.OldDevices);
  80. }
  81. private void UpdateDevices(
  82. EntityUid uid,
  83. SensorMonitoringConsoleComponent component,
  84. IEnumerable<EntityUid> newDevices,
  85. IEnumerable<EntityUid> oldDevices)
  86. {
  87. var kept = new HashSet<EntityUid>();
  88. foreach (var newDevice in newDevices)
  89. {
  90. var deviceType = DetectDeviceType(newDevice);
  91. if (deviceType == SensorDeviceType.Unknown)
  92. continue;
  93. kept.Add(newDevice);
  94. var sensor = component.Sensors.GetOrNew(newDevice);
  95. sensor.DeviceType = deviceType;
  96. if (sensor.NetId == 0)
  97. sensor.NetId = MakeNetId(component);
  98. }
  99. foreach (var oldDevice in oldDevices)
  100. {
  101. if (kept.Contains(oldDevice))
  102. continue;
  103. if (component.Sensors.TryGetValue(oldDevice, out var sensorData))
  104. {
  105. component.RemovedSensors.Add(sensorData.NetId);
  106. component.Sensors.Remove(oldDevice);
  107. }
  108. }
  109. }
  110. private SensorDeviceType DetectDeviceType(EntityUid entity)
  111. {
  112. if (HasComp<TegGeneratorComponent>(entity))
  113. return SensorDeviceType.Teg;
  114. if (HasComp<AtmosMonitorComponent>(entity))
  115. return SensorDeviceType.AtmosSensor;
  116. if (HasComp<GasThermoMachineComponent>(entity))
  117. return SensorDeviceType.ThermoMachine;
  118. if (HasComp<GasVolumePumpComponent>(entity))
  119. return SensorDeviceType.VolumePump;
  120. if (HasComp<BatterySensorComponent>(entity))
  121. return SensorDeviceType.Battery;
  122. return SensorDeviceType.Unknown;
  123. }
  124. private void DevicePacketReceived(EntityUid uid, SensorMonitoringConsoleComponent component,
  125. DeviceNetworkPacketEvent args)
  126. {
  127. if (!component.Sensors.TryGetValue(args.Sender, out var sensorData))
  128. return;
  129. if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command))
  130. return;
  131. switch (sensorData.DeviceType)
  132. {
  133. case SensorDeviceType.Teg:
  134. if (command != TegSystem.DeviceNetworkCommandSyncData)
  135. return;
  136. if (!args.Data.TryGetValue(TegSystem.DeviceNetworkCommandSyncData, out TegSensorData? tegData))
  137. return;
  138. // @formatter:off
  139. WriteSample(component, sensorData, "teg_last_generated", SensorUnit.EnergyJ, tegData.LastGeneration);
  140. WriteSample(component, sensorData, "teg_power", SensorUnit.PowerW, tegData.PowerOutput);
  141. if (component.DebugStreams)
  142. WriteSample(component, sensorData, "teg_ramp_pos", SensorUnit.PowerW, tegData.RampPosition);
  143. WriteSample(component, sensorData, "teg_circ_a_in_pressure", SensorUnit.PressureKpa, tegData.CirculatorA.InletPressure);
  144. WriteSample(component, sensorData, "teg_circ_a_in_temperature", SensorUnit.TemperatureK, tegData.CirculatorA.InletTemperature);
  145. WriteSample(component, sensorData, "teg_circ_a_out_pressure", SensorUnit.PressureKpa, tegData.CirculatorA.OutletPressure);
  146. WriteSample(component, sensorData, "teg_circ_a_out_temperature", SensorUnit.TemperatureK, tegData.CirculatorA.OutletTemperature);
  147. WriteSample(component, sensorData, "teg_circ_b_in_pressure", SensorUnit.PressureKpa, tegData.CirculatorB.InletPressure);
  148. WriteSample(component, sensorData, "teg_circ_b_in_temperature", SensorUnit.TemperatureK, tegData.CirculatorB.InletTemperature);
  149. WriteSample(component, sensorData, "teg_circ_b_out_pressure", SensorUnit.PressureKpa, tegData.CirculatorB.OutletPressure);
  150. WriteSample(component, sensorData, "teg_circ_b_out_temperature", SensorUnit.TemperatureK, tegData.CirculatorB.OutletTemperature);
  151. // @formatter:on
  152. break;
  153. case SensorDeviceType.AtmosSensor:
  154. if (command != AtmosDeviceNetworkSystem.SyncData)
  155. return;
  156. if (!args.Data.TryGetValue(AtmosDeviceNetworkSystem.SyncData, out AtmosSensorData? atmosData))
  157. return;
  158. // @formatter:off
  159. WriteSample(component, sensorData, "atmo_pressure", SensorUnit.PressureKpa, atmosData.Pressure);
  160. WriteSample(component, sensorData, "atmo_temperature", SensorUnit.TemperatureK, atmosData.Temperature);
  161. // @formatter:on
  162. break;
  163. case SensorDeviceType.ThermoMachine:
  164. if (command != AtmosDeviceNetworkSystem.SyncData)
  165. return;
  166. if (!args.Data.TryGetValue(AtmosDeviceNetworkSystem.SyncData, out GasThermoMachineData? thermoData))
  167. return;
  168. // @formatter:off
  169. WriteSample(component, sensorData, "abs_energy_delta", SensorUnit.EnergyJ, MathF.Abs(thermoData.EnergyDelta));
  170. // @formatter:on
  171. break;
  172. case SensorDeviceType.VolumePump:
  173. if (command != AtmosDeviceNetworkSystem.SyncData)
  174. return;
  175. if (!args.Data.TryGetValue(AtmosDeviceNetworkSystem.SyncData, out GasVolumePumpData? volumePumpData))
  176. return;
  177. // @formatter:off
  178. WriteSample(component, sensorData, "moles_transferred", SensorUnit.Moles, volumePumpData.LastMolesTransferred);
  179. // @formatter:on
  180. break;
  181. case SensorDeviceType.Battery:
  182. if (command != BatterySensorSystem.DeviceNetworkCommandSyncData)
  183. return;
  184. if (!args.Data.TryGetValue(BatterySensorSystem.DeviceNetworkCommandSyncData, out BatterySensorData? batteryData))
  185. return;
  186. // @formatter:off
  187. WriteSample(component, sensorData, "charge", SensorUnit.EnergyJ, batteryData.Charge);
  188. WriteSample(component, sensorData, "charge_max", SensorUnit.EnergyJ, batteryData.MaxCharge);
  189. WriteSample(component, sensorData, "receiving", SensorUnit.PowerW, batteryData.Receiving);
  190. WriteSample(component, sensorData, "receiving_max", SensorUnit.PowerW, batteryData.MaxReceiving);
  191. WriteSample(component, sensorData, "supplying", SensorUnit.PowerW, batteryData.Supplying);
  192. WriteSample(component, sensorData, "supplying_max", SensorUnit.PowerW, batteryData.MaxSupplying);
  193. // @formatter:on
  194. break;
  195. }
  196. }
  197. private void WriteSample(
  198. SensorMonitoringConsoleComponent component,
  199. SensorMonitoringConsoleComponent.SensorData sensorData,
  200. string streamName,
  201. SensorUnit unit,
  202. float value)
  203. {
  204. var stream = sensorData.Streams.GetOrNew(streamName);
  205. stream.Unit = unit;
  206. if (stream.NetId == 0)
  207. stream.NetId = MakeNetId(component);
  208. var time = _gameTiming.CurTime;
  209. stream.Samples.Enqueue(new SensorSample(time, value));
  210. }
  211. private static int MakeNetId(SensorMonitoringConsoleComponent component)
  212. {
  213. return ++component.IdCounter;
  214. }
  215. private void AtmosUpdate(
  216. EntityUid uid,
  217. SensorMonitoringConsoleComponent comp,
  218. AtmosDeviceUpdateEvent args)
  219. {
  220. foreach (var (ent, data) in comp.Sensors)
  221. {
  222. // Send network requests for new data!
  223. NetworkPayload payload;
  224. switch (data.DeviceType)
  225. {
  226. case SensorDeviceType.Teg:
  227. payload = new NetworkPayload
  228. {
  229. [DeviceNetworkConstants.Command] = TegSystem.DeviceNetworkCommandSyncData
  230. };
  231. break;
  232. case SensorDeviceType.AtmosSensor:
  233. case SensorDeviceType.ThermoMachine:
  234. case SensorDeviceType.VolumePump:
  235. payload = new NetworkPayload
  236. {
  237. [DeviceNetworkConstants.Command] = AtmosDeviceNetworkSystem.SyncData
  238. };
  239. break;
  240. default:
  241. // Unknown device type, don't do anything.
  242. continue;
  243. }
  244. var address = _deviceNetworkQuery.GetComponent(ent);
  245. _deviceNetwork.QueuePacket(uid, address.Address, payload);
  246. }
  247. }
  248. private void SensorUpdate(EntityUid uid, SensorMonitoringConsoleComponent comp)
  249. {
  250. foreach (var (ent, data) in comp.Sensors)
  251. {
  252. // Send network requests for new data!
  253. NetworkPayload payload;
  254. switch (data.DeviceType)
  255. {
  256. case SensorDeviceType.Battery:
  257. payload = new NetworkPayload
  258. {
  259. [DeviceNetworkConstants.Command] = BatterySensorSystem.DeviceNetworkCommandSyncData
  260. };
  261. break;
  262. default:
  263. // Unknown device type, don't do anything.
  264. continue;
  265. }
  266. var address = _deviceNetworkQuery.GetComponent(ent);
  267. _deviceNetwork.QueuePacket(uid, address.Address, payload);
  268. }
  269. }
  270. }