English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
OOP in MQL5 come Esempio: Elaborazione dei Codici di Avviso e di Errore

OOP in MQL5 come Esempio: Elaborazione dei Codici di Avviso e di Errore

MetaTrader 5Esempi | 16 dicembre 2021, 10:22
69 0
KlimMalgin
KlimMalgin

Breve introduzione all'OOP

Prima di iniziare a sviluppare, facciamo conoscenza con alcune funzionalità dell'OOP che verranno utilizzate in questo articolo. 

Ovviamente useremo strutture e classi. Queste sono le basi dei linguaggi orientati agli oggetti. Qual è la struttura, qual è la classe e in cosa differiscono?

Una struttura è una costruzione che permette di contenere un insieme di variabili e funzioni di diverso tipo (eccetto void).

Una classe così come la struttura è un insieme di campi dati. Ma una classe è una costruzione più complicata e "flessibile". Le classi sono il concetto base dell'OOP. Le differenze di classi e strutture sono descritte nella documentazione. Ripeterò:

  • La parola chiave class viene utilizzata nella dichiarazione;
  • Per impostazione predefinita, l'identificatore di accesso di tutti i membri della classe è privato, se non diversamente indicato. I membri dati delle strutture sono di tipo ad accesso pubblico per impostazione predefinita, se non diversamente indicato;
  • Gli oggetti classe hanno sempre una tabella di funzioni virtuali, anche se le funzioni virtuali non sono dichiarate nella classe. Le strutture non possono avere funzioni virtuali;
  • L'operatore new può essere applicato agli oggetti classe, questo operatore non può essere applicato alle strutture;
  • Le classi possono essere ereditate solo dalle classi; le strutture possono essere ereditate solo dalle strutture.

 

Consideriamo ora le classi. Tutti i campi di classe sono divisi in due tipi. Questi sono membri di dati (variabili, array, ecc.) e funzioni definite all'interno di una classe.

I membri datisono generalmente chiamati proprietàdi classe, perché quando un oggetto viene creato da una classe, i membri dati riflettono esattamente le proprietà di questo oggetto. Ad esempio, se si tratta di una forma geometrica - cerchio, le proprietà conterranno il raggio, lo spessore della linea, il colore della forma, ovvero le proprietà dell'oggetto.

Le funzionidefinite all'interno di una classe sono chiamate metodi. Sono utilizzati sia per lavorare con le proprietà della classe sia per implementare qualsiasi altro algoritmo, che è programmato in esse.

La programmazione orientata agli oggetti ha la nozione di incapsulamento. Questa è la possibilità di nascondere i dati e l'implementazione di un oggetto all'influenza diretta di un utente (programmatore di applicazioni). Un programmatore ottiene solo la documentazione che descrive l'utilizzo di quali metodi è possibile modificare le proprietà dell'oggetto, mentre è impossibile modificare direttamente le proprietà dell'oggetto.

Tali misure di protezione sono necessarie nei casi in cui sono necessarie una serie di controlli prima di modificare il valore di una proprietà. Tutti i controlli richiesti sono implementati nei metodi e, se eseguiti con successo, consentono di modificare il valore della proprietà. E nel caso in cui l'utente ha accesso diretto alle proprietà, questi controlli non vengono eseguiti e, di conseguenza, il valore della proprietà può essere impostato in modo errato e il programma MQL non funzionerà in modo appropriato.

Per qualsiasi proprietà e metodo della classe, possiamo impostare un livello di accesso utilizzando tre modificatori: privato, protettoe pubblico.

Se si utilizza il modificatore private per un campo di classe, l'accesso a questo campo è possibile solo utilizzando metodi della stessa classe, quindi non può essere modificato dall'esterno. Il modificatore protetto impone anche alcune sull’ accesso al campo dall'esterno, ma permette di accedere ai campi di classe per i metodi della stessa classe e per i metodi delle sottoclassi. Al contrario, public rimuove tutte le restrizioni di accesso e apre un libero accesso ai campi della classe.

Creare un file include mqh-file

La classe che andremo a scrivere dovrebbe trovarsi in un file mqh separato, in modo che possa essere inclusa nei nostri programmi (expert advisor, script, indicatori)

