/// <summary> /// Löschte diesen Auftrag. /// </summary> /// <param name="target">Der Pfad zu einem Zielverzeichnis.</param> /// <returns>Gesetzt, wenn der Löschvorgang erfolgreich war. <i>null</i> wird gemeldet, /// wenn die Datei nicht existierte.</returns> public bool?Delete(DirectoryInfo target) { // Get the file var file = GetFileName(target); if (file == null) { return(null); } if (!file.Exists) { return(null); } // Be safe try { // Process file.Delete(); } catch (Exception e) { // Report error VCRServer.Log(e); // Failed return(false); } // Did it return(true); }
/// <summary> /// Speichert diesen Auftrag ab. /// </summary> /// <param name="target">Der Pfad zu einem Zielverzeichnis.</param> /// <returns>Gesetzt, wenn der Speichervorgang erfolgreich war. <i>null</i> wird /// gemeldet, wenn diesem Auftrag keine Datei zugeordnet ist.</returns> public bool?Save(DirectoryInfo target) { // Get the file var file = GetFileName(target); if (file == null) { return(null); } // Be safe try { // Process SerializationTools.Save(this, file); } catch (Exception e) { // Report VCRServer.Log(e); // Done return(false); } // Done return(true); }
/// <summary> /// Erzeugt eine neue Laufzeitumgebung und bindet diese an einen lokalen /// TCP/IP Port. /// </summary> /// <remarks> /// Als virtuelles Verzeichnis wird <i>/VCR.NET</i> verwendet. Dieses wird /// physikalisch an das Verzeichnis gebunden, dass der aktuellen Anwendung /// übergeordnet ist. Es empfiehlt sich, die Anwendung in einem Unterverzeichnis /// <i>bin</i> unterzubringen. /// </remarks> /// <exception cref="InvalidOperationException">Es wurde bereits eine Laufzeitumgebung /// angelegt.</exception> public void Start() { // Report Tools.ExtendedLogging("Starting Web Server"); // Already running if (m_endPoints.Count > 0) { return; } // Create m_endPoints.Add(new PrimaryEndPoint(m_server)); // Check the application directory var appDir = new DirectoryInfo(Path.Combine(Tools.ApplicationDirectory.Parent.FullName, ApplicationRoot)); if (appDir.Exists) { foreach (var app in appDir.GetDirectories()) { m_endPoints.Add(new ExtensionEndPoint(app.Name, Path.Combine(ApplicationRoot, app.Name))); } } // Report Tools.ExtendedLogging("Listener is up and running"); // Report VCRServer.Log(LoggingLevel.Full, Properties.Resources.WebServerStarted); }
/// <summary> /// Aktualisiert die Programmzeitschrift mit neuen Daten. /// </summary> /// <param name="entries">Die neuen Daten.</param> internal void UpdateGuide(ProgramGuideEntries entries) { // Report Tools.ExtendedLogging("Program Guide of {0} will be updated", ProfileName); // Did collection LastUpdateTime = DateTime.UtcNow; // Try to load resulting file try { // Create brand new var newData = m_Events.Clone(); // Merge new newData.Merge(entries); // Cleanup newData.DiscardOld(); // Save SerializationTools.Save(newData, ProgramGuideFile, Encoding.UTF8); // Use it m_Events = newData; // Report Tools.ExtendedLogging("Now using new Program Guide"); } catch (Exception e) { // Report VCRServer.Log(LoggingLevel.Errors, Properties.Resources.EPGMergeFailed, e); } }
/// <summary> /// Erzeugt eine neue Verwaltungsinstanz. /// </summary> /// <param name="jobs">Die zugehörige Auftragsverwaltung.</param> /// <param name="profileName">Der Name des verwalteten DVB.NET Geräteprofils.</param> public ProgramGuideManager(JobManager jobs, string profileName) { // Remember ProfileName = profileName; JobManager = jobs; // Calculate file ProgramGuideFile = new FileInfo(Path.Combine(JobManager.CollectorDirectory.FullName, $"EPGData for {ProfileName}.xml")); // See if profile has it's own program guide if (!HasProgramGuide) { return; } // Report Tools.ExtendedLogging("Looking for Program Guide of {0}", ProfileName); // No such file - start empty if (!ProgramGuideFile.Exists) { return; } // Process var events = SerializationTools.Load <ProgramGuideEntries>(ProgramGuideFile); if (events != null) { // Use it m_Events = events; // Report Tools.ExtendedLogging("Found valid Program Guide and using it"); // Done return; } // Report VCRServer.Log(LoggingLevel.Errors, Properties.Resources.EPGFileCorrupted, ProgramGuideFile.FullName); // Save delete try { // Process ProgramGuideFile.Delete(); } catch { // Discard any error VCRServer.Log(LoggingLevel.Errors, Properties.Resources.EPGDeleteDenied, ProgramGuideFile.FullName); } }
/// <summary> /// Beendet die Anwendung endgültig. /// </summary> public void Dispose() { // Forward var listener = Interlocked.Exchange(ref m_listener, null); if (listener != null) { if (listener.IsListening) { try { // Process listener.Stop(); } catch (Exception) { // Report to event log VCRServer.Log(LoggingLevel.Errors, EventLogEntryType.Warning, Properties.Resources.WebServerStopFailedChannel); } } } // Wait for all outstanding requests to terminate while (Thread.VolatileRead(ref m_Threads) > 0) { Thread.Sleep(100); } // See if worker is up var runtime = Interlocked.Exchange(ref m_runtime, null); if (runtime != null) { try { // Save call - may be already dead runtime.Stop(); // Terminate the domain AppDomain.Unload(runtime.AppDomain); } catch (Exception) { // Report to event log VCRServer.Log(LoggingLevel.Errors, EventLogEntryType.Warning, Properties.Resources.WebServerStopFailed); } } }
/// <summary> /// Erstellt eine neue Aktualisierung. /// </summary> /// <param name="state">Das zugehörige Geräteprofil.</param> /// <param name="recording">Daten der primären Aufzeichnung.</param> private ProgramGuideProxy(ProfileState state, VCRRecordingInfo recording) : base(state, recording) { // Reset fields if (VCRConfiguration.Current.EnableFreeSat) { m_extensions = EPGExtensions.FreeSatUK; } else { m_extensions = EPGExtensions.None; } // All sources we know about var allSources = new Dictionary <string, SourceSelection>(StringComparer.InvariantCultureIgnoreCase); // Load all sources of this profile foreach (var source in VCRProfiles.GetSources(ProfileName)) { // Remember by direct name allSources[source.DisplayName] = source; // allSources by unique name allSources[source.QualifiedName] = source; } // Fill in all foreach (var legacyName in VCRConfiguration.Current.ProgramGuideSources) { // Skip if empty if (string.IsNullOrEmpty(legacyName)) { continue; } // Locate SourceSelection realSource; if (allSources.TryGetValue(legacyName, out realSource)) { m_selected.Add(realSource.Source); } else { VCRServer.Log(LoggingLevel.Full, Properties.Resources.BadEPGStation, legacyName); } } }
/// <summary> /// Beendet die Nutzung dieser Instantz. /// </summary> /// <param name="explicitDisposing">Wird ignoriert.</param> protected override void Dispose(bool explicitDisposing) { // Self try { // Forward AbortWaiter(); } catch (Exception e) { // Report VCRServer.Log(e); } // Forward base.Dispose(explicitDisposing); }
/// <summary> /// Beendet den Suchlauf endgültig. /// </summary> protected override void OnStop() { // Remember the time of the last scan ProfileState.LastSourceUpdateTime = DateTime.UtcNow; // Log VCRServer.Log(LoggingLevel.Full, Properties.Resources.PSIReplace); // Finish ServerImplementation.EndRequest(Server.BeginEndScan(m_mergeSources)); // Report Tools.ExtendedLogging("Card Server has updated Profile {0} - VCR.NET will reload all Profiles now", ProfileName); // Time to refresh our lists VCRProfiles.Reset(); }
/// <summary> /// Beendet einen asynchronen Aufruf. Im Fall einer Ausnahme wird diese nur protokolliert /// und der Aufruf als abgeschlossen angenommen. /// </summary> /// <typeparam name="TAsync">Die Art des Aufrufs.</typeparam> /// <param name="request">Der aktive Aufruf.</param> /// <param name="traceFormat">Meldung bei Abschluss des Aufrufs. Ist dieser Parameter <i>null</i>, so /// wartet die Methode auf den Abschluss des asynchronen Aufrufs.</param> /// <param name="traceArgs">Parameter zum Aufbau der Meldung.</param> /// <returns>Gesetzt, wenn der Aufruf abgeschlossen wurde oder bereits war. Ansonsten /// ist der Aufruf weiterhin aktiv.</returns> protected bool WaitForEnd <TAsync>(ref TAsync request, string traceFormat = null, params object[] traceArgs) where TAsync : class, IAsyncResult { // No request at all var pending = request; if (pending == null) { return(true); } // Request still busy - calling this method with only a single parameter will do a synchronous call if (traceFormat != null) { if (!pending.IsCompleted) { return(false); } } // Synchronize try { // Fetch result ServerImplementation.EndRequest(pending); // Report if (!string.IsNullOrEmpty(traceFormat)) { Tools.ExtendedLogging(traceFormat, traceArgs); } } catch (Exception e) { // Report only! VCRServer.Log(LoggingLevel.Errors, "Failed Operation for '{0}': {1}", ProfileName, e.Message); } finally { // Forget request = null; } // Stopped it return(true); }
/// <summary> /// Beendet die aktuelle ASP.NET Laufzeitumgebung. /// <seealso cref="ServerRuntime.Stop"/> /// </summary> public void Stop() { // Terminate all end-points we started foreach (var endPoint in Interlocked.Exchange(ref m_endPoints, new List <IDisposable>())) { try { // Try shutdown endPoint.Dispose(); } catch (Exception) { // Ignore any error } } // Report VCRServer.Log(LoggingLevel.Full, Properties.Resources.WebServerStopped); }
/// <summary> /// Löst auf einem separaten <see cref="Thread"/> <see cref="OnPowerUp"/> aus. /// </summary> public static void OnResume() { // Start wakeup thread new Thread(forbid => { // With reset using ((IDisposable)forbid) try { // Trigger events var sink = OnPowerUp; if (sink != null) { sink(); } } catch (Exception e) { // Report VCRServer.Log(e); } }).Start(StartForbidHibernation()); }
/// <summary> /// Erstellt eine neue Planung. /// </summary> /// <param name="site">Die zugehörige Arbeitsumgebung.</param> private RecordingPlanner(IRecordingPlannerSite site) { // Remember m_site = site; // Process all profiles foreach (var profileName in site.ProfileNames) { // Look up the profile var profile = ProfileManager.FindProfile(profileName); if (profile == null) { continue; } // Create the resource for it var profileResource = ProfileScheduleResource.Create(profileName); // Remember m_resources.Add(profileName, profileResource); // See if this is a leaf profile if (!string.IsNullOrEmpty(profile.UseSourcesFrom)) { continue; } // See if we should process guide updates var guideTask = site.CreateProgramGuideTask(profileResource, profile); if (guideTask != null) { m_tasks.Add(guideTask); } // See if we should update the source list var scanTask = site.CreateSourceScanTask(profileResource, profile); if (scanTask != null) { m_tasks.Add(scanTask); } } // Make sure we report all errors try { // Create the manager m_manager = ResourceManager.Create(site.ScheduleRulesPath, ProfileManager.ProfileNameComparer); } catch (Exception e) { // Report VCRServer.LogError(Properties.Resources.BadRuleFile, e.Message); // Use standard rules m_manager = ResourceManager.Create(ProfileManager.ProfileNameComparer); } // Safe configure it try { // All all resources foreach (var resource in m_resources.Values) { m_manager.Add(resource); } } catch (Exception e) { // Cleanup Dispose(); // Report VCRServer.Log(e); } }
/// <summary> /// Startet die eigentlich Aufzeichnung in einer abgesicherten Umgebung. /// </summary> private void Run() { // Be fully safe try { // Create raw environment var coreEnvironment = new Dictionary <string, string> { { "%wakeupprofile%", ProfileState.WakeUpRequired ? "1" : "0" }, { "%dvbnetprofile%", ProfileName }, }; // Be fully safe try { // Log it VCRServer.Log(LoggingLevel.Schedules, Properties.Resources.RecordingStarted, TypeName); // Report Tools.ExtendedLogging("Started Recording Control Thread for {0}", ProfileName); // Fire all extensions Tools.RunSynchronousExtensions("BeforeProfileAccess", coreEnvironment); // Use it using (Server = CreateCardServerProxy()) { // Check mode var mustWakeUp = ProfileState.WakeUpRequired; if (mustWakeUp) { // Log VCRServer.Log(LoggingLevel.Full, Properties.Resources.RestartMessage); // Report Tools.ExtendedLogging("Will restart Hardware for {0} if Restart Device is configured in Profile", ProfileName); } // Start synchronously ServerImplementation.EndRequest( Server.BeginSetProfile ( ProfileName, mustWakeUp, VCRConfiguration.Current.DisablePCRFromH264Generation, VCRConfiguration.Current.DisablePCRFromMPEG2Generation )); // Report Tools.ExtendedLogging("Card Server is up and running"); // Remember time Representative.PhysicalStart = DateTime.UtcNow; // Create fresh environment and fire extensions - with no files so far FireRecordingStartedExtensions(ExtensionEnvironment = Representative.GetReplacementPatterns()); // Time to allow derived class to start up OnStart(); // Process idle loop - done every second if not interrupted earlier for (IAsyncResult <ServerInformation> stateRequest = null; ; m_wakeUp.WaitOne(m_running ? 1000 : 100)) { // First check for state request if (stateRequest != null) { // No yet done if (!stateRequest.IsCompleted) { continue; } // Process the state OnNewStateAvailable(m_state = stateRequest.Result); // No longer waiting for the next state stateRequest = null; } // See if we are busy in the derived class if (HasPendingServerRequest) { continue; } // Process actions - no asynchronous operations allowed in current version ProcessActions(); // Make sure that scheduler knows we accepted the request ConfirmPendingRequest(); // If we are still idle fire a new state request if (!HasPendingServerRequest) { if (m_running) { stateRequest = Server.BeginGetState(); } else { break; } } } // Time to let derived class shut down properly OnStop(); // Run final state update before protocol entry is created if (m_state != null) { OnNewStateAvailable(m_state); } // Update recording data for the very last time var info = CreateFullInformation(true); if (info != null) { Representative = info.Recording; } // Set real send Representative.EndsAt = DateTime.UtcNow; // Set end state ExtensionEnvironment["%Aborted%"] = m_aborted ? "1" : "0"; // Process extensions FireRecordingFinishedExtensions(ExtensionEnvironment); // No need for further wakeups ProfileState.WakeUpRequired = false; } } catch (Exception e) { // Report VCRServer.Log(e); // Try to make sure that job is not restarted try { // Update SetRestartThreshold(null); } catch (Exception ex) { // Report VCRServer.Log(ex); } } finally { // Detach from server - is already disposed and shut down Server = null; // Fire all extensions Tools.RunSynchronousExtensions("AfterProfileAccess", coreEnvironment); // Detach from profile ProfileState.EndRequest(this); // Write recording ProfileState.Server.JobManager.CreateLogEntry(Representative); // May go to sleep after job is finished ProfileState.Server.ReportRecordingDone(Representative.DisableHibernation, IsRealRecording); // Check for next job on all profiles ProfileState.Collection.BeginNewPlan(); // Report Tools.ExtendedLogging("Recording finished for {0}", ProfileName); // Log it VCRServer.Log(LoggingLevel.Schedules, Properties.Resources.RecordingFinished, TypeName); } } catch (Exception e) { // Report VCRServer.Log(e); } finally { // Make sure that even in case of error we do a full notification ConfirmPendingRequest(true); // Always fire event RequestFinished.Set(); } }
/// <summary> /// Bearbeitet einen einzelnen HTTP Aufruf. /// </summary> /// <param name="result">Der aktuelle Aufruf.</param> private void StartContext(IAsyncResult result) { // No longer active var listener = m_listener; if (listener == null) { return; } if (!listener.IsListening) { return; } // Correct counter Interlocked.Increment(ref m_Threads); // Check current state var endContext = true; // With cleanup try { // Context to process HttpListenerContext context; // Safe load try { // Read the context context = listener.EndGetContext(result); // Got a full request endContext = false; // Special if (context == null) { VCRServer.Log(LoggingLevel.Full, "Lost Context - Web Server may be in Error"); } } catch (Exception e) { // This can be quite normal on XP so better only do a light report Tools.ExtendedLogging("EndGetContext Exception: {0}", e.Message); // Reset context = null; } // Needs cleanup try { // Start next if (listener.IsListening) { listener.BeginGetContext(StartContext, null); } // Process if (context != null) { RunTime.ProcessRequest(new ContextAccessor(context)); } } catch (Exception) { // Be safe try { // Release respose if (context != null) { context.Response.Close(); } } catch { // Ignore any error during cleanup } } } catch (ThreadAbortException e) { // Report VCRServer.Log(e); } catch (HttpListenerException e) { // Report if (!endContext) { VCRServer.Log(e); } } catch (Exception e) { // Report VCRServer.Log(e); } finally { // Correct counter Interlocked.Decrement(ref m_Threads); } }
/// <summary> /// Steuert die Übergänge zwischen den Zuständen. /// </summary> private static void ControlThread() { // Process for (; ;) { // Collected change var operations = new List <Operation>(); // Wait for next operation lock (m_operations) { // Load while (m_operations.Count < 1) { Monitor.Wait(m_operations); } // Detach - true is for forbid and false is for allow while (m_operations.Count > 0) { operations.Add(m_operations.Dequeue()); } } // See if there is something to do var delta = operations.Sum(operation => operation.IsForbid ? +1 : -1); if (delta == 0) { // Report Tools.ExtendedLogging("PowerManagement Delta is 0"); } else { // To report transitions var sink = OnChanged; // Must forbid now var oldCount = m_HibCount; if (oldCount == 0) { if (SetThreadExecutionState(ExecutionState.SystemRequired | ExecutionState.Continuous) == ExecutionState.Error) { VCRServer.Log(LoggingLevel.Errors, Properties.Resources.HibernationNotBlocked); } else if (sink != null) { sink(true); } } // Adjust m_HibCount += delta; // Must allow now var newCount = m_HibCount; if (newCount == 0) { if (SetThreadExecutionState(ExecutionState.Continuous) == ExecutionState.Error) { VCRServer.Log(LoggingLevel.Errors, Properties.Resources.HibernationNotUnblocked); } else if (sink != null) { sink(false); } } // Report Tools.ExtendedLogging("PowerManagement: {0} => {1}", oldCount, newCount); } // Wakeup all requestors to make call appear synchronously operations.ForEach(operation => { // Wakeup lock (operation) Monitor.PulseAll(operation); }); } }