This commit is contained in:
GukSang.Jin
2026-06-09 19:40:43 +08:00
parent 86bb4c83bc
commit b74d20f8f3
2 changed files with 137 additions and 39 deletions

View File

@@ -872,7 +872,6 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Content="前进"
Tag="SpeedTorqueForward"
@@ -896,20 +895,16 @@
LostTouchCapture="ManualMotionButton_LostTouchCapture"
Margin="8,0,8,0" />
<Button Grid.Column="2"
Content="{Binding VentValveButtonText}"
Command="{Binding VentValveCommand}"
Margin="8,0,8,0" />
<Button Grid.Column="3"
Content="{Binding SpeedTorqueTestButtonText}"
Command="{Binding StartSpeedTorqueCommand}"
Style="{StaticResource StartButtonStyle}"
Margin="8,0,8,0" />
<Button Grid.Column="4"
<Button Grid.Column="3"
Content="停止"
Command="{Binding StopSpeedTorqueCommand}"
Style="{StaticResource StopButtonStyle}"
Margin="8,0,8,0" />
<Button Grid.Column="5"
<Button Grid.Column="4"
Content="{Binding SpeedTorqueResetButtonText}"
Command="{Binding ResetSpeedTorqueCommand}"
Margin="8,0,0,0" />

View File

