531 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			531 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								from flask import Blueprint, jsonify, request, current_app, g
							 | 
						|||
| 
								 | 
							
								import sqlite3
							 | 
						|||
| 
								 | 
							
								import datetime
							 | 
						|||
| 
								 | 
							
								from dateutil.relativedelta import relativedelta
							 | 
						|||
| 
								 | 
							
								import logging
							 | 
						|||
| 
								 | 
							
								import random
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# 配置日志
							 | 
						|||
| 
								 | 
							
								logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
							 | 
						|||
| 
								 | 
							
								logger = logging.getLogger(__name__)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								bp = Blueprint('ph_data', __name__, url_prefix='/ph_data')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# 数据缓存字典,格式: {time_range: (seed, cached_data)}
							 | 
						|||
| 
								 | 
							
								ph_data_cache = {}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								def get_db():
							 | 
						|||
| 
								 | 
							
								    """获取数据库连接"""
							 | 
						|||
| 
								 | 
							
								    if 'db' not in g:
							 | 
						|||
| 
								 | 
							
								        try:
							 | 
						|||
| 
								 | 
							
								            g.db = sqlite3.connect(
							 | 
						|||
| 
								 | 
							
								                current_app.config.get('DATABASE', 'agriculture.db'),
							 | 
						|||
| 
								 | 
							
								                check_same_thread=False,
							 | 
						|||
| 
								 | 
							
								                detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES
							 | 
						|||
| 
								 | 
							
								            )
							 | 
						|||
| 
								 | 
							
								            g.db.row_factory = sqlite3.Row
							 | 
						|||
| 
								 | 
							
								            logger.info("数据库连接成功")
							 | 
						|||
| 
								 | 
							
								        except Exception as e:
							 | 
						|||
| 
								 | 
							
								            logger.error(f"数据库连接失败: {str(e)}")
							 | 
						|||
| 
								 | 
							
								            raise
							 | 
						|||
| 
								 | 
							
								    return g.db
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								def close_db(e=None):
							 | 
						|||
| 
								 | 
							
								    """关闭数据库连接"""
							 | 
						|||
| 
								 | 
							
								    db = g.pop('db', None)
							 | 
						|||
| 
								 | 
							
								    if db is not None:
							 | 
						|||
| 
								 | 
							
								        db.close()
							 | 
						|||
| 
								 | 
							
								        logger.info("数据库连接已关闭")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								@bp.route('/get_time_ranges', methods=['GET'])
							 | 
						|||
| 
								 | 
							
								def get_time_ranges():
							 | 
						|||
| 
								 | 
							
								    """获取时间范围选项"""
							 | 
						|||
| 
								 | 
							
								    try:
							 | 
						|||
| 
								 | 
							
								        time_ranges = [
							 | 
						|||
| 
								 | 
							
								            {'value': 'today', 'label': '今日'},
							 | 
						|||
| 
								 | 
							
								            {'value': 'last_three_days', 'label': '前三天'},
							 | 
						|||
| 
								 | 
							
								            {'value': 'next_two_days', 'label': '后两天'},
							 | 
						|||
| 
								 | 
							
								        ]
							 | 
						|||
| 
								 | 
							
								        logger.info("成功返回时间范围选项")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'time_ranges': time_ranges})
							 | 
						|||
| 
								 | 
							
								    except Exception as e:
							 | 
						|||
| 
								 | 
							
								        logger.error(f"获取时间范围失败: {str(e)}")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'error': '获取时间范围失败'}), 500
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								def increase_ph_fluctuation(data_list, time_range, amplitude=1.5):
							 | 
						|||
| 
								 | 
							
								    """
							 | 
						|||
| 
								 | 
							
								    增加PH值的波动幅度,使用固定随机种子确保相同时间范围生成相同波动数据
							 | 
						|||
| 
								 | 
							
								    :param data_list: 包含PH值的数据列表
							 | 
						|||
| 
								 | 
							
								    :param time_range: 时间范围参数
							 | 
						|||
| 
								 | 
							
								    :param amplitude: 波动幅度(默认±1.5)
							 | 
						|||
| 
								 | 
							
								    :return: 修改后的列表
							 | 
						|||
| 
								 | 
							
								    """
							 | 
						|||
| 
								 | 
							
								    # 检查缓存
							 | 
						|||
| 
								 | 
							
								    if time_range in ph_data_cache:
							 | 
						|||
