Services/DataSourceService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
 
namespace pscommander
{
    public class DataSourceService
    {
        private readonly List<DataSourceJob> _cronJobs = new List<DataSourceJob>();
        public readonly List<DataSource> DataSources = new List<DataSource>();
        private readonly PowerShellService _powerShellService;
        private readonly MenuService _menuService;
        private CancellationTokenSource _cancelSource;
        public DataSourceService(PowerShellService powerShellService, MenuService menuService)
        {
            _powerShellService = powerShellService;
            _menuService = menuService;
        }
 
        public void SetDataSources(IEnumerable<DataSource> dataSources)
        {
            if (_cancelSource != null)
            {
                _cancelSource.Cancel();
                _cancelSource = null;
            }
             
            _cancelSource = new CancellationTokenSource();
 
            foreach(var job in _cronJobs)
            {
                job.Dispose();
            }
 
            _cronJobs.Clear();
            DataSources.Clear();
 
            foreach(var dataSource in dataSources)
            {
                var job = new DataSourceJob(dataSource, _powerShellService, _menuService);
                job.StartAsync(_cancelSource.Token).Wait();
                _cronJobs.Add(job);
            }
 
            DataSources.AddRange(dataSources);
        }
    }
 
    public class DataSourceJob : IDisposable
    {
        private System.Timers.Timer _timer;
        private readonly DataSource _dataSource;
        private readonly PowerShellService _powerShellService;
        private readonly MenuService _menuService;
 
        public DataSourceJob(DataSource dataSource, PowerShellService powerShellService, MenuService menuService)
        {
            _dataSource = dataSource;
            _powerShellService = powerShellService;
            _menuService = menuService;
        }
 
        public virtual async Task StartAsync(CancellationToken cancellationToken)
        {
            await DoWork(cancellationToken);
            await ScheduleJob(cancellationToken);
        }
 
        protected virtual async Task ScheduleJob(CancellationToken cancellationToken)
        {
            _timer = new System.Timers.Timer(_dataSource.RefreshInterval * 1000);
            _timer.Elapsed += async (sender, args) =>
            {
                _timer.Dispose(); // reset and dispose timer
                _timer = null;
 
                if (!cancellationToken.IsCancellationRequested)
                {
                    await DoWork(cancellationToken);
                }
 
                if (!cancellationToken.IsCancellationRequested)
                {
                    await ScheduleJob(cancellationToken); // reschedule next
                }
            };
            _timer.Start();
            await Task.CompletedTask;
        }
 
        public virtual async Task DoWork(CancellationToken cancellationToken)
        {
            await Task.CompletedTask;
 
            try
            {
                _dataSource.CurrentValue = _powerShellService.ExecuteNoUnwrap(_dataSource.LoadData, _dataSource.ArgumentList).First();
                if (_dataSource.History.Count == _dataSource.HistoryLimit)
                {
                    var key = _dataSource.History.Keys.OrderByDescending(m => m).First();
                    _dataSource.History.Remove(key);
                }
 
                _dataSource.History.Add(DateTime.Now, _dataSource.CurrentValue);
            }
            catch (Exception ex)
            {
                _menuService.ShowError(ex.Message);
            }
        }
 
        public virtual async Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Stop();
            await Task.CompletedTask;
        }
 
        public virtual void Dispose()
        {
            _timer?.Dispose();
        }
    }
}