Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66f85bc74a | |||
| 16ce5d2591 | |||
| 4efe6fa6ca | |||
| 04fee9fc48 |
+64
@@ -0,0 +1,64 @@
|
||||
# 依赖目录
|
||||
node_modules/
|
||||
vendor/
|
||||
|
||||
# 编译输出
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
|
||||
# IDE 和编辑器文件
|
||||
.idea/
|
||||
.vscode/
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.vs/
|
||||
|
||||
# 操作系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# 环境文件
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# 日志文件
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# 临时文件
|
||||
*.tmp
|
||||
*.temp
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# 测试覆盖率报告
|
||||
coverage/
|
||||
|
||||
# 缓存目录
|
||||
.cache/
|
||||
.npm/
|
||||
.yarn/
|
||||
|
||||
# 其他
|
||||
*.bak
|
||||
*.zip
|
||||
*.tar.gz
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
## 更新日志
|
||||
|
||||
### 2025-01-21更新
|
||||
0. 更新代码结构
|
||||
### 2025-01-17更新
|
||||
0. 本次主要更新paper页面;
|
||||
1. 接入了memarid.js, 后端分析报告添加了对流程图的输出,前端渲染流程图;
|
||||
@@ -35,29 +37,32 @@ Initial commit
|
||||
|
||||
## 系统架构
|
||||
|
||||
### 1. 采集端 (boot.py)
|
||||
### 1. 数据采集 (boot/)
|
||||
- 基于MicroPython实现的高速数据采集器
|
||||
- 支持多通道数据采集
|
||||
- 使用UDP协议传输数据
|
||||
- 支持设备序列号注册机制
|
||||
|
||||
### 2. 服务器端 (data.py)
|
||||
### 2. 数据处理 (data/)
|
||||
- 基于Python asyncio的异步UDP服务器
|
||||
- 支持数据解析和转发
|
||||
- 与MongoDB集成存储实验数据
|
||||
- 实现WebSocket实时数据推送
|
||||
- 设备在线状态管理
|
||||
|
||||
### 3. 设备管理 (device.py)
|
||||
### 3. 设备管理 (device-manager/)
|
||||
- 设备注册和管理API
|
||||
- 序列号生成和验证
|
||||
- 设备状态监控
|
||||
- Redis集成用于在线状态追踪
|
||||
- device-register.html: 设备注册界面
|
||||
|
||||
### 4. Web界面
|
||||
- lab.html: 主界面
|
||||
- device.html: 设备管理界面
|
||||
- device-monitor.html: 单个设备数据监控界面
|
||||
### 4. 主程序 (app/)
|
||||
- 主程序 app/main.py
|
||||
- web页面 app/frontend/
|
||||
- 项目管理
|
||||
- 实验管理
|
||||
- 报告生成
|
||||
|
||||
## 主要功能
|
||||
1. 高速数据采集
|
||||
@@ -67,112 +72,49 @@ Initial commit
|
||||
5. 在线状态追踪
|
||||
|
||||
## 使用说明
|
||||
### 文件夹
|
||||
1. history 保存了历史版本代码,可以忽略
|
||||
2. lab 项目历史版本,可以忽略
|
||||
|
||||
### 文件结构
|
||||
lab/
|
||||
├── #后端
|
||||
├── boot.py # 采集端程序
|
||||
├── data.py # 数据处理服务器
|
||||
├── device.py # 设备管理API
|
||||
├── main.py # 主程序
|
||||
├── #前端
|
||||
├── device.html # 设备管理
|
||||
├── device-monitor.html # 单个设备数据监控界面
|
||||
├── device-register.html # 设备注册
|
||||
├── paper.html # 文献分析
|
||||
├── reports.html # 报告管理
|
||||
├── lab.html # 前端主界面
|
||||
└── login.html # 登录界面
|
||||
|
|
||||
└── README.md # 项目说明
|
||||
|
||||
#### 主程序 main.py https://dev.obscura.work/lab
|
||||
├── app/ #主程序文件夹
|
||||
| ├── app/ # app主程序
|
||||
| ├── docs/ # app文档
|
||||
| ├── frontend/ # app前端页面
|
||||
| ├── main.py # 主程序
|
||||
| └── README.md # 项目说明
|
||||
├── boot/ # 采集端
|
||||
├── data/ # 数据处理服务器
|
||||
├── device-manager/ # 设备管理
|
||||
└── README.md # 项目说明
|
||||
|
||||
### 项目部署
|
||||
#### 主程序 app/main.py
|
||||
1. 服务器:222.186.10.253
|
||||
2. PORT = 6000
|
||||
3. 数据库:
|
||||
mongodb: 222.186.10.253:27017/lab
|
||||
Redis:222.186.10.253:6379; db199、201-208
|
||||
4. 访问地址:[https://dev.obscura.work/lab]
|
||||
|
||||
#### 设备管理 device.py https://dev.obscura.work/devices
|
||||
#### 前端页面 app/frontend/
|
||||
1. 访问地址:[https://beta.obscura.work/lab]
|
||||
|
||||
#### 设备管理 device-manager/device-register.py
|
||||
1. 服务器:222.186.10.253
|
||||
2. PORT = 6001
|
||||
3. 数据库:
|
||||
mongodb: 222.186.10.253:27017/lab
|
||||
Redis:222.186.10.253:6379; db200
|
||||
|
||||
#### 采集端配置 boot.py
|
||||
4. 访问地址:[https://dev.obscura.work/devices]
|
||||
|
||||
#### 采集端配置 boot/
|
||||
SSID = "Obscura"
|
||||
UDP_HOST = "222.186.10.253"
|
||||
UDP_PORT = 6002
|
||||
|
||||
#### 数据处理 data.py
|
||||
#### 数据处理 data/
|
||||
1. 服务器:222.186.10.253
|
||||
2. PORT = 6002
|
||||
3. 数据库:
|
||||
mongodb: 222.186.10.253:27017/lab
|
||||
Redis:222.186.10.253:6379; db200; data type:stream
|
||||
|
||||
#### 前端页面 https://beta.obscura.work/lab/
|
||||
1. lab.html: 主界面
|
||||
2. device-register.html: 设备注册
|
||||
3. device-monitor.html: 单个设备数据监控
|
||||
4. device.html: 设备管理
|
||||
5. paper.html: 文献分析
|
||||
6. reports.html: 报告管理
|
||||
7. login.html: 登录界面
|
||||
|
||||
#### Redis 数据库使用情况
|
||||
|
||||
1. DB 199: 实验状态数据
|
||||
- 键格式: `experiment_status:{experiment_id}:{serial_number}`
|
||||
- 值类型: String
|
||||
- 存储内容: 实验设备的活动状态 ("active" 或 "inactive")
|
||||
|
||||
2. DB 200: 实验原始数据
|
||||
- 键格式: `experiment:{experiment_id}:{serial_number}`
|
||||
- 值类型: Redis Stream
|
||||
- 存储内容: 设备采集的原始数据点
|
||||
|
||||
3. DB 201: 实验分析报告
|
||||
- 键格式: `experiment_report:{experiment_id}`
|
||||
- 值类型: String (JSON)
|
||||
- 存储内容: 单个实验的分析报告
|
||||
- 使用位置: 实验数据分析相关路由
|
||||
|
||||
4. DB 202: 项目分析报告
|
||||
- 键格式: `project_report:{project_id}`
|
||||
- 值类型: String (JSON)
|
||||
- 存储内容: 整个项目的汇总分析报告
|
||||
|
||||
5. DB 203: 文献分析报告
|
||||
- 键格式: `reference_report:{reference_id}`
|
||||
- 值类型: String (JSON)
|
||||
- 存储内容: 单个文献的分析报告
|
||||
|
||||
6. DB 204: 文献汇总报告
|
||||
- 键格式: `reference_summary_report:{project_id}`
|
||||
- 值类型: String (JSON)
|
||||
- 存储内容: 项目所有文献的汇总分析报告
|
||||
|
||||
7. DB 205: 项目备忘录
|
||||
- 键格式: `memo:{project_id}`
|
||||
- 值类型: String (JSON)
|
||||
- 存储内容: 项目的备忘录内容和创建时间
|
||||
|
||||
8. DB 206: 实验备忘录
|
||||
- 键格式: `memo:{experiment_id}`
|
||||
- 值类型: String (JSON)
|
||||
- 存储内容: 实验的备忘录内容和创建时间
|
||||
|
||||
9. DB 207: 文献AI对话记录
|
||||
- 键格式: `reference_chat:{reference_id}`
|
||||
- 值类型: String (JSON)
|
||||
- 存储内容: 单个文献的AI对话记录
|
||||
|
||||
10. DB 208: 存储文献AI对话的任务状态
|
||||
- 键格式: `task:{task_id}`
|
||||
- 值类型: String (JSON)
|
||||
- 存储内容: 单个文献的AI对话的任务状态跟踪
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# Lab数据分析系统
|
||||
|
||||
## 项目概述
|
||||
|
||||
这是一个基于FastAPI开发的实验室数据分析平台,提供实验数据采集、分析、项目管理和文献管理等功能。
|
||||
系统使用MongoDB作为主数据库,Redis作为缓存和消息队列,并集成了DeepSeek API进行智能分析。
|
||||
|
||||
## 主要功能
|
||||
|
||||
### 1. 用户管理
|
||||
- 用户注册和登录
|
||||
- JWT token认证
|
||||
- 用户权限管理
|
||||
|
||||
### 2. 项目管理
|
||||
- 创建和管理研究项目
|
||||
- 项目数据分析和报告生成
|
||||
- 项目级别的问答系统
|
||||
- 项目备忘录功能
|
||||
|
||||
### 3. 实验管理
|
||||
- 实验创建和配置
|
||||
- 实验设备关联
|
||||
- 实验数据实时采集
|
||||
- 实验数据分析和可视化
|
||||
- 实验报告自动生成
|
||||
- 实验问答功能
|
||||
- 传感器数据采集
|
||||
- 实时数据传输(WebSocket)
|
||||
|
||||
### 4. 设备管理
|
||||
- 设备注册和激活
|
||||
|
||||
### 5. 文献管理
|
||||
- 文献上传和管理
|
||||
- 文献智能分析
|
||||
- 文献总结报告
|
||||
- 文献问答系统
|
||||
|
||||
### 6. web界面
|
||||
|
||||
## 技术架构
|
||||
|
||||
### 后端框架
|
||||
- FastAPI: 主要Web框架
|
||||
- Pydantic: 数据验证和序列化
|
||||
- Motor: 异步MongoDB驱动
|
||||
- aioredis: 异步Redis客户端
|
||||
- PyJWT: JWT认证
|
||||
- WebSocket: 实时数据传输
|
||||
|
||||
### 数据库
|
||||
- MongoDB: 主数据库
|
||||
- Redis: 缓存和消息队列
|
||||
|
||||
### AI集成
|
||||
- DeepSeek API: 智能分析和问答系统
|
||||
|
||||
## 项目结构
|
||||
frontend/ # web界面
|
||||
docs/ # 说明文档
|
||||
app/
|
||||
├── init.py
|
||||
├── cores/ # 核心配置和工具
|
||||
│ ├── config.py # 配置文件
|
||||
│ └── db.py # 数据库工具
|
||||
├── models/ # 数据模型
|
||||
│ ├── basemodel.py # 基础模型
|
||||
│ ├── project.py # 项目报告
|
||||
│ ├── experiment.py # 实验报告
|
||||
│ ├── paper.py # 文献报告
|
||||
│ └── paper_summary.py # 文献总结报告
|
||||
└── routers/ # API路由
|
||||
├── device.py # 设备管理
|
||||
├── project.py # 项目管理
|
||||
├── project_report.py # 项目报告
|
||||
├── experiment.py # 实验管理
|
||||
├── experiment_report.py # 实验报告
|
||||
├── experiment_device.py # 实验设备
|
||||
├── login.py # 用户认证
|
||||
├── paper.py # 文献报告
|
||||
├── paper_summary.py # 文献总结报告
|
||||
├── memo.py # 备忘录
|
||||
└── websocket.py # WebSocket处理
|
||||
|
||||
## Redis数据库映射
|
||||
|
||||
- db199: 实验状态
|
||||
- db200: 原始数据
|
||||
- db201: 实验分析报告
|
||||
- db202: 项目分析报告
|
||||
- db203: 文献分析报告
|
||||
- db204: 文献汇总报告
|
||||
- db205: 项目备忘录
|
||||
- db206: 实验备忘录
|
||||
- db207: 问答历史记录
|
||||
- db208: 问答任务状态
|
||||
|
||||
## 安装和部署
|
||||
|
||||
1. 环境要求
|
||||
```bash
|
||||
Python 3.8+
|
||||
MongoDB 4.0+
|
||||
Redis 6.0+
|
||||
```
|
||||
2. 启动服务
|
||||
```bash
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
## API文档
|
||||
|
||||
启动服务后访问:
|
||||
- Swagger UI: http://localhost:8000/docs
|
||||
- ReDoc: http://localhost:8000/redoc
|
||||
@@ -0,0 +1,3 @@
|
||||
from .main import app
|
||||
|
||||
__all__ = ['app']
|
||||
@@ -0,0 +1,4 @@
|
||||
from .cores import *
|
||||
from .routers import *
|
||||
|
||||
__all__ = ['cores', 'routers']
|
||||
@@ -0,0 +1,21 @@
|
||||
from .config import *
|
||||
from .db import *
|
||||
|
||||
__all__ = [
|
||||
'MONGODB_URL',
|
||||
'REDIS_URL',
|
||||
'DEEPSEEK_API_CONFIG',
|
||||
'client',
|
||||
'UPLOAD_PATH',
|
||||
'TaskStatus',
|
||||
'THREAD_POOL_CONFIG',
|
||||
'format_response',
|
||||
'format_error',
|
||||
'ALLOWED_FILE_TYPES',
|
||||
'Database',
|
||||
'get_database',
|
||||
'connect_to_mongo',
|
||||
'close_mongo_connection',
|
||||
'get_redis',
|
||||
'get_redis_key'
|
||||
]
|
||||
+13
-1
@@ -140,4 +140,16 @@ async def get_redis_key(redis_type: str, key_pattern: str, **kwargs) -> tuple[in
|
||||
pattern = db_info["key_pattern"]
|
||||
|
||||
key = pattern.format(**kwargs)
|
||||
return db_number, key
|
||||
return db_number, key
|
||||
|
||||
async def get_redis_db_number(redis_type: str) -> int:
|
||||
"""
|
||||
只获取Redis数据库编号
|
||||
|
||||
参数:
|
||||
redis_type: REDIS_DB_MAPPING中的键名
|
||||
|
||||
返回:
|
||||
int: 数据库编号
|
||||
"""
|
||||
return REDIS_DB_MAPPING[redis_type]["db"]
|
||||
@@ -1,6 +1,6 @@
|
||||
from ..cores.config import DEEPSEEK_API_CONFIG, TaskStatus, client
|
||||
from ..cores.db import get_redis, get_redis_key
|
||||
from ..cores.config import MONGODB_URL, REDIS_URL, REDIS_DB_MAP
|
||||
from ..cores.config import TaskStatus, client
|
||||
from ..cores.db import get_redis, get_redis_key, get_redis_db_number
|
||||
from ..cores.config import MONGODB_URL, REDIS_URL
|
||||
from datetime import datetime, timezone
|
||||
import json
|
||||
import asyncio
|
||||
@@ -10,11 +10,9 @@ from redis import asyncio as aioredis
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from ..cores.config import THREAD_POOL_CONFIG
|
||||
|
||||
|
||||
|
||||
# 创建项目分析线程池
|
||||
exp_analysis_executor = ThreadPoolExecutor(
|
||||
max_workers=THREAD_POOL_CONFIG["exp_analysis_workers"],
|
||||
max_workers=THREAD_POOL_CONFIG["analysis_workers"],
|
||||
thread_name_prefix="exp_analysis"
|
||||
)
|
||||
|
||||
@@ -112,10 +110,13 @@ async def process_experiment_analysis(experiment_id: str):
|
||||
redis = await aioredis.from_url(REDIS_URL, encoding="utf-8", decode_responses=True)
|
||||
|
||||
try:
|
||||
await redis.select(REDIS_DB_MAP["experiment_analysis"])
|
||||
status_key = f"experiment_analysis_status:{experiment_id}"
|
||||
|
||||
# 更新状态为进行中
|
||||
db_number, status_key = await get_redis_key(
|
||||
"experiment_analysis",
|
||||
"status",
|
||||
experiment_id=experiment_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
status_data = {
|
||||
"status": "processing",
|
||||
"start_time": datetime.now(timezone.utc).isoformat()
|
||||
@@ -156,7 +157,8 @@ async def process_experiment_analysis(experiment_id: str):
|
||||
session_points = 0
|
||||
for device in session_devices:
|
||||
stream_key = f"experiment:{experiment_id}:{device['serial_number']}"
|
||||
await redis.select(200)
|
||||
db_number = await get_redis_db_number("raw_data")
|
||||
await redis.select(db_number)
|
||||
data_points = await redis.xrange(
|
||||
stream_key,
|
||||
min=str(start_ms),
|
||||
@@ -214,6 +216,12 @@ async def process_experiment_analysis(experiment_id: str):
|
||||
print("分析报告已保存")
|
||||
else:
|
||||
print("分析失败")
|
||||
db_number, status_key = await get_redis_key(
|
||||
"experiment_analysis",
|
||||
"status",
|
||||
experiment_id=experiment_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
status_data = {
|
||||
"status": "failed",
|
||||
"error": "Failed to generate analysis result",
|
||||
@@ -326,6 +334,11 @@ async def process_experiment_question(task_id: str, experiment_id: str, question
|
||||
await redis.set(chat_history_key, json.dumps(history))
|
||||
|
||||
# 更新任务状态为完成
|
||||
db_number, task_key = await get_redis_key(
|
||||
"qa_task",
|
||||
"key_pattern",
|
||||
task_id=task_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
await redis.hset(
|
||||
task_key,
|
||||
@@ -339,6 +352,11 @@ async def process_experiment_question(task_id: str, experiment_id: str, question
|
||||
except Exception as e:
|
||||
print(f"Error processing question: {e}")
|
||||
# 更新任务状态为失败
|
||||
db_number, task_key = await get_redis_key(
|
||||
"qa_task",
|
||||
"key_pattern",
|
||||
task_id=task_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
await redis.hset(
|
||||
task_key,
|
||||
|
||||
+264
-4
@@ -5,9 +5,9 @@ from typing import List, Dict
|
||||
import PyPDF2
|
||||
import aiohttp
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from ..cores.config import client
|
||||
|
||||
|
||||
from ..cores.config import client,TaskStatus
|
||||
from ..cores.db import get_redis, get_redis_key
|
||||
import os
|
||||
# 在全局范围创建线程池
|
||||
pdf_thread_pool = ThreadPoolExecutor(max_workers=3) # 限制并发PDF处理数量
|
||||
|
||||
@@ -18,6 +18,133 @@ async def get_aiohttp_session():
|
||||
headers={"Authorization": f"Bearer sk-3027fb3c810b4e17985fa397d41250b9"}
|
||||
)
|
||||
|
||||
async def process_batch_analysis(references: List[dict]):
|
||||
"""批量处理文献分析的后台任务"""
|
||||
redis = await get_redis()
|
||||
|
||||
# 限制并发数量
|
||||
semaphore = asyncio.Semaphore(3)
|
||||
|
||||
async def process_single_reference(ref: dict):
|
||||
async with semaphore:
|
||||
try:
|
||||
reference_id = ref["reference_id"]
|
||||
file_path = ref["file_path"]
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
print(f"Reference file not found: {file_path}")
|
||||
return
|
||||
|
||||
print(f"\n开始处理文献 {ref.get('reference_title', '未知标题')}")
|
||||
print(f"文献ID: {reference_id}")
|
||||
print(f"文件路径: {file_path}")
|
||||
|
||||
# 异步读取PDF
|
||||
print("\n=== 步骤1: 读取PDF文件 ===")
|
||||
pdf_content = await read_pdf_async(file_path)
|
||||
if not pdf_content:
|
||||
raise Exception("Failed to read PDF content")
|
||||
|
||||
# 打印字符数
|
||||
content_length = len(pdf_content)
|
||||
print(f"\n=== 步骤2: 内容长度检查 ===")
|
||||
print(f"PDF内容总字符数: {content_length}")
|
||||
|
||||
# 根据内容长度选择不同的处理方式
|
||||
if content_length <= 200000:
|
||||
print(f"\n=== 步骤3A: 使用直接分析方式 ===")
|
||||
print(f"文档长度在处理范围内 ({content_length} <= 200000)")
|
||||
# 直接分析文档内容
|
||||
print("开始分析文档内容...")
|
||||
document_analysis = await analyze_reference_document_async(pdf_content[:180000])
|
||||
if not document_analysis:
|
||||
raise Exception("Failed to analyze document")
|
||||
print("文档分析完成")
|
||||
else:
|
||||
print(f"\n=== 步骤3B: 使用分段分析方式 ===")
|
||||
print(f"文档超过200000字符 ({content_length} > 200000)")
|
||||
# 分段分析长文档
|
||||
print("\n--- 开始分段分析 ---")
|
||||
print("正在调用 analyze_long_document_async...")
|
||||
try:
|
||||
analysis_results = await analyze_long_document_async(pdf_content)
|
||||
except Exception as e:
|
||||
print(f"分段分析过程中出错: {str(e)}")
|
||||
raise
|
||||
|
||||
if not analysis_results:
|
||||
raise Exception("Failed to analyze document in segments")
|
||||
print(f"分段分析完成,共分析了 {len(analysis_results)} 个段落")
|
||||
|
||||
# 合并分析结果
|
||||
try:
|
||||
document_analysis = await merge_analysis_results(analysis_results)
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
if not document_analysis:
|
||||
raise Exception("Failed to merge analysis results")
|
||||
|
||||
# 等待一小段时间避免API限制
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# 异步分析文献价值
|
||||
try:
|
||||
value_evaluation = await analyze_reference_value_async(document_analysis)
|
||||
print(f"analyze_reference_value_async 返回结果类型: {type(value_evaluation)}")
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
if not value_evaluation:
|
||||
raise Exception("Failed to evaluate value")
|
||||
print("文献价值分析完成")
|
||||
|
||||
# 合并结果
|
||||
print("\n=== 步骤5: 保存最终结果 ===")
|
||||
analysis_result = {
|
||||
**document_analysis,
|
||||
**value_evaluation,
|
||||
"status": "completed"
|
||||
}
|
||||
|
||||
# 保存结果
|
||||
db_number, report_key = await get_redis_key(
|
||||
"reference_analysis",
|
||||
"report",
|
||||
reference_id=reference_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
await redis.set(report_key, json.dumps(analysis_result))
|
||||
|
||||
except Exception as e:
|
||||
try:
|
||||
# 保存错误状态
|
||||
db_number, report_key = await get_redis_key(
|
||||
"reference_analysis",
|
||||
"report",
|
||||
reference_id=ref['reference_id']
|
||||
)
|
||||
await redis.select(db_number)
|
||||
error_status = {
|
||||
"status": "failed",
|
||||
"message": str(e)
|
||||
}
|
||||
await redis.set(report_key, json.dumps(error_status))
|
||||
except Exception as redis_error:
|
||||
print(f"Error updating Redis status: {redis_error}")
|
||||
|
||||
try:
|
||||
# 并发处理所有引用
|
||||
await asyncio.gather(
|
||||
*(process_single_reference(ref) for ref in references)
|
||||
)
|
||||
finally:
|
||||
try:
|
||||
await redis.aclose()
|
||||
except Exception as e:
|
||||
print(f"Error closing Redis connection: {e}")
|
||||
|
||||
|
||||
async def read_pdf_async(file_path: str) -> str:
|
||||
"""在线程池中异步读取PDF"""
|
||||
def read_pdf():
|
||||
@@ -316,4 +443,137 @@ async def analyze_reference_value_async(content_analysis: dict):
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt}
|
||||
])
|
||||
|
||||
async def process_reference_question(task_id: str, reference_id: str, question: str, reference: dict):
|
||||
"""处理文献问答的后台任务"""
|
||||
redis = await get_redis()
|
||||
|
||||
try:
|
||||
# 更新任务状态为处理中
|
||||
db_number, task_key = await get_redis_key(
|
||||
"qa_task",
|
||||
"key_pattern",
|
||||
task_id=task_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
await redis.hset(
|
||||
task_key,
|
||||
mapping={
|
||||
"status": TaskStatus.PROCESSING,
|
||||
"reference_id": reference_id,
|
||||
"question": question
|
||||
}
|
||||
)
|
||||
|
||||
# 从Redis获取分析报告
|
||||
db_number, report_key = await get_redis_key(
|
||||
"reference_analysis",
|
||||
"report",
|
||||
reference_id=reference_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
report_data = await redis.get(report_key)
|
||||
|
||||
if not report_data:
|
||||
raise Exception("文献分析报告不存在,请先进行分析")
|
||||
|
||||
# 读取PDF文件内容
|
||||
file_path = reference.get("reference_link")
|
||||
if not file_path or not os.path.exists(file_path):
|
||||
raise Exception("文献文件不存在")
|
||||
|
||||
# 读取PDF内容
|
||||
pdf_content = ""
|
||||
with open(file_path, 'rb') as file:
|
||||
pdf_reader = PyPDF2.PdfReader(file)
|
||||
for page in pdf_reader.pages:
|
||||
content = page.extract_text()
|
||||
pdf_content += content
|
||||
|
||||
# 限制内容长度
|
||||
MAX_CHARS = 180000
|
||||
pdf_content = pdf_content[:MAX_CHARS]
|
||||
|
||||
# 构建系统提示和用户提示
|
||||
system_prompt = """
|
||||
你是一个专业的学术助手,负责回答关于学术文献的问题。
|
||||
你应该基于文献内容和分析报告提供准确、专业的回答。
|
||||
回答应当简洁明了,并尽可能引用文献中的具体内容。
|
||||
"""
|
||||
|
||||
# 构建上下文
|
||||
context = {
|
||||
"文献内容": pdf_content,
|
||||
"分析报告": json.loads(report_data)
|
||||
}
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": f"基于以下文献内容和分析报告回答问题:\n\n文献信息:{json.dumps(context, ensure_ascii=False)}\n\n问题:{question}"}
|
||||
]
|
||||
|
||||
# 调用DeepSeek API获取回答
|
||||
response = client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=messages
|
||||
)
|
||||
answer = response.choices[0].message.content
|
||||
|
||||
# 保存对话历史
|
||||
db_number, chat_history_key = await get_redis_key(
|
||||
"chat_history",
|
||||
"reference",
|
||||
reference_id=reference_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
|
||||
# 获取现有历史记录
|
||||
existing_history = await redis.get(chat_history_key)
|
||||
history = json.loads(existing_history) if existing_history else []
|
||||
|
||||
# 添加新的对话
|
||||
history.append({
|
||||
"question": question,
|
||||
"answer": answer,
|
||||
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||
})
|
||||
|
||||
# 保存更新后的历史记录
|
||||
await redis.set(chat_history_key, json.dumps(history))
|
||||
|
||||
# 更新任务状态为完成
|
||||
db_number, task_key = await get_redis_key(
|
||||
"qa_task",
|
||||
"key_pattern",
|
||||
task_id=task_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
await redis.hset(
|
||||
task_key,
|
||||
mapping={
|
||||
"status": TaskStatus.COMPLETED,
|
||||
"answer": answer,
|
||||
"reference_title": reference.get("reference_title")
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error processing question: {e}")
|
||||
# 更新任务状态为失败
|
||||
db_number, task_key = await get_redis_key(
|
||||
"qa_task",
|
||||
"key_pattern",
|
||||
task_id=task_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
await redis.hset(
|
||||
task_key,
|
||||
mapping={
|
||||
"status": TaskStatus.FAILED,
|
||||
"error": str(e)
|
||||
}
|
||||
)
|
||||
finally:
|
||||
try:
|
||||
await redis.aclose()
|
||||
except Exception as e:
|
||||
print(f"Error closing Redis connection: {e}")
|
||||
@@ -0,0 +1,25 @@
|
||||
from .login import router as login_router
|
||||
from .project import router as project_router
|
||||
from .device import router as device_router
|
||||
from .experiment_device import router as experiment_device_router
|
||||
from .experiment import router as experiment_router
|
||||
from .experiment_report import router as experiment_report_router
|
||||
from .websocket import router as websocket_router
|
||||
from .project_report import router as project_report_router
|
||||
from .memo import router as memo_router
|
||||
from .paper import router as paper_router
|
||||
from .paper_summary import router as paper_summary_router
|
||||
|
||||
__all__ = [
|
||||
'login_router',
|
||||
'project_router',
|
||||
'device_router',
|
||||
'experiment_device_router',
|
||||
'experiment_router',
|
||||
'experiment_report_router',
|
||||
'websocket_router',
|
||||
'project_report_router',
|
||||
'memo_router',
|
||||
'paper_router',
|
||||
'paper_summary_router'
|
||||
]
|
||||
@@ -5,9 +5,12 @@ from bson import ObjectId
|
||||
from ..cores.db import get_database
|
||||
from .login import get_current_user, UserModel
|
||||
|
||||
router = APIRouter()
|
||||
router = APIRouter(
|
||||
prefix="/lab",
|
||||
tags=["device"]
|
||||
)
|
||||
|
||||
@router.post("/lab/user/devices/{serial_number}")
|
||||
@router.post("/user/devices/{serial_number}")
|
||||
async def add_device_to_user(
|
||||
serial_number: str,
|
||||
current_user: UserModel = Depends(get_current_user)
|
||||
@@ -122,7 +125,7 @@ async def add_device_to_user(
|
||||
print(f"Error adding device to user: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to add device: {str(e)}")
|
||||
|
||||
@router.get("/lab/userdevices")
|
||||
@router.get("/user/devices")
|
||||
async def get_user_devices(current_user: UserModel = Depends(get_current_user)):
|
||||
"""
|
||||
获取当前用户的所有传感器列表
|
||||
@@ -152,7 +155,7 @@ async def get_user_devices(current_user: UserModel = Depends(get_current_user)):
|
||||
print(f"Error getting user devices: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get user devices: {str(e)}")
|
||||
|
||||
@router.delete("/lab/user/devices/{device_id}")
|
||||
@router.delete("/user/devices/{device_id}")
|
||||
async def remove_user_device(
|
||||
device_id: str,
|
||||
current_user: UserModel = Depends(get_current_user)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime, timezone
|
||||
from bson import ObjectId
|
||||
import json
|
||||
from ..cores.db import get_database, get_redis, get_redis_key
|
||||
from ..cores.db import get_database, get_redis, get_redis_key, get_redis_db_number
|
||||
from ..models.basemodel import ExperimentModel, ExperimentCreate, UserModel, ExperimentStatus,FormulaCreate
|
||||
from .login import get_current_user
|
||||
from io import BytesIO, StringIO
|
||||
@@ -101,11 +101,10 @@ async def start_experiment_session(
|
||||
if not experiment_devices:
|
||||
raise HTTPException(status_code=400, detail="No devices added to the experiment")
|
||||
|
||||
# 更新实验状态到Redis (新增)
|
||||
await redis.select(199)
|
||||
# 更新实验状态到Redis
|
||||
for device in experiment_devices:
|
||||
db_number, status_key = await get_redis_key(
|
||||
"experiment_status",
|
||||
"experiment_status",
|
||||
"key_pattern",
|
||||
experiment_id=experiment_id,
|
||||
device_serial=device['serial_number']
|
||||
@@ -163,11 +162,10 @@ async def stop_experiment_session(
|
||||
if not session:
|
||||
raise HTTPException(status_code=404, detail="Active experiment session not found")
|
||||
|
||||
# 更新实验状态到Redis (新增)
|
||||
await redis.select(199) # 使用同一个db
|
||||
# 更新实验状态到Redis
|
||||
for device in session.get("devices", []):
|
||||
db_number, status_key = await get_redis_key(
|
||||
"experiment_status",
|
||||
"experiment_status",
|
||||
"key_pattern",
|
||||
experiment_id=experiment_id,
|
||||
device_serial=device['serial_number']
|
||||
@@ -291,8 +289,9 @@ async def export_experiment_data(
|
||||
|
||||
zip_file.writestr('README.txt', readme_content)
|
||||
|
||||
# 选择db200获取原始数据
|
||||
await redis.select(200)
|
||||
# 获取原始数据
|
||||
db_number = await get_redis_db_number("raw_data")
|
||||
await redis.select(db_number)
|
||||
|
||||
# 按设备处理数据
|
||||
for session in sessions:
|
||||
@@ -375,7 +374,7 @@ async def export_experiment_data(
|
||||
experiment = await db.experiments.find_one({"_id": ObjectId(experiment_id)})
|
||||
zip_filename = f"experiment_{experiment['experiment_name']}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
|
||||
|
||||
# 准备ZIP文<50><E69687>下载
|
||||
# 准备ZIP文件下载
|
||||
zip_buffer.seek(0)
|
||||
|
||||
return StreamingResponse(
|
||||
@@ -508,8 +507,13 @@ async def delete_experiment(
|
||||
await db.experiments.delete_one({"_id": ObjectId(experiment_id)})
|
||||
|
||||
# 删除Redis中的实验报告
|
||||
await redis.select(201)
|
||||
await redis.delete(f"experiment_report:{experiment_id}")
|
||||
db_number, report_key = await get_redis_key(
|
||||
"experiment_analysis",
|
||||
"report",
|
||||
experiment_id=experiment_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
await redis.delete(report_key)
|
||||
|
||||
return {"message": "Experiment successfully deleted"}
|
||||
|
||||
|
||||
@@ -4,13 +4,12 @@ from datetime import datetime, timezone
|
||||
import asyncio
|
||||
from bson import ObjectId
|
||||
from ..cores.config import (
|
||||
REDIS_DB_MAP, TaskStatus, format_response,
|
||||
exp_analysis_thread_pool
|
||||
TaskStatus, format_response
|
||||
)
|
||||
from ..cores.db import get_database, get_redis, get_redis_key
|
||||
from .login import get_current_user
|
||||
from ..models.basemodel import QuestionModel,UserModel
|
||||
from ..models.experiment import run_experiment_in_thread,process_experiment_question
|
||||
from ..models.experiment import run_experiment_in_thread,process_experiment_question, exp_analysis_executor
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/lab",
|
||||
@@ -24,12 +23,16 @@ async def analyze_data(
|
||||
current_user: UserModel = Depends(get_current_user)
|
||||
):
|
||||
"""启动实验数据分析"""
|
||||
db = await get_database()
|
||||
redis = await get_redis()
|
||||
|
||||
try:
|
||||
await redis.select(REDIS_DB_MAP["experiment_analysis"])
|
||||
status_key = f"experiment_analysis_status:{experiment_id}"
|
||||
# 获取分析状态
|
||||
db_number, status_key = await get_redis_key(
|
||||
"experiment_analysis",
|
||||
"status",
|
||||
experiment_id=experiment_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
current_status = await redis.get(status_key)
|
||||
|
||||
if current_status:
|
||||
@@ -47,8 +50,8 @@ async def analyze_data(
|
||||
}
|
||||
await redis.set(status_key, json.dumps(status_data))
|
||||
|
||||
# 使用线程池提交任务
|
||||
exp_analysis_thread_pool.submit(run_experiment_in_thread, experiment_id)
|
||||
# 使用从 experiment.py 导入的线程池执行器
|
||||
exp_analysis_executor.submit(run_experiment_in_thread, experiment_id)
|
||||
|
||||
return format_response({
|
||||
"experiment_id": experiment_id,
|
||||
@@ -182,9 +185,13 @@ async def get_experiment_qa_history(
|
||||
redis = await get_redis()
|
||||
|
||||
try:
|
||||
# 从Redis db207获取对话历史
|
||||
await redis.select(207)
|
||||
chat_history_key = f"experiment_chat_history:{experiment_id}"
|
||||
# 获取对话历史
|
||||
db_number, chat_history_key = await get_redis_key(
|
||||
"chat_history",
|
||||
"experiment",
|
||||
experiment_id=experiment_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
|
||||
history_data = await redis.get(chat_history_key)
|
||||
if not history_data:
|
||||
|
||||
+2
-133
@@ -10,10 +10,9 @@ from ..cores.db import get_database, get_redis, get_redis_key
|
||||
from ..cores.config import UPLOAD_PATH, TaskStatus
|
||||
from ..routers.login import get_current_user
|
||||
from ..models.paper import (
|
||||
process_batch_analysis, process_reference_question,
|
||||
run_analysis_in_thread
|
||||
process_batch_analysis, process_reference_question
|
||||
)
|
||||
from ..models.basemodel import ReferenceModel, QuestionModel, UserModel
|
||||
from ..models.basemodel import QuestionModel, UserModel
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/lab",
|
||||
@@ -115,136 +114,6 @@ async def get_reference_report(
|
||||
except Exception as e:
|
||||
print(f"Error closing Redis connection: {e}")
|
||||
|
||||
@router.get("/references/{project_id}/analyze_report")
|
||||
async def analyze_reference_summary_report(
|
||||
project_id: str,
|
||||
current_user: UserModel = Depends(get_current_user)
|
||||
):
|
||||
"""分析文献数据"""
|
||||
db = await get_database()
|
||||
redis = await get_redis()
|
||||
|
||||
try:
|
||||
db_number, status_key = await get_redis_key(
|
||||
"reference_summary", # 使用 REDIS_DB_MAPPING 中定义的类型
|
||||
"status", # 使用 key_patterns 中定义的模式
|
||||
project_id=project_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
current_status = await redis.get(status_key)
|
||||
|
||||
if current_status:
|
||||
status_data = json.loads(current_status)
|
||||
if status_data.get("status") == "processing":
|
||||
return {
|
||||
"message": "文献分析任务正在进行中",
|
||||
"status": "processing",
|
||||
"project_id": project_id,
|
||||
"start_time": status_data.get("start_time")
|
||||
}
|
||||
|
||||
reference_cursor = db.references.find({
|
||||
"project_id": ObjectId(project_id)
|
||||
})
|
||||
|
||||
reference_ids = []
|
||||
async for reference in reference_cursor:
|
||||
reference_ids.append(str(reference["_id"]))
|
||||
|
||||
if not reference_ids:
|
||||
raise HTTPException(status_code=404, detail="No references found for this project")
|
||||
|
||||
status_data = {
|
||||
"status": "processing",
|
||||
"start_time": datetime.now(timezone.utc).isoformat(),
|
||||
"total_references": len(reference_ids),
|
||||
"completed_references": 0
|
||||
}
|
||||
await redis.set(status_key, json.dumps(status_data))
|
||||
|
||||
asyncio.create_task(run_analysis_in_thread(project_id, reference_ids))
|
||||
|
||||
return {
|
||||
"message": "文献分析任务已启动",
|
||||
"status": "processing",
|
||||
"project_id": project_id,
|
||||
"start_time": status_data["start_time"],
|
||||
"total_references": len(reference_ids)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error starting reference analysis: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
finally:
|
||||
try:
|
||||
await redis.aclose()
|
||||
except Exception as e:
|
||||
print(f"Error closing Redis connection: {e}")
|
||||
|
||||
@router.get("/references/{project_id}/analysis_status")
|
||||
async def get_reference_analysis_status(
|
||||
project_id: str,
|
||||
current_user: UserModel = Depends(get_current_user)
|
||||
):
|
||||
"""获取文献分析任务的状态"""
|
||||
redis = await get_redis()
|
||||
|
||||
try:
|
||||
db_number, status_key = await get_redis_key(
|
||||
"reference_summary",
|
||||
"status", # 使用 status 键模式
|
||||
project_id=project_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
status_data = await redis.get(status_key)
|
||||
|
||||
if not status_data:
|
||||
return {
|
||||
"status": "not_started",
|
||||
"project_id": project_id
|
||||
}
|
||||
|
||||
return json.loads(status_data)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error getting analysis status: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
finally:
|
||||
try:
|
||||
await redis.aclose()
|
||||
except Exception as e:
|
||||
print(f"Error closing Redis connection: {e}")
|
||||
|
||||
@router.get("/references/{project_id}/summary_report")
|
||||
async def get_reference_summary_report(
|
||||
project_id: str,
|
||||
current_user: UserModel = Depends(get_current_user)
|
||||
):
|
||||
"""从 Redis 读取已保存的文献汇总报告"""
|
||||
redis = await get_redis()
|
||||
|
||||
try:
|
||||
db_number, report_key = await get_redis_key(
|
||||
"reference_summary", # 使用 REDIS_DB_MAPPING 中定义的类型
|
||||
"report", # 使用 key_patterns 中定义的模式
|
||||
project_id=project_id
|
||||
)
|
||||
await redis.select(db_number)
|
||||
existing_report = await redis.get(report_key)
|
||||
if not existing_report:
|
||||
raise HTTPException(status_code=404, detail="No saved reference report found")
|
||||
|
||||
return json.loads(existing_report)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error getting reference report: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
finally:
|
||||
try:
|
||||
await redis.aclose()
|
||||
except Exception as e:
|
||||
print(f"Error closing Redis connection: {e}")
|
||||
|
||||
@router.post("/references/{reference_id}/qa")
|
||||
async def ask_reference_question(
|
||||
reference_id: str,
|
||||
|
||||
@@ -144,4 +144,4 @@ async def get_reference_summary_report(
|
||||
try:
|
||||
await redis.aclose()
|
||||
except Exception as e:
|
||||
print(f"Error closing Redis connection: {e}")
|
||||
print(f"Error closing Redis connection: {e}")
|
||||
|
||||
@@ -6,8 +6,8 @@ from bson import ObjectId
|
||||
from ..cores.config import TaskStatus
|
||||
from ..cores.db import get_database, get_redis, get_redis_key
|
||||
from .login import get_current_user
|
||||
from ..models.project import pro_analysis_executor, run_project_in_thread, QuestionModel, process_project_question
|
||||
from ..models.basemodel import UserModel
|
||||
from ..models.project import pro_analysis_executor, run_project_in_thread, process_project_question
|
||||
from ..models.basemodel import UserModel, QuestionModel
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/lab",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# cores/config.py
|
||||
|
||||
## 文件说明
|
||||
该文件包含了应用程序的核心配置信息,包括数据库连接、API配置、文件上传设置等。
|
||||
|
||||
## 主要组件
|
||||
|
||||
### 数据库配置
|
||||
- `MONGODB_URL`: MongoDB连接字符串
|
||||
- `DATABASE_NAME`: 数据库名称
|
||||
- `REDIS_URL`: Redis连接字符串
|
||||
|
||||
### API配置
|
||||
- `DEEPSEEK_API_CONFIG`: DeepSeek API的配置信息
|
||||
- `client`: OpenAI客户端实例
|
||||
|
||||
### 文件处理配置
|
||||
- `UPLOAD_PATH`: 文件上传目录
|
||||
- `ALLOWED_FILE_TYPES`: 允许上传的文件类型集合
|
||||
|
||||
### 任务管理
|
||||
- `TaskStatus`: 任务状态枚举类(pending/processing/completed/failed)
|
||||
- `THREAD_POOL_CONFIG`: 不同类型任务的线程池配置
|
||||
|
||||
### 响应格式化工具
|
||||
- `format_response()`: 格式化成功响应的函数
|
||||
- `format_error()`: 格式化错误响应的函数
|
||||
@@ -0,0 +1,31 @@
|
||||
# cores/db.py
|
||||
|
||||
## 文件说明
|
||||
该文件包含数据库相关的工具和配置,主要处理MongoDB和Redis的连接和操作。
|
||||
|
||||
## 主要组件
|
||||
|
||||
### Redis数据库映射
|
||||
`REDIS_DB_MAPPING`: 详细的Redis数据库映射配置,包含:
|
||||
- 实验状态 (db: 199)
|
||||
- 原始数据 (db: 200)
|
||||
- 实验分析 (db: 201)
|
||||
- 项目分析 (db: 202)
|
||||
- 文献分析 (db: 203)
|
||||
- 文献汇总 (db: 204)
|
||||
- 项目备忘录 (db: 205)
|
||||
- 实验备忘录 (db: 206)
|
||||
- 聊天历史 (db: 207)
|
||||
- 问答任务 (db: 208)
|
||||
|
||||
### 工具类
|
||||
- `PyObjectId`: 用于Pydantic模型中处理MongoDB ObjectId的自定义类
|
||||
- `Database`: 数据库连接管理类
|
||||
|
||||
### 数据库操作函数
|
||||
- `get_database()`: 获取MongoDB数据库实例
|
||||
- `connect_to_mongo()`: 建立MongoDB连接
|
||||
- `close_mongo_connection()`: 关闭MongoDB连接
|
||||
- `get_redis()`: 获取Redis连接
|
||||
- `get_redis_key()`: 获取Redis键名和数据库编号
|
||||
- `get_redis_db_number()`: 获取Redis数据库编号
|
||||
@@ -0,0 +1,80 @@
|
||||
# basemodel.py
|
||||
|
||||
## 文件说明
|
||||
该文件定义了应用程序中使用的基础数据模型,使用Pydantic进行数据验证和序列化。
|
||||
|
||||
## 主要模型
|
||||
|
||||
### ExperimentDeviceModel
|
||||
实验-设备关联模型,用于关联实验和设备。
|
||||
- `id`: ObjectId,主键
|
||||
- `experiment_id`: 关联的实验ID
|
||||
- `user_device_id`: 关联的用户设备ID
|
||||
|
||||
### QuestionModel
|
||||
问题模型,用于处理用户提问。
|
||||
- `question`: 问题内容
|
||||
|
||||
### UserModel
|
||||
用户模型,存储用户信息。
|
||||
- `id`: ObjectId,主键
|
||||
- `username`: 用户名
|
||||
- `password`: 密码
|
||||
- `email`: 电子邮件
|
||||
- `name`: 姓名
|
||||
- `institution`: 所属机构
|
||||
|
||||
### ProjectModel
|
||||
项目模型,存储项目信息。
|
||||
- `id`: ObjectId,主键
|
||||
- `user_id`: 创建者ID
|
||||
- `project_name`: 项目名称
|
||||
- `create_time`: 创建时间
|
||||
- `description`: 项目描述
|
||||
|
||||
### ExperimentModel
|
||||
实验模型,存储实验信息。
|
||||
- `id`: ObjectId,主键
|
||||
- `project_id`: 所属项目ID
|
||||
- `experiment_name`: 实验名称
|
||||
- `create_time`: 创建时间
|
||||
- `description`: 实验描述
|
||||
- `status`: 实验状态
|
||||
|
||||
### ExperimentSession
|
||||
实验会话模型,记录实验会话信息。
|
||||
- `id`: ObjectId,主键
|
||||
- `experiment_id`: 实验ID
|
||||
- `user_id`: 用户ID
|
||||
- `start_time`: 开始时间
|
||||
- `end_time`: 结束时间
|
||||
- `duration`: 持续时间
|
||||
|
||||
### ExperimentData
|
||||
实验数据模型,存储实验数据。
|
||||
- `id`: ObjectId,主键
|
||||
- `experiment_id`: 实验ID
|
||||
- `session_ids`: 会话ID列表
|
||||
- `user_id`: 用户ID
|
||||
- `device_id`: 设备ID
|
||||
- `sensor_name`: 传感器名称
|
||||
- `last_update`: 最后更新时间
|
||||
|
||||
### MemoModel
|
||||
备忘录模型,用于存储备忘信息。
|
||||
- `content`: 备忘内容
|
||||
- `create_time`: 创建时间
|
||||
|
||||
### FormulaModel & FormulaCreate
|
||||
公式相关模型,用于存储和创建公式。
|
||||
- `data_name`: 数据名称
|
||||
- `data_unit`: 数据单位
|
||||
- `formula`: 公式内容
|
||||
|
||||
### ReferenceModel
|
||||
文献引用模型,存储文献信息。
|
||||
- `id`: ObjectId,主键
|
||||
- `project_id`: 项目ID
|
||||
- `reference_link`: 文献链接
|
||||
- `reference_title`: 文献标题
|
||||
- `upload_time`: 上传时间
|
||||
@@ -0,0 +1,48 @@
|
||||
# experiment.py
|
||||
|
||||
## 文件说明
|
||||
该文件包含了实验分析相关的功能实现,主要处理实验数据的分析和问答功能。
|
||||
|
||||
## 主要组件
|
||||
|
||||
### 线程池配置
|
||||
- `exp_analysis_executor`: 实验分析线程池,用于并发处理实验分析任务
|
||||
|
||||
### 核心函数
|
||||
|
||||
#### analyze_experiment_data
|
||||
异步函数,负责分析实验数据。
|
||||
- 输入:实验信息
|
||||
- 输出:JSON格式的分析报告
|
||||
- 功能:使用DeepSeek API生成实验分析报告
|
||||
|
||||
#### run_experiment_in_thread
|
||||
在独立线程中运行分析任务的函数。
|
||||
- 创建新的事件循环
|
||||
- 执行异步分析任务
|
||||
- 处理异常情况
|
||||
|
||||
#### process_experiment_analysis
|
||||
后台处理实验分析的主要函数。
|
||||
- 更新任务状态
|
||||
- 收集实验数据
|
||||
- 生成分析报告
|
||||
- 保存分析结果
|
||||
|
||||
#### process_experiment_question
|
||||
处理实验相关问答的后台任务。
|
||||
- 更新任务状态
|
||||
- 获取分析报告
|
||||
- 生成回答
|
||||
- 保存对话历史
|
||||
|
||||
## 主要流程
|
||||
1. 任务初始化和状态更新
|
||||
2. 数据收集和预处理
|
||||
3. 调用AI模型进行分析
|
||||
4. 结果保存和状态更新
|
||||
5. 异常处理和资源清理
|
||||
|
||||
## 数据存储
|
||||
- MongoDB: 存储实验基础信息
|
||||
- Redis: 存储分析报告和任务状态
|
||||
@@ -0,0 +1,63 @@
|
||||
# paper.py
|
||||
|
||||
## 文件说明
|
||||
该文件实现了文献分析相关的功能,包括PDF处理、文献分析和问答功能。
|
||||
|
||||
## 主要组件
|
||||
|
||||
### 工具函数
|
||||
|
||||
#### read_pdf_async
|
||||
异步读取PDF文件内容。
|
||||
- 使用线程池处理PDF读取
|
||||
- 根据内容长度进行处理
|
||||
- 返回处理后的文本内容
|
||||
|
||||
#### call_deepseek_api_async
|
||||
异步调用DeepSeek API。
|
||||
- 处理API请求
|
||||
- 返回JSON格式的响应
|
||||
|
||||
### 分析函数
|
||||
|
||||
#### analyze_long_document_async
|
||||
处理长文档的分析功能。
|
||||
- 文档分段处理
|
||||
- 每段内容分析
|
||||
- 合并分析结果
|
||||
|
||||
#### analyze_reference_document_async
|
||||
分析单个文献文档。
|
||||
- 提取基本信息
|
||||
- 内容分析
|
||||
- 生成流程图
|
||||
|
||||
#### analyze_reference_value_async
|
||||
评估文献的价值。
|
||||
- 学术贡献分析
|
||||
- 实践意义评估
|
||||
- 局限性分析
|
||||
|
||||
### 任务处理
|
||||
|
||||
#### process_batch_analysis
|
||||
批量处理文献分析任务。
|
||||
- 并发控制
|
||||
- 状态管理
|
||||
- 结果保存
|
||||
|
||||
#### process_reference_question
|
||||
处理文献相关的问答功能。
|
||||
- 问题处理
|
||||
- 生成回答
|
||||
- 保存对话历史
|
||||
|
||||
## 数据流程
|
||||
1. PDF文件读取和预处理
|
||||
2. 文本分析和价值评估
|
||||
3. 结果整合和存储
|
||||
4. 问答处理和历史记录
|
||||
|
||||
## 存储使用
|
||||
- Redis: 存储分析报告和状态
|
||||
- 文件系统: 存储PDF文件
|
||||
@@ -0,0 +1,75 @@
|
||||
# paper_summary.py
|
||||
|
||||
## 文件说明
|
||||
该文件实现了文献汇总分析的功能,用于对多篇文献进行综合分析和总结。
|
||||
|
||||
## 主要组件
|
||||
|
||||
### 线程池配置
|
||||
- `summary_executor`: 文献汇总分析线程池
|
||||
|
||||
### 核心函数
|
||||
|
||||
#### analyze_reference_summary
|
||||
分析文献汇总报告的主要函数。
|
||||
- 输入:多篇文献的分析数据
|
||||
- 输出:JSON格式的汇总报告
|
||||
- 内容包括:
|
||||
- 文献概览
|
||||
- 研究趋势
|
||||
- 关键发现
|
||||
- 研究空白
|
||||
- 未来方向
|
||||
- 影响评估
|
||||
|
||||
#### run_analysis_in_thread
|
||||
在独立线程中运行汇总分析任务。
|
||||
- 使用summary_executor执行任务
|
||||
- 异步处理分析流程
|
||||
|
||||
#### process_reference_analysis
|
||||
后台处理文献分析任务的主要函数。
|
||||
- 收集所有文献报告
|
||||
- 更新处理进度
|
||||
- 生成汇总分析
|
||||
- 保存分析结果
|
||||
|
||||
## 数据结构
|
||||
### 汇总报告格式
|
||||
```json
|
||||
{
|
||||
"Paper Summary Report": {
|
||||
"overview": {
|
||||
"total_papers": "数量",
|
||||
"time_range": {"start_year": "开始年份", "end_year": "结束年份"},
|
||||
"main_research_areas": "主要研究领域"
|
||||
},
|
||||
"research_trends": {
|
||||
"major_themes": "主要主题",
|
||||
"common_methodologies": "常用方法",
|
||||
"emerging_topics": "新兴话题"
|
||||
},
|
||||
"key_findings": {
|
||||
"theoretical_advances": "理论进展",
|
||||
"experimental_results": "实验结果",
|
||||
"common_conclusions": "共同结论"
|
||||
},
|
||||
"research_gaps": {
|
||||
"current_limitations": "当前限制",
|
||||
"unexplored_areas": "未探索领域",
|
||||
"technical_challenges": "技术挑战"
|
||||
},
|
||||
"future_directions": {
|
||||
"potential_applications": "潜在应用",
|
||||
"methodological_suggestions": "方法建议"
|
||||
},
|
||||
"impact_assessment": {
|
||||
"academic_influence": "学术影响",
|
||||
"practical_value": "实践价值"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 存储使用
|
||||
- Redis: 存储分析报告和状态信息
|
||||
@@ -0,0 +1,91 @@
|
||||
# project.py
|
||||
|
||||
## 文件说明
|
||||
该文件实现了项目分析相关的功能,包括项目数据分析和问答功能。
|
||||
|
||||
## 主要组件
|
||||
|
||||
### 线程池配置
|
||||
- `pro_analysis_executor`: 项目分析线程池
|
||||
|
||||
### 核心函数
|
||||
|
||||
#### analyze_project_data
|
||||
分析项目数据的主要函数。
|
||||
- 输入:项目数据和统计信息
|
||||
- 输出:JSON格式的分析报告
|
||||
- 报告内容:
|
||||
- 项目概览
|
||||
- 统计数据
|
||||
- 性能分析
|
||||
- 共同发现
|
||||
- 建议
|
||||
|
||||
#### run_project_in_thread
|
||||
在独立线程中运行项目分析任务。
|
||||
- 创建新的事件循环
|
||||
- 执行异步分析任务
|
||||
- 处理异常情况
|
||||
|
||||
#### process_project_analysis
|
||||
后台处理项目分析的主要函数。
|
||||
- 收集实验数据
|
||||
- 计算统计信息
|
||||
- 生成分析报告
|
||||
- 更新任务状态
|
||||
|
||||
#### process_project_question
|
||||
处理项目相关问答的后台任务。
|
||||
- 获取项目报告
|
||||
- 生成回答
|
||||
- 保存对话历史
|
||||
|
||||
## 数据结构
|
||||
### 项目分析报告格式
|
||||
```json
|
||||
{
|
||||
"Project Analysis Report": {
|
||||
"1. Project Overview": {
|
||||
"Project Name": "项目名称",
|
||||
"Total Experiments": "实验总数",
|
||||
"Total Data Points": "数据点总数"
|
||||
},
|
||||
"2. Aggregated Statistics": {
|
||||
"Total Sessions": "会话总数",
|
||||
"Total Duration": "总持续时间",
|
||||
"Average Session Duration": "平均会话时长",
|
||||
"Average Data Points per Session": "每会话平均数据点"
|
||||
},
|
||||
"3. Performance Analysis": {
|
||||
"Best Performing Sessions": [
|
||||
{
|
||||
"Session ID": "会话ID",
|
||||
"experiment_id": "实验ID",
|
||||
"Success Factors": "成功因素"
|
||||
}
|
||||
],
|
||||
"Problematic Sessions": [
|
||||
{
|
||||
"Session ID": "会话ID",
|
||||
"Issues": "问题",
|
||||
"Possible Causes": "可能原因"
|
||||
}
|
||||
]
|
||||
},
|
||||
"4. Common Findings": {
|
||||
"Recurring Issues": "常见问题",
|
||||
"Equipment performance": "设备性能",
|
||||
"Sensor reliability": "传感器可靠性"
|
||||
},
|
||||
"5. Recommendations": {
|
||||
"Equipment optimization": "设备优化建议",
|
||||
"Process improvement": "流程改进建议",
|
||||
"Data collection": "数据收集策略"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 存储使用
|
||||
- MongoDB: 存储项目和实验基础信息
|
||||
- Redis: 存储分析报告和任务状态
|
||||
@@ -0,0 +1,28 @@
|
||||
# device.py
|
||||
|
||||
## 文件说明
|
||||
该文件实现了设备管理相关的路由处理,包括设备添加、查询和移除功能。
|
||||
|
||||
## API端点
|
||||
|
||||
### POST /lab/user/devices/{serial_number}
|
||||
添加设备到用户设备列表
|
||||
- 参数:设备序列号
|
||||
- 功能:通过序列号验证并添加设备
|
||||
- 返回:设备信息和添加状态
|
||||
|
||||
### GET /lab/userdevices
|
||||
获取用户的所有设备列表
|
||||
- 功能:返回当前用户关联的所有设备信息
|
||||
- 返回:设备列表,包含详细信息
|
||||
|
||||
### DELETE /lab/user/devices/{device_id}
|
||||
将设备标记为非活动状态
|
||||
- 参数:设备ID
|
||||
- 功能:停用指定设备
|
||||
- 返回:停用状态确认
|
||||
|
||||
## 数据处理
|
||||
- 设备状态管理(active/inactive)
|
||||
- 设备-用户关联关系维护
|
||||
- 序列号验证和状态更新
|
||||
@@ -0,0 +1,41 @@
|
||||
# experiment.py
|
||||
|
||||
## 文件说明
|
||||
实验管理相关的路由处理,包括实验的创建、查询、数据导出等功能。
|
||||
|
||||
## API端点
|
||||
|
||||
### POST /lab/experiments
|
||||
创建新实验
|
||||
- 参数:实验创建请求模型
|
||||
- 功能:创建新的实验记录
|
||||
- 返回:实验ID和创建状态
|
||||
|
||||
### GET /lab/experiments
|
||||
获取项目下的所有实验
|
||||
- 参数:项目ID
|
||||
- 功能:返回指定项目的实验列表
|
||||
- 返回:实验列表
|
||||
|
||||
### POST /lab/experiments/{experiment_id}/start
|
||||
开始新的实验会话
|
||||
- 参数:实验ID
|
||||
- 功能:创建新的实验会话
|
||||
- 返回:会话信息和设备列表
|
||||
|
||||
### GET /lab/experiments/{experiment_id}/export
|
||||
导出实验数据
|
||||
- 参数:实验ID
|
||||
- 功能:将实验数据导出为ZIP文件
|
||||
- 返回:包含CSV文件的ZIP压缩包
|
||||
|
||||
### POST /lab/experiments/{experiment_id}/complete
|
||||
完成实验
|
||||
- 参数:实验ID
|
||||
- 功能:将实验状态标记为已完成
|
||||
- 返回:完成状态确认
|
||||
|
||||
## 数据处理
|
||||
- 实验会话管理
|
||||
- 数据导出格式化
|
||||
- 实验状态跟踪
|
||||
@@ -0,0 +1,35 @@
|
||||
# experiment_device.py
|
||||
|
||||
## 文件说明
|
||||
实验设备关联管理的路由处理,处理实验和设备之间的关联关系。
|
||||
|
||||
## API端点
|
||||
|
||||
### POST /lab/experiments/{experiment_id}/devices/{user_device_id}
|
||||
添加设备到实验
|
||||
- 参数:实验ID和用户设备ID
|
||||
- 功能:建立实验和设备的关联关系
|
||||
- 返回:关联状态确认
|
||||
|
||||
### GET /lab/experiments/{experiment_id}/devices
|
||||
获取实验中的所有设备
|
||||
- 参数:实验ID
|
||||
- 功能:返回实验关联的设备列表
|
||||
- 返回:设备详细信息列表
|
||||
|
||||
### GET /lab/experiments/{experiment_id}/devices/public
|
||||
公开接口获取实验设备
|
||||
- 参数:实验ID
|
||||
- 功能:无需认证获取设备信息
|
||||
- 返回:设备基本信息列表
|
||||
|
||||
### DELETE /lab/experiments/{experiment_id}/devices/{user_device_id}
|
||||
从实验中移除设备
|
||||
- 参数:实验ID和设备ID
|
||||
- 功能:解除实验和设备的关联
|
||||
- 返回:移除状态确认
|
||||
|
||||
## 数据处理
|
||||
- 设备关联关系管理
|
||||
- 设备信息格式化
|
||||
- 权限验证
|
||||
@@ -0,0 +1,41 @@
|
||||
# experiment_report.py
|
||||
|
||||
## 文件说明
|
||||
实验报告相关的路由处理,包括数据分析、状态查询和问答功能。
|
||||
|
||||
## API端点
|
||||
|
||||
### GET /lab/experiments/{experiment_id}/analyze
|
||||
启动实验数据分析
|
||||
- 参数:实验ID
|
||||
- 功能:异步启动数据分析任务
|
||||
- 返回:分析任务状态
|
||||
|
||||
### GET /lab/experiments/{experiment_id}/analysis_status
|
||||
获取分析任务状态
|
||||
- 参数:实验ID
|
||||
- 功能:查询分析进度
|
||||
- 返回:当前分析状态
|
||||
|
||||
### GET /lab/experiments/{experiment_id}/report
|
||||
获取已保存的报告
|
||||
- 参数:实验ID
|
||||
- 功能:获取分析报告内容
|
||||
- 返回:完整的分析报告
|
||||
|
||||
### POST /lab/experiments/{experiment_id}/qa
|
||||
向实验报告提问
|
||||
- 参数:实验ID和问题内容
|
||||
- 功能:创建问答任务
|
||||
- 返回:任务ID和状态
|
||||
|
||||
### GET /lab/experiments/{experiment_id}/qa/history
|
||||
获取问答历史
|
||||
- 参数:实验ID
|
||||
- 功能:获取历史问答记录
|
||||
- 返回:问答历史列表
|
||||
|
||||
## 数据处理
|
||||
- 异步任务管理
|
||||
- 报告存储和检索
|
||||
- 问答历史记录
|
||||
@@ -0,0 +1,40 @@
|
||||
# login.py
|
||||
|
||||
## 文件说明
|
||||
用户认证和授权相关的路由处理,包括用户注册、登录和Token验证。
|
||||
|
||||
## 配置
|
||||
- SECRET_KEY: JWT密钥
|
||||
- ALGORITHM: JWT算法(HS256)
|
||||
- ACCESS_TOKEN_EXPIRE_MINUTES: Token过期时间(30天)
|
||||
|
||||
## API端点
|
||||
|
||||
### POST /lab/register
|
||||
用户注册
|
||||
- 参数:用户注册信息(UserModel)
|
||||
- 功能:创建新用户账户
|
||||
- 返回:用户ID和注册状态
|
||||
|
||||
### POST /lab/token
|
||||
用户登录
|
||||
- 参数:用户名和密码
|
||||
- 功能:验证用户身份并生成访问令牌
|
||||
- 返回:JWT访问令牌
|
||||
|
||||
## 工具函数
|
||||
|
||||
### create_access_token
|
||||
- 功能:生成JWT访问令牌
|
||||
- 参数:用户数据
|
||||
- 返回:编码后的JWT令牌
|
||||
|
||||
### get_current_user
|
||||
- 功能:从JWT令牌获取当前用户
|
||||
- 参数:JWT令牌
|
||||
- 返回:当前用户模型
|
||||
|
||||
## 安全特性
|
||||
- JWT令牌认证
|
||||
- 密码验证
|
||||
- 用户会话管理
|
||||
@@ -0,0 +1,35 @@
|
||||
# memo.py
|
||||
|
||||
## 文件说明
|
||||
备忘录功能相关的路由处理,包括项目和实验备忘录的管理。
|
||||
|
||||
## API端点
|
||||
|
||||
### POST /lab/projects/{project_id}/memo
|
||||
保存项目备忘录
|
||||
- 参数:项目ID和备忘内容
|
||||
- 功能:保存或更新项目备忘录
|
||||
- 返回:保存状态确认
|
||||
|
||||
### GET /lab/projects/{project_id}/memo
|
||||
获取项目备忘录
|
||||
- 参数:项目ID
|
||||
- 功能:获取项目的备忘录内容
|
||||
- 返回:备忘录内容和创建时间
|
||||
|
||||
### POST /lab/experiments/{experiment_id}/memo
|
||||
保存实验备忘录
|
||||
- 参数:实验ID和备忘内容
|
||||
- 功能:保存或更新实验备忘录
|
||||
- 返回:保存状态确认
|
||||
|
||||
### GET /lab/experiments/{experiment_id}/memo
|
||||
获取实验备忘录
|
||||
- 参数:实验ID
|
||||
- 功能:获取实验的备忘录内容
|
||||
- 返回:备忘录内容和创建时间
|
||||
|
||||
## 数据存储
|
||||
- Redis数据库存储
|
||||
- 备忘录内容格式化
|
||||
- 时间戳管理
|
||||
@@ -0,0 +1,42 @@
|
||||
# paper.py
|
||||
|
||||
## 文件说明
|
||||
文献管理相关的路由处理,包括文献上传、分析和问答功能。
|
||||
|
||||
## API端点
|
||||
|
||||
### POST /lab/projects/{project_id}/references/batch
|
||||
批量上传文献
|
||||
- 参数:项目ID和文件列表
|
||||
- 功能:上传并处理多个文献文件
|
||||
- 返回:上传状态和文献信息
|
||||
|
||||
### GET /lab/projects/{project_id}/references
|
||||
获取项目文献列表
|
||||
- 参数:项目ID
|
||||
- 功能:获取项目关联的所有文献
|
||||
- 返回:文献列表
|
||||
|
||||
### GET /lab/references/{reference_id}/report
|
||||
获取文献分析报告
|
||||
- 参数:文献ID
|
||||
- 功能:获取已保存的分析报告
|
||||
- 返回:完整的分析报告
|
||||
|
||||
### POST /lab/references/{reference_id}/qa
|
||||
向文献提问
|
||||
- 参数:文献ID和问题内容
|
||||
- 功能:创建文献问答任务
|
||||
- 返回:任务ID和状态
|
||||
|
||||
### GET /lab/references/{reference_id}/qa/history
|
||||
获取文献问答历史
|
||||
- 参数:文献ID
|
||||
- 功能:获取历史问答记录
|
||||
- 返回:问答历史列表
|
||||
|
||||
## 功能特性
|
||||
- 文件上传处理
|
||||
- 异步分析任务
|
||||
- 问答历史管理
|
||||
- 报告存储和检索
|
||||
@@ -0,0 +1,30 @@
|
||||
# paper_summary.py
|
||||
|
||||
## 文件说明
|
||||
文献汇总分析相关的路由处理,提供项目级别的文献分析功能。
|
||||
|
||||
## API端点
|
||||
|
||||
### GET /lab/references/{project_id}/analyze_report
|
||||
启动文献汇总分析
|
||||
- 参数:项目ID
|
||||
- 功能:对项目下所有文献进行汇总分析
|
||||
- 返回:分析任务状态
|
||||
|
||||
### GET /lab/references/{project_id}/analysis_status
|
||||
获取汇总分析状态
|
||||
- 参数:项目ID
|
||||
- 功能:查询汇总分析进度
|
||||
- 返回:当前分析状态和进度
|
||||
|
||||
### GET /lab/references/{project_id}/summary_report
|
||||
获取文献汇总报告
|
||||
- 参数:项目ID
|
||||
- 功能:获取已完成的汇总分析报告
|
||||
- 返回:完整的汇总报告
|
||||
|
||||
## 功能特性
|
||||
- 批量文献分析
|
||||
- 进度追踪
|
||||
- 结果汇总
|
||||
- 异步任务处理
|
||||
@@ -0,0 +1,29 @@
|
||||
# project.py
|
||||
|
||||
## 文件说明
|
||||
项目管理相关的路由处理,包括项目的创建、查询和删除功能。
|
||||
|
||||
## API端点
|
||||
|
||||
### POST /lab/projects
|
||||
创建新项目
|
||||
- 参数:项目信息(ProjectModel)
|
||||
- 功能:创建新的项目记录
|
||||
- 返回:项目ID和创建状态
|
||||
|
||||
### GET /lab/projects
|
||||
获取用户的所有项目
|
||||
- 功能:返回当前用户的所有项目列表
|
||||
- 返回:项目列表,包含完整项目信息
|
||||
|
||||
### DELETE /lab/projects/{project_id}
|
||||
删除项目
|
||||
- 参数:项目ID
|
||||
- 功能:删除项目及其所有相关数据
|
||||
- 返回:删除状态确认
|
||||
|
||||
## 数据处理
|
||||
- 项目数据管理
|
||||
- 关联数据清理
|
||||
- 用户权限验证
|
||||
- Redis缓存清理
|
||||
@@ -0,0 +1,42 @@
|
||||
# project_report.py
|
||||
|
||||
## 文件说明
|
||||
项目报告相关的路由处理,包括项目数据分析和问答功能。
|
||||
|
||||
## API端点
|
||||
|
||||
### GET /lab/projects/{project_id}/analyze
|
||||
启动项目数据分析
|
||||
- 参数:项目ID
|
||||
- 功能:异步启动项目分析任务
|
||||
- 返回:分析任务状态
|
||||
|
||||
### GET /lab/projects/{project_id}/analysis_status
|
||||
获取项目分析状态
|
||||
- 参数:项目ID
|
||||
- 功能:查询分析进度
|
||||
- 返回:当前分析状态
|
||||
|
||||
### GET /lab/projects/{project_id}/report
|
||||
获取项目分析报告
|
||||
- 参数:项目ID
|
||||
- 功能:获取已保存的分析报告
|
||||
- 返回:完整的分析报告
|
||||
|
||||
### POST /lab/projects/{project_id}/qa
|
||||
向项目报告提问
|
||||
- 参数:项目ID和问题内容
|
||||
- 功能:创建项目问答任务
|
||||
- 返回:任务ID和状态
|
||||
|
||||
### GET /lab/projects/{project_id}/qa/history
|
||||
获取项目问答历史
|
||||
- 参数:项目ID
|
||||
- 功能:获取历史问答记录
|
||||
- 返回:问答历史列表
|
||||
|
||||
## 功能特性
|
||||
- 异步分析处理
|
||||
- 状态追踪
|
||||
- 问答历史管理
|
||||
- 报告存储和检索
|
||||
@@ -0,0 +1,32 @@
|
||||
# websocket.py
|
||||
|
||||
## 文件说明
|
||||
WebSocket连接管理相关的路由处理,用于实时数据传输。
|
||||
|
||||
## 主要组件
|
||||
|
||||
### ConnectionManager类
|
||||
WebSocket连接管理器
|
||||
- 管理活动连接
|
||||
- 处理连接/断开
|
||||
- 消息广播
|
||||
- 连接状态追踪
|
||||
|
||||
## API端点
|
||||
|
||||
### WebSocket /lab/ws/{experiment_id}/{serial_number}
|
||||
WebSocket连接端点
|
||||
- 参数:实验ID和设备序列号
|
||||
- 功能:建立WebSocket连接
|
||||
- 返回:连接确认
|
||||
|
||||
### GET /lab/status
|
||||
获取WebSocket服务状态
|
||||
- 功能:返回服务运行状态
|
||||
- 返回:状态信息和活动连接数
|
||||
|
||||
## 功能特性
|
||||
- 实时数据传输
|
||||
- 连接生命周期管理
|
||||
- 消息队列处理
|
||||
- 错误处理和恢复
|
||||
@@ -0,0 +1,91 @@
|
||||
# Lab 界面
|
||||
|
||||
这是一个用于实验室管理的web界面, 作为app/的web界面
|
||||
## 项目结构
|
||||
|
||||
## 页面说明
|
||||
|
||||
### login.html
|
||||
- 用户登录界面
|
||||
- 用户注册功能
|
||||
- 使用 JWT token 认证
|
||||
- 响应式设计
|
||||
|
||||
### lab.html
|
||||
- lab主页面
|
||||
- 项目管理功能
|
||||
- 实验管理功能
|
||||
- - 支持实验数据记录
|
||||
- - 支持实验备忘录
|
||||
- - 实时设备数据监控,公式计算
|
||||
- - 生成实验报告及AI问答
|
||||
|
||||
### paper.html
|
||||
- 论文分析功能
|
||||
- 支持论文上传
|
||||
- 论文分析报告生成
|
||||
- 支持流程图可视化
|
||||
- 集成了论文问答功能
|
||||
|
||||
### reports.html
|
||||
- 实验报告管理
|
||||
- 项目报告查看
|
||||
- 论文分析报告查看
|
||||
- 支持报告下载
|
||||
- 支持报告分类和排序
|
||||
|
||||
### device.html
|
||||
- 设备管理界面
|
||||
- 显示设备列表
|
||||
- 支持添加/删除设备
|
||||
- 显示设备详细信息(传感器等)
|
||||
- 支持设备状态监控
|
||||
|
||||
### device-monitor.html
|
||||
- 实时监控单一设备数据
|
||||
- 显示设备基本信息
|
||||
- 支持图表可视化展示数据
|
||||
- 支持自定义数据计算和显示
|
||||
- 使用 WebSocket 实现实时数据更新
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Bootstrap 5.1.3 - UI 框架
|
||||
- Chart.js - 图表可视化
|
||||
- Mermaid - 流程图渲染
|
||||
- WebSocket - 实时数据传输
|
||||
- JWT - 用户认证
|
||||
|
||||
## API 接口
|
||||
|
||||
所有页面通过统一的 API 基础 URL 访问后端服务:
|
||||
```javascript
|
||||
const API_BASE_URL = 'https://dev.obscura.work/lab'
|
||||
```
|
||||
|
||||
## 样式特点
|
||||
|
||||
- 现代化的 UI 设计
|
||||
- 响应式布局
|
||||
- 统一的色彩系统
|
||||
- 平滑的动画过渡
|
||||
- 良好的交互反馈
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 用户需要先登录/注册才能访问系统
|
||||
2. 登录后可以进行:
|
||||
- 项目管理
|
||||
- 设备管理
|
||||
- 实验记录
|
||||
- 论文分析
|
||||
- 报告生成
|
||||
3. 支持实时数据监控和可视化
|
||||
4. 可以生成和下载分析报告
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 需要现代浏览器支持
|
||||
- 需要保持网络连接以进行实时数据更新
|
||||
- 部分功能需要等待后端处理
|
||||
- 建议使用最新版本的 Chrome 或 Firefox 浏览器
|
||||
@@ -663,14 +663,6 @@
|
||||
<i class="bi bi-sort-down"></i>
|
||||
<span>Sort</span>
|
||||
</div>
|
||||
<div class="sidebar-item" onclick="generateSummaryReport()">
|
||||
<i class="bi bi-file-earmark-text"></i>
|
||||
<span>Summary</span>
|
||||
</div>
|
||||
<div class="sidebar-item" onclick="exportAllReports()">
|
||||
<i class="bi bi-download"></i>
|
||||
<span>Download</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1078,76 +1070,6 @@
|
||||
bootstrap.Modal.getInstance(document.getElementById('reportsSortModal')).hide();
|
||||
}
|
||||
|
||||
// Export all reports
|
||||
async function exportAllReports() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/projects`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const projects = await response.json();
|
||||
const reports = projects.map(project => ({
|
||||
project_name: project.project_name,
|
||||
description: project.description,
|
||||
project_reports: project.project_reports,
|
||||
paper_analysis: project.paper_analysis
|
||||
}));
|
||||
|
||||
const reportText = JSON.stringify(reports, null, 2);
|
||||
const blob = new Blob([reportText], { type: 'application/json' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'all_reports.json';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to export all reports:', error);
|
||||
alert('Failed to export all reports, please try again');
|
||||
}
|
||||
}
|
||||
|
||||
// Generate summary report
|
||||
async function generateSummaryReport() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/projects`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const projects = await response.json();
|
||||
const summary = projects.map(project => ({
|
||||
project_name: project.project_name,
|
||||
description: project.description,
|
||||
project_reports: project.project_reports,
|
||||
paper_analysis: project.paper_analysis
|
||||
}));
|
||||
|
||||
const summaryText = JSON.stringify(summary, null, 2);
|
||||
const blob = new Blob([summaryText], { type: 'application/json' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'summary_report.json';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to generate summary report:', error);
|
||||
alert('Failed to generate summary report, please try again');
|
||||
}
|
||||
}
|
||||
|
||||
// Switch between project report and paper analysis
|
||||
function switchReportSection(section) {
|
||||
// Remove active class from all sections
|
||||
-3930
File diff suppressed because it is too large
Load Diff
@@ -7,22 +7,22 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from openai import OpenAI
|
||||
|
||||
# Local application imports
|
||||
from .cores.config import client
|
||||
from .cores.db import (
|
||||
from app.cores.config import client
|
||||
from app.cores.db import (
|
||||
connect_to_mongo,
|
||||
close_mongo_connection,
|
||||
)
|
||||
from .routers.login import router as login_router
|
||||
from .routers.project import router as project_router
|
||||
from .routers.device import router as device_router
|
||||
from .routers.experiment_device import router as experiment_device_router
|
||||
from .routers.experiment import router as experiment_router
|
||||
from .routers.experiment_report import router as experiment_report_router
|
||||
from .routers.websocket import router as websocket_router
|
||||
from .routers.project_report import router as project_report_router
|
||||
from .routers.memo import router as memo_router
|
||||
from .routers.paper import router as paper_router
|
||||
from .routers.paper_summary import router as paper_summary_router
|
||||
from app.routers.login import router as login_router
|
||||
from app.routers.project import router as project_router
|
||||
from app.routers.device import router as device_router
|
||||
from app.routers.experiment_device import router as experiment_device_router
|
||||
from app.routers.experiment import router as experiment_router
|
||||
from app.routers.experiment_report import router as experiment_report_router
|
||||
from app.routers.websocket import router as websocket_router
|
||||
from app.routers.project_report import router as project_report_router
|
||||
from app.routers.memo import router as memo_router
|
||||
from app.routers.paper import router as paper_router
|
||||
from app.routers.paper_summary import router as paper_summary_router
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -0,0 +1,79 @@
|
||||
# 高速数据采集模块
|
||||
|
||||
这是一个基于MicroPython的高速数据采集模块,专门设计用于ESP32平台,可以进行高频率模拟信号采样并通过UDP网络传输。
|
||||
|
||||
## 主要功能
|
||||
|
||||
- 高速ADC采样(12位精度)
|
||||
- WiFi网络连接
|
||||
- UDP数据传输
|
||||
- 看门狗保护机制
|
||||
- 自动重试机制
|
||||
|
||||
## 技术规格
|
||||
|
||||
- 采样频率:20Hz
|
||||
- 每次采样包含50个数据点
|
||||
- ADC分辨率:12位
|
||||
- 网络协议:UDP
|
||||
- 看门狗超时时间:20秒
|
||||
|
||||
## 配置参数
|
||||
|
||||
```python
|
||||
SSID = "Obscura" # WiFi名称
|
||||
PASSWORD = "Obscura2024" # WiFi密码
|
||||
UDP_HOST = "222.186.10.253" # UDP服务器地址
|
||||
UDP_PORT = 6002 # UDP端口
|
||||
SERIAL_NUMBER = "ES01-208742cf95-efe5fb" # 设备序列号
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 导入模块:
|
||||
```python
|
||||
from boot import HighSpeedCollector
|
||||
```
|
||||
|
||||
2. 创建采集器实例:
|
||||
```python
|
||||
collector = HighSpeedCollector()
|
||||
```
|
||||
|
||||
3. 连接WiFi并启动采集:
|
||||
```python
|
||||
collector.connect_wifi()
|
||||
collector.setup_udp()
|
||||
collector.start()
|
||||
```
|
||||
|
||||
4. 停止采集:
|
||||
```python
|
||||
collector.stop()
|
||||
```
|
||||
|
||||
## 数据格式
|
||||
|
||||
每个UDP数据包的格式如下:
|
||||
- 时间戳(2字节)
|
||||
- 分片索引(2字节)
|
||||
- 50个采样数据点(每个2字节)
|
||||
|
||||
## 错误处理
|
||||
|
||||
- WiFi连接超时:30秒后触发异常
|
||||
- 自动重试机制:最多重试3次
|
||||
- 看门狗保护:防止程序死机
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保WiFi配置正确
|
||||
2. 确保UDP服务器地址可访问
|
||||
3. 程序运行时会自动进行内存回收(GC)
|
||||
4. 设备会定期发送序列号用于身份识别
|
||||
|
||||
## 依赖
|
||||
|
||||
- MicroPython
|
||||
- ESP32开发板
|
||||
- 需要ADC功能支持
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from .boot import HighSpeedCollector
|
||||
|
||||
__all__ = ['HighSpeedCollector']
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
# UDP数据采集服务器
|
||||
|
||||
这是一个基于Python异步IO的UDP数据采集服务器,用于接收、处理和转发实验设备的传感器数据。
|
||||
|
||||
## 主要功能
|
||||
|
||||
- 接收并解析设备传感器数据
|
||||
- 自动识别和管理设备序列号
|
||||
- 实时监控活跃实验状态
|
||||
- 数据存储到Redis流
|
||||
- 通过WebSocket实时转发数据
|
||||
- 支持数据降采样处理
|
||||
- 自动重连和错误恢复机制
|
||||
|
||||
## 系统要求
|
||||
|
||||
- Python 3.7+
|
||||
- MongoDB
|
||||
- Redis
|
||||
- 相关Python包依赖:
|
||||
- motor
|
||||
- websockets
|
||||
- redis
|
||||
- asyncio
|
||||
|
||||
## 配置说明
|
||||
|
||||
### MongoDB配置
|
||||
```python
|
||||
mongodb_url = "mongodb://lab:password@host:27017/lab"
|
||||
```
|
||||
|
||||
### Redis配置
|
||||
```python
|
||||
redis_url = 'redis://host:6379'
|
||||
redis_password = 'password'
|
||||
redis_db = 200
|
||||
```
|
||||
|
||||
### 服务器配置
|
||||
```python
|
||||
host = '0.0.0.0'
|
||||
port = 6002
|
||||
```
|
||||
|
||||
## 数据流程
|
||||
|
||||
1. 设备通过UDP发送数据包到服务器
|
||||
2. 服务器解析数据包,识别设备序列号
|
||||
3. 查询设备对应的活跃实验
|
||||
4. 将数据存储到Redis流
|
||||
5. 通过WebSocket实时转发处理后的数据
|
||||
|
||||
## 主要类和方法
|
||||
|
||||
### UDPServer
|
||||
- `init_mongodb()`: 初始化MongoDB连接
|
||||
- `init_redis()`: 初始化Redis连接
|
||||
- `update_active_experiments()`: 更新活跃实验信息
|
||||
- `process_and_forward_data()`: 处理和转发数据
|
||||
- `handle_sensor_data()`: 处理传感器数据
|
||||
- `batch_redis_write()`: 批量写入Redis
|
||||
|
||||
## 运行方式
|
||||
|
||||
```bash
|
||||
python data.py
|
||||
```
|
||||
|
||||
## 数据格式
|
||||
|
||||
### 传感器数据包格式
|
||||
|
||||
### WebSocket消息格式
|
||||
```json
|
||||
{
|
||||
"type": "data_batch",
|
||||
"data_points": [
|
||||
{
|
||||
"sensor_data": {
|
||||
"sensor_name": value
|
||||
},
|
||||
"timestamp": timestamp
|
||||
}
|
||||
],
|
||||
"serial_number": "device_serial",
|
||||
"total_points": count
|
||||
}
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
- 自动重连机制
|
||||
- 数据校验
|
||||
- 异常捕获和日志记录
|
||||
- 优雅关闭处理
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保MongoDB和Redis服务正常运行
|
||||
2. 检查网络防火墙设置,确保UDP端口开放
|
||||
3. 监控服务器资源使用情况
|
||||
4. 定期检查日志文件
|
||||
@@ -0,0 +1,64 @@
|
||||
# 设备管理系统 (Device Management System)
|
||||
|
||||
用于管理实验设备的Web应用系统,提供设备注册、序列号管理和在线状态监控等功能。
|
||||
|
||||
## 功能特点
|
||||
|
||||
### 1. 设备管理
|
||||
- 创建、编辑和删除设备
|
||||
- 支持多通道设备配置
|
||||
- 自定义传感器参数设置
|
||||
|
||||
### 2. 序列号管理
|
||||
- 批量生成设备序列号(每次10个)
|
||||
- 序列号状态跟踪
|
||||
- 序列号格式:`XX00-UUID(10位)-设备ID后6位`
|
||||
- XX: 设备类型前两位大写
|
||||
- 00: 批次号
|
||||
- UUID: 随机生成的10位字符
|
||||
- 设备ID后6位: 用作校验码
|
||||
|
||||
### 3. 在线设备监控
|
||||
- 实时设备状态监控
|
||||
- 设备活跃度追踪(5分钟超时)
|
||||
- 通道数据流监控
|
||||
|
||||
## 技术栈
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 文件夹结构
|
||||
device-manager/
|
||||
├── config/ # 配置文件
|
||||
│ ├── database.py # 数据库配置
|
||||
│ └── models.py # 数据模型
|
||||
├── device-register.py # 主程序
|
||||
├── device-register.html # web界面
|
||||
└── README.md
|
||||
|
||||
### 数据库配置
|
||||
|
||||
- MongoDB
|
||||
- URL: mongodb://lab:y6aHwySAhzrbibLD@222.186.10.253:27017/lab
|
||||
- 数据库: lab
|
||||
- 集合: devices
|
||||
- Redis
|
||||
- URL: redis://:Obscura@2024@222.186.10.253:6379
|
||||
- 数据库: db200
|
||||
- 用途: 存储设备实时状态和数据流
|
||||
|
||||
|
||||
## API 接口
|
||||
|
||||
### 设备管理
|
||||
- `POST /devices/devices` - 创建新设备
|
||||
- `GET /devices/devices` - 获取所有设备
|
||||
- `PUT /devices/devices/{device_id}` - 更新设备信息
|
||||
- `DELETE /devices/devices/{device_id}` - 删除设备
|
||||
|
||||
### 序列号管理
|
||||
- `POST /devices/devices/{device_id}/serials` - 生成设备序列号
|
||||
- `GET /devices/devices/{device_id}/serials` - 获取设备序列号列表
|
||||
|
||||
### 在线设备
|
||||
- `GET /devices/devices/online` - 获取在线设备列表
|
||||
@@ -0,0 +1,40 @@
|
||||
from motor.motor_asyncio import AsyncIOMotorClient
|
||||
from redis import asyncio as aioredis
|
||||
|
||||
# 数据库配置
|
||||
MONGODB_URL = "mongodb://lab:y6aHwySAhzrbibLD@222.186.10.253:27017/lab"
|
||||
REDIS_URL = "redis://:Obscura@2024@222.186.10.253:6379"
|
||||
|
||||
# 数据库连接类
|
||||
class Database:
|
||||
"""数据库连接管理类"""
|
||||
client: AsyncIOMotorClient = None
|
||||
|
||||
db = Database()
|
||||
redis_client = None
|
||||
|
||||
async def get_database():
|
||||
"""获取数据库连接"""
|
||||
if db.client is None:
|
||||
await connect_to_mongo()
|
||||
return db.client["lab"]
|
||||
|
||||
async def connect_to_mongo():
|
||||
try:
|
||||
db.client = AsyncIOMotorClient(MONGODB_URL)
|
||||
# 验证连接
|
||||
await db.client.admin.command('ping')
|
||||
print("Successfully connected to MongoDB")
|
||||
except Exception as e:
|
||||
print(f"Could not connect to MongoDB: {e}")
|
||||
raise
|
||||
|
||||
async def close_mongo_connection():
|
||||
if db.client:
|
||||
db.client.close()
|
||||
|
||||
async def init_redis():
|
||||
global redis_client
|
||||
if redis_client is None:
|
||||
redis_client = await aioredis.from_url(REDIS_URL, db=200)
|
||||
return redis_client
|
||||
@@ -0,0 +1,59 @@
|
||||
from typing import Optional, List
|
||||
from pydantic import BaseModel, Field
|
||||
from bson import ObjectId
|
||||
|
||||
class PyObjectId(ObjectId):
|
||||
"""
|
||||
自定义ObjectId类,用于在Pydantic模型中处理MongoDB的ObjectId
|
||||
"""
|
||||
@classmethod
|
||||
def __get_validators__(cls):
|
||||
yield cls.validate
|
||||
|
||||
@classmethod
|
||||
def validate(cls, v, handler):
|
||||
if not ObjectId.is_valid(v):
|
||||
raise ValueError("Invalid ObjectId")
|
||||
return ObjectId(v)
|
||||
|
||||
@classmethod
|
||||
def __get_pydantic_json_schema__(cls, _schema_cache, **_kwargs):
|
||||
return {
|
||||
'type': 'string',
|
||||
'description': 'ObjectId',
|
||||
'pattern': r'^[0-9a-fA-F]{24}$'
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def __modify_schema__(cls, field_schema):
|
||||
field_schema.update(
|
||||
type='string',
|
||||
description='ObjectId',
|
||||
pattern=r'^[0-9a-fA-F]{24}$'
|
||||
)
|
||||
|
||||
class SensorModel(BaseModel):
|
||||
"""传感器模型"""
|
||||
index: str
|
||||
sensor_name: str
|
||||
sensor_type: str
|
||||
unit: str
|
||||
|
||||
class DeviceModel(BaseModel):
|
||||
id: Optional[PyObjectId] = Field(alias="_id", default=None)
|
||||
device_name: str
|
||||
device_type: str
|
||||
device_number: int
|
||||
serial_numbers: list = Field(default_factory=list)
|
||||
sensors: List[SensorModel] = Field(default_factory=list)
|
||||
|
||||
class Config:
|
||||
populate_by_name = True
|
||||
arbitrary_types_allowed = True
|
||||
json_encoders = {ObjectId: str}
|
||||
|
||||
def dict(self, *args, **kwargs):
|
||||
"""确保返回的字典包含 _id 字段"""
|
||||
kwargs["by_alias"] = True
|
||||
return super().dict(*args, **kwargs)
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Device routes
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from typing import Optional
|
||||
from motor.motor_asyncio import AsyncIOMotorClient
|
||||
from bson import ObjectId
|
||||
from pydantic import BaseModel, Field
|
||||
from contextlib import asynccontextmanager
|
||||
from datetime import datetime, timedelta
|
||||
import uuid
|
||||
from redis import asyncio as aioredis
|
||||
import asyncio
|
||||
# Database Configuration
|
||||
MONGODB_URL = "mongodb://lab:y6aHwySAhzrbibLD@222.186.10.253:27017/lab"
|
||||
from config.database import (
|
||||
get_database, connect_to_mongo, close_mongo_connection,
|
||||
init_redis
|
||||
)
|
||||
from config.models import DeviceModel
|
||||
|
||||
# 更新 FastAPI 实例化
|
||||
@asynccontextmanager
|
||||
@@ -35,87 +35,7 @@ app.add_middleware(
|
||||
expose_headers=["*"]
|
||||
)
|
||||
|
||||
# MongoDB setup
|
||||
class PyObjectId(ObjectId):
|
||||
"""
|
||||
自定义ObjectId类,用于在Pydantic模型中处理MongoDB的ObjectId
|
||||
"""
|
||||
@classmethod
|
||||
def __get_validators__(cls):
|
||||
yield cls.validate
|
||||
|
||||
@classmethod
|
||||
def validate(cls, v, handler):
|
||||
if not ObjectId.is_valid(v):
|
||||
raise ValueError("Invalid ObjectId")
|
||||
return ObjectId(v)
|
||||
|
||||
@classmethod
|
||||
def __get_pydantic_json_schema__(cls, _schema_cache, **_kwargs):
|
||||
return {
|
||||
'type': 'string',
|
||||
'description': 'ObjectId',
|
||||
'pattern': r'^[0-9a-fA-F]{24}$'
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def __modify_schema__(cls, field_schema):
|
||||
field_schema.update(
|
||||
type='string',
|
||||
description='ObjectId',
|
||||
pattern=r'^[0-9a-fA-F]{24}$'
|
||||
)
|
||||
|
||||
class SensorModel(BaseModel):
|
||||
"""传感器模型"""
|
||||
index: str
|
||||
sensor_name: str
|
||||
sensor_type: str
|
||||
unit: str
|
||||
|
||||
class DeviceModel(BaseModel):
|
||||
id: Optional[PyObjectId] = Field(alias="_id", default=None)
|
||||
device_name: str
|
||||
device_type: str
|
||||
device_number: int
|
||||
serial_numbers: list = Field(default_factory=list)
|
||||
sensors: list[SensorModel] = Field(default_factory=list)
|
||||
|
||||
class Config:
|
||||
populate_by_name = True
|
||||
arbitrary_types_allowed = True
|
||||
json_encoders = {ObjectId: str}
|
||||
|
||||
def dict(self, *args, **kwargs):
|
||||
"""确保返回的字典包含 _id 字段"""
|
||||
kwargs["by_alias"] = True
|
||||
return super().dict(*args, **kwargs)
|
||||
|
||||
# Database connection
|
||||
class Database:
|
||||
"""数据库连接管理类"""
|
||||
client: AsyncIOMotorClient = None
|
||||
|
||||
db = Database()
|
||||
|
||||
async def get_database():
|
||||
"""获取数据库连接"""
|
||||
if db.client is None:
|
||||
await connect_to_mongo()
|
||||
return db.client["lab"]
|
||||
|
||||
async def connect_to_mongo():
|
||||
try:
|
||||
db.client = AsyncIOMotorClient(MONGODB_URL)
|
||||
# 验证连接
|
||||
await db.client.admin.command('ping')
|
||||
print("Successfully connected to MongoDB")
|
||||
except Exception as e:
|
||||
print(f"Could not connect to MongoDB: {e}")
|
||||
raise
|
||||
|
||||
async def close_mongo_connection():
|
||||
db.client.close()
|
||||
|
||||
@app.post("/devices/devices")
|
||||
async def create_device(device: DeviceModel):
|
||||
@@ -299,23 +219,12 @@ async def update_device(device_id: str, device: DeviceModel):
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
# 添加Redis连接配置
|
||||
REDIS_URL = "redis://:Obscura@2024@222.186.10.253:6379"
|
||||
redis_client = None
|
||||
|
||||
# 用于存储上一次检查时的stream长度
|
||||
last_stream_lengths = {}
|
||||
|
||||
# 在全局变量中添加设备活跃时间记录
|
||||
device_last_active = {}
|
||||
|
||||
# 初始化Redis连接
|
||||
async def init_redis():
|
||||
global redis_client
|
||||
if redis_client is None:
|
||||
redis_client = await aioredis.from_url(REDIS_URL, db=200)
|
||||
return redis_client
|
||||
|
||||
@app.get("/devices/devices/online")
|
||||
async def get_online_devices():
|
||||
"""获取在线设备列表"""
|
||||
Reference in New Issue
Block a user