Skip to content

The botrail command

Installed with the package: bake, check and export a cell without writing Python. A cell argument is either a .botrail project or a Python file — the file is run (not as __main__) and its top-level scene is taken, or its build() / build_cell() / build_scene() is called and must return a Scene. Everything prints JSON unless said otherwise. Exit codes: 0 ok, 1 findings with errors or a failed bake / stalled scenario, 2 the cell could not be loaded or the arguments were wrong ({"ok": false, "error": "..."} on stdout).

botrail check <cell>

Loads the cell, derives the I/O map and lints it, walks every sequence, lists unidentified BOM lines (with what the cell asks of them), compares every line's requirements with what its part says, and counts what is in the scene — the same list as scene.check():

{
  "ok": true,
  "cell": "cell.py",
  "robots": ["simple_arm"],
  "counts": {"obstacles": 19, "frames": 0, "sensors": 1, "devices": 1,
             "sequences": 1, "scenarios": 2, "parts": 8, "bom_rows": 8},
  "findings": [{"severity": "info", "code": "unidentified_part", "target": "eye",
                "message": "eye (sensor.photoelectric) has no maker, model or catalog reference — needs sensing_range_mm >= 200"}],
  "requirements": {"lines": 8, "short": 0, "unknown": 2, "unidentified": 1}
}

findings[].severity is error / warning / info (the I/O lint codes, sequence for a program that cannot be walked, unidentified_part, spec_short when a part's stated spec falls short of what the cell asks, spec_unknown when an identified part states no value, requirement_incomplete when a requirement could not be derived — a grasped part with no mass_kg — and the placement checks: controller_unplaced (info) for an arm's controller nobody has put on the floor plan, service_space (warning) when something stands in the service space in front of a control cabinet's or controller's door); exit 1 when any is an error. requirements counts the BOM lines by the outcome of that comparison.

botrail simulate <cell>

Bakes the sequences — all together, or the --sequence NAME set — and prints the cell report.

option meaning
--sequence NAME (repeatable) programs to bake together (default: all)
--scenarios bake the whole scenario matrix; the report gets the table and the cycles; exit 1 if any scenario stalled
--max-duration S bake time limit (default 120)
--clearance-dt S / --no-clearance clearance re-scan step, or skip it
--report PATH also write the report (.json or .md)
--usd PATH (--fps) also write the first baked cycle as USD
--title report title
--markdown print Markdown instead of JSON

botrail export <cell> --out DIR

Writes the document set — pick with --project --python --bom --io --topology --plc --interlocks --layout --usd --script --report, or --all (the default when nothing is picked). Files are named after the cell (--name overrides the stem): <stem>.botrail, <stem>.py, <stem>_bom.csv|.md, <stem>_io.csv, <stem>_topology.mmd, <stem>.plcopen.xml, <stem>_interlocks.md|.csv (the interlock table), <stem>_layout.svg|.dxf (--scale for the SVG), <stem>_<cycle>.usda per baked cycle (--fps), <stem>.script for one program or <stem>_<program>.script for each of several programs, and <stem>_report.md|.json last, with the digests of everything written before it. --sequence scopes every program-dependent document as well as the bake. --manifest keeps <stem>_manifest.json — the file hashes and the conditions the bake ran under.

DIR is created when missing; a set already there is overwritten file by file and anything else in the directory is left alone, so the command can be re-run in place. The files are generated from one isolated cell snapshot in a temporary directory and moved over only after every exporter succeeds. Besides the simulate bake options, accepts --dt (scan interval, default 0.01 s) and --plan-resolution (planner stride, default 0.05). Bakes are kinematic. A program that cannot compile to the robot dialect (a 7-axis arm, an uncovered branch) is a warning on stderr, not an export failure.

botrail schema [--out FILE]

The JSON Schema (draft 2020-12) of the .botrail project file. See below.

botrail studio <cell> [--port N]

Opens the cell in the studio.

The project schema

botrail schema and bt.project_schema() return the schema of the .botrail file, generated from the Rust types Scene.load_project reads — so a project that validates is a project that loads, and its descriptions are the types' doc comments. A copy ships with the docs: project.schema.json.

import json, jsonschema, botrail as bt
schema = json.loads(bt.project_schema())
jsonschema.Draft202012Validator(schema).validate(json.load(open("cell.botrail")))

The schema describes the file; the loader also checks what a schema cannot (a binding onto a node the file does not have, a part pinned to a missing obstacle), so botrail check is the definitive test.