|
@@ -1,12 +1,25 @@
|
|
|
from flask import Flask, request, jsonify, render_template, send_from_directory
|
|
from flask import Flask, request, jsonify, render_template, send_from_directory
|
|
|
import atexit
|
|
import atexit
|
|
|
import os
|
|
import os
|
|
|
|
|
+import logging
|
|
|
from datetime import datetime
|
|
from datetime import datetime
|
|
|
from .modules.serial import serial_manager
|
|
from .modules.serial import serial_manager
|
|
|
from dune_weaver_flask.modules.core import pattern_manager
|
|
from dune_weaver_flask.modules.core import pattern_manager
|
|
|
from dune_weaver_flask.modules.core import playlist_manager
|
|
from dune_weaver_flask.modules.core import playlist_manager
|
|
|
from .modules.firmware import firmware_manager
|
|
from .modules.firmware import firmware_manager
|
|
|
|
|
|
|
|
|
|
+# Configure logging
|
|
|
|
|
+logging.basicConfig(
|
|
|
|
|
+ level=logging.INFO,
|
|
|
|
|
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
|
|
|
+ handlers=[
|
|
|
|
|
+ logging.StreamHandler(),
|
|
|
|
|
+ # disable file logging for now, to not gobble up resources
|
|
|
|
|
+ # logging.FileHandler('dune_weaver.log')
|
|
|
|
|
+ ]
|
|
|
|
|
+)
|
|
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
|
|
+
|
|
|
app = Flask(__name__)
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
# Flask API Endpoints
|
|
# Flask API Endpoints
|
|
@@ -16,42 +29,52 @@ def index():
|
|
|
|
|
|
|
|
@app.route('/list_serial_ports', methods=['GET'])
|
|
@app.route('/list_serial_ports', methods=['GET'])
|
|
|
def list_ports():
|
|
def list_ports():
|
|
|
|
|
+ logger.debug("Listing available serial ports")
|
|
|
return jsonify(serial_manager.list_serial_ports())
|
|
return jsonify(serial_manager.list_serial_ports())
|
|
|
|
|
|
|
|
@app.route('/connect_serial', methods=['POST'])
|
|
@app.route('/connect_serial', methods=['POST'])
|
|
|
def connect_serial():
|
|
def connect_serial():
|
|
|
port = request.json.get('port')
|
|
port = request.json.get('port')
|
|
|
if not port:
|
|
if not port:
|
|
|
|
|
+ logger.warning('Serial connection attempt without port specified')
|
|
|
return jsonify({'error': 'No port provided'}), 400
|
|
return jsonify({'error': 'No port provided'}), 400
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
serial_manager.connect_to_serial(port)
|
|
serial_manager.connect_to_serial(port)
|
|
|
|
|
+ logger.info(f'Successfully connected to serial port {port}')
|
|
|
return jsonify({'success': True})
|
|
return jsonify({'success': True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f'Failed to connect to serial port {port}: {str(e)}')
|
|
|
return jsonify({'error': str(e)}), 500
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/disconnect_serial', methods=['POST'])
|
|
@app.route('/disconnect_serial', methods=['POST'])
|
|
|
def disconnect():
|
|
def disconnect():
|
|
|
try:
|
|
try:
|
|
|
serial_manager.disconnect_serial()
|
|
serial_manager.disconnect_serial()
|
|
|
|
|
+ logger.info('Successfully disconnected from serial port')
|
|
|
return jsonify({'success': True})
|
|
return jsonify({'success': True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f'Failed to disconnect serial: {str(e)}')
|
|
|
return jsonify({'error': str(e)}), 500
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/restart_serial', methods=['POST'])
|
|
@app.route('/restart_serial', methods=['POST'])
|
|
|
def restart():
|
|
def restart():
|
|
|
port = request.json.get('port')
|
|
port = request.json.get('port')
|
|
|
if not port:
|
|
if not port:
|
|
|
|
|
+ logger.warning("Restart serial request received without port")
|
|
|
return jsonify({'error': 'No port provided'}), 400
|
|
return jsonify({'error': 'No port provided'}), 400
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
|
|
+ logger.info(f"Restarting serial connection on port {port}")
|
|
|
serial_manager.restart_serial(port)
|
|
serial_manager.restart_serial(port)
|
|
|
return jsonify({'success': True})
|
|
return jsonify({'success': True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Failed to restart serial on port {port}: {str(e)}")
|
|
|
return jsonify({'error': str(e)}), 500
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/list_theta_rho_files', methods=['GET'])
|
|
@app.route('/list_theta_rho_files', methods=['GET'])
|
|
|
def list_theta_rho_files():
|
|
def list_theta_rho_files():
|
|
|
|
|
+ logger.debug("Listing theta-rho files")
|
|
|
files = pattern_manager.list_theta_rho_files()
|
|
files = pattern_manager.list_theta_rho_files()
|
|
|
return jsonify(sorted(files))
|
|
return jsonify(sorted(files))
|
|
|
|
|
|
|
@@ -59,11 +82,16 @@ def list_theta_rho_files():
|
|
|
def upload_theta_rho():
|
|
def upload_theta_rho():
|
|
|
custom_patterns_dir = os.path.join(pattern_manager.THETA_RHO_DIR, 'custom_patterns')
|
|
custom_patterns_dir = os.path.join(pattern_manager.THETA_RHO_DIR, 'custom_patterns')
|
|
|
os.makedirs(custom_patterns_dir, exist_ok=True)
|
|
os.makedirs(custom_patterns_dir, exist_ok=True)
|
|
|
|
|
+ logger.debug(f'Ensuring custom patterns directory exists: {custom_patterns_dir}')
|
|
|
|
|
|
|
|
file = request.files['file']
|
|
file = request.files['file']
|
|
|
if file:
|
|
if file:
|
|
|
- file.save(os.path.join(custom_patterns_dir, file.filename))
|
|
|
|
|
|
|
+ file_path = os.path.join(custom_patterns_dir, file.filename)
|
|
|
|
|
+ file.save(file_path)
|
|
|
|
|
+ logger.info(f'Successfully uploaded theta-rho file: {file.filename}')
|
|
|
return jsonify({'success': True})
|
|
return jsonify({'success': True})
|
|
|
|
|
+
|
|
|
|
|
+ logger.warning('Upload theta-rho request received without file')
|
|
|
return jsonify({'success': False})
|
|
return jsonify({'success': False})
|
|
|
|
|
|
|
|
@app.route('/run_theta_rho', methods=['POST'])
|
|
@app.route('/run_theta_rho', methods=['POST'])
|
|
@@ -72,17 +100,21 @@ def run_theta_rho():
|
|
|
pre_execution = request.json.get('pre_execution')
|
|
pre_execution = request.json.get('pre_execution')
|
|
|
|
|
|
|
|
if not file_name:
|
|
if not file_name:
|
|
|
|
|
+ logger.warning('Run theta-rho request received without file name')
|
|
|
return jsonify({'error': 'No file name provided'}), 400
|
|
return jsonify({'error': 'No file name provided'}), 400
|
|
|
|
|
|
|
|
file_path = os.path.join(pattern_manager.THETA_RHO_DIR, file_name)
|
|
file_path = os.path.join(pattern_manager.THETA_RHO_DIR, file_name)
|
|
|
if not os.path.exists(file_path):
|
|
if not os.path.exists(file_path):
|
|
|
|
|
+ logger.error(f'Theta-rho file not found: {file_path}')
|
|
|
return jsonify({'error': 'File not found'}), 404
|
|
return jsonify({'error': 'File not found'}), 404
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
files_to_run = [file_path]
|
|
files_to_run = [file_path]
|
|
|
|
|
+ logger.info(f'Running theta-rho file: {file_name} with pre_execution={pre_execution}')
|
|
|
pattern_manager.run_theta_rho_files(files_to_run, clear_pattern=pre_execution)
|
|
pattern_manager.run_theta_rho_files(files_to_run, clear_pattern=pre_execution)
|
|
|
return jsonify({'success': True})
|
|
return jsonify({'success': True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f'Failed to run theta-rho file {file_name}: {str(e)}')
|
|
|
return jsonify({'error': str(e)}), 500
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/stop_execution', methods=['POST'])
|
|
@app.route('/stop_execution', methods=['POST'])
|
|
@@ -96,6 +128,7 @@ def send_home():
|
|
|
serial_manager.send_command("HOME")
|
|
serial_manager.send_command("HOME")
|
|
|
return jsonify({'success': True})
|
|
return jsonify({'success': True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Failed to send home command: {str(e)}")
|
|
|
return jsonify({'error': str(e)}), 500
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/run_theta_rho_file/<file_name>', methods=['POST'])
|
|
@app.route('/run_theta_rho_file/<file_name>', methods=['POST'])
|
|
@@ -111,34 +144,42 @@ def run_specific_theta_rho_file(file_name):
|
|
|
def delete_theta_rho_file():
|
|
def delete_theta_rho_file():
|
|
|
file_name = request.json.get('file_name')
|
|
file_name = request.json.get('file_name')
|
|
|
if not file_name:
|
|
if not file_name:
|
|
|
|
|
+ logger.warning("Delete theta-rho file request received without filename")
|
|
|
return jsonify({"success": False, "error": "No file name provided"}), 400
|
|
return jsonify({"success": False, "error": "No file name provided"}), 400
|
|
|
|
|
|
|
|
file_path = os.path.join(pattern_manager.THETA_RHO_DIR, file_name)
|
|
file_path = os.path.join(pattern_manager.THETA_RHO_DIR, file_name)
|
|
|
if not os.path.exists(file_path):
|
|
if not os.path.exists(file_path):
|
|
|
|
|
+ logger.error(f"Attempted to delete non-existent file: {file_path}")
|
|
|
return jsonify({"success": False, "error": "File not found"}), 404
|
|
return jsonify({"success": False, "error": "File not found"}), 404
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
os.remove(file_path)
|
|
os.remove(file_path)
|
|
|
|
|
+ logger.info(f"Successfully deleted theta-rho file: {file_name}")
|
|
|
return jsonify({"success": True})
|
|
return jsonify({"success": True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Failed to delete theta-rho file {file_name}: {str(e)}")
|
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/move_to_center', methods=['POST'])
|
|
@app.route('/move_to_center', methods=['POST'])
|
|
|
def move_to_center():
|
|
def move_to_center():
|
|
|
try:
|
|
try:
|
|
|
if not serial_manager.is_connected():
|
|
if not serial_manager.is_connected():
|
|
|
|
|
+ logger.warning("Attempted to move to center without serial connection")
|
|
|
return jsonify({"success": False, "error": "Serial connection not established"}), 400
|
|
return jsonify({"success": False, "error": "Serial connection not established"}), 400
|
|
|
|
|
|
|
|
|
|
+ logger.info("Moving device to center position")
|
|
|
coordinates = [(0, 0)]
|
|
coordinates = [(0, 0)]
|
|
|
serial_manager.send_coordinate_batch(coordinates)
|
|
serial_manager.send_coordinate_batch(coordinates)
|
|
|
return jsonify({"success": True})
|
|
return jsonify({"success": True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Failed to move to center: {str(e)}")
|
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/move_to_perimeter', methods=['POST'])
|
|
@app.route('/move_to_perimeter', methods=['POST'])
|
|
|
def move_to_perimeter():
|
|
def move_to_perimeter():
|
|
|
try:
|
|
try:
|
|
|
if not serial_manager.is_connected():
|
|
if not serial_manager.is_connected():
|
|
|
|
|
+ logger.warning("Attempted to move to perimeter without serial connection")
|
|
|
return jsonify({"success": False, "error": "Serial connection not established"}), 400
|
|
return jsonify({"success": False, "error": "Serial connection not established"}), 400
|
|
|
|
|
|
|
|
MAX_RHO = 1
|
|
MAX_RHO = 1
|
|
@@ -146,27 +187,32 @@ def move_to_perimeter():
|
|
|
serial_manager.send_coordinate_batch(coordinates)
|
|
serial_manager.send_coordinate_batch(coordinates)
|
|
|
return jsonify({"success": True})
|
|
return jsonify({"success": True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Failed to move to perimeter: {str(e)}")
|
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/preview_thr', methods=['POST'])
|
|
@app.route('/preview_thr', methods=['POST'])
|
|
|
def preview_thr():
|
|
def preview_thr():
|
|
|
file_name = request.json.get('file_name')
|
|
file_name = request.json.get('file_name')
|
|
|
if not file_name:
|
|
if not file_name:
|
|
|
|
|
+ logger.warning("Preview theta-rho request received without filename")
|
|
|
return jsonify({'error': 'No file name provided'}), 400
|
|
return jsonify({'error': 'No file name provided'}), 400
|
|
|
|
|
|
|
|
file_path = os.path.join(pattern_manager.THETA_RHO_DIR, file_name)
|
|
file_path = os.path.join(pattern_manager.THETA_RHO_DIR, file_name)
|
|
|
if not os.path.exists(file_path):
|
|
if not os.path.exists(file_path):
|
|
|
|
|
+ logger.error(f"Attempted to preview non-existent file: {file_path}")
|
|
|
return jsonify({'error': 'File not found'}), 404
|
|
return jsonify({'error': 'File not found'}), 404
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
coordinates = pattern_manager.parse_theta_rho_file(file_path)
|
|
coordinates = pattern_manager.parse_theta_rho_file(file_path)
|
|
|
return jsonify({'success': True, 'coordinates': coordinates})
|
|
return jsonify({'success': True, 'coordinates': coordinates})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Failed to generate preview for {file_name}: {str(e)}")
|
|
|
return jsonify({'error': str(e)}), 500
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/send_coordinate', methods=['POST'])
|
|
@app.route('/send_coordinate', methods=['POST'])
|
|
|
def send_coordinate():
|
|
def send_coordinate():
|
|
|
if not serial_manager.is_connected():
|
|
if not serial_manager.is_connected():
|
|
|
|
|
+ logger.warning("Attempted to send coordinate without serial connection")
|
|
|
return jsonify({"success": False, "error": "Serial connection not established"}), 400
|
|
return jsonify({"success": False, "error": "Serial connection not established"}), 400
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
@@ -175,11 +221,14 @@ def send_coordinate():
|
|
|
rho = data.get('rho')
|
|
rho = data.get('rho')
|
|
|
|
|
|
|
|
if theta is None or rho is None:
|
|
if theta is None or rho is None:
|
|
|
|
|
+ logger.warning("Send coordinate request missing theta or rho values")
|
|
|
return jsonify({"success": False, "error": "Theta and Rho are required"}), 400
|
|
return jsonify({"success": False, "error": "Theta and Rho are required"}), 400
|
|
|
|
|
|
|
|
|
|
+ logger.debug(f"Sending coordinate: theta={theta}, rho={rho}")
|
|
|
serial_manager.send_coordinate_batch([(theta, rho)])
|
|
serial_manager.send_coordinate_batch([(theta, rho)])
|
|
|
return jsonify({"success": True})
|
|
return jsonify({"success": True})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Failed to send coordinate: {str(e)}")
|
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/download/<filename>', methods=['GET'])
|
|
@app.route('/download/<filename>', methods=['GET'])
|
|
@@ -188,13 +237,17 @@ def download_file(filename):
|
|
|
|
|
|
|
|
@app.route('/serial_status', methods=['GET'])
|
|
@app.route('/serial_status', methods=['GET'])
|
|
|
def serial_status():
|
|
def serial_status():
|
|
|
|
|
+ connected = serial_manager.is_connected()
|
|
|
|
|
+ port = serial_manager.get_port()
|
|
|
|
|
+ logger.debug(f"Serial status check - connected: {connected}, port: {port}")
|
|
|
return jsonify({
|
|
return jsonify({
|
|
|
- 'connected': serial_manager.is_connected(),
|
|
|
|
|
- 'port': serial_manager.get_port()
|
|
|
|
|
|
|
+ 'connected': connected,
|
|
|
|
|
+ 'port': port
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
@app.route('/pause_execution', methods=['POST'])
|
|
@app.route('/pause_execution', methods=['POST'])
|
|
|
def pause_execution():
|
|
def pause_execution():
|
|
|
|
|
+ logger.info("Pausing pattern execution")
|
|
|
pattern_manager.pause_requested = True
|
|
pattern_manager.pause_requested = True
|
|
|
return jsonify({'success': True, 'message': 'Execution paused'})
|
|
return jsonify({'success': True, 'message': 'Execution paused'})
|
|
|
|
|
|
|
@@ -204,6 +257,7 @@ def get_status():
|
|
|
|
|
|
|
|
@app.route('/resume_execution', methods=['POST'])
|
|
@app.route('/resume_execution', methods=['POST'])
|
|
|
def resume_execution():
|
|
def resume_execution():
|
|
|
|
|
+ logger.info("Resuming pattern execution")
|
|
|
with pattern_manager.pause_condition:
|
|
with pattern_manager.pause_condition:
|
|
|
pattern_manager.pause_requested = False
|
|
pattern_manager.pause_requested = False
|
|
|
pattern_manager.pause_condition.notify_all()
|
|
pattern_manager.pause_condition.notify_all()
|
|
@@ -278,6 +332,7 @@ def add_to_playlist():
|
|
|
def run_playlist():
|
|
def run_playlist():
|
|
|
data = request.get_json()
|
|
data = request.get_json()
|
|
|
if not data or "playlist_name" not in data:
|
|
if not data or "playlist_name" not in data:
|
|
|
|
|
+ logger.warning("Run playlist request received without playlist name")
|
|
|
return jsonify({"success": False, "error": "Missing 'playlist_name' field"}), 400
|
|
return jsonify({"success": False, "error": "Missing 'playlist_name' field"}), 400
|
|
|
|
|
|
|
|
playlist_name = data["playlist_name"]
|
|
playlist_name = data["playlist_name"]
|
|
@@ -295,11 +350,15 @@ def run_playlist():
|
|
|
start_time_obj = datetime.strptime(start_time, "%H:%M").time()
|
|
start_time_obj = datetime.strptime(start_time, "%H:%M").time()
|
|
|
end_time_obj = datetime.strptime(end_time, "%H:%M").time()
|
|
end_time_obj = datetime.strptime(end_time, "%H:%M").time()
|
|
|
if start_time_obj >= end_time_obj:
|
|
if start_time_obj >= end_time_obj:
|
|
|
|
|
+ logger.error(f"Invalid schedule times: start_time {start_time} >= end_time {end_time}")
|
|
|
return jsonify({"success": False, "error": "'start_time' must be earlier than 'end_time'"}), 400
|
|
return jsonify({"success": False, "error": "'start_time' must be earlier than 'end_time'"}), 400
|
|
|
schedule_hours = (start_time_obj, end_time_obj)
|
|
schedule_hours = (start_time_obj, end_time_obj)
|
|
|
|
|
+ logger.info(f"Playlist {playlist_name} scheduled to run between {start_time} and {end_time}")
|
|
|
except ValueError:
|
|
except ValueError:
|
|
|
|
|
+ logger.error(f"Invalid time format provided: start_time={start_time}, end_time={end_time}")
|
|
|
return jsonify({"success": False, "error": "Invalid time format. Use HH:MM (e.g., '09:30')"}), 400
|
|
return jsonify({"success": False, "error": "Invalid time format. Use HH:MM (e.g., '09:30')"}), 400
|
|
|
|
|
|
|
|
|
|
+ logger.info(f"Starting playlist '{playlist_name}' with mode={run_mode}, shuffle={shuffle}")
|
|
|
success, message = playlist_manager.run_playlist(
|
|
success, message = playlist_manager.run_playlist(
|
|
|
playlist_name,
|
|
playlist_name,
|
|
|
pause_time=pause_time,
|
|
pause_time=pause_time,
|
|
@@ -310,13 +369,16 @@ def run_playlist():
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
if not success:
|
|
if not success:
|
|
|
|
|
+ logger.error(f"Failed to run playlist '{playlist_name}': {message}")
|
|
|
return jsonify({"success": False, "error": message}), 500
|
|
return jsonify({"success": False, "error": message}), 500
|
|
|
|
|
+
|
|
|
return jsonify({"success": True, "message": message})
|
|
return jsonify({"success": True, "message": message})
|
|
|
|
|
|
|
|
# Firmware endpoints
|
|
# Firmware endpoints
|
|
|
@app.route('/set_speed', methods=['POST'])
|
|
@app.route('/set_speed', methods=['POST'])
|
|
|
def set_speed():
|
|
def set_speed():
|
|
|
if not serial_manager.is_connected():
|
|
if not serial_manager.is_connected():
|
|
|
|
|
+ logger.warning("Attempted to set speed without serial connection")
|
|
|
return jsonify({"success": False, "error": "Serial connection not established"}), 400
|
|
return jsonify({"success": False, "error": "Serial connection not established"}), 400
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
@@ -324,19 +386,23 @@ def set_speed():
|
|
|
speed = data.get('speed')
|
|
speed = data.get('speed')
|
|
|
|
|
|
|
|
if speed is None:
|
|
if speed is None:
|
|
|
|
|
+ logger.warning("Set speed request received without speed value")
|
|
|
return jsonify({"success": False, "error": "Speed is required"}), 400
|
|
return jsonify({"success": False, "error": "Speed is required"}), 400
|
|
|
|
|
|
|
|
if not isinstance(speed, (int, float)) or speed <= 0:
|
|
if not isinstance(speed, (int, float)) or speed <= 0:
|
|
|
|
|
+ logger.warning(f"Invalid speed value received: {speed}")
|
|
|
return jsonify({"success": False, "error": "Invalid speed value"}), 400
|
|
return jsonify({"success": False, "error": "Invalid speed value"}), 400
|
|
|
|
|
|
|
|
serial_manager.send_command(f"SET_SPEED {speed}")
|
|
serial_manager.send_command(f"SET_SPEED {speed}")
|
|
|
return jsonify({"success": True, "speed": speed})
|
|
return jsonify({"success": True, "speed": speed})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Failed to set speed: {str(e)}")
|
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/get_firmware_info', methods=['GET', 'POST'])
|
|
@app.route('/get_firmware_info', methods=['GET', 'POST'])
|
|
|
def get_firmware_info():
|
|
def get_firmware_info():
|
|
|
if not serial_manager.is_connected():
|
|
if not serial_manager.is_connected():
|
|
|
|
|
+ logger.warning("Attempted to get firmware info without serial connection")
|
|
|
return jsonify({"success": False, "error": "Arduino not connected or serial port not open"}), 400
|
|
return jsonify({"success": False, "error": "Arduino not connected or serial port not open"}), 400
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
@@ -347,22 +413,29 @@ def get_firmware_info():
|
|
|
success, result = firmware_manager.get_firmware_info()
|
|
success, result = firmware_manager.get_firmware_info()
|
|
|
|
|
|
|
|
if not success:
|
|
if not success:
|
|
|
|
|
+ logger.error(f"Failed to get firmware info: {result}")
|
|
|
return jsonify({"success": False, "error": result}), 500
|
|
return jsonify({"success": False, "error": result}), 500
|
|
|
return jsonify({"success": True, **result})
|
|
return jsonify({"success": True, **result})
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.error(f"Unexpected error while getting firmware info: {str(e)}")
|
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/flash_firmware', methods=['POST'])
|
|
@app.route('/flash_firmware', methods=['POST'])
|
|
|
def flash_firmware():
|
|
def flash_firmware():
|
|
|
try:
|
|
try:
|
|
|
motor_type = request.json.get("motorType", None)
|
|
motor_type = request.json.get("motorType", None)
|
|
|
|
|
+ logger.info(f"Starting firmware flash for motor type: {motor_type}")
|
|
|
success, message = firmware_manager.flash_firmware(motor_type)
|
|
success, message = firmware_manager.flash_firmware(motor_type)
|
|
|
|
|
|
|
|
if not success:
|
|
if not success:
|
|
|
|
|
+ logger.error(f"Firmware flash failed: {message}")
|
|
|
return jsonify({"success": False, "error": message}), 500
|
|
return jsonify({"success": False, "error": message}), 500
|
|
|
|
|
+
|
|
|
|
|
+ logger.info("Firmware flash completed successfully")
|
|
|
return jsonify({"success": True, "message": message})
|
|
return jsonify({"success": True, "message": message})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.critical(f"Unexpected error during firmware flash: {str(e)}")
|
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
return jsonify({"success": False, "error": str(e)}), 500
|
|
|
|
|
|
|
|
@app.route('/check_software_update', methods=['GET'])
|
|
@app.route('/check_software_update', methods=['GET'])
|
|
@@ -372,11 +445,14 @@ def check_updates():
|
|
|
|
|
|
|
|
@app.route('/update_software', methods=['POST'])
|
|
@app.route('/update_software', methods=['POST'])
|
|
|
def update_software():
|
|
def update_software():
|
|
|
|
|
+ logger.info("Starting software update process")
|
|
|
success, error_message, error_log = firmware_manager.update_software()
|
|
success, error_message, error_log = firmware_manager.update_software()
|
|
|
|
|
|
|
|
if success:
|
|
if success:
|
|
|
|
|
+ logger.info("Software update completed successfully")
|
|
|
return jsonify({"success": True})
|
|
return jsonify({"success": True})
|
|
|
else:
|
|
else:
|
|
|
|
|
+ logger.error(f"Software update failed: {error_message}\nDetails: {error_log}")
|
|
|
return jsonify({
|
|
return jsonify({
|
|
|
"success": False,
|
|
"success": False,
|
|
|
"error": error_message,
|
|
"error": error_message,
|
|
@@ -385,19 +461,25 @@ def update_software():
|
|
|
|
|
|
|
|
def on_exit():
|
|
def on_exit():
|
|
|
"""Function to execute on application shutdown."""
|
|
"""Function to execute on application shutdown."""
|
|
|
- print("Shutting down the application...")
|
|
|
|
|
pattern_manager.stop_actions()
|
|
pattern_manager.stop_actions()
|
|
|
- print("Execution stopped and resources cleaned up.")
|
|
|
|
|
|
|
|
|
|
# Register the on_exit function
|
|
# Register the on_exit function
|
|
|
atexit.register(on_exit)
|
|
atexit.register(on_exit)
|
|
|
|
|
|
|
|
def entrypoint():
|
|
def entrypoint():
|
|
|
|
|
+ logger.info("Starting Dune Weaver application...")
|
|
|
# Auto-connect to serial
|
|
# Auto-connect to serial
|
|
|
- serial_manager.connect_to_serial()
|
|
|
|
|
try:
|
|
try:
|
|
|
|
|
+ serial_manager.connect_to_serial()
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.warning(f"Failed to auto-connect to serial port: {str(e)}")
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ logger.info("Starting Flask server on port 8080...")
|
|
|
app.run(debug=False, host='0.0.0.0', port=8080)
|
|
app.run(debug=False, host='0.0.0.0', port=8080)
|
|
|
except KeyboardInterrupt:
|
|
except KeyboardInterrupt:
|
|
|
- print("Keyboard interrupt received. Shutting down.")
|
|
|
|
|
|
|
+ logger.info("Keyboard interrupt received. Shutting down.")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.critical(f"Unexpected error during server startup: {str(e)}")
|
|
|
finally:
|
|
finally:
|
|
|
on_exit()
|
|
on_exit()
|