forked from jchomaz/Vulture
Tracking de l'application VApp (IHM du jeu)
This commit is contained in:
10
VApp/src/components/BrainBlastBar.vue
Normal file
10
VApp/src/components/BrainBlastBar.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<v-app-bar :collapse="$route.name === 'Game Display (Projection)'" :elevation="5" height="50">
|
||||
<RouterMenu />
|
||||
<v-app-bar-title v-if="$route.name !== 'Accueil'">Brain Blast</v-app-bar-title>
|
||||
</v-app-bar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import RouterMenu from '@/components/RouterMenu.vue'
|
||||
</script>
|
||||
120
VApp/src/components/BuzzerWatcherCard.vue
Normal file
120
VApp/src/components/BuzzerWatcherCard.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div class="label-pos">
|
||||
<v-label class="labelTitle-style">Buzzer connectés</v-label>
|
||||
</div>
|
||||
<v-row no-gutters justify="space-around" class="button-pos">
|
||||
<v-icon v-bind:color="redBuzzerState == 1 ? 'RedBuzzer' : 'DisconnectedBuzzer'">mdi-radiobox-marked</v-icon>
|
||||
<v-icon v-bind:color="blueBuzzerState == 1 ? 'BlueBuzzer' : 'DisconnectedBuzzer'">mdi-radiobox-marked</v-icon>
|
||||
<v-icon v-bind:color="yellowBuzzerState == 1 ? 'YellowBuzzer' : 'DisconnectedBuzzer'">mdi-radiobox-marked</v-icon>
|
||||
<v-icon v-bind:color="greenBuzzerState == 1 ? 'GreenBuzzer' : 'DisconnectedBuzzer'">mdi-radiobox-marked</v-icon>
|
||||
</v-row>
|
||||
<v-divider :thickness="2" class="border-opacity-100" color="primary"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { subscribeToTopic } from '@/services/mqttService'
|
||||
import { onMounted, ref, onUnmounted } from 'vue';
|
||||
|
||||
// États réactifs pour chaque buzzer
|
||||
let redBuzzerState = ref(0);
|
||||
let blueBuzzerState = ref(0);
|
||||
let greenBuzzerState = ref(0);
|
||||
let yellowBuzzerState = ref(0);
|
||||
|
||||
// État pour surveiller la connexion générale
|
||||
let connectionStatus = ref("connected"); // "connected" ou "disconnected"
|
||||
|
||||
// Variable pour gérer le timeout global
|
||||
let globalTimeoutHandle = null;
|
||||
|
||||
// Fonction pour réinitialiser le timeout global
|
||||
function resetGlobalTimeout() {
|
||||
// Effacer le timeout précédent, s'il existe
|
||||
if (globalTimeoutHandle) {
|
||||
clearTimeout(globalTimeoutHandle);
|
||||
}
|
||||
|
||||
// Redémarrer un timeout de 5 minutes (300000 ms)
|
||||
globalTimeoutHandle = setTimeout(() => {
|
||||
handleGlobalTimeout(); // Appel si aucun message MQTT reçu depuis 5 minutes
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
// Fonction à exécuter si le timeout global est atteint
|
||||
function handleGlobalTimeout() {
|
||||
console.log("Aucun message MQTT reçu depuis plus de 5 minutes !");
|
||||
connectionStatus.value = "disconnected"; // Indiquer que la connexion est perdue
|
||||
redBuzzerState.value = 0;
|
||||
blueBuzzerState.value = 0;
|
||||
yellowBuzzerState.value = 0;
|
||||
greenBuzzerState.value = 0;
|
||||
|
||||
// Tu peux ajouter ici d'autres actions, comme afficher une alerte
|
||||
}
|
||||
|
||||
// Fonction pour traiter chaque message reçu et réinitialiser le timeout
|
||||
function handleMessage(topic, message) {
|
||||
let parsedMessage;
|
||||
try {
|
||||
parsedMessage = JSON.parse(message);
|
||||
} catch (e) {
|
||||
console.error("Erreur d'analyse JSON:", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extraire les informations
|
||||
const { buzzer, status } = parsedMessage;
|
||||
|
||||
// Mettre à jour l'état des buzzers en fonction des messages
|
||||
switch (buzzer) {
|
||||
case 'redBuzzerIP':
|
||||
redBuzzerState.value = status === "online" ? 1 : 0;
|
||||
break;
|
||||
case 'blueBuzzerIP':
|
||||
blueBuzzerState.value = status === "online" ? 1 : 0;
|
||||
break;
|
||||
case 'yellowBuzzerIP':
|
||||
yellowBuzzerState.value = status === "online" ? 1 : 0;
|
||||
break;
|
||||
case 'greenBuzzerIP':
|
||||
greenBuzzerState.value = status === "online" ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Réinitialiser le timeout global car un message a été reçu
|
||||
connectionStatus.value = "connected"; // Rétablir le statut de connexion
|
||||
resetGlobalTimeout();
|
||||
}
|
||||
|
||||
// S'abonner au topic lorsque le composant est monté
|
||||
onMounted(() => {
|
||||
subscribeToTopic('buzzer/watcher', (topic, message) => {
|
||||
handleMessage(topic, message);
|
||||
resetGlobalTimeout(); // Réinitialiser le timeout global au démarrage
|
||||
});
|
||||
});
|
||||
|
||||
// Nettoyer le timeout global lorsque le composant est démonté
|
||||
onUnmounted(() => {
|
||||
if (globalTimeoutHandle) {
|
||||
clearTimeout(globalTimeoutHandle);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.label-pos {
|
||||
padding-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.labelTitle-style {
|
||||
font-size: 20px !important;
|
||||
font-weight: 500;
|
||||
color: #e91e1e !important;
|
||||
opacity: 90% !important;
|
||||
}
|
||||
.button-pos {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
114
VApp/src/components/CardButtonScore.vue
Normal file
114
VApp/src/components/CardButtonScore.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
|
||||
<v-card-title class="card__title primary" @click="toggleCardSize">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-calculator-variant</v-icon>
|
||||
Gestion des scores
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn red xs12 sm6 md3" topic="game/score/update" message='{"Red": "-2"}'>
|
||||
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3" topic="game/score/update" message='{"Red": "-1"}'>
|
||||
<v-icon left size="40">mdi-minus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3" topic="game/score/update" message='{"Red": "+1"}'>
|
||||
<v-icon left size="40">mdi-plus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3 " topic="game/score/update" message='{"Red": "+2"}'>
|
||||
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="game/score/update" message='{"Blue": "-2"}'>
|
||||
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="game/score/update" message='{"Blue": "-1"}'>
|
||||
<v-icon left size="40">mdi-minus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="game/score/update" message='{"Blue": "+1"}'>
|
||||
<v-icon left size="40">mdi-plus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="game/score/update" message='{"Blue": "+2"}'>
|
||||
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn yellow card xs12 sm6 md3 " topic="game/score/update" message='{"Yellow": "-2"}'>
|
||||
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn yellow card xs12 sm6 md3 " topic="game/score/update" message='{"Yellow": "-1"}'>
|
||||
<v-icon left size="40">mdi-minus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn yellow card xs12 sm6 md3 " topic="game/score/update" message='{"Yellow": "+1"}'>
|
||||
<v-icon left size="40">mdi-plus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn yellow card xs12 sm6 md3 " topic="game/score/update" message='{"Yellow": "+2"}'>
|
||||
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="game/score/update" message='{"Green": "-2"}'>
|
||||
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="game/score/update" message='{"Green": "-1"}'>
|
||||
<v-icon left size="40">mdi-minus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="game/score/update" message='{"Green": "+1"}'>
|
||||
<v-icon left size="40">mdi-plus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="game/score/update" message='{"Green": "2"}'>
|
||||
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MqttButton from './MqttButton.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Variable pour contrôler l'état de la carte
|
||||
const isCardReduced = ref(false);
|
||||
|
||||
// Méthode pour basculer l'état de la carte
|
||||
function toggleCardSize() {
|
||||
isCardReduced.value = !isCardReduced.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.card--reduced {
|
||||
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
|
||||
width: 170px;
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
53
VApp/src/components/CardControl.vue
Normal file
53
VApp/src/components/CardControl.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
|
||||
<v-card-title class="card__title primary" @click="toggleCardSize">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-camera-control</v-icon>
|
||||
Contrôle du jeu
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="5" class="mt-4">
|
||||
<mqtt-button width="150" height="90" class="btn red" topic="/display/control" message="previous">
|
||||
<v-icon left size="60">mdi-skip-previous</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="5" class="mt-4">
|
||||
<mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="next">
|
||||
<v-icon left size="60">mdi-skip-next</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="5" class="mb-4">
|
||||
<mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="pause">
|
||||
<v-icon left size="60">mdi-pause</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="5" class="mb-4">
|
||||
<mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="play">
|
||||
<v-icon left size="60">mdi-play</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MqttButton from './MqttButton.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Variable pour contrôler l'état de la carte
|
||||
const isCardReduced = ref(false);
|
||||
|
||||
// Méthode pour basculer l'état de la carte
|
||||
function toggleCardSize() { isCardReduced.value = !isCardReduced.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.card--reduced {
|
||||
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
|
||||
width: 170px;
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
64
VApp/src/components/CardCurrentQuizz.vue
Normal file
64
VApp/src/components/CardCurrentQuizz.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div class="label-pos">
|
||||
<v-label class="labelTitle-style pb-4">Nom du Quizz</v-label>
|
||||
</div>
|
||||
|
||||
<!-- Sélection + Bouton -->
|
||||
<div class="select-style-div">
|
||||
<v-select
|
||||
label="Select"
|
||||
:items="quizzList"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
class="v-select-color"
|
||||
/>
|
||||
<v-btn color="primary" @click="publisCollectMessage" text="Mettre à jour"></v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { subscribeToTopic, publishMessage } from '@/services/mqttService';
|
||||
|
||||
const mqttQuizzCollectorList = 'game/quizz-collector/list';
|
||||
const mqttQuizzCollectorCmd = 'game/quizz-collector/cmd';
|
||||
|
||||
const quizzList = ref([]);
|
||||
|
||||
// Fonction pour mettre à jour la liste
|
||||
const handleMessage = (topic, message) => {
|
||||
try {
|
||||
quizzList.value = JSON.parse(message.toString());
|
||||
} catch (error) {
|
||||
console.error('Erreur de parsing JSON:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Fonction pour republier la commande "Collect"
|
||||
const publisCollectMessage = () => {
|
||||
publishMessage(mqttQuizzCollectorCmd, 'Collect');
|
||||
};
|
||||
|
||||
// Au montage du composant
|
||||
onMounted(() => {
|
||||
subscribeToTopic(mqttQuizzCollectorList, (topic, message) => {
|
||||
handleMessage(topic, message);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
publisCollectMessage();
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.select-style-div {
|
||||
padding: 2% 10% 2% 10%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.v-select-color {
|
||||
color: rgba(var(--v-theme-primary), 1.0);
|
||||
opacity: 100%;
|
||||
}
|
||||
</style>
|
||||
218
VApp/src/components/CardScore.vue
Normal file
218
VApp/src/components/CardScore.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="label-pos">
|
||||
<v-label class="labelTitle-style pb-4">Scores</v-label>
|
||||
</div>
|
||||
<!-- Équipes Rouges et Bleues côte à côte -->
|
||||
<v-row no-gutters class="scorebox-pos"> <!-- Équipe Rouge -->
|
||||
<v-col cols="6"> <!-- Colonnes de taille 6 pour chaque équipe -->
|
||||
<v-row no-gutters>
|
||||
<v-col class="scorediv-style-red">
|
||||
<div>
|
||||
<v-label class="labelRoundScore-style pt-3">Manche</v-label>
|
||||
<div>
|
||||
<v-label class="labelRoundScore-style">{{ scores.RedRoundScore }}</v-label>
|
||||
</div>
|
||||
</div>
|
||||
<v-divider color="background"/>
|
||||
<div>
|
||||
<v-label class="labelTotalScore-style pt-3">Total</v-label>
|
||||
<div>
|
||||
<v-label class="labelTotalScore-style pb-3">{{ scores.RedTotalScore }}</v-label>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
<!-- Équipe Bleue -->
|
||||
<v-col cols="6">
|
||||
<v-row no-gutters>
|
||||
<v-col class="scorediv-style-blue">
|
||||
<div>
|
||||
<v-label class="labelRoundScore-style pt-3">Manche</v-label>
|
||||
<div>
|
||||
<v-label class="labelRoundScore-style">{{ scores.BlueRoundScore }}</v-label>
|
||||
</div>
|
||||
</div>
|
||||
<v-divider color="background"/>
|
||||
<div>
|
||||
<v-label class="labelTotalScore-style pt-3">Total</v-label>
|
||||
<div>
|
||||
<v-label class="labelTotalScore-style pb-3">{{ scores.BlueTotalScore }}</v-label>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<!-- Équipes Oranges et Vertes côte à côte -->
|
||||
<v-row no-gutters class="scorebox-pos">
|
||||
<!-- Équipe Orange -->
|
||||
<v-col cols="6">
|
||||
<v-row no-gutters>
|
||||
<v-col class="scorediv-style-yellow">
|
||||
<div>
|
||||
<v-label class="labelRoundScore-style pt-3">Manche</v-label>
|
||||
<div>
|
||||
<v-label class="labelRoundScore-style">{{ scores.OrangeRoundScore }}</v-label>
|
||||
</div>
|
||||
</div>
|
||||
<v-divider color="background"/>
|
||||
<div>
|
||||
<v-label class="labelTotalScore-style pt-3">Total</v-label>
|
||||
<div>
|
||||
<v-label class="labelTotalScore-style pb-3">{{ scores.OrangeTotalScore }}</v-label>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
<!-- Équipe Verte -->
|
||||
<v-col cols="6">
|
||||
<v-row no-gutters>
|
||||
<v-col class="scorediv-style-green">
|
||||
<div>
|
||||
<v-label class="labelRoundScore-style pt-3">Manche</v-label>
|
||||
<div>
|
||||
<v-label class="labelRoundScore-style">{{ scores.GreenRoundScore }}</v-label>
|
||||
</div>
|
||||
</div>
|
||||
<v-divider color="background"/>
|
||||
<div>
|
||||
<v-label class="labelTotalScore-style pt-3">Total</v-label>
|
||||
<div>
|
||||
<v-label class="labelTotalScore-style pb-3">{{ scores.GreenTotalScore }}</v-label>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, reactive } from 'vue';
|
||||
import variables from '@/variables.js';
|
||||
import mqtt from 'mqtt'
|
||||
import config from '@/config.js'
|
||||
|
||||
const mqttBrokerUrl = config.mqttBrokerUrl
|
||||
// Créer une instance de client MQTT
|
||||
const client = mqtt.connect(mqttBrokerUrl)
|
||||
|
||||
// Déclaration des variables locales pour les scores
|
||||
const scores = reactive({
|
||||
RedTotalScore: 0, // Propriétés réactives
|
||||
BlueTotalScore: 0, // Propriétés réactives
|
||||
OrangeTotalScore: 0, // Propriétés réactives
|
||||
GreenTotalScore: 0, // Propriétés réactives
|
||||
RedRoundScore: 0, // Propriétés réactives
|
||||
BlueRoundScore: 0, // Propriétés réactives
|
||||
OrangeRoundScore: 0, // Propriétés réactives
|
||||
GreenRoundScore: 0, // Propriétés réactives
|
||||
});
|
||||
// Fonction pour traiter chaque message reçu et réinitialiser le timeout
|
||||
function handleMessage(topic, message) {
|
||||
let parsedMessage;
|
||||
try {
|
||||
parsedMessage = JSON.parse(message);
|
||||
} catch (e) {
|
||||
console.error("Erreur d'analyse JSON:", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extraire les informations
|
||||
//const { TEAM, Name } = parsedMessage;
|
||||
scores.RedTotalScore = parsedMessage.TEAM.Red.TotalScore
|
||||
scores.BlueTotalScore = parsedMessage.TEAM.Blue.TotalScore
|
||||
scores.YellowTotalScore = parsedMessage.TEAM.Yellow.TotalScore
|
||||
scores.GreenTotalScore = parsedMessage.TEAM.Green.TotalScore
|
||||
|
||||
scores.RedRoundScore = parsedMessage.TEAM.Red.RoundScore
|
||||
scores.BlueRoundScore = parsedMessage.TEAM.Blue.RoundScore
|
||||
scores.YellowRoundScore = parsedMessage.TEAM.Yellow.RoundScore
|
||||
scores.GreenRoundScore = parsedMessage.TEAM.Green.RoundScore
|
||||
// Mettre à jour l'état des buzzers en fonction des messages
|
||||
/*
|
||||
switch (buzzer) {
|
||||
case 'redBuzzerIP':
|
||||
redBuzzerState.value = status === "online" ? 1 : 0;
|
||||
break;
|
||||
case 'blueBuzzerIP':
|
||||
blueBuzzerState.value = status === "online" ? 1 : 0;
|
||||
break;
|
||||
case 'yellowBuzzerIP':
|
||||
yellowBuzzerState.value = status === "online" ? 1 : 0;
|
||||
break;
|
||||
case 'greenBuzzerIP':
|
||||
greenBuzzerState.value = status === "online" ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
function subscribeToTopic(topic, callback) {
|
||||
client.subscribe(topic)
|
||||
client.on('message', (receivedTopic, message) => { callback(receivedTopic.toString(), message.toString())
|
||||
})
|
||||
}
|
||||
// S'abonner au topic lorsque le composant est monté
|
||||
onMounted(() => {
|
||||
subscribeToTopic('game/score', (topic, message) => {
|
||||
handleMessage(topic, message);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.label-pos {
|
||||
padding-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.labelTitle-style {
|
||||
font-size: 20px !important;
|
||||
font-weight: 500;
|
||||
color: #d42828 !important;
|
||||
opacity: 90% !important;
|
||||
}
|
||||
.labelRoundScore-style {
|
||||
opacity: 100% !important;
|
||||
font-size: 25px !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
.labelTotalScore-style {
|
||||
opacity: 100% !important;
|
||||
font-size: 15px !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
.button-pos {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.scorebox-pos {
|
||||
text-align: center;
|
||||
}
|
||||
.scorediv-style-red {
|
||||
background-color: #d42828 !important;
|
||||
padding: 15px;
|
||||
border-top-left-radius: 10%;
|
||||
}
|
||||
.scorediv-style-yellow {
|
||||
background-color: #d4d100 !important;
|
||||
padding: 15px;
|
||||
border-bottom-left-radius: 10%;
|
||||
}
|
||||
.scorediv-style-blue {
|
||||
background-color: #2867d4 !important;
|
||||
padding: 15px;
|
||||
border-top-right-radius: 10%;
|
||||
}
|
||||
.scorediv-style-green {
|
||||
background-color: #28d42e !important;
|
||||
padding: 15px;
|
||||
border-bottom-right-radius: 10%;
|
||||
}
|
||||
</style>
|
||||
46
VApp/src/components/CardSolution.vue
Normal file
46
VApp/src/components/CardSolution.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
|
||||
<v-card-title class="card__title primary" @click="toggleCardSize">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-play-network-outline</v-icon>
|
||||
Solution </v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-container class="text-center"> <!-- Utilisation de styles CSS personnalisés pour centrer l'image -->
|
||||
<v-img width="450" src="@/assets/copilot-solution-FULL-HD.jpg" style="margin: 0 auto;">
|
||||
</v-img>
|
||||
</v-container>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.image-container {
|
||||
width: 300px; overflow: hidden; /* Pour masquer le dépassement de l'image */
|
||||
border: 1px solid #ccc; /* Bordure de l'image */
|
||||
}
|
||||
.image-container img {
|
||||
width: 100%; /* Pour remplir complètement le conteneur */
|
||||
height: auto; /* Pour maintenir le ratio d'aspect de l'image */
|
||||
display: block; /* Pour éviter l'espace réservé pour les images */
|
||||
}
|
||||
}
|
||||
.card--reduced {
|
||||
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
|
||||
width: 160px;
|
||||
overflow: hidden;
|
||||
transition: height 0.6s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Variable pour contrôler l'état de la carte
|
||||
const isCardReduced = ref(false);
|
||||
|
||||
// Méthode pour basculer l'état de la carte
|
||||
function toggleCardSize() {
|
||||
isCardReduced.value = !isCardReduced.value;
|
||||
}
|
||||
</script>
|
||||
61
VApp/src/components/CardSoundboard.vue
Normal file
61
VApp/src/components/CardSoundboard.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
|
||||
<v-card-title class="card__title primary" @click="toggleCardSize">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-music-box-multiple</v-icon>
|
||||
Soundboard
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="good-response" rounded>
|
||||
<v-icon size="60">mdi-check-circle-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bad-response" rounded>
|
||||
<v-icon size="60">mdi-close-circle-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="timer" rounded>
|
||||
<v-icon size="60">mdi-timer-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="4" class="mb-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="applause" rounded>
|
||||
<v-icon size="60">mdi-human-handsup</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mb-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bell" rounded>
|
||||
<v-icon size="60">mdi-bell-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MqttButton from './MqttButton.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Variable pour contrôler l'état de la carte
|
||||
const isCardReduced = ref(false);
|
||||
|
||||
// Méthode pour basculer l'état de la carte
|
||||
function toggleCardSize() {
|
||||
isCardReduced.value = !isCardReduced.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card--reduced {
|
||||
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
|
||||
width: 190px;
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
100
VApp/src/components/CardTimer.vue
Normal file
100
VApp/src/components/CardTimer.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="timer">
|
||||
<v-label color="primary" class="labelTime-style" >{{ formatTime }}</v-label>
|
||||
</div>
|
||||
<v-row no-gutters justify="space-around" >
|
||||
<v-btn class="buttons" color="primary" icon="mdi-play" @click="startTimer"></v-btn>
|
||||
<v-btn color="primary" icon="mdi-pause" @click="pauseTimer"></v-btn>
|
||||
<v-btn color="primary" icon="mdi-restart" @click="resetTimer"></v-btn>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onBeforeUnmount } from 'vue';
|
||||
|
||||
const timerActive = ref(false);
|
||||
const startTime = ref(null);
|
||||
const currentTime = ref(null);
|
||||
const elapsedTime = ref(0);
|
||||
|
||||
const formatTime = computed(() => {
|
||||
let seconds = Math.floor(elapsedTime.value / 1000);
|
||||
let minutes = Math.floor(seconds / 60);
|
||||
let hours = Math.floor(minutes / 60);
|
||||
seconds = seconds % 60; minutes = minutes % 60;
|
||||
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||||
});
|
||||
|
||||
const pad = (number) => {
|
||||
return (number < 10 ? "0" : "") + number;
|
||||
};
|
||||
|
||||
const startTimer = () => {
|
||||
if (!timerActive.value) {
|
||||
timerActive.value = true;
|
||||
startTime.value = Date.now() - elapsedTime.value;
|
||||
updateTimer(); }
|
||||
};
|
||||
|
||||
const pauseTimer = () => {
|
||||
if (timerActive.value) {
|
||||
timerActive.value = false;
|
||||
clearInterval(currentTime.value); }
|
||||
};
|
||||
|
||||
const resetTimer = () => {
|
||||
elapsedTime.value = 0;
|
||||
timerActive.value = false;
|
||||
clearInterval(currentTime.value);
|
||||
};
|
||||
|
||||
const updateTimer = () => {
|
||||
currentTime.value = setInterval(() => {elapsedTime.value = Date.now() - startTime.value; }, 1000);
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => { clearInterval(currentTime.value);
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
const startTimer = () => {
|
||||
if (!timerActive.value) {
|
||||
timerActive.value = true;
|
||||
startTime.value = Date.now() - elapsedTime.value;
|
||||
updateTimer(); } };
|
||||
const pauseTimer = () => {
|
||||
if (timerActive.value) {
|
||||
timerActive.value = false;
|
||||
clearInterval(currentTime.value); } };
|
||||
const resetTimer = () => {
|
||||
elapsedTime.value = 0;
|
||||
timerActive.value = false;
|
||||
clearInterval(currentTime.value); };
|
||||
export { startTimer, pauseTimer, resetTimer };
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
text-align: center;
|
||||
margin-top: auto; /* Place le container en bas de son parent */
|
||||
margin-bottom: 1px; /* Marge en bas pour un espacement */
|
||||
position: fixed; /* Le positionne de manière fixe */
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
.timer {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.labelTime-style {
|
||||
font-size: 30px !important;
|
||||
font-weight: 500;
|
||||
color: #d42828 !important;
|
||||
opacity: 90% !important;
|
||||
}
|
||||
.buttons{
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
</style>
|
||||
19
VApp/src/components/GameStatus.vue
Normal file
19
VApp/src/components/GameStatus.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<v-navigation-drawer width="250">
|
||||
<BuzzerWatcherCard/>
|
||||
<CardScore/>
|
||||
<CardCurrentQuizz/>
|
||||
<CardTimer/>
|
||||
</v-navigation-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import CardTimer from '@/components/CardTimer.vue'
|
||||
import CardScore from '@/components/CardScore.vue'
|
||||
import BuzzerWatcherCard from '@/components/BuzzerWatcherCard.vue'
|
||||
import CardCurrentQuizz from '@/components/CardCurrentQuizz.vue'
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
118
VApp/src/components/MQTTColorPublisher.vue
Normal file
118
VApp/src/components/MQTTColorPublisher.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<v-container class="v-container-style">
|
||||
<v-card tile outlined width="500">
|
||||
<v-card-title class="card__title primary centered-title">
|
||||
<v-icon left class="pr-5 pl-2" size="30">mdi-send</v-icon>
|
||||
Publier une couleur
|
||||
</v-card-title>
|
||||
|
||||
<div class="input-style">
|
||||
<v-select
|
||||
label="Topic"
|
||||
v-model="selectedTopic"
|
||||
:items="topics"
|
||||
prepend-icon="mdi-target"
|
||||
></v-select>
|
||||
</div>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="6" class="color-picker-style">
|
||||
<div>
|
||||
<v-color-picker
|
||||
mode="hex"
|
||||
v-model="selectedColor"
|
||||
border="md"
|
||||
width="250"
|
||||
></v-color-picker>
|
||||
<v-btn
|
||||
class="v-btn-style-validate"
|
||||
height="35"
|
||||
@click="publishCustomColor"
|
||||
>
|
||||
Publier
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="4" class="button-container-2">
|
||||
<v-btn color="#D42828" class="team-button" @click="publishButtonColor('#D42828')">Team Rouge</v-btn>
|
||||
<v-btn color="#00FF1F" class="team-button" @click="publishButtonColor('#00FF1F')">Team Verte</v-btn>
|
||||
<v-btn color="#007AFF" class="team-button" @click="publishButtonColor('#007AFF')">Team Bleue</v-btn>
|
||||
<v-btn color="#FFFC00" class="team-button" @click="publishButtonColor('#FFFC00')">Team Jaune</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { publishMessage } from '@/services/mqttService';
|
||||
|
||||
const selectedTopic = ref('Selectionnez un topic');
|
||||
const selectedColor = ref('#FF0000');
|
||||
const topics = ref([
|
||||
'/wled/all',
|
||||
'/wled/1',
|
||||
'/wled/2',
|
||||
'/wled/3',
|
||||
'/wled/4',
|
||||
'/wled/5',
|
||||
]);
|
||||
|
||||
const publishCustomColor = () => {
|
||||
if (selectedTopic.value && selectedColor.value) {
|
||||
publishMessage(selectedTopic.value, selectedColor.value);
|
||||
} else {
|
||||
console.warn('Topic ou couleur non sélectionné !');
|
||||
}
|
||||
};
|
||||
|
||||
const publishButtonColor = (color) => {
|
||||
if (selectedTopic.value) {
|
||||
publishMessage(selectedTopic.value, color);
|
||||
} else {
|
||||
console.warn('Topic non sélectionné !');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-container-style {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.input-style {
|
||||
margin: 20px;
|
||||
}
|
||||
.v-btn-style-validate {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
background-color: rgba(var(--v-theme-primary));
|
||||
border-top-right-radius: 0%;
|
||||
border-top-left-radius: 0%;
|
||||
}
|
||||
.centered-title {
|
||||
text-align: center;
|
||||
}
|
||||
.color-picker-style {
|
||||
margin-left: 5%;
|
||||
margin-bottom: 5%;
|
||||
display: flex;
|
||||
}
|
||||
.button-container-2 {
|
||||
text-align: center;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 27px;
|
||||
margin-left: 15px;
|
||||
margin-right: 13px;
|
||||
}
|
||||
.team-button {
|
||||
width: 140px;
|
||||
display: block;
|
||||
margin: 12% auto 0;
|
||||
background-color: rgba(var(--v-theme-primary));
|
||||
}
|
||||
</style>
|
||||
136
VApp/src/components/MQTTDebugConsole.vue
Normal file
136
VApp/src/components/MQTTDebugConsole.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<v-container class="v-container-style-console">
|
||||
<v-card tile outlined width="500">
|
||||
<v-card-title class="card__title primary centered-title">
|
||||
<v-icon left class="pr-5 pl-2" size="40">mdi-console-line</v-icon>
|
||||
Console MQTT
|
||||
</v-card-title>
|
||||
|
||||
<div class="div_topic">
|
||||
<v-select
|
||||
density="compact"
|
||||
label="Topic"
|
||||
v-model="selectedTopic"
|
||||
:items="topics"
|
||||
prepend-icon="mdi-target"
|
||||
></v-select>
|
||||
|
||||
<div class="button_div_style">
|
||||
<v-btn rounded @click="resetTopicFilter" color="primary">Unfilter</v-btn>
|
||||
<v-btn
|
||||
rounded
|
||||
:class="{ 'scrolling-paused': scrollingState, 'scrolling-active': !scrollingState }"
|
||||
@click="toggleScrollingState"
|
||||
>
|
||||
<v-icon>{{ scrollingState ? 'mdi-pause' : 'mdi-play' }}</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-container class="text-center">
|
||||
<div v-for="(log, index) in filteredLogs" :key="index">
|
||||
<v-label class="v-label-timestamp">{{ log.timestamp }} - </v-label>
|
||||
<v-label class="v-label-topic-message-title">Topic : </v-label>
|
||||
<v-label class="v-label-topic-message">{{ log.topic }} </v-label>
|
||||
<v-label class="v-label-topic-message-title">Msg : </v-label>
|
||||
<v-label class="v-label-topic-message">{{ log.message }}</v-label>
|
||||
</div>
|
||||
</v-container>
|
||||
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { subscribeToTopic } from '@/services/mqttService';
|
||||
|
||||
// Data
|
||||
const messageLogs = ref([]);
|
||||
const selectedTopic = ref('');
|
||||
const topics = ref([]);
|
||||
const scrollingState = ref(true);
|
||||
|
||||
// Methods
|
||||
const handleMessage = (topic, message) => {
|
||||
const timestamp = new Date().toLocaleString('fr-FR', {
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
|
||||
if (scrollingState.value) {
|
||||
if (!topics.value.includes(topic)) {
|
||||
topics.value.push(topic);
|
||||
}
|
||||
|
||||
messageLogs.value.unshift({ timestamp, topic, message });
|
||||
|
||||
if (messageLogs.value.length > 20) {
|
||||
messageLogs.value.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resetTopicFilter = () => {
|
||||
selectedTopic.value = '';
|
||||
};
|
||||
|
||||
const toggleScrollingState = () => {
|
||||
scrollingState.value = !scrollingState.value;
|
||||
};
|
||||
|
||||
// Computed
|
||||
const filteredLogs = computed(() => {
|
||||
if (!selectedTopic.value) {
|
||||
return messageLogs.value;
|
||||
}
|
||||
return messageLogs.value.filter((log) => log.topic === selectedTopic.value);
|
||||
});
|
||||
|
||||
// Lifecycle
|
||||
subscribeToTopic('#', (topic, message) => {
|
||||
handleMessage(topic, message);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.scrolling-paused {
|
||||
background-color: rgba(var(--v-theme-primary)) !important;
|
||||
}
|
||||
.scrolling-active {
|
||||
background-color: rgba(var(--v-theme-success)) !important;
|
||||
}
|
||||
.button_div_style {
|
||||
justify-content: space-evenly;
|
||||
display: flex;
|
||||
}
|
||||
.div_topic {
|
||||
text-align: center !important;
|
||||
padding: 5% 4% 1% 4%;
|
||||
}
|
||||
.v-container-style-console {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: sticky;
|
||||
top: 50px;
|
||||
}
|
||||
.centered-title {
|
||||
text-align: center;
|
||||
}
|
||||
.v-label-timestamp {
|
||||
opacity: 100%;
|
||||
font-style: oblique;
|
||||
font-weight: 400;
|
||||
color: #838383;
|
||||
}
|
||||
.v-label-topic-message-title {
|
||||
opacity: 100%;
|
||||
font-weight: 700;
|
||||
color: rgba(var(--v-theme-primary));
|
||||
}
|
||||
.v-label-topic-message {
|
||||
font-weight: 300;
|
||||
}
|
||||
</style>
|
||||
93
VApp/src/components/MQTTDebugPublish.vue
Normal file
93
VApp/src/components/MQTTDebugPublish.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<v-container class="v-container-style">
|
||||
<v-card tile outlined width="500">
|
||||
<v-card-title class="card__title primary centered-title">
|
||||
<v-icon left class="pr-5 pl-2" size="30">mdi-send</v-icon>
|
||||
Publier un message
|
||||
</v-card-title>
|
||||
|
||||
<div class="input-style">
|
||||
<v-select
|
||||
label="Topic"
|
||||
v-model="selectedTopic"
|
||||
:items="topics"
|
||||
prepend-icon="mdi-target"
|
||||
></v-select>
|
||||
|
||||
<v-text-field
|
||||
label="Message"
|
||||
v-model="message"
|
||||
prepend-icon="mdi-text-box"
|
||||
></v-text-field>
|
||||
</div>
|
||||
|
||||
<v-btn
|
||||
rounded
|
||||
class="v-btn-style-standalone"
|
||||
height="40"
|
||||
@click="publishBuzzerUnblock"
|
||||
>
|
||||
Déblocage<br>Buzzer
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
class="v-btn-style-validate"
|
||||
height="50"
|
||||
@click="publisCustomMessage"
|
||||
>
|
||||
Publier
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { publishMessage } from '@/services/mqttService';
|
||||
|
||||
// Reactive data
|
||||
const message = ref('');
|
||||
const selectedTopic = ref('Selectionnez un topic');
|
||||
const topics = [
|
||||
'wled/all',
|
||||
'display/control',
|
||||
'sound/playsound',
|
||||
'game/score/update',
|
||||
'game/score'
|
||||
];
|
||||
|
||||
// Methods
|
||||
const publisCustomMessage = () => {
|
||||
publishMessage(selectedTopic.value, message.value);
|
||||
};
|
||||
|
||||
const publishBuzzerUnblock = () => {
|
||||
publishMessage('brainblast/buzzer/unlock', "0");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-container-style {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.input-style {
|
||||
margin: 20px;
|
||||
}
|
||||
.v-btn-style-standalone {
|
||||
background-color: rgba(var(--v-theme-primary));
|
||||
margin-bottom: 5%;
|
||||
margin-left: 5%;
|
||||
}
|
||||
.v-btn-style-validate {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
background-color: rgba(var(--v-theme-primary));
|
||||
border-top-right-radius: 0%;
|
||||
border-top-left-radius: 0%;
|
||||
}
|
||||
.centered-title {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
22
VApp/src/components/MqttButton.vue
Normal file
22
VApp/src/components/MqttButton.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<v-btn @click="_publishMessage" v-bind="$attrs">
|
||||
<slot/>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { publishMessage } from '@/services/mqttService'
|
||||
import { ref, defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
topic: String,
|
||||
message: null
|
||||
})
|
||||
|
||||
const disabled = ref(false)
|
||||
|
||||
const _publishMessage = () => {
|
||||
publishMessage(props.topic, props.message)
|
||||
disabled.value = true
|
||||
}
|
||||
</script>
|
||||
29
VApp/src/components/RouterMenu.vue
Normal file
29
VApp/src/components/RouterMenu.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<v-app-bar-nav-icon v-on:click="menu = !menu"></v-app-bar-nav-icon>
|
||||
<v-menu v-model="menu" class="menu-below-bar">
|
||||
<v-list>
|
||||
<v-list-item v-for="route in filteredRoutes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const routes = router.options.routes;
|
||||
|
||||
let menu = ref(false);
|
||||
|
||||
// Filtrer les routes pour masquer une route spécifique (par exemple, 'RouteA')
|
||||
const filteredRoutes = computed(() => {
|
||||
return routes.filter(route => route.name !== 'Debugger MQTT');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu-below-bar {
|
||||
margin-top: 48px; /* La hauteur de la barre d'application */
|
||||
}
|
||||
</style>
|
||||
102
VApp/src/components/WSDebugControl.vue
Normal file
102
VApp/src/components/WSDebugControl.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<v-container class="v-container-style-console-dmx">
|
||||
<v-card tile outlined width="500">
|
||||
<v-card-title class="card__title primary centered-title">
|
||||
<v-icon left class="pr-5 pl-2" size="40">mdi-console-line</v-icon>
|
||||
Console DMX
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-label v-if="!isConnected">Connecting...</v-label>
|
||||
<div class="button-container">
|
||||
<v-btn class="v-btn-dmx white" @click="handleButtonPress(2, 1)" :disabled="!isConnected">Rouge</v-btn>
|
||||
<v-btn class="v-btn-dmx red" @click="handleButtonPress(7, 1)" :disabled="!isConnected">Rouge</v-btn>
|
||||
<v-btn class="v-btn-dmx green" @click="handleButtonPress(8, 1)" :disabled="!isConnected">Vert</v-btn>
|
||||
<v-btn class="v-btn-dmx blue" @click="handleButtonPress(9, 1)" :disabled="!isConnected">Blue</v-btn>
|
||||
</div>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { connectWebSocket, sendButtonPress } from '@/services/light-manager-DMX.js';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const websocketUrl = 'ws://192.168.1.30:9999/qlcplusWS';
|
||||
const isConnected = ref(false);
|
||||
|
||||
// Fonction pour connecter le WebSocket et mettre à jour l'état de la connexion
|
||||
const connect = () => {
|
||||
connectWebSocket(websocketUrl);
|
||||
isConnected.value = true; // Mettre à jour l'état de la connexion
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
connect();
|
||||
});
|
||||
|
||||
const handleButtonPress = (id, state) => {
|
||||
sendButtonPress(id, state);
|
||||
// Pause de 2 secondes
|
||||
setTimeout(() => {
|
||||
sendButtonPress(id, !state);
|
||||
}, 500); // 2000 millisecondes = 2 secondes
|
||||
|
||||
};
|
||||
|
||||
return {
|
||||
handleButtonPress,
|
||||
isConnected,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-container-style-console-dmx {
|
||||
justify-content: center;
|
||||
position: sticky;
|
||||
top: 50px; /* Distance depuis le haut de la fenêtre avant de "coller" */
|
||||
}
|
||||
.centered-title {
|
||||
text-align: center;
|
||||
}
|
||||
.v-label-timestamp{
|
||||
opacity: 100%;
|
||||
font-style: oblique;
|
||||
font-weight: 400;
|
||||
color: #838383;
|
||||
}
|
||||
.button-container{
|
||||
display: flex; /* Active le mode Flexbox */
|
||||
justify-content: space-around; /* Répartit les boutons équitablement */
|
||||
align-items: center; /* Aligne les boutons verticalement */
|
||||
text-align: center;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 27px;
|
||||
margin-left: 15px;
|
||||
margin-right: 13px;
|
||||
}
|
||||
.v-btn-dmx{
|
||||
width: 100px;
|
||||
margin: 2% auto 0; /* 5% de marge en bas pour espacer les boutons */
|
||||
|
||||
}
|
||||
.v-btn-dmx.white{
|
||||
background-color:#ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
.v-btn-dmx.red{
|
||||
background-color: rgba(var(--v-theme-primary));
|
||||
}
|
||||
|
||||
.v-btn-dmx.green{
|
||||
background-color: rgba(var(--v-theme-GreenBuzzer));
|
||||
}
|
||||
|
||||
.v-btn-dmx.blue{
|
||||
background-color: rgba(var(--v-theme-BlueBuzzer));
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user