Jelajahi Sumber

add home after x pattern

tuanchris 3 bulan lalu
induk
melakukan
63aecc4639
5 mengubah file dengan 126 tambahan dan 10 penghapusan
  1. 15 4
      main.py
  2. 25 0
      modules/core/pattern_manager.py
  3. 10 0
      modules/core/state.py
  4. 43 6
      static/js/settings.js
  5. 33 0
      templates/settings.html

+ 15 - 4
main.py

@@ -496,30 +496,41 @@ async def set_scheduled_pause(request: ScheduledPauseRequest):
 
 @app.get("/api/homing-config")
 async def get_homing_config():
-    """Get homing configuration (mode and compass offset)."""
+    """Get homing configuration (mode, compass offset, and auto-homing settings)."""
     return {
         "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_interval": state.auto_home_interval
     }
 
 class HomingConfigRequest(BaseModel):
     homing_mode: int = 0  # 0 = crash, 1 = sensor
     angular_homing_offset_degrees: float = 0.0
+    auto_home_enabled: bool = False
+    auto_home_interval: int = 10
 
 @app.post("/api/homing-config")
 async def set_homing_config(request: HomingConfigRequest):
-    """Set homing configuration (mode and compass offset)."""
+    """Set homing configuration (mode, compass offset, and auto-homing settings)."""
     try:
         # Validate homing mode
         if request.homing_mode not in [0, 1]:
             raise HTTPException(status_code=400, detail="Homing mode must be 0 (crash) or 1 (sensor)")
 
+        # Validate auto-homing interval
+        if request.auto_home_interval < 1 or request.auto_home_interval > 100:
+            raise HTTPException(status_code=400, detail="Auto-home interval must be between 1 and 100")
+
         state.homing = request.homing_mode
         state.angular_homing_offset_degrees = request.angular_homing_offset_degrees
+        state.auto_home_enabled = request.auto_home_enabled
+        state.auto_home_interval = request.auto_home_interval
         state.save()
 
         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}°")
+        auto_home_status = f", auto-home: {'enabled' if request.auto_home_enabled else 'disabled'} (every {request.auto_home_interval} patterns)" if request.homing_mode == 1 else ""
+        logger.info(f"Homing mode set to {mode_name}, compass offset set to {request.angular_homing_offset_degrees}°{auto_home_status}")
         return {"success": True, "message": "Homing configuration updated"}
     except HTTPException:
         raise

+ 25 - 0
modules/core/pattern_manager.py

@@ -794,6 +794,31 @@ async def run_theta_rho_file(file_path, is_playlist=False):
             start_idle_led_timeout()
             logger.debug("LED effect set to idle after pattern completion")
 
+        # Increment pattern count for auto-homing (only for non-clear patterns)
+        # This happens after pattern completes successfully (not stopped or skipped)
+        if not state.stop_requested and not state.skip_requested and not is_clear_file:
+            state.auto_home_pattern_count += 1
+            logger.info(f"Pattern completed. Auto-home count: {state.auto_home_pattern_count}/{state.auto_home_interval}")
+
+            # Check if we should auto-home
+            if (state.auto_home_enabled and
+                state.homing == 1 and  # Only for sensor homing mode
+                state.auto_home_interval > 0 and
+                state.auto_home_pattern_count >= state.auto_home_interval):
+
+                logger.info(f"Auto-homing triggered after {state.auto_home_pattern_count} patterns")
+                state.auto_home_pattern_count = 0  # Reset counter
+
+                # Perform homing
+                try:
+                    homing_success = await asyncio.to_thread(connection_manager.home)
+                    if homing_success:
+                        logger.info("Auto-homing completed successfully")
+                    else:
+                        logger.warning("Auto-homing failed")
+                except Exception as e:
+                    logger.error(f"Error during auto-homing: {e}")
+
         # Only clear state if not part of a playlist
         if not is_playlist:
             state.current_playing_file = None

+ 10 - 0
modules/core/state.py

@@ -45,6 +45,11 @@ class AppState:
         # After homing, theta will be set to this value
         self.angular_homing_offset_degrees = 0.0
 