Per creare questo file, utilizziamo la procedura guidata MQL5. Nel menu File -> Crea seleziona Includi file (*.mqh)e vai su Next. Nella finestra visualizzata, inserisci il nome del file (l'ho chiamato ControlErrors) e premi Done. Verrà aperto un modello di un file mqh. Continueremo a lavorare su questo file.

Iniziare

Ora conosci tutti i fondamenti teorici dell'OOP che possono essere utili nel processo di studio di questo articolo. Quindi, andiamo avanti.

Consideriamo il codice della classe con la dichiarazione di tutte le sue proprietà e metodi:

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
};

Proprietà della Classe

Prima viene la dichiarazione delle proprietà della classe, il modificatore private viene applicato a tutte le proprietà, quindi è impossibile lavorare con proprietà direttamente al di fuori della classe. Le proprietà sono suddivise in tre classi per comodità:

  1. Flag che determinano quali tipi di report devono essere conservati. Tutti questi flag possono accettare solo due valori: true - significa che questo tipo di report (notifiche) è abilitato e false - significa che questo tipo è disabilitato.
    • _PlaySound - variabile che disabilita o abilita la riproduzione di una melodia o di un suono selezionati quando si verifica un errore.
    • _PrintInfo - è responsabile dell'aggiunta di dettagli sull'errore nel diario di Expert Advisor.
    • _AlertInfo - abilita o disabilita l'output di Alert con informazioni sull'errore.
    • _WriteFile - abilita o disabilita la registrazione delle informazioni di errore in un file.
  2. Struttura per la memorizzazione dei dati di errore e degli elementi che utilizzano questa struttura.
    • Codice - struttura di per sé. È stato creato per la comodità dell'archiviazione dei dati di errore in un array.
    • Errori: un array di tipo Code, ovvero ogni elemento dell'array è una struttura Code.
    • _UserError - una variabile di tipo Code. È citato in giudizio per aver lavorato con errori personalizzati.
    • _Error - una variabile di tipo Code. L'ultimo errore verificatosi viene inserito in questa variabile e l'ulteriore lavoro con l'errore viene implementato tramite questa variabile.
  3. Altre proprietà del servizio.
    • _CountErrors - la variabile contiene il numero di errori, i cui dati devono essere memorizzati nell'array Errors. Viene utilizzato per specificare la dimensione dell'array.
    • _PlaySoundFile - contiene il nome del file che verrà riprodotto per un suono di avviso.
    • _DataPath: contiene il percorso e il nome del file di registro in cui vengono scritti i dati di errore.

Penso che sia tutto chiaro con il primo gruppo di proprietà: abilitano o disabilitano la conservazione di determinati report. Nel secondo gruppo interessa la struttura del Codice. Che cos'è e perché viene utilizzata esattamente questa struttura come elementi dell'array? È tutto facile! È molto più conveniente memorizzare tutti i dati richiesti in un singolo elemento dell'array, piuttosto che impostare array separati per un codice di errore e la sua descrizione. Una struttura viene utilizzata per implementare tale possibilità. Tutti i campi obbligatori sono dichiarati all'interno di una struttura. Nel nostro caso sono:

  • code - campo di tipo int che contiene il codice di errore;
  • desc - campo di tipo stringa. Contiene la descrizione dell'errore.

In realtà, la struttura è un tipo di daticomposto, cioè può essere usata per dichiarare variabili e array, cosa che è stata fatta. Di conseguenza, ogni variabile di tipo Code conterrà campi di questa struttura. Così come ogni elemento dell'array di tipo Code contiene due campi per memorizzare il codice e la sua descrizione. Pertanto, in MQL5 è implementato un modo abbastanza conveniente per archiviare dati oggetto di diversi tipi in un'unica posizione.

Poi vengono le variabili _UserError e _Error. Entrambi contengono informazioni sull'ultimo errore verificatosi, sebbene _UserError memorizzi informazioni sugli errori personalizzati, mentre _Error - su tutti gli errori.

E il terzo gruppo di proprietà. Qui ho incluso tutte le proprietà rimanenti che non possono essere incluse nel primo o nel secondo gruppo. Sono tre. Primo: _CountErrors, contiene il numero di errori le cui informazioni sono memorizzate nell'array _Errors. Questa proprietà viene utilizzata per impostare la dimensione dell'array _Errors nel costruttore e in alcuni metodi per chiamare gli elementi dell'array. La seconda proprietà è _PlaySoundFile. Memorizza il nome di un file audio che viene riprodotto quando si verifica un errore. La terza proprietà è _DataPath. Memorizza il percorso e il nome del file per la conservazione dei log.

Metodi delle Classe. Costruttore

