nomoInterpreter | nomoseed
design intelligence
Table des matières

nomoInterpreter

nomoInterpreter correspond à l'implémentation du moteur d'inférence et du moteur de création de règles. Cette implémentation prend la forme d'une librairie dynamique qui reçoit de manière asynchrone des messages de différentes sources et qui renvoie des commandes selon les cycles d'interprétations cadencés par une horloge externe comme l'illustre la figure ci-dessous :

Une fois chargée, la librairie dynamique nomoInterpreter doit être initialisée avec :

  • une base de règles contenue dans un fichier ”.seed” résultant de la compilation d'une unité nomo par le compilateur de nomoSDK,
  • la liste des fonctions de rappel (callback) gérant les sorties du moteur d'inférence,
  • et éventuellement un répertoire dédié à la journalisation.

Le premier point est réalisé par la présence d'un et d'un seul fichier ”.seed” dans le même répertoire contenant la librairie dynamique nomoInterpreter. Les deux autres points doivent être précisés directement dans l'application utilisant nomoInterpreter.

Une fois initialisées, les interprétations du moteur d'inférence sont cadencées via des appels de fonction provoqués par l'application embarquant nomoInterpreter. Pour conserver une cohérence temporelle, la fréquence du cadencement doit être la même que celle indiquée dans les paramètres initiaux du moteur d'inférence contenus dans le fichier d'initialisation ”.seed”.

Parallèlement à ce cadencement, nomoInterpreter peut recevoir de manière asynchrone des messages d'entrée par l'appel d'une fonction prédéfinie et peut envoyer des messages de sortie via les fonctions de rappel identifiées à l'initialisation.

Il est possible de spécifier au moment du cadencement une journalisation. La journalisation peut être effectuée selon le type des règles et peut porter sur toutes les règles ou uniquement sur la règle sélectionnée. L'enregistrement des règles peut être partiel ou complet.

La clôture de librairie dynamique nomoInterpreter s'effectue également via un appel de fonction dont le paramètre indique le mode de sauvegarde appliqué à la base de règles :

  • ne pas enregistrer les modifications de la base de règles,
  • enregistrer les modifications de la base de règles en écrasant les anciennes,
  • sauvegarder les modifications de la base de règles dans le répertoire dédié à la journalisation.

L'interface avec la librairie dynamique nomoInterpreter repose d'une part sur une interface dédiée à nomoInterpreter regroupant les appels de fonctions et d'autre part sur une interface dédiée à la base de connaissances définie par les modèles. Cette seconde interface regroupe les constantes utilisées pour identifier le type des messages. La seconde interface dépend de l'unité nomo définie et peut être générée automatiquement à partir de celle-ci dans l'environnement nomoSDK.

L'interface avec nomoInterpreter

Plus précisément, l'interface avec la librairie dynamique nomoInterpreter s'effectue via cinq fonctions utilisant la convention d'appel du langage C :

  • nomoInitialize
  • nomoInput
  • nomoTriggers
  • nomoTriggersWithLog
  • nomoFinalize

L'initialisation de nomoInterpreter s'effectue via l'appel de la fonction nomoInitialize. Cette fonction possède trois paramètres :

  • const nomoCallback * actions, ce paramètre correspond à la liste des fonctions de rappel.
  • const char* dirLog, ce paramètre correspond au tableau de caractère contenant le chemin du répertoire dédié à la journalisation, vaut NULL en cas de non utilisation.
  • const size_t dirLength, ce paramètre correspond à la taille du tableau dirLog.

Les fonctions de rappel sont utilisées pour envoyer la crédibilité et le vecteur de sortie des règles de catégorie commande qui sont sélectionnées. L'appel de ces fonctions par le moteur d'inférence nomoInterpreter n'est pas bloquant pour celui-ci. Le type des fonctions de rappel, nomoCallback, est donc défini comme une fonction ne renvoyant rien et possédant deux paramètres :

  • const float intensity, qui correspond à la crédibilité avec laquelle la règle a été sélectionnée,
  • const float * cmd, qui correspond au vecteur de sortie associé à la conclusion de la règle. La taille du tableau n'est pas transmise via la fonction car c'est un paramètre défini par l'interface dédiée à une base de connaissances.

L'envoie de message d'entrée à nomoInterpreter s'effectue par la fonction nomoInput qui ne renvoie rien et qui prend deux paramètres :

  • const short id, correspondant au type du message d'entrée,
  • const float * data, correspondant au vecteur d'entrée.

