feat(projects): 更新删除工程提示并添加存储空间监控

- 修改删除工程确认提示,明确说明会永久删除所有测线和测点数据
- 在连续采集过程中每30秒检查一次磁盘空间,低于100MB时自动停止采集并弹窗提醒

📝 docs(user-manual): 添加完整用户手册

- 新增 TEM Receiver 用户手册,涵盖安装、连接、配置、采集、波形查看、测点管理、剖面分析、工程管理、数据导出导入、主题切换、存储管理及常见问题等全部功能说明
This commit is contained in:
zhoujie 2026-06-21 16:58:20 +08:00
parent 5594035dc1
commit e1ca0d8512
3 changed files with 507 additions and 1 deletions

View File

@ -275,7 +275,7 @@ export default function ProjectsScreen() {
const handleDeleteProject = (project: ProjectInfo) => { const handleDeleteProject = (project: ProjectInfo) => {
Alert.alert( Alert.alert(
'删除工程', '删除工程',
`确定删除工程「${project.name}」?\n测线数据将保留,但与工程的关联会断开`, `确定删除工程「${project.name}」?\n该工程下的所有测线和测点数据将被永久删除,不可恢复`,
[ [
{ text: '取消', style: 'cancel' }, { text: '取消', style: 'cancel' },
{ {

485
docs/user-manual.md Normal file
View File

@ -0,0 +1,485 @@
# TEM Receiver 用户手册
> 版本1.0.0
> 最后更新2026-06-20
---
## 目录
1. [简介](#一简介)
2. [安装](#二安装)
3. [首次使用](#三首次使用)
4. [连接设备](#四连接设备)
5. [参数配置](#五参数配置)
6. [数据采集](#六数据采集)
7. [波形查看](#七波形查看)
8. [测点管理](#八测点管理)
9. [剖面分析](#九剖面分析)
10. [工程管理](#十工程管理)
11. [数据导出与分享](#十一数据导出与分享)
12. [数据导入](#十二数据导入)
13. [主题切换](#十三主题切换)
14. [存储管理](#十四存储管理)
15. [常见问题](#十五常见问题)
---
## 一、简介
TEM Receiver 是六通道瞬变电磁TEM接收机的手机端控制软件。通过 WiFi 连接接收机设备,可在手机上完成参数配置、数据采集、波形查看、剖面分析和数据管理全部操作。
### 主要功能
- 通过 WiFi 无线连接 TEM 接收机
- 配置采集参数(通道数、频率、增益等)
- 单次采集和连续采集
- 实时波形显示(支持全屏、缩放、拖拽)
- 工程-测线-测点三级数据管理
- 数据导出CSV 和 .tem 格式)
- 数据分享(微信、蓝牙、文件管理器等)
- 自动深色/浅色主题
- 设备状态实时监控电流、电池、温度、GPS、SD 卡)
---
## 二、安装
### Android
1. 将 `app-release.apk` 文件传输到手机
2. 在手机上打开 APK 文件
3. 如提示"未知来源",在设置中允许安装
4. 安装完成后在桌面找到「TEM Receiver」图标
### iOS
通过 TestFlight 安装(需开发者提供邀请链接)。
---
## 三、首次使用
首次打开 App 后:
1. 所有页面会显示「请先创建工程」提示
2. 点击「前往工程页」按钮
3. 在工程页点击右上角「+ 新建」
4. 输入工程名称(如:项目名称 + 日期)
5. 确认后 App 自动创建工程和第一条测线
6. 返回波形页即可开始使用
> **重要**:所有采集数据必须归属于一个工程。没有工程时无法采集数据。
---
## 四、连接设备
### 4.1 连接 WiFi
1. 打开 TEM 接收机电源
2. 手机进入系统设置 → WiFi
3. 连接接收机的 WiFi 热点(通常名称包含设备编号)
4. 默认 IP 地址:`192.168.4.1`,端口:`4321`
### 4.2 App 内连接
1. 点击顶部状态栏的「TCP 未连接」
2. 弹出连接窗口,确认 IP 和端口
3. 点击「连接」
4. 连接成功后状态变为绿色「TCP 已连接」
### 4.3 连接状态说明
| 状态 | 颜色 | 说明 |
|------|------|------|
| 已连接 | 绿色 | 正常工作 |
| 连接中 | 黄色 | 正在连接 |
| 重连 | 黄色 | 断线后自动重连 |
| 未连接 | 灰色 | 未连接设备 |
> App 会自动保持心跳(每 8 秒),连接断开时自动重连。
---
## 五、参数配置
### 5.1 打开配置面板
在波形页点击右下角的「⚙」设置按钮,弹出参数配置面板。
### 5.2 可配置参数
| 参数 | 说明 | 默认值 |
|------|------|--------|
| 通道数 | 采集通道数量1-6 | 3 |
| 发射频率 | 发射机工作频率 | 16 Hz |
| 采样频率 | ADC 采样率 | 250 kHz |
| 采样深度 | 每通道采样点数 | 2000 |
| 叠加次数 | 信号叠加次数,提升信噪比 | 32 |
| 增益 | 放大倍数 | 1× |
| 补偿电阻 | 补偿电阻值 | 12 |
| 补偿去使能延时 | 延时参数 | 60 |
| 源模式 | 单源/双源/三源 | 单源 A |
| 反向叠加 | 123/456 通道反向叠加 | 关闭 |
| 文件前缀 | SD 卡存储文件名前缀 | 当天日期 |
### 5.3 下发配置
1. 修改参数后,面板底部按钮变为蓝色「下发配置」
2. 点击下发,等待设备回复
3. 弹出「配置成功」表示参数已生效
4. 弹出「配置超时」表示设备未响应,请检查连接
> **注意**:采集过程中不可下发配置,需先停止采集。
---
## 六、数据采集
### 6.1 单次采集
1. 确保已连接设备且已下发配置
2. 点击波形页底部的「单次」按钮
3. 设备完成一次采集后自动停止
4. 波形图显示采集结果
### 6.2 连续采集
1. 点击「连续采集」按钮
2. 设备按设定的发射频率持续采集
3. 波形图实时更新
4. 点击「停止」按钮结束采集
### 6.3 采集状态
| 状态 | 按钮显示 | 说明 |
|------|---------|------|
| 空闲 | 连续采集 / 单次 | 可以开始采集 |
| 运行中 | 停止 | 连续采集中 |
| 单次采集中 | 单次采集中… | 等待数据返回 |
### 6.4 状态信息
波形图下方实时显示:
**第一行**源模式、发射电流A、电池电压V、温度°C
**第二行**Roll、Pitch、Yaw 姿态角、GPS 状态、SD 卡状态
GPS 状态含义:
| 值 | 说明 |
|----|------|
| 未定位 | GPS 未锁定 |
| 非差分 | 普通定位 |
| 差分 | 差分定位 |
| 固定解 | RTK 固定解(最高精度) |
| 浮点解 | RTK 浮点解 |
SD 卡状态:
| 值 | 说明 |
|----|------|
| 正常 | SD 卡工作正常 |
| 未插卡 | 未检测到 SD 卡 |
| 异常 | SD 卡读写异常 |
---
## 七、波形查看
### 7.1 通道选择
波形图上方有 CH1-CH6 通道按钮,点击可显示/隐藏对应通道。各通道颜色:
- CH1 红色 / CH2 绿色 / CH3 蓝色
- CH4 橙色 / CH5 紫色 / CH6 青色
### 7.2 显示模式
点击「LIN/LOG」按钮切换
- **LIN**(线性)— 默认模式,适合查看信号波形
- **LOG**(对数)— 适合查看大动态范围信号
### 7.3 全屏查看
1. 点击通道栏右侧的「⛶」全屏按钮
2. 进入全屏模式,支持横屏显示
3. 手势操作:
- **单指拖拽** — 平移波形
- **双指捏合** — 缩放波形
- **双击** — 恢复原始视图
4. 右上角缩放倍率显示(如 ×2.5
5. 点击「✕」或按手机返回键退出全屏
---
## 八、测点管理
切换到底部「测点」Tab 页。
### 8.1 测线选择
页面顶部显示当前测线信息,点击可切换到当前工程下的其他测线。
> **注意**:切换测线会同时切换活跃采集目标,后续采集的数据将存入所选测线。
### 8.2 测点列表
以表格形式显示当前测线的所有测点:
| 列 | 说明 |
|----|------|
| # | 测点编号 |
| 时间 | 采集时间UTC |
| GPS | 定位状态(✓/✗) |
| 经度 | GPS 经度 |
| 纬度 | GPS 纬度 |
| 叠加 | 叠加次数 |
| × | 删除按钮 |
### 8.3 查看波形
点击任意测点行,弹出波形详情窗口,显示该测点的完整波形数据。
### 8.4 删除测点
点击测点行右侧的「×」按钮,确认后删除该测点及其波形数据。
### 8.5 导出 CSV
点击「导出 CSV」按钮导出当前测线的所有测点数据为 CSV 文件。
---
## 九、剖面分析
切换到底部「剖面」Tab 页。
### 9.1 测线选择
与测点页相同,顶部可切换测线查看不同测线的剖面。
### 9.2 剖面显示
自动加载所选测线的全部测点波形数据,生成剖面图。
### 9.3 门窗配置
点击设置按钮可配置门窗参数:
| 参数 | 说明 |
|------|------|
| 起始时间 | 门窗起始时间μs |
| 结束时间 | 门窗结束时间μs |
| 门窗数量 | 门窗个数 |
| 间距模式 | 对数间距 / 线性间距 |
---
## 十、工程管理
切换到底部「工程」Tab 页。
### 10.1 数据组织结构
```
工程Project
└── 测线Session
└── 测点Frame
├── 元数据GPS、电流、温度…
└── 波形数据(.bin 文件)
```
### 10.2 新建工程
1. 点击右上角「+ 新建」
2. 输入工程名称
3. 确认后自动创建工程和第一条测线
### 10.3 打开工程
点击工程卡片上的「打开」按钮,可选择:
- **继续最近测线** — 恢复上次采集的测线
- **新建测线** — 在该工程下创建新测线
### 10.4 管理测线
展开工程卡片可看到该工程下的所有测线:
| 操作 | 说明 |
|------|------|
| 激活 | 切换到该测线(后续采集数据存入此测线) |
| 导出 | 导出该测线的 CSV 数据 |
| 删除 | 删除该测线及其所有测点数据 |
| + 新建测线 | 在该工程下新建一条测线 |
> 当前活跃测线以蓝色高亮显示,且不可删除(需先切换到其他测线)。
### 10.5 其他操作
| 操作 | 说明 |
|------|------|
| .tem | 导出整个工程为 .tem 文件(包含所有测线和波形数据) |
| 改名 | 重命名工程 |
| 删除 | 删除工程及其所有数据(不可恢复) |
---
## 十一、数据导出与分享
### 11.1 导出方式
导出时弹出选择:
- **分享** — 打开系统分享面板,可发送到微信、蓝牙、邮件等
- **保存到文件** — 选择手机上的目录保存文件如下载目录、SD 卡)
### 11.2 导出格式
| 格式 | 内容 | 适用场景 |
|------|------|---------|
| CSV | 测点元数据GPS、电流、温度等 | Excel 查看、数据分析 |
| .tem | 完整工程数据(元数据 + 全部波形) | 数据备份、跨设备传输、导入恢复 |
### 11.3 CSV 文件内容
```
FrameID, UTC, Longitude, Latitude, Altitude, GPS, SD,
AmpRatio, Current, Temp, Battery, Roll, Pitch, Yaw,
CH1_peak_uV, CH2_peak_uV, CH3_peak_uV, ...
```
### 11.4 .tem 文件内容
ZIP 压缩包,包含:
```
project.json ← 工程信息 + 设备配置
frames/
测线ID_1/
000000.bin ← 测点0 完整波形
000001.bin ← 测点1 完整波形
...
测线ID_2/
...
```
---
## 十二、数据导入
### 12.1 导入 .tem 文件
1. 在工程页点击「导入 .tem」
2. 选择 .tem 文件(从文件管理器、微信接收的文件等)
3. 等待导入完成
4. 导入后自动创建新工程,保留所有测线和波形数据
### 12.2 注意事项
- 导入的工程会创建新的工程 ID不会覆盖已有工程
- 导入过程可能需要几秒到几分钟,取决于数据量
- 导入期间请勿关闭 App
---
## 十三、主题切换
App 自动跟随手机系统的深色/浅色模式设置。
- **Android**:设置 → 显示 → 深色模式
- **iOS**:设置 → 显示与亮度 → 外观
无需在 App 内手动切换。
---
## 十四、存储管理
### 14.1 存储占用
连续采集时存储占用参考3 通道 2000 点):
| 时间 | 16Hz 连续采集 |
|------|-------------|
| 1 分钟 | ~46 MB |
| 10 分钟 | ~460 MB |
| 1 小时 | ~2.7 GB |
### 14.2 空间不足处理
当手机剩余存储不足 100MB 时App 会自动停止采集并弹窗提醒。
### 14.3 释放空间
1. 先导出需要保留的工程数据(.tem 文件)
2. 分享或保存到外部存储
3. 在工程页删除已备份的工程
4. 工程删除后,关联的所有测线、测点和波形文件将被永久删除
---
## 十五、常见问题
### Q: 连接不上设备
1. 确认手机已连接到设备的 WiFi 热点
2. 确认 IP 地址和端口正确(默认 192.168.4.1:4321
3. 确认没有其他设备(如 Qt 上位机)同时连接
4. 尝试断开后重新连接
### Q: 配置下发超时
1. 检查 TCP 连接是否正常(顶部状态栏)
2. 确认不在采集过程中(需先停止采集)
3. 断开重连后重试
### Q: 波形显示为直线
1. 切换到 LIN线性模式查看
2. 检查是否只显示了一个通道(其他通道可能在不同 Y 范围)
3. 确认增益设置是否正确
### Q: 连续采集无法停止
1. 多次点击停止按钮App 会自动重试 3 次)
2. 如仍无法停止,断开 TCP 连接后重新连接
### Q: 波形全屏后无法操作
1. 确保使用单指拖拽、双指缩放
2. 双击可恢复原始视图
3. 点击右上角「✕」退出全屏
### Q: 采集数据未显示在测点页
1. 切换到正确的测线(使用顶部测线选择器)
2. 等待几秒让数据库加载完成
3. 下拉刷新或切换 Tab 后重新进入
### Q: 导出的文件在微信中显示为 com.tencent.mm...
这是正常的,系统分享面板中微信的图标可能显示为包名。点击即可正常发送文件。
### Q: 手机发热严重
连续采集时数据处理量大,属于正常现象。建议:
- 关闭不需要的后台应用
- 避免在高温环境下长时间连续采集
- Release 版比 Debug 版性能更好,发热更少
### Q: 数据丢失
- 采集数据实时存入数据库和 bin 文件App 闪退不会丢失已保存的数据
- 卸载 App 会清除所有数据,请先导出备份
- 建议定期导出 .tem 文件到外部存储
---
## 技术支持
如遇到本手册未覆盖的问题,请联系开发团队。

View File

@ -95,6 +95,9 @@ export function handleIncomingFrame(frame: RawFrame) {
let _lastUIUpdateTime = 0; let _lastUIUpdateTime = 0;
const UI_THROTTLE_MS = 150; const UI_THROTTLE_MS = 150;
let _processing = false; let _processing = false;
let _lastDiskCheckTime = 0;
const DISK_CHECK_INTERVAL_MS = 30000;
const MIN_FREE_BYTES = 100 * 1024 * 1024; // 100MB
function processMeasurementPayload( function processMeasurementPayload(
payload: Uint8Array, payload: Uint8Array,
@ -126,6 +129,24 @@ function processMeasurementPayload(
void StorageService.persistFrame(measurementFrame, dataStore.sessionId); void StorageService.persistFrame(measurementFrame, dataStore.sessionId);
// Periodic disk space check during continuous acquisition
const now2 = Date.now();
if (now2 - _lastDiskCheckTime > DISK_CHECK_INTERVAL_MS) {
_lastDiskCheckTime = now2;
void (async () => {
try {
const FileSystem = await import('expo-file-system/legacy');
const free = await FileSystem.getFreeDiskStorageAsync();
if (free < MIN_FREE_BYTES) {
const { Alert } = require('react-native');
Alert.alert('存储空间不足', `剩余空间不足 100MB已自动停止采集。\n请清理手机存储或导出数据后删除旧数据。`);
void deviceStop();
devStore.setDeviceStatus('idle');
}
} catch {}
})();
}
const isSingle = devStore.deviceStatus === 'single'; const isSingle = devStore.deviceStatus === 'single';
const now = Date.now(); const now = Date.now();
if (isSingle || now - _lastUIUpdateTime >= UI_THROTTLE_MS) { if (isSingle || now - _lastUIUpdateTime >= UI_THROTTLE_MS) {