/// <summary> /// Prüft, ob ein weiterer Zeitpunkt vorliegt. /// </summary> public void MoveNext() { // As long as necessary for (; ;) { // Ask base if (m_Scan != null) { if (!m_Scan.MoveNext()) { m_Scan = null; } } // Report if (m_Scan == null) { break; } // Load what base gives us m_Current = m_Scan.Current; // Load to check - normally we will not change it var planned = m_Current.Planned; // Find exception to apply PlanException exception; if (m_Exceptions.TryGetValue(planned.Start.ToLocalTime().Date, out exception)) { #if !SILVERLIGHT // Report if (RecordingScheduler.SchedulerTrace.TraceVerbose) { Trace.TraceInformation(Properties.SchedulerResources.Trace_Exception, planned.Start, planned.Duration, exception.StartDelta, exception.DurationDelta); } #endif // Change planned = new PlannedTime { Duration = planned.Duration + exception.DurationDelta, Start = planned.Start + exception.StartDelta, }; // Write back m_Current.Planned = planned; } // Found it if (planned.End > m_MinTime) { if (planned.Duration.TotalSeconds > 0) { break; } } } }
/// <summary> /// Erzeugt eine neue Beschreibung. /// </summary> /// <param name="map">Die zugehörige Verwaltung einer Ressource.</param> /// <param name="source">Die Quelle, die verwendet werden soll.</param> /// <param name="plan">Die ursprüngliche Planung.</param> /// <param name="index">Der erste Eintrag in der Verwaltung, der belegt werden soll.</param> public AllocationPlan(AllocationMap map, IScheduleSource source, SuggestedPlannedTime plan, int index) { // Remember m_allocationIndex = index; m_source = source; m_plan = plan; m_map = map; }
/// <summary> /// Fordert eine Entschlüsselung an. /// </summary> /// <param name="source">Die zu verwendende Quelle.</param> /// <param name="timeHolder">Der Zeitraum, für den die Planung stattfinden soll.</param> /// <returns>Gesetzt, wenn eine Zuordnung überhaupt möglich ist. Gemeldet wird dann der Zeitversatz der Zuordnung.</returns> public AllocationPlan PrepareAllocation(IScheduleSource source, SuggestedPlannedTime timeHolder) { // Extract the time to use for plannung var time = timeHolder.Planned; // As long as needed for (var scanStart = 0; ;) { // See if there is at least one allocation area available var allocationIndex = m_Allocations.FindIndex(scanStart, a => a.Overlaps(time)); if (allocationIndex < 0) { return(null); } // On the start skip all having no recording left while (!m_Allocations[allocationIndex].CanAllocate(source)) { if (++allocationIndex == m_Allocations.Count) { return(null); } else if (!m_Allocations[allocationIndex].Overlaps(time)) { return(null); } } // Initial index - where we start the allocation var startIndex = allocationIndex; // Now make sure that we can record to the end - allocation maps from the beginning to the end of all times while (++allocationIndex < m_Allocations.Count) { if (!m_Allocations[allocationIndex].Overlaps(time)) { break; } else if (!m_Allocations[allocationIndex].CanAllocate(source)) { // Prepare to rescan scanStart = allocationIndex + 1; // Do not end startIndex = -1; // Done with loop break; } } // Response if (startIndex >= 0) { return(new AllocationPlan(this, source, timeHolder, startIndex)); } } }
/// <summary> /// Erzeugt eine Planungsinstanz basierend auf den aktuell bekannten Aufzeichnungen. /// </summary> /// <returns>Der gewünschte Plan.</returns> private SchedulePlan CreateSchedulePlan() { // Create var plan = new SchedulePlan(m_Resources); // Report var resources = plan.Resources.ToDictionary(r => r.Resource, ReferenceComparer <IScheduleResource> .Default); // Merge in each schedule foreach (var recording in m_Recordings) { // Attach to the resource var resource = recording.Resource; var resourcePlan = resources[resource]; // Check mode if (recording.Source == null) { // Try to reserve if (!resourcePlan.Reserve(recording.Time.End)) { return(null); } // Next continue; } // Attach to the timing SuggestedPlannedTime planned = recording.Time; // Try add if (!resourcePlan.Add(recording, planned, DateTime.MinValue)) { return(null); } // Must not start late if (planned.Planned.Start != recording.Time.Start) { return(null); } } // Report return(plan); }
/// <summary> /// Prüft, ob eine Aufzeichnung ergänzt werden kann. /// </summary> /// <param name="recording">Die neue Aufzeichnung.</param> /// <param name="time">Der Zeitraum der neuen Aufzeichnung.</param> /// <param name="minTime">Die Aufzeichnung darf auf keinen Fall vor diesem Zeitpunkt beginnen.</param> /// <returns>Gesetzt, wenn eine Aufzeichnung möglich war.</returns> /// <exception cref="ArgumentNullException">Es wurde keine Aufzeichnung übergeben.</exception> /// <exception cref="ArgumentOutOfRangeException">Die Startzeit liegt vor der Aktivierung des Gerätes.</exception> public bool Add(IRecordingDefinition recording, SuggestedPlannedTime time, DateTime minTime) { // Validate if (recording == null) { throw new ArgumentNullException("recording"); } // If the current resource can see the source we can do nothing at all var resource = Resource; if (!resource.CanAccess(recording.Source)) { return(false); } // If the recording is bound to dedicated sources but not use we should not process var allowedResources = recording.Resources; if (allowedResources != null) { if (allowedResources.Length > 0) { if (!allowedResources.Any(r => ReferenceEquals(r, resource))) { return(false); } } } // See if we have to cut it off and load the corresponding end of the recording var initialPlan = time.Planned; // Clip to minimum allowed if (time.Planned.Start < minTime) { // Not possible at all - should never ever be requested but better be safe if (time.Planned.End <= minTime) { return(false); } // Correct the parameters - end is calculated from start and duration time.Planned.Duration = time.Planned.End - minTime; time.Planned.Start = minTime; } // See if device could at least receive var sourceAllocation = Allocations.PrepareAllocation(recording.Source, time); if (sourceAllocation == null) { return(false); } // Add it - will clip the time accordingly sourceAllocation.Allocate(); // Time to check for encryption - technically it should not be possible to have a recording on a source which is for some time encypted and for some other time not but better be safe if (recording.Source.IsEncrypted) { // What to check for var counters = DecryptionCounters.Select(i => SchedulePlan.DecryptionCounters[i]).ToArray(); // Check all foreach (var counter in counters) { if (counter.PrepareAllocation(recording.Source, time) == null) { return(false); } } // Now reserve all - this may manipulate the time slice for the recording as well foreach (var counter in counters) { // Allocate again - we may only call the allocate immediatly after preparing var allocation = counter.PrepareAllocation(recording.Source, time); if (allocation == null) { throw new InvalidOperationException("PrepareAllocation"); } // Process allocation.Allocate(); } } // Remember recording m_Recordings.Add(new _RecordingItem(recording, time.Planned, time.Planned.Start > initialPlan.Start)); // Get cut time var delta = initialPlan.Duration - time.Planned.Duration; // Adjust cut time - this will be taken in account when checking weights to get the best plan TotalCut += delta; // Count failures if (delta.Ticks > 0) { CutRecordings += 1; } // We did it return(true); }
/// <summary> /// Prüft, ob ein weiterer Zeitpunkt vorliegt. /// </summary> public void MoveNext() { // As long as necessary for (; ; ) { // Ask base if (m_Scan != null) if (!m_Scan.MoveNext()) m_Scan = null; // Report if (m_Scan == null) break; // Load what base gives us m_Current = m_Scan.Current; // Load to check - normally we will not change it var planned = m_Current.Planned; // Find exception to apply PlanException exception; if (m_Exceptions.TryGetValue( planned.Start.ToLocalTime().Date, out exception )) { #if !SILVERLIGHT // Report if (RecordingScheduler.SchedulerTrace.TraceVerbose) Trace.TraceInformation( Properties.SchedulerResources.Trace_Exception, planned.Start, planned.Duration, exception.StartDelta, exception.DurationDelta ); #endif // Change planned = new PlannedTime { Duration = planned.Duration + exception.DurationDelta, Start = planned.Start + exception.StartDelta, }; // Write back m_Current.Planned = planned; } // Found it if (planned.End > m_MinTime) if (planned.Duration.TotalSeconds > 0) break; } }
/// <summary> /// Meldet alle Aufzeichnungen ab einem bestimmten Zeitpunkt. /// </summary> /// <param name="minTime">Alle Aufzeichnungen, die vor diesem Zeitpunkt enden, werden /// nicht berücksichtigt. Die Angabe erfolgt in UTC / GMT Notation.</param> /// <returns>Alle Aufzeichnungen.</returns> private IEnumerable <ScheduleInfo> GetSchedulesForRecordings(DateTime minTime) { // All items to process var items = new _ScheduleList(m_PlanItems.Where(r => !m_ForbiddenDefinitions.Contains(r.Definition.UniqueIdentifier)), minTime); // Create the plans to extend var plans = new List <SchedulePlan> { m_PlanCreator() }; var steps = 0; // As long as necessary while (items.MoveNext()) { // Load the item var candidate = items.Current; var candiateTime = candidate.Current; var planned = candiateTime.Planned; #if !SILVERLIGHT // Report if (SchedulerTrace.TraceVerbose) { Trace.TraceInformation(Properties.SchedulerResources.Trace_Candidate, candidate.Definition.Source, planned.Start, planned.Duration); } #endif // Get the current end of plans and see if we can dump the state - this may increase performance var planStart = plans.SelectMany(p => p.Resources).Min(r => (DateTime?)r.PlanStart); var planEnd = plans.SelectMany(p => p.Resources).Max(r => (DateTime?)r.PlanEnd); var canEndPlan = planEnd.HasValue && (planEnd.Value != DateTime.MinValue) && (planned.Start >= planEnd.Value); var mustEndPlan = planStart.HasValue && (planStart.Value != DateTime.MaxValue) && planEnd.HasValue && (planEnd.Value != DateTime.MinValue) && ((planEnd.Value - planStart.Value).TotalDays > 2); // Count this effort if ((++steps > MaximumRecordingsInPlan) || canEndPlan || mustEndPlan || (plans.Count > MaximumAlternativesInPlan)) { // Find best plan var best = SchedulePlan.FindBest(plans, m_comparer); // Report foreach (var info in Dump(best)) { yield return(info); } // Reset plans.Clear(); plans.Add(best.Restart(planned.Start)); // Reset steps = 1; } #if !SILVERLIGHT // Report if (SchedulerTrace.TraceVerbose) { Trace.TraceInformation(Properties.SchedulerResources.Trace_PlanCount, plans.Count); } #endif // All plans to extend var allPlans = plans.ToArray(); // Discard list plans.Clear(); // Iterate over all plans and try to add the current candidate - in worst case this will multiply possible plans by the number of resources available foreach (var plan in allPlans) { for (int i = plan.Resources.Length; i-- > 0;) { // Clone of plan must be recreated for each resource because test is allowed to modify it var clone = plan.Clone(); // Remember the time we tried - implicit cast is important, do NOT use var SuggestedPlannedTime plannedTime = planned; // See if resource can handle this if (clone.Resources[i].Add(candidate.Definition, plannedTime, minTime)) { plans.Add(clone); } } } // Must reset if the recording could not be scheduled at all if (plans.Count < 1) { // Report yield return(new ScheduleInfo(candidate.Definition, null, planned, false)); // Restore the original plans since we did nothing at all plans.AddRange(allPlans); } } // Send all we found foreach (var info in Dump(SchedulePlan.FindBest(plans, m_comparer))) { yield return(info); } }