[TUTO] Application web dynamique

Proposer ou rechercher un tutoriel concernant le Raspberry Pi

Modérateur : Francois

Bud Spencer
Raspinaute
Messages : 1089
Enregistré le : lun. 15 août 2016 21:38

Re: [TUTO] Application web dynamique

Message par Bud Spencer » mer. 13 mai 2020 22:43

Avec Javascript (qui est asynchrone) async et await permette entre autre d’exécuter des actions synchrones dans des fonctions asynchrones (ouais, je sais, c’est pas forcément évident à comprendre du premier coup). C’est aussi une aubaine pour se simplifier le codage notamment quand on a (avait) besoin de recourir à de l’imbrication de callbacks.

Pour résumer vite fait quand tu appelles une fonction async, elle s’exécute de façon totalement asynchrone (donc sans empêcher le reste du programme de continuer de tourner pour d’autres choses). A l’interieur de cette fonction, quand tu utilises une instruction await, ta fonction attend que cette instruction ait fini son boulot pour passer à l’instruction suivante
.
Les instructions await/async sont très utiles dans les concepts synchrones puisqu’elles permettent d’exécuter des taches asynchrones et a l’inverse tout aussi utile dans les concepts asynchrones puisqu’elles permettent d’exécuter des tâches qui contiennent des instructions synchrones. On ne parle plus ici de simple multithreading mais bien de parallélisation et de multitasking.

Exemple avec ta fonction qui joue de la musique. Elle est totalement asynchrone (qui ne bloque pas le reste du programme) mas toutes les instructions marquées await qui se trouvent à l’interieur de cette fonction attendent d’etre terminée pour passer à l’instruction suivante (la durée des notes et le délais entre chacune d’elles en l’occurrence).

Pour le reste, bien sûr, on pourrait ecrire ‘plus propre’, ou du moins plus organisé et plus flexible. Exemple, créer un fichier dédié pour les constantes de notes et un simple import(‘notes’) suffirait dans le programme principal, créer des fichiers de mélodies, remplacer la fonction loop par un objet qui pourrait recevoir ces mélodies ainsi que des ordres reçus par des bouton gpio (avance/retour rapide, pause, stop …), bref, il y a toujours moyens d’améliorer
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

BlackTom
Messages : 45
Enregistré le : jeu. 31 mars 2016 13:21

Re: [TUTO] Application web dynamique

Message par BlackTom » jeu. 14 mai 2020 00:48

Je suis d'accord avec toi pour les constantes de notes et les mélodies mais j'espérais améliorer la partie qui joue les notes. Je pensais qu'il y avait plus élégant que cette multitude de "await", "delay" et autres "async" ...
Bref, merci pour les explications.

Possesseur d'un RPi Zéro WH pour un projet de distributeur de croquettes pour chats


Bud Spencer
Raspinaute
Messages : 1089
Enregistré le : lun. 15 août 2016 21:38

Re: [TUTO] Application web dynamique

Message par Bud Spencer » jeu. 14 mai 2020 10:24

Soyons pragmatique.
Question:
Pourquoi tu as dû mettre des appel await Sleep après chaque note alors que la note a déjà un délais ?

Réponse:
Parce que tes appels PlayNote sont asynchrone (il sont non bloquant a l'intérieur de ta fonction loop qui elle s'exécute de facon asynchrone).

Question :
La meilleure idée et la bonne façon de faire est :
1 - Ajouter des appels Sleep synchrone (donc avec await) après chaque note (qui ont déjà une durée qui ne sert a rien)
2 – Rendre les appels PlayNote synchrone pour attendre la durée de la note avant de passer à la suivante.

T’as répondu 1, donc t’as perdu :P


Rend tes appel PlayNote synchrone en mettant await devant et supprime les appels Sleep qui ne servent à rien.

Avec un code bien structuré et surtout sans boucle while infinie, tu n’aurais besoins d’écrire qu’une seul fois await PlayNote(note) dans tout ton programme, tu pourrais détecter le changement d’état du bouton à n’importe quel moment de la mélodie (ce qui ‘est pas le cas ici) et bien d’autres choses encore, mais ... step by step ;)

un petit up tout bete pour comprendre le rôle de l'instruction await devant un appel . Il suffit juste de traduire await (qui veux dire attendre).
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

BlackTom
Messages : 45
Enregistré le : jeu. 31 mars 2016 13:21

Re: [TUTO] Application web dynamique

