2021-06-23 11:35:30 +02:00
using Content.Server.Atmos.EntitySystems ;
2023-08-12 22:41:55 +02:00
using Content.Server.Atmos.Monitor.Systems ;
2021-06-19 13:25:05 +02:00
using Content.Server.Atmos.Piping.Components ;
using Content.Server.Atmos.Piping.Unary.Components ;
2023-08-12 22:41:55 +02:00
using Content.Server.DeviceNetwork ;
using Content.Server.DeviceNetwork.Components ;
using Content.Server.DeviceNetwork.Systems ;
2021-06-19 13:25:05 +02:00
using Content.Server.NodeContainer ;
2023-06-28 14:28:38 +03:00
using Content.Server.NodeContainer.EntitySystems ;
2021-07-04 18:11:52 +02:00
using Content.Server.NodeContainer.Nodes ;
2023-08-12 22:41:55 +02:00
using Content.Server.Power.Components ;
2022-02-20 17:17:45 -07:00
using Content.Shared.Atmos ;
2022-02-22 21:09:01 -07:00
using Content.Shared.Atmos.Piping.Unary.Components ;
2021-06-19 13:25:05 +02:00
using JetBrains.Annotations ;
2022-02-22 21:09:01 -07:00
using Robust.Server.GameObjects ;
2023-04-23 05:05:32 +02:00
using Content.Server.Power.EntitySystems ;
2024-02-01 11:45:24 +03:00
using Content.Shared.UserInterface ;
2024-01-13 20:05:45 -08:00
using Content.Shared.Administration.Logs ;
using Content.Shared.Database ;
2024-02-11 14:19:45 +11:00
using Content.Shared.DeviceNetwork ;
2023-04-23 05:05:32 +02:00
using Content.Shared.Examine ;
2021-06-19 13:25:05 +02:00
namespace Content.Server.Atmos.Piping.Unary.EntitySystems
{
[UsedImplicitly]
2022-02-16 00:23:23 -07:00
public sealed class GasThermoMachineSystem : EntitySystem
2021-06-19 13:25:05 +02:00
{
2021-07-26 12:58:17 +02:00
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default ! ;
2022-02-22 21:09:01 -07:00
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default ! ;
2023-04-23 05:05:32 +02:00
[Dependency] private readonly PowerReceiverSystem _power = default ! ;
2023-06-28 14:28:38 +03:00
[Dependency] private readonly NodeContainerSystem _nodeContainer = default ! ;
2023-08-12 22:41:55 +02:00
[Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default ! ;
2024-01-13 20:05:45 -08:00
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default ! ;
2023-08-12 22:41:55 +02:00
2021-06-19 13:25:05 +02:00
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < GasThermoMachineComponent , AtmosDeviceUpdateEvent > ( OnThermoMachineUpdated ) ;
2023-04-23 05:05:32 +02:00
SubscribeLocalEvent < GasThermoMachineComponent , ExaminedEvent > ( OnExamined ) ;
2022-02-22 21:09:01 -07:00
// UI events
2024-01-07 08:55:22 -05:00
SubscribeLocalEvent < GasThermoMachineComponent , BeforeActivatableUIOpenEvent > ( OnBeforeOpened ) ;
2022-02-22 21:09:01 -07:00
SubscribeLocalEvent < GasThermoMachineComponent , GasThermomachineToggleMessage > ( OnToggleMessage ) ;
SubscribeLocalEvent < GasThermoMachineComponent , GasThermomachineChangeTemperatureMessage > ( OnChangeTemperature ) ;
2023-08-12 22:41:55 +02:00
// Device network
SubscribeLocalEvent < GasThermoMachineComponent , DeviceNetworkPacketEvent > ( OnPacketRecv ) ;
2021-06-19 13:25:05 +02:00
}
2024-01-07 08:55:22 -05:00
private void OnBeforeOpened ( Entity < GasThermoMachineComponent > ent , ref BeforeActivatableUIOpenEvent args )
{
DirtyUI ( ent , ent . Comp ) ;
}
2023-12-21 18:48:18 -07:00
private void OnThermoMachineUpdated ( EntityUid uid , GasThermoMachineComponent thermoMachine , ref AtmosDeviceUpdateEvent args )
2021-06-19 13:25:05 +02:00
{
2024-02-28 19:27:29 +01:00
thermoMachine . LastEnergyDelta = 0f ;
2024-02-15 02:00:21 +01:00
if ( ! ( _power . IsPowered ( uid ) & & TryComp < ApcPowerReceiverComponent > ( uid , out var receiver ) ) )
return ;
GetHeatExchangeGasMixture ( uid , thermoMachine , out var heatExchangeGasMixture ) ;
if ( heatExchangeGasMixture = = null )
2021-06-19 13:25:05 +02:00
return ;
2023-08-22 00:34:45 -07:00
float sign = Math . Sign ( thermoMachine . Cp ) ; // 1 if heater, -1 if freezer
float targetTemp = thermoMachine . TargetTemperature ;
float highTemp = targetTemp + sign * thermoMachine . TemperatureTolerance ;
2024-02-15 02:00:21 +01:00
float temp = heatExchangeGasMixture . Temperature ;
2023-08-22 00:34:45 -07:00
if ( sign * temp > = sign * highTemp ) // upper bound
thermoMachine . HysteresisState = false ; // turn off
else if ( sign * temp < sign * targetTemp ) // lower bound
thermoMachine . HysteresisState = true ; // turn on
2021-06-19 13:25:05 +02:00
2023-08-22 00:34:45 -07:00
if ( thermoMachine . HysteresisState )
targetTemp = highTemp ; // when on, target upper hysteresis bound
2024-02-15 02:00:21 +01:00
else // Hysteresis is the same as "Should this be on?"
2021-06-19 13:25:05 +02:00
{
2023-08-22 00:34:45 -07:00
// Turn dynamic load back on when power has been adjusted to not cause lights to
// blink every time this heater comes on.
//receiver.Load = 0f;
return ;
}
2023-08-12 22:41:55 +02:00
2023-08-22 00:34:45 -07:00
// Multiply power in by coefficient of performance, add that heat to gas
float dQ = thermoMachine . HeatCapacity * thermoMachine . Cp * args . dt ;
// Clamps the heat transferred to not overshoot
2024-02-15 02:00:21 +01:00
float Cin = _atmosphereSystem . GetHeatCapacity ( heatExchangeGasMixture , true ) ;
2023-08-22 00:34:45 -07:00
float dT = targetTemp - temp ;
float dQLim = dT * Cin ;
float scale = 1f ;
if ( Math . Abs ( dQ ) > Math . Abs ( dQLim ) )
{
scale = dQLim / dQ ; // reduce power consumption
thermoMachine . HysteresisState = false ; // turn off
2021-06-19 13:25:05 +02:00
}
2024-02-15 02:00:21 +01:00
2023-08-22 00:34:45 -07:00
float dQActual = dQ * scale ;
2024-02-15 02:00:21 +01:00
if ( thermoMachine . Atmospheric )
{
_atmosphereSystem . AddHeat ( heatExchangeGasMixture , dQActual ) ;
2024-02-28 19:27:29 +01:00
thermoMachine . LastEnergyDelta = dQActual ;
2024-02-15 02:00:21 +01:00
}
else
{
float dQLeak = dQActual * thermoMachine . EnergyLeakPercentage ;
float dQPipe = dQActual - dQLeak ;
_atmosphereSystem . AddHeat ( heatExchangeGasMixture , dQPipe ) ;
2024-02-28 19:27:29 +01:00
thermoMachine . LastEnergyDelta = dQPipe ;
2024-01-07 08:55:22 -05:00
2024-03-30 17:17:53 +13:00
if ( dQLeak ! = 0f & & _atmosphereSystem . GetContainingMixture ( uid , args . Grid , args . Map , excite : true ) is { } containingMixture )
2024-02-15 02:00:21 +01:00
_atmosphereSystem . AddHeat ( containingMixture , dQLeak ) ;
}
2024-01-07 08:55:22 -05:00
2023-08-22 00:34:45 -07:00
receiver . Load = thermoMachine . HeatCapacity ; // * scale; // we're not ready for dynamic load yet, see note above
}
2024-02-15 02:00:21 +01:00
/// <summary>
/// Returns the gas mixture with which the thermomachine will exchange heat (the local atmosphere if atmospheric or the inlet pipe
/// air if not). Returns null if no gas mixture is found.
/// </summary>
private void GetHeatExchangeGasMixture ( EntityUid uid , GasThermoMachineComponent thermoMachine , out GasMixture ? heatExchangeGasMixture )
{
heatExchangeGasMixture = null ;
if ( thermoMachine . Atmospheric )
{
2024-03-07 22:05:43 +01:00
heatExchangeGasMixture = _atmosphereSystem . GetContainingMixture ( uid , excite : true ) ;
2024-02-15 02:00:21 +01:00
}
else
{
2024-03-30 17:17:53 +13:00
if ( ! _nodeContainer . TryGetNode ( uid , thermoMachine . InletName , out PipeNode ? inlet ) )
2024-02-15 02:00:21 +01:00
return ;
heatExchangeGasMixture = inlet . Air ;
}
}
2023-08-22 00:34:45 -07:00
private bool IsHeater ( GasThermoMachineComponent comp )
{
return comp . Cp > = 0 ;
2021-06-19 13:25:05 +02:00
}
2023-04-23 05:05:32 +02:00
private void OnToggleMessage ( EntityUid uid , GasThermoMachineComponent thermoMachine , GasThermomachineToggleMessage args )
2022-02-22 21:09:01 -07:00
{
2024-01-13 20:05:45 -08:00
var powerState = _power . TogglePower ( uid ) ;
2024-04-26 18:16:24 +10:00
_adminLogger . Add ( LogType . AtmosPowerChanged , $"{ToPrettyString(args.Actor)} turned {(powerState ? " On " : " Off ")} {ToPrettyString(uid)}" ) ;
2023-04-23 05:05:32 +02:00
DirtyUI ( uid , thermoMachine ) ;
2022-02-22 21:09:01 -07:00
}
2023-04-23 05:05:32 +02:00
private void OnChangeTemperature ( EntityUid uid , GasThermoMachineComponent thermoMachine , GasThermomachineChangeTemperatureMessage args )
2022-02-22 21:09:01 -07:00
{
2023-08-22 00:34:45 -07:00
if ( IsHeater ( thermoMachine ) )
thermoMachine . TargetTemperature = MathF . Min ( args . Temperature , thermoMachine . MaxTemperature ) ;
else
thermoMachine . TargetTemperature = MathF . Max ( args . Temperature , thermoMachine . MinTemperature ) ;
thermoMachine . TargetTemperature = MathF . Max ( thermoMachine . TargetTemperature , Atmospherics . TCMB ) ;
2024-04-26 18:16:24 +10:00
_adminLogger . Add ( LogType . AtmosTemperatureChanged , $"{ToPrettyString(args.Actor)} set temperature on {ToPrettyString(uid)} to {thermoMachine.TargetTemperature}" ) ;
2023-04-23 05:05:32 +02:00
DirtyUI ( uid , thermoMachine ) ;
2022-02-22 21:09:01 -07:00
}
2023-09-11 21:20:46 +10:00
private void DirtyUI ( EntityUid uid , GasThermoMachineComponent ? thermoMachine , UserInterfaceComponent ? ui = null )
2022-02-22 21:09:01 -07:00
{
2023-04-23 05:05:32 +02:00
if ( ! Resolve ( uid , ref thermoMachine , ref ui , false ) )
2022-02-22 21:09:01 -07:00
return ;
2023-08-12 22:41:55 +02:00
ApcPowerReceiverComponent ? powerReceiver = null ;
if ( ! Resolve ( uid , ref powerReceiver ) )
return ;
2023-04-23 05:05:32 +02:00
2024-04-26 18:16:24 +10:00
_userInterfaceSystem . SetUiState ( uid , ThermomachineUiKey . Key ,
new GasThermomachineBoundUserInterfaceState ( thermoMachine . MinTemperature , thermoMachine . MaxTemperature , thermoMachine . TargetTemperature , ! powerReceiver . PowerDisabled , IsHeater ( thermoMachine ) ) ) ;
2023-04-23 05:05:32 +02:00
}
private void OnExamined ( EntityUid uid , GasThermoMachineComponent thermoMachine , ExaminedEvent args )
{
if ( ! args . IsInDetailsRange )
return ;
if ( Loc . TryGetString ( "gas-thermomachine-system-examined" , out var str ,
2023-08-22 00:34:45 -07:00
( "machineName" , ! IsHeater ( thermoMachine ) ? "freezer" : "heater" ) ,
( "tempColor" , ! IsHeater ( thermoMachine ) ? "deepskyblue" : "red" ) ,
2023-04-23 05:05:32 +02:00
( "temp" , Math . Round ( thermoMachine . TargetTemperature , 2 ) )
) )
args . PushMarkup ( str ) ;
2022-02-20 17:17:45 -07:00
}
2023-08-12 22:41:55 +02:00
private void OnPacketRecv ( EntityUid uid , GasThermoMachineComponent component , DeviceNetworkPacketEvent args )
{
if ( ! TryComp ( uid , out DeviceNetworkComponent ? netConn )
| | ! args . Data . TryGetValue ( DeviceNetworkConstants . Command , out var cmd ) )
return ;
var payload = new NetworkPayload ( ) ;
switch ( cmd )
{
case AtmosDeviceNetworkSystem . SyncData :
payload . Add ( DeviceNetworkConstants . Command , AtmosDeviceNetworkSystem . SyncData ) ;
payload . Add ( AtmosDeviceNetworkSystem . SyncData , new GasThermoMachineData ( component . LastEnergyDelta ) ) ;
_deviceNetwork . QueuePacket ( uid , args . SenderAddress , payload , device : netConn ) ;
return ;
}
}
2021-06-19 13:25:05 +02:00
}
}