English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
OOP no MQL5 por exemplo: Processando os avisos e os códigos de erro

OOP no MQL5 por exemplo: Processando os avisos e os códigos de erro

MetaTrader 5Exemplos | 9 janeiro 2014, 14:14
1 606 0
KlimMalgin
KlimMalgin

Breve introdução ao OOP

Antes de começarmos a planejar, vamos nos familiarizar com algumas características do OOP que serão usadas neste artigo.

Certamente, usaremos estruturas e classes. Essas são as noções básicas das linguagens orientadas ao objeto. O que é a estrutura, o que é a classe e como elas se diferenciam?

A estrutura é uma construção que permite abranger um conjunto de variáveis e funções de tipos diferentes (exceto void).

A classe assim como a estrutura é uma série de campos de dados. Mas uma classe é uma construção mais complexa e "flexível". Classes são o conceito básico do OOP. Diferenças de classes e estruturas estão descritas na documentação. Vou repetir:

  • A palavra-chave classe é usada na declaração;
  • Por padrão, o especificador de acesso de todos os membros da classe é privado, salvo indicação em contrário. Os dados dos membros das estruturas são do tipo acesso público por padrão, salvo indicação em contrário;
  • Os objetos da classe sempre tem uma tabela de funções virtuais, mesmo que as funções virtuais não estejam definidas na classe. As estruturas não podem ter funções virtuais;
  • O operador novo pode ser aplicado aos objetos da classe, este operador não pode ser aplicado às estruturas;
  • As classes podem ser herdadas apenas de classes, as estruturas podem ser herdadas apenas de estruturas.

Agora vamos examinar as classes. Todos os campos da classe são divididos em dois tipos. Existem os membros de dados (variáveis, arranjos, etc.) e as funções definidas dentro de uma classe.

Os membros de dados são normalmente chamados de propriedades da classe, pois quando um objeto é criado a partir de uma classe, os membros de dados refletem exatamente as propriedades desse objeto. Por exemplo, se for uma forma geométrica - círculo, as propriedades irão conter seu raio, largura de linha, cor da forma, ou seja, as propriedades do objeto.

As funções definidas dentro de uma classe são chamadas de métodos. Eles são usados tanto para trabalhar com as classes das propriedades como para implementar quaisquer outros algoritmos, os quais são programados dentro deles.

A programação orientada ao objeto possui a ideia de encapsulamento. Essa é a possibilidade de ocultar os dados e a implementação de um objeto da influência direta de um usuário (programador de aplicação). Um programador obtém apenas a documentação que descreve o uso dos métodos que podem alterar as propriedades do objeto, embora alterar as propriedades do objeto diretamente seja impossível.

Tais medidas de proteção são necessárias nos casos em que um número de verificações é exigido antes de alterar o valor da propriedade. Todas as verificações exigidas são implementadas nos métodos, e caso elas sejam realizadas com sucesso, elas permitem a alteração do valor da propriedade. E nos casos em que um usuário tem acesso direto às propriedades, essas exigências não são efetuadas e, como resultado, o valor da propriedade pode ser configurado incorretamente, e o programa MQL não funcionará adequadamente.

Para qualquer uma das propriedades da classe e dos métodos, podemos configurar um nível de acesso utilizando três modificadores: privado, protegido e público.

Se o modificador privado é usado para um campo da classe, o acesso a este campo será possível apenas utilizando métodos da mesma classe, assim ele não pode ser alterado a partir do exterior. O modificador protegido também impõe algumas restrições no acesso ao campo a partir do exterior, mas ele torna possível o acesso aos campos da classe pelos métodos da mesma classe e pelos métodos das subclasses. Inversamente, o público remove todas as restrições de acesso e abre um acesso livre a todos os campos da classe.

Criando uma inclusão do arquivo mqh

A classe que escreveremos deve ser localizada em um arquivo mqh separado, de modo que ele possa ser incluído em nossos programas (conselheiros especialistas, roteiros, indicadores).

