using API.Schema;
using log4net;
namespace API.Workers;
public abstract class BaseWorker : Identifiable
{
///
/// Workers this Worker depends on being completed before running.
///
public BaseWorker[] DependsOn { get; init; }
///
/// Dependencies and dependencies of dependencies. See also .
///
public IEnumerable AllDependencies => DependsOn.Select(d => d.AllDependencies).SelectMany(x => x);
///
/// and Self.
///
public IEnumerable DependenciesAndSelf => AllDependencies.Append(this);
///
/// where is less than Completed.
///
public IEnumerable MissingDependencies => DependsOn.Where(d => d.State < WorkerExecutionState.Completed);
public bool AllDependenciesFulfilled => DependsOn.All(d => d.State >= WorkerExecutionState.Completed);
internal WorkerExecutionState State { get; private set; }
private static readonly CancellationTokenSource CancellationTokenSource = new(TimeSpan.FromMinutes(10));
protected ILog Log { get; init; }
///
/// Stops worker, and marks as .Cancelled
///
public void Cancel()
{
Log.Debug($"Cancelled {this}");
this.State = WorkerExecutionState.Cancelled;
CancellationTokenSource.Cancel();
}
///
/// Stops worker, and marks as .Failed
///
protected void Fail()
{
Log.Debug($"Failed {this}");
this.State = WorkerExecutionState.Failed;
CancellationTokenSource.Cancel();
}
public BaseWorker(IEnumerable? dependsOn = null)
{
this.DependsOn = dependsOn?.ToArray() ?? [];
this.Log = LogManager.GetLogger(GetType());
}
///
/// Sets States during worker-run.
/// States:
///
/// - .Waiting when waiting for
/// - .Running when running
/// - .Completed after finished
///
///
///
///
/// - If has , missing dependencies.
/// - If are .Running, itself after waiting for dependencies.
/// - If has run, additional .
///
///
public Task DoWork()
{
Log.Debug($"Checking {this}");
this.State = WorkerExecutionState.Waiting;
BaseWorker[] missingDependenciesThatNeedStarting = MissingDependencies.Where(d => d.State < WorkerExecutionState.Waiting).ToArray();
if(missingDependenciesThatNeedStarting.Any())
return new Task(() => missingDependenciesThatNeedStarting);
if (MissingDependencies.Any())
return new Task(WaitForDependencies);
Log.Info($"Running {this}");
DateTime startTime = DateTime.UtcNow;
Task task = new (DoWorkInternal, CancellationTokenSource.Token);
task.GetAwaiter().OnCompleted(() =>
{
DateTime endTime = DateTime.UtcNow;
Log.Info($"Completed {this}\n\t{endTime.Subtract(startTime).TotalMilliseconds} ms");
this.State = WorkerExecutionState.Completed;
});
task.Start();
this.State = WorkerExecutionState.Running;
return task;
}
protected abstract BaseWorker[] DoWorkInternal();
private BaseWorker[] WaitForDependencies()
{
Log.Info($"Waiting for {MissingDependencies.Count()} Dependencies {this}:\n\t{string.Join("\n\t", MissingDependencies.Select(d => d.ToString()))}");
while (CancellationTokenSource.IsCancellationRequested == false && MissingDependencies.Any())
{
Thread.Sleep(TrangaSettings.workCycleTimeout);
}
return [this];
}
}
public enum WorkerExecutionState
{
Failed = 0,
Cancelled = 32,
Created = 64,
Waiting = 96,
Running = 128,
Completed = 192
}