957157ae80
## Compilation fixes - Fix XAML namespace reference XplorePlane.Converters → XplorePlane.Helpers (EventLogView, StateDisplayView) - Add missing using for ISampleTypeRepository / JsonSampleTypeRepository in App.xaml.cs - Fix ILoggerService.Warn() calls using Exception as first arg instead of message string - Serialize CncProgram to JSON when passing to MatrixLayout (expects string, not CncProgram object) - Suppress CS8632 nullable annotation warnings project-wide ## BGA wizard fixes - Fix preview not showing on step 3: force RefreshBgaPreview() when entering final step - Add step validation in CanNext() to prevent skipping to preview with invalid inputs - Notify NextCommand.CanExecuteChanged when inputs change - Fix RunProgress runtime binding error: set Mode=OneWay for ProgressBar.Value ## Operator toolbox: prevent duplicate insertion - Add static OperatorTarget property for direct dispatch to active pipeline - PipelineEditorView registers as toolbox target on load (floating window only) - CNC page sets initial target; PipelineEditorWindow resets to CNC pipeline on close - '+' button inserts only to the active pipeline instead of broadcasting to all subscribers ## Pipeline editor status bar messages - Add StatusBarMessageEvent / StatusBarMessagePayload to CommonEvents.cs - PipelineEditorViewModel publishes status on all ops (add/remove/reorder/save/execute) - MainViewModel subscribes and displays on main window bottom status bar - Auto-clear after timeout (3s info, 5s error) ## docs - Merge three architecture docs into consolidated XplorePlane-架构与结构说明.md
66 lines
2.2 KiB
C#
66 lines
2.2 KiB
C#
using System.Threading.Tasks;
|
|
using Xunit;
|
|
using XP.Hardware.MotionControl.Abstractions.Enums;
|
|
using XP.Hardware.MotionControl.Implementations;
|
|
|
|
namespace XplorePlane.Tests.Hardware
|
|
{
|
|
public class SimulatedRotaryAxisTests
|
|
{
|
|
[Fact]
|
|
public async Task MoveToTarget_SetsStatusToMoving_ThenIdle()
|
|
{
|
|
// Arrange - use fast speed for quicker test
|
|
var axis = new SimulatedRotaryAxis(
|
|
RotaryAxisId.DetectorSwing, minAngle: -180, maxAngle: 180, enabled: true, defaultSpeed: 200);
|
|
|
|
// Act
|
|
var result = axis.MoveToTarget(90);
|
|
|
|
// Assert - should be Moving immediately after call
|
|
Assert.True(result.Success);
|
|
Assert.Equal(AxisStatus.Moving, axis.Status);
|
|
|
|
// Wait for move to complete (90° at 200°/s = 450ms, add generous buffer)
|
|
await Task.Delay(1000);
|
|
|
|
Assert.Equal(AxisStatus.Idle, axis.Status);
|
|
Assert.Equal(90.0, axis.ActualAngle, precision: 1);
|
|
}
|
|
|
|
[Fact]
|
|
public void MoveToTarget_OutOfRange_ReturnsFail()
|
|
{
|
|
// Arrange
|
|
var axis = new SimulatedRotaryAxis(
|
|
RotaryAxisId.DetectorSwing, minAngle: -180, maxAngle: 180, enabled: true);
|
|
|
|
// Act
|
|
var result = axis.MoveToTarget(200);
|
|
|
|
// Assert
|
|
Assert.False(result.Success);
|
|
Assert.NotNull(result.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Stop_DuringMove_KeepsCurrentAngle()
|
|
{
|
|
// Arrange - use slow speed so we can stop mid-move
|
|
var axis = new SimulatedRotaryAxis(
|
|
RotaryAxisId.DetectorSwing, minAngle: -180, maxAngle: 180, enabled: true, defaultSpeed: 10);
|
|
|
|
// Act - start a long move (180° at 10°/s = 18s)
|
|
axis.MoveToTarget(180);
|
|
await Task.Delay(200); // Let it move a bit
|
|
|
|
axis.Stop();
|
|
|
|
// Assert
|
|
Assert.Equal(AxisStatus.Idle, axis.Status);
|
|
Assert.True(axis.ActualAngle > 0, "Angle should have moved from 0");
|
|
Assert.True(axis.ActualAngle < 180, "Angle should not have reached target");
|
|
}
|
|
}
|
|
}
|