更新0528
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Windows.Threading;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using ConeCalorimeter.Models;
|
using ConeCalorimeter.Models;
|
||||||
@@ -15,31 +14,36 @@ namespace ConeCalorimeter.ViewModels;
|
|||||||
public sealed class TestPageViewModel : PageViewModel
|
public sealed class TestPageViewModel : PageViewModel
|
||||||
{
|
{
|
||||||
private const string ResetAction = "复位";
|
private const string ResetAction = "复位";
|
||||||
|
private const string TestAction = "测试控制";
|
||||||
|
private const string TestStartDisplayText = "测试开始";
|
||||||
|
private const string TestStopDisplayText = "测试结束";
|
||||||
private const string FanAction = "风机";
|
private const string FanAction = "风机";
|
||||||
private const string IgniterAction = "点火器";
|
private const string IgniterAction = "点火器";
|
||||||
private const ushort ResetCoil = 88;
|
private const ushort ResetCoil = 88;
|
||||||
private const ushort ResetCompleteCoil = 89;
|
private const ushort TestStartCoil = 65;
|
||||||
|
private const ushort TestStopCoil = 67;
|
||||||
private const ushort FanCoil = 54;
|
private const ushort FanCoil = 54;
|
||||||
private const ushort IgniterCoil = 53;
|
private const ushort IgniterCoil = 53;
|
||||||
private const double MinimumHeatReleaseAxisMaximum = 150;
|
private const double MinimumHeatReleaseAxisMaximum = 150;
|
||||||
private const double MinimumTotalAxisMaximum = 150;
|
private const double MinimumTotalAxisMaximum = 150;
|
||||||
private const double PlotAxisPaddingFactor = 1.1;
|
private const double PlotAxisPaddingFactor = 1.1;
|
||||||
private const int MaximumPlotPoints = 600;
|
private const int MaximumPlotPoints = 600;
|
||||||
private static readonly TimeSpan ResetPulseDuration = TimeSpan.FromMilliseconds(300);
|
private const int ControlPulseReleaseRetryCount = 3;
|
||||||
private static readonly TimeSpan ResetCompletionTimeout = TimeSpan.FromSeconds(15);
|
private static readonly TimeSpan ControlPulseDuration = TimeSpan.FromMilliseconds(300);
|
||||||
|
private static readonly TimeSpan ControlPulseReleaseRetryDelay = TimeSpan.FromMilliseconds(100);
|
||||||
|
|
||||||
private readonly IExperimentDataService _experimentDataService;
|
private readonly IExperimentDataService _experimentDataService;
|
||||||
private readonly ITcpDeviceConnectionService _tcpDeviceConnectionService;
|
private readonly ITcpDeviceConnectionService _tcpDeviceConnectionService;
|
||||||
private readonly LineSeries _heatReleaseSeries;
|
private readonly LineSeries _heatReleaseSeries;
|
||||||
private readonly LineSeries _totalHeatSeries;
|
private readonly LineSeries _totalHeatSeries;
|
||||||
private readonly LineSeries _totalSmokeSeries;
|
private readonly LineSeries _totalSmokeSeries;
|
||||||
private readonly DispatcherTimer _resetStatusTimer;
|
|
||||||
private readonly Dictionary<string, DeviceActionViewModel> _deviceActionsByLabel = [];
|
private readonly Dictionary<string, DeviceActionViewModel> _deviceActionsByLabel = [];
|
||||||
|
private readonly HashSet<string> _pulseActionsInProgress = [];
|
||||||
|
private DeviceActionViewModel _testAction = null!;
|
||||||
private DeviceActionViewModel _resetAction = null!;
|
private DeviceActionViewModel _resetAction = null!;
|
||||||
private bool _flameDetected;
|
private bool _flameDetected;
|
||||||
private bool _resetInProgress;
|
private bool _resetInProgress;
|
||||||
private int? _lastPlottedSeconds;
|
private int? _lastPlottedSeconds;
|
||||||
private DateTime _resetStartedAt;
|
|
||||||
private string _lastAction = "待机";
|
private string _lastAction = "待机";
|
||||||
|
|
||||||
public TestPageViewModel(
|
public TestPageViewModel(
|
||||||
@@ -83,6 +87,8 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
];
|
];
|
||||||
|
|
||||||
ExecuteDeviceActionCommand = new RelayCommand<string>(ExecuteDeviceAction);
|
ExecuteDeviceActionCommand = new RelayCommand<string>(ExecuteDeviceAction);
|
||||||
|
_testAction = new DeviceActionViewModel(TestAction, ExecuteDeviceActionCommand);
|
||||||
|
_testAction.SetDisplayText(TestStartDisplayText);
|
||||||
_resetAction = new DeviceActionViewModel(ResetAction, ExecuteDeviceActionCommand);
|
_resetAction = new DeviceActionViewModel(ResetAction, ExecuteDeviceActionCommand);
|
||||||
DeviceActions =
|
DeviceActions =
|
||||||
[
|
[
|
||||||
@@ -90,8 +96,7 @@ 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),
|
_testAction,
|
||||||
new DeviceActionViewModel("测试结束", ExecuteDeviceActionCommand),
|
|
||||||
new DeviceActionViewModel(FanAction, ExecuteDeviceActionCommand),
|
new DeviceActionViewModel(FanAction, ExecuteDeviceActionCommand),
|
||||||
new DeviceActionViewModel(IgniterAction, ExecuteDeviceActionCommand),
|
new DeviceActionViewModel(IgniterAction, ExecuteDeviceActionCommand),
|
||||||
_resetAction
|
_resetAction
|
||||||
@@ -101,21 +106,10 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
_deviceActionsByLabel[action.Label] = action;
|
_deviceActionsByLabel[action.Label] = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefreshToggleActionStates();
|
||||||
HeatReleasePlot = CreatePlotModel(out _heatReleaseSeries, out _totalHeatSeries, out _totalSmokeSeries);
|
HeatReleasePlot = CreatePlotModel(out _heatReleaseSeries, out _totalHeatSeries, out _totalSmokeSeries);
|
||||||
UpdateSnapshot(_experimentDataService.CurrentSnapshot);
|
UpdateSnapshot(_experimentDataService.CurrentSnapshot);
|
||||||
_experimentDataService.SnapshotUpdated += (_, snapshot) => UpdateSnapshot(snapshot);
|
_experimentDataService.SnapshotUpdated += (_, snapshot) => UpdateSnapshot(snapshot);
|
||||||
|
|
||||||
_resetStatusTimer = new DispatcherTimer
|
|
||||||
{
|
|
||||||
Interval = TimeSpan.FromSeconds(1)
|
|
||||||
};
|
|
||||||
_resetStatusTimer.Tick += (_, _) =>
|
|
||||||
{
|
|
||||||
RefreshResetStatus();
|
|
||||||
RefreshToggleActionStates();
|
|
||||||
};
|
|
||||||
RefreshToggleActionStates();
|
|
||||||
_resetStatusTimer.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<MetricDisplayViewModel> TopMetrics { get; }
|
public ObservableCollection<MetricDisplayViewModel> TopMetrics { get; }
|
||||||
@@ -329,34 +323,6 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
return Math.Max(minimumMaximum, Math.Ceiling(maximum * PlotAxisPaddingFactor));
|
return Math.Max(minimumMaximum, Math.Ceiling(maximum * PlotAxisPaddingFactor));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool StartMomentaryDeviceAction(string? action)
|
|
||||||
{
|
|
||||||
return TryWriteMomentaryDeviceAction(action, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool StopMomentaryDeviceAction(string? action)
|
|
||||||
{
|
|
||||||
return TryWriteMomentaryDeviceAction(action, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryWriteMomentaryDeviceAction(string? action, bool isRunning)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(action)
|
|
||||||
|| !TryGetMomentaryDeviceActionCoil(action, out var coilAddress))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LastAction = isRunning ? action : $"停止:{action}";
|
|
||||||
|
|
||||||
if (!_tcpDeviceConnectionService.TryWriteCoil(coilAddress, isRunning))
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"Momentary device action '{action}' write failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteDeviceAction(string? action)
|
private void ExecuteDeviceAction(string? action)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(action))
|
if (string.IsNullOrWhiteSpace(action))
|
||||||
@@ -364,8 +330,9 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsMomentaryDeviceAction(action))
|
if (action == TestAction)
|
||||||
{
|
{
|
||||||
|
ExecuteTestControlAction();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,33 +343,19 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsToggleDeviceAction(action))
|
if (TryGetToggleDeviceActionCoil(action, out var toggleCoilAddress))
|
||||||
{
|
{
|
||||||
ToggleDeviceAction(action);
|
ToggleDeviceAction(action, toggleCoilAddress);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryGetPulseDeviceActionCoil(action, out var coilAddress))
|
||||||
|
{
|
||||||
|
ExecutePulseDeviceAction(action, coilAddress);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LastAction = action;
|
LastAction = action;
|
||||||
|
|
||||||
if (action == "测试开始")
|
|
||||||
{
|
|
||||||
_experimentDataService.StartTest();
|
|
||||||
ClearPlotSeries();
|
|
||||||
}
|
|
||||||
else if (action == "测试结束")
|
|
||||||
{
|
|
||||||
_experimentDataService.StopTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryGetDeviceActionCoil(action, out var coilAddress, out var value))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_tcpDeviceConnectionService.TryWriteCoil(coilAddress, value))
|
|
||||||
{
|
|
||||||
Debug.WriteLine($"Device action '{action}' write failed.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ExecuteResetAction()
|
private async void ExecuteResetAction()
|
||||||
@@ -421,37 +374,18 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
|
|
||||||
SetResetInProgress(true);
|
SetResetInProgress(true);
|
||||||
LastAction = "复位中";
|
LastAction = "复位中";
|
||||||
_resetStartedAt = DateTime.Now;
|
|
||||||
|
|
||||||
await Task.Delay(ResetPulseDuration);
|
await Task.Delay(ControlPulseDuration);
|
||||||
if (!_tcpDeviceConnectionService.TryWriteCoil(ResetCoil, false))
|
if (await ReleaseControlPulseAsync(ResetCoil, ResetAction))
|
||||||
{
|
{
|
||||||
Debug.WriteLine("Device reset pulse release failed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshResetStatus()
|
|
||||||
{
|
|
||||||
if (!_resetInProgress)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var statusRead = _tcpDeviceConnectionService.TryReadCoil(ResetCompleteCoil, out var resetComplete);
|
|
||||||
if (statusRead && resetComplete)
|
|
||||||
{
|
|
||||||
SetResetInProgress(false);
|
|
||||||
LastAction = "复位完成";
|
LastAction = "复位完成";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (DateTime.Now - _resetStartedAt < ResetCompletionTimeout)
|
|
||||||
{
|
{
|
||||||
return;
|
LastAction = "复位线圈复位失败";
|
||||||
}
|
}
|
||||||
|
|
||||||
SetResetInProgress(false);
|
SetResetInProgress(false);
|
||||||
LastAction = statusRead ? "复位超时" : "复位状态读取失败";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetResetInProgress(bool resetInProgress)
|
private void SetResetInProgress(bool resetInProgress)
|
||||||
@@ -474,6 +408,58 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
HeatReleasePlot.InvalidatePlot(true);
|
HeatReleasePlot.InvalidatePlot(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void ExecuteTestControlAction()
|
||||||
|
{
|
||||||
|
if (!TryBeginPulseAction(TestAction))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isStarting = !_testAction.IsActive;
|
||||||
|
var actionText = isStarting ? TestStartDisplayText : TestStopDisplayText;
|
||||||
|
var coilAddress = isStarting ? TestStartCoil : TestStopCoil;
|
||||||
|
|
||||||
|
if (!_tcpDeviceConnectionService.TryWriteCoil(coilAddress, true))
|
||||||
|
{
|
||||||
|
LastAction = $"{actionText}失败";
|
||||||
|
Debug.WriteLine($"Device action '{actionText}' write failed.");
|
||||||
|
EndPulseAction(TestAction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isStarting)
|
||||||
|
{
|
||||||
|
_experimentDataService.StartTest();
|
||||||
|
ClearPlotSeries();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_experimentDataService.StopTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTestRunning(isStarting);
|
||||||
|
LastAction = actionText;
|
||||||
|
|
||||||
|
await Task.Delay(ControlPulseDuration);
|
||||||
|
if (!await ReleaseControlPulseAsync(coilAddress, actionText))
|
||||||
|
{
|
||||||
|
LastAction = $"{actionText}线圈复位失败";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
EndPulseAction(TestAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTestRunning(bool isRunning)
|
||||||
|
{
|
||||||
|
_testAction.UpdateStatus(isRunning);
|
||||||
|
_testAction.SetDisplayText(isRunning ? TestStopDisplayText : TestStartDisplayText);
|
||||||
|
}
|
||||||
|
|
||||||
private void RefreshToggleActionStates()
|
private void RefreshToggleActionStates()
|
||||||
{
|
{
|
||||||
UpdateToggleActionStatus(FanAction, FanCoil);
|
UpdateToggleActionStatus(FanAction, FanCoil);
|
||||||
@@ -497,17 +483,13 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleDeviceAction(string action)
|
private void ToggleDeviceAction(string action, ushort coilAddress)
|
||||||
{
|
{
|
||||||
if (!TryGetToggleDeviceActionCoil(action, out var coilAddress))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_tcpDeviceConnectionService.TryReadCoil(coilAddress, out var isActive))
|
if (!_tcpDeviceConnectionService.TryReadCoil(coilAddress, out var isActive))
|
||||||
{
|
{
|
||||||
LastAction = $"{action}状态读取失败";
|
LastAction = $"{action}状态读取失败";
|
||||||
Debug.WriteLine($"Device action '{action}' state read failed.");
|
Debug.WriteLine($"Device action '{action}' state read failed.");
|
||||||
|
UpdateToggleActionStatus(action, coilAddress);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,27 +510,71 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
Debug.WriteLine($"Device action '{action}' write failed.");
|
Debug.WriteLine($"Device action '{action}' write failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryGetDeviceActionCoil(string action, out ushort coilAddress, out bool value)
|
private async void ExecutePulseDeviceAction(string action, ushort coilAddress)
|
||||||
{
|
{
|
||||||
value = true;
|
if (!TryBeginPulseAction(action))
|
||||||
|
|
||||||
switch (action)
|
|
||||||
{
|
{
|
||||||
case "测试开始":
|
return;
|
||||||
coilAddress = 65;
|
}
|
||||||
return true;
|
|
||||||
case "测试结束":
|
_deviceActionsByLabel.TryGetValue(action, out var actionViewModel);
|
||||||
coilAddress = 67;
|
|
||||||
return true;
|
if (!_tcpDeviceConnectionService.TryWriteCoil(coilAddress, true))
|
||||||
default:
|
{
|
||||||
coilAddress = 0;
|
LastAction = $"{action}失败";
|
||||||
return false;
|
Debug.WriteLine($"Device action '{action}' write failed.");
|
||||||
|
EndPulseAction(action);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
actionViewModel?.UpdateStatus(true);
|
||||||
|
LastAction = action;
|
||||||
|
|
||||||
|
await Task.Delay(ControlPulseDuration);
|
||||||
|
if (await ReleaseControlPulseAsync(coilAddress, action))
|
||||||
|
{
|
||||||
|
actionViewModel?.UpdateStatus(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionViewModel?.UpdateStatus(true, false);
|
||||||
|
LastAction = $"{action}线圈复位失败";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
EndPulseAction(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsToggleDeviceAction(string action)
|
private bool TryBeginPulseAction(string action)
|
||||||
{
|
{
|
||||||
return TryGetToggleDeviceActionCoil(action, out _);
|
return _pulseActionsInProgress.Add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndPulseAction(string action)
|
||||||
|
{
|
||||||
|
_pulseActionsInProgress.Remove(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ReleaseControlPulseAsync(ushort coilAddress, string actionText)
|
||||||
|
{
|
||||||
|
for (var attempt = 1; attempt <= ControlPulseReleaseRetryCount; attempt++)
|
||||||
|
{
|
||||||
|
if (_tcpDeviceConnectionService.TryWriteCoil(coilAddress, false))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt < ControlPulseReleaseRetryCount)
|
||||||
|
{
|
||||||
|
await Task.Delay(ControlPulseReleaseRetryDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.WriteLine($"Device action '{actionText}' pulse release failed.");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryGetToggleDeviceActionCoil(string action, out ushort coilAddress)
|
private static bool TryGetToggleDeviceActionCoil(string action, out ushort coilAddress)
|
||||||
@@ -567,12 +593,7 @@ public sealed class TestPageViewModel : PageViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsMomentaryDeviceAction(string action)
|
private static bool TryGetPulseDeviceActionCoil(string action, out ushort coilAddress)
|
||||||
{
|
|
||||||
return TryGetMomentaryDeviceActionCoil(action, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetMomentaryDeviceActionCoil(string action, out ushort coilAddress)
|
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
FontWeight="SemiBold"
|
FontWeight="SemiBold"
|
||||||
Foreground="#1B2826" />
|
Foreground="#1B2826" />
|
||||||
<TextBlock Margin="0,22,0,0"
|
<TextBlock Margin="0,22,0,0"
|
||||||
Text="1. 0%为不透光校准;"
|
Text="1. 0%为不遮光校准;"
|
||||||
FontSize="22"
|
FontSize="22"
|
||||||
Foreground="#111A18" />
|
Foreground="#111A18" />
|
||||||
<TextBlock Margin="0,14,0,0"
|
<TextBlock Margin="0,14,0,0"
|
||||||
|
|||||||
@@ -262,29 +262,20 @@
|
|||||||
<ItemsControl ItemsSource="{Binding DeviceActions}">
|
<ItemsControl ItemsSource="{Binding DeviceActions}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<UniformGrid Columns="3" Rows="3" />
|
<UniformGrid Columns="2" Rows="4" />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Button Content="{Binding DisplayText}"
|
<Button Content="{Binding DisplayText}"
|
||||||
Command="{Binding Command}"
|
Command="{Binding Command}"
|
||||||
CommandParameter="{Binding Label}"
|
CommandParameter="{Binding Label}"
|
||||||
Margin="5,3"
|
Margin="5,3"
|
||||||
MinHeight="40"
|
MinHeight="40"
|
||||||
PreviewMouseLeftButtonDown="DeviceActionButton_PreviewMouseLeftButtonDown"
|
|
||||||
PreviewMouseLeftButtonUp="DeviceActionButton_PreviewMouseLeftButtonUp"
|
|
||||||
MouseLeave="DeviceActionButton_MouseLeave"
|
|
||||||
LostMouseCapture="DeviceActionButton_LostMouseCapture"
|
|
||||||
LostKeyboardFocus="DeviceActionButton_LostKeyboardFocus"
|
|
||||||
TouchDown="DeviceActionButton_TouchDown"
|
|
||||||
TouchUp="DeviceActionButton_TouchUp"
|
|
||||||
TouchLeave="DeviceActionButton_TouchLeave"
|
|
||||||
LostTouchCapture="DeviceActionButton_LostTouchCapture"
|
|
||||||
Style="{StaticResource StatusDeviceActionButtonStyle}" />
|
Style="{StaticResource StatusDeviceActionButtonStyle}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -1,159 +1,11 @@
|
|||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
|
||||||
using ConeCalorimeter.ViewModels;
|
|
||||||
|
|
||||||
namespace ConeCalorimeter.Views;
|
namespace ConeCalorimeter.Views;
|
||||||
|
|
||||||
public partial class TestPageView : UserControl
|
public partial class TestPageView : UserControl
|
||||||
{
|
{
|
||||||
private readonly HashSet<string> _runningMomentaryActions = [];
|
|
||||||
|
|
||||||
public TestPageView()
|
public TestPageView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeviceActionButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
if (StartMomentaryAction(sender))
|
|
||||||
{
|
|
||||||
if (sender is Button button)
|
|
||||||
{
|
|
||||||
button.CaptureMouse();
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceActionButton_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
if (StopMomentaryAction(sender))
|
|
||||||
{
|
|
||||||
ReleaseInputCapture(sender);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceActionButton_MouseLeave(object sender, MouseEventArgs e)
|
|
||||||
{
|
|
||||||
if (StopMomentaryAction(sender))
|
|
||||||
{
|
|
||||||
ReleaseInputCapture(sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceActionButton_LostMouseCapture(object sender, MouseEventArgs e)
|
|
||||||
{
|
|
||||||
StopMomentaryAction(sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceActionButton_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (StopMomentaryAction(sender))
|
|
||||||
{
|
|
||||||
ReleaseInputCapture(sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceActionButton_TouchDown(object sender, TouchEventArgs e)
|
|
||||||
{
|
|
||||||
if (StartMomentaryAction(sender))
|
|
||||||
{
|
|
||||||
if (sender is Button button)
|
|
||||||
{
|
|
||||||
button.CaptureTouch(e.TouchDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceActionButton_TouchUp(object sender, TouchEventArgs e)
|
|
||||||
{
|
|
||||||
if (StopMomentaryAction(sender))
|
|
||||||
{
|
|
||||||
ReleaseInputCapture(sender);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceActionButton_TouchLeave(object sender, TouchEventArgs e)
|
|
||||||
{
|
|
||||||
if (StopMomentaryAction(sender))
|
|
||||||
{
|
|
||||||
ReleaseInputCapture(sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeviceActionButton_LostTouchCapture(object sender, TouchEventArgs e)
|
|
||||||
{
|
|
||||||
StopMomentaryAction(sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool StartMomentaryAction(object sender)
|
|
||||||
{
|
|
||||||
if (!TryGetDeviceAction(sender, out var label, out var viewModel))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_runningMomentaryActions.Contains(label))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!viewModel.StartMomentaryDeviceAction(label))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_runningMomentaryActions.Add(label);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool StopMomentaryAction(object sender)
|
|
||||||
{
|
|
||||||
if (!TryGetDeviceAction(sender, out var label, out var viewModel)
|
|
||||||
|| !_runningMomentaryActions.Remove(label))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return viewModel.StopMomentaryDeviceAction(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryGetDeviceAction(
|
|
||||||
object sender,
|
|
||||||
out string label,
|
|
||||||
out TestPageViewModel viewModel)
|
|
||||||
{
|
|
||||||
label = string.Empty;
|
|
||||||
viewModel = null!;
|
|
||||||
|
|
||||||
if (sender is not Button { DataContext: DeviceActionViewModel action }
|
|
||||||
|| DataContext is not TestPageViewModel testPageViewModel)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
label = action.Label;
|
|
||||||
viewModel = testPageViewModel;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReleaseInputCapture(object sender)
|
|
||||||
{
|
|
||||||
if (sender is not Button button)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button.IsMouseCaptured)
|
|
||||||
{
|
|
||||||
button.ReleaseMouseCapture();
|
|
||||||
}
|
|
||||||
|
|
||||||
button.ReleaseAllTouchCaptures();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user