using MainShell.Common; using MainShell.Models; using MainShell.Models.Wafer; using MainShell.Recipe.BaseBoard.Model; using System; using System.Collections.Generic; using System.Linq; namespace MainShell.Process { public class DieTransferPlanningContext { public DieTransferPlanningContext() { PadRows = new List(); DieRows = new List(); TransPathType = TransPathType.Sequence; PadRowDirectionStrategy = DieTransferRowTraversalStrategy.AllPositive; DieRowDirectionStrategy = DieTransferRowTraversalStrategy.Serpentine; SkipNgDie = true; } public DieTransferRegion DieRegion { get; set; } public DieTransferRegion SubstrateRegion { get; set; } public TransPathType TransPathType { get; set; } public DieTransferRowTraversalStrategy PadRowDirectionStrategy { get; set; } public DieTransferRowTraversalStrategy DieRowDirectionStrategy { get; set; } public bool SkipNgDie { get; set; } public List PadRows { get; set; } public List DieRows { get; set; } public int AvailablePadCount { get { return PadRows == null ? 0 : PadRows.Sum(row => row.AvailableCount); } } public int AvailableDieCount { get { return DieRows == null ? 0 : DieRows.Sum(row => row.AvailableCount); } } public List GetOrderedPads() { List pads = new List(); if (PadRows == null) { return pads; } foreach (PadTransferRow row in PadRows.OrderBy(item => item.RowIndex)) { pads.AddRange(row.GetAvailablePads()); } return pads; } public List GetOrderedDies() { List dies = new List(); if (DieRows == null) { return dies; } foreach (DieTransferRow row in DieRows.OrderBy(item => item.RowIndex)) { dies.AddRange(row.GetAvailableDies()); } return dies; } public static DieTransferPlanningContext Create(DieTransferPathRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } DieTransferPlanningContext context = new DieTransferPlanningContext(); context.DieRegion = DieTransferRegion.FromRegionModel(request.DieRegion); context.SubstrateRegion = DieTransferRegion.FromRegionModel(request.SubstrateRegion); context.TransPathType = request.TransPathType; context.PadRowDirectionStrategy = request.PadRowDirectionStrategy; context.DieRowDirectionStrategy = request.DieRowDirectionStrategy; context.SkipNgDie = request.SkipNgDie; context.PadRows = CreatePadRows(request.PadCandidates, context.SubstrateRegion, context.PadRowDirectionStrategy); context.DieRows = CreateDieRows(request.DieCandidates, context.DieRegion, request.SkipNgDie, context.DieRowDirectionStrategy); return context; } public static List CreatePadRows(IReadOnlyCollection padCandidates, DieTransferRegion region, DieTransferRowTraversalStrategy rowTraversalStrategy) { IEnumerable filteredPads = (padCandidates ?? Array.Empty()) .Where(pad => pad != null && IsInRegion(pad.Row, pad.Column, region)); List rows = new List(); foreach (IGrouping rowGroup in filteredPads.GroupBy(pad => pad.Row).OrderBy(group => group.Key)) { PadTransferRow row = new PadTransferRow(); row.RowIndex = rowGroup.Key; row.Direction = ResolveRowDirection(rowGroup.Key, rowTraversalStrategy); row.Pads = rowGroup.OrderBy(pad => pad.Column).ToList(); rows.Add(row); } return rows; } public static List CreateDieRows(IReadOnlyCollection dieCandidates, DieTransferRegion region, bool skipNgDie, DieTransferRowTraversalStrategy rowTraversalStrategy) { IEnumerable filteredDies = (dieCandidates ?? Array.Empty()) .Where(die => die != null && IsInRegion(die.Row, die.Column, region)); List rows = new List(); foreach (IGrouping rowGroup in filteredDies.GroupBy(die => die.Row).OrderBy(group => group.Key)) { DieTransferRow row = new DieTransferRow(); row.RowIndex = rowGroup.Key; row.Direction = ResolveRowDirection(rowGroup.Key, rowTraversalStrategy); row.SkipNgDie = skipNgDie; row.Dies = rowGroup.OrderBy(die => die.Column).ToList(); rows.Add(row); } return rows; } public static List TrimPadRows(IReadOnlyCollection sourceRows, int targetCount, out List remainingRows) { List activeRows = new List(); remainingRows = new List(); int remainingCount = Math.Max(0, targetCount); foreach (PadTransferRow sourceRow in (sourceRows ?? Array.Empty()).Where(row => row != null).OrderBy(row => row.RowIndex)) { List availablePads = sourceRow.GetAvailablePads().ToList(); if (availablePads.Count == 0) { continue; } if (remainingCount <= 0) { remainingRows.Add(CreatePadRow(sourceRow.RowIndex, sourceRow.Direction, availablePads)); continue; } if (availablePads.Count <= remainingCount) { activeRows.Add(CreatePadRow(sourceRow.RowIndex, sourceRow.Direction, availablePads)); remainingCount -= availablePads.Count; continue; } activeRows.Add(CreatePadRow(sourceRow.RowIndex, sourceRow.Direction, availablePads.Take(remainingCount))); remainingRows.Add(CreatePadRow(sourceRow.RowIndex, sourceRow.Direction, availablePads.Skip(remainingCount))); remainingCount = 0; } return activeRows; } public static List TrimDieRows(IReadOnlyCollection sourceRows, int targetCount, out List remainingRows) { List activeRows = new List(); remainingRows = new List(); int remainingCount = Math.Max(0, targetCount); foreach (DieTransferRow sourceRow in (sourceRows ?? Array.Empty()).Where(row => row != null).OrderBy(row => row.RowIndex)) { List availableDies = sourceRow.GetAvailableDies().ToList(); if (availableDies.Count == 0) { continue; } if (remainingCount <= 0) { remainingRows.Add(CreateDieRow(sourceRow.RowIndex, sourceRow.Direction, sourceRow.SkipNgDie, availableDies)); continue; } if (availableDies.Count <= remainingCount) { activeRows.Add(CreateDieRow(sourceRow.RowIndex, sourceRow.Direction, sourceRow.SkipNgDie, availableDies)); remainingCount -= availableDies.Count; continue; } activeRows.Add(CreateDieRow(sourceRow.RowIndex, sourceRow.Direction, sourceRow.SkipNgDie, availableDies.Take(remainingCount))); remainingRows.Add(CreateDieRow(sourceRow.RowIndex, sourceRow.Direction, sourceRow.SkipNgDie, availableDies.Skip(remainingCount))); remainingCount = 0; } return activeRows; } private static bool IsInRegion(int row, int column, DieTransferRegion region) { if (region == null) { return true; } return region.Contains(row, column); } private static DieTransferRowDirection ResolveRowDirection(int rowIndex, DieTransferRowTraversalStrategy rowTraversalStrategy) { switch (rowTraversalStrategy) { case DieTransferRowTraversalStrategy.AllNegative: return DieTransferRowDirection.Negative; case DieTransferRowTraversalStrategy.Serpentine: return rowIndex % 2 == 0 ? DieTransferRowDirection.Negative : DieTransferRowDirection.Positive; case DieTransferRowTraversalStrategy.AllPositive: default: return DieTransferRowDirection.Positive; } } private static PadTransferRow CreatePadRow(int rowIndex, DieTransferRowDirection direction, IEnumerable pads) { PadTransferRow row = new PadTransferRow(); row.RowIndex = rowIndex; row.Direction = direction; row.Pads = (pads ?? Array.Empty()).Where(pad => pad != null).ToList(); return row; } private static DieTransferRow CreateDieRow(int rowIndex, DieTransferRowDirection direction, bool skipNgDie, IEnumerable dies) { DieTransferRow row = new DieTransferRow(); row.RowIndex = rowIndex; row.Direction = direction; row.SkipNgDie = skipNgDie; row.Dies = (dies ?? Array.Empty()).Where(die => die != null).ToList(); return row; } } }