+        # Auto-homing after X patterns (only for sensor homing mode)
+        self.auto_home_enabled = False  # Enable/disable auto-homing
+        self.auto_home_interval = 10  # Home after this many pattern runs
+        self.auto_home_pattern_count = 0  # Current count (runtime only, not persisted)
+
         self.STATE_FILE = "state.json"
         self.mqtt_handler = None  # Will be set by the MQTT handler
         self.conn = None
@@ -210,6 +215,8 @@ class AppState:
             "gear_ratio": self.gear_ratio,
             "homing": self.homing,
             "angular_homing_offset_degrees": self.angular_homing_offset_degrees,
+            "auto_home_enabled": self.auto_home_enabled,
+            "auto_home_interval": self.auto_home_interval,
             "current_playlist": self._current_playlist,
             "current_playlist_name": self._current_playlist_name,
             "current_playlist_index": self.current_playlist_index,
@@ -261,6 +268,9 @@ class AppState:
         self.gear_ratio = data.get('gear_ratio', 10)
         self.homing = data.get('homing', 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_interval = data.get('auto_home_interval', 10)
+        # Note: auto_home_pattern_count is not loaded - it's runtime only
         self._current_playlist = data.get("current_playlist", None)
         self._current_playlist_name = data.get("current_playlist_name", None)
         self.current_playlist_index = data.get("current_playlist_index", None)

+ 43 - 6
static/js/settings.js

@@ -1517,6 +1517,10 @@ async function initializeHomingConfig() {
     const homingModeSensor = document.getElementById('homingModeSensor');
     const angularOffsetInput = document.getElementById('angularOffsetInput');
     const compassOffsetContainer = document.getElementById('compassOffsetContainer');
+    const autoHomingContainer = document.getElementById('autoHomingContainer');
+    const autoHomeEnabled = document.getElementById('autoHomeEnabled');
+    const autoHomeIntervalContainer = document.getElementById('autoHomeIntervalContainer');
+    const autoHomeInterval = document.getElementById('autoHomeInterval');
     const saveHomingConfigButton = document.getElementById('saveHomingConfig');
     const homingInfoContent = document.getElementById('homingInfoContent');
 
@@ -1537,9 +1541,10 @@ async function initializeHomingConfig() {
     function updateHomingInfo() {
         const mode = getSelectedMode();
 
-        // Show/hide compass offset based on mode
+        // Show/hide compass offset and auto-homing based on mode
         if (mode === 0) {
             compassOffsetContainer.style.display = 'none';
+            if (autoHomingContainer) autoHomingContainer.style.display = 'none';
             homingInfoContent.innerHTML = `
                 <p class="font-medium text-blue-800">Crash Homing Mode:</p>
                 <ul class="mt-1 space-y-1 text-blue-700">
@@ -1551,6 +1556,7 @@ async function initializeHomingConfig() {
             `;
         } else {
             compassOffsetContainer.style.display = 'block';
+            if (autoHomingContainer) autoHomingContainer.style.display = 'block';
             homingInfoContent.innerHTML = `
                 <p class="font-medium text-blue-800">Sensor Homing Mode:</p>
                 <ul class="mt-1 space-y-1 text-blue-700">
@@ -1561,6 +1567,13 @@ async function initializeHomingConfig() {
         }
     }
 
+    // Function to update auto-homing interval visibility based on checkbox
+    function updateAutoHomeIntervalVisibility() {
+        if (autoHomeIntervalContainer) {
+            autoHomeIntervalContainer.style.display = autoHomeEnabled.checked ? 'block' : 'none';
+        }
+    }
+
     // Load current homing configuration
     try {
         const response = await fetch('/api/homing-config');
@@ -1574,15 +1587,28 @@ async function initializeHomingConfig() {
         }
 
         angularOffsetInput.value = data.angular_homing_offset_degrees || 0;
+
+        // Set auto-homing settings
+        if (autoHomeEnabled) {
+            autoHomeEnabled.checked = data.auto_home_enabled || false;
+        }
+        if (autoHomeInterval) {
+            autoHomeInterval.value = data.auto_home_interval || 10;
+        }
+
         updateHomingInfo();
+        updateAutoHomeIntervalVisibility();
 
-        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}`, LOG_TYPE.INFO);
     } catch (error) {
         logMessage(`Error loading homing configuration: ${error.message}`, LOG_TYPE.ERROR);
         // Initialize with defaults if load fails
         homingModeCrash.checked = true;
         angularOffsetInput.value = 0;
+        if (autoHomeEnabled) autoHomeEnabled.checked = false;
+        if (autoHomeInterval) autoHomeInterval.value = 10;
         updateHomingInfo();
+        updateAutoHomeIntervalVisibility();
     }
 
     // Function to save homing configuration
@@ -1593,13 +1619,21 @@ async function initializeHomingConfig() {
         saveHomingConfigButton.innerHTML = '<span class="material-icons text-lg animate-spin">refresh</span><span class="truncate">Saving...</span>';
 
         try {
+            const payload = {
+                homing_mode: getSelectedMode(),
+                angular_homing_offset_degrees: parseFloat(angularOffsetInput.value) || 0
+            };
+
+            // Add auto-homing settings if elements exist
+            if (autoHomeEnabled && autoHomeInterval) {
+                payload.auto_home_enabled = autoHomeEnabled.checked;
+                payload.auto_home_interval = parseInt(autoHomeInterval.value) || 10;
+            }
+
             const response = await fetch('/api/homing-config', {
                 method: 'POST',
                 headers: { 'Content-Type': 'application/json' },
-                body: JSON.stringify({
-                    homing_mode: getSelectedMode(),
-                    angular_homing_offset_degrees: parseFloat(angularOffsetInput.value) || 0
-                })
+                body: JSON.stringify(payload)
             });
 
             if (!response.ok) {
@@ -1629,5 +1663,8 @@ async function initializeHomingConfig() {
     // Event listeners
     homingModeCrash.addEventListener('change', updateHomingInfo);
     homingModeSensor.addEventListener('change', updateHomingInfo);
+    if (autoHomeEnabled) {
+        autoHomeEnabled.addEventListener('change', updateAutoHomeIntervalVisibility);
+    }
     saveHomingConfigButton.addEventListener('click', saveHomingConfig);
 }

+ 33 - 0
templates/settings.html

@@ -405,6 +405,39 @@ input:checked + .slider:before {
         </p>
       </div>
 
+      <!-- Auto-Homing Configuration (Sensor mode only) -->
+      <div id="autoHomingContainer" class="space-y-3">
+        <div class="flex items-center justify-between">
+          <label for="autoHomeEnabled" class="text-sm font-medium text-slate-700 flex items-center gap-2">
+            <span class="material-icons text-slate-600 text-base">refresh</span>
+            Auto-Home After Patterns <span class="text-xs text-slate-400">(Sensor mode only)</span>
+          </label>
+          <label class="relative inline-flex items-center cursor-pointer">
+            <input type="checkbox" id="autoHomeEnabled" class="sr-only peer">
+            <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-sky-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-sky-600"></div>
+          </label>
+        </div>
+
+        <div id="autoHomeIntervalContainer" class="space-y-2" style="display: none;">
+          <label for="autoHomeInterval" class="text-sm text-slate-700">
+            Home every X patterns (excluding clearing patterns):
+          </label>
+          <input
+            type="number"
+            id="autoHomeInterval"
+            min="1"
+            max="100"
+            step="1"
+            value="10"
+            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="10"
+          />
+          <p class="text-xs text-slate-500">
+            Automatically home the table after completing this many patterns. This helps maintain accuracy during long playlists by periodically recalibrating to the sensor position.
+          </p>
+        </div>
+      </div>
+
       <!-- Homing Info Box -->
       <div id="homingInfoBox" class="text-xs text-slate-600 bg-blue-50 border border-blue-200 rounded-lg p-3">
         <div class="flex items-start gap-2">