@@ -45,6 +45,7 @@ public sealed class MainWindowViewModel : ObservableObject
private const ushort AxialDoneCoil = 72;
private const ushort AxialStopCoil = 73;
private const ushort SpeedTorqueStartCoil = 80;
private const ushort SpeedTorqueEnabledCoil = 81;
private const ushort SpeedTorqueDoneCoil = 82;
private const ushort SpeedTorqueStopCoil = 83;
private const ushort SpeedTorqueResetCoil = 90;
@@ -56,6 +57,11 @@ public sealed class MainWindowViewModel : ObservableObject
private const ushort AxialResetCoil = 95;
private const ushort AxialResetEnabledCoil = 96;
private const ushort AxialResetDoneCoil = 97;
private const ushort AxialForceZeroCoil = 1100;
private const ushort TorqueZeroCoil = 1101;
private const ushort SpeedZeroCoil = 1300;
private const ushort PressureZeroCoil = 1301;
private const ushort SecondaryTorqueZeroCoil = 1302;
private const ushort SpeedTorquePositionRegister = 14;
private const ushort AxialPositionRegister = 16;
private const ushort AxialSampleStartRegister = 72;
@@ -208,7 +214,6 @@ public sealed class MainWindowViewModel : ObservableObject
private bool _isApplyingParameterConfigToInputs;
private bool _isDisplacementResetting;
private bool _isSpeedTorqueResetting;
private bool _isVentValveOpen;
private bool _hasShownSpeedTorqueEndWarnings;
private DateTime _lastParameterReadFailureLogAt = DateTime.MinValue;
private string _dialIndicatorText = "0.000 mm";
@@ -247,7 +252,6 @@ public sealed class MainWindowViewModel : ObservableObject
private string _noLoadSpeedTestButtonText = "测试";
private string _displacementResetButtonText = "复位";
private string _speedTorqueResetButtonText = "复位";
private string _ventValveButtonText = "开启通气阀";
public MainWindowViewModel(IPlcCoilService plcCoilService, IPlcRegisterService plcRegisterService, IFileDialogService fileDialogService)
{
@@ -271,7 +275,6 @@ public sealed class MainWindowViewModel : ObservableObject
SelectAxialJumpForceSetpointModeCommand = new AsyncRelayCommand(SelectAxialJumpForceSetpointModeAsync);
ForwardSpeedTorqueCommand = new AsyncRelayCommand(ForwardSpeedTorqueAsync);
BackwardSpeedTorqueCommand = new AsyncRelayCommand(BackwardSpeedTorqueAsync);
VentValveCommand = new AsyncRelayCommand(ToggleVentValveAsync);
StartSpeedTorqueCommand = new AsyncRelayCommand(StartSpeedTorqueAsync);
StopSpeedTorqueCommand = new AsyncRelayCommand(StopSpeedTorqueAsync);
ResetSpeedTorqueCommand = new AsyncRelayCommand(ResetSpeedTorqueAsync);
@@ -328,8 +331,6 @@ public sealed class MainWindowViewModel : ObservableObject
public IAsyncRelayCommand BackwardSpeedTorqueCommand { get; }
public IAsyncRelayCommand VentValveCommand { get; }
public IAsyncRelayCommand StartSpeedTorqueCommand { get; }
public IAsyncRelayCommand StopSpeedTorqueCommand { get; }
@@ -690,12 +691,6 @@ public sealed class MainWindowViewModel : ObservableObject
private set => SetProperty(ref _speedTorqueResetButtonText, value);
}
public string VentValveButtonText
{
get => _ventValveButtonText;
private set => SetProperty(ref _ventValveButtonText, value);
}
private async void RealtimeTimer_Tick(object? sender, EventArgs e)
{
if (_isReadingRealtime)
@@ -732,7 +727,13 @@ public sealed class MainWindowViewModel : ObservableObject
_realtimeSpeed = realtimeSpeed;
AppendTorqueSample(GetScaledTorque(), DateTime.Now);
ApplyResetCoilValues(coilValues);
UpdateVentValveState(ReadCoilValue(coilValues, VentValveCoil));
bool isVentValveOpen = ReadCoilValue(coilValues, VentValveCoil);
if (_isSpeedTorqueRunning && !isVentValveOpen)
{
Log.Error("转速/扭矩测试运行中检测到通气阀关闭M{VentValveCoil}=0立即停止测试", VentValveCoil);
await AutoStopSpeedTorqueAsync("状态:通气阀关闭保护,已停止");
}
CaptureRealtimeSample(dialIndicator, coilValues);
FinalizeNoLoadSpeedRunIfDue();
QueueSnapshotIfDue();
@@ -2097,28 +2098,49 @@ public sealed class MainWindowViewModel : ObservableObject
await MoveSpeedTorqueDisplacementAsync();
}
private async Task ToggleVentValveAsync()
private async Task<bool> WriteAndVerifyVentValveStateAsync(bool targetState, string reason)
{
try
{
PlcConnectionConfig config = _parameterConfig.ToPlcConnectionConfig();
await _plcCoilService.WriteCoilAsync(config, VentValveCoil, targetState);
await Task.Delay(100);
IReadOnlyDictionary<ushort, bool> values = await _plcCoilService.ReadCoilValuesAsync(
_parameterConfig.ToPlcConnectionConfig(),
[VentValveCoil]);
bool targetState = !ReadCoilValue(values, VentValveCoil);
config,
[VentValveCoil, SpeedTorqueEnabledCoil]);
bool actualState = ReadCoilValue(values, VentValveCoil);
bool speedTorqueEnabled = ReadCoilValue(values, SpeedTorqueEnabledCoil);
if (actualState != targetState)
{
StatusText = targetState
? "通气阀未能保持开启,已阻止测试启动。"
: "通气阀未能关闭,请检查 PLC 状态。";
Log.Error(
"PLC通气阀状态校验失败原因 {Reason},要求 M{VentValveCoil}={Expected},实际 {Actual},扭矩使能 M{EnabledCoil}={Enabled}",
reason,
VentValveCoil,
targetState ? 1 : 0,
actualState ? 1 : 0,
SpeedTorqueEnabledCoil,
speedTorqueEnabled ? 1 : 0);
return false;
}
await _plcCoilService.WriteCoilAsync(
_parameterConfig.ToPlcConnectionConfig(),
Log.Information(
"PLC通气阀状态校验成功原因 {Reason}M{VentValveCoil}={Value},扭矩使能 M{EnabledCoil}={Enabled}",
reason,
VentValveCoil,
targetState);
UpdateVentValveState(targetState);
StatusText = targetState ? "通气阀已开启。" : "通气阀已关闭。";
Log.Information("PLC常开触点开关成功通气阀M{CoilAddress}={Value}", VentValveCoil, targetState ? 1 : 0);
actualState ? 1 : 0,
SpeedTorqueEnabledCoil,
speedTorqueEnabled ? 1 : 0);
return true;
}
catch (Exception ex)
{
StatusText = $"PLC 通气阀开关失败:{OperatorMessageFormatter.FromException(ex)}";
Log.Error(ex, "PLC常开触点开关失败通气阀M{CoilAddress}", VentValveCoil);
StatusText = $"PLC 通气阀状态确认失败:{OperatorMessageFormatter.FromException(ex)}";
Log.Error(ex, "PLC通气阀状态确认失败:原因 {Reason}M{VentValveCoil}", reason, VentValveCoil);
return false;
}
}
@@ -2338,13 +2360,46 @@ public sealed class MainWindowViewModel : ObservableObject
return;
}
SpeedTorqueTestButtonText = "归零中";
StatusText = "转速/扭矩测试启动前归零中。";
if (!await ExecuteZeroingSequenceAsync(
"转速/扭矩测试",
(TorqueZeroCoil, "扭矩归零 M1101"),
(SpeedZeroCoil, "转速归零 M1300"),
(PressureZeroCoil, "压力归零 M1301"),
(SecondaryTorqueZeroCoil, "扭矩归零 M1302")))
{
SpeedTorqueTestButtonText = "测试";
return;
}
if (!await WriteAndVerifyVentValveStateAsync(true, "转速/扭矩测试启动前"))
{
SpeedTorqueTestButtonText = "测试";
return;
}
if (!await PulsePlcAsync(SpeedTorqueStartCoil, "转速/扭矩测试"))
{
await WriteAndVerifyVentValveStateAsync(false, "转速/扭矩测试启动失败");
SpeedTorqueTestButtonText = "测试";
return;
}
if (!await WriteAndVerifyVentValveStateAsync(true, "转速/扭矩测试启动后"))
{
await PulsePlcAsync(SpeedTorqueStopCoil, "通气阀未保持开启,停止转速/扭矩测试");
await WriteAndVerifyVentValveStateAsync(false, "转速/扭矩测试启动后校验失败");
StatusText = "测试启动后通气阀未保持开启,已发送停止指令。";
SpeedTorqueTestButtonText = "测试";
return;
}
if (!TryGetRealtimeSpeed(out _) || !TryGetRealtimeTorque(out _))
{
await PulsePlcAsync(SpeedTorqueStopCoil, "实时数据无效,停止转速/扭矩测试");
await WriteAndVerifyVentValveStateAsync(false, "转速/扭矩测试启动数据无效");
SpeedTorqueTestButtonText = "测试";
UpdateSpeedTorqueDisplay();
return;
}
@@ -2362,7 +2417,13 @@ public sealed class MainWindowViewModel : ObservableObject
AppendTorqueSample(GetScaledTorque(), _speedTorqueStartedAt.Value);
_maxSpeedTorqueDisplacement = Math.Max(_maxSpeedTorqueDisplacement, Math.Abs(_speedTorqueDisplacement));
UpdateSpeedTorqueDisplay();
Log.Information("转速/扭矩测试已启动,起始转速 {Speed},起始扭矩 {Torque},起始位移 {Displacement}", _realtimeSpeed, GetScaledTorque(), _speedTorqueDisplacement);
Log.Information(
"转速/扭矩测试已启动,通气阀 M{VentValveCoil}=1启动 M{StartCoil}已触发,起始转速 {Speed},起始扭矩 {Torque},起始位移 {Displacement}",
VentValveCoil,
SpeedTorqueStartCoil,
_realtimeSpeed,
GetScaledTorque(),
_speedTorqueDisplacement);
await AutoStopIfSpeedTorqueProtectionReachedAsync();
}
@@ -2415,6 +2476,7 @@ public sealed class MainWindowViewModel : ObservableObject
_hasShownSpeedTorqueEndWarnings = false;
ClearTorqueSamples();
UpdateSpeedTorqueDisplay();
await WriteAndVerifyVentValveStateAsync(false, "转速/扭矩复位");
Log.Information("转速/扭矩复位完成,零点 {ZeroPosition}", _speedTorqueZero);
}
finally
@@ -2452,13 +2514,26 @@ public sealed class MainWindowViewModel : ObservableObject
return;
}
DisplacementTestButtonText = "归零中";
StatusText = "轴向测试启动前归零中。";
if (!await ExecuteZeroingSequenceAsync(
"轴向测试",
(AxialForceZeroCoil, "轴向力归零 M1100")))
{
DisplacementTestButtonText = "测试";
return;
}
if (!await PulsePlcAsync(AxialStartCoil, "轴向测试"))
{
DisplacementTestButtonText = "测试";
return;
}
if (!TryGetDialValue(out _) || !TryGetAxialForceValue(out _))
{
await PulsePlcAsync(AxialStopCoil, "实时数据无效,停止轴向测试");
DisplacementTestButtonText = "测试";
UpdateDisplacementDisplay();
return;
}
@@ -2589,6 +2664,32 @@ public sealed class MainWindowViewModel : ObservableObject
}
}
private async Task<bool> ExecuteZeroingSequenceAsync(
string testName,
params (ushort CoilAddress, string ActionName)[] zeroingActions)
{
foreach ((ushort coilAddress, string actionName) in zeroingActions)
{
if (!await PulsePlcAsync(coilAddress, actionName))
{
StatusText = $"{testName}启动已阻止:{actionName}失败。";
Log.Error(
"{TestName}启动前归零失败,停止启动序列,失败点位 M{CoilAddress},动作 {ActionName}",
testName,
coilAddress,
actionName);
return false;
}
}
StatusText = $"{testName}归零完成,正在启动测试。";
Log.Information(
"{TestName}启动前归零序列完成,点位 {ZeroingCoils}",
testName,
string.Join(", ", zeroingActions.Select(static action => $"M{action.CoilAddress}")));
return true;
}
private async Task<bool> BeginAxialManualMotionAsync(ushort coilAddress, string actionName)
{
if (!await WriteManualMotionCoilAsync(coilAddress, true, $"{actionName}按下"))
@@ -2822,6 +2923,9 @@ public sealed class MainWindowViewModel : ObservableObject
{
if (!TryGetRealtimeSpeed(out double speed) || !TryGetRealtimeTorque(out double torque))
{
_isSpeedTorqueRunning = false;
SpeedTorqueTestButtonText = "测试";
await WriteAndVerifyVentValveStateAsync(false, "转速/扭矩测试结束数据无效");
UpdateSpeedTorqueDisplay();
return;
}
@@ -2837,6 +2941,11 @@ public sealed class MainWindowViewModel : ObservableObject
PersistCurrentPayloadSnapshot("转速/扭矩测试停止");
UpdateSpeedTorqueDisplay();
Log.Information("转速/扭矩测试停止:{Status},最终位移 {FinalDisplacement},最终转速 {FinalSpeed},最终扭矩 {FinalTorque}", status, _finalSpeedTorqueDisplacement, _finalSpeed, _finalTorque);
if (!await WriteAndVerifyVentValveStateAsync(false, "转速/扭矩测试结束"))
{
StatusText = "测试已停止,但通气阀关闭状态未确认,请检查设备。";
}
await ShowSpeedTorqueEndWarningsAsync();
}
@@ -2926,12 +3035,6 @@ public sealed class MainWindowViewModel : ObservableObject
SpeedTorqueResetButtonText = _isSpeedTorqueResetting || enabled ? "复位中" : "复位";
}
private void UpdateVentValveState(bool isOpen)
{
_isVentValveOpen = isOpen;
VentValveButtonText = _isVentValveOpen ? "关闭通气阀" : "开启通气阀";
}
private static bool ReadCoilValue(IReadOnlyDictionary<ushort, bool> coilValues, ushort address)
{
return coilValues.TryGetValue(address, out bool value) && value;