Message par BlackTom » jeu. 14 mai 2020 15:03

Ok, il reste encore à déporter les notes et la mélodie dans un fichier (ou deux) à part mais j'ai fait quelque chose qui me plait un peu plus.
Dis moi si tu trouves ça potable, stp.

Code : Tout sélectionner

var Gpio = require('pigpio').Gpio;
var buzz = new Gpio(21, {mode: Gpio.OUTPUT});
var button = new Gpio(7, {mode: Gpio.PUD_DOWN}); //use GPIO pin 7 as input for button

const delay = require('delay');

//FREQUENCIES

const cL=129;
const cLS=139;
const dL=146;
const dLS=156;
const eL=163;
const fL=173;
const fLS=185;
const gL=194;
const gLS=207;
const aL=219;
const aLS=228;
const bL=232;

const c=261;
const cS=277;
const d=294;
const dS=311;
const e=329;
const f=349;
const fS=370;
const g=391;
const gS=415;
const a=440;
const aS=455;
const b=466;

const cH=523;
const cHS=554;
const dH=587;
const dHS=622;
const eH=659;
const fH=698;
const fHS=740;
const gH=784;
const gHS=830;
const aH=880;
const aHS=910;
const bH=933;

const ImperialMarch_notes  = [ {freq: a, duree: 500},   
    {freq: a, duree: 500},   
    {freq: f, duree: 350},   
    {freq: cH, duree: 150},  
    {freq: a, duree: 500},  
    {freq: f, duree: 350},  
    // ... et des tas d'autres notes
    {freq: a, duree: 1000} ];


const sleep = (milliseconds) => {
    return new Promise(resolve => setTimeout(resolve, milliseconds))
  }

async function PlayNote(freq, duree)
{
    buzz.pwmFrequency(freq);

    buzz.pwmWrite(200);
    await sleep(duree);
    buzz.pwmWrite(0);
    await sleep(duree);

    buzz.pwmWrite(0);
    await sleep(20);
}

async function asyncForEach(array, callback) 
{
    for (let index = 0; index < array.length; index++) 
    {
        await callback(array[index], index, array);
    }
}

const PlayMelody = async () => {
    await asyncForEach(ImperialMarch_notes, async (note) => {
      await PlayNote(note.freq, note.duree);
    });
    console.log('Done');
  }

console.log("Appuyez sur le bouton quand vous voulez.");
console.log("Ctrl+C pour sortir.");

async function loop()
{
    while(true)
    {
        etat = button.digitalRead();

        if ( etat == 1 )
        {
            PlayMelody(); // On appelle les fauves
            
            // Ici, on fera tourner le moteur pour distribuer les croquettes
        }
        await delay(50);
    }

}
loop();

Bud Spencer
Raspinaute
Messages : 1089
Enregistré le : lun. 15 août 2016 21:38

Re: [TUTO] Application web dynamique

Message par Bud Spencer » ven. 15 mai 2020 08:44

Oui, c’est bien ce que tu as fait. C‘est déjà beaucoup plus propre et plus concis. Perso j’aurais inclus l’itération de la liste des notes de la mélodie (asyncForeach) à l’intérieur de la fonction PlayMelody. Ça aurait permis de faire plus explicite en pouvant écrire un truc du genre PlayMelody = async (melodie) => … et de réduire les appel asynchrone entre fonctions.

Avec await/async tu utilises des fonctions très moderne en terme de méthode de programmation et c’est très bien, donc ne fais pas la bêtise de garder cette fonction loop avec sa boucle while. Ça s’est carrément dépassé et c’est un vrai goulet déjà dans un concept synchrone mais plus encore avec de l’asynchrone. Regarde plutôt du côté des timers et des évènements, tu vas voir que ça peu énormément élargir le potentiel de ton application. De mémoire j'ai bien expliqué ca notamment avec le code du data-logger (page 8 à 10) et beaucoup plus récemment avec les évènements pour les objets serveurs du projet redscreen (quelques pages en arrière)
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

BlackTom
Messages : 45
Enregistré le : jeu. 31 mars 2016 13:21

Re: [TUTO] Application web dynamique

Message par BlackTom » ven. 15 mai 2020 09:56

