Skip to content

Requirements and the check (bt.select)

What the cell asks of every bill-of-materials line, compared with what the chosen part says — and every static check in one report. botrail derives and compares; it does not choose. See Selecting parts.

req = scene.requirements()                 # bt.select.requirements(scene)
print(req.to_markdown())
req["ur5e/tool"].minimum                   # {"payload_kg": 2.3, "stroke_mm": 150.0}

report = scene.check()                     # bt.select.check(scene)
assert report.ok, report.to_markdown()

select

Requirements derived from the cell, and the selection check.

botrail does not choose parts. It derives what every bill-of-materials line must be able to do — from the cell the part sits in — compares that with what the chosen part says it can do, and reports where the two disagree or where it does not know. Choosing stays with the person, the agent or the vendor; :mod:botrail.catalog finds the real candidates to choose from.

req = scene.requirements()          # one row per BOM line
print(req.to_markdown())
req["tool"].minimum                 # {"payload_kg": 2.3, "stroke_mm": 150.0}
report = scene.check()              # I/O lint + sequences + parts + requirements
assert report.ok, report.to_markdown()

Vocabulary. A requirement names a spec the catalog names too, so a value read from a catalog package (Robot.from_catalog, bt.parts.*(catalog=...), bt.catalog.Product.identify) or typed by hand on set_part(...) lands in the same column. The keys, and the attribute names that answer them (:data:ALIASES):

requirement derived from answered by
payload_kg tool mass + the heaviest part the robot grasps; parts riding a vehicle's deck at start payload_kg
reach_mm the farthest taught target from the base, plus a margin; the table of a machining centre (or the spindle of a lathe) in the cell, through its opening reach_mm
stroke_mm the smallest side of the grasped parts (parallel gripper) stroke_mm, opening_mm
sensing_range_mm a beam sensor's span sensing_range_mm, range_mm, max_range_mm
range_mm a light curtain's span / an area sensor's half-diagonal range_mm, max_range_mm, sensing_range_mm
protective_height_mm a light curtain's post height protective_height_mm, height_mm
scan_fov_deg a lidar's authored sweep angle scan_fov_deg
length_mm, width_mm a conveyor's zone along and across its belt length_mm / width_mm, belt_width_mm
speed_mps a conveyor's belt speed, an axis speed max_speed_mps, speed_max_mps, speed_mps
max_speed_mps a vehicle's travel speed the same
max_climb_mps an aerial vehicle's climb rate the same
max_descent_mps an aerial vehicle's descent rate the same
flight_time_min an aerial vehicle's airborne time per cycle, from the baked timeline (requirements(timeline=tl)) the same
load_kg parts on a conveyor / an axis; robots standing on a pedestal load_kg, capacity_kg, max_load_kg, payload_kg
di do ai ao safe_di safe_do points assigned to an I/O node the node's declared channels

Every requirement is a minimum (>=) unless noted. The derivations are geometric and deterministic — no sizing, no safety evaluation; a value that cannot be derived (a grasped part without mass_kg) is reported as a note, never guessed.

ALIASES module-attribute

ALIASES = {
    "payload_kg": ("payload_kg",),
    "reach_mm": ("reach_mm",),
    "stroke_mm": ("stroke_mm", "opening_mm"),
    "aperture_mm": ("aperture_mm",),
    "grip_force_n": (
        "grip_force_max_n",
        "grip_force_min_n",
    ),
    "sensing_range_mm": (
        "sensing_range_mm",
        "range_mm",
        "max_range_mm",
    ),
    "fov_deg": ("fov_h_deg", "hfov_deg", "fov_deg"),
    "resolution_h_px": ("resolution_h_px",),
    "resolution_v_px": ("resolution_v_px",),
    "max_range_mm": ("max_range_mm",),
    "min_range_mm": ("min_range_mm",),
    "scan_fov_deg": ("scan_fov_deg",),
    "range_mm": (
        "range_mm",
        "max_range_mm",
        "sensing_range_mm",
    ),
    "protective_height_mm": (
        "protective_height_mm",
        "height_mm",
    ),
    "length_mm": ("length_mm",),
    "width_mm": ("width_mm", "belt_width_mm"),
    "speed_mps": (
        "max_speed_mps",
        "speed_max_mps",
        "speed_mps",
    ),
    "max_speed_mps": (
        "max_speed_mps",
        "speed_max_mps",
        "speed_mps",
    ),
    "max_climb_mps": ("max_climb_mps",),
    "max_descent_mps": ("max_descent_mps",),
    "flight_time_min": ("flight_time_min",),
    "load_kg": (
        "load_kg",
        "capacity_kg",
        "max_load_kg",
        "payload_kg",
    ),
    "output_a": ("output_a", "current_a"),
    "di": ("di",),
    "do": ("do",),
    "ai": ("ai",),
    "ao": ("ao",),
    "safe_di": ("safe_di",),
    "safe_do": ("safe_do",),
}

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

CheckReport

Every static check of a cell in one list — what botrail check prints.

Finding

Finding(severity: 'str', code: 'str', message: 'str', target: 'Optional[str]' = None)

Requirement

One thing a BOM line must be able to do, and whether its part can.

basis class-attribute

basis = ''

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

op class-attribute

op = '>='

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

status class-attribute

status = 'unknown'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

Requirements

What the cell asks of every BOM line — the result of :func:requirements.

ok property

ok

No line falls short of what the cell asks (unknowns do not count).

findings

findings() -> list[Finding]

spec_short (error), spec_unknown (warning) and requirement_incomplete (info), in row order. I/O nodes get no spec findings — the I/O report already lints their capacity.

Row

One bill-of-materials line with what the cell asks of it.

minimum property

minimum

The >= requirements as {key: value} — what bt.catalog.search(**row.minimum) takes.

status property

status

ok | short | unknown | unidentified | none (nothing derived).

check

check(
    scene,
    *,
    sequences: Optional[list[str]] = None,
    timeline=None,
) -> CheckReport

Every static check in one report: the I/O lint, each sequence walked for dangling references, unidentified equipment lines (with what the cell asks of them) and the requirement comparison. timeline (a baked cycle) adds the cycle-fact requirements — an aerial vehicle's flight time. Errors make ok false; botrail check prints exactly this.

requirements

requirements(
    scene,
    *,
    sequences: Optional[list[str]] = None,
    margin: float = 0.1,
    timeline=None,
) -> Requirements

Derive what every BOM line must be able to do from the cell it is in, and compare it with what the chosen part says (its catalog specs or the attributes typed on set_part).

sequences limits the programs whose grasps and I/O points are counted (default: all). margin is added to the reach and flight-time requirements (0.1 = 10 %). timeline is an optional baked simulate_sequences result: cycle facts only it can supply — an aerial vehicle's airborne time — are derived from it, and left as a note when it is absent. Nothing is chosen and nothing is sized: a number the cell cannot supply (a grasped part with no mass_kg) becomes a note, not a guess.