This commit is contained in:
GukSang.Jin
2026-04-27 17:31:39 +08:00
parent aaeecc6176
commit 8b03b0039f

View File

@@ -116,9 +116,11 @@ public sealed class MainViewModel : ObservableObject, IDisposable
private IReadOnlyList<RawSampleRecord> _lastCompletedSamples = Array.Empty<RawSampleRecord>(); private IReadOnlyList<RawSampleRecord> _lastCompletedSamples = Array.Empty<RawSampleRecord>();
private bool _isShowingHistoricalRun; private bool _isShowingHistoricalRun;
private bool _deviceConnectionFailureLogged; private bool _deviceConnectionFailureLogged;
private bool _isInitializingDeviceConnection;
private bool _isReadingFrame; private bool _isReadingFrame;
private bool _isPlcCommandBusy; private bool _isPlcCommandBusy;
private bool _isSyncingRecipeFromPlc; private bool _isSyncingRecipeFromPlc;
private bool _activeRunStartedByPlc;
private MachineRuntimeState _machineRuntimeState = MachineRuntimeState.Idle; private MachineRuntimeState _machineRuntimeState = MachineRuntimeState.Idle;
private string _plcCommandSummary = "等待 PLC 控制指令。"; private string _plcCommandSummary = "等待 PLC 控制指令。";
@@ -582,6 +584,13 @@ public sealed class MainViewModel : ObservableObject, IDisposable
private async Task InitializeDeviceConnectionAsync() private async Task InitializeDeviceConnectionAsync()
{ {
if (_isInitializingDeviceConnection)
{
return;
}
_isInitializingDeviceConnection = true;
try try
{ {
await _deviceConnectionService.ConnectAsync(); await _deviceConnectionService.ConnectAsync();
@@ -609,6 +618,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
_deviceConnectionService.Disconnect();
DeviceReconnectCount++; DeviceReconnectCount++;
if (!_deviceConnectionFailureLogged) if (!_deviceConnectionFailureLogged)
@@ -629,10 +639,19 @@ public sealed class MainViewModel : ObservableObject, IDisposable
_deviceReconnectTimer.Start(); _deviceReconnectTimer.Start();
} }
finally
{
_isInitializingDeviceConnection = false;
}
} }
private async void OnDeviceReconnectTimerTick(object? sender, EventArgs e) private async void OnDeviceReconnectTimerTick(object? sender, EventArgs e)
{ {
if (_isInitializingDeviceConnection)
{
return;
}
if (_deviceConnectionService.IsConnected) if (_deviceConnectionService.IsConnected)
{ {
_deviceReconnectTimer.Stop(); _deviceReconnectTimer.Stop();
@@ -642,13 +661,21 @@ public sealed class MainViewModel : ObservableObject, IDisposable
await InitializeDeviceConnectionAsync(); await InitializeDeviceConnectionAsync();
} }
private void StartOrResume() private async void StartOrResume()
{ {
if (!TryValidateStartOrResume()) if (!TryValidateStartOrResume())
{ {
return; return;
} }
if (_machineRuntimeState == MachineRuntimeState.Paused && _activeRunStartedByPlc)
{
if (!await ExecutePlcPulseCommandAsync("继续", CoilStart, updateLocalState: false))
{
return;
}
}
StartOrResumeCore(); StartOrResumeCore();
} }
@@ -669,7 +696,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
return false; return false;
} }
private void StartOrResumeCore() private void StartOrResumeCore(bool startedByPlc = false)
{ {
var isRecoveringActiveRun = _machineRuntimeState == MachineRuntimeState.Interlocked && HasRecoverableActiveRunContext(); var isRecoveringActiveRun = _machineRuntimeState == MachineRuntimeState.Interlocked && HasRecoverableActiveRunContext();
@@ -678,6 +705,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
{ {
_activeRunId = Guid.NewGuid(); _activeRunId = Guid.NewGuid();
_activeRecipeSnapshot = TestRecipeSnapshot.FromRecipe(Recipe); _activeRecipeSnapshot = TestRecipeSnapshot.FromRecipe(Recipe);
_activeRunStartedByPlc = startedByPlc;
_displayedRecipeSnapshot = _activeRecipeSnapshot; _displayedRecipeSnapshot = _activeRecipeSnapshot;
_isShowingHistoricalRun = false; _isShowingHistoricalRun = false;
_currentRunSamples.Clear(); _currentRunSamples.Clear();
@@ -711,12 +739,14 @@ public sealed class MainViewModel : ObservableObject, IDisposable
} }
else if (isRecoveringActiveRun) else if (isRecoveringActiveRun)
{ {
_activeRunStartedByPlc |= startedByPlc;
_isShowingHistoricalRun = false; _isShowingHistoricalRun = false;
_displayedRecipeSnapshot = _activeRecipeSnapshot; _displayedRecipeSnapshot = _activeRecipeSnapshot;
AddInfoEvent($"试验通信恢复后继续运行,已保留 {_currentRunSamples.Count} 个采样点。"); AddInfoEvent($"试验通信恢复后继续运行,已保留 {_currentRunSamples.Count} 个采样点。");
} }
else else
{ {
_activeRunStartedByPlc |= startedByPlc;
AddInfoEvent("试验继续运行。"); AddInfoEvent("试验继续运行。");
} }
@@ -728,8 +758,16 @@ public sealed class MainViewModel : ObservableObject, IDisposable
RaiseStatusProperties(); RaiseStatusProperties();
} }
private void Pause() private async void Pause()
{ {
if (_activeRunStartedByPlc)
{
if (!await ExecutePlcPulseCommandAsync("暂停", CoilStop, updateLocalState: false))
{
return;
}
}
_timer.Stop(); _timer.Stop();
_machineRuntimeState = MachineRuntimeState.Paused; _machineRuntimeState = MachineRuntimeState.Paused;
MachineState = "暂停"; MachineState = "暂停";
@@ -783,7 +821,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
if (await ExecutePlcPulseCommandAsync("启动", CoilStart, updateLocalState: false)) if (await ExecutePlcPulseCommandAsync("启动", CoilStart, updateLocalState: false))
{ {
StartOrResumeCore(); StartOrResumeCore(startedByPlc: true);
} }
} }
@@ -932,6 +970,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
_lastCompletedRecipeSnapshot = _activeRecipeSnapshot ?? TestRecipeSnapshot.FromRecipe(Recipe); _lastCompletedRecipeSnapshot = _activeRecipeSnapshot ?? TestRecipeSnapshot.FromRecipe(Recipe);
_lastCompletedSamples = _currentRunSamples.ToArray(); _lastCompletedSamples = _currentRunSamples.ToArray();
PersistLastCompletedRun(); PersistLastCompletedRun();
ResetActiveRunContext();
SelectedRunRecord = record; SelectedRunRecord = record;
NextRunIndex++; NextRunIndex++;
UpdateDerivedValues(); UpdateDerivedValues();
@@ -1280,6 +1319,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
{ {
_activeRunId = Guid.Empty; _activeRunId = Guid.Empty;
_activeRecipeSnapshot = null; _activeRecipeSnapshot = null;
_activeRunStartedByPlc = false;
_currentRunSamples.Clear(); _currentRunSamples.Clear();
} }
@@ -1357,12 +1397,19 @@ public sealed class MainViewModel : ObservableObject, IDisposable
try try
{ {
var master = _deviceConnectionService.Master; var master = _deviceConnectionService.Master;
Recipe.SledMassGrams = await ReadFloatHoldingRegisterAsync(master, RegisterSledMassGrams); var sledMassGrams = await ReadFloatHoldingRegisterAsync(master, RegisterSledMassGrams);
Recipe.LiftSpeedMmPerMin = await ReadFloatHoldingRegisterAsync(master, RegisterLiftSpeed); var liftSpeedMmPerMin = await ReadFloatHoldingRegisterAsync(master, RegisterLiftSpeed);
Recipe.LiftDisplacementMm = await ReadFloatHoldingRegisterAsync(master, RegisterLiftDisplacement); var liftDisplacementMm = await ReadFloatHoldingRegisterAsync(master, RegisterLiftDisplacement);
Recipe.SpeedMmPerMin = await ReadFloatHoldingRegisterAsync(master, RegisterHorizontalSpeed); var speedMmPerMin = await ReadFloatHoldingRegisterAsync(master, RegisterHorizontalSpeed);
Recipe.TravelMm = await ReadFloatHoldingRegisterAsync(master, RegisterHorizontalDisplacement); var travelMm = await ReadFloatHoldingRegisterAsync(master, RegisterHorizontalDisplacement);
Recipe.ReplicateCount = Math.Clamp((int)Math.Round(await ReadFloatHoldingRegisterAsync(master, RegisterReplicateCount)), 0, 1); var replicateCount = Math.Clamp((int)Math.Round(await ReadFloatHoldingRegisterAsync(master, RegisterReplicateCount)), 0, 1);
Recipe.SledMassGrams = sledMassGrams;
Recipe.LiftSpeedMmPerMin = liftSpeedMmPerMin;
Recipe.LiftDisplacementMm = liftDisplacementMm;
Recipe.SpeedMmPerMin = speedMmPerMin;
Recipe.TravelMm = travelMm;
Recipe.ReplicateCount = replicateCount;
} }
finally finally
{ {