Пример #1
0
        SpotCountAndAverageLength(SmoothBreak smoothBreak)
        {
            var spots = smoothBreak.SmoothSpots
                        .Select(s => s.Spot)
                        .ToList();

            int spotsCount = spots.Count;

            TimeSpan avgSpotLength = spotsCount > 0
                ? SpotUtilities.GetAverageSpotLength(spots)
                : TimeSpan.Zero;

            int averageSpotLengthInSeconds = (int)avgSpotLength.TotalSeconds;

            return(spotsCount, averageSpotLengthInSeconds);
        }
Пример #2
0
        private void LogBestBreakFactorScore(
            SmoothPass smoothPass,
            SmoothPassDefaultIteration smoothPassIteration,
            SmoothBreak smoothBreak,
            IReadOnlyCollection <Spot> spots,
            decimal breakScore,
            BestBreakFactorGroup bestBreakFactorGroup,
            string scoreDebug,
            string type)
        {
            var spotsDuration = Duration.Zero;

            foreach (var spot in spots)
            {
                spotsDuration = spotsDuration.Plus(spot.SpotLength);
            }

            string spotList = SpotUtilities.GetSpotDetailsListString(spots);

            var message = new StringBuilder(128);

            _ = message
                .AppendFormat("Break={0}; ", smoothBreak.TheBreak.ExternalBreakRef)
                .AppendFormat("BreakDur={0} sec(s);", ((int)smoothBreak.TheBreak.Duration.ToTimeSpan().TotalSeconds).ToString())
                .AppendFormat("Avail={0}s;", ((int)smoothBreak.RemainingAvailability.ToTimeSpan().TotalSeconds).ToString())
                .AppendFormat("SpotsDetails={0}; ", spotList)
                .AppendFormat("SpotsLength={0}s; ", ((int)spotsDuration.ToTimeSpan().TotalSeconds).ToString())
                .AppendFormat("ScoreDebug={0}; ", scoreDebug)
                .AppendFormat("Type={0}", type)
            ;

            _smoothDiagnostics.LogBestBreakFactorMessage(
                smoothPass,
                smoothPassIteration,
                bestBreakFactorGroup,
                smoothBreak.TheBreak,
                spots,
                breakScore,
                message.ToString());
        }
