tuanchris hace 3 meses
padre
commit
1a52ffedf4

+ 4 - 2
modules/core/pattern_manager.py

@@ -681,8 +681,10 @@ async def run_theta_rho_file(file_path, is_playlist=False):
         
         start_time = time.time()
 
-        # Start ball tracking if mode is "playing_only"
+        # Check if ball tracking should be active during playback
         ball_tracking_active = False
+        logger.info(f"Ball tracking mode: {state.ball_tracking_mode}, manager exists: {state.ball_tracking_manager is not None}")
+
         if state.ball_tracking_mode == "playing_only" and state.ball_tracking_manager:
             logger.info("Starting ball tracking (playing_only mode)")
             state.ball_tracking_manager.start()
@@ -692,7 +694,7 @@ async def run_theta_rho_file(file_path, is_playlist=False):
         if state.led_controller:
             if ball_tracking_active:
                 # Use ball tracking effect (ID 45)
-                logger.info("Setting LED to ball tracking effect")
+                logger.info("Setting LED to ball tracking effect (ID 45)")
                 controller = state.led_controller.get_controller()
                 if controller:
                     controller.set_power(1)

+ 22 - 0
modules/core/state.py

@@ -256,6 +256,16 @@ class AppState:
             "scheduled_pause_enabled": self.scheduled_pause_enabled,
             "scheduled_pause_time_slots": self.scheduled_pause_time_slots,
             "scheduled_pause_control_wled": self.scheduled_pause_control_wled,
+            "ball_tracking_enabled": self.ball_tracking_enabled,
+            "ball_tracking_mode": self.ball_tracking_mode,
+            "ball_tracking_led_offset": self.ball_tracking_led_offset,
+            "ball_tracking_reversed": self.ball_tracking_reversed,
+            "ball_tracking_spread": self.ball_tracking_spread,
+            "ball_tracking_lookback": self.ball_tracking_lookback,
+            "ball_tracking_brightness": self.ball_tracking_brightness,
+            "ball_tracking_color": self.ball_tracking_color,
+            "ball_tracking_trail_enabled": self.ball_tracking_trail_enabled,
+            "ball_tracking_trail_length": self.ball_tracking_trail_length,
         }
 
     def from_dict(self, data):
@@ -326,6 +336,18 @@ class AppState:
         self.scheduled_pause_time_slots = data.get("scheduled_pause_time_slots", [])
         self.scheduled_pause_control_wled = data.get("scheduled_pause_control_wled", False)
 
+        # Load ball tracking settings
+        self.ball_tracking_enabled = data.get("ball_tracking_enabled", False)
+        self.ball_tracking_mode = data.get("ball_tracking_mode", "disabled")
+        self.ball_tracking_led_offset = data.get("ball_tracking_led_offset", 0)
+        self.ball_tracking_reversed = data.get("ball_tracking_reversed", False)
+        self.ball_tracking_spread = data.get("ball_tracking_spread", 3)
+        self.ball_tracking_lookback = data.get("ball_tracking_lookback", 5)
+        self.ball_tracking_brightness = data.get("ball_tracking_brightness", 50)
+        self.ball_tracking_color = data.get("ball_tracking_color", "#ffffff")
+        self.ball_tracking_trail_enabled = data.get("ball_tracking_trail_enabled", False)
+        self.ball_tracking_trail_length = data.get("ball_tracking_trail_length", 10)
+
     def save(self):
         """Save the current state to a JSON file."""
         try:

+ 11 - 4
modules/led/ball_tracking_manager.py

@@ -5,6 +5,7 @@ Tracks the ball bearing's position and updates LEDs in real-time to follow its m
 import asyncio
 import time
 import logging
+import threading
 from collections import deque
 from typing import Optional, Tuple, Dict
 from .dw_led_controller import DWLEDController
@@ -43,6 +44,7 @@ class BallTrackingManager:
         self._active = False
         self._update_task = None
         self._last_led_index = None
+        self._lock = threading.Lock()  # Thread safety for LED index updates
 
         logger.info(f"BallTrackingManager initialized with {num_leds} LEDs")
 
@@ -98,8 +100,9 @@ class BallTrackingManager:
         # Calculate LED index
         led_index = self._theta_to_led(theta)
 
-        # Store the LED index (effect will read this)
-        self._last_led_index = led_index
+        # Store the LED index (effect will read this) - thread-safe update
+        with self._lock:
+            self._last_led_index = led_index
 
     def _get_tracked_position(self) -> Optional[Tuple[float, float, float]]:
         """Get position to track (accounting for lookback delay)"""
@@ -159,7 +162,11 @@ class BallTrackingManager:
             Dictionary with led_index, spread, brightness, color
             or None if no tracking data available
         """
-        if self._last_led_index is None:
+        # Thread-safe read of LED index
+        with self._lock:
+            led_index = self._last_led_index
+
+        if led_index is None:
             return None
 
         # Get configuration
@@ -174,7 +181,7 @@ class BallTrackingManager:
         b = int(color_hex[4:6], 16)
 
         return {
-            'led_index': self._last_led_index,
+            'led_index': led_index,
             'spread': spread,
             'brightness': brightness,
             'color': (r, g, b)

+ 10 - 0
modules/led/dw_leds/effects/basic_effects.py

@@ -1073,11 +1073,15 @@ def mode_ball_tracking(seg: Segment) -> int:
     """
     # Import state here to avoid circular dependency
     from modules.core.state import state
+    import logging
+    logger = logging.getLogger(__name__)
 
     # Get ball tracking manager
     manager = state.ball_tracking_manager
     if not manager or not manager._active:
         # No active tracking, show static color or turn off
+        if seg.call % 100 == 0:  # Log occasionally
+            logger.debug(f"Ball tracking effect: manager active={manager._active if manager else 'N/A'}")
         seg.fill(0x000000)
         return FRAMETIME
 
@@ -1085,9 +1089,15 @@ def mode_ball_tracking(seg: Segment) -> int:
     tracking_data = manager.get_tracking_data()
     if not tracking_data:
         # No position data yet
+        if seg.call % 100 == 0:  # Log occasionally
+            logger.debug("Ball tracking effect: No tracking data available yet")
         seg.fill(0x000000)
         return FRAMETIME
 
+    # Log tracking data occasionally
+    if seg.call % 100 == 0:
+        logger.info(f"Ball tracking effect: LED index={tracking_data['led_index']}, spread={tracking_data['spread']}, buffer_size={len(manager.position_buffer)}")
+
     center_led = tracking_data['led_index']
     spread = tracking_data['spread']
     brightness = tracking_data['brightness']