tuanchris 3 hónapja
szülő
commit
ecc82bb0d9
3 módosított fájl, 169 hozzáadás és 153 törlés
  1. 53 5
      main.py
  2. 54 99
      static/js/settings.js
  3. 62 49
      templates/settings.html

+ 53 - 5
main.py

@@ -2073,6 +2073,40 @@ async def get_ball_tracking_status():
         logger.error(f"Failed to get ball tracking status: {str(e)}")
         raise HTTPException(status_code=500, detail=str(e))
 
+@app.post("/api/ball_tracking/test_led")
+async def test_led_for_calibration(request: dict):
+    """
+    Light up a specific LED for calibration testing
+    Turn off all other LEDs
+    """
+    try:
+        led_index = request.get("led_index")
+        if led_index is None:
+            raise HTTPException(status_code=400, detail="led_index is required")
+
+        # Check if DW LEDs are active
+        if state.led_provider != "dw_leds" or not state.led_controller:
+            raise HTTPException(status_code=400, detail="DW LEDs must be configured")
+
+        controller = state.led_controller.get_controller()
+
+        # Clear all LEDs
+        controller.clear_all_leds()
+
+        # Light up only the selected LED in white
+        controller.set_single_led(led_index, (255, 255, 255), 0.5)
+
+        if controller._pixels:
+            controller._pixels.show()
+
+        return {
+            "success": True,
+            "message": f"LED {led_index} illuminated"
+        }
+    except Exception as e:
+        logger.error(f"Failed to test LED: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
 @app.post("/api/ball_tracking/calibrate")
 async def calibrate_ball_tracking():
     """
@@ -2081,6 +2115,7 @@ async def calibrate_ball_tracking():
     """
     try:
         from modules.core import pattern_manager
+        from modules.connection import connection_manager
 
         # Check if DW LEDs are active
         if state.led_provider != "dw_leds" or not state.led_controller:
@@ -2092,16 +2127,29 @@ async def calibrate_ball_tracking():
 
         logger.info("Starting ball tracking calibration")
 
+        # Turn off all LEDs except LED 0 to show reference
+        try:
+            controller = state.led_controller.get_controller()
+            controller.clear_all_leds()
+            # Light up only LED 0 in white
+            controller.set_single_led(0, (255, 255, 255), 0.5)
+            if controller._pixels:
+                controller._pixels.show()
+            logger.info("LED 0 illuminated as reference")
+        except Exception as e:
+            logger.warning(f"Failed to set reference LED: {e}")
+
         # Step 1: Reset theta
         await pattern_manager.reset_theta()
         logger.info("Theta reset complete")
 
-        # Step 2: Move to (0°, rho=1.0) at perimeter
-        await pattern_manager.move_polar(0.0, 1.0, state.speed)
-        logger.info("Moved to reference position (0°, 1.0)")
+        # Step 2: Move to (0°, rho=1.0) at perimeter with 400 speed
+        await pattern_manager.move_polar(0.0, 1.0, 400)
+        logger.info("Moved to reference position (0°, 1.0) at 400 speed")
 
-        # Wait for movement to complete
-        await asyncio.sleep(0.5)
+        # Step 3: Wait for idle to ensure movement is complete
+        await connection_manager.check_idle_async()
+        logger.info("Movement complete, machine is idle")
 
         return {
             "success": True,

+ 54 - 99
static/js/settings.js

@@ -1749,32 +1749,30 @@ function initCalibrationWizard() {
     // Step elements
     const step1 = document.getElementById('calibrationStep1');
     const step2 = document.getElementById('calibrationStep2');
-    const step3 = document.getElementById('calibrationStep3');
     const complete = document.getElementById('calibrationComplete');
 
     // Step indicators
     const step1Indicator = document.getElementById('step1Indicator');
     const step2Indicator = document.getElementById('step2Indicator');
-    const step3Indicator = document.getElementById('step3Indicator');
     const line1 = document.getElementById('line1');
-    const line2 = document.getElementById('line2');
 
     // Action buttons
     const startMoveBtn = document.getElementById('startCalibrationMove');
     const moveStatus = document.getElementById('calibrationMoveStatus');
-    const testDirectionBtn = document.getElementById('testDirection');
-    const directionStatus = document.getElementById('directionTestStatus');
-    const directionQuestion = document.getElementById('directionQuestion');
-    const directionClockwise = document.getElementById('directionClockwise');
-    const directionCounterClockwise = document.getElementById('directionCounterClockwise');
+    const confirmCalibrationBtn = document.getElementById('confirmCalibration');
 
-    // LED visualization
-    const ledCircle = document.getElementById('ledCircle');
-    const selectedLedInfo = document.getElementById('selectedLedInfo');
+    // LED navigation buttons
+    const ledPrev1 = document.getElementById('ledPrev1');
+    const ledNext1 = document.getElementById('ledNext1');
+    const ledPrev5 = document.getElementById('ledPrev5');
+    const ledNext5 = document.getElementById('ledNext5');
+    const currentLedNumber = document.getElementById('currentLedNumber');
+    const ledStatusText = document.getElementById('ledStatusText');
+    const reverseDirectionToggle = document.getElementById('reverseDirectionToggle');
 
     // State
     let numLeds = 60;
-    let selectedLed = null;
+    let currentLed = 0;
     let reversed = false;
 
     // Open wizard
@@ -1795,11 +1793,10 @@ function initCalibrationWizard() {
     // Reset wizard to step 1
     function resetWizard() {
         showStep(1);
-        selectedLed = null;
+        currentLed = 0;
         reversed = false;
         moveStatus.classList.add('hidden');
-        directionStatus.classList.add('hidden');
-        directionQuestion.classList.add('hidden');
+        reverseDirectionToggle.checked = false;
     }
 
     // Show specific step
@@ -1807,16 +1804,14 @@ function initCalibrationWizard() {
         // Hide all steps
         step1.classList.add('hidden');
         step2.classList.add('hidden');
-        step3.classList.add('hidden');
         complete.classList.add('hidden');
 
         // Reset indicators
-        [step1Indicator, step2Indicator, step3Indicator].forEach(ind => {
-            ind.classList.remove('bg-sky-600', 'text-white');
+        [step1Indicator, step2Indicator].forEach(ind => {
+            ind.classList.remove('bg-sky-600', 'text-white', 'bg-green-600');
             ind.classList.add('bg-slate-200', 'text-slate-500');
         });
         line1.classList.remove('bg-sky-600');
-        line2.classList.remove('bg-sky-600');
 
         // Show active step
         if (stepNum === 1) {
@@ -1830,21 +1825,13 @@ function initCalibrationWizard() {
             step2Indicator.classList.remove('bg-slate-200', 'text-slate-500');
             step2Indicator.classList.add('bg-sky-600', 'text-white');
             line1.classList.add('bg-sky-600');
-        } else if (stepNum === 3) {
-            step3.classList.remove('hidden');
-            step1Indicator.classList.add('bg-green-600', 'text-white');
-            step2Indicator.classList.add('bg-green-600', 'text-white');
-            step3Indicator.classList.remove('bg-slate-200', 'text-slate-500');
-            step3Indicator.classList.add('bg-sky-600', 'text-white');
-            line1.classList.add('bg-sky-600');
-            line2.classList.add('bg-sky-600');
         } else if (stepNum === 'complete') {
             complete.classList.remove('hidden');
-            [step1Indicator, step2Indicator, step3Indicator].forEach(ind => {
+            [step1Indicator, step2Indicator].forEach(ind => {
+                ind.classList.remove('bg-slate-200', 'text-slate-500');
                 ind.classList.add('bg-green-600', 'text-white');
             });
             line1.classList.add('bg-sky-600');
-            line2.classList.add('bg-sky-600');
         }
     }
 
@@ -1863,8 +1850,9 @@ function initCalibrationWizard() {
 
             if (data.success) {
                 numLeds = data.num_leds;
+                currentLed = 0;
                 showStatusMessage('Ball moved to reference position!', 'success');
-                renderLEDCircle();
+                updateLEDDisplay();
                 showStep(2);
             } else {
                 throw new Error(data.detail || 'Calibration failed');
@@ -1877,93 +1865,60 @@ function initCalibrationWizard() {
         }
     });
 
-    // Render LED circle
-    function renderLEDCircle() {
-        // Remove existing LEDs
-        const existingLeds = ledCircle.querySelectorAll('.led-dot');
-        existingLeds.forEach(led => led.remove());
-
-        const centerX = 200;
-        const centerY = 200;
-        const radius = 150;
-
-        for (let i = 0; i < numLeds; i++) {
-            const angle = (i / numLeds) * 2 * Math.PI - Math.PI / 2; // Start from top (270°)
-            const x = centerX + radius * Math.cos(angle);
-            const y = centerY + radius * Math.sin(angle);
-
-            const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
-            circle.setAttribute('cx', x);
-            circle.setAttribute('cy', y);
-            circle.setAttribute('r', '6');
-            circle.setAttribute('fill', '#cbd5e1');
-            circle.setAttribute('stroke', '#64748b');
-            circle.setAttribute('stroke-width', '1');
-            circle.setAttribute('class', 'led-dot cursor-pointer hover:fill-sky-400 transition-colors');
-            circle.setAttribute('data-led-index', i);
-
-            circle.addEventListener('click', () => selectLED(i));
-            ledCircle.appendChild(circle);
-        }
+    // Update LED display
+    function updateLEDDisplay() {
+        currentLedNumber.textContent = currentLed;
+        ledStatusText.textContent = 'This LED is now lit on your strip';
     }
 
-    // Select LED
-    function selectLED(index) {
-        selectedLed = index;
-
-        // Update visuals
-        const allDots = ledCircle.querySelectorAll('.led-dot');
-        allDots.forEach(dot => {
-            dot.setAttribute('fill', '#cbd5e1');
-        });
-        allDots[index].setAttribute('fill', '#0c7ff2');
-
-        selectedLedInfo.textContent = `Selected: LED ${index}`;
-
-        // Move to step 3 after selection
-        setTimeout(() => showStep(3), 500);
-    }
+    // Navigate to specific LED and light it
+    async function navigateToLED(newLedIndex) {
+        // Wrap around LED count
+        currentLed = ((newLedIndex % numLeds) + numLeds) % numLeds;
 
-    // Step 3: Test direction
-    testDirectionBtn.addEventListener('click', async () => {
-        testDirectionBtn.disabled = true;
-        directionStatus.classList.remove('hidden');
+        // Update UI
+        updateLEDDisplay();
 
+        // Light up the physical LED
         try {
-            // Move to 90° to test direction
-            const response = await fetch('/send_coordinate', {
+            const response = await fetch('/api/ball_tracking/test_led', {
                 method: 'POST',
                 headers: {'Content-Type': 'application/json'},
-                body: JSON.stringify({theta: 90, rho: 1.0})
+                body: JSON.stringify({ led_index: currentLed })
             });
 
-            if (response.ok) {
-                await new Promise(resolve => setTimeout(resolve, 2000)); // Wait for movement
-                directionStatus.classList.add('hidden');
-                directionQuestion.classList.remove('hidden');
-            } else {
-                throw new Error('Failed to move ball');
+            const data = await response.json();
+            if (!data.success) {
+                ledStatusText.textContent = 'Failed to light LED';
             }
         } catch (error) {
-            console.error('Direction test error:', error);
-            showStatusMessage(`Direction test failed: ${error.message}`, 'error');
-            testDirectionBtn.disabled = false;
-            directionStatus.classList.add('hidden');
+            console.error('Failed to light LED:', error);
+            ledStatusText.textContent = 'Error lighting LED';
         }
+    }
+
+    // LED navigation button handlers
+    ledPrev1.addEventListener('click', () => navigateToLED(currentLed - 1));
+    ledNext1.addEventListener('click', () => navigateToLED(currentLed + 1));
+    ledPrev5.addEventListener('click', () => navigateToLED(currentLed - 5));
+    ledNext5.addEventListener('click', () => navigateToLED(currentLed + 5));
+
+    // Reverse direction toggle
+    reverseDirectionToggle.addEventListener('change', () => {
+        reversed = reverseDirectionToggle.checked;
     });
 
-    // Direction confirmation
-    directionClockwise.addEventListener('click', () => completeCalibration(false));
-    directionCounterClockwise.addEventListener('click', () => completeCalibration(true));
+    // Confirm calibration
+    confirmCalibrationBtn.addEventListener('click', async () => {
+        await completeCalibration();
+    });
 
     // Complete calibration
-    async function completeCalibration(reverseDirection) {
-        reversed = reverseDirection;
-
+    async function completeCalibration() {
         try {
             // Save calibration settings
             const config = {
-                led_offset: selectedLed,
+                led_offset: currentLed,
                 reversed: reversed
             };
 
@@ -1977,7 +1932,7 @@ function initCalibrationWizard() {
 
             if (data.success) {
                 // Show completion
-                document.getElementById('finalLedOffset').textContent = selectedLed;
+                document.getElementById('finalLedOffset').textContent = currentLed;
                 document.getElementById('finalDirection').textContent = reversed ? 'Reversed (Counter-Clockwise)' : 'Normal (Clockwise)';
                 showStep('complete');
                 showStatusMessage('Calibration complete!', 'success');

+ 62 - 49
templates/settings.html

@@ -888,7 +888,7 @@ input:checked + .slider:before {
 
   <!-- Calibration Wizard Modal -->
   <div id="calibrationModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex items-center justify-center p-4">
-    <div class="bg-white rounded-xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
+    <div class="bg-white dark:bg-neutral-800 rounded-xl shadow-2xl w-full max-w-xl max-h-[85vh] overflow-y-auto">
       <!-- Modal Header -->
       <div class="flex items-center justify-between px-6 py-4 border-b border-slate-200">
         <h3 class="text-slate-900 text-xl font-semibold">Ball Tracking Calibration</h3>
@@ -903,10 +903,8 @@ input:checked + .slider:before {
         <div class="flex items-center justify-between mb-6">
           <div class="flex items-center gap-2">
             <div id="step1Indicator" class="flex items-center justify-center w-8 h-8 rounded-full bg-sky-600 text-white text-sm font-medium">1</div>
-            <div class="w-16 h-1 bg-slate-200" id="line1"></div>
+            <div class="w-24 h-1 bg-slate-200" id="line1"></div>
             <div id="step2Indicator" class="flex items-center justify-center w-8 h-8 rounded-full bg-slate-200 text-slate-500 text-sm font-medium">2</div>
-            <div class="w-16 h-1 bg-slate-200" id="line2"></div>
-            <div id="step3Indicator" class="flex items-center justify-center w-8 h-8 rounded-full bg-slate-200 text-slate-500 text-sm font-medium">3</div>
           </div>
         </div>
 
@@ -929,64 +927,79 @@ input:checked + .slider:before {
           </div>
         </div>
 
-        <!-- Step 2: Identify LED -->
+        <!-- Step 2: Identify LED & Configure -->
         <div id="calibrationStep2" class="space-y-4 hidden">
           <h4 class="text-lg font-medium text-slate-800">Step 2: Identify LED Position</h4>
-          <p class="text-sm text-slate-600 mb-4">
-            The ball is now at the 0° reference position (perimeter). Click on the LED that is at this position in the circular diagram below.
-          </p>
-          <!-- LED Circle Visualization -->
-          <div class="flex justify-center my-6">
-            <svg id="ledCircle" viewBox="0 0 400 400" class="w-full max-w-md">
-              <!-- Circle representing LED strip -->
-              <circle cx="200" cy="200" r="150" fill="none" stroke="#e2e8f0" stroke-width="2"/>
-              <!-- 0° marker (East) -->
-              <line x1="350" y1="200" x2="370" y2="200" stroke="#0c7ff2" stroke-width="3"/>
-              <text x="380" y="205" fill="#0c7ff2" font-size="14" font-weight="bold">0°</text>
-              <!-- LEDs will be added here by JavaScript -->
-            </svg>
-          </div>
-          <p id="selectedLedInfo" class="text-sm text-center text-slate-600">
-            No LED selected
+          <p class="text-sm text-slate-600">
+            The ball is at the 0° reference position. Use the buttons below to navigate through your LEDs until the <strong>lit LED matches the ball's position</strong>.
           </p>
-        </div>
 
-        <!-- Step 3: Test Direction -->
-        <div id="calibrationStep3" class="space-y-4 hidden">
-          <h4 class="text-lg font-medium text-slate-800">Step 3: Test Direction</h4>
-          <p class="text-sm text-slate-600 mb-4">
-            We'll move the ball to 90° to test LED direction. Watch which way the LEDs move.
-          </p>
-          <button
-            id="testDirection"
-            class="flex items-center justify-center gap-2 w-full cursor-pointer rounded-lg h-12 px-4 bg-sky-600 hover:bg-sky-700 text-white text-base font-medium transition-colors"
-          >
-            <span class="material-icons text-xl">rotate_right</span>
-            <span>Move to 90° and Test</span>
-          </button>
-          <div id="directionTestStatus" class="hidden text-sm text-slate-600 text-center">
-            <span class="inline-block animate-spin material-icons text-sky-600">refresh</span>
-            Testing direction...
+          <!-- Current LED Display -->
+          <div class="bg-slate-100 dark:bg-slate-700 rounded-lg p-4 text-center">
+            <p class="text-xs text-slate-600 dark:text-slate-400 mb-1">Currently Selected LED</p>
+            <p class="text-4xl font-bold text-sky-600" id="currentLedNumber">0</p>
+            <p class="text-xs text-slate-500 dark:text-slate-400 mt-2" id="ledStatusText">This LED is now lit on your strip</p>
           </div>
-          <div id="directionQuestion" class="hidden space-y-4 mt-4">
-            <p class="text-sm text-slate-700 font-medium text-center">Did the LED move clockwise?</p>
-            <div class="grid grid-cols-2 gap-4">
+
+          <!-- Navigation Controls -->
+          <div class="space-y-3">
+            <p class="text-xs font-medium text-slate-700 dark:text-slate-300 text-center">Navigate LEDs</p>
+            <div class="grid grid-cols-2 gap-3">
               <button
-                id="directionClockwise"
-                class="flex items-center justify-center gap-2 cursor-pointer rounded-lg h-12 px-4 bg-green-600 hover:bg-green-700 text-white text-base font-medium transition-colors"
+                id="ledPrev1"
+                class="flex items-center justify-center gap-2 h-12 px-4 rounded-lg border-2 border-slate-300 hover:border-sky-500 hover:bg-sky-50 dark:hover:bg-sky-900 text-slate-700 dark:text-slate-300 font-medium transition-all"
               >
-                <span class="material-icons">check_circle</span>
-                <span>Yes, Clockwise</span>
+                <span class="material-icons">chevron_left</span>
+                <span>← 1 LED</span>
               </button>
               <button
-                id="directionCounterClockwise"
-                class="flex items-center justify-center gap-2 cursor-pointer rounded-lg h-12 px-4 bg-orange-600 hover:bg-orange-700 text-white text-base font-medium transition-colors"
+                id="ledNext1"
+                class="flex items-center justify-center gap-2 h-12 px-4 rounded-lg border-2 border-slate-300 hover:border-sky-500 hover:bg-sky-50 dark:hover:bg-sky-900 text-slate-700 dark:text-slate-300 font-medium transition-all"
               >
-                <span class="material-icons">sync</span>
-                <span>No, Reverse It</span>
+                <span>1 LED →</span>
+                <span class="material-icons">chevron_right</span>
               </button>
+              <button
+                id="ledPrev5"
+                class="flex items-center justify-center gap-2 h-12 px-4 rounded-lg border-2 border-slate-300 hover:border-sky-500 hover:bg-sky-50 dark:hover:bg-sky-900 text-slate-700 dark:text-slate-300 font-medium transition-all"
+              >
+                <span class="material-icons">fast_rewind</span>
+                <span>← 5 LEDs</span>
+              </button>
+              <button
+                id="ledNext5"
+                class="flex items-center justify-center gap-2 h-12 px-4 rounded-lg border-2 border-slate-300 hover:border-sky-500 hover:bg-sky-50 dark:hover:bg-sky-900 text-slate-700 dark:text-slate-300 font-medium transition-all"
+              >
+                <span>5 LEDs →</span>
+                <span class="material-icons">fast_forward</span>
+              </button>
+            </div>
+          </div>
+
+          <!-- Direction Reversal -->
+          <div class="border-t border-slate-200 pt-4 mt-4">
+            <div class="flex items-center justify-between p-3 bg-amber-50 border border-amber-200 rounded-lg">
+              <div class="flex-1">
+                <h5 class="text-sm font-medium text-slate-800">Reverse LED Direction</h5>
+                <p class="text-xs text-slate-600 mt-1">
+                  Enable if your LED strip runs counter-clockwise
+                </p>
+              </div>
+              <label class="switch">
+                <input type="checkbox" id="reverseDirectionToggle">
+                <span class="slider round"></span>
+              </label>
             </div>
           </div>
+
+          <!-- Confirm Button -->
+          <button
+            id="confirmCalibration"
+            class="flex items-center justify-center gap-2 w-full h-12 px-4 rounded-lg bg-green-600 hover:bg-green-700 text-white text-base font-medium transition-colors mt-4"
+          >
+            <span class="material-icons">check_circle</span>
+            <span>Confirm Calibration</span>
+          </button>
         </div>
 
         <!-- Completion -->