Keep wires attached when a project's copy of a symbol is replaced

A wire whose terminals have uuids is saved against them, and on load it
is reattached to a terminal with that uuid or dropped, with only a qDebug
line. Re-importing a changed symbol and choosing "replace" swapped the
project's definition for one whose terminal uuids differ; the placed
symbols kept the old ones until the project was reopened, so every wire
on them was lost at the next open, silently, and gone for good at the
next save.

- XmlElementCollection::copyElement(), where an embedded definition is
  overwritten, carries each old terminal uuid onto the new terminal at
  the same place and orientation (TerminalUuids::keep()). Terminals that
  moved, and new ones, keep their own.
- Diagram::fromXml() records wires it could not reattach, logs them, and
  the editor lists them in one warning after opening a project.

Measured on 2612_ats_singlephase.qet with the stored splice's terminal
uuids made to differ from the collection's: replace, save, reopen loads
34 of 131 wires on master, 131 with this change (GUI, both arms).
tst_terminaluuids covers keep() and runs the real loader.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-09-29 07:21:19 +13:00
parent 9cbf26dcc9
commit e6ac117f9b
9 changed files with 401 additions and 1 deletions
+47
View File
@@ -773,6 +773,18 @@ QUuid Diagram::uuid()
return m_uuid;
}
/**
@brief Diagram::wiresNotReconnected
@return one line per wire of the loaded file that was left out because
a terminal it joins could not be found, for example after the symbol's
definition in the project was replaced by one whose terminals differ.
Empty for a folio that loaded every wire.
*/
QStringList Diagram::wiresNotReconnected() const
{
return m_wires_not_reconnected;
}
/**
@brief Diagram::uuidUsedByOtherDiagram
A hand-edited or merged project file can contain two folios with the same
@@ -1837,6 +1849,8 @@ bool Diagram::fromXml(QDomElement &document,
}
// Load conductor
if (consider_informations)
m_wires_not_reconnected.clear();
QList<Conductor *> added_conductors;
for (auto f : QET::findInDomElement(root,
QStringLiteral("conductors"),
@@ -1849,6 +1863,33 @@ bool Diagram::fromXml(QDomElement &document,
Terminal* p1 = findTerminal(1, f, table_adr_id, added_elements);
Terminal* p2 = findTerminal(2, f, table_adr_id, added_elements);
//Keep a trace of the wire, it will be missing from the next save
if ((!p1 || !p2) && consider_informations)
{
//The symbol's label, else its name. For an end not found,
//only the uuid form of a wire says which symbol it is on.
auto end_label = [&f, &added_elements](const QString &index,
Terminal *found) {
Element *element = found ? found->parentElement() : nullptr;
const QUuid uuid(f.attribute(QStringLiteral("element") + index));
for (int i = 0 ; !element && !uuid.isNull()
&& i < added_elements.size() ; ++i) {
if (added_elements.at(i)->uuid() == uuid)
element = added_elements.at(i);
}
if (!element)
return QStringLiteral("?");
const QString label = element->actualLabel();
return label.isEmpty() ? element->name() : label;
};
QString wire = QStringLiteral("%1 - %2").arg(end_label(QStringLiteral("1"), p1),
end_label(QStringLiteral("2"), p2));
const QString num = f.attribute(QStringLiteral("num"));
if (!num.isEmpty() && num != QLatin1String("_"))
wire += QStringLiteral(" (%1)").arg(num);
m_wires_not_reconnected << wire;
}
if (p1 && p2 && p1 != p2)
{
Conductor *c = new Conductor(p1, p2);
@@ -1884,6 +1925,12 @@ bool Diagram::fromXml(QDomElement &document,
delete c;
}
}
if (!m_wires_not_reconnected.isEmpty()) {
qWarning().noquote() << "Diagram::fromXml():"
<< m_wires_not_reconnected.size()
<< "wire(s) not loaded, a terminal they join was not found:"
<< m_wires_not_reconnected.join(QStringLiteral(", "));
}
//Filling of falculatory lists
if (content_ptr) {