/// <summary> /// Erzeugt eine neue Hintergrundaufgabe. /// </summary> /// <param name="worker">Der Algorithmus zur Ausführung.</param> /// <returns>Die neue Aufgabe.</returns> public static CancellableTask <TResultType> Run(Func <CancellationToken, TResultType> worker) { // Validate if (worker == null) { throw new ArgumentException("no task worker provided", "worker"); } // Allow self reference to new instance CancellableTask <TResultType> task = null; // Create instance task = new CancellableTask <TResultType>(() => { // Provide cleanup of cancel source using (var controller = task.m_cancel) try { // Try to process without generating exception - if possible return(worker(controller.Token)); } catch (Exception) { // In case of any error just report nothing - not applyable to value types return(null); } finally { // Never use cancel source again task.m_cancel = null; } }); // Make sure we clean up in case we are not able to start the task try { // Request start task.Start(); } catch (Exception) { // Forward with cleanup using (task.m_cancel) throw; } // Report return(task); }
/// <summary> /// Initialisiert eine Hintergrundaufgabe. /// </summary> /// <typeparam name="TResultType">Die Art des Ergebnisses der Aufgabe.</typeparam> /// <param name="reader">Die aktuelle Hintergrundaufgabe.</param> /// <param name="factory">Optional die Methode zur Neuinitialisierung.</param> /// <returns>Die neue Hintergrundaufgabe.</returns> private CancellableTask <TResultType> ResetReader <TResultType>(ref CancellableTask <TResultType> reader, Func <CancellableTask <TResultType> > factory = null) where TResultType : class { // Wipe out var previous = Interlocked.Exchange(ref reader, null); // Stop it if (previous != null) { previous.Cancel(); } // New one if (factory != null) { reader = factory(); } // Report return(reader); }
/// <summary> /// Beginnt mit dem Auslesen der Quelldaten. /// </summary> /// <param name="source">Die gewünschte Quelle.</param> /// <param name="device">Das zu verwendende Gerät.</param> /// <param name="profile">Optional das zu berücksichtigende Geräteprofil.</param> /// <returns>Die Hintergrundaufgabe zum Auslesen der Quelledaten.</returns> public static CancellableTask <SourceInformation> GetSourceInformationAsync(this Hardware device, SourceIdentifier source, Profile profile = null) { // Validate if (device == null) { throw new ArgumentNullException("no hardware to use", "device"); } if (source == null) { throw new ArgumentException("no source to get information for", "source"); } // Attach to tasks var patReader = device.AssociationTableReader; var groupReader = device.GroupReader; // Start return (CancellableTask <SourceInformation> .Run(cancel => { // Check tasks if (groupReader == null) { return null; } if (patReader == null) { return null; } // Wait on tasks if (!groupReader.CancellableWait(cancel)) { return null; } if (!patReader.CancellableWait(cancel)) { return null; } // Get the current group information var groupInfo = groupReader.Result; if (groupInfo == null) { return null; } // See if group exists if (!groupInfo.Sources.Any(source.Equals)) { return null; } // Find the stream identifier for the service var pmtIdentifier = patReader.Result.FindService(source.Service); if (!pmtIdentifier.HasValue) { return null; } // Wait for mapping table var pmtReader = device.GetTableAsync <PMT>(pmtIdentifier.Value); if (!pmtReader.CancellableWait(cancel)) { return null; } // Request table var pmts = pmtReader.Result; if (pmts == null) { return null; } // Create dummy var currentSettings = new SourceInformation { Source = source, VideoType = VideoTypes.NoVideo }; // Process all PMT - actually should be only one foreach (var pmt in pmts) { // Overwrite encryption if CA descriptor is present if (pmt.Table.Descriptors != null) { currentSettings.IsEncrypted = pmt.Table.Descriptors.Any(descriptor => EPG.DescriptorTags.CA == descriptor.Tag); } // Process the program entries foreach (var program in pmt.Table.ProgramEntries) { currentSettings.Update(program); } } // Find the related station information var station = groupInfo.Sources.FirstOrDefault(source.Equals) as Station; if (station != null) { // Take data from there currentSettings.Provider = station.Provider; currentSettings.Name = station.Name; // See if this is a service currentSettings.IsService = station.IsService; // Overwrite encryption if regular service entry exists if (!currentSettings.IsService) { currentSettings.IsEncrypted = station.IsEncrypted; } } // See if profile is attached if (profile != null) { // Apply the modifier var modifier = profile.GetFilter(currentSettings.Source); if (modifier != null) { modifier.ApplyTo(currentSettings); } } // Report return currentSettings; })); }
/// <summary> /// Erzeugt eine neue Hintergrundaufgabe zum Auslesen von SI Tabellen. /// </summary> /// <param name="device">Das zu verwendende Gerät.</param> /// <param name="stream">Die Datenstromkennung, die überwacht werden soll.</param> /// <returns>Die neue Aufgabe.</returns> public static CancellableTask <TTableType[]> GetTableAsync <TTableType>(this Hardware device, ushort stream) where TTableType : Table { // Validate if (device == null) { throw new ArgumentException("no hardware to use", "device"); } // Create the task return (CancellableTask <TTableType[]> .Run(cancel => { // Termination synchronisation var sync = new object(); var done = false; // Parsing state var tables = default(TTableType[]); var expectedVersion = 0; var collectedParts = 0; // Create parser var parser = TableParser.Create(( TTableType table ) => { // Disabled if (done) { return; } // Check version if (tables != null) { if (table.Version != expectedVersion) { tables = null; } } // Discard on count mismatch if (tables != null) { if (tables.Length != (table.LastSection + 1)) { tables = null; } } // Discard on overrun if (tables != null) { if (table.CurrentSection >= tables.Length) { tables = null; } } // Discard on duplicates if (tables != null) { if (tables[table.CurrentSection] != null) { tables = null; } } // Create once if (tables == null) { // Create in full size tables = new TTableType[table.LastSection + 1]; expectedVersion = table.Version; collectedParts = 0; } // Add it tables[table.CurrentSection] = table; // Mark as done if (++collectedParts >= tables.Length) { lock (sync) { done = true; // Our wake up call Monitor.Pulse(sync); } } }); // Register with cleanup var tableType = Table.GetIsExtendedTable <TTableType>() ? StreamTypes.ExtendedTable : StreamTypes.StandardTable; var registration = device.AddConsumer(stream, tableType, parser.AddPayload); try { // Start receiving data device.SetConsumerState(registration, true); // Wait for end and fully process the cancellation token using (cancel.Register(() => { lock (sync) Monitor.Pulse(sync); })) lock (sync) while (!done) { if (cancel.IsCancellationRequested) { return null; } else { Monitor.Wait(sync); } } } finally { // Cleanup device.SetConsumerState(registration, null); } // Report return tables; })); }