2022-01-01 20:56:24 -08:00
using Content.Server.Atmos.Monitor.Components ;
using Content.Server.Atmos.Piping.Components ;
2023-08-21 22:18:30 +01:00
using Content.Server.DeviceLinking.Systems ;
2022-01-01 20:56:24 -08:00
using Content.Server.DeviceNetwork ;
using Content.Server.DeviceNetwork.Components ;
using Content.Server.DeviceNetwork.Systems ;
using Content.Server.Popups ;
using Content.Server.Power.Components ;
2022-05-27 10:36:12 +10:00
using Content.Server.Power.EntitySystems ;
2022-01-01 20:56:24 -08:00
using Content.Shared.Access.Components ;
using Content.Shared.Access.Systems ;
2025-02-06 16:10:21 +11:00
using Content.Shared.Administration.Logs ;
2022-01-01 20:56:24 -08:00
using Content.Shared.Atmos ;
using Content.Shared.Atmos.Monitor ;
using Content.Shared.Atmos.Monitor.Components ;
2022-08-17 23:46:15 -07:00
using Content.Shared.Atmos.Piping.Unary.Components ;
2025-02-06 16:10:21 +11:00
using Content.Shared.Database ;
2023-08-21 22:18:30 +01:00
using Content.Shared.DeviceLinking ;
2024-02-11 14:19:45 +11:00
using Content.Shared.DeviceNetwork ;
2023-05-26 00:08:22 +02:00
using Content.Shared.DeviceNetwork.Systems ;
2022-01-01 20:56:24 -08:00
using Content.Shared.Interaction ;
2024-08-25 22:18:42 +10:00
using Content.Shared.Power ;
2023-03-24 03:09:45 +03:00
using Content.Shared.Wires ;
2022-01-01 20:56:24 -08:00
using Robust.Server.GameObjects ;
2023-10-29 04:21:02 +11:00
using Robust.Shared.Player ;
2024-09-04 20:13:17 -05:00
using System.Linq ;
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
namespace Content.Server.Atmos.Monitor.Systems ;
// AirAlarm system - specific for atmos devices, rather than
// atmos monitors.
//
// oh boy, message passing!
//
// Commands should always be sent into packet's Command
// data key. In response, a packet will be transmitted
// with the response type as its command, and the
// response data in its data key.
public sealed class AirAlarmSystem : EntitySystem
2022-01-01 20:56:24 -08:00
{
2023-08-21 22:18:30 +01:00
[Dependency] private readonly AccessReaderSystem _access = default ! ;
2025-02-06 16:10:21 +11:00
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default ! ;
2022-08-23 10:55:46 -07:00
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default ! ;
2023-08-21 22:18:30 +01:00
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default ! ;
[Dependency] private readonly DeviceNetworkSystem _deviceNet = default ! ;
[Dependency] private readonly DeviceLinkSystem _deviceLink = default ! ;
[Dependency] private readonly DeviceListSystem _deviceList = default ! ;
2022-08-23 10:55:46 -07:00
[Dependency] private readonly PopupSystem _popup = default ! ;
2023-08-21 22:18:30 +01:00
[Dependency] private readonly UserInterfaceSystem _ui = default ! ;
2022-08-23 10:55:46 -07:00
#region Device Network API
/// <summary>
/// Command to set an air alarm's mode.
/// </summary>
public const string AirAlarmSetMode = "air_alarm_set_mode" ;
// -- API --
/// <summary>
/// Set the data for an air alarm managed device.
/// </summary>
/// <param name="address">The address of the device.</param>
/// <param name="data">The data to send to the device.</param>
public void SetData ( EntityUid uid , string address , IAtmosDeviceData data )
2022-01-01 20:56:24 -08:00
{
2023-08-21 22:18:30 +01:00
_atmosDevNet . SetDeviceState ( uid , address , data ) ;
_atmosDevNet . Sync ( uid , address ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
/// <summary>
/// Broadcast a sync packet to an air alarm's local network.
/// </summary>
private void SyncAllDevices ( EntityUid uid )
{
2023-08-21 22:18:30 +01:00
_atmosDevNet . Sync ( uid , null ) ;
2022-08-23 10:55:46 -07:00
}
/// <summary>
/// Send a sync packet to a specific device from an air alarm.
/// </summary>
/// <param name="address">The address of the device.</param>
private void SyncDevice ( EntityUid uid , string address )
{
2023-08-21 22:18:30 +01:00
_atmosDevNet . Sync ( uid , address ) ;
2022-08-23 10:55:46 -07:00
}
/// <summary>
/// Register and synchronize with all devices
/// on this network.
/// </summary>
/// <param name="uid"></param>
2022-08-29 07:37:26 -07:00
private void SyncRegisterAllDevices ( EntityUid uid )
2022-08-23 10:55:46 -07:00
{
2023-08-21 22:18:30 +01:00
_atmosDevNet . Register ( uid , null ) ;
_atmosDevNet . Sync ( uid , null ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
/// <summary>
/// Synchronize all sensors on an air alarm, but only if its current tab is set to Sensors.
/// </summary>
/// <param name="uid"></param>
/// <param name="monitor"></param>
private void SyncAllSensors ( EntityUid uid , AirAlarmComponent ? monitor = null )
{
if ( ! Resolve ( uid , ref monitor ) )
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
return ;
2022-08-22 01:14:39 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
foreach ( var addr in monitor . SensorData . Keys )
2022-08-22 01:14:39 -07:00
{
2022-08-23 10:55:46 -07:00
SyncDevice ( uid , addr ) ;
2022-01-01 20:56:24 -08:00
}
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
private void SetThreshold ( EntityUid uid , string address , AtmosMonitorThresholdType type ,
AtmosAlarmThreshold threshold , Gas ? gas = null )
{
var payload = new NetworkPayload
2022-08-17 23:46:15 -07:00
{
2022-08-23 10:55:46 -07:00
[DeviceNetworkConstants.Command] = AtmosMonitorSystem . AtmosMonitorSetThresholdCmd ,
[AtmosMonitorSystem.AtmosMonitorThresholdDataType] = type ,
[AtmosMonitorSystem.AtmosMonitorThresholdData] = threshold ,
} ;
2022-08-17 23:46:15 -07:00
2022-08-23 10:55:46 -07:00
if ( gas ! = null )
2022-08-18 06:44:43 -07:00
{
2022-08-23 10:55:46 -07:00
payload . Add ( AtmosMonitorSystem . AtmosMonitorThresholdGasType , gas ) ;
}
2022-08-18 06:44:43 -07:00
2022-08-23 10:55:46 -07:00
_deviceNet . QueuePacket ( uid , address , payload ) ;
2022-08-18 06:44:43 -07:00
2022-08-23 10:55:46 -07:00
SyncDevice ( uid , address ) ;
}
2022-08-18 06:44:43 -07:00
2025-01-10 01:55:05 -05:00
private void SetAllThresholds ( EntityUid uid , string address , AtmosSensorData data )
{
var payload = new NetworkPayload
{
[DeviceNetworkConstants.Command] = AtmosMonitorSystem . AtmosMonitorSetAllThresholdsCmd ,
[AtmosMonitorSystem.AtmosMonitorAllThresholdData] = data
} ;
_deviceNet . QueuePacket ( uid , address , payload ) ;
SyncDevice ( uid , address ) ;
}
2022-08-23 10:55:46 -07:00
/// <summary>
/// Sync this air alarm's mode with the rest of the network.
/// </summary>
/// <param name="mode">The mode to sync with the rest of the network.</param>
2022-08-29 07:37:26 -07:00
private void SyncMode ( EntityUid uid , AirAlarmMode mode )
2022-08-23 10:55:46 -07:00
{
2023-08-21 22:18:30 +01:00
if ( TryComp < AtmosMonitorComponent > ( uid , out var monitor ) & & ! monitor . NetEnabled )
2022-08-23 10:55:46 -07:00
return ;
2022-08-18 06:44:43 -07:00
2022-08-23 10:55:46 -07:00
var payload = new NetworkPayload
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
[DeviceNetworkConstants.Command] = AirAlarmSetMode ,
[AirAlarmSetMode] = mode
} ;
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
_deviceNet . QueuePacket ( uid , null , payload ) ;
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
#endregion
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
#region Events
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
public override void Initialize ( )
{
SubscribeLocalEvent < AirAlarmComponent , DeviceNetworkPacketEvent > ( OnPacketRecv ) ;
SubscribeLocalEvent < AirAlarmComponent , AtmosDeviceUpdateEvent > ( OnAtmosUpdate ) ;
SubscribeLocalEvent < AirAlarmComponent , AtmosAlarmEvent > ( OnAtmosAlarm ) ;
SubscribeLocalEvent < AirAlarmComponent , PowerChangedEvent > ( OnPowerChanged ) ;
SubscribeLocalEvent < AirAlarmComponent , DeviceListUpdateEvent > ( OnDeviceListUpdate ) ;
2023-08-21 22:18:30 +01:00
SubscribeLocalEvent < AirAlarmComponent , ComponentInit > ( OnInit ) ;
SubscribeLocalEvent < AirAlarmComponent , MapInitEvent > ( OnMapInit ) ;
2022-08-23 10:55:46 -07:00
SubscribeLocalEvent < AirAlarmComponent , ComponentShutdown > ( OnShutdown ) ;
2023-08-12 22:41:55 +02:00
SubscribeLocalEvent < AirAlarmComponent , ActivateInWorldEvent > ( OnActivate ) ;
2024-01-14 08:18:39 +01:00
Subs . BuiEvents < AirAlarmComponent > ( SharedAirAlarmInterfaceKey . Key , subs = >
{
subs . Event < BoundUIClosedEvent > ( OnClose ) ;
subs . Event < AirAlarmResyncAllDevicesMessage > ( OnResyncAll ) ;
subs . Event < AirAlarmUpdateAlarmModeMessage > ( OnUpdateAlarmMode ) ;
subs . Event < AirAlarmUpdateAutoModeMessage > ( OnUpdateAutoMode ) ;
subs . Event < AirAlarmUpdateAlarmThresholdMessage > ( OnUpdateThreshold ) ;
subs . Event < AirAlarmUpdateDeviceDataMessage > ( OnUpdateDeviceData ) ;
subs . Event < AirAlarmCopyDeviceDataMessage > ( OnCopyDeviceData ) ;
} ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
private void OnDeviceListUpdate ( EntityUid uid , AirAlarmComponent component , DeviceListUpdateEvent args )
{
2022-10-01 09:36:59 -07:00
var query = GetEntityQuery < DeviceNetworkComponent > ( ) ;
foreach ( var device in args . OldDevices )
{
if ( ! query . TryGetComponent ( device , out var deviceNet ) )
{
continue ;
}
2023-08-21 22:18:30 +01:00
_atmosDevNet . Deregister ( uid , deviceNet . Address ) ;
2022-10-01 09:36:59 -07:00
}
component . ScrubberData . Clear ( ) ;
component . SensorData . Clear ( ) ;
component . VentData . Clear ( ) ;
component . KnownDevices . Clear ( ) ;
UpdateUI ( uid , component ) ;
2022-08-23 10:55:46 -07:00
SyncRegisterAllDevices ( uid ) ;
}
2022-01-01 20:56:24 -08:00
2022-10-15 15:08:15 +11:00
private void OnPowerChanged ( EntityUid uid , AirAlarmComponent component , ref PowerChangedEvent args )
2022-08-23 10:55:46 -07:00
{
2022-09-04 02:04:15 -07:00
if ( args . Powered )
2022-08-18 08:14:18 -07:00
{
2022-09-04 02:04:15 -07:00
return ;
2022-01-01 20:56:24 -08:00
}
2022-09-04 02:04:15 -07:00
ForceCloseAllInterfaces ( uid ) ;
component . CurrentModeUpdater = null ;
component . KnownDevices . Clear ( ) ;
component . ScrubberData . Clear ( ) ;
component . SensorData . Clear ( ) ;
component . VentData . Clear ( ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
private void OnClose ( EntityUid uid , AirAlarmComponent component , BoundUIClosedEvent args )
{
2024-04-26 18:16:24 +10:00
if ( ! _ui . IsUiOpen ( uid , SharedAirAlarmInterfaceKey . Key ) )
2022-08-23 10:55:46 -07:00
RemoveActiveInterface ( uid ) ;
}
2022-01-01 20:56:24 -08:00
2023-08-21 22:18:30 +01:00
private void OnInit ( EntityUid uid , AirAlarmComponent comp , ComponentInit args )
{
_deviceLink . EnsureSourcePorts ( uid , comp . DangerPort , comp . WarningPort , comp . NormalPort ) ;
}
private void OnMapInit ( EntityUid uid , AirAlarmComponent comp , MapInitEvent args )
{
// for mapped linked air alarms, start with high so when it changes for the first time it goes from high to low
// without this the output would suddenly get sent a low signal after nothing which is bad
_deviceLink . SendSignal ( uid , GetPort ( comp ) , true ) ;
}
2022-08-23 10:55:46 -07:00
private void OnShutdown ( EntityUid uid , AirAlarmComponent component , ComponentShutdown args )
{
_activeUserInterfaces . Remove ( uid ) ;
}
2023-08-12 22:41:55 +02:00
private void OnActivate ( EntityUid uid , AirAlarmComponent component , ActivateInWorldEvent args )
2022-08-23 10:55:46 -07:00
{
2024-05-31 16:26:19 -04:00
if ( ! args . Complex )
return ;
2023-03-24 03:09:45 +03:00
if ( TryComp < WiresPanelComponent > ( uid , out var panel ) & & panel . Open )
2022-01-26 00:37:34 +01:00
{
2022-08-23 10:55:46 -07:00
args . Handled = false ;
return ;
2022-01-26 00:37:34 +01:00
}
2022-08-23 10:55:46 -07:00
if ( ! this . IsPowered ( uid , EntityManager ) )
return ;
2024-04-26 18:16:24 +10:00
_ui . OpenUi ( uid , SharedAirAlarmInterfaceKey . Key , args . User ) ;
2022-08-23 10:55:46 -07:00
AddActiveInterface ( uid ) ;
SyncAllDevices ( uid ) ;
UpdateUI ( uid , component ) ;
}
private void OnResyncAll ( EntityUid uid , AirAlarmComponent component , AirAlarmResyncAllDevicesMessage args )
{
2024-04-26 18:16:24 +10:00
if ( ! AccessCheck ( uid , args . Actor , component ) )
2022-01-01 20:56:24 -08:00
{
2022-08-29 07:37:26 -07:00
return ;
2022-08-23 10:55:46 -07:00
}
2022-08-29 07:37:26 -07:00
component . KnownDevices . Clear ( ) ;
component . VentData . Clear ( ) ;
component . ScrubberData . Clear ( ) ;
component . SensorData . Clear ( ) ;
SyncRegisterAllDevices ( uid ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
private void OnUpdateAlarmMode ( EntityUid uid , AirAlarmComponent component , AirAlarmUpdateAlarmModeMessage args )
{
2024-04-26 18:16:24 +10:00
if ( AccessCheck ( uid , args . Actor , component ) )
2023-08-21 22:18:30 +01:00
{
var addr = string . Empty ;
if ( TryComp < DeviceNetworkComponent > ( uid , out var netConn ) )
{
addr = netConn . Address ;
}
2025-02-06 16:10:21 +11:00
_adminLogger . Add ( LogType . AtmosDeviceSetting , LogImpact . Medium , $"{ToPrettyString(args.Actor)} changed {ToPrettyString(uid)} mode to {args.Mode}" ) ;
2022-10-16 03:18:14 -07:00
SetMode ( uid , addr , args . Mode , false ) ;
2023-08-21 22:18:30 +01:00
}
2022-08-23 10:55:46 -07:00
else
2023-08-21 22:18:30 +01:00
{
2022-08-23 10:55:46 -07:00
UpdateUI ( uid , component ) ;
2023-08-21 22:18:30 +01:00
}
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2023-07-09 22:22:41 +03:00
private void OnUpdateAutoMode ( EntityUid uid , AirAlarmComponent component , AirAlarmUpdateAutoModeMessage args )
{
component . AutoMode = args . Enabled ;
2025-02-06 16:10:21 +11:00
_adminLogger . Add ( LogType . AtmosDeviceSetting , LogImpact . Medium , $"{ToPrettyString(args.Actor)} changed {ToPrettyString(uid)} auto mode to {args.Enabled}" ) ;
2023-07-09 22:22:41 +03:00
UpdateUI ( uid , component ) ;
}
2022-08-23 10:55:46 -07:00
private void OnUpdateThreshold ( EntityUid uid , AirAlarmComponent component , AirAlarmUpdateAlarmThresholdMessage args )
{
2024-04-26 18:16:24 +10:00
if ( AccessCheck ( uid , args . Actor , component ) )
2025-02-06 16:10:21 +11:00
{
if ( args . Gas ! = null )
_adminLogger . Add ( LogType . AtmosDeviceSetting , LogImpact . Medium , $"{ToPrettyString(args.Actor)} changed {args.Address} {args.Gas} {args.Type} threshold using {ToPrettyString(uid)}" ) ;
else
_adminLogger . Add ( LogType . AtmosDeviceSetting , LogImpact . Medium , $"{ToPrettyString(args.Actor)} changed {args.Address} {args.Type} threshold using {ToPrettyString(uid)}" ) ;
2022-08-23 10:55:46 -07:00
SetThreshold ( uid , args . Address , args . Type , args . Threshold , args . Gas ) ;
2025-02-06 16:10:21 +11:00
}
2022-08-23 10:55:46 -07:00
else
2025-02-06 16:10:21 +11:00
{
2022-08-23 10:55:46 -07:00
UpdateUI ( uid , component ) ;
2025-02-06 16:10:21 +11:00
}
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
private void OnUpdateDeviceData ( EntityUid uid , AirAlarmComponent component , AirAlarmUpdateDeviceDataMessage args )
{
2024-04-26 18:16:24 +10:00
if ( AccessCheck ( uid , args . Actor , component )
2023-08-21 22:18:30 +01:00
& & _deviceList . ExistsInDeviceList ( uid , args . Address ) )
2023-08-01 20:27:59 +02:00
{
2025-02-06 16:10:21 +11:00
_adminLogger . Add ( LogType . AtmosDeviceSetting , LogImpact . Medium , $"{ToPrettyString(args.Actor)} changed {args.Address} settings using {ToPrettyString(uid)}" ) ;
2022-08-23 10:55:46 -07:00
SetDeviceData ( uid , args . Address , args . Data ) ;
2023-08-01 20:27:59 +02:00
}
2022-08-23 10:55:46 -07:00
else
2023-08-01 20:27:59 +02:00
{
2022-08-17 23:46:15 -07:00
UpdateUI ( uid , component ) ;
2023-08-01 20:27:59 +02:00
}
2022-08-23 10:55:46 -07:00
}
2023-09-03 13:05:22 +12:00
2023-08-09 22:20:19 +04:00
private void OnCopyDeviceData ( EntityUid uid , AirAlarmComponent component , AirAlarmCopyDeviceDataMessage args )
{
2024-04-26 18:16:24 +10:00
if ( ! AccessCheck ( uid , args . Actor , component ) )
2023-08-09 22:20:19 +04:00
{
UpdateUI ( uid , component ) ;
2023-09-03 13:05:22 +12:00
return ;
2023-08-09 22:20:19 +04:00
}
switch ( args . Data )
{
case GasVentPumpData ventData :
foreach ( string addr in component . VentData . Keys )
{
2025-02-06 16:10:21 +11:00
_adminLogger . Add ( LogType . AtmosDeviceSetting , LogImpact . Medium , $"{ToPrettyString(args.Actor)} copied settings to vent {addr}" ) ;
2023-08-09 22:20:19 +04:00
SetData ( uid , addr , args . Data ) ;
}
break ;
2023-09-03 13:05:22 +12:00
2023-08-09 22:20:19 +04:00
case GasVentScrubberData scrubberData :
foreach ( string addr in component . ScrubberData . Keys )
{
2025-02-06 16:10:21 +11:00
_adminLogger . Add ( LogType . AtmosDeviceSetting , LogImpact . Medium , $"{ToPrettyString(args.Actor)} copied settings to scrubber {addr}" ) ;
2023-08-09 22:20:19 +04:00
SetData ( uid , addr , args . Data ) ;
}
2025-01-10 01:55:05 -05:00
break ;
case AtmosSensorData sensorData :
foreach ( string addr in component . SensorData . Keys )
{
SetAllThresholds ( uid , addr , sensorData ) ;
}
2023-08-09 22:20:19 +04:00
break ;
}
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
private bool AccessCheck ( EntityUid uid , EntityUid ? user , AirAlarmComponent ? component = null )
{
if ( ! Resolve ( uid , ref component ) )
return false ;
2022-08-17 23:46:15 -07:00
2023-08-21 22:18:30 +01:00
// if it has no access reader behave as if the user has AA
if ( ! TryComp < AccessReaderComponent > ( uid , out var reader ) )
return true ;
if ( user = = null )
2022-08-23 10:55:46 -07:00
return false ;
2022-01-01 20:56:24 -08:00
2023-09-03 13:05:22 +12:00
if ( ! _access . IsAllowed ( user . Value , uid , reader ) )
2022-01-01 20:56:24 -08:00
{
2022-12-19 10:41:47 +13:00
_popup . PopupEntity ( Loc . GetString ( "air-alarm-ui-access-denied" ) , user . Value , user . Value ) ;
2025-02-06 16:10:21 +11:00
_adminLogger . Add ( LogType . AtmosDeviceSetting , LogImpact . Low , $"{ToPrettyString(user)} attempted to access {ToPrettyString(uid)} without access" ) ;
2022-08-23 10:55:46 -07:00
return false ;
2022-01-01 20:56:24 -08:00
}
2022-08-23 10:55:46 -07:00
return true ;
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
private void OnAtmosAlarm ( EntityUid uid , AirAlarmComponent component , AtmosAlarmEvent args )
{
2024-04-26 18:16:24 +10:00
if ( _ui . IsUiOpen ( uid , SharedAirAlarmInterfaceKey . Key ) )
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
SyncAllDevices ( uid ) ;
2022-01-01 20:56:24 -08:00
}
2022-08-29 07:37:26 -07:00
var addr = string . Empty ;
2023-08-21 22:18:30 +01:00
if ( TryComp < DeviceNetworkComponent > ( uid , out var netConn ) )
{
2022-10-01 09:36:59 -07:00
addr = netConn . Address ;
2023-08-21 22:18:30 +01:00
}
2022-01-01 20:56:24 -08:00
2023-07-09 22:22:41 +03:00
if ( component . AutoMode )
2022-01-01 20:56:24 -08:00
{
2023-07-09 22:22:41 +03:00
if ( args . AlarmType = = AtmosAlarmType . Danger )
{
SetMode ( uid , addr , AirAlarmMode . WideFiltering , false ) ;
}
else if ( args . AlarmType = = AtmosAlarmType . Normal | | args . AlarmType = = AtmosAlarmType . Warning )
{
SetMode ( uid , addr , AirAlarmMode . Filtering , false ) ;
}
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2023-08-21 22:18:30 +01:00
if ( component . State ! = args . AlarmType )
{
TryComp < DeviceLinkSourceComponent > ( uid , out var source ) ;
// send low to old state's port
_deviceLink . SendSignal ( uid , GetPort ( component ) , false , source ) ;
// send high to new state's port, along with updating the cached state
component . State = args . AlarmType ;
_deviceLink . SendSignal ( uid , GetPort ( component ) , true , source ) ;
}
2022-08-23 10:55:46 -07:00
UpdateUI ( uid , component ) ;
}
2022-08-17 23:46:15 -07:00
2023-08-21 22:18:30 +01:00
private string GetPort ( AirAlarmComponent comp )
{
if ( comp . State = = AtmosAlarmType . Danger )
return comp . DangerPort ;
if ( comp . State = = AtmosAlarmType . Warning )
return comp . WarningPort ;
return comp . NormalPort ;
}
2022-08-23 10:55:46 -07:00
#endregion
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
#region Air Alarm Settings
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
/// <summary>
/// Set an air alarm's mode.
/// </summary>
/// <param name="origin">The origin address of this mode set. Used for network sync.</param>
/// <param name="mode">The mode to set the alarm to.</param>
/// <param name="uiOnly">Whether this change is for the UI only, or if it changes the air alarm's operating mode. Defaults to true.</param>
2022-10-16 03:18:14 -07:00
public void SetMode ( EntityUid uid , string origin , AirAlarmMode mode , bool uiOnly = true , AirAlarmComponent ? controller = null )
2022-08-23 10:55:46 -07:00
{
2022-10-16 03:18:14 -07:00
if ( ! Resolve ( uid , ref controller ) | | controller . CurrentMode = = mode )
{
return ;
}
2022-08-23 10:55:46 -07:00
controller . CurrentMode = mode ;
2022-01-01 20:56:24 -08:00
2022-08-29 07:37:26 -07:00
// setting it to UI only means we don't have
2022-08-23 10:55:46 -07:00
// to deal with the issue of not-single-owner
// alarm mode executors
if ( ! uiOnly )
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
var newMode = AirAlarmModeFactory . ModeToExecutor ( mode ) ;
if ( newMode ! = null )
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
newMode . Execute ( uid ) ;
if ( newMode is IAirAlarmModeUpdate updatedMode )
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
controller . CurrentModeUpdater = updatedMode ;
controller . CurrentModeUpdater . NetOwner = origin ;
2022-01-01 20:56:24 -08:00
}
2022-08-23 10:55:46 -07:00
else if ( controller . CurrentModeUpdater ! = null )
controller . CurrentModeUpdater = null ;
2022-01-01 20:56:24 -08:00
}
}
2022-08-23 10:55:46 -07:00
// only one air alarm in a network can use an air alarm mode
// that updates, so even if it's a ui-only change,
2022-08-29 07:37:26 -07:00
// we have to invalidate the last mode's updater and
2022-08-23 10:55:46 -07:00
// remove it because otherwise it'll execute a now
// invalid mode
else if ( controller . CurrentModeUpdater ! = null
& & controller . CurrentModeUpdater . NetOwner ! = origin )
2022-08-29 07:37:26 -07:00
{
2022-08-23 10:55:46 -07:00
controller . CurrentModeUpdater = null ;
2022-08-29 07:37:26 -07:00
}
2022-08-23 10:55:46 -07:00
UpdateUI ( uid , controller ) ;
// setting sync deals with the issue of air alarms
// in the same network needing to have the same mode
// as other alarms
2022-10-16 03:18:14 -07:00
SyncMode ( uid , mode ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
/// <summary>
/// Sets device data. Practically a wrapper around the packet sending function, SetData.
/// </summary>
/// <param name="address">The address to send the new data to.</param>
/// <param name="devData">The device data to be sent.</param>
2022-08-29 07:37:26 -07:00
private void SetDeviceData ( EntityUid uid , string address , IAtmosDeviceData devData , AirAlarmComponent ? controller = null )
2022-08-23 10:55:46 -07:00
{
2022-08-29 07:37:26 -07:00
if ( ! Resolve ( uid , ref controller ) )
{
return ;
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
devData . Dirty = true ;
SetData ( uid , address , devData ) ;
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
private void OnPacketRecv ( EntityUid uid , AirAlarmComponent controller , DeviceNetworkPacketEvent args )
{
if ( ! args . Data . TryGetValue ( DeviceNetworkConstants . Command , out string? cmd ) )
return ;
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
switch ( cmd )
{
case AtmosDeviceNetworkSystem . SyncData :
if ( ! args . Data . TryGetValue ( AtmosDeviceNetworkSystem . SyncData , out IAtmosDeviceData ? data )
| | ! controller . CanSync )
break ;
// Save into component.
// Sync data to interface.
switch ( data )
{
case GasVentPumpData ventData :
if ( ! controller . VentData . TryAdd ( args . SenderAddress , ventData ) )
controller . VentData [ args . SenderAddress ] = ventData ;
2022-08-17 20:32:31 -07:00
break ;
2022-08-23 10:55:46 -07:00
case GasVentScrubberData scrubberData :
if ( ! controller . ScrubberData . TryAdd ( args . SenderAddress , scrubberData ) )
controller . ScrubberData [ args . SenderAddress ] = scrubberData ;
break ;
case AtmosSensorData sensorData :
if ( ! controller . SensorData . TryAdd ( args . SenderAddress , sensorData ) )
controller . SensorData [ args . SenderAddress ] = sensorData ;
break ;
}
2022-01-01 20:56:24 -08:00
2022-08-23 12:04:45 -07:00
controller . KnownDevices . Add ( args . SenderAddress ) ;
2022-08-23 10:55:46 -07:00
UpdateUI ( uid , controller ) ;
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
return ;
case AirAlarmSetMode :
2022-10-16 03:18:14 -07:00
if ( ! args . Data . TryGetValue ( AirAlarmSetMode , out AirAlarmMode alarmMode ) )
break ;
2022-01-01 20:56:24 -08:00
2022-09-08 07:01:56 -07:00
SetMode ( uid , args . SenderAddress , alarmMode , uiOnly : false ) ;
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
return ;
2022-01-01 20:56:24 -08:00
}
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
#endregion
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
#region UI
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
// List of active user interfaces.
2022-08-29 07:37:26 -07:00
private readonly HashSet < EntityUid > _activeUserInterfaces = new ( ) ;
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
/// <summary>
/// Adds an active interface to be updated.
/// </summary>
2022-08-29 07:37:26 -07:00
private void AddActiveInterface ( EntityUid uid )
{
2022-08-23 10:55:46 -07:00
_activeUserInterfaces . Add ( uid ) ;
2022-08-29 07:37:26 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
/// <summary>
/// Removes an active interface from the system update loop.
/// </summary>
2022-08-29 07:37:26 -07:00
private void RemoveActiveInterface ( EntityUid uid )
{
2022-08-23 10:55:46 -07:00
_activeUserInterfaces . Remove ( uid ) ;
2022-08-29 07:37:26 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
/// <summary>
/// Force closes all interfaces currently open related to this air alarm.
/// </summary>
2022-08-29 07:37:26 -07:00
private void ForceCloseAllInterfaces ( EntityUid uid )
2022-08-23 10:55:46 -07:00
{
2024-04-26 18:16:24 +10:00
_ui . CloseUi ( uid , SharedAirAlarmInterfaceKey . Key ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2023-12-21 18:48:18 -07:00
private void OnAtmosUpdate ( EntityUid uid , AirAlarmComponent alarm , ref AtmosDeviceUpdateEvent args )
2022-08-23 10:55:46 -07:00
{
2022-08-29 07:37:26 -07:00
alarm . CurrentModeUpdater ? . Update ( uid ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
public float CalculatePressureAverage ( AirAlarmComponent alarm )
{
return alarm . SensorData . Count ! = 0
? alarm . SensorData . Values . Select ( v = > v . Pressure ) . Average ( )
: 0f ;
}
2022-08-18 06:44:43 -07:00
2022-08-23 10:55:46 -07:00
public float CalculateTemperatureAverage ( AirAlarmComponent alarm )
{
return alarm . SensorData . Count ! = 0
? alarm . SensorData . Values . Select ( v = > v . Temperature ) . Average ( )
: 0f ;
}
2024-09-04 20:13:17 -05:00
public float CalculateGasMolarConcentrationAverage ( AirAlarmComponent alarm , Gas gas , out float percentage )
{
percentage = 0f ;
var data = alarm . SensorData . Values . SelectMany ( v = > v . Gases . Where ( g = > g . Key = = gas ) ) ;
if ( data . Count ( ) = = 0 )
return 0f ;
var averageMol = data . Select ( kvp = > kvp . Value ) . Average ( ) ;
percentage = data . Select ( kvp = > kvp . Value ) . Sum ( ) / alarm . SensorData . Values . Select ( v = > v . TotalMoles ) . Sum ( ) ;
return averageMol ;
}
2022-08-18 06:44:43 -07:00
2022-08-23 10:55:46 -07:00
public void UpdateUI ( EntityUid uid , AirAlarmComponent ? alarm = null , DeviceNetworkComponent ? devNet = null , AtmosAlarmableComponent ? alarmable = null )
{
if ( ! Resolve ( uid , ref alarm , ref devNet , ref alarmable ) )
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
return ;
}
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
var pressure = CalculatePressureAverage ( alarm ) ;
var temperature = CalculateTemperatureAverage ( alarm ) ;
2024-08-25 03:01:46 +01:00
var dataToSend = new List < ( string , IAtmosDeviceData ) > ( ) ;
2022-01-01 20:56:24 -08:00
2024-08-25 03:01:46 +01:00
foreach ( var ( addr , data ) in alarm . VentData )
2022-08-23 10:55:46 -07:00
{
2024-08-25 03:01:46 +01:00
dataToSend . Add ( ( addr , data ) ) ;
}
foreach ( var ( addr , data ) in alarm . ScrubberData )
{
dataToSend . Add ( ( addr , data ) ) ;
}
foreach ( var ( addr , data ) in alarm . SensorData )
{
dataToSend . Add ( ( addr , data ) ) ;
2022-08-23 10:55:46 -07:00
}
2022-01-01 20:56:24 -08:00
2022-08-23 12:04:45 -07:00
var deviceCount = alarm . KnownDevices . Count ;
2022-08-22 05:49:51 -07:00
2022-08-23 10:55:46 -07:00
if ( ! _atmosAlarmable . TryGetHighestAlert ( uid , out var highestAlarm ) )
{
2022-08-29 07:37:26 -07:00
highestAlarm = AtmosAlarmType . Normal ;
2022-01-01 20:56:24 -08:00
}
2024-04-26 18:16:24 +10:00
_ui . SetUiState (
2022-08-23 10:55:46 -07:00
uid ,
SharedAirAlarmInterfaceKey . Key ,
2024-08-25 03:01:46 +01:00
new AirAlarmUIState ( devNet . Address , deviceCount , pressure , temperature , dataToSend , alarm . CurrentMode , highestAlarm . Value , alarm . AutoMode ) ) ;
2022-08-23 10:55:46 -07:00
}
2022-08-29 07:37:26 -07:00
private const float Delay = 8f ;
private float _timer ;
2022-01-01 20:56:24 -08:00
2022-08-23 10:55:46 -07:00
public override void Update ( float frameTime )
{
_timer + = frameTime ;
2022-08-29 07:37:26 -07:00
if ( _timer > = Delay )
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
_timer = 0f ;
foreach ( var uid in _activeUserInterfaces )
2022-01-01 20:56:24 -08:00
{
2022-08-23 10:55:46 -07:00
SyncAllSensors ( uid ) ;
2022-01-01 20:56:24 -08:00
}
}
}
2022-08-23 10:55:46 -07:00
#endregion
2022-01-01 20:56:24 -08:00
}