using MainShell.Common; using MainShell.Hardware; using MainShell.DeviceMaintance.Model; using MainShell.Log; using MainShell.Motion; using MaxwellFramework.Core.Interfaces; using Stylet; using StyletIoC; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using MwFramework.Device; namespace MainShell.DeviceMaintance.ViewModel { public class LaserCompensationViewModel : DeviceMaintanceBaseViewModel, IPage { private const string ProjectPageName = "激光补偿"; private const int MinimumMoveTimeoutMilliseconds = 15000; private const double MoveTimeoutScaleFactor = 3d; private const int MoveTimeoutExtraMilliseconds = 2000; private readonly SafeAxisMotion _safeAxisMotion; private readonly AxisSpeedManager _axisSpeedManager; private readonly HardwareManager _hardwareManager; private CancellationTokenSource _cancellationTokenSource; private bool _uiEnable = true; private bool _isRunning; private string _statusMessage; private int _currentLoopIndex; private int _currentPointIndex; private double _currentTargetPosition; private string _selectedAxisName; public LaserCompensationViewModel(SafeAxisMotion safeAxisMotion, AxisSpeedManager axisSpeedManager, HardwareManager hardwareManager) { _safeAxisMotion = safeAxisMotion; _axisSpeedManager = axisSpeedManager; _hardwareManager = hardwareManager; Setting = new LaserCompensationSetting(); Setting.LoadFromFile(); AxisNames = new System.Collections.ObjectModel.ObservableCollection(BuildAxisNames()); if (!string.IsNullOrWhiteSpace(Setting.AxisName) && AxisNames.Contains(Setting.AxisName)) { SelectedAxisName = Setting.AxisName; } else { SelectedAxisName = AxisNames.FirstOrDefault(); } StatusMessage = "待机"; } public string Name => "LaserCompensationMaint"; public LaserCompensationSetting Setting { get; } public System.Collections.ObjectModel.ObservableCollection AxisNames { get; } public string SelectedAxisName { get { return _selectedAxisName; } set { if (SetAndNotify(ref _selectedAxisName, value)) { Setting.AxisName = value; } } } public bool UiEnable { get { return _uiEnable; } set { if (SetAndNotify(ref _uiEnable, value)) { OnUiStateChanged(value); } } } public bool IsRunning { get { return _isRunning; } set { SetAndNotify(ref _isRunning, value); } } public string StatusMessage { get { return _statusMessage; } set { SetAndNotify(ref _statusMessage, value); } } public int CurrentLoopIndex { get { return _currentLoopIndex; } set { SetAndNotify(ref _currentLoopIndex, value); } } public int CurrentPointIndex { get { return _currentPointIndex; } set { SetAndNotify(ref _currentPointIndex, value); } } public double CurrentTargetPosition { get { return _currentTargetPosition; } set { SetAndNotify(ref _currentTargetPosition, value); } } public async Task StartAsync() { if (IsRunning) { return; } string validationMessage = ValidateSetting(); if (!string.IsNullOrWhiteSpace(validationMessage)) { StatusMessage = validationMessage; LocalizedMessageBox.Show(MessageKey.ParamInvalid, MessageKey.TitleError, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); return; } _cancellationTokenSource?.Dispose(); _cancellationTokenSource = new CancellationTokenSource(); UiEnable = false; IsRunning = true; CurrentLoopIndex = 0; CurrentPointIndex = 0; CurrentTargetPosition = 0d; Setting.AxisName = SelectedAxisName; StatusMessage = "激光补偿运行中"; IoC.Get().EnablePageAndDisableOther(ProjectPageName); BuildStartLogMessage().LogInfo(); try { CancellationToken cancellationToken = _cancellationTokenSource.Token; await ExecuteCompensationAsync(cancellationToken).ConfigureAwait(true); StatusMessage = "激光补偿执行完成"; BuildFinishLogMessage("完成").LogInfo(); } catch (OperationCanceledException) { StatusMessage = "激光补偿已停止"; BuildFinishLogMessage("已停止").LogInfo(); } catch (Exception ex) { StatusMessage = $"激光补偿异常:{ex.Message}"; LogManager.LogSysError(ex, true); } finally { IsRunning = false; UiEnable = true; _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; IoC.Get().SwitchState(); } } public void Stop() { if (!IsRunning) { return; } StatusMessage = "正在停止激光补偿"; "激光补偿收到停止请求".LogInfo(); _cancellationTokenSource?.Cancel(); } public void Save() { try { Setting.SaveToFile(); StatusMessage = "参数已保存"; "激光补偿参数已保存".LogInfo(); LocalizedMessageBox.Show(MessageKey.CommonSaveSucceeded, MessageKey.TitleInfo, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information); } catch (Exception ex) { StatusMessage = $"参数保存失败:{ex.Message}"; LogManager.LogSysError(ex, true); } } private async Task ExecuteCompensationAsync(CancellationToken cancellationToken) { IReadOnlyList trajectory = BuildTrajectory(); ApplyAxisSpeedIfPossible(); for (int workIndex = 0; workIndex < Setting.WorkCount; workIndex++) { cancellationToken.ThrowIfCancellationRequested(); CurrentLoopIndex = workIndex + 1; $"激光补偿开始第{CurrentLoopIndex}次循环,总点数={trajectory.Count}".LogInfo(); for (int pointIndex = 0; pointIndex < trajectory.Count; pointIndex++) { cancellationToken.ThrowIfCancellationRequested(); double targetPosition = trajectory[pointIndex]; CurrentPointIndex = pointIndex + 1; CurrentTargetPosition = targetPosition; StatusMessage = $"循环 {CurrentLoopIndex}/{Setting.WorkCount},点位 {CurrentPointIndex}/{trajectory.Count}"; int timeoutMilliseconds = CalculateMoveTimeoutMilliseconds(targetPosition, trajectory, pointIndex); $"激光补偿移动开始:轴={Setting.AxisName},循环={CurrentLoopIndex},点位={CurrentPointIndex},目标={targetPosition:F4},超时={timeoutMilliseconds}ms".LogInfo(); MotionResult motionResult = await _safeAxisMotion.MoveAbsAsync( Setting.AxisName, targetPosition, timeoutMilliseconds, cancellationToken, alarmId: null).ConfigureAwait(false); motionResult.EnsureSuccess(); $"激光补偿移动完成:轴={Setting.AxisName},循环={CurrentLoopIndex},点位={CurrentPointIndex},目标={targetPosition:F4},到位={motionResult.EndPosition:F4}".LogInfo(); if (Setting.DwellTime > 0d) { int dwellMilliseconds = (int)Math.Ceiling(Setting.DwellTime * 1000d); $"激光补偿延时采样:{dwellMilliseconds}ms".LogInfo(); await Task.Delay(dwellMilliseconds, cancellationToken).ConfigureAwait(false); } } } } private void ApplyAxisSpeedIfPossible() { try { if (!string.IsNullOrWhiteSpace(Setting.AxisName) && Setting.Speed > 0d) { _axisSpeedManager.SetAxisSpeed(Setting.AxisName, Setting.Speed); $"激光补偿轴速度已设置:轴={Setting.AxisName},Speed={Setting.Speed:F4}".LogInfo(); } } catch (Exception ex) { $"激光补偿轴速度设置失败:{ex}".LogSysError(); throw; } } private IReadOnlyList BuildTrajectory() { List measurePoints = Enumerable.Range(0, Setting.StepsCount) .Select(index => Setting.StartPos + index * Setting.Steps) .ToList(); double readyPosition = Setting.StartPos - Setting.JumpPos; double overshootPosition = measurePoints[measurePoints.Count - 1] + Setting.JumpPos; List trajectory = new List(measurePoints.Count * 2 + 3) { readyPosition }; trajectory.AddRange(measurePoints); trajectory.Add(overshootPosition); trajectory.AddRange(measurePoints.AsEnumerable().Reverse()); trajectory.Add(readyPosition); return trajectory; } private int CalculateMoveTimeoutMilliseconds(double targetPosition, IReadOnlyList trajectory, int pointIndex) { IAxis axis = _hardwareManager.GetAxisByName(Setting.AxisName); double startPosition = GetMoveStartPosition(axis, trajectory, pointIndex, targetPosition); double distance = Math.Abs(targetPosition - startPosition); double speed = ResolvePositiveMotionParameter(axis != null ? axis.Param.Velocity : 0d, Math.Abs(Setting.Speed)); if (speed <= 0d) { return MinimumMoveTimeoutMilliseconds; } double acceleration = ResolvePositiveMotionParameter(axis != null ? axis.Param.Accelerate : 0d, 0d); double deceleration = ResolvePositiveMotionParameter(axis != null ? axis.Param.Decelerate : 0d, acceleration); double expectedMilliseconds = CalculateExpectedMoveMilliseconds(distance, speed, acceleration, deceleration); return Math.Max(MinimumMoveTimeoutMilliseconds, (int)Math.Ceiling(expectedMilliseconds * MoveTimeoutScaleFactor + MoveTimeoutExtraMilliseconds)); } private static double GetMoveStartPosition(IAxis axis, IReadOnlyList trajectory, int pointIndex, double targetPosition) { if (pointIndex > 0) { return trajectory[pointIndex - 1]; } if (axis != null) { return axis.State != null ? axis.State.ActualPos : axis.GetPositionImmediate(); } return targetPosition; } private static double ResolvePositiveMotionParameter(double primaryValue, double fallbackValue) { if (!double.IsNaN(primaryValue) && !double.IsInfinity(primaryValue) && primaryValue > 0d) { return primaryValue; } if (!double.IsNaN(fallbackValue) && !double.IsInfinity(fallbackValue) && fallbackValue > 0d) { return fallbackValue; } return 0d; } private static double CalculateExpectedMoveMilliseconds(double distance, double speed, double acceleration, double deceleration) { if (distance <= 0d) { return 0d; } if (speed <= 0d) { return 0d; } if (acceleration <= 0d || deceleration <= 0d) { return distance / speed * 1000d; } double accelerateDistance = speed * speed / (2d * acceleration); double decelerateDistance = speed * speed / (2d * deceleration); double criticalDistance = accelerateDistance + decelerateDistance; double expectedSeconds; if (distance >= criticalDistance) { expectedSeconds = speed / acceleration + speed / deceleration + (distance - criticalDistance) / speed; } else { double peakSpeed = Math.Sqrt(2d * distance * acceleration * deceleration / (acceleration + deceleration)); expectedSeconds = peakSpeed / acceleration + peakSpeed / deceleration; } return expectedSeconds * 1000d; } private string ValidateSetting() { if (string.IsNullOrWhiteSpace(Setting.AxisName)) { return "轴名称不能为空"; } if (!AxisNames.Contains(Setting.AxisName)) { return "请选择有效的轴名称"; } if (Setting.StepsCount <= 0) { return "步距次数必须大于0"; } if (Setting.WorkCount <= 0) { return "循环次数必须大于0"; } if (Math.Abs(Setting.Steps) <= 0d) { return "步距必须非0"; } if (Setting.JumpPos < 0d) { return "跃程不能小于0"; } if (Setting.Speed <= 0d) { return "速度必须大于0"; } if (Setting.DwellTime < 0d) { return "延时时间不能小于0"; } return null; } private string BuildStartLogMessage() { return $"激光补偿启动:轴={Setting.AxisName},StartPos={Setting.StartPos:F4},Steps={Setting.Steps:F4},StepsCount={Setting.StepsCount},JumpPos={Setting.JumpPos:F4},WorkCount={Setting.WorkCount},Speed={Setting.Speed:F4},DwellTime={Setting.DwellTime:F3}s"; } private string BuildFinishLogMessage(string state) { return $"激光补偿结束:状态={state},轴={Setting.AxisName},已执行循环={CurrentLoopIndex},最后点位={CurrentPointIndex},最后目标={CurrentTargetPosition:F4}"; } private IEnumerable BuildAxisNames() { return new[] { GetAxisName(_hardwareManager?.Axis_PHS_X1, MainShell.Hardware.AxisName.Axis_PHS_X1), GetAxisName(_hardwareManager?.Axis_PHS_X2, MainShell.Hardware.AxisName.Axis_PHS_X2), GetAxisName(_hardwareManager?.Axis_PHS_Y1, MainShell.Hardware.AxisName.Axis_PHS_Y1), GetAxisName(_hardwareManager?.Axis_WS_X3, MainShell.Hardware.AxisName.Axis_WS_X3), GetAxisName(_hardwareManager?.Axis_Stage_Y3, MainShell.Hardware.AxisName.Axis_Stage_Y3), GetAxisName(_hardwareManager?.Axis_WS_R, MainShell.Hardware.AxisName.Axis_WS_R), }.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToArray(); } private static string GetAxisName(IAxis axis, string fallbackName) { if (axis == null || string.IsNullOrWhiteSpace(axis.Name)) { return fallbackName; } return axis.Name; } } }