diff --git a/XplorePlane/Models/Inspection/Task/InspectionTaskStage.cs b/XplorePlane/Models/Inspection/Task/InspectionTaskStage.cs index f26e1641..99d1f7f6 100644 --- a/XplorePlane/Models/Inspection/Task/InspectionTaskStage.cs +++ b/XplorePlane/Models/Inspection/Task/InspectionTaskStage.cs @@ -18,6 +18,7 @@ namespace XplorePlane.Models.Inspection.Tasks BGADetected, /// Stage 3 检测池 Block 分组完成 PoolGenerated, + BubbleParameters, /// Stage 5 首件 Block 校准完成 Calibration, /// Stage 6 已应用校准到全部 Block,等待扫描 @@ -42,7 +43,8 @@ namespace XplorePlane.Models.Inspection.Tasks [InspectionTaskStage.Created] = new[] { InspectionTaskStage.ImageAdjust, InspectionTaskStage.Aborted }, [InspectionTaskStage.ImageAdjust] = new[] { InspectionTaskStage.BGADetected, InspectionTaskStage.Created, InspectionTaskStage.Aborted }, [InspectionTaskStage.BGADetected] = new[] { InspectionTaskStage.PoolGenerated, InspectionTaskStage.ImageAdjust, InspectionTaskStage.Aborted }, - [InspectionTaskStage.PoolGenerated] = new[] { InspectionTaskStage.Calibration, InspectionTaskStage.BGADetected, InspectionTaskStage.Aborted }, + [InspectionTaskStage.PoolGenerated] = new[] { InspectionTaskStage.BubbleParameters, InspectionTaskStage.BGADetected, InspectionTaskStage.Aborted }, + [InspectionTaskStage.BubbleParameters] = new[] { InspectionTaskStage.Calibration, InspectionTaskStage.PoolGenerated, InspectionTaskStage.Aborted }, [InspectionTaskStage.Calibration] = new[] { InspectionTaskStage.ReadyToScan, InspectionTaskStage.PoolGenerated, InspectionTaskStage.Aborted }, [InspectionTaskStage.ReadyToScan] = new[] { InspectionTaskStage.Scanning, InspectionTaskStage.Calibration, InspectionTaskStage.Aborted }, [InspectionTaskStage.Scanning] = new[] { InspectionTaskStage.Analysis, InspectionTaskStage.ReadyToScan, InspectionTaskStage.Aborted }, diff --git a/XplorePlane/Services/ImageProcessing/WizardOverlayRenderer.cs b/XplorePlane/Services/ImageProcessing/WizardOverlayRenderer.cs index e9f6ae2f..87f47679 100644 --- a/XplorePlane/Services/ImageProcessing/WizardOverlayRenderer.cs +++ b/XplorePlane/Services/ImageProcessing/WizardOverlayRenderer.cs @@ -101,7 +101,7 @@ namespace XplorePlane.Services.ImageProcessing Width = Math.Max(1, block.Width), Height = Math.Max(1, block.Height), Stroke = stroke, - StrokeThickness = 2, + StrokeThickness = 4, Fill = Brushes.Transparent, IsHitTestVisible = false }; @@ -113,13 +113,13 @@ namespace XplorePlane.Services.ImageProcessing { X1 = block.CenterX - crossHalf, Y1 = block.CenterY, X2 = block.CenterX + crossHalf, Y2 = block.CenterY, - Stroke = stroke, StrokeThickness = 1.5, IsHitTestVisible = false + Stroke = stroke, StrokeThickness = 2.5, IsHitTestVisible = false }; var vLine = new System.Windows.Shapes.Line { X1 = block.CenterX, Y1 = block.CenterY - crossHalf, X2 = block.CenterX, Y2 = block.CenterY + crossHalf, - Stroke = stroke, StrokeThickness = 1.5, IsHitTestVisible = false + Stroke = stroke, StrokeThickness = 2.5, IsHitTestVisible = false }; canvas.Children.Add(hLine); canvas.Children.Add(vLine); diff --git a/XplorePlane/Services/Inspection/Task/BallOverlayService.cs b/XplorePlane/Services/Inspection/Task/BallOverlayService.cs index c1f323c6..8d3f5ad9 100644 --- a/XplorePlane/Services/Inspection/Task/BallOverlayService.cs +++ b/XplorePlane/Services/Inspection/Task/BallOverlayService.cs @@ -28,9 +28,7 @@ namespace XplorePlane.Services.Inspection.Tasks { ArgumentNullException.ThrowIfNull(balls); ArgumentNullException.ThrowIfNull(context); - var labels = BgaBallGroupLabeler.AssignLabels( - balls, - XplorePlane.Models.Inspection.BgaGridIndexingMode.AlphaNumericTopLeft); + var labels = BgaBallGroupLabeler.AssignLabels(balls, context.IndexingMode); _overlay.PublishBgaBalls(balls.Select(ball => new BallOverlayItem { Id = ball.Id, diff --git a/XplorePlane/Services/Inspection/Task/BlockGeneratorService.cs b/XplorePlane/Services/Inspection/Task/BlockGeneratorService.cs index 9271eecd..4e90702a 100644 --- a/XplorePlane/Services/Inspection/Task/BlockGeneratorService.cs +++ b/XplorePlane/Services/Inspection/Task/BlockGeneratorService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using XplorePlane.Models.Inspection; using XplorePlane.Models.Inspection.Tasks; namespace XplorePlane.Services.Inspection.Tasks @@ -24,7 +25,8 @@ namespace XplorePlane.Services.Inspection.Tasks InspectionPool GeneratePool( IReadOnlyList balls, int columns, int rows, int ballsPerBlock, - int refWidth, int refHeight); + int refWidth, int refHeight, + BgaGridIndexingMode indexingMode = BgaGridIndexingMode.AlphaNumericTopLeft); } /// @@ -36,7 +38,8 @@ namespace XplorePlane.Services.Inspection.Tasks public InspectionPool GeneratePool( IReadOnlyList balls, int columns, int rows, int ballsPerBlock, - int refWidth, int refHeight) + int refWidth, int refHeight, + BgaGridIndexingMode indexingMode = BgaGridIndexingMode.AlphaNumericTopLeft) { if (columns <= 0) throw new ArgumentOutOfRangeException(nameof(columns)); if (rows <= 0) throw new ArgumentOutOfRangeException(nameof(rows)); @@ -72,21 +75,17 @@ namespace XplorePlane.Services.Inspection.Tasks blockId++; // 计算质心 - double centroidX = cellBalls.Length > 0 - ? cellBalls.Average(b => b.X) - : (xMin + xMax) / 2.0; - double centroidY = cellBalls.Length > 0 - ? cellBalls.Average(b => b.Y) - : (yMin + yMax) / 2.0; - - string name = $"{IndexToLetters(row)}{col + 1}"; + // Block 边界必须使用网格单元中心,不能使用球心作为矩形中心,否则相邻 Block 会重叠。 + double blockCenterX = (xMin + xMax) / 2.0; + double blockCenterY = (yMin + yMax) / 2.0; + string name = GetBlockName(row, col, rows, columns, indexingMode); blocks.Add(new InspectionBlock { Id = blockId, Name = name, - CenterX = centroidX, - CenterY = centroidY, + CenterX = blockCenterX, + CenterY = blockCenterY, Width = cellWidth, Height = cellHeight, Balls = cellBalls, @@ -107,11 +106,12 @@ namespace XplorePlane.Services.Inspection.Tasks }; } - /// 将行索引转换为字母前缀:0→A, 1→B, ..., 25→Z。 - private static string IndexToLetters(int index) + private static string GetBlockName( + int row, int column, int rows, int columns, BgaGridIndexingMode indexingMode) { - if (index < 0) return "?"; - return ((char)('A' + (index % 26))).ToString(); + var grid = new BgaGridGeometry( + rows, columns, 0, 0, Array.Empty(), indexingMode); + return BgaTargetIdGenerator.Generate(grid)[row * columns + column]; } } -} \ No newline at end of file +} diff --git a/XplorePlane/Services/Inspection/Task/TaskWizardOverlayService.cs b/XplorePlane/Services/Inspection/Task/TaskWizardOverlayService.cs index 9122f3a6..4b0331d2 100644 --- a/XplorePlane/Services/Inspection/Task/TaskWizardOverlayService.cs +++ b/XplorePlane/Services/Inspection/Task/TaskWizardOverlayService.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using XplorePlane.Models.Inspection; namespace XplorePlane.Services.Inspection.Tasks { @@ -84,7 +85,8 @@ namespace XplorePlane.Services.Inspection.Tasks double OriginY = 0, double ScaleX = 1, double ScaleY = 1, - string SourceId = ""); + string SourceId = "", + BgaGridIndexingMode IndexingMode = BgaGridIndexingMode.AlphaNumericTopLeft); /// Block 叠加层元素。 public class BlockOverlayItem diff --git a/XplorePlane/ViewModels/Inspection/TaskWizard/InspectionTaskWizardViewModel.cs b/XplorePlane/ViewModels/Inspection/TaskWizard/InspectionTaskWizardViewModel.cs index fba9a9b0..dbb2a021 100644 --- a/XplorePlane/ViewModels/Inspection/TaskWizard/InspectionTaskWizardViewModel.cs +++ b/XplorePlane/ViewModels/Inspection/TaskWizard/InspectionTaskWizardViewModel.cs @@ -204,6 +204,14 @@ namespace XplorePlane.ViewModels.Inspection.TaskWizard set => SetProperty(ref _ballsPerBlock, value); } + private string _indexingMode = "AlphaNumericTopLeft"; + + public string IndexingMode + { + get => _indexingMode; + set => SetProperty(ref _indexingMode, value); + } + private double _sensitivity = 50.0; public double Sensitivity @@ -356,10 +364,11 @@ namespace XplorePlane.ViewModels.Inspection.TaskWizard "首件校准", // Stage 5 "应用确认", // Stage 6 "自动扫描", // Stage 7 - "结果输出" // Stage 8 + "结果分析", // Stage 8 + "结果输出" // Stage 9 }; - public const int StageCount = 9; + public const int StageCount = 10; // ── 命令 ── @@ -466,6 +475,7 @@ namespace XplorePlane.ViewModels.Inspection.TaskWizard InspectionTaskStage.ImageAdjust => !string.IsNullOrWhiteSpace(PipelineSummary), InspectionTaskStage.BGADetected => DetectedBalls.Count > 0, InspectionTaskStage.PoolGenerated => Pool != null, + InspectionTaskStage.BubbleParameters => Pool != null, InspectionTaskStage.Calibration => TaskCalibrationResult != null, InspectionTaskStage.ReadyToScan => true, InspectionTaskStage.Completed => true, @@ -482,6 +492,7 @@ namespace XplorePlane.ViewModels.Inspection.TaskWizard InspectionTaskStage.ImageAdjust => string.IsNullOrWhiteSpace(PipelineSummary) ? "PipelineSummary=空" : "PipelineSummary=正常", InspectionTaskStage.BGADetected => $"DetectedBalls.Count={DetectedBalls.Count}", InspectionTaskStage.PoolGenerated => $"Pool={(Pool == null ? "null" : "正常")}", + InspectionTaskStage.BubbleParameters => $"Pool={(Pool == null ? "null" : "正常")}", InspectionTaskStage.Calibration => $"TaskCalibrationResult={(TaskCalibrationResult == null ? "null" : "正常")}", InspectionTaskStage.Scanning => "扫描中,下一步禁用", InspectionTaskStage.Analysis => "分析中,下一步禁用", @@ -634,7 +645,8 @@ namespace XplorePlane.ViewModels.Inspection.TaskWizard BallCoordinateSystem.ViewportImage, sourceImage.PixelWidth, sourceImage.PixelHeight, - SourceId: InspectionTask.Id.ToString())); + SourceId: InspectionTask.Id.ToString(), + IndexingMode: ParseIndexingMode(IndexingMode))); _eventAggregator.GetEvent().Publish(DetectedBalls); CurrentStage = InspectionTaskStage.BGADetected; } @@ -660,7 +672,7 @@ namespace XplorePlane.ViewModels.Inspection.TaskWizard var (refWidth, refHeight) = GetReferenceImageSize(); var pool = _blockGenerator.GeneratePool( DetectedBalls, PoolColumns, PoolRows, BallsPerBlock, - refWidth, refHeight); + refWidth, refHeight, ParseIndexingMode(IndexingMode)); Pool = pool; InspectionTask = InspectionTask with { Pool = pool, Stage = InspectionTaskStage.PoolGenerated }; @@ -703,10 +715,11 @@ namespace XplorePlane.ViewModels.Inspection.TaskWizard if (Pool != null) { - InspectionTask = InspectionTask with { BubbleParameters = bp, Stage = InspectionTaskStage.Calibration }; + InspectionTask = InspectionTask with { BubbleParameters = bp, Stage = InspectionTaskStage.BubbleParameters }; } - CurrentStage = InspectionTaskStage.Calibration; + // 参数设置完成后停留在当前步骤,使“下一步”进入首件校准;校准仍需显式执行。 + CurrentStage = InspectionTaskStage.BubbleParameters; _logger.LogInformation("✓ 气泡参数已应用"); } @@ -1007,5 +1020,13 @@ namespace XplorePlane.ViewModels.Inspection.TaskWizard return (bitmap.PixelWidth, bitmap.PixelHeight); return (1024, 1024); } + + private static BgaGridIndexingMode ParseIndexingMode(string? value) => value switch + { + "AlphaNumericTopRight" => BgaGridIndexingMode.AlphaNumericTopRight, + "AlphaNumericColumnMajorTopLeft" => BgaGridIndexingMode.AlphaNumericColumnMajorTopLeft, + "AlphaNumericColumnMajorBottomLeft" => BgaGridIndexingMode.AlphaNumericColumnMajorBottomLeft, + _ => BgaGridIndexingMode.AlphaNumericTopLeft + }; } } diff --git a/XplorePlane/Views/Inspection/TaskWizard/InspectionTaskWizardWindow.xaml b/XplorePlane/Views/Inspection/TaskWizard/InspectionTaskWizardWindow.xaml index f80a9e7c..4b5f53b8 100644 --- a/XplorePlane/Views/Inspection/TaskWizard/InspectionTaskWizardWindow.xaml +++ b/XplorePlane/Views/Inspection/TaskWizard/InspectionTaskWizardWindow.xaml @@ -219,6 +219,7 @@ + @@ -240,6 +241,17 @@ + + + + + + + + + +