2026-03-24 10:39:45 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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 <TSN\\ExtraCodes\\Func.mqh>
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-03-26 09:51:33 -05:00 | | | //| 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)
|
2026-03-24 10:39:45 -05:00 | | | // 1 hora (3600 * 1000) en ms
|
| | | input uint InpPyTimeoutMs = 3600000;
|
2026-03-28 10:58:12 -05:00 | | | input string InpFileNameBat = ""; // Path al bat (solo para eje py)
|
2026-03-24 10:39:45 -05:00 | | |
|
2026-03-28 10:58:12 -05:00 | | | //+------------------------------------------------------------------+
|
2026-03-28 11:09:56 -05:00 | | | //| Global variables |
|
2026-03-28 10:58:12 -05:00 | | | //+------------------------------------------------------------------+
|
2026-03-28 10:22:11 -05:00 | | | //---
|
| | | #resource "py.bat" as const string py_bat
|
| | |
|
2026-03-24 10:39:45 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| Expert initialization function |
|
| | | //+------------------------------------------------------------------+
|
| | | int OnInit()
|
| | | {
|
2026-03-26 09:51:33 -05:00 | | | //---
|
| | | FastLog(FUNCION_ACTUAL, INFO_TEXT, "Iniciando launcher de py");
|
| | |
|
2026-03-28 10:22:11 -05:00 | | | //--- Apertura en lecutra del file
|
2026-03-24 10:39:45 -05:00 | | | ::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);
|
2026-03-26 09:51:33 -05:00 | | | return Finalize(INIT_PARAMETERS_INCORRECT);
|
2026-03-24 10:39:45 -05:00 | | | }
|
2026-03-28 10:22:11 -05:00 | | |
|
| | | //--- Lecutra de file
|
2026-03-26 11:23:24 -05:00 | | | const string log_file = ::FileReadString(fh);
|
2026-03-24 10:39:45 -05:00 | | | const string path_file_py = ::FileReadString(fh);
|
| | | const string extension = ::FileReadString(fh);
|
2026-03-28 08:35:32 -05:00 | | | const string interpreter = ::FileReadString(fh);
|
2026-03-28 10:22:11 -05:00 | | | const string pythonpath = ::FileReadString(fh);
|
2026-03-24 10:39:45 -05:00 | | | ::FileClose(fh);
|
2026-03-28 10:22:11 -05:00 | | |
|
| | | //--- Args
|
2026-03-24 10:39:45 -05:00 | | | const string args = "--config \"" + (InpArgsBaseTerminalPath + InpArgsConfigJson) + "\""
|
2026-03-28 10:22:11 -05:00 | | | + " --results_json \"" + (InpArgsBaseTerminalPath + InpArgsResJson) + "\""
|
| | | + " --lock_bin \"" + (InpArgsBaseTerminalPath + InpArgsLockBin) + "\""
|
2026-03-26 11:23:44 -05:00 | | | + ((log_file.Length() > 4) ? (" --log_file \"" + (InpArgsBaseTerminalPath + log_file) + "\"") : ""); // el lenght mayor a .log
|
2026-03-28 10:58:12 -05:00 | | | Print(args);
|
2026-03-28 10:22:11 -05:00 | | | //--- Consutrccion del cmd
|
2026-03-28 08:35:32 -05:00 | | | string cmd;
|
| | | if(extension == "py")
|
2026-03-28 10:22:11 -05:00 | | | {
|
| | | string bat_content = py_bat;
|
2026-03-28 10:58:12 -05:00 | | | StringReplace(bat_content, "{PYTHONPATH_LINE}", (pythonpath.Length() > 1) ? ("set PYTHONPATH=" + pythonpath) : "");
|
2026-03-28 10:22:11 -05:00 | | | StringReplace(bat_content, "{INTERPRETER}", interpreter);
|
| | | StringReplace(bat_content, "{PY_FILE}", path_file_py);
|
| | | StringReplace(bat_content, "{ARGS}", args);
|
| | |
|
| | | //--- Bat filename
|
2026-03-28 10:58:12 -05:00 | | | 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)
|
2026-03-28 10:22:11 -05:00 | | | {
|
2026-03-28 10:58:12 -05:00 | | | FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Fallo CreateFileW bat, error = %d", kernel32::GetLastError()));
|
2026-03-28 10:22:11 -05:00 | | | ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_ERR_EXE_PY);
|
| | | return Finalize();
|
| | | }
|
| | |
|
2026-03-28 10:58:12 -05:00 | | | // 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);
|
| | |
|
| | | //---
|
2026-03-28 10:22:11 -05:00 | | | // Lanzamos el bat
|
| | | cmd = "cmd.exe /C \"\"" + bat_full + "\"\"";
|
| | | }
|
2026-03-28 08:35:32 -05:00 | | | else
|
| | | cmd = "\"" + path_file_py + "\" " + args;
|
| | |
|
2026-03-28 10:22:11 -05:00 | | | //--- Print
|
2026-03-26 09:51:33 -05:00 | | | Print(cmd);
|
2026-03-24 10:39:45 -05:00 | | |
|
| | | //--- 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)
|
| | | {
|
2026-03-26 09:51:33 -05:00 | | | FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Fallo CreateProcessW, ultimo error = %d", kernel32::GetLastError()));
|
2026-03-24 10:39:45 -05:00 | | | ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_ERR_EXE_PY);
|
2026-03-26 09:51:33 -05:00 | | | return Finalize();
|
2026-03-24 10:39:45 -05:00 | | | }
|
| | |
|
| | | //--- Esperamos
|
| | | const uint wait = WaitForSingleObject(pi.hProcess, InpPyTimeoutMs);
|
2026-04-09 20:49:16 -05:00 | | | if(wait == WAIT_TIMEOUT_VAL)
|
| | | TerminateProcess(pi.hProcess, 1);
|
| | |
|
2026-03-24 10:39:45 -05:00 | | | uint exit_code = 1;
|
| | | GetExitCodeProcess(pi.hProcess, exit_code);
|
| | | CloseHandle(pi.hProcess);
|
| | | CloseHandle(pi.hThread);
|
| | |
|
2026-04-09 20:49:16 -05:00 | | |
|
2026-03-24 10:39:45 -05:00 | | | //--- Vericamos como quedo el resultado
|
| | | if(wait == WAIT_TIMEOUT_VAL)
|
| | | {
|
2026-04-09 20:49:16 -05:00 | | | FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, "Timeout el py tardó demasiado");
|
2026-03-24 10:39:45 -05:00 | | | ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_TIMEOUT);
|
2026-03-26 09:51:33 -05:00 | | | return Finalize();
|
2026-03-24 10:39:45 -05:00 | | | }
|
| | |
|
| | | //---
|
2026-03-26 11:52:16 -05:00 | | | if(exit_code != AIDATATASKRUNNNER_TRAINIG_OUT_EXITO)
|
2026-03-24 10:39:45 -05:00 | | | {
|
| | | FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, ::StringFormat("Py terminó con exit_code = %u", exit_code));
|
| | | ResFinal(exit_code);
|
2026-03-26 09:51:33 -05:00 | | | return Finalize();
|
2026-03-24 10:39:45 -05:00 | | | }
|
| | |
|
2026-03-26 11:52:16 -05:00 | | | //--- Exito total res code 0
|
| | | // Liberamos el pool
|
| | | ResFinal(AIDATATASKRUNNNER_TRAINIG_OUT_EXITO);
|
2026-03-24 10:39:45 -05:00 | | |
|
| | | //---
|
2026-03-26 09:51:33 -05:00 | | | return(Finalize()); // Sale solo
|
2026-03-24 10:39:45 -05:00 | | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| 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()));
|
| | | }
|
| | | }
|
2026-03-26 09:51:33 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-03-26 11:23:24 -05:00 | | | int Finalize(const int res = INIT_FAILED)
|
2026-03-26 09:51:33 -05:00 | | | {
|
| | | ::ChartClose(ChartID());
|
| | | return res;
|
| | | }
|
2026-03-24 10:39:45 -05:00 | | | //+------------------------------------------------------------------+
|