| 
								 | 
							
								        logger.info(f"使用缓存的波动数据 for {time_range}")
							 | 
						|||
| 
								 | 
							
								        return ph_data_cache[time_range]
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # 为特定时间范围设置固定随机种子
							 | 
						|||
| 
								 | 
							
								    seed = hash(time_range)
							 | 
						|||
| 
								 | 
							
								    random.seed(seed)
							 | 
						|||
| 
								 | 
							
								    logger.info(f"为时间范围 {time_range} 设置随机种子: {seed}")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # 应用波动
							 | 
						|||
| 
								 | 
							
								    modified_data = []
							 | 
						|||
| 
								 | 
							
								    for item in data_list:
							 | 
						|||
| 
								 | 
							
								        original_ph = item.get('ph') or item.get('avg_ph')
							 | 
						|||
| 
								 | 
							
								        if original_ph is not None:
							 | 
						|||
| 
								 | 
							
								            # 随机波动(可正可负)
							 | 
						|||
| 
								 | 
							
								            fluctuation = random.uniform(-amplitude, amplitude)
							 | 
						|||
| 
								 | 
							
								            new_ph = original_ph + fluctuation
							 | 
						|||
| 
								 | 
							
								            # 确保PH值在合理范围(0-14)
							 | 
						|||
| 
								 | 
							
								            new_ph = max(0, min(14, new_ph))
							 | 
						|||
| 
								 | 
							
								            # 更新数据
							 | 
						|||
| 
								 | 
							
								            modified_item = item.copy()
							 | 
						|||
| 
								 | 
							
								            if 'ph' in modified_item:
							 | 
						|||
| 
								 | 
							
								                modified_item['ph'] = round(new_ph, 2)
							 | 
						|||
| 
								 | 
							
								            else:
							 | 
						|||
| 
								 | 
							
								                modified_item['avg_ph'] = round(new_ph, 2)
							 | 
						|||
| 
								 | 
							
								            modified_data.append(modified_item)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # 缓存波动后的数据
							 | 
						|||
| 
								 | 
							
								    ph_data_cache[time_range] = modified_data
							 | 
						|||
| 
								 | 
							
								    return modified_data
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								@bp.route('/get_ph_data', methods=['GET'])
							 | 
						|||
| 
								 | 
							
								def get_ph_data():
							 | 
						|||
| 
								 | 
							
								    """获取指定时间范围的PH值数据(合并所有设备)"""
							 | 
						|||
| 
								 | 
							
								    time_range = request.args.get('time_range', 'today')
							 | 
						|||
| 
								 | 
							
								    sample_method = request.args.get('sample_method', 'fixed')  # 采样方式:fixed(固定点)或hourly(每小时)
							 | 
						|||
| 
								 | 
							
								    logger.info(f"请求PH数据,时间范围: {time_range},采样方式: {sample_method}")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    if time_range not in ['today', 'last_three_days', 'next_two_days', 'all']:
							 | 
						|||
| 
								 | 
							
								        logger.warning(f"无效的时间范围: {time_range}")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'error': '无效的时间范围'}), 400
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    try:
							 | 
						|||
| 
								 | 
							
								        db = get_db()
							 | 
						|||
| 
								 | 
							
								        # 硬编码今日为2025-05-27
							 | 
						|||
| 
								 | 
							
								        today = datetime.datetime(2025, 5, 27)
							 | 
						|||
| 
								 | 
							
								        today_str = today.strftime('%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        result = []
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        if time_range == 'today':
							 | 
						|||
| 
								 | 
							
								            # 今日数据查询逻辑
							 | 
						|||
| 
								 | 
							
								            start_date = today_str + ' 00:00:00'
							 | 
						|||
| 
								 | 
							
								            end_date = today_str + ' 23:59:59'
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if sample_method == 'hourly':
							 | 
						|||
| 
								 | 
							
								                # 每小时采样
							 | 
						|||
| 
								 | 
							
								                for hour in range(0, 24):
							 | 
						|||
| 
								 | 
							
								                    sample_time = f"{today_str} {hour:02d}:00:00"
							 | 
						|||
| 
								 | 
							
								                    start_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                  - relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                    end_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                + relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                        '''SELECT AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                           FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                           WHERE (timestamp BETWEEN ? AND ?) 
							 | 
						|||
| 
								 | 
							
								                              OR (DATE(timestamp) = DATE(?))''',
							 | 
						|||
| 
								 | 
							
								                        (start_time, end_time, sample_time)
							 | 
						|||
| 
								 | 
							
								                    )
							 | 
						|||
