fix(zboss): keep reader/writer stream plumbing intact across port reopen - #1828
Open
tostmann wants to merge 1 commit into
Open
fix(zboss): keep reader/writer stream plumbing intact across port reopen#1828tostmann wants to merge 1 commit into
tostmann wants to merge 1 commit into
Conversation
Two stream-lifecycle bugs around the zboss uart's port reopen paths (the in-place reopen in onPortClose while `inReset` is set, and the stop() + resetNcp() reconnect flow): 1. pipe(reader) without `end: false`: a port that emits `end` (a remote FIN — e.g. the NCP behind a serial-to-TCP bridge rebooting on NCP_RESET) ends the shared reader Transform for good. The reopen then pipes the new port into a finished stream and every received byte is dropped as a silent write-after-end — the post-reboot boot frame never reaches onPackage and the reset times out although the device answered. 2. closePort() never unpiped the writer: destroy() schedules the socket's `close` for the next tick, but removeAllListeners() runs synchronously right after and strips the pipe machinery's own close-cleanup handler, so Node never auto-unpipes. After a reconnect the writer is piped to BOTH the dead and the live port and keeps directing writes at the dead one. Both are covered by new unit tests against a two-sided fake socket with real stream semantics; each test fails on the previous code and passes with the fix. The combination was root-caused and verified end-to-end on test hardware (TCP-bridged ZBOSS NCP: factory reset, NVRAM restore and reconnect flows now complete where they previously timed out).
Owner
|
@kirovilya could you check this? |
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.
Two stream-lifecycle bugs around the zboss uart's port reopen paths — the in-place reopen in
onPortClosewhileinResetis set, and thestop()+resetNcp()reconnect flow. Both bite on TCP transports (NCP behind a serial-to-TCP bridge), where the peer legitimately closes the connection whenever the NCP reboots.1.
pipe(this.reader)withoutend: falseA port that emits
end(remote FIN) alsoend()s the shared reader Transform — for good, perpipe()'s defaultend: true. The in-place reopen then pipes the new port into a finished stream, and every received byte is dropped as a silent write-after-end: the post-reboot boot frame never reachesonPackage, soNCP_RESETtimes out after 10 s although the device answered within ~2 s. Diagnosis signature:Socket readyin the log, then zerozh:zboss:readlines ever after.2.
closePort()never unpipes the writerdestroy()schedules the socket'scloseevent for the next tick, butremoveAllListeners()runs synchronously right after and strips the pipe machinery's own close-cleanup handler — so Node never auto-unpipes. After a reconnect the writer is piped to both the dead and the live port and keeps directing writes at the dead one; in the field this wedged outgoing frames after the first reconnect (command timeout, adapter restart).Fix:
pipe(reader, {end: false})on both the serial and the TCP branch, and an explicitthis.writer.unpipe()at the top ofclosePort().Tests: new
test/adapter/zboss/uart.test.tsruns the realZBOSSUartagainst a two-sided fake socket with genuine stream semantics (aDuplex; incoming bytes viapush(), remote FIN viapush(null), outgoing captured in_write). One test drives a captured, CRC-valid NCP_RESET response frame through FIN + reopen and asserts frames keep flowing; the other drives thestop()/resetNcp()flow and asserts not a single write is directed at the dead port. Both fail on the previous code and pass with the fix. Full vitest suite andpnpm run checkare green.The combination was root-caused and verified end-to-end on test hardware: over a TCP-bridged ZBOSS NCP, factory reset, network formation and NVRAM-restore reconnect flows now complete where they previously timed out.