From 6f2c66afefcd71707f0030f58303aa03a935bdeb Mon Sep 17 00:00:00 2001 From: ispyisail Date: Wed, 9 Sep 2026 10:49:16 +1200 Subject: [PATCH] Show used against declared capacity where a master declares contact groups Second half of #819: where a coil declares what contacts it provides, the General tab now reports each type as used against declared rather than as a bare count. NO : 3/4, NC : 1/2, inverseurs : 0/1, autres : 0/0 MasterElement::contactCapacity() sums contactCount over the element's SlaveContactGroup list, per type, reusing the same ContactUsage tally the used count is built on. The mapping from ElementData::SlaveState onto the tally's own type is factored into one helper so the used count and the declared capacity cannot classify a contact differently. Falls back to the plain count from the previous commit when an element declares no groups, which is every element in the standard collection today -- nothing in the corpus declares slaveContactGroups, so this changes no existing display. A type used beyond what is declared reads as e.g. "1/0". That is deliberate: it says this contact does not fit the part. Display only. Whether a declared capacity should also feed MasterElement::isFull() is the open question in #819 and is not touched here. Verified end to end against a purpose-built fixture, since no existing element exercises this path: a coil declaring two NO groups of two, one NC group of two and one changeover group of one parses and reports NO=4 NC=2 SW=1 other=0 total=7, matching the declaration exactly. tst_contactusage gains a case covering capacity summed across groups (10 cases, all passing). Co-Authored-By: Claude Opus 5 --- sources/qetgraphicsitem/masterelement.cpp | 54 ++++++++++++++++++----- sources/qetgraphicsitem/masterelement.h | 1 + sources/ui/elementpropertieswidget.cpp | 22 +++++++-- tests/qttest/tst_contactusage.cpp | 14 ++++++ 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/sources/qetgraphicsitem/masterelement.cpp b/sources/qetgraphicsitem/masterelement.cpp index 1ccebfd3a..b9c829a0f 100644 --- a/sources/qetgraphicsitem/masterelement.cpp +++ b/sources/qetgraphicsitem/masterelement.cpp @@ -235,6 +235,28 @@ void MasterElement::aboutDeleteXref() * so they cannot disagree with each other. * @return the per type usage */ +namespace { + + /** + Map the element data's contact type onto the tally's own, so that + the used count and the declared capacity cannot classify the same + contact type differently. + */ + ContactUsage::Type contactType(ElementData::SlaveState state) + { + switch (state) + { + case ElementData::NO: return ContactUsage::NO; + case ElementData::NC: return ContactUsage::NC; + case ElementData::SW: return ContactUsage::SW; + case ElementData::Other: break; + } + + return ContactUsage::Other; + } + +} + ContactUsage MasterElement::contactUsage() const { ContactUsage usage; @@ -246,22 +268,32 @@ ContactUsage MasterElement::contactUsage() const } const ElementData &data = elmt->elementData(); - - ContactUsage::Type type = ContactUsage::Other; - switch (data.m_slave_state) - { - case ElementData::NO: type = ContactUsage::NO; break; - case ElementData::NC: type = ContactUsage::NC; break; - case ElementData::SW: type = ContactUsage::SW; break; - case ElementData::Other: type = ContactUsage::Other; break; - } - - usage.addSlave(type, data.m_contact_count); + usage.addSlave(contactType(data.m_slave_state), data.m_contact_count); } return usage; } +/** + * @brief MasterElement::contactCapacity + * The contacts this master declares it provides, by type, summed over its + * contact groups. A group stands for contactCount contacts of its type. + * Returns an empty tally when the element declares no groups, which is the + * case for every element in the standard collection today -- callers use + * that to decide whether a capacity is worth showing at all. + * @return the per type capacity + */ +ContactUsage MasterElement::contactCapacity() const +{ + ContactUsage capacity; + + for (const auto &group : m_data.m_slave_contact_groups) { + capacity.addSlave(contactType(group.type), group.contactCount); + } + + return capacity; +} + /** * @brief MasterElement::isFull * @return true if the master has reached its maximum number of slaves diff --git a/sources/qetgraphicsitem/masterelement.h b/sources/qetgraphicsitem/masterelement.h index 21aa20f40..2892e68e1 100644 --- a/sources/qetgraphicsitem/masterelement.h +++ b/sources/qetgraphicsitem/masterelement.h @@ -49,6 +49,7 @@ class MasterElement : public Element QRectF XrefBoundingRect() const; ContactUsage contactUsage() const; + ContactUsage contactCapacity() const; bool isFull() const; // Check Slave-Limit protected: diff --git a/sources/ui/elementpropertieswidget.cpp b/sources/ui/elementpropertieswidget.cpp index 2dc75cd51..db6603663 100644 --- a/sources/ui/elementpropertieswidget.cpp +++ b/sources/ui/elementpropertieswidget.cpp @@ -393,16 +393,32 @@ QWidget *ElementPropertiesWidget::generalWidget() //Counted in contacts rather than in linked elements, so that a //slave standing for several contacts is reported as the number //of contacts it actually uses. - const ContactUsage usage = - static_cast(m_element.data())->contactUsage(); + const MasterElement *master = + static_cast(m_element.data()); + const ContactUsage usage = master->contactUsage(); description_string += QString(tr("Nombre de contacts esclaves utilisés : %1\n")).arg(usage.total()); //The breakdown is what tells you which auxiliary block would //satisfy this coil, so it is only worth printing once there is //something to break down. - if (usage.total() > 0) + const ContactUsage capacity = master->contactCapacity(); + + if (capacity.total() > 0) { + //The element declares contact groups, so it can say not + //only what has been used but what it has to offer. A type + //used beyond what is declared shows as e.g. "1/0", which + //is the point: it says this contact does not fit the part. + description_string += QString(tr(" NO : %1/%2, NC : %3/%4, inverseurs : %5/%6, autres : %7/%8\n")) + .arg(usage.no).arg(capacity.no) + .arg(usage.nc).arg(capacity.nc) + .arg(usage.sw).arg(capacity.sw) + .arg(usage.other).arg(capacity.other); + } + else if (usage.total() > 0) + { + //No declared groups, so a plain count of what is in use. description_string += QString(tr(" NO : %1, NC : %2, inverseurs : %3, autres : %4\n")) .arg(usage.no) .arg(usage.nc) diff --git a/tests/qttest/tst_contactusage.cpp b/tests/qttest/tst_contactusage.cpp index c587b3fbb..deb756933 100644 --- a/tests/qttest/tst_contactusage.cpp +++ b/tests/qttest/tst_contactusage.cpp @@ -101,6 +101,20 @@ private slots: QCOMPARE(usage.total(), 1); } + // A declared capacity is summed across groups, so two NO groups of two + // contacts each declare four NO contacts, not two groups. + void capacitySumsAcrossGroups() + { + ContactUsage capacity; + capacity.addSlave(ContactUsage::NO, 2); + capacity.addSlave(ContactUsage::NO, 2); + capacity.addSlave(ContactUsage::NC, 1); + + QCOMPARE(capacity.no, 4); + QCOMPARE(capacity.nc, 1); + QCOMPARE(capacity.total(), 5); + } + // The mix a coil would actually carry: two single NO, one 4 pole NO, // one NC and one changeover. void tallysARealisticMix()