Para criar esse arquivo, vamos utilizar o assistente do MQL5. No menu File -> Create selecione Include file (*.mqh) e depois Next. Na janela que aparecer, insira o nome do arquivo (eu chamei de ControlErrors) e pressione Done (pronto). Um modelo de um arquivo mqh será aberto. Continuaremos trabalhando nesse arquivo.

Guia de introdução

Agora você sabe todos os fundamentos teóricos do OOP que podem ser usados no processo de estudo desse artigo. Então, vamos prosseguir.

Vamos examinar o código da classe com a definição de todas as suas propriedades e métodos:

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

Propriedades da classe

Primeiro, vem a definição das propriedades da classe, o modificador privado é aplicado a todas as propriedades, portanto, trabalhar com as propriedades diretamente de fora da classe é impossível. As propriedades são divididas em três classes por conveniência:

  1. Os sinalizadores que determinam quais os tipos de relatórios devem ser mantidos. Todas esses sinalizadores podem aceitar apenas dois valores: verdadeiro - significa que esse tipo de relatório (notificação) está habilitada e falso - significa que este tipo está desabilitado.
    • _PlaySound - variável que desabilita habilita o toque de uma melodia selecionada ou som quando ocorre um erro.
    • _PrintInfo - é responsável pela adição dos detalhes do erro no registro do Expert Advisor.
    • _AlertInfo - habilita ou desabilita a saída do Alerta com a informação do erro.
    • _WriteFile - habilita ou desabilita a gravação da informação do erro em um arquivo.
  2. A estrutura para armazenar os dados do erro e os elementos que utilizam essa estrutura.
    • Código - estrutura em si. Foi criado pela conveniência de armazenamento dos dados de erro em um arranjo.
    • Errors - um arranjo do tipo Código, ou seja, cada elemento do arranjo é uma estrutura do Código.
    • _UserError - uma variável do tipo Código. é processada por trabalhar com erros personalizados.
    • _Error - uma variável do tipo código. O último erro ocorrido é colocado nessa variável e o trabalho futuro com o erro é implementado por essa variável.
  3. Outras propriedades do serviço.
    • _CountErrors - a variável contém o número de erros cujos dados devem ser armazenados nos Erros do arranjo. é usada para especificar o tamanho do arranjo.
    • _PlaySoundFile - contém o nome do arquivo que será tocado para um som de alerta.
    • _DataPath - contém o caminho e o nome do erro do arquivo de registro ao qual os dados são escritos.

Eu acho, que está tudo claro com o primeiro grupo de propriedades: elas habilitam ou desabilitam o armazenamento de certos relatórios. No segundo grupo, a estrutura do Código é de interesse. O que é e por que é exatamente esta estrutura usada como os elemento do arranjo? É muito fácil! É muito mais conveniente armazenar todos os dados necessários em um único elemento do arranjo que configurar arranjos separados para um código de erro e sua descrição. Uma estrutura é utilizada para implementar tal possibilidade. Todos os campos necessários são definidos dentro da estrutura. Em nosso caso, são eles:

  • code - campo do tipo int que contém o código do erro;
  • desc - campo do tipo cadeia Ele contém a descrição do erro.

Atualmente, a estrutura é um tipo de dado composto, ou seja, pode ser usada para definir a variável e os arranjos que foram feitos. Como resultado, cada variável do tipo Código conterá campos dessa estrutura. Assim como cada elemento do arranjo do tipo código contém dois campos para armazenar o código e sua descrição. Portanto, uma forma muito conveniente para armazenar os dados de objetos de tipos diferentes em um único local é implementada no MQL5.

Depois vêm as variáveis _UserError e _Error. Ambas contém informação sobre o último erro ocorrido, porém a _UserError armazena informação sobre os erros personalizados, enquanto a _Error - sobre todos os erros.

E o terceiro grupo de propriedades. Aqui eu incluí todas as propriedades restantes as quais não podem ser incluídas no primeiro ou no segundo grupo. Elas são três. Primeiro, - _CountErrors, ela contém o número de erros cuja informação é armazenada no arranjo _Errors. Essa propriedade é utilizada para configurar o tamanho do arranjo _Errors no construtor e em alguns métodos para ativar os elementos do arranjo. A segunda propriedade é a _PlaySoundFile. Ela armazena o nome de um arquivo de som que é tocado quando o erro ocorre. A terceira propriedade é a _DataPath. Ela armazena o caminho e o nome do arquivo para manter os registros.

