Export the wiring list without building every folio (stage D5)

The wiring list export read the project as QETProject::toXml() writes
it, which builds every folio. It now reads a document made without
building: a built folio written as saving writes it
(QETProject::folioXml(), which keeps its symbols' links to partners not
built), a folio not built copied from its XML, and what QElectroTech
computes for a wire's ends -- its symbols' labels, their last partner's
label, the terminals' names -- filled in from the symbols built and from
the project database. A symbol still waiting for a partner not built
takes its partners from the database, saved order.

136-folio .qetz, wiring list from the command line: 30.1 s with every
folio built, 4.5 s lazily, same file. tst_lazyfolios: the wiring list of
a .qet and of a .qetz as when built, building no folio. New corpus gate
(it fails on
all 6 projects tried with terminal names broken): --export-cables and
--export-wires lazily from the .qet and the .qetz = eager on 342
projects; one (forum f3386) differed until waiting symbols took their
partners from the database. ctest 96/96 eager and lazy (one earlier
run had modal_quit_regression and tst_resaveunchanged fail once each,
both passing on rerun in both modes); 342 corpus projects save the same
in all four arms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-12 12:20:46 +13:00
parent b972dbec4b
commit 65b10b01d9
8 changed files with 153 additions and 11 deletions
+28
View File
@@ -1289,6 +1289,34 @@ QList<projectDataBase::FolioWire> projectDataBase::wiresOnFolios(const QList<QUu
return wires;
}
/**
@brief projectDataBase::elementLabel
@return the label the symbol @p element shows, read from the element
info table as it is (see elementTypesOnFolios())
*/
QString projectDataBase::elementLabel(const QUuid &element) const
{
QSqlQuery query(m_data_base);
query.prepare(QStringLiteral("SELECT label FROM element_info WHERE element_uuid = :uuid"));
query.bindValue(QStringLiteral(":uuid"), element.toString());
return query.exec() && query.next() ? query.value(0).toString() : QString();
}
/**
@brief projectDataBase::terminalName
@return the name of the terminal @p terminal of the symbol @p element,
read from the terminal table as it is (see elementTypesOnFolios()): a
terminal a wire ends on has a row
*/
QString projectDataBase::terminalName(const QUuid &terminal, const QUuid &element) const
{
QSqlQuery query(m_data_base);
query.prepare(QStringLiteral("SELECT name FROM terminal WHERE uuid = :uuid AND element_uuid = :element"));
query.bindValue(QStringLiteral(":uuid"), terminal.toString());
query.bindValue(QStringLiteral(":element"), element.toString());
return query.exec() && query.next() ? query.value(0).toString() : QString();
}
/**
@brief projectDataBase::linkedSymbols
@return the symbols @p element is linked to, read from the link table
+2
View File
@@ -195,6 +195,8 @@ class projectDataBase : public QObject
/// symbols at its ends (ElementData::typeToString())
struct FolioWire { QString text; QString kind1; QString kind2; };
QList<FolioWire> wiresOnFolios(const QList<QUuid> &folios) const;
QString elementLabel(const QUuid &element) const;
QString terminalName(const QUuid &terminal, const QUuid &element) const;
static QVariant sequenceValue(const autonum::sequentialNumbers &sequence);
static autonum::sequentialNumbers sequenceFromValue(const QVariant &value);
+2
View File
@@ -229,6 +229,8 @@ class Diagram : public QGraphicsScene
/// Symbols built as partners (buildSymbols()) changed since:
/// the folio must be built to be saved
bool partialChanged() const {return m_partial_changed;}
/// The symbols built as partners (buildSymbols()), by uuid
QHash<QUuid, Element *> partialElements() const {return m_partial_elements;}
QString headerXml();
/// The folio's text in the file it was read from, while it is
/// not built (QETProject::toXml())
+14
View File
@@ -2978,6 +2978,20 @@ bool QETProject::usesElement(const ElementsLocation &location) const
return(unbuiltFolioElements().contains(location));
}
/**
@brief QETProject::folioXml
@return the built folio @p folio written as saving writes it: its
symbols keep the links to partners on folios not built yet
(QET_LAZY_FOLIOS), which Diagram::toXml() alone leaves out
*/
QDomDocument QETProject::folioXml(Diagram *folio)
{
m_waiting_link_targets = unloadedFolioCount() ? symbolsOnUnbuiltFolios() : QSet<QUuid>();
const QDomDocument xml = folio->toXml();
m_waiting_link_targets.clear();
return xml;
}
/**
@brief QETProject::buildFoliosHoldingTables
Build the folios not built yet (QET_LAZY_FOLIOS) that hold a drawn
+1
View File
@@ -140,6 +140,7 @@ class QETProject : public QObject
/// While saving: the symbols on folios not built a built symbol
/// may be saved linked to (Element::toXml())
QSet<QUuid> waitingLinkTargets() const {return m_waiting_link_targets;}
QDomDocument folioXml(Diagram *folio);
QSet<Diagram *> tableChainFolios(Diagram *folio) const;
void loadFolios() const;
void buildFolios(const QList<Diagram *> &folios) const;
+87 -1
View File
@@ -1,6 +1,9 @@
#include "wiringlistexport.h"
#include "qetproject.h"
#include "diagram.h"
#include "dataBase/projectdatabase.h"
#include "ElementsCollection/xmlelementcollection.h"
#include "qetgraphicsitem/element.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QTextStream>
@@ -48,6 +51,89 @@ QDomElement WiringListExport::climbToDiagram(QDomNode node) const
return QDomElement();
}
/**
The project as the export reads it: its symbol collection and its folios
in order, each with its "order". A folio not built (QET_LAZY_FOLIOS) is
not built for it: its XML is copied, and what QElectroTech computes for
its wires' ends -- the labels of the symbols they end on and of their
partners, the terminals' names -- is filled in from the symbols built
(on built folios or alone, as partners) and from the project database.
*/
QDomDocument WiringListExport::projectDocument() const
{
QDomDocument document;
QDomElement root = document.createElement(QStringLiteral("project"));
document.appendChild(root);
if (XmlElementCollection *collection = m_project->embeddedElementCollection())
root.appendChild(document.importNode(collection->root(), true));
//A folio whose partners built alone were linked or unlinked since
//is not what its XML says: built, as saving builds it
for (Diagram *diagram : m_project->folios())
if (!diagram->isLoaded() && diagram->partialChanged())
m_project->buildFolios({diagram});
QHash<QUuid, Element *> built;
for (Diagram *diagram : m_project->folios()) {
if (diagram->isLoaded()) {
for (Element *element : diagram->elements()) built.insert(element->uuid(), element);
} else {
const QHash<QUuid, Element *> partial = diagram->partialElements();
for (auto it = partial.cbegin() ; it != partial.cend() ; ++it) built.insert(it.key(), it.value());
}
}
projectDataBase *database = m_project->dataBase();
auto label = [&built, database](const QUuid &uuid) {
Element *element = built.value(uuid);
return element ? element->actualLabel() : database->elementLabel(uuid);
};
//Conductor::toXml(): the label of the last of the symbol's partners.
//A symbol still waiting for a partner not built has its links as
//saved, in the database
auto linked = [&built, database, &label](const QUuid &uuid) -> std::optional<QString> {
Element *element = built.value(uuid);
if (element && element->pendingLinks().isEmpty()) {
if (element->linkedElements().isEmpty()) return std::nullopt;
return element->linkedElements().last()->actualLabel();
}
const QList<QUuid> links = database->linkedSymbols(uuid);
if (links.isEmpty()) return std::nullopt;
return label(links.last());
};
const QList<Diagram *> folios = m_project->folios();
for (int i = 0 ; i < folios.size() ; ++i) {
Diagram *diagram = folios.at(i);
QDomElement folio;
const bool is_built = diagram->isLoaded();
folio = document.importNode(is_built ? m_project->folioXml(diagram).documentElement()
: diagram->deferredXml(), true).toElement();
//A built folio's wires describe their ends but a partner not
//built yet; a folio not built has none of it in its part (.qetz)
//or as read, before any change (.qet)
QDomNodeList wires = folio.elementsByTagName(QStringLiteral("conductor"));
for (int w = 0 ; w < wires.size() ; ++w) {
QDomElement wire = wires.at(w).toElement();
for (const QString end : {QStringLiteral("1"), QStringLiteral("2")}) {
const QUuid element(wire.attribute(QStringLiteral("element") + end));
if (element.isNull()) continue; //an end saved the older way
if (const auto partner = linked(element))
wire.setAttribute(QStringLiteral("element") + end + QStringLiteral("_linked"), *partner);
else
wire.removeAttribute(QStringLiteral("element") + end + QStringLiteral("_linked"));
if (is_built) continue;
wire.setAttribute(QStringLiteral("element") + end + QStringLiteral("_label"), label(element));
wire.setAttribute(QStringLiteral("terminalname") + end,
database->terminalName(QUuid(wire.attribute(QStringLiteral("terminal") + end)),
element));
}
}
folio.setAttribute(QStringLiteral("order"), i + 1);
root.appendChild(folio);
}
return document;
}
QMap<QString, ElementInfo> WiringListExport::collectElementsInfo(const QDomElement &root) const
{
QMap<QString, ElementInfo> infoMap;
@@ -191,7 +277,7 @@ QString WiringListExport::toCsvString() const
{
if (!m_project) return QString();
QDomDocument doc = m_project->toXml();
QDomDocument doc = projectDocument();
if (doc.isNull()) return QString();
QSet<QString> conductorDefinitionTypes;
+1
View File
@@ -60,6 +60,7 @@ private:
QString normalizeUuid(const QString &u) const;
QString findDiagramFolio(const QDomElement &diagramElem) const;
QDomElement climbToDiagram(QDomNode node) const;
QDomDocument projectDocument() const;
QMap<QString, ElementInfo> collectElementsInfo(const QDomElement &root) const;
QList<ConductorData> collectConductors(const QDomElement &root) const;
+18 -10
View File
@@ -385,8 +385,16 @@ int main(int argc, char **argv)
QETProject exported(path);
check(ConductorNumExport(&exported).wiresNum() == ConductorNumExport(&eager).wiresNum(),
"wire numbers of a .qet exported as when built");
const QString wiring = WiringListExport(&exported).toCsvString(), usual_wiring = WiringListExport(&eager).toCsvString();
if (wiring != usual_wiring) {
const QStringList a = wiring.split('\n'), b = usual_wiring.split('\n');
for (const QString &line : b) if (!a.contains(line)) std::printf(" wiring when built: %s\n", qPrintable(line));
for (const QString &line : a) if (!b.contains(line)) std::printf(" wiring lazily: %s\n", qPrintable(line));
std::printf(" %d lines lazily, %d when built\n", int(a.size()), int(b.size()));
}
check(wiring == usual_wiring, "wiring list of a .qet exported as when built");
check(exported.unloadedFolioCount() == folios.size() - strip_folios.size(),
"exporting a .qet's wire numbers builds no folio");
"exporting a .qet's wire numbers and wiring list builds no folio");
}
//A symbol placed on a folio, numbered by a scheme others follow
QString location, title;
@@ -707,13 +715,6 @@ int main(int argc, char **argv)
++continued;
}
check(continued > 0, "a folio whose table continues another");
{
//The wiring list export writes the whole project out
//(toXml()), building every folio: as when built
QETProject exported(zipped);
check(WiringListExport(&exported).toCsvString() == WiringListExport(&eager).toCsvString(),
"wiring list exported as when built");
}
//Numbering, the searches for symbols and the unused symbols
//are answered from the database: no folio file is read
@@ -799,7 +800,7 @@ int main(int argc, char **argv)
check(listed.unloadedFolioCount() > 0, "listing the tables does not build every folio");
}
//The wire numbers export and the terminal
//The wire numbers and wiring list exports, and the terminal
//numbering dialog: as when built, building only the folios
//holding a terminal (D5)
{
@@ -807,7 +808,14 @@ int main(int argc, char **argv)
const int unbuilt = exported.unloadedFolioCount();
check(ConductorNumExport(&exported).wiresNum() == ConductorNumExport(&eager).wiresNum(),
"wire numbers exported as when built");
check(exported.unloadedFolioCount() == unbuilt, "exporting wire numbers builds no folio");
const QString wiring = WiringListExport(&exported).toCsvString(), usual_wiring = WiringListExport(&eager).toCsvString();
if (wiring != usual_wiring) {
const QStringList a = wiring.split('\n'), b = usual_wiring.split('\n');
for (const QString &line : b) if (!a.contains(line)) std::printf(" wiring when built: %s\n", qPrintable(line));
for (const QString &line : a) if (!b.contains(line)) std::printf(" wiring lazily: %s\n", qPrintable(line));
}
check(wiring == usual_wiring, "wiring list exported as when built");
check(exported.unloadedFolioCount() == unbuilt, "exporting wire numbers and the wiring list builds no folio");
{ TerminalNumberingDialog dialog(nullptr, &exported); }
int with_terminals = 0;
for (Diagram *folio : folios)