2 Commits
Author SHA1 Message Date
cristhian a95d804f0b fix jogging stepping by holding one velocity instead of re-arming
validate / validate (pull_request) Successful in 58m41s
validate / test (pull_request) Successful in 58m50s
validate / markdownlint (pull_request) Successful in 58m39s
`set_cartesian_velocity` passed `duration=velocity_command_ttl_s` on every
message, so each one re-armed a timed segment and the controller re-planned
the same motion. On a real Lite6 that is not smooth travel: the arm moves in
steps. Reducing how often the velocity is sent does not help — re-arming four
times a second still steps — so it now uses `duration=0`, the SDK's "always
effective, will not stop automatically", and skips a velocity that has not
changed. One command stands until a different one replaces it.

That removes the controller-side expiry, which `_expire_jog` was relying on:
it flipped `_jogging` and reported `ready` without commanding anything,
because the controller's own timer had already stopped the arm. With the
velocity held indefinitely that would leave an arm travelling while the node
reported idle, so `_expire_jog` now queues the stop itself. It goes through
the job queue rather than the driver, since it runs on the reporter thread
and the control thread owns the driver.

`VELOCITY_COMMAND_TTL_S` is unchanged in value and meaning to a caller, but
it is now enforced here rather than by the controller.

Both halves are one commit on purpose: `duration=0` without the stop in
`_expire_jog` is an arm that never stops when its sender dies.

Adds tests/test_driver.py — the driver had no direct coverage, because
FakeArm replaces it everywhere else, which is why this was invisible to the
suite.
2026-09-16 14:27:46 -03:00
cristhian eccd9e3c24 add xArm driver layer ported from littlehand
Brings across the robot layer from monolitic-littlehand-python, which was
written against this same controller. The point of it is that a command
either moved the arm or raised: the SDK returns a code for a motion the
controller quietly refused, wait_move calls that refusal finished, and
arm.error_code is the report stream's stale copy.

So errors are read live, arrival is confirmed rather than assumed, and a
move whose straight-line path leaves the joint envelope is refused before
it is commanded. Failures are typed, because the distinctions decide the
response: an unreachable pose is not an unavailable arm, and a joint past
its limit needs a motion nobody should start without warning the room.

read_report is the deliberate exception to all of it: a report-cache read
that costs no round trip and cannot block, which is the only read that
works while a motion is in flight.
2026-09-12 11:46:54 -03:00