mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-29 22:24:13 +02:00
Work out formula labels and wire texts from the file, with the folios' code
The first fill from the document stored the label and wire text saved in the file. QElectroTech saves what it last worked out, not what the formula gives now: K%total-%id saved as K1-1 on a 3-folio project is K3-1 on the folios, and the same for a wire's formula. So the document fill disagreed with the folio fill on any file whose folios had been added or moved. AssignVariables now works from a FormulaContext: the folio's number, index, total, plant, location, title-block and project variables, and the element's grid cell and prefix or the wire's four properties. The Diagram overload fills one from a built folio, the database fills one from the file, and both call the same evaluation. Also brought in line with Diagram::fromXml(): an element Element::valideXml() rejects is skipped with its wires; a folio where two elements number their terminals alike (Element::fromXml() refuses one, by geometry), a frozen formula wire text, and the older sequential-number attributes are left to the folios. Each folio's border now starts from the defaults a new Diagram has, not the previous folio's values. tst_databasefromdocument: formulasAreWorkedOut (every variable kind, stale saved values), unbuiltElementIsLeftOut, clashingTerminalIdsFallBack. Each fails with its fix removed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -220,12 +220,56 @@ namespace autonum
|
||||
Diagram *diagram,
|
||||
const Element *elmt,
|
||||
const Conductor *cndr)
|
||||
{
|
||||
if (!diagram) {
|
||||
return formula;
|
||||
}
|
||||
|
||||
FormulaContext context;
|
||||
const BorderTitleBlock &border = diagram->border_and_titleblock;
|
||||
context.folio = border.folio();
|
||||
context.folio_index = diagram->folioIndex();
|
||||
context.folio_total = border.folioTotal();
|
||||
context.plant = border.plant();
|
||||
context.locmach = border.locmach();
|
||||
context.title_block_fields = border.additionalFields();
|
||||
context.project_properties = diagram->project()->projectProperties();
|
||||
if (elmt)
|
||||
{
|
||||
context.has_element = true;
|
||||
context.element_position = diagram->convertPosition(elmt->scenePos());
|
||||
context.element_prefix = elmt->getPrefix();
|
||||
}
|
||||
if (cndr)
|
||||
{
|
||||
context.has_conductor = true;
|
||||
context.wire_function = cndr->properties().m_function;
|
||||
context.wire_tension_protocol = cndr->properties().m_tension_protocol;
|
||||
context.wire_color = cndr->properties().m_wire_color;
|
||||
context.wire_section = cndr->properties().m_wire_section;
|
||||
}
|
||||
return formulaToLabel(std::move(formula), seqStruct, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AssignVariables::formulaToLabel
|
||||
Return the formula with its variables assigned from @p context,
|
||||
which describes a folio and the element or conductor the formula
|
||||
belongs to without needing either to be built.
|
||||
@param formula - the formula to work
|
||||
@param seqStruct - struct where is stocked int values
|
||||
(struct is passed as a reference
|
||||
and modified by this static method)
|
||||
@param context - what the variables are read from
|
||||
@return the string with variable assigned.
|
||||
*/
|
||||
QString AssignVariables::formulaToLabel(QString formula,
|
||||
sequentialNumbers &seqStruct,
|
||||
const FormulaContext &context)
|
||||
{
|
||||
AssignVariables av(std::move(formula),
|
||||
seqStruct,
|
||||
diagram,
|
||||
elmt,
|
||||
cndr);
|
||||
context);
|
||||
seqStruct = av.m_seq_struct;
|
||||
return av.m_assigned_label;
|
||||
}
|
||||
@@ -347,76 +391,54 @@ namespace autonum
|
||||
|
||||
AssignVariables::AssignVariables(const QString& formula,
|
||||
const sequentialNumbers& seqStruct,
|
||||
Diagram *diagram,
|
||||
const Element *elmt,
|
||||
const Conductor *cndr):
|
||||
m_diagram(diagram),
|
||||
const FormulaContext &context):
|
||||
m_context(context),
|
||||
m_arg_formula(formula),
|
||||
m_assigned_label(formula),
|
||||
m_seq_struct(seqStruct),
|
||||
m_element(elmt),
|
||||
m_conductor(cndr)
|
||||
m_seq_struct(seqStruct)
|
||||
{
|
||||
if (m_diagram)
|
||||
m_assigned_label.replace("%F", m_context.folio);
|
||||
m_assigned_label.replace("%f",
|
||||
QString::number(m_context.folio_index+1));
|
||||
m_assigned_label.replace("%id",
|
||||
QString::number(m_context.folio_index+1));
|
||||
m_assigned_label.replace("%total",
|
||||
QString::number(m_context.folio_total));
|
||||
m_assigned_label.replace("%M", m_context.plant);
|
||||
m_assigned_label.replace("%LM", m_context.locmach);
|
||||
|
||||
QSettings settings;
|
||||
if (m_context.has_element)
|
||||
{
|
||||
m_assigned_label.replace("%F",
|
||||
m_diagram
|
||||
-> border_and_titleblock
|
||||
.folio());
|
||||
m_assigned_label.replace("%f",
|
||||
QString::number(
|
||||
m_diagram
|
||||
->folioIndex()+1));
|
||||
m_assigned_label.replace("%id",
|
||||
QString::number(
|
||||
m_diagram
|
||||
->folioIndex()+1));
|
||||
m_assigned_label.replace("%total",
|
||||
QString::number(
|
||||
m_diagram
|
||||
->border_and_titleblock
|
||||
.folioTotal()));
|
||||
m_assigned_label.replace("%M",
|
||||
m_diagram
|
||||
-> border_and_titleblock
|
||||
.plant());
|
||||
m_assigned_label.replace("%LM",
|
||||
m_diagram
|
||||
-> border_and_titleblock
|
||||
.locmach());
|
||||
|
||||
QSettings settings;
|
||||
if (m_element)
|
||||
{
|
||||
if (settings.value("border-columns_0", true).toBool()){
|
||||
m_assigned_label.replace("%c", QString::number(m_diagram->convertPosition(m_element->scenePos()).number() - 1));
|
||||
}else{
|
||||
m_assigned_label.replace("%c", QString::number(m_diagram->convertPosition(m_element->scenePos()).number()));
|
||||
}
|
||||
m_assigned_label.replace("%l", m_diagram->convertPosition(m_element->scenePos()).letter());
|
||||
m_assigned_label.replace("%prefix", m_element->getPrefix());
|
||||
if (settings.value("border-columns_0", true).toBool()){
|
||||
m_assigned_label.replace("%c", QString::number(m_context.element_position.number() - 1));
|
||||
}else{
|
||||
m_assigned_label.replace("%c", QString::number(m_context.element_position.number()));
|
||||
}
|
||||
|
||||
if (m_conductor)
|
||||
{
|
||||
m_assigned_label.replace("%wf", cndr->properties().m_function);
|
||||
m_assigned_label.replace("%wv", cndr->properties().m_tension_protocol);
|
||||
m_assigned_label.replace("%wc", cndr->properties().m_wire_color);
|
||||
m_assigned_label.replace("%ws", cndr->properties().m_wire_section);
|
||||
}
|
||||
|
||||
assignTitleBlockVar();
|
||||
assignProjectVar();
|
||||
assignSequence();
|
||||
m_assigned_label.replace("%l", m_context.element_position.letter());
|
||||
m_assigned_label.replace("%prefix", m_context.element_prefix);
|
||||
}
|
||||
|
||||
if (m_context.has_conductor)
|
||||
{
|
||||
m_assigned_label.replace("%wf", m_context.wire_function);
|
||||
m_assigned_label.replace("%wv", m_context.wire_tension_protocol);
|
||||
m_assigned_label.replace("%wc", m_context.wire_color);
|
||||
m_assigned_label.replace("%ws", m_context.wire_section);
|
||||
}
|
||||
|
||||
assignTitleBlockVar();
|
||||
assignProjectVar();
|
||||
assignSequence();
|
||||
}
|
||||
|
||||
void AssignVariables::assignTitleBlockVar()
|
||||
{
|
||||
for (int i = 0; i < m_diagram->border_and_titleblock.additionalFields().count(); i++)
|
||||
DiagramContext fields = m_context.title_block_fields;
|
||||
for (int i = 0; i < fields.count(); i++)
|
||||
{
|
||||
QString folio_variable = m_diagram->border_and_titleblock.additionalFields().keys().at(i);
|
||||
QVariant folio_value = m_diagram->border_and_titleblock.additionalFields().operator [](folio_variable);
|
||||
QString folio_variable = fields.keys().at(i);
|
||||
QVariant folio_value = fields[folio_variable];
|
||||
|
||||
if (m_assigned_label.contains(folio_variable)) {
|
||||
m_assigned_label.replace("%{" + folio_variable + "}", folio_value.toString());
|
||||
@@ -427,10 +449,11 @@ namespace autonum
|
||||
|
||||
void AssignVariables::assignProjectVar()
|
||||
{
|
||||
for (int i = 0; i < m_diagram->project()->projectProperties().count(); i++)
|
||||
DiagramContext properties = m_context.project_properties;
|
||||
for (int i = 0; i < properties.count(); i++)
|
||||
{
|
||||
QString folio_variable = m_diagram->project()->projectProperties().keys().at(i);
|
||||
QVariant folio_value = m_diagram->project()->projectProperties().operator [](folio_variable);
|
||||
QString folio_variable = properties.keys().at(i);
|
||||
QVariant folio_value = properties[folio_variable];
|
||||
|
||||
if (m_assigned_label.contains(folio_variable)) {
|
||||
m_assigned_label.replace("%{" + folio_variable + "}", folio_value.toString());
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifndef ASSIGNVARIABLES_H
|
||||
#define ASSIGNVARIABLES_H
|
||||
#include "../diagramcontext.h"
|
||||
#include "../diagramposition.h"
|
||||
#include "numerotationcontext.h"
|
||||
|
||||
#include <QPointF>
|
||||
@@ -57,6 +58,36 @@ namespace autonum
|
||||
QStringList alpha;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief The FormulaContext struct
|
||||
Everything a formula's variables are read from: the folio it is on
|
||||
and, for an element's or a conductor's formula, that item.
|
||||
AssignVariables::formulaToLabel() fills one from a built folio; the
|
||||
project database fills one from the project's file.
|
||||
*/
|
||||
struct FormulaContext
|
||||
{
|
||||
/// border_and_titleblock.folio(), folioIndex(), folioTotal()...
|
||||
QString folio;
|
||||
int folio_index = 0;
|
||||
int folio_total = 0;
|
||||
QString plant;
|
||||
QString locmach;
|
||||
/// the title block's additional fields and the project's properties
|
||||
DiagramContext title_block_fields;
|
||||
DiagramContext project_properties;
|
||||
/// an element's grid cell (%c, %l) and prefix
|
||||
bool has_element = false;
|
||||
DiagramPosition element_position;
|
||||
QString element_prefix;
|
||||
/// a conductor's %wf, %wv, %wc and %ws
|
||||
bool has_conductor = false;
|
||||
QString wire_function;
|
||||
QString wire_tension_protocol;
|
||||
QString wire_color;
|
||||
QString wire_section;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief The AssignVariables class
|
||||
This class assign variable of a formula string.
|
||||
@@ -66,21 +97,20 @@ namespace autonum
|
||||
{
|
||||
public:
|
||||
static QString formulaToLabel (QString formula, sequentialNumbers &seqStruct, Diagram *diagram, const Element *elmt = nullptr, const Conductor *cndr = nullptr);
|
||||
static QString formulaToLabel (QString formula, sequentialNumbers &seqStruct, const FormulaContext &context);
|
||||
static QString replaceVariable (const QString &formula, const DiagramContext &dc);
|
||||
static QString genericXref (const Element *element);
|
||||
|
||||
private:
|
||||
AssignVariables(const QString& formula, const sequentialNumbers& seqStruct , Diagram *diagram, const Element *elmt = nullptr, const Conductor *cndr = nullptr);
|
||||
AssignVariables(const QString& formula, const sequentialNumbers& seqStruct, const FormulaContext &context);
|
||||
void assignTitleBlockVar();
|
||||
void assignProjectVar();
|
||||
void assignSequence();
|
||||
|
||||
Diagram *m_diagram = nullptr;
|
||||
const FormulaContext &m_context;
|
||||
QString m_arg_formula;
|
||||
QString m_assigned_label;
|
||||
sequentialNumbers m_seq_struct;
|
||||
const Element *m_element = nullptr;
|
||||
const Conductor *m_conductor = nullptr;
|
||||
};
|
||||
|
||||
void setSequentialToList(QStringList &list, NumerotationContext &nc, const QString& type);
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "sqlreadonly.h"
|
||||
|
||||
#include "../autoNum/assignvariables.h"
|
||||
#include "../borderproperties.h"
|
||||
#include "../bordertitleblock.h"
|
||||
#include "../diagram.h"
|
||||
#include "../diagramposition.h"
|
||||
@@ -34,6 +36,7 @@
|
||||
#include "../qetinformation.h"
|
||||
#include "../qetproject.h"
|
||||
#include "../qet.h"
|
||||
#include "../titleblockproperties.h"
|
||||
#include "../ElementsCollection/xmlelementcollection.h"
|
||||
#include "../properties/elementdata.h"
|
||||
|
||||
@@ -195,9 +198,24 @@ struct DocumentElement
|
||||
QString sub_type;
|
||||
QVariant group;
|
||||
DiagramContext informations;
|
||||
QString label;
|
||||
QHash<QUuid, DocumentTerminal> terminals;
|
||||
};
|
||||
|
||||
//The sequential values an element or a conductor was saved with, as
|
||||
//Element::fromXml() and Conductor::fromXml() read them -- false for
|
||||
//the attributes files written before <sequentialNumbers> carry.
|
||||
bool readSequence(const QDomElement &item, autonum::sequentialNumbers *sequence)
|
||||
{
|
||||
for (const char *name : {"sequ_1", "sequf_1", "seqt_1", "seqtf_1", "seqh_1"}) {
|
||||
if (item.hasAttribute(QLatin1String(name))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sequence->fromXml(item.firstChildElement(QStringLiteral("sequentialNumbers")));
|
||||
return true;
|
||||
}
|
||||
|
||||
struct DocumentConductor
|
||||
{
|
||||
QString uuid;
|
||||
@@ -221,12 +239,17 @@ struct DocumentConductor
|
||||
instead -- when a folio, element or conductor carries no saved uuid (the
|
||||
folios derive one on load), a conductor names its ends the older way,
|
||||
a folio number uses %autonum, a conductor ends on a terminal that shows
|
||||
its master's contact label, or an element's definition is missing or
|
||||
not one the folios could build. A file saved by a current QElectroTech
|
||||
carries everything else.
|
||||
its master's contact label, an element's definition is missing or
|
||||
not one the folios could build, two elements on a folio number their
|
||||
terminals alike, or sequential numbers are saved as the attributes
|
||||
older files carry. A file saved by a current QElectroTech carries
|
||||
everything else.
|
||||
|
||||
A symbol label computed from a formula is the label saved in the file,
|
||||
which QElectroTech writes as it computes it on every save.
|
||||
A label or a conductor text made from a formula is worked out again,
|
||||
as the folios do, with the same AssignVariables code: the one saved
|
||||
in the file is what the formula gave when it was saved, and a folio
|
||||
added or moved since changes it. A frozen conductor text is left to the
|
||||
folios, which work it out part-way through loading.
|
||||
@return true if the tables were filled
|
||||
*/
|
||||
bool projectDataBase::populateFromDocument(const QDomDocument &document, QString *why)
|
||||
@@ -248,6 +271,7 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
}
|
||||
|
||||
const DiagramContext project_wide = m_project->projectWideProperties();
|
||||
const DiagramContext project_properties = m_project->projectProperties();
|
||||
//One border and title block read from each folio's XML in turn:
|
||||
//only what it gives is kept.
|
||||
BorderTitleBlock reader;
|
||||
@@ -288,6 +312,9 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
}
|
||||
|
||||
BorderTitleBlock *border = &reader;
|
||||
//As a new Diagram's border starts, before initFromXml() reads it
|
||||
border->importBorder(BorderProperties());
|
||||
border->importTitleBlock(TitleBlockProperties());
|
||||
border->titleBlockFromXml(diagram_xml);
|
||||
border->borderFromXml(diagram_xml);
|
||||
if (border->folio().contains(QStringLiteral("%autonum"))) {
|
||||
@@ -295,16 +322,49 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
}
|
||||
border->setFolioData(i + 1, int(diagram_nodes.size()), QString(), project_wide);
|
||||
|
||||
//What a formula on this folio is worked out from, as
|
||||
//AssignVariables::formulaToLabel() reads it off a built folio.
|
||||
autonum::FormulaContext folio_context;
|
||||
folio_context.folio = border->folio();
|
||||
folio_context.folio_index = i;
|
||||
folio_context.folio_total = border->folioTotal();
|
||||
folio_context.plant = border->plant();
|
||||
folio_context.locmach = border->locmach();
|
||||
folio_context.title_block_fields = border->additionalFields();
|
||||
folio_context.project_properties = project_properties;
|
||||
|
||||
QHash<QUuid, int> on_this_folio; //element uuid -> index in elements
|
||||
for (const QDomElement &element_xml : QET::findInDomElement(
|
||||
QSet<int> terminal_ids; //the older terminal ids used so far
|
||||
for (QDomElement element_xml : QET::findInDomElement(
|
||||
diagram_xml, QStringLiteral("elements"), QStringLiteral("element")))
|
||||
{
|
||||
//Skipped by Diagram::fromXml() as well
|
||||
if (!Element::valideXml(element_xml)) {
|
||||
continue;
|
||||
}
|
||||
const QUuid uuid(element_xml.attribute(QStringLiteral("uuid")));
|
||||
if (uuid.isNull() || element_uuids.contains(uuid)) {
|
||||
return refuse(QStringLiteral("an element has no saved uuid, or shares one"));
|
||||
}
|
||||
element_uuids.insert(uuid);
|
||||
|
||||
//Element::fromXml() refuses an element whose terminals are
|
||||
//numbered like those of one read before it on the folio;
|
||||
//which of them it matches needs their geometry, so leave any
|
||||
//such folio to the folios. A current file numbers them
|
||||
//across the folio.
|
||||
QSet<int> ids;
|
||||
for (QDomElement t : QET::findInDomElement(
|
||||
element_xml, QStringLiteral("terminals"), QStringLiteral("terminal"))) {
|
||||
if (Terminal::valideXml(t)) {
|
||||
ids.insert(t.attribute(QStringLiteral("id")).toInt());
|
||||
}
|
||||
}
|
||||
if (terminal_ids.intersects(ids)) {
|
||||
return refuse(QStringLiteral("two elements on a folio number their terminals alike"));
|
||||
}
|
||||
terminal_ids.unite(ids);
|
||||
|
||||
//The definition the folio builds the element from, with the
|
||||
//checks Element::buildFromXml() refuses an element on --
|
||||
//read once per type, as many elements share one.
|
||||
@@ -351,10 +411,10 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
DocumentElement element;
|
||||
element.uuid = uuid.toString();
|
||||
element.diagram_uuid = diagram_uuid.toString();
|
||||
element.pos = border->convertPosition(
|
||||
QPointF(element_xml.attribute(QStringLiteral("x")).toDouble(),
|
||||
element_xml.attribute(QStringLiteral("y")).toDouble()))
|
||||
.toString();
|
||||
DiagramPosition position = border->convertPosition(
|
||||
QPointF(element_xml.attribute(QStringLiteral("x")).toDouble(),
|
||||
element_xml.attribute(QStringLiteral("y")).toDouble()));
|
||||
element.pos = position.toString();
|
||||
element.type = known->type;
|
||||
element.sub_type = known->sub_type;
|
||||
element.terminals = known->terminals;
|
||||
@@ -363,6 +423,21 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
element.informations.fromXml(
|
||||
element_xml.firstChildElement(QStringLiteral("elementInformations")),
|
||||
QStringLiteral("elementInformation"));
|
||||
//Element::actualLabel()
|
||||
const QString formula = element.informations.value(QStringLiteral("formula")).toString();
|
||||
if (formula.isEmpty()) {
|
||||
element.label = element.informations.value(QStringLiteral("label")).toString();
|
||||
} else {
|
||||
autonum::sequentialNumbers sequence;
|
||||
if (!readSequence(element_xml, &sequence)) {
|
||||
return refuse(QStringLiteral("an element's sequential numbers are saved the older way"));
|
||||
}
|
||||
autonum::FormulaContext context = folio_context;
|
||||
context.has_element = true;
|
||||
context.element_position = position;
|
||||
context.element_prefix = element_xml.attribute(QStringLiteral("prefix"));
|
||||
element.label = autonum::AssignVariables::formulaToLabel(formula, sequence, context);
|
||||
}
|
||||
|
||||
on_this_folio.insert(uuid, int(elements.size()));
|
||||
elements << element;
|
||||
@@ -429,8 +504,27 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
conductor.element2 = ends[1][0];
|
||||
conductor.terminal2 = ends[1][1];
|
||||
//ConductorProperties::fromXml()'s text, without reading the
|
||||
//rest of the properties
|
||||
conductor.text = conductor_xml.attribute(QStringLiteral("num"));
|
||||
//rest of the properties -- or, as Conductor::refreshText()
|
||||
//makes it, what its formula gives.
|
||||
const QString formula = conductor_xml.attribute(QStringLiteral("formula"));
|
||||
if (formula.isEmpty()) {
|
||||
conductor.text = conductor_xml.attribute(QStringLiteral("num"));
|
||||
} else {
|
||||
autonum::sequentialNumbers sequence;
|
||||
if (conductor_xml.attribute(QStringLiteral("freezeLabel")) == QLatin1String("true")) {
|
||||
return refuse(QStringLiteral("a conductor's text made from a formula is frozen"));
|
||||
}
|
||||
if (!readSequence(conductor_xml, &sequence)) {
|
||||
return refuse(QStringLiteral("a conductor's sequential numbers are saved the older way"));
|
||||
}
|
||||
autonum::FormulaContext context = folio_context;
|
||||
context.has_conductor = true;
|
||||
context.wire_function = conductor_xml.attribute(QStringLiteral("function"));
|
||||
context.wire_tension_protocol = conductor_xml.attribute(QStringLiteral("tension_protocol"));
|
||||
context.wire_color = conductor_xml.attribute(QStringLiteral("conductor_color"));
|
||||
context.wire_section = conductor_xml.attribute(QStringLiteral("conductor_section"));
|
||||
conductor.text = autonum::AssignVariables::formulaToLabel(formula, sequence, context);
|
||||
}
|
||||
conductors << conductor;
|
||||
}
|
||||
|
||||
@@ -473,7 +567,7 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
qDebug() << "projectDataBase::populateFromDocument element insert error : " << m_insert_elements_query.lastError();
|
||||
}
|
||||
bindElementInfoValues(m_insert_element_info_query, element.uuid, element.informations,
|
||||
element.informations[QStringLiteral("label")].toString());
|
||||
element.label);
|
||||
if (!m_insert_element_info_query.exec()) {
|
||||
qDebug() << "projectDataBase::populateFromDocument element_info insert error : " << m_insert_element_info_query.lastError();
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ if(QET_HAS_SCRIPTING)
|
||||
tst_databasefromdocument.cpp)
|
||||
add_test(NAME tst_databasefromdocument COMMAND tst_databasefromdocument)
|
||||
add_dependencies(tst_databasefromdocument qelectrotech)
|
||||
target_link_libraries(tst_databasefromdocument PRIVATE Qt::Test)
|
||||
target_link_libraries(tst_databasefromdocument PRIVATE Qt::Test Qt::Xml)
|
||||
target_compile_definitions(tst_databasefromdocument PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <QtTest>
|
||||
|
||||
#include <QDir>
|
||||
#include <QDomDocument>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@@ -74,6 +75,44 @@ class tst_databasefromdocument : public QObject
|
||||
return tables;
|
||||
}
|
||||
|
||||
// tremie_vibrante.qet saved once, as a document to change
|
||||
QDomDocument resaved(const QString &saved)
|
||||
{
|
||||
run({QStringLiteral("--resave"), QStringLiteral(QET_EXAMPLES_DIR "/tremie_vibrante.qet"), saved});
|
||||
QFile file(saved);
|
||||
QDomDocument document;
|
||||
if (file.open(QIODevice::ReadOnly))
|
||||
document.setContent(&file);
|
||||
return document;
|
||||
}
|
||||
|
||||
static bool write(const QString &path, const QDomDocument &document)
|
||||
{
|
||||
QFile file(path);
|
||||
return file.open(QIODevice::WriteOnly | QIODevice::Truncate)
|
||||
&& file.write(document.toByteArray()) > 0;
|
||||
}
|
||||
|
||||
// Both fills of @p saved, which must come from the document, agree.
|
||||
void compareBothWays(const QString &saved)
|
||||
{
|
||||
QString how_document, how_folios;
|
||||
const QJsonObject document = dump(saved, false, &how_document);
|
||||
const QJsonObject folios = dump(saved, true, &how_folios);
|
||||
QVERIFY2(how_document == QLatin1String("Project database filled from the document"),
|
||||
qPrintable(how_document));
|
||||
QVERIFY2(how_folios.contains(QStringLiteral("QET_DATABASE_FROM_FOLIOS")), qPrintable(how_folios));
|
||||
QCOMPARE(document.keys().size(), 6);
|
||||
for (const QString &table : folios.keys()) {
|
||||
const QJsonArray a = document.value(table).toArray(), b = folios.value(table).toArray();
|
||||
QVERIFY2(a == b, qPrintable(QStringLiteral("%1: %2 rows from the document, %3 from the folios")
|
||||
.arg(table).arg(a.size()).arg(b.size())));
|
||||
}
|
||||
m_last = document;
|
||||
}
|
||||
|
||||
QJsonObject m_last; // the document's tables, from compareBothWays()
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
@@ -101,19 +140,144 @@ private slots:
|
||||
const QString saved = m_dir.filePath(QStringLiteral("saved%1.qet").arg(m_run));
|
||||
run({QStringLiteral("--resave"), project, saved});
|
||||
QVERIFY2(QFile::exists(saved), "--resave failed");
|
||||
compareBothWays(saved);
|
||||
}
|
||||
|
||||
QString how_document, how_folios;
|
||||
const QJsonObject document = dump(saved, false, &how_document);
|
||||
const QJsonObject folios = dump(saved, true, &how_folios);
|
||||
QVERIFY2(how_document == QLatin1String("Project database filled from the document"),
|
||||
qPrintable(how_document));
|
||||
QVERIFY2(how_folios.contains(QStringLiteral("QET_DATABASE_FROM_FOLIOS")), qPrintable(how_folios));
|
||||
QCOMPARE(document.keys().size(), 6);
|
||||
for (const QString &table : folios.keys()) {
|
||||
const QJsonArray a = document.value(table).toArray(), b = folios.value(table).toArray();
|
||||
QVERIFY2(a == b, qPrintable(QStringLiteral("%1: %2 rows from the document, %3 from the folios")
|
||||
.arg(table).arg(a.size()).arg(b.size())));
|
||||
// A label or a conductor text made from a formula is what the formula
|
||||
// gives now, not what it gave when the file was saved: every folio's
|
||||
// first element and first conductor is given a formula using each kind
|
||||
// of variable, and a saved label no formula gives.
|
||||
void formulasAreWorkedOut()
|
||||
{
|
||||
const QString saved = m_dir.filePath(QStringLiteral("formulas-saved.qet"));
|
||||
run({QStringLiteral("--resave"), QStringLiteral(QET_EXAMPLES_DIR "/tremie_vibrante.qet"), saved});
|
||||
QFile file(saved);
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
QDomDocument document;
|
||||
QVERIFY(document.setContent(&file));
|
||||
file.close();
|
||||
|
||||
auto property = [&document](QDomElement parent, const QString &name, const QString &value) {
|
||||
QDomElement properties = parent.firstChildElement(QStringLiteral("properties"));
|
||||
if (properties.isNull())
|
||||
properties = parent.appendChild(document.createElement(QStringLiteral("properties"))).toElement();
|
||||
QDomElement p = document.createElement(QStringLiteral("property"));
|
||||
p.setAttribute(QStringLiteral("name"), name);
|
||||
p.appendChild(document.createTextNode(value));
|
||||
properties.appendChild(p);
|
||||
};
|
||||
auto sequence = [&document](QDomElement item, const QString &unit) {
|
||||
item.removeChild(item.firstChildElement(QStringLiteral("sequentialNumbers")));
|
||||
QDomElement s = document.createElement(QStringLiteral("sequentialNumbers"));
|
||||
QDomElement u = document.createElement(QStringLiteral("unit"));
|
||||
u.appendChild(document.createTextNode(unit));
|
||||
s.appendChild(u);
|
||||
item.appendChild(s);
|
||||
};
|
||||
|
||||
property(document.documentElement(), QStringLiteral("site"), QStringLiteral("S"));
|
||||
const QDomNodeList diagrams = document.elementsByTagName(QStringLiteral("diagram"));
|
||||
QCOMPARE(diagrams.size(), 3);
|
||||
for (int i = 0 ; i < diagrams.size() ; ++i)
|
||||
{
|
||||
QDomElement diagram = diagrams.at(i).toElement();
|
||||
diagram.setAttribute(QStringLiteral("folio"), QStringLiteral("F%id"));
|
||||
diagram.setAttribute(QStringLiteral("plant"), QStringLiteral("P"));
|
||||
diagram.setAttribute(QStringLiteral("locmach"), QStringLiteral("L"));
|
||||
property(diagram, QStringLiteral("zone"), QStringLiteral("Z%1").arg(i));
|
||||
|
||||
QDomElement element = diagram.firstChildElement(QStringLiteral("elements"))
|
||||
.firstChildElement(QStringLiteral("element"));
|
||||
QVERIFY(!element.isNull());
|
||||
element.setAttribute(QStringLiteral("prefix"), QStringLiteral("X"));
|
||||
sequence(element, QStringLiteral("7"));
|
||||
QDomElement informations = element.firstChildElement(QStringLiteral("elementInformations"));
|
||||
if (informations.isNull())
|
||||
informations = element.appendChild(document.createElement(QStringLiteral("elementInformations"))).toElement();
|
||||
while (!informations.firstChild().isNull())
|
||||
informations.removeChild(informations.firstChild());
|
||||
for (const auto &info : {std::make_pair(QStringLiteral("formula"),
|
||||
QStringLiteral("K%total-%f-%F-%M-%LM-%c%l-%prefix-%{zone}-%{site}-%sequ_1")),
|
||||
std::make_pair(QStringLiteral("label"), QStringLiteral("OLD"))}) {
|
||||
QDomElement e = document.createElement(QStringLiteral("elementInformation"));
|
||||
e.setAttribute(QStringLiteral("name"), info.first);
|
||||
e.setAttribute(QStringLiteral("show"), QStringLiteral("1"));
|
||||
e.appendChild(document.createTextNode(info.second));
|
||||
informations.appendChild(e);
|
||||
}
|
||||
|
||||
QDomElement conductor = diagram.firstChildElement(QStringLiteral("conductors"))
|
||||
.firstChildElement(QStringLiteral("conductor"));
|
||||
QVERIFY(!conductor.isNull());
|
||||
conductor.setAttribute(QStringLiteral("formula"), QStringLiteral("W%total-%id-%wf-%{zone}-%sequ_1"));
|
||||
conductor.setAttribute(QStringLiteral("function"), QStringLiteral("N"));
|
||||
conductor.setAttribute(QStringLiteral("num"), QStringLiteral("OLD"));
|
||||
sequence(conductor, QStringLiteral("3"));
|
||||
}
|
||||
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Truncate));
|
||||
file.write(document.toByteArray());
|
||||
file.close();
|
||||
|
||||
compareBothWays(saved);
|
||||
// ...and they were worked out, not left as saved
|
||||
const QString info = QString::fromUtf8(QJsonDocument(m_last.value(QStringLiteral("element_info")).toArray())
|
||||
.toJson(QJsonDocument::Compact));
|
||||
const QString wires = QString::fromUtf8(QJsonDocument(m_last.value(QStringLiteral("conductor")).toArray())
|
||||
.toJson(QJsonDocument::Compact));
|
||||
QVERIFY2(info.contains(QStringLiteral("K3-1-F1-P-L-")), qPrintable(info.left(400)));
|
||||
QVERIFY2(info.contains(QStringLiteral("-X-Z0-S-7")), qPrintable(info.left(400)));
|
||||
QVERIFY2(wires.contains(QStringLiteral("W3-3-N-Z2-3")), qPrintable(wires.left(400)));
|
||||
QVERIFY(!info.contains(QStringLiteral("OLD")));
|
||||
QVERIFY(!wires.contains(QStringLiteral("OLD")));
|
||||
}
|
||||
|
||||
// An element the folio does not build, and so the conductors ending on
|
||||
// it, are left out the same way.
|
||||
void unbuiltElementIsLeftOut()
|
||||
{
|
||||
const QString saved = m_dir.filePath(QStringLiteral("unbuilt.qet"));
|
||||
QDomDocument document = resaved(saved);
|
||||
QDomElement conductor = document.elementsByTagName(QStringLiteral("conductor")).at(0).toElement();
|
||||
QVERIFY(!conductor.isNull());
|
||||
const QString uuid = conductor.attribute(QStringLiteral("element1"));
|
||||
const QDomNodeList elements = document.elementsByTagName(QStringLiteral("element"));
|
||||
bool found = false;
|
||||
for (int i = 0 ; i < elements.size() ; ++i) {
|
||||
QDomElement e = elements.at(i).toElement();
|
||||
if (e.attribute(QStringLiteral("uuid")) == uuid) {
|
||||
e.setAttribute(QStringLiteral("x"), QStringLiteral("nan"));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
QVERIFY(found);
|
||||
QVERIFY(write(saved, document));
|
||||
compareBothWays(saved);
|
||||
const QString wires = QString::fromUtf8(QJsonDocument(m_last.value(QStringLiteral("conductor")).toArray())
|
||||
.toJson(QJsonDocument::Compact));
|
||||
QVERIFY(!wires.contains(conductor.attribute(QStringLiteral("uuid"))));
|
||||
}
|
||||
|
||||
// Two elements on a folio numbering their terminals alike: which one the
|
||||
// folio then refuses depends on the terminals' geometry, so the folios
|
||||
// fill the database.
|
||||
void clashingTerminalIdsFallBack()
|
||||
{
|
||||
const QString saved = m_dir.filePath(QStringLiteral("clash.qet"));
|
||||
QDomDocument document = resaved(saved);
|
||||
const QDomElement diagram = document.elementsByTagName(QStringLiteral("diagram")).at(0).toElement();
|
||||
QDomElement first = diagram.firstChildElement(QStringLiteral("elements")).firstChildElement(QStringLiteral("element"));
|
||||
QDomElement second = first.nextSiblingElement(QStringLiteral("element"));
|
||||
const QString id = first.firstChildElement(QStringLiteral("terminals"))
|
||||
.firstChildElement(QStringLiteral("terminal")).attribute(QStringLiteral("id"));
|
||||
QDomElement terminal = second.firstChildElement(QStringLiteral("terminals"))
|
||||
.firstChildElement(QStringLiteral("terminal"));
|
||||
QVERIFY(!id.isEmpty() && !terminal.isNull());
|
||||
terminal.setAttribute(QStringLiteral("id"), id);
|
||||
QVERIFY(write(saved, document));
|
||||
QString how;
|
||||
dump(saved, false, &how);
|
||||
QCOMPARE(how, QStringLiteral("Project database filled from the folios: "
|
||||
"two elements on a folio number their terminals alike"));
|
||||
}
|
||||
|
||||
// A file whose items carry no saved uuid is filled from the folios,
|
||||
|
||||
Reference in New Issue
Block a user