diff --git a/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs b/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs
index 0b5fae2255..cf884dd509 100644
--- a/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs
+++ b/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs
@@ -20,7 +20,7 @@ public sealed class AirAlarmBoundUserInterface : BoundUserInterface
{
base.Open();
- _window = new AirAlarmWindow();
+ _window = new AirAlarmWindow(Owner);
if (State != null)
{
diff --git a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml
index b8f18a8b3d..2914223be9 100644
--- a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml
+++ b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml
@@ -1,11 +1,17 @@
-
+
-
-
+
+
+
+
+
+
@@ -25,8 +31,8 @@
-
-
+
+
@@ -35,17 +41,35 @@
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
@@ -53,4 +77,4 @@
-
+
diff --git a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs
index e73c25a510..5b682e2620 100644
--- a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs
+++ b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs
@@ -1,22 +1,21 @@
-using System;
-using System.Collections.Generic;
using Content.Client.Atmos.Monitor.UI.Widgets;
using Content.Client.Message;
+using Content.Client.Stylesheets;
+using Content.Client.UserInterface.Controls;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Temperature;
using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Localization;
namespace Content.Client.Atmos.Monitor.UI;
[GenerateTypedNameReferences]
-public sealed partial class AirAlarmWindow : DefaultWindow
+public sealed partial class AirAlarmWindow : FancyWindow
{
public event Action? AtmosDeviceDataChanged;
public event Action? AtmosAlarmThresholdChanged;
@@ -25,8 +24,8 @@ public sealed partial class AirAlarmWindow : DefaultWindow
public event Action? ResyncAllRequested;
public event Action? AirAlarmTabChange;
- private Label _address => CDeviceAddress;
- private Label _deviceTotal => CDeviceTotal;
+ private RichTextLabel _address => CDeviceAddress;
+ private RichTextLabel _deviceTotal => CDeviceTotal;
private RichTextLabel _pressure => CPressureLabel;
private RichTextLabel _temperature => CTemperatureLabel;
private RichTextLabel _alarmState => CStatusLabel;
@@ -45,7 +44,7 @@ public sealed partial class AirAlarmWindow : DefaultWindow
private OptionButton _modes => CModeButton;
- public AirAlarmWindow()
+ public AirAlarmWindow(ClientUserInterfaceComponent component)
{
RobustXamlLoader.Load(this);
@@ -88,15 +87,19 @@ public sealed partial class AirAlarmWindow : DefaultWindow
_sensors.Clear();
ResyncAllRequested!.Invoke();
};
+
+ EntityView.Sprite = IoCManager.Resolve().GetComponent(component.Owner);
}
public void UpdateState(AirAlarmUIState state)
{
- _address.Text = state.Address;
- _deviceTotal.Text = $"{state.DeviceCount}";
+ _address.SetMarkup(state.Address);
+ _deviceTotal.SetMarkup($"{state.DeviceCount}");
_pressure.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure", ("pressure", $"{state.PressureAverage:0.##}")));
_temperature.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature", ("tempC", $"{TemperatureHelpers.KelvinToCelsius(state.TemperatureAverage):0.#}"), ("temperature", $"{state.TemperatureAverage:0.##}")));
- _alarmState.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state", ("state", $"{state.AlarmType}")));
+ _alarmState.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state",
+ ("color", ColorForAlarm(state.AlarmType)),
+ ("state", $"{state.AlarmType}")));
UpdateModeSelector(state.Mode);
foreach (var (addr, dev) in state.DeviceData)
{
@@ -159,4 +162,26 @@ public sealed partial class AirAlarmWindow : DefaultWindow
break;
}
}
+
+ public static Color ColorForThreshold(float amount, AtmosAlarmThreshold threshold)
+ {
+ threshold.CheckThreshold(amount, out AtmosAlarmType curAlarm);
+ return ColorForAlarm(curAlarm);
+ }
+
+ public static Color ColorForAlarm(AtmosAlarmType curAlarm)
+ {
+ if(curAlarm == AtmosAlarmType.Danger)
+ {
+ return StyleNano.DangerousRedFore;
+ }
+ else if(curAlarm == AtmosAlarmType.Warning)
+ {
+ return StyleNano.ConcerningOrangeFore;
+ }
+
+ return StyleNano.GoodGreenFore;
+ }
+
+
}
diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/PumpControl.xaml b/Content.Client/Atmos/Monitor/UI/Widgets/PumpControl.xaml
index 20bb895596..ad0e31fbfd 100644
--- a/Content.Client/Atmos/Monitor/UI/Widgets/PumpControl.xaml
+++ b/Content.Client/Atmos/Monitor/UI/Widgets/PumpControl.xaml
@@ -4,7 +4,7 @@
-
+
diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/ScrubberControl.xaml b/Content.Client/Atmos/Monitor/UI/Widgets/ScrubberControl.xaml
index 145f68de50..9a147b29cb 100644
--- a/Content.Client/Atmos/Monitor/UI/Widgets/ScrubberControl.xaml
+++ b/Content.Client/Atmos/Monitor/UI/Widgets/ScrubberControl.xaml
@@ -3,7 +3,7 @@
Orientation="Vertical" Margin="2 0 2 4">
-
+
@@ -25,7 +25,7 @@
-
+
diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml b/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml
index f0760dc47d..b90ca3f1f6 100644
--- a/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml
+++ b/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml
@@ -1,41 +1,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml.cs b/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml.cs
index d4ce72e789..fa7cb970cd 100644
--- a/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml.cs
+++ b/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml.cs
@@ -17,7 +17,7 @@ public sealed partial class SensorInfo : BoxContainer
private ThresholdControl _pressureThreshold;
private ThresholdControl _temperatureThreshold;
private Dictionary _gasThresholds = new();
- private Dictionary _gasLabels = new();
+ private Dictionary _gasLabels = new();
public SensorInfo(AtmosSensorData data, string address)
{
@@ -27,22 +27,42 @@ public sealed partial class SensorInfo : BoxContainer
SensorAddress.Title = $"{address} : {data.AlarmState}";
- PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure", ("pressure", $"{data.Pressure:0.##}")));
- TemperatureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature", ("tempC", $"{TemperatureHelpers.KelvinToCelsius(data.Temperature):0.#}"), ("temperature", $"{data.Temperature:0.##}")));
- AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state", ("state", $"{data.AlarmState}")));
+ AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
+ ("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)),
+ ("state", $"{data.AlarmState}")));
+ PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure-indicator",
+ ("color", AirAlarmWindow.ColorForThreshold(data.Pressure, data.PressureThreshold)),
+ ("pressure", $"{data.Pressure:0.##}")));
+ TemperatureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature-indicator",
+ ("color", AirAlarmWindow.ColorForThreshold(data.Temperature, data.TemperatureThreshold)),
+ ("tempC", $"{TemperatureHelpers.KelvinToCelsius(data.Temperature):0.#}"),
+ ("temperature", $"{data.Temperature:0.##}")));
foreach (var (gas, amount) in data.Gases)
{
- var label = new Label();
- label.Text = Loc.GetString("air-alarm-ui-gases", ("gas", $"{gas}"),
+ var label = new RichTextLabel();
+
+ var fractionGas = amount / data.TotalMoles;
+ label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", ("gas", $"{gas}"),
+ ("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"),
- ("percentage", $"{(100 * amount / data.TotalMoles):0.##}"));
+ ("percentage", $"{(100 * fractionGas):0.##}")));
GasContainer.AddChild(label);
_gasLabels.Add(gas, label);
+
+ var threshold = data.GasThresholds[gas];
+ var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
+ gasThresholdControl.Margin = new Thickness(20, 2, 2, 2);
+ gasThresholdControl.ThresholdDataChanged += (type, threshold, arg3) =>
+ {
+ OnThresholdUpdate!(_address, type, threshold, arg3);
+ };
+
+ _gasThresholds.Add(gas, gasThresholdControl);
+ GasContainer.AddChild(gasThresholdControl);
}
- _pressureThreshold =
- new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-pressure-title"), data.PressureThreshold, AtmosMonitorThresholdType.Pressure);
+ _pressureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-pressure-title"), data.PressureThreshold, AtmosMonitorThresholdType.Pressure);
PressureThresholdContainer.AddChild(_pressureThreshold);
_temperatureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-temperature-title"), data.TemperatureThreshold,
AtmosMonitorThresholdType.Temperature);
@@ -60,23 +80,24 @@ public sealed partial class SensorInfo : BoxContainer
foreach (var (gas, threshold) in data.GasThresholds)
{
- var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
- gasThresholdControl.ThresholdDataChanged += (type, threshold, arg3) =>
- {
- OnThresholdUpdate!(_address, type, threshold, arg3);
- };
-
- _gasThresholds.Add(gas, gasThresholdControl);
- GasThresholds.AddChild(gasThresholdControl);
- }
+ }
}
public void ChangeData(AtmosSensorData data)
{
SensorAddress.Title = $"{_address} : {data.AlarmState}";
- PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure", ("pressure", $"{data.Pressure:0.##}")));
- TemperatureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature", ("tempC", $"{TemperatureHelpers.KelvinToCelsius(data.Temperature):0.#}"), ("temperature", $"{data.Temperature:0.##}")));
- AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state", ("state", $"{data.AlarmState}")));
+
+ AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
+ ("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)),
+ ("state", $"{data.AlarmState}")));
+
+ PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure-indicator",
+ ("color", AirAlarmWindow.ColorForThreshold(data.Pressure, data.PressureThreshold)),
+ ("pressure", $"{data.Pressure:0.##}")));
+ TemperatureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature-indicator",
+ ("color", AirAlarmWindow.ColorForThreshold(data.Temperature, data.TemperatureThreshold)),
+ ("tempC", $"{TemperatureHelpers.KelvinToCelsius(data.Temperature):0.#}"),
+ ("temperature", $"{data.Temperature:0.##}")));
foreach (var (gas, amount) in data.Gases)
{
@@ -85,13 +106,15 @@ public sealed partial class SensorInfo : BoxContainer
continue;
}
- label.Text = Loc.GetString("air-alarm-ui-gases", ("gas", $"{gas}"),
+ var fractionGas = amount / data.TotalMoles;
+ label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", ("gas", $"{gas}"),
+ ("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"),
- ("percentage", $"{(100 * amount / data.TotalMoles):0.##}"));
+ ("percentage", $"{(100 * fractionGas):0.##}")));
}
- _pressureThreshold.UpdateThresholdData(data.PressureThreshold);
- _temperatureThreshold.UpdateThresholdData(data.TemperatureThreshold);
+ _pressureThreshold.UpdateThresholdData(data.PressureThreshold, data.Pressure);
+ _temperatureThreshold.UpdateThresholdData(data.TemperatureThreshold, data.Temperature);
foreach (var (gas, control) in _gasThresholds)
{
if (!data.GasThresholds.TryGetValue(gas, out var threshold))
@@ -99,7 +122,8 @@ public sealed partial class SensorInfo : BoxContainer
continue;
}
- control.UpdateThresholdData(threshold);
+ control.UpdateThresholdData(threshold, data.Gases[gas] / data.TotalMoles);
}
}
-}
+
+ }
diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdBoundControl.xaml b/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdBoundControl.xaml
new file mode 100644
index 0000000000..3885127384
--- /dev/null
+++ b/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdBoundControl.xaml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdBoundControl.xaml.cs b/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdBoundControl.xaml.cs
new file mode 100644
index 0000000000..26782ab424
--- /dev/null
+++ b/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdBoundControl.xaml.cs
@@ -0,0 +1,95 @@
+using Content.Client.Message;
+using Content.Shared.Atmos;
+using Content.Shared.Atmos.Monitor;
+using Content.Shared.Temperature;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Atmos.Monitor.UI.Widgets;
+
+[GenerateTypedNameReferences]
+public sealed partial class ThresholdBoundControl : BoxContainer
+{
+ // raw values to use in thresholds, prefer these
+ // over directly setting Modified(Value/LastValue)
+ // when working with the FloatSpinBox
+ private float _value;
+
+ // convenience thing for getting multiplied values
+ // and also setting value to a usable value
+ private float ScaledValue
+ {
+ get => _value * _uiValueScale;
+ set => _value = value / _uiValueScale;
+ }
+
+ private float _uiValueScale;
+
+ public event Action? OnValidBoundChanged;
+ public Action? OnBoundChanged;
+ public Action? OnBoundEnabled;
+
+ public void SetValue(float value)
+ {
+ _value = value;
+ CSpinner.Value = (float) ScaledValue!;
+ }
+
+ public void SetEnabled(bool enabled)
+ {
+ CBoundEnabled.Pressed = enabled;
+
+ if (enabled)
+ {
+ CBoundLabel.RemoveStyleClass("Disabled");
+ }
+ else
+ {
+ CBoundLabel.SetOnlyStyleClass("Disabled");
+ }
+ }
+
+ public void SetWarningState(AtmosAlarmType alarm)
+ {
+ if(alarm == AtmosAlarmType.Normal)
+ {
+ CBoundLabel.FontColorOverride = null;
+ }
+ else
+ {
+ CBoundLabel.FontColorOverride = AirAlarmWindow.ColorForAlarm(alarm);
+ }
+ }
+
+ public ThresholdBoundControl(string controlLabel, float value, float uiValueScale = 1)
+ {
+ RobustXamlLoader.Load(this);
+
+ _uiValueScale = uiValueScale > 0 ? uiValueScale : 1;
+ _value = value;
+
+ CBoundLabel.Text = controlLabel;
+
+ CSpinner.Value = ScaledValue;
+ CBoundEnabled.Pressed = _value != null;
+
+ CSpinner.OnValueChanged += SpinnerValueChanged;
+ CBoundEnabled.OnToggled += CheckboxToggled;
+ }
+
+ private void SpinnerValueChanged(FloatSpinBox.FloatSpinBoxEventArgs args)
+ {
+ // ensure that the value in the spinbox is transformed
+ ScaledValue = args.Value;
+ // set the value in the scope above
+ OnBoundChanged!(_value);
+ OnValidBoundChanged!.Invoke();
+ }
+
+ private void CheckboxToggled(BaseButton.ButtonToggledEventArgs args)
+ {
+ OnBoundEnabled!(args.Pressed);
+ OnValidBoundChanged!.Invoke();
+ }
+}
diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdControl.xaml b/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdControl.xaml
index c6cdd8c987..0f53673da1 100644
--- a/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdControl.xaml
+++ b/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdControl.xaml
@@ -3,15 +3,15 @@
Orientation="Vertical" Margin="0 0 0 4">
-
+
-
+
-
+
diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdControl.xaml.cs b/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdControl.xaml.cs
index 54672f995a..78c73fa573 100644
--- a/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdControl.xaml.cs
+++ b/Content.Client/Atmos/Monitor/UI/Widgets/ThresholdControl.xaml.cs
@@ -23,7 +23,7 @@ public sealed partial class ThresholdControl : BoxContainer
public event Action? ThresholdDataChanged;
private CollapsibleHeading _name => CName;
- private CheckBox _ignore => CIgnore;
+ private CheckBox _enabled => CEnabled;
private BoxContainer _dangerBounds => CDangerBounds;
private BoxContainer _warningBounds => CWarningBounds;
private ThresholdBoundControl _upperBoundControl;
@@ -46,24 +46,14 @@ public sealed partial class ThresholdControl : BoxContainer
// i miss rust macros
- _upperBoundControl = new ThresholdBoundControl("upper-bound", _threshold.UpperBound, modifier);
- _upperBoundControl.OnBoundChanged += value =>
+ _upperBoundControl = new ThresholdBoundControl(LabelForBound("upper-bound"), _threshold.UpperBound.Value, modifier);
+ _upperBoundControl.OnBoundChanged += (value) =>
{
- // a lot of threshold logic is baked into the properties,
- // so setting this just returns if a change occurred or not
- _threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Upper, value);
- return _threshold.UpperBound;
+ _threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, value);
};
- _upperBoundControl.OnBoundEnabled += () =>
+ _upperBoundControl.OnBoundEnabled += (isEnabled) =>
{
- var value = 0f;
-
- if (_threshold.LowerWarningBound != null)
- value = (float) _threshold.LowerWarningBound + 0.1f;
- else if (_threshold.LowerBound != null)
- value = (float) _threshold.LowerBound + 0.1f;
-
- return value;
+ _threshold.SetEnabled(AtmosMonitorLimitType.UpperDanger, isEnabled);
};
_upperBoundControl.OnValidBoundChanged += () =>
{
@@ -71,227 +61,101 @@ public sealed partial class ThresholdControl : BoxContainer
};
_dangerBounds.AddChild(_upperBoundControl);
- _lowerBoundControl = new ThresholdBoundControl("lower-bound", _threshold.LowerBound, modifier);
+ _lowerBoundControl = new ThresholdBoundControl(LabelForBound("lower-bound"), _threshold.LowerBound.Value, modifier);
_lowerBoundControl.OnBoundChanged += value =>
{
- _threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, value);
- return _threshold.LowerBound;
+ _threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, value);
};
- _lowerBoundControl.OnBoundEnabled += () =>
+ _lowerBoundControl.OnBoundEnabled += (isEnabled) =>
{
- var value = 0f;
-
- if (_threshold.UpperWarningBound != null)
- value = (float) _threshold.UpperWarningBound - 0.1f;
- else if (_threshold.UpperBound != null)
- value = (float) _threshold.UpperBound - 0.1f;
-
- return value;
+ _threshold.SetEnabled(AtmosMonitorLimitType.LowerDanger, isEnabled);
};
_lowerBoundControl.OnValidBoundChanged += () =>
+ {
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
+ };
_dangerBounds.AddChild(_lowerBoundControl);
- _upperWarningBoundControl = new ThresholdBoundControl("upper-warning-bound", _threshold.UpperWarningBound, modifier);
+ _upperWarningBoundControl = new ThresholdBoundControl(LabelForBound("upper-warning-bound"), _threshold.UpperWarningBound.Value, modifier);
_upperWarningBoundControl.OnBoundChanged += value =>
{
- _threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Upper, value);
- return _threshold.UpperWarningBound;
+ _threshold.SetLimit(AtmosMonitorLimitType.UpperWarning, value);
};
- _upperWarningBoundControl.OnBoundEnabled += () =>
+ _upperWarningBoundControl.OnBoundEnabled += (isEnabled) =>
{
- var value = 0f;
-
- if (_threshold.LowerWarningBound != null)
- value = (float) _threshold.LowerWarningBound + 0.1f;
- else if (_threshold.LowerBound != null)
- value = (float) _threshold.LowerBound + 0.1f;
-
- return value;
+ _threshold.SetEnabled(AtmosMonitorLimitType.UpperWarning, isEnabled);
};
_upperWarningBoundControl.OnValidBoundChanged += () =>
+ {
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
+ };
_warningBounds.AddChild(_upperWarningBoundControl);
- _lowerWarningBoundControl = new ThresholdBoundControl("lower-warning-bound", _threshold.LowerWarningBound, modifier);
+ _lowerWarningBoundControl = new ThresholdBoundControl(LabelForBound("lower-warning-bound"), _threshold.LowerWarningBound.Value, modifier);
_lowerWarningBoundControl.OnBoundChanged += value =>
{
- _threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Lower, value);
- return _threshold.LowerWarningBound;
+ _threshold.SetLimit(AtmosMonitorLimitType.LowerWarning, value);
};
- _lowerWarningBoundControl.OnBoundEnabled += () =>
+ _lowerWarningBoundControl.OnBoundEnabled += (isEnabled) =>
{
- var value = 0f;
-
- if (_threshold.UpperWarningBound != null)
- value = (float) _threshold.UpperWarningBound - 0.1f;
- else if (_threshold.UpperBound != null)
- value = (float) _threshold.UpperBound - 0.1f;
-
- return value;
+ _threshold.SetEnabled(AtmosMonitorLimitType.LowerWarning, isEnabled);
};
_lowerWarningBoundControl.OnValidBoundChanged += () =>
+ {
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
+ };
_warningBounds.AddChild(_lowerWarningBoundControl);
- _ignore.OnToggled += args =>
+ _enabled.OnToggled += args =>
{
- _threshold.Ignore = args.Pressed;
+ _threshold.Ignore = !args.Pressed;
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
};
- _ignore.Pressed = _threshold.Ignore;
+ _enabled.Pressed = !_threshold.Ignore;
}
- public void UpdateThresholdData(AtmosAlarmThreshold threshold)
+ private String LabelForBound(string boundType) // _value * _modifier;
- set => _value = value / _modifier;
+ if(which == AtmosMonitorThresholdBound.Upper) upperDangerState = alarm;
+ else lowerDangerState = alarm;
+ }
+ else if(alarm == AtmosAlarmType.Warning)
+ {
+ if(which == AtmosMonitorThresholdBound.Upper) upperWarningState = alarm;
+ else lowerWarningState = alarm;
}
- private float ModifiedLastValue
- {
- get => _lastValue * _modifier;
- set => _lastValue = value / _modifier;
- }
+ _upperBoundControl.SetValue(threshold.UpperBound.Value);
+ _upperBoundControl.SetEnabled(threshold.UpperBound.Enabled);
+ _upperBoundControl.SetWarningState(upperDangerState);
- private float _modifier;
+ _lowerBoundControl.SetValue(threshold.LowerBound.Value);
+ _lowerBoundControl.SetEnabled(threshold.LowerBound.Enabled);
+ _lowerBoundControl.SetWarningState(lowerDangerState);
- private FloatSpinBox _bound;
- private CheckBox _boundEnabled;
+ _upperWarningBoundControl.SetValue(threshold.UpperWarningBound.Value);
+ _upperWarningBoundControl.SetEnabled(threshold.UpperWarningBound.Enabled);
+ _upperWarningBoundControl.SetWarningState(upperWarningState);
- public event Action? OnValidBoundChanged;
- public Func? OnBoundChanged;
- public Func? OnBoundEnabled;
+ _lowerWarningBoundControl.SetValue(threshold.LowerWarningBound.Value);
+ _lowerWarningBoundControl.SetEnabled(threshold.LowerWarningBound.Enabled);
+ _lowerWarningBoundControl.SetWarningState(lowerWarningState);
- public void SetValue(float? value)
- {
- _value = value;
-
- if (_value == null)
- {
- _boundEnabled.Pressed = false;
- _bound.Value = 0;
- }
- else
- {
- _boundEnabled.Pressed = true;
- _bound.Value = (float) ModifiedValue!;
- }
- }
-
- // Modifier indicates what factor the value should be multiplied by.
- // Mostly useful to convert tiny decimals to human-readable 'percentages'
- // (yes it's still a float, but floatspinbox unfucks that)
- public ThresholdBoundControl(string name, float? value, float modifier = 1)
- {
- _modifier = modifier > 0 ? modifier : 1;
- _value = value;
-
- HorizontalExpand = true;
- Orientation = LayoutOrientation.Vertical;
-
- AddChild(new Label { Text = Loc.GetString($"air-alarm-ui-thresholds-{name}") });
- _bound = new FloatSpinBox(.01f, 2);
- AddChild(_bound);
-
- _boundEnabled = new CheckBox
- {
- Text = Loc.GetString("Enabled")
- };
- AddChild(_boundEnabled);
-
- _bound.Value = ModifiedValue ?? 0;
- _lastValue = _value ?? 0;
- _boundEnabled.Pressed = _value != null;
-
- _bound.OnValueChanged += ChangeValue;
- _bound.IsValid += ValidateThreshold;
- _boundEnabled.OnToggled += ToggleBound;
- }
-
- private void ChangeValue(FloatSpinBox.FloatSpinBoxEventArgs args)
- {
- // ensure that the value in the spinbox is transformed
- ModifiedValue = args.Value;
- // set the value in the scope above
- var value = OnBoundChanged!(_value);
- // is the value not null, or has it changed?
- if (value != null || value != _lastValue)
- {
- _value = value;
- _lastValue = (float) value!;
- OnValidBoundChanged!.Invoke();
- }
- // otherwise, just set it to the last known value
- else
- {
- _value = _lastValue;
- _bound.Value = ModifiedLastValue;
- }
- }
-
- private void ToggleBound(BaseButton.ButtonToggledEventArgs args)
- {
- if (args.Pressed)
- {
- var value = OnBoundChanged!(_lastValue);
-
- if (value != _lastValue)
- {
- value = OnBoundChanged!(OnBoundEnabled!());
-
- if (value == null || value < 0)
- {
- // TODO: Improve UX here, this is ass
- // basically this implies that the bound
- // you currently have is too aggressive
- // for the other set of values, so a
- // default value (which is +/-0.1) can't
- // be used
- _boundEnabled.Pressed = false;
- return;
- }
- }
-
- _value = value;
-
- _bound.Value = (float) ModifiedValue!;
- _lastValue = (float) _value;
- }
- else
- {
- _value = null;
- _bound.Value = 0f;
- OnBoundChanged!(_value);
- }
-
- OnValidBoundChanged!.Invoke();
- }
-
- private bool ValidateThreshold(float value)
- {
- return _value != null && value >= 0;
- }
+ _enabled.Pressed = !threshold.Ignore;
}
}
diff --git a/Content.Client/UserInterface/Controls/FancyWindow.xaml.cs b/Content.Client/UserInterface/Controls/FancyWindow.xaml.cs
index ab150b4156..ec1d7a89c8 100644
--- a/Content.Client/UserInterface/Controls/FancyWindow.xaml.cs
+++ b/Content.Client/UserInterface/Controls/FancyWindow.xaml.cs
@@ -8,6 +8,8 @@ namespace Content.Client.UserInterface.Controls
[Virtual]
public partial class FancyWindow : BaseWindow
{
+ private const int DRAG_MARGIN_SIZE = 7;
+
public FancyWindow()
{
RobustXamlLoader.Load(this);
@@ -24,7 +26,30 @@ namespace Content.Client.UserInterface.Controls
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
{
- return DragMode.Move;
+ var mode = DragMode.Move;
+
+ if (Resizable)
+ {
+ if (relativeMousePos.Y < DRAG_MARGIN_SIZE)
+ {
+ mode = DragMode.Top;
+ }
+ else if (relativeMousePos.Y > Size.Y - DRAG_MARGIN_SIZE)
+ {
+ mode = DragMode.Bottom;
+ }
+
+ if (relativeMousePos.X < DRAG_MARGIN_SIZE)
+ {
+ mode |= DragMode.Left;
+ }
+ else if (relativeMousePos.X > Size.X - DRAG_MARGIN_SIZE)
+ {
+ mode |= DragMode.Right;
+ }
+ }
+
+ return mode;
}
}
}
diff --git a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs
index 98d5ac9222..fb483ebe75 100644
--- a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs
+++ b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs
@@ -12,10 +12,14 @@ namespace Content.IntegrationTests.Tests.Atmos
private const string Prototypes = @"
- type: alarmThreshold
id: testThreshold
- upperBound: 5
- lowerBound: 1
- upperWarnAround: 0.5
- lowerWarnAround: 1.5
+ upperBound: !type:AlarmThresholdSetting
+ threshold: 5
+ lowerBound: !type:AlarmThresholdSetting
+ threshold: 1
+ upperWarnAround: !type:AlarmThresholdSetting
+ threshold: 0.5
+ lowerWarnAround: !type:AlarmThresholdSetting
+ threshold: 1.5
";
[Test]
@@ -35,57 +39,100 @@ namespace Content.IntegrationTests.Tests.Atmos
await server.WaitAssertion(() =>
{
// ensure upper/lower bounds are calculated
- Assert.That(threshold.UpperWarningBound, Is.EqualTo(5f * 0.5f));
- Assert.That(threshold.LowerWarningBound, Is.EqualTo(1f * 1.5f));
+ Assert.That(threshold.UpperWarningBound.Value, Is.EqualTo(5f * 0.5f));
+ Assert.That(threshold.LowerWarningBound.Value, Is.EqualTo(1f * 1.5f));
// ensure that setting bounds to zero/
- // negative numbers is an invalid
- // set
- threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Upper, 0);
- Assert.That(threshold.UpperBound, Is.EqualTo(5f));
- threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Upper, -1);
- Assert.That(threshold.UpperBound, Is.EqualTo(5f));
+ // negative numbers is an invalid set
+ {
+ threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, 0f);
+ Assert.That(threshold.UpperBound.Value, Is.EqualTo(5f));
+ threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, -1f);
+ Assert.That(threshold.UpperBound.Value, Is.EqualTo(5f));
- threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, 0);
- Assert.That(threshold.LowerBound, Is.EqualTo(1f));
- threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, -1);
- Assert.That(threshold.LowerBound, Is.EqualTo(1f));
+ threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, 0f);
+ Assert.That(threshold.LowerBound.Value, Is.EqualTo(1f));
+ threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, -1f);
+ Assert.That(threshold.LowerBound.Value, Is.EqualTo(1f));
+ }
// test if making the lower bound higher
- // than upper is invalid
- // aka just returns the previous value
- // instead of setting it to null
- threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, 6f);
- Assert.That(threshold.LowerBound, Is.EqualTo(1f));
+ // than upper will adjust the upper value
+ {
+ threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, 5f);
+ threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, 6f);
+ Assert.That(threshold.LowerBound.Value, Is.LessThanOrEqualTo(threshold.UpperBound.Value));
+ }
// same as above, sets it lower
- threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Upper, 0.5f);
- Assert.That(threshold.UpperBound, Is.EqualTo(5f));
+ {
+ threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, 5f);
+ threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, 6f);
+ threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, 1f);
+ Assert.That(threshold.LowerBound.Value, Is.LessThanOrEqualTo(threshold.UpperBound.Value));
+ }
- threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Upper, threshold.UpperBound + 1);
- Assert.That(threshold.UpperWarningPercentage, Is.EqualTo(0.5f));
- threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Lower, threshold.LowerBound - 1);
- Assert.That(threshold.LowerWarningPercentage, Is.EqualTo(1.5f));
+ // Check that the warning percentage is calculated correcly
+ {
+ threshold.SetLimit(AtmosMonitorLimitType.UpperWarning, threshold.UpperBound.Value * 0.5f);
+ Assert.That(threshold.UpperWarningPercentage.Value, Is.EqualTo(0.5f));
- threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Upper, threshold.LowerBound - 1);
- Assert.That(threshold.UpperWarningPercentage, Is.EqualTo(0.5f));
+ threshold.SetLimit(AtmosMonitorLimitType.LowerWarning, threshold.LowerBound.Value * 1.5f);
+ Assert.That(threshold.LowerWarningPercentage.Value, Is.EqualTo(1.5f));
- threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Lower, threshold.UpperBound + 1);
- Assert.That(threshold.LowerWarningPercentage, Is.EqualTo(1.5f));
+ threshold.SetLimit(AtmosMonitorLimitType.UpperWarning, threshold.UpperBound.Value * 0.5f);
+ Assert.That(threshold.UpperWarningPercentage.Value, Is.EqualTo(0.5f));
- threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Upper, null);
- threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Lower, null);
+ threshold.SetLimit(AtmosMonitorLimitType.LowerWarning, threshold.LowerBound.Value * 1.5f);
+ Assert.That(threshold.LowerWarningPercentage.Value, Is.EqualTo(1.5f));
+ }
- Assert.That(threshold.UpperWarningBound, Is.EqualTo(null));
- Assert.That(threshold.LowerWarningBound, Is.EqualTo(null));
+ // Check that the threshold reporting works correctly:
+ {
+ // Set threshold to some known state
+ threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, 5f);
+ threshold.SetEnabled(AtmosMonitorLimitType.UpperDanger, true);
+ threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, 1f);
+ threshold.SetEnabled(AtmosMonitorLimitType.LowerDanger, true);
+ threshold.SetLimit(AtmosMonitorLimitType.UpperWarning, 4f);
+ threshold.SetEnabled(AtmosMonitorLimitType.UpperWarning, true);
+ threshold.SetLimit(AtmosMonitorLimitType.LowerWarning, 2f);
+ threshold.SetEnabled(AtmosMonitorLimitType.LowerWarning, true);
- threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Upper, null);
- threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, null);
+ // Check a value that's in between each upper/lower warning/panic:
+ threshold.CheckThreshold(3f, out AtmosAlarmType alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Normal));
+ threshold.CheckThreshold(1.5f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Warning));
+ threshold.CheckThreshold(4.5f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Warning));
+ threshold.CheckThreshold(5.5f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Danger));
+ threshold.CheckThreshold(0.5f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Danger));
- Assert.That(threshold.UpperBound, Is.EqualTo(null));
- Assert.That(threshold.LowerBound, Is.EqualTo(null));
+ // Check that enable/disable is respected:
+ threshold.CheckThreshold(123.4f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Danger));
+ threshold.SetEnabled(AtmosMonitorLimitType.UpperDanger, false);
+ threshold.CheckThreshold(123.4f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Warning));
+ threshold.SetEnabled(AtmosMonitorLimitType.UpperWarning, false);
+ threshold.CheckThreshold(123.4f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Normal));
+
+ // And for lower thresholds:
+ threshold.CheckThreshold(0.01f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Danger));
+ threshold.SetEnabled(AtmosMonitorLimitType.LowerDanger, false);
+ threshold.CheckThreshold(0.01f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Warning));
+ threshold.SetEnabled(AtmosMonitorLimitType.LowerWarning, false);
+ threshold.CheckThreshold(0.01f, out alarmType);
+ Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Normal));
+ }
});
await pairTracker.CleanReturnAsync();
}
diff --git a/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs b/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs
index dae855a9fb..af4f9b850e 100644
--- a/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs
+++ b/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs
@@ -14,35 +14,66 @@ public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks
[DataField("ignore")]
public bool Ignore;
- // zero bounds are not allowed - just
- // set the bound to null if you want
- // to disable it
[DataField("upperBound")]
- public float? UpperBound { get; private set; }
+ private AlarmThresholdSetting _UpperBound;
+
+ public AlarmThresholdSetting UpperBound { get { return _UpperBound; } private set
+ {
+ // Because the warnings are stored as percentages of the bounds,
+ // Make a copy of the calculated bounds, so that the real warning amount
+ // doesn't change value when user changes the bounds
+ var oldWarning = UpperWarningBound;
+ _UpperBound = value;
+ UpperWarningBound = oldWarning;
+ }
+ }
[DataField("lowerBound")]
- public float? LowerBound { get; private set; }
+ public AlarmThresholdSetting _LowerBound;
+
+ public AlarmThresholdSetting LowerBound { get { return _LowerBound; } private set
+ {
+ // Because the warnings are stored as percentages of the bounds,
+ // Make a copy of the calculated bounds, so that the real warning amount
+ // doesn't change value when user changes the bounds
+ var oldWarning = LowerWarningBound;
+ _LowerBound = value;
+ LowerWarningBound = oldWarning;
+ }
+ }
// upper warning percentage
// must always cause UpperWarningBound
// to be smaller
[DataField("upperWarnAround")]
- public float? UpperWarningPercentage { get; private set; }
+ public AlarmThresholdSetting UpperWarningPercentage { get; private set; }
// lower warning percentage
// must always cause LowerWarningBound
// to be larger
[DataField("lowerWarnAround")]
- public float? LowerWarningPercentage { get; private set; }
+ public AlarmThresholdSetting LowerWarningPercentage { get; private set; }
[ViewVariables]
- public float? UpperWarningBound => CalculateWarningBound(AtmosMonitorThresholdBound.Upper);
+ public AlarmThresholdSetting UpperWarningBound
+ {
+ get { return CalculateWarningBound(AtmosMonitorThresholdBound.Upper); }
+ set { UpperWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Upper, value); }
+ }
[ViewVariables]
- public float? LowerWarningBound => CalculateWarningBound(AtmosMonitorThresholdBound.Lower);
+ public AlarmThresholdSetting LowerWarningBound
+ {
+ get { return CalculateWarningBound(AtmosMonitorThresholdBound.Lower); }
+ set { LowerWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Lower, value); }
+ }
public AtmosAlarmThreshold()
{
+ UpperBound = new AlarmThresholdSetting();
+ LowerBound = new AlarmThresholdSetting();
+ UpperWarningPercentage = new AlarmThresholdSetting();
+ LowerWarningPercentage = new AlarmThresholdSetting();
}
public AtmosAlarmThreshold(AtmosAlarmThreshold other)
@@ -56,186 +87,189 @@ public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks
void ISerializationHooks.AfterDeserialization()
{
- if (UpperBound <= LowerBound)
- UpperBound = null;
-
- if (LowerBound >= UpperBound)
- LowerBound = null;
-
- if (UpperWarningPercentage != null)
- TrySetWarningBound(AtmosMonitorThresholdBound.Upper, UpperBound * UpperWarningPercentage);
-
- if (LowerWarningPercentage != null)
- TrySetWarningBound(AtmosMonitorThresholdBound.Lower, LowerBound * LowerWarningPercentage);
+ UpperBound = new AlarmThresholdSetting{ Enabled = UpperBound.Value != 0, Value = UpperBound.Value };
+ LowerBound = new AlarmThresholdSetting{ Enabled = LowerBound.Value != 0, Value = LowerBound.Value };
+ UpperWarningPercentage = new AlarmThresholdSetting{ Enabled = UpperWarningPercentage.Value != 0, Value = UpperWarningPercentage.Value };
+ LowerWarningPercentage = new AlarmThresholdSetting{ Enabled = LowerWarningPercentage.Value != 0, Value = LowerWarningPercentage.Value };
}
// utility function to check a threshold against some calculated value
public bool CheckThreshold(float value, out AtmosAlarmType state)
+ {
+ return CheckThreshold(value, out state, out AtmosMonitorThresholdBound _);
+ }
+
+ // utility function to check a threshold against some calculated value. If the output state
+ // is normal, whichFailed should not be used..
+ public bool CheckThreshold(float value, out AtmosAlarmType state, out AtmosMonitorThresholdBound whichFailed)
{
state = AtmosAlarmType.Normal;
+ whichFailed = AtmosMonitorThresholdBound.Upper;
+
if (Ignore)
{
return false;
}
- if (value >= UpperBound || value <= LowerBound)
+ if (value >= UpperBound)
{
state = AtmosAlarmType.Danger;
+ whichFailed = AtmosMonitorThresholdBound.Upper;
return true;
}
- if (value >= UpperWarningBound || value <= LowerWarningBound)
+ if(value <= LowerBound)
+ {
+ state = AtmosAlarmType.Danger;
+ whichFailed = AtmosMonitorThresholdBound.Lower;
+ return true;
+ }
+ if (value >= UpperWarningBound)
{
state = AtmosAlarmType.Warning;
+ whichFailed = AtmosMonitorThresholdBound.Upper;
return true;
}
-
- return true;
- }
-
- // set the primary bound, takes a hard value
- public bool TrySetPrimaryBound(AtmosMonitorThresholdBound bound, float? input)
- {
- if (input == null)
+ if (value <= LowerWarningBound)
{
- switch (bound)
- {
- case AtmosMonitorThresholdBound.Upper:
- UpperBound = null;
- break;
- case AtmosMonitorThresholdBound.Lower:
- LowerBound = null;
- break;
- }
-
+ state = AtmosAlarmType.Warning;
+ whichFailed = AtmosMonitorThresholdBound.Lower;
return true;
}
- var value = (float) input;
-
- if (value <= 0f || float.IsNaN(value))
- return false;
-
- (float target, int compare)? targetValue = null;
- switch (bound)
- {
- case AtmosMonitorThresholdBound.Upper:
- if (float.IsPositiveInfinity(value))
- return false;
-
- if (LowerBound != null)
- targetValue = ((float) LowerBound, -1);
- break;
- case AtmosMonitorThresholdBound.Lower:
- if (float.IsNegativeInfinity(value))
- return false;
-
- if (UpperBound != null)
- targetValue = ((float) UpperBound, 1);
- break;
- }
-
- var isValid = true;
- if (targetValue != null)
- {
- var result = targetValue.Value.target.CompareTo(value);
- isValid = targetValue.Value.compare == result;
- }
-
- if (isValid)
- {
- switch (bound)
- {
- case AtmosMonitorThresholdBound.Upper:
- UpperBound = value;
- return true;
- case AtmosMonitorThresholdBound.Lower:
- LowerBound = value;
- return true;
- }
- }
-
return false;
}
- // set the warning bound, takes a hard value
- //
- // this will always set the percentage and
- // the raw value at the same time
- public bool TrySetWarningBound(AtmosMonitorThresholdBound bound, float? input)
+ /// Warnings are stored in prototypes as a percentage, for ease of content
+ /// maintainers. This recalculates a new "real" value of the warning
+ /// threshold, for use in the actual atmosphereic checks.
+ public AlarmThresholdSetting CalculateWarningBound(AtmosMonitorThresholdBound bound)
{
- if (input == null)
- {
- switch (bound)
- {
- case AtmosMonitorThresholdBound.Upper:
- UpperWarningPercentage = null;
- break;
- case AtmosMonitorThresholdBound.Lower:
- LowerWarningPercentage = null;
- break;
- }
-
- return true;
- }
-
switch (bound)
{
case AtmosMonitorThresholdBound.Upper:
- if (UpperBound == null)
- return false;
-
- var upperWarning = (float) (input / UpperBound);
- var upperTestValue = upperWarning * (float) UpperBound;
-
- if (upperWarning > 1f
- || upperTestValue < LowerWarningBound
- || upperTestValue < LowerBound)
- return false;
-
- UpperWarningPercentage = upperWarning;
-
- return true;
+ return new AlarmThresholdSetting {
+ Enabled = UpperWarningPercentage.Enabled,
+ Value = UpperBound.Value * UpperWarningPercentage.Value};
case AtmosMonitorThresholdBound.Lower:
- if (LowerBound == null)
- return false;
-
- var lowerWarning = (float) (input / LowerBound);
- var testValue = lowerWarning * (float) LowerBound;
-
- if (lowerWarning < 1f
- || testValue > UpperWarningBound
- || testValue > UpperBound)
- return false;
-
- LowerWarningPercentage = lowerWarning;
-
- return true;
+ return new AlarmThresholdSetting {
+ Enabled = LowerWarningPercentage.Enabled,
+ Value = LowerBound.Value * LowerWarningPercentage.Value};
+ default:
+ // Unreachable.
+ return new AlarmThresholdSetting();
}
-
- return false;
}
- public float? CalculateWarningBound(AtmosMonitorThresholdBound bound)
+ public AlarmThresholdSetting CalculateWarningPercentage(AtmosMonitorThresholdBound bound, AlarmThresholdSetting warningBound)
{
- float? value = null;
-
switch (bound)
{
case AtmosMonitorThresholdBound.Upper:
- if (UpperBound == null || UpperWarningPercentage == null)
- break;
-
- value = UpperBound * UpperWarningPercentage;
- break;
+ return new AlarmThresholdSetting {
+ Enabled = UpperWarningPercentage.Enabled,
+ Value = UpperBound.Value == 0 ? 0 : warningBound.Value / UpperBound.Value};
case AtmosMonitorThresholdBound.Lower:
- if (LowerBound == null || LowerWarningPercentage == null)
- break;
+ return new AlarmThresholdSetting {
+ Enabled = LowerWarningPercentage.Enabled,
+ Value = LowerBound.Value == 0 ? 0 : warningBound.Value / LowerBound.Value };
+ default:
+ // Unreachable.
+ return new AlarmThresholdSetting();
+ }
+ }
- value = LowerBound * LowerWarningPercentage;
+ // Enable or disable a single threshold setting
+ public void SetEnabled(AtmosMonitorLimitType whichLimit, bool isEnabled)
+ {
+ switch(whichLimit)
+ {
+ case AtmosMonitorLimitType.LowerDanger:
+ LowerBound = LowerBound.WithEnabled(isEnabled);
+ break;
+ case AtmosMonitorLimitType.LowerWarning:
+ LowerWarningPercentage = LowerWarningPercentage.WithEnabled(isEnabled);
+ break;
+ case AtmosMonitorLimitType.UpperWarning:
+ UpperWarningPercentage = UpperWarningPercentage.WithEnabled(isEnabled);
+ break;
+ case AtmosMonitorLimitType.UpperDanger:
+ UpperBound = UpperBound.WithEnabled(isEnabled);
break;
}
+ }
- return value;
+ // Set the limit for a threshold. Will clamp other limits appropriately to
+ // enforce that LowerBound <= LowerWarningBound <= UpperWarningBound <= UpperBound
+ public void SetLimit(AtmosMonitorLimitType whichLimit, float limit)
+ {
+ if (limit <= 0)
+ {
+ // Unit tests expect that setting value of 0 or less should not change the limit.
+ // Feels a bit strange, but does avoid a bug where the warning data (stored as a
+ // percentage of danger bounds) is lost when setting the danger threshold to zero
+ return;
+ }
+
+ switch (whichLimit)
+ {
+ case AtmosMonitorLimitType.LowerDanger:
+ LowerBound = LowerBound.WithThreshold(limit);
+ LowerWarningBound = LowerWarningBound.WithThreshold(Math.Max(limit, LowerWarningBound.Value));
+ UpperWarningBound = UpperWarningBound.WithThreshold(Math.Max(limit, UpperWarningBound.Value));
+ UpperBound = UpperBound.WithThreshold(Math.Max(limit, UpperBound.Value));
+ break;
+ case AtmosMonitorLimitType.LowerWarning:
+ LowerBound = LowerBound.WithThreshold(Math.Min(LowerBound.Value, limit));
+ LowerWarningBound = LowerWarningBound.WithThreshold(limit);
+ UpperWarningBound = UpperWarningBound.WithThreshold(Math.Max(limit, UpperWarningBound.Value));
+ UpperBound = UpperBound.WithThreshold(Math.Max(limit, UpperBound.Value));
+ break;
+ case AtmosMonitorLimitType.UpperWarning:
+ LowerBound = LowerBound.WithThreshold(Math.Min(LowerBound.Value, limit));
+ LowerWarningBound = LowerWarningBound.WithThreshold(Math.Min(LowerWarningBound.Value, limit));
+ UpperWarningBound = UpperWarningBound.WithThreshold(limit);
+ UpperBound = UpperBound.WithThreshold(Math.Max(limit, UpperBound.Value));
+ break;
+ case AtmosMonitorLimitType.UpperDanger:
+ LowerBound = LowerBound.WithThreshold(Math.Min(LowerBound.Value, limit));
+ LowerWarningBound = LowerWarningBound.WithThreshold(Math.Min(LowerWarningBound.Value, limit));
+ UpperWarningBound = UpperWarningBound.WithThreshold(Math.Min(UpperWarningBound.Value, limit));
+ UpperBound = UpperBound.WithThreshold(limit);
+ break;
+ }
+ }
+
+ [DataDefinition, Serializable]
+ public struct AlarmThresholdSetting
+ {
+ [DataField("enabled")]
+ public bool Enabled { get; set; } = false;
+ [DataField("threshold")]
+ public float Value { get; set; } = 0;
+
+ public AlarmThresholdSetting()
+ {
+ }
+
+ public static bool operator <=(float a, AlarmThresholdSetting b)
+ {
+ return b.Enabled && a <= b.Value;
+ }
+
+ public static bool operator >=(float a, AlarmThresholdSetting b)
+ {
+ return b.Enabled && a >= b.Value;
+ }
+
+ public AlarmThresholdSetting WithThreshold(float threshold)
+ {
+ return new AlarmThresholdSetting{ Enabled = Enabled, Value = threshold };
+ }
+
+ public AlarmThresholdSetting WithEnabled(bool enabled)
+ {
+ return new AlarmThresholdSetting{ Enabled = enabled, Value = Value };
+ }
}
}
@@ -245,6 +279,14 @@ public enum AtmosMonitorThresholdBound
Lower
}
+public enum AtmosMonitorLimitType //