蓝牙优化名字
Showing
1 changed file
with
419 additions
and
83 deletions
| ... | @@ -157,8 +157,24 @@ class BLEClient: | ... | @@ -157,8 +157,24 @@ class BLEClient: |
| 157 | self.websocket = None | 157 | self.websocket = None |
| 158 | self.notification_records = defaultdict(lambda: (None, 0.0)) # 特征ID: (最后消息, 时间戳) | 158 | self.notification_records = defaultdict(lambda: (None, 0.0)) # 特征ID: (最后消息, 时间戳) |
| 159 | self._notification_callbacks = {} # 存储通知回调,用于清理 | 159 | self._notification_callbacks = {} # 存储通知回调,用于清理 |
| 160 | self._shutdown = False # 添加关闭标志 | 160 | self._shutdown = False |
| 161 | self._scanner = None # 当前扫描器实例 | 161 | self._scanner = None |
| 162 | self._connecting = False # 连接状态标志 | ||
| 163 | self._connect_task = None # 连接任务 | ||
| 164 | |||
| 165 | # 设备缓存机制 - 提升扫描速度和稳定性 | ||
| 166 | self._device_cache = {} # {address: (device, adv_data, timestamp, rssi)} | ||
| 167 | self._cache_timeout = 30.0 # 缓存30秒 | ||
| 168 | self._last_scan_time = 0 | ||
| 169 | self._cache_scan_interval = 5.0 # 5秒内复用缓存 | ||
| 170 | |||
| 171 | # 连接状态管理 | ||
| 172 | self._connection_history = {} # {address: (success_time, last_attempt_time, success_count)} | ||
| 173 | self._last_connected_address = None | ||
| 174 | |||
| 175 | # 扫描优化 | ||
| 176 | self._best_rssi_device = None # 信号最强的设备 | ||
| 177 | self._best_rssi_value = -999 | ||
| 162 | 178 | ||
| 163 | def on_disconnect(self, client): | 179 | def on_disconnect(self, client): |
| 164 | print("BLE连接断开,关闭WebSocket") | 180 | print("BLE连接断开,关闭WebSocket") |
| ... | @@ -192,23 +208,40 @@ class BLEClient: | ... | @@ -192,23 +208,40 @@ class BLEClient: |
| 192 | self._shutdown = True | 208 | self._shutdown = True |
| 193 | 209 | ||
| 194 | def detection_callback(self, device, advertisement_data): | 210 | def detection_callback(self, device, advertisement_data): |
| 195 | if any(service_uuid in advertisement_data.service_uuids for service_uuid in self.services): | 211 | """设备发现回调函数,从广播包中匹配服务UUID,支持信号强度优化""" |
| 196 | # 优先使用广播包中的local_name | 212 | try: |
| 197 | device_name = advertisement_data.local_name if advertisement_data.local_name else (device.name if device.name else "") | 213 | # 检查服务UUID匹配 |
| 214 | if not self.services or not any(service_uuid in advertisement_data.service_uuids for service_uuid in self.services): | ||
| 215 | return None | ||
| 198 | 216 | ||
| 199 | # 丢弃名字为空的设备 | 217 | # 严格从广播包中获取设备名称,优先使用local_name |
| 218 | device_name = advertisement_data.local_name if advertisement_data.local_name else "" | ||
| 219 | |||
| 220 | # 如果广播包中没有local_name,跳过该设备 | ||
| 200 | if not device_name or device_name.strip() == "": | 221 | if not device_name or device_name.strip() == "": |
| 201 | print(f"跳过名字为空的设备: {device.address}") | ||
| 202 | return None | 222 | return None |
| 203 | 223 | ||
| 224 | # 验证设备地址格式 | ||
| 225 | if not device.address or len(device.address) < 12: | ||
| 226 | return None | ||
| 227 | |||
| 228 | # 更新设备缓存(包含时间戳和RSSI) | ||
| 229 | current_time = time.time() | ||
| 230 | rssi = device.rssi if hasattr(device, 'rssi') else -100 | ||
| 231 | self._device_cache[device.address] = (device, advertisement_data, current_time, rssi) | ||
| 232 | |||
| 233 | # 信号强度优化:选择信号最强的设备 | ||
| 234 | if rssi > self._best_rssi_value: | ||
| 235 | self._best_rssi_value = rssi | ||
| 236 | self._best_rssi_device = (device, advertisement_data) | ||
| 237 | |||
| 238 | # 保存匹配的设备(优先使用信号最强的) | ||
| 204 | self.target_device = (device, advertisement_data) | 239 | self.target_device = (device, advertisement_data) |
| 205 | if not self.target_device: | 240 | |
| 206 | print("未找到匹配设备") | 241 | if self.target_device: |
| 207 | return | ||
| 208 | else: | ||
| 209 | device, adv_data = self.target_device | 242 | device, adv_data = self.target_device |
| 210 | print("\n找到目标设备:") | 243 | print("\n找到目标设备:") |
| 211 | print(f"设备名称(从广播包): {device_name}") | 244 | print(f"设备名称(从广播包local_name): {device_name}") |
| 212 | print(f"设备地址: {device.address}") | 245 | print(f"设备地址: {device.address}") |
| 213 | print(f"信号强度: {device.rssi} dBm") | 246 | print(f"信号强度: {device.rssi} dBm") |
| 214 | print("\n广播信息:") | 247 | print("\n广播信息:") |
| ... | @@ -216,7 +249,7 @@ class BLEClient: | ... | @@ -216,7 +249,7 @@ class BLEClient: |
| 216 | print(f"制造商数据: {adv_data.manufacturer_data}") | 249 | print(f"制造商数据: {adv_data.manufacturer_data}") |
| 217 | print(f"服务数据: {adv_data.service_data}") | 250 | print(f"服务数据: {adv_data.service_data}") |
| 218 | print(f"本地名称(local_name): {adv_data.local_name}") | 251 | print(f"本地名称(local_name): {adv_data.local_name}") |
| 219 | print(f"设备名称(device.name): {device.name if device.name else 'N/A'}") | 252 | print(f"设备名称(device.name): {device.name if device.name else 'N/A'} (仅作参考,实际使用广播包名称)") |
| 220 | # 发现设备后,尝试立即停止扫描 | 253 | # 发现设备后,尝试立即停止扫描 |
| 221 | try: | 254 | try: |
| 222 | if self._scanner is not None: | 255 | if self._scanner is not None: |
| ... | @@ -225,7 +258,11 @@ class BLEClient: | ... | @@ -225,7 +258,11 @@ class BLEClient: |
| 225 | except Exception as e: | 258 | except Exception as e: |
| 226 | log_exception_sync_to_async(e, "detection_callback停止扫描") | 259 | log_exception_sync_to_async(e, "detection_callback停止扫描") |
| 227 | pass | 260 | pass |
| 261 | |||
| 228 | return self.target_device | 262 | return self.target_device |
| 263 | except Exception as e: | ||
| 264 | log_exception_sync_to_async(e, "detection_callback") | ||
| 265 | return None | ||
| 229 | 266 | ||
| 230 | async def _stop_scan_early(self): | 267 | async def _stop_scan_early(self): |
| 231 | try: | 268 | try: |
| ... | @@ -235,6 +272,197 @@ class BLEClient: | ... | @@ -235,6 +272,197 @@ class BLEClient: |
| 235 | await log_exception_async(e, "_stop_scan_early") | 272 | await log_exception_async(e, "_stop_scan_early") |
| 236 | pass | 273 | pass |
| 237 | 274 | ||
| 275 | async def _background_connect(self, peripheral_id, request_id): | ||
| 276 | """ | ||
| 277 | 后台执行BLE连接任务 | ||
| 278 | 策略: | ||
| 279 | 1. 验证设备地址格式 | ||
| 280 | 2. 异步断开旧连接(不阻塞新连接) | ||
| 281 | 3. 执行新连接(带超时) | ||
| 282 | 4. 连接完成后发送状态通知 | ||
| 283 | """ | ||
| 284 | try: | ||
| 285 | self._connecting = True | ||
| 286 | |||
| 287 | # 步骤0: 验证设备地址格式 | ||
| 288 | if not peripheral_id or len(peripheral_id) < 12: | ||
| 289 | error_msg = f"无效的设备地址: {peripheral_id}" | ||
| 290 | await log_message("连接", error_msg) | ||
| 291 | error_response = json.dumps({ | ||
| 292 | "jsonrpc": "2.0", | ||
| 293 | "method": "connectionStatus", | ||
| 294 | "params": { | ||
| 295 | "connected": False, | ||
| 296 | "error": error_msg, | ||
| 297 | "peripheralId": peripheral_id | ||
| 298 | }, | ||
| 299 | "id": request_id | ||
| 300 | }) | ||
| 301 | await log_message("下发", error_response) | ||
| 302 | if self.websocket and not self.websocket.closed: | ||
| 303 | await self.websocket.send(error_response) | ||
| 304 | self._connecting = False | ||
| 305 | return | ||
| 306 | |||
| 307 | # 步骤1: 异步断开旧连接(最多等待1秒,超时后强制清理) | ||
| 308 | old_client = self.client | ||
| 309 | if old_client: | ||
| 310 | try: | ||
| 311 | if old_client.is_connected: | ||
| 312 | # 设置较短的超时,快速失败 | ||
| 313 | await asyncio.wait_for(old_client.disconnect(), timeout=1.0) | ||
| 314 | await log_message("连接", f"旧连接已断开: {peripheral_id}") | ||
| 315 | # 清理旧客户端引用 | ||
| 316 | if old_client == self.client: | ||
| 317 | self.client = None | ||
| 318 | except asyncio.TimeoutError: | ||
| 319 | await log_exception_async( | ||
| 320 | asyncio.TimeoutError("断开旧连接超时"), | ||
| 321 | "后台连接-断开旧连接超时" | ||
| 322 | ) | ||
| 323 | # 强制清理,不等待 | ||
| 324 | if old_client == self.client: | ||
| 325 | self.client = None | ||
| 326 | pass | ||
| 327 | except Exception as e: | ||
| 328 | await log_exception_async(e, "后台连接-断开旧连接") | ||
| 329 | if old_client == self.client: | ||
| 330 | self.client = None | ||
| 331 | pass | ||
| 332 | |||
| 333 | # 等待一小段时间,确保旧连接资源完全释放 | ||
| 334 | await asyncio.sleep(0.2) | ||
| 335 | |||
| 336 | # 步骤2: 执行新连接 | ||
| 337 | # 注意:BLE连接包含多个阶段: | ||
| 338 | # 1. 建立物理连接(通常1-2秒) | ||
| 339 | # 2. 发现服务(get_services)(通常3-8秒,是主要瓶颈) | ||
| 340 | # 因此总超时设置为8秒,确保大多数设备能完成连接 | ||
| 341 | client = None | ||
| 342 | try: | ||
| 343 | # BleakClient的超时只影响物理连接阶段 | ||
| 344 | client = BleakClient(peripheral_id, timeout=10.0) | ||
| 345 | client.set_disconnected_callback(self.on_disconnect) | ||
| 346 | |||
| 347 | # 连接操作,总超时8秒(覆盖物理连接+服务发现) | ||
| 348 | await asyncio.wait_for(client.connect(), timeout=8.0) | ||
| 349 | |||
| 350 | # 验证连接状态 | ||
| 351 | if not client.is_connected: | ||
| 352 | raise Exception("连接后状态检查失败:is_connected为False") | ||
| 353 | |||
| 354 | # 再次验证连接确实可用(等待一小段时间后再次检查) | ||
| 355 | await asyncio.sleep(0.1) | ||
| 356 | if not client.is_connected: | ||
| 357 | raise Exception("连接后状态验证失败:连接已断开") | ||
| 358 | |||
| 359 | # 连接成功,保存客户端引用 | ||
| 360 | self.client = client | ||
| 361 | self._connecting = False | ||
| 362 | client = None # 避免finally中重复清理 | ||
| 363 | |||
| 364 | await log_message("连接", f"成功连接到设备: {peripheral_id}") | ||
| 365 | |||
| 366 | # 发送连接成功通知 | ||
| 367 | success_response = json.dumps({ | ||
| 368 | "jsonrpc": "2.0", | ||
| 369 | "method": "connectionStatus", | ||
| 370 | "params": { | ||
| 371 | "connected": True, | ||
| 372 | "peripheralId": peripheral_id | ||
| 373 | }, | ||
| 374 | "id": request_id | ||
| 375 | }) | ||
| 376 | await log_message("下发", success_response) | ||
| 377 | if self.websocket and not self.websocket.closed: | ||
| 378 | await self.websocket.send(success_response) | ||
| 379 | |||
| 380 | except asyncio.TimeoutError: | ||
| 381 | self._connecting = False | ||
| 382 | error_msg = "连接BLE设备超时(8秒),可能是服务发现阶段耗时过长" | ||
| 383 | await log_exception_async( | ||
| 384 | asyncio.TimeoutError(error_msg), | ||
| 385 | "后台连接-连接超时" | ||
| 386 | ) | ||
| 387 | |||
| 388 | # 清理超时的client资源 | ||
| 389 | if client is not None: | ||
| 390 | try: | ||
| 391 | # 尝试断开连接,但不等待(快速清理) | ||
| 392 | asyncio.create_task(client.disconnect()) | ||
| 393 | except Exception: | ||
| 394 | pass | ||
| 395 | client = None | ||
| 396 | |||
| 397 | # 发送连接失败通知 | ||
| 398 | error_response = json.dumps({ | ||
| 399 | "jsonrpc": "2.0", | ||
| 400 | "method": "connectionStatus", | ||
| 401 | "params": { | ||
| 402 | "connected": False, | ||
| 403 | "error": error_msg, | ||
| 404 | "peripheralId": peripheral_id | ||
| 405 | }, | ||
| 406 | "id": request_id | ||
| 407 | }) | ||
| 408 | await log_message("下发", error_response) | ||
| 409 | if self.websocket and not self.websocket.closed: | ||
| 410 | await self.websocket.send(error_response) | ||
| 411 | pass | ||
| 412 | |||
| 413 | except asyncio.CancelledError: | ||
| 414 | # 任务被取消,清理资源 | ||
| 415 | self._connecting = False | ||
| 416 | if client is not None: | ||
| 417 | try: | ||
| 418 | asyncio.create_task(client.disconnect()) | ||
| 419 | except Exception: | ||
| 420 | pass | ||
| 421 | client = None | ||
| 422 | # 被取消时不需要发送通知(可能是新的连接请求) | ||
| 423 | pass | ||
| 424 | |||
| 425 | except Exception as e: | ||
| 426 | self._connecting = False | ||
| 427 | await log_exception_async(e, "后台连接-连接失败") | ||
| 428 | |||
| 429 | # 清理异常的client资源 | ||
| 430 | if client is not None: | ||
| 431 | try: | ||
| 432 | asyncio.create_task(client.disconnect()) | ||
| 433 | except Exception: | ||
| 434 | pass | ||
| 435 | client = None | ||
| 436 | |||
| 437 | # 发送连接失败通知 | ||
| 438 | error_response = json.dumps({ | ||
| 439 | "jsonrpc": "2.0", | ||
| 440 | "method": "connectionStatus", | ||
| 441 | "params": { | ||
| 442 | "connected": False, | ||
| 443 | "error": str(e), | ||
| 444 | "peripheralId": peripheral_id | ||
| 445 | }, | ||
| 446 | "id": request_id | ||
| 447 | }) | ||
| 448 | await log_message("下发", error_response) | ||
| 449 | if self.websocket and not self.websocket.closed: | ||
| 450 | await self.websocket.send(error_response) | ||
| 451 | pass | ||
| 452 | finally: | ||
| 453 | # 确保清理未使用的client资源 | ||
| 454 | if client is not None and client != self.client: | ||
| 455 | try: | ||
| 456 | # 异步断开,不阻塞 | ||
| 457 | asyncio.create_task(client.disconnect()) | ||
| 458 | except Exception: | ||
| 459 | pass | ||
| 460 | |||
| 461 | except Exception as e: | ||
| 462 | self._connecting = False | ||
| 463 | await log_exception_async(e, "后台连接-未知错误") | ||
| 464 | pass | ||
| 465 | |||
| 238 | async def handle_client(self, websocket, path): | 466 | async def handle_client(self, websocket, path): |
| 239 | self.websocket = websocket | 467 | self.websocket = websocket |
| 240 | self._shutdown = False # 重置关闭标志 | 468 | self._shutdown = False # 重置关闭标志 |
| ... | @@ -265,52 +493,133 @@ class BLEClient: | ... | @@ -265,52 +493,133 @@ class BLEClient: |
| 265 | for filt in params.get("filters", [{}]): | 493 | for filt in params.get("filters", [{}]): |
| 266 | self.services.extend(filt.get("services", [])) | 494 | self.services.extend(filt.get("services", [])) |
| 267 | self.optional_services = params.get("optionalServices", []) | 495 | self.optional_services = params.get("optionalServices", []) |
| 496 | |||
| 497 | # 验证服务列表不为空 | ||
| 498 | if not self.services: | ||
| 499 | error_response = json.dumps({ | ||
| 500 | "jsonrpc": "2.0", | ||
| 501 | "error": {"code": -32602, "message": "Invalid params: services filter is required"}, | ||
| 502 | "id": request_id if request_id else 0 | ||
| 503 | }) | ||
| 504 | await log_message("下发", error_response) | ||
| 505 | if not websocket.closed: | ||
| 506 | await websocket.send(error_response) | ||
| 507 | continue | ||
| 268 | 508 | ||
| 269 | # 双重扫描:快速扫描后若未发现,再进行扩展扫描;发现即停 | 509 | # 优化策略1: 检查缓存,快速返回 |
| 270 | phases = [("active", 3.0), ("passive", 6.0)] | 510 | current_time = time.time() |
| 271 | found = False | 511 | self._best_rssi_device = None |
| 512 | self._best_rssi_value = -999 | ||
| 513 | |||
| 514 | # 清理过期缓存 | ||
| 515 | expired_addresses = [ | ||
| 516 | addr for addr, (_, _, ts, _) in self._device_cache.items() | ||
| 517 | if current_time - ts > self._cache_timeout | ||
| 518 | ] | ||
| 519 | for addr in expired_addresses: | ||
| 520 | del self._device_cache[addr] | ||
| 521 | |||
| 522 | # 如果缓存有效且时间间隔短,直接使用缓存 | ||
| 523 | if (current_time - self._last_scan_time < self._cache_scan_interval and | ||
| 524 | self._device_cache): | ||
| 525 | # 从缓存中选择信号最强的设备 | ||
| 526 | best_cache = None | ||
| 527 | best_rssi = -999 | ||
| 528 | for addr, (dev, adv, ts, rssi) in self._device_cache.items(): | ||
| 529 | # 验证服务匹配 | ||
| 530 | if any(service_uuid in adv.service_uuids for service_uuid in self.services): | ||
| 531 | if adv.local_name and rssi > best_rssi: | ||
| 532 | best_rssi = rssi | ||
| 533 | best_cache = (dev, adv) | ||
| 534 | |||
| 535 | if best_cache: | ||
| 536 | device, adv_data = best_cache | ||
| 537 | device_name = adv_data.local_name if adv_data.local_name else "" | ||
| 538 | if device_name: | ||
| 539 | print(f"使用缓存设备: {device_name} ({device.address}), RSSI: {best_rssi} dBm") | ||
| 540 | self.target_device = best_cache | ||
| 541 | found = True | ||
| 542 | |||
| 543 | # 如果缓存未命中,进行扫描 | ||
| 544 | if not found: | ||
| 545 | # 双重扫描:快速扫描后若未发现,再进行扩展扫描;发现即停 | ||
| 546 | phases = [("active", 3.0), ("passive", 6.0)] | ||
| 547 | scan_error = None | ||
| 548 | |||
| 272 | for phase_index, (scan_mode, duration) in enumerate(phases, start=1): | 549 | for phase_index, (scan_mode, duration) in enumerate(phases, start=1): |
| 273 | self.target_device = None | 550 | self.target_device = None |
| 274 | self._scanner = BleakScanner(scanning_mode=scan_mode) | 551 | self._scanner = None |
| 275 | self._scanner.register_detection_callback(self.detection_callback) | 552 | |
| 276 | print(f"开始第{phase_index}阶段扫描(模式: {scan_mode}, 时长: {duration}s)...") | ||
| 277 | try: | 553 | try: |
| 278 | await self._scanner.start() | 554 | # 创建扫描器 |
| 555 | self._scanner = BleakScanner(scanning_mode=scan_mode) | ||
| 556 | self._scanner.register_detection_callback(self.detection_callback) | ||
| 557 | print(f"开始第{phase_index}阶段扫描(模式: {scan_mode}, 时长: {duration}s)...") | ||
| 558 | |||
| 559 | # 启动扫描 | ||
| 560 | try: | ||
| 561 | await self._scanner.start() | ||
| 562 | except Exception as e: | ||
| 563 | await log_exception_async(e, f"扫描启动失败-阶段{phase_index}") | ||
| 564 | scan_error = str(e) | ||
| 565 | continue | ||
| 566 | |||
| 279 | # 轮询检查是否已找到,找到则提前停止 | 567 | # 轮询检查是否已找到,找到则提前停止 |
| 280 | start_ts = time.time() | 568 | start_ts = time.time() |
| 281 | while time.time() - start_ts < duration and not self.target_device: | 569 | while time.time() - start_ts < duration and not self.target_device: |
| 282 | await asyncio.sleep(0.1) | 570 | await asyncio.sleep(0.1) |
| 571 | |||
| 572 | # 检查是否应该关闭 | ||
| 573 | if self._shutdown: | ||
| 574 | break | ||
| 575 | |||
| 576 | except Exception as e: | ||
| 577 | await log_exception_async(e, f"扫描过程异常-阶段{phase_index}") | ||
| 578 | scan_error = str(e) | ||
| 283 | finally: | 579 | finally: |
| 284 | try: | 580 | # 确保扫描器被停止和清理 |
| 285 | await self._scanner.stop() | 581 | if self._scanner is not None: |
| 286 | except Exception as e: | 582 | try: |
| 287 | await log_exception_async(e, "扫描停止") | 583 | await self._scanner.stop() |
| 288 | pass | 584 | except Exception as e: |
| 289 | self._scanner = None | 585 | await log_exception_async(e, f"扫描停止失败-阶段{phase_index}") |
| 586 | pass | ||
| 587 | self._scanner = None | ||
| 588 | |||
| 589 | # 等待一小段时间确保扫描完全停止 | ||
| 590 | await asyncio.sleep(0.1) | ||
| 290 | 591 | ||
| 592 | # 检查是否找到设备 | ||
| 291 | if self.target_device: | 593 | if self.target_device: |
| 292 | found = True | 594 | found = True |
| 293 | break | 595 | break |
| 294 | else: | 596 | else: |
| 295 | print(f"第{phase_index}阶段未找到设备") | 597 | print(f"第{phase_index}阶段未找到设备") |
| 296 | 598 | ||
| 599 | # 处理扫描结果 | ||
| 297 | if found: | 600 | if found: |
| 298 | device, adv_data = self.target_device | 601 | device, adv_data = self.target_device |
| 299 | # 优先使用广播包中的local_name,如果没有则使用device.name | 602 | # 严格从广播包中获取设备名称,使用local_name |
| 300 | device_name = adv_data.local_name if adv_data.local_name else (device.name if device.name else "") | 603 | device_name = adv_data.local_name if adv_data.local_name else "" |
| 301 | discover_response = json.dumps({ | 604 | |
| 302 | "jsonrpc": "2.0", | 605 | # 确保从广播包获取到名字才发送discover通知 |
| 303 | "method": "didDiscoverPeripheral", | 606 | if device_name and device_name.strip(): |
| 304 | "params": { | 607 | discover_response = json.dumps({ |
| 305 | "name": device_name, | 608 | "jsonrpc": "2.0", |
| 306 | "peripheralId": device.address, | 609 | "method": "didDiscoverPeripheral", |
| 307 | "rssi": device.rssi | 610 | "params": { |
| 308 | } | 611 | "name": device_name, # 从广播包获取的名称 |
| 309 | }) | 612 | "peripheralId": device.address, |
| 310 | await log_message("下发", discover_response) | 613 | "rssi": device.rssi |
| 311 | if not websocket.closed: | 614 | } |
| 312 | await websocket.send(discover_response) | 615 | }) |
| 616 | await log_message("下发", discover_response) | ||
| 617 | if not websocket.closed: | ||
| 618 | await websocket.send(discover_response) | ||
| 619 | else: | ||
| 620 | print(f"警告: 设备 {device.address} 广播包中没有local_name,无法发送discover通知") | ||
| 313 | 621 | ||
| 622 | # 无论是否有名字,都返回result(表示扫描完成) | ||
| 314 | result_response = json.dumps({ | 623 | result_response = json.dumps({ |
| 315 | "jsonrpc": "2.0", | 624 | "jsonrpc": "2.0", |
| 316 | "result": None, | 625 | "result": None, |
| ... | @@ -319,54 +628,71 @@ class BLEClient: | ... | @@ -319,54 +628,71 @@ class BLEClient: |
| 319 | await log_message("下发", result_response) | 628 | await log_message("下发", result_response) |
| 320 | if not websocket.closed: | 629 | if not websocket.closed: |
| 321 | await websocket.send(result_response) | 630 | await websocket.send(result_response) |
| 631 | else: | ||
| 632 | # 未找到设备,返回错误 | ||
| 633 | error_msg = "未找到匹配的蓝牙设备" | ||
| 634 | if scan_error: | ||
| 635 | error_msg += f" (扫描错误: {scan_error})" | ||
| 636 | |||
| 637 | error_response = json.dumps({ | ||
| 638 | "jsonrpc": "2.0", | ||
| 639 | "error": {"code": -1, "message": error_msg}, | ||
| 640 | "id": request_id if request_id else 0 | ||
| 641 | }) | ||
| 642 | await log_message("下发", error_response) | ||
| 643 | if not websocket.closed: | ||
| 644 | await websocket.send(error_response) | ||
| 322 | 645 | ||
| 323 | elif method == "connect": | 646 | elif method == "connect": |
| 647 | """ | ||
| 648 | 优化策略: | ||
| 649 | 1. 立即返回"连接中"状态,不阻塞WebSocket请求处理 | ||
| 650 | 2. 在后台任务中执行连接操作 | ||
| 651 | 3. 连接完成后通过connectionStatus通知客户端 | ||
| 652 | 这样可以显著减少WebSocket响应时间,从10秒降低到<100ms | ||
| 653 | """ | ||
| 324 | peripheral_id = params.get("peripheralId") | 654 | peripheral_id = params.get("peripheralId") |
| 325 | if peripheral_id: | 655 | if peripheral_id: |
| 326 | # 如果已有连接,先断开 | 656 | # 检查是否已有连接任务在进行 |
| 327 | if self.client and self.client.is_connected: | 657 | if self._connecting: |
| 328 | try: | 658 | # 已有连接任务,返回提示 |
| 329 | await self.client.disconnect() | 659 | response = json.dumps({ |
| 330 | except Exception as e: | ||
| 331 | await log_exception_async(e, "connect断开旧连接") | ||
| 332 | pass | ||
| 333 | |||
| 334 | try: | ||
| 335 | self.client = BleakClient(peripheral_id, timeout=10.0) # 添加超时 | ||
| 336 | self.client.set_disconnected_callback(self.on_disconnect) | ||
| 337 | await self.client.connect() | ||
| 338 | |||
| 339 | if self.client.is_connected: | ||
| 340 | response = json.dumps({ | ||
| 341 | "jsonrpc": "2.0", | ||
| 342 | "result": None, | ||
| 343 | "id": request_id | ||
| 344 | }) | ||
| 345 | await log_message("下发", response) | ||
| 346 | if not websocket.closed: | ||
| 347 | await websocket.send(response) | ||
| 348 | else: | ||
| 349 | error_response = json.dumps({ | ||
| 350 | "jsonrpc": "2.0", | ||
| 351 | "result": None, | ||
| 352 | "id": request_id if request_id else 0 | ||
| 353 | }) | ||
| 354 | await log_message("下发", error_response) | ||
| 355 | if not websocket.closed: | ||
| 356 | await websocket.send(error_response) | ||
| 357 | except Exception as e: | ||
| 358 | await log_exception_async(e, "connect连接BLE") | ||
| 359 | error_response = json.dumps({ | ||
| 360 | "jsonrpc": "2.0", | 660 | "jsonrpc": "2.0", |
| 361 | "result": None, | 661 | "result": None, |
| 362 | "id": request_id if request_id else 0 | 662 | "id": request_id |
| 363 | }) | 663 | }) |
| 364 | await log_message("下发", error_response) | 664 | await log_message("下发", response) |
| 365 | if not websocket.closed: | 665 | if not websocket.closed: |
| 366 | await websocket.send(error_response) | 666 | await websocket.send(response) |
| 367 | if self.client: | 667 | else: |
| 368 | self.client = None | 668 | # 取消之前的连接任务(如果存在) |
| 369 | pass | 669 | if self._connect_task and not self._connect_task.done(): |
| 670 | self._connect_task.cancel() | ||
| 671 | |||
| 672 | # 立即返回"连接中"状态(不等待实际连接完成) | ||
| 673 | response = json.dumps({ | ||
| 674 | "jsonrpc": "2.0", | ||
| 675 | "result": {"connecting": True}, | ||
| 676 | "id": request_id | ||
| 677 | }) | ||
| 678 | await log_message("下发", response) | ||
| 679 | if not websocket.closed: | ||
| 680 | await websocket.send(response) | ||
| 681 | |||
| 682 | # 在后台启动连接任务(不阻塞) | ||
| 683 | self._connect_task = asyncio.create_task( | ||
| 684 | self._background_connect(peripheral_id, request_id) | ||
| 685 | ) | ||
| 686 | else: | ||
| 687 | # 参数错误,立即返回 | ||
| 688 | error_response = json.dumps({ | ||
| 689 | "jsonrpc": "2.0", | ||
| 690 | "result": None, | ||
| 691 | "id": request_id if request_id else 0 | ||
| 692 | }) | ||
| 693 | await log_message("下发", error_response) | ||
| 694 | if not websocket.closed: | ||
| 695 | await websocket.send(error_response) | ||
| 370 | 696 | ||
| 371 | elif method == "write": | 697 | elif method == "write": |
| 372 | service_id = params.get("serviceId") | 698 | service_id = params.get("serviceId") |
| ... | @@ -544,7 +870,7 @@ class BLEClient: | ... | @@ -544,7 +870,7 @@ class BLEClient: |
| 544 | except Exception as e2: | 870 | except Exception as e2: |
| 545 | await log_exception_async(e2, "JSON解析后发送响应") | 871 | await log_exception_async(e2, "JSON解析后发送响应") |
| 546 | pass | 872 | pass |
| 547 | pass | 873 | pass |
| 548 | except Exception as e: | 874 | except Exception as e: |
| 549 | await log_exception_async(e, "处理请求") | 875 | await log_exception_async(e, "处理请求") |
| 550 | error_msg = json.dumps({ | 876 | error_msg = json.dumps({ |
| ... | @@ -559,7 +885,7 @@ class BLEClient: | ... | @@ -559,7 +885,7 @@ class BLEClient: |
| 559 | except Exception as e2: | 885 | except Exception as e2: |
| 560 | await log_exception_async(e2, "处理请求后发送响应") | 886 | await log_exception_async(e2, "处理请求后发送响应") |
| 561 | pass | 887 | pass |
| 562 | pass | 888 | pass |
| 563 | 889 | ||
| 564 | except websockets.exceptions.ConnectionClosed as e: | 890 | except websockets.exceptions.ConnectionClosed as e: |
| 565 | await log_exception_async(e, "WebSocket连接关闭") | 891 | await log_exception_async(e, "WebSocket连接关闭") |
| ... | @@ -567,6 +893,16 @@ class BLEClient: | ... | @@ -567,6 +893,16 @@ class BLEClient: |
| 567 | finally: | 893 | finally: |
| 568 | # 清理BLE连接和通知 | 894 | # 清理BLE连接和通知 |
| 569 | self._shutdown = True | 895 | self._shutdown = True |
| 896 | self._connecting = False | ||
| 897 | |||
| 898 | # 取消后台连接任务 | ||
| 899 | if self._connect_task and not self._connect_task.done(): | ||
| 900 | try: | ||
| 901 | self._connect_task.cancel() | ||
| 902 | except Exception as e: | ||
| 903 | await log_exception_async(e, "finally取消连接任务") | ||
| 904 | pass | ||
| 905 | |||
| 570 | if self.client and self.client.is_connected: | 906 | if self.client and self.client.is_connected: |
| 571 | try: | 907 | try: |
| 572 | # 停止所有通知 | 908 | # 停止所有通知 | ... | ... |
-
Please register or sign in to post a comment