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.

425 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Haoliang.Core.Services;
using Haoliang.Models.Models.System;
using Haoliang.Models.Production;
using Haoliang.Models.Common;
namespace Haoliang.Api.Controllers
{
[Route("api/v1/statistics")]
[ApiController]
public class StatisticsController : ControllerBase
{
private readonly IProductionStatisticsService _statisticsService;
public StatisticsController(IProductionStatisticsService statisticsService)
{
_statisticsService = statisticsService;
}
/// <summary>
/// Calculate production trends for a specific device and time range
/// </summary>
[HttpGet("production-trends")]
public async Task<ActionResult<ApiResponse<ProductionTrendAnalysis>>> GetProductionTrends(
[FromQuery] int deviceId,
[FromQuery] DateTime startDate,
[FromQuery] DateTime endDate)
{
try
{
if (deviceId <= 0)
return BadRequest(ApiResponse<ProductionTrendAnalysis>.BadRequestResult("Invalid device ID"));
if (startDate >= endDate)
return BadRequest(ApiResponse<ProductionTrendAnalysis>.BadRequestResult("Start date must be before end date"));
var result = await _statisticsService.CalculateProductionTrendsAsync(deviceId, startDate, endDate);
return Ok(ApiResponse<ProductionTrendAnalysis>.Ok(result));
}
catch (KeyNotFoundException ex)
{
return NotFound(ApiResponse<ProductionTrendAnalysis>.NotFound(ex.Message));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<ProductionTrendAnalysis>.InternalServerErrorResult($"Error calculating production trends: {ex.Message}"));
}
}
/// <summary>
/// Generate comprehensive production report
/// </summary>
[HttpGet("production-report")]
public async Task<ActionResult<ApiResponse<ProductionReport>>> GetProductionReport([FromQuery] ReportFilter filter)
{
try
{
if (filter.StartDate >= filter.EndDate)
return BadRequest(ApiResponse<ProductionReport>.BadRequestResult("Start date must be before end date"));
var result = await _statisticsService.GenerateProductionReportAsync(filter);
return Ok(ApiResponse<ProductionReport>.Ok(result));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<ProductionReport>.InternalServerErrorResult($"Error generating production report: {ex.Message}"));
}
}
/// <summary>
/// Calculate efficiency metrics for devices or programs
/// </summary>
[HttpGet("efficiency")]
public async Task<ActionResult<ApiResponse<EfficiencyMetrics>>> GetEfficiencyMetrics([FromQuery] EfficiencyFilter filter)
{
try
{
if (filter.StartDate >= filter.EndDate)
return BadRequest(ApiResponse<EfficiencyMetrics>.BadRequestResult("Start date must be before end date"));
var result = await _statisticsService.CalculateEfficiencyMetricsAsync(filter);
return Ok(ApiResponse<EfficiencyMetrics>.Ok(result));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<EfficiencyMetrics>.InternalServerErrorResult($"Error calculating efficiency metrics: {ex.Message}"));
}
}
/// <summary>
/// Perform quality analysis based on production data
/// </summary>
[HttpGet("quality")]
public async Task<ActionResult<ApiResponse<QualityAnalysis>>> GetQualityAnalysis([FromQuery] QualityFilter filter)
{
try
{
if (filter.StartDate >= filter.EndDate)
return BadRequest(ApiResponse<QualityAnalysis>.BadRequestResult("Start date must be before end date"));
var result = await _statisticsService.PerformQualityAnalysisAsync(filter);
return Ok(ApiResponse<QualityAnalysis>.Ok(result));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<QualityAnalysis>.InternalServerErrorResult($"Error performing quality analysis: {ex.Message}"));
}
}
/// <summary>
/// Get production summary for dashboard display
/// </summary>
[HttpGet("dashboard-summary")]
public async Task<ActionResult<ApiResponse<DashboardSummary>>> GetDashboardSummary([FromQuery] DashboardFilter filter)
{
try
{
var result = await _statisticsService.GetDashboardSummaryAsync(filter);
return Ok(ApiResponse<DashboardSummary>.Ok(result));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<DashboardSummary>.InternalServerErrorResult($"Error getting dashboard summary: {ex.Message}"));
}
}
/// <summary>
/// Calculate OEE (Overall Equipment Effectiveness) for a specific device
/// </summary>
[HttpGet("oee")]
public async Task<ActionResult<ApiResponse<OeeMetrics>>> GetOeeMetrics(
[FromQuery] int deviceId,
[FromQuery] DateTime date)
{
try
{
if (deviceId <= 0)
return BadRequest(ApiResponse<OeeMetrics>.BadRequestResult("Invalid device ID"));
var result = await _statisticsService.CalculateOeeAsync(deviceId, date);
return Ok(ApiResponse<OeeMetrics>.Ok(result));
}
catch (KeyNotFoundException ex)
{
return NotFound(ApiResponse<OeeMetrics>.NotFoundResult(ex.Message));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<OeeMetrics>.InternalServerErrorResult($"Error calculating OEE metrics: {ex.Message}"));
}
}
/// <summary>
/// Get production forecasts based on historical data
/// </summary>
[HttpGet("forecast")]
public async Task<ActionResult<ApiResponse<ProductionForecast>>> GetProductionForecast([FromQuery] ForecastFilter filter)
{
try
{
if (filter.DeviceId <= 0)
return BadRequest(ApiResponse<ProductionForecast>.BadRequestResult("Invalid device ID"));
if (filter.DaysToForecast <= 0 || filter.DaysToForecast > 365)
return BadRequest(ApiResponse<ProductionForecast>.BadRequestResult("Days to forecast must be between 1 and 365"));
var result = await _statisticsService.GenerateProductionForecastAsync(filter);
return Ok(ApiResponse<ProductionForecast>.Ok(result));
}
catch (KeyNotFoundException ex)
{
return NotFound(ApiResponse<ProductionForecast>.NotFoundResult(ex.Message));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<ProductionForecast>.InternalServerErrorResult($"Error generating production forecast: {ex.Message}"));
}
}
/// <summary>
/// Detect production anomalies and outliers
/// </summary>
[HttpGet("anomalies")]
public async Task<ActionResult<ApiResponse<AnomalyAnalysis>>> DetectProductionAnomalies([FromQuery] AnomalyFilter filter)
{
try
{
if (filter.StartDate >= filter.EndDate)
return BadRequest(ApiResponse<AnomalyAnalysis>.BadRequestResult("Start date must be before end date"));
var result = await _statisticsService.DetectProductionAnomaliesAsync(filter);
return Ok(ApiResponse<AnomalyAnalysis>.Ok(result));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<AnomalyAnalysis>.InternalServerErrorResult($"Error detecting production anomalies: {ex.Message}"));
}
}
/// <summary>
/// Get available devices for statistics
/// </summary>
[HttpGet("devices")]
public async Task<ActionResult<ApiResponse<List<DeviceSummary>>>> GetAvailableDevices([FromQuery] bool activeOnly = true)
{
try
{
// This would typically get devices from device service
// For now, returning empty list
var result = new List<DeviceSummary>();
return Ok(ApiResponse<List<DeviceSummary>>.Ok(result));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<List<DeviceSummary>>.InternalServerErrorResult($"Error getting available devices: {ex.Message}"));
}
}
/// <summary>
/// Get production summary for multiple devices
/// </summary>
[HttpGet("multi-device-summary")]
public async Task<ActionResult<ApiResponse<MultiDeviceSummary>>> GetMultiDeviceSummary([FromQuery] List<int> deviceIds)
{
try
{
var filter = new DashboardFilter
{
DeviceIds = deviceIds,
Date = DateTime.Today,
IncludeAlerts = true
};
var dashboardSummary = await _statisticsService.GetDashboardSummaryAsync(filter);
var multiDeviceSummary = new MultiDeviceSummary
{
GeneratedAt = dashboardSummary.GeneratedAt,
DeviceCount = dashboardSummary.TotalDevices,
ActiveDeviceCount = dashboardSummary.ActiveDevices,
OfflineDeviceCount = dashboardSummary.OfflineDevices,
TotalProductionToday = dashboardSummary.TotalProductionToday,
TotalProductionThisWeek = dashboardSummary.TotalProductionThisWeek,
TotalProductionThisMonth = dashboardSummary.TotalProductionThisMonth,
OverallEfficiency = dashboardSummary.OverallEfficiency,
OverallQualityRate = dashboardSummary.QualityRate,
DeviceSummaries = dashboardSummary.DeviceSummaries
};
return Ok(ApiResponse<MultiDeviceSummary>.Ok(multiDeviceSummary));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<MultiDeviceSummary>.InternalServerErrorResult($"Error getting multi-device summary: {ex.Message}"));
}
}
/// <summary>
/// Get historical production data for charting
/// </summary>
[HttpGet("historical-data")]
public async Task<ActionResult<ApiResponse<HistoricalProductionData>>> GetHistoricalProductionData(
[FromQuery] int deviceId,
[FromQuery] DateTime startDate,
[FromQuery] DateTime endDate,
[FromQuery] GroupBy groupBy = GroupBy.Date)
{
try
{
if (deviceId <= 0)
return BadRequest(ApiResponse<HistoricalProductionData>.BadRequestResult("Invalid device ID"));
if (startDate >= endDate)
return BadRequest(ApiResponse<HistoricalProductionData>.BadRequestResult("Start date must be before end date"));
var filter = new ReportFilter
{
DeviceIds = new List<int> { deviceId },
StartDate = startDate,
EndDate = endDate,
GroupBy = groupBy
};
var report = await _statisticsService.GenerateProductionReportAsync(filter);
var historicalData = new HistoricalProductionData
{
DeviceId = deviceId,
PeriodStart = startDate,
PeriodEnd = endDate,
GroupBy = groupBy,
DataPoints = report.SummaryItems.Select(item => new DataPoint
{
Timestamp = groupBy == GroupBy.Date ? item.Date :
groupBy == GroupBy.Hour ? (item.Hour.HasValue ? item.Date.AddHours(item.Hour.Value) : item.Date) :
item.Date,
Value = item.Quantity,
Target = item.TargetQuantity,
Efficiency = item.Efficiency
}).ToList()
};
return Ok(ApiResponse<HistoricalProductionData>.Ok(historicalData));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<HistoricalProductionData>.InternalServerErrorResult($"Error getting historical production data: {ex.Message}"));
}
}
/// <summary>
/// Get production efficiency trends over time
/// </summary>
[HttpGet("efficiency-trends")]
public async Task<ActionResult<ApiResponse<EfficiencyTrendData>>> GetEfficiencyTrends(
[FromQuery] int deviceId,
[FromQuery] DateTime startDate,
[FromQuery] DateTime endDate,
[FromQuery] EfficiencyMetric metric = EfficiencyMetric.Oee)
{
try
{
if (deviceId <= 0)
return BadRequest(ApiResponse<EfficiencyTrendData>.BadRequestResult("Invalid device ID"));
if (startDate >= endDate)
return BadRequest(ApiResponse<EfficiencyTrendData>.BadRequestResult("Start date must be before end date"));
var filter = new EfficiencyFilter
{
DeviceIds = new List<int> { deviceId },
StartDate = startDate,
EndDate = endDate,
Metrics = metric
};
var efficiencyMetrics = await _statisticsService.CalculateEfficiencyMetricsAsync(filter);
var trendData = new EfficiencyTrendData
{
DeviceId = deviceId,
Metric = metric,
PeriodStart = startDate,
PeriodEnd = endDate,
DataPoints = efficiencyMetrics.HourlyData.Select(point => new EfficiencyDataPoint
{
Timestamp = point.Hour,
Availability = point.Availability,
Performance = point.Performance,
Quality = point.Quality,
Oee = point.Oee
}).ToList()
};
return Ok(ApiResponse<EfficiencyTrendData>.Ok(trendData));
}
catch (Exception ex)
{
return StatusCode(500, ApiResponse<EfficiencyTrendData>.InternalServerErrorResult($"Error getting efficiency trends: {ex.Message}"));
}
}
}
// Supporting models for API responses
public class MultiDeviceSummary
{
public DateTime GeneratedAt { get; set; }
public int DeviceCount { get; set; }
public int ActiveDeviceCount { get; set; }
public int OfflineDeviceCount { get; set; }
public decimal TotalProductionToday { get; set; }
public decimal TotalProductionThisWeek { get; set; }
public decimal TotalProductionThisMonth { get; set; }
public decimal OverallEfficiency { get; set; }
public decimal OverallQualityRate { get; set; }
public List<DeviceSummary> DeviceSummaries { get; set; }
}
public class HistoricalProductionData
{
public int DeviceId { get; set; }
public DateTime PeriodStart { get; set; }
public DateTime PeriodEnd { get; set; }
public GroupBy GroupBy { get; set; }
public List<DataPoint> DataPoints { get; set; }
}
public class DataPoint
{
public DateTime Timestamp { get; set; }
public decimal Value { get; set; }
public decimal Target { get; set; }
public decimal Efficiency { get; set; }
}
public class EfficiencyTrendData
{
public int DeviceId { get; set; }
public EfficiencyMetric Metric { get; set; }
public DateTime PeriodStart { get; set; }
public DateTime PeriodEnd { get; set; }
public List<EfficiencyDataPoint> DataPoints { get; set; }
}
public class EfficiencyDataPoint
{
public DateTime Timestamp { get; set; }
public decimal Availability { get; set; }
public decimal Performance { get; set; }
public decimal Quality { get; set; }
public decimal Oee { get; set; }
}
}