English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
POO en MQL5 par exemple : Traitement des Codes d'Avertissement et d'Erreur

POO en MQL5 par exemple : Traitement des Codes d'Avertissement et d'Erreur

MetaTrader 5Exemples | 17 novembre 2021, 14:41
180 0
KlimMalgin
KlimMalgin

Brève introduction à la POO

Avant de commencer à élaborer, familiarisons-nous avec certaines fonctionnalités de la POO, qui seront utilisées dans cet article. 

Bien sûr, nous utiliserons des structures et des classes. Ce sont les bases des langages orientés-objet. Quelle est la structure, quelle est la classe et en quoi diffèrent-elles ?

Une structure est une construction qui permet de comprendre un ensemble de variables et de fonctions de différents types (sauf void).

Une classe ainsi que la structure est un ensemble de champs de données. Mais une classe est une construction plus compliquée et "flexible". Les classes sont le concept de base de la POO. Les différences de classes et de structures sont décrites dans la documentation. je vais répéter :

  • le mot-clé class est utilisé en la déclaration ;
  • Par défaut, le spécificateur d'accès de tous les membres de la classe est privé, sauf indication contraire. Les données membres des structures sont de type accès public par défaut, sauf indication contraire ;
  • les instances de classe disposent toujours d’un tableau des fonctions virtuelles, même s'il n'y a aucune fonction virtuelle déclarée dans la classe. Les structures ne peuvent pas avoir de fonctions virtuelles ;
  • l'opérateur new peut être appliqué aux classes ; cet opérateur ne peut pas être appliqué aux structures ;
  • les classes ne peuvent dériver que d'autres classes, les structures ne peuvent dériver que d'autres structures.

 

Examinons maintenant les classes. Tous les champs de classe sont divisés en deux types. Ce sont des données membres (variables, tableaux, etc.) et des fonctions définies à l'intérieur d'une classe.

Les membres de donnéessont généralement appelés propriétésde classe, car lorsqu'un objet est créé à partir d'une classe, les membres de données reflètent exactement les propriétés de cet objet. Par exemple, s'il s'agit d'une forme géométrique - cercle, les propriétés comprendront son rayon, la largeur de la ligne, la couleur de la forme, c'est-à-dire les propriétés de l'objet.

Les fonctionsdéfinies à l'intérieur d'une classe sont appelées méthodes. Ils sont utilisés à la fois pour travailler avec les propriétés de classe et pour implémenter tout autre algorithme qui y est programmé.

La programmation orientée objet a la notion d'encapsulation. Il s'agit de la possibilité de cacher des données et l’implémentation d'un objet à l'influence directe d'un utilisateur (programmeur d'application). Un programmeur n'obtient que de la documentation décrivant les méthodes permettant de modifier les propriétés d'un objet, tandis que la modification directe des propriétés d'un objet est impossible.

De telles mesures de protection sont nécessaires en les cas où un certain nombre de contrôles sont nécessaires avant de modifier la valeur d'un bien. Toutes les vérifications nécessaires sont implémentées dans les méthodes et, si elles sont effectuées avec succès, elles permettent de modifier la valeur de la propriété. Et en le cas où un utilisateur bénéficie d’un accès direct aux propriétés, ces vérifications ne sont pas effectuées et, par conséquent, la valeur de la propriété peut être définie de manière incorrecte, et le programme MQL ne fonctionnera pas correctement.

Pour toutes les propriétés et méthodes de classe, nous pouvons définir un niveau d'accès à l'aide de trois modificateurs : private, protectedet public.

Si le modificateur privé est utilisé pour un champ de classe, l'accès à ce champ n'est possible qu'à l'aide de méthodes de la même classe, il ne peut donc pas être modifié de l'extérieur. Le modificateur protégé impose également certaines restrictions sur accès au champ de l'extérieur, mais il permet d'accéder aux champs de classe pour les méthodes de la même classe et pour les méthodes des sous-classes. Au contraire, public supprime toutes les restrictions d'accès et ouvre un accès libre aux champs de classe.

Création d'un fichier include mqh

La classe que nous allons écrire doit être située dans un fichier mqh séparé, afin qu'elle puisse être incluse dans nos programmes (Expert Advisors, scripts, indicateurs).

Pour créer ce fichier, utilisons l'assistant MQL5. dans le menu Fichier -> Créez, sélectionnez Inclure le fichier (*.mqh)et passez au Suivant. Dans la fenêtre qui apparaît, entrez le nom du fichier (je l'ai appelé ControlErrors) et appuyez sur Terminé. Un modèle de fichier mqh sera ouvert. Nous allons poursuivre le travail ce fichier.

Pour Commencer

Vous connaissez maintenant tous les fondamentaux théoriques de la POO qui peuvent vous être utiles dans le processus d'étude de cet article. Alors, continuons

