using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading.Tasks; using Pf2e.Scrapper.Helpers; using Pf2e.Scrapper.Processors.Collection; using Pf2e.Scrapper.Processors.Definitions; using Pf2e.Scrapper.Processors.Factory; namespace Pf2e.Scrapper.Processors; /// /// Класс, выполняющий обработку коллекции обработчиков. /// /// [DI] /// [DI] /// [DI] public sealed class ProcessorExecutor( ProcessorCollection collection, ProcessProgress progress, ApplicationSettings settings ) { /// public ProcessorCollection Collection { get; } = collection; /// public ProcessProgress Progress { get; } = progress; /// public ApplicationSettings Settings { get; } = settings; /// /// Запускает обработку коллекции обработчиков. /// public void Start() { Collection.SortItems(); Progress.Processors.Reset(Collection.Items.Count); StartItem(); } /// /// Текущий индекс обрабатываемого элемента в коллекции. /// private int _currentIndex; /// /// Текущая фабрика процессоров, которая используется для обработки. /// private AbstractProcessorFactory CurrentFactory { get; set; } = null!; /// /// Инициирует обработку текущего элемента в коллекции. /// /// Этот метод извлекает текущий элемент из коллекции, подготавливает связанную /// фабрику и запускает фоновый обработчик для обработки задач элемента. Обработка /// выполняется параллельно, если конфигурация элемента это позволяет, со степенью /// параллелизма, определяемой настройками приложения. После завершения обработки метод /// переходит к следующему шагу рабочего процесса. private void StartItem() { ProcessorRecord item = Collection.Items[_currentIndex]; Progress.Processors.Start(item.Description); Progress.Progress(Progress.Processors.ToString()); BackgroundWorker worker = new(); CurrentFactory = item.Factory; CurrentFactory.Prepare(); worker.DoWork += (_, _) => { Parallel.ForEach( IterateProcessors(), new() { MaxDegreeOfParallelism = item.Parallel ? Settings.MaxThreads : 1 }, RunProcessor ); }; worker.RunWorkerCompleted += (_, _) => Continue(); worker.RunWorkerAsync(); } /// /// Запускает указанный процессор для выполнения операций обработки и сохранения. /// /// Если процессор выдает , сообщение об исключении /// регистрируется как информационный прогресс. Если выдается , /// оно молча игнорируется. /// Экземпляр процессора, выполняющий операции обработки и сохранения. /// Экземпляр , который можно использовать для управления выполнением /// параллельного цикла. /// Длинное значение, представляющее текущий индекс итерации или связанные данные для процессора. private void RunProcessor(IProcessor processor, ParallelLoopState state, long arg3) { try { processor.Process(); processor.Save(); } catch (SkipItemException exception) { Progress.Information(exception.Message); } catch (SilentSkipItemException) { // } } /// /// Переходит к обработке следующего элемента в коллекции или завершает процесс, если все элементы были обработаны. /// /// Этот метод увеличивает текущий индекс и определяет, начинать ли обработку следующего элемента или завершить /// процесс. Если текущий индекс превышает количество элементов в коллекции, процесс помечается как завершенный. private void Continue() { Progress.Processors.End(); _currentIndex++; if (_currentIndex < Collection.Items.Count) { StartItem(); } else { Progress.Processors.Complete(); } } /// /// Выполняет итерацию по всем процессорам, предоставленным текущей фабрикой. /// /// Этот метод выдает каждый процессор последовательно, позволяя вызывающему /// обрабатывать их по одному за раз. Отслеживание прогресса обновляется для каждого /// процессора во время итерации. /// Перечисляемая коллекция экземпляров , /// представляющая процессоры, доступные в текущей фабрике. private IEnumerable IterateProcessors() { Progress.Local.Reset(CurrentFactory.Count()); foreach ((string title, IProcessor processor) in CurrentFactory.GetEnumerator()) { Progress.Local.Start(title); yield return processor; Progress.Local.End(); } Progress.Local.Complete(); } } /// /// Исключение, показывающее, что обработка сущности пропускается как устаревшая. /// /// Название сущности. public class LegacyItemException(string name) : SkipItemException(name, "Устаревший элемент") { } /// /// Исключение, показывающее, что обработка сущности пропускается. /// /// Название сущности. /// Причина пропуска. public class SkipItemException(string name, string reason) : Exception($"Пропускаю элемент '{name}', указана причина: {reason}.") { } /// /// Исключение, показывающее, что обработка сущности пропускается без вывода сообщения. /// public class SilentSkipItemException : Exception { }