Пример #3
0
        /// <summary>
        /// <para>
        /// Get SmoothScenario instances for sponsored spots. We try and return scenarios.
        /// </para>
        /// <para>
        /// Unfortunately we have some business rules regarding sponsor spots
        /// coded here. We should try and make it more data driven later on. The
        /// business rules are that the first sponsor spot is placed in break
        /// #1, the next in break #2.
        /// </para>
        /// </summary>
        private IReadOnlyCollection <SmoothScenario> GetSmoothScenariosForSponsoredSpots(
            IReadOnlyCollection <Spot> spotsForBreak,
            IReadOnlyList <SmoothBreak> progSmoothBreaks,
            IReadOnlyCollection <SmoothFailureMessagesForSpotsCollection> validateAddSpotsToBreakResults,
            int lastSmoothScenarioSequence,
            SpotPositionRules breakPositionRules,
            bool respectSpotTime)
        {
            if (progSmoothBreaks.Count == 0)
            {
                return(new List <SmoothScenario>());
            }

            var sponsoredSpots = spotsForBreak
                                 .Where(s => s.Sponsored);

            if (!sponsoredSpots.Any())
            {
                return(new List <SmoothScenario>());
            }

            var sponsoredSpot = sponsoredSpots.First();

            var validBreaksForSpotTime = progSmoothBreaks
                                         .Where(sb =>
            {
                var canAddSpotService = CanAddSpotService.Factory(sb);
                return(canAddSpotService.CanAddSpotWithTime(sponsoredSpot.StartDateTime, sponsoredSpot.EndDateTime));
            })
                                         .OrderBy(sb => sb.TheBreak.ScheduledDate);

            if (!validBreaksForSpotTime.Any())
            {
                return(new List <SmoothScenario>());
            }

            bool isRestrictedSpotTime = validBreaksForSpotTime.First().Position != 1 ||
                                        validBreaksForSpotTime.Last().Position != progSmoothBreaks[progSmoothBreaks.Count - 1].Position;

            var smoothScenarios = new List <SmoothScenario>();

            var listOfSpotValidations       = validateAddSpotsToBreakResults.ToList();
            var listOfProgrammeSmoothBreaks = progSmoothBreaks.ToList();

            // Check each break, see if there are sponsored spots that we could move
            foreach (var progSmoothBreak in progSmoothBreaks)
            {
                // Lists of groups of spots moved out of break, used for
                // preventing duplicate scenarios which is inefficient
                var externalSpotRefsMovedOutOfBreakByGroup = new HashSet <string>();

                // Get results for adding spot to break in its current state
                var validateAddSpotsToBreakResult = listOfSpotValidations[listOfProgrammeSmoothBreaks.IndexOf(progSmoothBreak)];

                // Check if any failures. Ignore if T1_BreakPosition which
                // happens if break requested and this break is not allowed If
                // break position not allowed then don't move any spots. This
                // would happen if a break request was specified and this isn't
                // the requested break
                if (validateAddSpotsToBreakResult[sponsoredSpot.Uid].Failures.Count > 0 &&    // Failures prevent spot being added
                    !validateAddSpotsToBreakResult[sponsoredSpot.Uid].Failures.Any(f => f.FailureMessage == SmoothFailureMessages.T1_BreakPosition))
                {
                    var spotGroupsToMove = new List <Spot[]>();

                    // Only consider moving spots from this break if any of the
                    // following conditions are true:
                    // - No break request and first or last break (as per
                    // business rules).
                    // - Requested break and this is requested break.
                    // - Spot has restricted times and break is within it.
                    bool addScenariosToFixFailures = false;

                    if (!HasBreakRequest(sponsoredSpot) &&
                        (
                            progSmoothBreak.Position == listOfProgrammeSmoothBreaks[0].Position ||
                            progSmoothBreak.Position == progSmoothBreaks[progSmoothBreaks.Count - 1].Position
                        )
                        )
                    {
                        addScenariosToFixFailures = true;
                    }
                    else if (HasBreakRequest(sponsoredSpot))
                    {
                        addScenariosToFixFailures = SpotUtilities.CanSpotBePlacedInRequestedBreakOrContainer(
                            sponsoredSpot,
                            progSmoothBreaks,
                            breakPositionRules,
                            respectSpotTime,
                            validBreaksForSpotTime,
                            isRestrictedSpotTime,
                            progSmoothBreak);
                    }
                    else if (SpotUtilities.IsBreakWithinSpotTimeRestriction(
                                 respectSpotTime,
                                 isRestrictedSpotTime,
                                 validBreaksForSpotTime,
                                 progSmoothBreak))
                    {
                        addScenariosToFixFailures = true;
                    }

                    if (addScenariosToFixFailures)
                    {
                        // See if we can find spots to move out of the break,
                        // check failures
                        foreach (var failure in validateAddSpotsToBreakResult[sponsoredSpot.Uid].Failures)
                        {
                            switch (failure.FailureMessage)
                            {
                            case SmoothFailureMessages.T1_CampaignClash:
                                MoveCampaignClashSpots(spotsForBreak, sponsoredSpot, progSmoothBreak, spotGroupsToMove);
                                break;

                            case SmoothFailureMessages.T1_InsufficentRemainingDuration:
                                MoveSpotsToIncreaseBreakAvailability(spotsForBreak, sponsoredSpot, progSmoothBreak, spotGroupsToMove);
                                break;

                            case SmoothFailureMessages.T1_ProductClash:
                                MoveProductClashSpots(spotsForBreak, sponsoredSpot, progSmoothBreak, spotGroupsToMove);
                                break;

                            case SmoothFailureMessages.T1_RequestedPositionInBreak:
                                MoveRequestedPositionInBreakSpots(spotsForBreak, sponsoredSpot, progSmoothBreak, spotGroupsToMove);
                                break;
                            }
                        }
                    }

                    // Add scenario to move group of spots, avoid duplicating
                    // scenarios for same spot
                    foreach (var spotGroupToMove in spotGroupsToMove)
                    {
                        string externalSpotRefsForGroup = SpotUtilities.GetListOfSpotExternalReferences(
                            ",",
                            spotGroupToMove.ToList());

                        if (!externalSpotRefsMovedOutOfBreakByGroup.Contains(externalSpotRefsForGroup))
                        {
                            const int PriorityBase2 = 1_000_000;

                            var smoothScenario = new SmoothScenario()
                            {
                                Id       = Guid.NewGuid(),
                                Sequence = lastSmoothScenarioSequence + 1,
                                Priority = PriorityBase2 + (100_000 - spotGroupToMove[0].Preemptlevel)
                            };

                            smoothScenario.Actions.Add(
                                new SmoothActionMoveSpotToUnplaced(
                                    sequence: 1,
                                    spotGroupToMove.Select(s => s.ExternalSpotRef)
                                    )
                                );

                            smoothScenarios.Add(smoothScenario);

                            lastSmoothScenarioSequence = smoothScenarios.Last().Sequence;

                            _ = externalSpotRefsMovedOutOfBreakByGroup.Add(externalSpotRefsForGroup);
                        }
                    }
                }
            }

            return(smoothScenarios);
Пример #4
0
        /// <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);
        }
