Compare commits
2 Commits
a40feb04ee
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ad0488b26 | |||
| c25257b0e3 |
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
.venv
|
||||||
|
.claude
|
||||||
|
*.pyc
|
||||||
|
*\__pycache__
|
||||||
75
README.md
75
README.md
@@ -82,19 +82,7 @@ send.py --type disconnect --port 58000
|
|||||||
|
|
||||||
PC 端蓝牙 SPP 作为服务端,等待手机 APP 连接后,模拟 Trimble DiNi 03 水准仪向手机发送 M5 格式测量数据。
|
PC 端蓝牙 SPP 作为服务端,等待手机 APP 连接后,模拟 Trimble DiNi 03 水准仪向手机发送 M5 格式测量数据。
|
||||||
|
|
||||||
### 工作原理
|
数据发送为**直接发送**(调用 API 时立即通过串口发出,不等手机轮询)。
|
||||||
|
|
||||||
```
|
|
||||||
手机APP ──蓝牙──▶ 蓝牙模块 ──SPP串口──▶ PC (本服务)
|
|
||||||
▲ │
|
|
||||||
│ ?0100 轮询 │ 测量数据 (M5)
|
|
||||||
└────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
- 手机通过蓝牙连接到 PC 的蓝牙模块
|
|
||||||
- 手机 APP 周期性发送 `?0100` 轮询水准仪
|
|
||||||
- 本服务响应协议握手/控制码,并在收到轮询时发送排队测量数据
|
|
||||||
- 手机似乎只接收不回复确认
|
|
||||||
|
|
||||||
### 启动
|
### 启动
|
||||||
|
|
||||||
@@ -103,7 +91,7 @@ cd shouji
|
|||||||
python main.py # 默认 0.0.0.0:58100
|
python main.py # 默认 0.0.0.0:58100
|
||||||
python main.py --port 8081 # 指定 HTTP 端口
|
python main.py --port 8081 # 指定 HTTP 端口
|
||||||
python main.py --bt-port COM3 # 启动时自动连接蓝牙 COM 口
|
python main.py --bt-port COM3 # 启动时自动连接蓝牙 COM 口
|
||||||
```
|
python main.py --level-url http://192.168.1.100:58000 # 指定水准仪服务地址
|
||||||
|
|
||||||
### API 接口
|
### API 接口
|
||||||
|
|
||||||
@@ -111,21 +99,19 @@ python main.py --bt-port COM3 # 启动时自动连接蓝牙 COM
|
|||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `GET` | `/` | 服务状态 + COM 口列表 |
|
| `GET` | `/` | 服务状态 + COM 口列表 |
|
||||||
| `GET/POST` | `/test` | 测试:未连接→COM列表;已连接→手机连接状态+统计 |
|
| `GET/POST` | `/test` | 测试:未连接→COM列表;已连接→手机连接状态+统计 |
|
||||||
| `GET` | `/status` | 详细状态:手机连接、待发送数、收发字节数 |
|
| `GET` | `/status` | 详细状态:手机连接、收发字节数 |
|
||||||
| `POST` | `/connect` | `{"port":"COM3"}` 打开蓝牙 COM 口 |
|
| `POST` | `/connect` | `{"port":"COM3"}` 打开蓝牙 COM 口 |
|
||||||
| `POST` | `/disconnect` | 断开 |
|
| `POST` | `/disconnect` | 断开 |
|
||||||
| `POST` | `/command` | `{"cmd":"..."}` 发送命令 |
|
| `POST` | `/command` | `{"cmd":"..."}` 发送命令 |
|
||||||
|
| `POST` | `/measure-and-send` | 一键操作:触发水准仪测量→获取数据→发到手机 |
|
||||||
|
|
||||||
### send.py 调用示例
|
### send.py 调用示例
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 从上级目录执行
|
# 从上级目录执行
|
||||||
send.py --type test --port 58100
|
send.py --type test --port 58100
|
||||||
send.py --type connect --data COM3 --port 58100 # 连接蓝牙 COM 口
|
send.py --type connect --data COM3 --port 58100 # 连接蓝牙 COM 口
|
||||||
send.py --type command --data "send 0.89182 3.323" --port 58100 # 添加测量到队列
|
send.py --type command --data "send 0.89182 3.323" --port 58100 # 直接发送测量数据
|
||||||
send.py --type command --data "send 1.50000 5.000" --port 58100 # 再添加一条
|
|
||||||
send.py --type command --data "force" --port 58100 # 强制发送 (不等轮询)
|
|
||||||
send.py --type command --data "clear" --port 58100 # 清空队列
|
|
||||||
send.py --type disconnect --port 58100
|
send.py --type disconnect --port 58100
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -133,12 +119,26 @@ send.py --type disconnect --port 58100
|
|||||||
|
|
||||||
| 命令 | 格式 | 说明 |
|
| 命令 | 格式 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `send` | `send <R> <HD>` | 添加测量到发送队列 (R=标尺读数, HD=距离) |
|
| `send` | `send <R> <HD>` | 直接发送测量数据到手机 (R=标尺读数, HD=距离) |
|
||||||
| `force` | `force` | 强制立即发送队列中第一条 |
|
|
||||||
| `clear` | `clear` | 清空待发送队列 |
|
|
||||||
| `disconnect` | `disconnect` | 断开蓝牙连接 |
|
| `disconnect` | `disconnect` | 断开蓝牙连接 |
|
||||||
| 原始 | `hex:AABBCC` | 发送原始 hex 字节 |
|
| 原始 | `hex:AABBCC` | 发送原始 hex 字节 |
|
||||||
|
|
||||||
|
### /measure-and-send — 一键测量+发送
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# curl 调用 (send.py 暂不支持此接口,直接用 curl)
|
||||||
|
curl -X POST http://127.0.0.1:58100/measure-and-send \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{}'
|
||||||
|
|
||||||
|
# 指定水准仪服务地址
|
||||||
|
curl -X POST http://127.0.0.1:58100/measure-and-send \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"level_url":"http://192.168.1.100:58000"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
工作流:先向水准仪服务发 `FML` 触发测量 → 解析返回的 `staff_reading` + `distance` → 直接通过蓝牙发到手机。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 共享客户端 (`send.py`)
|
## 4. 共享客户端 (`send.py`)
|
||||||
@@ -178,21 +178,34 @@ python send.py --type command --data "force"
|
|||||||
|
|
||||||
### 场景: 电脑读取水准仪数据 → 发送到手机 APP
|
### 场景: 电脑读取水准仪数据 → 发送到手机 APP
|
||||||
|
|
||||||
|
**方式一:一键操作 (推荐)**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 终端1: 启动水准仪服务
|
# 终端1: 启动水准仪服务
|
||||||
cd shuizhunyi
|
cd shuizhunyi && python main.py --dini-port COM6
|
||||||
python main.py --dini-port COM6 # 端口 58000
|
|
||||||
|
|
||||||
# 终端2: 启动手机通信服务
|
# 终端2: 启动手机通信服务
|
||||||
cd shouji
|
cd shouji && python main.py --bt-port COM3
|
||||||
python main.py --bt-port COM3 # 端口 58100
|
|
||||||
|
# 一键: 触发测量 + 发送到手机
|
||||||
|
curl -X POST http://127.0.0.1:58100/measure-and-send \
|
||||||
|
-H "Content-Type: application/json" -d '{}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**方式二:分步操作**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 终端1: 启动水准仪服务
|
||||||
|
cd shuizhunyi && python main.py --dini-port COM6
|
||||||
|
|
||||||
|
# 终端2: 启动手机通信服务
|
||||||
|
cd shouji && python main.py --bt-port COM3
|
||||||
|
|
||||||
# 终端3: 操作
|
# 终端3: 操作
|
||||||
python send.py --type command --data "FML" --port 58000 # 从水准仪获取测量
|
python send.py --type command --data "FML" --port 58000 # 触发水准仪测量
|
||||||
# → 假设返回 staff_reading=0.89182, distance=3.323
|
# → 返回 staff_reading=0.89182, distance=3.323
|
||||||
|
|
||||||
python send.py --type command --data "send 0.89182 3.323" --port 58100 # 排队发给手机
|
python send.py --type command --data "send 0.89182 3.323" --port 58100 # 直接发到手机
|
||||||
# 手机APP发送 ?0100 轮询时自动收到数据
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
182
shouji/main.py
182
shouji/main.py
@@ -16,7 +16,8 @@ API 接口:
|
|||||||
POST /connect {port} 打开蓝牙 COM 口, 等待手机连接
|
POST /connect {port} 打开蓝牙 COM 口, 等待手机连接
|
||||||
POST /disconnect 断开
|
POST /disconnect 断开
|
||||||
POST /command {cmd} 命令: send <r> <hd> 发送测量 | disconnect 断开
|
POST /command {cmd} 命令: send <r> <hd> 发送测量 | disconnect 断开
|
||||||
GET /status 手机连接状态 + 待发送队列
|
GET /status 手机连接状态
|
||||||
|
POST /measure-and-send 触发水准仪测量(FML)→获取数据→发到手机 (一键操作) + 待发送队列
|
||||||
|
|
||||||
协议说明:
|
协议说明:
|
||||||
手机→PC: 0x02F0... 握手 | ?0100 轮询 | 0x050B8D 控制码 | KENC 编码
|
手机→PC: 0x02F0... 握手 | ?0100 轮询 | 0x050B8D 控制码 | KENC 编码
|
||||||
@@ -29,6 +30,8 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
import threading
|
import threading
|
||||||
import asyncio
|
import asyncio
|
||||||
import argparse
|
import argparse
|
||||||
@@ -46,6 +49,7 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
DEFAULT_HTTP_PORT = 58100
|
DEFAULT_HTTP_PORT = 58100
|
||||||
DEFAULT_BAUDRATE = 9600
|
DEFAULT_BAUDRATE = 9600
|
||||||
|
_config = {"level_url": "http://127.0.0.1:58000"} # 水准仪服务地址
|
||||||
|
|
||||||
# ── 协议常量 ──
|
# ── 协议常量 ──
|
||||||
HANDSHAKE_PHONE = bytes([0x02, 0xF0, 0x00, 0x00, 0xDE, 0x03, 0x00, 0x07])
|
HANDSHAKE_PHONE = bytes([0x02, 0xF0, 0x00, 0x00, 0xDE, 0x03, 0x00, 0x07])
|
||||||
@@ -384,15 +388,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 +410,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 +443,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 — 全局状态
|
||||||
@@ -524,6 +492,10 @@ class CommandRequest(BaseModel):
|
|||||||
cmd: str
|
cmd: str
|
||||||
|
|
||||||
|
|
||||||
|
class MeasureAndSendRequest(BaseModel):
|
||||||
|
level_url: str = "" # 留空则使用默认地址
|
||||||
|
|
||||||
|
|
||||||
# ════════════════════════════════════════════════════════════════
|
# ════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
@@ -563,8 +535,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 +557,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,
|
||||||
}
|
}
|
||||||
@@ -625,6 +593,80 @@ async def disconnect():
|
|||||||
return await loop.run_in_executor(None, _do)
|
return await loop.run_in_executor(None, _do)
|
||||||
|
|
||||||
|
|
||||||
|
# ── POST /measure-and-send ──
|
||||||
|
|
||||||
|
@app.post("/measure-and-send")
|
||||||
|
async def measure_and_send(req: MeasureAndSendRequest = MeasureAndSendRequest()):
|
||||||
|
"""
|
||||||
|
一键操作: 触发水准仪测量(FML) → 拿到数据 → 发到手机蓝牙。
|
||||||
|
|
||||||
|
请求体 (均可选):
|
||||||
|
{ "level_url": "http://127.0.0.1:58000" } # 水准仪服务地址
|
||||||
|
"""
|
||||||
|
level_url = req.level_url.strip() or _config["level_url"]
|
||||||
|
|
||||||
|
def _do():
|
||||||
|
# 1. 检查本服务蓝牙已连接
|
||||||
|
if not svc.connected:
|
||||||
|
raise HTTPException(status_code=400,
|
||||||
|
detail="手机蓝牙未连接, 请先 POST /connect")
|
||||||
|
|
||||||
|
# 2. 向水准仪服务发 FML 触发测量
|
||||||
|
try:
|
||||||
|
fm_req = urllib.request.Request(
|
||||||
|
f"{level_url}/command",
|
||||||
|
data=json.dumps({"cmd": "FML"}).encode("utf-8"),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
method="POST",
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(fm_req, timeout=15) as resp:
|
||||||
|
fm_result = json.loads(resp.read().decode("utf-8"))
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=502,
|
||||||
|
detail=f"无法连接到水准仪服务 {level_url}: {e.reason}"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=502,
|
||||||
|
detail=f"调用水准仪服务失败: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. 解析测量结果
|
||||||
|
if fm_result.get("status") != "ok":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=502,
|
||||||
|
detail=f"水准仪测量失败: {fm_result.get('error_desc') or fm_result.get('raw_text') or '未知错误'}"
|
||||||
|
)
|
||||||
|
|
||||||
|
r = fm_result.get("staff_reading")
|
||||||
|
hd = fm_result.get("distance")
|
||||||
|
if r is None or hd is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=502,
|
||||||
|
detail=f"水准仪返回数据不完整: {json.dumps(fm_result, ensure_ascii=False)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. 直接发到手机蓝牙
|
||||||
|
ok = svc.send_measurement(r, hd)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "ok" if ok else "send_error",
|
||||||
|
"measurement": {
|
||||||
|
"staff_reading": r,
|
||||||
|
"distance": hd,
|
||||||
|
"height_diff": fm_result.get("height_diff"),
|
||||||
|
"std_dev": fm_result.get("std_dev"),
|
||||||
|
"raw_text": fm_result.get("raw_text"),
|
||||||
|
},
|
||||||
|
"level_service": level_url,
|
||||||
|
"phone_sent": ok,
|
||||||
|
}
|
||||||
|
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
return await loop.run_in_executor(None, _do)
|
||||||
|
|
||||||
|
|
||||||
# ── POST /command ──
|
# ── POST /command ──
|
||||||
|
|
||||||
@app.post("/command")
|
@app.post("/command")
|
||||||
@@ -633,9 +675,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 +703,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:
|
||||||
@@ -729,8 +752,13 @@ def main():
|
|||||||
help=f"HTTP 服务端口 (默认 {DEFAULT_HTTP_PORT})")
|
help=f"HTTP 服务端口 (默认 {DEFAULT_HTTP_PORT})")
|
||||||
parser.add_argument("--bt-port", default=None,
|
parser.add_argument("--bt-port", default=None,
|
||||||
help="启动时自动连接的蓝牙 COM 口 (如 COM3)")
|
help="启动时自动连接的蓝牙 COM 口 (如 COM3)")
|
||||||
|
parser.add_argument("--level-url", default=_config["level_url"],
|
||||||
|
help=f"水准仪服务地址 (默认 {_config['level_url']})")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# 更新全局配置
|
||||||
|
_config["level_url"] = args.level_url
|
||||||
|
|
||||||
# ── 打印可连接 COM 口 ──
|
# ── 打印可连接 COM 口 ──
|
||||||
print_com_list()
|
print_com_list()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user