| 
								 | 
							
								                    row = cursor.fetchone()
							 | 
						|||
| 
								 | 
							
								                    avg_ph = float(row['avg_ph']) if row and row['avg_ph'] is not None else 0
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    result.append({
							 | 
						|||
| 
								 | 
							
								                        'timestamp': sample_time,
							 | 
						|||
| 
								 | 
							
								                        'ph': round(avg_ph, 2)
							 | 
						|||
| 
								 | 
							
								                    })
							 | 
						|||
| 
								 | 
							
								            else:
							 | 
						|||
| 
								 | 
							
								                # 固定点采样
							 | 
						|||
| 
								 | 
							
								                cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                    '''SELECT timestamp, AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                       FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                       WHERE (timestamp BETWEEN ? AND ?) 
							 | 
						|||
| 
								 | 
							
								                          OR (DATE(timestamp) = DATE(?))
							 | 
						|||
| 
								 | 
							
								                       GROUP BY timestamp
							 | 
						|||
| 
								 | 
							
								                       ORDER BY timestamp''',
							 | 
						|||
| 
								 | 
							
								                    (start_date, end_date, today_str)
							 | 
						|||
| 
								 | 
							
								                )
							 | 
						|||
| 
								 | 
							
								                data = cursor.fetchall()
							 | 
						|||
| 
								 | 
							
								                result = [{'timestamp': row['timestamp'], 'ph': float(row['avg_ph'])} for row in data]
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            logger.info(f"查询到今日PH数据记录: {len(result)} 条")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        elif time_range == 'last_three_days':
							 | 
						|||
| 
								 | 
							
								            # 前三天数据查询
							 | 
						|||
| 
								 | 
							
								            if sample_method == 'hourly':
							 | 
						|||
| 
								 | 
							
								                # 每小时采样
							 | 
						|||
| 
								 | 
							
								                for i in range(3, 0, -1):
							 | 
						|||
| 
								 | 
							
								                    date = today - relativedelta(days=i)
							 | 
						|||
| 
								 | 
							
								                    date_str = date.strftime('%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								                    for hour in range(0, 24):
							 | 
						|||
| 
								 | 
							
								                        sample_time = f"{date_str} {hour:02d}:00:00"
							 | 
						|||
| 
								 | 
							
								                        start_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                      - relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                        end_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                    + relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                            '''SELECT AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                               FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                               WHERE (timestamp BETWEEN ? AND ?) 
							 | 
						|||
| 
								 | 
							
								                                  OR (DATE(timestamp) = DATE(?))''',
							 | 
						|||
| 
								 | 
							
								                            (start_time, end_time, sample_time)
							 | 
						|||
| 
								 | 
							
								                        )
							 | 
						|||
| 
								 | 
							
								                        row = cursor.fetchone()
							 | 
						|||
| 
								 | 
							
								                        avg_ph = float(row['avg_ph']) if row and row['avg_ph'] is not None else 0
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        result.append({
							 | 
						|||
| 
								 | 
							
								                            'timestamp': sample_time,
							 | 
						|||
| 
								 | 
							
								                            'ph': round(avg_ph, 2)
							 | 
						|||
| 
								 | 
							
								                        })
							 | 
						|||
| 
								 | 
							
								            else:
							 | 
						|||
| 
								 | 
							
								                # 固定点采样(每天6个点)
							 | 
						|||
| 
								 | 
							
								                sample_hours = [4, 8, 12, 16, 20, 23]
							 | 
						|||
| 
								 | 
							
								                for i in range(3, 0, -1):
							 | 
						|||
| 
								 | 
							
								                    date = today - relativedelta(days=i)
							 | 
						|||
