|
|
@@ -0,0 +1,160 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Diagnostics;
|
|
|
+using System.IO.Abstractions;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using BO.AppServer.Metadata.Enums;
|
|
|
+using BO.ProcessServer.Business.Enums;
|
|
|
+using BO.ProcessServer.Business.StatisticCounters;
|
|
|
+using BO.ProcessServer.Business.Workers.Contexts;
|
|
|
+using BO.ProcessServer.Options;
|
|
|
+using Connector.PS;
|
|
|
+using Quadarax.Foundation.Core.IO.MimeTypes;
|
|
|
+using Quadarax.Foundation.Core.Logging;
|
|
|
+using Quadarax.Foundation.Core.Thread;
|
|
|
+
|
|
|
+namespace BO.ProcessServer.Business.Workers
|
|
|
+{
|
|
|
+ internal class SingleDocumentCalculationWorker : ContinousWorker
|
|
|
+ {
|
|
|
+ #region *** Constants ***
|
|
|
+ private const string CS_WK_NAME = "Document";
|
|
|
+ #endregion
|
|
|
+ #region *** Private fields ***
|
|
|
+ private IFileSystem _fileSystem;
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Properties ***
|
|
|
+ public string ShellCommand { get; set; }
|
|
|
+ public ServerProcessStateEnum Status { get; set; }
|
|
|
+ public DateTime Started { get; set; }
|
|
|
+ public DateTime Finished { get; set; }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Constructors ***
|
|
|
+ public SingleDocumentCalculationWorker(IConnectionPS connection, IFileSystem fileSystem, long documentId, IList<string> files, Configuration configuration, Statistics statistics, Pool pool,
|
|
|
+ ILogger logger) : base($"{CS_WK_NAME}#{documentId:D8}", new CalculationCtx(connection, documentId, files, configuration, statistics, pool),
|
|
|
+ logger)
|
|
|
+ {
|
|
|
+ Status = ServerProcessStateEnum.Pending;
|
|
|
+ _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Iteration ***
|
|
|
+ protected override async Task Do(IWorkerContext context)
|
|
|
+ {
|
|
|
+ var ctx = (CalculationCtx)context;
|
|
|
+
|
|
|
+ Status = ServerProcessStateEnum.Running;
|
|
|
+ Started = DateTime.Now;
|
|
|
+
|
|
|
+ // get document metadata
|
|
|
+ var document = await ctx.Connection.GetDocumentInfoAsync(ctx.DocumentId);
|
|
|
+
|
|
|
+ // find document proper scenario
|
|
|
+ var scenarioMan = new Scenarios.Scenarios(ctx.Configuration, GetLogger());
|
|
|
+ var scenario = scenarioMan.FindScenario(document);
|
|
|
+ if (scenario == null)
|
|
|
+ {
|
|
|
+ // scenario doesn't exists for document -> change status to pending and move files to Failed branch.
|
|
|
+ await ctx.Connection.SetDocumentStateAsync(ctx.DocumentId, DocumentStatusEnum.Pending);
|
|
|
+ ctx.Pool.MoveDocumentFiles(Pool.PoolTypeEnum.Working, Pool.PoolTypeEnum.Fail, ctx.DocumentId);
|
|
|
+ Status = ServerProcessStateEnum.FailInternal;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // scenario exists
|
|
|
+ var processInfo = new ProcessStartInfo();
|
|
|
+ processInfo.FileName = scenario.ShellCommand;
|
|
|
+ processInfo.WindowStyle = scenario.RunWindowsStyle;
|
|
|
+ processInfo.WorkingDirectory = ctx.Configuration.System.ScenarioPathBase;
|
|
|
+ processInfo.Arguments = TranslateArguments(scenario.ShellCommandArguments, ctx.InputFiles, document.PresetProcessProfile,document.PresetQuality, document.PresetWatermark,document.PresetTest);
|
|
|
+ ShellCommand = processInfo.FileName + " " + processInfo.Arguments;
|
|
|
+ ctx.Process = Process.Start(processInfo);
|
|
|
+ Log(LogSeverityEnum.Info, $"Starting process '{Name}.{ctx.Process?.ProcessName}' [Id:{ctx.Process?.Id}]: {ShellCommand}");
|
|
|
+ if (ctx.Process != null)
|
|
|
+ {
|
|
|
+ ctx.Process.WaitForExit();
|
|
|
+ Log(LogSeverityEnum.Info, $"End process '{Name}.{ctx.Process?.ProcessName}' [Id:{ctx.Process?.Id}]: Exit code {ctx.Process.ExitCode}");
|
|
|
+
|
|
|
+ if (ctx.Process.ExitCode == scenario.ExitCodeSucc)
|
|
|
+ {
|
|
|
+ Status = ServerProcessStateEnum.Done;
|
|
|
+
|
|
|
+ }else if (ctx.Process.ExitCode == scenario.ExitCodeFail)
|
|
|
+ {
|
|
|
+ Status = ServerProcessStateEnum.FailExit;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Log(LogSeverityEnum.Warn, $"Process '{Name}.{ctx.Process?.ProcessName}' has unhandled exitcode {ctx.Process.ExitCode}! Goes to fail.");
|
|
|
+ Status = ServerProcessStateEnum.FailInternal;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Status = ServerProcessStateEnum.FailInternal;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Finished = DateTime.Now;
|
|
|
+
|
|
|
+ if (Status == ServerProcessStateEnum.Done)
|
|
|
+ {
|
|
|
+ var files = ctx.Pool.GetDocumentFiles(Pool.PoolTypeEnum.Working, document.Id, Constants.CS_POOL_OUTFILE_EXT);
|
|
|
+ foreach (var file in files)
|
|
|
+ {
|
|
|
+ await using (var fs = await ctx.Pool.PopDocumentAsync(file))
|
|
|
+ {
|
|
|
+ await ctx.Connection.SetDocumentContentAsync(document.Id,
|
|
|
+ _fileSystem.Path.GetFileNameWithoutExtension(file),
|
|
|
+ MimeTypeUtil.GetMimeType(_fileSystem.Path.GetExtension(file)), false, fs);
|
|
|
+ Log(LogSeverityEnum.Debug, $"Process '{Name}.{ctx.Process?.ProcessName}' file '{file}' uploaded.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ await ctx.Connection.SetDocumentStateAsync(document.Id, DocumentStatusEnum.DoneOk);
|
|
|
+ ctx.Pool.MoveDocumentFiles(Pool.PoolTypeEnum.Working, Pool.PoolTypeEnum.Done, document.Id);
|
|
|
+ }
|
|
|
+ else if (Status == ServerProcessStateEnum.FailInternal || Status == ServerProcessStateEnum.FailExit ||
|
|
|
+ Status == ServerProcessStateEnum.FailTimeout)
|
|
|
+ {
|
|
|
+ ctx.Pool.MoveDocumentFiles(Pool.PoolTypeEnum.Working, Pool.PoolTypeEnum.Fail, document.Id);
|
|
|
+ }
|
|
|
+ Log(LogSeverityEnum.Info, $"Process '{Name}.{ctx.Process?.ProcessName}' resolution state is '{Status}'.");
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+
|
|
|
+ #region *** Overrides ***
|
|
|
+ protected override void OnBeforeStart()
|
|
|
+ {
|
|
|
+ Status = ServerProcessStateEnum.Running;
|
|
|
+ Started = DateTime.Now;
|
|
|
+ base.OnBeforeStart();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override void OnTimeout(IWorkerContext context)
|
|
|
+ {
|
|
|
+ var ctx = (CalculationCtx)context;
|
|
|
+ ctx.Process.Dispose();
|
|
|
+ ctx.Process = null;
|
|
|
+ Finished = DateTime.Now;
|
|
|
+ Status = ServerProcessStateEnum.FailTimeout;
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Private Operations ***
|
|
|
+ private string TranslateArguments(string arguments,IEnumerable<string> files, string profile, int quality, bool isWatermark,bool isTest)
|
|
|
+ {
|
|
|
+ var result = arguments.Replace(Constants.CS_TAG_INPUT_FILES, string.Join(Constants.CS_TAG_INPUT_FILES_SEP, files),StringComparison.OrdinalIgnoreCase);
|
|
|
+ result = result.Replace(Constants.CS_TAG_P_PROFILE, profile, StringComparison.OrdinalIgnoreCase);
|
|
|
+ result = result.Replace(Constants.CS_TAG_P_TEST, isTest.ToString(), StringComparison.OrdinalIgnoreCase);
|
|
|
+ result = result.Replace(Constants.CS_TAG_P_WATERMARK, isWatermark.ToString(), StringComparison.OrdinalIgnoreCase);
|
|
|
+ result = result.Replace(Constants.CS_TAG_P_QUALITY, quality.ToString(), StringComparison.OrdinalIgnoreCase);
|
|
|
+ result = result.Replace(Constants.CS_TAG_P_OUT_EXT, Constants.CS_POOL_OUTFILE_EXT, StringComparison.OrdinalIgnoreCase);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+}
|