La fonction nomoInput est gérée de manière asynchrone de telle sorte qu'elle peut être appelée par plusieurs modules à la fois. Pour un id donné, seul l'événement précédant l'interprétation est pris en compte dans la mesure où il arrivé après la dernière interprétation, comme l'illustre la figure suivante où les flèches indiquent la prise en compte de l'événement.

Pour le cadencement du moteur d'inférence, deux fonctions sont disponibles, l'une déclenchant uniquement une interprétation, l'autre déclenchant une interprétation et une journalisation. La journalisation conduit à un enregistrement partiel ou total des règles, respectivement dans un fichier ”.pr” et un fichier ”.fr”. A ces deux fichiers sont associés respectivement un fichier ”.pt” et un fichier ”.ft” qui contiennent le temps et le nombre de règles enregistrées. L'exploitation de ces fichiers s'effectue via l'environnement nomoSDK. Les fichiers sont enregistrés dans le répertoire défini à l'initialisation et sont nommés en fonction de la date et de l'heure.

La première fonction de cadencement se nomme nomoTriggers, elle ne renvoie rien et ne prend aucun paramètre.

La seconde fonction de cadencement se nomme nomoTriggersWithLog, elle ne renvoie rien également mais prend trois paramètres :

  • const nomoLogFlag *, qui correspond à un tableau spécifiant, pour tous les types de règles, le mode d'enregistrement :
    • NOMO_WINNER_PARTIAL indique que seule la règle sélectionnée devra être enregistrés avec l'identifiant, la catégorie, le type, le nombre d'ajustement, la pertinence et la crédibilité.
    • NOMO_WINNER_FULL indique que seule la règle sélectionnée devra être enregistrée dans son intégralité.
    • NOMO_ALL_RULE_PARTIAL indique que pour toutes les règles devront être enregistrés l'identifiant, la catégorie, le type, le nombre d'ajustement, la pertinence et la crédibilité.
    • NOMO_ALL_RULE_FULL indique que toutes les règles devront être enregistrées dans leur intégralité.
    • NOMO_NO_LOG indique qu'aucune journalisation ne doit être effectuée.
  • const nomoLogPositionFlag, qui signale si l'enregistrement des règles doit être effectué avant l'interprétation, avec la valeur NOMO_BEFORE_ASSUME, ou après l'interprétation, avec la valeur NOMO_AFTER_ASSUME.
  • const nomoFileFlag, qui signale s'il faut créer de nouveaux couples de fichiers ou non :
    • NOMO_NEW_FULL_FILE indique la création d'un nouveau couple de fichier dédié à l'enregistrement complet des règles (”.fr” et ”.ft”).
    • NOMO_NEW_PARTIAL_FILE indique la création d'un nouveau couple de fichiers dédié à l'enregistrement partiel des règles (”.pr” et ”.pt”).
    • NOMO_NEW_FILES indique la création d'un couple de fichiers dédié à l'enregistrement partiel des règles et d'un autre couple dédié (”.pr” et ”.pt”) à l'enregistrement complet des règles (”.fr” et ”.ft”).
    • NOMO_ANY_NEW_FILE indique qu'aucune création de fichier n'est nécessaire.

La taille du tableau nomoLogFlag ainsi que la signification des indices sont décrits dans l'interface dédiée à la base de connaissances.

A la fin de l'utilisation du moteur d'inférence, avant de décharger la librairie dynamique nomoInterpreter, la clôture de celle-ci s'effectue via la fonction nomoFinalize qui ne renvoie rien et possède le paramètre nomoFinalizationFlag :

  • NOMO_ANY_MODIFICATION correspond à une terminaison sans sauvegarde.
  • NOMO_SAVE_SEED correspond à la sauvegarde du dernier état de la base de la règle dans le répertoire indiqué à l'initialisation. La sauvegarde correspond à un couple de fichier de journalisation (.ft et .fp) et un fichier ”.seed”
  • NOMO_UPDATE_SEED correspond à la mise à jour du fichier d'initialisation ”.seed” avec le dernier état de la base de la règle.

L'interface avec une base de connaissances

Le rôle de l'interface est ici de donner une correspondance entre les types en nomo et les identifiants utilisés avec les fonctions déclarées dans l'interface de base de nomoInterpreter.

L'interface avec une base de connaissances est spécifique à chaque ensemble de types définis dans une unité nomo. Autrement dit, deux programmes différents, avec un ensemble de types identiques, possèdent une interface identique, si leur unité a la même valeur à l'attribut multiplexer.

L'interface avec une base de connaissances peut être générée automatiquement avec l'environnement nomoSDK.

Les exemples utilisés pour deux parties correspondent au cas d'une interface en C, mais ils sont facilement transposables dans les autres langages de programmation.

