diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasDuplicateSiblingNameTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasDuplicateSiblingNameTests.cs index a2d1c33b..441b03aa 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasDuplicateSiblingNameTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasDuplicateSiblingNameTests.cs @@ -53,10 +53,24 @@ public sealed class FocasDuplicateSiblingNameTests { var drv = NewDriver(tags); await drv.InitializeAsync("{}", CancellationToken.None); - var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(5); - while (DateTime.UtcNow < deadline && drv.GetDeviceState(Host)?.LastFixedPollUtc is null) - await Task.Delay(25, TestContext.Current.CancellationToken); - drv.GetDeviceState(Host)!.LastFixedPollUtc.ShouldNotBeNull("the fixed-tree loop never completed a poll"); + // LastFixedPollUtc alone is NOT "the first axis pass finished": the bootstrap stamps it + // after the sysinfo probe, before any axis snapshot is published (#591 — under load a + // read slipped into that window and saw BadNodeIdUnknown). Wait for the data itself: + // the last axis slot plus the servo/spindle caches this fake's first tick populates. + var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(30); + var ready = false; + while (!ready && DateTime.UtcNow < deadline) + { + var st = drv.GetDeviceState(Host); + ready = st is not null + && st.LastFixedPollUtc is not null + && st.LastFixedSnapshots.ContainsKey(FocasDriver.FixedTreeReference(Host, "Axes/AA_2/AbsolutePosition")) + && st.LastServoLoads.Count >= 3 + && st.LastSpindleLoads.Count >= 2 + && st.LastSpindleMotorSpeeds.Count >= 2; + if (!ready) await Task.Delay(10, TestContext.Current.CancellationToken); + } + ready.ShouldBeTrue("the fixed-tree loop never completed its first full poll"); return drv; }