Skip to content

Equipment connections and supply capacity

Declare the interfaces each piece of equipment needs, connect them, and check the resulting supply loads and compatibility. Ports refer to existing Scene equipment or I/O nodes. They survive project save/load and generated Python, and do not add BOM rows or change the operating sequence.

import botrail as bt

scene = bt.Scene()
bt.parts.power_supply(scene, "PS24", (0, 0, 0), size=(.1, .1, .2),
                      model="Example supply", output_v=24, output_a=2)
scene.add_beam_sensor("eye", frm=(.2, 0, .1), to=(.6, 0, .1))
scene.set_part("eye", model="Example sensor", voltage_v=24, current_a=.1)

bt.connections.port(scene, "PS24.out", "PS24", "power", "supply",
                    terminal="X1:+/-", reference="E-001")
bt.connections.port(scene, "eye.power", "eye", "power", "load")
bt.connections.connect(scene, "PS24.out", "eye.power", cable="W-01")

result = bt.connections.report(scene)
print(result.to_markdown())
result.save("connections.csv")
result.save("power.csv", table="power")

This illustrative supply has 0.1 A of connected demand against 2 A of capacity. Another supply receives only its own connected loads. An unconnected component never enters either subtotal.

Declaring interfaces

port(scene, name, target, medium, role, ...) defines a named endpoint. Reusing a name replaces its declaration. target_kind= disambiguates resident names, using the same kinds as set_part. Equipment may have several endpoints: for example, separate control and drive power inputs.

medium roles specifications
power supplyload voltage_v or voltage_min_v / voltage_max_v; supply capacity_a, load current_a
pneumatic supplyload pressure_bar or pressure_min_bar / pressure_max_bar; supply capacity_l_min, load flow_l_min; both flow_reference
signal outputinput signal_type: digital, safe_digital, analog, word; voltage as above; digital logic: pnp, npn
network peerpeer protocol, compared without case sensitivity

Consumer, signal and network ports require a connection by default. Supply ports are optional by default. Set required=False for a spare interface. Supply/output fan-out is supported; multiple connections terminating at a load, input or network socket fail. Model separate sockets as separate ports.

terminal, cable and reference are drawing/specification references. They do not create terminals or cables as equipment. Use disconnect(scene, name) to remove a connection. Removing a port with remove_port, or deleting equipment, retains dangling connections so the report can name the missing references. restore(scene, plan) accepts the project's typed connection_plan object, including unresolved references.

Where specifications come from

An explicit port value takes precedence. Otherwise a port can read the exact Part pinned to its target when the target has qty=1 and only one port of that medium and role. Power supplies read output_v / output_a as voltage_v / capacity_a; other attributes use the names above. Editing the Part changes the next report. A merged BOM row is never used to infer an individual endpoint's consumption.

For quantities greater than one or multiple ports of the same medium/role, declare values on each port. These are endpoint totals, without quantity multiplication. Several supply ports on one equipment target leave a shared capacity question unknown; the current model cannot establish whether their ratings are independent. A common source port with fan-out permits checking its combined load.

The JSON report records each resolved value and its origin: port, part, io_channel or unknown. No catalog download or current-product lookup occurs during checking. Non-numeric, negative and non-finite Part values remain unknown. Invalid port field names, units-as-strings and numeric values are rejected when authored.

Reusing an I/O assignment

Declare the field interface and reference the existing assignment from the controller port. Reassigning DI0 to DI1 updates the next connection report.

scene.add_io_node("PLC", channels=bt.io.di16(voltage=24, logic="pnp"))
scene.declare_io("eye", role="input", kind="di")
scene.bind_input("eye", "PLC", "DI0")

bt.connections.port(scene, "eye.output", "eye", "signal", "output",
                    signal_type="digital", voltage_v=24, logic="pnp")
bt.connections.port(scene, "PLC.eye", "PLC", "signal", "input",
                    io={"point": "eye", "direction": "input", "node": "PLC"})
bt.connections.connect(scene, "eye.output", "PLC.eye")

Channel kind, voltage and logic remain authoritative. Conflicting port values fail. Missing assignments remain unresolved; two declared ports cannot alias the same physical channel. Where an existing I/O point names a sensor/device, connecting a different sensor/device fails. An existing I/O uplink also needs declared network endpoints: its bus label alone does not specify both devices' protocol capabilities.

Reading the result

The report includes one requirements row per port, connection rows, supply capacity rows and individual checks. ready requires the declared checks to be resolved. Empty declarations are not_run. Part attributes revealing power/air consumption or power supply capacity also expose missing interface declarations. Other omitted interfaces cannot be inferred automatically.

condition result
Missing equipment/port, wrong medium/direction, multiple feeds or incompatible known specifications fail
Required port unconnected, missing specs or unknown connected consumption unknown
Optional unconnected port not_applicable
Complete compatible declared interfaces and sufficient capacity pass

The entire supply voltage/pressure range must fit within the accepted load range. A nominal-only value means that exact declared value; no tolerance is invented. A partly specified range stays unknown. Word signals do not require electrical voltage checks.

Power budgets sum only directly connected loads' steady current_a. known_subtotal, known_loads, total_loads and missing_loads keep partial information explicit. All loads unknown yields a null subtotal; explicit zero remains known. A known subtotal exceeding capacity fails even if other loads are unknown. Air flow contributes only when both endpoints state the same flow_reference conditions; no conversion is performed.

These checks cover declared steady interface requirements. Protection, cable sizing, AC/DC and phase compatibility, transient/inrush behaviour, demand factors, pneumatic dynamics, analog transfer ranges, network timing and safety performance require separate evaluations.

Review and deliverables

scene.check() includes physical failures as errors and unknowns as warnings. bt.review(scene, stage="design") also treats unresolved physical connections as review blockers. Power supply specification checks use connected budgets; the previous whole-BOM current requirement is removed.

botrail connections examples/engineering/cell_connections_demo.py \
  --report connections.md --csv connections.csv --power power.csv
botrail export examples/engineering/cell_connections_demo.py \
  --out deliverables/connections-r1 --project --python --connections --report

The example includes separate 24 V / 48 V supplies, a sensor assignment, air service and a network uplink. Its ratings are illustrative. Calling build(unknown_valve_current=True) leaves one load unknown: the 24 V subtotal is 0.1 A with one missing load; the 48 V budget stays complete at 2 A.

The CLI exits 0 for resolved declared requirements, 1 for unresolved findings, and 2 for invalid input/output arguments. --markdown prints Markdown; otherwise stdout is JSON. No simulation bake is needed.

Batch export's --connections (included in --all) writes <name>_connections.csv, .md, .json and <name>_power.csv. The main batch report also includes the connection results. These use the same snapshot and manifest revision as the other files; changing a connection invalidates verification against the old package. Physical requirements always cover the whole cell, even when a subset of operating programs is selected. Direct scene.cell_report() continues to describe simulation results; use the batch export or bt.connections.report() for the physical connection tables.