- 实现REST API服务器,支持IoT数据、手动录入和批量导入接口 - 实现WebSocket服务器,支持实时数据推送和连接管理 - 实现批量导入模块,支持CSV、Excel、JSON多种格式 - 实现数据处理工具,包含字段映射和单位转换功能 - 实现数据模型定义和数据验证机制 - 创建主程序入口和配置文件 - 添加详细的使用文档和API说明
317 lines
10 KiB
Python
317 lines
10 KiB
Python
"""
|
|
数据模型定义
|
|
定义水务管理系统的各种数据结构
|
|
"""
|
|
from dataclasses import dataclass, field
|
|
from typing import Dict, List, Optional, Any
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
class DataType(Enum):
|
|
"""数据类型枚举"""
|
|
LL = "LL" # 流量
|
|
YL = "YL" # 压力
|
|
SW = "SW" # 水位
|
|
ZD = "ZD" # 浊度
|
|
PH = "PH" # pH值
|
|
WD = "WD" # 温度
|
|
DD = "DD" # 电导率
|
|
YD = "YD" # 硬度
|
|
|
|
class AlertLevel(Enum):
|
|
"""警报级别枚举"""
|
|
INFO = "info"
|
|
WARNING = "warning"
|
|
ERROR = "error"
|
|
CRITICAL = "critical"
|
|
|
|
@dataclass
|
|
class Device:
|
|
"""设备模型"""
|
|
id: str
|
|
name: str
|
|
device_type: str
|
|
location: str
|
|
description: Optional[str] = None
|
|
install_date: Optional[datetime] = None
|
|
status: str = "active" # active, inactive, maintenance
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
@dataclass
|
|
class SensorData:
|
|
"""传感器数据模型"""
|
|
id: str
|
|
device_id: str
|
|
data_type: DataType
|
|
value: float
|
|
unit: str
|
|
timestamp: datetime
|
|
location: str
|
|
quality_score: float = 1.0
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""转换为字典"""
|
|
return {
|
|
"id": self.id,
|
|
"device_id": self.device_id,
|
|
"data_type": self.data_type.value,
|
|
"value": self.value,
|
|
"unit": self.unit,
|
|
"timestamp": self.timestamp.isoformat(),
|
|
"location": self.location,
|
|
"quality_score": self.quality_score,
|
|
"metadata": self.metadata
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, Any]) -> 'SensorData':
|
|
"""从字典创建对象"""
|
|
return cls(
|
|
id=data["id"],
|
|
device_id=data["device_id"],
|
|
data_type=DataType(data["data_type"]),
|
|
value=float(data["value"]),
|
|
unit=data.get("unit", ""),
|
|
timestamp=datetime.fromisoformat(data["timestamp"]),
|
|
location=data.get("location", ""),
|
|
quality_score=float(data.get("quality_score", 1.0)),
|
|
metadata=data.get("metadata", {})
|
|
)
|
|
|
|
@dataclass
|
|
class Alert:
|
|
"""警报模型"""
|
|
id: str
|
|
device_id: str
|
|
alert_type: str
|
|
level: AlertLevel
|
|
message: str
|
|
timestamp: datetime
|
|
resolved: bool = False
|
|
resolved_by: Optional[str] = None
|
|
resolved_at: Optional[datetime] = None
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""转换为字典"""
|
|
return {
|
|
"id": self.id,
|
|
"device_id": self.device_id,
|
|
"alert_type": self.alert_type,
|
|
"level": self.level.value,
|
|
"message": self.message,
|
|
"timestamp": self.timestamp.isoformat(),
|
|
"resolved": self.resolved,
|
|
"resolved_by": self.resolved_by,
|
|
"resolved_at": self.resolved_at.isoformat() if self.resolved_at else None,
|
|
"metadata": self.metadata
|
|
}
|
|
|
|
@dataclass
|
|
class BatchImport:
|
|
"""批量导入记录模型"""
|
|
id: str
|
|
batch_id: str
|
|
data_source: str
|
|
total_records: int
|
|
successful_records: int
|
|
failed_records: int
|
|
status: str # pending, processing, completed, failed
|
|
file_name: Optional[str] = None
|
|
import_time: Optional[datetime] = None
|
|
completed_time: Optional[datetime] = None
|
|
error_messages: List[str] = field(default_factory=list)
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""转换为字典"""
|
|
return {
|
|
"id": self.id,
|
|
"batch_id": self.batch_id,
|
|
"data_source": self.data_source,
|
|
"total_records": self.total_records,
|
|
"successful_records": self.successful_records,
|
|
"failed_records": self.failed_records,
|
|
"status": self.status,
|
|
"file_name": self.file_name,
|
|
"import_time": self.import_time.isoformat() if self.import_time else None,
|
|
"completed_time": self.completed_time.isoformat() if self.completed_time else None,
|
|
"error_messages": self.error_messages,
|
|
"metadata": self.metadata
|
|
}
|
|
|
|
@dataclass
|
|
class APIRequest:
|
|
"""API请求模型"""
|
|
id: str
|
|
method: str
|
|
endpoint: str
|
|
params: Dict[str, Any]
|
|
headers: Dict[str, Any]
|
|
body: Optional[Any] = None
|
|
timestamp: datetime = field(default_factory=datetime.now)
|
|
response_code: Optional[int] = None
|
|
response_time_ms: Optional[float] = None
|
|
response_body: Optional[Any] = None
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""转换为字典"""
|
|
return {
|
|
"id": self.id,
|
|
"method": self.method,
|
|
"endpoint": self.endpoint,
|
|
"params": self.params,
|
|
"headers": self.headers,
|
|
"body": self.body,
|
|
"timestamp": self.timestamp.isoformat(),
|
|
"response_code": self.response_code,
|
|
"response_time_ms": self.response_time_ms,
|
|
"response_body": self.response_body
|
|
}
|
|
|
|
@dataclass
|
|
class WebSocketConnection:
|
|
"""WebSocket连接模型"""
|
|
id: str
|
|
client_ip: str
|
|
connected_at: datetime
|
|
disconnected_at: Optional[datetime] = None
|
|
subscriptions: List[str] = field(default_factory=list)
|
|
message_count: int = 0
|
|
last_message_at: Optional[datetime] = None
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""转换为字典"""
|
|
return {
|
|
"id": self.id,
|
|
"client_ip": self.client_ip,
|
|
"connected_at": self.connected_at.isoformat(),
|
|
"disconnected_at": self.disconnected_at.isoformat() if self.disconnected_at else None,
|
|
"subscriptions": self.subscriptions,
|
|
"message_count": self.message_count,
|
|
"last_message_at": self.last_message_at.isoformat() if self.last_message_at else None,
|
|
"metadata": self.metadata
|
|
}
|
|
|
|
@dataclass
|
|
class SystemStats:
|
|
"""系统统计模型"""
|
|
timestamp: datetime
|
|
total_records: int
|
|
total_devices: int
|
|
active_connections: int
|
|
api_requests_count: int
|
|
alerts_count: int
|
|
data_quality_score: float
|
|
memory_usage_mb: float
|
|
cpu_usage_percent: float
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""转换为字典"""
|
|
return {
|
|
"timestamp": self.timestamp.isoformat(),
|
|
"total_records": self.total_records,
|
|
"total_devices": self.total_devices,
|
|
"active_connections": self.active_connections,
|
|
"api_requests_count": self.api_requests_count,
|
|
"alerts_count": self.alerts_count,
|
|
"data_quality_score": self.data_quality_score,
|
|
"memory_usage_mb": self.memory_usage_mb,
|
|
"cpu_usage_percent": self.cpu_usage_percent
|
|
}
|
|
|
|
class DataValidator:
|
|
"""数据验证器"""
|
|
|
|
@staticmethod
|
|
def validate_sensor_data(data: Dict[str, Any]) -> List[str]:
|
|
"""验证传感器数据"""
|
|
errors = []
|
|
|
|
# 必需字段检查
|
|
required_fields = ["device_id", "data_type", "value", "location"]
|
|
for field in required_fields:
|
|
if field not in data:
|
|
errors.append(f"缺少必需字段: {field}")
|
|
|
|
# 数据类型验证
|
|
if "data_type" in data:
|
|
try:
|
|
DataType(data["data_type"])
|
|
except ValueError:
|
|
errors.append(f"无效的数据类型: {data['data_type']}")
|
|
|
|
# 数值验证
|
|
if "value" in data:
|
|
try:
|
|
value = float(data["value"])
|
|
# 根据数据类型进行数值范围检查
|
|
data_type = data.get("data_type")
|
|
if data_type == "LL" and value < 0:
|
|
errors.append("流量不能为负数")
|
|
elif data_type == "YL" and value < 0:
|
|
errors.append("压力不能为负数")
|
|
elif data_type == "SW" and value < 0:
|
|
errors.append("水位不能为负数")
|
|
except (ValueError, TypeError):
|
|
errors.append(f"无效的数值: {data['value']}")
|
|
|
|
# 时间戳验证
|
|
if "timestamp" in data:
|
|
try:
|
|
if isinstance(data["timestamp"], str):
|
|
datetime.fromisoformat(data["timestamp"])
|
|
except (ValueError, TypeError):
|
|
errors.append(f"无效的时间戳格式: {data['timestamp']}")
|
|
|
|
return errors
|
|
|
|
@staticmethod
|
|
def validate_device_data(data: Dict[str, Any]) -> List[str]:
|
|
"""验证设备数据"""
|
|
errors = []
|
|
|
|
# 必需字段检查
|
|
required_fields = ["id", "name", "device_type", "location"]
|
|
for field in required_fields:
|
|
if field not in data:
|
|
errors.append(f"缺少必需字段: {field}")
|
|
|
|
# 设备ID格式验证
|
|
if "id" in data:
|
|
device_id = data["id"]
|
|
if not isinstance(device_id, str) or not device_id.strip():
|
|
errors.append("设备ID不能为空")
|
|
elif len(device_id) > 50:
|
|
errors.append("设备ID长度不能超过50个字符")
|
|
|
|
# 状态验证
|
|
if "status" in data and data["status"] not in ["active", "inactive", "maintenance"]:
|
|
errors.append("设备状态必须是: active, inactive, maintenance")
|
|
|
|
return errors
|
|
|
|
@staticmethod
|
|
def validate_alert_data(data: Dict[str, Any]) -> List[str]:
|
|
"""验证警报数据"""
|
|
errors = []
|
|
|
|
# 必需字段检查
|
|
required_fields = ["device_id", "alert_type", "level", "message"]
|
|
for field in required_fields:
|
|
if field not in data:
|
|
errors.append(f"缺少必需字段: {field}")
|
|
|
|
# 警报级别验证
|
|
if "level" in data:
|
|
try:
|
|
AlertLevel(data["level"])
|
|
except ValueError:
|
|
errors.append(f"无效的警报级别: {data['level']}")
|
|
|
|
return errors
|
|
|
|
# 全局验证器实例
|
|
validator = DataValidator() |