Răsfoiți Sursa

inital commit

Tuan Nguyen 1 an în urmă
părinte
comite
10b7f75eb3

+ 14 - 19
dune_weaver_flask/app.py

@@ -125,7 +125,7 @@ def stop_execution():
 @app.route('/send_home', methods=['POST'])
 def send_home():
     try:
-        serial_manager.send_command("HOME", ack="HOMED")
+        serial_manager.home()
         return jsonify({'success': True})
     except Exception as e:
         logger.error(f"Failed to send home command: {str(e)}")
@@ -162,14 +162,15 @@ def delete_theta_rho_file():
 
 @app.route('/move_to_center', methods=['POST'])
 def move_to_center():
+    global current_theta
     try:
         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
 
         logger.info("Moving device to center position")
-        coordinates = [(0, 0)]
-        serial_manager.send_coordinate_batch(coordinates)
+        pattern_manager.reset_theta()
+        pattern_manager.interpolate_path(0, 0)
         return jsonify({"success": True})
     except Exception as e:
         logger.error(f"Failed to move to center: {str(e)}")
@@ -177,14 +178,13 @@ def move_to_center():
 
 @app.route('/move_to_perimeter', methods=['POST'])
 def move_to_perimeter():
+    global current_theta
     try:
         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
-
-        MAX_RHO = 1
-        coordinates = [(0, MAX_RHO)]
-        serial_manager.send_coordinate_batch(coordinates)
+        pattern_manager.reset_theta()
+        pattern_manager.interpolate_path(0,1)
         return jsonify({"success": True})
     except Exception as e:
         logger.error(f"Failed to move to perimeter: {str(e)}")
@@ -225,7 +225,7 @@ def send_coordinate():
             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)])
+        pattern_manager.interpolate_path(theta, rho)
         return jsonify({"success": True})
     except Exception as e:
         logger.error(f"Failed to send coordinate: {str(e)}")
@@ -377,24 +377,19 @@ def run_playlist():
 # Firmware endpoints
 @app.route('/set_speed', methods=['POST'])
 def set_speed():
-    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
-
     try:
         data = request.json
-        speed = data.get('speed')
+        new_speed = data.get('speed')
 
-        if speed is None:
+        if new_speed is None:
             logger.warning("Set speed request received without speed value")
             return jsonify({"success": False, "error": "Speed is required"}), 400
 
-        if not isinstance(speed, (int, float)) or speed <= 0:
-            logger.warning(f"Invalid speed value received: {speed}")
+        if not isinstance(new_speed, (int, float)) or new_speed <= 0:
+            logger.warning(f"Invalid speed value received: {new_speed}")
             return jsonify({"success": False, "error": "Invalid speed value"}), 400
-
-        serial_manager.send_command(f"SET_SPEED {speed}", ack="SPEED_SET")
-        return jsonify({"success": True, "speed": speed})
+        pattern_manager.set_speed(new_speed)
+        return jsonify({"success": True, "speed": new_speed})
     except Exception as e:
         logger.error(f"Failed to set speed: {str(e)}")
         return jsonify({"success": False, "error": str(e)}), 500

+ 40 - 61
dune_weaver_flask/modules/core/pattern_manager.py

@@ -6,6 +6,7 @@ import logging
 from datetime import datetime
 from tqdm import tqdm
 from dune_weaver_flask.modules.serial import serial_manager
+from math import pi
 
 # Configure logging
 logger = logging.getLogger(__name__)
@@ -28,6 +29,8 @@ execution_progress = None
 current_playing_index = None
 current_playlist = None
 is_clearing = False
+current_theta = current_rho = 0
+speed = 800
 
 def list_theta_rho_files():
     files = []
@@ -71,7 +74,7 @@ def get_clear_pattern_file(clear_pattern_mode, path=None):
     """Return a .thr file path based on pattern_name."""
     if not clear_pattern_mode or clear_pattern_mode == 'none':
         return
-    print("Clear pattern mode: " + clear_pattern_mode)
+    logger.info("Clear pattern mode: " + clear_pattern_mode)
     if clear_pattern_mode == "random":
         return random.choice(list(CLEAR_PATTERNS.values()))
 
@@ -120,10 +123,31 @@ def wait_for_start_time(schedule_hours):
             break
         else:
             time.sleep(30)