| 
								 | 
							
								                    date_str = date.strftime('%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								                    for hour in sample_hours:
							 | 
						|||
| 
								 | 
							
								                        if hour == 23:
							 | 
						|||
| 
								 | 
							
								                            sample_time = f"{date_str} {hour:02d}:00:00"
							 | 
						|||
| 
								 | 
							
								                        else:
							 | 
						|||
| 
								 | 
							
								                            sample_time = f"{date_str} {hour:02d}:00:00"
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        start_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                      - relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                        end_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                    + relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                            '''SELECT AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                               FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                               WHERE (timestamp BETWEEN ? AND ?) 
							 | 
						|||
| 
								 | 
							
								                                  OR (DATE(timestamp) = DATE(?))''',
							 | 
						|||
| 
								 | 
							
								                            (start_time, end_time, sample_time)
							 | 
						|||
| 
								 | 
							
								                        )
							 | 
						|||
| 
								 | 
							
								                        row = cursor.fetchone()
							 | 
						|||
| 
								 | 
							
								                        avg_ph = float(row['avg_ph']) if row and row['avg_ph'] is not None else 0
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        result.append({
							 | 
						|||
| 
								 | 
							
								                            'timestamp': sample_time,
							 | 
						|||
| 
								 | 
							
								                            'ph': round(avg_ph, 2)
							 | 
						|||
| 
								 | 
							
								                        })
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            logger.info(f"查询到前三天PH数据记录: {len(result)} 条")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        elif time_range == 'next_two_days':
							 | 
						|||
| 
								 | 
							
								            # 后两天数据查询
							 | 
						|||
| 
								 | 
							
								            if sample_method == 'hourly':
							 | 
						|||
| 
								 | 
							
								                for i in range(1, 3):
							 | 
						|||
| 
								 | 
							
								                    date = today + relativedelta(days=i)
							 | 
						|||
| 
								 | 
							
								                    date_str = date.strftime('%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								                    for hour in range(0, 24):
							 | 
						|||
| 
								 | 
							
								                        sample_time = f"{date_str} {hour:02d}:00:00"
							 | 
						|||
| 
								 | 
							
								                        start_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                      - relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                        end_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                    + relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                            '''SELECT AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                               FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                               WHERE (timestamp BETWEEN ? AND ?) 
							 | 
						|||
| 
								 | 
							
								                                  OR (DATE(timestamp) = DATE(?))''',
							 | 
						|||
| 
								 | 
							
								                            (start_time, end_time, sample_time)
							 | 
						|||
| 
								 | 
							
								                        )
							 | 
						|||
| 
								 | 
							
								                        row = cursor.fetchone()
							 | 
						|||
| 
								 | 
							
								                        avg_ph = float(row['avg_ph']) if row and row['avg_ph'] is not None else 0
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        result.append({
							 | 
						|||
| 
								 | 
							
								                            'timestamp': sample_time,
							 | 
						|||
| 
								 | 
							
								                            'ph': round(avg_ph, 2)
							 | 
						|||
| 
								 | 
							
								                        })
							 | 
						|||
| 
								 | 
							
								            else:
							 | 
						|||
| 
								 | 
							
								                sample_hours = [8, 12, 16, 20]
							 | 
						|||
| 
								 | 
							
								                for i in range(1, 3):
							 | 
						|||
| 
								 | 
							
								                    date = today + relativedelta(days=i)
							 | 
						|||
| 
								 | 
							
								                    date_str = date.strftime('%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								                    for hour in sample_hours:
							 | 
						|||
| 
								 | 
							
								                        sample_time = f"{date_str} {hour:02d}:00:00"
							 | 
						|||
| 
								 | 
							
								                        start_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                      - relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                        end_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                    + relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                            '''SELECT AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                               FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                               WHERE (timestamp BETWEEN ? AND ?) 
							 | 
						|||
| 
								 | 
							
								                                  OR (DATE(timestamp) = DATE(?))''',
							 | 
						|||
| 
								 | 
							
								                            (start_time, end_time, sample_time)
							 | 
						|||
| 
								 | 
							
								                        )
							 | 
						|||
| 
								 | 
							
								                        row = cursor.fetchone()
							 | 
						|||
| 
								 | 
							
								                        avg_ph = float(row['avg_ph']) if row and row['avg_ph'] is not None else 0
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        result.append({
							 | 
						|||
| 
								 | 
							
								                            'timestamp': sample_time,
							 | 
						|||
| 
								 | 
							
								                            'ph': round(avg_ph, 2)
							 | 
						|||
| 
								 | 
							
								                        })
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            logger.info(f"查询到后两天PH数据记录: {len(result)} 条")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        elif time_range == 'all':
							 | 
						|||
| 
								 | 
							
								            # 查询所有PH数据
							 | 
						|||
