Aller au contenu

appel fonction lisp depuis .Net


GEGEMATIC

Messages recommandés

ça y est, j'ai trouvé un truc qui marche, en C# :

 

Il reste un message d'erreur après l'évaluation, j'espère que j'arriverais à le comprendre ...

mais bon, on peu appeller un lisp sans sendcommand, c'est déjà pas mal ...

 

 

 //
// Only for AutoCAD 2007+
// This class is using undocumented function acedEvaluateLisp
//
using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using System.Runtime.InteropServices;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(Rivilis.CSharpToLisp))]

namespace Rivilis
{
   public class CSharpToLisp
   {
       //
       // From adscodes.h :
       //
       // Type of resbuf element
       const int RTNONE = 5000; /* No result */
       const int RTREAL = 5001; /* Real number */
       const int RTPOINT = 5002; /* 2D point X and Y only */
       const int RTSHORT = 5003; /* Short integer */
       const int RTANG = 5004; /* Angle */
       const int RTSTR = 5005; /* String */
       const int RTENAME = 5006; /* Entity name */
       const int RTPICKS = 5007; /* Pick set */
       const int RTORINT = 5008; /* Orientation */
       const int RT3DPOINT = 5009; /* 3D point - X, Y, and Z */
       const int RTLONG = 5010; /* Long integer */
       const int RTVOID = 5014; /* Blank symbol */
       const int RTLB = 5016; /* list begin */
       const int RTLE = 5017; /* list end */
       const int RTDOTE = 5018; /* dotted pair */
       const int RTNIL = 5019; /* nil */
       const int RTDXF0 = 5020; /* DXF code 0 for ads_buildlist only */
       const int RTT = 5021; /* T atom */
       const int RTRESBUF = 5023; /* resbuf */

       [system.Security.SuppressUnmanagedCodeSecurity]
       [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl,
       EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")]
       extern private static int acedEvaluateLisp(string lispLine, out IntPtr result);

       static public ResultBuffer AcadEvalLisp(string arg)
       {
           IntPtr rb = IntPtr.Zero;
           acedEvaluateLisp(arg, out rb);
           if (rb != IntPtr.Zero)
           {
               try
               {
                   ResultBuffer rbb = DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer;
                   return rbb;
               }
               catch
               {
                   return null;
               }
           }
           return null;
       }

       static void PrintResbuf(ResultBuffer rb)
       {
           string s = "\n-----------------------------";
           foreach (TypedValue val in rb)
               s += string.Format("\n{0} -> {1}", val.TypeCode,
               val.Value.ToString());
           s += "\n-----------------------------";
           AcadApp.DocumentManager.MdiActiveDocument.
           Editor.WriteMessage(s);
       }

       // Define Command "CSharpToLisp"
       // Only for testing we can define this function.
       //
       // Example:
       // Command: CSharpToLisp
       // Enter lisp expression: (+ 100 50 30 20 10)
       // -----------------------------
       // 5003 -> 210
       // -----------------------------
       [CommandMethod("CSharpToLisp")]
       static public void test()
       {
           PromptResult rs =
           AcadApp.DocumentManager.MdiActiveDocument.Editor.GetString("\nEnter lisp expression: ");
           if (rs.Status == PromptStatus.OK && rs.StringResult != "")
           {
               ResultBuffer rb = AcadEvalLisp(rs.StringResult);
               if (rb != null)
               {
                   PrintResbuf(rb);
               }
               else
               {
                   AcadApp.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nError in evaluation");
               }
           }
       }
   }
}

----------------------------------------------------------------------

Site: https://www.g-eaux.fr

Blog: http://g-eaux.over-blog.com

Lien vers le commentaire
Partager sur d’autres sites

Salut,

 

Tu as trouvé chez les "pointures" (Tony Tanzillo et Alexander Rivilis excusez du peu !...)

 

Malgré ça, il semble qu'il y ait un problème avec les expressions qui retournent des listes, dommage pour évaluer LISP...

 

Ces expressions provoquent une erreur dans la méthode PrintResbuf(), j'ai donc un peu modifié le code et j'en ai profité pour remplacer val.TypeCode par (LispDataType)val.TypeCode qui retourne quelque chose de plus explicite que les codes 50## (et rend inutile l'assignation de toutes les constantes).

 

On voit clairement que si l'expression LISP retourne une liste, le ResultBuffer ne contient qu'une TypedValue égale à Nil.

//
// Only for AutoCAD 2007+
// This class is using undocumented function acedEvaluateLisp
//
using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using System.Runtime.InteropServices;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(Rivilis.CSharpToLisp))]

