from flask import Flask, request, jsonify, render_template, redirect, url_for, flash from flask_sqlalchemy import SQLAlchemy from datetime import datetime, timedelta import os import threading import requests import base64 app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///logs.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.secret_key = 'supersecretkey' db = SQLAlchemy(app) class Log(db.Model): id = db.Column(db.Integer, primary_key=True) hostname = db.Column(db.String(100), nullable=False) ip_address = db.Column(db.String(100), nullable=False) message = db.Column(db.String(500), nullable=False) timestamp = db.Column(db.DateTime, default=datetime.utcnow) relay1_status = db.Column(db.String(3), default='off') relay2_status = db.Column(db.String(3), default='off') relay3_status = db.Column(db.String(3), default='off') relay4_status = db.Column(db.String(3), default='off') input1_status = db.Column(db.String(3), default='off') input2_status = db.Column(db.String(3), default='off') input3_status = db.Column(db.String(3), default='off') input4_status = db.Column(db.String(3), default='off') mission1 = db.Column(db.String(100), default='') mission2 = db.Column(db.String(100), default='') mission3 = db.Column(db.String(100), default='') mission4 = db.Column(db.String(100), default='') class MirServerSettings(db.Model): id = db.Column(db.Integer, primary_key=True) ip_address = db.Column(db.String(100), nullable=False) user = db.Column(db.String(100), nullable=False) password = db.Column(db.String(100), nullable=False) auth_header = db.Column(db.String(500), nullable=False) class BoardSettings(db.Model): id = db.Column(db.Integer, primary_key=True) hostname = db.Column(db.String(100), nullable=False) mission1 = db.Column(db.String(100), nullable=False) mission2 = db.Column(db.String(100), nullable=False) mission3 = db.Column(db.String(100), nullable=False) mission4 = db.Column(db.String(100), nullable=False) # Create the database if it does not exist if not os.path.exists('instance/logs.db'): with app.app_context(): db.create_all() # Flag to check if tables are created tables_created = False @app.before_request def create_tables(): global tables_created if not tables_created: db.create_all() tables_created = True @app.route('/log', methods=['POST']) def log_message(): data = request.get_json() hostname = data.get('hostname') ip_address = data.get('ip_address') message = data.get('message') # Fetch the latest log for the hostname to get the current relay and input statuses latest_log = Log.query.filter_by(hostname=hostname).order_by(Log.timestamp.desc()).first() relay1_status = latest_log.relay1_status if latest_log else 'off' relay2_status = latest_log.relay2_status if latest_log else 'off' relay3_status = latest_log.relay3_status if latest_log else 'off' relay4_status = latest_log.relay4_status if latest_log else 'off' input1_status = latest_log.input1_status if latest_log else 'off' input2_status = latest_log.input2_status if latest_log else 'off' input3_status = latest_log.input3_status if latest_log else 'off' input4_status = latest_log.input4_status if latest_log else 'off' if hostname and ip_address and message: # Parse the message to update relay status if "Relay" in message and "turned" in message: parts = message.split() relay_index = int(parts[1]) action = parts[3].lower() if relay_index == 1: relay1_status = action elif relay_index == 2: relay2_status = action elif relay_index == 3: relay3_status = action elif relay_index == 4: relay4_status = action # Parse the message to update input status if "Input" in message and ("pressed" in message or "released" in message): parts = message.split() input_index = int(parts[1]) action = 'on' if "pressed" in message else 'off' if input_index == 1: input1_status = action elif input_index == 2: input2_status = action elif input_index == 3: input3_status = action elif input_index == 4: input4_status = action new_log = Log( hostname=hostname, ip_address=ip_address, message=message, relay1_status=relay1_status, relay2_status=relay2_status, relay3_status=relay3_status, relay4_status=relay4_status, input1_status=input1_status, input2_status=input2_status, input3_status=input3_status, input4_status=input4_status ) db.session.add(new_log) # Check if the board settings already exist board_settings = BoardSettings.query.filter_by(hostname=hostname).first() if not board_settings: # Initialize board settings if they do not exist board_settings = BoardSettings( hostname=hostname, mission1='', mission2='', mission3='', mission4='' ) db.session.add(board_settings) db.session.commit() return jsonify({'status': 'success', 'message': 'Log saved'}), 201 return jsonify({'status': 'error', 'message': 'Invalid data'}), 400 @app.route('/logs', methods=['GET']) def get_logs(): logs = Log.query.all() return jsonify([{'id': log.id, 'hostname': log.hostname, 'message': log.message, 'timestamp': log.timestamp} for log in logs]) @app.route('/cleanup', methods=['DELETE']) def cleanup_logs(): cutoff_date = datetime.utcnow() - timedelta(hours=app.config.get('CLEANUP_TIME', 24)) Log.query.filter(Log.timestamp < cutoff_date).delete() db.session.commit() return jsonify({'status': 'success', 'message': 'Old logs deleted'}) @app.route('/') def index(): # Get the latest log entry for each board latest_logs = db.session.query(Log).order_by(Log.timestamp.desc()).all() boards = {} for log in latest_logs: if log.hostname not in boards: boards[log.hostname] = log # Determine the status of each board board_status = [] for hostname, log in boards.items(): time_diff = datetime.utcnow() - log.timestamp status = 'Online' if time_diff.total_seconds() <= 20 else 'Possible Offline' board_status.append({'hostname': hostname, 'status': status, 'last_seen': log.timestamp}) return render_template('index.html', boards=board_status) @app.route('/board/') def view_board(hostname): logs = Log.query.filter_by(hostname=hostname).order_by(Log.timestamp.desc()).limit(50).all() relay_status = ['off', 'off', 'off', 'off'] input_status = ['off', 'off', 'off', 'off'] if logs: latest_log = logs[0] relay_status = [ latest_log.relay1_status, latest_log.relay2_status, latest_log.relay3_status, latest_log.relay4_status ] input_status = [ latest_log.input1_status, latest_log.input2_status, latest_log.input3_status, latest_log.input4_status ] ip_address = logs[0].ip_address if logs else '' # Get the IP address from the logs last_update = logs[0].timestamp if logs else None # Get the last update time # Add placeholders for MIR server status and last input triggered mir_status = "Unknown" last_input_triggered = "None" return render_template('board.html', hostname=hostname, ip_address=ip_address, logs=logs, relay_status=relay_status, input_status=input_status, last_update=last_update, mir_status=mir_status, last_input_triggered=last_input_triggered) @app.route('/delete_board/', methods=['POST']) def delete_board(hostname): # Delete all logs related to the board Log.query.filter_by(hostname=hostname).delete() # Delete the board settings if they exist board_settings = BoardSettings.query.filter_by(hostname=hostname).first() if board_settings: db.session.delete(board_settings) db.session.commit() flash(f'Board {hostname} and its settings have been deleted.', 'success') return redirect(url_for('settings')) @app.route('/board//logs', methods=['GET']) def get_board_logs(hostname): logs = Log.query.filter_by(hostname=hostname).order_by(Log.timestamp.desc()).all() return jsonify({'logs': [{'timestamp': log.timestamp, 'message': log.message} for log in logs]}) @app.route('/board//relay//', methods=['POST']) def control_relay(hostname, relay, action): log = Log.query.filter_by(hostname=hostname).order_by(Log.timestamp.desc()).first() if log: ip_address = log.ip_address url = f"http://{ip_address}/relay" payload = { "relay": relay, "action": action } try: response = requests.post(url, data=payload) if response.status_code == 200: return redirect(url_for('view_board', hostname=hostname)) else: return f"Failed to control relay: {response.text}", 500 except Exception as e: return f"Error controlling relay: {e}", 500 else: return f"No logs found for hostname: {hostname}", 404 @app.route('/settings', methods=['GET']) def settings(): cleanup_time = app.config.get('CLEANUP_TIME', 24) mir_ip = app.config.get('MIR_IP', '') mir_user = app.config.get('MIR_USER', '') mir_password = app.config.get('MIR_PASSWORD', '') mir_auth_header = app.config.get('MIR_AUTH_HEADER', '') boards = BoardSettings.query.all() board_settings = [] for board in boards: board_settings.append({ 'hostname': board.hostname, 'mission1': board.mission1, 'mission2': board.mission2, 'mission3': board.mission3, 'mission4': board.mission4 }) return render_template('settings.html', cleanup_time=cleanup_time, mir_ip=mir_ip, mir_user=mir_user, mir_password=mir_password, mir_auth_header=mir_auth_header, boards=board_settings) @app.route('/set_cleanup_time', methods=['POST']) def set_cleanup_time(): cleanup_time = request.form.get('cleanup_time') if cleanup_time: app.config['CLEANUP_TIME'] = int(cleanup_time) flash('Cleanup time set successfully!', 'success') else: flash('Invalid cleanup time!', 'danger') return redirect(url_for('settings')) @app.route('/set_mir_server', methods=['POST']) def set_mir_server(): mir_ip = request.form.get('mir_ip') mir_user = request.form.get('mir_user') mir_password = request.form.get('mir_password') mir_auth_header = request.form.get('mir_auth_header') if mir_ip and mir_user and mir_password and mir_auth_header: app.config['MIR_IP'] = mir_ip app.config['MIR_USER'] = mir_user app.config['MIR_PASSWORD'] = mir_password app.config['MIR_AUTH_HEADER'] = mir_auth_header flash('MIR Server settings updated successfully!', 'success') else: flash('Invalid MIR Server settings!', 'danger') return redirect(url_for('settings')) @app.route('/set_board_settings/', methods=['POST']) def set_board_settings(hostname): mission1 = request.form.get('mission1') mission2 = request.form.get('mission2') mission3 = request.form.get('mission3') mission4 = request.form.get('mission4') if mission1 and mission2 and mission3 and mission4: board_settings = BoardSettings.query.filter_by(hostname=hostname).first() if board_settings: board_settings.mission1 = mission1 board_settings.mission2 = mission2 board_settings.mission3 = mission3 board_settings.mission4 = mission4 else: board_settings = BoardSettings( hostname=hostname, mission1=mission1, mission2=mission2, mission3=mission3, mission4=mission4 ) db.session.add(board_settings) db.session.commit() flash('Board settings updated successfully!', 'success') else: flash('Invalid board settings!', 'danger') return redirect(url_for('settings')) @app.route('/run_cleanup_now', methods=['POST']) def run_cleanup_now(): with app.app_context(): cutoff_date = datetime.utcnow() - timedelta(hours=app.config.get('CLEANUP_TIME', 24)) Log.query.filter(Log.timestamp < cutoff_date).delete() db.session.commit() flash('Cleanup executed successfully!', 'success') return redirect(url_for('settings')) @app.route('/board//input_status', methods=['GET']) def get_input_status(hostname): log = Log.query.filter_by(hostname=hostname).order_by(Log.timestamp.desc()).first() if log: input_status = [ log.input1_status, log.input2_status, log.input3_status, log.input4_status ] return jsonify({'input_status': input_status}) return jsonify({'input_status': ['off', 'off', 'off', 'off']}) def post_action_to_server(hostname, action): url = "http://your-server-url.com/action" payload = { "hostname": hostname, "action": action } try: response = requests.post(url, json=payload) if response.status_code == 200: print(f"Action posted successfully: {action}") else: print(f"Failed to post action: {action}") except Exception as e: print(f"Error posting action: {e}") def execute_mission(mission_id): mir_ip = app.config.get('MIR_IP') mir_user = app.config.get('MIR_USER') mir_password = app.config.get('MIR_PASSWORD') url = f"http://{mir_ip}/api/v2.0.0/missions/{mission_id}/dispatch" headers = { 'Content-Type': 'application/json', 'Authorization': f'Basic {base64.b64encode(f"{mir_user}:{mir_password}".encode()).decode()}' } try: response = requests.post(url, headers=headers) if response.status_code == 200: print(f"Mission {mission_id} executed successfully.") else: print(f"Failed to execute mission {mission_id}: {response.text}") except Exception as e: print(f"Error executing mission {mission_id}: {e}") def schedule_cleanup(): with app.app_context(): cutoff_date = datetime.utcnow() - timedelta(hours=app.config.get('CLEANUP_TIME', 24)) Log.query.filter(Log.timestamp < cutoff_date).delete() db.session.commit() threading.Timer(3600, schedule_cleanup).start() if __name__ == '__main__': schedule_cleanup() # Start the cleanup scheduler app.run(host='0.0.0.0', port=5000)