Generalize worker operations

This commit is contained in:
Ben Olden-Cooligan 2018-08-08 11:25:03 -04:00
parent 66eb4f018f
commit 89795c946f
6 changed files with 93 additions and 29 deletions

View File

@ -21,7 +21,7 @@ namespace NAPS2.ImportExport.Pdf
private readonly IOverwritePrompt overwritePrompt;
private readonly ThreadFactory threadFactory;
private readonly AppConfigManager appConfigManager;
private Thread thread;
public SavePdfOperation(FileNamePlaceholders fileNamePlaceholders, IPdfExporter pdfExporter, IOverwritePrompt overwritePrompt, ThreadFactory threadFactory, AppConfigManager appConfigManager, IWorkerServiceFactory workerServiceFactory)
@ -64,20 +64,13 @@ namespace NAPS2.ImportExport.Pdf
{
try
{
if (UseWorker)
Status.Success = DoWork(new SavePdfWorkArgs
{
using (var worker = WorkerServiceFactory.Create())
{
worker.Service.SetRecoveryFolder(RecoveryImage.RecoveryFolder.FullName);
worker.Callback.OnProgress += OnProgress;
worker.Service.ExportPdf(subFileName, snapshots.Export(), pdfSettings, ocrLanguageCode);
Status.Success = worker.Callback.WaitForFinish();
}
}
else
{
Status.Success = pdfExporter.Export(subFileName, snapshots, pdfSettings, ocrLanguageCode, OnProgress);
}
SubFileName = subFileName,
Snapshots = snapshots.Export(),
PdfSettings = pdfSettings,
OcrLanguageCode = ocrLanguageCode
});
}
catch (UnauthorizedAccessException ex)
{
@ -114,9 +107,24 @@ namespace NAPS2.ImportExport.Pdf
return true;
}
protected internal override bool DoWorkInternal(WorkArgs args)
{
var a = (SavePdfWorkArgs)args;
return pdfExporter.Export(a.SubFileName, a.Snapshots.Import(), a.PdfSettings, a.OcrLanguageCode, OnProgress);
}
public override void WaitUntilFinished()
{
thread.Join();
}
[Serializable]
internal class SavePdfWorkArgs : WorkArgs
{
public string SubFileName { get; set; }
public List<ScannedImage.SnapshotExport> Snapshots { get; set; }
public PdfSettings PdfSettings { get; set; }
public string OcrLanguageCode { get; set; }
}
}
}

View File

@ -5282,13 +5282,13 @@
<Content Include="Icons\contrast_with_sun.ico" />
<Content Include="Icons\control_play_blue-small.png" />
<Content Include="Icons\email.ico" />
<None Include="Icons\email.png" />
<None Include="Icons\email_setting.png" />
<Content Include="Icons\file_extension_pdf.ico" />
<None Include="Icons\gmail.png" />
<Content Include="Icons\image_edit.png" />
<None Include="Icons\outlookweb.png" />
<None Include="Icons\mail_yellow.png" />
<Content Include="Icons\key.ico" />
<Content Include="Icons\picture.ico" />
<Content Include="Icons\pictures.png" />
<Content Include="Icons\picture_edit.png" />

View File