Examinons le code de la classe avec l’annonce de toutes ses propriétés et méthodes :

class ControlErrors
{
private:

   // Flags determining what types of statements should be introduced
   bool _PlaySound;    // Play or don't play a sound when an error occurs
   bool _PrintInfo;    // Enter error data the the journal of Expert Advisors
   bool _AlertInfo;    // Generate Alert alert with error data
   bool _WriteFile;    // Write reports on errors onto a file or not
   
   // A structure for storing error data and elements that use this structure
   struct Code
   {
      int code;      // Error code
      string desc;   // Description of an error code
   };
   Code Errors[];    // Array that contains error codes and their descriptions
   Code _UserError;  // Stores information about a custom error
   Code _Error;      // Stores information about the last error of any type   
   
   // Different service properties
   short  _CountErrors;     // Number of errors stored in array Errors[]
   string _PlaySoundFile;   // File that will be played for an alert sound
   string _DataPath;        // Path to the log storing directory

   
public:
   // Constructor
   ControlErrors(void);
   
   // Methods for setting flags
   void SetSound(bool value);          // Play or don't play a sound when an error occurs
   void SetPrint(bool value);          // Enter error data the the journal of Expert Advisors or not
   void SetAlert(bool value);          // Generate an Alert message or not
   void SetWriteFlag(bool flag);       // Set the writing flag. true - keep logs, false - do not keep
   
   // Methods for working with errors
   int  mGetLastError();            // Returns contents of the system variable _LastError
   int  mGetError();                // Returns code of the last obtained error
   int  mGetTypeError();            // Returns error type (Custom = 1 ore predefined = 0)
   void mResetLastError();          // Resets the contents of the system variable _LastError
   void mSetUserError(ushort value, string desc = "");   // Sets the custom error
   void mResetUserError();          // Resets class fields that contain information about the custom error
   void mResetError();              // Resets the structure that contains information about the last error
   string mGetDesc(int nErr = 0);   // Returns error description by the number, or that of the current error of no number
   int Check(string st = "");       // Method to check the current system state for errors
   
   // Alert methods(Alert, Print, Sound)
   void mAlert(string message = "");
   void mPrint(string message = "");
   void mSound();
      
   // Various service methods
   void SetPlaySoundFile(string file); // Method sets the file name to play an sound
   void SetWritePath(string path);     // Set the path to store logs  
   int mFileWrite();                   // Record into a file the available information about the last error
};

Propriétés de Classe

Vient d'abord l’annonce des propriétés de la classe, le modificateur privé st appliqué à toutes les propriétés, il est donc impossible de travailler avec des propriétés directement en dehors de la classe. Les propriétés sont divisées en trois classes pour plus de commodité :

  1. Indicateurs qui déterminent les types de rapports doivent être conservés. Tous ces drapeaux ne peuvent accepter que deux valeurs : true indique que ce type de rapports (notifications) est activé et false - indique que ce type est désactivé.
    • _PlaySound - variable qui désactive permet la lecture d'une mélodie ou d'un son sélectionnés lorsqu'une erreur se produit.
    • _PrintInfo - est chargé d'ajouter les détails des erreurs dans le journal Expert Advisor.
    • _AlertInfo - active ou désactive la sortie de l'alerte avec les informations d'erreur.
    • _WriteFile - active ou désactive l'enregistrement des informations d'erreur en un fichier.
  2. Structure de stockage des données d'erreur et des éléments qui utilisent cette structure.
    • Code - structure proprement dite Il a été créé pour faciliter le stockage des données d'erreur dans un tableau.
    • Erreurs - un tableau de type Code, c'est-à-dire que chaque élément du tableau est une structure Code.
    • _UserError - une variable de type Code. Il est poursuivi pour avoir travaillé avec des erreurs personnalisées.
    • _Error - une variable de type Code. La dernière erreur survenue est placée dans cette variable et un travail ultérieur avec l'erreur est entrepris via cette variable.
  3. Autres propriétés de service.
    • _CountErrors - la variable comporte le nombre d'erreurs, dont les données doivent être stockées en le tableau Errors. Il est utilisé pour indiquer la taille du tableau.
    • _PlaySoundFile - il comporte le nom du fichier qui sera joué pour un son d'alerte.
    • _DataPath - il comporte le chemin et le nom des données d'erreur du fichier journal dans lesquelles sont écrites.

