282 lines
8.8 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|