La prossima è la descrizione del costruttore e dei metodi della classe. Iniziamo a considerare il costruttore e cerchiamo di capire di cosa si tratta. Come i metodi, questa è una funzione comune definita all'interno di una classe, ma che possiede alcune caratteristiche specifiche:

  • Un nome di costruttore mahces il nome della classe.
  • Un costruttore non ha un valore restituito (è specificato il tipo void).
  • Un costruttore non ha parametri di input.

Di solito i membri della classe vengono inizializzati nei costruttori. Ad esempio, nel nostro costruttore di classi, vengono impostati tutti i flag per disabilitare la conservazione dei report, vengono specificati i nomi di un file audio e un file di registro, viene impostata la dimensione dell'array _Errors e questo array viene riempito con i dati. Di seguito pubblicherò solo una parte del codice del costruttore, perché è troppo grande e dello stesso tipo: la parte principale è occupata dalla compilazione dell'array _Errors con i codici e le loro descrizioni. Il codice completo è allegato all'articolo.

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";

}

Si prega di notare che la descrizione dell'implementazione viene eseguita al di fuori della classe! Solo i metodi sono dichiarati all'interno della classe! Anche se questo non è richiesto. Se vuoi, puoi descrivere il corpo di ogni metodo nella classe, ma a mio parere, è scomodo e difficile da capire.

Come ho detto, nel corpo della classe vengono dichiarate solo le intestazioni delle funzioni del metodo, mentre la descrizione viene eseguita all'esterno della classe. Quando si descrive un metodo, è necessario specificare a quale classe appartiene. Viene utilizzato il contesto che consente l'operazione ::. Come visto dal codice sopra, prima viene specificato il tipo restituito di un metodo (per un costruttore è void), quindi viene il nome della classe (nome del contesto a cui appartiene il metodo) e il nome della classe è seguito dal contesto permettendo l'operazione e poi arriva il nome del metodo con i suoi parametri di input. Dopo tutto questo, inizia la descrizione dell'algoritmo per il metodo.

Per prima cosa nel costruttore vengono impostati tutti i flag e vengono specificati i file audio e di registro: 

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

Ciascuno di questi metodi funziona con una determinata proprietà di classe. Questo viene fatto intenzionalmente nel caso in cui sia necessario filtrare i valori impostati da un utente per le proprietà. Ad esempio, è possibile impostare una determinata maschera, alla quale dovrebbe corrispondere un nome file e un percorso personalizzati. Se ora c'è corrispondenza da mascherare, l'utente ne sarà informato. 

Come avrai già detto, tutti i flag assumono il valore falso. Ad esempio, nessun report verrà mantenuto per impostazione predefinita, quando viene creato un campione di classe. Un utente dovrebbe selezionare quali report devono essere conservati e attivarli utilizzando gli stessi metodi "Set" nella funzione OnInit(). Allo stesso modo, puoi cambiare il nome e il percorso del file di log (il percorso è impostato rispetto alla directory 'MetaTrader 5\MQL5\Files\') e il file audio (il percorso è impostato rispetto alla directory 'MetaTrader 5\Sounds\').

Dopo che i flag sono stati impostati, inizializziamo la variabile _CountErrors, assegnandole il valore 150 (le informazioni su 149 array verranno memorizzate nell'array) e quindi impostiamo la dimensione dell'array richiesta utilizzando la funzione ArrayResize(). Dopodiché, iniziamo a riempire l'array.

Metodi di Impostazione del Flag

La descrizione del costruttore è seguita dalla descrizione dei metodi di impostazione dei flag e dell'impostazione dei nomi dei file audio e di log:

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;
}

Come si vede dal codice, si tratta di una semplice assegnazione passata al metodo del parametro, proprietà della classe. I flag non richiedono alcun controllo, perché possono assumere solo due valori. Tuttavia, i nomi e i percorsi dei file devono essere controllati prima dell'assegnazione.

Le chiamate di questi metodi, così come di tutti gli altri, hanno il seguente aspetto: 

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

La prossima è la descrizione dei metodi per lavorare con gli errori, e i primi di questi sono mGetLastError() e mResetLastError().

Metodi mGetLastError() e mResetLastError() 

Il nome del metodo mGetLastError() parla da solo. Duplica la funzione GetLastError(). Ma oltre alla chiamata di GetLastError(), viene cercata una descrizione per il codice di errore ottenuto nell'array _Errors e i dettagli dell'errore (codice e la sua descrizione) vengono salvati nella variabile _Error, in modo che oltre a utilizzare il valore salvato invece di chiamare GetLastError () ogni volta.

Il codice del metodo:

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

Il metodo mResetLastError() duplica la funzione ResetLastError():

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

Metodi per Lavorare con l'Ultimo Messaggio di Errore

Questi sono due metodi: mGetError() e mResetError().

Il metodo mGetError() restituisce il codice contenuto in _Error.code:

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

Il metodo mResetError() ripristina il contenuto della variabile _Error:

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

Metodo di determinazione del tipo di errore mGetTypeError()

Il metodo successivo è mGetTypeError(). Controlla se l'ultimo errore verificatosi è personalizzato o è predefinito (è contenuto nell'array _Errors).

