Files
XplorePlane/XplorePlane/Services/MainViewport/MainViewportService.cs
T
zhengxuan.zhang cfdfe330a5 修复运行错误
2026-05-11 16:15:19 +08:00

282 lines
8.8 KiB
C#

using System;
using System.Configuration;
using System.IO;
using System.Windows.Media;
using XP.Common.Logging.Interfaces;
namespace XplorePlane.Services.MainViewport
{
public sealed class MainViewportService : IMainViewportService
{
private readonly object _syncRoot = new();
private readonly ILoggerService _logger;
private MainViewportSourceMode _currentSourceMode = MainViewportSourceMode.LiveDetector;
private bool _isRealtimeDisplayEnabled;
private bool _isCncRunning;
private ImageSource _currentDisplayImage;
private string _currentDisplayInfo = "等待探测器图像...";
private ImageSource _latestDetectorImage;
private string _latestDetectorInfo = "等待探测器图像...";
private ImageSource _latestManualImage;
private string _latestManualInfo = "未加载手动图像";
public MainViewportService(ILoggerService logger)
{
_logger = logger?.ForModule<MainViewportService>() ?? throw new ArgumentNullException(nameof(logger));
_isRealtimeDisplayEnabled = ReadBoolean("MainViewport:RealtimeEnabledDefault", true);
}
public MainViewportSourceMode CurrentSourceMode
{
get
{
lock (_syncRoot)
{
return _currentSourceMode;
}
}
}
public bool IsRealtimeDisplayEnabled
{
get
{
lock (_syncRoot)
{
return _isRealtimeDisplayEnabled;
}
}
}
public bool IsCncRunning
{
get
{
lock (_syncRoot)
{
return _isCncRunning;
}
}
}
public ImageSource CurrentDisplayImage
{
get
{
lock (_syncRoot)
{
return _currentDisplayImage;
}
}
}
public string CurrentDisplayInfo
{
get
{
lock (_syncRoot)
{
return _currentDisplayInfo;
}
}
}
public ImageSource LatestDetectorImage
{
get
{
lock (_syncRoot)
{
return _latestDetectorImage;
}
}
}
public ImageSource LatestManualImage
{
get
{
lock (_syncRoot)
{
return _latestManualImage;
}
}
}
public event EventHandler StateChanged;
public void SetRealtimeDisplayEnabled(bool isEnabled)
{
bool changed;
lock (_syncRoot)
{
if (!isEnabled && _isCncRunning)
{
_logger.Warn("CNC 正在运行,忽略 SetRealtimeDisplayEnabled(false) 调用");
return;
}
changed = _isRealtimeDisplayEnabled != isEnabled
|| (isEnabled && _currentSourceMode == MainViewportSourceMode.ManualImage);
_isRealtimeDisplayEnabled = isEnabled;
if (isEnabled)
{
// 开启实时:无论当前是 ManualImage 还是 LiveDetector,都切回实时帧显示
_currentSourceMode = MainViewportSourceMode.LiveDetector;
ApplyLiveDetectorDisplay_NoLock();
}
}
if (!changed)
return;
_logger.Info("主界面实时刷新已{State}", isEnabled ? "开启" : "关闭");
RaiseStateChanged();
}
public void SetSourceMode(MainViewportSourceMode sourceMode)
{
bool changed;
lock (_syncRoot)
{
changed = _currentSourceMode != sourceMode;
_currentSourceMode = sourceMode;
switch (sourceMode)
{
case MainViewportSourceMode.LiveDetector:
ApplyLiveDetectorDisplay_NoLock();
break;
case MainViewportSourceMode.ManualImage:
ApplyManualDisplay_NoLock();
break;
}
}
if (!changed)
return;
_logger.Info("主界面图像来源已切换为 {Mode}", sourceMode);
RaiseStateChanged();
}
public void UpdateDetectorFrame(DetectorFrame frame)
{
if (frame == null)
return;
bool shouldRaise = false;
lock (_syncRoot)
{
_latestDetectorImage = frame.PreviewImage;
_latestDetectorInfo = $"实时探测器图像 {frame.Width}x{frame.Height} 帧#{frame.FrameId} {frame.CaptureTime:HH:mm:ss.fff}";
if (_currentSourceMode == MainViewportSourceMode.LiveDetector && _isRealtimeDisplayEnabled)
{
_currentDisplayImage = _latestDetectorImage;
_currentDisplayInfo = _latestDetectorInfo;
shouldRaise = true;
}
}
if (shouldRaise)
RaiseStateChanged();
}
public void SetCncResultImage(ImageSource image, string label)
{
if (image == null)
return;
var displayLabel = string.IsNullOrWhiteSpace(label) ? "CNC 节点结果" : label;
lock (_syncRoot)
{
// Intentionally bypasses the _isCncRunning guard so the node result
// is visible in the viewport immediately after each node completes.
_latestManualImage = image;
_latestManualInfo = displayLabel;
_currentSourceMode = MainViewportSourceMode.ManualImage;
_currentDisplayImage = _latestManualImage;
_currentDisplayInfo = _latestManualInfo;
}
_logger.Info("[图像链路] MainViewportService.SetCncResultImage:已推送 CNC 节点结果图像 {Label}", displayLabel);
RaiseStateChanged();
}
public void SetManualImage(ImageSource image, string filePath)
{
if (image == null)
return;
var fileName = string.IsNullOrWhiteSpace(filePath) ? "未命名图像" : Path.GetFileName(filePath);
lock (_syncRoot)
{
if (_isCncRunning)
{
_logger.Warn("CNC 正在运行,忽略 SetManualImage 调用");
return;
}
_latestManualImage = image;
_latestManualInfo = $"手动加载图像 {fileName}";
_currentSourceMode = MainViewportSourceMode.ManualImage;
_currentDisplayImage = _latestManualImage;
_currentDisplayInfo = _latestManualInfo;
}
_logger.Info("[图像链路] MainViewportService.SetManualImage:已更新图像 {FileName},触发 StateChanged", fileName);
RaiseStateChanged();
}
public void SetCncRunning(bool isRunning)
{
bool modeChanged = false;
lock (_syncRoot)
{
_isCncRunning = isRunning;
if (isRunning && _currentSourceMode == MainViewportSourceMode.ManualImage)
{
_currentSourceMode = MainViewportSourceMode.LiveDetector;
ApplyLiveDetectorDisplay_NoLock();
modeChanged = true;
}
}
_logger.Info("CNC 运行状态已更新:{IsRunning}", isRunning);
if (modeChanged) RaiseStateChanged();
}
private void ApplyLiveDetectorDisplay_NoLock()
{
_currentDisplayImage = _latestDetectorImage;
_currentDisplayInfo = _latestDetectorImage == null
? "等待探测器图像..."
: _latestDetectorInfo;
}
private void ApplyManualDisplay_NoLock()
{
_currentDisplayImage = _latestManualImage;
_currentDisplayInfo = _latestManualImage == null
? "未加载手动图像"
: _latestManualInfo;
}
private void RaiseStateChanged()
{
StateChanged?.Invoke(this, EventArgs.Empty);
}
private static bool ReadBoolean(string key, bool defaultValue)
{
var raw = ConfigurationManager.AppSettings[key];
return bool.TryParse(raw, out var parsed) ? parsed : defaultValue;
}
}
}