@ -46,8 +46,9 @@ namespace NAPS2.Operation
Error?.Invoke(this, args);
}
protected bool OnProgress(int current, int max)
protected virtual bool OnProgress(int current, int max)
{
// TODO: Maybe don't make this virtual. Instead, clone the status object, and project event invocations back to the client.
Status.CurrentProgress = current;
Status.MaxProgress = max;
InvokeStatusChanged();

View File

@ -1,19 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using NAPS2.Recovery;
using NAPS2.Util;
using NAPS2.Worker;
namespace NAPS2.Operation
{
public abstract class WorkerOperation : OperationBase
{
private readonly IWorkerServiceFactory workerServiceFactory;
protected WorkerOperation(IWorkerServiceFactory workerServiceFactory)
{
WorkerServiceFactory = workerServiceFactory;
this.workerServiceFactory = workerServiceFactory;
}
protected bool UseWorker => !Environment.Is64BitProcess;
protected virtual bool UseWorker => !Environment.Is64BitProcess;
protected IWorkerServiceFactory WorkerServiceFactory { get; }
public ProgressHandler ProgressProxy { get; set; }
protected bool DoWork(WorkArgs args)
{
if (UseWorker)
{
using (var worker = workerServiceFactory.Create())
{
worker.Service.SetRecoveryFolder(RecoveryImage.RecoveryFolder.FullName);
worker.Callback.OnProgress += OnProgress;
worker.Service.DoOperationWork(GetType().FullName, args);
return worker.Callback.WaitForFinish();
}
}
return DoWorkInternal(args);
}
protected internal abstract bool DoWorkInternal(WorkArgs args);
protected override bool OnProgress(int current, int max)
{
return ProgressProxy?.Invoke(current, max) ?? base.OnProgress(current, max);
}
[KnownType("DerivedTypes")]
[Serializable]
public class WorkArgs
{
// ReSharper disable once UnusedMember.Local
private static Type[] DerivedTypes()
{
return Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsSubclassOf(typeof(WorkArgs))).ToArray();
}
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using NAPS2.ImportExport.Pdf;
using NAPS2.Operation;
using NAPS2.Recovery;
using NAPS2.Scan;
using NAPS2.Scan.Images;
@ -25,6 +26,6 @@ namespace NAPS2.Worker
List<RecoveryIndexImage> TwainScan(int recoveryFileNumber, ScanDevice scanDevice, ScanProfile scanProfile, ScanParams scanParams);
[OperationContract(IsOneWay = true)]
void ExportPdf(string subFileName, List<ScannedImage.SnapshotExport> snapshots, PdfSettings pdfSettings, string ocrLanguageCode);
void DoOperationWork(string operationTypeName, WorkerOperation.WorkArgs args);
}
}

View File

@ -2,15 +2,18 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Windows.Forms;
using NAPS2.ImportExport.Pdf;
using NAPS2.Operation;
using NAPS2.Recovery;
using NAPS2.Scan;
using NAPS2.Scan.Images;
using NAPS2.Scan.Images.Transforms;
using NAPS2.Scan.Twain;
using NAPS2.Util;
namespace NAPS2.Worker
{
@ -21,13 +24,15 @@ namespace NAPS2.Worker
{
private readonly TwainWrapper twainWrapper;
private readonly IPdfExporter pdfExporter;
private readonly IOperationFactory operationFactory;
public Form ParentForm { get; set; }
public WorkerService(TwainWrapper twainWrapper, IPdfExporter pdfExporter)
public WorkerService(TwainWrapper twainWrapper, IPdfExporter pdfExporter, IOperationFactory operationFactory)
{
this.twainWrapper = twainWrapper;
this.pdfExporter = pdfExporter;
this.operationFactory = operationFactory;
}
public void Init()
@ -55,23 +60,32 @@ namespace NAPS2.Worker
public void Dispose()
{
}
public void ExportPdf(string subFileName, List<ScannedImage.SnapshotExport> snapshots, PdfSettings pdfSettings, string ocrLanguageCode)
public void DoOperationWork(string operationTypeName, WorkerOperation.WorkArgs args)
{
// TODO: Make a type for a serializable snapshot
// TODO: Other operations. Import. Recovery. Save images. Password and ghostscript callbacks.
// Figure out ghostscript operation in general.
// Also - consider off-process thumbnail rendering. That's probably IO bound though, right?
// So parellization doesn't help. The only benefit would be memory. Which is not a bad benefit.
WrapOperation(() => pdfExporter.Export(subFileName, snapshots.Import(), pdfSettings, ocrLanguageCode, Callback.Progress));
}
var operationType = Type.GetType(operationTypeName);
if (operationType == null)
{
Log.Error($"Operation type not available: {operationTypeName}");
return;
}
var op = (WorkerOperation)typeof(IOperationFactory).GetMethod("Create")?.MakeGenericMethod(operationType).Invoke(operationFactory, new object[0]);
if (op == null)
{
Log.Error($"Could not create operation: {operationTypeName}");
return;
}
private void WrapOperation(Func<bool> op)
{
bool success = false;
try
{
success = op();
op.ProgressProxy = Callback.Progress;
success = op.DoWorkInternal(args);
}
catch (Exception e)
{
@ -82,7 +96,7 @@ namespace NAPS2.Worker
Callback.Finish(success);
}
}
public IWorkerCallback Callback { get; set; }
}
}