Il codice del metodo:

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

La costante ERR_USER_ERROR_FIRST ha il valore 65536. Da questi codici iniziano gli errori personalizzati. Quindi, nel corpo del metodo, viene controllato l'ultimo codice di errore ricevuto. Se il metodo restituisce zero, si tratta di un errore predefinito. Se ne viene restituito uno, si tratta di un errore personalizzato.

Metodi per Lavorare con Errori Personalizzati

In MQL5, gli utenti possono impostare i propri errori nel corso del programma. Affinché i codici personalizzati possano essere assegnati a descrizioni appropriate, la proprietà _UserError è disponibile nella classe. Per lavorare con questa proprietà vengono utilizzati due metodi.

Il metodo mSetUserError() viene utilizzato per impostare un codice e descrivere l'errore personalizzato:

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

Innanzitutto, la funzione SetUserError() imposta la variabile predefinita _LastError al valore uguale a ERR_USER_ERROR_FIRST + value. E quindi il valore e la sua descrizione assegnata appropriata vengono salvati nella variabile _UserError.

Il secondo metodo mResetUserError() ripristina i campi della variabile _UserError:

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

Questo metodo può funzionare solo con la variabile _UserError. Per ripristinare il valore della variabile di sistema _LastError, viene utilizzato un altro metodo: mResetLastError(), descritto sopra.

Metodo per Ottenere la Descrizione del Codice di Errore

C'è anche un metodo speciale mGetDesc() nella classe, che viene chiamato restituirà la descrizione del codice di errore dall'array Errors, o dal campo desc della variabile _UserError, se l'errore è stato impostato da un utente:

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;
}

Questo metodo ha un parametro nErr. È uguale a zero per impostazione predefinita. Se durante la chiamata al metodo viene impostato un valore al parametro, la descrizione verrà cercata per il valore impostato. Se il parametro non è impostato, nella descrizione verrà cercato l'ultimo codice di errore ricevuto.

All'inizio, nel metodo vengono dichiarate due variabili: ErrorNumber - utilizzando questa variabile verrà elaborato il codice di errore; e ReturnDesc - la descrizione ottenuta per ErrorNumber verrà memorizzata in essa. Nelle due righe successive, quando si assegna un valore a ErrorNumber, viene utilizzato l'operatore condizionale ?:. Questo è un analogo semplificato della costruzione if-else.

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

Nella prima riga definiamo: se un errore è stato corretto, cioè mGetError() ha restituito un risultato diverso da zero, allora il codice di errore ottenuto (valore restituito dal metodo mGetError()) sarà assegnato alla variabile ErrorNumber; in caso contrario verrà assegnato il valore della variabile ErrorNumber. Nella seconda riga viene eseguito lo stesso controllo, ma per il parametro del metodo mGetError(). Se il valore di nErr è diverso da zero, viene assegnato alla variabile ErrorNumber.

Una volta ricevuto il codice di errore, inizia a cercare le descrizioni per questo codice. Se il codice ottenuto è maggiore di zero e minore di ERR_USER_ERROR_FIRST, cioè non è un errore personalizzato, cerchiamo la sua descrizione in un ciclo. E se il codice ottenuto è maggiore di ERR_USER_ERROR_FIRST, prendiamo una descrizione dal campo desc della variabile _UserError.

Alla fine controlliamo se la descrizione è stata trovata. In caso contrario, restituire un messaggio su un codice di errore sconosciuto.

Metodi di Segnale

I metodi di segnale includono mAlert(), mPrint() e mSound(). Nella loro disposizione, questi metodi sono molto simili:

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);
   }
}

