feat
This commit is contained in:
parent
6e7e065763
commit
eb8af4ba8b
40
server/app.py
Normal file
40
server/app.py
Normal file
@ -0,0 +1,40 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from routers import algorithms, events, devices
|
||||
from core.database import engine
|
||||
from models.base import Base
|
||||
|
||||
# 创建数据库表
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
app = FastAPI(
|
||||
title="边检CV算法接口服务",
|
||||
description="边检计算机视觉算法管理系统API",
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
# 配置CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # 生产环境中应该指定具体域名
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 注册路由
|
||||
app.include_router(algorithms.router, prefix="/api/algorithms", tags=["算法管理"])
|
||||
app.include_router(events.router, prefix="/api/events", tags=["事件管理"])
|
||||
app.include_router(devices.router, prefix="/api/devices", tags=["设备管理"])
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "边检CV算法接口服务"}
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {"status": "healthy"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000, reload=True, workers=10)
|
27
server/core/database.py
Normal file
27
server/core/database.py
Normal file
@ -0,0 +1,27 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
import os
|
||||
|
||||
# 数据库URL
|
||||
SQLALCHEMY_DATABASE_URL = "sqlite:///./border_inspection.db"
|
||||
|
||||
# 创建数据库引擎
|
||||
engine = create_engine(
|
||||
SQLALCHEMY_DATABASE_URL,
|
||||
connect_args={"check_same_thread": False}
|
||||
)
|
||||
|
||||
# 创建会话工厂
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
# 创建基础模型类
|
||||
Base = declarative_base()
|
||||
|
||||
# 依赖注入函数
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
19
server/env.example
Normal file
19
server/env.example
Normal file
@ -0,0 +1,19 @@
|
||||
# 数据库配置
|
||||
DATABASE_URL=sqlite:///./border_inspection.db
|
||||
|
||||
# 服务器配置
|
||||
HOST=0.0.0.0
|
||||
PORT=8000
|
||||
|
||||
# 安全配置
|
||||
SECRET_KEY=your-secret-key-here
|
||||
ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
|
||||
# 文件上传配置
|
||||
UPLOAD_DIR=./uploads
|
||||
MAX_FILE_SIZE=10485760 # 10MB
|
||||
|
||||
# 日志配置
|
||||
LOG_LEVEL=INFO
|
||||
LOG_FILE=./logs/app.log
|
219
server/init_data.py
Normal file
219
server/init_data.py
Normal file
@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
初始化示例数据脚本
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from core.database import SessionLocal
|
||||
from models.algorithm import Algorithm
|
||||
from models.device import Device
|
||||
from models.event import Event
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
|
||||
def init_sample_data():
|
||||
"""初始化示例数据"""
|
||||
db = SessionLocal()
|
||||
|
||||
try:
|
||||
# 创建示例算法
|
||||
algorithms = [
|
||||
{
|
||||
"name": "YOLOv11n人员检测",
|
||||
"description": "基于YOLOv11n的人员检测算法,适用于边检场景",
|
||||
"version": "1.0.0",
|
||||
"model_path": "/models/yolo11n.pt",
|
||||
"config_path": "/configs/yolo11n.yaml",
|
||||
"status": "active",
|
||||
"accuracy": 0.95,
|
||||
"detection_classes": json.dumps(["person"]),
|
||||
"input_size": "640x640",
|
||||
"inference_time": 15.5,
|
||||
"is_enabled": True,
|
||||
"creator": "admin",
|
||||
"tags": json.dumps(["person", "detection", "yolo"])
|
||||
},
|
||||
{
|
||||
"name": "车辆检测算法",
|
||||
"description": "专门用于车辆检测的深度学习算法",
|
||||
"version": "2.1.0",
|
||||
"model_path": "/models/vehicle_detection.pt",
|
||||
"config_path": "/configs/vehicle.yaml",
|
||||
"status": "active",
|
||||
"accuracy": 0.92,
|
||||
"detection_classes": json.dumps(["car", "truck", "bus", "motorcycle"]),
|
||||
"input_size": "640x640",
|
||||
"inference_time": 18.2,
|
||||
"is_enabled": True,
|
||||
"creator": "admin",
|
||||
"tags": json.dumps(["vehicle", "detection"])
|
||||
},
|
||||
{
|
||||
"name": "人脸识别算法",
|
||||
"description": "高精度人脸识别算法",
|
||||
"version": "1.5.0",
|
||||
"model_path": "/models/face_recognition.pt",
|
||||
"config_path": "/configs/face.yaml",
|
||||
"status": "inactive",
|
||||
"accuracy": 0.98,
|
||||
"detection_classes": json.dumps(["face"]),
|
||||
"input_size": "512x512",
|
||||
"inference_time": 25.0,
|
||||
"is_enabled": False,
|
||||
"creator": "admin",
|
||||
"tags": json.dumps(["face", "recognition"])
|
||||
}
|
||||
]
|
||||
|
||||
for alg_data in algorithms:
|
||||
algorithm = Algorithm(**alg_data)
|
||||
db.add(algorithm)
|
||||
|
||||
db.commit()
|
||||
print("✅ 算法数据初始化完成")
|
||||
|
||||
# 创建示例设备
|
||||
devices = [
|
||||
{
|
||||
"name": "港口A区主监控",
|
||||
"device_type": "camera",
|
||||
"location": "港口A区",
|
||||
"ip_address": "192.168.1.100",
|
||||
"port": 554,
|
||||
"username": "admin",
|
||||
"password": "admin123",
|
||||
"rtsp_url": "rtsp://192.168.1.100:554/stream1",
|
||||
"status": "online",
|
||||
"resolution": "1920x1080",
|
||||
"fps": 25,
|
||||
"algorithm_id": 1,
|
||||
"is_enabled": True,
|
||||
"latitude": 22.3193,
|
||||
"longitude": 114.1694,
|
||||
"description": "港口A区主要监控摄像头",
|
||||
"manufacturer": "Hikvision",
|
||||
"model": "DS-2CD2T47G1-L",
|
||||
"serial_number": "HK123456789"
|
||||
},
|
||||
{
|
||||
"name": "港口B区监控",
|
||||
"device_type": "camera",
|
||||
"location": "港口B区",
|
||||
"ip_address": "192.168.1.101",
|
||||
"port": 554,
|
||||
"username": "admin",
|
||||
"password": "admin123",
|
||||
"rtsp_url": "rtsp://192.168.1.101:554/stream1",
|
||||
"status": "online",
|
||||
"resolution": "1920x1080",
|
||||
"fps": 25,
|
||||
"algorithm_id": 2,
|
||||
"is_enabled": True,
|
||||
"latitude": 22.3195,
|
||||
"longitude": 114.1696,
|
||||
"description": "港口B区监控摄像头",
|
||||
"manufacturer": "Dahua",
|
||||
"model": "IPC-HFW4431R-ZE",
|
||||
"serial_number": "DH987654321"
|
||||
},
|
||||
{
|
||||
"name": "边检站门禁",
|
||||
"device_type": "gate",
|
||||
"location": "边检站入口",
|
||||
"ip_address": "192.168.1.102",
|
||||
"port": 80,
|
||||
"username": "admin",
|
||||
"password": "admin123",
|
||||
"status": "online",
|
||||
"is_enabled": True,
|
||||
"latitude": 22.3190,
|
||||
"longitude": 114.1690,
|
||||
"description": "边检站入口门禁系统",
|
||||
"manufacturer": "Suprema",
|
||||
"model": "BioStation 2",
|
||||
"serial_number": "SP123456789"
|
||||
}
|
||||
]
|
||||
|
||||
for dev_data in devices:
|
||||
device = Device(**dev_data)
|
||||
db.add(device)
|
||||
|
||||
db.commit()
|
||||
print("✅ 设备数据初始化完成")
|
||||
|
||||
# 创建示例事件
|
||||
events = [
|
||||
{
|
||||
"event_type": "person_detection",
|
||||
"device_id": 1,
|
||||
"algorithm_id": 1,
|
||||
"severity": "medium",
|
||||
"status": "pending",
|
||||
"confidence": 0.95,
|
||||
"bbox": json.dumps([100, 150, 80, 160]),
|
||||
"image_path": "/events/images/person_001.jpg",
|
||||
"description": "检测到人员进入监控区域",
|
||||
"location": "港口A区",
|
||||
"detected_objects": json.dumps([{"type": "person", "confidence": 0.95}]),
|
||||
"processing_time": 15.5,
|
||||
"is_alert": True,
|
||||
"alert_sent": True
|
||||
},
|
||||
{
|
||||
"event_type": "vehicle_detection",
|
||||
"device_id": 2,
|
||||
"algorithm_id": 2,
|
||||
"severity": "low",
|
||||
"status": "resolved",
|
||||
"confidence": 0.92,
|
||||
"bbox": json.dumps([200, 100, 120, 80]),
|
||||
"image_path": "/events/images/vehicle_001.jpg",
|
||||
"description": "检测到车辆通过",
|
||||
"location": "港口B区",
|
||||
"detected_objects": json.dumps([{"type": "car", "confidence": 0.92}]),
|
||||
"processing_time": 18.2,
|
||||
"is_alert": False,
|
||||
"alert_sent": False,
|
||||
"operator_id": 1,
|
||||
"resolution_notes": "正常车辆通行",
|
||||
"resolved_at": datetime.utcnow() - timedelta(hours=2)
|
||||
},
|
||||
{
|
||||
"event_type": "intrusion",
|
||||
"device_id": 1,
|
||||
"algorithm_id": 1,
|
||||
"severity": "high",
|
||||
"status": "processing",
|
||||
"confidence": 0.88,
|
||||
"bbox": json.dumps([300, 200, 60, 120]),
|
||||
"image_path": "/events/images/intrusion_001.jpg",
|
||||
"description": "检测到可疑人员入侵",
|
||||
"location": "港口A区",
|
||||
"detected_objects": json.dumps([{"type": "person", "confidence": 0.88}]),
|
||||
"processing_time": 16.0,
|
||||
"is_alert": True,
|
||||
"alert_sent": True
|
||||
}
|
||||
]
|
||||
|
||||
for evt_data in events:
|
||||
event = Event(**evt_data)
|
||||
db.add(event)
|
||||
|
||||
db.commit()
|
||||
print("✅ 事件数据初始化完成")
|
||||
|
||||
print("\n🎉 所有示例数据初始化完成!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 初始化数据时出错: {e}")
|
||||
db.rollback()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
init_sample_data()
|
6
server/models/__init__.py
Normal file
6
server/models/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from .algorithm import Algorithm
|
||||
from .event import Event
|
||||
from .device import Device
|
||||
from .base import Base
|
||||
|
||||
__all__ = ["Algorithm", "Event", "Device", "Base"]
|
19
server/models/algorithm.py
Normal file
19
server/models/algorithm.py
Normal file
@ -0,0 +1,19 @@
|
||||
from sqlalchemy import Column, String, Text, Boolean, Float, Integer
|
||||
from .base import BaseModel
|
||||
|
||||
class Algorithm(BaseModel):
|
||||
__tablename__ = "algorithms"
|
||||
|
||||
name = Column(String(100), nullable=False, comment="算法名称")
|
||||
description = Column(Text, comment="算法描述")
|
||||
version = Column(String(20), nullable=False, comment="算法版本")
|
||||
model_path = Column(String(255), comment="模型文件路径")
|
||||
config_path = Column(String(255), comment="配置文件路径")
|
||||
status = Column(String(20), default="inactive", comment="算法状态: active, inactive, training")
|
||||
accuracy = Column(Float, comment="算法准确率")
|
||||
detection_classes = Column(Text, comment="检测类别,JSON格式")
|
||||
input_size = Column(String(20), comment="输入尺寸,如: 640x640")
|
||||
inference_time = Column(Float, comment="推理时间(ms)")
|
||||
is_enabled = Column(Boolean, default=True, comment="是否启用")
|
||||
creator = Column(String(50), comment="创建者")
|
||||
tags = Column(Text, comment="标签,JSON格式")
|
12
server/models/base.py
Normal file
12
server/models/base.py
Normal file
@ -0,0 +1,12 @@
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import Column, Integer, DateTime
|
||||
from datetime import datetime
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class BaseModel(Base):
|
||||
__abstract__ = True
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
26
server/models/device.py
Normal file
26
server/models/device.py
Normal file
@ -0,0 +1,26 @@
|
||||
from sqlalchemy import Column, String, Text, Boolean, Integer, Float, DateTime
|
||||
from .base import BaseModel
|
||||
|
||||
class Device(BaseModel):
|
||||
__tablename__ = "devices"
|
||||
|
||||
name = Column(String(100), nullable=False, comment="设备名称")
|
||||
device_type = Column(String(50), nullable=False, comment="设备类型: camera, sensor, etc")
|
||||
location = Column(String(200), comment="设备位置")
|
||||
ip_address = Column(String(50), comment="IP地址")
|
||||
port = Column(Integer, comment="端口号")
|
||||
username = Column(String(50), comment="用户名")
|
||||
password = Column(String(100), comment="密码")
|
||||
rtsp_url = Column(String(500), comment="RTSP流地址")
|
||||
status = Column(String(20), default="offline", comment="设备状态: online, offline, error")
|
||||
resolution = Column(String(20), comment="分辨率,如: 1920x1080")
|
||||
fps = Column(Integer, comment="帧率")
|
||||
algorithm_id = Column(Integer, comment="关联的算法ID")
|
||||
is_enabled = Column(Boolean, default=True, comment="是否启用")
|
||||
last_heartbeat = Column(DateTime, comment="最后心跳时间")
|
||||
latitude = Column(Float, comment="纬度")
|
||||
longitude = Column(Float, comment="经度")
|
||||
description = Column(Text, comment="设备描述")
|
||||
manufacturer = Column(String(100), comment="制造商")
|
||||
model = Column(String(100), comment="设备型号")
|
||||
serial_number = Column(String(100), comment="序列号")
|
24
server/models/event.py
Normal file
24
server/models/event.py
Normal file
@ -0,0 +1,24 @@
|
||||
from sqlalchemy import Column, String, Text, Boolean, Integer, Float, DateTime, JSON
|
||||
from .base import BaseModel
|
||||
|
||||
class Event(BaseModel):
|
||||
__tablename__ = "events"
|
||||
|
||||
event_type = Column(String(50), nullable=False, comment="事件类型: person_detection, vehicle_detection, intrusion, etc")
|
||||
device_id = Column(Integer, nullable=False, comment="关联设备ID")
|
||||
algorithm_id = Column(Integer, comment="关联算法ID")
|
||||
severity = Column(String(20), default="medium", comment="严重程度: low, medium, high, critical")
|
||||
status = Column(String(20), default="pending", comment="事件状态: pending, processing, resolved, ignored")
|
||||
confidence = Column(Float, comment="置信度")
|
||||
bbox = Column(Text, comment="边界框坐标,JSON格式: [x, y, width, height]")
|
||||
image_path = Column(String(500), comment="事件图片路径")
|
||||
video_path = Column(String(500), comment="事件视频路径")
|
||||
description = Column(Text, comment="事件描述")
|
||||
location = Column(String(200), comment="事件发生位置")
|
||||
detected_objects = Column(Text, comment="检测到的对象,JSON格式")
|
||||
processing_time = Column(Float, comment="处理时间(ms)")
|
||||
is_alert = Column(Boolean, default=False, comment="是否触发告警")
|
||||
alert_sent = Column(Boolean, default=False, comment="是否已发送告警")
|
||||
operator_id = Column(Integer, comment="处理人员ID")
|
||||
resolution_notes = Column(Text, comment="处理备注")
|
||||
resolved_at = Column(DateTime, comment="解决时间")
|
177
server/readme.md
Normal file
177
server/readme.md
Normal file
@ -0,0 +1,177 @@
|
||||
# 边检CV算法接口服务
|
||||
|
||||
### 技术栈
|
||||
- **后端框架**: FastAPI
|
||||
- **数据库**: SQLite (SQLAlchemy ORM)
|
||||
- **AI模型**: YOLOv11n
|
||||
- **进程管理**: Supervisor
|
||||
- **开发语言**: Python 3.8+
|
||||
|
||||
### 项目结构
|
||||
|
||||
```
|
||||
server/
|
||||
├── app.py # FastAPI主应用
|
||||
├── start.py # 启动脚本
|
||||
├── requirements.txt # Python依赖
|
||||
├── env.example # 环境变量示例
|
||||
├── init_data.py # 示例数据初始化
|
||||
├── core/ # 核心配置
|
||||
│ └── database.py # 数据库配置
|
||||
├── models/ # 数据模型
|
||||
│ ├── __init__.py
|
||||
│ ├── base.py # 基础模型
|
||||
│ ├── algorithm.py # 算法模型
|
||||
│ ├── device.py # 设备模型
|
||||
│ └── event.py # 事件模型
|
||||
├── schemas/ # Pydantic模型
|
||||
│ ├── __init__.py
|
||||
│ ├── algorithm.py # 算法相关模型
|
||||
│ ├── device.py # 设备相关模型
|
||||
│ └── event.py # 事件相关模型
|
||||
└── routers/ # API路由
|
||||
├── __init__.py
|
||||
├── algorithms.py # 算法管理接口
|
||||
├── devices.py # 设备管理接口
|
||||
└── events.py # 事件管理接口
|
||||
```
|
||||
|
||||
### 功能模块
|
||||
|
||||
#### 1. 算法管理模块
|
||||
- 算法的增删改查
|
||||
- 算法状态管理(启用/禁用)
|
||||
- 算法版本管理
|
||||
- 算法性能指标(准确率、推理时间等)
|
||||
|
||||
#### 2. 设备管理模块
|
||||
- 设备信息管理(摄像头、传感器等)
|
||||
- 设备状态监控
|
||||
- 设备类型管理
|
||||
- 设备地理位置信息
|
||||
|
||||
#### 3. 事件管理模块
|
||||
- 事件记录和查询
|
||||
- 事件状态管理
|
||||
- 事件统计分析
|
||||
- 告警管理
|
||||
|
||||
### API接口
|
||||
|
||||
#### 算法管理接口
|
||||
- `POST /api/algorithms/` - 创建算法
|
||||
- `GET /api/algorithms/` - 获取算法列表
|
||||
- `GET /api/algorithms/{id}` - 获取算法详情
|
||||
- `PUT /api/algorithms/{id}` - 更新算法
|
||||
- `DELETE /api/algorithms/{id}` - 删除算法
|
||||
- `PATCH /api/algorithms/{id}/status` - 更新算法状态
|
||||
- `PATCH /api/algorithms/{id}/enable` - 启用/禁用算法
|
||||
|
||||
#### 设备管理接口
|
||||
- `POST /api/devices/` - 创建设备
|
||||
- `GET /api/devices/` - 获取设备列表
|
||||
- `GET /api/devices/{id}` - 获取设备详情
|
||||
- `PUT /api/devices/{id}` - 更新设备
|
||||
- `DELETE /api/devices/{id}` - 删除设备
|
||||
- `PATCH /api/devices/{id}/status` - 更新设备状态
|
||||
- `PATCH /api/devices/{id}/enable` - 启用/禁用设备
|
||||
- `GET /api/devices/types/list` - 获取设备类型列表
|
||||
- `GET /api/devices/status/stats` - 获取设备状态统计
|
||||
|
||||
#### 事件管理接口
|
||||
- `POST /api/events/` - 创建事件
|
||||
- `GET /api/events/` - 获取事件列表
|
||||
- `GET /api/events/{id}` - 获取事件详情
|
||||
- `PUT /api/events/{id}` - 更新事件
|
||||
- `DELETE /api/events/{id}` - 删除事件
|
||||
- `PATCH /api/events/{id}/status` - 更新事件状态
|
||||
- `GET /api/events/types/list` - 获取事件类型列表
|
||||
- `GET /api/events/stats/summary` - 获取事件统计摘要
|
||||
- `GET /api/events/stats/by-type` - 按类型统计事件
|
||||
|
||||
### 安装和运行
|
||||
|
||||
#### 1. 环境准备
|
||||
```bash
|
||||
# 创建虚拟环境
|
||||
conda create -n border_inspection python=3.8
|
||||
conda activate border_inspection
|
||||
|
||||
# 安装依赖
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
#### 2. 初始化数据库
|
||||
```bash
|
||||
# 启动服务(会自动创建数据库表)
|
||||
python start.py
|
||||
```
|
||||
|
||||
#### 3. 初始化示例数据
|
||||
```bash
|
||||
python init_data.py
|
||||
```
|
||||
|
||||
#### 4. 启动服务
|
||||
```bash
|
||||
python start.py
|
||||
```
|
||||
|
||||
服务将在 `http://localhost:8000` 启动
|
||||
|
||||
### API文档
|
||||
|
||||
启动服务后,可以访问以下地址查看API文档:
|
||||
- Swagger UI: `http://localhost:8000/docs`
|
||||
- ReDoc: `http://localhost:8000/redoc`
|
||||
|
||||
### 数据库设计
|
||||
|
||||
#### 算法表 (algorithms)
|
||||
- 基本信息:名称、描述、版本
|
||||
- 模型信息:模型路径、配置文件路径
|
||||
- 性能指标:准确率、推理时间、输入尺寸
|
||||
- 状态管理:启用状态、算法状态
|
||||
- 分类信息:检测类别、标签
|
||||
|
||||
#### 设备表 (devices)
|
||||
- 基本信息:名称、类型、位置
|
||||
- 连接信息:IP地址、端口、用户名、密码
|
||||
- 视频流:RTSP地址、分辨率、帧率
|
||||
- 状态信息:在线状态、最后心跳时间
|
||||
- 地理位置:经纬度坐标
|
||||
|
||||
#### 事件表 (events)
|
||||
- 事件信息:类型、设备ID、算法ID
|
||||
- 检测结果:置信度、边界框、检测对象
|
||||
- 状态管理:事件状态、严重程度
|
||||
- 告警信息:是否告警、告警发送状态
|
||||
- 处理信息:处理人员、处理备注、解决时间
|
||||
|
||||
### 开发说明
|
||||
|
||||
1. **添加新模型**: 在 `models/` 目录下创建新的模型文件
|
||||
2. **添加新接口**: 在 `routers/` 目录下创建新的路由文件
|
||||
3. **添加新Schema**: 在 `schemas/` 目录下创建新的Pydantic模型
|
||||
4. **数据库迁移**: 修改模型后重启服务,数据库表会自动更新
|
||||
|
||||
### 部署说明
|
||||
|
||||
#### 使用Supervisor管理进程
|
||||
```ini
|
||||
[program:border_inspection]
|
||||
command=python /path/to/server/start.py
|
||||
directory=/path/to/server
|
||||
user=www-data
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stderr_logfile=/var/log/border_inspection.err.log
|
||||
stdout_logfile=/var/log/border_inspection.out.log
|
||||
```
|
||||
|
||||
#### 环境变量配置
|
||||
复制 `env.example` 为 `.env` 并修改相应配置:
|
||||
```bash
|
||||
cp env.example .env
|
||||
# 编辑 .env 文件
|
||||
```
|
12
server/requirements.txt
Normal file
12
server/requirements.txt
Normal file
@ -0,0 +1,12 @@
|
||||
fastapi==0.104.1
|
||||
uvicorn==0.24.0
|
||||
sqlalchemy==2.0.23
|
||||
pydantic==2.5.0
|
||||
python-multipart==0.0.6
|
||||
python-jose[cryptography]==3.3.0
|
||||
passlib[bcrypt]==1.7.4
|
||||
python-dotenv==1.0.0
|
||||
aiofiles==23.2.1
|
||||
pillow==10.1.0
|
||||
opencv-python==4.8.1.78
|
||||
ultralytics==8.0.196
|
1
server/routers/__init__.py
Normal file
1
server/routers/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
# 路由包
|
130
server/routers/algorithms.py
Normal file
130
server/routers/algorithms.py
Normal file
@ -0,0 +1,130 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
from core.database import get_db
|
||||
from models.algorithm import Algorithm
|
||||
from schemas.algorithm import (
|
||||
AlgorithmCreate,
|
||||
AlgorithmUpdate,
|
||||
AlgorithmResponse,
|
||||
AlgorithmListResponse
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/", response_model=AlgorithmResponse, summary="创建算法")
|
||||
async def create_algorithm(
|
||||
algorithm: AlgorithmCreate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""创建新的算法"""
|
||||
db_algorithm = Algorithm(**algorithm.dict())
|
||||
db.add(db_algorithm)
|
||||
db.commit()
|
||||
db.refresh(db_algorithm)
|
||||
return db_algorithm
|
||||
|
||||
@router.get("/", response_model=AlgorithmListResponse, summary="获取算法列表")
|
||||
async def get_algorithms(
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(10, ge=1, le=100, description="返回记录数"),
|
||||
name: Optional[str] = Query(None, description="算法名称"),
|
||||
status: Optional[str] = Query(None, description="算法状态"),
|
||||
is_enabled: Optional[bool] = Query(None, description="是否启用"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取算法列表,支持分页和筛选"""
|
||||
query = db.query(Algorithm)
|
||||
|
||||
if name:
|
||||
query = query.filter(Algorithm.name.contains(name))
|
||||
if status:
|
||||
query = query.filter(Algorithm.status == status)
|
||||
if is_enabled is not None:
|
||||
query = query.filter(Algorithm.is_enabled == is_enabled)
|
||||
|
||||
total = query.count()
|
||||
algorithms = query.offset(skip).limit(limit).all()
|
||||
|
||||
return AlgorithmListResponse(
|
||||
algorithms=algorithms,
|
||||
total=total,
|
||||
page=skip // limit + 1,
|
||||
size=limit
|
||||
)
|
||||
|
||||
@router.get("/{algorithm_id}", response_model=AlgorithmResponse, summary="获取算法详情")
|
||||
async def get_algorithm(
|
||||
algorithm_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""根据ID获取算法详情"""
|
||||
algorithm = db.query(Algorithm).filter(Algorithm.id == algorithm_id).first()
|
||||
if not algorithm:
|
||||
raise HTTPException(status_code=404, detail="算法不存在")
|
||||
return algorithm
|
||||
|
||||
@router.put("/{algorithm_id}", response_model=AlgorithmResponse, summary="更新算法")
|
||||
async def update_algorithm(
|
||||
algorithm_id: int,
|
||||
algorithm: AlgorithmUpdate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""更新算法信息"""
|
||||
db_algorithm = db.query(Algorithm).filter(Algorithm.id == algorithm_id).first()
|
||||
if not db_algorithm:
|
||||
raise HTTPException(status_code=404, detail="算法不存在")
|
||||
|
||||
update_data = algorithm.dict(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(db_algorithm, field, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_algorithm)
|
||||
return db_algorithm
|
||||
|
||||
@router.delete("/{algorithm_id}", summary="删除算法")
|
||||
async def delete_algorithm(
|
||||
algorithm_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""删除算法"""
|
||||
algorithm = db.query(Algorithm).filter(Algorithm.id == algorithm_id).first()
|
||||
if not algorithm:
|
||||
raise HTTPException(status_code=404, detail="算法不存在")
|
||||
|
||||
db.delete(algorithm)
|
||||
db.commit()
|
||||
return {"message": "算法删除成功"}
|
||||
|
||||
@router.patch("/{algorithm_id}/status", response_model=AlgorithmResponse, summary="更新算法状态")
|
||||
async def update_algorithm_status(
|
||||
algorithm_id: int,
|
||||
status: str = Query(..., description="新状态"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""更新算法状态"""
|
||||
algorithm = db.query(Algorithm).filter(Algorithm.id == algorithm_id).first()
|
||||
if not algorithm:
|
||||
raise HTTPException(status_code=404, detail="算法不存在")
|
||||
|
||||
algorithm.status = status
|
||||
db.commit()
|
||||
db.refresh(algorithm)
|
||||
return algorithm
|
||||
|
||||
@router.patch("/{algorithm_id}/enable", response_model=AlgorithmResponse, summary="启用/禁用算法")
|
||||
async def toggle_algorithm_enabled(
|
||||
algorithm_id: int,
|
||||
enabled: bool = Query(..., description="是否启用"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""启用或禁用算法"""
|
||||
algorithm = db.query(Algorithm).filter(Algorithm.id == algorithm_id).first()
|
||||
if not algorithm:
|
||||
raise HTTPException(status_code=404, detail="算法不存在")
|
||||
|
||||
algorithm.is_enabled = enabled
|
||||
db.commit()
|
||||
db.refresh(algorithm)
|
||||
return algorithm
|
165
server/routers/devices.py
Normal file
165
server/routers/devices.py
Normal file
@ -0,0 +1,165 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
from core.database import get_db
|
||||
from models.device import Device
|
||||
from schemas.device import (
|
||||
DeviceCreate,
|
||||
DeviceUpdate,
|
||||
DeviceResponse,
|
||||
DeviceListResponse
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/", response_model=DeviceResponse, summary="创建设备")
|
||||
async def create_device(
|
||||
device: DeviceCreate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""创建新的设备"""
|
||||
db_device = Device(**device.dict())
|
||||
db.add(db_device)
|
||||
db.commit()
|
||||
db.refresh(db_device)
|
||||
return db_device
|
||||
|
||||
@router.get("/", response_model=DeviceListResponse, summary="获取设备列表")
|
||||
async def get_devices(
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(10, ge=1, le=100, description="返回记录数"),
|
||||
name: Optional[str] = Query(None, description="设备名称"),
|
||||
device_type: Optional[str] = Query(None, description="设备类型"),
|
||||
status: Optional[str] = Query(None, description="设备状态"),
|
||||
location: Optional[str] = Query(None, description="设备位置"),
|
||||
is_enabled: Optional[bool] = Query(None, description="是否启用"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取设备列表,支持分页和筛选"""
|
||||
query = db.query(Device)
|
||||
|
||||
if name:
|
||||
query = query.filter(Device.name.contains(name))
|
||||
if device_type:
|
||||
query = query.filter(Device.device_type == device_type)
|
||||
if status:
|
||||
query = query.filter(Device.status == status)
|
||||
if location:
|
||||
query = query.filter(Device.location.contains(location))
|
||||
if is_enabled is not None:
|
||||
query = query.filter(Device.is_enabled == is_enabled)
|
||||
|
||||
total = query.count()
|
||||
devices = query.offset(skip).limit(limit).all()
|
||||
|
||||
return DeviceListResponse(
|
||||
devices=devices,
|
||||
total=total,
|
||||
page=skip // limit + 1,
|
||||
size=limit
|
||||
)
|
||||
|
||||
@router.get("/{device_id}", response_model=DeviceResponse, summary="获取设备详情")
|
||||
async def get_device(
|
||||
device_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""根据ID获取设备详情"""
|
||||
device = db.query(Device).filter(Device.id == device_id).first()
|
||||
if not device:
|
||||
raise HTTPException(status_code=404, detail="设备不存在")
|
||||
return device
|
||||
|
||||
@router.put("/{device_id}", response_model=DeviceResponse, summary="更新设备")
|
||||
async def update_device(
|
||||
device_id: int,
|
||||
device: DeviceUpdate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""更新设备信息"""
|
||||
db_device = db.query(Device).filter(Device.id == device_id).first()
|
||||
if not db_device:
|
||||
raise HTTPException(status_code=404, detail="设备不存在")
|
||||
|
||||
update_data = device.dict(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(db_device, field, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_device)
|
||||
return db_device
|
||||
|
||||
@router.delete("/{device_id}", summary="删除设备")
|
||||
async def delete_device(
|
||||
device_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""删除设备"""
|
||||
device = db.query(Device).filter(Device.id == device_id).first()
|
||||
if not device:
|
||||
raise HTTPException(status_code=404, detail="设备不存在")
|
||||
|
||||
db.delete(device)
|
||||
db.commit()
|
||||
return {"message": "设备删除成功"}
|
||||
|
||||
@router.patch("/{device_id}/status", response_model=DeviceResponse, summary="更新设备状态")
|
||||
async def update_device_status(
|
||||
device_id: int,
|
||||
status: str = Query(..., description="新状态"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""更新设备状态"""
|
||||
device = db.query(Device).filter(Device.id == device_id).first()
|
||||
if not device:
|
||||
raise HTTPException(status_code=404, detail="设备不存在")
|
||||
|
||||
device.status = status
|
||||
db.commit()
|
||||
db.refresh(device)
|
||||
return device
|
||||
|
||||
@router.patch("/{device_id}/enable", response_model=DeviceResponse, summary="启用/禁用设备")
|
||||
async def toggle_device_enabled(
|
||||
device_id: int,
|
||||
enabled: bool = Query(..., description="是否启用"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""启用或禁用设备"""
|
||||
device = db.query(Device).filter(Device.id == device_id).first()
|
||||
if not device:
|
||||
raise HTTPException(status_code=404, detail="设备不存在")
|
||||
|
||||
device.is_enabled = enabled
|
||||
db.commit()
|
||||
db.refresh(device)
|
||||
return device
|
||||
|
||||
@router.get("/types/list", summary="获取设备类型列表")
|
||||
async def get_device_types():
|
||||
"""获取所有设备类型"""
|
||||
return {
|
||||
"types": [
|
||||
{"value": "camera", "label": "摄像头"},
|
||||
{"value": "sensor", "label": "传感器"},
|
||||
{"value": "gate", "label": "门禁"},
|
||||
{"value": "alarm", "label": "报警器"},
|
||||
{"value": "other", "label": "其他"}
|
||||
]
|
||||
}
|
||||
|
||||
@router.get("/status/stats", summary="获取设备状态统计")
|
||||
async def get_device_status_stats(db: Session = Depends(get_db)):
|
||||
"""获取设备状态统计信息"""
|
||||
total = db.query(Device).count()
|
||||
online = db.query(Device).filter(Device.status == "online").count()
|
||||
offline = db.query(Device).filter(Device.status == "offline").count()
|
||||
error = db.query(Device).filter(Device.status == "error").count()
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"online": online,
|
||||
"offline": offline,
|
||||
"error": error,
|
||||
"online_rate": round(online / total * 100, 2) if total > 0 else 0
|
||||
}
|
213
server/routers/events.py
Normal file
213
server/routers/events.py
Normal file
@ -0,0 +1,213 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
from core.database import get_db
|
||||
from models.event import Event
|
||||
from schemas.event import (
|
||||
EventCreate,
|
||||
EventUpdate,
|
||||
EventResponse,
|
||||
EventListResponse
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/", response_model=EventResponse, summary="创建事件")
|
||||
async def create_event(
|
||||
event: EventCreate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""创建新的事件"""
|
||||
db_event = Event(**event.dict())
|
||||
db.add(db_event)
|
||||
db.commit()
|
||||
db.refresh(db_event)
|
||||
return db_event
|
||||
|
||||
@router.get("/", response_model=EventListResponse, summary="获取事件列表")
|
||||
async def get_events(
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(10, ge=1, le=100, description="返回记录数"),
|
||||
event_type: Optional[str] = Query(None, description="事件类型"),
|
||||
device_id: Optional[int] = Query(None, description="设备ID"),
|
||||
algorithm_id: Optional[int] = Query(None, description="算法ID"),
|
||||
severity: Optional[str] = Query(None, description="严重程度"),
|
||||
status: Optional[str] = Query(None, description="事件状态"),
|
||||
is_alert: Optional[bool] = Query(None, description="是否告警"),
|
||||
start_time: Optional[str] = Query(None, description="开始时间"),
|
||||
end_time: Optional[str] = Query(None, description="结束时间"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取事件列表,支持分页和筛选"""
|
||||
query = db.query(Event)
|
||||
|
||||
if event_type:
|
||||
query = query.filter(Event.event_type == event_type)
|
||||
if device_id:
|
||||
query = query.filter(Event.device_id == device_id)
|
||||
if algorithm_id:
|
||||
query = query.filter(Event.algorithm_id == algorithm_id)
|
||||
if severity:
|
||||
query = query.filter(Event.severity == severity)
|
||||
if status:
|
||||
query = query.filter(Event.status == status)
|
||||
if is_alert is not None:
|
||||
query = query.filter(Event.is_alert == is_alert)
|
||||
if start_time:
|
||||
try:
|
||||
start_dt = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
|
||||
query = query.filter(Event.created_at >= start_dt)
|
||||
except ValueError:
|
||||
pass
|
||||
if end_time:
|
||||
try:
|
||||
end_dt = datetime.fromisoformat(end_time.replace('Z', '+00:00'))
|
||||
query = query.filter(Event.created_at <= end_dt)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 按创建时间倒序排列
|
||||
query = query.order_by(Event.created_at.desc())
|
||||
|
||||
total = query.count()
|
||||
events = query.offset(skip).limit(limit).all()
|
||||
|
||||
return EventListResponse(
|
||||
events=events,
|
||||
total=total,
|
||||
page=skip // limit + 1,
|
||||
size=limit
|
||||
)
|
||||
|
||||
@router.get("/{event_id}", response_model=EventResponse, summary="获取事件详情")
|
||||
async def get_event(
|
||||
event_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""根据ID获取事件详情"""
|
||||
event = db.query(Event).filter(Event.id == event_id).first()
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="事件不存在")
|
||||
return event
|
||||
|
||||
@router.put("/{event_id}", response_model=EventResponse, summary="更新事件")
|
||||
async def update_event(
|
||||
event_id: int,
|
||||
event: EventUpdate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""更新事件信息"""
|
||||
db_event = db.query(Event).filter(Event.id == event_id).first()
|
||||
if not db_event:
|
||||
raise HTTPException(status_code=404, detail="事件不存在")
|
||||
|
||||
update_data = event.dict(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(db_event, field, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_event)
|
||||
return db_event
|
||||
|
||||
@router.delete("/{event_id}", summary="删除事件")
|
||||
async def delete_event(
|
||||
event_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""删除事件"""
|
||||
event = db.query(Event).filter(Event.id == event_id).first()
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="事件不存在")
|
||||
|
||||
db.delete(event)
|
||||
db.commit()
|
||||
return {"message": "事件删除成功"}
|
||||
|
||||
@router.patch("/{event_id}/status", response_model=EventResponse, summary="更新事件状态")
|
||||
async def update_event_status(
|
||||
event_id: int,
|
||||
status: str = Query(..., description="新状态"),
|
||||
resolution_notes: Optional[str] = Query(None, description="处理备注"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""更新事件状态"""
|
||||
event = db.query(Event).filter(Event.id == event_id).first()
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="事件不存在")
|
||||
|
||||
event.status = status
|
||||
if resolution_notes:
|
||||
event.resolution_notes = resolution_notes
|
||||
|
||||
# 如果状态为resolved,设置解决时间
|
||||
if status == "resolved":
|
||||
event.resolved_at = datetime.utcnow()
|
||||
|
||||
db.commit()
|
||||
db.refresh(event)
|
||||
return event
|
||||
|
||||
@router.get("/types/list", summary="获取事件类型列表")
|
||||
async def get_event_types():
|
||||
"""获取所有事件类型"""
|
||||
return {
|
||||
"types": [
|
||||
{"value": "person_detection", "label": "人员检测"},
|
||||
{"value": "vehicle_detection", "label": "车辆检测"},
|
||||
{"value": "intrusion", "label": "入侵检测"},
|
||||
{"value": "face_recognition", "label": "人脸识别"},
|
||||
{"value": "license_plate", "label": "车牌识别"},
|
||||
{"value": "object_detection", "label": "物体检测"},
|
||||
{"value": "behavior_analysis", "label": "行为分析"},
|
||||
{"value": "other", "label": "其他"}
|
||||
]
|
||||
}
|
||||
|
||||
@router.get("/stats/summary", summary="获取事件统计摘要")
|
||||
async def get_event_stats_summary(db: Session = Depends(get_db)):
|
||||
"""获取事件统计摘要"""
|
||||
total = db.query(Event).count()
|
||||
pending = db.query(Event).filter(Event.status == "pending").count()
|
||||
processing = db.query(Event).filter(Event.status == "processing").count()
|
||||
resolved = db.query(Event).filter(Event.status == "resolved").count()
|
||||
ignored = db.query(Event).filter(Event.status == "ignored").count()
|
||||
alerts = db.query(Event).filter(Event.is_alert == True).count()
|
||||
|
||||
# 按严重程度统计
|
||||
critical = db.query(Event).filter(Event.severity == "critical").count()
|
||||
high = db.query(Event).filter(Event.severity == "high").count()
|
||||
medium = db.query(Event).filter(Event.severity == "medium").count()
|
||||
low = db.query(Event).filter(Event.severity == "low").count()
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"pending": pending,
|
||||
"processing": processing,
|
||||
"resolved": resolved,
|
||||
"ignored": ignored,
|
||||
"alerts": alerts,
|
||||
"severity": {
|
||||
"critical": critical,
|
||||
"high": high,
|
||||
"medium": medium,
|
||||
"low": low
|
||||
}
|
||||
}
|
||||
|
||||
@router.get("/stats/by-type", summary="按类型统计事件")
|
||||
async def get_event_stats_by_type(db: Session = Depends(get_db)):
|
||||
"""按事件类型统计"""
|
||||
from sqlalchemy import func
|
||||
|
||||
stats = db.query(
|
||||
Event.event_type,
|
||||
func.count(Event.id).label('count')
|
||||
).group_by(Event.event_type).all()
|
||||
|
||||
return {
|
||||
"stats": [
|
||||
{"type": stat.event_type, "count": stat.count}
|
||||
for stat in stats
|
||||
]
|
||||
}
|
5
server/schemas/__init__.py
Normal file
5
server/schemas/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .algorithm import *
|
||||
from .device import *
|
||||
from .event import *
|
||||
|
||||
__all__ = ["algorithm", "device", "event"]
|
50
server/schemas/algorithm.py
Normal file
50
server/schemas/algorithm.py
Normal file
@ -0,0 +1,50 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
class AlgorithmBase(BaseModel):
|
||||
name: str = Field(..., description="算法名称")
|
||||
description: Optional[str] = Field(None, description="算法描述")
|
||||
version: str = Field(..., description="算法版本")
|
||||
model_path: Optional[str] = Field(None, description="模型文件路径")
|
||||
config_path: Optional[str] = Field(None, description="配置文件路径")
|
||||
status: str = Field("inactive", description="算法状态")
|
||||
accuracy: Optional[float] = Field(None, description="算法准确率")
|
||||
detection_classes: Optional[str] = Field(None, description="检测类别")
|
||||
input_size: Optional[str] = Field(None, description="输入尺寸")
|
||||
inference_time: Optional[float] = Field(None, description="推理时间")
|
||||
is_enabled: bool = Field(True, description="是否启用")
|
||||
creator: Optional[str] = Field(None, description="创建者")
|
||||
tags: Optional[str] = Field(None, description="标签")
|
||||
|
||||
class AlgorithmCreate(AlgorithmBase):
|
||||
pass
|
||||
|
||||
class AlgorithmUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
version: Optional[str] = None
|
||||
model_path: Optional[str] = None
|
||||
config_path: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
accuracy: Optional[float] = None
|
||||
detection_classes: Optional[str] = None
|
||||
input_size: Optional[str] = None
|
||||
inference_time: Optional[float] = None
|
||||
is_enabled: Optional[bool] = None
|
||||
creator: Optional[str] = None
|
||||
tags: Optional[str] = None
|
||||
|
||||
class AlgorithmResponse(AlgorithmBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class AlgorithmListResponse(BaseModel):
|
||||
algorithms: List[AlgorithmResponse]
|
||||
total: int
|
||||
page: int
|
||||
size: int
|
63
server/schemas/device.py
Normal file
63
server/schemas/device.py
Normal file
@ -0,0 +1,63 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
class DeviceBase(BaseModel):
|
||||
name: str = Field(..., description="设备名称")
|
||||
device_type: str = Field(..., description="设备类型")
|
||||
location: Optional[str] = Field(None, description="设备位置")
|
||||
ip_address: Optional[str] = Field(None, description="IP地址")
|
||||
port: Optional[int] = Field(None, description="端口号")
|
||||
username: Optional[str] = Field(None, description="用户名")
|
||||
password: Optional[str] = Field(None, description="密码")
|
||||
rtsp_url: Optional[str] = Field(None, description="RTSP流地址")
|
||||
status: str = Field("offline", description="设备状态")
|
||||
resolution: Optional[str] = Field(None, description="分辨率")
|
||||
fps: Optional[int] = Field(None, description="帧率")
|
||||
algorithm_id: Optional[int] = Field(None, description="关联的算法ID")
|
||||
is_enabled: bool = Field(True, description="是否启用")
|
||||
latitude: Optional[float] = Field(None, description="纬度")
|
||||
longitude: Optional[float] = Field(None, description="经度")
|
||||
description: Optional[str] = Field(None, description="设备描述")
|
||||
manufacturer: Optional[str] = Field(None, description="制造商")
|
||||
model: Optional[str] = Field(None, description="设备型号")
|
||||
serial_number: Optional[str] = Field(None, description="序列号")
|
||||
|
||||
class DeviceCreate(DeviceBase):
|
||||
pass
|
||||
|
||||
class DeviceUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
device_type: Optional[str] = None
|
||||
location: Optional[str] = None
|
||||
ip_address: Optional[str] = None
|
||||
port: Optional[int] = None
|
||||
username: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
rtsp_url: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
resolution: Optional[str] = None
|
||||
fps: Optional[int] = None
|
||||
algorithm_id: Optional[int] = None
|
||||
is_enabled: Optional[bool] = None
|
||||
latitude: Optional[float] = None
|
||||
longitude: Optional[float] = None
|
||||
description: Optional[str] = None
|
||||
manufacturer: Optional[str] = None
|
||||
model: Optional[str] = None
|
||||
serial_number: Optional[str] = None
|
||||
|
||||
class DeviceResponse(DeviceBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
last_heartbeat: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class DeviceListResponse(BaseModel):
|
||||
devices: List[DeviceResponse]
|
||||
total: int
|
||||
page: int
|
||||
size: int
|
59
server/schemas/event.py
Normal file
59
server/schemas/event.py
Normal file
@ -0,0 +1,59 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
class EventBase(BaseModel):
|
||||
event_type: str = Field(..., description="事件类型")
|
||||
device_id: int = Field(..., description="关联设备ID")
|
||||
algorithm_id: Optional[int] = Field(None, description="关联算法ID")
|
||||
severity: str = Field("medium", description="严重程度")
|
||||
status: str = Field("pending", description="事件状态")
|
||||
confidence: Optional[float] = Field(None, description="置信度")
|
||||
bbox: Optional[str] = Field(None, description="边界框坐标")
|
||||
image_path: Optional[str] = Field(None, description="事件图片路径")
|
||||
video_path: Optional[str] = Field(None, description="事件视频路径")
|
||||
description: Optional[str] = Field(None, description="事件描述")
|
||||
location: Optional[str] = Field(None, description="事件发生位置")
|
||||
detected_objects: Optional[str] = Field(None, description="检测到的对象")
|
||||
processing_time: Optional[float] = Field(None, description="处理时间")
|
||||
is_alert: bool = Field(False, description="是否触发告警")
|
||||
alert_sent: bool = Field(False, description="是否已发送告警")
|
||||
operator_id: Optional[int] = Field(None, description="处理人员ID")
|
||||
resolution_notes: Optional[str] = Field(None, description="处理备注")
|
||||
|
||||
class EventCreate(EventBase):
|
||||
pass
|
||||
|
||||
class EventUpdate(BaseModel):
|
||||
event_type: Optional[str] = None
|
||||
device_id: Optional[int] = None
|
||||
algorithm_id: Optional[int] = None
|
||||
severity: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
confidence: Optional[float] = None
|
||||
bbox: Optional[str] = None
|
||||
image_path: Optional[str] = None
|
||||
video_path: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
location: Optional[str] = None
|
||||
detected_objects: Optional[str] = None
|
||||
processing_time: Optional[float] = None
|
||||
is_alert: Optional[bool] = None
|
||||
alert_sent: Optional[bool] = None
|
||||
operator_id: Optional[int] = None
|
||||
resolution_notes: Optional[str] = None
|
||||
|
||||
class EventResponse(EventBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
resolved_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class EventListResponse(BaseModel):
|
||||
events: List[EventResponse]
|
||||
total: int
|
||||
page: int
|
||||
size: int
|
Loading…
x
Reference in New Issue
Block a user