去队列

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

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.venv
.claude
*.pyc
*\__pycache__

View File

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