//+------------------------------------------------------------------+ //| TrainingLauncher.mq5 | //| 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 version "1.00" #property strict //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include "Def.mqh" #include //+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input string InpFileNameArgs = ""; // Path al archivo donde se ubica datos extra para el py input int InpFileCommonFlag = 0; // El path de archivo args essta en common flag input string InpArgsConfigJson = ""; // Path al archivo de configuracion para el py json input string InpArgsResJson = ""; // Path el archivo donde se pondran los resultados input string InpArgsLockBin = ""; // Path al arcvhio binario input string InpArgsBaseTerminalPath = ""; // Path del terminal base (common o files) // 1 hora (3600 * 1000) en ms input uint InpPyTimeoutMs = 3600000; input string InpFileNameBat = ""; // Path al bat (solo para eje py) //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ #define WAIT_TIMEOUT_VAL 0x102 #define FILE_ATTRIBUTE_NORMAL 0x80 //--- #resource "py.bat" as const string py_bat //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- FastLog(FUNCION_ACTUAL, INFO_TEXT, "Iniciando launcher de py"); //--- Apertura en lecutra del file ::ResetLastError(); const int fh = ::FileOpen(InpFileNameArgs, FILE_READ | FILE_TXT | InpFileCommonFlag); if(fh == INVALID_HANDLE) { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Fallo al abrir el archivo args = %s, ultimo error = %d", InpFileNameArgs, ::GetLastError())); ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_ERR_EXE_PY); return Finalize(INIT_PARAMETERS_INCORRECT); } //--- Lecutra de file const string log_file = ::FileReadString(fh); const string path_file_py = ::FileReadString(fh); const string extension = ::FileReadString(fh); const string interpreter = ::FileReadString(fh); const string pythonpath = ::FileReadString(fh); ::FileClose(fh); //--- Args const string args = "--config \"" + (InpArgsBaseTerminalPath + InpArgsConfigJson) + "\"" + " --results_json \"" + (InpArgsBaseTerminalPath + InpArgsResJson) + "\"" + " --lock_bin \"" + (InpArgsBaseTerminalPath + InpArgsLockBin) + "\"" + ((log_file.Length() > 4) ? (" --log_file \"" + (InpArgsBaseTerminalPath + log_file) + "\"") : ""); // el lenght mayor a .log Print(args); //--- Consutrccion del cmd string cmd; if(extension == "py") { string bat_content = py_bat; StringReplace(bat_content, "{PYTHONPATH_LINE}", (pythonpath.Length() > 1) ? ("set PYTHONPATH=" + pythonpath) : ""); StringReplace(bat_content, "{INTERPRETER}", interpreter); StringReplace(bat_content, "{PY_FILE}", path_file_py); StringReplace(bat_content, "{ARGS}", args); //--- Bat filename const string bat_full = InpArgsBaseTerminalPath + InpFileNameBat; // Convert to uchar uchar buf[]; StringToCharArray(bat_content, buf, 0, StringLen(bat_content), CP_ACP); // ANSI para cmd.exe // Open const HANDLE hFile = CreateFileW(bat_full, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if(hFile == -1) { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Fallo CreateFileW bat, error = %d", kernel32::GetLastError())); ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_ERR_EXE_PY); return Finalize(); } // Write uint written; WriteFile(hFile, buf, ArraySize(buf), written, 0); // tal cual dado que StringLen solo opcioas los caracter vlaidos quitanel el nulo \0 CloseHandle(hFile); //--- // Lanzamos el bat cmd = "cmd.exe /C \"\"" + bat_full + "\"\""; } else cmd = "\"" + path_file_py + "\" " + args; //--- Print Print(cmd); //--- Lanzamos el proceso PROCESS_INFORMATION pi; STARTUPINFOW si; ::ZeroMemory(si); si.cb = sizeof(si); const int created = CreateProcessW(NULL, cmd, NULL, NULL, false, 0, NULL, NULL, si, pi); if(created == 0) { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Fallo CreateProcessW, ultimo error = %d", kernel32::GetLastError())); ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_ERR_EXE_PY); return Finalize(); } //--- Esperamos const uint wait = WaitForSingleObject(pi.hProcess, InpPyTimeoutMs); if(wait == WAIT_TIMEOUT_VAL) TerminateProcess(pi.hProcess, 1); uint exit_code = 1; GetExitCodeProcess(pi.hProcess, exit_code); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); //--- Vericamos como quedo el resultado if(wait == WAIT_TIMEOUT_VAL) { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, "Timeout el py tardó demasiado"); ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_TIMEOUT); return Finalize(); } //--- if(exit_code != AIDATATASKRUNNNER_TRAINIG_OUT_EXITO) { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Py terminó con exit_code = %u", exit_code)); ResFinal(exit_code); return Finalize(); } //--- Exito total res code 0 // Liberamos el pool ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_EXITO); //--- return(Finalize()); // Sale solo } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ResFinal(int finish_code) { //--- Creacion del json ::ResetLastError(); const int fh = ::FileOpen(InpArgsResJson, FILE_WRITE | FILE_TXT | InpFileCommonFlag, 0); if(fh != INVALID_HANDLE) { ::FileWriteString(fh, ::StringFormat("{\"finish\":%d}", finish_code)); ::FileClose(fh); } else { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Fallo al escribir res.json, ultimo error = %d", ::GetLastError())); } //--- Creasion del bin ::ResetLastError(); const int fh2 = ::FileOpen(InpArgsLockBin, FILE_WRITE | FILE_BIN | InpFileCommonFlag, 0); if(fh2 != INVALID_HANDLE) ::FileClose(fh2); else { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Fallo al escribir lock.bin, ultimo error = %d", ::GetLastError())); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int Finalize(const int res = INIT_FAILED) { ::ChartClose(ChartID()); return res; } //+------------------------------------------------------------------+