using System.IO;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests.S7_1500;
using ZB.MOM.WW.OtOpcUa.Driver.S7.SymbolImport;
namespace ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests.SymbolImport;
///
/// PR-S7-D3 / #301 — golden-fixture integration test for instance-DB / FB parameter
/// resolution. Loads Fixtures/sample_tia_export_with_fb_instance.csv, materialises
/// a driver-options object via ,
/// and exercises the runtime read path against the python-snap7 simulator using the
/// resolved instance-DB addresses.
///
///
///
/// The fixture mixes three categories so a single import covers the full
/// DB type column matrix:
///
/// - Two global-DB tags (DB type = Global DB) — pre-existing D1 path.
/// - Five instance-DB tags (DB type = Instance DB) — new D3 path.
/// - One HMI-hidden row — must be filtered.
/// - One M-area probe (no DB type) — global by default.
///
///
///
/// The instance-DB tag addresses deliberately overlap with the snap7 seed offsets
/// baked into so a successful round-trip proves the
/// resolver normalises addresses identically to the global-DB path. The simulator
/// doesn't know (or care) that a tag came from an FB-instance row — what we're
/// testing is that the importer recognised it and the driver
/// accepted the resolved address verbatim.
///
///
/// Auto-skips when no simulator is reachable (build-only on hosts without snap7).
///
///
[Collection(Snap7ServerCollection.Name)]
[Trait("Category", "Integration")]
[Trait("Device", "S7_1500")]
public sealed class InstanceDbImportIntegrationTests(Snap7ServerFixture sim)
{
private static string FixturePath(string name) =>
Path.Combine(AppContext.BaseDirectory, "Fixtures", name);
[Fact]
public async Task Driver_resolves_fb_instance_then_reads_seeded_member()
{
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
var baseOptions = new S7DriverOptions
{
Host = sim.Host,
Port = sim.Port,
CpuType = global::S7.Net.CpuType.S71500,
Timeout = TimeSpan.FromSeconds(5),
Probe = new S7ProbeOptions { Enabled = false },
Tags = [],
};
var options = baseOptions.AddTiaCsvImport(
FixturePath("sample_tia_export_with_fb_instance.csv"),
out var importResult);
// Fixture: 9 rows total = 1 probe + 2 global-DB + 5 instance-DB + 1 hidden.
// Hidden filtered → SkippedCount = 1; everything else lands.
importResult.ParsedCount.ShouldBe(8);
importResult.SkippedCount.ShouldBe(1);
importResult.InstanceDbCount.ShouldBe(5);
importResult.UdtPlaceholderCount.ShouldBe(0);
importResult.ErrorCount.ShouldBe(0);
options.Tags.Count.ShouldBe(8);
await using var drv = new S7Driver(options, driverInstanceId: "s7-tia-import-fb");
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
// Read three resolved instance-DB members. Each address overlaps a snap7 seed in
// S7_1500Profile, so a successful round-trip proves the resolver pointed the
// driver at the right absolute byte offset.
var snapshots = await drv.ReadAsync(
["MotorFB_Inst.Speed", "MotorFB_Inst.Setpoint", "MotorFB_Inst.Enabled"],
TestContext.Current.CancellationToken);
snapshots.Count.ShouldBe(3);
foreach (var s in snapshots)
s.StatusCode.ShouldBe(0u, "instance-DB resolved address must read end-to-end");
// Speed sits at DB1.DBW10 → SmokeI16Seed; Setpoint at DB1.DBD30 → SmokeF32Seed;
// Enabled at DB1.DBX50.3 → SmokeBool.
Convert.ToInt32(snapshots[0].Value).ShouldBe((int)S7_1500Profile.SmokeI16SeedValue);
Convert.ToSingle(snapshots[1].Value).ShouldBe(S7_1500Profile.SmokeF32SeedValue, tolerance: 0.0001f);
Convert.ToBoolean(snapshots[2].Value).ShouldBeTrue();
}
}