La première partie de l'interface définit les identifiants correspondant aux types d'entrée. Le nom de ces identifiants correspond à la concaténation, séparé d'un tiré bas, du nom de la catégorie (ici nécessairement INPUT), du nom du type, du nom du modèle (avec, s'il est plusieurs fois instancié, son numéro d'instance) et ID. Le commentaire rappelle le nom du programme qui le contient directement et le nom du modèle.

La deuxième partie de l'interface définit la taille des tableaux des entrées et des sorties en fonction de leur type (indépendamment de leur instance). La nomenclature ressemble à celle utilisée pour les identifiants, sauf que le numéro d'instance n'est pas précisé et que le terme de fin est SIZE. Le numéro d'instance n'est pas précisé car la taille des vecteurs d'entrées ou de sorties reste la même quelle que soit l'instance du modèle.

La troisième partie de l'interface définit les identifiants des types pour la journalisation. La nomenclature est identique à celle utilisée pour les identifiants des types d'entrée hormis que le terme de fin est LOG. A la fin de la liste des identifiants des types pour la journalisation, la taille du tableau nomoLogFlag est définie par TYPES_LOG_NUMBER.

Le reste de l'interface dépend si le mode multiplexage a été choisi.

Sans multiplexage

Le mode sans multiplexage signifie qu'à chaque type de commande est associée une fonction de rappel. Dans ce cas, la quatrième partie porte sur les indices des vecteurs des entrées et des sorties. Comme pour la taille des vecteurs, pour chaque type d'entrée ou de commande indépendamment du nombre de leur instance, un typedef enum est défini. Le nom de l'énumération correspond à la concaténation du nom de la catégorie, du nom du type, du nom du modèle et du terme Component. La nomenclature des constantes correspond à la concaténation, séparée d'un tiré bas, du nom de la catégorie, du nom du type et du nom de la composante.

La partie suivante déclare les fonctions de rappel respectant le type de fonction nomoCallback défini dans “nomoInterpreter.h”. La nomenclature de ces fonctions repose sur la concaténation du terme command suivi du nom du type et du nom du modèle avec son numéro d'instance si il y a plus d'une instance de ce modèle.

Le nombre total de fonctions de rappel est défini par CALLBACKS_NUMBER. Le tableau nomocallbacks est ensuite déclaré et initialisé avec les fonctions de rappel précédemment déclarées. C'est ce tableau qui devra être utilisé comme paramètre actions de la fonction nomoInitialize déclarée dans “nomoInterpreter.h”.

Ci-dessous un exemple d'interface avec une base de connaissances sans multiplexage où un programme nommé “team” contient deux sous-programmes agent1 et agent2, tous deux des instances du programme “agent” possédant le modèle “body” qui définit une structure perceptive nommée “sensor” et un type de commande nommé “motor” :

#ifndef NOMOINTERFACE_H
#define NOMOINTERFACE_H
 
/* Interface for nomoInterpreter created with nomoSDK */
 
#include "nomointerpreter.h"
 
/* ID of input types */
#define INPUT_SENSOR_BODY_1_ID 1 // program: team/agent-0 (agent1) model: body
#define INPUT_SENSOR_BODY_2_ID 2 // program: team/agent-1 (agent2) model: body
 
/* Size of inputs and commands */
#define INPUT_SENSOR_BODY_SIZE 4
#define COMMAND_MOTOR_BODY_SIZE 2
 
/* Components of inputs and components of commands */
typedef enum {INPUT_SENSOR_BODY_LEFT = 0,
              INPUT_SENSOR_BODY_FRONT = 1,
              INPUT_SENSOR_BODY_RIGHT = 2,
              INPUT_SENSOR_BODY_BACK = 2} inputSensorBodyComponent;
typedef enum {COMMAND_MOTOR_BODY_LEFT = 0,
              COMMAND_MOTOR_BODY_RIGHT = 1} commandMotorBodyComponent;
 
/* Command functions */
void commandMotorBody1 (const float intensity, const float * cmd);
void commandMotorBody2 (const float intensity, const float * cmd);
 
#define CALLBACKS_NUMBER 2
 
const nomoCallback nomocallbacks [CALLBACKS_NUMBER] = {commandMotorBody1,
                                                       commandMotorBody2};
 
/* ID of log types */
#define COMMAND_MOTOR_BODY_1_LOG 0 // program: team/agent-0 (agent1) model: body
#define COMMAND_MOTOR_BODY_2_LOG 1 // program: team/agent-1 (agent2) model: body
#define PERCEPTION_SENSOR_BODY_1_LOG 2 // program: team/agent-0 (agent1) model: body
#define PERCEPTION_SENSOR_BODY_2_LOG 3 // program: team/agent-1 (agent2) model: body
 
