/// <summary> /// Returns whether spot can be removed from break /// </summary> /// <param name="spot"></param> /// <returns></returns> private bool CanMoveSpotFromBreak(Spot spot) { // Unbooked spots can always be moved // Booked spots can only be moved if preemptable return(BreakUtilities.IsBreakRefNotSetOrUnused(_spotInfos[spot.Uid].ExternalBreakRefAtRunStart) || (!BreakUtilities.IsBreakRefNotSetOrUnused(_spotInfos[spot.Uid].ExternalBreakRefAtRunStart) && spot.Preemptable)); }
private static void ValidateMultipartSpots( IReadOnlyList <SmoothBreak> progSmoothBreaks, SpotPositionRules breakPositionRules, SpotPositionRules requestedPositionInBreakRules, bool canSplitMultipartSpotsOverBreaks, CanAddSpotService canAddSpotService, SmoothFailureMessagesForSpotsCollection result, IReadOnlyCollection <Spot> spotsAlreadyInTheBreak, IReadOnlyList <Spot> multipartSpots) { if (multipartSpots.Count == 0) { return; } foreach (var spot in multipartSpots) { bool canAddSpotAtBreakPosition = canAddSpotService .CanAddSpotWithBreakRequestForMultipartSpotWithDefaultPosition( spot.BreakRequest, progSmoothBreaks, breakPositionRules); if (!canAddSpotAtBreakPosition) { result.Add(spot.Uid, SmoothFailureMessages.T1_BreakPosition); } // Determine if we can add at requested position in break // TODO: Check that multipart Spots are all linked if (!canAddSpotService.CanAddMultipartSpotAtRequestedPosition( spot.MultipartSpot, spot.MultipartSpotPosition, requestedPositionInBreakRules)) { result.Add(spot.Uid, SmoothFailureMessages.T1_RequestedPositionInBreak); } } // For the multipart spot type then check if this is the break // that it must be added to if (canSplitMultipartSpotsOverBreaks) { return; } // Multipart spots can't be split over breaks, ensure that // break will contain all linked multipart spots if (multipartSpots[0].MultipartSpot == MultipartSpotTypes.TopTail && multipartSpots.Count < 2) { // We're not placing both spots, ensure that this break // has linked spot. Get all linked spots that have been // placed in this break. var linkedSpotsPlacedInBreak = BreakUtilities.GetLinkedMultipartSpots( multipartSpots[0], spotsAlreadyInTheBreak, includeInputSpotInOutput: false ); if (linkedSpotsPlacedInBreak.Count == 0) { // Break doesn't contain linked spots, can't add to // this break foreach (var spot in multipartSpots) { result.Add( spot.Uid, SmoothFailureMessages.T1_CantAddTopAndTailToSameBreak); } } } }
/// <summary> /// Returns best break to add spot(s) to. /// </summary> public BestBreakResult GetBestBreak( SmoothPass smoothPass, SmoothPassDefaultIteration smoothPassIteration, IReadOnlyCollection <BestBreakFactorGroup> bestBreakFactorGroups, IReadOnlyCollection <SmoothBreak> validSmoothBreaks, IReadOnlyCollection <Spot> spotsToPlace, IReadOnlyDictionary <Guid, SpotInfo> spotInfos, IReadOnlyDictionary <string, Clash> clashesByExternalRef, IReadOnlyDictionary <string, Product> productsByExternalRef, IReadOnlyCollection <SmoothBreak> progSmoothBreaks, out BestBreakFactorGroup usedBestBreakFactorGroup) { var bestBreakResult = new BestBreakResult(); usedBestBreakFactorGroup = null; string breakDetailsListString = BreakUtilities.GetListOfBreakExternalReferences( "; ", validSmoothBreaks.Select(sb => sb.TheBreak).ToList()); // Get spot details list as string string spotDetailsListString = SpotUtilities.GetSpotDetailsListString(spotsToPlace); // Check each group in priority order foreach (var bestBreakFactorGroup in bestBreakFactorGroups.OrderBy(g => g.Sequence)) { _smoothDiagnostics.LogBestBreakFactorMessage( smoothPass, smoothPassIteration, bestBreakFactorGroup, null, spotsToPlace, null, $"Checking best break factor group (Breaks={breakDetailsListString}; Spots={spotDetailsListString})" ); // Set all breaks to check var smoothBreaksRemaining = new List <SmoothBreak>(); smoothBreaksRemaining.AddRange(validSmoothBreaks); // Generate unique random score for each break so that we can // pick a random break if necessary. IReadOnlyCollection <double> randomScoreByBreak = GetRandomScoreByBreak( progSmoothBreaks, uniqueScore: true); // Calculate scores for each break, determine best score var scoreByBreak = new Dictionary <SmoothBreak, decimal>(); decimal bestBreakScore = -1; foreach (var smoothBreak in smoothBreaksRemaining) { var(breakScore, scoreDebug) = _bestBreakFactorForGroupService .GetBestBreakFactorScoreForGroup( smoothBreak, spotsToPlace, spotInfos, clashesByExternalRef, productsByExternalRef, progSmoothBreaks, randomScoreByBreak, bestBreakFactorGroup, _smoothResources ); LogBestBreakFactorScore( smoothPass, smoothPassIteration, smoothBreak, spotsToPlace, breakScore, bestBreakFactorGroup, "Main", scoreDebug ); scoreByBreak.Add(smoothBreak, breakScore); if (breakScore > bestBreakScore || bestBreakScore == -1) { bestBreakScore = breakScore; } } if (bestBreakScore > 0) // At least one break has a non-zero score { // Remove all breaks with score less than the best score var smoothBreaksToRemove = new List <SmoothBreak>(); foreach (var smoothBreak in smoothBreaksRemaining) { if (scoreByBreak[smoothBreak] < bestBreakScore) { smoothBreaksToRemove.Add(smoothBreak); } } while (smoothBreaksToRemove.Count > 0) { _ = smoothBreaksRemaining.Remove(smoothBreaksToRemove[0]); smoothBreaksToRemove.RemoveAt(0); } // Single break with best score, return it if (smoothBreaksRemaining.Count == 1) { _smoothDiagnostics.LogBestBreakFactorMessage( smoothPass, smoothPassIteration, bestBreakFactorGroup, smoothBreaksRemaining[0].TheBreak, spotsToPlace, bestBreakScore, $"Using single break which has best score (Spots={spotDetailsListString})" ); usedBestBreakFactorGroup = bestBreakFactorGroup; bestBreakResult.Score = bestBreakScore; bestBreakResult.SmoothBreak = smoothBreaksRemaining[0]; return(bestBreakResult); } // Multiple breaks with best score, determine what we do if (smoothBreaksRemaining.Count > 1) { switch (bestBreakFactorGroup.SameBreakGroupScoreAction) { // Check next group, our default and so no action // required here case SameBreakGroupScoreActions.CheckNextGroup: _smoothDiagnostics.LogBestBreakFactorMessage( smoothPass, smoothPassIteration, bestBreakFactorGroup, null, spotsToPlace, null, "CheckNextGroup: Need to check next group" ); break; // Check single factor to identify single break case SameBreakGroupScoreActions.UseSingleBreakFactorIfBestScoreIsNonZero: // Calculate score for each remaining break _smoothDiagnostics.LogBestBreakFactorMessage( smoothPass, smoothPassIteration, bestBreakFactorGroup, null, spotsToPlace, null, $"UseSingleBreakFactorIfBestScoreIsNonZero: Finding break for single break factor score for group (BreaksRemaining={smoothBreaksRemaining.Count.ToString()})" ); var scoreByBreak2 = new Dictionary <SmoothBreak, decimal>(); decimal bestBreakScore2 = -1; foreach (var smoothBreak in smoothBreaksRemaining) { decimal breakScore2 = _bestBreakFactorService .GetBestBreakFactorScoreForFactor( smoothBreak, spotsToPlace, spotInfos, clashesByExternalRef, productsByExternalRef, progSmoothBreaks, randomScoreByBreak, bestBreakFactorGroup.SameBreakGroupScoreFactor, _smoothResources ); var scoreDebug = $"UseSingleBreakFactorIfBestScoreIsNonZero: {bestBreakFactorGroup.SameBreakGroupScoreFactor.Factor.ToString()}={breakScore2.ToString("0.000000000000000000000000000000")}"; LogBestBreakFactorScore( smoothPass, smoothPassIteration, smoothBreak, spotsToPlace, breakScore2, bestBreakFactorGroup, "", scoreDebug ); scoreByBreak2.Add(smoothBreak, breakScore2); if (breakScore2 > bestBreakScore2 || bestBreakScore2 == -1) { bestBreakScore2 = breakScore2; } } // Remove all but the break with the best score var smoothBreaksToRemove2 = new List <SmoothBreak>(); foreach (var smoothBreak in smoothBreaksRemaining) { if (scoreByBreak2[smoothBreak] < bestBreakScore2) { smoothBreaksToRemove2.Add(smoothBreak); } } while (smoothBreaksToRemove2.Count > 0) { _ = smoothBreaksRemaining.Remove(smoothBreaksToRemove2[0]); smoothBreaksToRemove2.RemoveAt(0); } // Return first break Sanity check, if no break // then check next group if (smoothBreaksRemaining.Count > 0) { _smoothDiagnostics.LogBestBreakFactorMessage( smoothPass, smoothPassIteration, bestBreakFactorGroup, smoothBreaksRemaining[0].TheBreak, spotsToPlace, null, string.Format("UseSingleBreakFactorIfBestScoreIsNonZero: Found break for single break factor score for group (Spots={0}; BreaksRemaining={1})", spotDetailsListString, smoothBreaksRemaining.Count) ); usedBestBreakFactorGroup = bestBreakFactorGroup; bestBreakResult.Score = bestBreakScore2; bestBreakResult.SmoothBreak = smoothBreaksRemaining[0]; return(bestBreakResult); } else { _smoothDiagnostics.LogBestBreakFactorMessage( smoothPass, smoothPassIteration, bestBreakFactorGroup, null, spotsToPlace, null, string.Format("UseSingleBreakFactorIfBestScoreIsNonZero: WARNING: Not found break for single break factor score for group (Spots={0}; BreaksRemaining={1})", spotDetailsListString, smoothBreaksRemaining.Count) ); } break; } } } else // No break has a non-zero score, check next group { _smoothDiagnostics.LogBestBreakFactorMessage( smoothPass, smoothPassIteration, bestBreakFactorGroup, null, spotsToPlace, null, "No breaks with highest score, checking next group" ); } } _smoothDiagnostics.LogBestBreakFactorMessage( smoothPass, smoothPassIteration, null, null, spotsToPlace, null, "No best break for spots" ); // All groups checked, no best break, shouldn't really happen, // possibly bad configuration return(bestBreakResult); }