using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Haoliang.Models.Production; using Haoliang.Models.Device; using Haoliang.Models.DataCollection; using Haoliang.Data.Repositories; using Haoliang.Core.Services; namespace Haoliang.Core.Services { public interface IProductionCalculator { Task CalculateProductionAsync(DeviceCurrentStatus current, DeviceCurrentStatus last); Task CalculateProgramSwitchProductionAsync(DeviceCurrentStatus current, DeviceCurrentStatus last); Task IsNewProgramAsync(string currentProgram, string lastProgram); Task HandleCrossDayResetAsync(DeviceCurrentStatus current, DeviceCurrentStatus last); Task ValidateProductionValueAsync(int quantity, DeviceCurrentStatus current); Task CreateProductionRecordAsync(int deviceId, DeviceCurrentStatus current, DeviceCurrentStatus last, int quantity); } public interface IProductionService { Task CalculateProductionAsync(int deviceId); Task CalculateAllProductionAsync(); Task GetTodayProductionAsync(int deviceId); Task> GetProductionByDateAsync(int deviceId, DateTime date); Task GetProductionStatisticsAsync(int deviceId, DateTime date); Task GetProductionSummaryAsync(DateTime date); Task GetQualityRateAsync(int deviceId, DateTime date); Task HasProductionDataAsync(int deviceId, DateTime date); Task ArchiveProductionDataAsync(int daysToKeep = 90); } public interface IProductionScheduler { Task StartProductionCalculationAsync(); Task StopProductionCalculationAsync(); Task ScheduleProductionCalculationAsync(int deviceId); Task GetNextCalculationTimeAsync(); Task IsCalculationRunningAsync(); } public class ProductionCalculator : IProductionCalculator { private readonly IProductionRepository _productionRepository; private readonly IProgramProductionSummaryRepository _summaryRepository; public ProductionCalculator( IProductionRepository productionRepository, IProgramProductionSummaryRepository summaryRepository) { _productionRepository = productionRepository; _summaryRepository = summaryRepository; } public async Task CalculateProductionAsync(DeviceCurrentStatus current, DeviceCurrentStatus last) { // 同一程序连续加工 if (current.NCProgram == last.NCProgram) { int diff = current.CumulativeCount - last.CumulativeCount; return Math.Max(0, await ValidateProductionValueAsync(diff, current)); // 异常值保护,避免负数 } // 程序切换逻辑 return await CalculateProgramSwitchProductionAsync(current, last); } public async Task CalculateProgramSwitchProductionAsync(DeviceCurrentStatus current, DeviceCurrentStatus last) { // 检查是否切换到新程序 if (await IsNewProgramAsync(current.NCProgram, last.NCProgram)) { // 新程序以当前累计数为起点 return current.CumulativeCount; } else { // 切回历史程序,视为重新开始 return 0; } } public async Task IsNewProgramAsync(string currentProgram, string lastProgram) { // 实现程序切换判断逻辑 return currentProgram != lastProgram && !string.IsNullOrEmpty(currentProgram); } public async Task HandleCrossDayResetAsync(DeviceCurrentStatus current, DeviceCurrentStatus last) { // 跨天处理:0点自动重置 if (current.RecordTime.Date != last.RecordTime.Date) { // 新日期以首次采集累计值为起点 // 这个逻辑需要在业务服务中实现 var todayProduction = await _productionRepository.GetByDeviceAndDateAsync( current.DeviceId, current.RecordTime.Date); if (!todayProduction.Any()) { // 如果当天没有生产记录,创建一条记录记录当前累计数 var firstRecord = new ProductionRecord { DeviceId = current.DeviceId, NCProgram = current.NCProgram, ProductionDate = current.RecordTime.Date, Quantity = 0, QualityRate = 100, CreatedAt = DateTime.Now }; await _productionRepository.AddAsync(firstRecord); await _productionRepository.SaveAsync(); } } } public async Task ValidateProductionValueAsync(int quantity, DeviceCurrentStatus current) { // 跳变检测:产量变化超过阈值时跳过 const int maxJumpThreshold = 1000; // 单次最大产量变化 if (quantity > maxJumpThreshold) { await LogWarningAsync($"Production jump detected: {quantity} exceeds threshold {maxJumpThreshold}"); return 0; } // 负数保护:产量为负数时归零 if (quantity < 0) { await LogWarningAsync("Negative production quantity detected, setting to 0"); return 0; } return quantity; } public async Task CreateProductionRecordAsync(int deviceId, DeviceCurrentStatus current, DeviceCurrentStatus last, int quantity) { var productionRecord = new ProductionRecord { DeviceId = deviceId, NCProgram = current.NCProgram, ProductionDate = current.RecordTime.Date, Quantity = quantity, QualityRate = 100, // 默认合格率,可以根据实际情况调整 StartTime = last != null ? last.RecordTime : (DateTime?)null, EndTime = current.RecordTime, CreatedAt = DateTime.Now }; await _productionRepository.AddAsync(productionRecord); await _productionRepository.SaveAsync(); // 更新程序产量汇总 await _summaryRepository.UpdateSummaryAsync( deviceId, current.NCProgram, current.RecordTime.Date, quantity, productionRecord.QualityRate); return productionRecord; } private async Task LogWarningAsync(string message) { // 这里可以实现日志记录逻辑 Console.WriteLine($"Warning: {message}"); } } public class ProductionService : IProductionService { private readonly IProductionRepository _productionRepository; private readonly IProgramProductionSummaryRepository _summaryRepository; private readonly IDeviceRepository _deviceRepository; private readonly IProductionCalculator _calculator; private readonly IProductionScheduler _scheduler; private readonly ICollectionResultRepository _collectionRepository; private readonly ILoggerService _logger; public ProductionService( IProductionRepository productionRepository, IProgramProductionSummaryRepository summaryRepository, IDeviceRepository deviceRepository, IProductionCalculator calculator, IProductionScheduler scheduler, ICollectionResultRepository collectionRepository, ILoggerService logger) { _productionRepository = productionRepository; _summaryRepository = summaryRepository; _deviceRepository = deviceRepository; _calculator = calculator; _scheduler = scheduler; _collectionRepository = collectionRepository; _logger = logger; } public async Task CalculateProductionAsync(int deviceId) { try { var device = await _deviceRepository.GetByIdAsync(deviceId); if (device == null || !device.IsAvailable) return; var collectionResults = await _collectionRepository.GetResultsByDeviceIdAsync(deviceId) .OrderBy(cr => cr.CollectionTime) .ToListAsync(); for (int i = 1; i < collectionResults.Count; i++) { var current = collectionResults[i]; var last = collectionResults[i - 1]; if (current.IsSuccess && last.IsSuccess) { var currentStatus = ParseCollectionResult(current); var lastStatus = ParseCollectionResult(last); if (currentStatus != null && lastStatus != null) { // 处理跨天重置 await _calculator.HandleCrossDayResetAsync(currentStatus, lastStatus); // 计算产量 var quantity = await _calculator.CalculateProductionAsync(currentStatus, lastStatus); if (quantity > 0) { // 创建生产记录 var productionRecord = await _calculator.CreateProductionRecordAsync( deviceId, currentStatus, lastStatus, quantity); await _logger.LogInformationAsync( $"Production calculated: Device {device.DeviceCode}, " + $"Program {currentStatus.NCProgram}, Quantity {quantity}"); } } } } } catch (Exception ex) { await _logger.LogErrorAsync( $"Failed to calculate production for device {deviceId}: {ex.Message}"); throw; } } public async Task CalculateAllProductionAsync() { var devices = await _deviceRepository.GetAvailableDevicesAsync(); foreach (var device in devices) { try { await CalculateProductionAsync(device.Id); } catch (Exception ex) { await _logger.LogErrorAsync( $"Failed to calculate production for device {device.DeviceCode}: {ex.Message}"); } } } public async Task GetTodayProductionAsync(int deviceId) { var today = DateTime.Today; var productions = await _productionRepository.GetByDeviceAndDateAsync(deviceId, today); return productions.OrderByDescending(p => p.CreatedAt).FirstOrDefault(); } public async Task> GetProductionByDateAsync(int deviceId, DateTime date) { return await _productionRepository.GetByDeviceAndDateAsync(deviceId, date); } public async Task GetProductionStatisticsAsync(int deviceId, DateTime date) { var productions = await _productionRepository.GetByDeviceAndDateAsync(deviceId, date); var device = await _deviceRepository.GetByIdAsync(deviceId); if (!productions.Any()) { return new ProductionStatistics { Date = date, DeviceId = deviceId, DeviceName = device?.DeviceName ?? "", TotalQuantity = 0, ValidQuantity = 0, InvalidQuantity = 0, QualityRate = 100 }; } var totalQuantity = productions.Sum(p => p.Quantity); var qualityRate = productions.Any() ? productions.Average(p => p.QualityRate) : 100; return new ProductionStatistics { Date = date, DeviceId = deviceId, DeviceName = device?.DeviceName ?? "", TotalQuantity = totalQuantity, ValidQuantity = (int)(totalQuantity * qualityRate / 100), InvalidQuantity = totalQuantity - (int)(totalQuantity * qualityRate / 100), QualityRate = qualityRate }; } public async Task GetProductionSummaryAsync(DateTime date) { var devices = await _deviceRepository.GetAllAsync(); var summaries = new List(); foreach (var device in devices) { var stats = await GetProductionStatisticsAsync(device.Id, date); summaries.Add(stats); } var totalQuantity = summaries.Sum(s => s.TotalQuantity); var totalValidQuantity = summaries.Sum(s => s.ValidQuantity); var overallQualityRate = totalQuantity > 0 ? (decimal)totalValidQuantity / totalQuantity * 100 : 100; return new ProductionSummary { Date = date, DeviceCount = summaries.Count, TotalQuantity = totalQuantity, TotalValidQuantity = totalValidQuantity, TotalInvalidQuantity = totalQuantity - totalValidQuantity, OverallQualityRate = overallQualityRate, DeviceStatistics = summaries }; } public async Task GetQualityRateAsync(int deviceId, DateTime date) { return await _productionRepository.GetQualityRateAsync(deviceId, date); } public async Task HasProductionDataAsync(int deviceId, DateTime date) { return await _productionRepository.HasProductionDataAsync(deviceId, date); } public async Task ArchiveProductionDataAsync(int daysToKeep = 90) { var cutoffDate = DateTime.Now.AddDays(-daysToKeep); var oldProductions = await _productionRepository.FindAsync( p => p.CreatedAt < cutoffDate); if (oldProductions.Any()) { await _productionRepository.RemoveRange(oldProductions); await _productionRepository.SaveAsync(); await _logger.LogInformationAsync( $"Archived {oldProductions.Count()} production records older than {daysToKeep} days"); } } private DeviceCurrentStatus ParseCollectionResult(CollectionResult result) { // 这里需要根据实际的CollectionResult结构来解析 // 暂时返回一个空的实现 return new DeviceCurrentStatus { DeviceId = result.DeviceId, RecordTime = result.CollectionTime, NCProgram = "", CumulativeCount = 0 }; } } public class ProductionScheduler : IProductionScheduler { private readonly IProductionService _productionService; private readonly ILoggerService _logger; private Timer _calculationTimer; private bool _isRunning; public ProductionScheduler( IProductionService productionService, ILoggerService logger) { _productionService = productionService; _logger = logger; } public async Task StartProductionCalculationAsync() { if (_isRunning) return; _isRunning = true; // 立即执行一次计算 await _productionService.CalculateAllProductionAsync(); // 设置定时器,每5分钟执行一次 _calculationTimer = new Timer(async _ => { try { await _productionService.CalculateAllProductionAsync(); await _logger.LogInformationAsync("Scheduled production calculation completed"); } catch (Exception ex) { await _logger.LogErrorAsync($"Scheduled production calculation failed: {ex.Message}"); } }, null, TimeSpan.Zero, TimeSpan.FromMinutes(5)); await _logger.LogInformationAsync("Production calculation scheduler started"); } public async Task StopProductionCalculationAsync() { if (!_isRunning) return; _isRunning = false; _calculationTimer?.Dispose(); _calculationTimer = null; await _logger.LogInformationAsync("Production calculation scheduler stopped"); } public async Task ScheduleProductionCalculationAsync(int deviceId) { if (!_isRunning) { await _logger.LogWarningAsync("Production calculation scheduler is not running"); return; } await _productionService.CalculateProductionAsync(deviceId); } public async Task GetNextCalculationTimeAsync() { if (!_isRunning || _calculationTimer == null) return DateTime.MinValue; // 这里可以根据定时器的配置返回下一个执行时间 return DateTime.Now.AddMinutes(5); } public async Task IsCalculationRunningAsync() { return _isRunning; } } public class ProductionSummary { public DateTime Date { get; set; } public int DeviceCount { get; set; } public int TotalQuantity { get; set; } public int TotalValidQuantity { get; set; } public int TotalInvalidQuantity { get; set; } public decimal OverallQualityRate { get; set; } public List DeviceStatistics { get; set; } } public interface ILoggerService { Task LogInformationAsync(string message); Task LogWarningAsync(string message); Task LogErrorAsync(string message); Task LogDebugAsync(string message); } }