| 
								 | 
							
								            cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                '''SELECT timestamp, AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                   FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                   WHERE ph IS NOT NULL
							 | 
						|||
| 
								 | 
							
								                   GROUP BY timestamp
							 | 
						|||
| 
								 | 
							
								                   ORDER BY timestamp'''
							 | 
						|||
| 
								 | 
							
								            )
							 | 
						|||
| 
								 | 
							
								            data = cursor.fetchall()
							 | 
						|||
| 
								 | 
							
								            result = [{'timestamp': row['timestamp'], 'ph': float(row['avg_ph'])} for row in data]
							 | 
						|||
| 
								 | 
							
								            logger.info(f"查询到所有PH数据记录: {len(result)} 条")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        # 关键修改:使用带随机种子的波动函数,并缓存结果
							 | 
						|||
| 
								 | 
							
								        result = increase_ph_fluctuation(result, time_range, amplitude=2.0)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        return jsonify({
							 | 
						|||
| 
								 | 
							
								            'time_range': time_range,
							 | 
						|||
| 
								 | 
							
								            'sample_method': sample_method,
							 | 
						|||
| 
								 | 
							
								            'data': result
							 | 
						|||
| 
								 | 
							
								        })
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    except sqlite3.Error as e:
							 | 
						|||
| 
								 | 
							
								        logger.error(f"数据库查询错误: {str(e)}")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'error': '数据库查询错误'}), 500
							 | 
						|||
| 
								 | 
							
								    except Exception as e:
							 | 
						|||
| 
								 | 
							
								        logger.error(f"获取PH数据异常: {str(e)}")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'error': '服务器内部错误'}), 500
							 | 
						|||
| 
								 | 
							
								    finally:
							 | 
						|||
| 
								 | 
							
								        close_db()
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								@bp.route('/get_ph_data_by_date_range', methods=['GET'])
							 | 
						|||
| 
								 | 
							
								def get_ph_data_by_date_range():
							 | 
						|||
| 
								 | 
							
								    """获取指定日期范围内的PH值数据(合并所有设备)"""
							 | 
						|||
| 
								 | 
							
								    start_date_str = request.args.get('start_date')
							 | 
						|||
| 
								 | 
							
								    end_date_str = request.args.get('end_date')
							 | 
						|||
| 
								 | 
							
								    sample_method = request.args.get('sample_method', 'daily')  # 采样方式:daily(每日)或hourly(每小时)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # 验证日期格式
							 | 
						|||
| 
								 | 
							
								    try:
							 | 
						|||
| 
								 | 
							
								        start_date = datetime.datetime.strptime(start_date_str, '%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								        end_date = datetime.datetime.strptime(end_date_str, '%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								    except ValueError:
							 | 
						|||
| 
								 | 
							
								        logger.warning(f"无效的日期格式,需要YYYY-MM-DD格式,实际传入: {start_date_str}, {end_date_str}")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'error': '无效的日期格式,需要YYYY-MM-DD格式'}), 400
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    # 验证日期范围
							 | 
						|||
| 
								 | 
							
								    if start_date > end_date:
							 | 
						|||
| 
								 | 
							
								        logger.warning(f"开始日期不能大于结束日期: {start_date_str} > {end_date_str}")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'error': '开始日期不能大于结束日期'}), 400
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    logger.info(f"请求PH数据,日期范围: {start_date_str} 至 {end_date_str},采样方式: {sample_method}")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    try:
							 | 
						|||
| 
								 | 
							
								        db = get_db()
							 | 
						|||
| 
								 | 
							
								        result = []
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        if sample_method == 'hourly':
							 | 
						|||
| 
								 | 
							
								            # 每小时采样
							 | 
						|||
| 
								 | 
							
								            current_date = start_date
							 | 
						|||
| 
								 | 
							
								            while current_date <= end_date:
							 | 
						|||
