Skip to content

Worked examples

This page runs three worked examples from first to last: a relay with its datasheet figures, a contactor with an overload relay, and a digital input card. The outputs they assert are the ones proven against a reference run. The terminal block and power supply rows of the last section are proposed, not proven against a source.

A 24 V relay with two changeover contacts

The terminal connection is built once and shared by identity between the coil and the contacts. A contact holds one rated row per condition set, and pick returns the one row that covers an ask.

from dataclasses import replace
from fractions import Fraction

from elecdef import At, Coil, Contact, IssueCode, Product, Range, Relay, Role, Unit as U, check, connections, pick
from elecdef.pins import LinkKind, PinRole
from elecdef.vocab import ConductorKind, CoilKind, ContactForm, Current, Suppression, Utilisation

SCREW_M3 = replace(
    connections.SCREW,
    conductor=Range(Role.RATED, min=U.MM2("0.5"), max=U.MM2("2.5"), at=At(ConductorKind.SOLID)),
    torque=Range(Role.RATED, min=U.N_M("0.5"), max=U.N_M("0.6")),
    strip_length=U.MM(7),
)
COIL = Coil(
    CoilKind.DC,
    connection=SCREW_M3,
    suppression=Suppression.DIODE,
    voltage=Range(Role.RATED, nom=U.V(24), min=U.V("20.4"), max=U.V(30), at=At(Current.DC)),
    pick_up=Range(Role.LIMITING, max=U.PCT(75)),
    drop_out=Range(Role.LIMITING, min=U.PCT(10)),
)
CO = Contact(
    ContactForm.CHANGEOVER,
    connection=SCREW_M3,
    switching=(
        Range(Role.RATED, nom=U.A(6), at=At(Range(Role.LIMITING, max=U.V(250)), Current.AC, Utilisation.AC_15)),
        Range(Role.RATED, nom=U.A(10), at=At(Range(Role.LIMITING, max=U.V(250)), Current.AC, Utilisation.AC_1)),
        Range(Role.RATED, nom=U.A(6), at=At(Range(Role.LIMITING, max=U.V(24)), Current.DC, Utilisation.DC_13)),
    ),
)
RELAY = Product("DEMO-RLY-2CO-24DC", "Demo", Relay(contacts=(CO, CO), coil=COIL))
block = RELAY.block

assert [pin.marking for pin in block.pins] == ["A1", "A2", "11", "12", "14", "21", "22", "24"]
assert block.pins[0].role is PinRole.PLUS
links = {link.markings: link for link in block.links}
assert links[("11", "12")].kind is LinkKind.SWITCHED and links[("11", "12")].closed_at_rest is True
assert links[("11", "14")].kind is LinkKind.SWITCHED and links[("11", "14")].closed_at_rest is False
assert COIL.connection is CO.connection

dc = pick(CO.switching, U.V(24), Current.DC, Utilisation.DC_13)
assert dc.found is CO.switching[2]
assert pick(CO.switching, U.V(400), Current.AC, Utilisation.AC_15).issue.code is IssueCode.NO_RATING
assert pick(CO.switching, Current.AC).issue.code is IssueCode.AMBIGUOUS_RATING
assert COIL.voltage[0].tolerance == (Fraction(-3, 20), Fraction(1, 4))
assert check(RELAY) == ()

A contactor with a contact block and an overload relay

Main contacts take the pins 1/L1 2/T1 3/L2 ..., a mirror contact follows as 11 12, and a contact block continues from a number you give. Coil figures are rows under At, and pick finds the apparent power of the held state at 60 Hz.

from elecdef import At, Coil, Contact, ContactBlock, Contactor, Overload, Range, Role, Unit as U, pick
from elecdef.pins import LinkKind
from elecdef.vocab import CoilKind, CoilState, ContactFeature, ContactForm, Current, Reset, TripClass

