Power Monitoring Console (#7849)

This commit is contained in:
20kdc
2022-05-04 18:59:40 +01:00
committed by GitHub
parent 3d606f4316
commit 70cd3d18ca
9 changed files with 299 additions and 0 deletions

View File

@@ -138,6 +138,7 @@ namespace Content.Client.Entry
"Flash",
"Docking",
"Telecrystal",
"PowerMonitoringConsole",
"RCD",
"RCDAmmo",
"CursedEntityStorage",

View File

@@ -0,0 +1,20 @@
<DefaultWindow
xmlns="https://spacestation14.io"
Title="{Loc 'power-monitoring-window-title'}">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<GridContainer Columns="2">
<!-- Grid is used here to align things. -->
<Label Text="{Loc 'power-monitoring-window-total-sources'}"/><Label Name="TotalSourcesNum" Text="?"/>
<Label Text="{Loc 'power-monitoring-window-total-loads'}"/><Label Name="TotalLoadsNum" Text="?"/>
</GridContainer>
<TabContainer Name="MasterTabContainer" VerticalExpand="True">
<ItemList Name="SourcesList" VerticalExpand="True">
</ItemList>
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<CheckBox Margin="8 8" Name="ShowInactiveConsumersCheckBox" Text="{Loc 'power-monitoring-window-show-inactive-consumers'}" />
<ItemList Name="LoadsList" VerticalExpand="True">
</ItemList>
</BoxContainer>
</TabContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,85 @@
using System;
using System.Linq;
using Content.Client.Computer;
using Content.Client.IoC;
using Content.Shared.Power;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.Shared.Prototypes;
namespace Content.Client.Power;
[GenerateTypedNameReferences]
public sealed partial class PowerMonitoringWindow : DefaultWindow, IComputerWindow<PowerMonitoringConsoleBoundInterfaceState>
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public PowerMonitoringWindow()
{
RobustXamlLoader.Load(this);
SetSize = MinSize = (300, 450);
IoCManager.InjectDependencies(this);
MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-tab-sources"));
MasterTabContainer.SetTabTitle(1, Loc.GetString("power-monitoring-window-tab-loads"));
}
public void UpdateState(PowerMonitoringConsoleBoundInterfaceState scc)
{
UpdateList(TotalSourcesNum, scc.TotalSources, SourcesList, scc.Sources);
var loads = scc.Loads;
if (!ShowInactiveConsumersCheckBox.Pressed)
{
// Not showing inactive consumers, so hiding them.
// This means filtering out loads that are not either:
// + Batteries (always important)
// + Meaningful (size above 0)
loads = loads.Where(a => a.IsBattery || (a.Size > 0.0f)).ToArray();
}
UpdateList(TotalLoadsNum, scc.TotalLoads, LoadsList, loads);
}
public void UpdateList(Label number, double numberVal, ItemList list, PowerMonitoringConsoleEntry[] listVal)
{
number.Text = Loc.GetString("power-monitoring-window-value", ("value", numberVal));
// This magic is important to prevent scrolling issues.
while (list.Count > listVal.Length)
{
list.RemoveAt(list.Count - 1);
}
while (list.Count < listVal.Length)
{
list.AddItem("YOU SHOULD NEVER SEE THIS (REALLY!)", null, false);
}
// Now overwrite the items properly...
for (var i = 0; i < listVal.Length; i++)
{
var ent = listVal[i];
_prototypeManager.TryIndex(ent.IconEntityPrototypeId, out EntityPrototype? entityPrototype);
IRsiStateLike? iconState = null;
if (entityPrototype != null)
iconState = SpriteComponent.GetPrototypeIcon(entityPrototype, StaticIoC.ResC);
var icon = iconState?.GetFrame(RSI.State.Direction.South, 0);
var item = list[i];
item.Text = $"{ent.NameLocalized} {Loc.GetString("power-monitoring-window-value", ("value", ent.Size))}";
item.Icon = icon;
}
}
}
[UsedImplicitly]
public sealed class PowerMonitoringConsoleBoundUserInterface : ComputerBoundUserInterface<PowerMonitoringWindow, PowerMonitoringConsoleBoundInterfaceState>
{
public PowerMonitoringConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) {}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using Content.Server.Power.NodeGroups;
using Content.Server.Utility;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Power.Components;
[RegisterComponent]
public sealed class PowerMonitoringConsoleComponent : Component
{
}

View File

