Spaces:
Running
Running
| import flask | |
| from flask import request, jsonify | |
| import os | |
| import sqlite3 | |
| from datetime import datetime | |
| import pytz # Импортируем библиотеку для работы с часовыми поясами | |
| import matplotlib.pyplot as plt | |
| import io | |
| import base64 | |
| from dotenv import load_dotenv | |
| import globs | |
| from flask import render_template | |
| from api_logic import api | |
| load_dotenv() | |
| # Инициализация базы данных | |
| def init_db(db_name): | |
| conn = sqlite3.connect(db_name) | |
| cursor = conn.cursor() | |
| # Создаем таблицу | |
| cursor.execute(''' | |
| CREATE TABLE IF NOT EXISTS system_data ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, -- Автоинкрементный счётчик | |
| date_time TEXT, -- Дата и время | |
| dey TEXT, | |
| wek TEXT, | |
| v_hid TEXT, | |
| v_min TEXT, | |
| ph TEXT, | |
| ec TEXT, | |
| tS TEXT, | |
| tA TEXT, | |
| hDm TEXT, | |
| sVen TEXT, | |
| onA TEXT, | |
| onB TEXT, | |
| onC TEXT, | |
| nPh TEXT, | |
| nEC TEXT, | |
| nTa TEXT, | |
| nLon TEXT, | |
| nLoff TEXT | |
| ) | |
| ''') | |
| conn.commit() | |
| conn.close() | |
| # Глобальные переменные | |
| globs.dey = 0 | |
| globs.wek = 0 | |
| globs.v_hid = 0 | |
| globs.v_min = 0 | |
| globs.ph = 0 | |
| globs.ec = 0 | |
| globs.tS = 0 | |
| globs.tA = 0 | |
| globs.hDm = 0 | |
| globs.sVen = 0 | |
| globs.onA = 0 | |
| globs.onB = 0 | |
| globs.onC = 0 | |
| globs.ph_eep = 0 | |
| globs.ph_on_eep = 0 | |
| globs.ec_eep = 0 | |
| globs.ec_A_eep = 0 | |
| globs.ec_B_eep = 0 | |
| globs.ec_C_eep = 0 | |
| globs.l_ON_h_eep = 0 | |
| globs.l_ON_m_eep = 0 | |
| globs.l_OFF_h_eep = 0 | |
| globs.l_OFF_m_eep = 0 | |
| globs.t_Voz_eep = 0 | |
| # Создаем экземпляр Flask-приложения | |
| app = flask.Flask(__name__, template_folder="./") | |
| app.config['DEBUG'] = True | |
| # Функция сохранения в базу | |
| def save_data_to_db(db_name, data): | |
| try: | |
| conn = sqlite3.connect(db_name) | |
| cursor = conn.cursor() | |
| # ✅ Устанавливаем московское время (UTC+3) | |
| moscow_tz = pytz.timezone("Europe/Moscow") | |
| current_time = datetime.now(moscow_tz).strftime('%Y-%m-%d %H:%M:%S') | |
| # Вставляем данные в таблицу | |
| cursor.execute(''' | |
| INSERT INTO system_data ( | |
| date_time, dey, wek, v_hid, v_min, ph, ec, tS, tA, hDm, sVen, onA, onB, onC, nPh, nEC, nTa, nLon, nLoff | |
| ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) | |
| ''', ( | |
| current_time, # ✅ Дата и время по Москве | |
| data['dey'], data['wek'], data['v_hid'], data['v_min'], data['ph'], data['ec'], | |
| data['tS'], data['tA'], data['hDm'], data['sVen'], data['onA'], data['onB'], | |
| data['onC'], data['nPh'], data['nEC'], data['nTa'], data['nLon'], data['nLoff'] | |
| )) | |
| conn.commit() | |
| conn.close() | |
| except Exception as e: | |
| return jsonify({'status': 'error', 'message': str(e)}), 500 | |
| # Маршрут сохранения в базу | |
| def sav_db_api(): | |
| # Инициализируем базу данных | |
| init_db('system_data.db') | |
| # Получаем данные из запроса | |
| data = { | |
| 'dey': request.args.get('dey'), | |
| 'wek': request.args.get('wek'), | |
| 'v_hid': request.args.get('v_hid'), | |
| 'v_min': request.args.get('v_min'), | |
| 'ph': request.args.get('ph'), | |
| 'ec': request.args.get('ec'), | |
| 'tS': request.args.get('tS'), | |
| 'tA': request.args.get('tA'), | |
| 'hDm': request.args.get('hDm'), | |
| 'sVen': request.args.get('sVen'), | |
| 'onA': request.args.get('onA'), | |
| 'onB': request.args.get('onB'), | |
| 'onC': request.args.get('onC'), | |
| 'nPh': request.args.get('nPh'), | |
| 'nEC': request.args.get('nEC'), | |
| 'nTa': request.args.get('nTa'), | |
| 'nLon': request.args.get('nLon'), | |
| 'nLoff': request.args.get('nLoff') | |
| } | |
| # Проверяем, что все необходимые параметры переданы | |
| required_params = ['dey', 'wek', 'v_hid', 'v_min', 'ph', 'ec', 'tS', 'tA', 'hDm', 'sVen', 'onA', 'onB', 'onC', 'nPh', 'nEC', 'nTa', 'nLon', 'nLoff'] | |
| for param in required_params: | |
| if data[param] is None: | |
| return jsonify({'status': 'error', 'message': f'Отсутствует параметр: {param}'}), 400 | |
| # Сохраняем данные в базу | |
| save_data_to_db('system_data.db', data) | |
| # Возвращаем ответ | |
| return jsonify({'status': 'success', 'message': 'Save OK'}) | |
| # Маршрут для вывода всех данных из таблицы | |
| def get_all_data(): | |
| try: | |
| conn = sqlite3.connect('system_data.db') | |
| cursor = conn.cursor() | |
| # Выполняем запрос для получения всех данных из таблицы | |
| cursor.execute('SELECT * FROM system_data') | |
| rows = cursor.fetchall() | |
| # Получаем названия столбцов | |
| column_names = [description[0] for description in cursor.description] | |
| # Преобразуем данные в формат JSON | |
| data = [] | |
| for row in rows: | |
| data.append(dict(zip(column_names, row))) | |
| conn.close() | |
| # Возвращаем данные в формате JSON | |
| return jsonify(data) | |
| except Exception as e: | |
| return jsonify({'status': 'error', 'message': str(e)}), 500 | |
| def delete_db(): | |
| try: | |
| conn = sqlite3.connect("system_data.db") # Используем вашу БД | |
| cursor = conn.cursor() | |
| # ✅ Удаляем все записи из таблицы | |
| cursor.execute("DELETE FROM system_data") | |
| # ✅ Сбрасываем автоинкрементный счётчик ID (для SQLite) | |
| cursor.execute("DELETE FROM sqlite_sequence WHERE name='system_data'") | |
| conn.commit() | |
| conn.close() | |
| return jsonify({'status': 'ok', 'message': 'База данных успешно очищена'}) | |
| except Exception as e: | |
| return jsonify({'status': 'error', 'message': str(e)}), 500 | |
| def plot_ph(): | |
| try: | |
| # Получаем параметры из URL (если они есть) | |
| start_date = request.args.get('start_date') | |
| end_date = request.args.get('end_date') | |
| # Если параметры есть, проверяем их корректность | |
| if start_date and end_date: | |
| try: | |
| # Преобразуем даты в формат datetime | |
| start_date = datetime.strptime(start_date, '%Y-%m-%dT%H:%M') | |
| end_date = datetime.strptime(end_date, '%Y-%m-%dT%H:%M') | |
| # Проверяем, что дата начала раньше даты окончания | |
| if start_date >= end_date: | |
| return ''' | |
| <html> | |
| <body> | |
| <h1>Неправильная дата выбрана</h1> | |
| <p>Дата начала должна быть раньше даты окончания.</p> | |
| <button onclick="window.location.href='/plot_ph'">Новые данные</button> | |
| </body> | |
| </html> | |
| ''' | |
| except ValueError: | |
| return ''' | |
| <html> | |
| <body> | |
| <h1>Неправильная дата выбрана</h1> | |
| <p>Формат даты должен быть YYYY-MM-DDTHH:MM.</p> | |
| <button onclick="window.location.href='/plot_ph'">Новые данные</button> | |
| </body> | |
| </html> | |
| ''' | |
| # Подключаемся к базе данных | |
| conn = sqlite3.connect('system_data.db') | |
| cursor = conn.cursor() | |
| # Проверяем, существует ли таблица | |
| cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='system_data'") | |
| table_exists = cursor.fetchone() | |
| if not table_exists: | |
| return ''' | |
| <html> | |
| <body> | |
| <h1>В базе данных отсутствуют значения</h1> | |
| <p>Таблица system_data не существует. Обновите базу данных.</p> | |
| <button onclick="window.location.href='/plot_ph'">Обновить базу данных</button> | |
| </body> | |
| </html> | |
| ''' | |
| # Выполняем запрос для получения данных в заданном диапазоне | |
| cursor.execute(''' | |
| SELECT date_time, ph, dey, wek | |
| FROM system_data | |
| WHERE date_time BETWEEN ? AND ? | |
| ORDER BY date_time | |
| ''', (start_date, end_date)) | |
| rows = cursor.fetchall() | |
| conn.close() | |
| # Проверяем, есть ли данные | |
| if not rows: | |
| return ''' | |
| <html> | |
| <body> | |
| <h1>Данные отсутствуют в базе данных</h1> | |
| <p>Для выбранного диапазона дат данные не найдены.</p> | |
| <button onclick="window.location.href='/plot_ph'">Новые данные</button> | |
| </body> | |
| </html> | |
| ''' | |
| # Разделяем данные на дату и время, а также добавляем значения из столбцов dey и wek | |
| dates = [f"{row[0]} d: {row[2]} w: {row[3]}" for row in rows] # Дата и время + dey + wek | |
| ph_values = [float(row[1]) for row in rows] # Значения pH | |
| # Создаем график | |
| plt.figure(figsize=(15, 6)) # Увеличиваем ширину графика | |
| plt.plot(dates, ph_values, marker='o', linestyle='-', color='b') # Точки соединены линией | |
| plt.title('График значений pH') | |
| plt.xlabel('Дата и время') | |
| plt.ylabel('Значение pH') | |
| plt.xticks(rotation=90) # Поворачиваем подписи оси X вертикально | |
| plt.grid(True) | |
| plt.gcf().subplots_adjust(bottom=0.4) # Увеличиваем пространство снизу для надписей | |
| # Сохраняем график в буфер | |
| buffer = io.BytesIO() | |
| plt.savefig(buffer, format='png') | |
| buffer.seek(0) | |
| # Кодируем график в base64 | |
| plot_data = base64.b64encode(buffer.getvalue()).decode('utf-8') | |
| # Возвращаем график в формате HTML с кнопками "Новые данные" и "Обновить данные" | |
| return f''' | |
| <html> | |
| <body> | |
| <h1>График значений pH</h1> | |
| <button onclick="window.location.href='/plot_ph'">Новые данные</button> | |
| <button onclick="window.location.reload()">Обновить данные</button> | |
| <br><br> | |
| <img src="data:image/png;base64,{plot_data}" alt="График pH"> | |
| </body> | |
| </html> | |
| ''' | |
| # Если параметров нет, отображаем форму для ввода даты и времени | |
| return ''' | |
| <html> | |
| <body> | |
| <h1>Выберите диапазон даты и времени для графика pH</h1> | |
| <form method="GET"> | |
| <label for="start_date">Начало:</label> | |
| <input type="datetime-local" id="start_date" name="start_date" required> | |
| <br><br> | |
| <label for="end_date">Конец:</label> | |
| <input type="datetime-local" id="end_date" name="end_date" required> | |
| <br><br> | |
| <button type="submit">Показать график</button> | |
| </form> | |
| </body> | |
| </html> | |
| ''' | |
| except Exception as e: | |
| return jsonify({'status': 'error', 'message': str(e)}), 500 | |
| def plot_week(): | |
| try: | |
| # Получаем номер недели из параметров запроса | |
| week_number = request.args.get('week', default=1, type=int) | |
| week_number = max(1, min(30, week_number)) # Ограничиваем диапазон 1-30 | |
| # Подключаемся к базе данных | |
| conn = sqlite3.connect('system_data.db') | |
| cursor = conn.cursor() | |
| # Проверяем существование таблицы | |
| cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='system_data'") | |
| table_exists = cursor.fetchone() | |
| if not table_exists: | |
| conn.close() | |
| return render_template('plot_week.html', data=None, week_number=week_number, table_exists=False) | |
| # Запрашиваем данные за выбранную неделю | |
| cursor.execute(''' | |
| SELECT date_time, dey, ph, ec, tS, tA, hDm, sVen, onA, onB, onC, v_hid, v_min | |
| FROM system_data | |
| WHERE wek = ? | |
| ORDER BY date_time | |
| ''', (str(week_number),)) # Приводим week_number к строке, так как wek имеет тип TEXT | |
| rows = cursor.fetchall() | |
| conn.close() | |
| # Если данных нет | |
| if not rows: | |
| return render_template('plot_week.html', data=None, week_number=week_number, table_exists=True) | |
| # Формируем данные для JSON | |
| data = { | |
| 'week': week_number, | |
| 'dates': [row[0] for row in rows], | |
| 'days_of_week': [int(row[1]) if row[1] else 0 for row in rows], # Преобразуем dey в int | |
| 'ph': [float(row[2]) if row[2] else 0.0 for row in rows], # pH | |
| 'ec': [float(row[3]) if row[3] else 0.0 for row in rows], # EC | |
| 'tS': [float(row[4]) if row[4] else 0.0 for row in rows], # Температура раствора | |
| 'tA': [float(row[5]) if row[5] else 0.0 for row in rows], # Температура воздуха | |
| 'hDm': [float(row[6]) if row[6] else 0.0 for row in rows], # Влажность воздуха | |
| 'sVen': [float(row[7]) if row[7] else 0.0 for row in rows], # Обороты вентилятора | |
| 'onA': [float(row[8]) if row[8] else 0.0 for row in rows], # Насос A | |
| 'onB': [float(row[9]) if row[9] else 0.0 for row in rows], # Насос B | |
| 'onC': [float(row[10]) if row[10] else 0.0 for row in rows], # Насос C | |
| 'sus': [f"{row[11]}:{row[12]}" if row[11] and row[12] else "0:0" for row in rows] # Объединяем v_hid и v_min | |
| } | |
| # Отправляем данные в HTML-шаблон | |
| return render_template('plot_week.html', data=data, week_number=week_number, table_exists=True) | |
| except Exception as e: | |
| # Логируем ошибку в консоль для отладки | |
| print(f"Ошибка: {str(e)}") | |
| return render_template('plot_week.html', data=None, week_number=week_number, table_exists=True, message=f"Ошибка: {str(e)}") | |
| def index(): | |
| return flask.render_template('index.html') | |
| def online(): | |
| return render_template('online.html') | |
| def table(): | |
| return render_template('table.html') | |
| def online_api(): | |
| return jsonify(dey=globs.dey, | |
| wek=globs.wek, | |
| v_hid=globs.v_hid, | |
| v_min=globs.v_min, | |
| ph=globs.ph, | |
| ec=globs.ec, | |
| tS=globs.tS, | |
| tA=globs.tA, | |
| hDm=globs.hDm, | |
| sVen=globs.sVen, | |
| rFul=globs.rFul, | |
| rLi=globs.rLi, | |
| rWat=globs.rWat, | |
| rRas=globs.rRas, | |
| rPH=globs.rPH, | |
| rEC=globs.rEC, | |
| rSl=globs.rSl, | |
| rLe=globs.rLe, | |
| alW=globs.alW | |
| ) | |
| def settings(): | |
| return render_template('settings.html') | |
| def settings_api(): | |
| return jsonify(ph_eep=globs.ph_eep, | |
| ph_on_eep=globs.ph_on_eep, | |
| ec_eep=globs.ec_eep, | |
| ec_A_eep=globs.ec_A_eep, | |
| ec_B_eep=globs.ec_B_eep, | |
| ec_C_eep=globs.ec_C_eep, | |
| l_ON_h_eep=globs.l_ON_h_eep, | |
| l_ON_m_eep=globs.l_ON_m_eep, | |
| l_OFF_h_eep=globs.l_OFF_h_eep, | |
| l_OFF_m_eep=globs.l_OFF_m_eep, | |
| t_Voz_eep=globs.t_Voz_eep | |
| ) | |
| def set_pH_value(): | |
| ph_value = request.args.get('value') | |
| globs.ph_set = ph_value | |
| globs.eep_set = 1 | |
| return "pH value set successfully" | |
| def ph_on_value(): | |
| ph_on_value = request.args.get('value') | |
| globs.ph_on_set = ph_on_value | |
| globs.eep_set = 2 | |
| return "EC value set successfully" | |
| def set_EC_value(): | |
| ec_value = request.args.get('value') | |
| globs.ec_set = ec_value | |
| globs.eep_set = 3 | |
| return "EC value set successfully" | |
| def ec_A_setValue(): | |
| ec_A_setValue = request.args.get('value') | |
| globs.ec_A_set = ec_A_setValue | |
| globs.eep_set = 4 | |
| return "EC value set successfully" | |
| def ec_B_setValue(): | |
| ec_B_setValue = request.args.get('value') | |
| globs.ec_B_set = ec_B_setValue | |
| globs.eep_set = 5 | |
| return "EC value set successfully" | |
| def ec_C_setValue(): | |
| ec_C_setValue = request.args.get('value') | |
| globs.ec_C_set = ec_C_setValue | |
| globs.eep_set = 6 | |
| return "EC value set successfully" | |
| def l_ON_set(): | |
| globs.l_ON_h_set = request.args.get('l_ON_h_set') | |
| globs.l_ON_m_set = request.args.get('l_ON_m_set') | |
| globs.eep_set = 7 | |
| return "EC value set successfully" | |
| def l_OFF_set(): | |
| globs.l_OFF_h_set = request.args.get('l_OFF_h_set') | |
| globs.l_OFF_m_set = request.args.get('l_OFF_m_set') | |
| globs.eep_set = 8 | |
| return "EC value set successfully" | |
| def t_Voz_eep_set(): | |
| t_Voz_eep_set = request.args.get('value') | |
| globs.t_Voz_set = t_Voz_eep_set | |
| globs.eep_set = 9 | |
| return "EC value set successfully" | |
| def but_start(): | |
| globs.eep_set = 10 | |
| return jsonify(value_set="start") | |
| def but_stop(): | |
| globs.eep_set = 11 | |
| return jsonify(value_set="stop") | |
| def but_res(): | |
| globs.eep_set = 12 | |
| return jsonify(value_set="res") | |
| def but_sliv(): | |
| globs.eep_set = 13 | |
| return jsonify(value_set="sliv") | |
| def handle_api(): | |
| response = api() | |
| return response | |
| def handle_save_db(): | |
| response = save_db() | |
| return response | |
| def set_res(): | |
| globs.eep_set = 0 | |
| return jsonify(value_set="reset") | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860))) |