namespace Rivilis
{
   public class CSharpToLisp
   {
       [system.Security.SuppressUnmanagedCodeSecurity]
       [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl,
       EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")]
       extern private static int acedEvaluateLisp(string lispLine, out IntPtr result);

       static public ResultBuffer AcadEvalLisp(string arg)
       {
           IntPtr rb = IntPtr.Zero;
           acedEvaluateLisp(arg, out rb);
           if (rb != IntPtr.Zero)
           {
               try
               {
                   ResultBuffer rbb = DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer;
                   return rbb;
               }
               catch
               {
                   return null;
               }
           }
           return null;
       }

       static void PrintResbuf(ResultBuffer rb)
       {
           string s = "\n-----------------------------";
           foreach (TypedValue val in rb)
           {
               if (val.TypeCode == (short)LispDataType.Nil)
                   s += string.Format("\n{0} -> nil", (LispDataType)val.TypeCode);
               else
                   s += string.Format("\n{0} -> {1}", (LispDataType)val.TypeCode,
                   val.Value.ToString());
               s += "\n-----------------------------";
           }
           AcadApp.DocumentManager.MdiActiveDocument.
           Editor.WriteMessage(s);
       }

       // Define Command "CSharpToLisp"
       // Only for testing we can define this function.
       //
       // Example:
       // Command: CSharpToLisp
       // Enter lisp expression: (+ 100 50 30 20 10)
       // -----------------------------
       // 5003 -> 210
       // -----------------------------
       [CommandMethod("CSharpToLisp")]
       static public void test()
       {
           PromptResult rs =
           AcadApp.DocumentManager.MdiActiveDocument.Editor.GetString("\nEnter lisp expression: ");
           if (rs.Status == PromptStatus.OK && rs.StringResult != "")
           {
               ResultBuffer rb = AcadEvalLisp(rs.StringResult);
               if (rb != null)
               {
                   PrintResbuf(rb);
               }
               else
               {
                   AcadApp.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nError in evaluation");
               }
           }
       }
   }
}

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

salut,

Bizare, que ça marche pas complètement ...

incroyable qu'on arrive pas à faire en .Net ce qu'on faisait en VBA avec la class VLAX.

as tu testé l'adaptation de VLAX en c# que j'ai postée ?

mon pb avec .Net, ce que je n'ai pas encore compris les méthodes de debogage,

donc je n'arrive pas à comprendre ce qui ne marche pas

gégé

 

----------------------------------------------------------------------

Site: https://www.g-eaux.fr

Blog: http://g-eaux.over-blog.com

Lien vers le commentaire
Partager sur d’autres sites

au fait, comment ça se fait que ça marche chez toi avec

AutoCAD App,

alors que j'ai déclaré AcadApp ?

tu as un déclaration générale de AutoCAD App dans un module ?

 

la vraie difficulté avec .Net, c'est d'avoir un projet tamplate qui marche :

tous ceux que j'ai trouvé sont incomplets.

par exemple, je n'arrive pas à savoir quand on est obligé de charger manuellement une référence, et quand ça se fait tout seul ... c'est vraiment les écueils de .net

Gégé

----------------------------------------------------------------------

Site: https://www.g-eaux.fr

Blog: http://g-eaux.over-blog.com

Lien vers le commentaire
Partager sur d’autres sites

Salut,

 

Bizare, que ça marche pas complètement ...

incroyable qu'on arrive pas à faire en .Net ce qu'on faisait en VBA avec la class VLAX.

Je ne saurais dire pourquoi ça ne fonctionne pas avec les listes. La routine utilise la méthode P/Invoke qui permet d'invoquer des classes natives (ARX/C++) non 'managées' (acedEvaluateLisp) et je dois dire que ça dépasse mes maigres connaissances.

 

as tu testé l'adaptation de VLAX en c# que j'ai postée ?

Je n'ai pas essayé VLAX, je vais le faire.

 

mon pb avec .Net, ce que je n'ai pas encore compris les méthodes de debogage,

donc je n'arrive pas à comprendre ce qui ne marche pas

Je pense qu'il est relativement incontournable de se procurer un bouquin sur .NET et Visual Studio (C# ou VB) pour apprendre les fondements de VS du langage choisi et de la POO. Sans cela on risque de limiter très vite son utilisation de .NET à une programmation procédurale qui n'apporte pas grand chose par rapport au LISP ou au VBA.

 

au fait, comment ça se fait que ça marche chez toi avec

AutoCAD App,

alors que j'ai déclaré AcadApp ?

tu as un déclaration générale de AutoCAD App dans un module ?

C'est une erreur de copié/collé, j'ai bien AcadApp dans le code que j'ai testé. Je corrige.

 

la vraie difficulté avec .Net, c'est d'avoir un projet tamplate qui marche :

tous ceux que j'ai trouvé sont incomplets.

J'utilise DotNetARX et des modèles construits à partir de celui généré par cet applicatif.

J'ai essayé de faire un tuto sur AcadLabs.

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Pour ce qui était du bug que j'avais, Alexander m'a expliqué, ça vient de A64

Entry point is not found! E.g. you must replace '?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z' with

correct string from AutoCAD.exe. I have not AutoCAD x64, that is why I can not provide correct string.

You need found it youself with help of utility dumpbin.exe:

dumpbin.exe /EXPORTS AutoCAD.exe > acad.txt

In file AutoCAD.txt search substring "acedEvaluateLisp" and find full string such as '?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z',

 

donc pour A64,

EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")]

 

ça a été assez pénible de faire un dumpbin sous v64, mais c'est assez intéressant...

on découvre le ventre d'autocad ...

 

 

En tout cas, une barrière est levée : on peu évaluer silencieusement du lisp depuis .Net.

même si .net n'est pas capable de lire directement le résultat, dès l'instant ou les variables lisp sont correctement remplies, ça marche :

 

ci joint une suite d'exemples :

Commande: CSharpToLisp

 

Enter lisp expression: (setq z '( 1 2 3 4))

(1 2 3 4)

 

-----------------------------

Int16 -> 1

-----------------------------

Commande: CSharpToLisp

 

Enter lisp expression: z

 

-----------------------------

Int16 -> 1

-----------------------------

Int16 -> 2

-----------------------------

Int16 -> 3

-----------------------------

Int16 -> 4

-----------------------------

Commande: (setq z (list 1 2 3 4))

(1 2 3 4)

 

Commande: CSharpToLisp

 

Enter lisp expression: z

 

-----------------------------

Int16 -> 1

-----------------------------

Int16 -> 2

-----------------------------

Int16 -> 3

-----------------------------

Int16 -> 4

-----------------------------

Commande: CSharpToLisp

 

Enter lisp expression: (setq z "String")

"String"

 

-----------------------------

Nil -> nil

-----------------------------

Commande: CSharpToLisp

 

Enter lisp expression: z

 

-----------------------------

Text -> String

-----------------------------

Commande: CSharpToLisp

 

Enter lisp expression: (setq z '( 1 2 3 ))

(1 2 3)

 

-----------------------------

Nil -> nil

-----------------------------

Commande: CSharpToLisp

 

Enter lisp expression: z

 

-----------------------------

Point3d -> (1,2,3)

-----------------------------

Commande: !z

(1 2 3)

 

Commande: CSharpToLisp

 

Enter lisp expression: (setq z '( 1 2 3 4))

 

(1 2 3 4)

 

-----------------------------

Int16 -> 1

-----------------------------

Commande: !z

(1 2 3 4)

----------------------------------------------------------------------

Site: https://www.g-eaux.fr

Blog: http://g-eaux.over-blog.com

Lien vers le commentaire
Partager sur d’autres sites

en addition, le mail d'Alexander, qui précise certaines choses :

 

g> It seems that the result of a lisp routine cannot be passed to .net if

g> its a string, or a list.

 

1) string must be returned to .NET without problem

2) I have a problem only with lists looking like DXF-list, but was not correct:

'((0 . "entity-type") (1 . 1) (8 . "LAYER") ... )

With list such as '("string" 0.1 2 3 "string") - I have not problem.

3) vla-object can not be returned (IMHO)

 

g> I don't have found the way of checking if AutoCAD is 64 or 32 bits, except

g> this :

http://www.csharp411.com/determine-if-your-c-application-is-64-bit/

 

Best Regards,

Alexander Rivilis

----------------------------------------------------------------------

Site: https://www.g-eaux.fr

Blog: http://g-eaux.over-blog.com

Lien vers le commentaire
Partager sur d’autres sites

Très intéressant et à creuser (en attendant qu'Autodesk réponde aux souhaits des développeurs...).

Le problème avec P/Invoke (utilisation de classes non managées), c'est une moins bonne compatibilité entre les versions (ObjectARX étant modifié à chaque nouveau format d'AutoCAD).

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

la vraie difficulté avec .Net, c'est d'avoir un projet tamplate qui marche :

tous ceux que j'ai trouvé sont incomplets.

 

Si tu utilises AutoCAD 2010, regarde ici.

Ce modèle crée une classe MyPlugin qui implémente IExtensionApplication et une classe MyCommands qui crée l'ébauche de plusieurs types de commandes et d'une fonction LISP.

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

ça a été assez pénible de faire un dumpbin sous v64, mais c'est assez intéressant...

 

Une méthode donnée par Kean Walmsley :

Copier/coller ceci dans un fichier "findapi.bat" enregistré dans le répertoire d'installation d'AutoCAD :

 

@echo off

if "%1" == "" goto usage

:normal

for %%i IN (*.exe *.dll *.arx *.dbx *.ocx *.ddf) DO dumpbin /exports %%i | findstr "%%i %1"

goto end

:usage

echo findapi "function name"

:end

 

Dans la console d'invite de commande de Visual Studio (elle sait où trouver dumpbin.exe), se placer dans le répertoire d'installation d'AutoCAD et lancer :

 

C:\Program Files\AutoCAD 2010>findapi acedEvaluateLisp > results.txt

 

Le résultat sera copié dans le fichier results.txt.

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