In tutti e tre i metodi, all'inizio viene controllato il flag che consente i report e i segnali. Quindi, nei metodi mAlert() e mPrint(), il messaggio del parametro di input viene controllato per il messaggio che dovrebbe essere visualizzato nella finestra di avviso o aggiunto al journal. Se un messaggio è impostato in messaggio e l'ultimo codice di errore è maggiore di zero, viene visualizzato. In caso contrario, viene visualizzato un messaggio standard. Il metodo mSound() non ha dei parametri, quindi dopo aver controllato il flag, immediatamente viene chiamata la funzione PlaySound() per produrre un suono.

Controllo del Metodo()

Questo metodo chiama semplicemente tutte le funzioni di questa classe nella sequenza corretta, quindi viene verificato il verificarsi di un nuovo errore, vengono emessi tutti i report consentiti e subito dopo viene cancellato il codice di errore con la sua descrizione. Pertanto il metodo Check() esegue un controllo completo:

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

Check() ha un parametro di tipo stringa. Questo è un messaggio personalizzato che viene passato ai metodi mAlert() e mPrint() per essere scritto nei report.

Metodi per Scrivere Messaggi in un File di Log

Questo metodo si chiama mFileWrite(). Se mantenere un file di log è consentito e il percorso del file è specificato correttamente - questo metodo effettua il record nel file specificato.

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;
}

All'inizio  vengono dichiarate quattro variabili: handle - per memorizzare l'handle di un file aperto, _return - per memorizzare il valore di ritorno, ora, che mantiene l'ora corrente (tempo per registrare nel file) e testo - il testo del messaggio che sarà scritto nel file. Il metodo mFileWrite() ha un parametro di input - messaggio, in cui l'utente può passare qualsiasi stringa che dovrebbe essere scritta nel file.

Questa funzione può essere utilizzata per registrare i valori degli indicatori, i prezzi e altri dati richiesti in determinati momenti.

Dopo che le variabili sono state dichiarate, viene controllato il flag _WriteFile. E se è consentita la conservazione di un file di registro, il file viene aperto per la riscrittura utilizzando la funzione FileOpen(). Il primo parametro di FileOpen() è la proprietà DataPath, che contiene il nome del file e il relativo percorso. Il secondo parametro è un insieme di flag che determinano la modalità di lavoro con i flag. Nel nostro caso vengono utilizzati quattro flag:

  • FILE_READ e FILE_WRITE insieme indirizzano ad aprire un file non vuoto con la possibilità di aggiungere dati ad esso.
  • FILE_TXT mostra che il lavoro verrà eseguito con un semplice file di testo.
  • FILE_ANSI indica che i dati verranno scritti nel file come stringhe di tipo ANSI (simboli a byte singolo).

Nel passaggio successivo controlliamo se il file è stato aperto correttamente o meno. In caso contrario, l'handle avrà il valore INVALID_HANDLE e l'operazione del metodo termina qui. Ma se ha successo, otteniamo la dimensione del file usando FileSize(), quindi utilizzando FileSeek() spostiamo la posizione del puntatore del file alla fine del file e aggiungiamo un messaggio alla fine del file usando la funzione FileWrite(). Dopodiché chiudi questo file usando la funzione FileClose().

Passa l'handle del file di cui dobbiamo restituire la dimensione, come parametro di input alla funzione FileSize(). Questo è l'unico parametro di questa funzione.

È necessario specificare tre parametri per il funzionamento di FileSeek():

  • Handle del file con cui lavoriamo.
  • Spostamento del puntatore del file.
  • Punto di riferimento per il turno. Prende uno dei valori di ENUM_FILE_POSITION.

Sono richiesti almeno due parametri per il lavoro della funzione FileWrite(). Questo è l'handle di un file, in cui dobbiamo scrivere i dati di testo. La seconda è una riga di testo che deve essere scritta e tutte le righe di testo successive che verranno scritte nel file. Il numero di parametri non può superare 63.

Anche la funzione FileClose() necessita dell'handle del file per chiuderla.

Esempi

Ora vorrei aggiungere alcuni esempi comuni sull'uso della classe che abbiamo scritto. Iniziamo con la creazione dell'oggetto e consentiamo di conservare i report necessari.

Quindi, creiamo un oggetto classe:

#include <ControlErrors.mqh>

ControlErrors mControl;

Prima di creare un oggetto, dobbiamo aggiungere all'Expert Advisor il file che contiene la descrizione della classe. Questo viene fatto all'inizio del programma tramite la direttiva #include. E solo dopo che l'oggetto è stato creato, sembra uguale alla creazione di una nuova variabile. Ma invece del tipo di dati viene inserito il nome della classe. 

