更新
This commit is contained in:
@@ -21,8 +21,8 @@ public sealed class ModbusProcessDataReader
|
|||||||
private static readonly PlcTelemetryRegisterMap DefaultTelemetryRegisterMap = new(
|
private static readonly PlcTelemetryRegisterMap DefaultTelemetryRegisterMap = new(
|
||||||
SlaveAddress: 1,
|
SlaveAddress: 1,
|
||||||
ForceAddress: 1314,
|
ForceAddress: 1314,
|
||||||
HorizontalSpeedAddress: 350,
|
HorizontalSpeedAddress: 370,
|
||||||
HorizontalDisplacementAddress: 360,
|
HorizontalDisplacementAddress: 380,
|
||||||
LiftSpeedAddress: 310,
|
LiftSpeedAddress: 310,
|
||||||
LiftDisplacementAddress: 320,
|
LiftDisplacementAddress: 320,
|
||||||
LiftPositionAddress: 12,
|
LiftPositionAddress: 12,
|
||||||
@@ -111,10 +111,10 @@ public sealed class ModbusProcessDataReader
|
|||||||
var liftDisplacement = ReadFloatAt(motionRegisters, motionStartAddress, _telemetryRegisterMap.LiftDisplacementAddress);
|
var liftDisplacement = ReadFloatAt(motionRegisters, motionStartAddress, _telemetryRegisterMap.LiftDisplacementAddress);
|
||||||
var liftPosition = ReadFloatAt(positionRegisters, positionStartAddress, _telemetryRegisterMap.LiftPositionAddress);
|
var liftPosition = ReadFloatAt(positionRegisters, positionStartAddress, _telemetryRegisterMap.LiftPositionAddress);
|
||||||
var horizontalPosition = ReadFloatAt(positionRegisters, positionStartAddress, _telemetryRegisterMap.HorizontalPositionAddress);
|
var horizontalPosition = ReadFloatAt(positionRegisters, positionStartAddress, _telemetryRegisterMap.HorizontalPositionAddress);
|
||||||
var completed = horizontalDisplacement >= _recipe.TravelMm;
|
var completed = false;
|
||||||
|
|
||||||
return new ProcessFrame(
|
return new ProcessFrame(
|
||||||
horizontalDisplacement,
|
horizontalPosition,
|
||||||
Math.Max(force, 0.001),
|
Math.Max(force, 0.001),
|
||||||
horizontalSpeed,
|
horizontalSpeed,
|
||||||
completed,
|
completed,
|
||||||
|
|||||||
@@ -47,12 +47,13 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
private const ushort CoilReset = 90;
|
private const ushort CoilReset = 90;
|
||||||
private const ushort RegisterSledMassGrams = 400;
|
private const ushort RegisterSledMassGrams = 400;
|
||||||
private const ushort RegisterReplicateCount = 406;
|
private const ushort RegisterReplicateCount = 406;
|
||||||
private const ushort RegisterHorizontalSpeed = 350;
|
private const ushort RegisterHorizontalSpeedSetpoint = 370;
|
||||||
private const ushort RegisterHorizontalDisplacement = 360;
|
private const ushort RegisterHorizontalTravelSetpoint = 380;
|
||||||
private const ushort RegisterLiftSpeed = 310;
|
private const ushort RegisterLiftSpeed = 310;
|
||||||
private const ushort RegisterLiftDisplacement = 320;
|
private const ushort RegisterLiftDisplacement = 320;
|
||||||
private static readonly TimeSpan RealtimeChartRefreshInterval = TimeSpan.FromMilliseconds(250);
|
private static readonly TimeSpan RealtimeChartRefreshInterval = TimeSpan.FromMilliseconds(250);
|
||||||
private const double RealtimeChartDisplacementStepMm = 0.5;
|
private const double RealtimeChartDisplacementStepMm = 0.5;
|
||||||
|
private const double RealtimeChartXAxisPaddingMm = 5;
|
||||||
|
|
||||||
private readonly DispatcherTimer _timer;
|
private readonly DispatcherTimer _timer;
|
||||||
private readonly DispatcherTimer _deviceReconnectTimer;
|
private readonly DispatcherTimer _deviceReconnectTimer;
|
||||||
@@ -132,6 +133,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
private int _kineticSampleCount;
|
private int _kineticSampleCount;
|
||||||
private double _runStartHorizontalPositionMm = double.NaN;
|
private double _runStartHorizontalPositionMm = double.NaN;
|
||||||
private double _lastRealtimeChartDisplacementMm = double.NaN;
|
private double _lastRealtimeChartDisplacementMm = double.NaN;
|
||||||
|
private double _realtimeChartMaxDisplacementMm;
|
||||||
private DateTime _lastRealtimeChartRefreshAt = DateTime.MinValue;
|
private DateTime _lastRealtimeChartRefreshAt = DateTime.MinValue;
|
||||||
private double _lastForceXAxisMaxLimit;
|
private double _lastForceXAxisMaxLimit;
|
||||||
private double _lastForceYAxisMaxLimit;
|
private double _lastForceYAxisMaxLimit;
|
||||||
@@ -943,7 +945,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
private void UpdateLiveProcessSnapshot(ProcessFrame frame)
|
private void UpdateLiveProcessSnapshot(ProcessFrame frame)
|
||||||
{
|
{
|
||||||
CurrentForceN = Math.Max(frame.ForceN, 0.001);
|
CurrentForceN = Math.Max(frame.ForceN, 0.001);
|
||||||
CurrentDisplacementMm = frame.HorizontalPosition;
|
CurrentDisplacementMm = frame.DisplacementMm;
|
||||||
CurrentSpeedMmPerMin = frame.SpeedMmPerMin;
|
CurrentSpeedMmPerMin = frame.SpeedMmPerMin;
|
||||||
CurrentLiftSpeed = frame.LiftSpeed;
|
CurrentLiftSpeed = frame.LiftSpeed;
|
||||||
CurrentLiftDisplacement = frame.LiftDisplacement;
|
CurrentLiftDisplacement = frame.LiftDisplacement;
|
||||||
@@ -995,6 +997,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
private void AddRealtimeChartSample(double displacementMm, double forceN)
|
private void AddRealtimeChartSample(double displacementMm, double forceN)
|
||||||
{
|
{
|
||||||
|
TrackRealtimeChartBounds(displacementMm);
|
||||||
|
|
||||||
var sample = new ObservablePoint(displacementMm, forceN);
|
var sample = new ObservablePoint(displacementMm, forceN);
|
||||||
if (_forceSamples.Count == 0 || !IsFinite(_lastRealtimeChartDisplacementMm))
|
if (_forceSamples.Count == 0 || !IsFinite(_lastRealtimeChartDisplacementMm))
|
||||||
{
|
{
|
||||||
@@ -1013,37 +1017,46 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
_forceSamples[^1] = sample;
|
_forceSamples[^1] = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TrackRealtimeChartBounds(double displacementMm)
|
||||||
|
{
|
||||||
|
if (IsFinite(displacementMm))
|
||||||
|
{
|
||||||
|
_realtimeChartMaxDisplacementMm = Math.Max(_realtimeChartMaxDisplacementMm, displacementMm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool AppendRunningSample(ProcessFrame frame, out bool isCompleted)
|
private bool AppendRunningSample(ProcessFrame frame, out bool isCompleted)
|
||||||
{
|
{
|
||||||
isCompleted = false;
|
isCompleted = false;
|
||||||
if (!TryResolveRunningPosition(frame, out var horizontalPositionMm, out var travelDeltaMm))
|
if (!TryResolveRunningDisplacement(frame, out var displacementMm))
|
||||||
{
|
{
|
||||||
AddWarningEvent("实时采样包含无效水平位置,已跳过本次曲线点。");
|
AddWarningEvent("实时采样包含无效水平位移,已跳过本次曲线点。");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var forceN = CurrentForceN;
|
var forceN = Math.Max(frame.ForceN, 0.001);
|
||||||
if (!IsFinite(forceN))
|
if (!IsFinite(forceN))
|
||||||
{
|
{
|
||||||
AddWarningEvent("实时采样包含无效力值,已跳过本次曲线点。");
|
AddWarningEvent("实时采样包含无效力值,已跳过本次曲线点。");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentDisplacementMm = horizontalPositionMm;
|
CurrentDisplacementMm = displacementMm;
|
||||||
|
CurrentForceN = forceN;
|
||||||
var isFirstChartSample = _forceSamples.Count == 0;
|
var isFirstChartSample = _forceSamples.Count == 0;
|
||||||
_currentRunSamples.Add(new RawSampleRecord
|
_currentRunSamples.Add(new RawSampleRecord
|
||||||
{
|
{
|
||||||
RunId = _activeRunId,
|
RunId = _activeRunId,
|
||||||
SampleIndex = _currentRunSamples.Count + 1,
|
SampleIndex = _currentRunSamples.Count + 1,
|
||||||
CapturedAt = DateTime.Now,
|
CapturedAt = DateTime.Now,
|
||||||
DisplacementMm = horizontalPositionMm,
|
DisplacementMm = displacementMm,
|
||||||
ForceN = forceN,
|
ForceN = forceN,
|
||||||
SpeedMmPerMin = frame.SpeedMmPerMin
|
SpeedMmPerMin = frame.SpeedMmPerMin
|
||||||
});
|
});
|
||||||
|
|
||||||
AddRealtimeChartSample(horizontalPositionMm, forceN);
|
AddRealtimeChartSample(displacementMm, forceN);
|
||||||
UpdateCurrentPoint(horizontalPositionMm, forceN);
|
UpdateCurrentPoint(displacementMm, forceN);
|
||||||
UpdatePreviewResult(travelDeltaMm, forceN);
|
UpdatePreviewResult(displacementMm, forceN);
|
||||||
StaticCoefficient1 = frame.StaticCoefficient1;
|
StaticCoefficient1 = frame.StaticCoefficient1;
|
||||||
KineticCoefficient1 = frame.KineticCoefficient1;
|
KineticCoefficient1 = frame.KineticCoefficient1;
|
||||||
StandardDeviation1 = frame.StandardDeviation1;
|
StandardDeviation1 = frame.StandardDeviation1;
|
||||||
@@ -1053,8 +1066,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
var activeReplicateCount = GetActiveReplicateCount();
|
var activeReplicateCount = GetActiveReplicateCount();
|
||||||
CurrentStaticCoefficient = ResolveRepresentativeValue(activeReplicateCount, StaticCoefficient1, StaticCoefficient2);
|
CurrentStaticCoefficient = ResolveRepresentativeValue(activeReplicateCount, StaticCoefficient1, StaticCoefficient2);
|
||||||
CurrentKineticCoefficient = ResolveRepresentativeValue(activeReplicateCount, KineticCoefficient1, KineticCoefficient2);
|
CurrentKineticCoefficient = ResolveRepresentativeValue(activeReplicateCount, KineticCoefficient1, KineticCoefficient2);
|
||||||
TrialProgressPercent = Math.Min(100, travelDeltaMm / Math.Max(Recipe.TravelMm, 1) * 100);
|
TrialProgressPercent = Math.Min(100, displacementMm / Math.Max(GetActiveTravelMm(), 1) * 100);
|
||||||
isCompleted = travelDeltaMm >= GetActiveTravelMm();
|
isCompleted = displacementMm >= GetActiveTravelMm();
|
||||||
RefreshRealtimeChartPresentation(force: isFirstChartSample || isCompleted);
|
RefreshRealtimeChartPresentation(force: isFirstChartSample || isCompleted);
|
||||||
if (isFirstChartSample)
|
if (isFirstChartSample)
|
||||||
{
|
{
|
||||||
@@ -1064,23 +1077,21 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryResolveRunningPosition(ProcessFrame frame, out double horizontalPositionMm, out double travelDeltaMm)
|
private bool TryResolveRunningDisplacement(ProcessFrame frame, out double displacementMm)
|
||||||
{
|
{
|
||||||
horizontalPositionMm = 0;
|
displacementMm = 0;
|
||||||
travelDeltaMm = 0;
|
|
||||||
if (!IsFinite(frame.HorizontalPosition))
|
if (!IsFinite(frame.HorizontalPosition))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
horizontalPositionMm = frame.HorizontalPosition;
|
|
||||||
if (!IsFinite(_runStartHorizontalPositionMm))
|
if (!IsFinite(_runStartHorizontalPositionMm))
|
||||||
{
|
{
|
||||||
_runStartHorizontalPositionMm = horizontalPositionMm;
|
_runStartHorizontalPositionMm = frame.HorizontalPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
travelDeltaMm = Math.Abs(horizontalPositionMm - _runStartHorizontalPositionMm);
|
displacementMm = Math.Abs(frame.HorizontalPosition - _runStartHorizontalPositionMm);
|
||||||
return IsFinite(horizontalPositionMm) && IsFinite(travelDeltaMm);
|
return IsFinite(displacementMm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatePreviewResult(double displacementMm, double forceN)
|
private void UpdatePreviewResult(double displacementMm, double forceN)
|
||||||
@@ -1115,8 +1126,15 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
private void UpdateCurrentPoint(double x, double y)
|
private void UpdateCurrentPoint(double x, double y)
|
||||||
{
|
{
|
||||||
|
var sample = new ObservablePoint(x, y);
|
||||||
|
if (_currentPointSample.Count == 1)
|
||||||
|
{
|
||||||
|
_currentPointSample[0] = sample;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_currentPointSample.Clear();
|
_currentPointSample.Clear();
|
||||||
_currentPointSample.Add(new ObservablePoint(x, y));
|
_currentPointSample.Add(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateReferenceLines()
|
private void UpdateReferenceLines()
|
||||||
@@ -1148,12 +1166,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
private double CalculateRealtimeXAxisMaxLimit()
|
private double CalculateRealtimeXAxisMaxLimit()
|
||||||
{
|
{
|
||||||
var sampleMax = _forceSamples.Count == 0
|
var visibleMax = Math.Max(_realtimeChartMaxDisplacementMm, CurrentDisplacementMm);
|
||||||
? 0
|
return Math.Max(visibleMax + RealtimeChartXAxisPaddingMm, 1);
|
||||||
: _forceSamples.Max(sample => sample.X ?? 0);
|
|
||||||
var currentMax = Math.Max(CurrentDisplacementMm, CurrentHorizontalPosition);
|
|
||||||
var visibleMax = Math.Max(sampleMax, currentMax);
|
|
||||||
return Math.Max(visibleMax + 5, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAxisLimits(double xMax, double yMax)
|
private void UpdateAxisLimits(double xMax, double yMax)
|
||||||
@@ -1203,21 +1217,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
private bool UpdateKineticBand(double yMax)
|
private bool UpdateKineticBand(double yMax)
|
||||||
{
|
{
|
||||||
var travelMm = GetActiveTravelMm();
|
var travelMm = GetActiveTravelMm();
|
||||||
double xi;
|
var xi = travelMm * 0.35;
|
||||||
double xj;
|
var xj = travelMm;
|
||||||
if (IsFinite(_runStartHorizontalPositionMm))
|
|
||||||
{
|
|
||||||
var direction = CurrentHorizontalPosition >= _runStartHorizontalPositionMm ? 1 : -1;
|
|
||||||
var kineticStart = _runStartHorizontalPositionMm + direction * travelMm * 0.35;
|
|
||||||
var kineticEnd = _runStartHorizontalPositionMm + direction * travelMm;
|
|
||||||
xi = Math.Min(kineticStart, kineticEnd);
|
|
||||||
xj = Math.Max(kineticStart, kineticEnd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xi = travelMm * 0.35;
|
|
||||||
xj = travelMm;
|
|
||||||
}
|
|
||||||
|
|
||||||
var changed = HasMeaningfulChange(_kineticBand.Xi ?? double.NaN, xi)
|
var changed = HasMeaningfulChange(_kineticBand.Xi ?? double.NaN, xi)
|
||||||
|| HasMeaningfulChange(_kineticBand.Xj ?? double.NaN, xj)
|
|| HasMeaningfulChange(_kineticBand.Xj ?? double.NaN, xj)
|
||||||
@@ -1315,6 +1316,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
_kineticSampleCount = 0;
|
_kineticSampleCount = 0;
|
||||||
_runStartHorizontalPositionMm = double.NaN;
|
_runStartHorizontalPositionMm = double.NaN;
|
||||||
_lastRealtimeChartDisplacementMm = double.NaN;
|
_lastRealtimeChartDisplacementMm = double.NaN;
|
||||||
|
_realtimeChartMaxDisplacementMm = 0;
|
||||||
_lastRealtimeChartRefreshAt = DateTime.MinValue;
|
_lastRealtimeChartRefreshAt = DateTime.MinValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1813,8 +1815,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
var sledMassGrams = await ReadFloatHoldingRegisterAsync(master, RegisterSledMassGrams);
|
var sledMassGrams = await ReadFloatHoldingRegisterAsync(master, RegisterSledMassGrams);
|
||||||
var liftSpeedMmPerMin = await ReadFloatHoldingRegisterAsync(master, RegisterLiftSpeed);
|
var liftSpeedMmPerMin = await ReadFloatHoldingRegisterAsync(master, RegisterLiftSpeed);
|
||||||
var liftDisplacementMm = await ReadFloatHoldingRegisterAsync(master, RegisterLiftDisplacement);
|
var liftDisplacementMm = await ReadFloatHoldingRegisterAsync(master, RegisterLiftDisplacement);
|
||||||
var speedMmPerMin = await ReadFloatHoldingRegisterAsync(master, RegisterHorizontalSpeed);
|
var speedMmPerMin = await ReadFloatHoldingRegisterAsync(master, RegisterHorizontalSpeedSetpoint);
|
||||||
var travelMm = await ReadFloatHoldingRegisterAsync(master, RegisterHorizontalDisplacement);
|
var travelMm = await ReadFloatHoldingRegisterAsync(master, RegisterHorizontalTravelSetpoint);
|
||||||
var 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.SledMassGrams = sledMassGrams;
|
||||||
@@ -1849,10 +1851,10 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
await _deviceConnectionService.WriteFloatRegisterAsync(ModbusSlaveAddress, RegisterLiftDisplacement, Recipe.LiftDisplacementMm);
|
await _deviceConnectionService.WriteFloatRegisterAsync(ModbusSlaveAddress, RegisterLiftDisplacement, Recipe.LiftDisplacementMm);
|
||||||
break;
|
break;
|
||||||
case nameof(TestRecipe.SpeedMmPerMin):
|
case nameof(TestRecipe.SpeedMmPerMin):
|
||||||
await _deviceConnectionService.WriteFloatRegisterAsync(ModbusSlaveAddress, RegisterHorizontalSpeed, Recipe.SpeedMmPerMin);
|
await _deviceConnectionService.WriteFloatRegisterAsync(ModbusSlaveAddress, RegisterHorizontalSpeedSetpoint, Recipe.SpeedMmPerMin);
|
||||||
break;
|
break;
|
||||||
case nameof(TestRecipe.TravelMm):
|
case nameof(TestRecipe.TravelMm):
|
||||||
await _deviceConnectionService.WriteFloatRegisterAsync(ModbusSlaveAddress, RegisterHorizontalDisplacement, Recipe.TravelMm);
|
await _deviceConnectionService.WriteFloatRegisterAsync(ModbusSlaveAddress, RegisterHorizontalTravelSetpoint, Recipe.TravelMm);
|
||||||
break;
|
break;
|
||||||
case nameof(TestRecipe.ReplicateCount):
|
case nameof(TestRecipe.ReplicateCount):
|
||||||
await _deviceConnectionService.WriteFloatRegisterAsync(ModbusSlaveAddress, RegisterReplicateCount, Math.Clamp(Recipe.ReplicateCount, 0, 1));
|
await _deviceConnectionService.WriteFloatRegisterAsync(ModbusSlaveAddress, RegisterReplicateCount, Math.Clamp(Recipe.ReplicateCount, 0, 1));
|
||||||
|
|||||||
Reference in New Issue
Block a user