| 
								 | 
							
								                date_str = current_date.strftime('%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								                for hour in range(0, 24):
							 | 
						|||
| 
								 | 
							
								                    sample_time = f"{date_str} {hour:02d}:00:00"
							 | 
						|||
| 
								 | 
							
								                    start_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                  - relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                    end_time = (datetime.datetime.strptime(sample_time, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								                                + relativedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                        '''SELECT AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                           FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                           WHERE (timestamp BETWEEN ? AND ?) 
							 | 
						|||
| 
								 | 
							
								                              OR (DATE(timestamp) = DATE(?))''',
							 | 
						|||
| 
								 | 
							
								                        (start_time, end_time, sample_time)
							 | 
						|||
| 
								 | 
							
								                    )
							 | 
						|||
| 
								 | 
							
								                    row = cursor.fetchone()
							 | 
						|||
| 
								 | 
							
								                    avg_ph = float(row['avg_ph']) if row and row['avg_ph'] is not None else 0
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    result.append({
							 | 
						|||
| 
								 | 
							
								                        'timestamp': sample_time,
							 | 
						|||
| 
								 | 
							
								                        'ph': round(avg_ph, 2)
							 | 
						|||
| 
								 | 
							
								                    })
							 | 
						|||
| 
								 | 
							
								                current_date += relativedelta(days=1)
							 | 
						|||
| 
								 | 
							
								        else:
							 | 
						|||
| 
								 | 
							
								            # 每日采样
							 | 
						|||
| 
								 | 
							
								            current_date = start_date
							 | 
						|||
| 
								 | 
							
								            while current_date <= end_date:
							 | 
						|||
| 
								 | 
							
								                date_str = current_date.strftime('%Y-%m-%d')
							 | 
						|||
| 
								 | 
							
								                cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								                    '''SELECT AVG(ph) as avg_ph 
							 | 
						|||
| 
								 | 
							
								                       FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                       WHERE DATE(timestamp) = ?''',
							 | 
						|||
| 
								 | 
							
								                    (date_str,)
							 | 
						|||
| 
								 | 
							
								                )
							 | 
						|||
| 
								 | 
							
								                row = cursor.fetchone()
							 | 
						|||
| 
								 | 
							
								                avg_ph = float(row['avg_ph']) if row and row['avg_ph'] is not None else 0
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                result.append({
							 | 
						|||
| 
								 | 
							
								                    'date': date_str,
							 | 
						|||
| 
								 | 
							
								                    'avg_ph': round(avg_ph, 2)
							 | 
						|||
| 
								 | 
							
								                })
							 | 
						|||
| 
								 | 
							
								                current_date += relativedelta(days=1)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        logger.info(f"查询到{start_date_str}至{end_date_str}的PH数据记录: {len(result)} 条")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        # 增加PH值的波动幅度,使用日期范围字符串作为种子
							 | 
						|||
| 
								 | 
							
								        range_key = f"{start_date_str}_{end_date_str}"
							 | 
						|||
| 
								 | 
							
								        result = increase_ph_fluctuation(result, range_key, amplitude=2.0)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        return jsonify({
							 | 
						|||
| 
								 | 
							
								            'start_date': start_date_str,
							 | 
						|||
| 
								 | 
							
								            'end_date': end_date_str,
							 | 
						|||
| 
								 | 
							
								            'sample_method': sample_method,
							 | 
						|||
| 
								 | 
							
								            'data': result
							 | 
						|||
| 
								 | 
							
								        })
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    except sqlite3.Error as e:
							 | 
						|||
| 
								 | 
							
								        logger.error(f"数据库查询错误: {str(e)}")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'error': '数据库查询错误'}), 500
							 | 
						|||
| 
								 | 
							
								    except Exception as e:
							 | 
						|||
| 
								 | 
							
								        logger.error(f"获取PH数据异常: {str(e)}")
							 | 
						|||
| 
								 | 
							
								        return jsonify({'error': '服务器内部错误'}), 500
							 | 
						|||
| 
								 | 
							
								    finally:
							 | 
						|||
| 
								 | 
							
								        close_db()
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								@bp.route('/get_ph_today', methods=['GET'])
							 | 
						|||
| 
								 | 
							
								def get_ph_today():
							 | 
						|||
| 
								 | 
							
								    """获取2025年5月27日设备1的所有PH值数据"""
							 | 
						|||
| 
								 | 
							
								    logger.info("请求获取2025年5月27日设备1的PH数据")
							 | 
						|||
| 
								 | 
							
								    try:
							 | 
						|||
| 
								 | 
							
								        db = get_db()
							 | 
						|||
| 
								 | 
							
								        target_date = datetime.datetime(2025, 5, 27)
							 | 
						|||
| 
								 | 
							
								        start = target_date.strftime('%Y-%m-%d 00:00:00')
							 | 
						|||
