/// <summary> /// Erstellt eine neue Beschreibung aus dem Aufzeichnungsplan. /// </summary> /// <param name="plan">Die Planung der Aufzeichnung.</param> /// <param name="context">Die aktuelle Analyseumgebung.</param> /// <param name="server">Der zugehörige Dienst.</param> /// <returns>Die gewünschte Beschreibung.</returns> public static PlanCurrent Create(IScheduleInformation plan, PlanContext context, VCRServer server) { // Attach to the definition var definition = (IScheduleDefinition <VCRSchedule>)plan.Definition; var job = context.TryFindJob(definition.UniqueIdentifier); var schedule = (job == null) ? null : job[definition.UniqueIdentifier]; var source = (schedule == null) ? null : (schedule.Source ?? job.Source); // Create var planned = new PlanCurrent { Identifier = (schedule == null) ? null : ServerRuntime.GetUniqueWebId(job, schedule), ProfileName = plan.Resource.Name, Duration = plan.Time.Duration, StartTime = plan.Time.Start, IsLate = plan.StartsLate, SizeHint = string.Empty, Name = definition.Name, m_source = source, Files = _NoFiles, Index = -1, }; // Finish planned.Complete(server); // Report return(planned); }
/// <summary> /// Fordert zum Beenden einer Aufzeichnung oder Aufgabe aus. /// </summary> /// <param name="item">Alle notwendigen Informationen zur Aufzeichnung.</param> /// <param name="planner">Die zugehörige Aufzeichnungsplanung.</param> public void Stop(IScheduleInformation item, RecordingPlanner planner) { // Report Debug.WriteLine(string.Format("{0} stop {1}", m_planTime.ToLocalTime(), item.Definition.Name)); // Readout var definition = item.Definition; var resource = item.Resource; // Forward planner.Stop(definition.UniqueIdentifier); // Check mode if (definition is ProgramGuideTask) { m_lastRunGuide[resource.Name] = m_planTime; } else if (definition is SourceListTask) { m_lastRunScan[resource.Name] = m_planTime; } else { VCRConfiguration.Current.HasRecordedSomething = true; } }
/// <summary> /// Fordert zum Starten einer Aufzeichnung oder Aufgabe auf. /// </summary> /// <param name="item">Die Beschreibung der Aufgabe.</param> /// <param name="planner">Die zugehörige Aufzeichnungsplanung.</param> /// <param name="context">Zusatzinformationen zur Aufzeichnungsplanung.</param> public void Start(IScheduleInformation item, RecordingPlanner planner, PlanContext context) { // Report Debug.WriteLine(string.Format("{0} start{3} on {1}: {2}", m_planTime.ToLocalTime(), item.Resource, item.Definition.Name, item.StartsLate ? " late" : string.Empty)); // Forward planner.Start(item); }
/// <summary> /// Erstellt eine neue Beschreibung. /// </summary> /// <param name="original">Die originalen Informationen.</param> public ScheduleInformation( IScheduleInformation original ) { // Validate if (original == null) throw new ArgumentNullException( nameof( original ) ); // Remember RealTime = original.Time; Schedule = original; }
/// <summary> /// Erstellt eine neue Beschreibung. /// </summary> /// <param name="original">Die originalen Informationen.</param> public ScheduleInformation(IScheduleInformation original) { // Validate if (original == null) { throw new ArgumentNullException(nameof(original)); } // Remember RealTime = original.Time; Schedule = original; }
/// <summary> /// Bestätigt den Abschluss einer Operation und die Bereitschaft, die nächste Operation /// zu starten. /// </summary> /// <param name="scheduleIdentifier">Die eindeutige Kennung der Operation.</param> /// <param name="isStart">Gesetzt, wenn es sich um einen Startbefehl handelt.</param> internal void ConfirmOperation(Guid scheduleIdentifier, bool isStart) { // Protect and check var planner = m_planner; if (planner == null) { return; } // Make sure that we synchronize with the planning thread lock (planner) if (m_pendingSchedule == null) { // Report VCRServer.Log(LoggingLevel.Errors, "There is no outstanding asynchronous Recording Request for Schedule '{0}'", scheduleIdentifier); } else if (m_pendingSchedule.Definition.UniqueIdentifier != scheduleIdentifier) { // Report VCRServer.Log(LoggingLevel.Errors, "Confirmed asynchronous Recording Request for Schedule '{0}' but waiting for '{1}'", scheduleIdentifier, m_pendingSchedule.Definition.UniqueIdentifier); } else { // Report VCRServer.Log(LoggingLevel.Schedules, "Confirmed asynchronous Recording Request for Schedule '{0}'", scheduleIdentifier); // Check mode if (isStart != m_pendingStart) { VCRServer.Log(LoggingLevel.Errors, "Recording Request confirmed wrong Type of Operation"); } // Finish if (m_pendingStart) { planner.Start(m_pendingSchedule); } else { planner.Stop(scheduleIdentifier); } // Reset m_pendingSchedule = null; } // See what to do next BeginNewPlan(); }
/// <summary> /// Meldet, dass eine Aufzeichnung nun beendet werden kann. /// </summary> /// <param name="item">Die betroffene Aufzeichnung.</param> /// <param name="planner">Die Planungsinstanz.</param> void IRecordingPlannerSite.Stop(IScheduleInformation item, RecordingPlanner planner) { // Report VCRServer.Log(LoggingLevel.Schedules, "Done recording '{0}'", item.Definition.Name); // Locate the profile - if we don't find it we are in big trouble! if (!m_profiles.TryGetValue(item.Resource.Name, out ProfileState profile)) { return; } // Mark as pending m_pendingSchedule = item; m_pendingStart = false; // Forward request to profile manager m_pendingActions += () => profile.EndRecording(item.Definition.UniqueIdentifier); }
/// <summary> /// Überträgt eine geplante Ausführung. /// </summary> /// <param name="manager">Die zu erweiternde Schnittstelle.</param> /// <param name="schedule">Die Planungsinformationen zur Aufzeichnung.</param> /// <returns>Gesetzt, wenn ein Start möglich war.</returns> /// <exception cref="NullReferenceException">Es wurde keine Schnittstelle angegeben.</exception> /// <exception cref="ArgumentNullException">Es wurde keine Planung angegeben.</exception> public static bool Start(this IResourceManager manager, IScheduleInformation schedule) { // Validate if (manager == null) { throw new NullReferenceException(); } if (schedule == null) { throw new ArgumentNullException("schedule"); } // Load var definition = schedule.Definition; var recording = definition as IRecordingDefinition; // Forward return(manager.Start(schedule.Resource, (recording == null) ? null : recording.Source, definition.UniqueIdentifier, definition.Name, schedule.Time.Start, schedule.Time.End)); }
/// <summary> /// Startet eine Aufzeichnung oder eine Aufgabe. /// </summary> /// <param name="item">Die Beschreibung der Aufgabe.</param> /// <returns>Gesetzt, wenn der Vorgang erfolgreich war.</returns> public bool Start(IScheduleInformation item) { // Validate if (item is ScheduleInformation) { VCRServer.LogError(Properties.Resources.BadScheduleInformation, item.Definition.UniqueIdentifier, item.Definition.Name); } // Try start if (!m_manager.Start(item)) { return(false); } // Remember m_started.Add(item.Definition.UniqueIdentifier, new ScheduleInformation(item)); // Did it return(true); }
/// <summary> /// Meldet, dass eine Aufzeichnung nun beginnen sollte. /// </summary> /// <param name="item">Die zu startende Aufzeichnung.</param> /// <param name="planner">Die Planungsinstanz.</param> /// <param name="context">Zusatzinformationen zur Aufzeichnungsplanung.</param> void IRecordingPlannerSite.Start(IScheduleInformation item, RecordingPlanner planner, PlanContext context) { // We are no longer active - simulate start and do nothing if (!m_plannerActive) { // Make planner believe we did it planner.Start(item); // Make sure that we wake up after the grace period if (PowerManager.IsSuspended && VCRConfiguration.Current.SuppressDelayAfterForcedHibernation) { Tools.ExtendedLogging("Hibernation Delay is disabled and can not be enforced"); } else { m_timer.SecondsToWait = VCRConfiguration.Current.DelayAfterForcedHibernation.TotalSeconds; } // Done return; } // Report VCRServer.Log(LoggingLevel.Schedules, "Start recording '{0}'", item.Definition.Name); // Locate the profile - if we don't find it we are in big trouble! ProfileState profile; if (!m_profiles.TryGetValue(item.Resource.Name, out profile)) { return; } // Mark as pending m_pendingSchedule = item; m_pendingStart = true; // Create the recording var recording = VCRRecordingInfo.Create(item, context); // Check for EPG var guideUpdate = item.Definition as ProgramGuideTask; if (guideUpdate != null) { // Start a new guide collector m_pendingActions += ProgramGuideProxy.Create(profile, recording).Start; } else { // Check for PSI var sourceUpdate = item.Definition as SourceListTask; if (sourceUpdate != null) { // Start a new update m_pendingActions += SourceScanProxy.Create(profile, recording).Start; } else { // Start a regular recording - profile will decide if to join an existing recording m_pendingActions += () => profile.StartRecording(recording); } } }
/// <summary> /// Aktualisiert ständig die Planung. /// </summary> private void PlanThread() { // Always loop while (m_planThread != null) { // See if a new plan is requested lock (m_newPlanSync) while (!m_newPlan) { if (m_planThread == null) { return; } else { Monitor.Wait(m_newPlanSync); } } // At least we accepted the request m_newPlan = false; // See if we still have a planner var planner = m_planner; if (planner == null) { break; } // Just take a look what to do next using (PowerManager.StartForbidHibernation()) try { // Reset start actions m_pendingActions = null; // Protect planning. lock (planner) { // Report Tools.ExtendedLogging("Woke up for Plan Calculation at {0}", DateTime.Now); // Retest if (m_planThread == null) { break; } // See if we are allowed to take the next step in plan - we schedule only one activity at a time if (m_pendingSchedule == null) { // Release our lock AllowHibernation(); // Reset timer if (PowerManager.IsSuspended && VCRConfiguration.Current.SuppressDelayAfterForcedHibernation) { Tools.ExtendedLogging("VCR.NET is suspending - not updating Timer"); } else { m_timer.SecondsToWait = 0; } // Analyse plan planner.DispatchNextActivity(DateTime.UtcNow); // If we are shutting down better forget anything we did in the previous step - only timer setting matters! if (!m_plannerActive) { // Reset to initial state m_pendingSchedule = null; m_pendingActions = null; m_pendingStart = false; // And forget all allocations planner.Reset(); // Just in case allow hibernation again AllowHibernation(); } } } // Run start and stop actions outside the planning lock (to avoid deadlocks) but inside the hibernation protection (to forbid hibernation) var toStart = m_pendingActions; if (toStart != null) { toStart(); } } catch (Exception e) { // Report and ignore - we do not expect any error to occur VCRServer.Log(e); } // New plan is now available - beside termination this will do nothing at all but briefly aquiring an idle lock lock (m_planAvailableSync) Monitor.PulseAll(m_planAvailableSync); } }
/// <summary> /// Erzeugt eine neue Beschreibung. /// </summary> /// <param name="definition">Die Daten zur Aufzeichnung oder Aufgabe.</param> internal StartActivity(IScheduleInformation definition) { // Remember Recording = definition; }
/// <summary> /// Erstellt eine neue Beschreibung aus dem Aufzeichnungsplan. /// </summary> /// <param name="plan">Die Planung der Aufzeichnung.</param> /// <param name="context">Die aktuelle Analyseumgebung.</param> /// <param name="server">Der zugehörige Dienst.</param> /// <returns>Die gewünschte Beschreibung.</returns> public static PlanCurrent Create( IScheduleInformation plan, PlanContext context, VCRServer server ) { // Attach to the definition var definition = (IScheduleDefinition<VCRSchedule>) plan.Definition; var job = context.TryFindJob( definition.UniqueIdentifier ); var schedule = (job == null) ? null : job[definition.UniqueIdentifier]; var source = (schedule == null) ? null : (schedule.Source ?? job.Source); // Create var planned = new PlanCurrent { Identifier = (schedule == null) ? null : ServerRuntime.GetUniqueWebId( job, schedule ), ProfileName = plan.Resource.Name, Duration = plan.Time.Duration, StartTime = plan.Time.Start, IsLate = plan.StartsLate, SizeHint = string.Empty, Name = definition.Name, m_source = source, Files = _NoFiles, Index = -1, }; // Finish planned.Complete( server ); // Report return planned; }
/// <summary> /// Fordert zum Starten einer Aufzeichnung oder Aufgabe auf. /// </summary> /// <param name="item">Die Beschreibung der Aufgabe.</param> /// <param name="planner">Die zugehörige Aufzeichnungsplanung.</param> /// <param name="context">Zusatzinformationen zur Aufzeichnungsplanung.</param> public void Start( IScheduleInformation item, RecordingPlanner planner, PlanContext context ) { // Report Debug.WriteLine( string.Format( "{0} start{3} on {1}: {2}", m_planTime.ToLocalTime(), item.Resource, item.Definition.Name, item.StartsLate ? " late" : string.Empty ) ); // Forward planner.Start( item ); }
/// <summary> /// Fordert zum Beenden einer Aufzeichnung oder Aufgabe aus. /// </summary> /// <param name="item">Alle notwendigen Informationen zur Aufzeichnung.</param> /// <param name="planner">Die zugehörige Aufzeichnungsplanung.</param> public void Stop( IScheduleInformation item, RecordingPlanner planner ) { // Report Debug.WriteLine( string.Format( "{0} stop {1}", m_planTime.ToLocalTime(), item.Definition.Name ) ); // Readout var definition = item.Definition; var resource = item.Resource; // Forward planner.Stop( definition.UniqueIdentifier ); // Check mode if (definition is ProgramGuideTask) m_lastRunGuide[resource.Name] = m_planTime; else if (definition is SourceListTask) m_lastRunScan[resource.Name] = m_planTime; else VCRConfiguration.Current.HasRecordedSomething = true; }
/// <summary> /// Erzeugt eine neue Beschreibung. /// </summary> /// <param name="definition">Die Daten zur Aufzeichnung oder Aufgabe.</param> internal StartActivity( IScheduleInformation definition ) { // Remember Recording = definition; }
/// <summary> /// Erstellt einen neuen Eintrag. /// </summary> /// <param name="schedule">Die zugehörige Beschreibung der geplanten Aktivität.</param> /// <param name="context">Die Abbildung auf die Aufträge.</param> /// <param name="profiles">Die Verwaltung der Geräteprofile.</param> /// <returns>Die angeforderte Repräsentation.</returns> public static PlanActivity Create(IScheduleInformation schedule, PlanContext context, ProfileStateCollection profiles) { // Request context information var definition = schedule.Definition; var runningInfo = context.GetRunState(definition.UniqueIdentifier); var isAllocation = definition is IResourceAllocationInformation; // Maybe it's an resource allocation if (isAllocation) { if (runningInfo != null) { definition = runningInfo.Schedule.Definition; } else { return(null); } } // Create initial entry var time = schedule.Time; var start = time.Start; var end = time.End; var activity = new PlanActivity { IsHidden = (schedule.Resource == null), IsLate = schedule.StartsLate, }; // May need some correction if (runningInfo != null) { if (end == runningInfo.Schedule.Time.End) { // Only report the allocation itself if (!isAllocation) { return(null); } // Reload the real start and times - just in case someone manipulated start = runningInfo.Schedule.Time.Start; end = runningInfo.RealTime.End; // Never report as late - actually since we have some spin up time most of the time the recording is late activity.IsLate = false; } } // Get the beautified range start = PlanCurrent.RoundToSecond(start); end = PlanCurrent.RoundToSecond(end); // Set times activity.Duration = end - start; activity.StartTime = start; // Set name if (definition != null) { activity.FullName = definition.Name; } // Set resource var resource = schedule.Resource; if (resource != null) { activity.Device = resource.Name; } // Schedule to process VCRSchedule vcrSchedule = null; VCRJob vcrJob = null; // Analyse definition var scheduleDefinition = definition as IScheduleDefinition <VCRSchedule>; if (scheduleDefinition != null) { // Regular plan vcrSchedule = scheduleDefinition.Context; vcrJob = context.TryFindJob(vcrSchedule); } // Process if we found one if (vcrSchedule != null) { // See if we have a job if (vcrJob != null) { activity.LegacyReference = ServerRuntime.GetUniqueWebId(vcrJob, vcrSchedule); } // Find the source to use - stream selection is always bound to the context of the source var streams = vcrSchedule.Streams; var source = vcrSchedule.Source; if (source == null) { if (vcrJob != null) { // Try job source = vcrJob.Source; // Adjust stream flags to use if (source == null) { streams = null; } else { streams = vcrJob.Streams; } } } // Copy station name if (source != null) { // Remember activity.Source = SourceIdentifier.ToString(source.Source).Replace(" ", ""); activity.Station = source.DisplayName; // Load the profile var profile = profiles[activity.GuideEntryDevice = source.ProfileName]; if (profile != null) { activity.HasGuideEntry = profile.ProgramGuide.HasEntry(source.Source, activity.StartTime, activity.StartTime + activity.Duration); } } // Apply special settings activity.CurrentProgramGuide = streams.GetUsesProgramGuide(); activity.AllLanguages = streams.GetUsesAllAudio(); activity.SubTitles = streams.GetUsesSubtitles(); activity.VideoText = streams.GetUsesVideotext(); activity.Dolby = streams.GetUsesDolbyAudio(); // Check for exception rule on the day var exception = vcrSchedule.FindException(time.End); if (exception != null) { activity.ExceptionRule = PlanException.Create(exception, vcrSchedule); } // May want to add end time checks if (!isAllocation) { if (!activity.IsLate) { if (!activity.IsHidden) { if ((exception == null) || exception.IsEmpty) { activity.EndTimeCouldBeWrong = activity.CheckEndTime(vcrSchedule.FirstStart); } } } } } else if (definition is ProgramGuideTask) { activity.Station = VCRJob.ProgramGuideName; } else if (definition is SourceListTask) { activity.Station = VCRJob.SourceScanName; } // Report return(activity); }
/// <summary> /// Startet eine Aufzeichnung oder eine Aufgabe. /// </summary> /// <param name="item">Die Beschreibung der Aufgabe.</param> /// <returns>Gesetzt, wenn der Vorgang erfolgreich war.</returns> public bool Start( IScheduleInformation item ) { // Validate if (item is ScheduleInformation) VCRServer.LogError( Properties.Resources.BadScheduleInformation, item.Definition.UniqueIdentifier, item.Definition.Name ); // Try start if (!m_manager.Start( item )) return false; // Remember m_started.Add( item.Definition.UniqueIdentifier, new ScheduleInformation( item ) ); // Did it return true; }
/// <summary> /// Überträgt eine geplante Ausführung. /// </summary> /// <param name="manager">Die zu erweiternde Schnittstelle.</param> /// <param name="schedule">Die Planungsinformationen zur Aufzeichnung.</param> /// <returns>Gesetzt, wenn ein Start möglich war.</returns> /// <exception cref="NullReferenceException">Es wurde keine Schnittstelle angegeben.</exception> /// <exception cref="ArgumentNullException">Es wurde keine Planung angegeben.</exception> public static bool Start( this IResourceManager manager, IScheduleInformation schedule ) { // Validate if (manager == null) throw new NullReferenceException(); if (schedule == null) throw new ArgumentNullException( "schedule" ); // Load var definition = schedule.Definition; var recording = definition as IRecordingDefinition; // Forward return manager.Start( schedule.Resource, (recording == null) ? null : recording.Source, definition.UniqueIdentifier, definition.Name, schedule.Time.Start, schedule.Time.End ); }
/// <summary> /// Erstellt einen neuen Eintrag. /// </summary> /// <param name="planItem">Die zugehörige Beschreibung der geplanten Aktivität.</param> /// <param name="context">Die Abbildung auf die Aufträge.</param> /// <returns>Die angeforderte Repräsentation.</returns> public static VCRRecordingInfo Create(IScheduleInformation planItem, PlanContext context) { // Validate if (planItem == null) { throw new ArgumentNullException(nameof(planItem)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } // Check type var definition = planItem.Definition as IScheduleDefinition <VCRSchedule>; if (definition == null) { // Check for program guide collector var guideCollection = planItem.Definition as ProgramGuideTask; if (guideCollection != null) { return new VCRRecordingInfo { Source = new SourceSelection { ProfileName = planItem.Resource.Name, DisplayName = VCRJob.ProgramGuideName }, FileName = Path.Combine(guideCollection.CollectorDirectory.FullName, Guid.NewGuid().ToString("N") + ".epg"), ScheduleUniqueID = guideCollection.UniqueIdentifier, IsHidden = planItem.Resource == null, StartsLate = planItem.StartsLate, StartsAt = planItem.Time.Start, Name = guideCollection.Name, EndsAt = planItem.Time.End, } } ; // Check for source list update var sourceUpdater = planItem.Definition as SourceListTask; if (sourceUpdater != null) { return new VCRRecordingInfo { Source = new SourceSelection { ProfileName = planItem.Resource.Name, DisplayName = VCRJob.SourceScanName }, FileName = Path.Combine(sourceUpdater.CollectorDirectory.FullName, Guid.NewGuid().ToString("N") + ".psi"), ScheduleUniqueID = sourceUpdater.UniqueIdentifier, IsHidden = planItem.Resource == null, StartsLate = planItem.StartsLate, StartsAt = planItem.Time.Start, EndsAt = planItem.Time.End, Name = sourceUpdater.Name, } } ; // None return(null); } // Attach to the schedule and its job - using the context and the map is the easiest way although there may be better alternatives var job = context.TryFindJob(definition.UniqueIdentifier); var schedule = definition.Context; // Find the source var source = schedule.Source ?? job.Source; if (source != null) { // Create a clone source = new SourceSelection { DisplayName = source.DisplayName, SelectionKey = source.SelectionKey }; // Update the name of the profile var resource = planItem.Resource; if (resource != null) { source.ProfileName = resource.Name; } } // Create the description of this recording var recording = new VCRRecordingInfo { Streams = (schedule.Source == null) ? job.Streams : schedule.Streams, ScheduleUniqueID = schedule.UniqueID, IsHidden = planItem.Resource == null, StartsLate = planItem.StartsLate, StartsAt = planItem.Time.Start, EndsAt = planItem.Time.End, JobUniqueID = job.UniqueID, RelatedSchedule = schedule, FileName = job.Directory, Name = definition.Name, RelatedJob = job, Source = source, }; // May want to adjust start time if job is active var runningInfo = context.GetRunState(definition.UniqueIdentifier); if (runningInfo != null) { if (runningInfo.Schedule.Time.End == recording.EndsAt) { // Assume we never start late - we are running recording.StartsLate = false; // If we started prior to this plan report the time we really started if (planItem.Time.Start > runningInfo.Schedule.Time.Start) { recording.StartsAt = runningInfo.Schedule.Time.Start; } } } // Finish recording.LoadDefaults(); // Report return(recording); }