750 lines
21 KiB
MQL5
750 lines
21 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Orquestador.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 WORKFLOWSBYLEO_FINAL_ORQUESTADOR_MQH
|
|
#define WORKFLOWSBYLEO_FINAL_ORQUESTADOR_MQH
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#include "..\\Core\\Def.mqh"
|
|
#include <TSN\\MQLArticles\\Utils\\EnumReg.mqh>
|
|
// Para saber cuando termino un wf crear un callback on end SIEMPRE
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
CWorkflow::CWorkflow(void)
|
|
{
|
|
m_call_on_step_size = ArrayResize(m_call_on_step, 0);
|
|
m_call_on_start_size = ArrayResize(m_call_on_start, 0);
|
|
m_call_on_end_size = ArrayResize(m_call_on_end, 0);
|
|
m_steps_size = ArrayResize(m_steps, 0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
CWorkflow::~CWorkflow()
|
|
{
|
|
//--- Callbacks
|
|
for(int i = 0; i < m_call_on_step_size; i++)
|
|
{
|
|
if(CheckPointer(m_call_on_step[i]) == POINTER_DYNAMIC)
|
|
delete m_call_on_step[i];
|
|
}
|
|
for(int i = 0; i < m_call_on_start_size; i++)
|
|
{
|
|
if(CheckPointer(m_call_on_start[i]) == POINTER_DYNAMIC)
|
|
delete m_call_on_start[i];
|
|
}
|
|
for(int i = 0; i < m_call_on_end_size; i++)
|
|
{
|
|
if(CheckPointer(m_call_on_end[i]) == POINTER_DYNAMIC)
|
|
delete m_call_on_end[i];
|
|
}
|
|
|
|
//--- Pasos
|
|
for(int i = 0; i < m_steps_size; i++)
|
|
{
|
|
if(CheckPointer(m_steps[i]) == POINTER_DYNAMIC)
|
|
delete m_steps[i];
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
/*
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_BOOL = 1
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_CHAR = 2
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_UCHAR = 3
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_SHORT = 4
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_USHORT = 5
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_COLOR = 6
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_INT = 7
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_UINT = 8
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_DATETIME = 9
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_LONG = 10
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_ULONG = 11
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_FLOAT = 12
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_DOUBLE = 13
|
|
2026.04.13 12:36:25.128 tas (EURUSD,H1) TYPE_STRING = 14
|
|
*/
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Variables del YML |
|
|
//+------------------------------------------------------------------+
|
|
bool CWorkflow::AssingMqlParam(MqlParam& param, CYmlNode& node, const string stype)
|
|
{
|
|
//---
|
|
if(!node.IsValidDataType()) // Si no es un tipo de data valido salimos
|
|
return false;
|
|
|
|
//---
|
|
const ENUM_DATATYPE type = CEnumReg::GetValue<ENUM_DATATYPE>(stype, WRONG_VALUE);
|
|
if(type == WRONG_VALUE)
|
|
return false; // Tipo solicitado invalido
|
|
|
|
//---
|
|
param.type = type;
|
|
|
|
//---
|
|
switch(type)
|
|
{
|
|
case TYPE_BOOL:
|
|
param.integer_value = TranslateBool(node, "");
|
|
break;
|
|
case TYPE_CHAR:
|
|
param.integer_value = TranslateNumber<char>(node, "");
|
|
break;
|
|
case TYPE_UCHAR:
|
|
param.integer_value = TranslateNumber<uchar>(node, "");
|
|
break;
|
|
case TYPE_SHORT:
|
|
param.integer_value = TranslateNumber<short>(node, "");
|
|
break;
|
|
case TYPE_USHORT:
|
|
param.integer_value = TranslateNumber<ushort>(node, "");
|
|
break;
|
|
case TYPE_COLOR:
|
|
param.integer_value = long(color(TranslateStr(node, "")));
|
|
break;
|
|
case TYPE_INT:
|
|
param.integer_value = TranslateNumber<int>(node, "");
|
|
break;
|
|
case TYPE_UINT:
|
|
param.integer_value = TranslateNumber<uint>(node, "");
|
|
break;
|
|
case TYPE_DATETIME:
|
|
param.integer_value = long(datetime(TranslateStr(node, "")));
|
|
break;
|
|
case TYPE_LONG:
|
|
case TYPE_ULONG:
|
|
param.integer_value = TranslateNumber<long>(node, "");
|
|
break;
|
|
case TYPE_FLOAT:
|
|
param.double_value = TranslateNumber<float>(node, "");
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
param.double_value = TranslateNumber<double>(node, "");
|
|
break;
|
|
case TYPE_STRING:
|
|
param.string_value = TranslateStr(node, "");
|
|
break;
|
|
default:
|
|
param.string_value = TranslateStr(node, "");
|
|
break;
|
|
}
|
|
|
|
//---
|
|
return true;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
bool CWorkflow::AssingMqlParam(MqlParam& param, CYmlNode& node, ENUM_DATATYPE type)
|
|
{
|
|
//---
|
|
if(!node.IsValidDataType()) // Si no es un tipo de data valido salimos
|
|
return false;
|
|
|
|
//---
|
|
param.type = type;
|
|
|
|
//---
|
|
switch(type)
|
|
{
|
|
case TYPE_BOOL:
|
|
param.integer_value = TranslateBool(node, "");
|
|
break;
|
|
case TYPE_CHAR:
|
|
param.integer_value = TranslateNumber<char>(node, "");
|
|
break;
|
|
case TYPE_UCHAR:
|
|
param.integer_value = TranslateNumber<uchar>(node, "");
|
|
break;
|
|
case TYPE_SHORT:
|
|
param.integer_value = TranslateNumber<short>(node, "");
|
|
break;
|
|
case TYPE_USHORT:
|
|
param.integer_value = TranslateNumber<ushort>(node, "");
|
|
break;
|
|
case TYPE_COLOR:
|
|
param.integer_value = long(color(TranslateStr(node, "")));
|
|
break;
|
|
case TYPE_INT:
|
|
param.integer_value = TranslateNumber<int>(node, "");
|
|
break;
|
|
case TYPE_UINT:
|
|
param.integer_value = TranslateNumber<uint>(node, "");
|
|
break;
|
|
case TYPE_DATETIME:
|
|
param.integer_value = long(datetime(TranslateStr(node, "")));
|
|
break;
|
|
case TYPE_LONG:
|
|
case TYPE_ULONG:
|
|
param.integer_value = TranslateNumber<long>(node, "");
|
|
break;
|
|
case TYPE_FLOAT:
|
|
param.double_value = TranslateNumber<float>(node, "");
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
param.double_value = TranslateNumber<double>(node, "");
|
|
break;
|
|
case TYPE_STRING:
|
|
param.string_value = TranslateStr(node, "");
|
|
break;
|
|
default:
|
|
param.string_value = TranslateStr(node, "");
|
|
break;
|
|
}
|
|
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
template <typename TEnum>
|
|
TEnum CWorkflow::TranslateEnum(const CYmlNode &val, const string &def)
|
|
{
|
|
const string res = val.ToString(def);
|
|
const int t = StringLen(res);
|
|
int k = 0;
|
|
string final_res = "";
|
|
while(k < t)
|
|
{
|
|
if(res[k] < 33)
|
|
{
|
|
// Si son espacios ignoramos
|
|
//---
|
|
const int len = StringLen(final_res);
|
|
StringSetLength(final_res, len + 1);
|
|
final_res.SetChar(len, res[k++]);
|
|
continue;
|
|
}
|
|
|
|
//---
|
|
if(k + 2 < t &&
|
|
res[k] == '$' &&
|
|
res[k + 1] == '{' &&
|
|
res[k + 2] == '{')
|
|
{
|
|
k += 3;
|
|
const int start = k;
|
|
|
|
//--- Buscamos el final
|
|
while(k + 1 < t)
|
|
{
|
|
if(res[k] == '}' && res[k + 1] == '}')
|
|
{
|
|
break;
|
|
}
|
|
k++;
|
|
}
|
|
|
|
//---
|
|
// ${{aidata.hola}}|${{aidata.no}}
|
|
string temp = StringSubstrRange(res, start, k - 1);
|
|
if(!m_hash_str_val_to_str.TryGetStr(temp, temp))
|
|
{
|
|
final_res += "${{" + temp + "}}";
|
|
}
|
|
else
|
|
{
|
|
final_res += temp;
|
|
}
|
|
k += 2; // Saltamos los }}
|
|
}
|
|
|
|
//---
|
|
const int len = StringLen(final_res);
|
|
StringSetLength(final_res, len + 1);
|
|
final_res.SetChar(len, res[k++]);
|
|
}
|
|
return CEnumReg::GetValue<TEnum>(final_res, WRONG_VALUE); // Tal cual
|
|
// NOTA: dado que es WRONG_VALUE chekear eso o poner un valorp def en el str como "_Period" por ejemplo
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
string CWorkflow::TranslateStr(const CYmlNode& val, const string& def)
|
|
{
|
|
const string res = val.ToString(def);
|
|
const int t = StringLen(res);
|
|
int k = 0;
|
|
string final_res = "";
|
|
while(k < t)
|
|
{
|
|
if(res[k] < 33)
|
|
{
|
|
// Si son espacios ignoramos
|
|
//---
|
|
const int len = StringLen(final_res);
|
|
StringSetLength(final_res, len + 1);
|
|
final_res.SetChar(len, res[k++]);
|
|
continue;
|
|
}
|
|
|
|
//---
|
|
if(k + 2 < t &&
|
|
res[k] == '$' &&
|
|
res[k + 1] == '{' &&
|
|
res[k + 2] == '{')
|
|
{
|
|
k += 3;
|
|
const int start = k;
|
|
|
|
//--- Buscamos el final
|
|
while(k + 1 < t)
|
|
{
|
|
if(res[k] == '}' && res[k + 1] == '}')
|
|
{
|
|
break;
|
|
}
|
|
k++;
|
|
}
|
|
|
|
//---
|
|
// ${{aidata.hola}}|${{aidata.no}}
|
|
string temp = StringSubstrRange(res, start, k - 1);
|
|
if(!m_hash_str_val_to_str.TryGetStr(temp, temp))
|
|
{
|
|
final_res += "${{" + temp + "}}";
|
|
}
|
|
else
|
|
{
|
|
final_res += temp;
|
|
}
|
|
k += 2; // Saltamos los }}
|
|
}
|
|
|
|
//---
|
|
const int len = StringLen(final_res);
|
|
StringSetLength(final_res, len + 1);
|
|
final_res.SetChar(len, res[k++]);
|
|
}
|
|
return final_res; // Tal cual
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
// Integer \ Dbl
|
|
template <typename TNumber>
|
|
TNumber CWorkflow::TranslateNumber(const CYmlNode& val, const string& def)
|
|
{
|
|
const string res = val.ToString(def);
|
|
const int t = StringLen(res);
|
|
int k = 0;
|
|
string final_res = "";
|
|
while(k < t)
|
|
{
|
|
if(res[k] < 33)
|
|
{
|
|
// Si son espacios ignoramos
|
|
//---
|
|
const int len = StringLen(final_res);
|
|
StringSetLength(final_res, len + 1);
|
|
final_res.SetChar(len, res[k++]);
|
|
continue;
|
|
}
|
|
|
|
//---
|
|
if(k + 2 < t &&
|
|
res[k] == '$' &&
|
|
res[k + 1] == '{' &&
|
|
res[k + 2] == '{')
|
|
{
|
|
k += 3;
|
|
const int start = k;
|
|
|
|
//--- Buscamos el final
|
|
while(k + 1 < t)
|
|
{
|
|
if(res[k] == '}' && res[k + 1] == '}')
|
|
{
|
|
break;
|
|
}
|
|
k++;
|
|
}
|
|
|
|
//---
|
|
// ${{aidata.hola}}|${{aidata.no}}
|
|
string temp = StringSubstrRange(res, start, k - 1);
|
|
if(!m_hash_str_val_to_str.TryGetStr(temp, temp))
|
|
{
|
|
final_res += "${{" + temp + "}}";
|
|
}
|
|
else
|
|
{
|
|
final_res += temp;
|
|
}
|
|
k += 2; // Saltamos los }}
|
|
}
|
|
|
|
//---
|
|
const int len = StringLen(final_res);
|
|
StringSetLength(final_res, len + 1);
|
|
final_res.SetChar(len, res[k++]);
|
|
}
|
|
return TNumber(final_res); // Tal cual
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
bool CWorkflow::TranslateBool(const CYmlNode& val, const string& def)
|
|
{
|
|
const string res = val.ToString(def);
|
|
const int t = StringLen(res);
|
|
int k = 0;
|
|
string final_res = "";
|
|
while(k < t)
|
|
{
|
|
if(res[k] < 33)
|
|
{
|
|
// Si son espacios ignoramos
|
|
//---
|
|
const int len = StringLen(final_res);
|
|
StringSetLength(final_res, len + 1);
|
|
final_res.SetChar(len, res[k++]);
|
|
continue;
|
|
}
|
|
|
|
//---
|
|
if(k + 2 < t &&
|
|
res[k] == '$' &&
|
|
res[k + 1] == '{' &&
|
|
res[k + 2] == '{')
|
|
{
|
|
k += 3;
|
|
const int start = k;
|
|
|
|
//--- Buscamos el final
|
|
while(k + 1 < t)
|
|
{
|
|
if(res[k] == '}' && res[k + 1] == '}')
|
|
{
|
|
break;
|
|
}
|
|
k++;
|
|
}
|
|
|
|
//---
|
|
// ${{aidata.hola}}|${{aidata.no}}
|
|
string temp = StringSubstrRange(res, start, k - 1);
|
|
if(!m_hash_str_val_to_str.TryGetStr(temp, temp))
|
|
{
|
|
final_res += "${{" + temp + "}}";
|
|
}
|
|
else
|
|
{
|
|
final_res += temp;
|
|
}
|
|
k += 2; // Saltamos los }}
|
|
}
|
|
|
|
//---
|
|
const int len = StringLen(final_res);
|
|
StringSetLength(final_res, len + 1);
|
|
final_res.SetChar(len, res[k++]);
|
|
}
|
|
return (final_res == "true" || int(final_res) != 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CWorkflow::Init(const string &file_name, bool comon_flag)
|
|
{
|
|
//---
|
|
m_initialized = false;
|
|
if(!m_yml.ParseFile(file_name, comon_flag))
|
|
{
|
|
int errcol, errline;
|
|
m_yml.GetLastErrorLocation(errline, errcol);
|
|
LogError(StringFormat("Failed parser yml, last err = %s | col = %d, line = %d", EnumToString(m_yml.LastErr()), errcol, errline), FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
//Print(ShortArrayToString(m_yml.m_yml));
|
|
//---
|
|
return Procesar();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CWorkflow::Init(const string &yml)
|
|
{
|
|
//---
|
|
m_initialized = false;
|
|
if(!m_yml.Parse(yml))
|
|
{
|
|
LogError(StringFormat("Failed parser yml, last err = %s", EnumToString(m_yml.LastErr())), FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
//---
|
|
return Procesar();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CWorkflow::Procesar(void)
|
|
{
|
|
//--- Cleaning (limpiamos variables)
|
|
for(int i = 0; i < m_steps_size; i++)
|
|
{
|
|
if(CheckPointer(m_steps[i]) == POINTER_DYNAMIC)
|
|
delete m_steps[i];
|
|
}
|
|
m_steps_size = ArrayResize(m_steps, 0);
|
|
m_step_curr_pos = 0;
|
|
m_is_running = false;
|
|
|
|
//--- Get Root
|
|
CYmlNode root = m_yml.GetRoot();
|
|
|
|
//--- Env
|
|
CYmlNode env = root["env"];
|
|
if(!env.IsObject())
|
|
{
|
|
LogWarning("No hay variables de entorno que revisar", FUNCION_ACTUAL);
|
|
}
|
|
else
|
|
{
|
|
//---
|
|
LogCaution("Hay variables de entorno que se van a procesar", FUNCION_ACTUAL);
|
|
|
|
//---
|
|
CYmlIteratorObj it = env.BeginObj();
|
|
while(it.IsValid())
|
|
{
|
|
const string key = "env." + it.Key();
|
|
const string val = it.Val().ToString();
|
|
//PrintFormat("%s = %s", key, val);
|
|
Variables().SetStr(key, val);
|
|
it.Next();
|
|
}
|
|
}
|
|
|
|
//--- Metadata
|
|
// Name
|
|
m_name = root["name"].ToString("Mi flujo de trabajo");
|
|
|
|
|
|
//--- Pasos
|
|
// Steps
|
|
CYmlNode steps = root["steps"];
|
|
if(!steps.IsArray())
|
|
{
|
|
LogError("El campo steps debe de ser del tipo array", FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
|
|
// Check size
|
|
m_steps_size = ArrayResize(m_steps, steps.Size());
|
|
int k = 0;
|
|
if(m_steps_size < 1)
|
|
{
|
|
LogError("No hay ninguno paso", FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
CYmlIteratorArray it = steps.BeginArr();
|
|
|
|
//--- Iteration
|
|
while(it.IsValid())
|
|
{
|
|
//---
|
|
CYmlNode step = it.Val();
|
|
CYmlNode params = step["with"];
|
|
// [module] [name]
|
|
const string name = step["name"].ToString("");
|
|
const string module = step["module"].ToString("");
|
|
|
|
//---
|
|
CWorkflowStep* s = CWorflowsFactory::GetWorkflowStep(module, name);
|
|
if(!CheckPointer(s))
|
|
{
|
|
LogError(StringFormat("Par (%s\\%s) invalido, el puntero obtenido es invalid pointer", module, name), FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
s.MainPointer(&this);
|
|
s.ParseIndexes(step["valid_results"]); // Parseamos (en caso no exista por defecto soiemrpe se habilita el 0)
|
|
|
|
//---
|
|
if(!s.Init(params))
|
|
{
|
|
delete s;
|
|
LogError(StringFormat("Fallo al iniciar el work = (%s\\%s)", module, name), FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
m_steps[k++] = s;
|
|
|
|
//---
|
|
it.Next();
|
|
}
|
|
|
|
//--- Succes
|
|
m_initialized = true;
|
|
//---
|
|
return true;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CWorkflow::AddCallBack(IWorkflowCallback *callback)
|
|
{
|
|
//---
|
|
if(!CheckPointer(callback))
|
|
{
|
|
LogError("No se agrego callback dado que es invalido", FUNCION_ACTUAL);
|
|
return;
|
|
}
|
|
|
|
//---
|
|
const uint8_t flags = callback.FlagsCall();
|
|
if((flags & WORKFLOWBYLEO_CALLBACK_ON_START) != 0)
|
|
{
|
|
m_call_on_start[ArrayResize(m_call_on_start, (++m_call_on_start_size)) - 1] = callback;
|
|
}
|
|
if((flags & WORKFLOWBYLEO_CALLBACK_ON_STEP) != 0)
|
|
{
|
|
m_call_on_step[ArrayResize(m_call_on_step, (++m_call_on_step_size)) - 1] = callback;
|
|
}
|
|
if((flags & WORKFLOWBYLEO_CALLBACK_ON_END) != 0)
|
|
{
|
|
m_call_on_end[ArrayResize(m_call_on_end, (++m_call_on_end_size)) - 1] = callback;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
// Funcion que se llama luego de terminar cada training
|
|
// La idea es reinicar valores como posiciones actual o is running
|
|
void CWorkflow::OnEnd(const int p, const int res)
|
|
{
|
|
//--- Siempre primero fasle para evitar ambiguedad
|
|
m_is_running = false;
|
|
if(res != 0)
|
|
LogError(StringFormat("Fallo al ejeuctar el step %d", p), FUNCION_ACTUAL);
|
|
|
|
//---
|
|
m_step_curr_pos = p; // En caso de se consulten proiedas como GetStep.. enviatmos el fuera de rango (p esta normalziadno bien)
|
|
for(int k = 0; k < m_call_on_step_size; k++)
|
|
m_call_on_step[k].OnWorkflowEnd(res, m_step_curr_pos);
|
|
|
|
//--- Reinicamos
|
|
m_step_curr_pos = 0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Ejecucion de tareas tanto asyncronas como sincronas |
|
|
//+------------------------------------------------------------------+
|
|
// Algirtmo compatible con steps aynsc, y sincropnicos
|
|
#define WORKFLOWBYLEO_SUCCES (0)
|
|
#define WORKFLOWBYLEO_ERR_FIRST_NO_INIT (1)
|
|
#define WORKFLOWBYLEO_ERR_FIRST_CALLBACK_ONSTART (2)
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int CWorkflow::First(void)
|
|
{
|
|
//---
|
|
// Nota: para recibir los eventos de finalziacion hace falta registrarse
|
|
// Esta funcion no retura ture cuando se completa el flujo retora true si el primer paso tuvo exito
|
|
//---
|
|
if(!m_initialized)
|
|
return WORKFLOWBYLEO_ERR_FIRST_NO_INIT;
|
|
|
|
//--- Hook start
|
|
for(int i = 0; i < m_call_on_start_size; i++)
|
|
{
|
|
if(!m_call_on_start[i].OnWorkflowStart())
|
|
{
|
|
LogError(StringFormat("No se pudo ejeuctar la tarea, fallo al llamar al callback = %d", i), FUNCION_ACTUAL);
|
|
return WORKFLOWBYLEO_ERR_FIRST_CALLBACK_ONSTART;
|
|
}
|
|
}
|
|
|
|
//--- Ejecucion
|
|
m_is_running = true;
|
|
const int res = m_steps[m_step_curr_pos].Run();
|
|
_RecibedEvent(res, true);
|
|
|
|
//---
|
|
return WORKFLOWBYLEO_SUCCES;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
// Notas:
|
|
// flag: siempre debe de ser false si se llama de afuera siempre... (la clase si lo varia)
|
|
// otro: ovbiamnte esto no se peude llamar de afuera daod que podria dar array out range etc.. asi que cuidado con eso no deberia de llamarse
|
|
void CWorkflow::_RecibedEvent(const int res, bool flag = false)
|
|
{
|
|
//---
|
|
if(!m_steps[m_step_curr_pos].IsPassed(res))
|
|
{
|
|
OnEnd(m_step_curr_pos, res);
|
|
return; // Termino ya no hace falta ir abajo
|
|
}
|
|
|
|
//--- Solo revisamos el siguintye si es sincrenoico
|
|
if(!m_steps[m_step_curr_pos].m_async || !flag)
|
|
{
|
|
m_step_curr_pos++; // Aumentamos dado que el paso anterior no era sincrono
|
|
|
|
//---
|
|
if(m_step_curr_pos >= m_steps_size)
|
|
{
|
|
// Se supero todo finalizamos
|
|
OnEnd(m_step_curr_pos - 1, res);
|
|
}
|
|
else
|
|
{
|
|
// Hay mas tareas
|
|
// Primero notificamremos de la tarea anteiorr
|
|
for(int k = 0; k < m_call_on_step_size; k++)
|
|
m_call_on_step[k].OnWorkflowStep(res, m_step_curr_pos - 1);
|
|
|
|
// Luego continuamos con la siguiente
|
|
const int res = m_steps[m_step_curr_pos].Run();
|
|
_RecibedEvent(res, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
__forceinline void CWorkflow::ChartEvent(const int32_t id, const long& lparam, const double& dparam, const string& sparam)
|
|
{
|
|
if(m_is_running && (m_steps[m_step_curr_pos].m_async_flags & WORKFLOWBYLEO_STEP_FLAG_ON_CHART_EVENT) != 0)
|
|
m_steps[m_step_curr_pos].ChartEvent(id, lparam, dparam, sparam);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
__forceinline void CWorkflow::TimerEvent(void)
|
|
{
|
|
if(m_is_running && (m_steps[m_step_curr_pos].m_async_flags & WORKFLOWBYLEO_STEP_FLAG_ON_TIMER_EVENT) != 0)
|
|
m_steps[m_step_curr_pos].OnTimerEvent();
|
|
}
|
|
|
|
//---
|
|
#endif // WORKFLOWSBYLEO_FINAL_ORQUESTADOR_MQH
|