Ora creiamo i report ettor che vogliamo ricevere. Questo viene fatto nella funzione OnInit(): 

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

Per impostazione predefinita, quando viene creato un oggetto, tutti i flag di autorizzazione sono impostati su false, ovvero tutti i report sono disabilitati. Ecco perché in OnInit() non è necessario chiamare i metodi con il valore false, perché è fatto nell'esempio precedente (metodo SetSound()). Questi metodi possono essere richiamati anche in altre parti del programma. Ad esempio, se è necessario disabilitare la conservazione dei report in determinate condizioni, è possibile programmare queste condizioni e impostare i flag sui valori richiesti quando le condizioni sono soddisfatte.

E un'altra cosa che deve essere menzionata qui è la chiamata di metodi durante l'esecuzione del programma e la "cattura" degli errori. Questa parte non è difficile, perché puoi usare qui il singolo metodo Check(), impostando tutti i flag prima di quello:

mControl.Check();

Questo metodo, come affermato sopra, identificherà il codice dell'errore verificatosi, chiamerà tutti i metodi che conservano i report e quindi azzererà i valori di tutte le variabili che contengono informazioni sull'ultimo errore. Se il metodo di elaborazione degli errori offerto da Check() non è adatto per qualche motivo, è possibile generare i propri report utilizzando i metodi di classe disponibili.

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/70

File allegati |
controlerrors.mqh (19.82 KB)
MetaTrader 5: Pubblicazione di previsioni di trading e dichiarazioni di trading in tempo reale via e-mail su blog, social network e siti web dedicati MetaTrader 5: Pubblicazione di previsioni di trading e dichiarazioni di trading in tempo reale via e-mail su blog, social network e siti web dedicati
Questo articolo mira a presentare soluzioni pronte per la pubblicazione di previsioni utilizzando MetaTrader 5. Copre una vasta gamma di idee: dall'utilizzo di siti web dedicati per la pubblicazione di dichiarazioni MetaTrader, attraverso la creazione del proprio sito Web senza praticamente alcuna esperienza di programmazione web necessaria e infine l'integrazione con un servizio di microblogging di social network che consente a molti lettori di aderire e seguire le previsioni. Tutte le soluzioni presentate qui sono gratuite al 100% e possono essere configurate da chiunque abbia una conoscenza di base dei servizi di posta elettronica e ftp. Non ci sono ostacoli all'utilizzo delle stesse tecniche per l'hosting professionale e per i servizi di previsione del trading commerciale.
Applicazione Pratica dei Database per l'Analisi dei Mercati Applicazione Pratica dei Database per l'Analisi dei Mercati
Lavorare con i dati è diventato il compito principale del software moderno, sia per applicazioni standalone che di rete. Per risolvere questo problema è stato creato un software specializzato. Si tratta di Database Management Systems (DBMS), in grado di strutturare, sistematizzare e organizzare i dati per l'archiviazione e l'elaborazione del proprio computer. Per quanto riguarda il trading, la maggior parte degli analisti non utilizza database nel proprio lavoro. Ma ci sono compiti in cui una soluzione del genere dovrebbe essere utile. Questo articolo fornisce un esempio di indicatori, in grado di salvare e caricare dati da database sia con architetture client-server che file-server.
Migrazione da MQL4 a MQL5 Migrazione da MQL4 a MQL5
Questo articolo è una guida rapida alle funzioni del linguaggio MQL4, ti aiuterà a migrare i tuoi programmi da MQL4 a MQL5. Per ogni funzione MQL4 (ad eccezione delle funzioni di trading) vengono presentate la descrizione e l'implementazione MQL5, che consente di ridurre significativamente il tempo di conversione. Per comodità, le funzioni MQL4 sono suddivise in gruppi, in modo simile a MQL4 Reference.
Trasferimento di Indicatori da MQL4 a MQL5 Trasferimento di Indicatori da MQL4 a MQL5
Questo articolo è dedicato alle peculiarità del trasferimento delle costruzioni di prezzo scritte in MQL4 a MQL5. Per facilitare il processo di trasferimento dei calcoli degli indicatori da MQL4 a MQL5, si suggerisce la libreria di funzioni mql4_2_mql5.mqh. Il suo utilizzo è descritto sulla base del trasferimento degli indicatori MACD, Stocastico e RSI.