Je pense que tout est clair avec le premier groupe de propriétés : elles activent ou désactivent la conservation de certains rapports. Dans le deuxième groupe, la structure du code est intéressante. De quoi s'agit-il et pourquoi cette structure est-elle exactement utilisée comme éléments du tableau ? Tout est facile ! Il est beaucoup plus pratique de stocker toutes les données nécessaires dans un seul élément de tableau que de définir des tableaux séparés pour un code d'erreur et sa description. Une structure est utilisée pour implémenter une telle possibilité. Tous les champs obligatoires sont déclarés à l'intérieur d'une structure. en notre cas, ce sont :

  • code - champ de type int qui comporte le code d'erreur ;
  • desc - champ de type chaîne. Il comporte la description de l'erreur.

En fait, la structure est un type de donnéescomposé, c'est-à-dire qu'elle peut être utilisée pour déclarer des variables et des tableaux, ce qui a été fait. De ce fait, chaque variable de type Code comportera des champs de cette structure. De plus, chaque élément du tableau de type Code comporte deux champs pour stocker le code et sa description. Ainsi, un moyen assez pratique pour stocker des données d'objets de différents types dans un seul emplacement est implémenté en MQL5.

Viennent ensuite les variables _UserError et _Error. Elles contiennent toutes les deux des informations sur la dernière erreur survenue, bien que _UserError stocke des informations sur les erreurs personnalisées, tandis que _Error - sur toutes les erreurs.

Et le troisième groupe de propriétés. Ici, j'ai inclus toutes les propriétés restantes, qui ne peuvent pas être incluses dans le premier ou le deuxième groupe. Ils sont trois. Premièrement - _CountErrors, il comporte le nombre d'erreurs dont les informations sont stockées dans le tableau _Errors. Cette propriété est utilisée pour définir la taille du tableau _Errors dans le constructeur et certaines méthodes d'appel des éléments du tableau. La deuxième propriété est _PlaySoundFile. EIle stocke le nom d'un fichier son qui est lu lorsqu'une erreur se produit. La troisième propriété est _DataPath. EIle stocke le chemin et le nom du fichier pour la conservation des journaux.

Méthodes de classe. Constructeur

Vient ensuite la description du constructeur et des méthodes de classe. Commençons par l’examen du constructeur et essayons de comprendre de quoi il s'agit. Comme les méthodes, il s'agit d'une fonction commune définie à l'intérieur d'une classe, mais disposant de fonctionnalités spécifiques :

  • Un nom de constructeur correspond au nom de classe.
  • Un constructeur n'a pas de valeur de retour (le type void est indiqué).
  • Un constructeur ne dispose pas de paramètres d'entrée.

Habituellement, les membres de classe sont initialisés dans les constructeurs. Par exemple, dans notre constructeur de classe, tous les indicateurs pour désactiver la conservation des rapports sont définis, les noms d'un fichier son et d'un fichier journal sont indiqués, la taille du tableau _Errors est définie et ce tableau est rempli de données. Ci-dessous, je ne publierai qu'une partie du code du constructeur, car il est trop volumineux et du même type - la partie principale est occupée par le remplissage du tableau _Errors avec les codes et leurs descriptions. Le code complet est joint à l'article.

