using Content.Shared.GameObjects.EntitySystems; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.IoC; using SS14.Shared.Log; using System; using System.Collections.Generic; namespace Content.Server.GameObjects.Components.Power { /// /// Master class for group of powertransfercomponents, takes in and distributes power via nodes /// public class Powernet { public Powernet() { var EntitySystemManager = IoCManager.Resolve(); var powerSystem = EntitySystemManager.GetEntitySystem(); powerSystem.Powernets.Add(this); Uid = powerSystem.NewUid(); } public int Uid { get; } /// /// The entities that make up the powernet's physical location and allow powernet connection /// public List WireList { get; set; } = new List(); /// /// Entities that connect directly to the powernet through PTC above to add power or add power load /// public List NodeList { get; set; } = new List(); /// /// Subset of nodelist that adds a continuous power supply to the network /// public Dictionary GeneratorList { get; set; } = new Dictionary(); /// /// Subset of nodelist that draw power, stores information on current continuous powernet load /// public SortedSet DeviceLoadList { get; set; } = new SortedSet(new DevicePriorityCompare()); /// /// Comparer that keeps the device dictionary sorted by powernet priority /// public class DevicePriorityCompare : IComparer { public int Compare(PowerDeviceComponent x, PowerDeviceComponent y) { int compare = y.Priority.CompareTo(x.Priority); //If the comparer returns 0 sortedset will believe it is a duplicate and return 0, so return 1 instead if (compare == 0 && !x.Equals(y)) { return 1; } return compare; } } /// /// Priority that a device will receive power if powernet cannot supply every device /// public enum Priority { Necessary, High, Medium, Low, Provider, Unnecessary } /// /// All the devices that have been depowered by this powernet or depowered prior to being absorted into this powernet /// public List DepoweredDevices { get; set; } = new List(); /// /// A list of the energy storage components that will feed the powernet if necessary, and if there is enough power feed itself /// public List PowerStorageSupplierlist { get; set; } = new List(); /// /// A list of energy storage components that will never feed the powernet, will try to draw energy to feed themselves if possible /// public List PowerStorageConsumerlist { get; set; } = new List(); /// /// Static counter of all continuous load placed from devices on this power network /// public float Load { get; private set; } = 0; /// /// Static counter of all continiuous supply from generators on this power network /// public float Supply { get; private set; } = 0; /// /// Variable that causes powernet to be regenerated from its wires during the next update cycle /// public bool Dirty { get; set; } = false; public void Update(float frametime) { float activesupply = Supply; float activeload = Load; float storagedemand = 0; foreach (var supply in PowerStorageConsumerlist) { storagedemand += supply.RequestCharge(); } float passivesupply = 0; float passivedemand = 0; foreach (var supply in PowerStorageSupplierlist) { passivesupply += supply.AvailableCharge(); passivedemand += supply.RequestCharge(); } //If we have enough power to feed all load and storage demand, then feed everything if (activesupply > activeload + storagedemand + passivedemand) { PowerAllDevices(); ChargeActiveStorage(); ChargePassiveStorage(); } //We don't have enough power for the storage powernet suppliers, ignore powering them else if (activesupply > activeload + storagedemand) { PowerAllDevices(); ChargeActiveStorage(); } //We require the storage powernet suppliers to power the remaining storage components and device load else if (activesupply + passivesupply > activeload + storagedemand) { PowerAllDevices(); ChargeActiveStorage(); RetrievePassiveStorage(); } //We cant afford to fund the storage components, so lets try to power the basic load using our supply and storage supply else if (activesupply + passivesupply > activeload) { PowerAllDevices(); RetrievePassiveStorage(); } //We cant even cover the basic device load, start disabling devices in order of priority until the remaining load is lowered enough to be met else if (activesupply + passivesupply < activeload) { PowerAllDevices(); //This merely makes our inevitable betrayal all the sweeter RetrievePassiveStorage(); var depowervalue = activeload - (activesupply + passivesupply); //Providers use same method to recreate functionality foreach (var device in DeviceLoadList) { device.ExternalPowered = false; DepoweredDevices.Add(device); depowervalue -= device.Load; if (depowervalue < 0) break; } } } private void PowerAllDevices() { foreach (var device in DepoweredDevices) { device.ExternalPowered = true; } DepoweredDevices.Clear(); } private void ChargeActiveStorage() { foreach (var storage in PowerStorageConsumerlist) { storage.ChargePowerTick(); } } private void ChargePassiveStorage() { foreach (var storage in PowerStorageSupplierlist) { storage.ChargePowerTick(); } } private void RetrievePassiveStorage() { foreach (var storage in PowerStorageSupplierlist) { storage.ChargePowerTick(); } } /// /// Kills a powernet after it is marked dirty and its component have already been regenerated by the powernet system /// public void DirtyKill() { WireList.Clear(); while (NodeList.Count != 0) { NodeList[0].DisconnectFromPowernet(); } GeneratorList.Clear(); DeviceLoadList.Clear(); DepoweredDevices.Clear(); PowerStorageSupplierlist.Clear(); PowerStorageConsumerlist.Clear(); RemoveFromSystem(); } /// /// Combines two powernets when they connect via powertransfer components /// public void MergePowernets(Powernet toMerge) { //TODO: load balance reconciliation between powernets on merge tick here foreach (var wire in toMerge.WireList) { wire.Parent = this; } WireList.AddRange(toMerge.WireList); toMerge.WireList.Clear(); foreach (var node in toMerge.NodeList) { node.Parent = this; } NodeList.AddRange(toMerge.NodeList); toMerge.NodeList.Clear(); foreach (var generator in toMerge.GeneratorList) { GeneratorList.Add(generator.Key, generator.Value); } toMerge.GeneratorList.Clear(); foreach (var device in toMerge.DeviceLoadList) { DeviceLoadList.Add(device); } toMerge.DeviceLoadList.Clear(); DepoweredDevices.AddRange(toMerge.DepoweredDevices); toMerge.DepoweredDevices.Clear(); PowerStorageSupplierlist.AddRange(toMerge.PowerStorageSupplierlist); toMerge.PowerStorageSupplierlist.Clear(); PowerStorageConsumerlist.AddRange(toMerge.PowerStorageConsumerlist); toMerge.PowerStorageConsumerlist.Clear(); toMerge.RemoveFromSystem(); } /// /// Removes reference from the powernets list on the powernet system /// private void RemoveFromSystem() { var EntitySystemManager = IoCManager.Resolve(); EntitySystemManager.GetEntitySystem().Powernets.Remove(this); } #region Registration /// /// Register a continuous load from a device connected to the powernet /// public void AddDevice(PowerDeviceComponent device) { DeviceLoadList.Add(device); Load += device.Load; if (!device.Powered) DepoweredDevices.Add(device); } /// /// Update one of the loads from a deviceconnected to the powernet /// public void UpdateDevice(PowerDeviceComponent device, float oldLoad) { if (DeviceLoadList.Contains(device)) { Load -= oldLoad; Load += device.Load; } } /// /// Remove a continuous load from a device connected to the powernet /// public void RemoveDevice(PowerDeviceComponent device) { if (DeviceLoadList.Contains(device)) { Load -= device.Load; DeviceLoadList.Remove(device); if (DepoweredDevices.Contains(device)) DepoweredDevices.Remove(device); } else { var name = device.Owner.Prototype.Name; Logger.Info(String.Format("We tried to remove a device twice from the same powernet somehow, prototype {0}", name)); } } /// /// Register a power supply from a generator connected to the powernet /// public void AddGenerator(PowerGeneratorComponent generator) { GeneratorList.Add(generator, generator.Supply); Supply += generator.Supply; } /// /// Update the value supplied from a generator connected to the powernet /// public void UpdateGenerator(PowerGeneratorComponent generator) { if (GeneratorList.ContainsKey(generator)) { Supply -= GeneratorList[generator]; GeneratorList[generator] = generator.Supply; Supply += generator.Supply; } } /// /// Remove a power supply from a generator connected to the powernet /// public void RemoveGenerator(PowerGeneratorComponent generator) { if (GeneratorList.ContainsKey(generator)) { Supply -= GeneratorList[generator]; GeneratorList.Remove(generator); } else { var name = generator.Owner.Prototype.Name; Logger.Info(String.Format("We tried to remove a device twice from the same power somehow, prototype {1}", name)); } } /// /// Register a power supply from a generator connected to the powernet /// public void AddPowerStorage(PowerStorageComponent storage) { if (storage.ChargePowernet) PowerStorageSupplierlist.Add(storage); else PowerStorageConsumerlist.Add(storage); } //How do I even call this? TODO: fix public void UpdateStorageType(PowerStorageComponent storage) { //If our chargepowernet settings change we need to tell the powernet of this new setting and remove traces of our old setting if (PowerStorageSupplierlist.Contains(storage)) PowerStorageSupplierlist.Remove(storage); if (PowerStorageConsumerlist.Contains(storage)) PowerStorageConsumerlist.Remove(storage); //Apply new setting if (storage.ChargePowernet) PowerStorageSupplierlist.Add(storage); else PowerStorageConsumerlist.Add(storage); } /// /// Remove a power supply from a generator connected to the powernet /// public void RemovePowerStorage(PowerStorageComponent storage) { if (PowerStorageSupplierlist.Contains(storage)) { PowerStorageSupplierlist.Remove(storage); } if (PowerStorageConsumerlist.Contains(storage)) { PowerStorageSupplierlist.Remove(storage); } } #endregion Registration } }