Files
OrpaonVision/OrpaonVision.SiteApp/ViewModels/OptimizedMainWindowViewModel.cs
2026-04-06 22:04:05 +08:00

485 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.Extensions.Logging;
using OrpaonVision.SiteApp.Controls;
using OrpaonVision.SiteApp.Runtime.Contracts;
using OrpaonVision.SiteApp.Runtime.Services;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace OrpaonVision.SiteApp.ViewModels;
/// <summary>
/// 优化的运行端主窗口 ViewModel。
/// </summary>
public sealed class OptimizedMainWindowViewModel : INotifyPropertyChanged
{
private readonly ICameraService _cameraService;
private readonly IInferenceService _inferenceService;
private readonly IRuleEngineService _ruleEngineService;
private readonly IRuntimeStateMachineService _runtimeStateMachineService;
private readonly IImageProcessingService _imageProcessingService;
private readonly ILogger<OptimizedMainWindowViewModel> _logger;
private string _statusText = "准备就绪。";
private Brush _statusBrush = Brushes.DarkGreen;
private string _layerText = "1/1";
private string _inferenceText = "-";
private string _decisionText = "-";
private BitmapSource? _currentImage;
private List<DetectionBox> _detectionBoxes = new();
private ImageDisplayMode _displayMode = ImageDisplayMode.Fit;
private bool _showDetectionBoxes = true;
private bool _isAutoRunning = false;
private int _frameRate = 1;
private int _totalFrames = 0;
private int _successFrames = 0;
private int _failedFrames = 0;
private DateTime _lastFrameTime = DateTime.MinValue;
private double _actualFrameRate = 0;
/// <summary>
/// 构造函数。
/// </summary>
public OptimizedMainWindowViewModel(
ICameraService cameraService,
IInferenceService inferenceService,
IRuleEngineService ruleEngineService,
IRuntimeStateMachineService runtimeStateMachineService,
IImageProcessingService imageProcessingService,
ILogger<OptimizedMainWindowViewModel> logger)
{
_cameraService = cameraService;
_inferenceService = inferenceService;
_ruleEngineService = ruleEngineService;
_runtimeStateMachineService = runtimeStateMachineService;
_imageProcessingService = imageProcessingService;
_logger = logger;
// 初始化占位图像
_currentImage = _imageProcessingService.CreatePlaceholderImage(640, 480, "等待图像...");
RefreshSnapshot();
}
/// <summary>
/// 状态文本。
/// </summary>
public string StatusText
{
get => _statusText;
private set => SetProperty(ref _statusText, value);
}
/// <summary>
/// 状态颜色。
/// </summary>
public Brush StatusBrush
{
get => _statusBrush;
private set => SetProperty(ref _statusBrush, value);
}
/// <summary>
/// 当前层显示文本。
/// </summary>
public string LayerText
{
get => _layerText;
private set => SetProperty(ref _layerText, value);
}
/// <summary>
/// 最近一次推理结果。
/// </summary>
public string InferenceText
{
get => _inferenceText;
private set => SetProperty(ref _inferenceText, value);
}
/// <summary>
/// 最近一次规则判定结果。
/// </summary>
public string DecisionText
{
get => _decisionText;
private set => SetProperty(ref _decisionText, value);
}
/// <summary>
/// 当前图像。
/// </summary>
public BitmapSource? CurrentImage
{
get => _currentImage;
private set => SetProperty(ref _currentImage, value);
}
/// <summary>
/// 检测框列表。
/// </summary>
public List<DetectionBox> DetectionBoxes
{
get => _detectionBoxes;
private set => SetProperty(ref _detectionBoxes, value);
}
/// <summary>
/// 图像显示模式。
/// </summary>
public ImageDisplayMode DisplayMode
{
get => _displayMode;
set => SetProperty(ref _displayMode, value);
}
/// <summary>
/// 是否显示检测框。
/// </summary>
public bool ShowDetectionBoxes
{
get => _showDetectionBoxes;
set => SetProperty(ref _showDetectionBoxes, value);
}
/// <summary>
/// 是否自动运行。
/// </summary>
public bool IsAutoRunning
{
get => _isAutoRunning;
private set => SetProperty(ref _isAutoRunning, value);
}
/// <summary>
/// 帧率设置。
/// </summary>
public int FrameRate
{
get => _frameRate;
set => SetProperty(ref _frameRate, value);
}
/// <summary>
/// 总帧数。
/// </summary>
public int TotalFrames
{
get => _totalFrames;
private set => SetProperty(ref _totalFrames, value);
}
/// <summary>
/// 成功帧数。
/// </summary>
public int SuccessFrames
{
get => _successFrames;
private set => SetProperty(ref _successFrames, value);
}
/// <summary>
/// 失败帧数。
/// </summary>
public int FailedFrames
{
get => _failedFrames;
private set => SetProperty(ref _failedFrames, value);
}
/// <summary>
/// 实际帧率。
/// </summary>
public double ActualFrameRate
{
get => _actualFrameRate;
private set => SetProperty(ref _actualFrameRate, value);
}
/// <summary>
/// 成功率。
/// </summary>
public double SuccessRate
{
get => TotalFrames > 0 ? (double)SuccessFrames / TotalFrames * 100 : 0;
}
/// <inheritdoc />
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// 执行一轮采集-推理-规则判定。
/// </summary>
public void RunOneCycle()
{
try
{
var snapshot = _runtimeStateMachineService.GetSnapshot();
// 采集图像
var captureResult = _cameraService.CaptureFrame();
if (!captureResult.Succeeded || captureResult.Data is null)
{
SetError("相机采图失败。", captureResult.Message);
_failedFrames++;
UpdateStatistics();
return;
}
// 转换图像
var imageResult = _imageProcessingService.ConvertFrameToBitmapSource(captureResult.Data);
if (!imageResult.Succeeded || imageResult.Data is null)
{
SetError("图像转换失败。", imageResult.Message);
_failedFrames++;
UpdateStatistics();
return;
}
CurrentImage = imageResult.Data;
// 推理
var predictResult = _inferenceService.Predict(captureResult.Data);
if (!predictResult.Succeeded || predictResult.Data is null)
{
SetError("模型推理失败。", predictResult.Message);
_failedFrames++;
UpdateStatistics();
return;
}
var inference = predictResult.Data;
InferenceText = $"{inference.Label} (置信度: {inference.Confidence:P0})";
// 转换检测结果
var detectionResult = _imageProcessingService.ConvertInferenceToDetectionBoxes(inference);
if (detectionResult.Succeeded)
{
DetectionBoxes = detectionResult.Data ?? new List<DetectionBox>();
}
// 规则判定
var decisionResult = _ruleEngineService.Evaluate(snapshot.CurrentLayer, inference);
if (!decisionResult.Succeeded || decisionResult.Data is null)
{
SetError("规则判定失败。", decisionResult.Message);
_failedFrames++;
UpdateStatistics();
return;
}
var decision = decisionResult.Data;
DecisionText = $"{decision.Code} - {decision.Message}";
StatusBrush = decision.IsPass ? Brushes.DarkGreen : Brushes.OrangeRed;
StatusText = decision.IsPass ? "当前层判定通过。" : "当前层判定 NG请人工复核。";
_successFrames++;
UpdateStatistics();
}
catch (Exception ex)
{
_logger.LogError(ex, "执行一轮检测失败");
SetError("执行检测失败。", ex.Message);
_failedFrames++;
UpdateStatistics();
}
}
/// <summary>
/// 推进到下一层。
/// </summary>
public void MoveToNextLayer()
{
try
{
var moveResult = _runtimeStateMachineService.MoveToNextLayer();
if (!moveResult.Succeeded)
{
SetError("状态机切层失败。", moveResult.Message);
return;
}
RefreshSnapshot();
StatusBrush = Brushes.DarkGreen;
StatusText = moveResult.Message;
}
catch (Exception ex)
{
_logger.LogError(ex, "推进到下一层失败");
SetError("推进下一层失败。", ex.Message);
}
}
/// <summary>
/// 重置运行状态。
/// </summary>
public void ResetRuntime()
{
try
{
_runtimeStateMachineService.Reset();
InferenceText = "-";
DecisionText = "-";
DetectionBoxes = new List<DetectionBox>();
CurrentImage = _imageProcessingService.CreatePlaceholderImage(640, 480, "等待图像...");
RefreshSnapshot();
// 重置统计
_totalFrames = 0;
_successFrames = 0;
_failedFrames = 0;
_actualFrameRate = 0;
UpdateStatistics();
StatusBrush = Brushes.DarkGreen;
StatusText = "运行状态已重置。";
}
catch (Exception ex)
{
_logger.LogError(ex, "重置运行状态失败");
SetError("重置状态失败。", ex.Message);
}
}
/// <summary>
/// 切换自动运行。
/// </summary>
public void ToggleAutoRun()
{
IsAutoRunning = !IsAutoRunning;
if (IsAutoRunning)
{
StatusBrush = Brushes.Blue;
StatusText = "自动运行已启动。";
_logger.LogInformation("自动运行已启动,帧率: {FrameRate}", FrameRate);
}
else
{
StatusBrush = Brushes.DarkGreen;
StatusText = "自动运行已停止。";
_logger.LogInformation("自动运行已停止");
}
}
/// <summary>
/// 更新图像显示模式。
/// </summary>
public void UpdateDisplayMode(ImageDisplayMode mode)
{
DisplayMode = mode;
_logger.LogInformation("图像显示模式已更新: {Mode}", mode);
}
/// <summary>
/// 切换检测框显示。
/// </summary>
public void ToggleDetectionBoxes()
{
ShowDetectionBoxes = !ShowDetectionBoxes;
_logger.LogInformation("检测框显示已切换: {Show}", ShowDetectionBoxes);
}
/// <summary>
/// 应用图像滤镜。
/// </summary>
public void ApplyImageFilter(ImageFilter filter)
{
if (CurrentImage == null)
{
StatusText = "没有可应用滤镜的图像。";
return;
}
try
{
var filterResult = _imageProcessingService.ApplyFilter(CurrentImage, filter);
if (filterResult.Succeeded && filterResult.Data != null)
{
CurrentImage = filterResult.Data;
StatusText = $"已应用滤镜: {filter}";
}
else
{
SetError("应用滤镜失败。", filterResult.Message);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "应用图像滤镜失败");
SetError("应用滤镜失败。", ex.Message);
}
}
/// <summary>
/// 保存当前图像。
/// </summary>
public void SaveCurrentImage()
{
if (CurrentImage == null)
{
StatusText = "没有可保存的图像。";
return;
}
try
{
// 这里可以实现图像保存功能
StatusText = "图像保存功能待实现。";
}
catch (Exception ex)
{
_logger.LogError(ex, "保存图像失败");
SetError("保存图像失败。", ex.Message);
}
}
/// <summary>
/// 更新统计信息。
/// </summary>
private void UpdateStatistics()
{
TotalFrames = _totalFrames;
SuccessFrames = _successFrames;
FailedFrames = _failedFrames;
// 计算实际帧率
if (_lastFrameTime != DateTime.MinValue)
{
var elapsed = DateTime.Now - _lastFrameTime;
if (elapsed.TotalSeconds > 0)
{
_actualFrameRate = 1.0 / elapsed.TotalSeconds;
}
}
_lastFrameTime = DateTime.Now;
ActualFrameRate = Math.Round(_actualFrameRate, 2);
}
private void RefreshSnapshot()
{
var snapshot = _runtimeStateMachineService.GetSnapshot();
LayerText = $"{snapshot.CurrentLayer}/{snapshot.TotalLayers}";
}
private void SetError(string title, string message)
{
StatusBrush = Brushes.OrangeRed;
StatusText = $"{title} {message}";
}
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return;
}
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}