void ControlErrors::ControlErrors(void)
{
   SetAlert(false);
   SetPrint(false);
   SetSound(false);
   SetWriteFlag(false);
   SetPlaySoundFile("alert.wav");
   SetWritePath("LogErrors.txt");
   
   _CountErrors = 150;
   ArrayResize(Errors, _CountErrors);

   // Return codes of a trade server
   Errors[0].code = 10004;Errors[0].desc = "Requote";
   Errors[1].code = 10006;Errors[1].desc = "Request rejected";
   Errors[2].code = 10007;Errors[2].desc = "Request canceled by trader";
   Errors[3].code = 10008;Errors[3].desc = "Order placed";
   Errors[4].code = 10009;Errors[4].desc = "Request is completed";
   Errors[5].code = 10010;Errors[5].desc = "Request is partially completed";
   Errors[6].code = 10011;Errors[6].desc = "Request processing error";
   Errors[7].code = 10012;Errors[7].desc = "Request canceled by timeout";
   Errors[8].code = 10013;Errors[8].desc = "Invalid request";
   Errors[9].code = 10014;Errors[9].desc = "Invalid volume in the request";
   Errors[10].code = 10015;Errors[10].desc = "Invalid price in the request";
   Errors[11].code = 10016;Errors[11].desc = "Invalid stops in the request";
   Errors[12].code = 10017;Errors[12].desc = "Trade is disabled";
   Errors[13].code = 10018;Errors[13].desc = "Market is closed";
   Errors[14].code = 10019;Errors[14].desc = "There is not enough money to fulfill the request";
   Errors[15].code = 10020;Errors[15].desc = "Prices changed";
   Errors[16].code = 10021;Errors[16].desc = "There are no quotes to process the request";
   Errors[17].code = 10022;Errors[17].desc = "Invalid order expiration date in the request";
   Errors[18].code = 10023;Errors[18].desc = "Order state changed";
   Errors[19].code = 10024;Errors[19].desc = "Too frequent requests";
   Errors[20].code = 10025;Errors[20].desc = "No changes in request";
   Errors[21].code = 10026;Errors[21].desc = "Autotrading disabled by server";
   Errors[22].code = 10027;Errors[22].desc = "Autotrading disabled by client terminal";
   Errors[23].code = 10028;Errors[23].desc = "Request locked for processing";
   Errors[24].code = 10029;Errors[24].desc = "Order or position frozen";
   Errors[25].code = 10030;Errors[25].desc = "Invalid order filling type";

   // Common Errors
   Errors[26].code = 4001;Errors[26].desc = "Unexpected internal error";
   Errors[27].code = 4002;Errors[27].desc = "Wrong parameter in the inner call of the client terminal function";
   Errors[28].code = 4003;Errors[28].desc = "Wrong parameter when calling the system function";
   Errors[29].code = 4004;Errors[29].desc = "Not enough memory to perform the system function";
   Errors[30].code = 4005;Errors[30].desc = "The structure contains objects of strings and/or dynamic arrays and/or structure of such objects and/or classes";
   Errors[31].code = 4006;Errors[31].desc = "Array of a wrong type, wrong size, or a damaged object of a dynamic array";
   Errors[32].code = 4007;Errors[32].desc = "Not enough memory for the relocation of an array, or an attempt to change the size of a static array";
   Errors[33].code = 4008;Errors[33].desc = "Not enough memory for the relocation of string";
   Errors[34].code = 4009;Errors[34].desc = "Not initialized string";
   Errors[35].code = 4010;Errors[35].desc = "Invalid date and/or time";
   Errors[36].code = 4011;Errors[36].desc = "Requested array size exceeds 2 GB";
   Errors[37].code = 4012;Errors[37].desc = "Wrong pointer";
   Errors[38].code = 4013;Errors[38].desc = "Wrong type of pointer";
   Errors[39].code = 4014;Errors[39].desc = "System function is not allowed to call";

}

Veuillez noter que la description de l'implémentation est effectuée en dehors de la classe ! Seules les méthodes sont déclarées à l'intérieur de la classe ! Bien que ce ne soit pas obligatoire. Si vous souhaitez vous pouvez décrire le corps de chaque méthode de la classe, mais à mon avis, il est gênant et difficile à comprendre.

Comme je l'ai dit, seuls les en-têtes des fonctions de méthode sont déclarés dans le corps de la classe, tandis que la description est effectuée en dehors de la classe. Lors de la description d'une méthode, vous devez indiquer à quelle classe elle appartient. Le contexte permettant l'opération :: est utilisé pour cela. Comme vu depuis le code ci-dessus le type de retour d'une méthode est d'abord spécifié (pour un constructeur, il est void), puis vient le nom de la classe (nom du contexte auquel appartient la méthode), et le nom de la classe est suivi du contexte permettant l'opération, puis vient le nom de la méthode avec ses paramètres d'entrée. Après tout cela, la description de l'algorithme de la méthode commence.

Tout d'abord en le constructeur, tous les Indicateurs sont définis et les fichiers son et journaux sont indiqués : 

SetAlert(false);
SetPrint(false);
SetSound(false);
SetWriteFlag(false);
SetPlaySoundFile("alert.wav");
SetWritePath("LogErrors.txt"); 

Chacune de ces méthodes fonctionne avec une certaine propriété de classe. Ceci est fait volontiers pour le cas d'une nécessité de filtrer les valeurs définies par un utilisateur pour les propriétés. Par exemple, vous pouvez définir un certain masque, auquel un nom de fichier et un chemin d'accès personnalisés doivent correspondre. S'il y a maintenant correspondance à masquer, l'utilisateur en sera informé. 

