diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendCoverageTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendCoverageTests.cs
index b45b89f5..52f1b22c 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendCoverageTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendCoverageTests.cs
@@ -237,6 +237,73 @@ public sealed class WireBackendCoverageTests
}
}
+ ///
+ /// Verifies that per-axis position figures fetched via cnc_getfigure (wire command
+ /// 0x00D3) scale the published AbsolutePosition, and that the auto figure wins over
+ /// the configured PositionDecimalPlaces fallback knob.
+ /// Seed: X axis absolute = 12345, figure = 3 → published value = 12.345 (÷ 10^3).
+ /// Config knob = 1 → fallback would give 1234.5. 12.345 uniquely proves the wire path.
+ ///
+ [Fact]
+ public async Task Position_figures_scale_axis_via_wire_backend()
+ {
+ if (_fx.SkipReason is not null) Assert.Skip(_fx.SkipReason);
+ var ct = TestContext.Current.CancellationToken;
+
+ await _fx.LoadProfileAsync("FWLIB64", ct);
+ await _fx.PatchStateAsync(new
+ {
+ axis_names = new[] { "X" },
+ dynamic = new
+ {
+ alarm = 0, prgnum = 1, prgmnum = 1, seqnum = 1,
+ actf = 0, acts = 0,
+ axes = new
+ {
+ X = new { absolute = 12345, machine = 12345, relative = 0, distance = 0 },
+ },
+ },
+ // Per-axis decimal-place figures for cnc_getfigure (command 0x00D3).
+ // The mock's _wire_position_figures() reads state["position_figures"][axisName].
+ position_figures = new { X = 3 },
+ }, ct);
+
+ // PositionDecimalPlaces = 1 is intentionally different from the auto figure (3)
+ // so the assertion 12.345 uniquely proves the cnc_getfigure path won.
+ var driver = new FocasDriver(new FocasDriverOptions
+ {
+ Devices = [new FocasDeviceOptions(DeviceHost, PositionDecimalPlaces: 1)],
+ Tags = [],
+ Probe = new FocasProbeOptions { Enabled = false },
+ FixedTree = new FocasFixedTreeOptions
+ {
+ Enabled = true,
+ PollInterval = TimeSpan.FromMilliseconds(100),
+ ProgramPollInterval = TimeSpan.Zero,
+ TimerPollInterval = TimeSpan.Zero,
+ },
+ }, driverInstanceId: "wire-figures", clientFactory: new WireFocasClientFactory());
+
+ await using (driver)
+ {
+ await driver.InitializeAsync("{}", ct);
+
+ await WaitFor(() =>
+ driver.GetDeviceState(DeviceHost)?.LastFixedSnapshots
+ .ContainsKey($"{DeviceHost}/Axes/X/AbsolutePosition") == true,
+ TimeSpan.FromSeconds(5));
+
+ var state = driver.GetDeviceState(DeviceHost);
+ state.ShouldNotBeNull();
+ var published = state.LastFixedSnapshots[$"{DeviceHost}/Axes/X/AbsolutePosition"];
+
+ // 12345 ÷ 10^3 = 12.345 → auto figure (3) won.
+ // 12345 ÷ 10^1 = 1234.5 → would mean config knob (1) was used instead.
+ // 12345 ÷ 10^0 = 12345.0 → would mean no scaling at all.
+ published.ShouldBe(12.345, tolerance: 0.0001);
+ }
+ }
+
private static async Task WaitFor(Func pred, TimeSpan timeout)
{
var deadline = DateTime.UtcNow + timeout;