mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-05-20 11:59:59 +02:00
Merge pull request #462 from Kellermorph/makro-fix
follow up: wiring list
This commit is contained in:
@@ -5,9 +5,7 @@
|
|||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QDomDocument>
|
#include <QDomDocument>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QRegularExpression>
|
#include <algorithm>
|
||||||
#include <QQueue>
|
|
||||||
#include <QSet>
|
|
||||||
|
|
||||||
WiringListExport::WiringListExport(QETProject *project, QWidget *parent) :
|
WiringListExport::WiringListExport(QETProject *project, QWidget *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
@@ -45,8 +43,27 @@ QDomElement WiringListExport::climbToDiagram(QDomNode node) const
|
|||||||
QMap<QString, ElementInfo> WiringListExport::collectElementsInfo(const QDomElement &root) const
|
QMap<QString, ElementInfo> WiringListExport::collectElementsInfo(const QDomElement &root) const
|
||||||
{
|
{
|
||||||
QMap<QString, ElementInfo> infoMap;
|
QMap<QString, ElementInfo> infoMap;
|
||||||
QDomNodeList elements = root.elementsByTagName("element");
|
|
||||||
|
|
||||||
|
QSet<QString> placeholderTypes;
|
||||||
|
QDomElement collection = root.firstChildElement("collection");
|
||||||
|
if (!collection.isNull()) {
|
||||||
|
QDomNodeList defs = collection.elementsByTagName("definition");
|
||||||
|
for (int i = 0; i < defs.size(); ++i) {
|
||||||
|
QDomElement def = defs.at(i).toElement();
|
||||||
|
QString ltype = def.attribute("link_type");
|
||||||
|
if (ltype == "next_report" || ltype == "previous_report") {
|
||||||
|
QDomElement parentEl = def.parentNode().toElement();
|
||||||
|
if (parentEl.tagName().toLower() == "element") {
|
||||||
|
QString name = parentEl.attribute("name");
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
placeholderTypes.insert(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomNodeList elements = root.elementsByTagName("element");
|
||||||
for (int i = 0; i < elements.size(); ++i) {
|
for (int i = 0; i < elements.size(); ++i) {
|
||||||
QDomElement el = elements.at(i).toElement();
|
QDomElement el = elements.at(i).toElement();
|
||||||
QString uuid = normalizeUuid(el.attribute("uuid", el.attribute("id", "")));
|
QString uuid = normalizeUuid(el.attribute("uuid", el.attribute("id", "")));
|
||||||
@@ -75,10 +92,13 @@ QMap<QString, ElementInfo> WiringListExport::collectElementsInfo(const QDomEleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString typeVal = el.attribute("type").toLower();
|
QString typeVal = el.attribute("type");
|
||||||
if (typeVal.contains("naechste") || typeVal.contains("vorherige") ||
|
info.isPlaceholder = false;
|
||||||
typeVal.contains("next") || typeVal.contains("previous")) {
|
for (const QString &ptype : placeholderTypes) {
|
||||||
|
if (typeVal.endsWith(ptype)) {
|
||||||
info.isPlaceholder = true;
|
info.isPlaceholder = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
infoMap.insert(uuid, info);
|
infoMap.insert(uuid, info);
|
||||||
@@ -102,7 +122,15 @@ QList<ConductorData> WiringListExport::collectConductors(const QDomElement &root
|
|||||||
data.el2_uuid = normalizeUuid(cond.attribute("element2", cond.attribute("element2id", "")));
|
data.el2_uuid = normalizeUuid(cond.attribute("element2", cond.attribute("element2id", "")));
|
||||||
|
|
||||||
data.element1_label = cond.attribute("element1_label");
|
data.element1_label = cond.attribute("element1_label");
|
||||||
|
if (data.element1_label.isEmpty()) {
|
||||||
|
data.element1_label = cond.attribute("element1_linked");
|
||||||
|
}
|
||||||
|
|
||||||
data.element2_label = cond.attribute("element2_label");
|
data.element2_label = cond.attribute("element2_label");
|
||||||
|
if (data.element2_label.isEmpty()) {
|
||||||
|
data.element2_label = cond.attribute("element2_linked");
|
||||||
|
}
|
||||||
|
|
||||||
data.terminalname1 = cond.attribute("terminalname1");
|
data.terminalname1 = cond.attribute("terminalname1");
|
||||||
data.terminalname2 = cond.attribute("terminalname2");
|
data.terminalname2 = cond.attribute("terminalname2");
|
||||||
data.tension_protocol = cond.attribute("tension_protocol");
|
data.tension_protocol = cond.attribute("tension_protocol");
|
||||||
@@ -119,101 +147,6 @@ QList<ConductorData> WiringListExport::collectConductors(const QDomElement &root
|
|||||||
return conductors;
|
return conductors;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiringListExport::resolveEndpoints(QList<ConductorData> &conductors, const QMap<QString, ElementInfo> &elementsInfo) const
|
|
||||||
{
|
|
||||||
QRegularExpression numericLabelRe("^\\d+(\\.\\d+)?$");
|
|
||||||
|
|
||||||
QMap<QString, QList<ConductorData>> el_to_cons;
|
|
||||||
for (const ConductorData &c : conductors) {
|
|
||||||
if (!c.el1_uuid.isEmpty()) el_to_cons[c.el1_uuid].append(c);
|
|
||||||
if (!c.el2_uuid.isEmpty()) el_to_cons[c.el2_uuid].append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < conductors.size(); ++i) {
|
|
||||||
ConductorData &c = conductors[i];
|
|
||||||
|
|
||||||
auto resolveSide = [&](const QString &startUuid, QString &outLabel, QString &outTerminal) {
|
|
||||||
if (startUuid.isEmpty() || !elementsInfo.contains(startUuid)) return;
|
|
||||||
|
|
||||||
const ElementInfo &startInfo = elementsInfo[startUuid];
|
|
||||||
if (!startInfo.links.isEmpty() || startInfo.isPlaceholder) {
|
|
||||||
QQueue<QString> q;
|
|
||||||
QSet<QString> visited;
|
|
||||||
q.enqueue(startUuid);
|
|
||||||
visited.insert(startUuid);
|
|
||||||
|
|
||||||
int depth = 0;
|
|
||||||
while (!q.isEmpty() && depth < 3) {
|
|
||||||
int levelSize = q.size();
|
|
||||||
for (int k = 0; k < levelSize; ++k) {
|
|
||||||
QString curr = q.dequeue();
|
|
||||||
|
|
||||||
if (elementsInfo.contains(curr)) {
|
|
||||||
const ElementInfo &currInfo = elementsInfo[curr];
|
|
||||||
|
|
||||||
if (!currInfo.isPlaceholder && !currInfo.label.isEmpty() && !numericLabelRe.match(currInfo.label).hasMatch()) {
|
|
||||||
outLabel = currInfo.label;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QString &lnk : currInfo.links) {
|
|
||||||
if (!visited.contains(lnk)) {
|
|
||||||
visited.insert(lnk);
|
|
||||||
q.enqueue(lnk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const ConductorData &cond : el_to_cons.value(curr)) {
|
|
||||||
if (cond.index == c.index) continue;
|
|
||||||
|
|
||||||
QString other;
|
|
||||||
QString terminalHint;
|
|
||||||
if (cond.el1_uuid == curr) {
|
|
||||||
other = cond.el2_uuid;
|
|
||||||
terminalHint = cond.terminalname2;
|
|
||||||
} else {
|
|
||||||
other = cond.el1_uuid;
|
|
||||||
terminalHint = cond.terminalname1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!other.isEmpty() && !visited.contains(other)) {
|
|
||||||
if (elementsInfo.contains(other)) {
|
|
||||||
const ElementInfo &oInfo = elementsInfo[other];
|
|
||||||
if (!oInfo.isPlaceholder && !oInfo.label.isEmpty() && !numericLabelRe.match(oInfo.label).hasMatch()) {
|
|
||||||
outLabel = oInfo.label;
|
|
||||||
if (outTerminal.isEmpty()) outTerminal = terminalHint;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
visited.insert(other);
|
|
||||||
q.enqueue(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
depth++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (outLabel.isEmpty()) {
|
|
||||||
outLabel = startInfo.label.isEmpty() ? startInfo.name : startInfo.label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool p1 = elementsInfo.value(c.el1_uuid).isPlaceholder;
|
|
||||||
bool p2 = elementsInfo.value(c.el2_uuid).isPlaceholder;
|
|
||||||
|
|
||||||
if (c.element1_label.isEmpty() || p1) {
|
|
||||||
if (p1) c.element1_label = "";
|
|
||||||
resolveSide(c.el1_uuid, c.element1_label, c.terminalname1);
|
|
||||||
}
|
|
||||||
if (c.element2_label.isEmpty() || p2) {
|
|
||||||
if (p2) c.element2_label = "";
|
|
||||||
resolveSide(c.el2_uuid, c.element2_label, c.terminalname2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiringListExport::toCsv()
|
void WiringListExport::toCsv()
|
||||||
{
|
{
|
||||||
if (!m_project) return;
|
if (!m_project) return;
|
||||||
@@ -243,25 +176,140 @@ void WiringListExport::toCsv()
|
|||||||
QMap<QString, ElementInfo> elementsInfo = collectElementsInfo(doc.documentElement());
|
QMap<QString, ElementInfo> elementsInfo = collectElementsInfo(doc.documentElement());
|
||||||
QList<ConductorData> conductors = collectConductors(doc.documentElement());
|
QList<ConductorData> conductors = collectConductors(doc.documentElement());
|
||||||
|
|
||||||
resolveEndpoints(conductors, elementsInfo);
|
|
||||||
|
|
||||||
QList<ConductorData> uniqueConductors;
|
QList<ConductorData> uniqueConductors;
|
||||||
QSet<QString> seenConnections;
|
QMap<QString, ConductorData> partialWires;
|
||||||
|
|
||||||
for (const ConductorData &c : conductors) {
|
auto normalizePartial = [](ConductorData c, const QString &ph_uuid) {
|
||||||
if (c.element1_label.isEmpty() && c.element2_label.isEmpty()) continue;
|
if (c.el1_uuid == ph_uuid) {
|
||||||
|
std::swap(c.el1_uuid, c.el2_uuid);
|
||||||
|
std::swap(c.element1_label, c.element2_label);
|
||||||
|
std::swap(c.terminalname1, c.terminalname2);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
|
||||||
QString sideA = c.element1_label + ":" + c.terminalname1;
|
auto mergeField = [](const QString &a, const QString &b) {
|
||||||
QString sideB = c.element2_label + ":" + c.terminalname2;
|
QString at = a.trimmed();
|
||||||
|
QString bt = b.trimmed();
|
||||||
|
if (at.isEmpty()) return bt;
|
||||||
|
if (bt.isEmpty()) return at;
|
||||||
|
if (at == bt) return at;
|
||||||
|
return at + ", " + bt;
|
||||||
|
};
|
||||||
|
|
||||||
QString key = (sideA < sideB) ? (sideA + "||" + sideB) : (sideB + "||" + sideA);
|
for (int i = 0; i < conductors.size(); ++i) {
|
||||||
|
ConductorData c = conductors[i];
|
||||||
|
|
||||||
if (!seenConnections.contains(key)) {
|
if (c.element1_label.isEmpty() && elementsInfo.contains(c.el1_uuid)) {
|
||||||
seenConnections.insert(key);
|
c.element1_label = elementsInfo[c.el1_uuid].label;
|
||||||
|
if (c.element1_label.isEmpty()) c.element1_label = elementsInfo[c.el1_uuid].name;
|
||||||
|
}
|
||||||
|
if (c.element2_label.isEmpty() && elementsInfo.contains(c.el2_uuid)) {
|
||||||
|
c.element2_label = elementsInfo[c.el2_uuid].label;
|
||||||
|
if (c.element2_label.isEmpty()) c.element2_label = elementsInfo[c.el2_uuid].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool el1_ph = elementsInfo.value(c.el1_uuid).isPlaceholder;
|
||||||
|
bool el2_ph = elementsInfo.value(c.el2_uuid).isPlaceholder;
|
||||||
|
|
||||||
|
if (!el1_ph && !el2_ph) {
|
||||||
uniqueConductors.append(c);
|
uniqueConductors.append(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el1_ph && el2_ph) {
|
||||||
|
uniqueConductors.append(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ph_uuid = el1_ph ? c.el1_uuid : c.el2_uuid;
|
||||||
|
ConductorData normC = normalizePartial(c, ph_uuid);
|
||||||
|
|
||||||
|
QString matching_ph_uuid;
|
||||||
|
if (!elementsInfo[ph_uuid].links.isEmpty()) {
|
||||||
|
matching_ph_uuid = elementsInfo[ph_uuid].links.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matching_ph_uuid.isEmpty() && partialWires.contains(matching_ph_uuid)) {
|
||||||
|
ConductorData otherHalf = partialWires.take(matching_ph_uuid);
|
||||||
|
|
||||||
|
ConductorData merged;
|
||||||
|
merged.folio = mergeField(otherHalf.folio, normC.folio);
|
||||||
|
|
||||||
|
merged.el1_uuid = otherHalf.el1_uuid;
|
||||||
|
merged.element1_label = otherHalf.element1_label;
|
||||||
|
merged.terminalname1 = otherHalf.terminalname1;
|
||||||
|
|
||||||
|
merged.el2_uuid = normC.el1_uuid;
|
||||||
|
merged.element2_label = normC.element1_label;
|
||||||
|
merged.terminalname2 = normC.terminalname1;
|
||||||
|
|
||||||
|
merged.tension_protocol = mergeField(otherHalf.tension_protocol, normC.tension_protocol);
|
||||||
|
merged.conductor_color = mergeField(otherHalf.conductor_color, normC.conductor_color);
|
||||||
|
merged.conductor_section = mergeField(otherHalf.conductor_section, normC.conductor_section);
|
||||||
|
merged.function = mergeField(otherHalf.function, normC.function);
|
||||||
|
|
||||||
|
uniqueConductors.append(merged);
|
||||||
|
} else {
|
||||||
|
partialWires.insert(ph_uuid, normC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const ConductorData &leftover : partialWires.values()) {
|
||||||
|
uniqueConductors.append(leftover);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ConductorData &c : uniqueConductors) {
|
||||||
|
if (!c.element2_label.isEmpty() && (c.element1_label.isEmpty() || c.element2_label.toLower() < c.element1_label.toLower())) {
|
||||||
|
std::swap(c.element1_label, c.element2_label);
|
||||||
|
std::swap(c.terminalname1, c.terminalname2);
|
||||||
|
std::swap(c.el1_uuid, c.el2_uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(uniqueConductors.begin(), uniqueConductors.end(), [](const ConductorData &a, const ConductorData &b) {
|
||||||
|
QStringList partsA = a.folio.split(',');
|
||||||
|
QStringList partsB = b.folio.split(',');
|
||||||
|
int minLen = std::min(partsA.size(), partsB.size());
|
||||||
|
int folioCmp = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < minLen; ++i) {
|
||||||
|
bool okA, okB;
|
||||||
|
int numA = partsA[i].trimmed().toInt(&okA);
|
||||||
|
int numB = partsB[i].trimmed().toInt(&okB);
|
||||||
|
|
||||||
|
if (okA && okB) {
|
||||||
|
if (numA != numB) {
|
||||||
|
folioCmp = (numA < numB) ? -1 : 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int strCmp = partsA[i].trimmed().compare(partsB[i].trimmed(), Qt::CaseInsensitive);
|
||||||
|
if (strCmp != 0) {
|
||||||
|
folioCmp = strCmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folioCmp == 0 && partsA.size() != partsB.size()) {
|
||||||
|
folioCmp = (partsA.size() < partsB.size()) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folioCmp != 0) return folioCmp < 0;
|
||||||
|
|
||||||
|
int el1Cmp = a.element1_label.toLower().compare(b.element1_label.toLower());
|
||||||
|
if (el1Cmp != 0) return el1Cmp < 0;
|
||||||
|
|
||||||
|
int el2Cmp = a.element2_label.toLower().compare(b.element2_label.toLower());
|
||||||
|
if (el2Cmp != 0) return el2Cmp < 0;
|
||||||
|
|
||||||
|
int term1Cmp = a.terminalname1.compare(b.terminalname1);
|
||||||
|
if (term1Cmp != 0) return term1Cmp < 0;
|
||||||
|
|
||||||
|
return a.terminalname2 < b.terminalname2;
|
||||||
|
});
|
||||||
|
|
||||||
QTextStream out(&file);
|
QTextStream out(&file);
|
||||||
out << tr("Page", "Wiring list CSV header") << ";"
|
out << tr("Page", "Wiring list CSV header") << ";"
|
||||||
<< tr("Composant 1", "Wiring list CSV header") << ";"
|
<< tr("Composant 1", "Wiring list CSV header") << ";"
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ class QWidget;
|
|||||||
class QDomElement;
|
class QDomElement;
|
||||||
class QDomNode;
|
class QDomNode;
|
||||||
|
|
||||||
// Internal data structures for parsing the XML graph
|
|
||||||
struct ElementInfo {
|
struct ElementInfo {
|
||||||
QString folio;
|
QString folio;
|
||||||
QStringList links;
|
QStringList links;
|
||||||
@@ -34,18 +33,11 @@ struct ConductorData {
|
|||||||
QString conductor_section;
|
QString conductor_section;
|
||||||
QString function;
|
QString function;
|
||||||
QString folio;
|
QString folio;
|
||||||
|
|
||||||
// Resolved endpoints
|
|
||||||
QString chosen_a_uuid;
|
|
||||||
QString chosen_a_label;
|
|
||||||
QString chosen_b_uuid;
|
|
||||||
QString chosen_b_label;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The WiringListExport class
|
* @brief The WiringListExport class
|
||||||
* Handles the export of the wiring list (Verdrahtungsplan) to a CSV file.
|
* Exports the wiring diagram from QElectroTech as a CSV file.
|
||||||
* Automatically resolves links and placeholders to find physical endpoints.
|
|
||||||
*/
|
*/
|
||||||
class WiringListExport : public QObject
|
class WiringListExport : public QObject
|
||||||
{
|
{
|
||||||
@@ -64,8 +56,6 @@ private:
|
|||||||
|
|
||||||
QMap<QString, ElementInfo> collectElementsInfo(const QDomElement &root) const;
|
QMap<QString, ElementInfo> collectElementsInfo(const QDomElement &root) const;
|
||||||
QList<ConductorData> collectConductors(const QDomElement &root) const;
|
QList<ConductorData> collectConductors(const QDomElement &root) const;
|
||||||
|
|
||||||
void resolveEndpoints(QList<ConductorData> &conductors, const QMap<QString, ElementInfo> &elementsInfo) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WIRINGLISTEXPORT_H
|
#endif // WIRINGLISTEXPORT_H
|
||||||
|
|||||||
Reference in New Issue
Block a user