Refactor JunctionFiller and SchedulePlanner to improve parameter handling by introducing JunctionPlacement record for cleaner code and better readability. Update Directory.Build.props to suppress additional warnings related to API boundaries and unused route parameters, ensuring a more focused analysis during builds.
This commit is contained in:
@@ -9,6 +9,18 @@
|
|||||||
<AnalysisLevel>latest</AnalysisLevel>
|
<AnalysisLevel>latest</AnalysisLevel>
|
||||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
<NoWarn>$(NoWarn);CA1711;CA1716;CA1848;CA1873</NoWarn>
|
<!--
|
||||||
|
CA1859 (сменить интерфейс коллекции на конкретный тип ради девиртуализации) — осознанно
|
||||||
|
выключено: помечаются им границы API (доменные модели планировщика, порты, хелперы тестов),
|
||||||
|
и «ускорение» там означает изменяемую коллекцию, протекающую через границу. Пути не горячие
|
||||||
|
(генерация расписания идёт раз в тик, а не в цикле по кадрам). Понадобится — включать точечно
|
||||||
|
по месту, а не глобально.
|
||||||
|
|
||||||
|
ASP0018 (параметр маршрута не используется хендлером) — тоже осознанно: часть шаблонов
|
||||||
|
объявляет сегменты ради формы URL, а не ради хендлера. Так, превью заставки адресуется
|
||||||
|
подблоком, но канал и блок остаются в пути, потому что по ним строит ссылки плейлист;
|
||||||
|
у /{slug}/watch токен подтверждает зрителя, а не подписку на канал.
|
||||||
|
-->
|
||||||
|
<NoWarn>$(NoWarn);CA1711;CA1716;CA1848;CA1873;CA1859;ASP0018</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -18,6 +18,19 @@ public sealed class JunctionHistory
|
|||||||
_lastPlaced[element.Kind] = moment;
|
_lastPlaced[element.Kind] = moment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Где ставится стык и между чем. Собрано в один параметр: по отдельности эти четыре значения ехали
|
||||||
|
/// сквозь всю раскладку и вместе с курсором, пределом и накопителями раздували сигнатуры.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="FromShowId">Шоу перед стыком, <paramref name="ToShowId"/> — после; заставке нужна
|
||||||
|
/// именно пара соседей, её ассет рендерится под неё после сборки ленты.</param>
|
||||||
|
public sealed record JunctionPlacement(
|
||||||
|
Guid SlotId,
|
||||||
|
Guid? FromShowId,
|
||||||
|
Guid? ToShowId,
|
||||||
|
bool ElementChanged
|
||||||
|
);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Раскладка врезок стыка: реклама, промо, заставка, заполнитель. Ставит только то, что влезает
|
/// Раскладка врезок стыка: реклама, промо, заставка, заполнитель. Ставит только то, что влезает
|
||||||
/// целиком до предела (якорь или горизонт) — обрезать врезку нельзя, а перехлёст сдвинул бы якорь.
|
/// целиком до предела (якорь или горизонт) — обрезать врезку нельзя, а перехлёст сдвинул бы якорь.
|
||||||
@@ -27,19 +40,12 @@ public sealed class JunctionHistory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class JunctionFiller
|
public static class JunctionFiller
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>Раскладывает стык от <paramref name="cursor"/>. Возвращает курсор после стыка.</summary>
|
||||||
/// Раскладывает стык от <paramref name="cursor"/>. <paramref name="fromShowId"/>/<paramref name="toShowId"/>
|
|
||||||
/// нужны заставке: её ассет зависит от пары соседей и рендерится после сборки ленты.
|
|
||||||
/// Возвращает курсор после стыка.
|
|
||||||
/// </summary>
|
|
||||||
public static DateTimeOffset Fill(
|
public static DateTimeOffset Fill(
|
||||||
PlanningJunction? junction,
|
PlanningJunction? junction,
|
||||||
DateTimeOffset cursor,
|
DateTimeOffset cursor,
|
||||||
DateTimeOffset limit,
|
DateTimeOffset limit,
|
||||||
Guid slotId,
|
JunctionPlacement placement,
|
||||||
Guid? fromShowId,
|
|
||||||
Guid? toShowId,
|
|
||||||
bool elementChanged,
|
|
||||||
JunctionHistory history,
|
JunctionHistory history,
|
||||||
List<PlannedItem> items,
|
List<PlannedItem> items,
|
||||||
PlanTrace? trace
|
PlanTrace? trace
|
||||||
@@ -55,7 +61,7 @@ public static class JunctionFiller
|
|||||||
|
|
||||||
foreach (var element in ordered)
|
foreach (var element in ordered)
|
||||||
{
|
{
|
||||||
if (element.OnlyOnElementChange && !elementChanged)
|
if (element.OnlyOnElementChange && !placement.ElementChanged)
|
||||||
continue;
|
continue;
|
||||||
if (!history.Allows(element, cursor))
|
if (!history.Allows(element, cursor))
|
||||||
continue;
|
continue;
|
||||||
@@ -63,8 +69,8 @@ public static class JunctionFiller
|
|||||||
var placedAt = cursor;
|
var placedAt = cursor;
|
||||||
cursor =
|
cursor =
|
||||||
element.Kind == JunctionElementKind.Bumper
|
element.Kind == JunctionElementKind.Bumper
|
||||||
? PlaceBumper(element, cursor, limit, slotId, fromShowId, toShowId, items, trace)
|
? PlaceBumper(element, cursor, limit, placement, items, trace)
|
||||||
: PlaceUnits(element, cursor, limit, slotId, items, trace);
|
: PlaceUnits(element, cursor, limit, placement.SlotId, items, trace);
|
||||||
|
|
||||||
if (cursor > placedAt)
|
if (cursor > placedAt)
|
||||||
history.Record(element, placedAt);
|
history.Record(element, placedAt);
|
||||||
@@ -81,9 +87,7 @@ public static class JunctionFiller
|
|||||||
PlanningJunctionElement element,
|
PlanningJunctionElement element,
|
||||||
DateTimeOffset cursor,
|
DateTimeOffset cursor,
|
||||||
DateTimeOffset limit,
|
DateTimeOffset limit,
|
||||||
Guid slotId,
|
JunctionPlacement placement,
|
||||||
Guid? fromShowId,
|
|
||||||
Guid? toShowId,
|
|
||||||
List<PlannedItem> items,
|
List<PlannedItem> items,
|
||||||
PlanTrace? trace
|
PlanTrace? trace
|
||||||
)
|
)
|
||||||
@@ -97,14 +101,14 @@ public static class JunctionFiller
|
|||||||
Guid.Empty,
|
Guid.Empty,
|
||||||
cursor,
|
cursor,
|
||||||
end,
|
end,
|
||||||
toShowId,
|
placement.ToShowId,
|
||||||
null,
|
null,
|
||||||
slotId,
|
placement.SlotId,
|
||||||
PlannedItemKind.Bumper,
|
PlannedItemKind.Bumper,
|
||||||
trace,
|
trace,
|
||||||
element.BumperTemplateId,
|
element.BumperTemplateId,
|
||||||
fromShowId,
|
placement.FromShowId,
|
||||||
toShowId
|
placement.ToShowId
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return end;
|
return end;
|
||||||
|
|||||||
@@ -14,20 +14,35 @@ public static class SchedulePlanner
|
|||||||
{
|
{
|
||||||
private const int IterationBackstop = 100_000;
|
private const int IterationBackstop = 100_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Накопители одного прогона: собираемая лента, новые курсоры слотов, предупреждения, история
|
||||||
|
/// врезок и шоу последней поставленной единицы. Всё это протаскивалось через сигнатуры десятком
|
||||||
|
/// параметров (включая <c>ref</c>), хотя принадлежит прогону целиком, а не отдельному шагу.
|
||||||
|
///
|
||||||
|
/// История врезок общая на прогон намеренно: «не чаще раза в полчаса» должно работать и через
|
||||||
|
/// границу слота.
|
||||||
|
/// </summary>
|
||||||
|
private sealed class PlanningRun(PlanningInput input, IRandomSource random)
|
||||||
|
{
|
||||||
|
public PlanningInput Input { get; } = input;
|
||||||
|
public IRandomSource Random { get; } = random;
|
||||||
|
public List<PlannedItem> Items { get; } = [];
|
||||||
|
public List<PlanningCursorUpdate> Cursors { get; } = [];
|
||||||
|
public List<PlanningWarning> Warnings { get; } = [];
|
||||||
|
public JunctionHistory Junctions { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>Шоу последней поставленной единицы — по нему стык понимает, сменился ли элемент.</summary>
|
||||||
|
public Guid? PreviousShowId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public static PlanningResult Plan(PlanningInput input, IRandomSource random)
|
public static PlanningResult Plan(PlanningInput input, IRandomSource random)
|
||||||
{
|
{
|
||||||
var items = new List<PlannedItem>();
|
var run = new PlanningRun(input, random);
|
||||||
var cursors = new List<PlanningCursorUpdate>();
|
|
||||||
var warnings = new List<PlanningWarning>();
|
|
||||||
|
|
||||||
var slots = input.Slots.OrderBy(s => s.TargetStartUtc).ToList();
|
var slots = input.Slots.OrderBy(s => s.TargetStartUtc).ToList();
|
||||||
var cursor = input.StartUtc;
|
var cursor = input.StartUtc;
|
||||||
var iterations = 0;
|
var iterations = 0;
|
||||||
|
|
||||||
// История врезок общая на прогон: «не чаще раза в полчаса» должно работать и через границу слота.
|
|
||||||
var junctions = new JunctionHistory();
|
|
||||||
Guid? previousShowId = null;
|
|
||||||
|
|
||||||
for (var i = 0; i < slots.Count && cursor < input.HorizonEndUtc; i++)
|
for (var i = 0; i < slots.Count && cursor < input.HorizonEndUtc; i++)
|
||||||
{
|
{
|
||||||
if (iterations++ > IterationBackstop)
|
if (iterations++ > IterationBackstop)
|
||||||
@@ -42,32 +57,20 @@ public static class SchedulePlanner
|
|||||||
|
|
||||||
var nextAnchor = FindNextAnchor(slots, i + 1);
|
var nextAnchor = FindNextAnchor(slots, i + 1);
|
||||||
|
|
||||||
cursor = OpenSlot(slot, cursor, input, items, warnings, out var trace);
|
cursor = OpenSlot(slot, cursor, run, out var trace);
|
||||||
if (cursor >= input.HorizonEndUtc)
|
if (cursor >= input.HorizonEndUtc)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
cursor = FillSlot(
|
cursor = FillSlot(slot, cursor, nextAnchor, trace, run);
|
||||||
slot,
|
|
||||||
cursor,
|
|
||||||
nextAnchor,
|
|
||||||
input,
|
|
||||||
random,
|
|
||||||
items,
|
|
||||||
cursors,
|
|
||||||
warnings,
|
|
||||||
trace,
|
|
||||||
junctions,
|
|
||||||
ref previousShowId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Хвост до горизонта закрывает фон: лента обязана быть непрерывной, иначе живой край
|
// Хвост до горизонта закрывает фон: лента обязана быть непрерывной, иначе живой край
|
||||||
// упрётся в дыру.
|
// упрётся в дыру.
|
||||||
if (cursor < input.HorizonEndUtc)
|
if (cursor < input.HorizonEndUtc)
|
||||||
cursor = FillWithFallback(cursor, input.HorizonEndUtc, input, items, null);
|
cursor = FillWithFallback(cursor, input.HorizonEndUtc, run, null);
|
||||||
|
|
||||||
if (cursor < input.HorizonEndUtc && input.FallbackUnits.Count == 0)
|
if (cursor < input.HorizonEndUtc && input.FallbackUnits.Count == 0)
|
||||||
warnings.Add(
|
run.Warnings.Add(
|
||||||
new PlanningWarning(
|
new PlanningWarning(
|
||||||
PlanningWarningKind.FallbackEmpty,
|
PlanningWarningKind.FallbackEmpty,
|
||||||
null,
|
null,
|
||||||
@@ -75,7 +78,7 @@ public static class SchedulePlanner
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return new PlanningResult(items, cursors, warnings);
|
return new PlanningResult(run.Items, run.Cursors, run.Warnings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -85,9 +88,7 @@ public static class SchedulePlanner
|
|||||||
private static DateTimeOffset OpenSlot(
|
private static DateTimeOffset OpenSlot(
|
||||||
PlanningSlot slot,
|
PlanningSlot slot,
|
||||||
DateTimeOffset cursor,
|
DateTimeOffset cursor,
|
||||||
PlanningInput input,
|
PlanningRun run,
|
||||||
List<PlannedItem> items,
|
|
||||||
List<PlanningWarning> warnings,
|
|
||||||
out PlanTrace trace
|
out PlanTrace trace
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -97,7 +98,7 @@ public static class SchedulePlanner
|
|||||||
{
|
{
|
||||||
// До целевого времени ещё есть место — закрываем его фоном. Для якоря это обязательно,
|
// До целевого времени ещё есть место — закрываем его фоном. Для якоря это обязательно,
|
||||||
// для обычного слота тоже: иначе он начнётся раньше объявленного в программе времени.
|
// для обычного слота тоже: иначе он начнётся раньше объявленного в программе времени.
|
||||||
cursor = FillWithFallback(cursor, slot.TargetStartUtc, input, items, slot.SlotId);
|
cursor = FillWithFallback(cursor, slot.TargetStartUtc, run, slot.SlotId);
|
||||||
}
|
}
|
||||||
else if (slot.SnapToMinutes is { } snap && snap > 0)
|
else if (slot.SnapToMinutes is { } snap && snap > 0)
|
||||||
{
|
{
|
||||||
@@ -106,7 +107,7 @@ public static class SchedulePlanner
|
|||||||
var target = RoundUp(cursor, TimeSpan.FromMinutes(snap));
|
var target = RoundUp(cursor, TimeSpan.FromMinutes(snap));
|
||||||
if (target - cursor <= TimeSpan.FromMinutes(slot.MaxDriftMinutes))
|
if (target - cursor <= TimeSpan.FromMinutes(slot.MaxDriftMinutes))
|
||||||
{
|
{
|
||||||
var afterFill = FillWithFallback(cursor, target, input, items, slot.SlotId);
|
var afterFill = FillWithFallback(cursor, target, run, slot.SlotId);
|
||||||
snapped = afterFill > cursor;
|
snapped = afterFill > cursor;
|
||||||
cursor = afterFill;
|
cursor = afterFill;
|
||||||
}
|
}
|
||||||
@@ -114,7 +115,7 @@ public static class SchedulePlanner
|
|||||||
|
|
||||||
var drift = (int)Math.Round((cursor - slot.TargetStartUtc).TotalMinutes);
|
var drift = (int)Math.Round((cursor - slot.TargetStartUtc).TotalMinutes);
|
||||||
if (Math.Abs(drift) > slot.MaxDriftMinutes)
|
if (Math.Abs(drift) > slot.MaxDriftMinutes)
|
||||||
warnings.Add(
|
run.Warnings.Add(
|
||||||
new PlanningWarning(
|
new PlanningWarning(
|
||||||
PlanningWarningKind.DriftExceeded,
|
PlanningWarningKind.DriftExceeded,
|
||||||
slot.SlotId,
|
slot.SlotId,
|
||||||
@@ -140,17 +141,11 @@ public static class SchedulePlanner
|
|||||||
PlanningSlot slot,
|
PlanningSlot slot,
|
||||||
DateTimeOffset cursor,
|
DateTimeOffset cursor,
|
||||||
DateTimeOffset? nextAnchor,
|
DateTimeOffset? nextAnchor,
|
||||||
PlanningInput input,
|
|
||||||
IRandomSource random,
|
|
||||||
List<PlannedItem> items,
|
|
||||||
List<PlanningCursorUpdate> cursors,
|
|
||||||
List<PlanningWarning> warnings,
|
|
||||||
PlanTrace trace,
|
PlanTrace trace,
|
||||||
JunctionHistory junctions,
|
PlanningRun run
|
||||||
ref Guid? previousShowId
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var limit = Min(input.HorizonEndUtc, nextAnchor);
|
var limit = Min(run.Input.HorizonEndUtc, nextAnchor);
|
||||||
|
|
||||||
switch (slot.SlotKind)
|
switch (slot.SlotKind)
|
||||||
{
|
{
|
||||||
@@ -159,30 +154,17 @@ public static class SchedulePlanner
|
|||||||
return FillWithFallback(
|
return FillWithFallback(
|
||||||
cursor,
|
cursor,
|
||||||
Min(slot.TargetEndUtc, limit),
|
Min(slot.TargetEndUtc, limit),
|
||||||
input,
|
run,
|
||||||
items,
|
|
||||||
slot.SlotId,
|
slot.SlotId,
|
||||||
PlannedItemKind.SignOff,
|
PlannedItemKind.SignOff,
|
||||||
trace
|
trace
|
||||||
);
|
);
|
||||||
|
|
||||||
case SlotKind.Repeat:
|
case SlotKind.Repeat:
|
||||||
return FillRepeat(slot, cursor, limit, input, items, warnings, trace);
|
return FillRepeat(slot, cursor, limit, trace, run);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FillContent(
|
return FillContent(slot, cursor, limit, trace, run);
|
||||||
slot,
|
|
||||||
cursor,
|
|
||||||
limit,
|
|
||||||
input,
|
|
||||||
random,
|
|
||||||
items,
|
|
||||||
cursors,
|
|
||||||
warnings,
|
|
||||||
trace,
|
|
||||||
junctions,
|
|
||||||
ref previousShowId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,29 +172,21 @@ public static class SchedulePlanner
|
|||||||
PlanningSlot slot,
|
PlanningSlot slot,
|
||||||
DateTimeOffset cursor,
|
DateTimeOffset cursor,
|
||||||
DateTimeOffset limit,
|
DateTimeOffset limit,
|
||||||
PlanningInput input,
|
PlanTrace trace,
|
||||||
List<PlannedItem> items,
|
PlanningRun run
|
||||||
List<PlanningWarning> warnings,
|
|
||||||
PlanTrace trace
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var units = slot.RepeatUnits ?? [];
|
var units = slot.RepeatUnits ?? [];
|
||||||
if (units.Count == 0)
|
if (units.Count == 0)
|
||||||
{
|
{
|
||||||
warnings.Add(
|
run.Warnings.Add(
|
||||||
new PlanningWarning(
|
new PlanningWarning(
|
||||||
PlanningWarningKind.RepeatSourceEmpty,
|
PlanningWarningKind.RepeatSourceEmpty,
|
||||||
slot.SlotId,
|
slot.SlotId,
|
||||||
"В источнике повтора ничего не нашлось — слот закрыт фоном."
|
"В источнике повтора ничего не нашлось — слот закрыт фоном."
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return FillWithFallback(
|
return FillWithFallback(cursor, Min(slot.TargetEndUtc, limit), run, slot.SlotId);
|
||||||
cursor,
|
|
||||||
Min(slot.TargetEndUtc, limit),
|
|
||||||
input,
|
|
||||||
items,
|
|
||||||
slot.SlotId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var slotEnd = Min(slot.TargetEndUtc, limit);
|
var slotEnd = Min(slot.TargetEndUtc, limit);
|
||||||
@@ -220,7 +194,7 @@ public static class SchedulePlanner
|
|||||||
{
|
{
|
||||||
if (cursor + unit.Duration > slotEnd)
|
if (cursor + unit.Duration > slotEnd)
|
||||||
break;
|
break;
|
||||||
items.Add(Program(unit, cursor, slot.SlotId, trace));
|
run.Items.Add(Program(unit, cursor, slot.SlotId, trace));
|
||||||
cursor += unit.Duration;
|
cursor += unit.Duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,31 +205,19 @@ public static class SchedulePlanner
|
|||||||
PlanningSlot slot,
|
PlanningSlot slot,
|
||||||
DateTimeOffset cursor,
|
DateTimeOffset cursor,
|
||||||
DateTimeOffset limit,
|
DateTimeOffset limit,
|
||||||
PlanningInput input,
|
|
||||||
IRandomSource random,
|
|
||||||
List<PlannedItem> items,
|
|
||||||
List<PlanningCursorUpdate> cursors,
|
|
||||||
List<PlanningWarning> warnings,
|
|
||||||
PlanTrace trace,
|
PlanTrace trace,
|
||||||
JunctionHistory junctions,
|
PlanningRun run
|
||||||
ref Guid? previousShowId
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var pick = ElementSelector.Select(slot, cursor, random);
|
var pick = ElementSelector.Select(slot, cursor, run.Random);
|
||||||
if (pick is null)
|
if (pick is null)
|
||||||
{
|
{
|
||||||
warnings.Add(NoCandidatesWarning(slot));
|
run.Warnings.Add(NoCandidatesWarning(slot));
|
||||||
return FillWithFallback(
|
return FillWithFallback(cursor, Min(slot.TargetEndUtc, limit), run, slot.SlotId);
|
||||||
cursor,
|
|
||||||
Min(slot.TargetEndUtc, limit),
|
|
||||||
input,
|
|
||||||
items,
|
|
||||||
slot.SlotId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pick.CooldownExhausted)
|
if (pick.CooldownExhausted)
|
||||||
warnings.Add(
|
run.Warnings.Add(
|
||||||
new PlanningWarning(
|
new PlanningWarning(
|
||||||
PlanningWarningKind.CooldownExhausted,
|
PlanningWarningKind.CooldownExhausted,
|
||||||
slot.SlotId,
|
slot.SlotId,
|
||||||
@@ -283,8 +245,8 @@ public static class SchedulePlanner
|
|||||||
&& !FitsEntirely(element, unitIndex, cursor, budgetEnd)
|
&& !FitsEntirely(element, unitIndex, cursor, budgetEnd)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
cursors.Add(CursorUpdate(slot, element, unitIndex));
|
run.Cursors.Add(CursorUpdate(slot, element, unitIndex));
|
||||||
return FillWithFallback(cursor, budgetEnd, input, items, slot.SlotId);
|
return FillWithFallback(cursor, budgetEnd, run, slot.SlotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (unitIndex < element.Units.Count && cursor < limit)
|
while (unitIndex < element.Units.Count && cursor < limit)
|
||||||
@@ -297,12 +259,14 @@ public static class SchedulePlanner
|
|||||||
slot.JunctionBetween,
|
slot.JunctionBetween,
|
||||||
cursor,
|
cursor,
|
||||||
limit,
|
limit,
|
||||||
slot.SlotId,
|
new JunctionPlacement(
|
||||||
previousShowId,
|
slot.SlotId,
|
||||||
unit.ShowId,
|
run.PreviousShowId,
|
||||||
elementChanged: previousShowId != unit.ShowId,
|
unit.ShowId,
|
||||||
junctions,
|
ElementChanged: run.PreviousShowId != unit.ShowId
|
||||||
items,
|
),
|
||||||
|
run.Junctions,
|
||||||
|
run.Items,
|
||||||
slotTrace
|
slotTrace
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -313,12 +277,12 @@ public static class SchedulePlanner
|
|||||||
if (!WithinBudget(slot, placed, accumulated, cursor, unit, budgetEnd))
|
if (!WithinBudget(slot, placed, accumulated, cursor, unit, budgetEnd))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
items.Add(Program(unit, cursor, slot.SlotId, slotTrace, CollectionOf(element)));
|
run.Items.Add(Program(unit, cursor, slot.SlotId, slotTrace, CollectionOf(element)));
|
||||||
cursor += unit.Duration;
|
cursor += unit.Duration;
|
||||||
accumulated += unit.Duration;
|
accumulated += unit.Duration;
|
||||||
unitIndex++;
|
unitIndex++;
|
||||||
placed++;
|
placed++;
|
||||||
previousShowId = unit.ShowId;
|
run.PreviousShowId = unit.ShowId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Врезки в конце блока ставятся до добора фоном: иначе реклама оказалась бы после заполнителя.
|
// Врезки в конце блока ставятся до добора фоном: иначе реклама оказалась бы после заполнителя.
|
||||||
@@ -327,20 +291,22 @@ public static class SchedulePlanner
|
|||||||
slot.JunctionAfter,
|
slot.JunctionAfter,
|
||||||
cursor,
|
cursor,
|
||||||
limit,
|
limit,
|
||||||
slot.SlotId,
|
new JunctionPlacement(
|
||||||
previousShowId,
|
slot.SlotId,
|
||||||
null,
|
run.PreviousShowId,
|
||||||
elementChanged: true,
|
null,
|
||||||
junctions,
|
ElementChanged: true
|
||||||
items,
|
),
|
||||||
|
run.Junctions,
|
||||||
|
run.Items,
|
||||||
slotTrace
|
slotTrace
|
||||||
);
|
);
|
||||||
|
|
||||||
cursors.Add(CursorUpdate(slot, element, unitIndex));
|
run.Cursors.Add(CursorUpdate(slot, element, unitIndex));
|
||||||
|
|
||||||
// Недобор до целевого конца закрываем фоном — только для слотов, чей бюджет привязан ко времени.
|
// Недобор до целевого конца закрываем фоном — только для слотов, чей бюджет привязан ко времени.
|
||||||
if (slot.BlockMode == SlotBlockMode.FillSlot && cursor < budgetEnd)
|
if (slot.BlockMode == SlotBlockMode.FillSlot && cursor < budgetEnd)
|
||||||
cursor = FillWithFallback(cursor, budgetEnd, input, items, slot.SlotId);
|
cursor = FillWithFallback(cursor, budgetEnd, run, slot.SlotId);
|
||||||
|
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
@@ -416,14 +382,14 @@ public static class SchedulePlanner
|
|||||||
private static DateTimeOffset FillWithFallback(
|
private static DateTimeOffset FillWithFallback(
|
||||||
DateTimeOffset from,
|
DateTimeOffset from,
|
||||||
DateTimeOffset until,
|
DateTimeOffset until,
|
||||||
PlanningInput input,
|
PlanningRun run,
|
||||||
List<PlannedItem> items,
|
|
||||||
Guid? slotId,
|
Guid? slotId,
|
||||||
PlannedItemKind kind = PlannedItemKind.Fallback,
|
PlannedItemKind kind = PlannedItemKind.Fallback,
|
||||||
PlanTrace? trace = null
|
PlanTrace? trace = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (input.FallbackUnits.Count == 0 || until <= from)
|
var fallback = run.Input.FallbackUnits;
|
||||||
|
if (fallback.Count == 0 || until <= from)
|
||||||
return from;
|
return from;
|
||||||
|
|
||||||
var cursor = from;
|
var cursor = from;
|
||||||
@@ -432,13 +398,13 @@ public static class SchedulePlanner
|
|||||||
|
|
||||||
while (cursor < until && guard++ < IterationBackstop)
|
while (cursor < until && guard++ < IterationBackstop)
|
||||||
{
|
{
|
||||||
var unit = input.FallbackUnits[index % input.FallbackUnits.Count];
|
var unit = fallback[index % fallback.Count];
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
if (unit.Duration <= TimeSpan.Zero || cursor + unit.Duration > until)
|
if (unit.Duration <= TimeSpan.Zero || cursor + unit.Duration > until)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
items.Add(
|
run.Items.Add(
|
||||||
new PlannedItem(
|
new PlannedItem(
|
||||||
unit.MediaAssetId,
|
unit.MediaAssetId,
|
||||||
cursor,
|
cursor,
|
||||||
|
|||||||
Reference in New Issue
Block a user