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 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-09-09 10:49:16 +12:00
parent c39c7414cf
commit 6f2c66afef
4 changed files with 77 additions and 14 deletions
+19 -3
View File
@@ -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<MasterElement *>(m_element.data())->contactUsage();
const MasterElement *master =
static_cast<const MasterElement *>(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)