70 lines
3.1 KiB
C#
70 lines
3.1 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests;
|
|
|
|
/// <summary>
|
|
/// A controller's <c>@tags</c> listing names each program (<c>Program:UN01_Press</c>, a system
|
|
/// symbol) but none of its tags — those are listed only by <c>Program:<name>.@tags</c>.
|
|
/// Seen live on a production press: the PackML state (<c>Program:UN01_Press.Out_eState</c>) and
|
|
/// every alarm bit the Ignition project reads are program-scoped, and the browse showed none.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class AbCipProgramScopeEnumerationTests
|
|
{
|
|
// Captured entries (CompactLogix 5069-L320ERS2 rev 34.11): WW_InCycle + the Program:UN01_Press entry
|
|
// from @tags, and Out_eState from Program:UN01_Press.@tags.
|
|
private const string ControllerListing =
|
|
"AF010000C10001000000000000000000000000000A0057575F496E4379636C65" +
|
|
"7900000068100000000000000000000000000000120050726F6772616D3A554E30315F5072657373";
|
|
|
|
private const string Un01Listing = "1A000000C40004000000000000000000000000000A004F75745F655374617465";
|
|
|
|
private static readonly AbCipTagCreateParams Params = new(
|
|
Gateway: "10.0.0.5", Port: 44818, CipPath: "1,0", LibplctagPlcAttribute: "controllogix",
|
|
TagName: "@tags", Timeout: TimeSpan.FromSeconds(2));
|
|
|
|
/// <summary>The enumerator reads each program's own listing and yields its tags program-scoped.</summary>
|
|
[Fact]
|
|
public async Task Program_scoped_tags_are_enumerated_from_each_program_listing()
|
|
{
|
|
var reads = new List<string>();
|
|
using var enumerator = new SymbolListingEnumerator((_, name, _) =>
|
|
{
|
|
reads.Add(name);
|
|
return Task.FromResult(name switch
|
|
{
|
|
"@tags" => Convert.FromHexString(ControllerListing),
|
|
"Program:UN01_Press.@tags" => Convert.FromHexString(Un01Listing),
|
|
_ => throw new InvalidOperationException(name),
|
|
});
|
|
});
|
|
|
|
var tags = new List<AbCipDiscoveredTag>();
|
|
await foreach (var t in enumerator.EnumerateAsync(Params, TestContext.Current.CancellationToken))
|
|
tags.Add(t);
|
|
|
|
reads.ShouldBe(["@tags", "Program:UN01_Press.@tags"]);
|
|
var outState = tags.Single(t => t.Name == "Out_eState");
|
|
outState.ProgramScope.ShouldBe("UN01_Press");
|
|
outState.DataType.ShouldBe(AbCipDataType.DInt);
|
|
tags.ShouldContain(t => t.Name == "WW_InCycle" && t.ProgramScope == null);
|
|
}
|
|
|
|
/// <summary>One unreadable program listing does not cost the controller tags or other programs.</summary>
|
|
[Fact]
|
|
public async Task A_failing_program_listing_is_skipped()
|
|
{
|
|
using var enumerator = new SymbolListingEnumerator((_, name, _) => name == "@tags"
|
|
? Task.FromResult(Convert.FromHexString(ControllerListing))
|
|
: Task.FromException<byte[]>(new InvalidOperationException("ErrorNotFound")));
|
|
|
|
var tags = new List<AbCipDiscoveredTag>();
|
|
await foreach (var t in enumerator.EnumerateAsync(Params, TestContext.Current.CancellationToken))
|
|
tags.Add(t);
|
|
|
|
tags.ShouldContain(t => t.Name == "WW_InCycle");
|
|
}
|
|
}
|