525 lines
12 KiB
Markdown
525 lines
12 KiB
Markdown
# ✅ 巡检任务功能更新总结
|
||
|
||
## 🎯 功能实现
|
||
|
||
根据需求,已实现完整的巡检任务记录和AI识别流程。
|
||
|
||
## 📋 新增功能
|
||
|
||
### 1. ✅ 自动创建巡检记录
|
||
|
||
**何时创建**:巡检任务启动时自动创建
|
||
|
||
**InspectionTaskRecord字段**:
|
||
- `recordId`: 自动生成的记录ID
|
||
- `taskId`: 关联的巡检任务ID
|
||
- `executeTime`: 执行开始时间
|
||
- `duration`: 执行时长(秒)
|
||
- `accessory`: 视频URL(原始;处理后)
|
||
- `result`: AI识别结果摘要
|
||
- `status`: 0=成功, 1=失败, 2=部分成功
|
||
|
||
### 2. ✅ 自动保存视频
|
||
|
||
**视频保存流程**:
|
||
1. 从RTSP流录制视频(按task.duration时长)
|
||
2. 上传原始视频到MinIO
|
||
3. 保存URL到`record.accessory`
|
||
4. 继续分析处理
|
||
|
||
### 3. ✅ 调用Python服务识别
|
||
|
||
**识别流程**:
|
||
1. 创建`HttpYoloDetector`连接Python服务
|
||
2. 逐帧提取并调用YOLOv8检测
|
||
3. 每10帧检测一次(可调整)
|
||
4. 绘制检测框到视频上
|
||
5. 生成带标注的处理后视频
|
||
|
||
### 4. ✅ 更新识别结果
|
||
|
||
**result字段内容**:
|
||
```
|
||
共检测到 5 个问题,详情:垃圾(3) 烟雾(2)
|
||
```
|
||
|
||
### 5. ✅ 创建不重复告警
|
||
|
||
**去重机制**:
|
||
- 使用位置+类别生成唯一键
|
||
- 相同对象只创建一次告警
|
||
- 允许10像素的位置波动
|
||
- 60秒未检测到自动清除
|
||
|
||
**AlarmRecord包含**:
|
||
- 告警类型、内容、置信度
|
||
- 关联的任务ID和设备ID
|
||
- 告警图片(MinIO存储)
|
||
- 视频帧位置
|
||
- 未处理状态
|
||
|
||
## 🔄 完整执行流程
|
||
|
||
```
|
||
1. 用户启动巡检任务
|
||
↓
|
||
2. InspectionTaskServiceImpl.executeInspectionTask()
|
||
├── 创建InspectionTaskRecord (status=1执行中)
|
||
├── 更新InspectionTask (status=1执行中)
|
||
└── 调用performVideoAnalysisWithRecord()
|
||
↓
|
||
3. performVideoAnalysisWithRecord()
|
||
├── 录制RTSP视频流(30秒)
|
||
├── 上传原始视频到MinIO
|
||
├── 更新record.accessory = "原始视频URL"
|
||
└── 调用analyzeVideoAndUpdateRecord()
|
||
↓
|
||
4. VideoAnalysisService.analyzeVideoWithRecord()
|
||
├── 逐帧分析视频
|
||
├── 每10帧调用Python API检测
|
||
├── 发现新对象 → createAlarmRecordForRecord()
|
||
│ ├── 提取检测区域图片
|
||
│ ├── 上传告警图片到MinIO
|
||
│ └── 创建AlarmRecord(去重)
|
||
├── 绘制检测框
|
||
├── 保存处理后视频
|
||
├── 上传处理后视频到MinIO
|
||
├── 更新record.accessory += ";处理后视频URL"
|
||
└── 更新record.result = "检测结果摘要"
|
||
↓
|
||
5. 完成
|
||
├── 更新record.status = 0 (成功)
|
||
├── 更新record.duration = 实际执行时长
|
||
└── 更新task.status = 2 (已完成)
|
||
```
|
||
|
||
## 📦 新增/修改的文件
|
||
|
||
### 新增文件
|
||
|
||
1. **IInspectionTaskRecordService.java**
|
||
- 巡检记录服务接口
|
||
|
||
2. **IInspectionTaskRecordServiceImpl.java**
|
||
- 巡检记录服务实现
|
||
|
||
3. **INSPECTION-WORKFLOW.md**
|
||
- 详细工作流程文档
|
||
|
||
4. **INSPECTION-FEATURE-SUMMARY.md**
|
||
- 本文档
|
||
|
||
### 修改文件
|
||
|
||
1. **InspectionTaskServiceImpl.java**
|
||
- 添加依赖注入(InspectionTaskRecordMapper, MinioService, VMinioObjectService)
|
||
- 修改`executeInspectionTask()` - 创建记录
|
||
- 新增`performVideoAnalysisWithRecord()` - 录制视频并分析
|
||
- 新增`analyzeVideoAndUpdateRecord()` - 调用分析服务
|
||
- 新增`updateRecordFailed()` - 更新失败状态
|
||
|
||
2. **VideoAnalysisService.java**
|
||
- 添加InspectionTaskRecordMapper依赖
|
||
- 更新Python服务URL为容器名
|
||
- 更新模型名称为yolov8_detector
|
||
- 新增`analyzeVideoWithRecord()` - 带记录的视频分析
|
||
- 新增`processVideoWithRecord()` - 处理视频并记录结果
|
||
- 新增`createAlarmRecordForRecord()` - 创建去重告警
|
||
- 新增`uploadProcessedVideoForRecord()` - 上传处理后视频
|
||
|
||
## 🎯 数据流转
|
||
|
||
### InspectionTaskRecord字段变化
|
||
|
||
```
|
||
创建时:
|
||
recordId: [auto]
|
||
taskId: 1001
|
||
executeTime: 2025-09-30 14:30:00
|
||
status: 1 (执行中)
|
||
accessory: null
|
||
result: null
|
||
duration: null
|
||
|
||
录制视频后:
|
||
accessory: "http://.../inspection_1001_xxx.mp4"
|
||
|
||
分析完成后:
|
||
accessory: "http://.../inspection_1001_xxx.mp4;http://.../processed_xxx.mp4"
|
||
result: "共检测到 3 个问题,详情:垃圾(2) 烟雾(1)"
|
||
duration: 32
|
||
status: 0 (成功)
|
||
```
|
||
|
||
### AlarmRecord创建条件
|
||
|
||
仅当满足以下条件时创建告警:
|
||
1. ✅ 检测到新对象(不在缓存中)
|
||
2. ✅ 位置和类别不重复
|
||
3. ✅ 置信度超过阈值(Python服务的conf参数)
|
||
|
||
## 💾 数据库查询示例
|
||
|
||
### 查看任务执行历史
|
||
|
||
```sql
|
||
-- 查看任务的所有执行记录
|
||
SELECT
|
||
r.record_id,
|
||
r.execute_time,
|
||
r.duration,
|
||
r.status,
|
||
r.result,
|
||
(SELECT COUNT(*) FROM v_alarm_record a WHERE a.task_id = r.task_id
|
||
AND a.create_time >= r.execute_time) as alarm_count
|
||
FROM v_inspection_task_record r
|
||
WHERE r.task_id = 1001
|
||
ORDER BY r.execute_time DESC;
|
||
```
|
||
|
||
### 查看记录详情
|
||
|
||
```sql
|
||
-- 查看单条记录的完整信息
|
||
SELECT
|
||
r.*,
|
||
t.device_id,
|
||
d.ip as device_ip
|
||
FROM v_inspection_task_record r
|
||
JOIN v_inspection_task t ON r.task_id = t.task_id
|
||
JOIN v_device d ON t.device_id = d.device_id
|
||
WHERE r.record_id = 2001;
|
||
```
|
||
|
||
### 查看记录的所有告警
|
||
|
||
```sql
|
||
-- 查看某次执行产生的告警
|
||
SELECT
|
||
a.alarm_id,
|
||
a.alarm_type,
|
||
a.alarm_content,
|
||
a.confidence,
|
||
a.frame_position,
|
||
m.object_url as alarm_image_url
|
||
FROM v_alarm_record a
|
||
LEFT JOIN v_minio_object m ON a.image_oss_id = m.object_id
|
||
WHERE a.task_id = 1001
|
||
AND a.create_time >= (SELECT execute_time FROM v_inspection_task_record WHERE record_id = 2001)
|
||
AND a.create_time <= DATE_ADD((SELECT execute_time FROM v_inspection_task_record WHERE record_id = 2001),
|
||
INTERVAL (SELECT duration FROM v_inspection_task_record WHERE record_id = 2001) SECOND)
|
||
ORDER BY a.create_time;
|
||
```
|
||
|
||
## 🔧 配置参数
|
||
|
||
### 关键配置位置
|
||
|
||
**VideoAnalysisService.java**:
|
||
```java
|
||
// Python服务地址(使用容器名)
|
||
private static final String PYTHON_API_URL = "http://rtsp-python-service:8000/api/detect/file";
|
||
|
||
// 模型名称
|
||
private static final String MODEL_NAME = "yolov8_detector";
|
||
|
||
// 检测频率(每N帧)
|
||
if (frameCount % 10 == 0) { ... }
|
||
|
||
// 去重时间窗口(60秒)
|
||
(currentId - entry.getValue()) > grabber.getFrameRate() * 60
|
||
```
|
||
|
||
### 可调整参数
|
||
|
||
| 参数 | 位置 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| 检测频率 | processVideoWithRecord | 10帧 | 降低可提升性能 |
|
||
| 去重容差 | generateDetectionKey | 10像素 | 提高容差减少告警 |
|
||
| 去重时间窗口 | processVideoWithRecord | 60秒 | 缩短窗口增加告警 |
|
||
| 模型名称 | MODEL_NAME | yolov8_detector | 与Python配置对应 |
|
||
|
||
## 🚀 API接口
|
||
|
||
### Python服务接口
|
||
|
||
**请求**:
|
||
```http
|
||
POST http://rtsp-python-service:8000/api/detect/file
|
||
Content-Type: multipart/form-data
|
||
|
||
model_name: yolov8_detector
|
||
file: [图像文件]
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"model_name": "yolov8_detector",
|
||
"detections": [
|
||
{
|
||
"label": "[yolov8_detector] 垃圾",
|
||
"confidence": 0.95,
|
||
"x": 100,
|
||
"y": 200,
|
||
"width": 150,
|
||
"height": 180,
|
||
"color": 65280
|
||
}
|
||
],
|
||
"inference_time": 45.6
|
||
}
|
||
```
|
||
|
||
## 🎬 使用示例
|
||
|
||
### 示例1: 创建并执行巡检任务
|
||
|
||
```java
|
||
// 1. 创建任务
|
||
InspectionTask task = new InspectionTask();
|
||
task.setDeviceId(5001L);
|
||
task.setDuration(30); // 30秒
|
||
task.setStatus(0); // 待执行
|
||
inspectionTaskService.insertInspectionTask(task);
|
||
|
||
// 2. 启动任务(异步)
|
||
inspectionTaskService.executeInspectionTask(task.getTaskId());
|
||
|
||
// 3. 查询执行记录
|
||
List<InspectionTaskRecord> records =
|
||
inspectionTaskRecordService.selectInspectionTaskRecordList(
|
||
new InspectionTaskRecord().setTaskId(task.getTaskId())
|
||
);
|
||
```
|
||
|
||
### 示例2: 查询告警
|
||
|
||
```java
|
||
// 查询某任务的所有告警
|
||
AlarmRecord query = new AlarmRecord();
|
||
query.setTaskId(1001L);
|
||
query.setStatus(0); // 未处理
|
||
List<AlarmRecord> alarms = alarmRecordService.selectAlarmRecordList(query);
|
||
```
|
||
|
||
## ⚠️ 注意事项
|
||
|
||
### 1. 执行时间
|
||
|
||
- 录制视频需要时间(与duration设置一致)
|
||
- AI分析需要额外时间(取决于视频长度和CPU性能)
|
||
- 总执行时间 ≈ duration + 分析时间
|
||
|
||
### 2. 存储空间
|
||
|
||
每次执行会产生:
|
||
- 原始视频(~10-50MB,30秒)
|
||
- 处理后视频(~10-50MB)
|
||
- 告警图片(每个~100-500KB)
|
||
|
||
建议定期清理历史数据。
|
||
|
||
### 3. Python服务调用
|
||
|
||
- 使用HTTP调用Python服务
|
||
- 每帧调用可能较慢(已优化为每10帧)
|
||
- CPU模式下建议降低检测频率
|
||
|
||
### 4. MinIO存储
|
||
|
||
- 确保bucket已创建:
|
||
- `inspection-videos`(巡检视频)
|
||
- `alarm-images`(告警图片)
|
||
- 确保外部MinIO服务可访问
|
||
|
||
## 🔍 测试清单
|
||
|
||
### 部署后测试
|
||
|
||
- [ ] Python服务可访问
|
||
```bash
|
||
curl http://rtsp-python-service:8000/health
|
||
curl http://rtsp-python-service:8000/api/models
|
||
```
|
||
|
||
- [ ] MinIO bucket已创建
|
||
```bash
|
||
# 登录MinIO管理界面创建bucket
|
||
# 或使用mc命令创建
|
||
```
|
||
|
||
- [ ] 创建测试任务
|
||
```sql
|
||
INSERT INTO v_inspection_task (device_id, duration, status) VALUES (1, 30, 0);
|
||
```
|
||
|
||
- [ ] 执行任务并查看记录
|
||
```sql
|
||
SELECT * FROM v_inspection_task_record ORDER BY create_time DESC LIMIT 1;
|
||
```
|
||
|
||
- [ ] 查看生成的告警
|
||
```sql
|
||
SELECT * FROM v_alarm_record ORDER BY create_time DESC LIMIT 10;
|
||
```
|
||
|
||
- [ ] 验证视频URL可访问
|
||
```
|
||
访问record.accessory中的URL
|
||
```
|
||
|
||
## 📊 预期结果
|
||
|
||
### 成功执行的记录
|
||
|
||
```json
|
||
{
|
||
"recordId": 2001,
|
||
"taskId": 1001,
|
||
"executeTime": "2025-09-30 14:30:00",
|
||
"duration": 32,
|
||
"accessory": "http://49.232.154.205:10900/inspection-videos/inspection_1001_1696056600000.mp4;http://49.232.154.205:10900/inspection-videos/processed_1696056632000.mp4",
|
||
"result": "共检测到 5 个问题,详情:垃圾(3) 烟雾(2)",
|
||
"status": 0
|
||
}
|
||
```
|
||
|
||
### 生成的告警
|
||
|
||
```json
|
||
{
|
||
"alarmId": 3001,
|
||
"deviceId": 5001,
|
||
"taskId": 1001,
|
||
"alarmType": "detection",
|
||
"alarmContent": "垃圾 - 置信度: 0.95",
|
||
"imageOssId": 4001,
|
||
"framePosition": 150,
|
||
"confidence": 0.95,
|
||
"status": 0
|
||
}
|
||
```
|
||
|
||
## 🛠️ 维护和优化
|
||
|
||
### 1. 清理历史数据
|
||
|
||
```sql
|
||
-- 删除30天前的记录
|
||
DELETE FROM v_inspection_task_record
|
||
WHERE execute_time < DATE_SUB(NOW(), INTERVAL 30 DAY);
|
||
|
||
-- 删除已处理的告警
|
||
DELETE FROM v_alarm_record
|
||
WHERE status = 1 AND create_time < DATE_SUB(NOW(), INTERVAL 7 DAY);
|
||
```
|
||
|
||
### 2. 性能监控
|
||
|
||
```sql
|
||
-- 查看最近的执行统计
|
||
SELECT
|
||
DATE(execute_time) as date,
|
||
COUNT(*) as total_executions,
|
||
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as success_count,
|
||
AVG(duration) as avg_duration
|
||
FROM v_inspection_task_record
|
||
WHERE execute_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
|
||
GROUP BY DATE(execute_time)
|
||
ORDER BY date DESC;
|
||
```
|
||
|
||
### 3. 告警统计
|
||
|
||
```sql
|
||
-- 查看最近的告警统计
|
||
SELECT
|
||
DATE(create_time) as date,
|
||
alarm_type,
|
||
COUNT(*) as count,
|
||
AVG(confidence) as avg_confidence
|
||
FROM v_alarm_record
|
||
WHERE create_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
|
||
GROUP BY DATE(create_time), alarm_type
|
||
ORDER BY date DESC, count DESC;
|
||
```
|
||
|
||
## 📞 故障排查
|
||
|
||
### 问题1: Record未创建
|
||
|
||
**症状**:执行任务但`v_inspection_task_record`表无数据
|
||
|
||
**检查**:
|
||
```sql
|
||
SELECT * FROM v_inspection_task WHERE task_id = ?;
|
||
```
|
||
|
||
**解决**:
|
||
- 确认任务状态为0(待执行)
|
||
- 查看后端日志
|
||
- 检查Mapper XML配置
|
||
|
||
### 问题2: Accessory为空
|
||
|
||
**症状**:Record创建了但accessory字段为空
|
||
|
||
**检查**:
|
||
```bash
|
||
# 查看MinIO上传日志
|
||
docker-compose logs backend | grep "MinIO"
|
||
|
||
# 测试MinIO连接
|
||
curl http://49.232.154.205:10900/minio/health/live
|
||
```
|
||
|
||
**解决**:
|
||
- 确认MinIO服务可访问
|
||
- 检查application.yml中的MinIO配置
|
||
- 确认bucket已创建
|
||
|
||
### 问题3: Result为空
|
||
|
||
**症状**:视频已保存但result字段为空
|
||
|
||
**检查**:
|
||
```bash
|
||
# 查看Python服务日志
|
||
docker-compose logs python-service
|
||
|
||
# 测试Python服务
|
||
curl http://rtsp-python-service:8000/api/models
|
||
```
|
||
|
||
**解决**:
|
||
- 确认Python服务运行正常
|
||
- 确认best.pt模型文件存在
|
||
- 检查容器间网络通信
|
||
|
||
### 问题4: 告警重复
|
||
|
||
**症状**:相同对象产生多个告警
|
||
|
||
**调整去重参数**:
|
||
```java
|
||
// 在generateDetectionKey中增大容差
|
||
int x = rect.x() / 20 * 20; // 从10改为20
|
||
int y = rect.y() / 20 * 20;
|
||
```
|
||
|
||
## 📖 相关文档
|
||
|
||
- `INSPECTION-WORKFLOW.md` - 详细工作流程
|
||
- `YOLOV8-SETUP.md` - YOLOv8模型配置
|
||
- `DEPLOYMENT-NOTES.md` - 部署配置说明
|
||
- `DOCKER-QUICK-START.md` - Docker快速开始
|
||
|
||
---
|
||
|
||
**功能状态**: ✅ 已实现
|
||
**测试状态**: 待测试
|
||
**文档版本**: 1.0
|
||
**最后更新**: 2025-09-30
|
||
|
||
<EFBFBD><EFBFBD> **巡检任务记录功能已完整实现!** |