//+------------------------------------------------------------------+ //| Main.mqh | //| Copyright 2025, Niquel Mendoza. | //| https://www.mql5.com/es/users/nique_372 | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Niquel Mendoza." #property link "https://www.mql5.com/es/users/nique_372" #property strict #ifndef AIDATATASKRUNNER_BACKEND_WORKFLOWS_MAIN_MQH #define AIDATATASKRUNNER_BACKEND_WORKFLOWS_MAIN_MQH //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #define WORKFLOW_CALLBACK_PADRE CLoggerBase #include "Flows\\AllFlows.mqh" #include "Protocol\\Def.mqh" //+------------------------------------------------------------------+ //| Definicion de la clase base de callback | //+------------------------------------------------------------------+ class CWfCallBack : public IWorkflowCallback { private: //--- long m_chart_id_p; CWorkflow* m_flow; string m_path_file_temp; bool m_common_flag; string m_prev_file_yml; uint m_start_t; //--- void OnWfInit(); void OnNewWf(const string& sparam); //--- void Kill(); public: CWfCallBack(void) {} ~CWfCallBack(void) {} //--- void Set(long v, CWorkflow* f, const string& fn, bool comon); //--- inline uint8_t FlagsCall() const override final { return WORKFLOWBYLEO_CALLBACK_ALL_FLAGS; } bool OnWorkflowStart() override final; void OnWorkflowEnd(int code, int step_index) override final; void OnWorkflowStep(int code, int step_index) override final; //--- void ChartEvent(const int32_t id, const long& lparam, const double& dparam, const string& sparam); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CWfCallBack::Set(long v, CWorkflow *f, const string& fn, bool comon) { m_chart_id_p = v; m_path_file_temp = fn; m_flow = f; m_common_flag = comon; //--- Variables de yml generales CDict* dict = m_flow.Env(); dict.Set("chart_id", v); dict.Set("common_flag", comon); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CWfCallBack::Kill() { // La idea con esto es quitar al asesor.. dado que no se pudo inciar ExpertRemove(); ::ChartClose(); // Kill se cierra a si mismo } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Solo se llama una vez void CWfCallBack::OnWfInit(void) { //--- aqui los datos que leeremos (copiado del panel) /* FileWrite(fh, AIDATATASKRUNER_PATH_TEMPLATES); // Path templates FileWrite(fh, AIDATATASKRUNER_PATH_TASK); // Path task FileWrite(fh, AIDATATASKRUNER_BASE_FOLDER); // Base folder */ //--- ::ResetLastError(); const int vc = (m_common_flag ? FILE_COMMON : 0); const int fh = FileOpen(m_path_file_temp, FILE_READ | FILE_TXT | vc); if(fh == INVALID_HANDLE) { if(!::EventChartCustom(m_chart_id_p, AIDATATASKRUNER_RUNNER_WF_EVENT_ERR_INIT, 0, 0, StringFormat("Fallo al abrir el archivo = %s, ultimo error: %d", m_path_file_temp, ::GetLastError()))) { LogError("No se pudo enviar evento de fallo al iniciar", FUNCION_ACTUAL); Kill(); return; } } //--- Variabels del yml CDict* variables = m_flow.Variables(); variables.SetStr("aidata.templates_folder", FileReadString(fh)); variables.SetStr("aidata.task_folder", FileReadString(fh)); variables.SetStr("aidata.base_folder", FileReadString(fh)); variables.SetStr("terminal.data_path", TERMINAL_MT5_DATA_PAH); variables.SetStr("terminal.common_path", TERMINAL_MT5_COMMON_PATH); variables.Set("aidata.common_flag", m_common_flag); const string v = m_common_flag ? "FILE_COMMON" : ""; variables.SetStr("aidata.common_flag_val", v); //--- FileClose(fh); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CWfCallBack::OnNewWf(const string& sparam) { //--- Iniclziacion solo si se cambio el nombre o el tamaño respecto al anterior if(m_prev_file_yml != sparam || FileGetInteger(sparam, FILE_SIZE, m_common_flag) != FileGetInteger(m_prev_file_yml, FILE_SIZE, m_common_flag)) { if(!m_flow.Init(sparam, m_common_flag)) { const string val = StringFormat("Fallo al iniciar el wf, con el archivo = %s, ultimo err del parser = %s, revise los logs", sparam, EnumToString(m_flow.YmlParser().LastErr())); if(!::EventChartCustom(m_chart_id_p, AIDATATASKRUNER_RUNNER_WF_ON_EVENT, AIDATATASKRUNER_RUNNER_WF_E_ON_END_CRITICAL, 0.00, val)) { LogError("No se pudo enviar el comando de fallo al iniciar el wf", FUNCION_ACTUAL); } return; } m_prev_file_yml = sparam; } //--- Iniciamos // Solo ejecutamso el send critical SI no se inicio dado a razones de incio del wf // En caso de que sea un err de wf callback onstart entonces eso ya lo tratamos en la otra funcion if(m_flow.First() == WORKFLOWBYLEO_ERR_FIRST_NO_INIT) { if(!::EventChartCustom(m_chart_id_p, AIDATATASKRUNER_RUNNER_WF_ON_EVENT, AIDATATASKRUNER_RUNNER_WF_E_ON_END_CRITICAL, 0.00, "Fallo al inciar el wf, revise los logs")) { LogError("No se pudo enviar el comando de fallo al evniar el primer comando en el wf", FUNCION_ACTUAL); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CWfCallBack::ChartEvent(const int id, const long & lparam, const double & dparam, const string & sparam) { //--- if(id == CHARTEVENT_CUSTOM + AIDATATASKRUNER_RUNNER_WF_EVENT_INIT) { // Evneto de inciilizacion general del runner (el panel le pasa datos como paths fijos etc) OnWfInit(); return; } if(id == CHARTEVENT_CUSTOM + AIDATATASKRUNER_RUNNER_WF_EVENT_ON_NEW_WF) { // Evento de nuevo wf, el panel le dice "oye hay un nuevo wf" sparam contiene el nombre del wf OnNewWf(sparam); } } //+------------------------------------------------------------------+ //| Inicio del flow | //+------------------------------------------------------------------+ bool CWfCallBack::OnWorkflowStart(void) { //--- Apertura del temporal ::ResetLastError(); const int fh = ::FileOpen(m_path_file_temp, FILE_WRITE | FILE_TXT | (m_common_flag ? FILE_COMMON : 0)); if(fh == INVALID_HANDLE) { if(!::EventChartCustom(m_chart_id_p, AIDATATASKRUNER_RUNNER_WF_ON_EVENT, AIDATATASKRUNER_RUNNER_WF_E_ON_END_CRITICAL, 0.00, ::StringFormat("Cannont open temp file = %s, last err = %d", m_path_file_temp, ::GetLastError()))) { LogError("No se pudo enviar el comando de fallo al abrir el archivo temporal", FUNCION_ACTUAL); } // Sleep(5000);//Esperamos a que lo reciba return false; } const int t = m_flow.StepCount(); //--- Escritura en el teporal FileWrite(fh, m_flow.Name()); // Nombre del flow FileWrite(fh, t); // Pasos for(int i = 0; i < t; i++) { //--- Escritura FileWrite(fh, (m_flow.GetStep(i).Name() + "\\" + m_flow.GetStep(i).Namespace())); // Todos los flows } FileClose(fh); // Cierre //--- Mandamos el finish ::ResetLastError(); if(!::EventChartCustom(m_chart_id_p, AIDATATASKRUNER_RUNNER_WF_ON_EVENT, AIDATATASKRUNER_RUNNER_WF_E_ON_START, 0.00, "")) // sparam vacio (internatem se debera de abrir el archivo y leer) { if(!::EventChartCustom(m_chart_id_p, AIDATATASKRUNER_RUNNER_WF_ON_EVENT, AIDATATASKRUNER_RUNNER_WF_E_ON_END_CRITICAL, 0.00, "Cannont send start command")) { LogError("No se pudo enviar el comando de fallo fatal al enviar comndo de inicio", FUNCION_ACTUAL); } // Sleep(5000);//Esperamos a que lo reciba return false; } //--- m_start_t = GetTickCount(); //--- return true; // Exito permitidmos continuar } //+------------------------------------------------------------------+ //| Paso del flow | //+------------------------------------------------------------------+ void CWfCallBack::OnWorkflowStep(int code, int step_index) { //--- // stepindex | code const uint diff = (::GetTickCount() - m_start_t); const string v = StringFormat("%d%c%d%c%s", step_index, AIDATATASKRUNER_RUNNER_WF_SEP, code, AIDATATASKRUNER_RUNNER_WF_SEP, (diff < 1000 ? string(diff) + " ms" : DoubleToString(diff / 1000.0, 3) + " s")); //--- ::ResetLastError(); if(!::EventChartCustom(m_chart_id_p, AIDATATASKRUNER_RUNNER_WF_ON_EVENT, AIDATATASKRUNER_RUNNER_WF_E_ON_STEP, 0.00, v)) { LogError(StringFormat("Fallo al enviar evento onstep, ultimo err = %d", ::GetLastError()), FUNCION_ACTUAL); } //--- m_start_t = GetTickCount(); } //+------------------------------------------------------------------+ //| Final del flow | //+------------------------------------------------------------------+ void CWfCallBack::OnWorkflowEnd(int code, int step_index) { //--- // stepindex | code const uint diff = (::GetTickCount() - m_start_t); const string v = StringFormat("%d%c%d%c%s", step_index, AIDATATASKRUNER_RUNNER_WF_SEP, code, AIDATATASKRUNER_RUNNER_WF_SEP, (diff < 1000 ? string(diff) + " ms" : DoubleToString(diff / 1000.0, 3) + " s")); //--- ::ResetLastError(); if(!::EventChartCustom(m_chart_id_p, AIDATATASKRUNER_RUNNER_WF_ON_EVENT, AIDATATASKRUNER_RUNNER_WF_E_ON_END, 0.00, v)) { LogError(StringFormat("Fallo al enviar evento onend, ultimo err = %d", ::GetLastError()), FUNCION_ACTUAL); } } //+------------------------------------------------------------------+ #endif // AIDATATASKRUNNER_BACKEND_WORKFLOWS_MAIN_MQH //+------------------------------------------------------------------+