Context
Arm.connect() (src/litearm/arm.py:883) builds the session and then performs one handshake:
# 握手: 固件版本
self._raw_write(P.CMD_GET_FIRMWARE)
try:
_, verp = self._a.expect(P.RSP_FIRMWARE, 1.5, "get_firmware",
echo_cmd=P.CMD_GET_FIRMWARE)
self.firmware = verp.decode(errors="replace").strip()
except Exception:
self.close()
raise
One write, one 1.5 s window, then close() and re-raise. There is no retry anywhere in the function.
Expected
After a USB re-enumeration the first connect() succeeds, the same as any other connect().
Actual
After the board re-enumerates — re-plugging the cable, or re-attaching USB passthrough to a VM — the first handshake goes unanswered and connect() raises. Calling it again a second or two later always works.
Measured on hardware: across four re-plugs in one session, the first connection failed every time and every retry succeeded. It never happened on a link that had stayed up.
The cost is not the retry itself, it is the diagnosis. The failure presents as a firmware that is not answering, so the natural response is to check the cable, the permissions and the firmware — none of which is the problem. connect() has already established that the port opened and the reader thread is running; what is missing is only that the firmware needed a moment after enumeration.
Reproduce
On a board where the USB link has just been re-established:
arm = litearm.Arm()
try:
arm.connect() # raises: get_firmware 无应答
except litearm.MotionTimeoutError:
time.sleep(1.5)
arm.connect() # succeeds
I did not run this against the Python implementation; it is how the C++ port behaves, and the C++ port kept this function's structure on purpose so the two stay comparable.
Suggested direction
A short bounded retry of the handshake inside connect() — the port is already open at that point, so the retry is cheap and has no side effects. The sub-connect() behaviour is otherwise correct: FirmwareMismatchError and the version checks should not be retried, only the missing reply.
Context
Arm.connect()(src/litearm/arm.py:883) builds the session and then performs one handshake:One write, one 1.5 s window, then
close()and re-raise. There is no retry anywhere in the function.Expected
After a USB re-enumeration the first
connect()succeeds, the same as any otherconnect().Actual
After the board re-enumerates — re-plugging the cable, or re-attaching USB passthrough to a VM — the first handshake goes unanswered and
connect()raises. Calling it again a second or two later always works.Measured on hardware: across four re-plugs in one session, the first connection failed every time and every retry succeeded. It never happened on a link that had stayed up.
The cost is not the retry itself, it is the diagnosis. The failure presents as a firmware that is not answering, so the natural response is to check the cable, the permissions and the firmware — none of which is the problem.
connect()has already established that the port opened and the reader thread is running; what is missing is only that the firmware needed a moment after enumeration.Reproduce
On a board where the USB link has just been re-established:
I did not run this against the Python implementation; it is how the C++ port behaves, and the C++ port kept this function's structure on purpose so the two stay comparable.
Suggested direction
A short bounded retry of the handshake inside
connect()— the port is already open at that point, so the retry is cheap and has no side effects. The sub-connect()behaviour is otherwise correct:FirmwareMismatchErrorand the version checks should not be retried, only the missing reply.