|
|
|
|
@@ -7,14 +7,38 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
|
|
|
|
|
/// 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.
|
|
|
|
|
/// <para>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.</para>
|
|
|
|
|
/// <para>PR ablegacy-13 / #256 — the optional <see cref="BackplaneSlot"/>,
|
|
|
|
|
/// <see cref="DhPlusPort"/> and <see cref="DhPlusStation"/> fields are populated when the
|
|
|
|
|
/// CIP path matches the canonical 1756-DHRIO bridge form <c>1,<slot>,2,<station></c>:
|
|
|
|
|
/// port 1 (backplane) → DHRIO module slot → port 2 (DH+ side of the module) → DH+ node
|
|
|
|
|
/// address. The DH+ station number is octal in PLC-5 firmware (0..77 = decimal 0..63);
|
|
|
|
|
/// we parse it as octal and surface the decimal value for diagnostics. DHRIO bridging is
|
|
|
|
|
/// PLC-5-only — the family-validation guard lives on the driver.</para>
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public sealed record AbLegacyHostAddress(string Gateway, int Port, string CipPath)
|
|
|
|
|
public sealed record AbLegacyHostAddress(
|
|
|
|
|
string Gateway,
|
|
|
|
|
int Port,
|
|
|
|
|
string CipPath,
|
|
|
|
|
int? BackplaneSlot = null,
|
|
|
|
|
int? DhPlusPort = null,
|
|
|
|
|
int? DhPlusStation = null)
|
|
|
|
|
{
|
|
|
|
|
public const int DefaultEipPort = 44818;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Maximum chassis slot index accepted for the DHRIO bridge form. Real ControlLogix
|
|
|
|
|
/// chassis are 4 / 7 / 10 / 13 / 17 slots; capping at 16 covers the largest standard
|
|
|
|
|
/// 17-slot frame (slots 0..16) without rejecting anything an operator might legitimately
|
|
|
|
|
/// configure. Tighter / family-specific bounds are enforced elsewhere.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const int MaxBackplaneSlot = 16;
|
|
|
|
|
|
|
|
|
|
/// <summary>True iff the CIP path was the canonical 1756-DHRIO DH+ bridge form.</summary>
|
|
|
|
|
public bool IsDhPlusBridge => DhPlusStation is not null;
|
|
|
|
|
|
|
|
|
|
public override string ToString() => Port == DefaultEipPort
|
|
|
|
|
? $"ab://{Gateway}/{CipPath}"
|
|
|
|
|
: $"ab://{Gateway}:{Port}/{CipPath}";
|
|
|
|
|
@@ -48,6 +72,73 @@ public sealed record AbLegacyHostAddress(string Gateway, int Port, string CipPat
|
|
|
|
|
}
|
|
|
|
|
if (string.IsNullOrEmpty(gateway)) return null;
|
|
|
|
|
|
|
|
|
|
// PR ablegacy-13 / #256 — optional DHRIO DH+ bridge path detection.
|
|
|
|
|
// Shape: exactly four comma-separated decimal segments `port,slot,port,station` with
|
|
|
|
|
// port[0]=1 (backplane), slot in [0..16], port[2]=2 (DH+), station octal 0..77.
|
|
|
|
|
// Anything else is left as an opaque CIP path — direct-wired PLCs, MicroLogix empty
|
|
|
|
|
// paths, longer multi-hop bridges all flow through unchanged.
|
|
|
|
|
if (TryParseDhPlusBridge(cipPath, out var slot, out var dhPort, out var station))
|
|
|
|
|
{
|
|
|
|
|
return new AbLegacyHostAddress(gateway, port, cipPath,
|
|
|
|
|
BackplaneSlot: slot,
|
|
|
|
|
DhPlusPort: dhPort,
|
|
|
|
|
DhPlusStation: station);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new AbLegacyHostAddress(gateway, port, cipPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns <c>true</c> iff <paramref name="cipPath"/> is exactly the four-segment DHRIO
|
|
|
|
|
/// bridge form <c>1,<slot>,2,<station></c>. Returns <c>false</c> for every
|
|
|
|
|
/// other shape — including malformed near-misses (slot out of range, station out of
|
|
|
|
|
/// octal range, port-1 ≠ backplane). A near-miss returns false rather than throwing so
|
|
|
|
|
/// the caller can keep treating the CIP path as opaque.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This method is the single source of truth for the DHRIO octal-station range check.
|
|
|
|
|
/// Reuses the same "octal accepts only 0..7" rule as <c>AbLegacyAddress</c>'s PLC-5
|
|
|
|
|
/// I/O parser — a leading <c>8</c> or <c>9</c> in any digit is rejected.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private static bool TryParseDhPlusBridge(
|
|
|
|
|
string cipPath, out int slot, out int dhPort, out int station)
|
|
|
|
|
{
|
|
|
|
|
slot = 0;
|
|
|
|
|
dhPort = 0;
|
|
|
|
|
station = 0;
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(cipPath)) return false;
|
|
|
|
|
|
|
|
|
|
// Reject leading / trailing whitespace inside segments — `1, 3, 2, 07` (with spaces)
|
|
|
|
|
// is plausibly user typo but we keep the parser strict to avoid false positives.
|
|
|
|
|
var parts = cipPath.Split(',');
|
|
|
|
|
if (parts.Length != 4) return false;
|
|
|
|
|
|
|
|
|
|
if (!int.TryParse(parts[0], out var firstPort) || firstPort != 1) return false;
|
|
|
|
|
if (!int.TryParse(parts[1], out slot) || slot < 0 || slot > MaxBackplaneSlot) return false;
|
|
|
|
|
if (!int.TryParse(parts[2], out dhPort) || dhPort != 2) return false;
|
|
|
|
|
if (!TryParseOctal(parts[3], out station) || station < 0 || station > 63) return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Parse a DH+ station number written in octal (0..77 octal = 0..63 decimal). Mirrors
|
|
|
|
|
/// the octal-rule in <c>AbLegacyAddress.TryParseIndex</c> — digits 0..7 only, no sign,
|
|
|
|
|
/// no prefix. Returns <c>false</c> for empty input, illegal digits (8/9), or non-digit
|
|
|
|
|
/// characters.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static bool TryParseOctal(string text, out int value)
|
|
|
|
|
{
|
|
|
|
|
value = 0;
|
|
|
|
|
if (string.IsNullOrEmpty(text)) return false;
|
|
|
|
|
var acc = 0;
|
|
|
|
|
foreach (var c in text)
|
|
|
|
|
{
|
|
|
|
|
if (c < '0' || c > '7') return false;
|
|
|
|
|
acc = (acc * 8) + (c - '0');
|
|
|
|
|
}
|
|
|
|
|
value = acc;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|