Métodos da classe. O Construtor

Em seguida vem a descrição do construtor e dos métodos da classe. Vamos começar examinando o construtor e tentar entender o que ele é. Como os métodos, esta é uma função comum definida dentro da classe, mas possui algumas características específicas.

  • Um construtor nomeia como mahces o nome da classe.
  • Um construtor não possui um valor de retorno (o tipo void é especificado).
  • Um construtor não possui parâmetros de entrada.

Normalmente os membros da classe são inicializados nos construtores. Por exemplo, em nosso construtor da classe, todos os sinalizadores para desabilitar o armazenamento de relatório são configurados, os nomes de um arquivo de som e de um arquivo de registro são especificados, o tamanho do arranjo _Errors é configurado e esse arranjo é preenchido com os dados. Abaixo publicarei apenas parte do código do construtor, pois ele é muito extenso e do mesmo tipo - a parte principal é ocupada pelo preenchimento do arranjo _Errors com os códigos e suas descrições. O código completo está anexo ao artigo.

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

}

Note, por favor, que a descrição da implementação é realizada fora da classe! Apenas os métodos são definidos dentro da classe! No entanto, isso não é necessário. Se preferir - você pode descrever o corpo de cada método na classe, mas na minha opinião, isso é inconveniente e difícil de entender.

Como eu disse, apenas os cabeçalhos das funções do método são definidas no corpo da classe, enquanto a descrição é realizada fora da classe. Quando descrever um método, você deve especificar a qual classe ele pertence. A operação que permite contexto :: é usada para isso. Como visto no código acima, primeiro o tipo de retorno de um método é especificado (para um construtor é o void), depois vem o nome da classe (nome do contexto ao qual o método pertence), e o nome da classe é seguido pelo contexto permitindo a operação e então vem o nome do método com seus parâmetros de entrada. Após tudo isso, a descrição do algoritmo para o método se inicia.

Primeiro, no construtor todos os sinalizadores são configurados e o som e os arquivos de registro são especificados:

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

Cada um desses métodos trabalha com uma certa propriedade da classe. Isso é feito intencionalmente para o caso de uma necessidade de filtrar os valores configurados por um usuário para as propriedades. Por exemplo, você pode configurar uma certa máscara para a qual o nome do arquivo do conjunto personalizado e o caminho devam corresponder. Se existe agora correspondência para a máscara, o usuário será informado sobre isso.

