From 7542f00327207b6db7578579f829b9e6cb83fcc6 Mon Sep 17 00:00:00 2001 From: ske087 Date: Fri, 14 Mar 2025 11:43:12 +0200 Subject: [PATCH] database separate file --- server_api/app.py | 148 ++++++++++++++++++-------------- server_api/instance/logs.db | Bin 36864 -> 28672 bytes server_api/mir_server.py | 33 +++++++ server_api/models.py | 45 ++++++++++ server_api/templates/board.html | 37 ++++++++ 5 files changed, 199 insertions(+), 64 deletions(-) create mode 100644 server_api/mir_server.py create mode 100644 server_api/models.py 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 a7690674d7ad9e95ac5eb38828c4a66fcbce5018..97f4c922a055def9b8f11b183ee18d6df8ca09fa 100644 GIT binary patch literal 28672 zcmeI4%WoUU8Nio%kRtV9Y^PCO$MHIGQmYBK^Vr!3i?pRJ*;XpjsVXa=F2Y)*sHM1+ zDpC#Xo0Ap@+O$A{_S!$7K-&N*P$0(wy%zy;2y*GMhaQXe;0EY7lqK%c9=a7LIeh~X zTxxdrx8HAPW@l!p{lWKF!fxH%-aCkbuE~vOj8w|_u4x*Eu>$W5ypPiaEXen9N*yl@ z`CKuqt<)^6Fp>MaG5e31zfJ!c7BHa!G=K)s02)98XaEhMfiKv=gSRF!^9u{9cfxLP zw^fhAPAA-JZ+5yt_pp=vHhFz@skF9au9dE>ESbqX^J*>|*39L~+S1LXRkLz?&8*y6 zS$WOOWq0;E-F6Vw&Gpji^;@OYtCWz1#2UxDf*mN=IXWP9{+p}I<aX*TkJ#`KK)HQ7N8c(p3 z9m~vfo_g1RblwlZ&B2fA^5aNR6h8K+@r0)*tT04YxW5_HY6taBM}3%wa0u$${h-r% zd+%UKAqftp`ov+zz{8&?5BmwV)GjnHx7- z_iMM_*sr$N$z8teRM~2?+S~~%&C>nq{Tn+Qih?ucsh#%`Hy z?35Qv@O|m+^5WLL^8Jn7@?vH8_RaO3O0#^gvbYZU4PI@&vAa=v{q>{q|3^mtqtOGz zJ)i+JfCkV28bAYR01co4G=K)s02)98x(zJCY{I2G=jZ3^o&Daw@-WxqcYW@OLf}za zptQbS@ENTY2(wwRMHwN46$X+V|EK4EW90vm|7req{@J-t;R_}-fCkV28bAYR01co4 zG=K)s02)98U#bB=Jqcsn3;)*$_sx2{29Lh*Cr`iW#OXI3KmDd-r{6Sn>P_YN|BCS| zBmc|%i*p~&EzW*2yFK&onfGSSPycrMTe**Owd}vLKg&Kp^}DHWXFka^(+|_{r!P+a ze)2mLf1PNI|7ZN?<1dW;VeCfgvsBCY9CoB3HLjdBF1`F}=^Ugp@ScTqQUv-=DvK~M!m)e69vKx)HaD%B)mv$i&Iy)WN&-B|7xEsMlVT_=z%0m3A!d9s$zy6 zbNjHp)rAMgKmi+|v7#WB%_wK|Wd8#T(TkdJZjq59_bno$O9mGUG`uWvg{Mb%tP!5D zQ4a<@9-&-|`@|PwAfS`44RiD})*~?>d7fwUlU>V&*crQ|Qz7vzUpUKD0Zc|LJ{M~S%==&vTU`8y6&Mv?>h|Intr+ z+c1%t2r5=UzNSwh&_E&M#8wvk=)6uGoH5~0e@J=!sy>{M(ZREKC{4PI7yo&kIEc>X zT-bi%)Wvbn>BHGFIxaY6wi5RhO*kn3P`SB9Dfe7u>mNQVaU<5Y9ts*sk2!)XTmHs5 zi5ji8^+*(&V+hJgp0ZeNJF8P66cDItj!-I$&gjE=Mb0eWceqeCTG2B)aRhKS@tN|h zg?Wh^rGRjg+C>IkAI}nuP%n8Ejn%Z)TtBj*J*=S}pA#Il;Iz3k8Ck!Sv}X0u+#;vY z8F9RUt7xCrn31#*3ja}9&>itC%6+Epi%j>!I(_f)m_{HhXdIckbCA=AgDV@g7`J_O z?=i~i#6fh_BV65sjHdMA;K~L#>T*}z_GfhBAUYz1V9K6Wl-7rXD;u$V&v99@RgP8f zXi_JRz^~O^=5cj5VL~5{%jh_qx*^T-xK12I$B9R5b-f$Yhhs82CV-~yQKod_AUfg! zPkD`#HRTl=(Qb zmHsEJj0p{(0W^RH(0~pDQBlV=uQ%(<<+#=IJmwF%#QHjM3~BGVO4>@A9amjU zTHowPcKZ30BOH|?33DMON@Z+@!;-WSW;l*Pwt%LK6Ok++eNa^BhjscHj$;~gpyl#y zwZ-T)Z8*47z+eu*Wn#$O{j2(L&@KaxGvX&*#s}r!(20ZShzGyuR*xeW^x>dg1{~w= zz-^a!bXRra7|d`8hYh(i@pXMTXqN#87Xe#6!|{qv97JckE*Y3FjmZpOmbj5yF1Q0u Peao@o9!PR(Hir5y&C(kP literal 36864 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 BASM6+ 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 +
  • +
+
+
+