AiDataTaskRuner/Backend/Training/TrainingLauncher.mq5

210 lines
7.9 KiB
MQL5
Raw Permalink Normal View History

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
//+------------------------------------------------------------------+
#define WAIT_TIMEOUT_VAL 0x102
#define FILE_ATTRIBUTE_NORMAL 0x80
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
//+------------------------------------------------------------------+