fix(serial): 高占空比下接收帧合并不切分 + 总计字节数恒为 0 (#9) - #14
Merged
Conversation
At high duty cycles (e.g. 115200 baud, 2560 B every 250 ms) the inter-frame gap (~28 ms) never reaches the 50 ms read timeout, so read() returns the next chunk directly and flush_if_idle was never consulted — frames merged invisibly until the 64 KiB hard cap, making the RX frame counter appear stalled while TX kept climbing. Run the idle check before feed() on every data read: an observed gap longer than the segmentation timeout now closes the pending frame. Bytes were never lost (raw tap and stats are pre-framing); this restores correct frame boundaries. Regression pinned with a scripted DelayedBytes read that blocks 30 ms then delivers the next chunk, plus a companion test pinning that back-to-back chunks still merge. Also fix the log viewer's total-bytes counter: event-sourced entries carry empty data by design (Step 4), so the sum always showed 0. The frame DTO's len is now kept as byte_len and used for the total.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_bf5209a4-f698-4193-818c-9b52369ac866) |
This was referenced Sep 2, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
用户在 V1.4.0 实测:115200 波特率 + 2560 字节 / 250ms 周期自收发,发送计数涨到 30 时接收帧数停在 1。
根因
read()直接带着下一段数据返回,永远不会走 TimedOut 分支,而flush_if_idle只在空读/超时分支里调用。段内缓冲持续增长,直到 64KiB 硬上限才切一帧(30 次发送 = 76.8KB → 恰好 1 帧,与现象完全吻合)。字节没有丢失(raw 落盘与统计在分帧之前),只是帧边界消失、界面看起来「接收停了」。data为空,而totalBytes仍按data.length求和。修复
feed()\) 之前先flush_if_idle(now)`:只要观察到距上一段数据的空闲间隔超过切分超时,就先闭合待切帧——Timeout 语义不再受读超时粒度限制。FrameDto.len保留为LogEntry.byte_len,总计按byte_len ?? data.length求和。测试
ScriptEvent::DelayedBytes:read() 阻塞 30ms 后返回下一段(忠实模拟 POSIX read 在等待中收到数据的行为)。timeout_mode_cuts_frame_when_read_returns_after_idle_gap:30ms 间隔 > 10ms 切分超时 → 两帧(修复前合并为 1 帧,测试红)。timeout_mode_merges_when_gap_stays_below_timeout:背靠背到达仍合并为一帧(防止过度切分)。待人工复测
115200 + 2560B / 250ms 自收发:接收帧数应与发送帧数同步增长(约 4 帧/秒),总计字节数正常累计。
Closes #9
Note
Medium Risk
Touches core serial read/framing behavior on every successful read; regression risk is mitigated by new golden tests, but high-throughput framing edge cases deserve manual retest.
Overview
Fixes Issue #9, where high-duty-cycle traffic made the RX frame count look stuck while sends kept climbing. In default Timeout segmentation, frame boundaries relied on idle flushes mostly on empty reads or
TimedOut; when the next chunk arrived within the 50 ms read timeout but after the segmentation timeout (~28 ms gaps at 115200/2560 B/250 ms), chunks merged until the 64 KiB cap—so one giant RX frame matched “30 sends, 1 receive.”The reader now calls
flush_if_idlebeforefeedwhenever a read returns bytes, so an elapsed idle gap still closes the pending frame even without a timeout read. Golden harness addsDelayedBytes(blocked read then data) plus tests that a 30 ms gap splits into two frames and back-to-back chunks still merge.Separately, event-driven log rows carry empty
databutFrameDto.len; the UI addsbyte_lenonLogEntry, maps it inuseSerialLogs, and the footer total bytes sumsbyte_len ?? data.lengthso the counter is no longer always zero.Reviewed by Cursor Bugbot for commit 6fd7df7. Bugbot is set up for automated code reviews on this repo. Configure here.