bobaos.pub - KNX TP / UART, Raspberry Pi et Redis


Il n'y a pas de limite à la perfection. Il semblerait que tout fonctionnait bien, des bugs mineurs, etc. ont été corrigés.


Maintenant, je vais vous parler, premièrement, des problèmes que j'ai rencontrés pendant tout le temps qui s'est écoulé depuis l'article précédent, et, deuxièmement, des solutions qui ont contribué à l'état actuel du projet.


Article sur la version précédente


Désignations


bobaos - module npm pour interagir avec BAOS 83x en utilisant UART. Renvoie des données brutes. Utilisé dans tous les autres modules répertoriés ci-dessous.


bdsd.sock - un script pour travailler avec des objets KNX. Stocke une liste de points de données, convertit les valeurs lors de l'envoi / réception. De DPT1 à vrai / faux, de DPT9 à flotter. Écoute également sur Unix Socket pour recevoir les demandes d'autres processus.


bobaos.pub est une nouvelle version utilisant redis pour la communication interprocessus.
KNX / - un objet de communication du module BAOS 83x configuré dans ETS auquel correspond (ou non) l'adresse de groupe (a). Dans les versions actuelles du fer, la quantité maximale est de 1000.


Défi


La tâche principale est la même que la version précédente. Il n'y a qu'une seule connexion au port série. Il existe de nombreux scripts fonctionnant avec KNX. En plus de cela, je voulais implémenter la communication interprocessus. C'est-à-dire afin que non seulement un processus bdsd.sock le socket, mais que chaque script en cours d'exécution puisse à la fois envoyer et recevoir des demandes.


Idée


Une idée est née dans ma tête de créer mon propre courtier de messages sur node.js au-dessus des sockets Unix, auquel les clients se connecteraient, s'abonneraient aux sujets et recevraient / enverraient des messages conformément au code qui y était écrit. Je savais qu'il existait déjà des solutions toutes faites, dont seuls les paresseux n'avaient pas entendu parler récemment, ont étudié, mais l'idée de prendre ma propre décision était obsessionnelle.


Et en conséquence, le service est lancé.


A écrit un enregistreur qui envoie des messages au sujet. Les abonnés reçoivent et sont libres de faire n'importe quoi, ou plutôt, ce qui est prescrit. Pratique - les journaux de plusieurs sources peuvent être affichés dans une sortie de console.


J'ai écrit et publié le package bobaos.pub dans npm, qui, contrairement à bdsd.sock, ne crée plus de fichier socket, mais se connecte à un courtier. À première vue, tout fonctionne comme il se doit.


Le problème


Ensuite, j'ai exécuté un script qui envoie régulièrement des requêtes au bus KNX pendant la nuit. En me réveillant le matin, par le clignotement des LEDs signalant l'envoi / la transmission des données, j'ai réalisé que quelque chose n'allait pas. Les messages ne sont pas parvenus à temps. J'ai constaté que le courtier de messages auto-écrit occupait presque la totalité des 512 Mo de RAM disponibles (de BeagleBoard Black). La poursuite des travaux avec nodejs a confirmé que la mémoire est le point faible des scripts js.


Solution


En conséquence, il a été décidé de passer des sockets Unix génériques à Redis (au fait, il sait également comment travailler avec eux). Peut-être que cela valait la peine de trier la mémoire, de trouver des fuites, mais je voulais courir plus vite.


bobaos signifie communication UART avec habillage de message en FT1.2, nous avons une communication synchrone. C'est-à-dire <..--..> . Le module bobaos, qui est responsable de la communication, stocke toutes les demandes dans le tableau, les extrait à son tour, les envoie à l'UART, et avec la réponse entrante, il permet la promesse responsable de cette demande.


Vous pouvez procéder de cette façon: le service écoute le canal redis PUB / SUB, accepte les requêtes, les envoie à KNX. Dans ce cas, la charge sur la file d'attente des demandes incombe au module js bobaos . Pour l'implémentation, vous devez écrire un module simple abonné à un canal et convertir les messages à l'aide de la méthode JSON.parse() . De plus, ce module peut être utilisé dans d'autres scripts.


