public PathFindingResult <PickingTravelStep> FindPath(PickingOrder order) { var result = new PathFindingResult <PickingTravelStep> { Success = true, }; var currentPosition = new PickingTravelStep(_warehouseLayout.GetPickingStartPosition(), new Dictionary <long, int>(order.RequiredArticles)); var possiblePickingSlots = order.RequiredArticles .SelectMany(x => _warehouseLayout.GetPickingSlotsWithSku(x.Key)) .OrderBy(x => x.AlleyNumber).ThenBy(x => x.PositionNumber) .ToList(); var precalculatedRoutes = GetRoutesBetweenSlots(possiblePickingSlots); var remainingInOrder = new Dictionary <long, int>(order.RequiredArticles); foreach (var possiblePickingSlot in possiblePickingSlots) { var previousPosition = currentPosition; if (!remainingInOrder.TryGetValue(possiblePickingSlot.Sku, out int positionUnits) || remainingInOrder[possiblePickingSlot.Sku] < 1) { continue; } if (positionUnits < 1) { continue; } var unitsToTake = Math.Min(positionUnits, remainingInOrder[possiblePickingSlot.Sku]); _warehouseLayout.GetPickingSlots().FirstOrDefault(x => x == possiblePickingSlot).Units -= unitsToTake; remainingInOrder[possiblePickingSlot.Sku] -= unitsToTake; currentPosition = new PickingTravelStep(possiblePickingSlot, unitsToTake, new Dictionary <long, int>(remainingInOrder)); currentPosition.Parent = previousPosition; if (!remainingInOrder.Any(x => x.Value > 0)) { break; } } var endPosition = _warehouseLayout.GetPickingEndPosition(); var finalStep = new PickingTravelStep(endPosition, currentPosition.PendingSkus); finalStep.CostFromStart = currentPosition.CostFromStart + FindTravelCostBetween(precalculatedRoutes, finalStep.Position, currentPosition.Position); finalStep.Parent = currentPosition; currentPosition = finalStep; var steps = new List <ITravelStep>(); while (currentPosition != null) { var nextPosition = currentPosition.Parent; currentPosition.Parent = null; steps.Add(currentPosition); if (nextPosition != null && currentPosition != (PickingTravelStep)nextPosition) { var route = FindTravelRouteBetween(precalculatedRoutes, currentPosition.Position, nextPosition.Position).Route; foreach (var coord in route) { result.PathCoordinates.Add(coord); } } currentPosition = nextPosition as PickingTravelStep; } result.Steps = steps.ToArray(); return(result); }
public PathFindingResult <PickingTravelStep> FindPath(PickingOrder order) { var result = new PathFindingResult <PickingTravelStep> { Success = true, }; var openList = new Heap <PickingTravelStep>(); var currentPosition = new PickingTravelStep(_warehouseLayout.GetPickingStartPosition(), new Dictionary <long, int>(order.RequiredArticles)); var possiblePickingSlots = order.RequiredArticles .SelectMany(x => _warehouseLayout.GetPickingSlotsWithSku(x.Key)) .ToList(); var routesBetweenSlots = GetRoutesBetweenSlots(possiblePickingSlots); var endPosition = _warehouseLayout.GetPickingEndPosition(); while (currentPosition.PendingSkus.Any(x => x.Value > 0)) { if (currentPosition.Sku > 0 && currentPosition.PickingSlot.Units > 0 && currentPosition.PendingSkus.ContainsKey(currentPosition.Sku)) { var requiredUnits = currentPosition.PendingSkus[currentPosition.Sku]; var unitsToTake = Math.Min(requiredUnits, currentPosition.PickingSlot.AvailableUnits); currentPosition.PendingSkus[currentPosition.Sku] -= unitsToTake; } if (currentPosition.PendingSkus.All(x => x.Value == 0)) { result.Success = true; break; } var possibleNextSlots = possiblePickingSlots.Where(x => !currentPosition.VisitedSlots.Contains(x) && currentPosition.PendingSkus.TryGetValue(x.Sku, out var value) && value > 0) .ToList(); if (!possibleNextSlots.Any()) { result.Success = false; break; } foreach (var nextSlot in possibleNextSlots) { var remainingRequiredUnits = currentPosition.PendingSkus[nextSlot.Sku]; var unitsToTake = Math.Min(remainingRequiredUnits, nextSlot.Units - nextSlot.ReservedUnits); var remainingSlots = possibleNextSlots.Where(x => x != nextSlot); if (unitsToTake == remainingRequiredUnits) { remainingSlots = remainingSlots.Where(x => x.Sku != nextSlot.Sku); } var remainingSlotsList = remainingSlots.ToList(); var next = new PickingTravelStep(nextSlot, unitsToTake, new Dictionary <long, int>(currentPosition.PendingSkus)) { Parent = currentPosition, }; next.CostFromStart = currentPosition.CostFromStart + FindTravelCostBetween(routesBetweenSlots, next.Position, currentPosition.Position); next.VisitedSlots = new List <PickingSlot>(currentPosition.VisitedSlots) { nextSlot }; var tentativeCost = FindTravelCostBetween(routesBetweenSlots, nextSlot.Position, endPosition); tentativeCost += currentPosition.CostFromStart; tentativeCost += remainingSlotsList.Sum(x => FindTravelCostBetween(routesBetweenSlots, nextSlot.Position, x.Position)); openList.Add(new HeapNode <PickingTravelStep>(next, tentativeCost)); } currentPosition = openList.TakeHeapHeadPosition(); } var finalStep = new PickingTravelStep(endPosition, currentPosition.PendingSkus); finalStep.CostFromStart = currentPosition.CostFromStart + FindTravelCostBetween(routesBetweenSlots, finalStep.Position, currentPosition.Position); finalStep.Parent = currentPosition; currentPosition = finalStep; // recreate found path and reserve items on stock var pickedArticles = order.RequiredArticles.Select(x => x.Key).ToDictionary(x => x, x => 0); var steps = new List <ITravelStep>(); while (currentPosition != null) { if (pickedArticles.ContainsKey(currentPosition.Sku)) { pickedArticles[currentPosition.Sku] += currentPosition.UnitsToTake; } if (currentPosition.PickingSlot != null) { _warehouseLayout.ReserveArticles(currentPosition.PickingSlot.Address, currentPosition.Sku, currentPosition.UnitsToTake); } var nextPosition = currentPosition.Parent; currentPosition.Parent = null; steps.Add(currentPosition); if (nextPosition != null && currentPosition != (PickingTravelStep)nextPosition) { var route = FindTravelRouteBetween(routesBetweenSlots, currentPosition.Position, nextPosition.Position).Route; foreach (var coord in route) { result.PathCoordinates.Add(coord); } } currentPosition = nextPosition as PickingTravelStep; } result.Steps = steps.ToArray(); return(result); }