edz: name terminals terminalNr.designation (e.g. "XDI.2")

Previous logic resolved duplicate connectionDesignation values by
appending sanitised description text or numeric suffixes, which was
fragile and produced names an electrician could not easily map back to
the physical wiring.

New scheme:
  • terminalNr present  → "XDI.2", "XRO1.3", "XPOW.1" …
  • terminalNr absent   → designation as-is ("L1/U1", "UDC+", "PE") —
    these are busbar / power connections and are already globally unique
  • collision (malformed data) → numeric suffix as safety net

The description (connectiondescription) remains as the human-readable
label beside the terminal symbol, exactly as suggested by plc-user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Shane Ringrose
2026-06-21 08:58:30 +12:00
parent 03e6e76b3f
commit 8268df8a80
+19 -51
View File
@@ -238,63 +238,31 @@ QDomDocument EdzElementBuilder::build(const EdzPart &part)
dyn.appendChild(info_name); dyn.appendChild(info_name);
desc.appendChild(dyn); desc.appendChild(dyn);
// Build unique terminal names. QET requires every terminal in an element // Build unique terminal names that an electrician can read directly on
// to have a distinct name for wiring and terminal-diagram generation to // a wiring list. When a terminalNr connector label is present, prefix it:
// work. EPLAN parts sometimes repeat the same connectionDesignation across // "XDI.2" (terminal block XDI, position 2)
// multiple function templates (e.g. a drive with several connections named // "XRO1.3" (relay output block XRO1, position 3)
// "1", "2", "3"). Resolve duplicates: // Power/busbar connections that carry no terminalNr use their designation
// 1. If a designation is unique, keep it as-is. // as-is — these are already globally unique ("L1/U1", "UDC+", "PE", …).
// 2. If duplicated, append "_" + sanitised description when that yields // A numeric suffix is appended only if a collision still occurs, which
// a unique result (e.g. "1_L+_P" -> "1_L_P"). // should not happen with well-formed EPLAN data.
// 3. Fall back to appending "_2", "_3", … when descriptions are missing
// or still collide.
auto sanitise = [](const QString &s) -> QString {
QString out;
for (const QChar c : s) {
out += (c.isLetterOrNumber() || c == QLatin1Char('-')) ? c
: QLatin1Char('_');
}
// Collapse consecutive underscores and strip trailing ones.
while (out.contains(QStringLiteral("__")))
out.replace(QStringLiteral("__"), QStringLiteral("_"));
while (out.endsWith(QLatin1Char('_')))
out.chop(1);
return out;
};
// Count occurrences of each raw designation.
QMap<QString, int> desig_count;
for (const EdzPin &p : pins)
desig_count[p.designation]++;
QSet<QString> used_names; QSet<QString> used_names;
QVector<QString> terminal_names(n); QVector<QString> terminal_names(n);
// Per-designation occurrence counter for the fallback suffix.
QMap<QString, int> desig_seen;
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
const EdzPin &pin = pins.at(i); const EdzPin &pin = pins.at(i);
desig_seen[pin.designation]++; QString name = pin.group.isEmpty()
? pin.designation
if (desig_count[pin.designation] == 1) { : pin.group + QLatin1Char('.') + pin.designation;
// Unique designation — use directly. // Collision guard (malformed data safety net).
terminal_names[i] = pin.designation; if (used_names.contains(name)) {
} else { int suffix = 2;
// Try designation + sanitised description first. while (used_names.contains(name + QLatin1Char('_') + QString::number(suffix)))
const QString candidate = pin.description.isEmpty() ++suffix;
? QString() name += QLatin1Char('_') + QString::number(suffix);
: pin.designation + QLatin1Char('_') + sanitise(pin.description);
if (!candidate.isEmpty() && !used_names.contains(candidate)) {
terminal_names[i] = candidate;
} else {
// Numeric suffix fallback: "1", "1_2", "1_3", …
const int occ = desig_seen[pin.designation];
terminal_names[i] = (occ == 1)
? pin.designation
: pin.designation + QLatin1Char('_') + QString::number(occ);
}
} }
used_names.insert(terminal_names[i]); terminal_names[i] = name;
used_names.insert(name);
} }
// Terminals. // Terminals.