Context
Arm.get_status_now() (src/litearm/arm.py:1301) sends GET_STATUS 0x40 and waits for a conclusion. Its completion predicate is _done (src/litearm/arm.py:1321):
def _done(a) -> bool:
return bool(a._queues.get(err_key)) or a.status_seq != seq0
The two halves are independent sources. a._queues.get(err_key) is the reply to our 0x40. a.status_seq counts the firmware's passive 100 Hz status stream, which keeps arriving no matter what we asked for.
Expected
When the firmware refuses 0x40, the call raises. err_key exists for exactly that case, and the comment on _done says so: "本命令被拒也算有结论".
Actual
Whichever half becomes true first ends the wait, and the code then decides from the error queue alone (src/litearm/arm.py:1330):
self._pump_until(timeout, _done, "get_status_now")
errq = a._queues.get(err_key)
if errq:
raise _err_from(p, "get_status_now: ")
st = a.state
if st is None:
raise MotionTimeoutError(...)
return self._msg(st, P.RSP_STATUS)
A passive status frame that lands before our ERR{0x40,...} satisfies a.status_seq != seq0, so _pump_until returns with the error queue still empty. a.state is not None, because the passive stream has been filling it all along. The call returns that state as a success and the refusal disappears. The caller cannot tell its command was rejected.
Reproduce
I found this while porting the client to C++ and measured it there; the C++ port inherits these semantics deliberately, so _done and the block above are the same shape.
Measured against a fake transport, varying only how many status frames are delivered during the call:
- zero status frames delivered: 150/150 raised correctly;
- exactly one status frame delivered: 26 to 35 out of 150 returned a successful result instead.
I have not re-run this against the Python implementation itself. The experiment is a C++ harness and is not in any repository, so treat the numbers as an indication of the window's size rather than a figure reproducible from main.
The existing offline tests do not cover it: they drive a fake whose status stream they control, so a status frame never lands inside that window by accident.
Context
Arm.get_status_now()(src/litearm/arm.py:1301) sendsGET_STATUS 0x40and waits for a conclusion. Its completion predicate is_done(src/litearm/arm.py:1321):The two halves are independent sources.
a._queues.get(err_key)is the reply to our0x40.a.status_seqcounts the firmware's passive 100 Hz status stream, which keeps arriving no matter what we asked for.Expected
When the firmware refuses
0x40, the call raises.err_keyexists for exactly that case, and the comment on_donesays so: "本命令被拒也算有结论".Actual
Whichever half becomes true first ends the wait, and the code then decides from the error queue alone (
src/litearm/arm.py:1330):A passive status frame that lands before our
ERR{0x40,...}satisfiesa.status_seq != seq0, so_pump_untilreturns with the error queue still empty.a.stateis notNone, because the passive stream has been filling it all along. The call returns that state as a success and the refusal disappears. The caller cannot tell its command was rejected.Reproduce
I found this while porting the client to C++ and measured it there; the C++ port inherits these semantics deliberately, so
_doneand the block above are the same shape.Measured against a fake transport, varying only how many status frames are delivered during the call:
I have not re-run this against the Python implementation itself. The experiment is a C++ harness and is not in any repository, so treat the numbers as an indication of the window's size rather than a figure reproducible from
main.The existing offline tests do not cover it: they drive a fake whose status stream they control, so a status frame never lands inside that window by accident.