using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrpaonVision.Core.Statistics; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Options; using System.Collections.Concurrent; using System.Text.Json; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// 统计服务实现。 /// #if DISABLED_LEGACY_STATISTICS_SERVICE public sealed class StatisticsService : IStatisticsService { private readonly ILogger _logger; private readonly RuntimeOptions _options; private readonly ConcurrentDictionary _statisticsConfigs = new(); private readonly ConcurrentDictionary _statisticsCache = new(); private readonly object _lock = new(); /// /// 构造函数。 /// public StatisticsService(ILogger logger, IOptions options) { _logger = logger; _options = options.Value; InitializeDefaultStatisticsConfigs(); _logger.LogInformation("统计服务已初始化"); } /// public async Task> GetCycleTimeStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取节拍统计信息:开始时间={StartTime},结束时间={EndTime},产品类型={ProductTypeCode}", startTime, endTime, productTypeCode); var cacheKey = $"cycle_time_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{productTypeCode}"; if (_options.EnableStatisticsCache && _statisticsCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取节拍统计信息"); return Result.Success((CycleTimeStatistics)cached); } var statistics = await GenerateCycleTimeStatisticsAsync(startTime, endTime, productTypeCode, cancellationToken); if (_options.EnableStatisticsCache) { _statisticsCache.TryAdd(cacheKey, statistics); } _logger.LogInformation("节拍统计信息获取完成:总处理数={Total},成功率={SuccessRate:F2}%,平均节拍={AvgCycleTime:F2}秒", statistics.TotalProcessedCount, statistics.OverallSuccessRate, statistics.AverageCycleTimeSeconds); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取节拍统计信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_CYCLE_TIME_STATISTICS_FAILED", "获取节拍统计信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetShiftStatisticsAsync(DateTime shiftDate, ShiftType? shiftType = null, string? productTypeCode = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取班次统计信息:日期={ShiftDate},班次类型={ShiftType},产品类型={ProductTypeCode}", shiftDate, shiftType, productTypeCode); var cacheKey = $"shift_{shiftDate:yyyyMMdd}_{shiftType}_{productTypeCode}"; if (_options.EnableStatisticsCache && _statisticsCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取班次统计信息"); return Result.Success((ShiftStatistics)cached); } var statistics = await GenerateShiftStatisticsAsync(shiftDate, shiftType, productTypeCode, cancellationToken); if (_options.EnableStatisticsCache) { _statisticsCache.TryAdd(cacheKey, statistics); } _logger.LogInformation("班次统计信息获取完成:计划产量={Planned},实际产量={Actual},完成率={CompletionRate:F2}%,合格率={PassRate:F2}%", statistics.PlannedProductionCount, statistics.ActualProductionCount, statistics.ProductionCompletionRate, statistics.QualityPassRate); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取班次统计信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_SHIFT_STATISTICS_FAILED", "获取班次统计信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task>> GetShiftListAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取班次列表:开始日期={StartDate},结束日期={EndDate}", startDate, endDate); var shiftList = await GenerateShiftListAsync(startDate, endDate, cancellationToken); _logger.LogInformation("班次列表获取完成:班次数量={ShiftCount}", shiftList.Count); return Result.Success>(shiftList); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取班次列表失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_SHIFT_LIST_FAILED", "获取班次列表失败", traceId); return Result.FailWithTrace>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetProductionEfficiencyStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, StatisticsGranularity granularity = StatisticsGranularity.Hourly, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取生产效率统计:开始时间={StartTime},结束时间={EndTime},产品类型={ProductTypeCode},粒度={Granularity}", startTime, endTime, productTypeCode, granularity); var cacheKey = $"efficiency_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{productTypeCode}_{granularity}"; if (_options.EnableStatisticsCache && _statisticsCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取生产效率统计"); return Result.Success((ProductionEfficiencyStatistics)cached); } var statistics = await GenerateProductionEfficiencyStatisticsAsync(startTime, endTime, productTypeCode, granularity, cancellationToken); if (_options.EnableStatisticsCache) { _statisticsCache.TryAdd(cacheKey, statistics); } _logger.LogInformation("生产效率统计获取完成:OEE={OEE:F2}%,时间利用率={TimeUtilization:F2}%,设备利用率={EquipmentUtilization:F2}%", statistics.OverallEquipmentEffectiveness, statistics.TimeUtilizationRate, statistics.EquipmentUtilizationRate); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取生产效率统计失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_PRODUCTION_EFFICIENCY_STATISTICS_FAILED", "获取生产效率统计失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetQualityStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取质量统计信息:开始时间={StartTime},结束时间={EndTime},产品类型={ProductTypeCode}", startTime, endTime, productTypeCode); var cacheKey = $"quality_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{productTypeCode}"; if (_options.EnableStatisticsCache && _statisticsCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取质量统计信息"); return Result.Success((QualityStatistics)cached); } var statistics = await GenerateQualityStatisticsAsync(startTime, endTime, productTypeCode, cancellationToken); if (_options.EnableStatisticsCache) { _statisticsCache.TryAdd(cacheKey, statistics); } _logger.LogInformation("质量统计信息获取完成:总检测数={Total},合格数={Qualified},合格率={PassRate:F2}%", statistics.TotalInspectionCount, statistics.QualifiedCount, statistics.OverallPassRate); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取质量统计信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_QUALITY_STATISTICS_FAILED", "获取质量统计信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetEquipmentUtilizationStatisticsAsync(DateTime startTime, DateTime endTime, string? equipmentId = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取设备利用率统计:开始时间={StartTime},结束时间={EndTime},设备ID={EquipmentId}", startTime, endTime, equipmentId); var cacheKey = $"equipment_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{equipmentId}"; if (_options.EnableStatisticsCache && _statisticsCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取设备利用率统计"); return Result.Success((EquipmentUtilizationStatistics)cached); } var statistics = await GenerateEquipmentUtilizationStatisticsAsync(startTime, endTime, equipmentId, cancellationToken); if (_options.EnableStatisticsCache) { _statisticsCache.TryAdd(cacheKey, statistics); } _logger.LogInformation("设备利用率统计获取完成:总体利用率={Overall:F2}%,生产利用率={Production:F2}%,可用率={Availability:F2}%", statistics.OverallUtilizationRate, statistics.ProductionUtilizationRate, statistics.AvailabilityRate); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取设备利用率统计失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_EQUIPMENT_UTILIZATION_STATISTICS_FAILED", "获取设备利用率统计失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetAlarmStatisticsSummaryAsync(DateTime startTime, DateTime endTime, OrpaonVision.Core.Common.AlarmLevel? alarmLevel = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取报警统计摘要:开始时间={StartTime},结束时间={EndTime},报警级别={AlarmLevel}", startTime, endTime, alarmLevel); var cacheKey = $"alarm_summary_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{alarmLevel}"; if (_options.EnableStatisticsCache && _statisticsCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取报警统计摘要"); return Result.Success((AlarmStatisticsSummary)cached); } var statistics = await GenerateAlarmStatisticsSummaryAsync(startTime, endTime, alarmLevel, cancellationToken); if (_options.EnableStatisticsCache) { _statisticsCache.TryAdd(cacheKey, statistics); } _logger.LogInformation("报警统计摘要获取完成:总报警数={Total},活跃数={Active},确认数={Confirmed},清除数={Cleared}", statistics.TotalAlarmCount, statistics.ActiveAlarmCount, statistics.ConfirmedAlarmCount, statistics.ClearedAlarmCount); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取报警统计摘要失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_ALARM_STATISTICS_SUMMARY_FAILED", "获取报警统计摘要失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetComprehensiveProductionReportAsync(OrpaonVision.Core.Statistics.ProductionReportType reportType, DateTime startTime, DateTime endTime, string? productTypeCode = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取综合生产报告:报告类型={ReportType},开始时间={StartTime},结束时间={EndTime},产品类型={ProductTypeCode}", reportType, startTime, endTime, productTypeCode); var report = await GenerateComprehensiveProductionReportAsync(reportType, startTime, endTime, productTypeCode, cancellationToken); _logger.LogInformation("综合生产报告获取完成:报告ID={ReportId},总体评分={OverallScore:F2}", report.ReportId, report.ExecutiveSummary.OverallScore); return Result.Success(report); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取综合生产报告失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_COMPREHENSIVE_PRODUCTION_REPORT_FAILED", "获取综合生产报告失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetRealTimeStatisticsDashboardAsync(string? productTypeCode = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取实时统计仪表板:产品类型={ProductTypeCode}", productTypeCode); var dashboard = await GenerateRealTimeStatisticsDashboardAsync(productTypeCode, cancellationToken); _logger.LogInformation("实时统计仪表板获取完成:更新时间={UpdateTime},当前OEE={OEE:F2}%", dashboard.LastUpdatedUtc, dashboard.Efficiency.CurrentOEE); return Result.Success(dashboard); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取实时统计仪表板失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_REAL_TIME_STATISTICS_DASHBOARD_FAILED", "获取实时统计仪表板失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetHistoricalTrendDataAsync(MetricType metricType, DateTime startTime, DateTime endTime, string? productTypeCode = null, StatisticsGranularity granularity = StatisticsGranularity.Hourly, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取历史趋势数据:指标类型={MetricType},开始时间={StartTime},结束时间={EndTime},产品类型={ProductTypeCode},粒度={Granularity}", metricType, startTime, endTime, productTypeCode, granularity); var cacheKey = $"trend_{metricType}_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{productTypeCode}_{granularity}"; if (_options.EnableStatisticsCache && _statisticsCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取历史趋势数据"); return Result.Success((HistoricalTrendData)cached); } var trendData = await GenerateHistoricalTrendDataAsync(metricType, startTime, endTime, productTypeCode, granularity, cancellationToken); if (_options.EnableStatisticsCache) { _statisticsCache.TryAdd(cacheKey, trendData); } _logger.LogInformation("历史趋势数据获取完成:指标类型={MetricType},数据点数量={DataPointCount},趋势方向={Trend}", metricType, trendData.DataPoints.Count, trendData.TrendAnalysis.TrendDescription); return Result.Success(trendData); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取历史趋势数据失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_HISTORICAL_TREND_DATA_FAILED", "获取历史趋势数据失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> CreateStatisticsConfigAsync(StatisticsConfig config, CancellationToken cancellationToken = default) { try { _logger.LogInformation("创建统计配置:配置类型={ConfigType},产品类型={ProductTypeCode},配置名称={ConfigName}", config.ConfigType, config.ProductTypeCode, config.ConfigName); lock (_lock) { config.ConfigId = Guid.NewGuid(); config.CreatedAtUtc = DateTime.UtcNow; config.UpdatedAtUtc = DateTime.UtcNow; config.Version = "1.0"; var key = $"{config.ConfigType}_{config.ProductTypeCode}_{config.ConfigName}"; _statisticsConfigs[key] = config; } _logger.LogInformation("统计配置创建成功:配置ID={ConfigId},配置名称={ConfigName}", config.ConfigId, config.ConfigName); return Result.Success(config); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "创建统计配置失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CREATE_STATISTICS_CONFIG_FAILED", "创建统计配置失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task UpdateStatisticsConfigAsync(StatisticsConfig config, CancellationToken cancellationToken = default) { try { _logger.LogInformation("更新统计配置:配置ID={ConfigId},配置名称={ConfigName}", config.ConfigId, config.ConfigName); lock (_lock) { var key = $"{config.ConfigType}_{config.ProductTypeCode}_{config.ConfigName}"; if (_statisticsConfigs.ContainsKey(key)) { config.UpdatedAtUtc = DateTime.UtcNow; _statisticsConfigs[key] = config; } else { return Result.Fail("CONFIG_NOT_FOUND", $"未找到配置:{config.ConfigId}"); } } _logger.LogInformation("统计配置更新成功:配置ID={ConfigId}", config.ConfigId); return Result.Success(); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "更新统计配置失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "UPDATE_STATISTICS_CONFIG_FAILED", "更新统计配置失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetStatisticsConfigAsync(StatisticsConfigType configType, string? productTypeCode = null, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取统计配置:配置类型={ConfigType},产品类型={ProductTypeCode}", configType, productTypeCode); var key = $"{configType}_{productTypeCode}"; if (_statisticsConfigs.TryGetValue(key, out var config)) { return Result.Success(config); } // 尝试获取默认配置 var defaultKey = $"{configType}_default"; if (_statisticsConfigs.TryGetValue(defaultKey, out var defaultConfig)) { return Result.Success(defaultConfig); } return Result.FailWithTrace("CONFIG_NOT_FOUND", $"未找到配置:{configType}", string.Empty); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取统计配置失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_STATISTICS_CONFIG_FAILED", "获取统计配置失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> ExportStatisticsAsync(StatisticsExportRequest exportRequest, CancellationToken cancellationToken = default) { try { _logger.LogInformation("导出统计数据:导出类型={ExportType},格式={ExportFormat},统计类型={StatisticsType}", exportRequest.ExportType, exportRequest.ExportFormat, exportRequest.StatisticsType); var startTime = DateTime.UtcNow; var result = await GenerateStatisticsExportAsync(exportRequest, cancellationToken); var elapsed = DateTime.UtcNow - startTime; var exportResult = new StatisticsExportResult { RequestId = exportRequest.RequestId, IsSuccess = true, FilePath = result.FilePath, FileSizeBytes = result.FileSizeBytes, RecordCount = result.RecordCount, ExportElapsedMs = (long)elapsed.TotalMilliseconds, ExportTimeUtc = DateTime.UtcNow, ResultDescription = $"导出完成:{result.RecordCount}条记录,文件大小{result.FileSizeBytes}字节", ExtendedProperties = new Dictionary { ["export_type"] = exportRequest.ExportType, ["export_format"] = exportRequest.ExportFormat, ["statistics_type"] = exportRequest.StatisticsType } }; _logger.LogInformation("统计数据导出完成:文件路径={FilePath},记录数量={RecordCount},耗时={ElapsedMs}ms", exportResult.FilePath, exportResult.RecordCount, exportResult.ExportElapsedMs); return Result.Success(exportResult); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "导出统计数据失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "EXPORT_STATISTICS_FAILED", "导出统计数据失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } #region 私有方法 /// /// 初始化默认统计配置。 /// private void InitializeDefaultStatisticsConfigs() { var defaultConfigs = new List { new() { ConfigId = Guid.NewGuid(), ConfigType = StatisticsConfigType.CycleTime, ProductTypeCode = "default", ConfigName = "默认节拍统计配置", ConfigDescription = "系统默认的节拍统计配置", Parameters = new Dictionary { ["target_cycle_time_seconds"] = 60.0, ["tolerance_percentage"] = 10.0, ["enable_trend_analysis"] = true }, IsEnabled = true, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { ConfigId = Guid.NewGuid(), ConfigType = StatisticsConfigType.Shift, ProductTypeCode = "default", ConfigName = "默认班次统计配置", ConfigDescription = "系统默认的班次统计配置", Parameters = new Dictionary { ["shift_duration_hours"] = 8.0, ["target_production_rate"] = 100, ["enable_anomaly_detection"] = true }, IsEnabled = true, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { ConfigId = Guid.NewGuid(), ConfigType = StatisticsConfigType.Quality, ProductTypeCode = "default", ConfigName = "默认质量统计配置", ConfigDescription = "系统默认的质量统计配置", Parameters = new Dictionary { ["target_pass_rate"] = 95.0, ["enable_defect_analysis"] = true, ["enable_improvement_suggestions"] = true }, IsEnabled = true, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" } }; foreach (var config in defaultConfigs) { var key = $"{config.ConfigType}_{config.ProductTypeCode}_{config.ConfigName}"; _statisticsConfigs[key] = config; } } /// /// 生成节拍统计信息。 /// private async Task GenerateCycleTimeStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); // 简化处理:生成模拟数据 var random = new Random(); var totalProcessed = random.Next(100, 500); var successCount = (int)(totalProcessed * (0.85 + random.NextDouble() * 0.1)); var failedCount = totalProcessed - successCount; var cycleTimes = Enumerable.Range(0, totalProcessed) .Select(_ => 45 + random.NextDouble() * 30) // 45-75秒 .ToList(); var timeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }; return new CycleTimeStatistics { TimeRange = timeRange, ProductTypeCode = productTypeCode ?? "default", TotalProcessedCount = totalProcessed, SuccessfulProcessedCount = successCount, FailedProcessedCount = failedCount, AverageCycleTimeSeconds = cycleTimes.Average(), MinCycleTimeSeconds = cycleTimes.Min(), MaxCycleTimeSeconds = cycleTimes.Max(), CycleTimeStandardDeviationSeconds = CalculateStandardDeviation(cycleTimes), TargetCycleTimeSeconds = 60.0, CountAchievedCycleTime = cycleTimes.Count(t => t <= 60.0), ByHour = GenerateHourlyCycleTimeStatistics(startTime, endTime, cycleTimes), ByProductType = GenerateProductTypeCycleTimeStatistics(productTypeCode, totalProcessed, successCount, cycleTimes.Average()), Distribution = GenerateCycleTimeDistribution(cycleTimes, 60.0), Trend = GenerateCycleTimeTrend(cycleTimes), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 50, ["data_source"] = "simulated" } }; } /// /// 生成班次统计信息。 /// private async Task GenerateShiftStatisticsAsync(DateTime shiftDate, ShiftType? shiftType, string? productTypeCode, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var shiftInfo = GenerateShiftInfo(shiftDate, shiftType); var plannedProduction = random.Next(200, 400); var actualProduction = (int)(plannedProduction * (0.9 + random.NextDouble() * 0.15)); var qualifiedCount = (int)(actualProduction * (0.92 + random.NextDouble() * 0.06)); return new ShiftStatistics { ShiftInfo = shiftInfo, ProductTypeCode = productTypeCode ?? "default", PlannedProductionCount = plannedProduction, ActualProductionCount = actualProduction, QualifiedProductionCount = qualifiedCount, UnqualifiedProductionCount = actualProduction - qualifiedCount, ShiftStartTimeUtc = shiftDate.Add(shiftInfo.StartTime), ShiftEndTimeUtc = shiftDate.Add(shiftInfo.EndTime), ActualWorkingHours = shiftInfo.DurationHours * (0.85 + random.NextDouble() * 0.1), EquipmentDowntimeHours = shiftInfo.DurationHours * (0.05 + random.NextDouble() * 0.05), AverageCycleTimeSeconds = 55 + random.NextDouble() * 10, ByHour = GenerateHourlyShiftStatistics(shiftInfo, actualProduction, qualifiedCount), ByProductType = GenerateProductTypeShiftStatistics(productTypeCode, actualProduction, qualifiedCount, 60.0), AnomalyStatistics = GenerateShiftAnomalyStatistics(random), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 60, ["data_source"] = "simulated" } }; } /// /// 生成班次列表。 /// private async Task> GenerateShiftListAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var shiftList = new List(); var currentDate = startDate; while (currentDate <= endDate) { // 生成当天的班次 var shifts = new[] { ShiftType.Morning, ShiftType.Afternoon, ShiftType.Night }; foreach (var shiftType in shifts) { var shiftInfo = GenerateShiftInfo(currentDate, shiftType); shiftList.Add(shiftInfo); } currentDate = currentDate.AddDays(1); } return shiftList.AsReadOnly(); } /// /// 生成生产效率统计。 /// private async Task GenerateProductionEfficiencyStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode, StatisticsGranularity granularity, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var timeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }; var plannedHours = (endTime - startTime).TotalHours; var actualHours = plannedHours * (0.85 + random.NextDouble() * 0.1); var equipmentHours = actualHours * (0.9 + random.NextDouble() * 0.08); var plannedProduction = (int)(plannedHours * 50); // 每小时50个 var actualProduction = (int)(plannedProduction * (0.88 + random.NextDouble() * 0.1)); var qualifiedCount = (int)(actualProduction * (0.94 + random.NextDouble() * 0.04)); return new ProductionEfficiencyStatistics { TimeRange = timeRange, ProductTypeCode = productTypeCode ?? "default", Granularity = granularity, PlannedWorkingHours = plannedHours, ActualWorkingHours = actualHours, EquipmentRunningHours = equipmentHours, PlannedProductionCount = plannedProduction, ActualProductionCount = actualProduction, QualifiedProductionCount = qualifiedCount, ByTimeSlot = GenerateTimeSlotEfficiency(startTime, endTime, actualProduction, qualifiedCount, equipmentHours), Trend = GenerateEfficiencyTrend(), Benchmark = GenerateEfficiencyBenchmark(), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 70, ["data_source"] = "simulated" } }; } /// /// 生成质量统计信息。 /// private async Task GenerateQualityStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var timeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }; var totalInspection = random.Next(500, 1000); var qualifiedCount = (int)(totalInspection * (0.92 + random.NextDouble() * 0.05)); var unqualifiedCount = totalInspection - qualifiedCount; return new QualityStatistics { TimeRange = timeRange, ProductTypeCode = productTypeCode ?? "default", TotalInspectionCount = totalInspection, QualifiedCount = qualifiedCount, UnqualifiedCount = unqualifiedCount, ByDefectType = GenerateDefectStatistics(unqualifiedCount), BySeverity = GenerateSeverityStatistics(unqualifiedCount), ByProductType = GenerateProductTypeQualityStatistics(productTypeCode, totalInspection, qualifiedCount), ByTimeSlot = GenerateQualityTimeSlotStatistics(startTime, endTime, totalInspection, qualifiedCount), Trend = GenerateQualityTrend(), ImprovementSuggestions = GenerateQualityImprovementSuggestions(), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 80, ["data_source"] = "simulated" } }; } /// /// 生成设备利用率统计。 /// private async Task GenerateEquipmentUtilizationStatisticsAsync(DateTime startTime, DateTime endTime, string? equipmentId, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var timeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }; var plannedHours = (endTime - startTime).TotalHours; var actualHours = plannedHours * (0.8 + random.NextDouble() * 0.15); var productionHours = actualHours * (0.85 + random.NextDouble() * 0.1); var maintenanceHours = plannedHours * (0.02 + random.NextDouble() * 0.03); var failureHours = plannedHours * (0.01 + random.NextDouble() * 0.02); var idleHours = actualHours - productionHours - maintenanceHours - failureHours; return new EquipmentUtilizationStatistics { TimeRange = timeRange, EquipmentId = equipmentId ?? "EQ001", EquipmentName = "主检测设备", PlannedRunningHours = plannedHours, ActualRunningHours = actualHours, ProductionHours = productionHours, MaintenanceHours = maintenanceHours, FailureHours = failureHours, IdleHours = idleHours, MeanTimeBetweenFailuresHours = 100 + random.NextDouble() * 50, MeanTimeToRepairHours = 2 + random.NextDouble() * 3, ByDate = GenerateDailyUtilizationStatistics(startTime, endTime, plannedHours, actualHours), Trend = GenerateUtilizationTrend(), MaintenanceSuggestions = GenerateMaintenanceSuggestions(), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 90, ["data_source"] = "simulated" } }; } /// /// 生成报警统计摘要。 /// private async Task GenerateAlarmStatisticsSummaryAsync(DateTime startTime, DateTime endTime, AlarmLevel? alarmLevel, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var timeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }; var totalAlarms = random.Next(10, 50); var activeAlarms = random.Next(0, 5); var confirmedAlarms = (int)(totalAlarms * 0.8); var clearedAlarms = (int)(totalAlarms * 0.7); var byAlarmLevel = new Dictionary { [AlarmLevel.Info] = random.Next(5, 15), [AlarmLevel.Warning] = random.Next(3, 10), [AlarmLevel.Error] = random.Next(2, 8), [AlarmLevel.Critical] = random.Next(1, 5), [AlarmLevel.Fatal] = random.Next(0, 2) }; var byAlarmType = new Dictionary { [AlarmType.System] = random.Next(2, 8), [AlarmType.Equipment] = random.Next(3, 10), [AlarmType.Process] = random.Next(2, 6), [AlarmType.Quality] = random.Next(1, 5), [AlarmType.Safety] = random.Next(0, 3) }; return new AlarmStatisticsSummary { TimeRange = timeRange, TotalAlarmCount = totalAlarms, ActiveAlarmCount = activeAlarms, ConfirmedAlarmCount = confirmedAlarms, ClearedAlarmCount = clearedAlarms, ByAlarmLevel = byAlarmLevel, ByAlarmType = byAlarmType, AverageConfirmTimeMinutes = 5 + random.NextDouble() * 10, AverageClearTimeMinutes = 10 + random.NextDouble() * 20, AlarmFrequencyPerHour = totalAlarms / Math.Max((endTime - startTime).TotalHours, 1), Trend = GenerateAlarmTrend(), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 100, ["data_source"] = "simulated" } }; } /// /// 生成综合生产报告。 /// private async Task GenerateComprehensiveProductionReportAsync(ProductionReportType reportType, DateTime startTime, DateTime endTime, string? productTypeCode, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var timeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }; var reportId = Guid.NewGuid(); return new ComprehensiveProductionReport { ReportId = reportId, ReportType = reportType, TimeRange = timeRange, ProductTypeCode = productTypeCode ?? "default", GeneratedAtUtc = DateTime.UtcNow, ExecutiveSummary = GenerateExecutiveSummary(), Production = GenerateProductionStatistics(), Quality = await GenerateQualityStatisticsAsync(startTime, endTime, productTypeCode, cancellationToken), Equipment = GenerateEquipmentStatistics(), Alarms = await GenerateAlarmStatisticsSummaryAsync(startTime, endTime, null, cancellationToken), Trends = GenerateTrendAnalysis(), Recommendations = GenerateImprovementRecommendations(), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 200, ["data_source"] = "simulated" } }; } /// /// 生成实时统计仪表板。 /// private async Task GenerateRealTimeStatisticsDashboardAsync(string? productTypeCode, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var now = DateTime.UtcNow; return new RealTimeStatisticsDashboard { LastUpdatedUtc = now, ProductTypeCode = productTypeCode ?? "default", CurrentShift = GenerateShiftInfo(now.Date, GetCurrentShiftType(now)), Production = GenerateRealTimeProductionMetrics(random), Quality = GenerateRealTimeQualityMetrics(random), Equipment = GenerateRealTimeEquipmentMetrics(random), Alarms = GenerateRealTimeAlarmMetrics(random), Efficiency = GenerateRealTimeEfficiencyMetrics(random), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 30, ["data_source"] = "simulated" } }; } /// /// 生成历史趋势数据。 /// private async Task GenerateHistoricalTrendDataAsync(MetricType metricType, DateTime startTime, DateTime endTime, string? productTypeCode, StatisticsGranularity granularity, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var timeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }; var dataPoints = GenerateTrendDataPoints(metricType, startTime, endTime, granularity, random); return new HistoricalTrendData { MetricType = metricType, Granularity = granularity, TimeRange = timeRange, DataPoints = dataPoints, TrendAnalysis = GenerateTrendAnalysis(), Statistics = GenerateTrendStatisticsSummary(dataPoints), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 120, ["data_source"] = "simulated" } }; } /// /// 生成统计数据导出。 /// private async Task<(string FilePath, long FileSizeBytes, int RecordCount)> GenerateStatisticsExportAsync(StatisticsExportRequest exportRequest, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var fileName = $"statistics_export_{exportRequest.RequestId:N}.{exportRequest.ExportFormat.ToString().ToLower()}"; var filePath = Path.Combine(Path.GetTempPath(), fileName); // 简化处理:生成模拟文件 var recordCount = 1000; var fileContent = GenerateExportFileContent(exportRequest, recordCount); await File.WriteAllTextAsync(filePath, fileContent, cancellationToken); var fileInfo = new FileInfo(filePath); return (filePath, fileInfo.Length, recordCount); } #endregion #region 辅助方法 /// /// 计算标准差。 /// private double CalculateStandardDeviation(IEnumerable values) { var valuesList = values.ToList(); var mean = valuesList.Average(); var variance = valuesList.Sum(x => Math.Pow(x - mean, 2)) / valuesList.Count; return Math.Sqrt(variance); } /// /// 生成按小时分组的节拍统计。 /// private Dictionary GenerateHourlyCycleTimeStatistics(DateTime startTime, DateTime endTime, List cycleTimes) { var hourlyStats = new Dictionary(); var random = new Random(); for (int hour = 0; hour < 24; hour++) { var count = random.Next(5, 20); var successCount = (int)(count * (0.85 + random.NextDouble() * 0.1)); var avgCycleTime = 50 + random.NextDouble() * 20; hourlyStats[hour] = new HourlyCycleTimeStatistics { Hour = hour, ProcessedCount = count, SuccessCount = successCount, AverageCycleTimeSeconds = avgCycleTime }; } return hourlyStats; } /// /// 生成按产品类型分组的节拍统计。 /// private Dictionary GenerateProductTypeCycleTimeStatistics(string? productTypeCode, int totalProcessed, int successCount, double avgCycleTime) { return new Dictionary { [productTypeCode ?? "default"] = new ProductTypeCycleTimeStatistics { ProductTypeCode = productTypeCode ?? "default", ProcessedCount = totalProcessed, SuccessCount = successCount, AverageCycleTimeSeconds = avgCycleTime } }; } /// /// 生成节拍时间分布。 /// private CycleTimeDistribution GenerateCycleTimeDistribution(List cycleTimes, double targetTime) { var belowTarget = cycleTimes.Count(t => t < targetTime * 0.9); var withinRange = cycleTimes.Count(t => t >= targetTime * 0.9 && t <= targetTime * 1.1); var aboveRange = cycleTimes.Count - belowTarget - withinRange; var total = cycleTimes.Count; return new CycleTimeDistribution { BelowTargetCount = belowTarget, WithinTargetRangeCount = withinRange, AboveTargetRangeCount = aboveRange, BelowTargetPercentage = (double)belowTarget / total * 100, WithinTargetRangePercentage = (double)withinRange / total * 100, AboveTargetRangePercentage = (double)aboveRange / total * 100 }; } /// /// 生成节拍趋势分析。 /// private CycleTimeTrend GenerateCycleTimeTrend(List cycleTimes) { var random = new Random(); var direction = random.NextDouble() > 0.5 ? TrendDirection.Increasing : TrendDirection.Decreasing; var changeRate = random.NextDouble() * 10 - 5; // -5% to +5% return new CycleTimeTrend { Direction = direction, ChangeRatePercentage = changeRate, TrendDescription = $"节拍时间呈{direction}趋势,变化率{changeRate:F2}%", PredictedNextCycleTimeSeconds = cycleTimes.Average() * (1 + changeRate / 100), Confidence = 0.7 + random.NextDouble() * 0.2 }; } /// /// 生成班次信息。 /// private ShiftInfo GenerateShiftInfo(DateTime shiftDate, ShiftType? shiftType) { var type = shiftType ?? ShiftType.Morning; var (startTime, endTime) = type switch { ShiftType.Morning => (new TimeSpan(8, 0, 0), new TimeSpan(16, 0, 0)), ShiftType.Afternoon => (new TimeSpan(16, 0, 0), new TimeSpan(0, 0, 0)), ShiftType.Night => (new TimeSpan(0, 0, 0), new TimeSpan(8, 0, 0)), _ => (new TimeSpan(8, 0, 0), new TimeSpan(16, 0, 0)) }; return new ShiftInfo { ShiftId = Guid.NewGuid(), ShiftDate = shiftDate, ShiftType = type, ShiftName = $"{type}班", StartTime = startTime, EndTime = endTime, Operators = new[] { "操作员A", "操作员B" }, Supervisor = "主管A" }; } /// /// 生成按小时分组的班次统计。 /// private Dictionary GenerateHourlyShiftStatistics(ShiftInfo shiftInfo, int actualProduction, int qualifiedCount) { var hourlyStats = new Dictionary(); var random = new Random(); var startHour = (int)shiftInfo.StartTime.TotalHours; var endHour = (int)shiftInfo.EndTime.TotalHours; for (int hour = startHour; hour < endHour; hour++) { var hourProduction = random.Next(5, 15); var hourQualified = (int)(hourProduction * (0.9 + random.NextDouble() * 0.08)); hourlyStats[hour] = new HourlyShiftStatistics { Hour = hour, ProductionCount = hourProduction, QualifiedCount = hourQualified, EquipmentRunningMinutes = 50 + random.NextDouble() * 10, EquipmentDowntimeMinutes = random.NextDouble() * 5 }; } return hourlyStats; } /// /// 生成按产品类型分组的班次统计。 /// private Dictionary GenerateProductTypeShiftStatistics(string? productTypeCode, int actualProduction, int qualifiedCount, double avgCycleTime) { return new Dictionary { [productTypeCode ?? "default"] = new ProductTypeShiftStatistics { ProductTypeCode = productTypeCode ?? "default", ProductionCount = actualProduction, QualifiedCount = qualifiedCount, AverageCycleTimeSeconds = avgCycleTime } }; } /// /// 生成班次异常统计。 /// private ShiftAnomalyStatistics GenerateShiftAnomalyStatistics(Random random) { var totalAnomalies = random.Next(0, 10); return new ShiftAnomalyStatistics { TotalAnomalyCount = totalAnomalies, EquipmentFailureCount = random.Next(0, totalAnomalies / 2), QualityAnomalyCount = random.Next(0, totalAnomalies / 2), ProcessAnomalyCount = random.Next(0, totalAnomalies / 2), DowntimeMinutes = random.NextDouble() * 30, AnomalyRate = totalAnomalies > 0 ? random.NextDouble() * 5 : 0.0 }; } /// /// 生成时间段效率。 /// private Dictionary GenerateTimeSlotEfficiency(DateTime startTime, DateTime endTime, int actualProduction, int qualifiedCount, double equipmentHours) { var timeSlotEfficiency = new Dictionary(); var random = new Random(); var current = startTime; while (current < endTime) { var slotProduction = random.Next(5, 20); var slotQualified = (int)(slotProduction * (0.9 + random.NextDouble() * 0.08)); var slotEfficiency = 0.7 + random.NextDouble() * 0.2; timeSlotEfficiency[current] = new TimeSlotEfficiency { TimeSlot = current, ProductionCount = slotProduction, QualifiedCount = slotQualified, EquipmentRunningMinutes = 50 + random.NextDouble() * 10, OverallEfficiency = slotEfficiency * 100 }; current = current.AddHours(1); } return timeSlotEfficiency; } /// /// 生成效率趋势分析。 /// private EfficiencyTrend GenerateEfficiencyTrend() { var random = new Random(); var direction = random.NextDouble() > 0.5 ? TrendDirection.Increasing : TrendDirection.Decreasing; var changeRate = random.NextDouble() * 8 - 4; // -4% to +4% return new EfficiencyTrend { Direction = direction, ChangeRatePercentage = changeRate, TrendDescription = $"效率呈{direction}趋势,变化率{changeRate:F2}%", PredictedNextEfficiency = 75 + random.NextDouble() * 15, Confidence = 0.6 + random.NextDouble() * 0.3 }; } /// /// 生成效率基准对比。 /// private EfficiencyBenchmark GenerateEfficiencyBenchmark() { var random = new Random(); return new EfficiencyBenchmark { HistoricalBestEfficiency = 85 + random.NextDouble() * 10, HistoricalAverageEfficiency = 75 + random.NextDouble() * 8, IndustryBenchmarkEfficiency = 80 + random.NextDouble() * 10, TargetEfficiency = 82 + random.NextDouble() * 8 }; } /// /// 生成缺陷统计。 /// private Dictionary GenerateDefectStatistics(int totalDefects) { var random = new Random(); var defectStats = new Dictionary(); var defectTypes = new[] { DefectType.Appearance, DefectType.Dimension, DefectType.Function, DefectType.Material, DefectType.Process }; var remainingDefects = totalDefects; foreach (var defectType in defectTypes) { if (remainingDefects <= 0) break; var count = random.Next(0, remainingDefects / 2 + 1); remainingDefects -= count; defectStats[defectType] = new DefectStatistics { DefectType = defectType, DefectCount = count, Percentage = totalDefects > 0 ? (double)count / totalDefects * 100 : 0, AverageSeverity = 1 + random.NextDouble() * 2 }; } return defectStats; } /// /// 生成严重程度统计。 /// private Dictionary GenerateSeverityStatistics(int totalDefects) { var random = new Random(); var severityStats = new Dictionary(); var severities = new[] { DefectSeverity.Minor, DefectSeverity.Normal, DefectSeverity.Serious, DefectSeverity.Critical }; var remainingDefects = totalDefects; foreach (var severity in severities) { if (remainingDefects <= 0) break; var count = random.Next(0, remainingDefects / 2 + 1); remainingDefects -= count; severityStats[severity] = new SeverityStatistics { Severity = severity, DefectCount = count, Percentage = totalDefects > 0 ? (double)count / totalDefects * 100 : 0 }; } return severityStats; } /// /// 生成按产品类型分组的质量统计。 /// private Dictionary GenerateProductTypeQualityStatistics(string? productTypeCode, int totalInspection, int qualifiedCount) { return new Dictionary { [productTypeCode ?? "default"] = new ProductTypeQualityStatistics { ProductTypeCode = productTypeCode ?? "default", InspectionCount = totalInspection, QualifiedCount = qualifiedCount } }; } /// /// 生成按检测时间的质量统计。 /// private Dictionary GenerateQualityTimeSlotStatistics(DateTime startTime, DateTime endTime, int totalInspection, int qualifiedCount) { var timeSlotStats = new Dictionary(); var random = new Random(); var current = startTime; while (current < endTime) { var slotInspection = random.Next(10, 30); var slotQualified = (int)(slotInspection * (0.9 + random.NextDouble() * 0.08)); timeSlotStats[current] = new QualityTimeSlotStatistics { InspectionTime = current, InspectionCount = slotInspection, QualifiedCount = slotQualified }; current = current.AddHours(2); } return timeSlotStats; } /// /// 生成质量趋势分析。 /// private QualityTrend GenerateQualityTrend() { var random = new Random(); var direction = random.NextDouble() > 0.3 ? TrendDirection.Stable : (random.NextDouble() > 0.5 ? TrendDirection.Increasing : TrendDirection.Decreasing); var changeRate = random.NextDouble() * 6 - 3; // -3% to +3% return new QualityTrend { Direction = direction, ChangeRatePercentage = changeRate, TrendDescription = $"质量呈{direction}趋势,变化率{changeRate:F2}%", PredictedNextPassRate = 92 + random.NextDouble() * 6, Confidence = 0.7 + random.NextDouble() * 0.2 }; } /// /// 生成质量改进建议。 /// private IReadOnlyList GenerateQualityImprovementSuggestions() { var random = new Random(); var suggestions = new List(); var suggestionTypes = new[] { SuggestionType.ProcessImprovement, SuggestionType.EquipmentMaintenance, SuggestionType.OperatorTraining }; var difficulties = new[] { ImplementationDifficulty.Easy, ImplementationDifficulty.Medium, ImplementationDifficulty.Difficult }; foreach (var type in suggestionTypes.Take(2)) { suggestions.Add(new QualityImprovementSuggestion { SuggestionId = Guid.NewGuid(), SuggestionType = type, Title = $"{type}建议", Description = $"通过{type}提升产品质量", Priority = random.Next(1, 5), ExpectedImpact = "预计提升合格率2-5%", Difficulty = difficulties[random.Next(difficulties.Length)] }); } return suggestions.AsReadOnly(); } /// /// 生成按日期分组的利用率统计。 /// private Dictionary GenerateDailyUtilizationStatistics(DateTime startTime, DateTime endTime, double plannedHours, double actualHours) { var dailyStats = new Dictionary(); var random = new Random(); var current = startTime.Date; while (current <= endTime.Date) { dailyStats[current] = new DailyUtilizationStatistics { Date = current, PlannedRunningHours = 24, ActualRunningHours = 20 + random.NextDouble() * 4 }; current = current.AddDays(1); } return dailyStats; } /// /// 生成利用率趋势分析。 /// private UtilizationTrend GenerateUtilizationTrend() { var random = new Random(); var direction = random.NextDouble() > 0.4 ? TrendDirection.Stable : TrendDirection.Fluctuating; var changeRate = random.NextDouble() * 4 - 2; // -2% to +2% return new UtilizationTrend { Direction = direction, ChangeRatePercentage = changeRate, TrendDescription = $"设备利用率呈{direction}趋势,变化率{changeRate:F2}%", PredictedNextUtilization = 80 + random.NextDouble() * 15, Confidence = 0.6 + random.NextDouble() * 0.3 }; } /// /// 生成维护建议。 /// private IReadOnlyList GenerateMaintenanceSuggestions() { var random = new Random(); var suggestions = new List(); var maintenanceTypes = new[] { MaintenanceType.Preventive, MaintenanceType.Predictive, MaintenanceType.Corrective }; foreach (var type in maintenanceTypes.Take(2)) { suggestions.Add(new MaintenanceSuggestion { SuggestionId = Guid.NewGuid(), MaintenanceType = type, Title = $"{type}维护建议", Description = $"建议进行{type}维护", Priority = random.Next(1, 5), EstimatedMaintenanceHours = 2 + random.NextDouble() * 4, EstimatedCost = 1000 + random.NextDouble() * 3000, SuggestedMaintenanceTime = DateTime.UtcNow.AddDays(random.Next(1, 7)) }); } return suggestions.AsReadOnly(); } /// /// 生成报警趋势分析。 /// private AlarmTrend GenerateAlarmTrend() { var random = new Random(); var direction = random.NextDouble() > 0.6 ? TrendDirection.Stable : (random.NextDouble() > 0.5 ? TrendDirection.Increasing : TrendDirection.Decreasing); var changeRate = random.NextDouble() * 15 - 7.5; // -7.5% to +7.5% return new AlarmTrend { Direction = direction, ChangeRatePercentage = changeRate, TrendDescription = $"报警数量呈{direction}趋势,变化率{changeRate:F2}%", PredictedNextAlarmCount = random.Next(5, 25), Confidence = 0.5 + random.NextDouble() * 0.4 }; } /// /// 生成执行摘要。 /// private ExecutiveSummary GenerateExecutiveSummary() { var random = new Random(); return new ExecutiveSummary { OverallScore = 75 + random.NextDouble() * 15, KeyMetrics = new Dictionary { ["生产完成率"] = 85 + random.NextDouble() * 10, ["质量合格率"] = 92 + random.NextDouble() * 6, ["设备利用率"] = 80 + random.NextDouble() * 15, ["OEE"] = 70 + random.NextDouble() * 20 }, Achievements = new[] { "生产效率提升5%", "质量指标达标" }, Issues = new[] { "设备故障率偏高", "人员操作需优化" }, SummaryDescription = "整体运行状况良好,部分指标需持续改进" }; } /// /// 生成生产统计。 /// private ProductionStatistics GenerateProductionStatistics() { var random = new Random(); return new ProductionStatistics { PlannedProduction = 1000, ActualProduction = (int)(1000 * (0.88 + random.NextDouble() * 0.1)), QualifiedProduction = (int)(950 * (0.92 + random.NextDouble() * 0.06)), AverageCycleTimeSeconds = 55 + random.NextDouble() * 10 }; } /// /// 生成设备统计。 /// private EquipmentStatistics GenerateEquipmentStatistics() { var random = new Random(); return new EquipmentStatistics { EquipmentCount = 5, AverageUtilizationRate = 75 + random.NextDouble() * 15, AverageAvailabilityRate = 85 + random.NextDouble() * 10, TotalDowntimeHours = 2 + random.NextDouble() * 3 }; } /// /// 生成趋势分析。 /// private TrendAnalysis GenerateTrendAnalysis() { var random = new Random(); return new TrendAnalysis { ProductionTrend = TrendDirection.Stable, QualityTrend = TrendDirection.Increasing, EquipmentTrend = TrendDirection.Fluctuating, AlarmTrend = TrendDirection.Decreasing, TrendDescription = "生产稳定,质量提升,设备需关注" }; } /// /// 生成改进建议。 /// private IReadOnlyList GenerateImprovementRecommendations() { var random = new Random(); var recommendations = new List(); var categories = new[] { RecommendationCategory.ProductionOptimization, RecommendationCategory.QualityImprovement, RecommendationCategory.EquipmentMaintenance }; foreach (var category in categories.Take(3)) { recommendations.Add(new ImprovementRecommendation { RecommendationId = Guid.NewGuid(), Category = category, Title = $"{category}建议", Description = $"建议进行{category}改进", Priority = random.Next(1, 5), ExpectedBenefit = "预计提升效率3-8%", ImplementationCost = 5000 + random.NextDouble() * 10000, ReturnOnInvestment = 150 + random.NextDouble() * 100 }); } return recommendations.AsReadOnly(); } /// /// 生成实时生产指标。 /// private RealTimeProductionMetrics GenerateRealTimeProductionMetrics(Random random) { return new RealTimeProductionMetrics { CurrentHourProduction = random.Next(8, 15), CurrentShiftProduction = random.Next(80, 120), TodayProduction = random.Next(200, 300), CurrentCycleTimeSeconds = 50 + random.NextDouble() * 15, CycleTimeAchievementRate = 85 + random.NextDouble() * 10, ProductionSpeedTrend = TrendDirection.Stable }; } /// /// 生成实时质量指标。 /// private RealTimeQualityMetrics GenerateRealTimeQualityMetrics(Random random) { return new RealTimeQualityMetrics { CurrentHourPassRate = 92 + random.NextDouble() * 6, CurrentShiftPassRate = 93 + random.NextDouble() * 5, TodayPassRate = 94 + random.NextDouble() * 4, RecentDefectCount = random.Next(0, 5), QualityTrend = TrendDirection.Stable }; } /// /// 生成实时设备指标。 /// private RealTimeEquipmentMetrics GenerateRealTimeEquipmentMetrics(Random random) { return new RealTimeEquipmentMetrics { RunningEquipmentCount = 4, DownEquipmentCount = 1, CurrentUtilizationRate = 78 + random.NextDouble() * 12, CurrentAvailabilityRate = 88 + random.NextDouble() * 8, EquipmentStatusTrend = TrendDirection.Stable }; } /// /// 生成实时报警指标。 /// private RealTimeAlarmMetrics GenerateRealTimeAlarmMetrics(Random random) { return new RealTimeAlarmMetrics { ActiveAlarmCount = random.Next(0, 3), TodayNewAlarmCount = random.Next(5, 15), CriticalAlarmCount = random.Next(0, 2), LastAlarmTime = random.NextDouble() > 0.3 ? DateTime.UtcNow.AddMinutes(-random.Next(10, 120)) : null, AlarmTrend = TrendDirection.Stable }; } /// /// 生成实时效率指标。 /// private RealTimeEfficiencyMetrics GenerateRealTimeEfficiencyMetrics(Random random) { return new RealTimeEfficiencyMetrics { CurrentOEE = 72 + random.NextDouble() * 16, TimeUtilizationRate = 85 + random.NextDouble() * 10, EquipmentUtilizationRate = 80 + random.NextDouble() * 15, PerformanceEfficiency = 88 + random.NextDouble() * 8, EfficiencyTrend = TrendDirection.Stable }; } /// /// 生成趋势数据点。 /// private IReadOnlyList GenerateTrendDataPoints(MetricType metricType, DateTime startTime, DateTime endTime, StatisticsGranularity granularity, Random random) { var dataPoints = new List(); var current = startTime; var interval = granularity switch { StatisticsGranularity.Minutely => TimeSpan.FromMinutes(1), StatisticsGranularity.Hourly => TimeSpan.FromHours(1), StatisticsGranularity.Daily => TimeSpan.FromDays(1), _ => TimeSpan.FromHours(1) }; while (current <= endTime) { var value = GetMetricValue(metricType, random); dataPoints.Add(new TrendDataPoint { Timestamp = current, Value = value, Unit = GetMetricUnit(metricType) }); current = current.Add(interval); } return dataPoints.AsReadOnly(); } /// /// 获取指标值。 /// private double GetMetricValue(MetricType metricType, Random random) { return metricType switch { MetricType.Production => 50 + random.NextDouble() * 30, MetricType.Quality => 90 + random.NextDouble() * 8, MetricType.EquipmentUtilization => 75 + random.NextDouble() * 20, MetricType.CycleTime => 45 + random.NextDouble() * 25, MetricType.OEE => 70 + random.NextDouble() * 20, MetricType.AlarmCount => random.NextDouble() * 10, MetricType.Downtime => random.NextDouble() * 5, MetricType.Efficiency => 80 + random.NextDouble() * 15, _ => random.NextDouble() * 100 }; } /// /// 获取指标单位。 /// private string GetMetricUnit(MetricType metricType) { return metricType switch { MetricType.Production => "个", MetricType.Quality => "%", MetricType.EquipmentUtilization => "%", MetricType.CycleTime => "秒", MetricType.OEE => "%", MetricType.AlarmCount => "个", MetricType.Downtime => "小时", MetricType.Efficiency => "%", _ => "" }; } /// /// 生成趋势统计摘要。 /// private TrendStatisticsSummary GenerateTrendStatisticsSummary(IReadOnlyList dataPoints) { if (!dataPoints.Any()) { return new TrendStatisticsSummary(); } var values = dataPoints.Select(dp => dp.Value).ToList(); var sortedValues = values.OrderBy(v => v).ToList(); return new TrendStatisticsSummary { DataPointCount = dataPoints.Count, MinValue = sortedValues.First(), MaxValue = sortedValues.Last(), AverageValue = values.Average(), MedianValue = sortedValues[sortedValues.Count / 2], StandardDeviation = CalculateStandardDeviation(values) }; } /// /// 生成导出文件内容。 /// private string GenerateExportFileContent(StatisticsExportRequest exportRequest, int recordCount) { var random = new Random(); var content = new System.Text.StringBuilder(); // 简化处理:生成CSV格式内容 content.AppendLine("ID,时间,数值,单位"); for (int i = 0; i < recordCount; i++) { var timestamp = DateTime.UtcNow.AddMinutes(-random.Next(0, 1440)); var value = random.NextDouble() * 100; var unit = "个"; content.AppendLine($"{i + 1},{timestamp:yyyy-MM-dd HH:mm:ss},{value:F2},{unit}"); } return content.ToString(); } /// /// 获取当前班次类型。 /// private ShiftType GetCurrentShiftType(DateTime dateTime) { var hour = dateTime.Hour; return hour switch { >= 8 and < 16 => ShiftType.Morning, >= 16 and < 24 => ShiftType.Afternoon, _ => ShiftType.Night }; } #endregion } #endif /// /// 统计服务实现(最小可编译版本)。 /// public sealed class StatisticsService : IStatisticsService { private readonly ILogger _logger; public StatisticsService(ILogger logger, IOptions options) { _logger = logger; } public Task> GetCycleTimeStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, CancellationToken cancellationToken = default) { var data = new CycleTimeStatistics { TimeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }, ProductTypeCode = productTypeCode ?? string.Empty, TotalProcessedCount = 0, SuccessfulProcessedCount = 0, FailedProcessedCount = 0, AverageCycleTimeSeconds = 0, MinCycleTimeSeconds = 0, MaxCycleTimeSeconds = 0, CycleTimeStandardDeviationSeconds = 0, TargetCycleTimeSeconds = 0, CountAchievedCycleTime = 0 }; return Task.FromResult(Result.Success(data)); } public Task> GetShiftStatisticsAsync(DateTime shiftDate, ShiftType? shiftType = null, string? productTypeCode = null, CancellationToken cancellationToken = default) { var actualShiftType = shiftType ?? ShiftType.Morning; var data = new ShiftStatistics { ShiftInfo = new ShiftInfo { ShiftId = Guid.NewGuid(), ShiftDate = shiftDate.Date, ShiftType = actualShiftType, ShiftName = actualShiftType.ToString(), StartTime = TimeSpan.Zero, EndTime = TimeSpan.FromHours(8) }, ProductTypeCode = productTypeCode ?? string.Empty, PlannedProductionCount = 0, ActualProductionCount = 0, QualifiedProductionCount = 0, UnqualifiedProductionCount = 0, ShiftStartTimeUtc = shiftDate, ShiftEndTimeUtc = shiftDate.AddHours(8), ActualWorkingHours = 0, EquipmentDowntimeHours = 0, AverageCycleTimeSeconds = 0 }; return Task.FromResult(Result.Success(data)); } public Task>> GetShiftListAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default) { IReadOnlyList data = Array.Empty(); return Task.FromResult(Result>.Success(data)); } public Task> GetProductionEfficiencyStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, StatisticsGranularity granularity = StatisticsGranularity.Hourly, CancellationToken cancellationToken = default) { var data = new ProductionEfficiencyStatistics { TimeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }, ProductTypeCode = productTypeCode ?? string.Empty, Granularity = granularity, PlannedWorkingHours = 0, ActualWorkingHours = 0, EquipmentRunningHours = 0, PlannedProductionCount = 0, ActualProductionCount = 0, QualifiedProductionCount = 0 }; return Task.FromResult(Result.Success(data)); } public Task> GetQualityStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, CancellationToken cancellationToken = default) { var data = new QualityStatistics { TimeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }, ProductTypeCode = productTypeCode ?? string.Empty, TotalInspectionCount = 0, QualifiedCount = 0, UnqualifiedCount = 0 }; return Task.FromResult(Result.Success(data)); } public Task> GetEquipmentUtilizationStatisticsAsync(DateTime startTime, DateTime endTime, string? equipmentId = null, CancellationToken cancellationToken = default) { var data = new EquipmentUtilizationStatistics { TimeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }, EquipmentId = equipmentId ?? string.Empty, PlannedRunningHours = 0, ActualRunningHours = 0, ProductionHours = 0, MaintenanceHours = 0, FailureHours = 0, IdleHours = 0 }; return Task.FromResult(Result.Success(data)); } public Task> GetAlarmStatisticsSummaryAsync(DateTime startTime, DateTime endTime, OrpaonVision.Core.Common.AlarmLevel? alarmLevel = null, CancellationToken cancellationToken = default) { var data = new AlarmStatisticsSummary { TimeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }, TotalAlarmCount = 0, ActiveAlarmCount = 0, ConfirmedAlarmCount = 0, ClearedAlarmCount = 0 }; return Task.FromResult(Result.Success(data)); } public Task> GetComprehensiveProductionReportAsync(OrpaonVision.Core.Statistics.ProductionReportType reportType, DateTime startTime, DateTime endTime, string? productTypeCode = null, CancellationToken cancellationToken = default) { var data = new ComprehensiveProductionReport { ReportId = Guid.NewGuid(), ReportType = reportType, TimeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }, ProductTypeCode = productTypeCode ?? string.Empty, GeneratedAtUtc = DateTime.UtcNow }; return Task.FromResult(Result.Success(data)); } public Task> GetRealTimeStatisticsDashboardAsync(string? productTypeCode = null, CancellationToken cancellationToken = default) { var data = new RealTimeStatisticsDashboard { ProductTypeCode = productTypeCode ?? string.Empty, LastUpdatedUtc = DateTime.UtcNow }; return Task.FromResult(Result.Success(data)); } public Task> GetHistoricalTrendDataAsync(MetricType metricType, DateTime startTime, DateTime endTime, string? productTypeCode = null, StatisticsGranularity granularity = StatisticsGranularity.Hourly, CancellationToken cancellationToken = default) { var data = new HistoricalTrendData { MetricType = metricType, TimeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }, Granularity = granularity }; return Task.FromResult(Result.Success(data)); } public Task> CreateStatisticsConfigAsync(StatisticsConfig config, CancellationToken cancellationToken = default) { return Task.FromResult(Result.Success(config)); } public Task UpdateStatisticsConfigAsync(StatisticsConfig config, CancellationToken cancellationToken = default) { return Task.FromResult(Result.Success()); } public Task> GetStatisticsConfigAsync(StatisticsConfigType configType, string? productTypeCode = null, CancellationToken cancellationToken = default) { var config = new StatisticsConfig { ConfigId = Guid.NewGuid(), ConfigType = configType, ProductTypeCode = productTypeCode ?? string.Empty, ConfigName = $"{configType}-default", IsEnabled = true, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }; return Task.FromResult(Result.Success(config)); } public Task> ExportStatisticsAsync(StatisticsExportRequest exportRequest, CancellationToken cancellationToken = default) { _logger.LogInformation("统计导出请求: {RequestId}", exportRequest.RequestId); var result = new StatisticsExportResult { RequestId = exportRequest.RequestId, IsSuccess = true, FilePath = string.Empty, FileSizeBytes = 0, RecordCount = 0, ExportElapsedMs = 0, ExportTimeUtc = DateTime.UtcNow, ResultDescription = "OK" }; return Task.FromResult(Result.Success(result)); } }