Comme vous l'avez peut-être mentionné, tous les Indicateurs prennent la valeur false. C'est-à-dire qu'aucun rapport ne sera conservé par défaut, lorsqu'un échantillon de classe est créé. Un utilisateur doit sélectionner les rapports à conserver et les activer en utilisant les mêmes méthodes "Set" en la fonction OnInit(). De même, vous pouvez modifier le nom et le chemin du fichier journal (le chemin est défini par rapport au répertoire 'MetaTrader 5\MQL5\Files\') et le fichier son (le chemin est défini par rapport au répertoire 'MetaTrader 5\Sounds\').

Une fois les Indicateurs définis, nous initialisons la variable _CountErrors en lui attribuant la valeur 150 (des informations sur 149 tableaux seront stockées dans le tableau), puis définissons la taille du tableau requise à l'aide de la fonction ArrayResize(). Après cela, nous entamons le remplissage du tableau.

Méthodes de Réglage d’Indicateur

La description du constructeur est suivie de la description des méthodes de définition des indicateurs et de la définition des noms de fichiers audio et journaux :

void ControlErrors::SetAlert(bool value)
{
   _AlertInfo = value;
}

void ControlErrors::SetPrint(bool value)
{
   _PrintInfo = value;
}

void ControlErrors::SetSound(bool value)
{
   _PlaySound = value;
}

void ControlErrors::SetWriteFlag(bool flag)
{
   _WriteFile = flag;
}

void ControlErrors::SetWritePath(string path)
{
   _DataPath = path;
}

void ControlErrors::SetPlaySoundFile(string file)
{
   _PlaySoundFile = file;
}

Comme l’indique le code, il s'agit d'une simple affectation transmise à la méthode de paramètre, propriété de classe. Les indicateurs ne nécessitent aucune vérification, car ils ne peuvent prendre que deux valeurs. Cependant, les noms de fichiers et les chemins doivent être vérifiés avant l'attribution.

Les appels de ces méthodes, ainsi que toutes les autres, se présentent comme suit : 

type Class_name::Function_Name(parameters_description)
{
   // function body
}

Vient ensuite la description des méthodes de travail avec les erreurs, et les premières d'entre elles sont mGetLastError() et mResetLastError().

Méthodes mGetLastError() et mResetLastError() 

Le nom de la méthode mGetLastError() parle de lui-même. Il duplique la fonction GetLastError(). Mais en plus de l'appel de GetLastError(), une description est recherchée pour le code d'erreur obtenu dans le tableau _Errors, et les détails de l'erreur (le code et sa description) sont enregistrés dans la variable _Error, pour plus tard utiliser la valeur enregistrée au lieu d'appeler GetLastError () à chaque fois.

Le code méthode :

int ControlErrors::mGetLastError(void)
{
   _Error.code = GetLastError();
   _Error.desc = mGetDesc(_Error.code);
   return _Error.code;
}

La méthode mResetLastError() duplique la fonction ResetLastError() :

void ControlErrors::mResetLastError(void)
{
   ResetLastError();
}

Méthodes d'utilisation du dernier message d'erreur

Ce sont deux méthodes : mGetError() et mResetError().

La méthode mGetError() retourne le code contenu en _Error.code :

int ControlErrors::mGetError(void)
{
   return _Error.code;
}

La méthode mResetError() réinitialise le contenu de la variable _Error :

void ControlErrors::mResetError(void)
{
   _Error.code = 0;
   _Error.desc = "";
}

Méthode de Détermination du Type d'Erreur mGetTypeError()

La méthode suivante est mGetTypeError(). EIle vérifie si la dernière erreur survenue est une erreur personnalisée ou si elle est prédéfinie (est contenue dans le tableau _Errors).

Le code méthode :

int ControlErrors::mGetTypeError(void)
{
   if (mGetError() < ERR_USER_ERROR_FIRST)
   {
      return 0;
   }
   else if (mGetError() >= ERR_USER_ERROR_FIRST)
   {
      return 1;
   }
   return -1;
}

La constante ERR_USER_ERROR_FIRST est d’une de valeur 65536. À partir de ces codes, les erreurs personnalisées commencent. Ainsi, dans le corps de la méthode, le dernier code d'erreur reçu est vérifié. Si la méthode renvoie zéro, il s'agit d'une erreur prédéfinie. Si l'un est renvoyé, il s'agit d'une erreur personnalisée.

Méthodes de Travail avec des Erreurs Personnalisées

en MQL5, les utilisateurs peuvent définir leurs propres erreurs au cours du programme. Pour que les codes personnalisés puissent se voir attribuer des descriptions appropriées, la propriété _UserError est disponible en la classe. Deux méthodes sont utilisées pour travailler avec cette propriété.

La méthode mSetUserError() est utilisée pour définir un code et décrire l'erreur personnalisée :

void ControlErrors::mSetUserError(ushort value, string desc = "")
{
   SetUserError(value);
   _UserError.code = value;
   _UserError.desc = desc;
}

Tout d'abord, la fonction SetUserError() définit la variable prédéfinie _LastError sur la valeur égale à ERR_USER_ERROR_FIRST + valeur. Et puis la valeur et sa description attribuée appropriée sont enregistrées dans la variable _UserError.

La deuxième méthode mResetUserError() réinitialise les champs de la variable _UserError :

void ControlErrors::mResetUserError(void)
{
   _UserError.code = 0;
   _UserError.desc = "";
}

Cette méthode ne peut fonctionner qu'avec la variable _UserError. Pour réinitialiser la valeur de la variable système _LastError, une autre méthode est utilisée : mResetLastError(), elle est décrite ci-dessus.

Méthode d'Obtention de Description du Code d’Erreur

Il existe également une méthode spéciale mGetDesc() dans la classe, qui, appelée, renverra la description du code d'erreur à partir du tableau Errors ou à partir de la description du champ de la variable _UserError, si l'erreur a été définie par un utilisateur :

string ControlErrors::mGetDesc(int nErr=0)
{
   int ErrorNumber = 0;
   string ReturnDesc = "";
   
   ErrorNumber = (mGetError()>0)?mGetError():ErrorNumber;
   ErrorNumber = (nErr>0)?nErr:ErrorNumber;
   
   if ((ErrorNumber > 0) && (ErrorNumber < ERR_USER_ERROR_FIRST))
   {
      for (int i = 0;i<_CountErrors;i++)
      {
         if (Errors[i].code == ErrorNumber)
         {
            ReturnDesc = Errors[i].desc;
            break;
         }
      }
   }
   else if (ErrorNumber > ERR_USER_ERROR_FIRST)
   {
      ReturnDesc = (_UserError.desc=="")?"Cusrom error":_UserError.desc;
   }
      
   if (ReturnDesc == ""){return "Unknown error code: "+(string)ErrorNumber;}
   return ReturnDesc;
}

Cette méthode dispose d’ un paramètre nErr. Il est égal à zéro par défaut. Si pendant l'appel de méthode une valeur est définie sur le paramètre, la description sera recherchée pour la valeur définie. Si le paramètre n'est pas défini, la description sera recherchée pour le dernier code d'erreur reçu.

Au départ, deux variables sont déclarées dans la méthode : ErrorNumber - en utilisant cette variable, le code d'erreur sera traité ; et ReturnDesc - la description obtenue pour ErrorNumber y sera stockée. Dans les deux lignes suivantes, lors de l'affectation d'une valeur à ErrorNumber, l'opérateur conditionnel ?: est utilisé. Il s'agit d'un analogue simplifié de la construction if-else.

ErrorNumber = (mGetError()>0)?mGetError():ErrorNumber;
ErrorNumber = (nErr>0)?nErr:ErrorNumber;

Dans la première ligne nous définissons : si une erreur a été corrigée, c'est-à-dire que mGetError() a renvoyé un résultat non nul, alors le code d'erreur obtenu (valeur renvoyée par la méthode mGetError()) sera affecté à la variable ErrorNumber ; sinon, la valeur de la variable ErrorNumber sera attribuée. Dans la deuxième ligne, la même vérification est effectuée, mais pour le paramètre de la méthode mGetError(). Si la valeur de nErr est non-nulle, elle est attribuée à la variable ErrorNumber.

Une fois que nous recevons le code d'erreur, commencez à rechercher des descriptions pour ce code. Si le code obtenu est supérieur à zéro et inférieur à ERR_USER_ERROR_FIRST, c'est-à-dire qu'il ne s'agit pas d'une erreur personnalisée, nous recherchons sa description en boucle. Et si le code obtenu est supérieur à ERR_USER_ERROR_FIRST, on prend une description du champ desc de la variable _UserError.

A la fin nous vérifions si la description a été trouvée. Si ce n'est pas le cas, renvoyez un message à propos d'un code d'erreur inconnu.

Méthodes de Signal

Les méthodes de signal incluent mAlert(), mPrint() et mSound(). en leur disposition, ces méthodes sont très similaires:

void ControlErrors::mAlert(string message="")
{
   if (_AlertInfo == true)
   {
      if (message == "")
      {
         if (mGetError() > 0)
         {
            Alert("Error №",mGetError()," - ",mGetDesc());
         }
      }
      else
      {
         Alert(message);
      }   
   }
}

void ControlErrors::mPrint(string message="")
{
   if (_PrintInfo == true)
   {
      if (message == "")
      {
         if (mGetError() > 0)
         {
            Print("Error №",mGetError()," - ",mGetDesc());
         }
      }
      else
      {
         Print(message);
      }
   }
}

void ControlErrors::mSound(void)
{
   if (_PlaySound == true)
   {
      PlaySound(_PlaySoundFile);
   }
}

en les trois méthodes, au début, l’indicateur autorisant les rapports et les signaux est coché. Ensuite, dans les méthodes mAlert() et mPrint(), le message de paramètre d'entrée est vérifié pour le message qui doit être affiché dans la boîte de dialogue Alerte ou ajouté au journal. Si un message est défini dans message et que le dernier code d'erreur est supérieur à zéro, il s'affiche. Sinon, un message standard s'affiche. La méthode mSound() ne dispose pas de paramètres, donc après avoir vérifié l’indicateur, la fonction PlaySound() est immédiatement appelée pour produire un son.

Vérification de la Méthode()

Cette méthode appelle simplement toutes les fonctions de cette classe dans le bon ordre, ainsi l'apparition d'une nouvelle erreur est vérifiée, tous les rapports autorisés sont émis, et immédiatement après cela, le code d'erreur avec sa description est supprimé. Ainsi, la méthode Check() effectue une vérification complète :

int ControlErrors::Check(string st="")
{
   int errNum = 0;
   errNum = mGetLastError();
   mFileWrite();
   mAlert(st);
   mPrint(st);
   mSound();
   mResetError();
   mResetLastError();
   mResetUserError();
   return errNum;
}

Check() dispose d’ un paramètre de type chaîne. Il s'agit d'un message personnalisé qui est transmis aux méthodes mAlert() et mPrint() pour être porté dans les rapports.

Méthodes de Rédaction de Messages dans un Fichier Journal

Cette méthode s'appelle mFileWrite(). Si la conservation d'un fichier journal est autorisé et le chemin d'accès au fichier est correctement indiqué - cette méthode effectue l'enregistrement dans le fichier indiqué.

int ControlErrors::mFileWrite(string message = "")
{
   int      handle  = 0,
            _return = 0;
   datetime time    = TimeCurrent();
   string   text    = (message != "")?message:time+" - Error №"+mGetError()+" "+mGetDesc();
   
   if (_WriteFile == true)
   {
      handle = FileOpen(_DataPath,FILE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI);
      if (handle != INVALID_HANDLE)
      {
         ulong size = FileSize(handle);
         FileSeek(handle,size,SEEK_SET);
         _return = FileWrite(handle,text);
         FileClose(handle);
      }
   }
   return _return;
}

Au début  quatre variables sont déclarées : handle - pour stocker le fichier de traitement ouvert, _return - pour stocker la valeur de retour, time, qui maintient l'heure actuelle (temps d'enregistrement dans le fichier) et text - le texte du message qui sera porté dans le fichier. La méthode mFileWrite() dispose d’ un paramètre d'entrée - message, en lequel l'utilisateur peut transmettre n'importe quelle chaîne qui doit être portée dans le fichier.

