This commit is contained in:
GukSang.Jin
2026-06-15 18:45:15 +08:00
parent 57b423fef1
commit dc83cc8800
2 changed files with 391 additions and 90 deletions

View File

@@ -1,3 +1,4 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -13,6 +14,8 @@ public partial class MainWindow : Window
private readonly DispatcherTimer _hiddenSettingsPressTimer;
private bool _isHiddenSettingsPressActive;
private bool _hasOpenedHiddenSettingsForCurrentPress;
private bool _isCloseStopInProgress;
private bool _allowConfirmedClose;
public MainWindow()
{
@@ -25,9 +28,59 @@ public partial class MainWindow : Window
};
_hiddenSettingsPressTimer.Tick += HiddenSettingsPressTimer_Tick;
Deactivated += (_, _) => ReleaseAllManualMotionTargetsAsync();
Closing += MainWindow_Closing;
Log.Information("主窗口创建完成");
}
private async void MainWindow_Closing(object? sender, CancelEventArgs e)
{
if (_allowConfirmedClose
|| DataContext is not MainWindowViewModel viewModel
|| !viewModel.HasActiveControlRun())
{
return;
}
e.Cancel = true;
if (_isCloseStopInProgress)
{
return;
}
_isCloseStopInProgress = true;
try
{
Log.Warning("检测到活动测试,关闭窗口前开始发送并确认停止指令");
if (!await viewModel.TryStopActiveTestsForCloseAsync())
{
viewModel.CancelCloseStopRequest();
MessageBox.Show(
"测试停止状态未确认,软件将保持开启。请检查设备状态并再次点击停止。",
"无法安全关闭",
MessageBoxButton.OK,
MessageBoxImage.Warning);
return;
}
_allowConfirmedClose = true;
Close();
}
catch (Exception ex)
{
viewModel.CancelCloseStopRequest();
Log.Error(ex, "关闭软件前停止活动测试失败,窗口保持开启");
MessageBox.Show(
$"关闭软件前停止测试失败,软件将保持开启:{OperatorMessageFormatter.FromException(ex)}",
"无法安全关闭",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
finally
{
_isCloseStopInProgress = false;
}
}
private void HiddenSettingsHotspot_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;

View File

@@ -43,6 +43,7 @@ public sealed class MainWindowViewModel : ObservableObject
private const ushort VentValveCoil = 6;
private const ushort AxialForceModeCoil = 30;
private const ushort AxialStartCoil = 70;
private const ushort AxialEnabledCoil = 71;
private const ushort AxialDoneCoil = 72;
private const ushort AxialStopCoil = 73;
private const ushort SpeedTorqueStartCoil = 80;
@@ -107,7 +108,7 @@ public sealed class MainWindowViewModel : ObservableObject
private static readonly TimeSpan RealtimeDataFreshnessTimeout = TimeSpan.FromSeconds(3);
private static readonly TimeSpan SnapshotInterval = TimeSpan.FromSeconds(5);
private static readonly TimeSpan NoLoadCaptureDuration = TimeSpan.FromSeconds(3);
private static readonly TimeSpan SpeedTorqueStartConfirmationTimeout = TimeSpan.FromSeconds(5);
private static readonly TimeSpan RunStateConfirmationTimeout = TimeSpan.FromSeconds(5);
private static readonly ushort[] RealtimeRegisterAddresses =
[
@@ -154,7 +155,9 @@ public sealed class MainWindowViewModel : ObservableObject
[
VentValveCoil,
AxialForceModeCoil,
AxialEnabledCoil,
AxialDoneCoil,
SpeedTorqueEnabledCoil,
SpeedTorqueDoneCoil,
SpeedTorqueStableCoil,
SpeedTorqueResetEnabledCoil,
@@ -200,7 +203,10 @@ public sealed class MainWindowViewModel : ObservableObject
private double? _finalDisplacement;
private double _axialForce;
private double? _finalAxialForce;
private bool _isDisplacementStarting;
private bool _isDisplacementRunning;
private bool _isDisplacementStartupUncertain;
private int _axialDisabledPollCount;
private double _realtimeSpeed;
private double _realtimeTorque;
private double _realtimePressure;
@@ -215,23 +221,28 @@ public sealed class MainWindowViewModel : ObservableObject
private double? _finalSpeed;
private double? _finalTorque;
private DateTime? _speedTorqueStartedAt;
private bool _isSpeedTorqueStarting;
private bool _isSpeedTorqueRunning;
private bool _isSpeedTorqueCompletionArmed;
private bool _hasLoggedStaleSpeedTorqueDone;
private bool _isSpeedTorqueStartupUncertain;
private int _speedTorqueDisabledPollCount;
private bool _wasSpeedTorqueStable;
private bool _isReadingRealtime;
private bool _isWritingAxialForceMode;
private bool _isReadingParameterConfig;
private bool _hasLoadedParameterConfigFromPlc;
private bool _lastRealtimeReadFailed;
private bool _hasReportedRunStateCommunicationLoss;
private DateTime _lastSuccessfulRealtimeReadAt = DateTime.MinValue;
private bool _isAutoStoppingDisplacement;
private bool _isAutoStoppingSpeedTorque;
private bool _isConfirmingDisplacementStop;
private bool _isConfirmingSpeedTorqueStop;
private bool _isApplyingParameterConfigToInputs;
private bool _isDisplacementResetting;
private bool _isSpeedTorqueResetting;
private bool _isSpeedTorqueZeroing;
private bool _hasShownSpeedTorqueEndWarnings;
private bool _isCloseStopRequested;
private DateTime _lastParameterReadFailureLogAt = DateTime.MinValue;
private string _dialIndicatorText = "0.000 mm";
private string _axialAxisPositionText = "0.000 mm";
@@ -382,6 +393,39 @@ public sealed class MainWindowViewModel : ObservableObject
return _parameterConfig.ToPlcConnectionConfig();
}
internal bool HasActiveControlRun()
{
return _isDisplacementStarting
|| _isDisplacementRunning
|| _isSpeedTorqueStarting
|| _isSpeedTorqueRunning;
}
internal async Task<bool> TryStopActiveTestsForCloseAsync()
{
_isCloseStopRequested = true;
if (_isDisplacementStarting)
{
await AbortDisplacementStartupAsync("关闭软件前已中止轴向启动。", "关闭软件");
_isDisplacementStarting = false;
}
if (_isSpeedTorqueStarting)
{
await AbortSpeedTorqueStartupAsync("关闭软件前已中止转速/扭矩启动。", "关闭软件");
_isSpeedTorqueStarting = false;
}
bool axialStopped = await StopDisplacementControlAsync("状态:关闭软件前已停止", "关闭软件前轴向停止");
bool speedTorqueStopped = await StopSpeedTorqueControlAsync("状态:关闭软件前已停止", "关闭软件前转速/扭矩停止");
return axialStopped && speedTorqueStopped;
}
internal void CancelCloseStopRequest()
{
_isCloseStopRequested = false;
}
internal async Task<bool> BeginManualMotionAsync(ManualMotionTarget target)
{
return target switch
@@ -814,38 +858,30 @@ public sealed class MainWindowViewModel : ObservableObject
FinalizeNoLoadSpeedRunIfDue();
QueueSnapshotIfDue();
if (_isDisplacementRunning && ReadCoilValue(coilValues, AxialDoneCoil))
{
StopDisplacementTest("状态:已完成");
}
bool isSpeedTorqueDone = ReadCoilValue(coilValues, SpeedTorqueDoneCoil);
if (_isSpeedTorqueRunning && !_isSpeedTorqueCompletionArmed)
{
if (!isSpeedTorqueDone)
{
_isSpeedTorqueCompletionArmed = true;
_hasLoggedStaleSpeedTorqueDone = false;
Log.Information("转速/扭矩完成判定已就绪:新测试启动后已确认 M{DoneCoil}=0", SpeedTorqueDoneCoil);
}
else if (!_hasLoggedStaleSpeedTorqueDone)
{
_hasLoggedStaleSpeedTorqueDone = true;
Log.Warning("忽略转速/扭矩测试启动后的残留完成状态M{DoneCoil}=1等待先恢复为0后再接受完成信号", SpeedTorqueDoneCoil);
}
}
else if (_isSpeedTorqueRunning && isSpeedTorqueDone)
{
await StopSpeedTorqueTestAsync("状态:已完成");
}
bool hadActiveControlRun = HasActiveControlRun();
SynchronizeAxialRunState(
ReadCoilValue(coilValues, AxialEnabledCoil),
ReadCoilValue(coilValues, AxialDoneCoil));
await SynchronizeSpeedTorqueRunStateAsync(
ReadCoilValue(coilValues, SpeedTorqueEnabledCoil),
ReadCoilValue(coilValues, SpeedTorqueDoneCoil));
await AutoStopIfSetpointReachedAsync();
await AutoStopIfSpeedTorqueProtectionReachedAsync();
if (_lastRealtimeReadFailed)
{
StatusText = "实时数据已恢复";
if (HasActiveControlRun())
{
StatusText = "PLC 实时通信已恢复,测试运行状态已按实际点位同步。";
}
else if (!hadActiveControlRun)
{
StatusText = "实时数据已恢复";
}
_lastRealtimeReadFailed = false;
_hasReportedRunStateCommunicationLoss = false;
Log.Information("PLC实时数据读取已恢复");
}
}
@@ -857,6 +893,15 @@ public sealed class MainWindowViewModel : ObservableObject
_lastRealtimeReadFailed = true;
Log.Warning(ex, "PLC实时数据读取失败后续相同故障将等待恢复后再记录");
}
if (HasActiveControlRun()
&& !_hasReportedRunStateCommunicationLoss
&& DateTime.Now - _lastSuccessfulRealtimeReadAt > RealtimeDataFreshnessTimeout)
{
_hasReportedRunStateCommunicationLoss = true;
StatusText = "PLC 通讯异常超过 3 秒,测试运行状态无法确认;保持测试中并持续重连。";
Log.Error("PLC通讯异常超过 {TimeoutSeconds} 秒,活动测试运行状态无法确认,软件保持运行状态", RealtimeDataFreshnessTimeout.TotalSeconds);
}
}
finally
{
@@ -864,6 +909,48 @@ public sealed class MainWindowViewModel : ObservableObject
}
}
private void SynchronizeAxialRunState(bool enabled, bool done)
{
if (!_isDisplacementRunning || _isConfirmingDisplacementStop)
{
_axialDisabledPollCount = 0;
return;
}
if (done)
{
StopDisplacementTest("状态:已完成");
return;
}
_axialDisabledPollCount = enabled ? 0 : _axialDisabledPollCount + 1;
if (_axialDisabledPollCount >= 3)
{
StopDisplacementTest("状态PLC 轴向运行使能已断开");
}
}
private async Task SynchronizeSpeedTorqueRunStateAsync(bool enabled, bool done)
{
if (!_isSpeedTorqueRunning || _isConfirmingSpeedTorqueStop)
{
_speedTorqueDisabledPollCount = 0;
return;
}
if (done)
{
await StopSpeedTorqueTestAsync("状态:已完成");
return;
}
_speedTorqueDisabledPollCount = enabled ? 0 : _speedTorqueDisabledPollCount + 1;
if (_speedTorqueDisabledPollCount >= 3)
{
await StopSpeedTorqueTestAsync("状态PLC 转速/扭矩运行使能已断开");
}
}
private async void ParameterRetryTimer_Tick(object? sender, EventArgs e)
{
if (_hasLoadedParameterConfigFromPlc)
@@ -2597,18 +2684,20 @@ public sealed class MainWindowViewModel : ObservableObject
return;
}
if (_isSpeedTorqueRunning)
if (_isSpeedTorqueStarting || _isSpeedTorqueRunning)
{
UpdateSpeedTorqueDisplay();
return;
}
_isSpeedTorqueStarting = true;
SpeedTorqueTestButtonText = "启动中";
StatusText = "转速/扭矩测试启动中...";
if (!await PulsePlcAsync(SpeedTorqueStartCoil, "转速/扭矩测试"))
{
await AbortSpeedTorqueStartupAsync("M80 启动指令失败,已停止测试。", "M80启动指令失败");
_isSpeedTorqueStarting = false;
return;
}
@@ -2617,20 +2706,30 @@ public sealed class MainWindowViewModel : ObservableObject
if (!await WaitForSpeedTorqueStartAsync())
{
await AbortSpeedTorqueStartupAsync("PLC 未进入扭矩测试运行状态,已停止测试。", "启动状态确认失败");
_isSpeedTorqueStarting = false;
return;
}
if (_isCloseStopRequested)
{
await AbortSpeedTorqueStartupAsync("关闭软件请求已中止转速/扭矩启动。", "关闭软件");
_isSpeedTorqueStarting = false;
return;
}
if (!TryGetRealtimeSpeed(out _) || !TryGetRealtimeTorque(out _))
{
await AbortSpeedTorqueStartupAsync("实时数据无效,已停止测试。", "启动数据无效");
_isSpeedTorqueStarting = false;
UpdateSpeedTorqueDisplay();
return;
}
_isSpeedTorqueStarting = false;
PrepareSessionForNewRun();
_isSpeedTorqueRunning = true;
_isSpeedTorqueCompletionArmed = true;
_hasLoggedStaleSpeedTorqueDone = false;
_isSpeedTorqueStartupUncertain = false;
_speedTorqueDisabledPollCount = 0;
_wasSpeedTorqueStable = false;
SpeedTorqueTestButtonText = "测试中";
_hasShownSpeedTorqueEndWarnings = false;
@@ -2655,7 +2754,15 @@ public sealed class MainWindowViewModel : ObservableObject
private async Task<bool> WaitForSpeedTorqueStartAsync()
{
DateTime deadline = DateTime.Now.Add(SpeedTorqueStartConfirmationTimeout);
return await WaitForRunStartAsync(
SpeedTorqueEnabledCoil,
SpeedTorqueDoneCoil,
"转速/扭矩");
}
private async Task<bool> WaitForRunStartAsync(ushort enabledCoil, ushort doneCoil, string testName)
{
DateTime deadline = DateTime.Now.Add(RunStateConfirmationTimeout);
bool lastEnabled = false;
bool lastDone = false;
bool hasReadState = false;
@@ -2667,17 +2774,18 @@ public sealed class MainWindowViewModel : ObservableObject
{
IReadOnlyDictionary<ushort, bool> values = await _plcCoilService.ReadCoilValuesAsync(
_parameterConfig.ToPlcConnectionConfig(),
[SpeedTorqueEnabledCoil, SpeedTorqueDoneCoil]);
lastEnabled = ReadCoilValue(values, SpeedTorqueEnabledCoil);
lastDone = ReadCoilValue(values, SpeedTorqueDoneCoil);
[enabledCoil, doneCoil]);
lastEnabled = ReadCoilValue(values, enabledCoil);
lastDone = ReadCoilValue(values, doneCoil);
hasReadState = true;
if (lastEnabled && !lastDone)
{
Log.Information(
"转速/扭矩启动状态确认成功M{EnabledCoil}=1M{DoneCoil}=0,通气阀保持手动控制",
SpeedTorqueEnabledCoil,
SpeedTorqueDoneCoil);
"{TestName}启动状态确认成功M{EnabledCoil}=1M{DoneCoil}=0",
testName,
enabledCoil,
doneCoil);
return true;
}
}
@@ -2688,50 +2796,47 @@ public sealed class MainWindowViewModel : ObservableObject
hasLoggedReadFailure = true;
Log.Warning(
ex,
"转速/扭矩启动状态读取失败,继续等待 M{EnabledCoil}/M{DoneCoil}",
SpeedTorqueEnabledCoil,
SpeedTorqueDoneCoil);
"{TestName}启动状态读取失败,继续等待 M{EnabledCoil}/M{DoneCoil}",
testName,
enabledCoil,
doneCoil);
}
}
await Task.Delay(200);
}
StatusText = "M80 启动后 PLC 未确认扭矩使能,已阻止测试运行。";
StatusText = $"{testName}启动后 PLC 未确认运行使能,已阻止测试运行。";
Log.Error(
"转速/扭矩启动状态确认超时:等待 {TimeoutSeconds} 秒,已读取状态 {HasReadState},最后 M{EnabledCoil}={Enabled}M{DoneCoil}={Done}",
SpeedTorqueStartConfirmationTimeout.TotalSeconds,
"{TestName}启动状态确认超时:等待 {TimeoutSeconds} 秒,已读取状态 {HasReadState},最后 M{EnabledCoil}={Enabled}M{DoneCoil}={Done}",
testName,
RunStateConfirmationTimeout.TotalSeconds,
hasReadState,
SpeedTorqueEnabledCoil,
enabledCoil,
lastEnabled ? 1 : 0,
SpeedTorqueDoneCoil,
doneCoil,
lastDone ? 1 : 0);
return false;
}
private async Task AbortSpeedTorqueStartupAsync(string status, string reason)
{
bool stopSent = await PulsePlcAsync(SpeedTorqueStopCoil, $"转速/扭矩{reason}停止");
_isSpeedTorqueRunning = false;
_isSpeedTorqueCompletionArmed = false;
_hasLoggedStaleSpeedTorqueDone = false;
SpeedTorqueTestButtonText = "测试";
StatusText = status;
bool stopped = await SendStopAndConfirmAsync(SpeedTorqueStopCoil, SpeedTorqueEnabledCoil, $"转速/扭矩{reason}停止");
_isSpeedTorqueRunning = !stopped;
_isSpeedTorqueStartupUncertain = !stopped;
_speedTorqueDisabledPollCount = 0;
SpeedTorqueTestButtonText = stopped ? "测试" : "停止未确认";
StatusText = stopped ? status : $"{status} M83 停止未确认,请再次点击停止。";
Log.Error(
"转速/扭矩启动已中止:原因 {Reason}M{StopCoil}停止指令成功 {StopSent},通气阀保持手动控制",
"转速/扭矩启动已中止:原因 {Reason}M{StopCoil}停止状态确认 {Stopped},通气阀保持手动控制",
reason,
SpeedTorqueStopCoil,
stopSent);
stopped);
}
private async Task StopSpeedTorqueAsync()
{
if (!await PulsePlcAsync(SpeedTorqueStopCoil, "转速/扭矩停止"))
{
return;
}
await StopSpeedTorqueTestAsync("状态:已停止");
await StopSpeedTorqueControlAsync("状态:已停止", "转速/扭矩停止");
}
private async Task ResetSpeedTorqueAsync()
@@ -2767,8 +2872,8 @@ public sealed class MainWindowViewModel : ObservableObject
FinalizeSpeedTorqueRun("复位前中止");
PersistCurrentPayloadSnapshot("转速/扭矩复位前");
_isSpeedTorqueRunning = false;
_isSpeedTorqueCompletionArmed = false;
_hasLoggedStaleSpeedTorqueDone = false;
_isSpeedTorqueStartupUncertain = false;
_speedTorqueDisabledPollCount = 0;
SpeedTorqueTestButtonText = "测试";
_realtimeSpeed = 0;
_realtimeTorque = 0;
@@ -2857,31 +2962,52 @@ public sealed class MainWindowViewModel : ObservableObject
private async Task StartDisplacementAsync()
{
if (_isDisplacementRunning)
if (_isDisplacementStarting || _isDisplacementRunning)
{
UpdateDisplacementDisplay();
return;
}
_isDisplacementStarting = true;
DisplacementTestButtonText = "启动中";
StatusText = "轴向测试启动中...";
if (!await PulsePlcAsync(AxialStartCoil, "轴向测试"))
{
_isDisplacementStarting = false;
DisplacementTestButtonText = "测试";
return;
}
DisplacementTestButtonText = "启动确认中";
StatusText = "轴向测试启动指令已发送,正在确认 PLC 轴向使能。";
if (!await WaitForRunStartAsync(AxialEnabledCoil, AxialDoneCoil, "轴向测试"))
{
await AbortDisplacementStartupAsync("PLC 未进入轴向测试运行状态,已停止测试。", "启动状态确认失败");
_isDisplacementStarting = false;
return;
}
if (_isCloseStopRequested)
{
await AbortDisplacementStartupAsync("关闭软件请求已中止轴向启动。", "关闭软件");
_isDisplacementStarting = false;
return;
}
if (!TryGetDialValue(out _) || !TryGetAxialForceValue(out _))
{
await PulsePlcAsync(AxialStopCoil, "实时数据无效,停止轴向测试");
DisplacementTestButtonText = "测试";
await AbortDisplacementStartupAsync("实时数据无效,停止轴向测试。", "启动数据无效");
_isDisplacementStarting = false;
UpdateDisplacementDisplay();
return;
}
_isDisplacementStarting = false;
PrepareSessionForNewRun();
_isDisplacementRunning = true;
_isDisplacementStartupUncertain = false;
_axialDisabledPollCount = 0;
DisplacementTestButtonText = "测试中";
_activeDisplacementRun = CreateTestRun("轴向位移动量测试");
_finalDisplacement = null;
@@ -2894,12 +3020,22 @@ public sealed class MainWindowViewModel : ObservableObject
private async Task StopDisplacementAsync()
{
if (!await PulsePlcAsync(AxialStopCoil, "轴向停止"))
{
return;
}
await StopDisplacementControlAsync("状态:已停止", "轴向停止");
}
StopDisplacementTest("状态:已停止");
private async Task AbortDisplacementStartupAsync(string status, string reason)
{
bool stopped = await SendStopAndConfirmAsync(AxialStopCoil, AxialEnabledCoil, $"轴向{reason}停止");
_isDisplacementRunning = !stopped;
_isDisplacementStartupUncertain = !stopped;
_axialDisabledPollCount = 0;
DisplacementTestButtonText = stopped ? "测试" : "停止未确认";
StatusText = stopped ? status : $"{status} M73 停止未确认,请再次点击停止。";
Log.Error(
"轴向测试启动已中止:原因 {Reason}M{StopCoil}停止状态确认 {Stopped}",
reason,
AxialStopCoil,
stopped);
}
private async Task ResetDisplacementAsync()
@@ -2940,6 +3076,8 @@ public sealed class MainWindowViewModel : ObservableObject
_finalAxialForce = null;
await UpdateAxialForceFromInputAsync();
_isDisplacementRunning = false;
_isDisplacementStartupUncertain = false;
_axialDisabledPollCount = 0;
DisplacementTestButtonText = "测试";
UpdateDisplacementDisplay();
Log.Information("轴向复位完成,千分表零点 {DialZero}", _dialZero);
@@ -3006,6 +3144,53 @@ public sealed class MainWindowViewModel : ObservableObject
}
}
private async Task<bool> SendStopAndConfirmAsync(ushort stopCoil, ushort enabledCoil, string actionName)
{
if (!await PulsePlcAsync(stopCoil, actionName))
{
return false;
}
DateTime deadline = DateTime.Now.Add(RunStateConfirmationTimeout);
bool hasLoggedReadFailure = false;
while (DateTime.Now < deadline)
{
try
{
IReadOnlyDictionary<ushort, bool> values = await _plcCoilService.ReadCoilValuesAsync(
_parameterConfig.ToPlcConnectionConfig(),
[enabledCoil]);
if (!ReadCoilValue(values, enabledCoil))
{
Log.Information(
"PLC停止状态确认成功{ActionName}M{StopCoil}已触发M{EnabledCoil}=0",
actionName,
stopCoil,
enabledCoil);
return true;
}
}
catch (Exception ex)
{
if (!hasLoggedReadFailure)
{
hasLoggedReadFailure = true;
Log.Warning(ex, "PLC停止状态读取失败{ActionName},继续等待 M{EnabledCoil}=0", actionName, enabledCoil);
}
}
await Task.Delay(200);
}
StatusText = $"{actionName}未确认PLC M{enabledCoil}仍未确认断开。";
Log.Error(
"PLC停止状态确认超时{ActionName},等待 {TimeoutSeconds} 秒后 M{EnabledCoil}仍未确认断开",
actionName,
RunStateConfirmationTimeout.TotalSeconds,
enabledCoil);
return false;
}
private async Task<bool> ExecuteZeroingSequenceAsync(
string testName,
params (ushort CoilAddress, string ActionName)[] zeroingActions)
@@ -3249,10 +3434,7 @@ public sealed class MainWindowViewModel : ObservableObject
try
{
Log.Warning("轴向测试触发自动停止:{Status},位移 {Displacement},轴向力 {AxialForce}", status, _relativeDisplacement, GetScaledAxialForce());
if (await PulsePlcAsync(AxialStopCoil, "轴向保护停止"))
{
StopDisplacementTest(status);
}
await StopDisplacementControlAsync(status, "轴向保护停止");
}
finally
{
@@ -3260,21 +3442,59 @@ public sealed class MainWindowViewModel : ObservableObject
}
}
private async Task<bool> StopDisplacementControlAsync(string status, string actionName)
{
if (!_isDisplacementRunning)
{
return true;
}
_isConfirmingDisplacementStop = true;
try
{
StatusText = $"{actionName}指令已发送,正在确认 PLC 轴向使能断开。";
if (!await SendStopAndConfirmAsync(AxialStopCoil, AxialEnabledCoil, actionName))
{
DisplacementTestButtonText = "停止未确认";
StatusText = $"{actionName}未确认,现场运行状态未知;请检查设备并再次点击停止。";
return false;
}
StopDisplacementTest(status);
return true;
}
finally
{
_isConfirmingDisplacementStop = false;
}
}
private void StopDisplacementTest(string status)
{
if (!_isDisplacementRunning && _finalDisplacement.HasValue)
if (!_isDisplacementRunning)
{
UpdateDisplacementDisplay();
return;
}
_isDisplacementRunning = false;
_axialDisabledPollCount = 0;
DisplacementTestButtonText = "测试";
if (_isDisplacementStartupUncertain)
{
_isDisplacementStartupUncertain = false;
StatusText = status;
UpdateDisplacementDisplay();
Log.Warning("轴向启动未确认状态已解除:{Status}", status);
return;
}
_finalDisplacement = Math.Abs(_axialSampleDifference) > 0.000001 ? _axialSampleDifference : _relativeDisplacement;
_finalAxialForce = GetScaledAxialForce();
FinalizeDisplacementRun(status);
PersistCurrentPayloadSnapshot("轴向测试停止");
UpdateDisplacementDisplay();
StatusText = status;
Log.Information("轴向测试停止:{Status},最终位移 {FinalDisplacement},最终轴向力 {FinalAxialForce}", status, _finalDisplacement, _finalAxialForce);
}
@@ -3345,10 +3565,7 @@ public sealed class MainWindowViewModel : ObservableObject
try
{
Log.Warning("转速/扭矩测试触发自动停止:{Status},位移 {Displacement},转速 {Speed},扭矩 {Torque}", status, _speedTorqueDisplacement, _realtimeSpeed, GetScaledTorque());
if (await PulsePlcAsync(SpeedTorqueStopCoil, "转速/扭矩保护停止"))
{
await StopSpeedTorqueTestAsync(status);
}
await StopSpeedTorqueControlAsync(status, "转速/扭矩保护停止");
}
finally
{
@@ -3356,30 +3573,60 @@ public sealed class MainWindowViewModel : ObservableObject
}
}
private async Task<bool> StopSpeedTorqueControlAsync(string status, string actionName)
{
if (!_isSpeedTorqueRunning)
{
return true;
}
_isConfirmingSpeedTorqueStop = true;
try
{
StatusText = $"{actionName}指令已发送,正在确认 PLC 扭矩使能断开。";
if (!await SendStopAndConfirmAsync(SpeedTorqueStopCoil, SpeedTorqueEnabledCoil, actionName))
{
SpeedTorqueTestButtonText = "停止未确认";
StatusText = $"{actionName}未确认,现场运行状态未知;请检查设备并再次点击停止。";
return false;
}
await StopSpeedTorqueTestAsync(status);
return true;
}
finally
{
_isConfirmingSpeedTorqueStop = false;
}
}
private async Task StopSpeedTorqueTestAsync(string status)
{
if (!TryGetRealtimeSpeed(out double speed) || !TryGetRealtimeTorque(out double torque))
if (!_isSpeedTorqueRunning)
{
_isSpeedTorqueRunning = false;
_isSpeedTorqueCompletionArmed = false;
_hasLoggedStaleSpeedTorqueDone = false;
SpeedTorqueTestButtonText = "测试";
UpdateSpeedTorqueDisplay();
return;
}
_isSpeedTorqueRunning = false;
_isSpeedTorqueCompletionArmed = false;
_hasLoggedStaleSpeedTorqueDone = false;
_speedTorqueDisabledPollCount = 0;
SpeedTorqueTestButtonText = "测试";
_realtimeSpeed = speed;
_realtimeTorque = torque;
_finalSpeed = speed;
if (_isSpeedTorqueStartupUncertain)
{
_isSpeedTorqueStartupUncertain = false;
StatusText = status;
UpdateSpeedTorqueDisplay();
Log.Warning("转速/扭矩启动未确认状态已解除:{Status}", status);
return;
}
_finalSpeed = _realtimeSpeed;
_finalTorque = GetScaledTorque();
_finalSpeedTorqueDisplacement = _speedTorqueDisplacement;
FinalizeSpeedTorqueRun(status);
PersistCurrentPayloadSnapshot("转速/扭矩测试停止");
UpdateSpeedTorqueDisplay();
StatusText = status;
Log.Information("转速/扭矩测试停止:{Status},最终位移 {FinalDisplacement},最终转速 {FinalSpeed},最终扭矩 {FinalTorque},通气阀保持手动控制", status, _finalSpeedTorqueDisplacement, _finalSpeed, _finalTorque);
await ShowSpeedTorqueEndWarningsAsync();
@@ -3983,7 +4230,8 @@ public sealed class MainWindowViewModel : ObservableObject
private bool HasActiveRuns()
{
return _activeDisplacementRun is not null
return HasActiveControlRun()
|| _activeDisplacementRun is not null
|| _activeSpeedTorqueRun is not null
|| _activeNoLoadSpeedRun is not null;
}