更新
This commit is contained in:
@@ -14,12 +14,14 @@ public sealed class CValueCalibrationActionViewModel : ObservableObject
|
||||
string label,
|
||||
ICommand command,
|
||||
string inactiveDisplayText,
|
||||
string activeDisplayText)
|
||||
string activeDisplayText,
|
||||
bool showStateText = true)
|
||||
{
|
||||
Label = label;
|
||||
Command = command;
|
||||
InactiveDisplayText = inactiveDisplayText;
|
||||
ActiveDisplayText = activeDisplayText;
|
||||
ShowStateText = showStateText;
|
||||
}
|
||||
|
||||
public string Label { get; }
|
||||
@@ -28,6 +30,8 @@ public sealed class CValueCalibrationActionViewModel : ObservableObject
|
||||
|
||||
public string ActiveDisplayText { get; }
|
||||
|
||||
public bool ShowStateText { get; }
|
||||
|
||||
public ICommand Command { get; }
|
||||
|
||||
public bool IsActive
|
||||
@@ -74,6 +78,8 @@ public sealed class CValueCalibrationActionViewModel : ObservableObject
|
||||
|
||||
public string DisplayText => IsBusy
|
||||
? _busyDisplayText
|
||||
: !ShowStateText
|
||||
? Label
|
||||
: IsStatusKnown
|
||||
? IsActive ? ActiveDisplayText : InactiveDisplayText
|
||||
: $"{Label}:未知";
|
||||
|
||||
@@ -96,11 +96,11 @@ public sealed class CValueCalibrationViewModel : PageViewModel
|
||||
|
||||
TopActions =
|
||||
[
|
||||
CreateAction(CirculatingWaterAction, $"{CirculatingWaterAction}:关闭", $"{CirculatingWaterAction}:开启"),
|
||||
CreateAction(MethaneValveAction, $"{MethaneValveAction}:关闭", $"{MethaneValveAction}:开启"),
|
||||
CreateAction(FanAction, $"{FanAction}:关闭", $"{FanAction}:开启"),
|
||||
CreateAction(IgniterAction, $"{IgniterAction}:关闭", $"{IgniterAction}:开启"),
|
||||
CreateAction(SamplingPumpAction, $"{SamplingPumpAction}:关闭", $"{SamplingPumpAction}:开启")
|
||||
CreateAction(CirculatingWaterAction, CirculatingWaterAction, CirculatingWaterAction, showStateText: false),
|
||||
CreateAction(MethaneValveAction, MethaneValveAction, MethaneValveAction, showStateText: false),
|
||||
CreateAction(FanAction, FanAction, FanAction, showStateText: false),
|
||||
CreateAction(IgniterAction, IgniterAction, IgniterAction, showStateText: false),
|
||||
CreateAction(SamplingPumpAction, SamplingPumpAction, SamplingPumpAction, showStateText: false)
|
||||
];
|
||||
|
||||
BottomActions =
|
||||
@@ -524,9 +524,15 @@ public sealed class CValueCalibrationViewModel : PageViewModel
|
||||
private CValueCalibrationActionViewModel CreateAction(
|
||||
string label,
|
||||
string inactiveDisplayText,
|
||||
string activeDisplayText)
|
||||
string activeDisplayText,
|
||||
bool showStateText = true)
|
||||
{
|
||||
var action = new CValueCalibrationActionViewModel(label, ActionCommand, inactiveDisplayText, activeDisplayText);
|
||||
var action = new CValueCalibrationActionViewModel(
|
||||
label,
|
||||
ActionCommand,
|
||||
inactiveDisplayText,
|
||||
activeDisplayText,
|
||||
showStateText);
|
||||
_actionsByLabel[label] = action;
|
||||
return action;
|
||||
}
|
||||
@@ -574,7 +580,7 @@ public sealed class CValueCalibrationViewModel : PageViewModel
|
||||
var nextValue = !isActive;
|
||||
if (_tcpDeviceConnectionService.TryWriteCoil(coilAddress, nextValue))
|
||||
{
|
||||
LastAction = $"{action}{(nextValue ? "开启" : "关闭")}";
|
||||
LastAction = $"{action}控制完成";
|
||||
if (!UpdateActionStatus(action, coilAddress) && _actionsByLabel.TryGetValue(action, out var actionViewModel))
|
||||
{
|
||||
actionViewModel.UpdateStatus(nextValue);
|
||||
@@ -583,7 +589,7 @@ public sealed class CValueCalibrationViewModel : PageViewModel
|
||||
return;
|
||||
}
|
||||
|
||||
LastAction = $"{action}{(nextValue ? "开启" : "关闭")}失败";
|
||||
LastAction = $"{action}控制失败";
|
||||
Debug.WriteLine($"C value calibration action '{action}' write failed.");
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace ConeCalorimeter.ViewModels;
|
||||
public sealed class DeviceActionViewModel : ObservableObject
|
||||
{
|
||||
private string _displayText;
|
||||
private bool _isActive;
|
||||
private bool _isStatusKnown = true;
|
||||
|
||||
public DeviceActionViewModel(string label, ICommand command)
|
||||
{
|
||||
@@ -24,8 +26,26 @@ public sealed class DeviceActionViewModel : ObservableObject
|
||||
|
||||
public ICommand Command { get; }
|
||||
|
||||
public bool IsActive
|
||||
{
|
||||
get => _isActive;
|
||||
private set => SetProperty(ref _isActive, value);
|
||||
}
|
||||
|
||||
public bool IsStatusKnown
|
||||
{
|
||||
get => _isStatusKnown;
|
||||
private set => SetProperty(ref _isStatusKnown, value);
|
||||
}
|
||||
|
||||
public void SetDisplayText(string displayText)
|
||||
{
|
||||
DisplayText = displayText;
|
||||
}
|
||||
|
||||
public void UpdateStatus(bool isActive, bool isStatusKnown = true)
|
||||
{
|
||||
IsStatusKnown = isStatusKnown;
|
||||
IsActive = isActive;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,12 @@ namespace ConeCalorimeter.ViewModels;
|
||||
public sealed class TestPageViewModel : PageViewModel
|
||||
{
|
||||
private const string ResetAction = "复位";
|
||||
private const string FanAction = "风机";
|
||||
private const string IgniterAction = "点火器";
|
||||
private const ushort ResetCoil = 88;
|
||||
private const ushort ResetCompleteCoil = 89;
|
||||
private const ushort FanCoil = 54;
|
||||
private const ushort IgniterCoil = 53;
|
||||
private const double MinimumHeatReleaseAxisMaximum = 150;
|
||||
private const double MinimumTotalAxisMaximum = 150;
|
||||
private const double PlotAxisPaddingFactor = 1.1;
|
||||
@@ -30,6 +34,7 @@ public sealed class TestPageViewModel : PageViewModel
|
||||
private readonly LineSeries _totalHeatSeries;
|
||||
private readonly LineSeries _totalSmokeSeries;
|
||||
private readonly DispatcherTimer _resetStatusTimer;
|
||||
private readonly Dictionary<string, DeviceActionViewModel> _deviceActionsByLabel = [];
|
||||
private DeviceActionViewModel _resetAction = null!;
|
||||
private bool _flameDetected;
|
||||
private bool _resetInProgress;
|
||||
@@ -87,12 +92,14 @@ public sealed class TestPageViewModel : PageViewModel
|
||||
new DeviceActionViewModel("称重台降", ExecuteDeviceActionCommand),
|
||||
new DeviceActionViewModel("测试开始", ExecuteDeviceActionCommand),
|
||||
new DeviceActionViewModel("测试结束", ExecuteDeviceActionCommand),
|
||||
new DeviceActionViewModel("风机开", ExecuteDeviceActionCommand),
|
||||
new DeviceActionViewModel("风机关", ExecuteDeviceActionCommand),
|
||||
new DeviceActionViewModel("点火开", ExecuteDeviceActionCommand),
|
||||
new DeviceActionViewModel("点火关", ExecuteDeviceActionCommand),
|
||||
new DeviceActionViewModel(FanAction, ExecuteDeviceActionCommand),
|
||||
new DeviceActionViewModel(IgniterAction, ExecuteDeviceActionCommand),
|
||||
_resetAction
|
||||
];
|
||||
foreach (var action in DeviceActions)
|
||||
{
|
||||
_deviceActionsByLabel[action.Label] = action;
|
||||
}
|
||||
|
||||
HeatReleasePlot = CreatePlotModel(out _heatReleaseSeries, out _totalHeatSeries, out _totalSmokeSeries);
|
||||
UpdateSnapshot(_experimentDataService.CurrentSnapshot);
|
||||
@@ -102,7 +109,12 @@ public sealed class TestPageViewModel : PageViewModel
|
||||
{
|
||||
Interval = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
_resetStatusTimer.Tick += (_, _) => RefreshResetStatus();
|
||||
_resetStatusTimer.Tick += (_, _) =>
|
||||
{
|
||||
RefreshResetStatus();
|
||||
RefreshToggleActionStates();
|
||||
};
|
||||
RefreshToggleActionStates();
|
||||
_resetStatusTimer.Start();
|
||||
}
|
||||
|
||||
@@ -357,14 +369,21 @@ public sealed class TestPageViewModel : PageViewModel
|
||||
return;
|
||||
}
|
||||
|
||||
LastAction = action;
|
||||
|
||||
if (action == ResetAction)
|
||||
{
|
||||
LastAction = action;
|
||||
ExecuteResetAction();
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsToggleDeviceAction(action))
|
||||
{
|
||||
ToggleDeviceAction(action);
|
||||
return;
|
||||
}
|
||||
|
||||
LastAction = action;
|
||||
|
||||
if (action == "测试开始")
|
||||
{
|
||||
_experimentDataService.StartTest();
|
||||
@@ -455,6 +474,60 @@ public sealed class TestPageViewModel : PageViewModel
|
||||
HeatReleasePlot.InvalidatePlot(true);
|
||||
}
|
||||
|
||||
private void RefreshToggleActionStates()
|
||||
{
|
||||
UpdateToggleActionStatus(FanAction, FanCoil);
|
||||
UpdateToggleActionStatus(IgniterAction, IgniterCoil);
|
||||
}
|
||||
|
||||
private bool UpdateToggleActionStatus(string action, ushort coilAddress)
|
||||
{
|
||||
if (!_deviceActionsByLabel.TryGetValue(action, out var actionViewModel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_tcpDeviceConnectionService.TryReadCoil(coilAddress, out var isActive))
|
||||
{
|
||||
actionViewModel.UpdateStatus(isActive);
|
||||
return true;
|
||||
}
|
||||
|
||||
actionViewModel.UpdateStatus(actionViewModel.IsActive, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ToggleDeviceAction(string action)
|
||||
{
|
||||
if (!TryGetToggleDeviceActionCoil(action, out var coilAddress))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_tcpDeviceConnectionService.TryReadCoil(coilAddress, out var isActive))
|
||||
{
|
||||
LastAction = $"{action}状态读取失败";
|
||||
Debug.WriteLine($"Device action '{action}' state read failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
var nextValue = !isActive;
|
||||
if (_tcpDeviceConnectionService.TryWriteCoil(coilAddress, nextValue))
|
||||
{
|
||||
LastAction = $"{action}控制完成";
|
||||
if (!UpdateToggleActionStatus(action, coilAddress)
|
||||
&& _deviceActionsByLabel.TryGetValue(action, out var actionViewModel))
|
||||
{
|
||||
actionViewModel.UpdateStatus(nextValue);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LastAction = $"{action}控制失败";
|
||||
Debug.WriteLine($"Device action '{action}' write failed.");
|
||||
}
|
||||
|
||||
private static bool TryGetDeviceActionCoil(string action, out ushort coilAddress, out bool value)
|
||||
{
|
||||
value = true;
|
||||
@@ -467,19 +540,26 @@ public sealed class TestPageViewModel : PageViewModel
|
||||
case "测试结束":
|
||||
coilAddress = 67;
|
||||
return true;
|
||||
case "点火开":
|
||||
coilAddress = 53;
|
||||
default:
|
||||
coilAddress = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsToggleDeviceAction(string action)
|
||||
{
|
||||
return TryGetToggleDeviceActionCoil(action, out _);
|
||||
}
|
||||
|
||||
private static bool TryGetToggleDeviceActionCoil(string action, out ushort coilAddress)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case FanAction:
|
||||
coilAddress = FanCoil;
|
||||
return true;
|
||||
case "点火关":
|
||||
coilAddress = 53;
|
||||
value = false;
|
||||
return true;
|
||||
case "风机开":
|
||||
coilAddress = 54;
|
||||
return true;
|
||||
case "风机关":
|
||||
coilAddress = 54;
|
||||
value = false;
|
||||
case IgniterAction:
|
||||
coilAddress = IgniterCoil;
|
||||
return true;
|
||||
default:
|
||||
coilAddress = 0;
|
||||
|
||||
@@ -23,11 +23,36 @@
|
||||
<Setter Property="Background" Value="#EEF1EF" />
|
||||
<Setter Property="BorderBrush" Value="#A9B1AD" />
|
||||
<Setter Property="Foreground" Value="#172320" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="ButtonBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="6"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
RecognizesAccessKey="True" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="ButtonBorder" Property="Opacity" Value="0.86" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="ButtonBorder" Property="Opacity" Value="0.62" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsActive}" Value="True">
|
||||
<Setter Property="Background" Value="#DDEFE6" />
|
||||
<Setter Property="BorderBrush" Value="#3F8B63" />
|
||||
<Setter Property="Foreground" Value="#0D3B24" />
|
||||
<Setter Property="Background" Value="#2F8F5B" />
|
||||
<Setter Property="BorderBrush" Value="#236A45" />
|
||||
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsStatusKnown}" Value="False">
|
||||
<Setter Property="Background" Value="#E5E7E6" />
|
||||
|
||||
@@ -18,6 +18,51 @@
|
||||
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="StatusDeviceActionButtonStyle"
|
||||
TargetType="Button"
|
||||
BasedOn="{StaticResource InstrumentButtonStyle}">
|
||||
<Setter Property="Background" Value="#EEF1EF" />
|
||||
<Setter Property="BorderBrush" Value="#A9B1AD" />
|
||||
<Setter Property="Foreground" Value="#172320" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="ButtonBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="6"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
RecognizesAccessKey="True" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="ButtonBorder" Property="Opacity" Value="0.86" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="ButtonBorder" Property="Opacity" Value="0.62" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsActive}" Value="True">
|
||||
<Setter Property="Background" Value="#2F8F5B" />
|
||||
<Setter Property="BorderBrush" Value="#236A45" />
|
||||
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsStatusKnown}" Value="False">
|
||||
<Setter Property="Background" Value="#E5E7E6" />
|
||||
<Setter Property="BorderBrush" Value="#B8BFBB" />
|
||||
<Setter Property="Foreground" Value="#68736E" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="TopMetricTemplate">
|
||||
<Border Margin="8,10"
|
||||
Padding="10,8"
|
||||
@@ -206,7 +251,7 @@
|
||||
<ItemsControl ItemsSource="{Binding DeviceActions}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="3" Rows="4" />
|
||||
<UniformGrid Columns="3" Rows="3" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
@@ -225,7 +270,7 @@
|
||||
TouchUp="DeviceActionButton_TouchUp"
|
||||
TouchLeave="DeviceActionButton_TouchLeave"
|
||||
LostTouchCapture="DeviceActionButton_LostTouchCapture"
|
||||
Style="{StaticResource InstrumentButtonStyle}" />
|
||||
Style="{StaticResource StatusDeviceActionButtonStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
Reference in New Issue
Block a user