Cette caractéristique peut être utilisée pour enregistrer des valeurs d'indicateurs, des prix et d'autres données requises à certains moments.

Une fois les variables déclarées, l'indicateur _WriteFile est coché. Et si la conservation d'un fichier journal est autorisée, le fichier est ouvert pour une réécriture à l'aide de la fonction FileOpen(). Le premier paramètre de FileOpen() est la propriété DataPath, qui comporte le nom du fichier et son chemin. Le deuxième paramètre est unensemble d’indicateurs qui déterminent le mode de fonctionnement avec les indicateurs. en notre cas, quatre Indicateurs sont utilisés :

  • FILE_READ et FILE_WRITE dirigent ensemble pour ouvrir un fichier non vide avec la possibilité d'y ajouter des données.
  • FILE_TXT indique que le travail sera effectué avec un simple fichier texte.
  • FILE_ANSI indique que les données seront écrites dans le fichier sous forme de chaînes de type ANSI (symboles à un octet).

À l'étape suivante, nous vérifions si le fichier a été ouvert avec succès ou non. Sinon, le traitement disposera de la valeur INVALID_HANDLE et le mode opératoire se termine ici. Mais si cela réussit, nous obtenons la taille du fichier en utilisant FileSize(), puis en utilisant FileSeek() nous déplaçons la position du pointeur de fichier à la fin du fichier et ajoutons un message à la fin du fichier en utilisant la fonction FileWrite(). Après cela, fermez ce fichier à l'aide de la fonction FileClose().