#define TYPES_LOG_NUMBER 4
 
#endif // NOMOINTERFACE_H

Avec multiplexage

Le mode avec multiplexage signifie que l'ensemble des instances d'un type de commande appelle la même fonction de rappel.

Dans ce cas, la quatrième partie porte sur les indices des vecteurs des entrées et des sorties, de la même manière que pour le mode sans multiplexage sauf que, dans les énumérations des composantes des vecteurs de commandes, une dernière composante est rajoutée. Cette composante ne porte pas de nom et indiquera le numéro d'instance du type de commande. A noter que la taille des vecteurs de sortie est également augmentée de un.

La partie suivante déclare les fonctions de rappel respectant le type de fonction nomoCallback défini dans “nomoInterpreter.h” mais, contrairement au mode sans multiplexage, il y a une fonction de rappel par modèle et non par instance de modèle contenant les types de commande. La nomenclature de ces fonctions repose sur la concaténation du terme command suivi du nom du type et du nom du modèle.

Le nombre total de fonctions de rappel est défini par CALLBACKS_NUMBER. Le tableau nomocallbacks est ensuite déclaré et initialisé avec les fonctions de rappel précédemment déclarées. C'est ce tableau qui devra être utilisé comme paramètre actions de la fonction nomoInitialize déclarée dans “nomoInterpreter.h”.

Ci-dessous un exemple d'interface avec une base de connaissances avec multiplexage où un programme nommé “team” contient deux sous-programme agent1 et agent2, tous deux des instances du programme “agent” possédant le modèle “body” qui définit une structure perceptive nommée “sensor” et un type de commande nommé “motor” :

#ifndef NOMOINTERFACE_H
#define NOMOINTERFACE_H
 
/* Interface for nomoInterpreter created with nomoSDK */
 
#include "nomointerpreter.h"
 
/* ID of input types */
#define INPUT_SENSOR_BODY_1_ID 1 // program: team/agent-0 (agent1) model: body
#define INPUT_SENSOR_BODY_2_ID 2 // program: team/agent-1 (agent2) model: body
 
/* Size of inputs and commands */
#define INPUT_SENSOR_BODY_SIZE 4
#define COMMAND_MOTOR_BODY_SIZE 2
 
/* Components of inputs and components of commands */
typedef enum {INPUT_SENSOR_BODY_LEFT = 0,
              INPUT_SENSOR_BODY_FRONT = 1,
              INPUT_SENSOR_BODY_RIGHT = 2,
              INPUT_SENSOR_BODY_BACK = 2} inputSensorBodyComponent;
typedef enum {COMMAND_MOTOR_BODY_LEFT = 0,
              COMMAND_MOTOR_BODY_RIGHT = 1} commandMotorBodyComponent;
 
/* Command functions with demultiplexing */
void commandMotorBody (const float intensity, const float * cmd);
 
#define CALLBACKS_NUMBER 1
 
const nomoCallback nomocallbacks [CALLBACKS_NUMBER] = {commandMotorBody};
 
/* ID of log types */
#define COMMAND_MOTOR_BODY_1_LOG 0 // program: team/agent-0 (agent1) model: body
#define COMMAND_MOTOR_BODY_2_LOG 1 // program: team/agent-1 (agent2) model: body
#define PERCEPTION_SENSOR_BODY_1_LOG 2 // program: team/agent-0 (agent1) model: body
#define PERCEPTION_SENSOR_BODY_2_LOG 3 // program: team/agent-1 (agent2) model: body
 
#define TYPES_LOG_NUMBER 4
 
#endif // NOMOINTERFACE_H

Exemples d'interfaces

Les exemples ci-dessous s'appuient sur une unité minimale “HelloWorld” qui décrit une seule règle de commande possédant aucune prémisse. La fonction de commande attachée a pour tâche d'afficher “Hello world !”.

L'intégralité de l'unité “HelloWorld” se trouve dans le fichier HelloWorld.uni avec le programme suivant :

<program xmlns="http://www.nomoseed.org/program" name="pHelloWorld">
  <body>
    <models>
      <new instance="mHelloWorld">
        <model xmlns="http://www.nomoseed.org/model" name="mHelloWorld">
          <definition>
            <command_type name="label">
              <items>
                <item name="Hello_World"/>
              </items>
              <components>
                <component name="value"/>
              </components>
            </command_type>
          </definition>
        </model>
      </new>
    </models>
    <scheme>
      <rule name="rHelloWorld" relevance="0.1">
        <conclusion model="mHelloWorld" category="command" type="label">
          <information value="Hello_World"/>
          <output value="1"/>
        </conclusion>
      </rule>
    </scheme>
  </body>
