| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- 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);
- processInfo.RedirectStandardOutput = true;
- processInfo.RedirectStandardError = true;
- ShellCommand = processInfo.FileName + " " + processInfo.Arguments;
- using (ctx.Process = Process.Start(processInfo))
- {
- var errors = await ctx.Process.StandardError.ReadToEndAsync();
- var outputs = await ctx.Process.StandardOutput.ReadToEndAsync();
- Log(LogSeverityEnum.Info,
- $"Starting process '{Name}.{ctx.Process?.ProcessName}' [Id:{ctx.Process?.Id}]: {ShellCommand}");
- if (ctx.Process != null)
- {
- ctx.Process.WaitForExit();
- if (!string.IsNullOrEmpty(errors))
- Log(LogSeverityEnum.Error, errors);
- if (!string.IsNullOrEmpty(outputs))
- Log(LogSeverityEnum.Info, outputs);
- 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
- }
- }
|