Bud Spencer a écrit :
ven. 15 mai 2020 08:44
Oui, c’est bien ce que tu as fait. C‘est déjà beaucoup plus propre et plus concis. Perso j’aurais inclus l’itération de la liste des notes de la mélodie (asyncForeach) à l’intérieur de la fonction PlayMelody. Ça aurait permis de faire plus explicite en pouvant écrire un truc du genre PlayMelody = async (melodie) => … et de réduire les appel asynchrone entre fonctions.
Tu veux dire comme ça ?

Code : Tout sélectionner

const PlayMelody = async (melody) => {
    for (let index = 0; index < melody.length; index++) 
    {
        note=melody[index];
        await PlayNote(note.freq, note.duree);
    }
    console.log('Done');
  }
Je ne suis pas sûr d'avoir compris ce que tu veux dire, sinon.
En tous cas, ça fonctionne !
Bud Spencer a écrit :
ven. 15 mai 2020 08:44
Avec await/async tu utilises des fonctions très moderne en terme de méthode de programmation et c’est très bien, donc ne fais pas la bêtise de garder cette fonction loop avec sa boucle while. Ça s’est carrément dépassé et c’est un vrai goulet déjà dans un concept synchrone mais plus encore avec de l’asynchrone. Regarde plutôt du côté des timers et des évènements, tu vas voir que ça peu énormément élargir le potentiel de ton application. De mémoire j'ai bien expliqué ca notamment avec le code du data-logger (page 8 à 10) et beaucoup plus récemment avec les évènements pour les objets serveurs du projet redscreen (quelques pages en arrière)
Pour ça, j'ai trouvé encore plus propre à mon sens :

Code : Tout sélectionner

button.glitchFilter(10000);
button.on('interrupt', (level) => {
    io.emit('feeding', 'start');
    console.log("T'as appuyé sur le bouton physique");
    feed( iNbDistrib );
    io.emit('feeding', 'end');
});
Pas de boucle while infinie, ni de fonction async à ce niveau là.

Pour mettre en perspective, il s'agit de tests pour mon projet de distributeur de croquettes pour chats.
En gros, si je suis à la maison, je peux appuyer sur un bouton physique pour déclencher la distribution. Si je suis à distance, j'ai une page web avec un bouton pour faire la même chose.
La distribution consiste à jouer quelques notes d'une mélodie pour avertir les monstres que la bouffe arrive, PUIS de faire tourner le moteur pas à pas pour donner une dose de croquettes. Le tout entrecoupé d'allumage de Led RGB.

Là où je suis rendu : La mélodie se joue proprement mais la LED clignote un peu n'importe comment. Encore ces histoires de synchronisation ...

Pour le moteur, je m'en occuperai peut-être ce week end. Même si c'est plus compliqué qu'un servo moteur, ça devrait aller.

Bud Spencer
Raspinaute
Messages : 1089
Enregistré le : lun. 15 août 2016 21:38

Re: [TUTO] Application web dynamique

Message par Bud Spencer » ven. 15 mai 2020 15:55

BlackTom a écrit :
ven. 15 mai 2020 09:56
]
Tu veux dire comme ça ?
Oui, dans ce genre là, mais allons encore plus loin.

NodeJs à cette merveilleuse faculté d'être asynchrone sur un seul thread. C’est ce qui lui permet nativement et à moindre code de faire des choses qui sont très compliqué voir impossible à faire avec beaucoup d’autre langage ou compilateur ou alors avec des besoin en code énorme en comparaison . Le problème, c’est que ça demande une réflexion différente.

En utilisant trop await, tu perds une bonne partie de l’énorme avantage de cette faculté puisque tu rends ton programme synchrone donc bloquant (du moins partiellement). En admettant que par facilité, tu gardes l’option de ‘await’ pour jouer chaque note, tu n’en as besoin nulle part ailleurs. Tu vas me dire ‘oui, mais si j’appelle pas PlayMelody avec await, il va me lancer la distri de croquette tout de suite derrière sans attendre la fin de la musique’ et tu auras raison. Sauf que la bonne méthode avec un concept asynchrone, ce n’est pas d’attendre qu’une tache soit terminée, c’est d’être informé quand elle se termine et la différence est énorme.

Avec ton bouton, tu as trouvé un npm qui te permet de recevoir des évènements quand il y a une action dessus. Ce n’est pas plus propre que ce que je te ‘recommande’ de faire, c’est exactement la même chose mais en l'étendant au jeu de la musique et même au distributeur. Plutôt que de faire un appel bloquant à PlayMelody tu devrais avoir un signal (évènement) quand la musique se termine exactement de la même façon qu'avec une action sur ton bouton.

