using System; using System.Collections.Generic; using System.Linq; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Utils; namespace Sarand.Net.Standard.Base.Procs { //======================================================================= public interface ISacsProcessNotifier { void OnStartProcess (); void SetProgressInfo (List progressInfo); void OnEndProcess (); } //======================================================================= public abstract class SaProcess_Base : SacsDisposable { //------------------------------------------------------------------- // Process options and groups - for configuration of process to run. // See Sarand.Win.Forms.Utility.SaForm_SelectOptions. public class OptionData { public OptionData (TypeCodeDesc option) { Option = option; } public TypeCodeDesc Option { get; set; } public String Notes { get; set; } = ""; public bool Selected { get; set; } = false; } public class GroupData { public GroupData (TypeCodeDesc optionGroup) { OptionGroup = optionGroup; } public TypeCodeDesc OptionGroup { get; set; } public List Options { get; } = new List(); public bool ExclusiveOptions { get; set; } = false; public bool Selected { get; set; } = false; } //------------------------------------------------------------------- // Configuration. public enum ProcessType { Unknown = 0, ImportData, ExportData, ImportExportData, Other, } public class Config { protected Config () { } /// Identifies the type of process. public ProcessType ProcessType { get; set; } = ProcessType.Unknown; /// Is the process to be run in 'Test' mode. public bool TestProcess{ get; set; } = SacsSystem.DebugMode; /// Process imports data. public bool Import { get { return ProcessType == ProcessType.ImportData || ProcessType == ProcessType.ImportExportData; } } /// Process exports data. public bool Export { get { return ProcessType == ProcessType.ExportData || ProcessType == ProcessType.ImportExportData; } } /// Can the process be interrupted? public bool AllowInterruption { get; set; } = true; /// Confirm when interruption is requested. public bool ConfirmInterruption { get; set; } = true; /// Automated processes must not display desktop coponents - popup messages etc. public bool Automated { get; set; } = false; } //------------------------------------------------------------------- // Data members. private Config config; private String processError = ""; //------------------------------------------------------------------- // Construction. protected SaProcess_Base (Config config) { this.config = config; } protected override void Dispose (bool disposing) { if (disposing) { } } //------------------------------------------------------------------- // Properties. public bool DebugMode { get { return SacsSystem.DebugMode; } } public ISacsProcessNotifier ProcessNotifier { get; set; } = null; public bool Processing { get; protected set; } = false; public bool ProcessEnded { get; protected set; } = false; public DateTime StartedAt { get; private set; } = DateTime.MinValue; public DateTime FinishedAt { get; private set; } = DateTime.MinValue; public int ElapsedSecs { get { if (DateUtil.IsValid(StartedAt)) return (int)((DateUtil.IsValid(FinishedAt) ? FinishedAt : DateTime.Now) - StartedAt).TotalSeconds; return 0; } } //------------------------------------------------------------------- // Properties. /// Process caption. public abstract String ProcessDescription { get; } /// Process comments - to be displayed with process selection. public virtual List ProcessComments { get { return new List(); } } /// Processing statistics. public virtual List ProcessingStatistics { get { return new List(); } } //------------------------------------------------------------------- // Run process inline. public bool RunProcess () { String fn = GetType().Name+".RunProcess"; if (ProcessNotifier == null) throw new NotSupportedException("Process Notifier must be specified"); // Prepare process. bool runProcess = false; Reset(); Processing = true; StartedAt = DateTime.Now; try { // Notify start of process and log/ignore exceptions. ProcessNotifier.OnStartProcess(); } catch (Exception exc) { SaLogger.Error(fn + " OnStartProcess: " + GetProcessError() + exc.GetStackEntries(6).Concat("", "\n\tat "), true); } try { // Run process. OnProcessStarting(); runProcess = Run(); OnProcessEnding(); } catch (Exception exc) { SetProcessError(exc.GetType().Name + ": " + exc.Message); SaLogger.Error(fn + " " + GetProcessError() + exc.GetStackEntries(6).Concat("", "\n\tat "), true); } // Processing completed. FinishedAt = DateTime.Now; Processing = false; ProcessEnded = true; try { // Notify end of process and log/ignore exceptions. ProcessNotifier.OnEndProcess(); } catch (Exception exc) { SaLogger.Error(fn + " OnEndProcess: " + GetProcessError() + exc.GetStackEntries(6).Concat("", "\n\tat "), true); } return runProcess; } protected abstract bool Run (); protected virtual void OnProcessStarting () { } protected virtual void OnProcessEnding () { } //------------------------------------------------------------------- // Process control. public bool OK { get { if (HasProcessError()) return false; if (isInterrupted) return false; return true; } } private void Reset () { ClearProcessError(); isInterrupted = false; ProcessEnded = false; StartedAt = DateTime.MinValue; FinishedAt = DateTime.MinValue; } //------------------------------------------------------------------- // Progress information. protected void SetProgressInfo (String progressInfo) { SetProgressInfo(StringListUtil.CreateList(progressInfo)); } protected void SetProgressInfo (List progressInfo) { if (ProcessNotifier != null) ProcessNotifier.SetProgressInfo(progressInfo); } //------------------------------------------------------------------- // Error handling. public bool HasProcessError () { return !StringUtil.IsEmpty(processError); } public String GetProcessError () { return StringUtil.EmptyOnNull(processError); } public void ClearProcessError () { processError = ""; } /// Record the processing error - always returns false. public bool SetProcessError (String error) { processError = StringUtil.EmptyOnNull(error); return false; } //------------------------------------------------------------------- // Interruption. private bool isInterrupted = false; public bool IsInterrupted { get { return isInterrupted; } } public bool Interrupt () { if (!config.AllowInterruption) { SaLogger.Warn("SacsProcess.Interrupt() Interruptions not allowed in " + this.GetType().Name); return false; } if (config.ConfirmInterruption && !IsInterruptionConfirmed()) return false; isInterrupted = true; return true; } protected virtual bool IsInterruptionConfirmed () { return true; } //------------------------------------------------------------------- } //======================================================================= }