| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339 |
- #nullable enable
- using Content.Server.NodeContainer;
- using Content.Server.NodeContainer.EntitySystems;
- using Content.Server.NodeContainer.Nodes;
- using Content.Server.Power.Components;
- using Content.Server.Power.EntitySystems;
- using Content.Server.Power.Nodes;
- using Content.Shared.Coordinates;
- using Robust.Shared.GameObjects;
- using Robust.Shared.Map;
- using Robust.Shared.Maths;
- using Robust.Shared.Timing;
- namespace Content.IntegrationTests.Tests.Power
- {
- [TestFixture]
- public sealed class PowerTest
- {
- [TestPrototypes]
- private const string Prototypes = @"
- - type: entity
- id: GeneratorDummy
- components:
- - type: NodeContainer
- nodes:
- output:
- !type:CableDeviceNode
- nodeGroupID: HVPower
- - type: PowerSupplier
- - type: Transform
- anchored: true
- - type: entity
- id: ConsumerDummy
- components:
- - type: Transform
- anchored: true
- - type: NodeContainer
- nodes:
- input:
- !type:CableDeviceNode
- nodeGroupID: HVPower
- - type: PowerConsumer
- - type: entity
- id: ChargingBatteryDummy
- components:
- - type: Transform
- anchored: true
- - type: NodeContainer
- nodes:
- output:
- !type:CableDeviceNode
- nodeGroupID: HVPower
- - type: PowerNetworkBattery
- - type: Battery
- - type: BatteryCharger
- - type: entity
- id: DischargingBatteryDummy
- components:
- - type: Transform
- anchored: true
- - type: NodeContainer
- nodes:
- output:
- !type:CableDeviceNode
- nodeGroupID: HVPower
- - type: PowerNetworkBattery
- - type: Battery
- - type: BatteryDischarger
- - type: entity
- id: FullBatteryDummy
- components:
- - type: Transform
- anchored: true
- - type: NodeContainer
- nodes:
- output:
- !type:CableDeviceNode
- nodeGroupID: HVPower
- input:
- !type:CableTerminalPortNode
- nodeGroupID: HVPower
- - type: PowerNetworkBattery
- - type: Battery
- - type: BatteryDischarger
- node: output
- - type: BatteryCharger
- node: input
- - type: entity
- id: SubstationDummy
- components:
- - type: NodeContainer
- nodes:
- input:
- !type:CableDeviceNode
- nodeGroupID: HVPower
- output:
- !type:CableDeviceNode
- nodeGroupID: MVPower
- - type: BatteryCharger
- voltage: High
- - type: BatteryDischarger
- voltage: Medium
- - type: PowerNetworkBattery
- maxChargeRate: 1000
- maxSupply: 1000
- supplyRampTolerance: 1000
- - type: Battery
- maxCharge: 1000
- startingCharge: 1000
- - type: Transform
- anchored: true
- - type: entity
- id: ApcDummy
- components:
- - type: Battery
- maxCharge: 10000
- startingCharge: 10000
- - type: PowerNetworkBattery
- maxChargeRate: 1000
- maxSupply: 1000
- supplyRampTolerance: 1000
- - type: BatteryCharger
- voltage: Medium
- - type: BatteryDischarger
- voltage: Apc
- - type: Apc
- voltage: Apc
- - type: NodeContainer
- nodes:
- input:
- !type:CableDeviceNode
- nodeGroupID: MVPower
- output:
- !type:CableDeviceNode
- nodeGroupID: Apc
- - type: Transform
- anchored: true
- - type: UserInterface
- interfaces:
- enum.ApcUiKey.Key:
- type: ApcBoundUserInterface
- - type: AccessReader
- access: [['Engineering']]
- - type: entity
- id: ApcPowerReceiverDummy
- components:
- - type: ApcPowerReceiver
- - type: ExtensionCableReceiver
- - type: Transform
- anchored: true
- ";
- /// <summary>
- /// Test small power net with a simple surplus of power over the loads.
- /// </summary>
- [Test]
- public async Task TestSimpleSurplus()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var mapSys = entityManager.System<SharedMapSystem>();
- const float loadPower = 200;
- PowerSupplierComponent supplier = default!;
- PowerConsumerComponent consumer1 = default!;
- PowerConsumerComponent consumer2 = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 3; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
- var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 1));
- var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2));
- supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
- consumer1 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt1);
- consumer2 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt2);
- // Plenty of surplus and tolerance
- supplier.MaxSupply = loadPower * 4;
- supplier.SupplyRampTolerance = loadPower * 4;
- consumer1.DrawRate = loadPower;
- consumer2.DrawRate = loadPower;
- });
- server.RunTicks(1); //let run a tick for PowerNet to process power
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // Assert both consumers fully powered
- Assert.That(consumer1.ReceivedPower, Is.EqualTo(consumer1.DrawRate).Within(0.1));
- Assert.That(consumer2.ReceivedPower, Is.EqualTo(consumer2.DrawRate).Within(0.1));
- // Assert that load adds up on supply.
- Assert.That(supplier.CurrentSupply, Is.EqualTo(loadPower * 2).Within(0.1));
- });
- });
- await pair.CleanReturnAsync();
- }
- /// <summary>
- /// Test small power net with a simple deficit of power over the loads.
- /// </summary>
- [Test]
- public async Task TestSimpleDeficit()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var mapSys = entityManager.System<SharedMapSystem>();
- const float loadPower = 200;
- PowerSupplierComponent supplier = default!;
- PowerConsumerComponent consumer1 = default!;
- PowerConsumerComponent consumer2 = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 3; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
- var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 1));
- var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2));
- supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
- consumer1 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt1);
- consumer2 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt2);
- // Too little supply, both consumers should get 33% power.
- supplier.MaxSupply = loadPower;
- supplier.SupplyRampTolerance = loadPower;
- consumer1.DrawRate = loadPower;
- consumer2.DrawRate = loadPower * 2;
- });
- server.RunTicks(1); //let run a tick for PowerNet to process power
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // Assert both consumers get 33% power.
- Assert.That(consumer1.ReceivedPower, Is.EqualTo(consumer1.DrawRate / 3).Within(0.1));
- Assert.That(consumer2.ReceivedPower, Is.EqualTo(consumer2.DrawRate / 3).Within(0.1));
- // Supply should be maxed out
- Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task TestSupplyRamp()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var mapSys = entityManager.System<SharedMapSystem>();
- var gameTiming = server.ResolveDependency<IGameTiming>();
- PowerSupplierComponent supplier = default!;
- PowerConsumerComponent consumer = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 3; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2));
- supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
- consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
- // Supply has enough total power but needs to ramp up to match.
- supplier.MaxSupply = 400;
- supplier.SupplyRampRate = 400;
- supplier.SupplyRampTolerance = 100;
- consumer.DrawRate = 400;
- });
- // Exact values can/will be off by a tick, add tolerance for that.
- var tickPeriod = (float) gameTiming.TickPeriod.TotalSeconds;
- var tickDev = 400 * tickPeriod * 1.1f;
- server.RunTicks(1);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // First tick, supply should be delivering 100 W (max tolerance) and start ramping up.
- Assert.That(supplier.CurrentSupply, Is.EqualTo(100).Within(0.1));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(100).Within(0.1));
- });
- });
- // run for 0.25 seconds (minus the previous tick)
- var ticks = (int) Math.Round(0.25 * gameTiming.TickRate) - 1;
- server.RunTicks(ticks);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // After 15 ticks (0.25 seconds), supply ramp pos should be at 100 W and supply at 100, approx.
- Assert.That(supplier.CurrentSupply, Is.EqualTo(200).Within(tickDev));
- Assert.That(supplier.SupplyRampPosition, Is.EqualTo(100).Within(tickDev));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(200).Within(tickDev));
- });
- });
- // run for 0.75 seconds
- ticks = (int) Math.Round(0.75 * gameTiming.TickRate);
- server.RunTicks(ticks);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // After 1 second total, ramp should be at 400 and supply should be at 400, everybody happy.
- Assert.That(supplier.CurrentSupply, Is.EqualTo(400).Within(tickDev));
- Assert.That(supplier.SupplyRampPosition, Is.EqualTo(400).Within(tickDev));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(400).Within(tickDev));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task TestBatteryRamp()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var gameTiming = server.ResolveDependency<IGameTiming>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- const float startingCharge = 100_000;
- PowerNetworkBatteryComponent netBattery = default!;
- BatteryComponent battery = default!;
- PowerConsumerComponent consumer = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 3; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.Owner.ToCoordinates());
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2));
- netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(generatorEnt);
- battery = entityManager.GetComponent<BatteryComponent>(generatorEnt);
- consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
- batterySys.SetMaxCharge(generatorEnt, startingCharge, battery);
- batterySys.SetCharge(generatorEnt, startingCharge, battery);
- netBattery.MaxSupply = 400;
- netBattery.SupplyRampRate = 400;
- netBattery.SupplyRampTolerance = 100;
- consumer.DrawRate = 400;
- });
- // Exact values can/will be off by a tick, add tolerance for that.
- var tickPeriod = (float) gameTiming.TickPeriod.TotalSeconds;
- var tickDev = 400 * tickPeriod * 1.1f;
- server.RunTicks(1);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // First tick, supply should be delivering 100 W (max tolerance) and start ramping up.
- Assert.That(netBattery.CurrentSupply, Is.EqualTo(100).Within(0.1));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(100).Within(0.1));
- });
- });
- // run for 0.25 seconds (minus the previous tick)
- var ticks = (int) Math.Round(0.25 * gameTiming.TickRate) - 1;
- server.RunTicks(ticks);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // After 15 ticks (0.25 seconds), supply ramp pos should be at 100 W and supply at 100, approx.
- Assert.That(netBattery.CurrentSupply, Is.EqualTo(200).Within(tickDev));
- Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(100).Within(tickDev));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(200).Within(tickDev));
- // Trivial integral to calculate expected power spent.
- const double spentExpected = (200 + 100) / 2.0 * 0.25;
- Assert.That(battery.CurrentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
- });
- });
- // run for 0.75 seconds
- ticks = (int) Math.Round(0.75 * gameTiming.TickRate);
- server.RunTicks(ticks);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // After 1 second total, ramp should be at 400 and supply should be at 400, everybody happy.
- Assert.That(netBattery.CurrentSupply, Is.EqualTo(400).Within(tickDev));
- Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(400).Within(tickDev));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(400).Within(tickDev));
- // Trivial integral to calculate expected power spent.
- const double spentExpected = (400 + 100) / 2.0 * 0.75 + 400 * 0.25;
- Assert.That(battery.CurrentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task TestNoDemandRampdown()
- {
- // checks that batteries and supplies properly ramp down if the load is disconnected/disabled.
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerSupplierComponent supplier = default!;
- PowerNetworkBatteryComponent netBattery = default!;
- BatteryComponent battery = default!;
- PowerConsumerComponent consumer = default!;
- var rampRate = 500;
- var rampTol = 100;
- var draw = 1000;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 3; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 1));
- var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
- battery = entityManager.GetComponent<BatteryComponent>(batteryEnt);
- supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
- consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
- consumer.DrawRate = draw;
- supplier.MaxSupply = draw / 2;
- supplier.SupplyRampRate = rampRate;
- supplier.SupplyRampTolerance = rampTol;
- batterySys.SetMaxCharge(batteryEnt, 100_000, battery);
- batterySys.SetCharge(batteryEnt, 100_000, battery);
- netBattery.MaxSupply = draw / 2;
- netBattery.SupplyRampRate = rampRate;
- netBattery.SupplyRampTolerance = rampTol;
- });
- server.RunTicks(1);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(supplier.CurrentSupply, Is.EqualTo(rampTol).Within(0.1));
- Assert.That(netBattery.CurrentSupply, Is.EqualTo(rampTol).Within(0.1));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(rampTol * 2).Within(0.1));
- });
- });
- server.RunTicks(60);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(supplier.CurrentSupply, Is.EqualTo(draw / 2).Within(0.1));
- Assert.That(supplier.SupplyRampPosition, Is.EqualTo(draw / 2).Within(0.1));
- Assert.That(netBattery.CurrentSupply, Is.EqualTo(draw / 2).Within(0.1));
- Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(draw / 2).Within(0.1));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(draw).Within(0.1));
- });
- });
- // now we disconnect the load;
- consumer.NetworkLoad.Enabled = false;
- server.RunTicks(60);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(supplier.CurrentSupply, Is.EqualTo(0).Within(0.1));
- Assert.That(supplier.SupplyRampPosition, Is.EqualTo(0).Within(0.1));
- Assert.That(netBattery.CurrentSupply, Is.EqualTo(0).Within(0.1));
- Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(0).Within(0.1));
- Assert.That(consumer.ReceivedPower, Is.EqualTo(0).Within(0.1));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task TestSimpleBatteryChargeDeficit()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var gameTiming = server.ResolveDependency<IGameTiming>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerSupplierComponent supplier = default!;
- BatteryComponent battery = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 3; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
- var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
- var netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
- battery = entityManager.GetComponent<BatteryComponent>(batteryEnt);
- supplier.MaxSupply = 500;
- supplier.SupplyRampTolerance = 500;
- batterySys.SetMaxCharge(batteryEnt, 100_000, battery);
- netBattery.MaxChargeRate = 1_000;
- netBattery.Efficiency = 0.5f;
- });
- // run for 0.5 seconds
- var ticks = (int) Math.Round(0.5 * gameTiming.TickRate);
- server.RunTicks(ticks);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // half a second @ 500 W = 250
- // 50% efficiency, so 125 J stored total.
- Assert.That(battery.CurrentCharge, Is.EqualTo(125).Within(0.1));
- Assert.That(supplier.CurrentSupply, Is.EqualTo(500).Within(0.1));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task TestFullBattery()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var gameTiming = server.ResolveDependency<IGameTiming>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerConsumerComponent consumer = default!;
- PowerSupplierComponent supplier = default!;
- PowerNetworkBatteryComponent netBattery = default!;
- BatteryComponent battery = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 4; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1));
- entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
- consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
- supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
- netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
- battery = entityManager.GetComponent<BatteryComponent>(batteryEnt);
- // Consumer needs 1000 W, supplier can only provide 800, battery fills in the remaining 200.
- consumer.DrawRate = 1000;
- supplier.MaxSupply = 800;
- supplier.SupplyRampTolerance = 800;
- netBattery.MaxSupply = 400;
- netBattery.SupplyRampTolerance = 400;
- netBattery.SupplyRampRate = 100_000;
- batterySys.SetMaxCharge(batteryEnt, 100_000, battery);
- batterySys.SetCharge(batteryEnt, 100_000, battery);
- });
- // Run some ticks so everything is stable.
- server.RunTicks(gameTiming.TickRate);
- // Exact values can/will be off by a tick, add tolerance for that.
- var tickPeriod = (float) gameTiming.TickPeriod.TotalSeconds;
- var tickDev = 400 * tickPeriod * 1.1f;
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(consumer.ReceivedPower, Is.EqualTo(consumer.DrawRate).Within(0.1));
- Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1));
- // Battery's current supply includes passed-through power from the supply.
- // Assert ramp position is correct to make sure it's only supplying 200 W for real.
- Assert.That(netBattery.CurrentSupply, Is.EqualTo(1000).Within(0.1));
- Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(200).Within(0.1));
- const int expectedSpent = 200;
- Assert.That(battery.CurrentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task TestFullBatteryEfficiencyPassThrough()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var gameTiming = server.ResolveDependency<IGameTiming>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerConsumerComponent consumer = default!;
- PowerSupplierComponent supplier = default!;
- PowerNetworkBatteryComponent netBattery = default!;
- BatteryComponent battery = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 4; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1));
- entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
- consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
- supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
- netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
- battery = entityManager.GetComponent<BatteryComponent>(batteryEnt);
- // Consumer needs 1000 W, supply and battery can only provide 400 each.
- // BUT the battery has 50% input efficiency, so 50% of the power of the supply gets lost.
- consumer.DrawRate = 1000;
- supplier.MaxSupply = 400;
- supplier.SupplyRampTolerance = 400;
- netBattery.MaxSupply = 400;
- netBattery.SupplyRampTolerance = 400;
- netBattery.SupplyRampRate = 100_000;
- netBattery.Efficiency = 0.5f;
- batterySys.SetMaxCharge(batteryEnt, 1_000_000, battery);
- batterySys.SetCharge(batteryEnt, 1_000_000, battery);
- });
- // Run some ticks so everything is stable.
- server.RunTicks(gameTiming.TickRate);
- // Exact values can/will be off by a tick, add tolerance for that.
- var tickPeriod = (float) gameTiming.TickPeriod.TotalSeconds;
- var tickDev = 400 * tickPeriod * 1.1f;
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(consumer.ReceivedPower, Is.EqualTo(600).Within(0.1));
- Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1));
- Assert.That(netBattery.CurrentSupply, Is.EqualTo(600).Within(0.1));
- Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(400).Within(0.1));
- const int expectedSpent = 400;
- Assert.That(battery.CurrentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task TestFullBatteryEfficiencyDemandPassThrough()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerConsumerComponent consumer1 = default!;
- PowerConsumerComponent consumer2 = default!;
- PowerSupplierComponent supplier = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Map layout here is
- // C - consumer
- // B - battery
- // G - generator
- // B - battery
- // C - consumer
- // Connected in the only way that makes sense.
- // Power only works when anchored
- for (var i = 0; i < 5; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 2));
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 2));
- entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 1));
- var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 3));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 2));
- var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 0));
- var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 4));
- consumer1 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt1);
- consumer2 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt2);
- supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
- var netBattery1 = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt1);
- var netBattery2 = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt2);
- var battery1 = entityManager.GetComponent<BatteryComponent>(batteryEnt1);
- var battery2 = entityManager.GetComponent<BatteryComponent>(batteryEnt2);
- // There are two loads, 500 W and 1000 W respectively.
- // The 500 W load is behind a 50% efficient battery,
- // so *effectively* it needs 2x as much power from the supply to run.
- // Assert that both are getting 50% power.
- // Batteries are empty and only a bridge.
- consumer1.DrawRate = 500;
- consumer2.DrawRate = 1000;
- supplier.MaxSupply = 1000;
- supplier.SupplyRampTolerance = 1000;
- batterySys.SetMaxCharge(batteryEnt1, 1_000_000, battery1);
- batterySys.SetMaxCharge(batteryEnt2, 1_000_000, battery2);
- netBattery1.MaxChargeRate = 1_000;
- netBattery2.MaxChargeRate = 1_000;
- netBattery1.Efficiency = 0.5f;
- netBattery1.MaxSupply = 1_000_000;
- netBattery2.MaxSupply = 1_000_000;
- netBattery1.SupplyRampTolerance = 1_000_000;
- netBattery2.SupplyRampTolerance = 1_000_000;
- });
- // Run some ticks so everything is stable.
- server.RunTicks(10);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(consumer1.ReceivedPower, Is.EqualTo(250).Within(0.1));
- Assert.That(consumer2.ReceivedPower, Is.EqualTo(500).Within(0.1));
- Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1));
- });
- });
- await pair.CleanReturnAsync();
- }
- /// <summary>
- /// Checks that if there is insufficient supply to meet demand, generators will run at full power instead of
- /// having generators and batteries sharing the load.
- /// </summary>
- [Test]
- public async Task TestSupplyPrioritized()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var gameTiming = server.ResolveDependency<IGameTiming>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerConsumerComponent consumer = default!;
- PowerSupplierComponent supplier1 = default!;
- PowerSupplierComponent supplier2 = default!;
- PowerNetworkBatteryComponent netBattery1 = default!;
- PowerNetworkBatteryComponent netBattery2 = default!;
- BatteryComponent battery1 = default!;
- BatteryComponent battery2 = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Layout is two generators, two batteries, and one load. As to why two: because previously this test
- // would fail ONLY if there were more than two batteries present, because each of them tries to supply
- // the unmet load, leading to a double-battery supply attempt and ramping down of power generation from
- // supplies.
- // Actual layout is Battery Supply, Load, Supply, Battery
- // Place cables
- for (var i = -2; i <= 2; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, -2));
- var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 1));
- var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, -1));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 0));
- consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
- supplier1 = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt1);
- supplier2 = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt2);
- netBattery1 = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt1);
- netBattery2 = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt2);
- battery1 = entityManager.GetComponent<BatteryComponent>(batteryEnt1);
- battery2 = entityManager.GetComponent<BatteryComponent>(batteryEnt2);
- // Consumer wants 2k, supplies can only provide 1k (500 each). Expectation is that batteries will only provide the necessary remaining 1k (500 each).
- // Previously this failed with a 2x 333 w supplies and 2x 666 w batteries.
- consumer.DrawRate = 2000;
- supplier1.MaxSupply = 500;
- supplier2.MaxSupply = 500;
- supplier1.SupplyRampTolerance = 500;
- supplier2.SupplyRampTolerance = 500;
- netBattery1.MaxSupply = 1000;
- netBattery2.MaxSupply = 1000;
- netBattery1.SupplyRampTolerance = 1000;
- netBattery2.SupplyRampTolerance = 1000;
- netBattery1.SupplyRampRate = 100_000;
- netBattery2.SupplyRampRate = 100_000;
- batterySys.SetMaxCharge(batteryEnt1, 100_000, battery1);
- batterySys.SetMaxCharge(batteryEnt2, 100_000, battery2);
- batterySys.SetCharge(batteryEnt1, 100_000, battery1);
- batterySys.SetCharge(batteryEnt2, 100_000, battery2);
- });
- // Run some ticks so everything is stable.
- server.RunTicks(60);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(consumer.ReceivedPower, Is.EqualTo(consumer.DrawRate).Within(0.1));
- Assert.That(supplier1.CurrentSupply, Is.EqualTo(supplier1.MaxSupply).Within(0.1));
- Assert.That(supplier2.CurrentSupply, Is.EqualTo(supplier2.MaxSupply).Within(0.1));
- Assert.That(netBattery1.CurrentSupply, Is.EqualTo(500).Within(0.1));
- Assert.That(netBattery2.CurrentSupply, Is.EqualTo(500).Within(0.1));
- Assert.That(netBattery2.SupplyRampPosition, Is.EqualTo(500).Within(0.1));
- Assert.That(netBattery2.SupplyRampPosition, Is.EqualTo(500).Within(0.1));
- });
- });
- await pair.CleanReturnAsync();
- }
- /// <summary>
- /// Test that power is distributed proportionally, even through batteries.
- /// </summary>
- [Test]
- public async Task TestBatteriesProportional()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerConsumerComponent consumer1 = default!;
- PowerConsumerComponent consumer2 = default!;
- PowerSupplierComponent supplier = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Map layout here is
- // C - consumer
- // B - battery
- // G - generator
- // B - battery
- // C - consumer
- // Connected in the only way that makes sense.
- // Power only works when anchored
- for (var i = 0; i < 5; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 2));
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 2));
- entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 1));
- var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 3));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 2));
- var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 0));
- var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 4));
- consumer1 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt1);
- consumer2 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt2);
- supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
- var netBattery1 = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt1);
- var netBattery2 = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt2);
- var battery1 = entityManager.GetComponent<BatteryComponent>(batteryEnt1);
- var battery2 = entityManager.GetComponent<BatteryComponent>(batteryEnt2);
- consumer1.DrawRate = 500;
- consumer2.DrawRate = 1000;
- supplier.MaxSupply = 1000;
- supplier.SupplyRampTolerance = 1000;
- batterySys.SetMaxCharge(batteryEnt1, 1_000_000, battery1);
- batterySys.SetMaxCharge(batteryEnt2, 1_000_000, battery2);
- netBattery1.MaxChargeRate = 20;
- netBattery2.MaxChargeRate = 20;
- netBattery1.MaxSupply = 1_000_000;
- netBattery2.MaxSupply = 1_000_000;
- netBattery1.SupplyRampTolerance = 1_000_000;
- netBattery2.SupplyRampTolerance = 1_000_000;
- });
- // Run some ticks so everything is stable.
- server.RunTicks(60);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // NOTE: MaxChargeRate on batteries actually skews the demand.
- // So that's why the tolerance is so high, the charge rate is so *low*,
- // and we run so many ticks to stabilize.
- Assert.That(consumer1.ReceivedPower, Is.EqualTo(333.333).Within(10));
- Assert.That(consumer2.ReceivedPower, Is.EqualTo(666.666).Within(10));
- Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task TestBatteryEngineCut()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerConsumerComponent consumer = default!;
- PowerSupplierComponent supplier = default!;
- PowerNetworkBatteryComponent netBattery = default!;
- await server.WaitPost(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 4; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
- }
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1));
- entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
- consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
- supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
- netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
- var battery = entityManager.GetComponent<BatteryComponent>(batteryEnt);
- consumer.DrawRate = 1000;
- supplier.MaxSupply = 1000;
- supplier.SupplyRampTolerance = 1000;
- netBattery.MaxSupply = 1000;
- netBattery.SupplyRampTolerance = 200;
- netBattery.SupplyRampRate = 10;
- batterySys.SetMaxCharge(batteryEnt, 100_000, battery);
- batterySys.SetCharge(batteryEnt, 100_000, battery);
- });
- // Run some ticks so everything is stable.
- server.RunTicks(5);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // Supply and consumer are fully loaded/supplied.
- Assert.That(consumer.ReceivedPower, Is.EqualTo(consumer.DrawRate).Within(0.5));
- Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.5));
- });
- // Cut off the supplier
- supplier.Enabled = false;
- // Remove tolerance on battery too.
- netBattery.SupplyRampTolerance = 5;
- });
- server.RunTicks(3);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- // Assert that network drops to 0 power and starts ramping up
- Assert.That(consumer.ReceivedPower, Is.LessThan(50).And.GreaterThan(0));
- Assert.That(netBattery.CurrentReceiving, Is.EqualTo(0));
- Assert.That(netBattery.CurrentSupply, Is.GreaterThan(0));
- });
- });
- await pair.CleanReturnAsync();
- }
- /// <summary>
- /// Test that <see cref="CableTerminalNode"/> correctly isolates two networks.
- /// </summary>
- [Test]
- public async Task TestTerminalNodeGroups()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var nodeContainer = entityManager.System<NodeContainerSystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- CableNode leftNode = default!;
- CableNode rightNode = default!;
- Node batteryInput = default!;
- Node batteryOutput = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 4; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- }
- var leftEnt = entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 0));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 1));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 2));
- var rightEnt = entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 3));
- var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1));
- entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
- var battery = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- var batteryNodeContainer = entityManager.GetComponent<NodeContainerComponent>(battery);
- if (nodeContainer.TryGetNode<CableNode>(entityManager.GetComponent<NodeContainerComponent>(leftEnt),
- "power", out var leftN))
- leftNode = leftN;
- if (nodeContainer.TryGetNode<CableNode>(entityManager.GetComponent<NodeContainerComponent>(rightEnt),
- "power", out var rightN))
- rightNode = rightN;
- if (nodeContainer.TryGetNode<Node>(batteryNodeContainer, "input", out var nInput))
- batteryInput = nInput;
- if (nodeContainer.TryGetNode<Node>(batteryNodeContainer, "output", out var nOutput))
- batteryOutput = nOutput;
- });
- // Run ticks to allow node groups to update.
- server.RunTicks(1);
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(batteryInput.NodeGroup, Is.EqualTo(leftNode.NodeGroup));
- Assert.That(batteryOutput.NodeGroup, Is.EqualTo(rightNode.NodeGroup));
- Assert.That(leftNode.NodeGroup, Is.Not.EqualTo(rightNode.NodeGroup));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task ApcChargingTest()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var batterySys = entityManager.System<BatterySystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerNetworkBatteryComponent substationNetBattery = default!;
- BatteryComponent apcBattery = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- // Power only works when anchored
- for (var i = 0; i < 3; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- }
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 0));
- entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 1));
- entityManager.SpawnEntity("CableMV", grid.Owner.ToCoordinates(0, 1));
- entityManager.SpawnEntity("CableMV", grid.Owner.ToCoordinates(0, 2));
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
- var substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.Owner.ToCoordinates(0, 1));
- var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.Owner.ToCoordinates(0, 2));
- var generatorSupplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
- substationNetBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(substationEnt);
- apcBattery = entityManager.GetComponent<BatteryComponent>(apcEnt);
- generatorSupplier.MaxSupply = 1000;
- generatorSupplier.SupplyRampTolerance = 1000;
- batterySys.SetCharge(apcEnt, 0, apcBattery);
- });
- server.RunTicks(5); //let run a few ticks for PowerNets to reevaluate and start charging apc
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(substationNetBattery.CurrentSupply, Is.GreaterThan(0)); //substation should be providing power
- Assert.That(apcBattery.CurrentCharge, Is.GreaterThan(0)); //apc battery should have gained charge
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task ApcNetTest()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var mapManager = server.ResolveDependency<IMapManager>();
- var entityManager = server.ResolveDependency<IEntityManager>();
- var batterySys = entityManager.System<BatterySystem>();
- var extensionCableSystem = entityManager.System<ExtensionCableSystem>();
- var mapSys = entityManager.System<SharedMapSystem>();
- PowerNetworkBatteryComponent apcNetBattery = default!;
- ApcPowerReceiverComponent receiver = default!;
- ApcPowerReceiverComponent unpoweredReceiver = default!;
- await server.WaitAssertion(() =>
- {
- var map = mapSys.CreateMap(out var mapId);
- var grid = mapManager.CreateGridEntity(mapId);
- const int range = 5;
- // Power only works when anchored
- for (var i = 0; i < range; i++)
- {
- mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1));
- }
- var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.Owner.ToCoordinates(0, 0));
- var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", grid.Owner.ToCoordinates(0, 0));
- // Create a powered receiver in range (range is 0 indexed)
- var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.Owner.ToCoordinates(0, range - 1));
- receiver = entityManager.GetComponent<ApcPowerReceiverComponent>(powerReceiverEnt);
- // Create an unpowered receiver outside range
- var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.Owner.ToCoordinates(0, range));
- unpoweredReceiver = entityManager.GetComponent<ApcPowerReceiverComponent>(unpoweredReceiverEnt);
- var battery = entityManager.GetComponent<BatteryComponent>(apcEnt);
- apcNetBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(apcEnt);
- extensionCableSystem.SetProviderTransferRange(apcExtensionEnt, range);
- extensionCableSystem.SetReceiverReceptionRange(powerReceiverEnt, range);
- batterySys.SetMaxCharge(apcEnt, 10000, battery); //arbitrary nonzero amount of charge
- batterySys.SetCharge(apcEnt, battery.MaxCharge, battery); //fill battery
- receiver.Load = 1; //arbitrary small amount of power
- });
- server.RunTicks(1); //let run a tick for ApcNet to process power
- await server.WaitAssertion(() =>
- {
- Assert.Multiple(() =>
- {
- Assert.That(receiver.Powered, "Receiver in range should be powered");
- Assert.That(!unpoweredReceiver.Powered, "Out of range receiver should not be powered");
- Assert.That(apcNetBattery.CurrentSupply, Is.EqualTo(1).Within(0.1));
- });
- });
- await pair.CleanReturnAsync();
- }
- }
- }
|