[AZ-653] gimbal_controller ViewPro A40 vendor UDP transport (batch 10)
ci/woodpecker/push/build-arm Pipeline failed

Implements the vendor wire protocol for the A40 gimbal (XOR-8 checksum,
not CRC16 — task spec corrected against ArduPilot AP_Mount_Viewpro.h):
frame encode/decode, typed FrameId/CameraCommand/ImageSensor, A1 angles,
C1 camera, C2 set-zoom command builders, and a tokio UdpSocket transport
with bounded retry, per-command deadline, and atomic vendor-fault
counters surfaced via faults()/health(). GimbalControllerHandle::set_pose
and zoom now ride the transport when wired; remain disabled when no
transport is bound. 32/32 gimbal_controller tests green; workspace test
suite green except for a pre-existing flake in
mission_executor::state_machine::ac3_bounded_retry_then_success that
reproduces only under parallel workspace test load (passes 5/5 in
isolation; flagged in batch 8 report, unrelated to this batch).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-19 20:07:32 +03:00
parent 0993b87541
commit 288e7f8c46
13 changed files with 1656 additions and 21 deletions
@@ -0,0 +1,79 @@
# ViewPro A40 Vendor Transport
**Task**: AZ-653_gimbal_a40_transport
**Name**: ViewPro A40 vendor protocol UDP transport
**Description**: UDP transport, frame encode/decode, CRC16 (vendor spec), bounded retry on command timeout. Surface vendor faults to health.
**Complexity**: 5 points
**Dependencies**: AZ-640_initial_structure
**Component**: gimbal_controller
**Tracker**: AZ-653
**Epic**: AZ-634
## Problem
The gimbal is a ViewPro A40 vendor product reachable over UDP using a vendor-specified frame format with CRC16. The transport layer must encode and decode every command/response frame this codebase issues (yaw, pitch, zoom, feedback request, mode commands), validate CRC on inbound frames, and re-issue on timeout with bounded retry. The vendor protocol is fixed by the camera — the device's binary protocol is a `restrictions.md` constraint, not a design choice.
## Outcome
- `A40Transport::send(cmd) -> Result<A40Response, A40Error>` writes a CRC-correct vendor frame to the configured UDP endpoint and awaits the matching response within a deadline.
- Inbound frames are CRC-validated; mismatches are dropped and counted as `vendor_faults_total{kind="crc"}`.
- Bounded retry on timeout (default 3 attempts; configurable).
- Health surface: `commands_per_min`, `vendor_faults_total`, `last_command_in_flight`.
## Scope
### Included
- UDP socket (single endpoint).
- CRC16 (vendor polynomial) encode/decode helpers.
- Frame encoders for yaw / pitch / zoom commands + feedback request.
- Frame decoders for yaw / pitch / zoom feedback + vendor fault frames.
- Bounded retry on timeout.
### Excluded
- Sweep pattern primitive (task 15).
- Smooth-pan plan execution (task 16).
- Centre-on-target primitive (task 17).
- Vendor protocol *specification* — assumed to be reverse-engineered or vendor-supplied separately; this task implements against the documented frame layout in `misc/camera/a8/` (which is the predecessor model A8; A40 differs in command codes per architecture.md).
## Acceptance Criteria
**AC-1: CRC round-trip**
Given the encoder produces a yaw command frame for `yaw = 30°`
When the same frame is fed back through the decoder
Then the decoded command matches and `vendor_faults_total{kind="crc"} = 0`.
**AC-2: CRC mismatch counted**
Given an inbound frame with corrupted CRC
When the decoder consumes it
Then the frame is dropped and `vendor_faults_total{kind="crc"}` increments by 1.
**AC-3: Command timeout retries**
Given a fake A40 endpoint that drops the first command silently
When `send(yaw_cmd)` is called with default 3 attempts
Then the call succeeds on retry; `vendor_faults_total{kind="timeout"}` reports 1.
**AC-4: Cap exhaustion returns explicit error**
Given the endpoint never responds
When `send(yaw_cmd)` is called
Then after 3 attempts the call returns `Err(MaxRetriesExceeded)` and the error surfaces to the caller.
## Non-Functional Requirements
**Performance**
- Single command round-trip: ≤200 ms on a healthy link (well under the ≤500 ms decision-to-movement budget).
**Reliability**
- CRC mismatches counted, never silent.
- Bounded retry; no infinite retry.
## Constraints
- Vendor protocol is fixed; no negotiation.
- One A40 per autopilot instance.
## Runtime Completeness
- **Named capability**: ViewPro A40 vendor protocol on UDP.
- **Production code that must exist**: real CRC16; real UDP socket; real per-command encoder/decoder.
- **Allowed external stubs**: in tests, a UDP echo with vendor-frame replay can simulate the camera.
- **Unacceptable substitutes**: a generic "send raw bytes and assume success" path is unacceptable — the protocol's frame format and CRC are non-negotiable.