Skip to main content

Module ports

Module ports 

Source
Expand description

PORTS — a part’s declared connection points, as PART DATA rather than history features.

§The block

A part document carries ports as a sibling of features, beside the eCAD symbol and pads blocks:

"ports": [
  { "name": "J1", "purpose": "wiring",
    "points": [ { "name": "VCC", "transform": { … }, "pointRef": "Body_PZ" } ] }
]

A port is a NAMED GROUP of connection points with one purpose — pcb, wiring, piping, or anything else a later slice invents; the kernel carries the string and never enumerates it. One part may declare several ports with different purposes (a valve with a piping port and a wiring port for its pressure sensor). None of this is a feature: there is no history row, no dialog, no icon and no schema-catalogue entry.

§Addressing

A connection point is addressed [<occurrence chain>:]<port>.<point> — J1.VCC in its own part, ACOMP3:J1.VCC once placed, ACOMP7:ACOMP1:J1.VCC at depth. : is the occurrence namespace the assembly lane already speaks (component::namespaced, peeled by split_component_namespace); . separates the port group from the point. The NAME is the identity — there is no second hidden id — so :, . and / are refused inside a port or point name (/ because the harness routing graph keys its nodes {address}/A).

The address is the key in SceneMap::ports, so every existing consumer — wireHarness from/to, a spline anchor’s attachment.portRef, a sketch on the point’s frame — refers to a connection point exactly as it referred to a PORT feature id before, only with a name a user can read.

§One symbol UNIT per port group

A symbol pin binds to a connection point BY NAME (part_pins.rs). With several port groups J1.1 and J2.1 are both “point 1”, so the name alone cannot say which. The part’s SYMBOL says it instead: eCAD’s Symbol already carries unit_count, KiCad’s own model for a multi-unit device, and ONE SYMBOL UNIT MAPS TO ONE PORT GROUP. Pin 1 of unit 1 is J1.1 and pin 1 of unit 2 is J2.1, so pin labels stay short and two identical headers on one part are ordinary rather than a collision.

unit_map states the mapping:

  • a group may name its unit with symbolUnit (1-based), and that is the mapping, written down;
  • a document with ONE group and no symbolUnit anywhere maps that group to unit 1. This is the ordinary single-group part, and it declares nothing;
  • a document with SEVERAL groups binds only the groups that name a unit. Position is deliberately NOT a fallback here: a piping component declaring its piping port first and the wiring port of its pressure sensor second would otherwise bind the sensor’s pins into the piping port. A group with no unit is not a schematic thing at all, which is exactly right for that piping port.

Disagreements are REPORTED, never refused, and each names what it concerns: a unit with no group (UnitProblem::UnitWithoutGroup — its pins can bind to nothing), a symbolUnit past the symbol’s unit_count, and two groups claiming one unit. unit_count 0 reads as 1, which is what a symbol written before units existed carries.

§A placement is an OFFSET IN A SEAT

A point may name geometry that places it: pointRef locates it, directionRef turns it. Those two used to REPLACE the typed placement, which made picking a reference the end of the conversation — there was nothing left to adjust, and the transform beside them was dead data.

They now supply a SEAT (seat_of): a Frame whose origin is where the reference puts the point and whose x_axis is the direction it gives (x, because a point’s direction is its rotation applied to +X, the convention every feature transform reads — see Frame::from_origin_direction). The point’s own transform is then read IN that frame: position along the seat’s three axes, rotationEuler composed onto its basis. So “on this face, 2 mm off it, turned 30 degrees” is one point with a reference and a small offset, and moving the face carries all of it.

A point that names NOTHING has the WORLD seat — origin at the part origin, the world axes — in which an offset is an absolute placement. That is what a document written before the seat existed says, and it reads exactly as it did. A reference that seats the origin but implies no direction (a vertex, a bare scene point) keeps the world axes too.

What a reference may BE is the kernel’s own analytic vocabulary (crate::SelectionGeometry, the same one an assembly constraint and a PMI annotation measure against): a planar face seats on its plane along its normal, a cylindrical face on its axis, a CIRCULAR edge at its CENTRE along its axis, a straight edge at its chord midpoint, a vertex ({solid}@x,y,z) at the vertex. A face or edge the analytic lane cannot describe falls back to the parametric midpoint and the chord this module always read.

§Resolved AFTER history

finish_history_run runs at the tail of every history run, after the assembly solve and before the wire-harness router. That is deliberate: a port point may reference ANY geometry in the part without a history-ordering rule, because by the time it resolves the whole part is built. The tail is authoritative: it resolves every point and overwrites whatever was there.

There is ONE deviation, and it is a fixed two-phase, not an ordering rule. seed_history_run publishes, BEFORE the walk, every point whose placement is ALREADY FULLY DETERMINED — no pointRef, no directionRef, no mapsTo, so nothing the walk builds can move it. A SPLINE in the same document can then attach to it, and a sketch can sit on its frame, which is the whole standalone-harness workflow. A point that DOES reference geometry stays tail-only: a same-document spline attached to one reports the address unresolved and the harness lists it as a segment problem, by name, rather than quietly routing to a stale placement. In an assembly the question does not arise — a placed component’s points arrive with the component, during the walk.

§Assemblies declare DOWN

