fix(abcip): controller browse honours the authored EtherNet/IP port (#539)
The two discovery-path libplctag tags (@tags symbol-list enumerator, @udt template reader) still passed the bare Gateway host, so a browse against a device authored on a non-default port silently browsed whatever answers on 44818 — #534's defect surviving in its discovery half. Both sites now consume AbCipTagCreateParams.LibplctagGateway, with the Tag construction extracted into internal static builders so the seam is unit-assertable (the #534 tests pinned the property's composition but nothing pinned each site's consumption, which is exactly how these two survived). New guard: LibplctagGatewayConsumptionGuardTests scans both AB drivers' sources and fails on any libplctag Tag built from the bare Gateway, so a future call site cannot reintroduce the silent 44818 redirect. Live-gated against the fixture host: browse of ab://10.100.0.35:44818 returns the nine-tag symbol table; browse of ab://10.100.0.35:44999 (nothing listening — the issue's exact repro) now fails ErrorTimeout where it previously returned the 44818 fixture's table. Closes #539. Claude-Session: https://claude.ai/code/session_014WNM4vjoVksyyBraTXSZE1
This commit is contained in:
@@ -26,17 +26,7 @@ internal sealed class LibplctagTagEnumerator : IAbCipTagEnumerator
|
||||
AbCipTagCreateParams deviceParams,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
// Build a tag specifically for the @tags pseudo — same gateway + path as the device,
|
||||
// distinguished by the name alone.
|
||||
_tag = new Tag
|
||||
{
|
||||
Gateway = deviceParams.Gateway,
|
||||
Path = deviceParams.CipPath,
|
||||
PlcType = MapPlcType(deviceParams.LibplctagPlcAttribute),
|
||||
Protocol = Protocol.ab_eip,
|
||||
Name = "@tags",
|
||||
Timeout = deviceParams.Timeout,
|
||||
};
|
||||
_tag = BuildSymbolListTag(deviceParams);
|
||||
|
||||
await _tag.InitializeAsync(cancellationToken).ConfigureAwait(false);
|
||||
await _tag.ReadAsync(cancellationToken).ConfigureAwait(false);
|
||||
@@ -49,6 +39,26 @@ internal sealed class LibplctagTagEnumerator : IAbCipTagEnumerator
|
||||
yield return tag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the (uninitialized) libplctag tag for the <c>@tags</c> pseudo — same gateway +
|
||||
/// path as the device, distinguished by the name alone.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Gateway must be <see cref="AbCipTagCreateParams.LibplctagGateway"/> (host:port), never the
|
||||
/// bare <see cref="AbCipTagCreateParams.Gateway"/> — libplctag substitutes 44818 for a bare
|
||||
/// host, so a browse against a device authored on another port silently returns whatever
|
||||
/// answers on 44818 (Gitea #539, the discovery half of #534).
|
||||
/// </remarks>
|
||||
internal static Tag BuildSymbolListTag(AbCipTagCreateParams deviceParams) => new()
|
||||
{
|
||||
Gateway = deviceParams.LibplctagGateway,
|
||||
Path = deviceParams.CipPath,
|
||||
PlcType = MapPlcType(deviceParams.LibplctagPlcAttribute),
|
||||
Protocol = Protocol.ab_eip,
|
||||
Name = "@tags",
|
||||
Timeout = deviceParams.Timeout,
|
||||
};
|
||||
|
||||
/// <summary>Disposes the enumerator and releases the underlying libplctag tag.</summary>
|
||||
public void Dispose() => _tag?.Dispose();
|
||||
|
||||
|
||||
@@ -19,20 +19,29 @@ internal sealed class LibplctagTemplateReader : IAbCipTemplateReader
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
_tag?.Dispose();
|
||||
_tag = new Tag
|
||||
{
|
||||
Gateway = deviceParams.Gateway,
|
||||
Path = deviceParams.CipPath,
|
||||
PlcType = MapPlcType(deviceParams.LibplctagPlcAttribute),
|
||||
Protocol = Protocol.ab_eip,
|
||||
Name = $"@udt/{templateInstanceId}",
|
||||
Timeout = deviceParams.Timeout,
|
||||
};
|
||||
_tag = BuildTemplateTag(deviceParams, templateInstanceId);
|
||||
await _tag.InitializeAsync(cancellationToken).ConfigureAwait(false);
|
||||
await _tag.ReadAsync(cancellationToken).ConfigureAwait(false);
|
||||
return _tag.GetBuffer();
|
||||
}
|
||||
|
||||
/// <summary>Builds the (uninitialized) libplctag tag for the <c>@udt/{id}</c> pseudo.</summary>
|
||||
/// <remarks>
|
||||
/// Gateway must be <see cref="AbCipTagCreateParams.LibplctagGateway"/> (host:port), never the
|
||||
/// bare <see cref="AbCipTagCreateParams.Gateway"/> — libplctag substitutes 44818 for a bare
|
||||
/// host, so a browse against a device authored on another port silently returns whatever
|
||||
/// answers on 44818 (Gitea #539, the discovery half of #534).
|
||||
/// </remarks>
|
||||
internal static Tag BuildTemplateTag(AbCipTagCreateParams deviceParams, uint templateInstanceId) => new()
|
||||
{
|
||||
Gateway = deviceParams.LibplctagGateway,
|
||||
Path = deviceParams.CipPath,
|
||||
PlcType = MapPlcType(deviceParams.LibplctagPlcAttribute),
|
||||
Protocol = Protocol.ab_eip,
|
||||
Name = $"@udt/{templateInstanceId}",
|
||||
Timeout = deviceParams.Timeout,
|
||||
};
|
||||
|
||||
/// <summary>Disposes the template reader and releases the underlying libplctag tag.</summary>
|
||||
public void Dispose() => _tag?.Dispose();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user