去队列

This commit is contained in:
lhx
2026-06-09 10:29:17 +08:00
parent a40feb04ee
commit c25257b0e3
2 changed files with 22 additions and 76 deletions

View File

@@ -384,15 +384,13 @@ class BtSerial:
class MobileService:
"""
手机通信主服务。
- 后台线程处理串口读写 + 协议解析
- 手机发 ?0100 轮询时自动发送排队测量数据
- 后台线程处理串口读写 + 协议解析 (握手/控制码自动响应)
- 收到 send 命令时直接发送数据,不等手机轮询
"""
def __init__(self):
self.serial: Optional[BtSerial] = None
self.proto = ProtocolHandler()
self._pending: list[tuple[bytes, bytes]] = [] # 待发送测量数据
self._sent_count = 0
self._poll_thread: Optional[threading.Thread] = None
self._log_cb = None
@@ -408,14 +406,6 @@ class MobileService:
def phone_connected(self) -> bool:
return self.serial is not None and self.serial.phone_connected
@property
def sent_count(self) -> int:
return self._sent_count
@property
def pending_count(self) -> int:
return len(self._pending)
# ── 连接 ──
def connect(self, port: str) -> bool:
@@ -449,64 +439,38 @@ class MobileService:
self._poll_thread = None
def _poll_loop(self):
"""后台轮询 — 处理收数据 + 自动响应"""
"""后台轮询 — 处理收数据 + 自动响应握手/控制码"""
while self.serial and self.serial.running:
try:
data = self.serial.read_all()
if data:
responses, has_query = self.proto.feed(data)
responses, _ = self.proto.feed(data)
# 发送协议响应 (握手/控制码)
for rsp in responses:
self.serial.write(rsp)
# 手机轮询 (±0100) + 有待发送数据 → 发送
if has_query and self._pending:
self._send_data()
time.sleep(0.05)
except Exception:
time.sleep(0.1)
# ── 数据发送 ──
# ── 数据发送 (直接发,不等轮询) ──
def send_measurement(self, staff_reading: float, distance: float) -> bool:
"""直接构建 M5 两帧并通过串口发送"""
if not self.serial or not self.serial.running:
self._log("⚠ 串口未打开")
return False
def queue_measurement(self, staff_reading: float, distance: float):
"""将测量数据加入发送队列"""
line1, line2 = MeasurementBuilder.build(staff_reading, distance)
self._pending.append((line1, line2))
self._log(f"+ 队列: R={staff_reading:.5f}m HD={distance:.3f}m "
f"(共{len(self._pending)}条)")
def _send_data(self):
"""发送队列中第一条 (由轮询触发)"""
if not self._pending or not self.serial:
return
line1, line2 = self._pending.pop(0)
self.serial.write(line1)
time.sleep(0.03) # 两帧间隔 (模拟水准仪)
self.serial.write(line2)
self._sent_count += 1
self._log(f"↑ 发送 #{self._sent_count}")
def force_send(self, ignore_phone: bool = False) -> bool:
"""强制发送 (不等轮询)"""
if not self.serial or not self.serial.running:
self._log("⚠ 串口未打开")
return False
if not self.serial.phone_connected and not ignore_phone:
self._log("⚠ 手机未连接")
return False
if not self._pending:
self._log("⚠ 无待发送数据")
return False
self._send_data()
self._log(f"↑ 直接发送: R={staff_reading:.5f}m HD={distance:.3f}m")
return True
def clear_queue(self):
self._pending.clear()
# ════════════════════════════════════════════════════════════════
# FastAPI — 全局状态
@@ -563,8 +527,6 @@ async def status():
info = {
"connected": svc.connected,
"phone_connected": svc.phone_connected,
"pending_count": svc.pending_count,
"sent_count": svc.sent_count,
}
if svc.serial:
info["port"] = svc.serial.port
@@ -587,8 +549,6 @@ async def test():
"connected": True,
"port": svc.serial.port,
"phone_connected": svc.phone_connected,
"pending_count": svc.pending_count,
"sent_count": svc.sent_count,
"total_rx": svc.serial.total_rx,
"total_tx": svc.serial.total_tx,
}
@@ -633,9 +593,7 @@ async def send_command(req: CommandRequest):
发送命令。
支持的命令:
send <R> <HD> → 添加测量到队列 如: "send 0.89182 3.323"
force → 强制发送队列中数据 (不等轮询)
clear → 清空待发送队列
send <R> <HD> → 直接发送测量数据 如: "send 0.89182 3.323"
disconnect → 断开蓝牙连接
"""
cmd = req.cmd.strip()
@@ -663,37 +621,20 @@ async def send_command(req: CommandRequest):
except ValueError:
raise HTTPException(status_code=400, detail="参数必须是数字")
def _queue():
def _send():
with svc_lock:
if not svc.connected:
raise HTTPException(status_code=400,
detail="未连接, 请先 POST /connect")
svc.queue_measurement(r, hd)
ok = svc.send_measurement(r, hd)
return {
"command": "send",
"staff_reading": r,
"distance": hd,
"pending_count": svc.pending_count,
"status": "ok" if ok else "error",
}
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, _queue)
if action == "force":
def _force():
with svc_lock:
ok = svc.force_send(ignore_phone=False)
return {
"command": "force",
"status": "ok" if ok else "skipped",
"pending_count": svc.pending_count,
}
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, _force)
if action == "clear":
with svc_lock:
svc.clear_queue()
return {"command": "clear", "pending_count": svc.pending_count}
return await loop.run_in_executor(None, _send)
# ── 原始数据发送 ──
if not svc.connected: