Command line

The user-facing behaviour of each subcommand is documented in Command line. This page covers the module itself, for anyone embedding or extending it.

Command-line tools.

scan is the one that earns its place: it tells you whether anything is on the bus, which ids answer, which mode they are in, and which arbitration id a MIT driver replies on - the question the manual leaves ambiguous. Run it before anything else.

Nothing here runs a privileged command. doctor prints the interface bring-up lines for you to run yourself.

class Sighting(
motor_id: int,
mit_replies: int = 0,
servo_status: int = 0,
servo_acks: int = 0,
bootloader: int = 0,
mit_arbitration_ids: ~collections.Counter[int] = <factory>,
last_mit: ~cubemarspycan.codec.mit.MitFeedback | None = None,
last_servo: ~cubemarspycan.codec.servo_can.ServoFeedback | None = None,
)[source]

Bases: object

What one motor id looked like on the bus.

property mode: str

What this sighting looks like: "servo", "MIT" or "unknown".

Inferred from which replies arrived, not asked for: there is no documented frame that queries a driver’s mode.

class Scanner(spec: MotorSpec)[source]

Bases: object

A bus endpoint that accepts everything and classifies it.

accepts(frame: Frame) bool[source]

Accept everything. A scanner is looking for whatever is out there.

Runs on the receive thread. The opposite of a motor endpoint, which filters strictly - here an unmatched frame is the interesting case.

on_frame(frame: Frame, rx_monotonic: float) None[source]

Classify one frame into a per-id sighting. Runs on the receive thread.

Tries both framings and records what fits; never raises, and decodes nothing it is not sure of.

cmd_scan(args: Namespace) int[source]

Find motors on the bus and report what each one looks like.

Touches the bus. With --poke N it sends zero-gain, zero-torque MIT commands to ids 1..N, because a MIT driver only replies when commanded - so a purely passive scan of a MIT-mode motor finds nothing. Those frames produce no motion, but they do leave the driver in MIT mode.

Returns 0 when at least one motor answered, 1 when nothing did - with the candidate causes ranked, since “no frames at all” and “frames from the wrong id” have different fixes.

cmd_monitor(args: Namespace) int[source]

Print one motor’s state continuously until interrupted.

Touches the bus. Sends zero-gain, zero-torque frames, which produce no motion, so it is safe first contact with a powered motor and is what bench step B1 uses. Takes --mode {mit,servo}.

Returns 0 on a clean interrupt.

cmd_jog(args: Namespace) int[source]

Drive the motor to a position, gently, with everything printed first.

cmd_dump_spec(args: Namespace) int[source]

Print a motor spec and where every constant in it came from.

Does not touch the bus - it reads the registry only, so it works with no hardware and no adapter. With no motor named, lists the models and their MIT field ranges.

Returns 0, or 1 for an unknown model name.

cmd_doctor(args: Namespace) int[source]

Check the environment and print the CAN bring-up commands.

Does not touch the bus, and never runs a privileged command: it prints the ip link lines for you to run. Reports the Python and python-can versions, which optional backends are installed, and for each interface its link state, bitrate and controller error state. A link state of ? means neither sysfs nor ip could be consulted - deliberately distinct from down.

Returns 0.

build_parser() ArgumentParser[source]

Build the full argument parser, with every subcommand attached.

Public because tests/test_examples_and_docs.py introspects it to assert the README documents every subcommand - so the parser is the single source of truth for what the CLI offers.

main(argv: list[str] | None = None) int[source]

Entry point for the cubemars script. Returns the process exit code.

Turns any CubemarsError into a one-line message on stderr and a non-zero code, rather than a traceback: these are operating conditions - a missing interface, an unknown model, a motor that will not answer - not bugs.