UX improvements to Air Alarm UI (#12681)
Co-authored-by: Eoin Mcloughlin <helloworld@eoinrul.es>
This commit is contained in:
@@ -20,7 +20,7 @@ public sealed class AirAlarmBoundUserInterface : BoundUserInterface
|
|||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
_window = new AirAlarmWindow();
|
_window = new AirAlarmWindow(Owner);
|
||||||
|
|
||||||
if (State != null)
|
if (State != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
<DefaultWindow xmlns="https://spacestation14.io"
|
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2007/xaml"
|
||||||
MinSize="500 500" Title="Air Alarm">
|
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||||
|
MinSize="500 500" Resizable="True" Title="Air Alarm">
|
||||||
<BoxContainer Orientation="Vertical" Margin="5 5 5 5">
|
<BoxContainer Orientation="Vertical" Margin="5 5 5 5">
|
||||||
<!-- Status (pressure, temperature, alarm state, device total, address, etc) -->
|
<!-- Status (pressure, temperature, alarm state, device total, address, etc) -->
|
||||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 2">
|
<BoxContainer Orientation="Horizontal" Margin="0 0 0 2">
|
||||||
<!-- Left column (pressure, temperature, alarm state) -->
|
<!-- Left column (view of entity) -->
|
||||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
<PanelContainer Margin="2 0 6 0" StyleClasses="Inset" VerticalAlignment="Center" VerticalExpand="True">
|
||||||
|
<SpriteView Name="EntityView" OverrideDirection="South" Scale="2 2" />
|
||||||
|
</PanelContainer>
|
||||||
|
<!-- Center column (pressure, temperature, alarm state) -->
|
||||||
|
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 0 6 0">
|
||||||
<BoxContainer Orientation="Vertical" Margin="0 0 2 0" HorizontalExpand="True">
|
<BoxContainer Orientation="Vertical" Margin="0 0 2 0" HorizontalExpand="True">
|
||||||
<Label Text="{Loc 'air-alarm-ui-window-pressure-label'}" />
|
<Label Text="{Loc 'air-alarm-ui-window-pressure-label'}" />
|
||||||
<Label Text="{Loc 'air-alarm-ui-window-temperature-label'}" />
|
<Label Text="{Loc 'air-alarm-ui-window-temperature-label'}" />
|
||||||
@@ -25,8 +31,8 @@
|
|||||||
<Label Text="{Loc 'air-alarm-ui-window-device-count-label'}" />
|
<Label Text="{Loc 'air-alarm-ui-window-device-count-label'}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||||
<Label Name="CDeviceAddress" HorizontalAlignment="Right" />
|
<RichTextLabel Name="CDeviceAddress" HorizontalAlignment="Right" />
|
||||||
<Label Name="CDeviceTotal" HorizontalAlignment="Right" />
|
<RichTextLabel Name="CDeviceTotal" HorizontalAlignment="Right" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Button Name="CResyncButton" Text="{Loc 'air-alarm-ui-window-resync-devices-label'}" HorizontalExpand="True" />
|
<Button Name="CResyncButton" Text="{Loc 'air-alarm-ui-window-resync-devices-label'}" HorizontalExpand="True" />
|
||||||
@@ -35,17 +41,35 @@
|
|||||||
<!-- Gas/Device Data -->
|
<!-- Gas/Device Data -->
|
||||||
<TabContainer Name="CTabContainer" VerticalExpand="True" Margin="0 0 0 2">
|
<TabContainer Name="CTabContainer" VerticalExpand="True" Margin="0 0 0 2">
|
||||||
<!-- Vent devices -->
|
<!-- Vent devices -->
|
||||||
<ScrollContainer VerticalExpand="True">
|
<PanelContainer VerticalExpand="True">
|
||||||
<BoxContainer Name="CVentContainer" Orientation="Vertical"/>
|
<PanelContainer.PanelOverride>
|
||||||
</ScrollContainer>
|
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||||
|
</PanelContainer.PanelOverride>
|
||||||
|
|
||||||
|
<ScrollContainer>
|
||||||
|
<BoxContainer Name="CVentContainer" Orientation="Vertical"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
</PanelContainer>
|
||||||
<!-- Scrubber devices -->
|
<!-- Scrubber devices -->
|
||||||
<ScrollContainer VerticalExpand="True">
|
<PanelContainer VerticalExpand="True">
|
||||||
<BoxContainer Name="CScrubberContainer" Orientation="Vertical"/>
|
<PanelContainer.PanelOverride>
|
||||||
</ScrollContainer>
|
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||||
|
</PanelContainer.PanelOverride>
|
||||||
|
|
||||||
|
<ScrollContainer>
|
||||||
|
<BoxContainer Name="CScrubberContainer" Orientation="Vertical"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
</PanelContainer>
|
||||||
<!-- Sensors -->
|
<!-- Sensors -->
|
||||||
<ScrollContainer VerticalExpand="True">
|
<PanelContainer VerticalExpand="True">
|
||||||
<BoxContainer Name="CSensorContainer" Orientation="Vertical"/>
|
<PanelContainer.PanelOverride>
|
||||||
</ScrollContainer>
|
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||||
|
</PanelContainer.PanelOverride>
|
||||||
|
|
||||||
|
<ScrollContainer>
|
||||||
|
<BoxContainer Name="CSensorContainer" Orientation="Vertical"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
</PanelContainer>
|
||||||
</TabContainer>
|
</TabContainer>
|
||||||
<!-- Mode buttons -->
|
<!-- Mode buttons -->
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
@@ -53,4 +77,4 @@
|
|||||||
<OptionButton Name="CModeButton" HorizontalExpand="True" />
|
<OptionButton Name="CModeButton" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</ui:FancyWindow>
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Client.Atmos.Monitor.UI.Widgets;
|
using Content.Client.Atmos.Monitor.UI.Widgets;
|
||||||
using Content.Client.Message;
|
using Content.Client.Message;
|
||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Monitor;
|
using Content.Shared.Atmos.Monitor;
|
||||||
using Content.Shared.Atmos.Monitor.Components;
|
using Content.Shared.Atmos.Monitor.Components;
|
||||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||||
using Content.Shared.Temperature;
|
using Content.Shared.Temperature;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Localization;
|
|
||||||
|
|
||||||
namespace Content.Client.Atmos.Monitor.UI;
|
namespace Content.Client.Atmos.Monitor.UI;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class AirAlarmWindow : DefaultWindow
|
public sealed partial class AirAlarmWindow : FancyWindow
|
||||||
{
|
{
|
||||||
public event Action<string, IAtmosDeviceData>? AtmosDeviceDataChanged;
|
public event Action<string, IAtmosDeviceData>? AtmosDeviceDataChanged;
|
||||||
public event Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? AtmosAlarmThresholdChanged;
|
public event Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? AtmosAlarmThresholdChanged;
|
||||||
@@ -25,8 +24,8 @@ public sealed partial class AirAlarmWindow : DefaultWindow
|
|||||||
public event Action? ResyncAllRequested;
|
public event Action? ResyncAllRequested;
|
||||||
public event Action<AirAlarmTab>? AirAlarmTabChange;
|
public event Action<AirAlarmTab>? AirAlarmTabChange;
|
||||||
|
|
||||||
private Label _address => CDeviceAddress;
|
private RichTextLabel _address => CDeviceAddress;
|
||||||
private Label _deviceTotal => CDeviceTotal;
|
private RichTextLabel _deviceTotal => CDeviceTotal;
|
||||||
private RichTextLabel _pressure => CPressureLabel;
|
private RichTextLabel _pressure => CPressureLabel;
|
||||||
private RichTextLabel _temperature => CTemperatureLabel;
|
private RichTextLabel _temperature => CTemperatureLabel;
|
||||||
private RichTextLabel _alarmState => CStatusLabel;
|
private RichTextLabel _alarmState => CStatusLabel;
|
||||||
@@ -45,7 +44,7 @@ public sealed partial class AirAlarmWindow : DefaultWindow
|
|||||||
|
|
||||||
private OptionButton _modes => CModeButton;
|
private OptionButton _modes => CModeButton;
|
||||||
|
|
||||||
public AirAlarmWindow()
|
public AirAlarmWindow(ClientUserInterfaceComponent component)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
@@ -88,15 +87,19 @@ public sealed partial class AirAlarmWindow : DefaultWindow
|
|||||||
_sensors.Clear();
|
_sensors.Clear();
|
||||||
ResyncAllRequested!.Invoke();
|
ResyncAllRequested!.Invoke();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
EntityView.Sprite = IoCManager.Resolve<IEntityManager>().GetComponent<SpriteComponent>(component.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateState(AirAlarmUIState state)
|
public void UpdateState(AirAlarmUIState state)
|
||||||
{
|
{
|
||||||
_address.Text = state.Address;
|
_address.SetMarkup(state.Address);
|
||||||
_deviceTotal.Text = $"{state.DeviceCount}";
|
_deviceTotal.SetMarkup($"{state.DeviceCount}");
|
||||||
_pressure.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure", ("pressure", $"{state.PressureAverage:0.##}")));
|
_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.##}")));
|
_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);
|
UpdateModeSelector(state.Mode);
|
||||||
foreach (var (addr, dev) in state.DeviceData)
|
foreach (var (addr, dev) in state.DeviceData)
|
||||||
{
|
{
|
||||||
@@ -159,4 +162,26 @@ public sealed partial class AirAlarmWindow : DefaultWindow
|
|||||||
break;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<Collapsible Orientation="Vertical">
|
<Collapsible Orientation="Vertical">
|
||||||
<CollapsibleHeading Name="CAddress" />
|
<CollapsibleHeading Name="CAddress" />
|
||||||
<!-- Upper row: toggle, direction, checks -->
|
<!-- Upper row: toggle, direction, checks -->
|
||||||
<CollapsibleBody>
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal" Margin ="0 0 0 2">
|
<BoxContainer Orientation="Horizontal" Margin ="0 0 0 2">
|
||||||
<CheckBox Name="CEnableDevice" Text="{Loc 'air-alarm-ui-widget-enable'}" />
|
<CheckBox Name="CEnableDevice" Text="{Loc 'air-alarm-ui-widget-enable'}" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
Orientation="Vertical" Margin="2 0 2 4">
|
Orientation="Vertical" Margin="2 0 2 4">
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible Orientation="Vertical">
|
||||||
<CollapsibleHeading Name="CAddress" />
|
<CollapsibleHeading Name="CAddress" />
|
||||||
<CollapsibleBody>
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 2">
|
<BoxContainer Orientation="Horizontal" Margin="0 0 0 2">
|
||||||
<CheckBox Name="CEnableDevice" Text="{Loc 'air-alarm-ui-widget-enable'}" />
|
<CheckBox Name="CEnableDevice" Text="{Loc 'air-alarm-ui-widget-enable'}" />
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<!-- Lower row: every single gas -->
|
<!-- Lower row: every single gas -->
|
||||||
<Collapsible Orientation="Vertical" Margin="2 2 2 2">
|
<Collapsible Orientation="Vertical" Margin="2 2 2 2">
|
||||||
<CollapsibleHeading Title="Gas filters" />
|
<CollapsibleHeading Title="Gas filters" />
|
||||||
<CollapsibleBody>
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<GridContainer HorizontalExpand="True" Name="CGasContainer" Columns="3" />
|
<GridContainer HorizontalExpand="True" Name="CGasContainer" Columns="3" />
|
||||||
</CollapsibleBody>
|
</CollapsibleBody>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|||||||
@@ -1,41 +1,21 @@
|
|||||||
<BoxContainer xmlns="https://spacestation14.io" HorizontalExpand="True">
|
<BoxContainer xmlns="https://spacestation14.io" HorizontalExpand="True">
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible Orientation="Vertical">
|
||||||
<CollapsibleHeading Name="SensorAddress" />
|
<CollapsibleHeading Name="SensorAddress" />
|
||||||
<CollapsibleBody Margin="2">
|
<CollapsibleBody Margin="20 2 2 2">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
<BoxContainer Orientation="Vertical" Margin="0 0 2 0" HorizontalExpand="True">
|
||||||
<BoxContainer Orientation="Vertical" Margin="0 0 2 0" HorizontalExpand="True">
|
<RichTextLabel Name="AlarmStateLabel" />
|
||||||
<Label Text="{Loc 'air-alarm-ui-window-pressure-label'}" />
|
<RichTextLabel Name="PressureLabel" />
|
||||||
<Label Text="{Loc 'air-alarm-ui-window-temperature-label'}" />
|
<Control Name="PressureThresholdContainer" Margin="20 0 2 0" />
|
||||||
<Label Text="{Loc 'air-alarm-ui-window-alarm-state-label'}" />
|
<RichTextLabel Name="TemperatureLabel" />
|
||||||
</BoxContainer>
|
<Control Name="TemperatureThresholdContainer" Margin="20 0 2 0" />
|
||||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
|
||||||
<RichTextLabel Name="PressureLabel" />
|
|
||||||
<RichTextLabel Name="TemperatureLabel" />
|
|
||||||
<RichTextLabel Name="AlarmStateLabel" />
|
|
||||||
</BoxContainer>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Collapsible Orientation="Vertical" Margin="2">
|
<Collapsible Orientation="Vertical" Margin="2">
|
||||||
<CollapsibleHeading Title="{Loc 'air-alarm-ui-sensor-gases'}" />
|
<CollapsibleHeading Title="{Loc 'air-alarm-ui-sensor-gases'}" />
|
||||||
<CollapsibleBody>
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<BoxContainer Name="GasContainer" Orientation="Vertical" Margin="2" />
|
<BoxContainer Name="GasContainer" Orientation="Vertical" Margin="2" />
|
||||||
</CollapsibleBody>
|
</CollapsibleBody>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
<Collapsible Orientation="Vertical" Margin="2">
|
|
||||||
<CollapsibleHeading Title="{Loc 'air-alarm-ui-sensor-thresholds'}" />
|
|
||||||
<CollapsibleBody>
|
|
||||||
<BoxContainer Orientation="Vertical">
|
|
||||||
<Control Name="PressureThresholdContainer" Margin="2 0 2 0" />
|
|
||||||
<Control Name="TemperatureThresholdContainer" Margin="2 0 2 0" />
|
|
||||||
<Collapsible Orientation="Vertical" Margin="2 0 2 0">
|
|
||||||
<CollapsibleHeading Title="{Loc 'air-alarm-ui-sensor-gases'}" />
|
|
||||||
<CollapsibleBody Margin="4 2 4 2">
|
|
||||||
<BoxContainer Name="GasThresholds" Orientation="Vertical" Margin="2 0 2 0" />
|
|
||||||
</CollapsibleBody>
|
|
||||||
</Collapsible>
|
|
||||||
</BoxContainer>
|
|
||||||
</CollapsibleBody>
|
|
||||||
</Collapsible>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</CollapsibleBody>
|
</CollapsibleBody>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public sealed partial class SensorInfo : BoxContainer
|
|||||||
private ThresholdControl _pressureThreshold;
|
private ThresholdControl _pressureThreshold;
|
||||||
private ThresholdControl _temperatureThreshold;
|
private ThresholdControl _temperatureThreshold;
|
||||||
private Dictionary<Gas, ThresholdControl> _gasThresholds = new();
|
private Dictionary<Gas, ThresholdControl> _gasThresholds = new();
|
||||||
private Dictionary<Gas, Label> _gasLabels = new();
|
private Dictionary<Gas, RichTextLabel> _gasLabels = new();
|
||||||
|
|
||||||
public SensorInfo(AtmosSensorData data, string address)
|
public SensorInfo(AtmosSensorData data, string address)
|
||||||
{
|
{
|
||||||
@@ -27,22 +27,42 @@ public sealed partial class SensorInfo : BoxContainer
|
|||||||
|
|
||||||
SensorAddress.Title = $"{address} : {data.AlarmState}";
|
SensorAddress.Title = $"{address} : {data.AlarmState}";
|
||||||
|
|
||||||
PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure", ("pressure", $"{data.Pressure:0.##}")));
|
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
|
||||||
TemperatureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature", ("tempC", $"{TemperatureHelpers.KelvinToCelsius(data.Temperature):0.#}"), ("temperature", $"{data.Temperature:0.##}")));
|
("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)),
|
||||||
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state", ("state", $"{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)
|
foreach (var (gas, amount) in data.Gases)
|
||||||
{
|
{
|
||||||
var label = new Label();
|
var label = new RichTextLabel();
|
||||||
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.####}"),
|
("amount", $"{amount:0.####}"),
|
||||||
("percentage", $"{(100 * amount / data.TotalMoles):0.##}"));
|
("percentage", $"{(100 * fractionGas):0.##}")));
|
||||||
GasContainer.AddChild(label);
|
GasContainer.AddChild(label);
|
||||||
_gasLabels.Add(gas, 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 =
|
_pressureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-pressure-title"), data.PressureThreshold, AtmosMonitorThresholdType.Pressure);
|
||||||
new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-pressure-title"), data.PressureThreshold, AtmosMonitorThresholdType.Pressure);
|
|
||||||
PressureThresholdContainer.AddChild(_pressureThreshold);
|
PressureThresholdContainer.AddChild(_pressureThreshold);
|
||||||
_temperatureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-temperature-title"), data.TemperatureThreshold,
|
_temperatureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-temperature-title"), data.TemperatureThreshold,
|
||||||
AtmosMonitorThresholdType.Temperature);
|
AtmosMonitorThresholdType.Temperature);
|
||||||
@@ -60,23 +80,24 @@ public sealed partial class SensorInfo : BoxContainer
|
|||||||
|
|
||||||
foreach (var (gas, threshold) in data.GasThresholds)
|
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)
|
public void ChangeData(AtmosSensorData data)
|
||||||
{
|
{
|
||||||
SensorAddress.Title = $"{_address} : {data.AlarmState}";
|
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-indicator",
|
||||||
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state", ("state", $"{data.AlarmState}")));
|
("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)
|
foreach (var (gas, amount) in data.Gases)
|
||||||
{
|
{
|
||||||
@@ -85,13 +106,15 @@ public sealed partial class SensorInfo : BoxContainer
|
|||||||
continue;
|
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.####}"),
|
("amount", $"{amount:0.####}"),
|
||||||
("percentage", $"{(100 * amount / data.TotalMoles):0.##}"));
|
("percentage", $"{(100 * fractionGas):0.##}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
_pressureThreshold.UpdateThresholdData(data.PressureThreshold);
|
_pressureThreshold.UpdateThresholdData(data.PressureThreshold, data.Pressure);
|
||||||
_temperatureThreshold.UpdateThresholdData(data.TemperatureThreshold);
|
_temperatureThreshold.UpdateThresholdData(data.TemperatureThreshold, data.Temperature);
|
||||||
foreach (var (gas, control) in _gasThresholds)
|
foreach (var (gas, control) in _gasThresholds)
|
||||||
{
|
{
|
||||||
if (!data.GasThresholds.TryGetValue(gas, out var threshold))
|
if (!data.GasThresholds.TryGetValue(gas, out var threshold))
|
||||||
@@ -99,7 +122,8 @@ public sealed partial class SensorInfo : BoxContainer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
control.UpdateThresholdData(threshold);
|
control.UpdateThresholdData(threshold, data.Gases[gas] / data.TotalMoles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<BoxContainer xmlns="https://spacestation14.io"
|
||||||
|
HorizontalExpand="True" Orientation="Vertical"
|
||||||
|
Margin = "20 0 0 0" MinSize="160 0" >
|
||||||
|
<Label Name="CBoundLabel" HorizontalAlignment="Center" />
|
||||||
|
<CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'Enable'}"/>
|
||||||
|
<FloatSpinBox Name="CSpinner" />
|
||||||
|
</BoxContainer>
|
||||||
@@ -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<float>? OnBoundChanged;
|
||||||
|
public Action<bool>? 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,15 +3,15 @@
|
|||||||
Orientation="Vertical" Margin="0 0 0 4">
|
Orientation="Vertical" Margin="0 0 0 4">
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible Orientation="Vertical">
|
||||||
<CollapsibleHeading Name="CName" />
|
<CollapsibleHeading Name="CName" />
|
||||||
<CollapsibleBody>
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<CheckBox Name="CIgnore" Text="{Loc 'Ignore'}" />
|
<CheckBox Name="CEnabled" Text="{Loc 'Enabled'}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Upper row: Danger bounds -->
|
<!-- Upper row: Danger bounds -->
|
||||||
<BoxContainer Name="CDangerBounds" Orientation="Horizontal" Margin="0 0 0 2"/>
|
<BoxContainer Name="CDangerBounds" Orientation="Horizontal" Margin="0 0 0 2"/>
|
||||||
<!-- Lower row: Warning bounds -->
|
<!-- Lower row: Warning bounds -->
|
||||||
<BoxContainer Name="CWarningBounds" Orientation="Horizontal" Margin="0 0 0 2"/>
|
<BoxContainer Name="CWarningBounds" Orientation="Horizontal" Margin="0 6 0 2"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</CollapsibleBody>
|
</CollapsibleBody>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public sealed partial class ThresholdControl : BoxContainer
|
|||||||
public event Action<AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? ThresholdDataChanged;
|
public event Action<AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? ThresholdDataChanged;
|
||||||
|
|
||||||
private CollapsibleHeading _name => CName;
|
private CollapsibleHeading _name => CName;
|
||||||
private CheckBox _ignore => CIgnore;
|
private CheckBox _enabled => CEnabled;
|
||||||
private BoxContainer _dangerBounds => CDangerBounds;
|
private BoxContainer _dangerBounds => CDangerBounds;
|
||||||
private BoxContainer _warningBounds => CWarningBounds;
|
private BoxContainer _warningBounds => CWarningBounds;
|
||||||
private ThresholdBoundControl _upperBoundControl;
|
private ThresholdBoundControl _upperBoundControl;
|
||||||
@@ -46,24 +46,14 @@ public sealed partial class ThresholdControl : BoxContainer
|
|||||||
|
|
||||||
// i miss rust macros
|
// i miss rust macros
|
||||||
|
|
||||||
_upperBoundControl = new ThresholdBoundControl("upper-bound", _threshold.UpperBound, modifier);
|
_upperBoundControl = new ThresholdBoundControl(LabelForBound("upper-bound"), _threshold.UpperBound.Value, modifier);
|
||||||
_upperBoundControl.OnBoundChanged += value =>
|
_upperBoundControl.OnBoundChanged += (value) =>
|
||||||
{
|
{
|
||||||
// a lot of threshold logic is baked into the properties,
|
_threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, value);
|
||||||
// so setting this just returns if a change occurred or not
|
|
||||||
_threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Upper, value);
|
|
||||||
return _threshold.UpperBound;
|
|
||||||
};
|
};
|
||||||
_upperBoundControl.OnBoundEnabled += () =>
|
_upperBoundControl.OnBoundEnabled += (isEnabled) =>
|
||||||
{
|
{
|
||||||
var value = 0f;
|
_threshold.SetEnabled(AtmosMonitorLimitType.UpperDanger, isEnabled);
|
||||||
|
|
||||||
if (_threshold.LowerWarningBound != null)
|
|
||||||
value = (float) _threshold.LowerWarningBound + 0.1f;
|
|
||||||
else if (_threshold.LowerBound != null)
|
|
||||||
value = (float) _threshold.LowerBound + 0.1f;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
_upperBoundControl.OnValidBoundChanged += () =>
|
_upperBoundControl.OnValidBoundChanged += () =>
|
||||||
{
|
{
|
||||||
@@ -71,227 +61,101 @@ public sealed partial class ThresholdControl : BoxContainer
|
|||||||
};
|
};
|
||||||
_dangerBounds.AddChild(_upperBoundControl);
|
_dangerBounds.AddChild(_upperBoundControl);
|
||||||
|
|
||||||
_lowerBoundControl = new ThresholdBoundControl("lower-bound", _threshold.LowerBound, modifier);
|
_lowerBoundControl = new ThresholdBoundControl(LabelForBound("lower-bound"), _threshold.LowerBound.Value, modifier);
|
||||||
_lowerBoundControl.OnBoundChanged += value =>
|
_lowerBoundControl.OnBoundChanged += value =>
|
||||||
{
|
{
|
||||||
_threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, value);
|
_threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, value);
|
||||||
return _threshold.LowerBound;
|
|
||||||
};
|
};
|
||||||
_lowerBoundControl.OnBoundEnabled += () =>
|
_lowerBoundControl.OnBoundEnabled += (isEnabled) =>
|
||||||
{
|
{
|
||||||
var value = 0f;
|
_threshold.SetEnabled(AtmosMonitorLimitType.LowerDanger, isEnabled);
|
||||||
|
|
||||||
if (_threshold.UpperWarningBound != null)
|
|
||||||
value = (float) _threshold.UpperWarningBound - 0.1f;
|
|
||||||
else if (_threshold.UpperBound != null)
|
|
||||||
value = (float) _threshold.UpperBound - 0.1f;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
_lowerBoundControl.OnValidBoundChanged += () =>
|
_lowerBoundControl.OnValidBoundChanged += () =>
|
||||||
|
{
|
||||||
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
|
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
|
||||||
|
};
|
||||||
_dangerBounds.AddChild(_lowerBoundControl);
|
_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 =>
|
_upperWarningBoundControl.OnBoundChanged += value =>
|
||||||
{
|
{
|
||||||
_threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Upper, value);
|
_threshold.SetLimit(AtmosMonitorLimitType.UpperWarning, value);
|
||||||
return _threshold.UpperWarningBound;
|
|
||||||
};
|
};
|
||||||
_upperWarningBoundControl.OnBoundEnabled += () =>
|
_upperWarningBoundControl.OnBoundEnabled += (isEnabled) =>
|
||||||
{
|
{
|
||||||
var value = 0f;
|
_threshold.SetEnabled(AtmosMonitorLimitType.UpperWarning, isEnabled);
|
||||||
|
|
||||||
if (_threshold.LowerWarningBound != null)
|
|
||||||
value = (float) _threshold.LowerWarningBound + 0.1f;
|
|
||||||
else if (_threshold.LowerBound != null)
|
|
||||||
value = (float) _threshold.LowerBound + 0.1f;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
_upperWarningBoundControl.OnValidBoundChanged += () =>
|
_upperWarningBoundControl.OnValidBoundChanged += () =>
|
||||||
|
{
|
||||||
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
|
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
|
||||||
|
};
|
||||||
_warningBounds.AddChild(_upperWarningBoundControl);
|
_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 =>
|
_lowerWarningBoundControl.OnBoundChanged += value =>
|
||||||
{
|
{
|
||||||
_threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Lower, value);
|
_threshold.SetLimit(AtmosMonitorLimitType.LowerWarning, value);
|
||||||
return _threshold.LowerWarningBound;
|
|
||||||
};
|
};
|
||||||
_lowerWarningBoundControl.OnBoundEnabled += () =>
|
_lowerWarningBoundControl.OnBoundEnabled += (isEnabled) =>
|
||||||
{
|
{
|
||||||
var value = 0f;
|
_threshold.SetEnabled(AtmosMonitorLimitType.LowerWarning, isEnabled);
|
||||||
|
|
||||||
if (_threshold.UpperWarningBound != null)
|
|
||||||
value = (float) _threshold.UpperWarningBound - 0.1f;
|
|
||||||
else if (_threshold.UpperBound != null)
|
|
||||||
value = (float) _threshold.UpperBound - 0.1f;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
_lowerWarningBoundControl.OnValidBoundChanged += () =>
|
_lowerWarningBoundControl.OnValidBoundChanged += () =>
|
||||||
|
{
|
||||||
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
|
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
|
||||||
|
};
|
||||||
|
|
||||||
_warningBounds.AddChild(_lowerWarningBoundControl);
|
_warningBounds.AddChild(_lowerWarningBoundControl);
|
||||||
|
|
||||||
_ignore.OnToggled += args =>
|
_enabled.OnToggled += args =>
|
||||||
{
|
{
|
||||||
_threshold.Ignore = args.Pressed;
|
_threshold.Ignore = !args.Pressed;
|
||||||
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
|
ThresholdDataChanged!.Invoke(_type, _threshold, _gas);
|
||||||
};
|
};
|
||||||
_ignore.Pressed = _threshold.Ignore;
|
_enabled.Pressed = !_threshold.Ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateThresholdData(AtmosAlarmThreshold threshold)
|
private String LabelForBound(string boundType) //<todo.eoin Replace this with enums
|
||||||
{
|
{
|
||||||
_upperBoundControl.SetValue(threshold.UpperBound);
|
return Loc.GetString($"air-alarm-ui-thresholds-{boundType}");
|
||||||
_lowerBoundControl.SetValue(threshold.LowerBound);
|
|
||||||
_upperWarningBoundControl.SetValue(threshold.UpperWarningBound);
|
|
||||||
_lowerWarningBoundControl.SetValue(threshold.LowerWarningBound);
|
|
||||||
_ignore.Pressed = threshold.Ignore;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateThresholdData(AtmosAlarmThreshold threshold, float currentAmount)
|
||||||
private sealed class ThresholdBoundControl : BoxContainer
|
|
||||||
{
|
{
|
||||||
// raw values to use in thresholds, prefer these
|
threshold.CheckThreshold(currentAmount, out AtmosAlarmType alarm, out AtmosMonitorThresholdBound which);
|
||||||
// over directly setting Modified(Value/LastValue)
|
|
||||||
// when working with the FloatSpinBox
|
|
||||||
private float? _value;
|
|
||||||
private float _lastValue;
|
|
||||||
|
|
||||||
// convenience thing for getting multiplied values
|
var upperDangerState = AtmosAlarmType.Normal;
|
||||||
// and also setting value to a usable value
|
var lowerDangerState = AtmosAlarmType.Normal;
|
||||||
private float? ModifiedValue
|
var upperWarningState = AtmosAlarmType.Normal;
|
||||||
|
var lowerWarningState = AtmosAlarmType.Normal;
|
||||||
|
|
||||||
|
if(alarm == AtmosAlarmType.Danger)
|
||||||
{
|
{
|
||||||
get => _value * _modifier;
|
if(which == AtmosMonitorThresholdBound.Upper) upperDangerState = alarm;
|
||||||
set => _value = value / _modifier;
|
else lowerDangerState = alarm;
|
||||||
|
}
|
||||||
|
else if(alarm == AtmosAlarmType.Warning)
|
||||||
|
{
|
||||||
|
if(which == AtmosMonitorThresholdBound.Upper) upperWarningState = alarm;
|
||||||
|
else lowerWarningState = alarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float ModifiedLastValue
|
_upperBoundControl.SetValue(threshold.UpperBound.Value);
|
||||||
{
|
_upperBoundControl.SetEnabled(threshold.UpperBound.Enabled);
|
||||||
get => _lastValue * _modifier;
|
_upperBoundControl.SetWarningState(upperDangerState);
|
||||||
set => _lastValue = value / _modifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float _modifier;
|
_lowerBoundControl.SetValue(threshold.LowerBound.Value);
|
||||||
|
_lowerBoundControl.SetEnabled(threshold.LowerBound.Enabled);
|
||||||
|
_lowerBoundControl.SetWarningState(lowerDangerState);
|
||||||
|
|
||||||
private FloatSpinBox _bound;
|
_upperWarningBoundControl.SetValue(threshold.UpperWarningBound.Value);
|
||||||
private CheckBox _boundEnabled;
|
_upperWarningBoundControl.SetEnabled(threshold.UpperWarningBound.Enabled);
|
||||||
|
_upperWarningBoundControl.SetWarningState(upperWarningState);
|
||||||
|
|
||||||
public event Action? OnValidBoundChanged;
|
_lowerWarningBoundControl.SetValue(threshold.LowerWarningBound.Value);
|
||||||
public Func<float?, float?>? OnBoundChanged;
|
_lowerWarningBoundControl.SetEnabled(threshold.LowerWarningBound.Enabled);
|
||||||
public Func<float>? OnBoundEnabled;
|
_lowerWarningBoundControl.SetWarningState(lowerWarningState);
|
||||||
|
|
||||||
public void SetValue(float? value)
|
_enabled.Pressed = !threshold.Ignore;
|
||||||
{
|
|
||||||
_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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ namespace Content.Client.UserInterface.Controls
|
|||||||
[Virtual]
|
[Virtual]
|
||||||
public partial class FancyWindow : BaseWindow
|
public partial class FancyWindow : BaseWindow
|
||||||
{
|
{
|
||||||
|
private const int DRAG_MARGIN_SIZE = 7;
|
||||||
|
|
||||||
public FancyWindow()
|
public FancyWindow()
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
@@ -24,7 +26,30 @@ namespace Content.Client.UserInterface.Controls
|
|||||||
|
|
||||||
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,14 @@ namespace Content.IntegrationTests.Tests.Atmos
|
|||||||
private const string Prototypes = @"
|
private const string Prototypes = @"
|
||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: testThreshold
|
id: testThreshold
|
||||||
upperBound: 5
|
upperBound: !type:AlarmThresholdSetting
|
||||||
lowerBound: 1
|
threshold: 5
|
||||||
upperWarnAround: 0.5
|
lowerBound: !type:AlarmThresholdSetting
|
||||||
lowerWarnAround: 1.5
|
threshold: 1
|
||||||
|
upperWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 0.5
|
||||||
|
lowerWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 1.5
|
||||||
";
|
";
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -35,57 +39,100 @@ namespace Content.IntegrationTests.Tests.Atmos
|
|||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
// ensure upper/lower bounds are calculated
|
// ensure upper/lower bounds are calculated
|
||||||
Assert.That(threshold.UpperWarningBound, Is.EqualTo(5f * 0.5f));
|
Assert.That(threshold.UpperWarningBound.Value, Is.EqualTo(5f * 0.5f));
|
||||||
Assert.That(threshold.LowerWarningBound, Is.EqualTo(1f * 1.5f));
|
Assert.That(threshold.LowerWarningBound.Value, Is.EqualTo(1f * 1.5f));
|
||||||
|
|
||||||
// ensure that setting bounds to zero/
|
// ensure that setting bounds to zero/
|
||||||
// negative numbers is an invalid
|
// negative numbers is an invalid set
|
||||||
// set
|
{
|
||||||
threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Upper, 0);
|
threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, 0f);
|
||||||
Assert.That(threshold.UpperBound, Is.EqualTo(5f));
|
Assert.That(threshold.UpperBound.Value, Is.EqualTo(5f));
|
||||||
threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Upper, -1);
|
threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, -1f);
|
||||||
Assert.That(threshold.UpperBound, Is.EqualTo(5f));
|
Assert.That(threshold.UpperBound.Value, Is.EqualTo(5f));
|
||||||
|
|
||||||
threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, 0);
|
threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, 0f);
|
||||||
Assert.That(threshold.LowerBound, Is.EqualTo(1f));
|
Assert.That(threshold.LowerBound.Value, Is.EqualTo(1f));
|
||||||
threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, -1);
|
threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, -1f);
|
||||||
Assert.That(threshold.LowerBound, Is.EqualTo(1f));
|
Assert.That(threshold.LowerBound.Value, Is.EqualTo(1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// test if making the lower bound higher
|
// test if making the lower bound higher
|
||||||
// than upper is invalid
|
// than upper will adjust the upper value
|
||||||
// aka just returns the previous value
|
{
|
||||||
// instead of setting it to null
|
threshold.SetLimit(AtmosMonitorLimitType.UpperDanger, 5f);
|
||||||
threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, 6f);
|
threshold.SetLimit(AtmosMonitorLimitType.LowerDanger, 6f);
|
||||||
Assert.That(threshold.LowerBound, Is.EqualTo(1f));
|
Assert.That(threshold.LowerBound.Value, Is.LessThanOrEqualTo(threshold.UpperBound.Value));
|
||||||
|
}
|
||||||
|
|
||||||
// same as above, sets it lower
|
// 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);
|
// Check that the warning percentage is calculated correcly
|
||||||
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, threshold.LowerBound - 1);
|
threshold.SetLimit(AtmosMonitorLimitType.LowerWarning, threshold.LowerBound.Value * 1.5f);
|
||||||
Assert.That(threshold.UpperWarningPercentage, Is.EqualTo(0.5f));
|
Assert.That(threshold.LowerWarningPercentage.Value, Is.EqualTo(1.5f));
|
||||||
|
|
||||||
threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Lower, threshold.UpperBound + 1);
|
threshold.SetLimit(AtmosMonitorLimitType.UpperWarning, threshold.UpperBound.Value * 0.5f);
|
||||||
Assert.That(threshold.LowerWarningPercentage, Is.EqualTo(1.5f));
|
Assert.That(threshold.UpperWarningPercentage.Value, Is.EqualTo(0.5f));
|
||||||
|
|
||||||
threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Upper, null);
|
threshold.SetLimit(AtmosMonitorLimitType.LowerWarning, threshold.LowerBound.Value * 1.5f);
|
||||||
threshold.TrySetWarningBound(AtmosMonitorThresholdBound.Lower, null);
|
Assert.That(threshold.LowerWarningPercentage.Value, Is.EqualTo(1.5f));
|
||||||
|
}
|
||||||
|
|
||||||
Assert.That(threshold.UpperWarningBound, Is.EqualTo(null));
|
// Check that the threshold reporting works correctly:
|
||||||
Assert.That(threshold.LowerWarningBound, Is.EqualTo(null));
|
{
|
||||||
|
// 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);
|
// Check a value that's in between each upper/lower warning/panic:
|
||||||
threshold.TrySetPrimaryBound(AtmosMonitorThresholdBound.Lower, null);
|
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));
|
// Check that enable/disable is respected:
|
||||||
Assert.That(threshold.LowerBound, Is.EqualTo(null));
|
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();
|
await pairTracker.CleanReturnAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,35 +14,66 @@ public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks
|
|||||||
[DataField("ignore")]
|
[DataField("ignore")]
|
||||||
public bool Ignore;
|
public bool Ignore;
|
||||||
|
|
||||||
// zero bounds are not allowed - just
|
|
||||||
// set the bound to null if you want
|
|
||||||
// to disable it
|
|
||||||
[DataField("upperBound")]
|
[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")]
|
[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
|
// upper warning percentage
|
||||||
// must always cause UpperWarningBound
|
// must always cause UpperWarningBound
|
||||||
// to be smaller
|
// to be smaller
|
||||||
[DataField("upperWarnAround")]
|
[DataField("upperWarnAround")]
|
||||||
public float? UpperWarningPercentage { get; private set; }
|
public AlarmThresholdSetting UpperWarningPercentage { get; private set; }
|
||||||
|
|
||||||
// lower warning percentage
|
// lower warning percentage
|
||||||
// must always cause LowerWarningBound
|
// must always cause LowerWarningBound
|
||||||
// to be larger
|
// to be larger
|
||||||
[DataField("lowerWarnAround")]
|
[DataField("lowerWarnAround")]
|
||||||
public float? LowerWarningPercentage { get; private set; }
|
public AlarmThresholdSetting LowerWarningPercentage { get; private set; }
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public float? UpperWarningBound => CalculateWarningBound(AtmosMonitorThresholdBound.Upper);
|
public AlarmThresholdSetting UpperWarningBound
|
||||||
|
{
|
||||||
|
get { return CalculateWarningBound(AtmosMonitorThresholdBound.Upper); }
|
||||||
|
set { UpperWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Upper, value); }
|
||||||
|
}
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public float? LowerWarningBound => CalculateWarningBound(AtmosMonitorThresholdBound.Lower);
|
public AlarmThresholdSetting LowerWarningBound
|
||||||
|
{
|
||||||
|
get { return CalculateWarningBound(AtmosMonitorThresholdBound.Lower); }
|
||||||
|
set { LowerWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Lower, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public AtmosAlarmThreshold()
|
public AtmosAlarmThreshold()
|
||||||
{
|
{
|
||||||
|
UpperBound = new AlarmThresholdSetting();
|
||||||
|
LowerBound = new AlarmThresholdSetting();
|
||||||
|
UpperWarningPercentage = new AlarmThresholdSetting();
|
||||||
|
LowerWarningPercentage = new AlarmThresholdSetting();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtmosAlarmThreshold(AtmosAlarmThreshold other)
|
public AtmosAlarmThreshold(AtmosAlarmThreshold other)
|
||||||
@@ -56,186 +87,189 @@ public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks
|
|||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
{
|
{
|
||||||
if (UpperBound <= LowerBound)
|
UpperBound = new AlarmThresholdSetting{ Enabled = UpperBound.Value != 0, Value = UpperBound.Value };
|
||||||
UpperBound = null;
|
LowerBound = new AlarmThresholdSetting{ Enabled = LowerBound.Value != 0, Value = LowerBound.Value };
|
||||||
|
UpperWarningPercentage = new AlarmThresholdSetting{ Enabled = UpperWarningPercentage.Value != 0, Value = UpperWarningPercentage.Value };
|
||||||
if (LowerBound >= UpperBound)
|
LowerWarningPercentage = new AlarmThresholdSetting{ Enabled = LowerWarningPercentage.Value != 0, Value = LowerWarningPercentage.Value };
|
||||||
LowerBound = null;
|
|
||||||
|
|
||||||
if (UpperWarningPercentage != null)
|
|
||||||
TrySetWarningBound(AtmosMonitorThresholdBound.Upper, UpperBound * UpperWarningPercentage);
|
|
||||||
|
|
||||||
if (LowerWarningPercentage != null)
|
|
||||||
TrySetWarningBound(AtmosMonitorThresholdBound.Lower, LowerBound * LowerWarningPercentage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility function to check a threshold against some calculated value
|
// utility function to check a threshold against some calculated value
|
||||||
public bool CheckThreshold(float value, out AtmosAlarmType state)
|
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;
|
state = AtmosAlarmType.Normal;
|
||||||
|
whichFailed = AtmosMonitorThresholdBound.Upper;
|
||||||
|
|
||||||
if (Ignore)
|
if (Ignore)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value >= UpperBound || value <= LowerBound)
|
if (value >= UpperBound)
|
||||||
{
|
{
|
||||||
state = AtmosAlarmType.Danger;
|
state = AtmosAlarmType.Danger;
|
||||||
|
whichFailed = AtmosMonitorThresholdBound.Upper;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (value >= UpperWarningBound || value <= LowerWarningBound)
|
if(value <= LowerBound)
|
||||||
|
{
|
||||||
|
state = AtmosAlarmType.Danger;
|
||||||
|
whichFailed = AtmosMonitorThresholdBound.Lower;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value >= UpperWarningBound)
|
||||||
{
|
{
|
||||||
state = AtmosAlarmType.Warning;
|
state = AtmosAlarmType.Warning;
|
||||||
|
whichFailed = AtmosMonitorThresholdBound.Upper;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (value <= LowerWarningBound)
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the primary bound, takes a hard value
|
|
||||||
public bool TrySetPrimaryBound(AtmosMonitorThresholdBound bound, float? input)
|
|
||||||
{
|
|
||||||
if (input == null)
|
|
||||||
{
|
{
|
||||||
switch (bound)
|
state = AtmosAlarmType.Warning;
|
||||||
{
|
whichFailed = AtmosMonitorThresholdBound.Lower;
|
||||||
case AtmosMonitorThresholdBound.Upper:
|
|
||||||
UpperBound = null;
|
|
||||||
break;
|
|
||||||
case AtmosMonitorThresholdBound.Lower:
|
|
||||||
LowerBound = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the warning bound, takes a hard value
|
/// Warnings are stored in prototypes as a percentage, for ease of content
|
||||||
//
|
/// maintainers. This recalculates a new "real" value of the warning
|
||||||
// this will always set the percentage and
|
/// threshold, for use in the actual atmosphereic checks.
|
||||||
// the raw value at the same time
|
public AlarmThresholdSetting CalculateWarningBound(AtmosMonitorThresholdBound bound)
|
||||||
public bool TrySetWarningBound(AtmosMonitorThresholdBound bound, float? input)
|
|
||||||
{
|
{
|
||||||
if (input == null)
|
|
||||||
{
|
|
||||||
switch (bound)
|
|
||||||
{
|
|
||||||
case AtmosMonitorThresholdBound.Upper:
|
|
||||||
UpperWarningPercentage = null;
|
|
||||||
break;
|
|
||||||
case AtmosMonitorThresholdBound.Lower:
|
|
||||||
LowerWarningPercentage = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (bound)
|
switch (bound)
|
||||||
{
|
{
|
||||||
case AtmosMonitorThresholdBound.Upper:
|
case AtmosMonitorThresholdBound.Upper:
|
||||||
if (UpperBound == null)
|
return new AlarmThresholdSetting {
|
||||||
return false;
|
Enabled = UpperWarningPercentage.Enabled,
|
||||||
|
Value = UpperBound.Value * UpperWarningPercentage.Value};
|
||||||
var upperWarning = (float) (input / UpperBound);
|
|
||||||
var upperTestValue = upperWarning * (float) UpperBound;
|
|
||||||
|
|
||||||
if (upperWarning > 1f
|
|
||||||
|| upperTestValue < LowerWarningBound
|
|
||||||
|| upperTestValue < LowerBound)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
UpperWarningPercentage = upperWarning;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
case AtmosMonitorThresholdBound.Lower:
|
case AtmosMonitorThresholdBound.Lower:
|
||||||
if (LowerBound == null)
|
return new AlarmThresholdSetting {
|
||||||
return false;
|
Enabled = LowerWarningPercentage.Enabled,
|
||||||
|
Value = LowerBound.Value * LowerWarningPercentage.Value};
|
||||||
var lowerWarning = (float) (input / LowerBound);
|
default:
|
||||||
var testValue = lowerWarning * (float) LowerBound;
|
// Unreachable.
|
||||||
|
return new AlarmThresholdSetting();
|
||||||
if (lowerWarning < 1f
|
|
||||||
|| testValue > UpperWarningBound
|
|
||||||
|| testValue > UpperBound)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
LowerWarningPercentage = lowerWarning;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float? CalculateWarningBound(AtmosMonitorThresholdBound bound)
|
public AlarmThresholdSetting CalculateWarningPercentage(AtmosMonitorThresholdBound bound, AlarmThresholdSetting warningBound)
|
||||||
{
|
{
|
||||||
float? value = null;
|
|
||||||
|
|
||||||
switch (bound)
|
switch (bound)
|
||||||
{
|
{
|
||||||
case AtmosMonitorThresholdBound.Upper:
|
case AtmosMonitorThresholdBound.Upper:
|
||||||
if (UpperBound == null || UpperWarningPercentage == null)
|
return new AlarmThresholdSetting {
|
||||||
break;
|
Enabled = UpperWarningPercentage.Enabled,
|
||||||
|
Value = UpperBound.Value == 0 ? 0 : warningBound.Value / UpperBound.Value};
|
||||||
value = UpperBound * UpperWarningPercentage;
|
|
||||||
break;
|
|
||||||
case AtmosMonitorThresholdBound.Lower:
|
case AtmosMonitorThresholdBound.Lower:
|
||||||
if (LowerBound == null || LowerWarningPercentage == null)
|
return new AlarmThresholdSetting {
|
||||||
break;
|
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;
|
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
|
Lower
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum AtmosMonitorLimitType //<todo.eoin Very similar to the above...
|
||||||
|
{
|
||||||
|
LowerDanger,
|
||||||
|
LowerWarning,
|
||||||
|
UpperWarning,
|
||||||
|
UpperDanger,
|
||||||
|
}
|
||||||
|
|
||||||
// not really used in the prototype but in code,
|
// not really used in the prototype but in code,
|
||||||
// to differentiate between the different
|
// to differentiate between the different
|
||||||
// fields you can find this prototype in
|
// fields you can find this prototype in
|
||||||
|
|||||||
@@ -15,14 +15,18 @@ air-alarm-ui-window-resync-devices-label = Resync
|
|||||||
air-alarm-ui-window-mode-label = Mode
|
air-alarm-ui-window-mode-label = Mode
|
||||||
|
|
||||||
air-alarm-ui-window-pressure = {$pressure} kPa
|
air-alarm-ui-window-pressure = {$pressure} kPa
|
||||||
|
air-alarm-ui-window-pressure-indicator = Pressure: [color={$color}]{$pressure} kPa[/color]
|
||||||
air-alarm-ui-window-temperature = {$tempC} C ({$temperature} K)
|
air-alarm-ui-window-temperature = {$tempC} C ({$temperature} K)
|
||||||
air-alarm-ui-window-alarm-state = {$state}
|
air-alarm-ui-window-temperature-indicator = Temperature: [color={$color}]{$tempC} C ({$temperature} K)[/color]
|
||||||
|
air-alarm-ui-window-alarm-state = [color={$color}]{$state}[/color]
|
||||||
|
air-alarm-ui-window-alarm-state-indicator = Status: [color={$color}]{$state}[/color]
|
||||||
|
|
||||||
air-alarm-ui-window-tab-vents = Vents
|
air-alarm-ui-window-tab-vents = Vents
|
||||||
air-alarm-ui-window-tab-scrubbers = Scrubbers
|
air-alarm-ui-window-tab-scrubbers = Scrubbers
|
||||||
air-alarm-ui-window-tab-sensors = Sensors
|
air-alarm-ui-window-tab-sensors = Sensors
|
||||||
|
|
||||||
air-alarm-ui-gases = {$gas}: {$amount} mol ({$percentage}%)
|
air-alarm-ui-gases = {$gas}: {$amount} mol ({$percentage}%)
|
||||||
|
air-alarm-ui-gases-indicator = {$gas}: [color={$color}]{$amount} mol ({$percentage}%)[/color]
|
||||||
|
|
||||||
air-alarm-ui-mode-filtering = Filtering
|
air-alarm-ui-mode-filtering = Filtering
|
||||||
air-alarm-ui-mode-wide-filtering = Filtering (wide)
|
air-alarm-ui-mode-wide-filtering = Filtering (wide)
|
||||||
@@ -55,10 +59,10 @@ air-alarm-ui-scrubber-wide-net-label = WideNet
|
|||||||
|
|
||||||
air-alarm-ui-sensor-gases = Gases
|
air-alarm-ui-sensor-gases = Gases
|
||||||
air-alarm-ui-sensor-thresholds = Thresholds
|
air-alarm-ui-sensor-thresholds = Thresholds
|
||||||
air-alarm-ui-thresholds-pressure-title = Pressure (kPa)
|
air-alarm-ui-thresholds-pressure-title = Thresholds (kPa)
|
||||||
air-alarm-ui-thresholds-temperature-title = Temperature (K)
|
air-alarm-ui-thresholds-temperature-title = Thresholds (K)
|
||||||
air-alarm-ui-thresholds-gas-title = {$gas} (%)
|
air-alarm-ui-thresholds-gas-title = Thresholds (%)
|
||||||
air-alarm-ui-thresholds-upper-bound = Upper bound
|
air-alarm-ui-thresholds-upper-bound = Danger above
|
||||||
air-alarm-ui-thresholds-lower-bound = Lower bound
|
air-alarm-ui-thresholds-lower-bound = Danger below
|
||||||
air-alarm-ui-thresholds-upper-warning-bound = Upper warning bound
|
air-alarm-ui-thresholds-upper-warning-bound = Warning above
|
||||||
air-alarm-ui-thresholds-lower-warning-bound = Lower warning bound
|
air-alarm-ui-thresholds-lower-warning-bound = Warning below
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: stationTemperature
|
id: stationTemperature
|
||||||
upperBound: 393.15 # T20C + 200
|
upperBound: !type:AlarmThresholdSetting
|
||||||
lowerBound: 193.15 # T20C - 100
|
threshold: 393.15 # T20C + 200
|
||||||
upperWarnAround: 0.8
|
lowerBound: !type:AlarmThresholdSetting
|
||||||
lowerWarnAround: 1.1
|
threshold: 193.15 # T20C - 100
|
||||||
|
upperWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 0.8
|
||||||
|
lowerWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 1.1
|
||||||
|
|
||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: stationPressure
|
id: stationPressure
|
||||||
upperBound: 550 # as defined in Atmospherics.cs
|
upperBound: !type:AlarmThresholdSetting
|
||||||
lowerBound: 20 # as defined in Atmospherics.cs
|
threshold: 550 # as defined in Atmospherics.cs
|
||||||
upperWarnAround: 0.7
|
lowerBound: !type:AlarmThresholdSetting
|
||||||
lowerWarnAround: 2.5
|
threshold: 20 # as defined in Atmospherics.cs
|
||||||
|
upperWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 0.7
|
||||||
|
lowerWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 2.5
|
||||||
|
|
||||||
# a reminder that all of these are percentages (where 1 is 100%),
|
# a reminder that all of these are percentages (where 1 is 100%),
|
||||||
# so 0.01 is 1%,
|
# so 0.01 is 1%,
|
||||||
@@ -19,23 +27,31 @@
|
|||||||
|
|
||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: stationOxygen
|
id: stationOxygen
|
||||||
lowerBound: 0.10
|
lowerBound: !type:AlarmThresholdSetting
|
||||||
lowerWarnAround: 1.5
|
threshold: 0.10
|
||||||
|
lowerWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 1.5
|
||||||
|
|
||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: stationCO2
|
id: stationCO2
|
||||||
upperBound: 0.0025
|
upperBound: !type:AlarmThresholdSetting
|
||||||
upperWarnAround: 0.5
|
threshold: 0.0025
|
||||||
|
upperWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 0.5
|
||||||
|
|
||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: stationNO
|
id: stationNO
|
||||||
upperBound: 0.01
|
upperBound: !type:AlarmThresholdSetting
|
||||||
upperWarnAround: 0.5
|
threshold: 0.01
|
||||||
|
upperWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 0.5
|
||||||
|
|
||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: stationMiasma
|
id: stationMiasma
|
||||||
upperBound: 0.05
|
upperBound: !type:AlarmThresholdSetting
|
||||||
upperWarnAround: 0.5
|
threshold: 0.05
|
||||||
|
upperWarnAround: !type:AlarmThresholdSetting
|
||||||
|
threshold: 0.5
|
||||||
|
|
||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: ignore # just ignore nitrogen??? ??? ???
|
id: ignore # just ignore nitrogen??? ??? ???
|
||||||
@@ -43,4 +59,5 @@
|
|||||||
|
|
||||||
- type: alarmThreshold
|
- type: alarmThreshold
|
||||||
id: danger # just any gas you don't want at all
|
id: danger # just any gas you don't want at all
|
||||||
upperBound: 0.0001
|
upperBound: !type:AlarmThresholdSetting
|
||||||
|
threshold: 0.0001
|
||||||
|
|||||||
Reference in New Issue
Block a user