From d4db91235beee8f6389e65d04b7a08606a99762f Mon Sep 17 00:00:00 2001 From: CaoWangrenbo Date: Sat, 18 Jul 2026 14:58:19 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=82=B9=E5=87=BB=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E5=90=8E=E7=AB=8B=E5=8D=B3=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=BF=83=E8=B7=B3=E7=BA=BF=E7=A8=8B=EF=BC=8C=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E7=AD=89=E5=BE=85=E6=95=B0=E6=8D=AE=E5=8C=85=E6=8E=A5=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ProtocolClient.connect() 现在立即启动心跳循环(1Hz),而不是 等到 receiveLoop 首次收到数据包后才启动。心跳在 CONNECTING 和 CONNECTED 状态下均正常运行,确保连接建立过程中机器人能持续收到 心跳包。 Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- AGENTS.md | 1 + .../m20_gamepad/network/ProtocolClient.kt | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index adc174e..c802706 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -211,6 +211,7 @@ gradlew installDebug - [x] 连接状态准确性修复:`ProtocolClient` 收到首个有效数据包后才标记 `CONNECTED`,不再 socket 创建后立即标记;`CONNECTING` 状态 5 秒无响应自动超时回退 `TIMEOUT` - [x] 连接按钮交互优化:`CONNECTING` 状态下点击连接按钮仅断开回到 `DISCONNECTED`,不再自动重连,由用户手动再次点击发起重试 - [x] 状态机补充:`RobotConnection` 允许 `CONNECTING → TIMEOUT` 合法转换 +- [x] 心跳启动时机修复:`ProtocolClient.connect()` 立即启动心跳线程,不再等待首次收到数据包后才启动;心跳在 `CONNECTING` 和 `CONNECTED` 状态下均正常运行 ### 待办 diff --git a/app/src/main/java/com/example/m20_gamepad/network/ProtocolClient.kt b/app/src/main/java/com/example/m20_gamepad/network/ProtocolClient.kt index a116ec2..ccab4cd 100644 --- a/app/src/main/java/com/example/m20_gamepad/network/ProtocolClient.kt +++ b/app/src/main/java/com/example/m20_gamepad/network/ProtocolClient.kt @@ -92,7 +92,8 @@ class ProtocolClient( * 2. 启动接收/发送协程 * 3. 发送订阅请求 * 4. 启动断线检测(CONNECTING 5s / CONNECTED 3s 无数据 → TIMEOUT) - * 5. 首次收到有效数据包后标记已连接,并启动心跳 1Hz(在接收循环中处理) + * 5. 启动心跳 1Hz(立即发送,覆盖 CONNECTING 和 CONNECTED 状态) + * 6. 首次收到有效数据包后标记已连接 */ fun connect() { if (_state.value != State.DISCONNECTED && _state.value != State.ERROR) return @@ -136,6 +137,11 @@ class ProtocolClient( timeoutJob = scope.launch { timeoutCheckLoop() } + + // 启动心跳(立即开始,覆盖 CONNECTING 和 CONNECTED 状态) + heartbeatJob = scope.launch(Dispatchers.IO) { + heartbeatLoop(sock, address) + } } catch (e: Exception) { _state.value = State.ERROR closeSocket() @@ -227,12 +233,9 @@ class ProtocolClient( _statusReport.tryEmit(report) } - // 首次收到有效数据包:标记已连接,启动心跳 + // 首次收到有效数据包:标记已连接 if (_state.value == State.CONNECTING) { _state.value = State.CONNECTED - heartbeatJob = scope.launch(Dispatchers.IO) { - heartbeatLoop(sock, remoteAddress!!) - } } } catch (_: SocketTimeoutException) { // 超时正常,继续循环 @@ -246,9 +249,11 @@ class ProtocolClient( } } - /** 心跳循环(1 Hz) */ + /** 心跳循环(1 Hz)—— 在 CONNECTING 和 CONNECTED 状态下运行 */ private suspend fun CoroutineScope.heartbeatLoop(sock: DatagramSocket, address: InetAddress) { - while (isActive && _state.value == State.CONNECTED) { + while (isActive && + (_state.value == State.CONNECTING || _state.value == State.CONNECTED) + ) { try { val json = ControlCommands.heartbeat() val packet = encoder.encode(json)