tuanchris 2 месяцев назад
Родитель
Сommit
7db5a2a090
5 измененных файлов с 139 добавлено и 11 удалено
  1. 18 3
      main.py
  2. 31 3
      modules/core/pattern_manager.py
  3. 10 0
      modules/core/state.py
  4. 42 5
      static/js/settings.js
  5. 38 0
      templates/settings.html

+ 18 - 3
main.py

@@ -496,19 +496,23 @@ async def set_scheduled_pause(request: ScheduledPauseRequest):
 
 
 @app.get("/api/homing-config")
 @app.get("/api/homing-config")
 async def get_homing_config():
 async def get_homing_config():
-    """Get homing configuration (mode and compass offset)."""
+    """Get homing configuration (mode, compass offset, and auto-home settings)."""
     return {
     return {
         "homing_mode": state.homing,
         "homing_mode": state.homing,
-        "angular_homing_offset_degrees": state.angular_homing_offset_degrees
+        "angular_homing_offset_degrees": state.angular_homing_offset_degrees,
+        "auto_home_enabled": state.auto_home_enabled,
+        "auto_home_after_patterns": state.auto_home_after_patterns
     }
     }
 
 
 class HomingConfigRequest(BaseModel):
 class HomingConfigRequest(BaseModel):
     homing_mode: int = 0  # 0 = crash, 1 = sensor
     homing_mode: int = 0  # 0 = crash, 1 = sensor
     angular_homing_offset_degrees: float = 0.0
     angular_homing_offset_degrees: float = 0.0
+    auto_home_enabled: Optional[bool] = None
+    auto_home_after_patterns: Optional[int] = None
 
 
 @app.post("/api/homing-config")
 @app.post("/api/homing-config")
 async def set_homing_config(request: HomingConfigRequest):
 async def set_homing_config(request: HomingConfigRequest):
-    """Set homing configuration (mode and compass offset)."""
+    """Set homing configuration (mode, compass offset, and auto-home settings)."""
     try:
     try:
         # Validate homing mode
         # Validate homing mode
         if request.homing_mode not in [0, 1]:
         if request.homing_mode not in [0, 1]:
@@ -516,10 +520,21 @@ async def set_homing_config(request: HomingConfigRequest):
 
 
         state.homing = request.homing_mode
         state.homing = request.homing_mode
         state.angular_homing_offset_degrees = request.angular_homing_offset_degrees
         state.angular_homing_offset_degrees = request.angular_homing_offset_degrees
+
+        # Update auto-home settings if provided
+        if request.auto_home_enabled is not None:
+            state.auto_home_enabled = request.auto_home_enabled
+        if request.auto_home_after_patterns is not None:
+            if request.auto_home_after_patterns < 1:
+                raise HTTPException(status_code=400, detail="Auto-home after patterns must be at least 1")
+            state.auto_home_after_patterns = request.auto_home_after_patterns
+
         state.save()
         state.save()
 
 
         mode_name = "crash" if request.homing_mode == 0 else "sensor"
         mode_name = "crash" if request.homing_mode == 0 else "sensor"
         logger.info(f"Homing mode set to {mode_name}, compass offset set to {request.angular_homing_offset_degrees}°")
         logger.info(f"Homing mode set to {mode_name}, compass offset set to {request.angular_homing_offset_degrees}°")
+        if request.auto_home_enabled is not None:
+            logger.info(f"Auto-home enabled: {state.auto_home_enabled}, after {state.auto_home_after_patterns} patterns")
         return {"success": True, "message": "Homing configuration updated"}
         return {"success": True, "message": "Homing configuration updated"}
     except HTTPException:
     except HTTPException:
         raise
         raise

+ 31 - 3
modules/core/pattern_manager.py

