调整硬件appstate 保持与硬件库层面定义一致
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
using Moq;
|
||||
using Prism.Events;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
using XP.Hardware.MotionControl.Abstractions.Enums;
|
||||
using XP.Hardware.MotionControl.Abstractions.Events;
|
||||
using XP.Hardware.MotionControl.Services;
|
||||
using XP.Hardware.RaySource.Services;
|
||||
using XplorePlane.Models;
|
||||
using XplorePlane.Services.AppState;
|
||||
@@ -11,30 +16,62 @@ using Xunit.Abstractions;
|
||||
namespace XplorePlane.Tests.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// AppStateService 单元测试。
|
||||
/// 验证默认状态值、Dispose 后行为、null 参数校验、CalibrationMatrix 缺失时的错误处理。
|
||||
/// AppStateService unit tests.
|
||||
/// Verifies default values, null guards, dispose behavior, and hardware-driven motion-state sync.
|
||||
/// </summary>
|
||||
public class AppStateServiceTests : IDisposable
|
||||
{
|
||||
private readonly AppStateService _service;
|
||||
private readonly Mock<IRaySourceService> _mockRaySource;
|
||||
private readonly Mock<IMotionSystem> _mockMotionSystem;
|
||||
private readonly Mock<IMotionControlService> _mockMotionControlService;
|
||||
private readonly Mock<ILinearAxis> _mockStageX;
|
||||
private readonly Mock<ILinearAxis> _mockStageY;
|
||||
private readonly Mock<ILinearAxis> _mockSourceZ;
|
||||
private readonly Mock<ILinearAxis> _mockDetectorZ;
|
||||
private readonly Mock<IRotaryAxis> _mockDetectorSwing;
|
||||
private readonly Mock<ILoggerService> _mockLogger;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public AppStateServiceTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
|
||||
// Ensure WPF Application exists for Dispatcher
|
||||
if (Application.Current == null)
|
||||
{
|
||||
new Application();
|
||||
}
|
||||
|
||||
_mockRaySource = new Mock<IRaySourceService>();
|
||||
_mockMotionSystem = new Mock<IMotionSystem>();
|
||||
_mockMotionControlService = new Mock<IMotionControlService>();
|
||||
_mockStageX = CreateLinearAxis(AxisId.StageX, 0);
|
||||
_mockStageY = CreateLinearAxis(AxisId.StageY, 0);
|
||||
_mockSourceZ = CreateLinearAxis(AxisId.SourceZ, 0);
|
||||
_mockDetectorZ = CreateLinearAxis(AxisId.DetectorZ, 0);
|
||||
_mockDetectorSwing = CreateRotaryAxis(RotaryAxisId.DetectorSwing, 0);
|
||||
_mockLogger = new Mock<ILoggerService>();
|
||||
_eventAggregator = new EventAggregator();
|
||||
|
||||
_mockMotionSystem.Setup(x => x.GetLinearAxis(AxisId.StageX)).Returns(_mockStageX.Object);
|
||||
_mockMotionSystem.Setup(x => x.GetLinearAxis(AxisId.StageY)).Returns(_mockStageY.Object);
|
||||
_mockMotionSystem.Setup(x => x.GetLinearAxis(AxisId.SourceZ)).Returns(_mockSourceZ.Object);
|
||||
_mockMotionSystem.Setup(x => x.GetLinearAxis(AxisId.DetectorZ)).Returns(_mockDetectorZ.Object);
|
||||
_mockMotionSystem.Setup(x => x.GetRotaryAxis(RotaryAxisId.DetectorSwing)).Returns(_mockDetectorSwing.Object);
|
||||
|
||||
_mockMotionControlService
|
||||
.Setup(x => x.GetCurrentGeometry())
|
||||
.Returns((0d, 0d, 1d));
|
||||
|
||||
_mockLogger.Setup(l => l.ForModule<AppStateService>()).Returns(_mockLogger.Object);
|
||||
_service = new AppStateService(_mockRaySource.Object, _mockLogger.Object);
|
||||
|
||||
_service = new AppStateService(
|
||||
_mockRaySource.Object,
|
||||
_mockMotionSystem.Object,
|
||||
_mockMotionControlService.Object,
|
||||
_eventAggregator,
|
||||
_mockLogger.Object);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -42,13 +79,15 @@ namespace XplorePlane.Tests.Services
|
||||
_service.Dispose();
|
||||
}
|
||||
|
||||
// ── 默认状态值验证 ──
|
||||
|
||||
[Fact]
|
||||
public void DefaultState_MotionState_IsDefault()
|
||||
public void DefaultState_MotionState_IsHardwareSnapshot()
|
||||
{
|
||||
_output.WriteLine($"MotionState == MotionState.Default: {ReferenceEquals(MotionState.Default, _service.MotionState)}");
|
||||
Assert.Same(MotionState.Default, _service.MotionState);
|
||||
Assert.Equal(0, _service.MotionState.XM);
|
||||
Assert.Equal(0, _service.MotionState.YM);
|
||||
Assert.Equal(0, _service.MotionState.ZT);
|
||||
Assert.Equal(0, _service.MotionState.ZD);
|
||||
Assert.Equal(0, _service.MotionState.TiltD);
|
||||
Assert.Equal(0, _service.MotionState.Dist);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -72,8 +111,6 @@ namespace XplorePlane.Tests.Services
|
||||
Assert.Null(_service.CalibrationMatrix);
|
||||
}
|
||||
|
||||
// ── null 参数抛出 ArgumentNullException ──
|
||||
|
||||
[Fact]
|
||||
public void UpdateMotionState_NullArgument_ThrowsArgumentNullException()
|
||||
{
|
||||
@@ -102,36 +139,66 @@ namespace XplorePlane.Tests.Services
|
||||
_output.WriteLine($"UpdateSystemState(null) threw: {ex.GetType().Name}, Param={ex.ParamName}");
|
||||
}
|
||||
|
||||
// ── Dispose 后 Update 被忽略 ──
|
||||
|
||||
[Fact]
|
||||
public void Dispose_ThenUpdate_IsIgnored()
|
||||
{
|
||||
var originalState = _service.MotionState;
|
||||
_service.Dispose();
|
||||
_output.WriteLine("Service disposed, attempting UpdateMotionState...");
|
||||
|
||||
// Should not throw, and state should remain unchanged
|
||||
var newState = new MotionState(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
|
||||
_service.UpdateMotionState(newState);
|
||||
|
||||
_output.WriteLine($"State unchanged after dispose: {ReferenceEquals(originalState, _service.MotionState)}");
|
||||
Assert.Same(originalState, _service.MotionState);
|
||||
}
|
||||
|
||||
// ── CalibrationMatrix 为 null 时 RequestLinkedView 设置错误状态 ──
|
||||
|
||||
[Fact]
|
||||
public void RequestLinkedView_NoCalibrationMatrix_SetsErrorState()
|
||||
{
|
||||
// CalibrationMatrix is null by default
|
||||
Assert.Null(_service.CalibrationMatrix);
|
||||
|
||||
_service.RequestLinkedView(100.0, 200.0);
|
||||
|
||||
_output.WriteLine($"RequestLinkedView(100, 200) without CalibrationMatrix: HasError={_service.SystemState.HasError}, ErrorMessage='{_service.SystemState.ErrorMessage}'");
|
||||
Assert.True(_service.SystemState.HasError);
|
||||
Assert.NotEmpty(_service.SystemState.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GeometryUpdatedEvent_RefreshesMotionStateFromHardware()
|
||||
{
|
||||
_mockStageX.SetupGet(x => x.ActualPosition).Returns(12.5);
|
||||
_mockStageY.SetupGet(x => x.ActualPosition).Returns(34.5);
|
||||
_mockSourceZ.SetupGet(x => x.ActualPosition).Returns(56.5);
|
||||
_mockDetectorZ.SetupGet(x => x.ActualPosition).Returns(78.5);
|
||||
_mockDetectorSwing.SetupGet(x => x.ActualAngle).Returns(9.5);
|
||||
|
||||
_eventAggregator.GetEvent<GeometryUpdatedEvent>()
|
||||
.Publish(new GeometryData(100, 222.2, 2.22));
|
||||
|
||||
Assert.Equal(12.5, _service.MotionState.XM);
|
||||
Assert.Equal(34.5, _service.MotionState.YM);
|
||||
Assert.Equal(56.5, _service.MotionState.ZT);
|
||||
Assert.Equal(78.5, _service.MotionState.ZD);
|
||||
Assert.Equal(9.5, _service.MotionState.TiltD);
|
||||
Assert.Equal(222.2, _service.MotionState.Dist);
|
||||
}
|
||||
|
||||
private static Mock<ILinearAxis> CreateLinearAxis(AxisId axisId, double position)
|
||||
{
|
||||
var axis = new Mock<ILinearAxis>();
|
||||
axis.SetupGet(x => x.Id).Returns(axisId);
|
||||
axis.SetupGet(x => x.ActualPosition).Returns(position);
|
||||
axis.SetupGet(x => x.Status).Returns(AxisStatus.Idle);
|
||||
return axis;
|
||||
}
|
||||
|
||||
private static Mock<IRotaryAxis> CreateRotaryAxis(RotaryAxisId axisId, double angle)
|
||||
{
|
||||
var axis = new Mock<IRotaryAxis>();
|
||||
axis.SetupGet(x => x.Id).Returns(axisId);
|
||||
axis.SetupGet(x => x.ActualAngle).Returns(angle);
|
||||
axis.SetupGet(x => x.Status).Returns(AxisStatus.Idle);
|
||||
axis.SetupGet(x => x.Enabled).Returns(true);
|
||||
return axis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user