  • 3 mois après...

Salut,

 

J'ai trouvé un truc plus simple avec acedInvoke sur TheSwamp.

Pas besoin d'EntryPoint donc on devrait éviter les problèmes de compatibilité entre versions.

 

La méthode InvokeLisp requiert un ResultBuffer comme arguments dont la valeur du premier TypedValue est le nom de la fonction LISP invoquée, les TypedValue suivants sont les arguments requis par la fonction LISP.

Le résultat de l'évaluation de la fonction est retourné sous forme de ResulBuffer.

 

Côté LISP, si la fonction invoquée n'a pas le préfixe c:, il faut utiliser vl-acad-defun pour la rendre accessible aux applications ObjectARX.

 

Exemple avec une fonction LISP simple qui requiert une liste de chaînes comme argument :

(defun foo (l) (mapcar 'strcase l))
(vl-acad-defun 'foo)

 

using System;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;

namespace InvokeLispSample
{
   public class Class1
   {
       [system.Security.SuppressUnmanagedCodeSecurity]
       [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl)]
       extern static private int acedInvoke(IntPtr rbIn, out IntPtr rbOut);

       public static ResultBuffer InvokeLisp(ResultBuffer resbuf)
       {
           IntPtr rb = IntPtr.Zero;
           acedInvoke(resbuf.UnmanagedObject, out rb);
           return (ResultBuffer)DisposableWrapper.Create(typeof(ResultBuffer), rb, true);
       }

       [CommandMethod("Test")]
       public void Test()
       {
           Editor ed =
               Application.DocumentManager.MdiActiveDocument.Editor;

           // Argument pour InvokeLisp
           ResultBuffer resbuf = new ResultBuffer();

           // Nom de la fonction LISP : foo
           resbuf.Add(new TypedValue((int)LispDataType.Text, "foo"));

           // Argument de la fonction LISP : ("test" "invoke" "lisp")
           resbuf.Add(new TypedValue((int)LispDataType.ListBegin));
           resbuf.Add(new TypedValue((int)LispDataType.Text, "test"));
           resbuf.Add(new TypedValue((int)LispDataType.Text, "invoke"));
           resbuf.Add(new TypedValue((int)LispDataType.Text, "lisp"));
           resbuf.Add(new TypedValue((int)LispDataType.ListEnd));

           // Résultat de l'évaluation de (foo '("test" "invoke" "lisp"))
           ResultBuffer result = InvokeLisp(resbuf);
           foreach (TypedValue tv in result)
           {
               ed.WriteMessage(tv.Value + " ");
           }
       }
   }
}

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

  • 1 mois après...
  • 5 mois après...

Salut Giles,

je sort d'un long tunnel ,et je vois que les sujets avance, sans qu'on ai besoin de travailler :

le bonheur !

Je n'ai malheureusement pas eu le temps de poursuivre mes investigation en .Net, je vais être obligé de repartir à zéro.

pour le VAST promis, c'est pour quand ?

A+

Gégé

 

----------------------------------------------------------------------

Site: https://www.g-eaux.fr

Blog: http://g-eaux.over-blog.com

Lien vers le commentaire
Partager sur d’autres sites

  • 1 an après...

Bonjour

 

Désolé de revenir sur des vieux sujets mais cette dernière méthode de (gile) m'intéresse beaucoup (j'ai besoin d'une échange avec le Lisp) et je n'arrive pas à la faire marcher, j'ai un message d'erreur de Netload que je ne sais pas interpréter ... A tout hasard j'ai remplacé les IntPtr par des Int32/64 (version 2010) mais ça ne convient pas à acedInvoke (et l'aide ne me dit pas grand chose là dessus).

 

Sinon, est ce que cette méthode permettrait de renvoyer des objets, des listes, des listes de listes .. ?

Lien vers le commentaire
Partager sur d’autres sites

Salut Krunch

pour ce sujet, tu peux regarder cette conversation :

liaison lisp net

Je n'ai jamais eu le temps de finir le modèle tout fait, mais avec ce qu'il y a de posté, la liaison marche définitivement et très simplement dans les 2 sens.

----------------------------------------------------------------------

Site: https://www.g-eaux.fr

Blog: http://g-eaux.over-blog.com

Lien vers le commentaire
Partager sur d’autres sites

Salut.

Oui j'ai lu ce sujet, mais c'est du VB ..

 

ça ne change pas grand chose, l'exemple sont je suis partit était bien en c#...

 

L'interret de la méthode est de s'affranchir des contraintes d'appel avec l'usage d'eval.

 

ça ne doit pas être très compliqué à traduire en c#

----------------------------------------------------------------------

Site: https://www.g-eaux.fr

Blog: http://g-eaux.over-blog.com

Lien vers le commentaire
Partager sur d’autres sites

la liaison marche définitivement et très simplement

Le 'très simplement' est décidément une notion bien relative ...

 

Alors il y avait sans doute une bourde de ma part dans l'erreur du Netload (un "Test" doublon dans la solution d'après ce que j'ai compris)

 

Il me semble que j'arrive maintenant à faire marcher la méthode 'InvokeLisp' de (gile) ci-dessus ou encore celle-ci, par contre tel quel il y a une erreur dans la commande de Test non ?

 

Commande: test
; erreur: nombre d'arguments trop important
-1

Lien vers le commentaire
Partager sur d’autres sites

Bonjour

 

Désolé de revenir sur des vieux sujets mais cette dernière méthode de (gile) m'intéresse beaucoup (j'ai besoin d'une échange avec le Lisp) et je n'arrive pas à la faire marcher, j'ai un message d'erreur de Netload que je ne sais pas interpréter ... A tout hasard j'ai remplacé les IntPtr par des Int32/64 (version 2010) mais ça ne convient pas à acedInvoke (et l'aide ne me dit pas grand chose là dessus).

 

Sinon, est ce que cette méthode permettrait de renvoyer des objets, des listes, des listes de listes .. ?

 

Il ne faut pas modifier la partie Platform Invoke (P/invoke) qui permet d'appeler la méthode non managée acedInvoke() ni la méthode LispInvoke() (donc bien conserver le type Intptr).

 

Ensuite, pour l'utilisation de cette méthode LispInvoke (comme pour la méthode Invoke() à partir de 2011) l'argument doit être un ResultBuffer dont le premier élément est le nom de la fonction LISP appelée et les suivants les arguments requis par la fonction LISP.

Un ResultBuffer est un conteneur d'instances de TypedValue.

Une TypedValue est un peu l'équivalent d'une paire pointée en LISP ou d'un variant en VB(A) (même si fondamentalement ça n'a rien à voir) qu'on construit avec un entier (TypeCode) et un objet (Value). Le type sous-jascent de l'objet (Value) doit correspondre au TypeCode. Pour les TypedValue utilisées en interaction avec LISP les TypeCode utilsables se retrouvent dans l'énumération (Enum) LispDataType et permettent de construire des listes, des paires pointées, et tous les types utilisés par AutoLISP.

 

De même la valeur de retour de InvokeLisp (ou Invoke) est de type ResultBuffer et peut donc contenir des TypedValue de tous les TypeCode de l'énumération LispDataType.

 

LispDataType Enum

Angle      	5004
DottedPair    	5018
Double       	5001
Int16      	5003
Int32      	5010
ListBegin   	5016
ListEnd 	5017
Nil    		5019
None     	5000
ObjectId  	5006
Orientation     5008
Point2d 	5002
Point3d 	5009
SelectionSet	5007
T_atom       	5021
Text     	5005
Void     	5014

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !

Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.

Connectez-vous maintenant
×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer. Politique de confidentialité