135 lines
6.8 KiB
C#
135 lines
6.8 KiB
C#
using ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
||
|
||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests;
|
||
|
||
/// <summary>
|
||
/// Per-family provisioning profile for the <c>ab_server</c> simulator. Instead of hard-coding
|
||
/// one fixture shape + one set of CLI args, each integration test picks a profile matching the
|
||
/// family it wants to exercise — ControlLogix / CompactLogix / Micro800 / GuardLogix. The
|
||
/// profile composes the CLI arg list passed to <c>ab_server</c> + the tag-definition set the
|
||
/// driver uses to address the simulator's pre-provisioned tags.
|
||
/// </summary>
|
||
/// <param name="Family">OtOpcUa driver family this profile targets. Drives
|
||
/// <see cref="AbCipDeviceOptions.PlcFamily"/> + driver-side connection-parameter profile
|
||
/// (ConnectionSize, unconnected-only, etc.) per decision #9.</param>
|
||
/// <param name="AbServerPlcArg">The value passed to <c>ab_server --plc <arg></c>. Some families
|
||
/// map 1:1 (ControlLogix → "controllogix"); Micro800/GuardLogix fall back to the family whose
|
||
/// CIP behavior ab_server emulates most faithfully (see per-profile Notes).</param>
|
||
/// <param name="SeedTags">Tags to preseed on the simulator via <c>--tag <name>:<type>[:<size>]</c>
|
||
/// flags. Each entry becomes one CLI arg; the driver-side <see cref="AbCipTagDefinition"/>
|
||
/// list references the same names so tests can read/write without walking the @tags surface
|
||
/// first.</param>
|
||
/// <param name="Notes">Operator-facing description of what the profile covers + any quirks.</param>
|
||
public sealed record AbServerProfile(
|
||
AbCipPlcFamily Family,
|
||
string AbServerPlcArg,
|
||
IReadOnlyList<AbServerSeedTag> SeedTags,
|
||
string Notes)
|
||
{
|
||
/// <summary>Default port — every profile uses the same so parallel-runs-of-different-families
|
||
/// would conflict (deliberately — one simulator per test collection is the model).</summary>
|
||
public const int DefaultPort = 44818;
|
||
|
||
/// <summary>Compose the full <c>ab_server</c> CLI arg string for
|
||
/// <see cref="System.Diagnostics.ProcessStartInfo.Arguments"/>.</summary>
|
||
public string BuildCliArgs(int port)
|
||
{
|
||
var parts = new List<string>
|
||
{
|
||
"--port", port.ToString(),
|
||
"--plc", AbServerPlcArg,
|
||
};
|
||
foreach (var tag in SeedTags)
|
||
{
|
||
parts.Add("--tag");
|
||
parts.Add(tag.ToCliSpec());
|
||
}
|
||
return string.Join(' ', parts);
|
||
}
|
||
}
|
||
|
||
/// <summary>One tag the simulator pre-creates. ab_server spec format:
|
||
/// <c><name>:<type>[:<array_size>]</c>.</summary>
|
||
public sealed record AbServerSeedTag(string Name, string AbServerType, int? ArraySize = null)
|
||
{
|
||
public string ToCliSpec() => ArraySize is { } n ? $"{Name}:{AbServerType}:{n}" : $"{Name}:{AbServerType}";
|
||
}
|
||
|
||
/// <summary>Canonical profiles covering every AB CIP family shipped in PRs 9–12.</summary>
|
||
public static class KnownProfiles
|
||
{
|
||
/// <summary>
|
||
/// ControlLogix — the widest-coverage family: full CIP capabilities, generous connection
|
||
/// size, @tags controller-walk supported. Tag shape covers atomic types + a Program-scoped
|
||
/// tag so the Symbol-Object decoder's scope-split path is exercised.
|
||
/// </summary>
|
||
public static readonly AbServerProfile ControlLogix = new(
|
||
Family: AbCipPlcFamily.ControlLogix,
|
||
AbServerPlcArg: "controllogix",
|
||
SeedTags: new AbServerSeedTag[]
|
||
{
|
||
new("TestDINT", "DINT"),
|
||
new("TestREAL", "REAL"),
|
||
new("TestBOOL", "BOOL"),
|
||
new("TestSINT", "SINT"),
|
||
new("TestString","STRING"),
|
||
new("TestArray", "DINT", ArraySize: 16),
|
||
},
|
||
Notes: "Widest-coverage profile — PR 9 baseline. UDTs live in PR 6-shipped Template Object tests; ab_server lacks full UDT emulation.");
|
||
|
||
/// <summary>
|
||
/// CompactLogix — narrower ConnectionSize quirk exercised here. ab_server doesn't
|
||
/// enforce the narrower limit itself; the driver-side profile caps it + this simulator
|
||
/// honors whatever the client asks for. Tag set is a subset of ControlLogix.
|
||
/// </summary>
|
||
public static readonly AbServerProfile CompactLogix = new(
|
||
Family: AbCipPlcFamily.CompactLogix,
|
||
AbServerPlcArg: "compactlogix",
|
||
SeedTags: new AbServerSeedTag[]
|
||
{
|
||
new("TestDINT", "DINT"),
|
||
new("TestREAL", "REAL"),
|
||
new("TestBOOL", "BOOL"),
|
||
},
|
||
Notes: "Narrower ConnectionSize than ControlLogix — driver-side profile caps it per PR 10. Tag set mirrors the CompactLogix atomic subset.");
|
||
|
||
/// <summary>
|
||
/// Micro800 — unconnected-only family. ab_server has no explicit micro800 plc mode so
|
||
/// we fall back to the nearest CIP-compatible emulation (controllogix) + document the
|
||
/// discrepancy. Driver-side path enforcement (empty routing path, unconnected-only
|
||
/// sessions) is exercised in the unit suite; this integration profile smoke-tests that
|
||
/// reads work end-to-end against the unconnected path.
|
||
/// </summary>
|
||
public static readonly AbServerProfile Micro800 = new(
|
||
Family: AbCipPlcFamily.Micro800,
|
||
AbServerPlcArg: "controllogix", // ab_server lacks dedicated micro800 mode — see Notes
|
||
SeedTags: new AbServerSeedTag[]
|
||
{
|
||
new("TestDINT", "DINT"),
|
||
new("TestREAL", "REAL"),
|
||
},
|
||
Notes: "ab_server has no --plc micro800 — falls back to controllogix emulation. Driver side still enforces empty path + unconnected-only per PR 11. Real Micro800 coverage requires a 2080 on a lab rig.");
|
||
|
||
/// <summary>
|
||
/// GuardLogix — safety-capable ControlLogix variant with ViewOnly safety tags. ab_server
|
||
/// doesn't emulate the safety subsystem; we preseed a safety-suffixed name (<c>_S</c>) so
|
||
/// the driver's read-only classification path is exercised against a real tag.
|
||
/// </summary>
|
||
public static readonly AbServerProfile GuardLogix = new(
|
||
Family: AbCipPlcFamily.GuardLogix,
|
||
AbServerPlcArg: "controllogix",
|
||
SeedTags: new AbServerSeedTag[]
|
||
{
|
||
new("TestDINT", "DINT"),
|
||
new("SafetyDINT_S", "DINT"), // _S-suffixed → driver classifies as safety-ViewOnly per PR 12
|
||
},
|
||
Notes: "ab_server has no safety subsystem — this profile emulates the tag-naming contract. Real safety-lock behavior requires a physical GuardLogix 1756-L8xS rig.");
|
||
|
||
public static IReadOnlyList<AbServerProfile> All { get; } =
|
||
new[] { ControlLogix, CompactLogix, Micro800, GuardLogix };
|
||
|
||
public static AbServerProfile ForFamily(AbCipPlcFamily family) =>
|
||
All.FirstOrDefault(p => p.Family == family)
|
||
?? throw new ArgumentOutOfRangeException(nameof(family), family, "No integration profile for this family.");
|
||
}
|