Pour exemple, Si j’avais écrit un truc comme ça, j’aurais créé 2 objets. Un Objet Jukebox pour la musique et un objet Distributor pour le distributeur et voilà à peu près à quoi aurait ressembler mon programme principal :

MyJukebox = new jukebox() ;
MyDistributor = new Distributor() ;

MyJukebox.Events.on(‘end’ ()=>{ MyDistributor.Distribut() ;} ) ;

Myjukebox.PlayMelody(melodie) ;


En allant plus loin, création d’un un autre objet (Planning) qui génère un évènement en fonction d’un fichier de paramètre horaire et l’appel de la mélodie dans une fonction events.on de cet objet :
MyPlanning.Events.on(‘action’ ()=>{ Myjukebox.PlayMelody(melodie) ; }) ;

C'est la toutes la différence entre
synchrone : on exécute des choses et on attend quelles se termine en bloquant tout le reste
et
asynchrone : on lance l'exécution de choses sans rien bloquer et on reçoit l'information de fin quand elle se termine (et accessoirement des information intermédiaires).
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

BlackTom
Messages : 45
Enregistré le : jeu. 31 mars 2016 13:21

Re: [TUTO] Application web dynamique

Message par BlackTom » ven. 15 mai 2020 16:13

Ok, je pense que j'ai saisi l'idée ... Je vais voir pour la syntaxe et je reviens te montrer le résultat.

C'est particulier de devoir réfléchir à l'envers par rapport aux langages dont j'ai l'habitude ...

Bud Spencer
Raspinaute
Messages : 1089
Enregistré le : lun. 15 août 2016 21:38

Re: [TUTO] Application web dynamique

Message par Bud Spencer » ven. 15 mai 2020 18:39

Coté syntaxe pour les 'objets avec évènement', je pense pouvoir dire sans prétention que ce tuto est une bonne lecture parce que justement, c'est sur ces méthodes extrêmement pratiques, efficaces et particulièrement bien adaptées a l'usage avec NodeJS que je voulais amener les lecteurs, qui a l'époque du début du tuto ne juraient que par du python lent et lourding et le déploiement de reverse proxy cgi démesurés pour faire de l'appli web codé en php 'deconnectée'.

Apres, le fondamentale asynchrone, c'est vrai que c'est déroutant au début. La plupart des langages 'usuels' sont synchrone et offre des possibilités d'asynchronisme (multithread, parallélisme, multitasking, multiprocessing ect ect ect). Donc on développe principalement avec un raisonnement synchrone (je dirais presque classique) et quand on a besoin d'asynchrone on fait ce qu'il faut pour. Avec NodeJS, c'est juste l'exact contraire. J'avais fait un billet la dessus sur le blog il y a quelques années (que je t'invite à lire) ou j'écrivait que j'avais abordé NodeJS un peu comme n'importe quel autre langage (donc fondamentalement synchrone) et que rien ne fonctionnait comme je voulait du premier coup jusqu'à ce que je prenne ce reflexe de raisonner systématiquement de façon asynchrone en utilisant NodeJs.

Je ne suis pas inquiet pour toi et la suite de ton projet. Visiblement tu te documentes très bien, tu n'as pas peur de te lancer dans du code qui n'est pas forcement fait pour des tout débutants, donc tu devrais très vite etre en mesure de comprendre le fonctionnement et tout le potentiel de NodeJS (surtout sur un Raspberry Pi).
Le premier ennemi de la connaissance n’est pas l’ignorance, c’est l’illusion de la connaissance (S. Hawking).

BlackTom
Messages : 45
Enregistré le : jeu. 31 mars 2016 13:21

Re: [TUTO] Application web dynamique

Message par BlackTom » ven. 15 mai 2020 20:26

Je suis un peu développeur, de métier. Ça serait dommage que je ne sache pas gratter sur le net ou que j'ai peur de tester ...

Sinon j'ai réussi à créer les deux modules, à alléger le programme principal, et même à utiliser les modules nouvellement créés ...
Je suis assez content de moi ... Sauf que j'ai un souci d'interaction.

Le module Distributeur instancie le module Jukebox, mais j'ai problème de portée, je pense, sur le "this" ...

Bref, j'y retourne !!

Possesseur d'un RPi Zéro WH pour un projet de distributeur de croquettes pour chats


Répondre

Retourner vers « Tutoriels »