546 lines
20 KiB
C#
546 lines
20 KiB
C#
using Microsoft.Extensions.Logging;
|
||
using OrpaonVision.Core.Results;
|
||
using OrpaonVision.SiteApp.Runtime.Contracts;
|
||
|
||
namespace OrpaonVision.SiteApp.Runtime.Services;
|
||
|
||
/// <summary>
|
||
/// 输入输出一致性检查服务。
|
||
/// </summary>
|
||
public sealed class ConsistencyCheckService : IConsistencyCheckService
|
||
{
|
||
private readonly ILogger<ConsistencyCheckService> _logger;
|
||
private readonly IHikCameraService _cameraService;
|
||
private readonly IYoloInferenceService _yoloService;
|
||
|
||
/// <summary>
|
||
/// 构造函数。
|
||
/// </summary>
|
||
public ConsistencyCheckService(
|
||
ILogger<ConsistencyCheckService> logger,
|
||
IHikCameraService cameraService,
|
||
IYoloInferenceService yoloService)
|
||
{
|
||
_logger = logger;
|
||
_cameraService = cameraService;
|
||
_yoloService = yoloService;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public Result<ConsistencyCheckResult> CheckCameraInferenceConsistency()
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInformation("开始检查相机与推理服务一致性");
|
||
|
||
var result = new ConsistencyCheckResult
|
||
{
|
||
CheckTimestamp = DateTime.UtcNow,
|
||
CameraConnected = _cameraService.IsConnected,
|
||
YoloInitialized = _yoloService.IsInitialized
|
||
};
|
||
|
||
// 检查相机连接状态
|
||
if (!result.CameraConnected)
|
||
{
|
||
result.Status = ConsistencyStatus.Warning;
|
||
result.Message = "相机未连接";
|
||
result.Recommendations.Add("请先连接相机设备");
|
||
_logger.LogWarning("相机未连接,跳过其他一致性检查");
|
||
return Result<ConsistencyCheckResult>.Success(result);
|
||
}
|
||
|
||
// 检查YOLO初始化状态
|
||
if (!result.YoloInitialized)
|
||
{
|
||
result.Status = ConsistencyStatus.Warning;
|
||
result.Message = "YOLO模型未初始化";
|
||
result.Recommendations.Add("请先初始化YOLO模型");
|
||
_logger.LogWarning("YOLO模型未初始化,跳过其他一致性检查");
|
||
return Result<ConsistencyCheckResult>.Success(result);
|
||
}
|
||
|
||
// 获取相机配置信息
|
||
var cameraDevice = _cameraService.CurrentDevice;
|
||
if (cameraDevice == null)
|
||
{
|
||
result.Status = ConsistencyStatus.Error;
|
||
result.Message = "无法获取相机设备信息";
|
||
result.Recommendations.Add("检查相机连接状态");
|
||
return Result<ConsistencyCheckResult>.Success(result);
|
||
}
|
||
|
||
// 获取YOLO模型信息
|
||
var yoloModelInfo = _yoloService.GetModelInfo();
|
||
|
||
// 检查像素格式一致性
|
||
var pixelFormatCheck = CheckPixelFormatConsistency(cameraDevice, yoloModelInfo);
|
||
result.PixelFormatConsistent = pixelFormatCheck.IsConsistent;
|
||
result.PixelFormatDetails = pixelFormatCheck.Details;
|
||
|
||
if (!result.PixelFormatConsistent)
|
||
{
|
||
result.Status = ConsistencyStatus.Error;
|
||
result.Message = "像素格式不一致";
|
||
result.Recommendations.AddRange(pixelFormatCheck.Recommendations);
|
||
}
|
||
|
||
// 检查图像尺寸一致性
|
||
var imageSizeCheck = CheckImageSizeConsistency(cameraDevice, yoloModelInfo);
|
||
result.ImageSizeConsistent = imageSizeCheck.IsConsistent;
|
||
result.ImageSizeDetails = imageSizeCheck.Details;
|
||
|
||
if (!result.ImageSizeConsistent)
|
||
{
|
||
if (result.Status == ConsistencyStatus.Ok)
|
||
{
|
||
result.Status = ConsistencyStatus.Warning;
|
||
}
|
||
result.Recommendations.AddRange(imageSizeCheck.Recommendations);
|
||
}
|
||
|
||
// 检查数据流完整性
|
||
var dataFlowCheck = CheckDataFlowIntegrity();
|
||
result.DataFlowIntegrity = dataFlowCheck.IsIntegrity;
|
||
result.DataFlowDetails = dataFlowCheck.Details;
|
||
|
||
if (!result.DataFlowIntegrity)
|
||
{
|
||
if (result.Status == ConsistencyStatus.Ok)
|
||
{
|
||
result.Status = ConsistencyStatus.Warning;
|
||
}
|
||
result.Recommendations.AddRange(dataFlowCheck.Recommendations);
|
||
}
|
||
|
||
// 如果所有检查都通过
|
||
if (result.Status == ConsistencyStatus.Ok)
|
||
{
|
||
result.Message = "相机与推理服务一致性检查通过";
|
||
}
|
||
|
||
_logger.LogInformation("一致性检查完成,状态: {Status}, 消息: {Message}", result.Status, result.Message);
|
||
return Result<ConsistencyCheckResult>.Success(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "一致性检查失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "CONSISTENCY_CHECK_FAILED", "一致性检查失败。", traceId);
|
||
return Result<ConsistencyCheckResult>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public Result<HealthCheckResult> GetMinimalHealthInfo()
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("获取最小健康信息");
|
||
|
||
var healthResult = new HealthCheckResult
|
||
{
|
||
Timestamp = DateTime.UtcNow,
|
||
OverallStatus = HealthStatus.Healthy
|
||
};
|
||
|
||
// 检查相机健康状态
|
||
var cameraHealth = CheckCameraHealth();
|
||
healthResult.CameraHealth = cameraHealth;
|
||
|
||
if (cameraHealth.Status != HealthStatus.Healthy)
|
||
{
|
||
healthResult.OverallStatus = HealthStatus.Degraded;
|
||
}
|
||
|
||
// 检查YOLO健康状态
|
||
var yoloHealth = CheckYoloHealth();
|
||
healthResult.YoloHealth = yoloHealth;
|
||
|
||
if (yoloHealth.Status != HealthStatus.Healthy)
|
||
{
|
||
healthResult.OverallStatus = HealthStatus.Degraded;
|
||
}
|
||
|
||
// 检查系统资源
|
||
var systemHealth = CheckSystemHealth();
|
||
healthResult.SystemHealth = systemHealth;
|
||
|
||
if (systemHealth.Status != HealthStatus.Healthy)
|
||
{
|
||
healthResult.OverallStatus = HealthStatus.Degraded;
|
||
}
|
||
|
||
// 确定整体状态
|
||
if (healthResult.CameraHealth.Status == HealthStatus.Unhealthy ||
|
||
healthResult.YoloHealth.Status == HealthStatus.Unhealthy ||
|
||
healthResult.SystemHealth.Status == HealthStatus.Unhealthy)
|
||
{
|
||
healthResult.OverallStatus = HealthStatus.Unhealthy;
|
||
}
|
||
|
||
healthResult.Summary = GenerateHealthSummary(healthResult);
|
||
|
||
_logger.LogDebug("健康检查完成,整体状态: {Status}", healthResult.OverallStatus);
|
||
return Result<HealthCheckResult>.Success(healthResult);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var traceId = Guid.NewGuid().ToString("N");
|
||
_logger.LogError(ex, "健康检查失败。TraceId: {TraceId}", traceId);
|
||
var result = Result.FromException(ex, "HEALTH_CHECK_FAILED", "健康检查失败。", traceId);
|
||
return Result<HealthCheckResult>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查像素格式一致性。
|
||
/// </summary>
|
||
private PixelFormatCheckResult CheckPixelFormatConsistency(HikCameraDevice cameraDevice, YoloModelInfo modelInfo)
|
||
{
|
||
var result = new PixelFormatCheckResult
|
||
{
|
||
IsConsistent = true,
|
||
Details = new List<string>()
|
||
};
|
||
|
||
// 获取相机像素格式(这里简化处理,实际应该从相机参数获取)
|
||
var cameraPixelFormat = "BGR8Packed"; // 默认值,实际应该从相机获取
|
||
|
||
// YOLO通常接受RGB格式,需要检查转换兼容性
|
||
var yoloExpectedFormat = "RGB8Packed";
|
||
|
||
result.Details.Add($"相机像素格式: {cameraPixelFormat}");
|
||
result.Details.Add($"YOLO期望格式: {yoloExpectedFormat}");
|
||
|
||
// 检查格式兼容性
|
||
if (cameraPixelFormat == "BGR8Packed" || cameraPixelFormat == "RGB8Packed")
|
||
{
|
||
result.Details.Add("像素格式兼容,支持自动转换");
|
||
}
|
||
else if (cameraPixelFormat == "MONO8")
|
||
{
|
||
result.IsConsistent = false;
|
||
result.Recommendations.Add("相机设置为单色格式,建议改为彩色格式以获得更好的检测效果");
|
||
}
|
||
else
|
||
{
|
||
result.IsConsistent = false;
|
||
result.Recommendations.Add($"不支持的像素格式: {cameraPixelFormat},建议使用BGR8Packed或RGB8Packed");
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查图像尺寸一致性。
|
||
/// </summary>
|
||
private ImageSizeCheckResult CheckImageSizeConsistency(HikCameraDevice cameraDevice, YoloModelInfo modelInfo)
|
||
{
|
||
var result = new ImageSizeCheckResult
|
||
{
|
||
IsConsistent = true,
|
||
Details = new List<string>()
|
||
};
|
||
|
||
// 获取相机输出尺寸(这里简化处理,实际应该从相机参数获取)
|
||
var cameraSize = (1920, 1080); // 默认值,实际应该从相机获取
|
||
var yoloInputSize = modelInfo.InputSize;
|
||
|
||
result.Details.Add($"相机输出尺寸: {cameraSize.Item1}x{cameraSize.Item2}");
|
||
result.Details.Add($"YOLO输入尺寸: {yoloInputSize.Item1}x{yoloInputSize.Item2}");
|
||
|
||
// 检查尺寸比例
|
||
var cameraRatio = (double)cameraSize.Item1 / cameraSize.Item2;
|
||
var yoloRatio = (double)yoloInputSize.Item1 / yoloInputSize.Item2;
|
||
var ratioDiff = Math.Abs(cameraRatio - yoloRatio);
|
||
|
||
if (ratioDiff < 0.1) // 比例差异小于10%
|
||
{
|
||
result.Details.Add("图像尺寸比例基本一致");
|
||
}
|
||
else
|
||
{
|
||
result.IsConsistent = false;
|
||
result.Recommendations.Add("相机输出尺寸与YOLO输入尺寸比例差异较大,可能影响检测精度");
|
||
result.Recommendations.Add("建议调整相机分辨率或使用图像预处理调整尺寸");
|
||
}
|
||
|
||
// 检查分辨率是否过小
|
||
if (cameraSize.Item1 < 640 || cameraSize.Item2 < 480)
|
||
{
|
||
result.IsConsistent = false;
|
||
result.Recommendations.Add("相机分辨率过低,建议至少使用640x480分辨率");
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查数据流完整性。
|
||
/// </summary>
|
||
private DataFlowCheckResult CheckDataFlowIntegrity()
|
||
{
|
||
var result = new DataFlowCheckResult
|
||
{
|
||
IsIntegrity = true,
|
||
Details = new List<string>()
|
||
};
|
||
|
||
try
|
||
{
|
||
// 检查相机是否能获取帧
|
||
var frameResult = _cameraService.GetLatestFrame();
|
||
if (!frameResult.Succeeded)
|
||
{
|
||
result.IsIntegrity = false;
|
||
result.Details.Add("无法从相机获取图像帧");
|
||
result.Recommendations.Add("检查相机是否正常采集图像");
|
||
return result;
|
||
}
|
||
|
||
var frameData = frameResult.Data;
|
||
result.Details.Add($"成功获取图像帧,大小: {frameData.Length} bytes");
|
||
|
||
// 检查帧数据完整性
|
||
if (frameData.Length == 0)
|
||
{
|
||
result.IsIntegrity = false;
|
||
result.Details.Add("图像帧数据为空");
|
||
result.Recommendations.Add("检查相机连接和采集设置");
|
||
return result;
|
||
}
|
||
|
||
// 检查YOLO推理是否能正常工作
|
||
var testImage = new byte[640 * 480 * 3]; // 模拟测试图像
|
||
var inferenceResult = _yoloService.Predict(testImage, 640, 480);
|
||
|
||
if (!inferenceResult.Succeeded)
|
||
{
|
||
result.IsIntegrity = false;
|
||
result.Details.Add("YOLO推理失败");
|
||
result.Recommendations.Add("检查YOLO模型状态和配置");
|
||
return result;
|
||
}
|
||
|
||
result.Details.Add("YOLO推理测试通过");
|
||
result.Details.Add("数据流完整性检查通过");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.IsIntegrity = false;
|
||
result.Details.Add($"数据流检查异常: {ex.Message}");
|
||
result.Recommendations.Add("检查系统运行状态和依赖项");
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查相机健康状态。
|
||
/// </summary>
|
||
private ComponentHealth CheckCameraHealth()
|
||
{
|
||
var health = new ComponentHealth
|
||
{
|
||
ComponentName = "Camera",
|
||
Status = HealthStatus.Healthy,
|
||
Details = new List<string>(),
|
||
Metrics = new Dictionary<string, object>()
|
||
};
|
||
|
||
try
|
||
{
|
||
if (!_cameraService.IsConnected)
|
||
{
|
||
health.Status = HealthStatus.Unhealthy;
|
||
health.Details.Add("相机未连接");
|
||
return health;
|
||
}
|
||
|
||
var device = _cameraService.CurrentDevice;
|
||
if (device != null)
|
||
{
|
||
health.Details.Add($"设备名称: {device.DeviceName}");
|
||
health.Details.Add($"序列号: {device.SerialNumber}");
|
||
health.Details.Add($"IP地址: {device.IpAddress}");
|
||
|
||
if (device.ConnectedAt.HasValue)
|
||
{
|
||
var uptime = DateTime.UtcNow - device.ConnectedAt.Value;
|
||
health.Metrics["UptimeSeconds"] = uptime.TotalSeconds;
|
||
health.Details.Add($"连接时长: {uptime:h\\h\\ m\\m\\ s\\s}");
|
||
}
|
||
}
|
||
|
||
// 测试获取帧
|
||
var frameResult = _cameraService.GetLatestFrame();
|
||
if (frameResult.Succeeded)
|
||
{
|
||
health.Metrics["LastFrameSize"] = frameResult.Data.Length;
|
||
health.Details.Add("图像帧获取正常");
|
||
}
|
||
else
|
||
{
|
||
health.Status = HealthStatus.Degraded;
|
||
health.Details.Add($"图像帧获取失败: {frameResult.Message}");
|
||
}
|
||
|
||
health.Metrics["IsConnected"] = _cameraService.IsConnected;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
health.Status = HealthStatus.Unhealthy;
|
||
health.Details.Add($"相机健康检查异常: {ex.Message}");
|
||
}
|
||
|
||
return health;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查YOLO健康状态。
|
||
/// </summary>
|
||
private ComponentHealth CheckYoloHealth()
|
||
{
|
||
var health = new ComponentHealth
|
||
{
|
||
ComponentName = "YOLO",
|
||
Status = HealthStatus.Healthy,
|
||
Details = new List<string>(),
|
||
Metrics = new Dictionary<string, object>()
|
||
};
|
||
|
||
try
|
||
{
|
||
if (!_yoloService.IsInitialized)
|
||
{
|
||
health.Status = HealthStatus.Unhealthy;
|
||
health.Details.Add("YOLO模型未初始化");
|
||
return health;
|
||
}
|
||
|
||
var modelInfo = _yoloService.GetModelInfo();
|
||
health.Details.Add($"模型名称: {modelInfo.ModelName}");
|
||
health.Details.Add($"模型版本: {modelInfo.ModelVersion}");
|
||
health.Details.Add($"输入尺寸: {modelInfo.InputSize.Item1}x{modelInfo.InputSize.Item2}");
|
||
health.Details.Add($"类别数量: {modelInfo.ClassCount}");
|
||
health.Details.Add($"使用GPU: {modelInfo.UseGpu}");
|
||
|
||
health.Metrics["ModelFileSize"] = modelInfo.ModelFileSize;
|
||
health.Metrics["ClassCount"] = modelInfo.ClassCount;
|
||
health.Metrics["UseGpu"] = modelInfo.UseGpu;
|
||
|
||
// 测试推理
|
||
var testImage = new byte[640 * 480 * 3];
|
||
var inferenceResult = _yoloService.Predict(testImage, 640, 480);
|
||
|
||
if (inferenceResult.Succeeded)
|
||
{
|
||
health.Metrics["InferenceResultCount"] = inferenceResult.Data.Count;
|
||
health.Details.Add("推理测试通过");
|
||
}
|
||
else
|
||
{
|
||
health.Status = HealthStatus.Degraded;
|
||
health.Details.Add($"推理测试失败: {inferenceResult.Message}");
|
||
}
|
||
|
||
health.Metrics["IsInitialized"] = _yoloService.IsInitialized;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
health.Status = HealthStatus.Unhealthy;
|
||
health.Details.Add($"YOLO健康检查异常: {ex.Message}");
|
||
}
|
||
|
||
return health;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查系统健康状态。
|
||
/// </summary>
|
||
private ComponentHealth CheckSystemHealth()
|
||
{
|
||
var health = new ComponentHealth
|
||
{
|
||
ComponentName = "System",
|
||
Status = HealthStatus.Healthy,
|
||
Details = new List<string>(),
|
||
Metrics = new Dictionary<string, object>()
|
||
};
|
||
|
||
try
|
||
{
|
||
// 检查内存使用情况
|
||
var workingSet = Environment.WorkingSet;
|
||
var workingSetMB = workingSet / (1024 * 1024);
|
||
|
||
health.Metrics["WorkingSetMB"] = workingSetMB;
|
||
health.Details.Add($"工作集内存: {workingSetMB} MB");
|
||
|
||
if (workingSetMB > 1024) // 超过1GB
|
||
{
|
||
health.Status = HealthStatus.Degraded;
|
||
health.Details.Add("内存使用量较高");
|
||
}
|
||
|
||
// 检查处理器数量
|
||
var processorCount = Environment.ProcessorCount;
|
||
health.Metrics["ProcessorCount"] = processorCount;
|
||
health.Details.Add($"处理器核心数: {processorCount}");
|
||
|
||
// 检查GC状态
|
||
var totalMemory = GC.GetTotalMemory(false);
|
||
var totalMemoryMB = totalMemory / (1024 * 1024);
|
||
|
||
health.Metrics["ManagedMemoryMB"] = totalMemoryMB;
|
||
health.Details.Add($"托管内存: {totalMemoryMB} MB");
|
||
|
||
if (totalMemoryMB > 512) // 超过512MB托管内存
|
||
{
|
||
if (health.Status == HealthStatus.Healthy)
|
||
{
|
||
health.Status = HealthStatus.Degraded;
|
||
}
|
||
health.Details.Add("托管内存使用量较高");
|
||
}
|
||
|
||
health.Details.Add("系统资源检查完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
health.Status = HealthStatus.Unhealthy;
|
||
health.Details.Add($"系统健康检查异常: {ex.Message}");
|
||
}
|
||
|
||
return health;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成健康检查摘要。
|
||
/// </summary>
|
||
private string GenerateHealthSummary(HealthCheckResult healthResult)
|
||
{
|
||
var summary = new List<string>();
|
||
|
||
summary.Add($"整体状态: {healthResult.OverallStatus}");
|
||
|
||
if (healthResult.CameraHealth.Status != HealthStatus.Healthy)
|
||
{
|
||
summary.Add($"相机状态: {healthResult.CameraHealth.Status}");
|
||
}
|
||
|
||
if (healthResult.YoloHealth.Status != HealthStatus.Healthy)
|
||
{
|
||
summary.Add($"YOLO状态: {healthResult.YoloHealth.Status}");
|
||
}
|
||
|
||
if (healthResult.SystemHealth.Status != HealthStatus.Healthy)
|
||
{
|
||
summary.Add($"系统状态: {healthResult.SystemHealth.Status}");
|
||
}
|
||
|
||
return string.Join("; ", summary);
|
||
}
|
||
}
|