去队列
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
.venv
|
||||||
|
.claude
|
||||||
|
*.pyc
|
||||||
|
*\__pycache__
|
||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user