</program>

L'ensemble des fichiers évoqués se trouve dans le répertoire de “nomoSDK/helloworld” ainsi que les scripts pour leur compilation.

Ada

L'interface de base avec l'interpréteur nomoInterpreter correspond au fichier de spécification suivant, “Nomo_Interpreter.ads” :

Nomo_Interpreter.ads
with Interfaces.C.Strings;
 
with System;
 
package Nomo_Interpreter is
 
   type Log_Flag is (Winner_Partial,
                     Winner_Full,
                     All_Rule_Partial,
                     All_Rule_Full,
                     No_Log);
   for Log_Flag use (Winner_Partial   => 0,
                     Winner_Full      => 1,
                     All_Rule_Partial => 2,
                     All_Rule_Full    => 3,
                     No_Log           => 4);
   pragma Convention (C, Log_Flag);
 
   type File_Flag is (New_Full_File,
                      New_Partial_File,
                      New_Files,
                      No,
                      Temp);
   for File_Flag use (New_Full_File    => 0,
                      New_Partial_File => 1,
                      New_Files        => 2,
                      No               => 3,
                      Temp             => 4);
   pragma Convention (C, File_Flag);
 
   type Finalization_Flag is (Any_Modification,
                              Save_Seed,
                              Update_Seed);
   for Finalization_Flag use (Any_Modification => 0,
                              Save_Seed        => 1,
                              Update_Seed      => 2);
   pragma Convention (C, Finalization_Flag);
 
 
   type Log_Position_Flag is (Before_Assume,
                              After_Assume);
   for Log_Position_Flag use (Before_Assume => 0,
                              After_Assume  => 1);
 
   type Real is digits 6 range -16#0.FFFF_FF#E32 .. 16#0.FFFF_FF#E32;
   for Real'Size use 32;
   pragma Convention (C, Real);
 
   subtype Real_0_To_1 is Real range 0.0 .. 1.0;
 
   type Callback is access procedure (Intensity : in Real_0_To_1;
                                      Data      : in System.Address);
   pragma Convention (C, Callback);
   type Callbacks is array (Natural Range <>) of Callback;
   pragma Convention (C, Callbacks);
 
   procedure Initialize (Actions   : in Callbacks;
                         Directory : in Interfaces.C.Strings.chars_ptr;
                         Length    : in Interfaces.C.size_t);
   pragma Precondition (Actions'Length /= 0);
 
   procedure Input (Id   : in Interfaces.C.short;
                    Data : in System.Address);
   pragma Precondition (System."/="(Data,System.Null_Address));
 
   procedure Triggers;
 
   procedure Triggers_With_Log (Flags    : in System.Address;
                                Position : in Log_Position_Flag;
                                File     : in File_Flag);
   pragma Precondition (System."/="(Flags,System.Null_Address));
 
   procedure Finalize (Flag : in Finalization_Flag);
 
private
 
   pragma Linker_Options ("-lnomointerpreter");
   pragma Import (Convention => C, Entity => Initialize, External_Name => "nomoInitialize");
   pragma Import (Convention => C, Entity => Input, External_Name => "nomoInput");
   pragma Import (Convention => C, Entity => Triggers, External_Name => "nomoTriggers");
   pragma Import (Convention => C, Entity => Triggers_With_Log, External_Name => "nomoTriggersWithLog");
   pragma Import (Convention => C, Entity => Finalize, External_Name => "nomoFinalize");
 
end Nomo_Interpreter;

L'interface spécifiquement généré avec nomoSDK pour l'unité ciblé correspond au fichier de spécification suivant, “HelloWorld.ads” :

--  Interface for nomoInterpreter created with nomoSDK
with System;
with Nomo_Interpreter;
 
package HelloWorld is
 
    use Nomo_Interpreter;
 
    --  Size of inputs and commands
    Command_Label_Mhelloworld_Size : constant := 1;
 
   --  Components of inputs and components of commands
    type Command_Label_Mhelloworld_Component is (Value);
    for Command_Label_Mhelloworld_Component use (Value => 0);
    pragma Convention (C, Command_Label_Mhelloworld_Component);
    type Command_Label_Mhelloworld_Data is array (Command_Label_Mhelloworld_Component) of Nomo_Interpreter.Real;
    pragma Convention (C, Command_Label_Mhelloworld_Data);
 
   --  Command functions
    procedure Command_Label_Mhelloworld (Intensity : in Real_0_To_1; Data : in System.Address);
    pragma Convention (C, Command_Label_Mhelloworld);
 
    Actions : constant Callbacks (1..1) := (1 => Command_Label_Mhelloworld'Access);
 
    --  ID of log types
    type Log_ID is (Command_Label_Mhelloworld);  --  program: pHelloWorld model: mHelloWorld
    for Log_ID use (Command_Label_Mhelloworld => 0);
    pragma Convention (C, Log_ID);
 
    type Log_flags is array (Log_ID) of Log_Flag;
    No_Log_Flags : constant Log_Flags := (others => No_Log);
 
end HelloWorld;

Cette spécification doit être complétée par l'écriture du corps suivant, “HelloWorld.adb” :

with Ada.Text_IO;
 
package body HelloWorld is
 
    procedure Command_Label_Mhelloworld (Intensity : in Real_0_To_1;
                                         Data      : in System.Address) is
       Values : Command_Label_Mhelloworld_Data;
       for Values'Address use Data;
    begin
        if Values(Value) = 1.0 then
            Ada.Text_IO.Put ("Hello World !");
        end if;
   end Command_Label_Mhelloworld;
 
end HelloWorld;

Le corps du programme principal est alors, “test.adb” :

with nomoInterpreter;
with HelloWorld;
 
with Interfaces.C.Strings;
with Ada.Text_IO;
with Ada.Exceptions;
 
procedure Test is
 
   use nomoInterpreter;
 
   Echap  : Character;
 
begin
 
   Initialize (HelloWorld.Actions, Interfaces.C.Strings.New_String(""), 0);
   Triggers;
   Finalize (Any_Modification);
   Ada.Text_IO.Get_Immediate (Echap);
 
exception
   when Error : others =>
      Ada.Text_Io.Put_Line (Ada.Exceptions.Exception_Information (Error));
end Test;

C

L'interface de base avec l'interpréteur nomoInterpreter correspond au fichier d'en-tête suivant, “nomointerpreter.h” :

nomointerpreter.h
#ifndef NOMOINTERPRETER_H
#define NOMOINTERPRETER_H
 
typedef enum {NOMO_WINNER_PARTIAL   = 0,
              NOMO_WINNER_FULL      = 1,
              NOMO_ALL_RULE_PARTIAL = 2,
              NOMO_ALL_RULE_FULL    = 3,
              NOMO_NO_LOG           = 4} nomoLogFlag;
 
typedef enum {NOMO_NEW_FULL_FILE    = 0,
              NOMO_NEW_PARTIAL_FILE = 1,
              NOMO_NEW_FILES        = 2,
              NOMO_ANY_NEW_FILE     = 3} nomoFileFlag;
 
typedef enum {NOMO_BEFORE_ASSUME    = 0,
              NOMO_AFTER_ASSUME     = 1} nomoLogPositionFlag;
 
typedef enum {NOMO_ANY_MODIFICATION = 0,
              NOMO_SAVE_SEED        = 1,
              NOMO_UPDATE_SEED      = 2} nomoFinalizationFlag;
 
extern "C" typedef void (*nomoCallback)(const float intensity,
                                        const float * cmd);
 
extern "C" void nomoInitialize (const nomoCallback * actions,
                                const char* dirLog,
                                const size_t dirLength);
 
extern "C" void nomoInput (const short id,
                           const float * data);
 
extern "C" void nomoTriggers (void);
 
extern "C" void nomoTriggersWithLog (const nomoLogFlag *,
                                     const nomoLogPositionFlag,
                                     const nomoFileFlag);
 
extern "C" void nomoFinalize (const nomoFinalizationFlag);
 
#endif // NOMOINTERPRETER_H

L'interface spécifiquement générée avec nomoSDK pour l'unité ciblée correspond au fichier d'entête suivant, “helloworld.h” :

#ifndef HELLOWORLD_H
#define HELLOWORLD_H
 
/* Interface for nomoInterpreter created with nomoSDK */
 
#include "nomointerpreter.h"
 
/* Size of inputs and commands */
#define COMMAND_LABEL_MHELLOWORLD_SIZE 1
 
/* Components of inputs and components of commands */
typedef enum {COMMAND_LABEL_MHELLOWORLD_VALUE = 0} commandLabelMHelloWorldComponent;
 
/* Command functions */
void commandLabelMHelloWorld(const float intensity, const float * data);
 
#define CALLBACKS_NUMBER 1
 
const static nomoCallback nomoActions [CALLBACKS_NUMBER] = {
commandLabelMHelloWorld
};
 
/* ID of log types */
#define COMMAND_LABEL_MHELLOWORLD_LOG 0  // program: pHelloWorld model: mHelloWorld
 
#define TYPES_LOG_NUMBER 1
 
#endif // HELLOWORLD_H

Voici le programme principal contenant également la définition de la fonction de commande destinée à être attachée, “test.c” :

#include <stdio.h>
 
#include "nomointerpreter.h"
#include "HelloWorld.h"
 
void commandLabelMHelloWorld(const float intensity, const float * data){
    if (data[COMMAND_LABEL_MHELLOWORLD_VALUE] == 1)
        printf("Hello world !\n");
}
 
int main()
{
    nomoInitialize (nomoActions, "", 0);
    nomoTriggers();
    nomoFinalize(NOMO_ANY_MODIFICATION);
    getchar();
    return 0;
}

Java

L'interface en Java repose sur le composant “jna-3.4.0” (Java Native Access) sous licence LGPL v2.

L'interface de base avec l'interpréteur nomoInterpreter correspond à la classe java suivante, “NomoInterpreter.java” :

NomoInterpreter.java
import com.sun.jna.Library;
import com.sun.jna.*;
 
import com.sun.jna.ptr.PointerByReference;
 
public interface NomoInterpreter extends Library {
 
	public static enum nomoLogFlag {
		NOMO_WINNER_PARTIAL,
		NOMO_WINNER_FULL,
		NOMO_ALL_RULE_PARTIAL,
		NOMO_ALL_RULE_FULL,
		NOMO_NO_LOG;}
 
	public static enum nomoFileFlag {
		NOMO_NEW_FULL_FILE,
		NOMO_NEW_PARTIAL_FILE,
		NOMO_NEW_FILES,
		NOMO_ANY_NEW_FILE;}
 
	public static enum nomoLogPositionFlag {
		NOMO_BEFORE_ASSUME,
		NOMO_AFTER_ASSUME;}
 
	public static enum nomoFinalizationFlag {
		NOMO_ANY_MODIFICATION,
		NOMO_SAVE_SEED,
		NOMO_UPDATE_SEED;}
 
    interface NomoCallback extends Callback {
        public void invoke(final float intensity,final Pointer data );
    }
 
	public class NomoCallbackStructure extends Structure{
		public NomoCallbackStructure(){super();}
		public NomoCallbackStructure(Pointer pointer){super(pointer);}
		public static class ByValue extends NomoInterpreter.NomoCallbackStructure implements Structure.ByValue {};
		public NomoCallback callback;
		public static class ByReference extends NomoInterpreter.NomoCallbackStructure implements Structure.ByReference {};
	}
 
	public void nomoInitialize (final NomoCallbackStructure [] actions, final String dirLog, final int dirLength);
 
	public void nomoInput (final short id, final Pointer data);
 
	public void nomoTriggers ();
 
	public void nomoTriggersWithLog (final Pointer logFlag, final int logPositionFlag, final int fileFlag);
 
	public void nomoFinalize (final int finalizationFlag);
 
}

L'interface spécifiquement générée avec nomoSDK pour l'unité ciblée correspond à la classe Java dont il a fallu compléter les fonctions de commande, soit le fichier suivant, “HelloWorld.java” :

/* Interface for nomoInterpreter created with nomoSDK */
 
import com.sun.jna.Pointer;
 
public class HelloWorld {
 
    /* Size of inputs and commands */
    public static enum DataSize{
        COMMAND_LABEL_MHELLOWORLD (1);
        private final int size;
        private DataSize(int value) {
            size = value;
        }
        public int getSize() {
            return size;
        }
    }
 
    /* Components of inputs and components of commands */
    public static enum commandLabelMHelloWorldComponent {VALUE}
 
   /* Command functions */
    public static void commandLabelMHelloWorld(final float intensity, final Pointer data){
      if (data.getFloat(commandLabelMHelloWorldComponent.VALUE.ordinal()*Float.SIZE) == 1.0)
           System.out.println("Hello World !");
    }
 
    public static final NomoInterpreter.NomoCallback [] callbacks = new NomoInterpreter.NomoCallback [] {
        new NomoInterpreter.NomoCallback() { public void invoke( float intensity,  Pointer  data) { commandLabelMHelloWorld(intensity, data); } }
 
    };
 
    /* ID of log types */
    public static enum LogId {
        COMMAND_LABEL_MHELLOWORLD; // program: pHelloWorld model: mHelloWorld
    }
 
    public static final int TYPES_LOG_NUMBER = 1;
 
}

Le programme principal “test.java” devient :

import com.sun.jna.Native;
 
public class Test {
 
    public static void main(String[] args) {
        Native.setProtected(true);
        NomoInterpreter lib = (NomoInterpreter) Native.loadLibrary("nomointerpreter.dll", NomoInterpreter.class);
        NomoInterpreter.NomoCallbackStructure action = new NomoInterpreter.NomoCallbackStructure ();
        NomoInterpreter.NomoCallbackStructure [] actions = (NomoInterpreter.NomoCallbackStructure []) action.toArray (HelloWorld.callbacks.length);
        for( int i = 0; i< HelloWorld.callbacks.length; ++i){
            actions[i].callback = HelloWorld.callbacks[i];
        }
        lib.nomoInitialize (actions, "", 0); 
        lib.nomoTriggers ();
        lib.nomoFinalize (NomoInterpreter.nomoFinalizationFlag.NOMO_ANY_MODIFICATION.ordinal());
    }
 
}

Python

L'interface de base avec l'interpréteur nomoInterpreter correspond au fichier suivant, “nomointerpreter.py” :

nomointerpreter.py
import ctypes
from ctypes import cdll
from ctypes import c_int, c_short, c_float, c_char_p, POINTER, CFUNCTYPE
 
# nomoLogFlag
(NOMO_WINNER_PARTIAL, NOMO_WINNER_FULL, NOMO_ALL_RULE_PARTIAL, NOMO_ALL_RULE_FULL, NOMO_NO_LOG) = range (0, 5) 
 
# nomoFileFlag
(NOMO_NEW_FULL_FILE, NOMO_NEW_PARTIAL_FILE, NOMO_NEW_FILES, NOMO_ANY_NEW_FILE) = range (0, 4) 
 
# nomoLogPositionFlag
(NOMO_BEFORE_ASSUME, NOMO_AFTER_ASSUME) = range (0, 2)
 
# nomoFinalizationFlag
(NOMO_ANY_MODIFICATION, NOMO_SAVE_SEED, NOMO_UPDATE_SEED) = range (0, 3)
 
nomoCallback = CFUNCTYPE(None, c_float, POINTER (c_float));
 
nomoInterpreter = cdll.LoadLibrary('nomointerpreter.dll')
 
nomoInitialize = nomoInterpreter.nomoInitialize # (actions, dirLog, dirLength)
nomoInitialize.argtypes = [POINTER (nomoCallback), c_char_p, c_int]
nomoInitialize.restype = None
 
nomoInput = nomoInterpreter.nomoInput # (id, data)
nomoInput.argtypes = [c_short, POINTER (c_float)]
nomoInput.restype = None
 
nomoTriggers = nomoInterpreter.nomoTriggers
nomoTriggers.argtypes = None
nomoTriggers.restype = None
 
nomoTriggersWithLog = nomoInterpreter.nomoTriggersWithLog # (nomoLogFlags, nomoLogPositionFlag, nomoFileFlag)
nomoTriggersWithLog.argtypes = [POINTER (c_int), c_int, c_int]
nomoTriggersWithLog.restype = None
 
nomoFinalize = nomoInterpreter.nomoFinalize  # (nomoFinalizationFlag)
nomoFinalize.argtypes = [c_int]
nomoFinalize.restype = None

L'interface spécifiquement généré avec nomoSDK pour l'unité ciblée correspond au fichier suivant avec les fonctions commandes complétées, “HelloWorld.java” :

# Interface for nomoInterpreter created with nomoSDK
 
import nomointerpreter
from nomointerpreter import nomoCallback
 
# Size of inputs and commands
COMMAND_LABEL_MHELLOWORLD_SIZE = 1
 
# Components of inputs and components of commands
[COMMAND_LABEL_MHELLOWORLD_VALUE] = range (0, 1)
 
# Command functions
def commandLabelMHelloWorld(intensity, data):
    if data[COMMAND_LABEL_MHELLOWORLD_VALUE] == 1.0 :
        print ("Hello World !")
 
actions = [
nomoCallback(commandLabelMHelloWorld)
]
 
# ID of log types
[COMMAND_LABEL_MHELLOWORLD_LOG # program: pHelloWorld model: mHelloWorld
] = range (0, 1)
 
TYPES_LOG_NUMBER = 1

Le programme principal “test.py” devient :

def test():
    import nomointerpreter
    from nomointerpreter import nomoInitialize, nomoTriggers, nomoFinalize
    import HelloWorld
    import ctypes
    from ctypes import pointer
 
    nomoInitialize (pointer(HelloWorld.actions[0]), "", 0)
    nomoTriggers ()
    nomoFinalize (nomointerpreter.NOMO_ANY_MODIFICATION)
 
if __name__ == "__main__":
    test()