mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-07-30 07:44:13 +02:00
7cc5790f7c
Port of the edz2qet.py prototype's mapping to C++. EdzPart parses an EPLAN part.xml into a portable model (identity/metadata, localized names, connection list); EdzElementBuilder turns that into a QET element (generic symbol: body rectangle + one west-facing terminal per pin, per-pin labels, localized <name>s, and elementInformations for the BOM). Pins are natural-sorted by designation so they stack 1,2,3,4 regardless of the order EPLAN lists them (MFH200 lists 1,3,4,2). Output verified structurally identical (uuids aside) to the Python oracle for three ifm samples — KG6000 (4-pin), MFH200 (4-pin, reordered) and R1D200 (5-pin, incl. Dutch name) — and the generated element loads in the QET editor with correct UTF-8. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
236 lines
8.7 KiB
C++
236 lines
8.7 KiB
C++
/*
|
|
Copyright 2006 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "edzelementbuilder.h"
|
|
|
|
#include "edzpart.h"
|
|
|
|
#include <QUuid>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace {
|
|
|
|
const QString STYLE =
|
|
QStringLiteral("line-style:normal;line-weight:normal;filling:none;color:black");
|
|
const QString FONT =
|
|
QStringLiteral("Liberation Sans,5,-1,5,50,0,0,0,0,0,Regular");
|
|
const QString TITLE_FONT =
|
|
QStringLiteral("Liberation Sans,5,-1,5,75,0,0,0,0,0,Bold");
|
|
|
|
QString uuidStr()
|
|
{
|
|
return QUuid::createUuid().toString(); // "{....}"
|
|
}
|
|
|
|
QDomElement makeText(QDomDocument &doc, int x, int y, const QString &text,
|
|
const QString &font)
|
|
{
|
|
QDomElement e = doc.createElement(QStringLiteral("text"));
|
|
e.setAttribute(QStringLiteral("x"), x);
|
|
e.setAttribute(QStringLiteral("y"), y);
|
|
e.setAttribute(QStringLiteral("text"), text);
|
|
e.setAttribute(QStringLiteral("rotation"), QStringLiteral("0"));
|
|
e.setAttribute(QStringLiteral("font"), font);
|
|
e.setAttribute(QStringLiteral("color"), QStringLiteral("#000000"));
|
|
return e;
|
|
}
|
|
|
|
int ceilTo10(int v)
|
|
{
|
|
if (v <= 0) {
|
|
return 0;
|
|
}
|
|
return ((v + 9) / 10) * 10;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
QDomDocument EdzElementBuilder::build(const EdzPart &part)
|
|
{
|
|
QList<EdzPin> pins = part.pins();
|
|
if (pins.isEmpty()) {
|
|
pins.append(EdzPin{QStringLiteral("1"), QString()});
|
|
}
|
|
const int n = pins.size();
|
|
const int pitch = 10;
|
|
const int body_left = 0, body_right = 90, body_top = -20;
|
|
const int body_bottom = (n - 1) * pitch + 10;
|
|
|
|
// Coordinate accumulators for the bounding box (origin always included).
|
|
QList<int> xs{0}, ys{0};
|
|
xs << body_left << body_right;
|
|
ys << body_top << body_bottom;
|
|
xs << (body_left + 6); // title x
|
|
ys << (body_top + 7); // title y
|
|
for (int i = 0; i < n; ++i) {
|
|
xs << 6 << 0; // pin label x, terminal x
|
|
ys << (i * pitch + 2) << (i * pitch); // pin label y, terminal y
|
|
}
|
|
|
|
const int pad = 10;
|
|
const int min_x = *std::min_element(xs.begin(), xs.end()) - pad;
|
|
const int max_x = *std::max_element(xs.begin(), xs.end()) + pad;
|
|
const int min_y = *std::min_element(ys.begin(), ys.end()) - pad;
|
|
const int max_y = *std::max_element(ys.begin(), ys.end()) + pad;
|
|
const int width = std::max(10, ceilTo10(max_x - min_x));
|
|
const int height = std::max(10, ceilTo10(max_y - min_y));
|
|
const int hotspot_x = -min_x;
|
|
const int hotspot_y = -min_y;
|
|
|
|
QDomDocument doc;
|
|
doc.appendChild(doc.createProcessingInstruction(
|
|
QStringLiteral("xml"),
|
|
QStringLiteral("version=\"1.0\" encoding=\"utf-8\"")));
|
|
|
|
QDomElement defn = doc.createElement(QStringLiteral("definition"));
|
|
defn.setAttribute(QStringLiteral("version"), QStringLiteral("0.100.0"));
|
|
defn.setAttribute(QStringLiteral("type"), QStringLiteral("element"));
|
|
defn.setAttribute(QStringLiteral("link_type"), QStringLiteral("simple"));
|
|
defn.setAttribute(QStringLiteral("width"), width);
|
|
defn.setAttribute(QStringLiteral("height"), height);
|
|
defn.setAttribute(QStringLiteral("hotspot_x"), hotspot_x);
|
|
defn.setAttribute(QStringLiteral("hotspot_y"), hotspot_y);
|
|
doc.appendChild(defn);
|
|
|
|
QDomElement uuid_el = doc.createElement(QStringLiteral("uuid"));
|
|
uuid_el.setAttribute(QStringLiteral("uuid"), uuidStr());
|
|
defn.appendChild(uuid_el);
|
|
|
|
// Localized names: fr/en first, then the rest alphabetically.
|
|
QDomElement names = doc.createElement(QStringLiteral("names"));
|
|
QMap<QString, QString> name_map = part.names();
|
|
const QString fallback = !part.orderNumber().isEmpty() ? part.orderNumber()
|
|
: (!part.partNumber().isEmpty() ? part.partNumber()
|
|
: QStringLiteral("part"));
|
|
if (name_map.isEmpty()) {
|
|
name_map.insert(QStringLiteral("en"),
|
|
!part.description().isEmpty() ? part.description()
|
|
: fallback);
|
|
}
|
|
QStringList ordered;
|
|
for (const QString &c : {QStringLiteral("fr"), QStringLiteral("en")}) {
|
|
if (name_map.contains(c)) ordered << c;
|
|
}
|
|
QStringList rest;
|
|
for (const QString &c : name_map.keys()) {
|
|
if (c != QLatin1String("fr") && c != QLatin1String("en")) rest << c;
|
|
}
|
|
std::sort(rest.begin(), rest.end());
|
|
ordered << rest;
|
|
for (const QString &lang : ordered) {
|
|
QDomElement nm = doc.createElement(QStringLiteral("name"));
|
|
nm.setAttribute(QStringLiteral("lang"), lang);
|
|
nm.appendChild(doc.createTextNode(name_map.value(lang)));
|
|
names.appendChild(nm);
|
|
}
|
|
defn.appendChild(names);
|
|
|
|
QDomElement infos = doc.createElement(QStringLiteral("informations"));
|
|
infos.appendChild(doc.createTextNode(
|
|
QStringLiteral("Imported from EPLAN .edz by edz2qet (%1)")
|
|
.arg(part.partNumber())));
|
|
defn.appendChild(infos);
|
|
|
|
// Element information -> QET BOM / nomenclature.
|
|
QDomElement einfos = doc.createElement(QStringLiteral("elementInformations"));
|
|
const QList<QPair<QString, QString>> info_map{
|
|
{QStringLiteral("manufacturer"), part.manufacturer()},
|
|
{QStringLiteral("manufacturer_reference"), part.orderNumber()},
|
|
{QStringLiteral("designation"), part.description()},
|
|
{QStringLiteral("comment"), part.comment()},
|
|
};
|
|
for (const auto &kv : info_map) {
|
|
if (kv.second.isEmpty()) {
|
|
continue;
|
|
}
|
|
QDomElement e = doc.createElement(QStringLiteral("elementInformation"));
|
|
e.setAttribute(QStringLiteral("name"), kv.first);
|
|
e.setAttribute(QStringLiteral("show"), QStringLiteral("1"));
|
|
e.appendChild(doc.createTextNode(kv.second));
|
|
einfos.appendChild(e);
|
|
}
|
|
defn.appendChild(einfos);
|
|
|
|
QDomElement desc = doc.createElement(QStringLiteral("description"));
|
|
|
|
// Body rectangle.
|
|
QDomElement rect = doc.createElement(QStringLiteral("rect"));
|
|
rect.setAttribute(QStringLiteral("x"), body_left);
|
|
rect.setAttribute(QStringLiteral("y"), body_top);
|
|
rect.setAttribute(QStringLiteral("width"), body_right - body_left);
|
|
rect.setAttribute(QStringLiteral("height"), body_bottom - body_top);
|
|
rect.setAttribute(QStringLiteral("style"), STYLE);
|
|
rect.setAttribute(QStringLiteral("antialias"), QStringLiteral("false"));
|
|
desc.appendChild(rect);
|
|
|
|
// Title (order number) in its own band at the top.
|
|
const QString title = !part.orderNumber().isEmpty() ? part.orderNumber()
|
|
: (!part.partNumber().isEmpty() ? part.partNumber()
|
|
: QStringLiteral("PART"));
|
|
desc.appendChild(makeText(doc, body_left + 6, body_top + 7, title, TITLE_FONT));
|
|
|
|
// Per-pin labels.
|
|
for (int i = 0; i < n; ++i) {
|
|
QString label = pins.at(i).designation;
|
|
if (!pins.at(i).description.isEmpty()) {
|
|
label += QStringLiteral(" ") + pins.at(i).description;
|
|
}
|
|
desc.appendChild(makeText(doc, 6, i * pitch + 2, label, FONT));
|
|
}
|
|
|
|
// Device-tag label (per-instance, above the body).
|
|
QDomElement dyn = doc.createElement(QStringLiteral("dynamic_text"));
|
|
dyn.setAttribute(QStringLiteral("x"), min_x + pad);
|
|
dyn.setAttribute(QStringLiteral("y"), min_y - 2);
|
|
dyn.setAttribute(QStringLiteral("z"), QStringLiteral("5"));
|
|
dyn.setAttribute(QStringLiteral("text_width"), QStringLiteral("-1"));
|
|
dyn.setAttribute(QStringLiteral("Halignment"), QStringLiteral("AlignLeft"));
|
|
dyn.setAttribute(QStringLiteral("Valignment"), QStringLiteral("AlignTop"));
|
|
dyn.setAttribute(QStringLiteral("frame"), QStringLiteral("false"));
|
|
dyn.setAttribute(QStringLiteral("rotation"), QStringLiteral("0"));
|
|
dyn.setAttribute(QStringLiteral("keep_visual_rotation"), QStringLiteral("false"));
|
|
dyn.setAttribute(QStringLiteral("text_from"), QStringLiteral("ElementInfo"));
|
|
dyn.setAttribute(QStringLiteral("uuid"), uuidStr());
|
|
dyn.setAttribute(QStringLiteral("font"), FONT);
|
|
dyn.appendChild(doc.createElement(QStringLiteral("text")));
|
|
QDomElement info_name = doc.createElement(QStringLiteral("info_name"));
|
|
info_name.appendChild(doc.createTextNode(QStringLiteral("label")));
|
|
dyn.appendChild(info_name);
|
|
desc.appendChild(dyn);
|
|
|
|
// Terminals.
|
|
for (int i = 0; i < n; ++i) {
|
|
QDomElement t = doc.createElement(QStringLiteral("terminal"));
|
|
t.setAttribute(QStringLiteral("uuid"), uuidStr());
|
|
t.setAttribute(QStringLiteral("name"), pins.at(i).designation);
|
|
t.setAttribute(QStringLiteral("x"), 0);
|
|
t.setAttribute(QStringLiteral("y"), i * pitch);
|
|
t.setAttribute(QStringLiteral("orientation"), QStringLiteral("w"));
|
|
t.setAttribute(QStringLiteral("type"), QStringLiteral("Generic"));
|
|
desc.appendChild(t);
|
|
}
|
|
|
|
defn.appendChild(desc);
|
|
return doc;
|
|
}
|
|
|
|
QString EdzElementBuilder::toElmtString(const QDomDocument &doc)
|
|
{
|
|
return doc.toString(0);
|
|
}
|