+            
+def interpolate_path(theta, rho, speed=speed):
+    global current_theta, current_rho
+    delta_theta = current_theta - theta
+    delta_rho = current_rho - rho
+    x = (theta - current_theta)/(2*pi)*100
+    y = (rho-current_rho) * 100
+    offset = x/100 * 27.8260869565
+    y += offset
+    serial_manager.send_grbl_coordinates(x, y, speed)
+    current_theta = theta
+    current_rho = rho
+    
+def reset_theta():
+    logger.info('Resetting Theta')
+    global current_theta
+    current_theta = 0
+
+def set_speed(new_speed):
+    global speed
+    speed = new_speed
 
 def run_theta_rho_file(file_path, schedule_hours=None):
     """Run a theta-rho file by sending data in optimized batches with tqdm ETA tracking."""
-    global current_playing_file, execution_progress, stop_requested
+    global current_playing_file, execution_progress, stop_requested, current_theta, current_rho, speed
     coordinates = parse_theta_rho_file(file_path)
     total_coordinates = len(coordinates)
 
@@ -134,7 +158,6 @@ def run_theta_rho_file(file_path, schedule_hours=None):
         return
 
     execution_progress = (0, total_coordinates, None)
-    batch_size = 10
 
     stop_actions()
     with serial_manager.serial_lock:
@@ -142,47 +165,23 @@ def run_theta_rho_file(file_path, schedule_hours=None):
         execution_progress = (0, 0, None)
         stop_requested = False
         logger.info(f"Starting pattern execution: {file_path}")
-        with tqdm(total=total_coordinates, unit="coords", desc=f"Executing Pattern {file_path}", dynamic_ncols=True, disable=None) as pbar:
-            for i in range(0, total_coordinates, batch_size):
-                if stop_requested:
-                    logger.info("Execution stopped by user after completing the current batch")
-                    break
-
-                with pause_condition:
-                    while pause_requested:
-                        logger.info("Execution paused...")
-                        pause_condition.wait()
-
-                batch = coordinates[i:i + batch_size]
+        logger.info(f"t: {current_theta}, r: {current_rho}")
+        reset_theta()
+        for coordinate in tqdm(coordinates):
+            theta, rho = coordinate
+            if stop_requested:
+                logger.info("Execution stopped by user after completing the current batch")
+                break
 
-                if i == 0:
-                    serial_manager.send_coordinate_batch(batch)
-                    execution_progress = (i + batch_size, total_coordinates, None)
-                    pbar.update(batch_size)
-                    continue
+            with pause_condition:
+                while pause_requested:
+                    logger.info("Execution paused...")
+                    pause_condition.wait()
 
-                while True:
-                    schedule_checker(schedule_hours)
-                    if serial_manager.ser.in_waiting > 0:
-                        response = serial_manager.ser.readline().decode().strip()
-                        if response == "R":
-                            serial_manager.send_coordinate_batch(batch)
-                            pbar.update(batch_size)
-                            estimated_remaining_time = pbar.format_dict['elapsed'] / (i + batch_size) * (total_coordinates - (i + batch_size))
-                            execution_progress = (i + batch_size, total_coordinates, estimated_remaining_time)
-                            break
-                        elif response != "IGNORED: FINISHED" and response.startswith("IGNORE"):
-                            logger.warning(f"Received IGNORE response: {response}")
-                            prev_start = max(0, i - batch_size)
-                            prev_end = i
-                            previous_batch = coordinates[prev_start:prev_end]
-                            serial_manager.send_coordinate_batch(previous_batch)
-                            break
-                        else:
-                            logger.debug(f"Arduino response: {response}")
+            schedule_checker(schedule_hours)
+            interpolate_path(theta, rho, speed)
 
-        reset_theta()
-        serial_manager.ser.write("FINISHED\n".encode())
+        serial_manager.check_idle()
 
     current_playing_file = None
     execution_progress = None