@@ -0,0 +1,102 @@
using Content.Shared.Popups;
using Content.Shared.Power;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Server.Power.NodeGroups;
using Content.Server.UserInterface;
using Content.Server.WireHacking;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
namespace Content.Server.Power.EntitySystems;
[UsedImplicitly]
internal sealed class PowerMonitoringConsoleSystem : EntitySystem
{
private float _updateTimer = 0.0f;
private const float UpdateTime = 1.0f;
[Dependency]
private UserInterfaceSystem _userInterfaceSystem = default!;
public override void Update(float frameTime)
{
_updateTimer += frameTime;
if (_updateTimer >= UpdateTime)
{
_updateTimer -= UpdateTime;
foreach (var component in EntityQuery<PowerMonitoringConsoleComponent>())
{
UpdateUIState(component.Owner, component);
}
}
}
public void UpdateUIState(EntityUid target, PowerMonitoringConsoleComponent? pmcComp = null, NodeContainerComponent? ncComp = null)
{
if (!Resolve(target, ref pmcComp))
return;
if (!Resolve(target, ref ncComp))
return;
var totalSources = 0.0d;
var totalLoads = 0.0d;
var sources = new List<PowerMonitoringConsoleEntry>();
var loads = new List<PowerMonitoringConsoleEntry>();
PowerMonitoringConsoleEntry LoadOrSource(Component comp, double rate, bool isBattery)
{
var md = MetaData(comp.Owner);
var prototype = md.EntityPrototype?.ID ?? "";
return new PowerMonitoringConsoleEntry(md.EntityName, prototype, rate, isBattery);
}
// Right, so, here's what needs to be considered here.
var netQ = ncComp.GetNode<Node>("hv").NodeGroup as PowerNet;
if (netQ != null)
{
var net = netQ!;
foreach (PowerConsumerComponent pcc in net.Consumers)
{
loads.Add(LoadOrSource(pcc, pcc.DrawRate, false));
totalLoads += pcc.DrawRate;
}
foreach (BatteryChargerComponent pcc in net.Chargers)
{
if (!TryComp(pcc.Owner, out PowerNetworkBatteryComponent? batteryComp))
{
continue;
}
var rate = batteryComp.NetworkBattery.CurrentReceiving;
loads.Add(LoadOrSource(pcc, rate, true));
totalLoads += rate;
}
foreach (PowerSupplierComponent pcc in net.Suppliers)
{
sources.Add(LoadOrSource(pcc, pcc.MaxSupply, false));
totalSources += pcc.MaxSupply;
}
foreach (BatteryDischargerComponent pcc in net.Dischargers)
{
if (!TryComp(pcc.Owner, out PowerNetworkBatteryComponent? batteryComp))
{
continue;
}
var rate = batteryComp.NetworkBattery.CurrentSupply;
sources.Add(LoadOrSource(pcc, rate, true));
totalSources += rate;
}
}
// Sort
loads.Sort(CompareLoadOrSources);
sources.Sort(CompareLoadOrSources);
// Actually set state.
var state = new PowerMonitoringConsoleBoundInterfaceState(totalSources, totalLoads, sources.ToArray(), loads.ToArray());
_userInterfaceSystem.GetUiOrNull(target, PowerMonitoringConsoleUiKey.Key)?.SetState(state);
}
private int CompareLoadOrSources(PowerMonitoringConsoleEntry x, PowerMonitoringConsoleEntry y)
{
return -x.Size.CompareTo(y.Size);
}
}

View File

@@ -0,0 +1,46 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
namespace Content.Shared.Power;
[Serializable, NetSerializable]
public sealed class PowerMonitoringConsoleBoundInterfaceState : BoundUserInterfaceState
{
public double TotalSources;
public double TotalLoads;
public PowerMonitoringConsoleEntry[] Sources;
public PowerMonitoringConsoleEntry[] Loads;
public PowerMonitoringConsoleBoundInterfaceState(double totalSources, double totalLoads, PowerMonitoringConsoleEntry[] sources, PowerMonitoringConsoleEntry[] loads)
{
TotalSources = totalSources;
TotalLoads = totalLoads;
Sources = sources;
Loads = loads;
}
}
[Serializable, NetSerializable]
public sealed class PowerMonitoringConsoleEntry
{
public string NameLocalized;
public string IconEntityPrototypeId;
public double Size;
public bool IsBattery;
public PowerMonitoringConsoleEntry(string nl, string ipi, double size, bool isBattery)
{
NameLocalized = nl;
IconEntityPrototypeId = ipi;
Size = size;
IsBattery = isBattery;
}
}
[Serializable, NetSerializable]
public enum PowerMonitoringConsoleUiKey
{
Key
}

View File

@@ -0,0 +1,8 @@
power-monitoring-window-title = Power Monitoring Console
power-monitoring-window-tab-sources = Sources
power-monitoring-window-tab-loads = Loads
power-monitoring-window-total-sources = Total Sources:
power-monitoring-window-total-loads = Total Loads:
power-monitoring-window-value = { POWERWATTS($value) }
power-monitoring-window-show-inactive-consumers = Show Inactive Consumers

View File

@@ -66,6 +66,14 @@
- type: ComputerBoard
prototype: ComputerSupplyRequest
- type: entity
parent: BaseComputerCircuitboard
id: PowerMonitoringComputerCircuitboard
name: power monitoring computer board
components:
- type: ComputerBoard
prototype: ComputerPowerMonitoring
- type: entity
parent: BaseComputerCircuitboard
id: ResearchComputerCircuitboard

View File

@@ -75,6 +75,19 @@
color: "#c9c042"
- type: Computer
board: PowerComputerCircuitboard
- type: PowerMonitoringConsole
- type: NodeContainer
examinable: true
nodes:
hv:
!type:CableDeviceNode
nodeGroupID: HVPower
- type: ActivatableUI
key: enum.PowerMonitoringConsoleUiKey.Key
- type: UserInterface
interfaces:
- key: enum.PowerMonitoringConsoleUiKey.Key
type: PowerMonitoringConsoleBoundUserInterface
- type: entity
parent: ComputerBase