Pastebin
Retrouvez, créez et partagez vos snippets en temps réel.
Rechercher un Pastebin
Aucun paste trouvé.
Créer un paste
Pastebin
Blog
cds
<style> /* --- THEME BLANC / CLAIR --- */ .atv-panel { font-family: 'Helvetica Neue', Arial, sans-serif; background: #ffffff; color: #333; padding: 15px; border-radius: 8px; border: 1px solid #e0e0e0; box-shadow: 0 2px 12px rgba(0,0,0,0.08); height: 100%; display: flex; flex-direction: column; justify-content: space-between; } .header { display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #f0f0f0; padding-bottom: 10px; margin-bottom: 15px; } .title { font-weight: 600; color: #555; font-size: 14px; text-transform: uppercase; } /* Zone Centrale */ .lcd-screen { background: #f9f9f9; border: 1px solid #ddd; border-radius: 6px; padding: 10px; margin-bottom: 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; } .lcd-main { font-family: 'Roboto Mono', monospace; font-size: 32px; font-weight: bold; color: #27ae60; line-height: 1.2; } .lcd-sub { font-family: 'Roboto Mono', monospace; font-size: 18px; color: #7f8c8d; font-weight: 500; margin-top: 5px; border-top: 1px dashed #ccc; padding-top: 5px; width: 80%; text-align: center; } .unit { font-size: 14px; color: #999; font-weight: normal; } /* Boutons */ .controls-row { display: flex; gap: 12px; margin-bottom: 20px; } .btn-custom { flex: 1; padding: 14px; border: 1px solid #ddd; border-radius: 6px; background: #f0f0f0; color: #666; font-weight: 600; cursor: pointer; transition: all 0.2s; text-transform: uppercase; font-size: 12px; display: flex; flex-direction: column; align-items: center; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .btn-custom.active-green { background: #2ecc71; color: white; border-color: #27ae60; } .btn-custom.active-blue { background: #3498db; color: white; border-color: #2980b9; } .btn-custom.active-red { background: #e74c3c; color: white; border-color: #c0392b; } /* Slider */ .slider-container { padding: 10px 0; background: #fafafa; border-radius: 8px; padding: 10px; border: 1px solid #eee; } input[type=range] { -webkit-appearance: none; width: 100%; background: transparent; } input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; height: 22px; width: 22px; border-radius: 50%; background: #3498db; cursor: pointer; margin-top: -9px; box-shadow: 0 0 5px rgba(0,0,0,0.2); } input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 4px; background: #ddd; border-radius: 2px; } .slider-infos { display: flex; justify-content: space-between; font-size: 11px; color: #888; margin-top: 8px; font-weight: 500; } </style> <div class="atv-panel"> <div class="header"> <span class="title">Contrôle Moteur</span> <div style="width:10px; height:10px; border-radius:50%;" ng-style="{'background': status.enable ? '#2ecc71' : '#ccc'}"> </div> </div> <div class="lcd-screen"> <div class="lcd-main"> {{ displaySpeed }} <span class="unit">Hz (x10)</span> </div> <div class="lcd-sub"> {{ displaySpeed * 3 }} <span class="unit">tr/min</span> </div> </div> <div class="controls-row"> <button class="btn-custom" ng-class="{'active-green': status.enable}" ng-click="toggleEnable()"> {{ status.enable ? 'MARCHE' : 'ARRÊT' }} </button> <button class="btn-custom" ng-class="{'active-blue': status.forward, 'active-red': !status.forward}" ng-click="toggleDirection()"> {{ status.forward ? 'AVANT' : 'ARRIÈRE' }} </button> </div> <div class="slider-container" style="opacity: {{ status.enable ? 1 : 0.5 }}"> <input type="range" min="0" max="500" step="1" ng-model="status.speedRef" ng-change="updateSpeed()" ng-disabled="!status.enable"> <div class="slider-infos"> <span>0</span> <span style="color:#3498db">Consigne : {{ status.speedRef }}</span> <span>500</span> </div> </div> </div> <script> (function(scope) { scope.status = scope.status || { enable: false, forward: true, speedRef: 0 }; scope.displaySpeed = 0; var graphInterval; // Variable pour stocker le timer // --- RECEPTION DONNEES (Function 5) --- scope.$watch('msg', function(data) { if (data && (data.topic === 'feedback' || typeof data.payload === 'number')) { // ICI: On remet en positif pour l'écran, même si le graphe reçoit du négatif scope.displaySpeed = Math.abs(data.payload); } }); scope.toggleEnable = function() { scope.status.enable = !scope.status.enable; if(!scope.status.enable) { scope.status.speedRef = 0; scope.send({ topic: "set_speed", payload: 0 }); } scope.send({ topic: "set_enable", payload: scope.status.enable ? 1 : 0 }); }; scope.toggleDirection = function() { scope.status.forward = !scope.status.forward; scope.send({ topic: "set_direction", payload: scope.status.forward ? "fwd" : "rev" }); }; scope.updateSpeed = function() { // Envoi immédiat lors du mouvement scope.send({ topic: "set_speed", payload: parseInt(scope.status.speedRef) }); }; // --- BATTEMENT DE COEUR (PLATEAU GRAPHIQUE) --- // On envoie la valeur au graphique toutes les secondes if (!graphInterval) { graphInterval = setInterval(function() { // On calcule la consigne signée (Positive si Avant, Négative si Arrière) // Comme ça le trait de consigne suit la courbe de vitesse var signedRef = parseInt(scope.status.speedRef); if (!scope.status.forward) { signedRef = -signedRef; } // On envoie un topic spécial pour le graphique scope.send({ topic: "plot_consigne", payload: signedRef }); }, 1000); } // Nettoyage si on quitte la page (bonne pratique) scope.$on('$destroy', function() { if (graphInterval) clearInterval(graphInterval); }); })(scope); </script> // 1. Récupération de la valeur brute let rawValue = msg.payload; let signedValue = rawValue; // 2. Conversion Modbus 16-bit (Complément à 2) // Si > 32767, c'est un nombre négatif (ex: 65535 = -1) if (rawValue > 32767) { signedValue = rawValue - 65536; } [cite_start]// 3. ENVOI AU GRAPHIQUE (On garde le SIGNÉ ici) [cite: 1] // On veut que la courbe descende sous zéro. msg.payload = signedValue; // 4. Important pour le Template msg.topic = "feedback"; return msg;
Créé il y a 1 mois.