Change a wire's properties by uuid without building its folios (stage D5)

Scripts get conductorPropertyByUuid() and setConductorPropertyByUuid(),
the wire named by its uuid (conductorUuids()) rather than by a symbol's
terminal. A change goes to every wire at the wire's potential, as
setConductorProperty() does. When those wires are all on folios not
built, none is numbered by a formula, and what changes in each is what a
.qetz database holds in its own columns (number, function, voltage,
colour and section of the wire, cable, bus), it goes through the project
database: the store and the conductor rows, undoable by uuid. The
potential is followed in the database (projectDataBase::potentialWires():
through wire ends, folio reports and terminal blocks, as
Conductor::relatedPotentialConductors() follows it; a terminal block that
isolates its terminals answers nothing). Building a wire takes its change
(Conductor::fromXml()); a .qetz save writes it in the database, the folio
file copied; a folio written from its XML gets it there; a .qet builds.
Anything else builds the folio, as before.

tst_lazyfolios: every wire's potential from the database = as followed,
1 342 of 1 342 (industrial, eager and from a .qetz); a cable set by uuid
on a 14-wire potential builds no folio, reaches every wire, saves as when
built, undoes, shows once built (fails when building ignores the change,
and when the .qetz writer does). The test's run limit is raised (500 s ->
1 500 s, ctest 1 800 s): under load it ran past 500 s. ctest 96/96 both
ways.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-12 23:26:39 +13:00
parent b8390c853c
commit 798010cc83
10 changed files with 507 additions and 2 deletions
+173
View File
@@ -1317,6 +1317,77 @@ QString projectDataBase::terminalName(const QUuid &terminal, const QUuid &elemen
return query.exec() && query.next() ? query.value(0).toString() : QString();
}
/**
@brief projectDataBase::potentialWires
@return the wires at the potential of @p wire, @p wire included, read
from the conductor, element and link tables as they are (see
elementTypesOnFolios()), followed as Conductor::relatedPotentialConductors()
follows them over every folio: through a wire's ends, from a folio
report to the terminals of the first symbol it is linked to, and from a
terminal block's terminal to its others. Nothing when a terminal block
on the way isolates its terminals (their groups are in its definition,
not in the database) or @p wire has no row.
*/
std::optional<QSet<QUuid>> projectDataBase::potentialWires(const QUuid &wire) const
{
using Node = std::pair<QString, QString>; //element uuid, terminal uuid
QHash<QString, std::pair<Node, Node>> ends;
QHash<Node, QStringList> wires_at;
QHash<QString, QList<Node>> nodes_of;
{
QSqlQuery query(m_data_base);
query.exec(QStringLiteral("SELECT uuid, terminal1_element_uuid, terminal1_uuid, "
"terminal2_element_uuid, terminal2_uuid FROM conductor"));
while (query.next()) {
const Node a{query.value(1).toString(), query.value(2).toString()};
const Node b{query.value(3).toString(), query.value(4).toString()};
const QString uuid = query.value(0).toString();
ends.insert(uuid, {a, b});
for (const Node &n : {a, b}) {
wires_at[n] << uuid;
if (!nodes_of.value(n.first).contains(n)) nodes_of[n.first] << n;
}
}
}
const auto start = ends.constFind(wire.toString());
if (start == ends.constEnd()) {
return std::nullopt;
}
QHash<QString, ElementData::Type> kinds;
{
QSqlQuery query(m_data_base);
query.exec(QStringLiteral("SELECT uuid, type FROM element"));
while (query.next())
kinds.insert(query.value(0).toString(), ElementData::typeFromString(query.value(1).toString()));
}
QSet<QUuid> wires;
QSet<Node> seen;
QList<Node> todo{start->first, start->second};
while (!todo.isEmpty()) {
const Node node = todo.takeLast();
if (seen.contains(node)) continue;
seen.insert(node);
for (const QString &w : wires_at.value(node)) {
if (wires.contains(QUuid(w))) continue;
wires.insert(QUuid(w));
const auto e = ends.value(w);
todo << e.first << e.second;
}
const ElementData::Type kind = kinds.value(node.first, ElementData::Simple);
if (kind & ElementData::AllReport) {
const QList<QUuid> partners = linkedSymbols(QUuid(node.first));
if (!partners.isEmpty())
todo << nodes_of.value(partners.first().toString());
} else if (kind & ElementData::Terminal) {
if (QET::infoFlagIsTrue(m_element_information.value(QUuid(node.first))
.value(QStringLiteral("potential_isolating")).toString()))
return std::nullopt;
todo << nodes_of.value(node.first);
}
}
return wires;
}
/**
@brief projectDataBase::linkedSymbols
@return the symbols @p element is linked to, read from the link table
@@ -1467,9 +1538,111 @@ QSet<QUuid> projectDataBase::foliosWithUnbuiltEdits() const
{
QSet<QUuid> folios;
for (const auto &edit : m_unbuilt_edits) folios.insert(edit.folio);
for (const auto &edit : m_unbuilt_wire_edits) folios.insert(edit.folio);
return folios;
}
/**
@brief projectDataBase::unbuiltWire
@return true if the wire @p wire is on a folio not built (lazy folios):
it has a row and its properties are stored, and no wire built holds its
uuid. @p folio is set to its folio.
*/
bool projectDataBase::unbuiltWire(const QUuid &wire, QUuid *folio) const
{
if (placedConductorCount(wire) != 0 || !hasConductorProperties(wire)) {
return false;
}
QSqlQuery query(m_data_base);
query.prepare(QStringLiteral("SELECT diagram_uuid FROM conductor WHERE uuid = :uuid"));
query.bindValue(QStringLiteral(":uuid"), wire.toString());
if (!query.exec() || !query.next()) {
return false;
}
if (folio) *folio = QUuid(query.value(0).toString());
return !query.next();
}
/**
@brief projectDataBase::editUnbuiltConductorProperties
Give the wire @p wire, on a folio not built (unbuiltWire()), the
properties @p properties as Conductor::setProperties() would: the store,
its rows and its conductor row's text. Building it takes them
(takeUnbuiltWireEdit()); a .qetz saves them in its database (QETProject::
toXml()), anything else builds its folio to save.
*/
void projectDataBase::editUnbuiltConductorProperties(const QUuid &wire, const ConductorProperties &properties)
{
QUuid folio;
if (!unbuiltWire(wire, &folio)) {
return;
}
m_unbuilt_wire_edits.insert(wire, {properties, folio});
if (!storeConductorProperties(wire, properties)) {
return;
}
m_content_changed = true;
if (!m_update_blocked) {
m_update_conductor_query.bindValue(QStringLiteral(":uuid"), wire.toString());
m_update_conductor_query.bindValue(QStringLiteral(":text"), properties.text);
if (!m_update_conductor_query.exec()) {
qDebug() << "projectDataBase::editUnbuiltConductorProperties update error : " << m_update_conductor_query.lastError();
}
}
emit dataBaseUpdated();
}
/**
@brief projectDataBase::takeUnbuiltWireEdit
@return the properties @p wire was given while its folio was not built,
forgotten here: the wire being built takes them
*/
std::optional<ConductorProperties> projectDataBase::takeUnbuiltWireEdit(const QUuid &wire)
{
const auto edit = m_unbuilt_wire_edits.constFind(wire);
if (edit == m_unbuilt_wire_edits.constEnd()) {
return std::nullopt;
}
const ConductorProperties properties = edit->properties;
m_unbuilt_wire_edits.erase(edit);
return properties;
}
/**
@brief projectDataBase::unbuiltWireEditsOnFolio
@return the properties given to the wires of the folio @p folio while it
was not built, by wire
*/
QHash<QUuid, ConductorProperties> projectDataBase::unbuiltWireEditsOnFolio(const QUuid &folio) const
{
QHash<QUuid, ConductorProperties> edits;
for (auto it = m_unbuilt_wire_edits.cbegin() ; it != m_unbuilt_wire_edits.cend() ; ++it)
if (it->folio == folio) edits.insert(it.key(), it->properties);
return edits;
}
/**
@brief projectDataBase::setPlacedConductorProperties
Give the wire @p wire its properties @p properties: through the wire when
it is built, through the store when it is not (unbuiltWire()). What
undoing and redoing a change of wires not built does, the wires having
perhaps been built since.
@return false if neither can
*/
bool projectDataBase::setPlacedConductorProperties(const QUuid &wire, const ConductorProperties &properties)
{
const QList<QPointer<Conductor>> placed = m_placed_conductors.values(wire);
if (placed.size() == 1 && placed.first()) {
placed.first()->setProperties(properties);
return true;
}
if (placed.isEmpty() && unbuiltWire(wire)) {
editUnbuiltConductorProperties(wire, properties);
return true;
}
return false;
}
/**
@brief projectDataBase::unbuiltEditsOnFolio
@return the information changed through the store of the symbols on the