coil = Coil(
    CoilKind.AC,
    voltage=Range(Role.RATED, nom=U.V(230), at=At(Current.AC, U.HZ(50))),
    apparent_power=(
        Range(Role.RATED, nom=U.VA(70), at=At(CoilState.INRUSH, U.HZ(50))),
        Range(Role.RATED, nom=U.VA(9), at=At(CoilState.HOLD, U.HZ(60))),
    ),
)
main = Contact(ContactForm.MAKE)
mirror = Contact(ContactForm.BREAK, features=frozenset({ContactFeature.MIRROR}))
contactor = Contactor(coil, (main, main, main), (mirror,))
assert [pin.marking for pin in contactor.pins] == [
    "A1", "A2", "1/L1", "2/T1", "3/L2", "4/T2", "5/L3", "6/T3", "11", "12",
]
assert pick(coil.apparent_power, CoilState.HOLD, U.HZ(60)).found.nom == U.VA(9)

contact_block = ContactBlock((Contact(ContactForm.MAKE), Contact(ContactForm.BREAK)), numbered_from=5)
assert [pin.marking for pin in contact_block.pins] == ["53", "54", "61", "62"]

overload = Overload(
    poles=3,
    setting=Range(Role.RATED, min=U.A(7), max=U.A(10)),
    trip_class=TripClass.C10,
    reset=Reset.BOTH,
    phase_failure=True,
    aux=(Contact(ContactForm.BREAK), Contact(ContactForm.MAKE)),
)
assert [pin.marking for pin in overload.pins] == [
    "1/L1", "2/T1", "3/L2", "4/T2", "5/L3", "6/T3", "95", "96", "97", "98",
]
assert {link.markings: link.kind for link in overload.links}[("95", "96")] is LinkKind.SWITCHED

A 16-channel digital input card

One Channel is a template; each ChannelGroup stamps it onto its markings. The channels of both groups are numbered 1 to 16 and share one electrical value by identity.

from dataclasses import replace

from elecdef import At, PlcCard, Range, Role, Unit as U, pick
from elecdef.connections import PUSH_IN
from elecdef.plc import Channel, ChannelGroup, DigitalInput
from elecdef.supply import SupplyInput
from elecdef.vocab import Current, Signal

GROUP = ChannelGroup(
    Channel(Signal.DI, electrical=DigitalInput()),
    markings=tuple(str(n) for n in range(1, 9)),
    common=("1M",),
    isolated=True,
    connection=PUSH_IN,
)
supply = SupplyInput(
    voltage=Range(Role.RATED, nom=U.V(24), at=At(Current.DC)),
    current=(
        Range(Role.RATED, nom=U.A("0.07"), at=At(U.V(24))),
        Range(Role.RATED, nom=U.A("0.11"), at=At(U.V(30))),
    ),
)
card = PlcCard(
    supply=supply,
    groups=(GROUP, replace(GROUP, markings=tuple(str(n) for n in range(9, 17)), common=("2M",))),
)
assert [channel.number for channel in card.channels] == list(range(1, 17))
assert len({id(channel.electrical) for channel in card.channels}) == 1
assert [pin.marking for pin in card.pins] == (
    ["+", "-"] + [str(n) for n in range(1, 9)] + ["1M"] + [str(n) for n in range(9, 17)] + ["2M"]
)
assert pick(card.supply.current, U.V(24)).found.nom == U.A("0.07")

Proposed rows

The pins of a fuse, a breaker, a power supply, a terminal block and a cable are proposed. They are not proven against a source, and the review page lists them. The pages for blocks and cables run them: Fuse gives 1 2 with a protective link, Breaker(poles=2) gives four pins, PowerSupply with an OK contact gives L N PE + - 13 14, and Cable with a screen ends in SH.

from elecdef import Range, Role, TerminalBlock, Unit as U
from elecdef.connections import PUSH_IN, SCREW
from elecdef.terminal import Level
from elecdef.vocab import LevelKind, Potential

terminal = TerminalBlock(
    levels=(Level("1", Potential.L, LevelKind.FEED_THROUGH),),
    sides=(PUSH_IN, SCREW),
)
assert [pin.marking for pin in terminal.pins] == ["1"]
assert len(terminal.sides) == 2