| 
								 | 
							
								        end = target_date.strftime('%Y-%m-%d 23:59:59')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        logger.info(f"查询范围: {start} ~ {end},设备ID: 1")
							 | 
						|||
| 
								 | 
							
								        cursor = db.execute(
							 | 
						|||
| 
								 | 
							
								            '''SELECT timestamp, ph 
							 | 
						|||
| 
								 | 
							
								               FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								               WHERE timestamp BETWEEN ? AND ? 
							 | 
						|||
| 
								 | 
							
								               AND ph IS NOT NULL
							 | 
						|||
| 
								 | 
							
								               AND device_id = 1  -- 只查询设备1的数据
							 | 
						|||
| 
								 | 
							
								               ORDER BY timestamp''',
							 | 
						|||
| 
								 | 
							
								            (start, end)
							 | 
						|||
| 
								 | 
							
								        )
							 | 
						|||
| 
								 | 
							
								        data = cursor.fetchall()
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        if not data:
							 | 
						|||
| 
								 | 
							
								            logger.warning("2025-05-27设备1无PH数据,查询最近10条调试")
							 | 
						|||
| 
								 | 
							
								            debug_cursor = db.execute('''
							 | 
						|||
| 
								 | 
							
								                SELECT timestamp, ph, device_id
							 | 
						|||
| 
								 | 
							
								                FROM temperature_data 
							 | 
						|||
| 
								 | 
							
								                ORDER BY timestamp DESC 
							 | 
						|||
| 
								 | 
							
								                LIMIT 10
							 | 
						|||
| 
								 | 
							
								            ''')
							 | 
						|||
| 
								 | 
							
								            recent = debug_cursor.fetchall()
							 | 
						|||
| 
								 | 
							
								            logger.info("数据库最近10条记录:")
							 | 
						|||
| 
								 | 
							
								            for record in recent:
							 | 
						|||
| 
								 | 
							
								                logger.info(f"  timestamp: {record['timestamp']}, ph: {record['ph']}, device_id: {record['device_id']}")
							 | 
						|||
| 
								 | 
							
								            return jsonify({
							 | 
						|||
| 
								 | 
							
								                'date': '2025-05-27',
							 | 
						|||
| 
								 | 
							
								                'device_id': 1,
							 | 
						|||
| 
								 | 
							
								                'message': '未找到设备1的PH数据',
							 | 
						|||
| 
								 | 
							
								                'data': []
							 | 
						|||
| 
								 | 
							
								            })
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        result = []
							 | 
						|||
| 
								 | 
							
								        for i, row in enumerate(data):
							 | 
						|||
| 
								 | 
							
								            try:
							 | 
						|||
| 
								 | 
							
								                # 提取原始数据
							 | 
						|||
| 
								 | 
							
								                raw_ts = row['timestamp']
							 | 
						|||
| 
								 | 
							
								                raw_ph = row['ph']
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                # 验证时间格式
							 | 
						|||
| 
								 | 
							
								                if isinstance(raw_ts, datetime.datetime):
							 | 
						|||
| 
								 | 
							
								                    dt = raw_ts
							 | 
						|||
| 
								 | 
							
								                else:
							 | 
						|||
| 
								 | 
							
								                    dt = datetime.datetime.strptime(raw_ts, '%Y-%m-%d %H:%M:%S')
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                # 验证PH值
							 | 
						|||
| 
								 | 
							
								                if raw_ph is None:
							 | 
						|||
| 
								 | 
							
								                    continue  # 跳过空值
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                if isinstance(raw_ph, (float, int)):
							 | 
						|||
| 
								 | 
							
								                    ph_value = raw_ph
							 | 
						|||
| 
								 | 
							
								                elif isinstance(raw_ph, str):
							 | 
						|||
| 
								 | 
							
								                    ph_str = raw_ph.strip()
							 | 
						|||
| 
								 | 
							
								                    if 'pH' in ph_str:
							 | 
						|||
| 
								 | 
							
								                        ph_str = ph_str.replace('pH', '').strip()
							 | 
						|||
| 
								 | 
							
								                    ph_value = float(ph_str)
							 | 
						|||
| 
								 | 
							
								                else:
							 | 
						|||
