54 lines
2.0 KiB
C#
54 lines
2.0 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
|
|
|
|
/// <summary>
|
|
/// Parsed <c>ab://gateway[:port]/cip-path</c> host-address string for AB Legacy devices.
|
|
/// Same format as AbCip — PCCC-over-EIP uses the same gateway + optional routing path as
|
|
/// the CIP family (a PLC-5 bridged through a ControlLogix chassis takes the full CIP path;
|
|
/// a direct-wired SLC 500 uses an empty path).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Parser duplicated from AbCipHostAddress rather than shared because the two drivers ship
|
|
/// independently + a shared helper would force a reference between them. If a third AB
|
|
/// driver appears, extract into Core.Abstractions.
|
|
/// </remarks>
|
|
public sealed record AbLegacyHostAddress(string Gateway, int Port, string CipPath)
|
|
{
|
|
public const int DefaultEipPort = 44818;
|
|
|
|
public override string ToString() => Port == DefaultEipPort
|
|
? $"ab://{Gateway}/{CipPath}"
|
|
: $"ab://{Gateway}:{Port}/{CipPath}";
|
|
|
|
public static AbLegacyHostAddress? TryParse(string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value)) return null;
|
|
const string prefix = "ab://";
|
|
if (!value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) return null;
|
|
|
|
var remainder = value[prefix.Length..];
|
|
var slashIdx = remainder.IndexOf('/');
|
|
if (slashIdx < 0) return null;
|
|
|
|
var authority = remainder[..slashIdx];
|
|
var cipPath = remainder[(slashIdx + 1)..];
|
|
if (string.IsNullOrEmpty(authority)) return null;
|
|
|
|
var port = DefaultEipPort;
|
|
var colonIdx = authority.LastIndexOf(':');
|
|
string gateway;
|
|
if (colonIdx >= 0)
|
|
{
|
|
gateway = authority[..colonIdx];
|
|
if (!int.TryParse(authority[(colonIdx + 1)..], out port) || port is <= 0 or > 65535)
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
gateway = authority;
|
|
}
|
|
if (string.IsNullOrEmpty(gateway)) return null;
|
|
|
|
return new AbLegacyHostAddress(gateway, port, cipPath);
|
|
}
|
|
}
|