Skip to content

Add DotPad buffered-receive and skipped BLE tests#19942

Open
bramd wants to merge 4 commits into
nvaccess:masterfrom
bramd:dotpad-buffered-receive
Open

Add DotPad buffered-receive and skipped BLE tests#19942
bramd wants to merge 4 commits into
nvaccess:masterfrom
bramd:dotpad-buffered-receive

Conversation

@bramd

@bramd bramd commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Link to issue number:

N/A. This is a follow-up to #19122 (DotPad BLE support) and #19838 (hwIo.ble module). As discussed in those PRs, the work is being split into three smaller PRs. This is "PR B" — the buffered-receive implementation and skipped BLE tests in preparation for the final BLE driver integration in PR C. There is no standalone issue tracking this change.

Summary of the issue:

The DotPad driver's _onReceive method reads remaining packet bytes synchronously via self._dev.read() and raises RuntimeError on any malformed byte, which can kill the display connection. This approach also cannot handle BLE's packet-based delivery model where entire packets arrive in a single callback. The buffered-receive replaces this with a more robust approach as a prerequisite for BLE support.

Description of user facing changes:

DotPad braille displays now handle malformed serial data more gracefully. Instead of crashing the display connection on a single corrupted byte, the driver resynchronizes and continues operating. In practice, since DotPad devices currently only connect over USB serial (which is a stable connection), users are unlikely to notice this change. The real benefit comes when BLE support lands in PR C, where packet-based delivery makes the buffered approach essential.

Description of developer facing changes:

  • Replaced _onReceive(self, header1: bytes) with a buffered-receive approach _onReceive(self, data: bytes) that accepts variable-length input (1 byte from Serial, full packets from BLE).
  • Extracted _processPacket(self, packetBody: bytes) from the old inline parsing for clarity.
  • Added DP_MAX_PACKET_SIZE protocol constant to defs.py.
  • The buffered-receive handles: resynchronization on bad sync bytes, buffer overflow protection, partial-packet buffering, and multiple packets in a single call.

Description of development approach:

Ported the buffered-receive logic from the dotpad-ble branch (#19122), keeping only the transport-agnostic parts. No BLE-specific code is included — that lands in PR C.

The approach replaces synchronous self._dev.read() calls within the callback with a _receiveBuffer that accumulates bytes across callbacks. Complete packets are extracted from the buffer as they become available.

Testing strategy:

Unit tests: 9 unskipped tests covering:

  • Complete packet in one call (BLE-style) and byte-at-a-time (Serial)
  • Partial packets split across multiple calls
  • Multiple concatenated packets extracted in order
  • Bad sync byte resynchronization
  • Buffer overflow protection
  • Incomplete packet buffering
  • Empty data handling
  • Partial header buffering

Additionally, 5 skipped BLE-specific tests (_isBleDotPad, check(), addBleDevices registration, _tryConnect BLE path) are included as a TDD skeleton for PR C.

rununittests.bat: 1095 tests, 5 skipped, all pass. ruff, pyright, markdownlint all pass. runcheckpot.bat: 0 errors.

Manual testing: Tested with a DotPad connected over USB serial. The display works correctly with the new buffered-receive code — navigation, key input, and braille output all function as expected.

Known issues with pull request:

None.

Code Review Checklist:

  • Documentation:
    • Change log entry: N/A (internal implementation improvement, will be covered by PR C's BLE changelog entry)
    • User Documentation: N/A
    • Developer / Technical Documentation: docstrings on _onReceive, _processPacket
    • Context sensitive help for GUI changes: N/A
  • Testing:
    • Unit tests
    • System (end to end) tests: N/A
    • Manual testing
  • UX of all users considered: N/A (internal change, no UI impact)
  • API is compatible with existing add-ons.
  • Security precautions taken.

@bramd bramd force-pushed the dotpad-buffered-receive branch 3 times, most recently from 5b1170b to 2ca075c Compare April 13, 2026 21:51
@SaschaCowley SaschaCowley added the conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review. label Apr 14, 2026
@seanbudd

seanbudd commented May 6, 2026

Copy link
Copy Markdown
Member

@bramd - is this ready for review?

@seanbudd seanbudd added this to the 2026.3 milestone May 11, 2026
@seanbudd seanbudd removed this from the 2026.3 milestone May 25, 2026
@seanbudd

Copy link
Copy Markdown
Member

@bramd - is this ready for review?

Replace the fragile _onReceive that did inline blocking reads with a
buffered-receive approach that accumulates bytes and extracts complete
packets. This handles resynchronization on bad sync bytes, buffer
overflow protection, and partial-packet buffering. Works with both
byte-at-a-time delivery (Serial) and packet-based delivery (BLE).

Extract _processPacket from the old inline parsing for clarity.

Add 9 unit tests covering: complete packet, byte-at-a-time, partial
chunks, multiple packets, resync, overflow, incomplete, empty, and
partial header. All pass.

Add 5 skipped BLE-specific DotPad tests (TDD skeleton for PR C):
_isBleDotPad, check(), addBleDevices registration, _tryConnect BLE.

Also fixes "Received responce" typo → "Received response".
@bramd bramd force-pushed the dotpad-buffered-receive branch from 2ca075c to 79a17e9 Compare June 26, 2026 01:50
bramd added 3 commits June 26, 2026 12:31
Two correctness fixes to the buffered-receive path:

- Clear _receiveBuffer at the start of each _tryConnect probe. The buffer
  is instance-level and was reused across auto-detect probes; the USB match
  is a generic FTDI VID, so stray bytes from a non-DotPad device on an
  earlier port could prefix and corrupt the real device's first response.

- Validate the declared packet length at parse time instead of capping the
  accumulation buffer. Real DotPad frames are small, so a declared length
  beyond DP_MAX_PACKET_SIZE means a false/garbled header: discard one byte
  and resync. This drops the old pre-extraction buffer wipe, which (a)
  discarded multi-packet bursts exceeding 512 bytes before processing any
  of them and (b) could never assemble a frame larger than 512 bytes. It
  also fixes a stall where a false sync followed by a large bogus length
  waited indefinitely (recoverable only by wiping queued real packets).

Tests: replace test_bufferOverflow_cleared (obsolete semantics) with
test_implausibleLength_resynchronize, and assert extracted packet body
content in test_completePacketAtOnce to cover the header-stripping slice.
Replace the old "covered by the GNU General Public License / See the file
COPYING" reference with the current two-line license reference documented
in projectDocs/dev/copyrightHeaders.md, across the files touched in this PR.
Replace "frame" with "packet" in the comments/docstrings touched in this PR
to match the terminology used in the code (packet, packetBody, packetLength).
@bramd bramd marked this pull request as ready for review June 26, 2026 14:02
@bramd bramd requested a review from a team as a code owner June 26, 2026 14:02
@bramd bramd requested a review from seanbudd June 26, 2026 14:02
@bramd

bramd commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@seanbudd Yes, this is ready now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants