//+------------------------------------------------------------------+ //| 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_MAIN_MQH #define AIDATATASKRUNNER_BACKEND_MAIN_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Saver.mqh" #include "..\\..\\UI\\Main.mqh" #include #include //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CExecutionTester : public ITaskRunerAi { private: //--- long m_chart_id_ea_2; // Chart id del ea 2 TaskTester m_current_task; // Tarea actual en configuracion TaskTester m_tasks[]; // Array de tareas int m_tasks_size; // Cantidad de tareas CTaskSaver m_saver; // Guardado y carga de tareas const long m_current_chart_id; ENUM_MTTESTER_MODELADO_MODE m_modelado_type; //--- string m_expert_path; // Ruta al experto que se desea ejeuctar (para ejecutarlo) string m_main_folder; // Folder base string m_files_move_json_base_file; int m_files_common_to_move_size; // Tamaaño de archivos a mover string m_files_common_to_move[]; // Archivos a mover string m_progres_task_file; // Archivo de progreso //--- string m_base_path; //--- int m_current_task_procces_idx; // temporales (se modican en ejecucion) string m_temp_arr_file_to_move[]; string m_temp_exp_path; // path temproal string m_temp_progres_csv_file; string m_temp_main_folder; //--- bool m_in_execution; // Ahora mismo se estan ejeuctando tareas? CTaskRunerAiTabDGenMain* m_sec_generation; CQueue m_cola_indices; //--- void SentEvent(); // Envia evento void RecibirEvento(const bool result, const string& msg); bool LoadJsonMoveFiles(const string& str); public: CExecutionTester(void); ~CExecutionTester(void) {} //--- void Set(CTaskRunerAiTabDGenMain *sec_gen, bool comon_in, bool comon_out, ENUM_MTTESTER_MODELADO_MODE modelado_type_); //--- void InitCurrentTask(const TaskTester& t) override final { m_current_task = t; } //--- Configuracion de la tarea actual bool OnCurrentTask_ModifyConfigMoveFile(const string str) override final; __forceinline string MoveFiles() const override final { return !m_in_execution ? m_files_move_json_base_file : m_temp_arr_file_to_move[0]; } void OnCurrentTask_ModifyConfigExpertPath(const string str) override final; __forceinline string ExperthPath() const override final { return !m_in_execution ? m_expert_path : m_temp_exp_path; } void OnCurrentTask_ModifyConfigProgresCsvFile(const string str) override final; __forceinline string ProgreesFile() const override final { return !m_in_execution ? m_progres_task_file : m_temp_progres_csv_file; } void OnCurrentTask_ModifyConfigMainFolder(const string str) override final; __forceinline string MainFolder() const override final { return !m_in_execution ? m_main_folder : m_temp_main_folder; } //--- Modificacion de la tarea actual void OnCurrentTask_ModifyDateStart(const datetime new_start) override final; void OnCurrentTask_ModifyDateEnd(const datetime new_end) override final; void OnCurrentTask_ModifySimbolo(const string new_symbol) override final; void OnCurrentTask_ModifySimboloFolder(const string new_symbol_f) override final; void OnCurrentTask_ModifyTimeframe(const ENUM_TIMEFRAMES new_tf) override final; void OnCurrentTask_ModifyLabel(const string label) override final; void OnCurrentTask_ModifyLabelId(const int label_id) override final; void OnCurrentTask_ModifySetFile(const string set_file) override final; //--- bool OnCurrentTask_Add(); int RunAllTask(int& out[]) override final; int CleanAllTask(int& out[]) override final; //--- Guardado \ Carga bool SaveAllTaskInFile(const string& file_name, bool only_no_proccesed) override final; bool SetTastByFile(const string& file_name) override final; //--- bool LoadConfig(const string& file_name) override final; bool SaveConfig(const string& file_name) override final; // El archivo debera de estar en comon // task: aqui se pondran todas las tareas // retonar el numero de tareas inline TaskTester GetTaskByIndex(const int index) const override final { return m_tasks[index]; } inline ENUM_AIEXECUTOR_TASK_STATE GetTaskStatus(const int index) const override final { return m_tasks[index].state; } inline TaskTester CurrentTask() const override final { return m_current_task; } inline int TaskSize() const override final { return m_tasks_size; } //--- bool AddDirectTask(const TaskTester& task) override final; //--- void ChartEvent(const int32_t id, const long& lparam, const double& dparam, const string& sparam); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CExecutionTester::CExecutionTester(void) : m_progres_task_file(NULL), m_temp_main_folder(NULL), m_current_chart_id(ChartID()), m_in_execution(false), m_expert_path(NULL), m_main_folder(NULL), m_current_task_procces_idx(-1), m_files_move_json_base_file(NULL) { m_files_common_to_move_size = ArrayResize(m_files_common_to_move, 0); ArrayResize(m_temp_arr_file_to_move, 0); AddLogger(m_saver); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CExecutionTester::Set(CTaskRunerAiTabDGenMain *sec_gen, bool comon_in, bool comon_out, ENUM_MTTESTER_MODELADO_MODE modelado_type_) { //--- m_sec_generation = sec_gen; m_file_common_flag_in = comon_in ? FILE_COMMON : 0; m_file_common_flag_out = comon_out ? FILE_COMMON : 0; m_base_path = comon_out ? TERMINAL_MT5_COMMON_PATH : TERMINAL_MT5_DATA_PAH; m_modelado_type = modelado_type_; //--- Abrimos el segundo grafico // Sin const long chart_id_ea = DEFMTTesterGetChartId(); if(::ChartSymbol(chart_id_ea) == "") { LogError(StringFormat("Chart id = %d, invalido", chart_id_ea), FUNCION_ACTUAL); Remover(); return; } m_chart_id_ea_2 = chart_id_ea; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CExecutionTester::OnCurrentTask_ModifyConfigMoveFile(const string str) { //--- if(!m_in_execution) { m_files_move_json_base_file = str; return LoadJsonMoveFiles(str); } else { // En training guardamos el archivo ArrayResize(m_temp_arr_file_to_move, 1); m_temp_arr_file_to_move[0] = str; return true; } } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifyConfigExpertPath(const string str) { if(m_in_execution) { m_temp_exp_path = str; } else { m_expert_path = str; } } //+------------------------------------------------------------------+ // NOTA: // EL ARCHIVO DE PROGRESO VA RELAITIVO AL MAIN_FOLDER // por jeemplo Progreess\\temp.csv void CExecutionTester::OnCurrentTask_ModifyConfigProgresCsvFile(const string str) { if(m_in_execution) { m_temp_progres_csv_file = m_temp_main_folder + str; } else { m_progres_task_file = m_main_folder + str; } } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifyConfigMainFolder(const string str) { if(m_in_execution) { m_temp_main_folder = str; } else { m_main_folder = str; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifyDateStart(const datetime new_start) { m_current_task.start_date = new_start; } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifyDateEnd(const datetime new_end) { m_current_task.end_date = new_end; } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifySimbolo(const string new_symbol) { m_current_task.symbol = new_symbol; } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifySimboloFolder(const string new_symbol_f) { m_current_task.symbol_folder = new_symbol_f; } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifyTimeframe(const ENUM_TIMEFRAMES new_tf) { m_current_task.timeframe = new_tf; } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifyLabel(const string label) { m_current_task.label = label; } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifyLabelId(const int label_id) { m_current_task.label_id = label_id; } //+------------------------------------------------------------------+ void CExecutionTester::OnCurrentTask_ModifySetFile(const string set_file) { // set file debe de tener la ruta completa (asi que la aumtentamos) // el user solo pone la relative a Pro m_current_task.set_file = TERMINAL_MT5_ROOT + "Profiles\\Tester\\" + set_file; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CExecutionTester::OnCurrentTask_Add(void) { //--- m_last_error_msg = ""; bool res = true; bool is_cts; //--- if(m_main_folder == NULL) { m_last_error_msg += "Main folder invalido|"; res = false; } if(m_expert_path.Length() < 4 || StringFindAtras(m_expert_path, ".ex5") == -1) { m_last_error_msg += "Path invalido o extension no es ex5|"; res = false; } //--- Como tal de la tarea if(!::SymbolExist(m_current_task.symbol, is_cts)) { m_last_error_msg += "Simbolo no existe " + m_current_task.symbol + "|"; res = false; } if(m_current_task.timeframe == WRONG_VALUE) { m_last_error_msg += "Timeframe no configurado|"; res = false; } if(m_current_task.start_date >= m_current_task.end_date) { m_last_error_msg += "Fecha inicio debe ser menor a fecha fin|"; res = false; } //--- if(!res) LogError(m_last_error_msg, FUNCION_ACTUAL); else { ::ArrayResize(m_tasks, m_tasks_size + 1); m_tasks[m_tasks_size] = m_current_task; m_tasks_size++; } //--- return res; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CExecutionTester::AddDirectTask(const TaskTester& task) override final { //--- m_last_error_msg = ""; bool res = true; bool is_cts; //--- if(m_main_folder == NULL) { m_last_error_msg += "Main folder invalido|"; res = false; } if(m_expert_path.Length() < 4 || StringFindAtras(m_expert_path, ".ex5") == -1) { m_last_error_msg += "Path invalido o extension no es ex5|"; res = false; } //--- Como tal de la tarea if(!::SymbolExist(task.symbol, is_cts)) { m_last_error_msg += "Simbolo no existe " + task.symbol + "|"; res = false; } if(task.timeframe == WRONG_VALUE) { m_last_error_msg += "Timeframe no configurado|"; res = false; } if(task.start_date >= task.end_date) { m_last_error_msg += "Fecha inicio debe ser menor a fecha fin|"; res = false; } //--- if(!res) LogError(m_last_error_msg, FUNCION_ACTUAL); else { ::ArrayResize(m_tasks, m_tasks_size + 1); m_tasks[m_tasks_size] = task; m_tasks_size++; m_sec_generation.AddTaskToTable(task); } //--- return res; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Aquilimpiamos todas las tareas que se puedan limpiar // Osea que esa tarea NO se este procesando o este en cola a ser ejecutada int CExecutionTester::CleanAllTask(int &out[]) { //--- m_last_error_msg = ""; //--- Sin tareas if(m_tasks_size <= 0) { m_last_error_msg = "No hay tareas para limpiar"; return 0; } //--- for(int i = 0; i < m_tasks_size; i++) { if(m_tasks[i].state == AIEXECUTOR_TASK_STATE_IN_PROCESS || m_tasks[i].state == AIEXECUTOR_TASK_STATE_IN_QUEQE) continue; // No out.Push(i); } m_tasks_size = RemoveMultipleIndexes(m_tasks, out, 0); return ArraySize(out); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CExecutionTester::RunAllTask(int &out[]) { //--- m_last_error_msg = ""; //--- Sin tareas if(m_tasks_size <= 0) { m_last_error_msg = "No hay tareas para ejecutar"; return 0; } //--- int t = 0; //--- for(int i = 0; i < m_tasks_size; i++) { if(m_tasks[i].state == AIEXECUTOR_TASK_STATE_PENDIENTE) { // Actuliazamos el estado m_tasks[i].state = AIEXECUTOR_TASK_STATE_IN_QUEQE; // Añadimos a la cola interna m_cola_indices.Enqueue(i); // Agregamos este elemento a la cola // Añadimos al array out out[::ArrayResize(out, ++t) - 1] = i; } } //--- Sin tareas pendientes if(t < 1) { m_last_error_msg = "No hay tareas pendientes para ejecutar"; } else { //--- LogInfo(StringFormat("Numero de tareas en cola a ejeuctar = %d", m_cola_indices.Count()), FUNCION_ACTUAL); m_in_execution = true; //--- Valor incial de los temp m_temp_main_folder = m_main_folder; m_temp_exp_path = m_expert_path; m_temp_progres_csv_file = m_progres_task_file; ArrayCopy(m_temp_arr_file_to_move, m_files_common_to_move); // Mandamos el primer evento SentEvent(); } //--- return t; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CExecutionTester::SentEvent() { if(m_cola_indices.Count() < 1) { LogCaution("Todas las tareas han acabado", FUNCION_ACTUAL); m_in_execution = false; // Termino //--- asing de los temp m_main_folder = m_temp_main_folder; m_expert_path = m_temp_exp_path; m_progres_task_file = m_temp_progres_csv_file; //--- m_current_task_procces_idx = -1; // reinit //--- // Puede no haber valores... de str if(ArraySize(m_temp_arr_file_to_move) == 1) { if(m_temp_arr_file_to_move[0] != m_files_common_to_move[0]) // No hay modifiacion asi que retornamos no tiene sentido seguir aqui return; //--- m_files_move_json_base_file = m_temp_arr_file_to_move[0]; // Asignamos el archivo LoadJsonMoveFiles(m_files_move_json_base_file); } return; } //--- const int idx = m_cola_indices.Dequeue(); //--- const string full_path = AIDATATASKRUNER_PATH_TASK + "Com\\data.csv"; if(!DEFMMTesterCreateFileWriteData(full_path, AIDATATASK_RUNNER_COMON_FLAG_VAL, m_tasks[idx].ToStringToMTTester(m_current_chart_id, m_expert_path, m_modelado_type))) { //--- Imprimimos error LogError(StringFormat("Fallo al ejecutar el indice en cola = %d", idx), FUNCION_ACTUAL); // Actulizamso el fronted le decimos que fallo la ejecuion del evento m_tasks[idx].state = AIEXECUTOR_TASK_STATE_FAILED; m_sec_generation.UpdateStateInTable(idx, m_tasks[idx].state); //--- Continuamos SentEvent(); //--- return; } ::EventChartCustom(m_chart_id_ea_2, DEFMTTESTER_E_ON_TASK, m_current_chart_id, DEFMTTESTER_TO_DBL_ON_TASK(AIDATATASK_RUNNER_COMON_FLAG_VAL), full_path); //--- m_tasks[idx].state = AIEXECUTOR_TASK_STATE_IN_PROCESS; m_sec_generation.UpdateStateInTable(idx, m_tasks[idx].state); m_current_task_procces_idx = idx; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CExecutionTester::RecibirEvento(const bool result, const string & msg) { //--- if(m_current_task_procces_idx < 0 || m_current_task_procces_idx >= m_tasks_size) { LogError("Índice de tarea inválido en RecibirEvento, posible llamada desconocida", FUNCION_ACTUAL); return; } //--- if(result) { //--- m_tasks[m_current_task_procces_idx].state = AIEXECUTOR_TASK_STATE_FINISHED; //--- string tf = ::EnumToString(m_tasks[m_current_task_procces_idx].timeframe); ::StringReplace(tf, "PERIOD_", ""); // main folder debe de tener \ const string final_ubicaction_folder = ::StringFormat("%s%s\\%s\\%s_%d\\", m_main_folder, m_tasks[m_current_task_procces_idx].symbol_folder, tf, m_tasks[m_current_task_procces_idx].label, m_tasks[m_current_task_procces_idx].label_id); //--- if(m_files_common_to_move_size > 0) { //--- // creamos el folder por si acaso quizas exista entonces supongo qeu no pasara nada si no existe no lo creamos vale? // por lo visto filemove pide ya la ruta hecha no la contruye asi que tenemos que crearla ::ResetLastError(); if(!::FolderCreate(final_ubicaction_folder, m_file_common_flag_out)) { LogWarning(StringFormat("Fallo al crear el folder = %s, ultimo error =%d", final_ubicaction_folder, ::GetLastError()), FUNCION_ACTUAL); } //--- // Aqui movemos... for(int i = 0; i < m_files_common_to_move_size; i++) { //--- Movemos ::ResetLastError(); const string file_out = final_ubicaction_folder + (m_files_common_to_move[i + m_files_common_to_move_size]); // archivo final el offset de size indica que leermos la version sin el path tla cual if(!::FileMove(m_files_common_to_move[i], m_file_common_flag_in, file_out, FILE_REWRITE | m_file_common_flag_out)) { LogError(::StringFormat("No se pudo mover el archivo =%s, a = %s, ultimo error = %d" , m_files_common_to_move[i], file_out, ::GetLastError()), FUNCION_ACTUAL); } } } //--- Copiamos el setfile if(::StringLen(m_tasks[m_current_task_procces_idx].set_file) > 4) // .set { const string out = m_base_path + "\\Files\\" + final_ubicaction_folder + GetPureFileName(m_tasks[m_current_task_procces_idx].set_file); // donde se pondra (le quitamos cualquier anidacion que halla) // Nota set_file tiene ruta completa if(!MTTESTER::FileCopy(m_tasks[m_current_task_procces_idx].set_file, out, true)) // copiamos no movemos { LogError(::StringFormat("Fallo al mover:\n%s\nA\n%s\nUltimo error en kernel32 = %d", m_tasks[m_current_task_procces_idx].set_file, out, kernel32::GetLastError()), FUNCION_ACTUAL); } } //--- Guardamos progreso // El progreso se guarda junto con los archivos out (data que pertenece al backend) // solo se guardan las tareas (proceseando|pendientes|encola) if(m_progres_task_file.Length() > 4) // .txt \ .csv { m_last_error_msg = ""; if(!m_saver.Save(m_progres_task_file, m_tasks, m_tasks_size, m_file_common_flag_out, true, m_last_error_msg)) // filtrasmos solo guramos lo necesario { LogError(StringFormat("Fallo al guardar progreso en el archivo csv, %s", m_last_error_msg), FUNCION_ACTUAL); } } } else { m_tasks[m_current_task_procces_idx].state = AIEXECUTOR_TASK_STATE_FAILED; LogError(StringFormat("Fallo al procesar la tarea #%d, msg:\n%s", m_current_task_procces_idx, msg), FUNCION_ACTUAL); } //--- m_sec_generation.UpdateStateInTable(m_current_task_procces_idx, m_tasks[m_current_task_procces_idx].state); // Actulizar el estado //--- SentEvent(); // Lanzamos el siguiente evento (intemante se verifica si es que hay tareas ono) } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CExecutionTester::ChartEvent(const int id, const long & lparam, const double & dparam, const string & sparam) { //--- de vuelta //lparam = combinacion de banderas en caso de error 0 exito total //sparam = se empaqueta como (chartid|log) //dparam = nada //--- // Si se finaliza if(id == CHARTEVENT_CUSTOM + DEFMTTESTER_E_FINISH_TASK) { //--- static string arr[2]; if(StringSplit(sparam, DEFMTTESTER_OUT_SPARAM_SEP_U, arr) != DEFMTTESTER_OUT_SPARAM_TOTAL) // formato invalido { return; } //--- const long chart_id = long(arr[DEFMTTESTER_OUT_SPARAM_IDX_CHARTID]); Print("Nuevo evento chart_id =", chart_id); if(chart_id != m_current_chart_id) return; LogInfo(StringFormat("Evento recibido desde = %I64d | Resultado = %I64d", chart_id, lparam), FUNCION_ACTUAL); RecibirEvento((lparam == DEFMTTESTER_TESTER_R_EXITO), arr[DEFMTTESTER_OUT_SPARAM_IDX_LOG]); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CExecutionTester::LoadJsonMoveFiles(const string& str) { ::ResetLastError(); const int fh = FileOpen(str, FILE_BIN | FILE_READ | m_file_common_flag_out); if(fh == INVALID_HANDLE) { m_last_error_msg = StringFormat("Fallo al leer el archivo = %s, ultimo error = %d", str, ::GetLastError()); LogFatalError(m_last_error_msg, FUNCION_ACTUAL); return false; } //--- const ulong size = ::FileSize(fh); string file_content = ::FileReadString(fh, int(size)); file_content.SetChar(0, ' '); // quitamos el boom ::FileClose(fh); //--- CJson json; if(json.Parse(file_content)) // omitimos el bom del incio del utf16le { CJsonNode files_to_move_arr = json["files_to_move"]; //--- if(!files_to_move_arr.IsValid()) { m_last_error_msg = "El key 'files_to_move' no existe en el json"; LogFatalError(m_last_error_msg, FUNCION_ACTUAL); return false; } if(!files_to_move_arr.IsArray()) { m_last_error_msg = "El key 'files_to_move' no es un array"; LogFatalError(m_last_error_msg, FUNCION_ACTUAL); return false; } //--- m_files_common_to_move_size = files_to_move_arr.Size(); ArrayResize(m_files_common_to_move, m_files_common_to_move_size << 1); for(int i = 0; i < m_files_common_to_move_size; i++) { m_files_common_to_move[i] = files_to_move_arr[i].ToString(); m_files_common_to_move[i + m_files_common_to_move_size] = GetPureFileName(m_files_common_to_move[i]); } } else { m_last_error_msg = StringFormat("Fallo al parsear el json de archivos a mover =%s, ultimo error de json parser = %s", str, EnumToString(EnumJsonError(json.GetLastError()))); LogError(m_last_error_msg, FUNCION_ACTUAL); return false; } //--- return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CExecutionTester::LoadConfig(const string &file_name) { m_last_error_msg = ""; if(!m_in_execution) { if(!m_saver.LoadC(file_name, AIDATATASK_RUNNER_COMON_FLAG, m_last_error_msg, m_progres_task_file, m_files_move_json_base_file, m_main_folder, m_expert_path)) return false; return LoadJsonMoveFiles(m_files_move_json_base_file); } else { // El valor de 0 lo usamos para cargar en el indicae 0, duranete la generacion de datos solo almcenemos ese valor en el array pos 0 return m_saver.LoadC(file_name, AIDATATASK_RUNNER_COMON_FLAG, m_last_error_msg, m_temp_progres_csv_file, m_temp_arr_file_to_move[0], m_temp_main_folder, m_temp_exp_path); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CExecutionTester::SaveConfig(const string &file_name) { m_last_error_msg = ""; if(!m_in_execution) { return m_saver.SaveC(file_name, AIDATATASK_RUNNER_COMON_FLAG, m_last_error_msg, m_progres_task_file, m_files_move_json_base_file, m_main_folder, m_expert_path); } else { return m_saver.SaveC(file_name, AIDATATASK_RUNNER_COMON_FLAG, m_last_error_msg, m_temp_progres_csv_file, m_temp_arr_file_to_move[0], m_temp_main_folder, m_temp_exp_path); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CExecutionTester::SaveAllTaskInFile(const string &file_name, bool only_no_proccesed) { // usamos el valor de (is common flag) del global config que tine epuesto el fronted.. para almcenar los archivos // only_no_proccesed = true ? entonces solo se guarda las que NO se procesaron al 100% (en cola|pendicente|procesando) : se gurada todo (inlcuido los no procesado) m_last_error_msg = ""; return m_saver.Save(file_name, m_tasks, m_tasks_size, AIDATATASK_RUNNER_COMON_FLAG, only_no_proccesed, m_last_error_msg); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CExecutionTester::SetTastByFile(const string &file_name) { //--- Check quizas fallo al cargar m_last_error_msg = ""; if(! m_saver.Load(file_name, m_tasks, m_tasks_size, AIDATATASK_RUNNER_COMON_FLAG, m_last_error_msg)) return false; //--- No se agrego if(m_tasks_size < 1) { m_last_error_msg = "0 task added"; return false; } return true; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ #endif // AIDATATASKRUNNER_BACKEND_MAIN_MQH