Files
process-monitor/Services/ProcessScheduler.cs

168 lines
5.9 KiB
C#

using System.Collections.Concurrent;
using MarketAlly.ProcessMonitor.Interfaces;
using MarketAlly.ProcessMonitor.Models;
using Microsoft.Extensions.Logging;
namespace MarketAlly.ProcessMonitor.Services;
/// <summary>
/// Handles process scheduling with proper cleanup and error handling
/// </summary>
public class ProcessScheduler : IScheduler, IDisposable
{
private readonly ILogger<ProcessScheduler> _logger;
private readonly IProcessManager _processManager;
private readonly ConcurrentDictionary<string, List<Timer>> _scheduledTasks;
private bool _disposed;
public ProcessScheduler(ILogger<ProcessScheduler> logger, IProcessManager processManager)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_processManager = processManager ?? throw new ArgumentNullException(nameof(processManager));
_scheduledTasks = new ConcurrentDictionary<string, List<Timer>>();
}
public void ScheduleProcessStart(ProcessInfo processInfo, bool openInNewWindow)
{
ArgumentNullException.ThrowIfNull(processInfo);
if (string.IsNullOrWhiteSpace(processInfo.Time))
{
_logger.LogWarning("Cannot schedule process {ProcessName} without a time", processInfo.Name);
return;
}
try
{
var scheduledTime = DateTime.Today.Add(TimeSpan.Parse(processInfo.Time));
var delay = scheduledTime - DateTime.Now;
if (delay < TimeSpan.Zero)
{
delay = delay.Add(TimeSpan.FromDays(1));
scheduledTime = scheduledTime.AddDays(1);
}
var timer = new Timer(async _ =>
{
try
{
var runningCount = _processManager.GetRunningProcessCount(processInfo.Name);
if (runningCount == 0)
{
await _processManager.StartProcessAsync(processInfo, openInNewWindow);
_logger.LogInformation("Scheduled process {ProcessName} started at {Time}",
processInfo.Name, DateTime.Now);
}
else
{
_logger.LogInformation("Process {ProcessName} already running, skipping scheduled start",
processInfo.Name);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in scheduled start for process {ProcessName}", processInfo.Name);
}
}, null, delay, Timeout.InfiniteTimeSpan);
AddScheduledTask(processInfo.Name, timer);
_logger.LogInformation("Scheduled {ProcessName} to start at {ScheduledTime}",
processInfo.Name, scheduledTime);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to schedule process {ProcessName}", processInfo.Name);
throw;
}
}
public void ScheduleRepeatedExecution(ProcessInfo processInfo, bool openInNewWindow)
{
ArgumentNullException.ThrowIfNull(processInfo);
if (!processInfo.Interval.HasValue || processInfo.Interval.Value <= 0)
{
_logger.LogWarning("Cannot schedule repeated execution for {ProcessName} without valid interval",
processInfo.Name);
return;
}
try
{
var timer = new Timer(async _ =>
{
try
{
var runningCount = _processManager.GetRunningProcessCount(processInfo.Name);
if (runningCount == 0)
{
await _processManager.StartProcessAsync(processInfo, openInNewWindow);
_logger.LogInformation("Repeated execution: started {ProcessName} at {Time}",
processInfo.Name, DateTime.Now);
}
else
{
_logger.LogDebug("Process {ProcessName} already running, skipping interval start",
processInfo.Name);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in repeated execution for process {ProcessName}", processInfo.Name);
}
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(processInfo.Interval.Value));
AddScheduledTask(processInfo.Name, timer);
_logger.LogInformation("Scheduled {ProcessName} to run every {Interval} minutes",
processInfo.Name, processInfo.Interval.Value);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to schedule repeated execution for process {ProcessName}", processInfo.Name);
throw;
}
}
public void CancelScheduledTasks(string processName)
{
if (_scheduledTasks.TryRemove(processName, out var timers))
{
foreach (var timer in timers)
{
timer?.Dispose();
}
_logger.LogInformation("Cancelled all scheduled tasks for {ProcessName}", processName);
}
}
private void AddScheduledTask(string processName, Timer timer)
{
_scheduledTasks.AddOrUpdate(processName,
new List<Timer> { timer },
(key, list) =>
{
list.Add(timer);
return list;
});
}
public void Dispose()
{
if (_disposed) return;
foreach (var kvp in _scheduledTasks)
{
foreach (var timer in kvp.Value)
{
timer?.Dispose();
}
}
_scheduledTasks.Clear();
_disposed = true;
_logger.LogInformation("ProcessScheduler disposed");
}
}