Merge pull request #588 from Kellermorph/Fix-PLC-Manager

Fix-PLC-Manager
This commit is contained in:
Laurent Trinques
2026-08-01 15:30:10 +02:00
committed by GitHub
12 changed files with 356 additions and 137 deletions
+5
View File
@@ -292,6 +292,11 @@ namespace autonum
str.replace("%{plc_function}", dc.value("plc_function").toString()); str.replace("%{plc_function}", dc.value("plc_function").toString());
str.replace("%{plc_comment}", dc.value("plc_comment").toString()); str.replace("%{plc_comment}", dc.value("plc_comment").toString());
str.replace("%{plc_crossref}", dc.value("plc_crossref").toString()); str.replace("%{plc_crossref}", dc.value("plc_crossref").toString());
str.replace("%{plc_tc}", dc.value("plc_tc").toString());
str.replace("%{plc_t1}", dc.value("plc_t1").toString());
str.replace("%{plc_t2}", dc.value("plc_t2").toString());
str.replace("%{plc_t3}", dc.value("plc_t3").toString());
str.replace("%{plc_t4}", dc.value("plc_t4").toString());
return str; return str;
} }
+9 -3
View File
@@ -388,9 +388,15 @@ void projectDataBase::createElementNomenclatureView()
"ei.supplier_auxiliary4 AS supplier_auxiliary4," "ei.supplier_auxiliary4 AS supplier_auxiliary4,"
"ei.quantity_auxiliary4 AS quantity_auxiliary4," "ei.quantity_auxiliary4 AS quantity_auxiliary4,"
"ei.unity_auxiliary4 AS unity_auxiliary4," "ei.unity_auxiliary4 AS unity_auxiliary4,"
"ei.exclude_from_bom AS exclude_from_bom," "ei.exclude_from_bom AS exclude_from_bom,"
"d.pos AS diagram_position," "ei.plc_type AS plc_type,"
"ei.plc_address AS plc_address,"
"ei.plc_function AS plc_function,"
"ei.plc_comment AS plc_comment,"
"ei.plc_crossref AS plc_crossref,"
"d.pos AS diagram_position,"
"e.type AS element_type," "e.type AS element_type,"
"e.sub_type AS element_sub_type," "e.sub_type AS element_sub_type,"
"di.title AS title," "di.title AS title,"
+17 -7
View File
@@ -47,6 +47,7 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) :
m_button_group.addButton(ui->m_coil_cb, 4); m_button_group.addButton(ui->m_coil_cb, 4);
m_button_group.addButton(ui->m_protection_cb, 5); m_button_group.addButton(ui->m_protection_cb, 5);
m_button_group.addButton(ui->m_thumbnail_cb, 6); m_button_group.addButton(ui->m_thumbnail_cb, 6);
m_button_group.addButton(ui->m_plc_cb, 7);
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) // ### Qt 6: remove #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) // ### Qt 6: remove
connect(&m_button_group, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), [this](int id) connect(&m_button_group, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), [this](int id)
#else #else
@@ -76,7 +77,7 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) :
else else
{ {
int checked = 0; int checked = 0;
for (int i=1 ; i<7 ; ++i) { for (int i=1 ; i<8 ; ++i) {
if (m_button_group.button(i)->isChecked()) {++checked;} if (m_button_group.button(i)->isChecked()) {++checked;}
} }
@@ -85,7 +86,7 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) :
case 0 : case 0 :
check_box->setCheckState(Qt::Unchecked); check_box->setCheckState(Qt::Unchecked);
break; break;
case 6: case 7:
check_box->setCheckState(Qt::Checked); check_box->setCheckState(Qt::Checked);
break; break;
default: default:
@@ -187,12 +188,16 @@ void ElementQueryWidget::setQuery(const QString &query)
if (ui->m_protection_cb) { if (ui->m_protection_cb) {
++c; ++c;
} }
ui->m_thumbnail_cb->setChecked (str_type.contains(ElementData::typeToString(ElementData::Thumbnail)) ? true : false); ui->m_thumbnail_cb->setChecked (str_type.contains(ElementData::typeToString(ElementData::Thumbnail)) ? true : false);
if (ui->m_thumbnail_cb->isChecked()) { if (ui->m_thumbnail_cb->isChecked()) {
++c; ++c;
} }
ui->m_plc_cb->setChecked (str_type.contains(ElementData::masterTypeToString(ElementData::PLC)) ? true : false);
if (ui->m_plc_cb->isChecked()) {
++c;
}
if (c == 6) { if (c == 7) {
ui->m_all_cb->setCheckState(Qt::Checked); ui->m_all_cb->setCheckState(Qt::Checked);
} else if (c > 0) { } else if (c > 0) {
ui->m_all_cb->setCheckState(Qt::PartiallyChecked); ui->m_all_cb->setCheckState(Qt::PartiallyChecked);
@@ -366,6 +371,11 @@ QString ElementQueryWidget::queryStr() const
if (ui->m_protection_cb->isChecked()) { if (ui->m_protection_cb->isChecked()) {
if (b) where +=" OR"; if (b) where +=" OR";
where += QStringLiteral(" element_sub_type = '") += ElementData::masterTypeToString(ElementData::Protection) += "'"; where += QStringLiteral(" element_sub_type = '") += ElementData::masterTypeToString(ElementData::Protection) += "'";
b = true;
}
if (ui->m_plc_cb->isChecked()) {
if (b) where +=" OR";
where += QStringLiteral(" element_sub_type = '") += ElementData::masterTypeToString(ElementData::PLC) += "'";
} }
where.append(")"); where.append(")");
+90 -80
View File
@@ -234,86 +234,96 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QGroupBox" name="groupBox_3"> <widget class="QGroupBox" name="groupBox_3">
<property name="title"> <property name="title">
<string>Type d'éléments</string> <string>Type d'éléments</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_4">
<item row="5" column="0"> <item row="0" column="0">
<widget class="QCheckBox" name="m_simple_cb"> <widget class="QCheckBox" name="m_all_cb">
<property name="text"> <property name="text">
<string>Simples</string> <string>Tous</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="0" column="1">
<widget class="QCheckBox" name="m_protection_cb"> <widget class="QCheckBox" name="m_simple_cb">
<property name="text"> <property name="text">
<string>Organes de protection</string> <string>Simples</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="1" column="0">
<widget class="QCheckBox" name="m_all_cb"> <widget class="QCheckBox" name="m_terminal_cb">
<property name="text"> <property name="text">
<string>Tous</string> <string>Borniers</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="1" column="1">
<widget class="QCheckBox" name="m_button_cb"> <widget class="QCheckBox" name="m_protection_cb">
<property name="text"> <property name="text">
<string>Boutons et commutateurs</string> <string>Organes de protection</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="m_terminal_cb"> <widget class="QCheckBox" name="m_coil_cb">
<property name="text"> <property name="text">
<string>Borniers</string> <string>Contacteurs et relais</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="2" column="1">
<widget class="QCheckBox" name="m_thumbnail_cb"> <widget class="QCheckBox" name="m_thumbnail_cb">
<property name="text"> <property name="text">
<string>Vignettes</string> <string>Vignettes</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="3" column="0">
<widget class="QCheckBox" name="m_coil_cb"> <widget class="QCheckBox" name="m_button_cb">
<property name="text"> <property name="text">
<string>Contacteurs et relais</string> <string>Boutons et commutateurs</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="3" column="1">
</widget> <widget class="QCheckBox" name="m_plc_cb">
<property name="text">
<string>Automates (MAE/SPS)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
+11 -1
View File
@@ -240,7 +240,12 @@ void DynamicTextFieldEditor::fillInfoComboBox()
QETInformation::ELMT_PLC_ADDRESS, QETInformation::ELMT_PLC_ADDRESS,
QETInformation::ELMT_PLC_FUNCTION, QETInformation::ELMT_PLC_FUNCTION,
QETInformation::ELMT_PLC_COMMENT, QETInformation::ELMT_PLC_COMMENT,
QETInformation::ELMT_PLC_CROSSREF QETInformation::ELMT_PLC_CROSSREF,
QETInformation::ELMT_PLC_TC,
QETInformation::ELMT_PLC_T1,
QETInformation::ELMT_PLC_T2,
QETInformation::ELMT_PLC_T3,
QETInformation::ELMT_PLC_T4
}; };
strl = plc_keys + strl; strl = plc_keys + strl;
} else { } else {
@@ -249,6 +254,11 @@ void DynamicTextFieldEditor::fillInfoComboBox()
strl.removeAll(QETInformation::ELMT_PLC_FUNCTION); strl.removeAll(QETInformation::ELMT_PLC_FUNCTION);
strl.removeAll(QETInformation::ELMT_PLC_COMMENT); strl.removeAll(QETInformation::ELMT_PLC_COMMENT);
strl.removeAll(QETInformation::ELMT_PLC_CROSSREF); strl.removeAll(QETInformation::ELMT_PLC_CROSSREF);
strl.removeAll(QETInformation::ELMT_PLC_TC);
strl.removeAll(QETInformation::ELMT_PLC_T1);
strl.removeAll(QETInformation::ELMT_PLC_T2);
strl.removeAll(QETInformation::ELMT_PLC_T3);
strl.removeAll(QETInformation::ELMT_PLC_T4);
} }
} }
@@ -683,7 +683,7 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets()
m_plc_table->horizontalHeader()->resizeSection(2, 150); m_plc_table->horizontalHeader()->resizeSection(2, 150);
m_plc_table->horizontalHeader()->resizeSection(3, 150); m_plc_table->horizontalHeader()->resizeSection(3, 150);
m_plc_table->horizontalHeader()->resizeSection(4, 100); m_plc_table->horizontalHeader()->resizeSection(4, 100);
m_plc_table->setSelectionBehavior(QAbstractItemView::SelectRows); m_plc_table->setSelectionBehavior(QAbstractItemView::SelectItems);
m_plc_table->setSelectionMode(QAbstractItemView::ExtendedSelection); m_plc_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_plc_table->setMinimumHeight(200); m_plc_table->setMinimumHeight(200);
tables_splitter->addWidget(m_plc_table); tables_splitter->addWidget(m_plc_table);
@@ -697,7 +697,7 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets()
m_plc_terminal_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); m_plc_terminal_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
m_plc_terminal_table->horizontalHeader()->resizeSection(0, 50); m_plc_terminal_table->horizontalHeader()->resizeSection(0, 50);
m_plc_terminal_table->horizontalHeader()->resizeSection(1, 80); m_plc_terminal_table->horizontalHeader()->resizeSection(1, 80);
m_plc_terminal_table->setSelectionBehavior(QAbstractItemView::SelectRows); m_plc_terminal_table->setSelectionBehavior(QAbstractItemView::SelectItems);
m_plc_terminal_table->setSelectionMode(QAbstractItemView::ExtendedSelection); m_plc_terminal_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_plc_terminal_table->setMinimumHeight(200); m_plc_terminal_table->setMinimumHeight(200);
tables_splitter->addWidget(m_plc_terminal_table); tables_splitter->addWidget(m_plc_terminal_table);
@@ -780,7 +780,7 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets()
auto *sb = new QSpinBox(m_plc_gb); auto *sb = new QSpinBox(m_plc_gb);
sb->setMinimum(10); sb->setMinimum(10);
sb->setMaximum(200); sb->setMaximum(500);
sb->setValue(40); sb->setValue(40);
sb->setSuffix(tr(" mm")); sb->setSuffix(tr(" mm"));
m_plc_col_width_spinboxes.append(sb); m_plc_col_width_spinboxes.append(sb);
+86 -20
View File
@@ -1396,6 +1396,27 @@ void Element::setElementInformations(DiagramContext dc)
m_data.m_informations.addValue(QStringLiteral("label"), actual_label); //Update the label if there is a formula m_data.m_informations.addValue(QStringLiteral("label"), actual_label); //Update the label if there is a formula
} }
emit elementInfoChange(old_info, m_data.m_informations); emit elementInfoChange(old_info, m_data.m_informations);
// Propagate label change to linked PLC slaves (label is changed via
// setElementInformations through the undo stack, not via setElementData)
if (m_data.m_type == ElementData::Master && m_data.m_master_type == ElementData::PLC)
{
if (!m_group_index_map.isEmpty())
{
const QString new_label = actualLabel();
for (auto it = m_group_index_map.constBegin(); it != m_group_index_map.constEnd(); ++it)
{
Element *slave = it.key();
if (!slave)
continue;
if (slave->elementInformations().value(QETInformation::ELMT_LABEL).toString() == new_label)
continue;
DiagramContext ctx = slave->elementInformations();
ctx.addValue(QETInformation::ELMT_LABEL, new_label);
slave->setElementInformations(ctx);
}
}
}
} }
/** /**
@@ -1433,7 +1454,9 @@ void Element::setElementData(ElementData data)
{ {
const auto &new_plc = m_data.plcMasterData(); const auto &new_plc = m_data.plcMasterData();
bool plc_changed = (old_plc.ios != new_plc.ios); bool plc_changed = (old_plc.ios != new_plc.ios);
if (plc_changed && !m_group_index_map.isEmpty()) bool label_changed = (old_info.value(QStringLiteral("label")) !=
m_data.m_informations.value(QStringLiteral("label")));
if (!m_group_index_map.isEmpty() && (plc_changed || label_changed))
{ {
for (auto it = m_group_index_map.constBegin(); it != m_group_index_map.constEnd(); ++it) for (auto it = m_group_index_map.constBegin(); it != m_group_index_map.constEnd(); ++it)
{ {
@@ -1442,24 +1465,64 @@ void Element::setElementData(ElementData data)
if (!slave || io_idx < 0 || io_idx >= new_plc.ios.size()) if (!slave || io_idx < 0 || io_idx >= new_plc.ios.size())
continue; continue;
const auto &io = new_plc.ios.at(io_idx); const auto &io = new_plc.ios.at(io_idx);
DiagramContext ctx = slave->elementInformations();
ctx.addValue(QETInformation::ELMT_PLC_TYPE, if (plc_changed)
ElementData::translatedPlcIOType(io.type)); {
ctx.addValue(QETInformation::ELMT_PLC_ADDRESS, io.address); DiagramContext ctx = slave->elementInformations();
ctx.addValue(QETInformation::ELMT_PLC_FUNCTION, io.functionText); ctx.addValue(QETInformation::ELMT_PLC_TYPE,
ctx.addValue(QETInformation::ELMT_PLC_COMMENT, io.comment); ElementData::translatedPlcIOType(io.type));
ctx.addValue(QETInformation::ELMT_PLC_CROSSREF, ctx.addValue(QETInformation::ELMT_PLC_ADDRESS, io.address);
[&]() -> QString { ctx.addValue(QETInformation::ELMT_PLC_FUNCTION, io.functionText);
if (!diagram() || !diagram()->project()) ctx.addValue(QETInformation::ELMT_PLC_COMMENT, io.comment);
return QString(); ctx.addValue(QETInformation::ELMT_PLC_CROSSREF,
XRefProperties xrp = diagram()->project() [&]() -> QString {
->defaultXRefProperties("plc"); if (!diagram() || !diagram()->project())
autonum::sequentialNumbers seq; return QString();
return autonum::AssignVariables::formulaToLabel( XRefProperties xrp = diagram()->project()
xrp.slaveLabel(), seq, diagram(), this); ->defaultXRefProperties("plc");
}()); autonum::sequentialNumbers seq;
ctx.addValue(QETInformation::ELMT_LABEL, actualLabel()); return autonum::AssignVariables::formulaToLabel(
slave->setElementInformations(ctx); xrp.slaveLabel(), seq, diagram(), this);
}());
ctx.addValue(QETInformation::ELMT_LABEL, actualLabel());
ctx.addValue(QETInformation::ELMT_PLC_TC,
QString::number(io.terminalCount));
for (int t = 0; t < io.terminalCount && t < 4; ++t)
{
QString val = (t < io.terminals.size())
? io.terminals.at(t) : QString();
ctx.addValue(
QStringList({
QETInformation::ELMT_PLC_T1,
QETInformation::ELMT_PLC_T2,
QETInformation::ELMT_PLC_T3,
QETInformation::ELMT_PLC_T4
}).at(t), val);
}
slave->setElementInformations(ctx);
// Update master labels on slave terminals
QList<Terminal *> slave_terms = slave->terminals();
for (int t = 0; t < slave_terms.size(); ++t)
{
if (t < io.terminals.size())
{
slave_terms.at(t)->setUseMasterLabel(true);
slave_terms.at(t)->setMasterLabelIndex(t);
}
else
{
slave_terms.at(t)->setUseMasterLabel(false);
}
}
}
if (label_changed)
{
// Only label changed, update the label on the slave
DiagramContext ctx = slave->elementInformations();
ctx.addValue(QETInformation::ELMT_LABEL, actualLabel());
slave->setElementInformations(ctx);
}
} }
} }
} }
@@ -1900,7 +1963,10 @@ void Element::drawPlcTable(QPainter *painter)
} }
QRectF text_rect = cr.adjusted(1, 0, -1, 0); QRectF text_rect = cr.adjusted(1, 0, -1, 0);
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, cell_text); painter->save();
painter->setClipRect(text_rect, Qt::IntersectClip);
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, cell_text);
painter->restore();
cx += col_widths[col]; cx += col_widths[col];
} }
+40 -9
View File
@@ -289,8 +289,17 @@ void Terminal::paint(
painter->save(); painter->save();
painter->translate(label_pos); painter->translate(label_pos);
painter->rotate(d->m_label_rotation); painter->rotate(d->m_label_rotation);
QRectF text_rect(-text_size.width()/2.0, -text_size.height()/2.0,
text_size.width(), text_size.height()); qreal rx = 0, ry = 0;
if (d->m_label_halignment & Qt::AlignLeft) rx = 0;
else if (d->m_label_halignment & Qt::AlignHCenter) rx = -text_size.width() / 2.0;
else if (d->m_label_halignment & Qt::AlignRight) rx = -text_size.width();
if (d->m_label_valignment & Qt::AlignTop) ry = 0;
else if (d->m_label_valignment & Qt::AlignVCenter) ry = -text_size.height() / 2.0;
else if (d->m_label_valignment & Qt::AlignBottom) ry = -text_size.height();
QRectF text_rect(QPointF(rx, ry), text_size);
painter->drawText(text_rect, static_cast<int>(d->m_label_halignment | d->m_label_valignment), display_name); painter->drawText(text_rect, static_cast<int>(d->m_label_halignment | d->m_label_valignment), display_name);
painter->restore(); painter->restore();
} else { } else {
@@ -307,7 +316,7 @@ void Terminal::paint(
if (d->m_label_frame) { if (d->m_label_frame) {
painter->drawRect(text_rect.adjusted(-1, -1, 1, 1)); painter->drawRect(text_rect.adjusted(-1, -1, 1, 1));
} }
painter->drawText(text_rect, static_cast<int>(Qt::AlignLeft | Qt::AlignTop), display_name); painter->drawText(text_rect, static_cast<int>(d->m_label_halignment | d->m_label_valignment), display_name);
} }
} }
@@ -816,12 +825,24 @@ QString Terminal::name() const
if (elmt->linkType() == Element::Master) { if (elmt->linkType() == Element::Master) {
int group_idx = elmt->groupIndexForElement(parent_element_); int group_idx = elmt->groupIndexForElement(parent_element_);
if (group_idx >= 0) { if (group_idx >= 0) {
const auto &groups = elmt->elementData().m_slave_contact_groups; // For PLC masters, use io.terminals as labels
if (group_idx < groups.size()) { if (elmt->elementData().m_master_type == ElementData::PLC) {
int label_idx = d->m_master_label_index; const auto &plc_data = elmt->elementData().plcMasterData();
const QStringList &labels = groups.at(group_idx).labels; if (group_idx < plc_data.ios.size()) {
if (label_idx >= 0 && label_idx < labels.size()) { int label_idx = d->m_master_label_index;
return labels.at(label_idx); const QStringList &labels = plc_data.ios.at(group_idx).terminals;
if (label_idx >= 0 && label_idx < labels.size()) {
return labels.at(label_idx);
}
}
} else {
const auto &groups = elmt->elementData().m_slave_contact_groups;
if (group_idx < groups.size()) {
int label_idx = d->m_master_label_index;
const QStringList &labels = groups.at(group_idx).labels;
if (label_idx >= 0 && label_idx < labels.size()) {
return labels.at(label_idx);
}
} }
} }
} }
@@ -832,6 +853,16 @@ QString Terminal::name() const
return d->m_name; return d->m_name;
} }
/**
@brief Terminal::baseName
Return the original terminal name (T1, T2...) without master label override.
Used for sorting when linking.
*/
QString Terminal::baseName() const
{
return d->m_name;
}
/** /**
@brief Terminal::terminalType @brief Terminal::terminalType
@return the type of this terminal (Generic, Inner, Outer, No, Nc, Common) @return the type of this terminal (Generic, Inner, Outer, No, Nc, Common)
+1
View File
@@ -76,6 +76,7 @@ class Terminal : public QGraphicsObject
Element *parentElement () const; Element *parentElement () const;
QUuid uuid () const; QUuid uuid () const;
QString name () const; QString name () const;
QString baseName () const;
TerminalData::Type terminalType() const; TerminalData::Type terminalType() const;
bool useMasterLabel() const { return d->m_use_master_label; } bool useMasterLabel() const { return d->m_use_master_label; }
void setUseMasterLabel(bool use); void setUseMasterLabel(bool use);
+5
View File
@@ -130,6 +130,11 @@ namespace QETInformation
static QString ELMT_PLC_FUNCTION = "plc_function"; static QString ELMT_PLC_FUNCTION = "plc_function";
static QString ELMT_PLC_COMMENT = "plc_comment"; static QString ELMT_PLC_COMMENT = "plc_comment";
static QString ELMT_PLC_CROSSREF = "plc_crossref"; static QString ELMT_PLC_CROSSREF = "plc_crossref";
static QString ELMT_PLC_TC = "plc_tc";
static QString ELMT_PLC_T1 = "plc_t1";
static QString ELMT_PLC_T2 = "plc_t2";
static QString ELMT_PLC_T3 = "plc_t3";
static QString ELMT_PLC_T4 = "plc_t4";
+38 -1
View File
@@ -216,6 +216,16 @@ void MasterPropertiesWidget::reset()
*/ */
QUndoCommand* MasterPropertiesWidget::associatedUndo() const QUndoCommand* MasterPropertiesWidget::associatedUndo() const
{ {
// PLC masters manage their slave links via the IO table (setElementData),
// not via the link tree widget. The link tree is always empty for PLC
// masters, so we must not create an unlinkAll command.
if (m_element &&
m_element->elementData().m_type == ElementData::Master &&
m_element->elementData().m_master_type == ElementData::PLC)
{
return nullptr;
}
QList <Element *> to_link; QList <Element *> to_link;
QList <Element *> linked_ = m_element->linkedElements(); QList <Element *> linked_ = m_element->linkedElements();
@@ -880,6 +890,8 @@ void MasterPropertiesWidget::plcRemoveRow()
if (selected.isEmpty()) if (selected.isEmpty())
return; return;
m_plc_updating = true;
// Remove from bottom to top to preserve indices // Remove from bottom to top to preserve indices
std::sort(selected.begin(), selected.end(), std::sort(selected.begin(), selected.end(),
[](const QModelIndex &a, const QModelIndex &b) { return a.row() > b.row(); }); [](const QModelIndex &a, const QModelIndex &b) { return a.row() > b.row(); });
@@ -887,6 +899,9 @@ void MasterPropertiesWidget::plcRemoveRow()
for (const QModelIndex &idx : selected) { for (const QModelIndex &idx : selected) {
m_plc_table->removeRow(idx.row()); m_plc_table->removeRow(idx.row());
} }
m_plc_updating = false;
plcUpdateDisplaySettings();
} }
/** /**
@@ -902,6 +917,8 @@ void MasterPropertiesWidget::plcMoveRowUp()
if (row <= 0) if (row <= 0)
return; return;
m_plc_updating = true;
// Swap with row above // Swap with row above
for (int col = 0; col < m_plc_table->columnCount(); ++col) { for (int col = 0; col < m_plc_table->columnCount(); ++col) {
QWidget *w1 = m_plc_table->cellWidget(row, col); QWidget *w1 = m_plc_table->cellWidget(row, col);
@@ -916,6 +933,8 @@ void MasterPropertiesWidget::plcMoveRowUp()
} }
m_plc_table->setCurrentCell(row - 1, m_plc_table->currentColumn()); m_plc_table->setCurrentCell(row - 1, m_plc_table->currentColumn());
m_plc_updating = false;
plcUpdateDisplaySettings();
} }
/** /**
@@ -931,6 +950,8 @@ void MasterPropertiesWidget::plcMoveRowDown()
if (row < 0 || row >= m_plc_table->rowCount() - 1) if (row < 0 || row >= m_plc_table->rowCount() - 1)
return; return;
m_plc_updating = true;
// Swap with row below // Swap with row below
for (int col = 0; col < m_plc_table->columnCount(); ++col) { for (int col = 0; col < m_plc_table->columnCount(); ++col) {
QWidget *w1 = m_plc_table->cellWidget(row, col); QWidget *w1 = m_plc_table->cellWidget(row, col);
@@ -945,6 +966,8 @@ void MasterPropertiesWidget::plcMoveRowDown()
} }
m_plc_table->setCurrentCell(row + 1, m_plc_table->currentColumn()); m_plc_table->setCurrentCell(row + 1, m_plc_table->currentColumn());
m_plc_updating = false;
plcUpdateDisplaySettings();
} }
/** /**
@@ -978,7 +1001,14 @@ void MasterPropertiesWidget::plcUpdateDisplaySettings()
ElementData::PlcMasterData plc_data = ed.plcMasterData(); ElementData::PlcMasterData plc_data = ed.plcMasterData();
plc_data.ios.clear(); plc_data.ios.clear();
// Read IOs from table // Build address -> original IO lookup to correctly reattach terminal data
// after row reorder (move up/down) or row removal
QHash<QString, int> addr_to_orig_idx;
for (int i = 0; i < ed.plcMasterData().ios.size(); ++i) {
addr_to_orig_idx[ed.plcMasterData().ios.at(i).address] = i;
}
// Read IOs from table, preserving terminal data from original IOs
for (int row = 0; row < m_plc_table->rowCount(); ++row) { for (int row = 0; row < m_plc_table->rowCount(); ++row) {
ElementData::PlcIO io; ElementData::PlcIO io;
@@ -1002,6 +1032,13 @@ void MasterPropertiesWidget::plcUpdateDisplaySettings()
if (crossref_item) if (crossref_item)
io.crossRef = crossref_item->text(); io.crossRef = crossref_item->text();
// Preserve terminal data by looking up original IO via address
if (addr_to_orig_idx.contains(io.address)) {
const auto &orig_io = ed.plcMasterData().ios.at(addr_to_orig_idx.value(io.address));
io.terminalCount = orig_io.terminalCount;
io.terminals = orig_io.terminals;
}
plc_data.ios.append(io); plc_data.ios.append(io);
} }
+51 -13
View File
@@ -406,22 +406,46 @@ void LinkElementCommand::makeLink(const QList<Element *> &element_list)
elmt->setGroupIndexForElement(m_element, group_idx); elmt->setGroupIndexForElement(m_element, group_idx);
// Set master labels on slave terminals // Set master labels on slave terminals
const auto &groups = elmt->elementData().m_slave_contact_groups; if (elmt->elementData().m_master_type == ElementData::PLC)
if (group_idx < groups.size())
{ {
const QStringList &labels = groups.at(group_idx).labels; // For PLC masters, use io.terminals as labels
QList<Terminal *> slave_terms = m_element->terminals(); const auto &plc_data = elmt->elementData().plcMasterData();
// Sort terminals by name (T1, T2, T3...) to match label order if (group_idx < plc_data.ios.size())
std::sort(slave_terms.begin(), slave_terms.end(),
[](Terminal *a, Terminal *b) {
return a->name() < b->name();
});
for (int i = 0; i < slave_terms.size(); ++i)
{ {
if (i < labels.size()) const QStringList &labels = plc_data.ios.at(group_idx).terminals;
QList<Terminal *> slave_terms = m_element->terminals();
std::sort(slave_terms.begin(), slave_terms.end(),
[](Terminal *a, Terminal *b) {
return a->baseName() < b->baseName();
});
for (int i = 0; i < slave_terms.size(); ++i)
{ {
slave_terms.at(i)->setUseMasterLabel(true); if (i < labels.size())
slave_terms.at(i)->setMasterLabelIndex(i); {
slave_terms.at(i)->setUseMasterLabel(true);
slave_terms.at(i)->setMasterLabelIndex(i);
}
}
}
}
else
{
const auto &groups = elmt->elementData().m_slave_contact_groups;
if (group_idx < groups.size())
{
const QStringList &labels = groups.at(group_idx).labels;
QList<Terminal *> slave_terms = m_element->terminals();
std::sort(slave_terms.begin(), slave_terms.end(),
[](Terminal *a, Terminal *b) {
return a->baseName() < b->baseName();
});
for (int i = 0; i < slave_terms.size(); ++i)
{
if (i < labels.size())
{
slave_terms.at(i)->setUseMasterLabel(true);
slave_terms.at(i)->setMasterLabelIndex(i);
}
} }
} }
} }
@@ -443,6 +467,20 @@ void LinkElementCommand::makeLink(const QList<Element *> &element_list)
plcCrossRefText(elmt, m_element)); plcCrossRefText(elmt, m_element));
ctx.addValue(QETInformation::ELMT_LABEL, ctx.addValue(QETInformation::ELMT_LABEL,
elmt->actualLabel()); elmt->actualLabel());
ctx.addValue(QETInformation::ELMT_PLC_TC,
QString::number(io.terminalCount));
for (int t = 0; t < io.terminalCount && t < 4; ++t)
{
QString val = (t < io.terminals.size())
? io.terminals.at(t) : QString();
ctx.addValue(
QStringList({
QETInformation::ELMT_PLC_T1,
QETInformation::ELMT_PLC_T2,
QETInformation::ELMT_PLC_T3,
QETInformation::ELMT_PLC_T4
}).at(t), val);
}
m_element->setElementInformations(ctx); m_element->setElementInformations(ctx);
} }
} }