You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
384 lines
13 KiB
C#
384 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
using Haoliang.Core.Services;
|
|
using Haoliang.Data.Repositories;
|
|
|
|
namespace Haoliang.Core.Services
|
|
{
|
|
public interface IBackgroundTaskService
|
|
{
|
|
Task StartAsync(CancellationToken cancellationToken);
|
|
Task StopAsync(CancellationToken cancellationToken);
|
|
}
|
|
|
|
public class BackgroundTaskService : BackgroundService, IBackgroundTaskService
|
|
{
|
|
private readonly ILogger<BackgroundTaskService> _logger;
|
|
private readonly IDeviceCollectionService _collectionService;
|
|
private readonly IProductionService _productionService;
|
|
private readonly IAlarmService _alarmService;
|
|
private readonly IRealTimeService _realTimeService;
|
|
private readonly ISchedulerService _schedulerService;
|
|
private readonly Timer _collectionTimer;
|
|
private Timer _productionTimer;
|
|
private Timer _alarmTimer;
|
|
private Timer _realTimeTimer;
|
|
private bool _isRunning;
|
|
|
|
public BackgroundTaskService(
|
|
ILogger<BackgroundTaskService> logger,
|
|
IDeviceCollectionService collectionService,
|
|
IProductionService productionService,
|
|
IAlarmService alarmService,
|
|
IRealTimeService realTimeService,
|
|
ISchedulerService schedulerService)
|
|
{
|
|
_logger = logger;
|
|
_collectionService = collectionService;
|
|
_productionService = productionService;
|
|
_alarmService = alarmService;
|
|
_realTimeService = realTimeService;
|
|
_schedulerService = schedulerService;
|
|
|
|
_isRunning = false;
|
|
_collectionTimer = new Timer(ExecuteCollectionTasks, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
_logger.LogInformation("Background Task Service is starting.");
|
|
|
|
_isRunning = true;
|
|
|
|
try
|
|
{
|
|
await StartTimers(stoppingToken);
|
|
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
|
|
|
|
// Log service health
|
|
await LogServiceHealthAsync();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Background Task Service encountered an error.");
|
|
}
|
|
finally
|
|
{
|
|
await StopTimersAsync();
|
|
_logger.LogInformation("Background Task Service is stopping.");
|
|
}
|
|
}
|
|
|
|
public async Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
if (_isRunning)
|
|
return;
|
|
|
|
_logger.LogInformation("Starting background task service...");
|
|
|
|
try
|
|
{
|
|
// Start device collection
|
|
await _collectionService.CollectAllDevicesAsync();
|
|
_logger.LogInformation("Device collection started.");
|
|
|
|
// Start production calculation
|
|
await _productionService.CalculateAllProductionAsync();
|
|
_logger.LogInformation("Production calculation started.");
|
|
|
|
// Start alarm monitoring
|
|
var activeAlarms = await _alarmService.GetActiveAlarmsAsync();
|
|
if (activeAlarms.Any())
|
|
{
|
|
_logger.LogInformation($"Found {activeAlarms.Count()} active alarms.");
|
|
}
|
|
|
|
_isRunning = true;
|
|
_logger.LogInformation("Background task service started successfully.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to start background task service.");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task StopAsync(CancellationToken cancellationToken)
|
|
{
|
|
if (!_isRunning)
|
|
return;
|
|
|
|
_logger.LogInformation("Stopping background task service...");
|
|
|
|
try
|
|
{
|
|
await StopTimersAsync();
|
|
_isRunning = false;
|
|
|
|
_logger.LogInformation("Background task service stopped successfully.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error while stopping background task service.");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private async void ExecuteCollectionTasks(object state)
|
|
{
|
|
if (!_isRunning)
|
|
return;
|
|
|
|
try
|
|
{
|
|
await _collectionService.CollectAllDevicesAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error executing collection tasks.");
|
|
}
|
|
}
|
|
|
|
private async Task StartTimers(CancellationToken cancellationToken)
|
|
{
|
|
// Start production calculation timer (every 5 minutes)
|
|
_productionTimer = new Timer(async _ =>
|
|
{
|
|
if (!_isRunning) return;
|
|
|
|
try
|
|
{
|
|
await _productionService.CalculateAllProductionAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error in production calculation timer.");
|
|
}
|
|
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
|
|
|
|
// Start alarm monitoring timer (every 1 minute)
|
|
_alarmTimer = new Timer(async _ =>
|
|
{
|
|
if (!_isRunning) return;
|
|
|
|
try
|
|
{
|
|
var activeAlarms = await _alarmService.GetActiveAlarmsAsync();
|
|
_logger.LogInformation($"Monitoring {activeAlarms.Count()} active alarms.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error in alarm monitoring timer.");
|
|
}
|
|
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
|
|
|
// Start real-time data push timer (every 30 seconds)
|
|
_realTimeTimer = new Timer(async _ =>
|
|
{
|
|
if (!_isRunning) return;
|
|
|
|
try
|
|
{
|
|
await _realTimeService.BroadcastDeviceStatusAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error in real-time data push timer.");
|
|
}
|
|
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(30));
|
|
|
|
_logger.LogInformation("Background timers started.");
|
|
}
|
|
|
|
private async Task StopTimersAsync()
|
|
{
|
|
_collectionTimer?.Dispose();
|
|
_productionTimer?.Dispose();
|
|
_alarmTimer?.Dispose();
|
|
_realTimeTimer?.Dispose();
|
|
|
|
_logger.LogInformation("Background timers stopped.");
|
|
}
|
|
|
|
private async Task LogServiceHealthAsync()
|
|
{
|
|
try
|
|
{
|
|
var collectionHealth = await _collectionService.GetCollectionHealthAsync();
|
|
var deviceCount = await _collectionService.GetCollectionStatisticsAsync(DateTime.Today);
|
|
|
|
_logger.LogInformation($"Service Health - Devices: {collectionHealth.TotalDevices}, " +
|
|
$"Online: {collectionHealth.OnlineDevices}, " +
|
|
$"Success Rate: {collectionHealth.SuccessRate:F1}%, " +
|
|
$"Active Tasks: {collectionHealth.ActiveCollectionTasks}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error logging service health.");
|
|
}
|
|
}
|
|
}
|
|
|
|
public interface ISchedulerService
|
|
{
|
|
Task ScheduleDeviceCollectionAsync(int deviceId);
|
|
Task ScheduleProductionCalculationAsync(int deviceId);
|
|
Task ScheduleAlarmCheckAsync();
|
|
Task CancelScheduledTaskAsync(string taskId);
|
|
Task<IEnumerable<ScheduledTask>> GetScheduledTasksAsync();
|
|
}
|
|
|
|
public class BackgroundTaskManager : ISchedulerService
|
|
{
|
|
private readonly ILogger<BackgroundTaskManager> _logger;
|
|
private readonly IDeviceCollectionService _collectionService;
|
|
private readonly IProductionService _productionService;
|
|
private readonly IAlarmService _alarmService;
|
|
private readonly Dictionary<string, Timer> _scheduledTasks = new Dictionary<string, Timer>();
|
|
private readonly object _lock = new object();
|
|
|
|
public BackgroundTaskManager(
|
|
ILogger<BackgroundTaskManager> logger,
|
|
IDeviceCollectionService collectionService,
|
|
IProductionService productionService,
|
|
IAlarmService alarmService)
|
|
{
|
|
_logger = logger;
|
|
_collectionService = collectionService;
|
|
_productionService = productionService;
|
|
_alarmService = alarmService;
|
|
}
|
|
|
|
public async Task ScheduleDeviceCollectionAsync(int deviceId)
|
|
{
|
|
var taskId = $"collection_{deviceId}_{DateTime.Now:yyyyMMddHHmmss}";
|
|
|
|
lock (_lock)
|
|
{
|
|
if (_scheduledTasks.ContainsKey(taskId))
|
|
{
|
|
_scheduledTasks[taskId].Dispose();
|
|
}
|
|
|
|
var timer = new Timer(async _ =>
|
|
{
|
|
try
|
|
{
|
|
await _collectionService.CollectDeviceAsync(deviceId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Failed to execute scheduled collection for device {deviceId}");
|
|
}
|
|
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
|
|
|
|
_scheduledTasks[taskId] = timer;
|
|
}
|
|
|
|
_logger.LogInformation($"Scheduled device collection for device {deviceId} with task ID {taskId}");
|
|
}
|
|
|
|
public async Task ScheduleProductionCalculationAsync(int deviceId)
|
|
{
|
|
var taskId = $"production_{deviceId}_{DateTime.Now:yyyyMMddHHmmss}";
|
|
|
|
lock (_lock)
|
|
{
|
|
if (_scheduledTasks.ContainsKey(taskId))
|
|
{
|
|
_scheduledTasks[taskId].Dispose();
|
|
}
|
|
|
|
var timer = new Timer(async _ =>
|
|
{
|
|
try
|
|
{
|
|
await _productionService.CalculateProductionAsync(deviceId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Failed to execute scheduled production calculation for device {deviceId}");
|
|
}
|
|
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(10));
|
|
|
|
_scheduledTasks[taskId] = timer;
|
|
}
|
|
|
|
_logger.LogInformation($"Scheduled production calculation for device {deviceId} with task ID {taskId}");
|
|
}
|
|
|
|
public async Task ScheduleAlarmCheckAsync()
|
|
{
|
|
var taskId = $"alarm_check_{DateTime.Now:yyyyMMddHHmmss}";
|
|
|
|
lock (_lock)
|
|
{
|
|
if (_scheduledTasks.ContainsKey(taskId))
|
|
{
|
|
_scheduledTasks[taskId].Dispose();
|
|
}
|
|
|
|
var timer = new Timer(async _ =>
|
|
{
|
|
try
|
|
{
|
|
var activeAlarms = await _alarmService.GetActiveAlarmsAsync();
|
|
_logger.LogInformation($"Alarm check completed: {activeAlarms.Count()} active alarms");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to execute alarm check");
|
|
}
|
|
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(2));
|
|
|
|
_scheduledTasks[taskId] = timer;
|
|
}
|
|
|
|
_logger.LogInformation($"Scheduled alarm check with task ID {taskId}");
|
|
}
|
|
|
|
public Task CancelScheduledTaskAsync(string taskId)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (_scheduledTasks.TryGetValue(taskId, out var timer))
|
|
{
|
|
timer.Dispose();
|
|
_scheduledTasks.Remove(taskId);
|
|
_logger.LogInformation($"Cancelled scheduled task {taskId}");
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|
|
|
|
_logger.LogWarning($"Attempted to cancel non-existent task {taskId}");
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task<IEnumerable<ScheduledTask>> GetScheduledTasksAsync()
|
|
{
|
|
var tasks = _scheduledTasks.Keys.Select(key => new ScheduledTask
|
|
{
|
|
TaskId = key,
|
|
ScheduledTime = DateTime.Now,
|
|
Status = "Active"
|
|
}).ToList();
|
|
|
|
return Task.FromResult<IEnumerable<ScheduledTask>>(tasks);
|
|
}
|
|
}
|
|
|
|
public class ScheduledTask
|
|
{
|
|
public string TaskId { get; set; }
|
|
public DateTime ScheduledTime { get; set; }
|
|
public string Status { get; set; }
|
|
}
|
|
} |