diff --git a/server_api/app.py b/server_api/app.py index c020097..c9ca826 100644 --- a/server_api/app.py +++ b/server_api/app.py @@ -1,53 +1,19 @@ 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 +from .mir_server import execute_mission # Import the execute_mission function +from .models import db, Log, MirServerSettings, BoardSettings, MissionStatus # Import the models 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) +db.init_app(app) # Initialize the database with the app # Create the database if it does not exist if not os.path.exists('instance/logs.db'): + os.makedirs('instance', exist_ok=True) with app.app_context(): db.create_all() @@ -101,6 +67,8 @@ def log_message(): action = 'on' if "pressed" in message else 'off' if input_index == 1: input1_status = action + if action == 'on': + initiate_mission(hostname, 1) elif input_index == 2: input2_status = action elif input_index == 3: @@ -235,10 +203,11 @@ def control_relay(hostname, relay, action): @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', '') + mir_settings = MirServerSettings.query.first() + mir_ip = mir_settings.ip_address if mir_settings else '' + mir_user = mir_settings.user if mir_settings else '' + mir_password = mir_settings.password if mir_settings else '' + mir_auth_header = mir_settings.auth_header if mir_settings else '' boards = BoardSettings.query.all() board_settings = [] for board in boards: @@ -268,10 +237,21 @@ def set_mir_server(): 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 + mir_settings = MirServerSettings.query.first() + if mir_settings: + mir_settings.ip_address = mir_ip + mir_settings.user = mir_user + mir_settings.password = mir_password + mir_settings.auth_header = mir_auth_header + else: + mir_settings = MirServerSettings( + ip_address=mir_ip, + user=mir_user, + password=mir_password, + auth_header=mir_auth_header + ) + db.session.add(mir_settings) + db.session.commit() flash('MIR Server settings updated successfully!', 'success') else: flash('Invalid MIR Server settings!', 'danger') @@ -327,6 +307,21 @@ def get_input_status(hostname): return jsonify({'input_status': input_status}) return jsonify({'input_status': ['off', 'off', 'off', 'off']}) +@app.route('/board//mission_status', methods=['GET']) +def get_mission_status(hostname): + mission_status = MissionStatus.query.filter_by(hostname=hostname).order_by(MissionStatus.id.desc()).first() + if mission_status: + return jsonify({ + 'step1': 'Started' if mission_status.status == 'Started' else 'Pending', + 'step2': 'Pending', + 'mission': mission_status.status + }) + return jsonify({ + 'step1': 'Pending', + 'step2': 'Pending', + 'mission': 'Pending' + }) + def post_action_to_server(hostname, action): url = "http://your-server-url.com/action" payload = { @@ -342,23 +337,47 @@ def post_action_to_server(hostname, 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 initiate_mission(hostname, input_index): + board_settings = BoardSettings.query.filter_by(hostname=hostname).first() + if not board_settings: + return + + mission_id = getattr(board_settings, f'mission{input_index}') + if not mission_id: + return + + execute_mission(mission_id, hostname) + mission_status = MissionStatus(hostname=hostname, mission_id=mission_id, status='Started') + db.session.add(mission_status) + db.session.commit() + +def fetch_mission_status(): + with app.app_context(): + missions = MissionStatus.query.filter_by(status='Started').all() + mir_settings = MirServerSettings.query.first() + if not mir_settings: + print("MIR server settings not found.") + return + + mir_ip = mir_settings.ip_address + auth_header = mir_settings.auth_header + headers = { + 'Content-Type': 'application/json', + 'Authorization': auth_header + } + for mission in missions: + url = f"http://{mir_ip}/api/v2.0.0/missions/{mission.mission_guid}" # Use mission_guid + try: + response = requests.get(url, headers=headers) + if response.status_code == 200: + mission_data = response.json() + mission.status = mission_data.get('state', 'Pending') + db.session.commit() + else: + print(f"Failed to fetch mission status {mission.mission_id}: {response.text}") + except Exception as e: + print(f"Error fetching mission status {mission.mission_id}: {e}") + threading.Timer(30, fetch_mission_status).start() def schedule_cleanup(): with app.app_context(): @@ -369,4 +388,5 @@ def schedule_cleanup(): if __name__ == '__main__': schedule_cleanup() # Start the cleanup scheduler - app.run(host='0.0.0.0', port=5000) \ No newline at end of file + fetch_mission_status() # Start fetching mission status + app.run(host='0.0.0.0', port=80) \ No newline at end of file diff --git a/server_api/instance/logs.db b/server_api/instance/logs.db index a769067..97f4c92 100644 Binary files a/server_api/instance/logs.db and b/server_api/instance/logs.db differ diff --git a/server_api/mir_server.py b/server_api/mir_server.py new file mode 100644 index 0000000..ffd7408 --- /dev/null +++ b/server_api/mir_server.py @@ -0,0 +1,33 @@ +import requests +from .models import MirServerSettings, MissionStatus +from . import db + +def execute_mission(mission_id, hostname): + mir_settings = MirServerSettings.query.first() + if not mir_settings: + print("MIR server settings not found.") + return + + mir_ip = mir_settings.ip_address + auth_header = mir_settings.auth_header + url = f"http://{mir_ip}/api/v2.0.0/mission_scheduler" + headers = { + 'accept': 'application/json', + 'Authorization': auth_header, + 'Accept-Language': 'en_US', + 'Content-Type': 'application/json' + } + + try: + response = requests.post(url, headers=headers, json={"mission_id": mission_id}) + if response.status_code in {200, 201}: + print(f"Mission {mission_id} executed successfully.") + mission_data = response.json() + mission_guid = mission_data.get('id') + mission_status = MissionStatus(hostname=hostname, mission_id=mission_id, mission_guid=mission_guid, status='Started') + db.session.add(mission_status) + db.session.commit() + else: + print(f"Failed to execute mission {mission_id}: {response.text}") + except Exception as e: + print(f"Error executing mission {mission_id}: {e}") \ No newline at end of file diff --git a/server_api/models.py b/server_api/models.py new file mode 100644 index 0000000..cc8b3d1 --- /dev/null +++ b/server_api/models.py @@ -0,0 +1,45 @@ +from flask_sqlalchemy import SQLAlchemy +from datetime import datetime + +db = SQLAlchemy() + +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) + +class MissionStatus(db.Model): + id = db.Column(db.Integer, primary_key=True) + hostname = db.Column(db.String(100), nullable=False) + mission_id = db.Column(db.String(100), nullable=False) + mission_guid = db.Column(db.String(100), nullable=True) # Add this field + status = db.Column(db.String(100), nullable=False, default='Pending') \ No newline at end of file diff --git a/server_api/templates/board.html b/server_api/templates/board.html index c8f28bb..0bb46f4 100644 --- a/server_api/templates/board.html +++ b/server_api/templates/board.html @@ -149,6 +149,28 @@ +
+

MIR Mission Status

+
+
+
Mission Execution Steps
+
    +
  • + Step 1: Receiving start trigger from the board input + Pending +
  • +
  • + Step 2: Posting mission to MIR server + Pending +
  • +
  • + Step 3: Mission execution status + Pending +
  • +
+
+
+