Skip to content

Equipment connections

The I/O map is the signal side of a cell: which contact lands on which channel. The utilities are the other side — which supply feeds which load, which valve feeds which actuator, which controller input reads which sensor — and most cells never have to say it: scene.requirements() already sums the current_a of the parts at each supply's voltage. When a cell does have to say it (two 24 V supplies, a drive on its own 48 V unit, an air main shared by several grippers), the connection plan is where: ports declared on existing equipment, connections between them, and a report that checks the pairing.

import botrail as bt

cx = bt.connections

cx.port(scene, "24V", "psu", "power", "supply")                 # capacity from the part: output_v / output_a
cx.port(scene, "eye.power", "eye", "power", "load")             # demand from the part: voltage_v / current_a
cx.port(scene, "air", "valve", "pneumatic", "supply", pressure_bar=6, capacity_l_min=100, flow_reference="ANR")
cx.port(scene, "grip.air", "grip", "pneumatic", "load", pressure_min_bar=4, pressure_max_bar=7,
        flow_l_min=20, flow_reference="ANR")

cx.connect(scene, "24V", "eye.power", name="W1", cable="CBL-001", reference="E-001")
cx.connect(scene, "air", "grip.air", name="P1")

report = cx.report(scene)
assert report.ready, report.to_markdown()
report.save("connections.md")

Ports describe declared interfaces; they do not run the cell. Nothing here changes a bake, a derived I/O point or a BOM line.

Ports

port defines (or replaces) a named interface on a resident that already exists — a robot, device, sensor, I/O node, obstacle or group, found by name like set_part finds its target (target_kind= disambiguates). A port has a medium and a role:

medium roles specs
power supply / load voltage_v (or voltage_min_v / voltage_max_v), capacity_a on a supply, current_a on a load
pneumatic supply / load pressure_bar (or min / max), capacity_l_min on a supply, flow_l_min on a load, flow_reference (the reference conditions both numbers are stated at)
signal output / input signal_type (digital, safe_digital, analog, word), a voltage range, logic (pnp / npn)
network peer protocol

Values are the total at that endpoint — a load's whole steady demand, a supply's whole rating. Nothing is converted (no W to A, no diversity factor), and a value nobody states stays unknown. A port with a single counterpart on a qty=1 resident inherits what its part already says — voltage_v and current_a on a load, output_v and output_a on a power supply — so the two lines in the example above carry no numbers of their own. Every resolved value records where it came from.

Supply ports are optional by default; a load, input or peer is required and reports unknown until something connects to it. terminal= and reference= carry drawing labels and never mean a channel address. A signal port on an I/O node may point at an existing assignment of the I/O map — io={"point": "eye", "direction": "input", "node": "PLC1"} — and the report then checks that the point still derives, that it is bound, and that the channel's kind and electrical data agree with the port.

Connections

connect joins two declared ports. The name defaults to "source -> target"; reusing a name replaces that link. Incompatible pairs (two supplies, a signal into a power port) are kept and reported, like a wrong I/O assignment, rather than rejected on the spot — the report is where the cell is reviewed. cable= is a reference to the cable this connection runs on (a part number, an ECAD id); it does not add a BOM line, and no length is computed from it.

disconnect removes a link; remove_port removes a port but keeps the links that named it, so a dangling reference shows up in the report instead of vanishing. The whole plan rides the .botrail project and comes back from generate_python as a bt.connections.restore call.

The report

report checks the whole cell, independent of what was baked, and returns a ConnectionReport with four tables — the ports with their resolved specs, the connections, the supplies with their known subtotals, and the checks — rendered as Markdown, JSON or CSV (to_markdown(), to_json(), to_csv(table="connections" | "power"), save(path)). Each check has an id, a target, a status and the basis it was decided on:

status meaning
pass the declared values agree
fail they contradict: a supply range outside what the load accepts, two supplies into one load, a missing port or resident, known demand over a supply's capacity, a part whose I/O channel disagrees with its port
unknown something needed is not declared — a load without current_a, a required port nobody connected, a part that declares output_a or current_a but has no port, a controller uplink with no network ports
not_run / not_applicable no requirements declared at all / an optional port left unconnected

ready is true when nothing failed or stayed unknown. It means the declared requirements are resolved; it is not an installation approval. What the report does not evaluate is stated on every table it prints: electrical protection, cable sizing, pressure drop and dynamic behaviour are outside it, and the connection plan is its own report — it is not part of scene.check().

From the plan to the wiring

A port says what an interface is; give it a place (at= the resident and offset it sits on, exit= the direction the line leaves it) and a route can run between two ports — the physical line, its spec, its way through ducts and clamps — with its length, BOM row and checks derived from where the equipment stands. A connection whose ports both have a place and that no route carries is the wiring to-do (route_unrouted). The plan stays the connection table; the route is the run of one of its lines.

A port's place can come from the product itself: a catalog pack that declares ports: (where each line plugs in, which way it leaves, how much stays straight) answers bt.connections.ports_of(scene, target), and port(scene, name, target, from_catalog="robot_cable") takes that place, exit and straight length — and the port's medium and role — as defaults.