Como você deve ter mencionado, todos os sinalizadores pegam o valor falso. Ou seja, nenhum relatório será mantido por padrão quando uma amostra da classe é criada. Um usuário deve selecionar quais relatórios devem ser mantidos e ativá-los usando o mesmo "conjunto" de métodos na função OnInit(). Da mesma forma, você pode alterar o nome e o caminho para o arquivo de registro (o caminho é configurado relativo ao diretório 'MetaTrader 5\MQL5\Files\') e o arquivo de som (o caminho é configurado relativo ao diretório 'MetaTrader 5\Sounds\').

Depois que os sinalizadores estiverem configurados, iniciamos a variável _CountErrors, atribuindo o valor 150 à ela (a informação sobre os 149 arranjos serão armazenadas no arranjo) e então configuramos o tamanho do arranjo necessário usando a função ArrayResize(). Em seguida começamos a preencher o arranjo.

Os métodos de configuração do sinalizador

A descrição do construtor é seguida pela descrição dos métodos de configuração do sinalizador e da configuração do som e dos nomes do arquivo de registro:

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

Como visto do código, esta é uma atribuição simples passada ao método do parâmetro, a propriedade da classe. Os sinalizadores não requerem quaisquer verificações, pois podem pegar apenas dois valores. No entanto, os nomes dos arquivos e os caminhos precisam ser verificados antes da atribuição.

As ativações desses métodos, assim como de todos os outros aparecem a seguir:

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

A próxima é a descrição dos métodos de trabalho com os erros, e os primeiros são o mGetLastError() e o mResetLastError().

Os métodos mGetLastError() e mResetLastError()

O nome do método mGetLastError() fala por si mesmo. Ele duplica a função GetLastError(). Mas além da ativação da GetLastError(), uma descrição é procurada para o código do erro obtido no arranjo _Errors, e os detalhes do erro (o código e sua descrição) são salvos na variável ao invés de ativar a GetLastError() toda vez.

O código do método:

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

O método mResetLastError() duplica a função ResetLastError():

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

Os métodos para trabalhar com a última mensagem de erro

Estes são dois métodos: o mGetError() e o mResetError().

O método mGetError() retorna o código contido no _Error.code:

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

O método mResetError() restaura os conteúdos da variável _Error:

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

O método de determinação do tipo de erro, mGetTypeError()

O próximo método é o mGetTypeError(). Ele verifica se o último erro ocorrido é personalizado, ou predefinido (está contido no arranjo _Errors).

O código do método:

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

A constante ERR_USER_ERROR_FIRST tem o valor 65536. A partir desses códigos se iniciam os erros definidos personalizados. Então no corpo do método, o último código de erro recebido é verificado. Se o método retornar zero, este é um erro predefinido. Se um é retornado, é um erro personalizado.

Os métodos para trabalhar com erros personalizados

No MQL5, os usuários configuram seus próprios erros no curso do programa. Para que aos códigos personalizados pudessem ser atribuídas descrições apropriadas, a propriedade _UserError está disponível na classe. Dois métodos são utilizados para trabalhar com essa propriedade.

O método mSetUserError() é usado para configurar um código e descrever o erro personalizado:

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

Primeiro, a função SetUserError() configura a variável predefinida _LastError para o valor igual a ERR_USER_ERROR_FIRST + valor. E então o valor e sua apropriada descrição atribuída são salvos na variável _UserError.

O segundo método, o mResetUserError() restaura os campos da variável _UserError:

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

Esse método pode trabalhar apenas com a variável _UserError. Para restaurar o valor da variável do sistema _LastError, um outro método é usado: o mResetLastError(), ele está descrito acima.

O método para obter a descrição do código de erro

Existe também o método especial mGetDesc() na classe, o qual sendo ativado retornará a descrição do código de erro dos Errors do arranjo, ou do campo desc da variável _UserError, se o erro foi configurado por um usuário.

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

Esse método tem um parâmetro nErr. Ele é igual a zero por padrão. Se durante a ativação do método um valor for configurado ao parâmetro, a descrição será procurada pelo valor configurado. Se o parâmetro não for configurado, a descrição será procurada pelo último código de erro recebido.

No início, duas variáveis são definidas no método: a ErrorNumber - usando essa variável o código de erro será processado; e a ReturnDesc - a descrição obtida pelo ErrorNumber será armazenada nela. Nas próximas duas linhas, ao atribuir um valor à ErrorNumber, o operador condicional ?: será usado. Esse é um análogo simplificado da construção if-else.

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

Na primeira linha definimos: se um erro foi fixado, ou seja, o mGetError() retornou um resultado diferente de zero, então o código de erro obtido (valor retornado pelo método mGetError()) será atribuído à variável ErrorNumber; caso contrário o valor da variável ErrorNumber será atribuído. Na segunda linha a mesma verificação é realizada, mas pelo parâmetro do método mGetError(). Se o valor de nErr for diferente de zero, ele é atribuído à variável ErrorNumber.

Quando recebermos o código de erro, começamos a procurar as descrições para esse código. Se o código obtido for maior que zero e menor que ERR_USER_ERROR_FIRST, ou seja, um erro não personalizado, procuramos sua descrição em um ciclo. E se o código obtido for maior que ERR_USER_ERROR_FIRST, pegamos uma descrição do campo desc da variável _UserError.

Ao final verificamos se a descrição foi encontrada. Se não, retornamos uma mensagem sobre o código de erro desconhecido.

Os métodos de sinal

Os métodos de sinal incluem os mAlert(), mPrint() e mSound(). Em suas organizações, esses métodos são bem similares.

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

Em todos os três métodos, no início o sinalizador permitindo os relatórios e os sinais é verificado. Então, nos métodos mAlert() e mPrint(), a mensagem do parâmetro de entrada é verificada pela mensagem que deve ser exibida na caixa de diálogo de Alerta ou adicionada ao diário. Se uma mensagem for configurada na mensagem, e o último código de erro for maior que zero, então ela será exibida. Se não, então uma mensagem padrão será exibida. O método mSound() não tem parâmetros, então após verificar o sinalizador, imediatamente a função PlaySound() é ativada nele para produzir um som.

O método Check()

Este método simplesmente ativa todas as funções desta classe na sequência correta, assim a ocorrência de um novo erro é verificado, todos os relatórios permitidos são emitidos, e imediatamente após isso o código de erro com sua descrição é apagado. Assim o método Check() realiza uma verificação abrangente.

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

O Check() tem um parâmetro do tipo cadeia. Esta é uma mensagem personalizada que é passada aos métodos mAlert() e mPrint() para ser escrita nos relatórios.

Os métodos para escrever mensagens a um arquivo de registro

Este método é chamado mFileWrite(). Se manter um arquivo de registro é permitido e o caminho para o arquivo é especificado corretamente - esse método faz a gravação no arquivo especificado.

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

No início, quatro variáveis são definidas: identificador - para armazenar o identificador de um arquivo aberto, _retorno - para armazenar o valor de retorno, tempo, que mantém o tempo atual (tempo para gravar para o arquivo) e texto - a mensagem de texto que será escrita ao arquivo. O método mFileWrite() tem um parâmetro de entrada - mensagem, na qual o usuário pode passar qualquer cadeia que deva ser escrita ao arquivo.

Essa característica pode ser usada para gravar os valores do indicador, os preços e outros dados necessários em certos momentos.

Após as variáveis serem definidas, o flag _WriteFile é verificado. E se a manutenção de um arquivo de registro for permitida, o arquivo é aberto para re-escrita usando a função FileOpen(). O primeiro parâmetro da FileOpen() é a propriedade DataPath, a qual contém o nome do arquivo e o caminho para ele. O segundo parâmetro é um conjunto de sinalizadores que determina o modo de trabalho com os sinalizadores. Em nosso caso quatro sinalizadores são usados:

  • O FILE_READ e o FILE_WRITE juntos direto para abrir um arquivo não vazio com a possibilidade de adicionar dados a ele;
  • O FILE_TXT mostra que o trabalho será realizado com um arquivo de texto simples;
  • O FILE_ANSI indica que os dados serão escritos ao arquivo como cadeias do tipo ANSI (símbolos de byte único).

Na próxima etapa verificamos se o arquivo foi aberto com sucesso ou não. Se não, o identificador terá o valor INVALID_HANDLE e a operação do método termina aqui. Mas se foi bem sucedido, obtemos o tamanho do arquivo usando a FileSize(), depois usando a FileSeek() movemos a posição do ponteiro do arquivo para o final do arquivo e adicionamos uma mensagem ao final do arquivo usando a função FileWrite(). Após isso feche esse arquivo usando a função FileClose().

Passe o identificador do arquivo cujo tamanho precisamos para retornar, como um parâmetro de entrada para a função FileSize(). Esse é o único parâmetro dessa função.

Três parâmetros precisam ser especificados para a operação da FileSeek():

  • O identificador do arquivo com o qual trabalhamos;
  • A mudança do ponteiro do arquivo;
  • O ponto de referência para a mudança. Ele pega um dos valores da ENUM_FILE_POSITION.

Ao menos dois parâmetros são necessários para o trabalho da função FileWrite(). Esse é o identificador de um arquivo, para o qual precisamos escrever os dados do texto. O segundo é a linha do texto que precisa ser escrita e todas as próximas linhas de texto que serão escritas no arquivo. O número de parâmetros não deve exceder 63.

A função FileClose() também precisa do identificador do arquivo para fechá-lo.

Exemplos

Agora, eu gostaria de acrescentar alguns exemplos comuns do uso da classe que escrevemos. Vamos começar com a criação do objeto e permitir a manutenção do relatórios necessários.

Então, vamos criar um objeto da classe:

#include 

ControlErrors mControl;

Antes de criarmos um objeto, precisamos adicionar ao Conselheiro Especialista o arquivo que contém a descrição da classe. Isso é feito ao início do programa pela diretiva #include. E apenas depois disso que o objeto é criado - é parecido com a criação de uma nova variável. Mas ao invés do tipo de dados o nome da classe é inserido.

Agora vamos criar os relatórios ettor que queremos receber. Isso é feito na função OnInit():

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

Por padrão, quando um objeto é criado, todos os sinalizadores são configurados para falso, ou seja, todos os relatórios são desabilitados. Essa é a razão pela qual na OnInit() não é necessário ativar os métodos com valor falso, pois isso é feito no exemplo acima (método SetSound()). Esses métodos também podem ser ativados em outras partes do programa. Por exemplo, se você precisa desabilitar a manutenção de relatório sob certas condições, você pode programar essas condições e configurar os sinalizadores para os valores necessários quando as condições forem cumpridas.

E mais um ponto que precisa ser mencionado aqui é a ativação dos métodos durante o funcionamento do programa e a "captura" dos erros. Essa parte não é difícil, pois você pode usar aqui o único método Check(), configurando todos os sinalizadores antes disso:

mControl.Check();

Esse método, como mencionado acima, identificará o código do erro ocorrido, ativará os métodos que mantém os relatórios e então restaurará os valores de todas as variáveis que contenham informação sobre o último erro. Se o modo de processamento do erro pelo Check() não servir por algumas razões, você pode gerar seus próprios relatórios usando os métodos da classe disponíveis.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/70

Arquivos anexados |
controlerrors.mqh (19.82 KB)
Sistemas de negociação adaptativos e seu uso no terminal do cliente do MetaTrader 5 Sistemas de negociação adaptativos e seu uso no terminal do cliente do MetaTrader 5
Este artigo sugere uma variável de um sistema adaptativo que consiste em várias estratégias, cada uma realizando suas operações de negociação "virtuais". Negociações reais são realizadas de acordo com os sinais da estratégia mais rentável no momento. Obrigado por utilizar a abordagem orientada a objeto, classes para trabalhar com dados e classes de negociação da biblioteca padrão, a arquitetura do sistema pareceu ser simples e escalável; agora você pode facilmente criar e analisar os sistemas adaptativos que incluem centenas de estratégias de negociação.
Aplicação prática de bancos de dados para análise de mercado Aplicação prática de bancos de dados para análise de mercado
Trabalhar com dados se tornou a tarefa principal para o software moderno - tanto para aplicativos autônomos quanto para os de rede. Para solucionar esse problema um software especializado foi criado. São sistemas de gerenciamento de banco de dados (DBMS) que podem estruturar, sistematizar e organizar dados para seu armazenamento e processamento. Quanto aos negócios, a maioria dos analistas não utiliza bancos de dados em seu trabalho. Mas existem tarefas para as quais essa solução teria que ser útil. Este artigo fornece um exemplo de indicadores que podem salvar e carregar dados da partir de bancos de dados com ambas arquiteturas cliente-servidor ou arquivo-servidor.
Guia para testes e otimização de Expert Advisors no MQL5 Guia para testes e otimização de Expert Advisors no MQL5
Este artigo explica o processo passo-a-passo de identificação e solução de erros de código, assim como os passos para testar e otimizar os parâmetros de entrada do Expert Advisor. Você aprenderá como utilizar o Strategy Tester do terminal do cliente MetaTrader 5 para descobrir o melhor símbolo e definição dos parâmetros de entrada para seu Expert Advisor.
Transferindo indicadores do MQL4 para o MQL5 Transferindo indicadores do MQL4 para o MQL5
Este artigo é dedicado às peculiaridades da transferência de construções de preço escritas no MQL4 para MQL5. Para facilitar o processo de transferência de cálculos do indicador do MQL4 para MQL5, é sugerida a biblioteca de funções mql4_2_mql5.mqh. Sua utilização é descrita com base na transferência de indicadores RSI, estocásticos e MACD.