mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-22 08:34:12 +02:00
Add terminal and conductor tables to projectDataBase
Slice 2 of discussion #503 (from-to wiring list built on projectDataBase), building on the conductor uuid from slice 1 (#625). Pure plumbing: two new additive tables plus their populate/add/remove hooks. No view, no UI, no visible behavior change yet -- the wiring-list view is slice 3. Follows the existing shape of the class throughout: same table/column naming, same prepared-statement idiom in prepareQuery(), same bind/exec/qDebug-lastError error handling, same DELETE-then-loop populate pattern. - `terminal (uuid, element_uuid, name)` and `conductor (uuid, diagram_uuid, terminal1_uuid, terminal1_element_uuid, terminal2_uuid, terminal2_element_uuid, text)` created alongside the existing tables in createDataBase(). - populateConductorTable() added as a fifth populate* call in updateDB(). Terminal population is folded into it, since a terminal only matters here in the context of a conductor referencing it. - addConductor()/removeConductor() hooked into the already-existing Conductor::Type branch of Diagram::addItem()/removeItem(), mirroring the Element::Type branch directly above. Two things the original schema sketch in the discussion got wrong, found by testing rather than inspection: 1. Terminal::uuid() is NOT unique per placed terminal. It is the terminal-position id baked into the catalog .elmt definition ("the top terminal"), so every placed instance of the same catalog element shares it. A terminal instance is only uniquely identified by (uuid, element_uuid) together, so that pair is the terminal table's primary key and the conductor table carries both halves for each endpoint. With uuid alone as PK, the second placed instance of any element silently lost its terminals to the INSERT OR IGNORE. 2. Conductors whose terminals predate terminal uuids are omitted rather than given a fabricated identity, as agreed in the discussion. This turns out to matter far more than expected in practice -- see below. Testing (all live, in the running app): - Incremental add: fresh project, two vertically aligned contacts placed so autoconnect creates a conductor -> 2 terminals, 1 conductor. - Incremental remove: deleting that conductor -> conductor count 1 -> 0. - Undo: ctrl+Z after the delete -> back to 1, no duplicate-primary-key error (the same Conductor object keeps its uuid). - Bulk populate: examples/weneedpolonez-Polonez_MR89_wiring_diagram.qet (366 conductors) -> 478 terminals, 280 conductors; the 86 conductors touching legacy terminals correctly omitted. - Join correctness: conductor -> terminal (composite key) -> element_info resolves real from-to rows with real element labels. - Legacy-only project: examples/industrial.qet has 1794 terminals and *zero* terminal uuids, so all 671 of its conductors are omitted. Loads and renders fine, no crash, no spurious rows -- but worth stating plainly that a from-to wiring list for that project would be empty today. This is a property of the element catalog definitions, not of the project file, and is the strongest argument for surfacing an "N conductors excluded" count to the user when the view lands. - No SQL errors logged in any of the above. Known limitation, consistent with existing behavior: removeDiagram() does not cascade-delete the conductor rows of that diagram, exactly as it already does not cascade to element/element_info. A full updateDB() rebuild clears them, and the future wiring-list view INNER JOINs from conductor, so orphan terminal rows never surface.
This commit is contained in:
@@ -21,7 +21,9 @@
|
||||
#include "../diagramposition.h"
|
||||
#include "../elementprovider.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "../qetproject.h"
|
||||
|
||||
@@ -87,6 +89,7 @@ void projectDataBase::updateDB()
|
||||
populateDiagramInfoTable();
|
||||
populateElementTable();
|
||||
populateElementInfoTable();
|
||||
populateConductorTable();
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
|
||||
@@ -245,6 +248,57 @@ void projectDataBase::diagramOrderChanged()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::addConductor
|
||||
@param conductor
|
||||
*/
|
||||
void projectDataBase::addConductor(Conductor *conductor)
|
||||
{
|
||||
if (!conductor || !conductor->diagram()) {
|
||||
qDebug() << "projectDataBase::addConductor: null conductor or diagram";
|
||||
return;
|
||||
}
|
||||
|
||||
//A conductor whose terminal(s) predate terminal uuids (legacy
|
||||
//elements not yet re-saved by a uuid-aware element editor) can't
|
||||
//be given a stable identity here -- omitted the same way
|
||||
//element_nomenclature_view already omits exclude_from_bom elements,
|
||||
//rather than fabricating one.
|
||||
if (conductor->terminal1->uuid().isNull() || conductor->terminal2->uuid().isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
insertTerminal(conductor->terminal1);
|
||||
insertTerminal(conductor->terminal2);
|
||||
|
||||
m_insert_conductor_query.bindValue(":uuid", conductor->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":diagram_uuid", conductor->diagram()->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":terminal1_uuid", conductor->terminal1->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":terminal1_element_uuid", conductor->terminal1->parentElement()->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":terminal2_uuid", conductor->terminal2->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":terminal2_element_uuid", conductor->terminal2->parentElement()->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":text", conductor->properties().text);
|
||||
if (!m_insert_conductor_query.exec()) {
|
||||
qDebug() << "projectDataBase::addConductor insert error : " << m_insert_conductor_query.lastError();
|
||||
} else {
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::removeConductor
|
||||
@param conductor
|
||||
*/
|
||||
void projectDataBase::removeConductor(Conductor *conductor)
|
||||
{
|
||||
m_remove_conductor_query.bindValue(":uuid", conductor->uuid().toString());
|
||||
if (!m_remove_conductor_query.exec()) {
|
||||
qDebug() << "projectDataBase::removeConductor delete error : " << m_remove_conductor_query.lastError();
|
||||
} else {
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::createDataBase
|
||||
Create the data base
|
||||
@@ -323,6 +377,42 @@ bool projectDataBase::createDataBase()
|
||||
qDebug() << " element_info_table query : " << query_.lastError();
|
||||
}
|
||||
|
||||
//Create the terminal table.
|
||||
//Terminal::uuid() is the terminal-position id baked into the catalog
|
||||
//.elmt definition (e.g. "the top terminal") -- identical across every
|
||||
//placed instance of that catalog element, not a per-instance id. A
|
||||
//terminal instance is only uniquely identified by (uuid, element_uuid)
|
||||
//together, so that pair is the primary key here, not uuid alone.
|
||||
QString terminal_table("CREATE TABLE terminal"
|
||||
"( "
|
||||
"uuid VARCHAR(50) NOT NULL, "
|
||||
"element_uuid VARCHAR(50) NOT NULL,"
|
||||
"name VARCHAR(50),"
|
||||
"PRIMARY KEY (uuid, element_uuid),"
|
||||
"FOREIGN KEY (element_uuid) REFERENCES element (uuid)"
|
||||
")");
|
||||
if (!query_.exec(terminal_table)) {
|
||||
qDebug() << "terminal_table query : "<< query_.lastError();
|
||||
}
|
||||
|
||||
//Create the conductor table
|
||||
QString conductor_table("CREATE TABLE conductor"
|
||||
"( "
|
||||
"uuid VARCHAR(50) PRIMARY KEY NOT NULL, "
|
||||
"diagram_uuid VARCHAR(50) NOT NULL,"
|
||||
"terminal1_uuid VARCHAR(50) NOT NULL,"
|
||||
"terminal1_element_uuid VARCHAR(50) NOT NULL,"
|
||||
"terminal2_uuid VARCHAR(50) NOT NULL,"
|
||||
"terminal2_element_uuid VARCHAR(50) NOT NULL,"
|
||||
"text VARCHAR(100),"
|
||||
"FOREIGN KEY (diagram_uuid) REFERENCES diagram (uuid),"
|
||||
"FOREIGN KEY (terminal1_uuid, terminal1_element_uuid) REFERENCES terminal (uuid, element_uuid),"
|
||||
"FOREIGN KEY (terminal2_uuid, terminal2_element_uuid) REFERENCES terminal (uuid, element_uuid)"
|
||||
")");
|
||||
if (!query_.exec(conductor_table)) {
|
||||
qDebug() << "conductor_table query : "<< query_.lastError();
|
||||
}
|
||||
|
||||
createElementNomenclatureView();
|
||||
createSummaryView();
|
||||
prepareQuery();
|
||||
@@ -534,6 +624,62 @@ void projectDataBase::populateDiagramInfoTable()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::populateConductorTable
|
||||
Populate the terminal and conductor tables. Terminals only matter here
|
||||
in the context of a conductor referencing them, so their population is
|
||||
folded into this method rather than tracked independently.
|
||||
*/
|
||||
void projectDataBase::populateConductorTable()
|
||||
{
|
||||
QSqlQuery query(m_data_base);
|
||||
query.exec(QStringLiteral("DELETE FROM conductor"));
|
||||
query.exec(QStringLiteral("DELETE FROM terminal"));
|
||||
|
||||
for (auto *diagram : m_project->diagrams())
|
||||
{
|
||||
const auto conductor_list = diagram->conductors();
|
||||
for (auto *conductor : conductor_list)
|
||||
{
|
||||
//See addConductor() for why terminals without a uuid
|
||||
//(legacy elements) are omitted rather than fabricating one.
|
||||
if (conductor->terminal1->uuid().isNull() || conductor->terminal2->uuid().isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
insertTerminal(conductor->terminal1);
|
||||
insertTerminal(conductor->terminal2);
|
||||
|
||||
m_insert_conductor_query.bindValue(":uuid", conductor->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":diagram_uuid", diagram->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":terminal1_uuid", conductor->terminal1->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":terminal1_element_uuid", conductor->terminal1->parentElement()->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":terminal2_uuid", conductor->terminal2->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":terminal2_element_uuid", conductor->terminal2->parentElement()->uuid().toString());
|
||||
m_insert_conductor_query.bindValue(":text", conductor->properties().text);
|
||||
if (!m_insert_conductor_query.exec()) {
|
||||
qDebug() << "projectDataBase::populateConductorTable insert error : " << m_insert_conductor_query.lastError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::insertTerminal
|
||||
Insert (or, if already present -- e.g. a junction shared by several
|
||||
conductors -- silently keep) @terminal in the terminal table.
|
||||
@param terminal
|
||||
*/
|
||||
void projectDataBase::insertTerminal(Terminal *terminal)
|
||||
{
|
||||
m_insert_terminal_query.bindValue(":uuid", terminal->uuid().toString());
|
||||
m_insert_terminal_query.bindValue(":element_uuid", terminal->parentElement()->uuid().toString());
|
||||
m_insert_terminal_query.bindValue(":name", terminal->name());
|
||||
if (!m_insert_terminal_query.exec()) {
|
||||
qDebug() << "projectDataBase::insertTerminal insert error : " << m_insert_terminal_query.lastError();
|
||||
}
|
||||
}
|
||||
|
||||
void projectDataBase::prepareQuery()
|
||||
{
|
||||
//INSERT DIAGRAM
|
||||
@@ -606,6 +752,19 @@ void projectDataBase::prepareQuery()
|
||||
update_str.append(" WHERE element_uuid = :uuid");
|
||||
m_update_element_query = QSqlQuery(m_data_base);
|
||||
m_update_element_query.prepare(update_str);
|
||||
|
||||
//INSERT TERMINAL
|
||||
m_insert_terminal_query = QSqlQuery(m_data_base);
|
||||
m_insert_terminal_query.prepare("INSERT OR IGNORE INTO terminal (uuid, element_uuid, name) VALUES (:uuid, :element_uuid, :name)");
|
||||
|
||||
//INSERT CONDUCTOR
|
||||
m_insert_conductor_query = QSqlQuery(m_data_base);
|
||||
m_insert_conductor_query.prepare("INSERT INTO conductor (uuid, diagram_uuid, terminal1_uuid, terminal1_element_uuid, terminal2_uuid, terminal2_element_uuid, text) "
|
||||
"VALUES (:uuid, :diagram_uuid, :terminal1_uuid, :terminal1_element_uuid, :terminal2_uuid, :terminal2_element_uuid, :text)");
|
||||
|
||||
//REMOVE CONDUCTOR
|
||||
m_remove_conductor_query = QSqlQuery(m_data_base);
|
||||
m_remove_conductor_query.prepare("DELETE FROM conductor WHERE uuid=:uuid");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
class Element;
|
||||
class QETProject;
|
||||
class Diagram;
|
||||
class Conductor;
|
||||
class Terminal;
|
||||
class sqlite3;
|
||||
|
||||
/**
|
||||
@@ -58,6 +60,9 @@ class projectDataBase : public QObject
|
||||
void diagramInfoChanged (Diagram *diagram);
|
||||
void diagramOrderChanged();
|
||||
|
||||
void addConductor (Conductor *conductor);
|
||||
void removeConductor (Conductor *conductor);
|
||||
|
||||
signals:
|
||||
void dataBaseUpdated();
|
||||
|
||||
@@ -69,6 +74,8 @@ class projectDataBase : public QObject
|
||||
void populateElementTable();
|
||||
void populateElementInfoTable();
|
||||
void populateDiagramInfoTable();
|
||||
void populateConductorTable();
|
||||
void insertTerminal(Terminal *terminal);
|
||||
void prepareQuery();
|
||||
static QHash<QString, QString> elementInfoToString(
|
||||
Element *elmt);
|
||||
@@ -86,7 +93,10 @@ class projectDataBase : public QObject
|
||||
m_insert_diagram_info_query,
|
||||
m_update_diagram_info_query,
|
||||
m_diagram_order_changed,
|
||||
m_diagram_info_order_changed;
|
||||
m_diagram_info_order_changed,
|
||||
m_insert_terminal_query,
|
||||
m_insert_conductor_query,
|
||||
m_remove_conductor_query;
|
||||
|
||||
#ifdef QET_EXPORT_PROJECT_DB
|
||||
public:
|
||||
|
||||
@@ -1674,6 +1674,7 @@ void Diagram::addItem(QGraphicsItem *item)
|
||||
conductor->terminal1->addConductor(conductor);
|
||||
conductor->terminal2->addConductor(conductor);
|
||||
conductor->calculateTextItemPosition();
|
||||
m_project->dataBase()->addConductor(conductor);
|
||||
break;
|
||||
}
|
||||
default: {break;}
|
||||
@@ -1704,6 +1705,7 @@ void Diagram::removeItem(QGraphicsItem *item)
|
||||
Conductor *conductor = static_cast<Conductor *>(item);
|
||||
conductor->terminal1->removeConductor(conductor);
|
||||
conductor->terminal2->removeConductor(conductor);
|
||||
m_project->dataBase()->removeConductor(conductor);
|
||||
break;
|
||||
}
|
||||
default: {break;}
|
||||
|
||||
Reference in New Issue
Block a user