From a439ba8fc4d42e858449a91ee101682f544b8a17 Mon Sep 17 00:00:00 2001 From: ske087 Date: Tue, 11 Mar 2025 13:41:49 +0200 Subject: [PATCH] adding boards and settings --- server_api/app.py | 129 ++++++++++++++++++++++++++--- server_api/instance/logs.db | Bin 20480 -> 65536 bytes server_api/templates/board.html | 12 ++- server_api/templates/settings.html | 87 +++++++++++++++---- 4 files changed, 198 insertions(+), 30 deletions(-) diff --git a/server_api/app.py b/server_api/app.py index e764253..fe87f12 100644 --- a/server_api/app.py +++ b/server_api/app.py @@ -17,8 +17,14 @@ class Log(db.Model): 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) - relay_status = db.Column(db.String(4), default='off,off,off,off') # Status of 4 relays - input_status = db.Column(db.String(4), default='off,off,off,off') # Status of 4 inputs + 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') # Create the database if it does not exist if not os.path.exists('instance/logs.db'): @@ -41,20 +47,54 @@ def log_message(): hostname = data.get('hostname') ip_address = data.get('ip_address') message = data.get('message') - relay_status = data.get('relay_status', 'off,off,off,off') - input_status = data.get('input_status', 'off,off,off,off') + + # 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]) - 1 + relay_index = int(parts[1]) action = parts[3].lower() - relay_status_list = relay_status.split(',') - relay_status_list[relay_index] = action - relay_status = ','.join(relay_status_list) + 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 - new_log = Log(hostname=hostname, ip_address=ip_address, message=message, relay_status=relay_status, input_status=input_status) + # 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) db.session.commit() return jsonify({'status': 'success', 'message': 'Log saved'}), 201 @@ -97,12 +137,26 @@ def view_board(hostname): input_status = ['off', 'off', 'off', 'off'] if logs: latest_log = logs[0] - relay_status = latest_log.relay_status.split(',') - input_status = latest_log.input_status.split(',') + 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 - 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) + # 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): @@ -139,7 +193,21 @@ def control_relay(hostname, relay, action): @app.route('/settings', methods=['GET']) def settings(): cleanup_time = app.config.get('CLEANUP_TIME', 24) - return render_template('settings.html', cleanup_time=cleanup_time) + mir_ip = app.config.get('MIR_IP', '') + mir_user = app.config.get('MIR_USER', '') + mir_password = app.config.get('MIR_PASSWORD', '') + boards = Log.query.with_entities(Log.hostname).distinct().all() + board_settings = [] + for board in boards: + latest_log = Log.query.filter_by(hostname=board.hostname).order_by(Log.timestamp.desc()).first() + board_settings.append({ + 'hostname': board.hostname, + 'input1': latest_log.input1_status if latest_log else 'off', + 'input2': latest_log.input2_status if latest_log else 'off', + 'input3': latest_log.input3_status if latest_log else 'off', + 'input4': latest_log.input4_status if latest_log else 'off' + }) + return render_template('settings.html', cleanup_time=cleanup_time, mir_ip=mir_ip, mir_user=mir_user, mir_password=mir_password, boards=board_settings) @app.route('/set_cleanup_time', methods=['POST']) def set_cleanup_time(): @@ -151,6 +219,41 @@ def set_cleanup_time(): 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') + if mir_ip and mir_user and mir_password: + app.config['MIR_IP'] = mir_ip + app.config['MIR_USER'] = mir_user + app.config['MIR_PASSWORD'] = mir_password + 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): + input1 = request.form.get('input1') + input2 = request.form.get('input2') + input3 = request.form.get('input3') + input4 = request.form.get('input4') + if input1 and input2 and input3 and input4: + latest_log = Log.query.filter_by(hostname=hostname).order_by(Log.timestamp.desc()).first() + if latest_log: + latest_log.input1_status = input1 + latest_log.input2_status = input2 + latest_log.input3_status = input3 + latest_log.input4_status = input4 + db.session.commit() + flash('Board settings updated successfully!', 'success') + else: + flash('No logs found for the specified board.', 'danger') + 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(): diff --git a/server_api/instance/logs.db b/server_api/instance/logs.db index c9446e98d6c2854ab4dd6d18ac59db77f4c0c787..1ed704e33740e9845918f9a5904a5c9e94ef6496 100644 GIT binary patch literal 65536 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 X0DzY+WjoPJD4Y{^LPS`}+MWLfq<_4z literal 20480 zcmeI2U2I%O6~}jN?|QxdDg+uz`M8C)iJRnRzVFf{&ddwXFZKE^+1T0LD*B16-B+vEYv+=rR;%^!YT)(GQ^%XJcN+NL zx2dOBOS|=He5yY4W({A}Q(2(0KxKi-0+j_S3se@UEKpgXvOr~l$^w-I?y&{FK3X3? ze(YHN8(aNi?S{Ltvx%>fg_Xtm)x~6W{`}Hnf^Q@z+Y`O@h`aOK4 zUu@k>p2lCcdhzn2hwr)@#h0(+`~6#G=W5RXsJDIdR)6r1o?E$id4Abb&+5CyxS8KE1pVc#eoY|UbPXBQFiK*A8_NHbge>{1v{YJaro}PGZ;;Gi_ ztuM9O<3AdIy!of*c5|}vT4Q1C&tqR1yMOd&qst?I9r@bG1NEQQFV+5n?%vzuPPA&r zAJOxtPA%@;l!E6X<;JEgmHPZnvAdr1_L7ZT+ueR|XS=t(DVWe_m^{OI!fc0|j!>!0 z@Z>ui8>ip(de>Y3ZTDylMt3wYiYudneuJiaBo5=%Fw1jF&BHMm<<*Ex3rk@>8Ha%y zp}EdkV5OkxJ`sbF`)Zi5MhE={P4`e725PvGQrn>4pxI44h7X$1E52ZtsHA^uciXKe zS1w!#rnBtuEHyIEtO$Ayn#IRG$XGaeuU5=hUs8=sBYY5r@%l9=5((z$J2Op|Kr@7Ib^T0Ymvv`2eQ9=gjP$`3@4BI)-FhGagFl7ke#XblZ17GNvO1v>>)5mS%T?fRy{?q2ULFrv{k<6<2m!1f&cu z20>UVa>U4lFYAsIkfXyrhU_IrOv#NR-2YnBL(aaJm@?RwJwv6L!Ol-juZCyb<-Y7O zGBuge84>ZVjS*7HK{+gi>r`orNgS-ept&|mFv{6AfPu}0b8N~%r$KXVgkY2o>@b7D zNg7N0uX@aoAZf*rct1&`c|QMtwDxnK|BIR5&#X`XXZm~7XQzHURZPA)`Q6FW?ccT6 zCf=HOb>d9xH?8aA{~rI&_+!mqHD7A{qw(#=iLqafT^s%T=(k269{I({)%xG+->g4` z&hE+ME=L$h>W;`##&ZOd!Qci>w-kdxx^2>&SyV29euJjF6o(Newb(|;koD6s7^K@K z&2p`&Md&WZVW37ME+T6b&&FVoP8igWa?Xe~;+~1aKn>5B%%~}FAqInV0vN=_lGwD| zVjKo)n9T(ze7$=*27`107;Oy_$}(IXlu<6kVT4YtvY62X=6nQ3Af3>@v}37Kf}4-S zK#h`IQTwnv7lT1Mp;KwBCY{YwaTutPBG1Sv@MH`Ik-kQ-haxgHp`VDuK#efkk{iL} zF&ISpz(_3Nd$(+VZ0iV3qzC?9E-sq4p6Ddt;i`gXX7wXgZvev zLl`ZMz;MI?zzD|3aN|rI25JyLBNimooQ}aD4gf}Duc5}~R2;^u5h#>Xq2OaN7{mc8 zRVeyM%5BcYVW387j!8g_&B+)H;s9Vkp|Sy=|3BDRI6D9Tb^GOseS zcBsmBcY}bG75ebl#6uWPn(3{r>j+3$q8|X^HkGIg<=skS8<9p0#T00VFXjiYj9{%${2J@rYHzRIUWNLP~k(` z+Xb|P%O62-l19MioRfssheEpEj!E2*(rjljU91mpDC=5sL^WXu_A&DKyEv%3<9ff_E5np0C?IR=BuIgYnc&=Q1){x1&@ BH(>w( diff --git a/server_api/templates/board.html b/server_api/templates/board.html index 5ccca9f..c08677f 100644 --- a/server_api/templates/board.html +++ b/server_api/templates/board.html @@ -118,7 +118,7 @@
-
+

Logs

@@ -139,6 +139,16 @@ {% endfor %}
+
+

MIR Server Status

+
+
+
Input Mission Status
+

Status: Unknown

+

Last Input Triggered: None

+
+
+