@@ -866,6 +866,10 @@ async def run_theta_rho_files(file_paths, pause_time=0, clear_pattern=None, run_
 
 
             # Set the playlist to the first pattern
             # Set the playlist to the first pattern
             state.current_playlist = pattern_sequence
             state.current_playlist = pattern_sequence
+
+            # Reset pattern counter at the start of the playlist
+            state.patterns_since_last_home = 0
+
             # Execute the pattern sequence
             # Execute the pattern sequence
             for idx, file_path in enumerate(pattern_sequence):
             for idx, file_path in enumerate(pattern_sequence):
                 state.current_playlist_index = idx
                 state.current_playlist_index = idx
@@ -873,16 +877,40 @@ async def run_theta_rho_files(file_paths, pause_time=0, clear_pattern=None, run_
                     logger.info("Execution stopped")
                     logger.info("Execution stopped")
                     return
                     return
 
 
+                # Check if we need to auto-home before this pattern
+                # Auto-home happens right after the clear pattern, before the actual pattern
+                current_is_clear = is_clear_pattern(file_path)
+
+                if not current_is_clear and state.auto_home_enabled:
+                    # Check if we've reached the pattern threshold
+                    if state.patterns_since_last_home >= state.auto_home_after_patterns:
+                        logger.info(f"Auto-homing triggered after {state.patterns_since_last_home} patterns")
+                        try:
+                            # Perform homing using connection_manager
+                            success = await asyncio.to_thread(connection_manager.home)
+                            if success:
+                                logger.info("Auto-homing completed successfully")
+                                state.patterns_since_last_home = 0
+                            else:
+                                logger.warning("Auto-homing failed, continuing with playlist")
+                        except Exception as e:
+                            logger.error(f"Error during auto-homing: {e}")
+
                 # Update state for main patterns only
                 # Update state for main patterns only
                 logger.info(f"Running pattern {file_path}")
                 logger.info(f"Running pattern {file_path}")
-                
+
                 # Execute the pattern
                 # Execute the pattern
                 await run_theta_rho_file(file_path, is_playlist=True)
                 await run_theta_rho_file(file_path, is_playlist=True)
 
 
+                # Increment pattern counter only for non-clear patterns
+                if not current_is_clear:
+                    state.patterns_since_last_home += 1
+                    logger.debug(f"Patterns since last home: {state.patterns_since_last_home}")
+
                 # Handle pause between patterns
                 # Handle pause between patterns
                 if idx < len(pattern_sequence) - 1 and not state.stop_requested and pause_time > 0 and not state.skip_requested:
                 if idx < len(pattern_sequence) - 1 and not state.stop_requested and pause_time > 0 and not state.skip_requested:
                     # Check if current pattern is a clear pattern
                     # Check if current pattern is a clear pattern
-                    if is_clear_pattern(file_path):
+                    if current_is_clear:
                         logger.info("Skipping pause after clear pattern")
                         logger.info("Skipping pause after clear pattern")
                     else:
                     else:
                         logger.info(f"Pausing for {pause_time} seconds")
                         logger.info(f"Pausing for {pause_time} seconds")
@@ -895,7 +923,7 @@ async def run_theta_rho_files(file_paths, pause_time=0, clear_pattern=None, run_
                                 break
                                 break
                             await asyncio.sleep(1)
                             await asyncio.sleep(1)
                         state.pause_time_remaining = 0
                         state.pause_time_remaining = 0
-                    
+
                 state.skip_requested = False
                 state.skip_requested = False
 
 
             if run_mode == "indefinite":
             if run_mode == "indefinite":

+ 10 - 0
modules/core/state.py

@@ -45,6 +45,12 @@ class AppState:
         # After homing, theta will be set to this value
         # After homing, theta will be set to this value
         self.angular_homing_offset_degrees = 0.0
         self.angular_homing_offset_degrees = 0.0
 
 
+        # Auto-homing settings for playlists
+        # When enabled, performs homing after X patterns during playlist execution
+        self.auto_home_enabled = False
+        self.auto_home_after_patterns = 5  # Number of patterns after which to auto-home
+        self.patterns_since_last_home = 0  # Counter for patterns played since last home
+
         self.STATE_FILE = "state.json"
         self.STATE_FILE = "state.json"
         self.mqtt_handler = None  # Will be set by the MQTT handler
         self.mqtt_handler = None  # Will be set by the MQTT handler
         self.conn = None
         self.conn = None
@@ -210,6 +216,8 @@ class AppState:
             "gear_ratio": self.gear_ratio,
             "gear_ratio": self.gear_ratio,
             "homing": self.homing,
             "homing": self.homing,
             "angular_homing_offset_degrees": self.angular_homing_offset_degrees,
             "angular_homing_offset_degrees": self.angular_homing_offset_degrees,
+            "auto_home_enabled": self.auto_home_enabled,
+            "auto_home_after_patterns": self.auto_home_after_patterns,
             "current_playlist": self._current_playlist,
             "current_playlist": self._current_playlist,
             "current_playlist_name": self._current_playlist_name,
             "current_playlist_name": self._current_playlist_name,
             "current_playlist_index": self.current_playlist_index,
             "current_playlist_index": self.current_playlist_index,
@@ -261,6 +269,8 @@ class AppState:
         self.gear_ratio = data.get('gear_ratio', 10)
         self.gear_ratio = data.get('gear_ratio', 10)
         self.homing = data.get('homing', 0)
         self.homing = data.get('homing', 0)
         self.angular_homing_offset_degrees = data.get('angular_homing_offset_degrees', 0.0)
         self.angular_homing_offset_degrees = data.get('angular_homing_offset_degrees', 0.0)
+        self.auto_home_enabled = data.get('auto_home_enabled', False)
+        self.auto_home_after_patterns = data.get('auto_home_after_patterns', 5)
         self._current_playlist = data.get("current_playlist", None)
         self._current_playlist = data.get("current_playlist", None)
         self._current_playlist_name = data.get("current_playlist_name", None)
         self._current_playlist_name = data.get("current_playlist_name", None)
         self.current_playlist_index = data.get("current_playlist_index", None)
         self.current_playlist_index = data.get("current_playlist_index", None)

+ 42 - 5
static/js/settings.js

@@ -1519,6 +1519,9 @@ async function initializeHomingConfig() {
     const compassOffsetContainer = document.getElementById('compassOffsetContainer');
     const compassOffsetContainer = document.getElementById('compassOffsetContainer');
     const saveHomingConfigButton = document.getElementById('saveHomingConfig');
     const saveHomingConfigButton = document.getElementById('saveHomingConfig');
     const homingInfoContent = document.getElementById('homingInfoContent');
     const homingInfoContent = document.getElementById('homingInfoContent');
+    const autoHomeEnabledToggle = document.getElementById('autoHomeEnabledToggle');
+    const autoHomeSettings = document.getElementById('autoHomeSettings');
+    const autoHomeAfterPatternsInput = document.getElementById('autoHomeAfterPatternsInput');
 
 
     // Check if elements exist
     // Check if elements exist
     if (!homingModeCrash || !homingModeSensor || !angularOffsetInput || !saveHomingConfigButton || !homingInfoContent || !compassOffsetContainer) {
     if (!homingModeCrash || !homingModeSensor || !angularOffsetInput || !saveHomingConfigButton || !homingInfoContent || !compassOffsetContainer) {
@@ -1574,14 +1577,28 @@ async function initializeHomingConfig() {
         }
         }
 
 
         angularOffsetInput.value = data.angular_homing_offset_degrees || 0;
         angularOffsetInput.value = data.angular_homing_offset_degrees || 0;
+
+        // Load auto-home settings
+        if (autoHomeEnabledToggle) {
+            autoHomeEnabledToggle.checked = data.auto_home_enabled || false;
+            if (autoHomeSettings) {
+                autoHomeSettings.style.display = data.auto_home_enabled ? 'block' : 'none';
+            }
+        }
+        if (autoHomeAfterPatternsInput) {
+            autoHomeAfterPatternsInput.value = data.auto_home_after_patterns || 5;
+        }
+
         updateHomingInfo();
         updateHomingInfo();
 
 
-        logMessage(`Loaded homing config: mode=${data.homing_mode}, offset=${data.angular_homing_offset_degrees}°`, LOG_TYPE.INFO);
+        logMessage(`Loaded homing config: mode=${data.homing_mode}, offset=${data.angular_homing_offset_degrees}°, auto_home=${data.auto_home_enabled}, after=${data.auto_home_after_patterns}`, LOG_TYPE.INFO);
     } catch (error) {
     } catch (error) {
         logMessage(`Error loading homing configuration: ${error.message}`, LOG_TYPE.ERROR);
         logMessage(`Error loading homing configuration: ${error.message}`, LOG_TYPE.ERROR);
         // Initialize with defaults if load fails
         // Initialize with defaults if load fails
         homingModeCrash.checked = true;
         homingModeCrash.checked = true;
         angularOffsetInput.value = 0;
         angularOffsetInput.value = 0;
+        if (autoHomeEnabledToggle) autoHomeEnabledToggle.checked = false;
+        if (autoHomeAfterPatternsInput) autoHomeAfterPatternsInput.value = 5;
         updateHomingInfo();
         updateHomingInfo();
     }
     }
 
 
@@ -1593,13 +1610,26 @@ async function initializeHomingConfig() {
         saveHomingConfigButton.innerHTML = '<span class="material-icons text-lg animate-spin">refresh</span><span class="truncate">Saving...</span>';
         saveHomingConfigButton.innerHTML = '<span class="material-icons text-lg animate-spin">refresh</span><span class="truncate">Saving...</span>';
 
 
         try {
         try {
+            const requestBody = {
+                homing_mode: getSelectedMode(),
+                angular_homing_offset_degrees: parseFloat(angularOffsetInput.value) || 0
+            };
+
+            // Include auto-home settings if elements exist
+            if (autoHomeEnabledToggle) {
+                requestBody.auto_home_enabled = autoHomeEnabledToggle.checked;
+            }
+            if (autoHomeAfterPatternsInput) {
+                const afterPatterns = parseInt(autoHomeAfterPatternsInput.value);
+                if (!isNaN(afterPatterns) && afterPatterns >= 1) {
+                    requestBody.auto_home_after_patterns = afterPatterns;
+                }
+            }
+
             const response = await fetch('/api/homing-config', {
             const response = await fetch('/api/homing-config', {
                 method: 'POST',
                 method: 'POST',
                 headers: { 'Content-Type': 'application/json' },
                 headers: { 'Content-Type': 'application/json' },
-                body: JSON.stringify({
-                    homing_mode: getSelectedMode(),
-                    angular_homing_offset_degrees: parseFloat(angularOffsetInput.value) || 0
-                })
+                body: JSON.stringify(requestBody)
             });
             });
 
 
             if (!response.ok) {
             if (!response.ok) {
@@ -1630,4 +1660,11 @@ async function initializeHomingConfig() {
     homingModeCrash.addEventListener('change', updateHomingInfo);
     homingModeCrash.addEventListener('change', updateHomingInfo);
     homingModeSensor.addEventListener('change', updateHomingInfo);
     homingModeSensor.addEventListener('change', updateHomingInfo);
     saveHomingConfigButton.addEventListener('click', saveHomingConfig);
     saveHomingConfigButton.addEventListener('click', saveHomingConfig);
+
+    // Auto-home toggle event listener
+    if (autoHomeEnabledToggle && autoHomeSettings) {
+        autoHomeEnabledToggle.addEventListener('change', () => {
+            autoHomeSettings.style.display = autoHomeEnabledToggle.checked ? 'block' : 'none';
+        });
+    }
 }
 }

+ 38 - 0
templates/settings.html

@@ -421,6 +421,44 @@ input:checked + .slider:before {
         </div>
         </div>
       </div>
       </div>
 
 
+      <!-- Auto-Home During Playlists -->
+      <div class="bg-slate-50 rounded-lg p-4 space-y-4">
+        <div class="flex items-center justify-between">
+          <div class="flex-1">
+            <h3 class="text-slate-800 text-base font-semibold flex items-center gap-2">
+              <span class="material-icons text-slate-600 text-base">autorenew</span>
+              Auto-Home During Playlists
+            </h3>
+            <p class="text-xs text-slate-500 mt-1">
+              Automatically perform homing after a certain number of patterns during playlist playback to maintain accuracy.
+            </p>
+          </div>
+          <label class="switch">
+            <input type="checkbox" id="autoHomeEnabledToggle">
+            <span class="slider round"></span>
+          </label>
+        </div>
+
+        <div id="autoHomeSettings" style="display: none;">
+          <label class="flex flex-col gap-1.5">
+            <span class="text-slate-700 text-sm font-medium leading-normal">Home after every X patterns</span>
+            <input
+              type="number"
+              id="autoHomeAfterPatternsInput"
+              min="1"
+              max="100"
+              step="1"
+              value="5"
+              class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-sky-500 focus:border-sky-500 text-sm"
+              placeholder="5"
+            />
+            <p class="text-xs text-slate-500">
+              Homing will occur right after the clear pattern completes, before the next actual pattern begins.
+            </p>
+          </label>
+        </div>
+      </div>
+
       <div class="flex justify-end">
       <div class="flex justify-end">
         <button
         <button
           id="saveHomingConfig"
           id="saveHomingConfig"