using MainShell.Common; using MainShell.Hardware; using MainShell.Log; using MaxwellFramework.Core.Attributes; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace MainShell.Motion { /// /// 逼近对位服务:实现迭代式的多轴对齐算法 /// public class ApproachAlignmentService { private readonly SafeAxisMotion _safeAxisMotion; private readonly ICenterRecognizer _centerRecognizer; private readonly ICoordinateTransformer _coordinateTransformer; private readonly HardwareManager _hardwareManager; public ApproachAlignmentService( SafeAxisMotion safeAxisMotion, ICenterRecognizer centerRecognizer, ICoordinateTransformer coordinateTransformer, HardwareManager hardwareManager) { _safeAxisMotion = safeAxisMotion ?? throw new ArgumentNullException(nameof(safeAxisMotion)); _centerRecognizer = centerRecognizer ?? throw new ArgumentNullException(nameof(centerRecognizer)); _coordinateTransformer = coordinateTransformer ?? throw new ArgumentNullException(nameof(coordinateTransformer)); _hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager)); } /// /// 执行逼近对位 /// /// 逼近请求参数 /// 取消令牌 /// 逼近结果 public async Task ApproachAlignmentAsync( ApproachAlignmentRequest request, CancellationToken cancellationToken = default(CancellationToken)) { if (request == null) { throw new ArgumentNullException(nameof(request)); } ApproachAlignmentResult result = new ApproachAlignmentResult(); try { (double CenterX, double CenterY)? center = null; // 循环执行逼近 for (int iteration = 0; iteration < request.MaxIterations; iteration++) { cancellationToken.ThrowIfCancellationRequested(); $"Approach alignment iteration {iteration + 1}/{request.MaxIterations} started.".LogInfo(); // 第一步:识别中心。首次迭代执行识别,后续迭代复用上一次移动后的识别结果。 if (center == null) { center = await RecognizeCenterAsync( request.Camera, request.RecognitionTimeoutMilliseconds, request.RecognitionParameters, cancellationToken).ConfigureAwait(false); if (center == null) { throw new InvalidOperationException("Center recognition failed."); } $"Center recognized: X={center.Value.CenterX:F4}, Y={center.Value.CenterY:F4}".LogInfo(); } else { $"Center reused from previous iteration: X={center.Value.CenterX:F4}, Y={center.Value.CenterY:F4}".LogInfo(); } // 第二步:坐标转换 Point rulerPoint = BuildRulerPoint(request.Axes); Point pixelPoint = new Point(center.Value.CenterX, center.Value.CenterY); string cameraName = GetCameraName(request.Camera); CoordinateTransformResult transformResult = await _coordinateTransformer.TransformAsync( rulerPoint, pixelPoint, cameraName).ConfigureAwait(false); if (!transformResult.Succeeded) { throw new InvalidOperationException($"Coordinate transformation failed: {transformResult.Message}"); } $"Coordinate transformation completed: Target=({transformResult.TargetPoint.X:F4}, {transformResult.TargetPoint.Y:F4}).".LogInfo(); // 第三步:移动所有轴 MotionMoveRequest[] moveRequests = CreateMoveRequests( request.Axes, transformResult.TargetPoint, request.MoveTimeoutMilliseconds); MotionBatchResult moveResult = await _safeAxisMotion.SafeMoveAsync(cancellationToken, moveRequests).ConfigureAwait(false); moveResult.EnsureSuccess(); $"Axes moved successfully: {moveResult.Results.Count} axes completed.".LogInfo(); // 第四步:再次识别中心 (double CenterX, double CenterY)? newCenter = await RecognizeCenterAsync( request.Camera, request.RecognitionTimeoutMilliseconds, request.RecognitionParameters, cancellationToken).ConfigureAwait(false); if (newCenter == null) { throw new InvalidOperationException("Center recognition failed on second attempt."); } $"Center recognized again: X={newCenter.Value.CenterX:F4}, Y={newCenter.Value.CenterY:F4}".LogInfo(); // 第五步:转换新中心的轴位置 Point newRulerPoint = BuildRulerPoint(request.Axes); Point newPixelPoint = new Point(newCenter.Value.CenterX, newCenter.Value.CenterY); CoordinateTransformResult newTransform = await _coordinateTransformer.TransformAsync( newRulerPoint, newPixelPoint, cameraName).ConfigureAwait(false); if (!newTransform.Succeeded) { throw new InvalidOperationException($"Coordinate transformation failed on second attempt: {newTransform.Message}"); } // 第六步:检查误差 Dictionary errors = new Dictionary(); bool allWithinTolerance = true; foreach (ApproachAlignmentAxis axis in request.Axes) { double currentAxisPos = GetCurrentAxisPosition(axis.AxisName); double expectedAxisPos = GetExpectedAxisPosition(axis.AxisName, newTransform.TargetPoint); double error = Math.Abs(currentAxisPos - expectedAxisPos); errors[axis.AxisName] = error; result.FinalAxisPositions[axis.AxisName] = currentAxisPos; if (error > axis.ToleranceValue) { allWithinTolerance = false; } $"Axis: {axis.AxisName}, Current: {currentAxisPos:F4}, Expected: {expectedAxisPos:F4}, Error: {error:F6}, Tolerance: {axis.ToleranceValue:F4}, WithinTolerance: {error <= axis.ToleranceValue}".LogInfo(); } result.FinalErrors.Clear(); foreach (KeyValuePair error in errors) { result.FinalErrors.Add(error.Key, error.Value); } result.CompletedIterations = iteration + 1; if (allWithinTolerance) { result.Succeeded = true; $"Approach alignment succeeded after {result.CompletedIterations} iteration(s).".LogInfo(); break; } center = newCenter; $"Approach alignment iteration {iteration + 1} completed. Not all axes within tolerance, continuing...".LogInfo(); } if (!result.Succeeded) { $"Approach alignment failed after maximum iterations ({request.MaxIterations}).".LogInfo(); } } catch (OperationCanceledException) { result.Exception = new OperationCanceledException("Approach alignment was cancelled."); "Approach alignment cancelled.".LogInfo(); } catch (Exception ex) { result.Exception = ex; $"Approach alignment error: {ex.Message}".LogSysError(); } return result; } public async Task SingleAlignmentAsync( ApproachAlignmentRequest request, CancellationToken cancellationToken = default(CancellationToken)) { if (request == null) { throw new ArgumentNullException(nameof(request)); } ApproachAlignmentResult result = new ApproachAlignmentResult(); try { cancellationToken.ThrowIfCancellationRequested(); "Single alignment started.".LogInfo(); (double CenterX, double CenterY)? center = await RecognizeCenterAsync( request.Camera, request.RecognitionTimeoutMilliseconds, request.RecognitionParameters, cancellationToken).ConfigureAwait(false); if (center == null) { throw new InvalidOperationException("Center recognition failed."); } Point rulerPoint = BuildRulerPoint(request.Axes); Point pixelPoint = new Point(center.Value.CenterX, center.Value.CenterY); string cameraName = GetCameraName(request.Camera); CoordinateTransformResult transformResult = await _coordinateTransformer.TransformAsync( rulerPoint, pixelPoint, cameraName).ConfigureAwait(false); if (!transformResult.Succeeded) { throw new InvalidOperationException($"Coordinate transformation failed: {transformResult.Message}"); } MotionMoveRequest[] moveRequests = CreateMoveRequests( request.Axes, transformResult.TargetPoint, request.MoveTimeoutMilliseconds); MotionBatchResult moveResult = await _safeAxisMotion.SafeMoveAsync(cancellationToken, moveRequests).ConfigureAwait(false); moveResult.EnsureSuccess(); result.Succeeded = ProcessAlignmentResult(request.Axes, transformResult.TargetPoint, result); result.CompletedIterations = 1; if (result.Succeeded) { "Single alignment completed successfully.".LogInfo(); } else { result.Exception = new InvalidOperationException("Single alignment completed but result exceeded tolerance."); "Single alignment completed but result exceeded tolerance.".LogInfo(); } } catch (OperationCanceledException) { result.Exception = new OperationCanceledException("Single alignment was cancelled."); "Single alignment cancelled.".LogInfo(); } catch (Exception ex) { result.Exception = ex; $"Single alignment error: {ex.Message}".LogSysError(); } return result; } private async Task<(double CenterX, double CenterY)?> RecognizeCenterAsync( CameraType camera, int timeoutMilliseconds, CenterRecognitionParameters parameters, CancellationToken cancellationToken) { try { return await _centerRecognizer.RecognizeCenterAsync( camera, timeoutMilliseconds, parameters, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { $"Center recognition exception: {ex.Message}".LogSysError(); throw; } } private double GetCurrentAxisPosition(string axisName) { try { MwFramework.Device.IAxis axis = _hardwareManager.GetAxisByName(axisName); if (axis == null) { throw new InvalidOperationException($"Axis '{axisName}' not found."); } return axis.State != null ? axis.State.ActualPos : axis.GetPositionImmediate(); } catch (Exception ex) { $"Failed to get current position for axis '{axisName}': {ex.Message}".LogSysError(); throw; } } private MotionMoveRequest[] CreateMoveRequests( IReadOnlyList axes, Point targetPoint, int moveTimeoutMilliseconds) { List moveRequests = new List(); foreach (ApproachAlignmentAxis axis in axes) { double targetPosition = GetExpectedAxisPosition(axis.AxisName, targetPoint); moveRequests.Add(MotionMoveRequest.ForAxisName(axis.AxisName, targetPosition, moveTimeoutMilliseconds)); } return moveRequests.ToArray(); } private bool ProcessAlignmentResult( IReadOnlyList axes, Point targetPoint, ApproachAlignmentResult result) { bool allWithinTolerance = true; result.FinalErrors.Clear(); foreach (ApproachAlignmentAxis axis in axes) { double currentAxisPos = GetCurrentAxisPosition(axis.AxisName); double expectedAxisPos = GetExpectedAxisPosition(axis.AxisName, targetPoint); double error = Math.Abs(currentAxisPos - expectedAxisPos); result.FinalErrors[axis.AxisName] = error; result.FinalAxisPositions[axis.AxisName] = currentAxisPos; if (error > axis.ToleranceValue) { allWithinTolerance = false; } $"Axis: {axis.AxisName}, Current: {currentAxisPos:F4}, Expected: {expectedAxisPos:F4}, Error: {error:F6}, Tolerance: {axis.ToleranceValue:F4}, WithinTolerance: {error <= axis.ToleranceValue}".LogInfo(); } return allWithinTolerance; } private Point BuildRulerPoint(IReadOnlyList axes) { double x = 0; double y = 0; bool hasX = false; bool hasY = false; foreach (ApproachAlignmentAxis axis in axes) { if (axis == null || string.IsNullOrWhiteSpace(axis.AxisName)) { continue; } double currentPosition = GetCurrentAxisPosition(axis.AxisName); if (IsYAxis(axis.AxisName)) { if (!hasY) { y = currentPosition; hasY = true; } } else { if (!hasX) { x = currentPosition; hasX = true; } } } return new Point(x, y); } private double GetExpectedAxisPosition(string axisName, Point targetPoint) { return IsYAxis(axisName) ? targetPoint.Y : targetPoint.X; } private static bool IsYAxis(string axisName) { return !string.IsNullOrWhiteSpace(axisName) && axisName.IndexOf("Y", StringComparison.OrdinalIgnoreCase) >= 0; } private string GetCameraName(CameraType camera) { return _hardwareManager.GetCameraName(camera); } } }