Transmettez le fichier du traitement dont nous devons retourner la taille, en tant que paramètre d'entrée à la fonction FileSize(). C'est l’unique paramètre de cette fonction.

Trois paramètres doivent être indiqués dans le fonctionnement de FileSeek() :

  • Fichier du traitement avec lequel nous travaillons.
  • Décalage du pointeur de fichier.
  • Point de référence pour le décalage. Il prend l'une des valeurs ENUM_FILE_POSITION.

Au moins deux paramètres sont nécessaires pour le travail de la fonction FileWrite(). Il s'agit du descripteur d'un fichier, dans lequel nous devons écrire des données texte. La seconde est une ligne de texte qui doit être écrite et toutes les lignes de texte suivantes qui seront écrites dans le fichier. Le nombre de paramètres ne doit pas dépasser 63.

La fonction FileClose() a également besoin du descripteur de fichier pour la fermer.

Exemples

J'aimerais maintenant ajouter quelques exemples courants d'utilisation de la classe que nous avons écrite. Commençons par la création de l'objet et autorisons la conservation des rapports nécessaires.

Créons donc un objet de classe :

#include <ControlErrors.mqh>

ControlErrors mControl;

Avant de créer un objet, nous devons ajouter à l'Expert Advisor le fichier qui comporte la description de la classe. Cela se fait au tout début du programme via l’instruction #include. Et seulement après cela, l'objet est créé - cela ressemble à la création d'une nouvelle variable. Mais au lieu du type de données, le nom de la classe est inséré. 