An assembly declares its own ports and maps each point to a descendant’s point with mapsTo: J1.VCC -> ACOMP2:J5.VCC. The mapped point takes the target’s resolved placement, so the interface moves with the child. Depth is unbounded because the target is just another address.

§Encapsulation

A PCB assembly is a BOUNDARY: its child components are not directly wireable, and the only way to reach inside is a port the board itself declares. An ordinary mechanical assembly is TRANSPARENT — an enclosure holding a motor and a sensor does not hide them.

The mark is document_is_boundary: the explicit portBoundary flag when the document sets one, else derived — a document carrying a pcb block is a board and therefore a boundary. Boundary is INDEPENDENT of purpose: a terminal block on a board carries wiring-purpose points and must still be declared at board level before anything may wire to it, so purpose filters what KIND of connection a point is, never whether it is reachable.

The filter is applied at EXPORT, in export_ports: what a part’s library entry carries is what a parent can see. A boundary part exports only the points it declares itself; a transparent part exports everything its run published, descendants included. attach_component_ports then namespaces whatever arrived, and arbitrary depth falls out of the address.

Structs§

PortDeclaration
One declared port: a named group of connection points with one purpose.
PortPoint
One connection point of a declared port.
PortPointRow
One resolved connection point, as the tail publishes it.
PortSeat
A Frame as the ports REPORT carries it: four plain triples rather than Vec3s, because a report is compared and serialized (a Vec3 carries no PartialEq, by choice, and this is the shape the app and the verifier read).
PortsOutcome
What the ports tail produced: its report, and the appended result the display pipeline consumes.
PortsReport
What the ports tail resolved, and everything about the block that does not hold — a name it refused, a reference that resolved to nothing, a mapping whose target is not in the scene. Never fatal: a problem is reported and the point still publishes at whatever placement could be worked out, so nothing downstream vanishes (the same contract rule the features follow).

Enums§

UnitProblem
Something about the unit-to-group mapping that does not hold. Never fatal: what maps still binds, and this says what did not.

Constants§

ADDRESS_SEPARATOR
What separates a port group from a point in an address.
BOUNDARY_KEY
The part document’s top-level key for the explicit encapsulation flag.
DEFAULT_DISPLAY_LENGTH
Default length of the drawn point line.
DEFAULT_EXTENSION
Default straight run a wire keeps from a connection point before it may bend.
PORTS_BLOCK
The part document’s top-level key for its declared ports.
PORTS_FEATURE_ID
The id of the tail’s appended result (never a history feature). The scene entities a declared point publishes ride it, so the display pipeline draws and picks a connection point exactly as it draws a feature’s curves.
PORTS_FEATURE_TYPE
The type of the appended tail result.
RESERVED
Characters a port or point name may not contain: the occurrence namespace separator, the address separator, and the harness graph’s side separator.

Functions§

address
The address of a point within its own part: {port}.{point}.
document_is_boundary
Whether a DOCUMENT hides its children behind its declared ports.
export_ports
What a part’s library entry carries out of its own run — the encapsulation filter, applied ONCE, here.
finish_history_run
Resolve the request’s ports block against the FINISHED scene and publish every point. Runs after the assembly solve (so a mapped point reads its child’s posed placement) and before the wire-harness router (which routes over what this published). Empty when the document declares no ports.
place_scene_port
The same entities written straight into a scene — the ports tail and the component re-pose lane, which moves a placed part’s points without re-running its feature.
port_line
The drawn line for a connection point: a termination’s wire leaves along the direction; a waypoint’s wire passes through, so its line straddles the base point.
publish_port
Publish one connection point’s scene entities under address: the record (ports[address]), its base point ({address}:Base), its axis and its frame (both address) and its drawn line ({address}:PortLine). A sketch on the frame is a connector face square to the wire.
request_is_boundary
The same question for a parsed request (the tail’s lane).
resolve_direction
A directionRef’s unit direction, or None when the name resolves to nothing that implies one. The WAYPOINT feature’s lane — a waypoint has no seat (it is a feature with its own transform), so it takes the direction alone and lists an unresolved name in its own report.
seat_of
The FRAME a connection point’s placement is an offset in.
seat_point
A local offset carried into world space by seat.
seat_vector
A local DIRECTION carried into world space by seat (no translation).
seated_direction
The world direction a point with this seat and this rotation faces: the rotation applied to +X (every feature transform’s convention), carried through the seat’s basis. With the world seat this is the rotated +X itself.
seed_history_run
Publish, BEFORE the history walk, every declared point whose placement is already fully determined — see the module doc. Returns each published scene name with the CONTENT VERSION of the declaration behind it, so the incremental cache invalidates a feature that consumed the point when the declaration moves (without this a spline attached to a point would replay from cache at the point’s old placement).
split_address
Split a PART-LOCAL address into (port, point). None when it carries no separator — a WAYPOINT feature id, for one, which is an address in its own right and belongs to no port.
unit_map
Which port group each symbol unit binds its pins into, and everything about the mapping that does not hold. The map is unit (1-based) -> index into declarations``; a unit absent from it binds no pins.
unseat_point
A world position read back as an offset in seat — the inverse of seat_point, which is what a gizmo drag writes. The basis is orthonormal, so the inverse is the transpose.
unseat_vector
A world direction read back in seat’s basis.
world_seat
The seat of a point that names no reference: the world frame, in which an offset is an absolute placement.