using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests;
///
/// Phase 4b — per-axis auto-scale from cnc_getfigure figures. The driver fetches
/// per-axis position decimal-place figures at init and applies them at the
/// publish seam. Precedence: an auto figure for an axis WINS;
/// the configured PositionDecimalPlaces is the per-axis fallback when the CNC did
/// not report a figure for that axis. (The managed wire backend returns no figures today,
/// so the real backend always uses the manual fallback — these tests drive the Fake.)
///
[Trait("Category", "Unit")]
public sealed class FocasPositionAutoScaleTests
{
private const string Host = "focas://10.0.0.5:8193";
// A single-axis CNC (X only) so the snapshot we seed is unambiguous.
private static FakeFocasClient SingleAxisClient(
FocasDynamicSnapshot snap, IReadOnlyList figures)
{
var c = new FakeFocasClient();
c.AxisNames.Clear();
c.AxisNames.Add(new("X", ""));
c.DynamicByAxis[1] = snap; // FOCAS axis indexing is 1-based
c.PositionFigures = figures;
return c;
}
// A two-axis CNC (X, Y) — each axis gets its own seeded snapshot + figure.
private static FakeFocasClient TwoAxisClient(
FocasDynamicSnapshot xSnap, FocasDynamicSnapshot ySnap, IReadOnlyList figures)
{
var c = new FakeFocasClient();
c.AxisNames.Clear();
c.AxisNames.Add(new("X", ""));
c.AxisNames.Add(new("Y", ""));
c.DynamicByAxis[1] = xSnap; // axis 0 → 1-based index 1
c.DynamicByAxis[2] = ySnap; // axis 1 → 1-based index 2
c.PositionFigures = figures;
return c;
}
private static FocasDynamicSnapshot AbsSnap(int axisIndex, int absolutePosition) =>
new(AxisIndex: axisIndex, AlarmFlags: 0, ProgramNumber: 0, MainProgramNumber: 0,
SequenceNumber: 0, ActualFeedRate: 0, ActualSpindleSpeed: 0,
AbsolutePosition: absolutePosition, MachinePosition: 0,
RelativePosition: 0, DistanceToGo: 0);
private static FocasDriver NewDriver(int positionDecimalPlaces, FakeFocasClientFactory factory)
{
return new FocasDriver(new FocasDriverOptions
{
Devices = [new FocasDeviceOptions(Host, PositionDecimalPlaces: positionDecimalPlaces)],
Probe = new FocasProbeOptions { Enabled = false },
FixedTree = new FocasFixedTreeOptions
{
Enabled = true,
PollInterval = TimeSpan.FromMilliseconds(10),
},
}, "drv-1", factory);
}
// Drive the fixed-tree poll loop and wait (bounded) for the cached value to appear.
private static async Task