Une autre option que j'ai fini par redis : utiliser un gestionnaire de tâches existant en plus de redis . Il existe plusieurs choix dans la bee-queue .


Sous le capot


Il décrit le fonctionnement de la bee-queue . Si vous implémentez cette bibliothèque pour d'autres langages de programmation, vous pouvez créer des bibliothèques clientes pour les bobaos de cette manière.


Dans la deuxième version, toutes les demandes sont stockées dans des listes redis , extraites à leur tour et envoyées au port série.


De plus, la réécriture de la version précédente suit, mais je stocke déjà toutes les données sur les points de données dans la base de données redis . Le seul inconvénient que j'éprouve est que toutes les demandes sont asynchrones, donc obtenir un tableau de valeurs est un peu plus difficile que d'accéder simplement au tableau.


Des optimisations mineures ont été apportées.


S'il existait auparavant des méthodes distinctes, getValue/getValues/readValue/readValues/setValue/setValues , maintenant getValue/readValue/setValues accepte à la fois une valeur unique et un tableau.


La méthode getValue([id1, id2, ...]) dans la version précédente a envoyé une demande au port série pour chaque point de données. Mais il est possible d'envoyer une demande pour plusieurs valeurs. Limitations - la taille de la réponse doit être égale à BufferSize , le maximum - 250 octets; il est également impossible d'aller au-delà du nombre d'objets; pour les versions actuelles des modules BAOS 83x - 1000.


Les longueurs des valeurs sont connues, l'en-tête aussi. De plus, un algorithme assez simple avec des cycles while et wait =)


  1. Triez le tableau, supprimez les éléments en double, le cas échéant. nous obtenons un tableau idUniq .
  2. Nous commençons le cycle i < idUniq.length , dans lequel nous faisons ce qui suit:
    a) start: idUniq[i] , pour cela nous considérons le nombre maximum de valeurs que nous pouvons obtenir. Par exemple, si tous les objets sont de type DPT1 / DPT5 (1 octet), alors nous pouvons obtenir des valeurs de 48. Il y a une remarque: si, par exemple, nous avons configuré les objets #[1, 2, 3, 10, 20] , puis lors de l'interrogation de GetDatapointValue.Req(1, 30) , la réponse renverra zéro valeur à un octet même pour les points de données inexistants [4, 5, 6, ..., 30] .
    b) Le comptage a lieu dans un nouveau cycle j < i + max (où max est 50, ou, s'il est proche de 1000, alors maximum 1000 - id + 1 , pour 990 ce sera 11, pour 999-2), si dans le processus de comptage nous nous rencontrons éléments du tableau de la requête d'origine, puis affectez l'index i variable i .
    c) Si dans le cycle j longueur calculée dépasse la longueur maximale du tampon, alors nous formons l'élément de carte de requête {start: start, number: number} , le idUniq dans un tableau séparé, idUniq la variable i (ou idUniq l'index à l' idUniq trouvé dans le tableau), interrompons cycle j , les deux cycles sont redémarrés.

Ainsi, nous formons plusieurs demandes de bobaos . Par exemple, si vous envoyez une demande getValue([1, 2, 3, 40, 48, 49, 50, 100, 998, 999, 1000]) , les demandes peuvent être les suivantes pour un cas spécial:


 {start: 1, number: 48}, // 1, 2, 3, 40, 48 {start: 49, number: 48}, // 49, 50 {start: 100, number: 48}, // 100 {start: 998, number: 3} // 998, 999, 1000 

Cela pourrait être fait différemment:


 {start: 1, number: 48}, // 1, 2, 3, 40, 48 {start: 49, number: 2}, // 49, 50 {start: 100, number: 1}, // 100 {start: 998, number: 3} // 998, 999, 1000 

Il y aurait autant de demandes, moins de données. Mais je me suis arrêté à la première option, puisque les valeurs obtenues sont stockées dans la base de données redis , respectivement, elles peuvent être obtenues en utilisant la méthode getStoredValue , que j'essaie d'utiliser plus souvent que getValue , qui envoie des données via le port série.