@@ -241,28 +240,8 @@ def run_theta_rho_files(file_paths, pause_time=0, clear_pattern=None, run_mode="
         else:
             logger.info("Playlist completed")
             break
-
-    reset_theta()
-    with serial_manager.serial_lock:
-        serial_manager.ser.write("FINISHED\n".encode())
-        
     logger.info("All requested patterns completed (or stopped)")
 
-def reset_theta():
-    """Reset theta on the Arduino."""
-    logger.debug("Resetting theta on Arduino")
-    with serial_manager.serial_lock:
-        serial_manager.ser.write("RESET_THETA\n".encode())
-        while True:
-            with serial_manager.serial_lock:
-                if serial_manager.ser.in_waiting > 0:
-                    response = serial_manager.ser.readline().decode().strip()
-                    logger.debug(f"Arduino response: {response}")
-                    if response == "THETA_RESET":
-                        logger.info("Theta successfully reset.")
-                        break
-            time.sleep(0.5)
-
 def stop_actions():
     """Stop all current pattern execution."""
     global pause_requested, stop_requested, current_playing_index, current_playlist, is_clearing, current_playing_file, execution_progress

+ 51 - 29
dune_weaver_flask/modules/serial/serial_manager.py

@@ -41,7 +41,7 @@ def connect_to_serial(port=None, baudrate=115200):
                 ser.close()
             ser = serial.Serial(port, baudrate, timeout=2)
             ser_port = port
-
+        home()
         logger.info(f"Connected to serial port: {port}")
         time.sleep(2)  # Allow time for the connection to establish
 
@@ -84,34 +84,6 @@ def restart_serial(port, baudrate=115200):
     disconnect_serial()
     return connect_to_serial(port, baudrate)
 
-def send_coordinate_batch(coordinates):
-    """Send a batch of theta-rho pairs to the Arduino."""
-    batch_str = ";".join(f"{theta:.5f},{rho:.5f}" for theta, rho in coordinates) + ";\n"
-    with serial_lock:
-        ser.write(batch_str.encode())
-        logger.debug(f"Sent coordinate batch: {batch_str.strip()}")
-
-def send_command(command, ack=None):
-    """Send a single command to the Arduino."""
-    timeout = 10  # Timeout in seconds
-    start_time = time.time()
-    with serial_lock:
-        ser.write(f"{command}\n".encode())
-        logger.debug(f"Sent command: {command}")
-
-        # Wait for "R" acknowledgment from Arduino
-        while True:
-            if time.time() - start_time > timeout:
-                logger.error(f"Timeout: No acknowledgment received within {timeout} seconds")
-                break  # Exit loop after timeout
-            with serial_lock:
-                if ser.in_waiting > 0:
-                    response = ser.readline().decode().strip()
-                    logger.debug(f"Arduino response: {response}")
-                    if response == ack:
-                        logger.debug("Command execution completed")
-                        break
-
 def is_connected():
     """Check if serial connection is established and open."""
     return ser is not None and ser.is_open
@@ -119,3 +91,53 @@ def is_connected():
 def get_port():
     """Get the current serial port."""
     return ser_port
+
+
+def send_grbl_coordinates(x, y, speed=600, max_retries=20, retry_interval=0.5):
+    """Send G-code command to FluidNC and retry every 0.5s if no 'ok' is received."""
+    attempt = 0  # Retry counter
+
+    while attempt < max_retries:
+        with serial_lock:
+            gcode = f"$J=G91 G21 X{x} Y{y} F{speed}"
+            ser.write(f"{gcode}\n".encode())
+            ser.flush()
+            logger.debug(f"Sent command (Attempt {attempt+1}/{max_retries}): {gcode}")
+
+            # Wait for response
+            while True:
+                if ser.in_waiting > 0:
+                    response = ser.readline().decode().strip()
+                    logger.debug(f"Response: {response}")
+                    if response.lower() == "ok":
+                        logger.debug("Command execution completed")
+                        return  # Exit function if 'ok' is received
+
+        # Wait before retrying
+        attempt += 1
+        logger.warning(f"Attempt {attempt}: No 'ok' received, retrying in {retry_interval}s...")
+        time.sleep(retry_interval)
+
+    logger.error(f"Failed to receive 'ok' after {max_retries} attempts. Giving up.")
+
+def home():
+    logger.info("Homing")
+    send_grbl_coordinates(0, -110, 1000)
+    current_theta = current_rho = 0
+
+def check_idle():
+    """Continuously check if the machine is in the 'Idle' state."""
+    logger.info("Checking idle")
+    while True:
+        with serial_lock:
+            ser.write('?'.encode())  # Send status query
+            ser.flush()  # Ensure it's sent immediately
+
+            if ser.in_waiting > 0:
+                response = ser.readline().decode().strip()
+                logger.info(f"Response: {response}")
+                if "Idle" in response:
+                    logger.info("Tabble is idle")
+                    return True  # Exit function once 'Idle' is received
+
+        time.sleep(1)  # Wait before retrying