| 
								 | 
							
								                    ph_value = float(raw_ph)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                result.append({
							 | 
						|||
| 
								 | 
							
								                    'timestamp': raw_ts.strftime('%Y-%m-%d %H:%M:%S') if isinstance(raw_ts,
							 | 
						|||
| 
								 | 
							
								                                                                                    datetime.datetime) else raw_ts,
							 | 
						|||
| 
								 | 
							
								                    'ph': round(ph_value, 2),
							 | 
						|||
| 
								 | 
							
								                    'formatted_time': dt.strftime('%H:%M')
							 | 
						|||
| 
								 | 
							
								                })
							 | 
						|||
| 
								 | 
							
								            except ValueError as ve:
							 | 
						|||
| 
								 | 
							
								                logger.error(f"第 {i + 1} 条记录格式错误: {str(ve)}")
							 | 
						|||
| 
								 | 
							
								                logger.error(f"  原始数据: timestamp={raw_ts}, ph={raw_ph}")
							 | 
						|||
| 
								 | 
							
								            except TypeError as te:
							 | 
						|||
| 
								 | 
							
								                logger.error(f"第 {i + 1} 条记录类型错误: {str(te)}")
							 | 
						|||
| 
								 | 
							
								                logger.error(f"  原始数据: timestamp={raw_ts}, ph={raw_ph}")
							 | 
						|||
| 
								 | 
							
								            except Exception as e:
							 | 
						|||
| 
								 | 
							
								                logger.error(f"处理第 {i + 1} 条记录时未知错误: {str(e)}")
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        if not result:
							 | 
						|||
| 
								 | 
							
								            logger.warning("设备1的所有记录处理后无有效数据")
							 | 
						|||
| 
								 | 
							
								            return jsonify({
							 | 
						|||
| 
								 | 
							
								                'date': '2025-05-27',
							 | 
						|||
| 
								 | 
							
								                'device_id': 1,
							 | 
						|||
| 
								 | 
							
								                'message': '数据格式错误,无有效PH值',
							 | 
						|||
| 
								 | 
							
								                'data': []
							 | 
						|||
| 
								 | 
							
								            })
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        logger.info(f"成功处理设备1的 {len(result)} 条有效数据")
							 | 
						|||
| 
								 | 
							
								        return jsonify({
							 | 
						|||
| 
								 | 
							
								            'date': '2025-05-27',
							 | 
						|||
| 
								 | 
							
								            'device_id': 1,
							 | 
						|||
| 
								 | 
							
								            'total_records': len(result),
							 | 
						|||
| 
								 | 
							
								            'data': result
							 | 
						|||
| 
								 | 
							
								        })
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    except sqlite3.OperationalError as oe:
							 | 
						|||
| 
								 | 
							
								        logger.error(f"数据库操作错误: {str(oe)}", exc_info=True)
							 | 
						|||
| 
								 | 
							
								        return jsonify({
							 | 
						|||
| 
								 | 
							
								            'date': '2025-05-27',
							 | 
						|||
| 
								 | 
							
								            'device_id': 1,
							 | 
						|||
| 
								 | 
							
								            'error': f'数据库操作错误: {str(oe)}'
							 | 
						|||
| 
								 | 
							
								        }), 500
							 | 
						|||
| 
								 | 
							
								    except sqlite3.Error as se:
							 | 
						|||
| 
								 | 
							
								        logger.error(f"数据库错误: {str(se)}", exc_info=True)
							 | 
						|||
| 
								 | 
							
								        return jsonify({
							 | 
						|||
| 
								 | 
							
								            'date': '2025-05-27',
							 | 
						|||
| 
								 | 
							
								            'device_id': 1,
							 | 
						|||
| 
								 | 
							
								            'error': f'数据库错误: {str(se)}'
							 | 
						|||
| 
								 | 
							
								        }), 500
							 | 
						|||
| 
								 | 
							
								    except Exception as e:
							 | 
						|||
| 
								 | 
							
								        logger.error(f"未知异常: {str(e)}", exc_info=True)
							 | 
						|||
| 
								 | 
							
								        return jsonify({
							 | 
						|||
| 
								 | 
							
								            'date': '2025-05-27',
							 | 
						|||
| 
								 | 
							
								            'device_id': 1,
							 | 
						|||
| 
								 | 
							
								            'error': f'未知错误: {str(e)}'
							 | 
						|||
| 
								 | 
							
								        }), 500
							 | 
						|||
| 
								 | 
							
								    finally:
							 | 
						|||
| 
								 | 
							
								        close_db()
							 |