using MainShell.Common; using MainShell.Models.Wafer; using MainShell.Recipe.BaseBoard.Model; using System; using System.Collections.Generic; using System.Linq; namespace MainShell.Process { public class DieTransferPathGenerator : IDieTransferPathGenerator { public DieTransferPathPlan Generate(DieTransferPathRequest request) { return GenerateByRegion(request); } public DieTransferPathRegionPlan GenerateByRegion(DieTransferPathRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } DieTransferPlanningContext planningContext = DieTransferPlanningContext.Create(request); int effectiveCount = Math.Min(planningContext.AvailablePadCount, planningContext.AvailableDieCount); List remainingPadRows; List remainingDieRows; List activePadRows = DieTransferPlanningContext.TrimPadRows(planningContext.PadRows, effectiveCount, out remainingPadRows); List activeDieRows = DieTransferPlanningContext.TrimDieRows(planningContext.DieRows, effectiveCount, out remainingDieRows); DieTransferPathRegionPlan pathPlan = GenerateByRows(activePadRows, activeDieRows, planningContext.TransPathType); List remainingPads = GetOrderedPads(remainingPadRows); List remainingDies = GetOrderedDies(remainingDieRows); pathPlan.AvailableDieCount = planningContext.AvailableDieCount; pathPlan.AvailablePadCount = planningContext.AvailablePadCount; pathPlan.RemainingPads = remainingPads; pathPlan.RemainingDies = remainingDies; pathPlan.RemainingPadRows = DieTransferPlanningContext.CreatePadRows(remainingPads, planningContext.SubstrateRegion, planningContext.PadRowDirectionStrategy); pathPlan.RemainingDieRows = DieTransferPlanningContext.CreateDieRows(remainingDies, planningContext.DieRegion, planningContext.SkipNgDie, planningContext.DieRowDirectionStrategy); pathPlan.RemainingSubstrateRegion = CreateRegionFromPads(remainingPads); pathPlan.RemainingDieRegion = CreateRegionFromDies(remainingDies); return pathPlan; } public DieTransferPathRegionPlan GenerateByCandidates( IReadOnlyCollection padCandidates, IReadOnlyCollection dieCandidates, TransPathType transPathType, bool skipNgDie, DieTransferRowTraversalStrategy padRowDirectionStrategy, DieTransferRowTraversalStrategy dieRowDirectionStrategy) { DieTransferPathRequest request = new DieTransferPathRequest(); request.PadCandidates = padCandidates ?? Array.Empty(); request.DieCandidates = dieCandidates ?? Array.Empty(); request.TransPathType = transPathType; request.SkipNgDie = skipNgDie; request.PadRowDirectionStrategy = padRowDirectionStrategy; request.DieRowDirectionStrategy = dieRowDirectionStrategy; return GenerateByRegion(request); } public DieTransferPathRegionPlan GenerateByRows( IReadOnlyCollection padRows, IReadOnlyCollection dieRows, TransPathType transPathType) { IReadOnlyCollection safePadRows = padRows ?? Array.Empty(); IReadOnlyCollection safeDieRows = dieRows ?? Array.Empty(); List orderedPads = GetOrderedPads(safePadRows); List orderedDies = GetOrderedDies(safeDieRows); List steps = CreateSteps(orderedDies, orderedPads, transPathType); List remainingPads = CreateRemainingPads(orderedPads, steps); List remainingDies = CreateRemainingDies(orderedDies, steps); DieTransferPathRegionPlan pathPlan = new DieTransferPathRegionPlan(); pathPlan.TransPathType = transPathType; pathPlan.AvailablePadCount = GetAvailablePadCount(safePadRows); pathPlan.AvailableDieCount = GetAvailableDieCount(safeDieRows); pathPlan.Steps = steps; pathPlan.RemainingPads = remainingPads; pathPlan.RemainingDies = remainingDies; pathPlan.RemainingPadRows = CreateRemainingPadRows(safePadRows, remainingPads); pathPlan.RemainingDieRows = CreateRemainingDieRows(safeDieRows, remainingDies); pathPlan.RemainingSubstrateRegion = CreateRegionFromPads(remainingPads); pathPlan.RemainingDieRegion = CreateRegionFromDies(remainingDies); return pathPlan; } private static List CreateSteps(IReadOnlyList dies, IReadOnlyList pads, TransPathType transPathType) { return transPathType == TransPathType.Nearest ? BuildNearestSteps(dies, pads, transPathType) : BuildSequenceSteps(dies, pads, transPathType); } private static List BuildSequenceSteps(IReadOnlyList dies, IReadOnlyList pads, TransPathType transPathType) { int stepCount = Math.Min(dies.Count, pads.Count); List steps = new List(stepCount); for (int i = 0; i < stepCount; i++) { steps.Add(CreateStep(i + 1, dies[i], pads[i], transPathType)); } return steps; } private static List BuildNearestSteps(IReadOnlyList dies, IReadOnlyList pads, TransPathType transPathType) { List remainingDies = (dies ?? Array.Empty()).Where(die => die != null).ToList(); List steps = new List(Math.Min(remainingDies.Count, pads.Count)); int stepIndex = 1; foreach (Pad pad in pads) { if (remainingDies.Count == 0) { break; } Die nearestDie = SelectNearestDie(pad, remainingDies); steps.Add(CreateStep(stepIndex, nearestDie, pad, transPathType)); remainingDies.Remove(nearestDie); stepIndex++; } return steps; } private static Die SelectNearestDie(Pad pad, IReadOnlyList dies) { Die selectedDie = null; double bestDistance = double.MaxValue; for (int index = 0; index < dies.Count; index++) { Die die = dies[index]; double currentDistance = CalculateSquaredDistance(die.X, die.Y, pad.X, pad.Y); if (currentDistance < bestDistance) { selectedDie = die; bestDistance = currentDistance; } } return selectedDie; } private static double CalculateSquaredDistance(double sourceX, double sourceY, double targetX, double targetY) { double deltaX = sourceX - targetX; double deltaY = sourceY - targetY; return (deltaX * deltaX) + (deltaY * deltaY); } private static DieTransferPathStep CreateStep(int stepIndex, Die die, Pad pad, TransPathType transPathType) { DieTransferPathStep step = new DieTransferPathStep(); step.StepIndex = stepIndex; step.DieRow = die.Row; step.DieColumn = die.Column; step.PadRow = pad.Row; step.PadColumn = pad.Column; step.DieX = die.X; step.DieY = die.Y; step.PadX = pad.X; step.PadY = pad.Y; step.TransPathType = transPathType; return step; } private static int GetAvailablePadCount(IEnumerable padRows) { return (padRows ?? Array.Empty()) .Where(row => row != null) .Sum(row => row.AvailableCount); } private static int GetAvailableDieCount(IEnumerable dieRows) { return (dieRows ?? Array.Empty()) .Where(row => row != null) .Sum(row => row.AvailableCount); } private static List GetOrderedPads(IEnumerable padRows) { List orderedPads = new List(); foreach (PadTransferRow padRow in (padRows ?? Array.Empty()).Where(row => row != null).OrderBy(row => row.RowIndex)) { orderedPads.AddRange(padRow.GetAvailablePads()); } return orderedPads; } private static List GetOrderedDies(IEnumerable dieRows) { List orderedDies = new List(); foreach (DieTransferRow dieRow in (dieRows ?? Array.Empty()).Where(row => row != null).OrderBy(row => row.RowIndex)) { orderedDies.AddRange(dieRow.GetAvailableDies()); } return orderedDies; } private static List CreateRemainingPadRows(IEnumerable sourceRows, IReadOnlyCollection remainingPads) { HashSet remainingPadKeys = CreatePointKeys(remainingPads, pad => pad.Row, pad => pad.Column); List remainingPadRows = new List(); foreach (PadTransferRow sourceRow in (sourceRows ?? Array.Empty()).Where(row => row != null).OrderBy(row => row.RowIndex)) { List rowPads = (sourceRow.Pads ?? new List()) .Where(pad => pad != null && remainingPadKeys.Contains(CreatePointKey(pad.Row, pad.Column))) .OrderBy(pad => pad.Column) .ToList(); if (rowPads.Count == 0) { continue; } PadTransferRow remainingRow = new PadTransferRow(); remainingRow.RowIndex = sourceRow.RowIndex; remainingRow.Direction = sourceRow.Direction; remainingRow.Pads = rowPads; remainingPadRows.Add(remainingRow); } return remainingPadRows; } private static List CreateRemainingDieRows(IEnumerable sourceRows, IReadOnlyCollection remainingDies) { HashSet remainingDieKeys = CreatePointKeys(remainingDies, die => die.Row, die => die.Column); List remainingDieRows = new List(); foreach (DieTransferRow sourceRow in (sourceRows ?? Array.Empty()).Where(row => row != null).OrderBy(row => row.RowIndex)) { List rowDies = (sourceRow.Dies ?? new List()) .Where(die => die != null && remainingDieKeys.Contains(CreatePointKey(die.Row, die.Column))) .OrderBy(die => die.Column) .ToList(); if (rowDies.Count == 0) { continue; } DieTransferRow remainingRow = new DieTransferRow(); remainingRow.RowIndex = sourceRow.RowIndex; remainingRow.Direction = sourceRow.Direction; remainingRow.SkipNgDie = sourceRow.SkipNgDie; remainingRow.Dies = rowDies; remainingDieRows.Add(remainingRow); } return remainingDieRows; } private static HashSet CreatePointKeys(IEnumerable points, Func getRow, Func getColumn) where TPoint : class { HashSet pointKeys = new HashSet(); foreach (TPoint point in points ?? Array.Empty()) { if (point == null) { continue; } pointKeys.Add(CreatePointKey(getRow(point), getColumn(point))); } return pointKeys; } private static List CreateRemainingPads(IReadOnlyList orderedPads, IReadOnlyList steps) { HashSet usedPadKeys = new HashSet(); foreach (DieTransferPathStep step in steps ?? Array.Empty()) { usedPadKeys.Add(CreatePointKey(step.PadRow, step.PadColumn)); } return (orderedPads ?? Array.Empty()) .Where(pad => pad != null && !usedPadKeys.Contains(CreatePointKey(pad.Row, pad.Column))) .ToList(); } private static List CreateRemainingDies(IReadOnlyList orderedDies, IReadOnlyList steps) { HashSet usedDieKeys = new HashSet(); foreach (DieTransferPathStep step in steps ?? Array.Empty()) { usedDieKeys.Add(CreatePointKey(step.DieRow, step.DieColumn)); } return (orderedDies ?? Array.Empty()) .Where(die => die != null && !usedDieKeys.Contains(CreatePointKey(die.Row, die.Column))) .ToList(); } private static string CreatePointKey(int row, int column) { return $"{row}:{column}"; } private static DieTransferRegion CreateRegionFromPads(IEnumerable pads) { List availablePads = (pads ?? Array.Empty()).Where(pad => pad != null).ToList(); if (availablePads.Count == 0) { return null; } DieTransferRegion region = new DieTransferRegion(); region.StartRow = availablePads.Min(pad => pad.Row); region.StartCol = availablePads.Min(pad => pad.Column); region.EndRow = availablePads.Max(pad => pad.Row); region.EndCol = availablePads.Max(pad => pad.Column); return region; } private static DieTransferRegion CreateRegionFromDies(IEnumerable dies) { List availableDies = (dies ?? Array.Empty()).Where(die => die != null).ToList(); if (availableDies.Count == 0) { return null; } DieTransferRegion region = new DieTransferRegion(); region.StartRow = availableDies.Min(die => die.Row); region.StartCol = availableDies.Min(die => die.Column); region.EndRow = availableDies.Max(die => die.Row); region.EndCol = availableDies.Max(die => die.Column); return region; } } }