Comme expliqué ici, je souhaiterais permettre aux utilisateurs de mon programme de créer des modules et de les faire communiquer avec le reste, sans avoir à éditer mon code. Pyro semble être une méthode intéressante pour implémenter un mode d'échange du genre publish/subscribe comme conseillé dans le premier lien.
Seulement, j'ignore un peu comment utiliser le module. A priori, j'ai deux possibilités :
Comme la seconde fonctionnalité n'est actuellement pas disponible avec Pyro4, elle est a priori hors circuit.
De ce que j'ai pu lire, chaque module diffuserait un objet via un nameserver et les autres utiliseraient son nom, pré-défini, pour vérifier sa présence et communiquer avec lui.
Quelle est la fonctionnalité coeur ? Quel genre de module l'utilisateur veut-il créer ?
Je te demande de détailler spécifiquement cette partie, avec des schémas ou autre, pour définir le scope de ce que tu veux faire à ce niveau.
Créer un système de plugins ne demande aucune bibliothèque tierce. En balançant des liens vers des bibliothèques tu nous montres que tu es en train de prendre le problème à l'envers :
Qu'est ce que je veux faire ?
Comment ça s'intègre dans le projet d'un point de vue fonctionnel ?
Comment l'utilisateur utilise-t'il cette feature ?
C'est seulement une fois que ça, ce sera défini clairement sur ce sujet, que tu auras besoin de te poser la question des éventuelles bibliothèques externes, mais je le répète : ce n'est pas à la bibliothèque de définir ta spec. C'est à ta spec de définir le besoin, et à toi de réfléchir à la façon dont ça s'intègre dans le projet, avant de voir si éventuellement une bibliothèque pourrait te faire gagner du temps.
Quelques question pouvant aider à spécifier le besoin :
Qui écrit le code des "plugins" ?
Qui l'exécute ?
Où ce code est-il exécuté "physiquement" ? (Au même endroit que le logiciel ? Ailleurs ?)
Quel est le contexte d'exécution du plugin ? A quelles variables / ressources le code a-t-il accès ? Quels sont les données de sortie du plugin ? Où sont-elles stockées / affichées ?
Les données d'entrée du plugin (pour qu'il puisse faire ce qu'il doit faire) sont-elles figées tout au long de son exécution ? Où déterminées une fois et c'est tout ?
Le code du plugin est-il modifiable ? Que se passe-t-il dans ce cas ? Comment tu le re-déploies ?
Y'a plein d'idées sous-jacentes à ce que tu sembles vouloir faire, j'avais parlé de publish/subscribe, ce ne serait applicable que dans une certaine configuration en fonction des réponses ci-dessus (Appli + plugins sur une même machine, dans des processus séparés qui ont besoin de communiquer en temps réel, …).
Définis proprement ton "écosystème applicatif" (qui est lié à quoi ? comment ? qui exécute quoi ? qui est le fils de qui ? qui déploie quoi ?).
Exemple alakon : un serveur web XXX "écoute" un répertoire du disque pour en extraire les zips (jars, wars, tars, …) une fois qu'ils sont déposés dedans. Une fois extrait, il déploie l'application qui se trouve à l'intérieur en lisant son fichier de configuration. Il saura qu'il doit lui déléguer certaines requêtes HTTP et exécuter telle ou telle portion de code en fonction de l'URL d'accès, qu'il doit exécuter ce code dans un certain contexte, en donnant accès à certaines variables, etc. Ce ne sont pas des "plugins", mais pourtant il y a toute une mécanique d'extraction / instanciation / contexte / communication à définir avant de chercher à implémenter ce concept.
Partons du principe tout a été intégralement codé. Il va donc se passer cela :
L'agriculteur - pour l'instant, mon oncle - va créer une carte via le module de cartographie, qui lui retournera un fichier texte - a priori du JSON - qu'il stockera sur une clé USB. Il se rend alors dans son tracteur, allume sa Raspberry et démarre l'application de contrôle : un serveur web se met en route. L'agriculteur ouvre alors la page web, charge la carte et les divers modules se mettent au travail.
Seulement, pour localiser le tracteur, par exemple, nous nous basons sur la vitesse du véhicule. Mais si un autre utilisateur - ou nous, plus tard - décide de se localiser via un GPS, par exemple, le module de localisation ne lui conviendra pas. Je veux donc lui offrir la possibilité d'écrire son propre module et de l'intégrer le plus simplement possible au reste - i.e. sans modifier mon code.
Ce système permettrait également à l'utilisateur d'ajouter des modules : s'il veut mesurer le débit par exemple, faire dépendre le module de gestion de dose de la température…
Le module est donc exécuté sur la carte électronique, au même endroit que le module de contrôle et des autres modules.
Pour l'instant, je n'envisage pas de distribuer le programme - il faudrait déjà l'écrire - donc j'écrirai les plugins - enfin un seul par tâche (localiser, gérer la dose…), pour répondre au besoin de mon oncle - mais je souhaite organiser mon code de sorte qu'on puisse employer ses propres modules sans modifier le code du module de contrôle.
C'est pourquoi utiliser un système de messages - du genre publish/subscribe - me paraissait intéressant. L'UI étant une app web, j'aurais quelque chose comme ça :
Les messages de type A et D seront probablement transmis via websockets. Ceux de type B et C seront… c'est la question.
Les messages seraient reliés à des méthodes du module. Ça sera donc des trucs du genre : "getHTML" (pour afficher l'UI), "setThreshold" (pour la gestion de dose par exemple), "setRow" (pour la localisation), "getCurrentPosition"…
Il est de plus nécessaire que le module soit exécuté en permanence, pour localiser le tracteur par exemple. Donc, au démarrage du serveur - i.e. le module de contrôle -, le module de localisation est mis en route et il est alors possible de communiquer avec lui : "quel est le code HTML à afficher pour l'UI ?", "quelle est la position actuelle ?", "peux-tu mettre ce paramètre à telle valeur ?"…
Pour l'instant, je me suis contenté d'un simple import (le fichier où est exécuté le serveur web importe les modules, comme la classe de localisation) et d'appels aux méthodes. Mais il y a deux soucis :
La gestion des erreurs
La flexibilité
Pour la gestion des erreurs, pour l'instant, je fais comme ça :
Action du client
Notification du serveur via les websockets
Appel d'une méthode d'un module
OK : retourne True
KO : retourne un intitulé de message d'erreur
Si l'appel retourne True, c'est bon. Sinon, on envoie au client le message associé à l'intitulé.
Je me retrouve avec des if de partout, c'est crade. Du coup, je me disais qu'un système du genre celui des signaux de Qt serait intéressant :
Appel à une méthode
Si on rencontre une erreur, signal envoyé par le module.
Sinon, rien, sauf si on attend une valeur de retour.
En ce qui concerne la flexibilité… ben si mes modules sont importés en dur dans mon code via import, l'utilisateur aura du mal à en rajouter sans modifier mon fichier.
Merci et n'hésitez pas si vous avez des questions : ça m'aidera à étoffer la doc. =)
Ce que tu cherches à faire c'est très exactement un système de communication entre modules à travers un EventBus.
Et quand je lis : serveur web / websockets et communication entre modules. Je pense directement à Vert.x. Soit je déforme la réalité de tes propos (déformation professionnelle) soit j'ai mal compris mais ça me semble couvrir l'ensemble du spectre de ce que tu cherches à faire.
Chaque module est déployé sur ton serveur. Les modules communiquent à travers un eventBus.
Dans le code de ton module tu vas écrire (exemple en Groovy) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vertx.eventBus.registerHandler("ladresseSurLaquelleIlEcoute"){message->printlnmessage.bodyif(isErrorMessage(message.body){message.reply("I understood your error and I'm handling it")notifyClient(getError(message.body))}elsehandle(message.body)}defunMessage="Ceci est un message"vertx.eventBus.send("ladresseDeLAutreModule",unMessage){reply->// ce code est appelé quand l'autre module aura réponduif(isErrorMessage(reply.body))notifyClient(getErrorMessage(reply.body))}
Est-ce-que c'est ce genre de trucs que tu recherches ?
J'ai mis ça à titre d'exemple, pour montrer que tu "réagissais" en fonction des messages reçus. notifyClient ça serait par exemple envoyer un message à travers une websocket pour dire à ton client "Oulala j'ai transféré ta requête à un autre module et il m'a retourné une erreur : regarde ce que c'est".
Pour le faire tourner sur une Rapsberry et bien, je n'ai jamais essayé, mais Vert.x est censé être un serveur assez léger et performant. L'avantage c'est que c'est un serveur basé sur Java NIO (et Netty) donc dans la famille des serveurs performants pour du temps réel et la gestion de beaucoup de données en parallèle, aussi légers que possible. (En comparaison d'un Tomcat qui n'a pas du tout vocation à faire ça). Si j'en crois ce lien c'est vraiment pas trop mal en terme de perfs.
Je me dis que si tu as besoin de la partie web (websockets si j'ai bien compris) et communication entre modules à travers un canal de messages :
Soit tu te sers d'un serveur web existant pour la partie sockets et tu implémentes ton propre système d'EventBus (le fameux canal de communication) c'est ce que tu étais parti pour faire
Soit tu profites à la fois d'un serveur websockets qui propose également un système d'EventBus intégré, développé, testé et performant (en l'occurrence hazelcast pour Vert.x)
La solution 1 est bonne pédagogiquement mais attention : on est en train de parler d'EventBus : il est HORS DE QUESTION de paumer un message dans le cas où un bug se produit, sinon ton appli sera complètement indebuggable.
La solution 2 me paraît plus fiable honnêtement. Après, c'est pas trop le côté usine à gaz (perfs) qui me ferait peur, mais plutôt le temps d'apprentissage et de voir comment tu peux porter tes modules existants en modules vertx (qui se déploient au lancement de ton application etc.)
A toi de voir, je ne veux pas t'emmener sur une fausse piste et te faire perdre du temps.
Est-il nécessaire de s'y connaître en Java pour utiliser Vert.x ? Le cas échéant, c'est mort. De plus, bien que complet et très intéressant, Vert.x me semble complexe, au premier abord.
Sinon, je pourrais me baser sur les technos suivantes :
Vert.x (node.js sous stéroïdes si on parle en termes marketing pourris) est polyglote, c'est bien là tout son intérêt. Et parmi les langages proposés pour écrire ses modules, Python est présent.
(au même titre que Java, Javascript, Ruby, Scala et Groovy : en gros tous les langages interprétables sur une JVM)
Actuellement, j'utilise Flask et les websockets pour communiquer avec le client et PyPubSub côté serveur. Apparemment, crossbar.io me permettrait de tout faire. Qu'en pensez-vous ?
Connectez-vous pour pouvoir poster un message.
Connexion
Pas encore membre ?
Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte