Files

447 lines
16 KiB
C#
Raw Permalink Normal View History

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<string>(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<string> 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<IProjectManager>().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<IProjectManager>().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<double> 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<double> BuildTrajectory()
{
List<double> 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<double> trajectory = new List<double>(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<double> 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<double> 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<string> 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;
}
}
}