Пример #5
0
        public void LogBestBreakFactorMessage(
            SmoothPass smoothPass,
            SmoothPassDefaultIteration smoothPassIteration,
            BestBreakFactorGroup bestBreakFactorGroup,
            Break theBreak,
            IReadOnlyCollection <Spot> spots,
            decimal?bestBreakScore,
            string message)
        {
            if (!_isloggingEnabled || !_logBestBreakFactor)
            {
                return;
            }

            string file = _logFilenameFactory.FilenameFor(LogFilenameFactory.LogFileType.BestBreak);

            bool addHeaders = !File.Exists(file);
            int  attempts   = 0;

            do
            {
                attempts++;

                try
                {
                    using var writer = new StreamWriter(file, true);
                    if (addHeaders)
                    {
                        writer.WriteLine(string.Format("{1}{0}{2}{0}{3}{0}{4}{0}{5}{0}{6}{0}{7}{0}{8}{0}{9}{0}{10}", Delimiter, "ProcessorDateTime", "PassSeq", "PassIterationSeq", "PlaceSpotsSeq", "GroupName", "GroupSeq", "ExternalBreakRef", "Spots", "BestBreakScore", "Message"));
                    }

                    writer.WriteLine(string.Format("{1}{0}{2}{0}{3}{0}{4}{0}{5}{0}{6}{0}{7}{0}{8}{0}{9}{0}{10}", Delimiter, _processorDateTimeToString,
                                                   smoothPass == null ? "" : smoothPass.Sequence.ToString(),
                                                   smoothPassIteration == null ? "" : smoothPassIteration.Sequence.ToString(),
                                                   0,
                                                   bestBreakFactorGroup == null ? "" : bestBreakFactorGroup.Name,
                                                   bestBreakFactorGroup == null ? "" : bestBreakFactorGroup.Sequence.ToString(),
                                                   theBreak == null ? "" : theBreak.ExternalBreakRef,
                                                   spots == null || spots.Count == 0 ? "" : SpotUtilities.GetListOfSpotExternalReferences(",", spots),
                                                   bestBreakScore == null ? "" : bestBreakScore.Value.ToString("0.000000000000000000000000000000"),
                                                   message));

                    writer.Flush();
                    writer.Close();

                    return;
                }
                catch (Exception exception) when(IsExceptionForFileInUse(exception) && attempts < 10)
                {
                    Thread.Sleep(100);
                }
            } while (attempts != -1);
        }