:lang: fr :toc: = comp: outil pour créer les modules HAL [[cha:comp-hal-component-generator]] (((Comp HAL Component Generator))) == Introduction Écrire un composant de HAL peut se révéler être une tâche ennuyeuse, la plupart de cette tâche consiste à appeler des fonctions _rtapi_ et _hal_ et à contrôler les erreurs associées à ces fonctions. _comp_ va écrire tout ce code pour vous, automatiquement. Compiler un composant de HAL est également beaucoup plus simple en utilisant _comp_ , que le composant fasse partie de l'arborescence de LinuxCNC, ou qu'il en soit extérieur. Par exemple, cette portion des blocs _ddt_, codée en C, fait environ 80 lignes de code, alors que le composant équivalent est vraiment très court quand il est créé en utilisant le préprocesseur _comp_. .Exemple pour comp [[code:exemple-comp]] ---- component ddt "Calcule la dérivée de la fonction d'entrée"; pin in float in; pin out float out; variable float old; function _; license "GPLv2 or later"; ;; float tmp = in; out = (tmp - old) / fperiod; old = tmp; ---- == Installation Si une version pré-installée de LinuxCNC est utilisée, il sera nécessaire d'installer les paquets de développement en passant par Synaptic depuis le menu _Système → Administration → Gestionnaire de paquets Synaptic_ ou en utilisant la commande suivante dans un terminal: .Installation des paquets de développement ---- sudo apt-get install linuxcnc-dev ---- == Définitions component:: Un composant est un simple module temps réel, qui se charge avec _halcmd loadrt_. Un fichier _.comp_ spécifie un seul composant. instance:: Un composant peut avoir zéro ou plusieurs instances. Chaque instance d'un composant est créée égale (elles ont toutes les mêmes pins, les mêmes paramètres, les mêmes fonctions et les mêmes données) mais elle se comporte de manière différente quand leurs pins, leurs paramètres et leur données ont des valeurs différentes. singleton:: Il est possible pour un composant d'être un _singleton_ (composant dont il n'existe qu'une seule instance), dans ce cas, exactement une seule instance est créée. Il est rarement logique d'écrire un composant _singleton_ , à moins qu'il n'y ait qu'un seul objet de ce type dans le système (par exemple, un composant ayant pour but de fournir une pin avec le temps Unix courant, ou un pilote matériel pour le haut parleur interne du PC) == Création d'instance Pour un singleton, une seule instance est créée quand le composant est chargé. Pour un non-singleton, le paramètre _count_ du module détermine combien d'instances seront créées. == Paramètres implicites Le paramètres _period_ est implicitement passé aux fonctions, c'est la durée, en nanosecondes, de la dernière période d'exécution du comp. Les fonctions qui utilisent des flottants peuvent aussi se référer à _fperiod_, qui est la durée en secondes, soit (period*1e-9). Cela peut être utile pour les comps ayant besoin de l'information de timming. == Syntaxe Un fichier _.comp_ commence par un certain nombre de déclarations, puis par un délimiteur constitué de deux points virgule _;;_ seuls sur leur propre ligne et enfin du code C implémentant les fonctions du module. Déclarations d'include: * _component HALNAME (DOC);_ * _pin PINDIRECTION TYPE HALNAME ([SIZE]|[MAXSIZE : CONDSIZE]) (if CONDITION) (= STARTVALUE) (DOC);_ * _param PARAMDIRECTION TYPE HALNAME ([SIZE]|[MAXSIZE : CONDSIZE]) (if CONDITION) (= STARTVALUE) (DOC);_ * _function HALNAME (fp | nofp) (DOC);_ * _option OPT (VALUE);_ * _variable CTYPE NAME ([SIZE]);_ * _description DOC;_ * _see_also DOC;_ * _license LICENSE;_ * _author AUTHOR;_ Les parenthèses indiquent un item optionnel. Une barre verticale indique une alternative. Les mots en _MAJUSCULES_ indiquent une variable texte, comme ci-dessous: NAME:: Un identifiant C standard. STARREDNAME:: Un identifiant C, précédé ou non d'une _*_. Cette syntaxe est utilisée pour déclarer les variables qui sont des pointeurs. Noter qu'à cause de la grammaire, il ne doit pas y avoir d'espace entre _*_ et le nom de la variable. HALNAME:: Un identifiant étendu. Lorsqu'ils sont utilisés pour créer un identifiant de HAL, tous les caractères soulignés sont remplacés par des tirets, tous les points et les virgules de fin, sont supprimés, ainsi *ce_nom_* est remplacé par *ce-nom*, si le nom est `"_"`, alors le point final est enlevé aussi, ainsi `"function_"` donne un nom de fonction HAL tel que `"component."` au lieu de `"component.."` S'il est présent, le préfixe _hal__ est enlevé du début d'un nom de composant lors de la création des pins, des paramètres et des fonctions. Dans l'identifiant de HAL pour une pin ou un paramètre, _#_ indique un membre de tableau, il doit être utilisé conjointement avec une déclaration _[SIZE]_. Les _hash marks_ sont remplacées par des nombres de 0-barrés équivalents aux nombres de caractères #. Quand ils sont utilisés pour créer des identifiants C, les changements de caractères suivants sont appliqués au HALNAME: . Tous les caractères "`#`" sont enlevés ainsi que tous les caractères "`.`", "`_`" ou "`-`" immédiatement devant eux. . Dans un nom, tous les caractères "`.`" et "`-`" sont remplacés par "`_`". . Les caractères "`__`" répétitifs sont remplacés par un seul caractère "`_`". Un "`_`" final est maintenu, de sorte que les identifiants de HAL, qui autrement seraient en conflit avec les noms ou mots clé réservés (par exemple: _min_), puissent être utilisés. [width="90%", options="header"] |======================================== |HALNAME | Identifiant C | Identifiant HAL |x_y_z | x_y_z | x-y-z |x-y.z | x_y_z | x-y.z |x_y_z_ | x_y_z_ | x-y-z |x.##.y | x_y(MM) | x.MM.z |x.## | x(MM) | x.MM |======================================== if CONDITION:: Une expression impliquant la _personnalité_ d'une variable non nulle quand la variable ou le paramètre doit être créé. SIZE:: Un nombre donnant la taille d'un tableau. Les items des tableaux sont numérotés de 0 à _SIZE_-1. MAXSIZE : CONDSIZE:: Un nombre donnant la taille maximum d'un tableau, suivi d'une expression impliquant la _personnalité_ d'une variable et qui aura toujours une valeur inférieure à _MAXSIZE_. Quand le tableau est créé sa taille est égale à _CONDSIZE_. DOC:: Une chaine qui documente l'item. La chaine doit être au format C, entre guillemets, comme _"Sélectionnez le front désiré: TRUE pour descendant, FALSE pour montant"_ ou au format Python triples guillemets, pouvant inclure des caractères newlines et des guillemets, comme: param rw bit zot=TRUE La chaine de documentation est en format _groff -man_. Pour plus d'informations sur ce format de markup, voyez _groff_man(7)_ . Souvenez vous que comp interprète backslash comme Echap dans les chaines, ainsi par exemple pour passer le mot _example_ en font italique, écrivez _\\fIexample\\fB_. TYPE:: Un des types de HAL: _bit_, _signed_ (signé), _unsigned_ (non signé) ou _float_ (flottant). Les anciens noms _s32_ et _u32_ peuvent encore être utilisés, mais _signed_ et _unsigned_ sont préférables. PINDIRECTION:: Une des ces directions: _in_, _out_, ou _io_ . Le composant pourra positionner la valeur d'une pin de sortie, il pourra lire la valeur sur une pin d'entrée et il pourra lire ou positionner la valeur d'une pin _io_. PARAMDIRECTION:: Une des valeurs suivantes: _r_ ou _rw_. Le composant pourra positionner la valeur d'un paramètre _r_ et il pourra positionner ou lire la valeur d'un paramètre rw. STARTVALUE:: Spécifie la valeur initiale d'une pin ou d'un paramètre. Si il n'est pas spécifié, alors la valeur par défaut est _0_ ou _FALSE_, selon le type de l'item. === Fonctions HAL fp:: Indique que la fonction effectuera ses calculs en virgule flottante. nofp:: Indique que la fonction effectuera ses calculs sur des entiers. Si il n'est pas spécifié, _fp_ est utilisé. Ni comp ni gcc ne peuvent détecter l'utilisation de calculs en virgule flottante dans les fonctions marquées _nofp_. === Options Selon le nom de l'option OPT, les valeurs VALUE varient. Les options actuellement définies sont les suivantes: OPT, VALUE: option singleton yes;; (défaut: no) Ne crée pas le paramètre numéro de module et crée toujours une seule instance. Avec _singleton_, les items sont nommés _composant-name.item-name_ et sans _singleton_, les items des différentes instances sont nommés _composant-name..item-name_. option default_count ;; _number_ (défaut: 1) Normalement, le paramètre _count_ par défaut est 0. Si spécifié, _count_ remplace la valeur par défaut. option count_function yes;; (défaut: no) Normalement, le numéro des instances à créer est specifié dans le paramètre _count_ du module, si _count_function_ est spécifié, la valeur retournée par la fonction _int get_count(void)_ est utilisée à la place de la valeur par défaut et le paramètre _count_ du module n'est pas défini. option rtapi_app no;; (défaut: yes) Normalement, les fonctions _rtapi_app_main_ et _rtapi_app_exit_ sont définies automatiquement. Avec _option rtapi_app no_, elles ne le seront pas et doivent être fournies dans le code C. Quand vous implémentez votre propre _rtapi_app_main_, appellez la fonction _int export(char _prefix, long extra_arg)_ pour enregistrer les pins, paramètres et fonctions pour _préfix_er. option data ;; _type_ (défaut: none) deprecated If specified, each instance of the component will have an associated data block of _type_ (which can be a simple type like _float_ or the name of a type created with _typedef_). In new components, _variable_ should be used instead. option extra_setup yes;; (défaut: no) option extra_cleanup yes;; (défaut: no) Si spécifié, appelle la fonction définie par _EXTRA_CLEANUP_ depuis la fonction définie automatiquement _rtapi_app_exit_, ou une erreur est détectée dans la fonction automatiquement définie _rtapi_app_main_. option userspace yes;; (défaut: no) Si spécifié, ce fichier décrit un composant d'espace utilisateur, plutôt que le réel. Un composant d'espace utilisateur peut ne pas avoir de fonction définie par la directive de fonction. Au lieu de cela, après que toutes les instances soient construites, la fonction C _user_mainloop()_ est appelée. Dès la fin de cette fonction, le composant se termine. En règle générale, _user_mainloop()_ va utiliser _FOR_ALL_INSTS()_ pour effectuer la mise à jour pour chaque action, puis attendre un court instant. Une autre action commune dans _user_mainloop()_ peut être d'appeler le gestionnaire de boucles d'événements d'une interface graphique. option userinit yes;; (défaut: no) Si spécifiée, la fonction _userinit(argc,argv)_ est appelée avant _rtapi_app_main()_ (et cela avant l'appel de _hal_init()_ ). Cette fonction peut traiter les arguments de la ligne de commande ou exécuter d'autres actions. Son type de retour est _void_; elle peut appeler _exit()_ et si elle le veut, se terminer sans créer de composant HAL (par exemple, parce que les arguments de la ligne de commande sont invalides). Si aucune option VALUE n'est spécifiée, alors c'est équivalent à spécifier la valeur _… yes_ . Le résultat consécutif à l'assignation d'une valeur inappropriée à une option est indéterminé. Le résultat consécutif à n'utiliser aucune autre option est indéfini. === Licence et auteur LICENSE:: Spécifie la license du module, pour la documentation et pour le module déclaré dans MODULE_LICENSE(). AUTHOR:: Spécifie l'auteur du module, pour la documentation === Stockage des données *par instance* variable :: _CTYPE NAME_; variable :: _CTYPE NAME_[_SIZE_]; variable :: _CTYPE NAME = default_; variable :: _CTYPE NAME_[_SIZE_] = _default_; Déclare la variable _par-instance_ _NAME_ de type _CTYPE_, optionnellement comme un tableau de _SIZE_ items et optionnellement avec une valeur par default. Les items sans _default_ sont initialisés _all-bits-zero_. _CTYPE_ est un simple mot de type C, comme _float_, _u32_, _s32_, etc. Les variables d'un tableau sont mises entre crochets. === Commentaires Les commentaires de style C++ une ligne (_// …_) et de style C multi-lignes (_/_ … _/_) sont supportés tous les deux dans la section déclaration. == Restrictions sur les fichiers comp Bien que HAL permette à une pin, un paramètre et une fonction d'avoir le même nom, comp ne le permet pas. Les noms de variable et de fonction qui ne doivent pas être utilisés ou qui posent problème sont les suivants: * Tous noms commençant par ___comp__. * _comp_id_ * _fperiod_ * _rtapi_app_main_ * _rtapi_app_exit_ * _extra_setup_ * _extra_cleanup_ == Conventions des macros En se basant sur les déclarations des items de section, _comp_ crée une structure C appellée _structure d'état_ . Cependant, au lieu de faire référence aux membres de cette structure (par exemple: _*(inst->name)_ ), il leur sera généralement fait référence en utilisant les macros ci-dessous. Certains détails de la structure d'état et de ces macros peuvent différer d'une version de _comp_ à la suivante. FUNCTION(name):: Cette macro s'utilise au début de la définition d'une fonction temps réel qui aura été précédemment déclarée avec _function NAME_. function inclus un paramètre _period_ qui est le nombre entier de nanosecondes entre les appels à la fonction. EXTRA_SETUP():: Cette macro s'utilise au début de la définition de la fonction appelée pour exécuter les réglages complémentaires à cette instance. Une valeur de retour négative Unix _errno_ indique un défaut (par exemple: _elle retourne -EBUSY_ comme défaut à la réservation d'un port d'entrées/sorties), une valeur égale à 0 indique le succés. EXTRA_CLEANUP():: Cette macro s'utilise au début de la définition de la fonction appelée pour exécuter un nettoyage (cleanup) du composant. Noter que cette fonction doit nettoyer toutes les instances du composant, pas juste un. Les macros _pin_name_, _parameter_name_ et _data_ ne doivent pas être utilisées ici. pin_name ou parameter_name:: Pour chaque pin, _pin_name_ ou pour chaque paramètre, _parameter_name_ il y a une macro qui permet d'utiliser le nom seul pour faire référence à la pin ou au paramètre. Quand _pin_name_ ou _parameter_name_ sont des tableaux, la macro est de la forme _pin_name(idx)_ ou _param_name(idx)_ dans laquelle _idx_ est l'index dans le tableau de pins. Quand le tableau est de taille variable, il est seulement légal de faire référence aux items par leurs _condsize_. + Quand un item est conditionnel, il est seulement légal de faire référence à cet item quand ses conditions sont évaluées à des valeurs différentes de zéro. variable_name:: Pour chaque variable, _il y a une macro variable_name_ qui permet au nom seul d'être utilisé pour faire référence à la variable. Quand _variable_name_ est un tableau, le style normal de C est utilisé: _variable_name[idx]_ data:: Si l'_option data_ est spécifiée, cette macro permet l'accès à l'instance de la donnée. fperiod:: Le nombre de secondes en virgule flottante entre les appels à cette fonction temps réel. FOR_ALL_INSTS() {*…*}:: Pour les composants de l'espace utilisateur. Cette macro utilise la variable *struct state _inst_ pour itérer au dessus de toutes les instances définies. Dans le corps de la boucle, les macros _pin_name_, _parameter_name_ et _data_ travaillent comme elles le font dans les fonctions temps réel. == Composants avec une seule fonction Si un composant a seulement une fonction et que la chaine _FUNCTION_ n'apparaît nulle part après _;;_, alors la portion après _;;_ est considérée comme étant le corps d'un composant simple fonction. == Personnalité du composant Si un composant a n'importe combien de pins ou de paramètres avec un if condition ou _[maxsize : condsize]_, il est appelé un composant avec personnalité. La personnalité de chaque instance est spécifiée quand le module est chargé. La personnalité peut être utilisée pour créer les pins seulement quand c'est nécessaire. Par exemple, la personnalité peut être utilisée dans un composant logique, pour donner un nombre variable de broches d'entrée à chaque porte logique et permettre la sélection de n'importe quelle fonction de logique booléenne de base _and_, _or_ et _xor_. == Compiler un fichier _.comp_ dans l'arborescence Placer le fichier _.comp_ dans le répertoire _linuxcnc/src/hal/components_ et lancer/relancer _make_. Les fichiers Comp sont automatiquement détectés par le système de compilation. Si un fichier _.comp_ est un pilote de périphérique, il peut être placé dans _linuxcnc/src/hal/components_ et il y sera construit excepté si LinuxCNC est configuré en mode simulation. == Compiler un composant temps réel hors de l'arborescence[[sec:Compiler-composants-rt]] (((Compiling realtime components outside the source tree))) _comp_ peut traiter, compiler et installer un composant temps réel en une seule étape, en plaçant _rtexample.ko_ dans le répertoire du module temps réel de LinuxCNC: comp --install rtexample.comp Ou il peut aussi être traité et compilé en une seule étape en laissant _example.ko_ (ou _example.so_ pour la simulation) dans le répertoire courant: comp --compile rtexample.comp Ou il peut simplement être traité en laissant _example.c_ dans le répertoire courant: comp rtexample.comp _comp_ peut aussi compiler et installer un composant écrit en C, en utilisant _les options --install_ et _--compile_ comme ci-dessous: comp --install rtexample2.c La documentation au format man peut être créée à partir des informations de la section _declaration_: comp --document rtexample.comp La manpage résultante, _exemple.9_ peut être lue avec: man ./exemple.9 ou copiée à un emplacement standard pour une page de manuel. == Compiler un composant de l'espace utilisateur hors de l'arborescence _comp_ peut traiter, compiler et installer un document de l'espace utilisateur: comp usrexample.comp Cela fonctionne seulement pour les fichiers _.comp_ mais pas pour les fichiers _.c_. == Exemples === constant Ce composant fonctionne comme dans _blocks_, y compris la valeur par défaut à 1.0. La déclaration _function __ crée les fonctions nommées _constant.0_, etc. component constant; === sincos Ce composant calcule le sinus et le cosinus d'un angle entré en radians. Il a différentes possibilités comme les sorties _sinus_ et _cosinus_ de siggen, parce que l'entrée est un angle au lieu d'être librement basé sur un paramètre _frequency_. Les pins sont déclarées avec les noms _sin__ et _cos__ dans le code source pour que ça n'interfère pas avec les fonctions _sin()_ et _cos()_. Les pins de HAL sont toujours appelées _sincos..sin_. component sincos; === out8 Ce composant est un pilote pour une carte imaginaire appelée _out8_, qui a 8 pins de sortie digitales qui sont traitées comme une simple valeur sur 8 bits. Il peut y avoir un nombre quelconque de ces cartes dans le système et elles peuvent avoir des adresses variées. La pin est appelée _out__ parce que _out_ est un identifiant utilisé dans __. Il illustre l'utilisation de _EXTRA_SETUP_ et de _EXTRA_CLEANUP_ pour sa requête de région d'entrées/sorties et libère cette région en cas d'erreur ou quand le module est déchargé. [source,c] ---- component out8; pin out unsigned out_ "Output value; only low 8 bits are used"; param r unsigned ioaddr; function _; option count_function; option extra_setup; option extra_cleanup; option constructable no; license "GPL"; ;; #include #define MAX 8 int io[MAX] = {0,}; RTAPI_MP_ARRAY_INT(io, MAX, "I/O addresses of out8 boards"); int get_count(void) { int i = 0; for(i=0; i void user_mainloop(void) { while(1) { usleep(1000); FOR_ALL_INSTS() out = drand48(); } } ---- === logic Ce composant temps réel montre l'utilisation de la personnalité pour créer un tableau de taille variable et des pins optionnelles. [source,c] ---- component logic "LinuxCNC HAL component providing experimental logic functions"; pin in bit in-##[16 : personality & 0xff]; pin out bit and if personality & 0x100; pin out bit or if personality & 0x200; pin out bit xor if personality & 0x400; function _ nofp; description """ Experimental general logic function component. Can perform and, or and xor of up to 16 inputs. Determine the proper value for personality by adding: .IP \\(bu 4 The number of input pins, usually from 2 to 16 .IP \\(bu 256 (0x100) if the and output is desired .IP \\(bu 512 (0x200) if the or output is desired .IP \\(bu 1024 (0x400) if the xor (exclusive or) output is desired"""; license "GPL"; ;; FUNCTION(_) { int i, a=1, o=0, x=0; for(i=0; i < (personality & 0xff); i++) { if(in(i)) { o = 1; x = !x; } else { a = 0; } } if(personality & 0x100) and = a; if(personality & 0x200) or = o; if(personality & 0x400) xor = x; } ---- Une ligne de chargement typique pourrait être: ---- loadrt logic count=3 personality=0x102,0x305,0x503 ---- qui créerait les pins suivantes: - Une porte AND à 2 entrées: logic.0.and, logic.0.in-00, logic.0.in-01 - des portes AND et OR à 5 entrées: logic.1.and, logic.1.or, logic.1.in-00, logic.1.in-01, logic.1.in-02, logic.1.in-03, logic.1.in-04, - des portes AND et XOR à 3 entrées: logic.2.and, logic.2.xor, logic.2.in-00, logic.2.in-01, logic.2.in-02