Une file d'attente distincte est créée pour les méthodes de service ping/get sdk state/reset . Ainsi, si quelque chose ne va pas avec la communication sur le port série (le compteur de trames s'est égaré, etc.) et que l'exécution s'arrête sur une tâche, vous pouvez envoyer une demande de reset dans une autre file d'attente et, en conséquence, redémarrer sdk .


Côté client - bobaos.sub


Pour contrôler les points de données KNX dans les scripts utilisateur, le module bobaos.sub peut être utilisé.


L'exemple suivant couvre toutes les fonctions d'un module:


 const BobaosSub = require("bobaos.sub"); //     : // redis:   url // request_channel: "bobaos_req"  , // service_channel: "bobaos_service"  , // broadcast_channel: "bobaos_bcast"   let my = BobaosSub(); my.on("connect", _ => { console.log("connected to ipc, still not subscribed to channels"); }); my.on("ready", async _ => { try { console.log("hello, friend"); console.log("ping:", await my.ping()); console.log("get sdk state:", await my.getSdkState()); console.log("get value:", await my.getValue([1, 107, 106])); console.log("get stored value:", await my.getValue([1, 107, 106])); console.log("get server item:", await my.getServerItem([1, 2, 3])); console.log("set value:", await my.setValue({id: 103, value: 0})); console.log("read value:", await my.readValue([1, 103, 104, 105])); console.log("get programming mode:", await my.getProgrammingMode()); console.log("set programming mode:", await my.setProgrammingMode(true)); console.log("get parameter byte", await my.getParameterByte([1, 2, 3, 4])); console.log("reset", await my.reset()); } catch(e) { console.log("err", e.message); } }); my.on("datapoint value", payload => { //   ,  payload    ,    //     Array.isArray(payload) console.log("broadcasted datapoint value: ", payload); }); my.on("server item", payload => { //   ,  payload    ,    //     Array.isArray(payload) console.log("broadcasted server item: ", payload); }); my.on("sdk state", payload => { console.log("broadcasted sdk state: ", payload); }); 

bobaos.tool


L'interface de ligne de commande a été réécrite. À propos de la façon dont je l'ai implémenté, l'article suivant:


Écriture CLI sur NodeJS


Les équipes sont devenues plus courtes, plus claires et plus fonctionnelles.


 bobaos> progmode ? BAOS module in programming mode: false bobaos> progmode 1 BAOS module in programming mode: true bobaos> progmode false BAOS module in programming mode: false bobaos> description 1 2 3 #1: length = 2, dpt = dpt9, prio: low, flags: [C-WTU] #2: length = 1, dpt = dpt1, prio: low, flags: [C-WT-] #3: length = 1, dpt = dpt1, prio: low, flags: [C-WT-] bobaos> set 2: 0 20:27:06:239, id: 2, value: false, raw: [AA==] bobaos> set [2: 0, 3: false] 20:28:48:586, id: 2, value: false, raw: [AA==] 20:28:48:592, id: 3, value: false, raw: [AA==] 

Postface


Le résultat a été un système de travail stable. Redis tant que backend fonctionne parfaitement bien. Pendant le développement, beaucoup de cônes ont été emballés. Mais le processus d'apprentissage est tel qu'il est parfois inévitable. D'après l'expérience acquise, je note que les processus nodejs consomment beaucoup de RAM (20 Mo au départ) et qu'il peut y avoir des fuites. Pour la domotique, cela est essentiel - car le script doit fonctionner en permanence, et s'il se développe de plus en plus au fil du temps, il peut à un certain moment occuper tout l'espace. Par conséquent, vous devez soigneusement écrire des scripts, comprendre comment fonctionne le garbage collector et tout garder sous contrôle.


La mise à jour de la documentation se trouve ici .


Dans le prochain article, je parlerai de l'utilisation de redis et bee-queue redis un service d'accessoires logiciels.


UPD: bobaoskit - accessoires, dnssd et WebSocket


Je serai heureux de tout commentaire.

Source: https://habr.com/ru/post/fr436378/


All Articles