Créons maintenant les rapports d'ettor que nous souhaitons recevoir. Cela se fait dans la fonction OnInit() : 

int OnInit()
{
//---
mControl.SetAlert(true);
mControl.SetPrint(true);
mControl.SetSound(false);
mControl.SetWriteFlag(true);
mControl.SetPlaySoundFile("news.wav");
//---
return(0);
}

Par défaut, lorsqu'un objet est créé, tous les indicateurs d'autorisation sont définis sur false, c'est-à-dire que tous les rapports sont désactivés. C'est pourquoi dans OnInit() il n'est pas nécessaire d'appeler des méthodes avec la valeur false, car cela est fait dans l'exemple ci-dessus (méthode SetSound()). Ces méthodes peuvent également être appelées dans d'autres parties du programme. Par exemple, si vous devez désactiver la conservation des rapports dans certaines conditions, vous pouvez programmer ces conditions et définir des indicateurs sur les valeurs nécessaires lorsque les conditions sont remplies.

Et une autre chose qui doit être mentionnée ici est l'appel de méthodes pendant l'exécution du programme et la "récupération" des erreurs. Cette partie n'est pas difficile, car vous pouvez utiliser ici la méthode unique Check(), en définissant tous les indicateurs avant cela :

mControl.Check();

Cette méthode, comme indiqué ci-dessus, identifiera le code de l'erreur survenue, appellera toutes les méthodes qui conservent les rapports, puis réinitialisera les valeurs de toutes les variables contenant des informations sur la dernière erreur. Si la méthode de traitement des erreurs proposée par Check() ne convient pas pour certaines raisons, vous pouvez générer vos propres rapports en utilisant les méthodes de classe disponibles.

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/70

Fichiers joints |
controlerrors.mqh (19.82 KB)
MetaTrader 5 Publication des prévisions de trading et des relevés de trading en direct par e-mail sur les blogs, les réseaux sociaux et les sites internet dédiés MetaTrader 5 Publication des prévisions de trading et des relevés de trading en direct par e-mail sur les blogs, les réseaux sociaux et les sites internet dédiés
Cet article vise à présenter des solutions prêtes à l'emploi pour la publication de prévisions à l'aide de MetaTrader 5. Il couvre un éventail d'idées : de l'utilisation de sites Web dédiés pour la publication des relevés MetaTrader à la création de son propre site Web sans pratiquement aucune expérience de programmation Web nécessaire et enfin à l'intégration à un service de micro-blogging de réseau social qui permet à de nombreux lecteurs de rejoindre et de suivre les prévisions. Toutes les solutions présentées ici sont 100% gratuites et peuvent être configurées par toute personne ayant une connaissance de base des services e-mail et ftp. Il n'y a aucun obstacle à utiliser les mêmes techniques pour les services d'hébergement professionnel et de prévision des échanges de trade.
Application Pratique des Bases de Données pour l'Analyse des Marchés Application Pratique des Bases de Données pour l'Analyse des Marchés
Travailler avec des données est devenu la tâche principale des logiciels modernes, à la fois pour les applications autonomes et en réseau. Pour résoudre ce problème, un logiciel spécialisé a été créé. Ce sont des Systèmes de Gestion de Bases de Données (SGBD), qui peuvent structurer, systématiser et organiser les données pour leur stockage et leur traitement informatique. Quant au trading, la plupart des analystes n'utilisent pas de bases de données dans leur travail. Mais il y a des tâches où une telle solution devrait être pratique. Cet article fournit un exemple d'indicateurs, qui peuvent enregistrer et charger des données à partir de bases de données à la fois avec des architectures client-serveur et serveur de fichiers.
Migration de MQL4 vers MQL5 Migration de MQL4 vers MQL5
Cet article est un guide rapide des fonctions du langage MQL4, il vous aidera à migrer vos programmes de MQL4 vers MQL5. Pour chaque fonction MQL4 (hors fonctions de trading) la description et l'implémentation MQL5 sont présentées, cela vous permet de réduire considérablement le temps de conversion. Pour plus de commodité, les fonctions MQL4 sont réparties en groupes, similaires à MQL4 Reference.
Transfert d'indicateurs de MQL4 vers MQL5 Transfert d'indicateurs de MQL4 vers MQL5
Cet article est dédié aux spécificités du transfert des constructions de prix rédigées en MQL4 vers MQL5. Pour rendre le processus de transfert des calculs d'indicateurs de MQL4 à MQL5 plus facile, la bibliothèque de fonctions mql4_2_mql5.mqh est suggérée. Son usage est décrit sur la base du transfert des indicateurs MACD, Stochastique et RSI.