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#
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; }
|
|
}
|
|
} |