Files
nonye/back/blueprints/device_warning.py

325 lines
10 KiB
Python
Raw Normal View History

2025-07-17 23:13:04 +08:00
from flask import Blueprint, jsonify, current_app, g
import sqlite3
import datetime
bp = Blueprint('device_warning', __name__, url_prefix='/api/warning')
def get_db():
"""获取数据库连接"""
if 'db' not in g:
g.db = sqlite3.connect(
current_app.config['DATABASE'],
detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
"""关闭数据库连接"""
db = g.pop('db', None)
if db is not None:
db.close()
def is_data_constant(data_points, threshold=0.5, time_threshold_hours=2):
"""
检测数据是否长时间保持不变
data_points: 数据点列表每个元素为(timestamp, value)
threshold: 数值变化阈值小于此值视为不变
time_threshold_hours: 时间阈值单位小时默认2小时
"""
if len(data_points) < 2:
return False, None
# 检查时间跨度是否达到阈值
first_time = datetime.datetime.strptime(data_points[0][0], '%Y-%m-%d %H:%M:%S')
last_time = datetime.datetime.strptime(data_points[-1][0], '%Y-%m-%d %H:%M:%S')
time_diff = last_time - first_time
if time_diff.total_seconds() < time_threshold_hours * 3600:
return False, None
# 检查数值变化是否小于阈值
first_value = data_points[0][1]
for time, value in data_points[1:]:
if abs(value - first_value) > threshold:
return False, None
return True, first_value
def check_device_warnings():
"""检查所有设备是否有数据长时间不变的情况"""
db = get_db()
cursor = db.cursor()
# 获取所有设备
cursor.execute("SELECT id, device_name, device_code FROM device")
devices = cursor.fetchall()
warning_updates = []
for device in devices:
device_id = device['id']
device_name = device['device_name']
device_code = device['device_code']
# 检查设备当前状态
cursor.execute("SELECT status FROM device WHERE id = ?", (device_id,))
device_status = cursor.fetchone()
# 如果设备已经是故障状态,则跳过检测
if device_status['status'] == 'Faulty':
continue
warning_type = None
warning_value = None
# 检查温度数据使用2小时阈值
cursor.execute(
"""SELECT timestamp, temperature FROM temperature_data
WHERE device_id = ?
ORDER BY timestamp DESC
LIMIT 24""", # 取最近24条数据
(device_id,)
)
temp_data = cursor.fetchall()
if temp_data:
is_constant, value = is_data_constant(
[(item['timestamp'], item['temperature']) for item in temp_data]
)
if is_constant:
warning_type = 'temperature_constant'
warning_value = value
# 如果温度没有问题检查湿度数据使用2小时阈值
if not warning_type:
cursor.execute(
"""SELECT timestamp, humidity FROM temperature_data
WHERE device_id = ?
ORDER BY timestamp DESC
LIMIT 24""",
(device_id,)
)
humidity_data = cursor.fetchall()
if humidity_data:
is_constant, value = is_data_constant(
[(item['timestamp'], item['humidity']) for item in humidity_data]
)
if is_constant:
warning_type = 'humidity_constant'
warning_value = value
# 如果温度和湿度都没有问题检查pH值数据使用1小时阈值
if not warning_type:
cursor.execute(
"""SELECT timestamp, ph FROM temperature_data
WHERE device_id = ?
ORDER BY timestamp DESC
LIMIT 24""",
(device_id,)
)
ph_data = cursor.fetchall()
if ph_data:
is_constant, value = is_data_constant(
[(item['timestamp'], item['ph']) for item in ph_data],
threshold=0.2, # pH变化阈值更小
time_threshold_hours=1 # pH检查时间阈值更短
)
if is_constant:
warning_type = 'ph_constant'
warning_value = value
# 更新设备状态
if warning_type:
# 如果设备之前不是警告状态,则更新
if device_status['status'] != 'Faulty':
warning_updates.append({
'device_id': device_id,
'status': 'warning',
'warning_type': warning_type,
'warning_value': warning_value,
'warning_time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
})
else:
# 如果设备之前是警告状态,但现在不是了,则恢复为正常状态
if device_status['status'] == 'warning':
warning_updates.append({
'device_id': device_id,
'status': 'normal',
'warning_type': None,
'warning_value': None,
'warning_time': None
})
# 批量更新设备状态
for update in warning_updates:
cursor.execute(
"""
UPDATE device
SET status = ?,
warning_type = ?,
warning_value = ?,
warning_time = ?
WHERE id = ?
""",
(
update['status'],
update['warning_type'],
update['warning_value'],
update['warning_time'],
update['device_id']
)
)
db.commit()
return warning_updates
@bp.route('/check', methods=['GET'])
def check_warnings():
"""检查并更新设备警告状态"""
try:
updates = check_device_warnings()
return jsonify({
'status': 'success',
'data': updates,
'message': f'更新了{len(updates)}个设备状态'
})
except Exception as e:
return jsonify({
'status': 'error',
'message': f'检查设备警告失败: {str(e)}'
}), 500
@bp.route('/list', methods=['GET'])
def get_warning_list():
"""获取警告设备列表(优先使用存储的故障描述)"""
try:
db = get_db()
cursor = db.cursor()
# 尝试获取完整警告信息包括fault_description字段
try:
cursor.execute(
"""
SELECT
id,
device_name,
device_code,
status,
warning_type,
warning_value,
warning_time,
fault_description # 显式查询存储的故障描述
FROM device
WHERE status = 'Faulty' OR status = 'warning'
ORDER BY warning_time DESC
"""
)
warnings = cursor.fetchall()
warning_list = []
for warning in warnings:
warning_dict = dict(warning)
# 优先使用数据库中存储的fault_description
stored_description = warning_dict.get('fault_description')
if stored_description:
warning_dict['fault_description'] = stored_description
else:
# 如果没有存储描述则根据warning_type生成默认描述
warning_type = warning_dict.get('warning_type')
if warning_type == 'temperature_constant':
warning_dict['fault_description'] = '温度持续异常'
elif warning_type == 'humidity_constant':
warning_dict['fault_description'] = '湿度持续异常'
elif warning_type == 'ph_constant':
warning_dict['fault_description'] = 'pH值持续异常'
else:
warning_dict['fault_description'] = '环境数据异常'
warning_list.append(warning_dict)
return jsonify({
'status': 'success',
'data': warning_list,
'message': f'获取到{len(warning_list)}个警告设备'
})
except sqlite3.OperationalError as e:
# 如果表结构不完整缺少fault_description等字段回退到简单查询
cursor.execute(
"""
SELECT
id,
device_name,
device_code,
status
FROM device
WHERE status = 'Faulty' OR status = 'warning'
ORDER BY created_at DESC
"""
)
warnings = cursor.fetchall()
# 简单查询结果只能提供默认描述
warning_list = [dict(warning, fault_description='环境数据异常')
for warning in warnings]
return jsonify({
'status': 'success',
'data': warning_list,
'message': f'获取到{len(warning_list)}个警告设备(简化模式)'
})
except Exception as e:
return jsonify({
'status': 'error',
'message': f'获取警告设备列表失败: {str(e)}'
}), 500
@bp.route('/resolve/<int:device_id>', methods=['POST'])
def resolve_warning(device_id):
"""解决设备警告"""
try:
db = get_db()
cursor = db.cursor()
cursor.execute(
"""
UPDATE device
SET status = 'Online',
warning_type = NULL,
warning_value = NULL,
warning_time = NULL
WHERE id = ?
""",
(device_id,)
)
db.commit()
return jsonify({
'status': 'success',
'message': '设备警告已解除'
})
except Exception as e:
return jsonify({
'status': 'error',
'message': f'解除设备警告失败: {str(e)}'
}), 500
# 初始化数据库表结构(如需在代码中初始化)
def init_db():
db = get_db()
with current_app.open_resource('schema.sql') as f:
db.executescript(f.read().decode('utf8'))