版本260406

This commit is contained in:
2026-04-06 22:04:05 +08:00
parent 7dc5e73af7
commit 0b150470be
216 changed files with 98993 additions and 33 deletions

View File

@@ -0,0 +1,180 @@
using OrpaonVision.SiteApp.Runtime.Services;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Media;
namespace OrpaonVision.SiteApp.ViewModels
{
/// <summary>
/// 运行端主窗口 ViewModel。
/// </summary>
public sealed class MainWindowViewModel : INotifyPropertyChanged
{
private readonly ICameraService _cameraService;
private readonly IInferenceService _inferenceService;
private readonly IRuleEngineService _ruleEngineService;
private readonly IRuntimeStateMachineService _runtimeStateMachineService;
private string _statusText = "准备就绪。";
private Brush _statusBrush = Brushes.DarkGreen;
private string _layerText = "1/1";
private string _inferenceText = "-";
private string _decisionText = "-";
/// <summary>
/// 构造函数。
/// </summary>
public MainWindowViewModel(
ICameraService cameraService,
IInferenceService inferenceService,
IRuleEngineService ruleEngineService,
IRuntimeStateMachineService runtimeStateMachineService)
{
_cameraService = cameraService;
_inferenceService = inferenceService;
_ruleEngineService = ruleEngineService;
_runtimeStateMachineService = runtimeStateMachineService;
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 void RunOneCycle()
{
var snapshot = _runtimeStateMachineService.GetSnapshot();
var captureResult = _cameraService.CaptureFrame();
if (!captureResult.Succeeded || captureResult.Data is null)
{
SetError("相机采图失败。", captureResult.Message);
return;
}
var predictResult = _inferenceService.Predict(captureResult.Data);
if (!predictResult.Succeeded || predictResult.Data is null)
{
SetError("模型推理失败。", predictResult.Message);
return;
}
var inference = predictResult.Data;
InferenceText = $"{inference.Label} (置信度: {inference.Confidence:P0})";
var decisionResult = _ruleEngineService.Evaluate(snapshot.CurrentLayer, inference);
if (!decisionResult.Succeeded || decisionResult.Data is null)
{
SetError("规则判定失败。", decisionResult.Message);
return;
}
var decision = decisionResult.Data;
DecisionText = $"{decision.Code} - {decision.Message}";
StatusBrush = decision.IsPass ? Brushes.DarkGreen : Brushes.OrangeRed;
StatusText = decision.IsPass ? "当前层判定通过。" : "当前层判定 NG请人工复核。";
}
/// <summary>
/// 推进到下一层。
/// </summary>
public void MoveToNextLayer()
{
var moveResult = _runtimeStateMachineService.MoveToNextLayer();
if (!moveResult.Succeeded)
{
SetError("状态机切层失败。", moveResult.Message);
return;
}
RefreshSnapshot();
StatusBrush = Brushes.DarkGreen;
StatusText = moveResult.Message;
}
/// <summary>
/// 重置运行状态。
/// </summary>
public void ResetRuntime()
{
_runtimeStateMachineService.Reset();
InferenceText = "-";
DecisionText = "-";
RefreshSnapshot();
StatusBrush = Brushes.DarkGreen;
StatusText = "运行状态已重置。";
}
/// <inheritdoc />
public event PropertyChangedEventHandler? PropertyChanged;
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));
}
}
}

View File

@@ -0,0 +1,484 @@
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));
}
}