Compare commits
23 Commits
release-1.4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 802c86c9e3 | |||
| 67aac378f5 | |||
| f9bf8d94a3 | |||
| c8f16bf27d | |||
| 981394b9e9 | |||
| 2dac5b433d | |||
| b3f817eda1 | |||
| 081500e0f9 | |||
| 4468c27d30 | |||
| df54353865 | |||
| 66602232d6 | |||
| 5738f693d1 | |||
| afab5f93cc | |||
| 795acc13c8 | |||
| 184b9f6d8b | |||
| 96fd26ad85 | |||
| 1d6b1aa2d5 | |||
| 07414d6300 | |||
| 8475ecc03a | |||
| 1d127c66b7 | |||
| 101807d69e | |||
| cd7e5f0e47 | |||
| bab1c9753a |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
Binary file not shown.
@@ -25,21 +25,57 @@
|
||||
class GlobalVariable
|
||||
{
|
||||
public:
|
||||
static int total() {return GlobalVariablesTotal();}
|
||||
static string name(int index) {return GlobalVariableName(index);}
|
||||
static void flush() {GlobalVariablesFlush();}
|
||||
static int total()
|
||||
{
|
||||
return GlobalVariablesTotal();
|
||||
}
|
||||
static string name(int index)
|
||||
{
|
||||
return GlobalVariableName(index);
|
||||
}
|
||||
static void flush()
|
||||
{
|
||||
GlobalVariablesFlush();
|
||||
}
|
||||
|
||||
static bool exists(string name) {return GlobalVariableCheck(name);}
|
||||
static datetime lastAccess(string name) {return GlobalVariableTime(name);}
|
||||
static bool exists(string name)
|
||||
{
|
||||
return GlobalVariableCheck(name);
|
||||
}
|
||||
static datetime lastAccess(string name)
|
||||
{
|
||||
return GlobalVariableTime(name);
|
||||
}
|
||||
|
||||
static bool makeTemp(string name) {return GlobalVariableTemp(name);}
|
||||
static double get(string name) {return GlobalVariableGet(name);}
|
||||
static bool get(string name,double &value) {return GlobalVariableGet(name,value);}
|
||||
static datetime set(string name,double value) {return GlobalVariableSet(name,value);}
|
||||
static bool setOn(string name,double value,double check) {return GlobalVariableSetOnCondition(name,value,check);}
|
||||
static bool makeTemp(string name)
|
||||
{
|
||||
return GlobalVariableTemp(name);
|
||||
}
|
||||
static double get(string name)
|
||||
{
|
||||
return GlobalVariableGet(name);
|
||||
}
|
||||
static bool get(string name,double &value)
|
||||
{
|
||||
return (bool)GlobalVariableGet(name,value);
|
||||
}
|
||||
static bool set(string name,double value)
|
||||
{
|
||||
return (bool)GlobalVariableSet(name,value);
|
||||
}
|
||||
static bool setOn(string name,double value,double check)
|
||||
{
|
||||
return GlobalVariableSetOnCondition(name,value,check);
|
||||
}
|
||||
|
||||
static bool remove(string name) {return GlobalVariableDel(name);}
|
||||
static bool removeAll(string prefix=NULL,datetime before=0) {return GlobalVariablesDeleteAll(prefix,before);}
|
||||
static bool remove(string name)
|
||||
{
|
||||
return (bool)GlobalVariableDel(name);
|
||||
}
|
||||
static bool removeAll(string prefix=NULL,datetime before=0)
|
||||
{
|
||||
return (bool)GlobalVariablesDeleteAll(prefix,before);
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| TempVar is a variable whose life time is the same as the program |
|
||||
@@ -57,14 +93,38 @@ public:
|
||||
GlobalVariable::makeTemp(name);
|
||||
}
|
||||
}
|
||||
~TempVar() {if(m_owned && isValid()) {GlobalVariable::remove(m_name);}}
|
||||
~TempVar()
|
||||
{
|
||||
if(m_owned && isValid())
|
||||
{
|
||||
GlobalVariable::remove(m_name);
|
||||
}
|
||||
}
|
||||
|
||||
bool isValid() const {return GlobalVariable::exists(m_name);}
|
||||
string getName() const {return m_name;}
|
||||
bool set(double value) {return GlobalVariable::set(m_name,value);}
|
||||
double get() const {return GlobalVariable::get(m_name);}
|
||||
bool setOn(double value,double check) {return GlobalVariable::setOn(m_name,value,check);}
|
||||
datetime lastAccess() const {return GlobalVariable::lastAccess(m_name);}
|
||||
bool isValid() const
|
||||
{
|
||||
return GlobalVariable::exists(m_name);
|
||||
}
|
||||
string getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
bool set(double value)
|
||||
{
|
||||
return GlobalVariable::set(m_name,value);
|
||||
}
|
||||
double get() const
|
||||
{
|
||||
return GlobalVariable::get(m_name);
|
||||
}
|
||||
bool setOn(double value,double check)
|
||||
{
|
||||
return GlobalVariable::setOn(m_name,value,check);
|
||||
}
|
||||
datetime lastAccess() const
|
||||
{
|
||||
return GlobalVariable::lastAccess(m_name);
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
@@ -77,7 +137,10 @@ public:
|
||||
set(initial);
|
||||
}
|
||||
long increment(long by=1);
|
||||
long decrement(long by=1) {return increment(-by);}
|
||||
long decrement(long by=1)
|
||||
{
|
||||
return increment(-by);
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
@@ -91,7 +154,7 @@ long AtomicVar::increment(long by)
|
||||
value=(long)get();
|
||||
success=setOn(value+by,value);
|
||||
}
|
||||
while(!success);
|
||||
while(!success && !IsStopped());
|
||||
return (value+by);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -103,7 +166,10 @@ private:
|
||||
TempVar m_var;
|
||||
public:
|
||||
Semaphore(string name,long initial=0);
|
||||
bool isValid() const {return m_var.isValid();}
|
||||
bool isValid() const
|
||||
{
|
||||
return m_var.isValid();
|
||||
}
|
||||
bool acquire();
|
||||
void release();
|
||||
};
|
||||
@@ -127,7 +193,8 @@ bool Semaphore::acquire(void)
|
||||
do
|
||||
{
|
||||
long value=(long)m_var.get();
|
||||
if(value == 0) return false;
|
||||
if(value == 0)
|
||||
return false;
|
||||
success=m_var.setOn(value-1,value);
|
||||
}
|
||||
while(!success && !IsStopped());
|
||||
@@ -168,22 +235,47 @@ class CriticalSection
|
||||
private:
|
||||
const string m_name;
|
||||
public:
|
||||
CriticalSection(string name):m_name(name){}
|
||||
CriticalSection(string name):m_name(name) {}
|
||||
|
||||
bool isValid() const {return m_name!=NULL;}
|
||||
string getName() const {return m_name;}
|
||||
bool isValid() const
|
||||
{
|
||||
return m_name!=NULL;
|
||||
}
|
||||
string getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void enter() { while(!GlobalVariable::makeTemp(m_name) && !IsStopped())Sleep(100); }
|
||||
bool tryEnter() { return GlobalVariable::makeTemp(m_name); }
|
||||
void leave() { GlobalVariable::remove(m_name);}
|
||||
void enter()
|
||||
{
|
||||
GlobalVariable::remove(m_name);
|
||||
while(!GlobalVariable::makeTemp(m_name) && !IsStopped())
|
||||
Sleep(100);
|
||||
}
|
||||
bool tryEnter()
|
||||
{
|
||||
return GlobalVariable::makeTemp(m_name);
|
||||
}
|
||||
void leave()
|
||||
{
|
||||
GlobalVariable::remove(m_name);
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| HandleManager should implement 2 methods: create & destroy |
|
||||
//+------------------------------------------------------------------+
|
||||
template<typename T>
|
||||
interface HandleManager
|
||||
{
|
||||
T create();
|
||||
void destroy(T);
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| A reference counted global pointer (or handle) |
|
||||
//| Generic type parameter T can be long or int depending the handle |
|
||||
//| length (64bit or 32bit) |
|
||||
//| HandleManager should implement 2 static methods: create & destroy|
|
||||
//+------------------------------------------------------------------+
|
||||
template<typename T,typename HandleManager>
|
||||
template<typename T,typename HM>
|
||||
class GlobalHandle
|
||||
{
|
||||
private:
|
||||
@@ -192,12 +284,15 @@ private:
|
||||
string m_counterName;
|
||||
protected:
|
||||
T m_ref;
|
||||
HandleManager<T> *m_hm;
|
||||
public:
|
||||
GlobalHandle(string sharedKey=NULL):m_cs(sharedKey)
|
||||
{
|
||||
m_refName=m_cs.getName()+"_Ref";
|
||||
m_counterName=m_cs.getName()+"_Count";
|
||||
if(!m_cs.isValid()) m_ref=HandleManager::create();
|
||||
m_hm = new HM;
|
||||
if(!m_cs.isValid())
|
||||
m_ref = m_hm.create();
|
||||
else
|
||||
{
|
||||
m_cs.enter();
|
||||
@@ -208,7 +303,7 @@ public:
|
||||
}
|
||||
if(long(GlobalVariable::get(m_counterName))==0)
|
||||
{
|
||||
m_ref=HandleManager::create();
|
||||
m_ref = m_hm.create();
|
||||
if(!GlobalVariable::exists(m_refName))
|
||||
{
|
||||
GlobalVariable::makeTemp(m_refName);
|
||||
@@ -225,18 +320,28 @@ public:
|
||||
}
|
||||
~GlobalHandle()
|
||||
{
|
||||
if(!m_cs.isValid()) {HandleManager::destroy(m_ref); return;}
|
||||
m_cs.enter();
|
||||
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)-1);
|
||||
if(long(GlobalVariable::get(m_counterName))==0)
|
||||
if(!m_cs.isValid())
|
||||
{
|
||||
HandleManager::destroy(m_ref);
|
||||
GlobalVariable::remove(m_refName);
|
||||
GlobalVariable::remove(m_counterName);
|
||||
m_hm.destroy(m_ref);
|
||||
}
|
||||
m_cs.leave();
|
||||
else
|
||||
{
|
||||
m_cs.enter();
|
||||
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)-1);
|
||||
if(long(GlobalVariable::get(m_counterName))==0)
|
||||
{
|
||||
m_hm.destroy(m_ref);
|
||||
GlobalVariable::remove(m_refName);
|
||||
GlobalVariable::remove(m_counterName);
|
||||
}
|
||||
m_cs.leave();
|
||||
}
|
||||
delete m_hm;
|
||||
}
|
||||
|
||||
T ref() const {return m_ref;}
|
||||
T ref() const
|
||||
{
|
||||
return m_ref;
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
+60
-17
@@ -19,14 +19,7 @@
|
||||
//| and limitations under the License. |
|
||||
//+------------------------------------------------------------------+
|
||||
#property strict
|
||||
|
||||
#import "stdlib.ex4"
|
||||
string ErrorDescription(int error_code);
|
||||
int RGB(int red_value,int green_value,int blue_value);
|
||||
bool CompareDoubles(double number1,double number2);
|
||||
string DoubleToStrMorePrecision(double number,int precision);
|
||||
string IntegerToHexString(int integer_number);
|
||||
#import
|
||||
#include "Error.mqh"
|
||||
//+------------------------------------------------------------------+
|
||||
//| Mql language specific methods |
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -34,13 +27,29 @@ class Mql
|
||||
{
|
||||
public:
|
||||
static int getLastError() {return GetLastError();}
|
||||
static string getErrorMessage(int errorCode) {return ErrorDescription(errorCode);}
|
||||
static string getErrorMessage(int errorCode) {return GetErrorDescription(errorCode);}
|
||||
|
||||
static string doubleToString(double value,int precision) {return DoubleToStrMorePrecision(value,precision);}
|
||||
static string integerToHexString(int value) {return IntegerToHexString(value);}
|
||||
static int rgb(int red,int green,int blue) {return RGB(red,green,blue);}
|
||||
//--- Prefer global DoubleToString function
|
||||
static string doubleToString(double value,int precision) {return DoubleToString(value,precision);}
|
||||
//--- Adapted from stdlib.mq4 by using `StringSetCharacter` instead of `StringSetChar`
|
||||
static string integerToHexString(int value)
|
||||
{
|
||||
static const string digits="0123456789ABCDEF";
|
||||
string hex="00000000";
|
||||
int digit,shift=28;
|
||||
for(int i=0; i<8; i++)
|
||||
{
|
||||
digit=(value>>shift)&0x0F;
|
||||
StringSetCharacter(hex,i,digits[digit]);
|
||||
shift-=4;
|
||||
}
|
||||
return(hex);
|
||||
}
|
||||
//--- Prefer Canvas/Canvas.mqh XRGB
|
||||
static int rgb(int r,int g,int b) {return int(0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b));}
|
||||
|
||||
static bool isEqual(double a,double b) {return CompareDoubles(a,b);}
|
||||
//--- Prefer Lang/Number.mqh Double::IsEqual
|
||||
static bool isEqual(double a,double b) {return NormalizeDouble(a-b,8)==0;}
|
||||
|
||||
static bool isStopped() {return IsStopped();}
|
||||
|
||||
@@ -67,7 +76,19 @@ public:
|
||||
static string getProgramName() {return MQLInfoString(MQL_PROGRAM_NAME);}
|
||||
static string getProgramPath() {return MQLInfoString(MQL_PROGRAM_PATH);}
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Object getter/setter generator |
|
||||
//| ObjectAttr generates: |
|
||||
//| 1. private member m_property |
|
||||
//| 2. public method setProperty |
|
||||
//| 3. public method getProperty |
|
||||
//| ObjectAttrBool is specific to boolean type properties: |
|
||||
//| 1. private member m_isProperty |
|
||||
//| 2. public method setProperty |
|
||||
//| 3. public method isProperty |
|
||||
//| Use *Read or *Write versions for read only or write only |
|
||||
//| properties |
|
||||
//+------------------------------------------------------------------+
|
||||
#define ObjectAttr(Type, Private, Public) \
|
||||
public:\
|
||||
Type get##Public() const {return m_##Private;}\
|
||||
@@ -87,13 +108,35 @@ public:\
|
||||
private:\
|
||||
Type m_##Private\
|
||||
|
||||
#define ObjectAttrBool(Public) \
|
||||
public:\
|
||||
bool is##Public() const {return m_is##Public;}\
|
||||
void set##Public(bool value) {m_is##Public=value;}\
|
||||
private:\
|
||||
bool m_is##Public\
|
||||
|
||||
#define ObjectAttrBoolRead(Public) \
|
||||
public:\
|
||||
bool is##Public() const {return m_is##Public;}\
|
||||
private:\
|
||||
bool m_is##Public\
|
||||
|
||||
#define ObjectAttrBoolWrite(Public) \
|
||||
public:\
|
||||
void set##Public(bool value) {m_is##Public=value;}\
|
||||
private:\
|
||||
bool m_##Private\
|
||||
//+------------------------------------------------------------------+
|
||||
//| Print debug messages: only generate code for debugging runs |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifdef _DEBUG
|
||||
#define Debug(msg) Print(">>> DEBUG: In ",__FUNCTION__,"(",__FILE__,":",__LINE__,") [", msg, "]")
|
||||
#define Debug(msg) PrintFormat(">>> DEBUG[%s,%d,%s]: %s",__FILE__,__LINE__,__FUNCTION__,msg);
|
||||
#else
|
||||
#define Debug(msg)
|
||||
#endif
|
||||
|
||||
//--- Execute some code in the global scope
|
||||
//+------------------------------------------------------------------+
|
||||
//| Execute some code in the global scope |
|
||||
//+------------------------------------------------------------------+
|
||||
#define BEGIN_EXECUTE(Name) class __Execute##Name\
|
||||
{\
|
||||
public:__Execute##Name()\
|
||||
|
||||
+34
-57
@@ -45,31 +45,13 @@
|
||||
#define size_t int
|
||||
#endif
|
||||
|
||||
//--- _WIN32_WINNT version constants
|
||||
#define _WIN32_WINNT_NT4 0x0400 // Windows NT 4.0
|
||||
#define _WIN32_WINNT_WIN2K 0x0500 // Windows 2000
|
||||
#define _WIN32_WINNT_WINXP 0x0501 // Windows XP
|
||||
#define _WIN32_WINNT_WS03 0x0502 // Windows Server 2003
|
||||
#define _WIN32_WINNT_WIN6 0x0600 // Windows Vista
|
||||
#define _WIN32_WINNT_VISTA 0x0600 // Windows Vista
|
||||
#define _WIN32_WINNT_WS08 0x0600 // Windows Server 2008
|
||||
#define _WIN32_WINNT_LONGHORN 0x0600 // Windows Vista
|
||||
#define _WIN32_WINNT_WIN7 0x0601 // Windows 7
|
||||
#define _WIN32_WINNT_WIN8 0x0602 // Windows 8
|
||||
#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1
|
||||
#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 // Windows 10
|
||||
#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10
|
||||
|
||||
//--- define you own for your target platform
|
||||
#define WINVER 0x0A00
|
||||
#define _WIN32_WINNT 0x0A00
|
||||
|
||||
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
|
||||
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
|
||||
|
||||
#import "kernel32.dll"
|
||||
void RtlMoveMemory(intptr_t dest,const uchar &array[],size_t length);
|
||||
void RtlMoveMemory(uchar &array[],intptr_t src,size_t length);
|
||||
void RtlMoveMemory(intptr_t dest,const MqlRates &array[],size_t length);
|
||||
void RtlMoveMemory(MqlRates &array[],intptr_t src,size_t length);
|
||||
void RtlMoveMemory(intptr_t dest,const MqlTick &value,size_t length);
|
||||
void RtlMoveMemory(MqlTick &value,intptr_t src,size_t length);
|
||||
void RtlMoveMemory(intptr_t &dest,intptr_t src,size_t length);
|
||||
int lstrlen(intptr_t psz);
|
||||
int lstrlenW(intptr_t psz);
|
||||
@@ -83,34 +65,45 @@ int MultiByteToWideChar(uint codePage,
|
||||
string &str,
|
||||
int length
|
||||
);
|
||||
uint FormatMessageW(uint dwFlags,
|
||||
intptr_t lpSource,
|
||||
uint dwMessageId,
|
||||
uint dwLanguageId,
|
||||
ushort &buffer[],
|
||||
uint nSize,
|
||||
intptr_t Arguments
|
||||
);
|
||||
#import
|
||||
|
||||
//--- This is a standard header of the official MetaTrader distribution
|
||||
#include <WinUser32.mqh>
|
||||
//+------------------------------------------------------------------+
|
||||
//| Copy the memory contents pointed by src to array |
|
||||
//| array parameter should be initialized to the desired size |
|
||||
//| Need to import RtlMoveMemory with appropriate parameter type T |
|
||||
//+------------------------------------------------------------------+
|
||||
void ArrayFromPointer(uchar &array[],intptr_t src,int count=WHOLE_ARRAY)
|
||||
template<typename T>
|
||||
void ArrayFromPointer(T &array[],intptr_t src,int count=WHOLE_ARRAY)
|
||||
{
|
||||
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
|
||||
RtlMoveMemory(array,src,(size_t)size);
|
||||
RtlMoveMemory(array,src,(size_t)(size*sizeof(T)));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Copy array to the memory pointed by dest |
|
||||
//| Need to import RtlMoveMemory with appropriate parameter type T |
|
||||
//+------------------------------------------------------------------+
|
||||
void ArrayToPointer(const uchar &array[],intptr_t dest,int count=WHOLE_ARRAY)
|
||||
template<typename T>
|
||||
void ArrayToPointer(const T &array[],intptr_t dest,int count=WHOLE_ARRAY)
|
||||
{
|
||||
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
|
||||
RtlMoveMemory(dest,array,(size_t)size);
|
||||
RtlMoveMemory(dest,array,(size_t)(size*sizeof(T)));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Copy the memory contents pointed by src to value type T |
|
||||
//| Need to import RtlMoveMemory with appropriate parameter type T |
|
||||
//+------------------------------------------------------------------+
|
||||
template<typename T>
|
||||
void ValueFromPointer(T &value,intptr_t src)
|
||||
{
|
||||
RtlMoveMemory(value,src,(size_t)sizeof(T));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Copy value to the memory pointed by dest |
|
||||
//| Need to import RtlMoveMemory with appropriate parameter type T |
|
||||
//+------------------------------------------------------------------+
|
||||
template<typename T>
|
||||
void ValueToPointer(const T &value,intptr_t dest)
|
||||
{
|
||||
RtlMoveMemory(dest,value,(size_t)sizeof(T));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| For void** type, dereference a level to void* |
|
||||
@@ -150,16 +143,10 @@ string StringFromUtf8Pointer(intptr_t psz,int len)
|
||||
if(len < 0) return NULL;
|
||||
string res;
|
||||
int required=MultiByteToWideChar(CP_UTF8,0,psz,len,res,0);
|
||||
StringInit(res,required);
|
||||
int resLength = MultiByteToWideChar(CP_UTF8,0,psz,len,res,required);
|
||||
if(resLength != required)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return res;
|
||||
}
|
||||
// need to include the NULL terminating character
|
||||
StringInit(res,required+1);
|
||||
int rlen=MultiByteToWideChar(CP_UTF8,0,psz,len,res,required);
|
||||
return rlen != required ? NULL : res;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| for null-terminated string |
|
||||
@@ -188,13 +175,3 @@ void StringToUtf8(const string str,uchar &utf8[],bool ending=true)
|
||||
StringToCharArray(str,utf8,0,count,CP_UTF8);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get system defined error code message |
|
||||
//+------------------------------------------------------------------+
|
||||
string GetErrorMessage(int errorCode)
|
||||
{
|
||||
static ushort buffer[64*1024];
|
||||
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
0,errorCode,0,buffer,ArraySize(buffer),0);
|
||||
return ShortArrayToString(buffer);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
+21
-7
@@ -45,6 +45,20 @@ int zmq_ctx_shutdown(intptr_t context);
|
||||
int zmq_ctx_set(intptr_t context,int option,int optval);
|
||||
int zmq_ctx_get(intptr_t context,int option);
|
||||
#import
|
||||
class ContextHandleManager: public HandleManager<intptr_t>
|
||||
{
|
||||
intptr_t create() override
|
||||
{
|
||||
return zmq_ctx_new();
|
||||
}
|
||||
void destroy(intptr_t handle) override
|
||||
{
|
||||
if(0!=zmq_ctx_term(handle))
|
||||
{
|
||||
Debug("failed to terminate context");
|
||||
}
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Wraps a 0MZ context |
|
||||
//| |
|
||||
@@ -64,7 +78,7 @@ int zmq_ctx_get(intptr_t context,int option);
|
||||
//| and in a manner not easily recognized by humans, for example: |
|
||||
//| "__3kewducdxhkd__" |
|
||||
//+------------------------------------------------------------------+
|
||||
class Context: public GlobalHandle<intptr_t,Context>
|
||||
class Context: public GlobalHandle<intptr_t,ContextHandleManager>
|
||||
{
|
||||
protected:
|
||||
int get(int option) {return zmq_ctx_get(m_ref,option);}
|
||||
@@ -75,26 +89,26 @@ public:
|
||||
static intptr_t create() {return zmq_ctx_new();}
|
||||
static void destroy(intptr_t handle) {if(0!=zmq_ctx_term(handle)) {Debug("failed to terminate context");}}
|
||||
|
||||
Context(string shared=NULL):GlobalHandle<intptr_t,Context>(shared) {}
|
||||
Context(string shared=NULL):GlobalHandle<intptr_t,ContextHandleManager>(shared) {}
|
||||
|
||||
bool shutdown() {return 0==zmq_ctx_shutdown(m_ref);}
|
||||
|
||||
int getIoThreads() {return get(ZMQ_IO_THREADS);}
|
||||
void setIoThreads(int value) {if(!set(ZMQ_IO_THREADS,value)){Debug("failed to set ZMQ_IO_THREADS");}}
|
||||
void setIoThreads(int value) {if(!set(ZMQ_IO_THREADS,value)) {Debug("failed to set ZMQ_IO_THREADS");}}
|
||||
|
||||
int getMaxSockets() {return get(ZMQ_MAX_SOCKETS);}
|
||||
void setMaxSockets(int value) {if(!set(ZMQ_MAX_SOCKETS,value)){Debug("failed to set ZMQ_MAX_SOCKETS");}}
|
||||
void setMaxSockets(int value) {if(!set(ZMQ_MAX_SOCKETS,value)) {Debug("failed to set ZMQ_MAX_SOCKETS");}}
|
||||
|
||||
int getMaxMessageSize() {return get(ZMQ_MAX_MSGSZ);}
|
||||
void setMaxMessageSize(int value) {if(!set(ZMQ_MAX_MSGSZ,value)){Debug("failed to set ZMQ_MAX_MSGSZ");}}
|
||||
void setMaxMessageSize(int value) {if(!set(ZMQ_MAX_MSGSZ,value)) {Debug("failed to set ZMQ_MAX_MSGSZ");}}
|
||||
|
||||
int getSocketLimit() {return get(ZMQ_SOCKET_LIMIT);}
|
||||
|
||||
int getIpv6Options() {return get(ZMQ_IPV6);}
|
||||
void setIpv6Options(int value) {if(!set(ZMQ_IPV6,value)){Debug("failed to set ZMQ_IPV6");}}
|
||||
void setIpv6Options(int value) {if(!set(ZMQ_IPV6,value)) {Debug("failed to set ZMQ_IPV6");}}
|
||||
|
||||
bool isBlocky() {return 1==get(ZMQ_BLOCKY);}
|
||||
void setBlocky(bool value) {if(!set(ZMQ_BLOCKY,value?1:0)){Debug("failed to set ZMQ_BLOCKY");}}
|
||||
void setBlocky(bool value) {if(!set(ZMQ_BLOCKY,value?1:0)) {Debug("failed to set ZMQ_BLOCKY");}}
|
||||
|
||||
//--- Following options is not supported on windows
|
||||
void setSchedulingPolicy(int value) {/*ZMQ_THREAD_SCHED_POLICY*/}
|
||||
|
||||
+30
-76
@@ -79,6 +79,12 @@ struct PollItem
|
||||
uintptr_t fd;
|
||||
short events;
|
||||
short revents;
|
||||
#ifdef __X64__
|
||||
//--- note here that if the program runs on 64bit runtime, we need to pad the
|
||||
//--- struct to align to 8 bytes
|
||||
//--- thanks https://github.com/feng-ye for the solution to https://github.com/dingmaotu/mql-zmq/issues/14
|
||||
int __unused;
|
||||
#endif
|
||||
|
||||
bool hasInput() const {return(revents&ZMQ_POLLIN)!=0;}
|
||||
bool hasOutput() const {return(revents&ZMQ_POLLOUT)!=0;}
|
||||
@@ -133,16 +139,32 @@ public:
|
||||
bool connect(string addr);
|
||||
bool disconnect(string addr);
|
||||
|
||||
//--- send and receive packets
|
||||
bool recv(uchar &buf[],bool nowait=false);
|
||||
bool send(const uchar &buf[],bool nowait=false,bool more=false);
|
||||
bool sendConst(const uchar &buf[],bool nowait=false,bool more=false);
|
||||
//--- send raw bytes
|
||||
bool send(const uchar &buf[],bool nowait=false) {return -1!=zmq_send(m_ref,buf,ArraySize(buf),nowait?ZMQ_DONTWAIT:0);}
|
||||
bool sendConst(const uchar &buf[],bool nowait=false) {return -1!=zmq_send_const(m_ref,buf,ArraySize(buf),nowait?ZMQ_DONTWAIT:0);}
|
||||
|
||||
bool send(ZmqMsg &msg,bool nowait=false,bool more=false);
|
||||
bool recv(ZmqMsg &msg,bool nowait=false);
|
||||
//--- send ZmqMsg
|
||||
bool send(ZmqMsg &msg,bool nowait=false) {return -1!=zmq_msg_send(msg,m_ref,nowait?ZMQ_DONTWAIT:0);}
|
||||
//--- send string
|
||||
bool send(string msg,bool nowait=false) {ZmqMsg m(msg); return send(m,nowait);}
|
||||
//--- send empty
|
||||
bool send(bool nowait=false) {ZmqMsg m; return send(m,nowait);}
|
||||
|
||||
void register(PollItem &pollitem,bool read=false,bool write=false);
|
||||
void register(PollItem &pollitems[],int index,bool read=false,bool write=false);
|
||||
//--- same as above 5 but for multipart messages
|
||||
bool sendMore(const uchar &buf[],bool nowait=false);
|
||||
bool sendConstMore(const uchar &buf[],bool nowait=false);
|
||||
bool sendMore(ZmqMsg &msg,bool nowait=false)
|
||||
{
|
||||
int flags=ZMQ_SNDMORE;
|
||||
if(nowait) flags|=ZMQ_DONTWAIT;
|
||||
return -1!=zmq_msg_send(msg,m_ref,flags);
|
||||
}
|
||||
bool sendMore(string msg,bool nowait=false) {ZmqMsg m(msg); return sendMore(m,nowait);}
|
||||
bool sendMore(bool nowait=false) {ZmqMsg m; return sendMore(m,nowait);}
|
||||
|
||||
//--- receive packets
|
||||
bool recv(uchar &buf[],bool nowait=false) {return -1!=zmq_recv(m_ref,buf,ArraySize(buf),nowait?ZMQ_DONTWAIT:0);}
|
||||
bool recv(ZmqMsg &msg,bool nowait=false) {return -1!=zmq_msg_recv(msg,m_ref,nowait?ZMQ_DONTWAIT:0);}
|
||||
|
||||
//--- monitor socket events
|
||||
bool monitor(string addr,int events);
|
||||
@@ -204,54 +226,6 @@ bool Socket::disconnect(string addr)
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Socket::recv(uchar &buf[],bool nowait=false)
|
||||
{
|
||||
int options=0;
|
||||
if(nowait) options|=ZMQ_DONTWAIT;
|
||||
return -1!=zmq_recv(m_ref,buf,ArraySize(buf),options);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Socket::send(const uchar &buf[],bool nowait=false,bool more=false)
|
||||
{
|
||||
int options=0;
|
||||
if(nowait) options|=ZMQ_DONTWAIT;
|
||||
if(more) options|=ZMQ_SNDMORE;
|
||||
return -1!=zmq_send(m_ref,buf,ArraySize(buf),options);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Socket::sendConst(const uchar &buf[],bool nowait=false,bool more=false)
|
||||
{
|
||||
int options=0;
|
||||
if(nowait) options|=ZMQ_DONTWAIT;
|
||||
if(more) options|=ZMQ_SNDMORE;
|
||||
return -1!=zmq_send_const(m_ref,buf,ArraySize(buf),options);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Send a zmq_msg_t through a socket |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Socket::send(ZmqMsg &msg,bool nowait=false,bool more=false)
|
||||
{
|
||||
int flags=0;
|
||||
if(nowait) flags|=ZMQ_DONTWAIT;
|
||||
if(more) flags|=ZMQ_SNDMORE;
|
||||
return -1!=zmq_msg_send(msg,m_ref,flags);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Receive a zmq_msg_t from a socket |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Socket::recv(ZmqMsg &msg,bool nowait=false)
|
||||
{
|
||||
int flags=0;
|
||||
if(nowait) flags|=ZMQ_DONTWAIT;
|
||||
return -1!=zmq_msg_recv(msg,m_ref,flags);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Socket::monitor(string addr,int events)
|
||||
{
|
||||
uchar str[];
|
||||
@@ -263,26 +237,6 @@ bool Socket::monitor(string addr,int events)
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
void Socket::register(PollItem &pollitem,bool read=false,bool write=false)
|
||||
{
|
||||
ZeroMemory(pollitem);
|
||||
pollitem.socket=m_ref;
|
||||
if(read) pollitem.events|=ZMQ_POLLIN;
|
||||
if(write) pollitem.events|=ZMQ_POLLOUT;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
void Socket::register(PollItem &pollitems[],int index,bool read=false,bool write=false)
|
||||
{
|
||||
ZeroMemory(pollitems[index]);
|
||||
pollitems[index].socket=m_ref;
|
||||
if(read) pollitems[index].events|=ZMQ_POLLIN;
|
||||
if(write) pollitems[index].events|=ZMQ_POLLOUT;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Socket::proxy(Socket *frontend,Socket *backend,Socket *capture)
|
||||
{
|
||||
intptr_t frontend_ref= CheckPointer(frontend)==POINTER_DYNAMIC?frontend.ref():0;
|
||||
|
||||
@@ -184,10 +184,13 @@ public:
|
||||
SOCKOPT_GET_BYTES(OptionName,Macro,InitSize)
|
||||
|
||||
//--- for curve key
|
||||
//--- NOTE that the length of the key array must be 32.
|
||||
//--- since some versions of mt4 and newer versions of mt5 will report error
|
||||
//--- when the length of the array is specified, so the length specification is removed
|
||||
#define SOCKOPT_CURVE_KEY(KeyType,Macro) \
|
||||
bool getCurve##KeyType##Key(uchar &key[32]) {size_t len=32; return getOption(Macro,key,len);} \
|
||||
bool getCurve##KeyType##Key(uchar &key[]) {size_t len=32; return getOption(Macro,key,len);} \
|
||||
bool getCurve##KeyType##Key(string &key) {return getStringOption(Macro,key,41);} \
|
||||
bool setCurve##KeyType##Key(const uchar &key[32]) {return setOption(Macro,key,32);} \
|
||||
bool setCurve##KeyType##Key(const uchar &key[]) {return setOption(Macro,key,32);} \
|
||||
bool setCurve##KeyType##Key(string key) {return setStringOption(Macro,key);}
|
||||
|
||||
SOCKOPT_GET(int,Type,ZMQ_TYPE)
|
||||
@@ -251,7 +254,7 @@ public:
|
||||
SOCKOPT(int,ReceiveTimeout,ZMQ_RCVTIMEO) // milliseconds
|
||||
SOCKOPT(int,SendBuffer,ZMQ_SNDBUF) // bytes
|
||||
SOCKOPT(int,SendHighWaterMark,ZMQ_SNDHWM) // messages
|
||||
SOCKOPT(int,SendTimout,ZMQ_SNDTIMEO)
|
||||
SOCKOPT(int,SendTimeout,ZMQ_SNDTIMEO)
|
||||
|
||||
SOCKOPT_GET_BOOL(ReceiveMore,ZMQ_RCVMORE)
|
||||
|
||||
|
||||
@@ -88,9 +88,9 @@ public:
|
||||
|
||||
size_t size() {return zmq_msg_size(this);}
|
||||
|
||||
void getData(uchar &data[]);
|
||||
void getData(uchar &bytes[]);
|
||||
string getData();
|
||||
void setData(const uchar &data[]);
|
||||
void setData(const uchar &bytes[]);
|
||||
|
||||
bool more() {return 1==zmq_msg_more(this);}
|
||||
|
||||
@@ -113,12 +113,12 @@ bool ZmqMsg::setStringData(string data,bool nullterminated)
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get message data as bytes array |
|
||||
//+------------------------------------------------------------------+
|
||||
void ZmqMsg::getData(uchar &data[])
|
||||
void ZmqMsg::getData(uchar &bytes[])
|
||||
{
|
||||
size_t size=size();
|
||||
intptr_t src=data();
|
||||
ArrayResize(data,(int)size);
|
||||
ArrayFromPointer(data,src);
|
||||
if(ArraySize(bytes)<size) ArrayResize(bytes,(int)size);
|
||||
ArrayFromPointer(bytes,src);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get message data as utf-8 string |
|
||||
@@ -132,11 +132,11 @@ string ZmqMsg::getData()
|
||||
//+------------------------------------------------------------------+
|
||||
//| copy data to message internal storage |
|
||||
//+------------------------------------------------------------------+
|
||||
void ZmqMsg::setData(const uchar &data[])
|
||||
void ZmqMsg::setData(const uchar &bytes[])
|
||||
{
|
||||
intptr_t dest=data();
|
||||
size_t size=size();
|
||||
ArrayToPointer(data,dest,size);
|
||||
ArrayToPointer(bytes,dest,(int)size);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Wraps zmq_msg_gets: get metadata associated with the msg |
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,6 +2,15 @@
|
||||
|
||||
ZMQ binding for the MQL language (both 32bit MT4 and 64bit MT5)
|
||||
|
||||
* [1. Introduction](#introduction)
|
||||
* [2. Files and Installation](#files-and-installation)
|
||||
* [3. About string encoding](#about-string-encoding)
|
||||
* [4. Notes on context creation](#notes-on-context-creation)
|
||||
* [5. Usage](#usage)
|
||||
* [6. TODO](#todo)
|
||||
* [7. Changes](#changes)
|
||||
* [8. Donation](#donation)
|
||||
|
||||
## Introduction
|
||||
|
||||
This is a complete binding of the [ZeroMQ](http://zeromq.org/) library
|
||||
@@ -39,13 +48,13 @@ This binding contains three sets of files:
|
||||
use them in MetaTrader5.
|
||||
|
||||
3. Precompiled DLLs of both 64bit (`Library/MT5`) and 32bit (`Library/MT4`)
|
||||
ZeroMQ and libsodium are provided. Copy the corresponding DLLs to the
|
||||
`Library` folder of your MetaTrader terminal. If you are using MT5 32bit, use
|
||||
the 32bit version from `Library/MT4`. **The DLLs require that you have the
|
||||
latest Visual C++ runtime (2015)**.
|
||||
ZeroMQ (4.2.0) and libsodium (1.0.11) are provided. Copy the corresponding
|
||||
DLLs to the `Library` folder of your MetaTrader terminal. If you are using
|
||||
MT5 32bit, use the 32bit version from `Library/MT4`. **The DLLs require that
|
||||
you have the latest Visual C++ runtime (2015)**.
|
||||
|
||||
*Note* that if you are using **MT5 32bit**, you need to comment out the
|
||||
`__X64__` macro definition at the top of the `Include/Zmq/Common.mqh`. I
|
||||
`__X64__` macro definition at the top of the `Include/Mql/Lang/Native.mqh`. I
|
||||
assume MT5 is 64 bit, since their is no way to detect 32 bit by native
|
||||
macros, and to define pointer related values a macro like this is required.
|
||||
|
||||
@@ -54,6 +63,19 @@ This binding contains three sets of files:
|
||||
`libsodium.dll` is copied from the official binary release. If you want to
|
||||
support security mechanisms other than `curve`, or you want to use transports
|
||||
like OpenPGM, you need to compile your own DLL.
|
||||
|
||||
*Note* for WINE users, if the default binaries do not work for you, you can
|
||||
try the binaries in the `Library/VC2010` directory. The new binaries are a
|
||||
little newer (libzmq 4.2.2 and libsodium 1.0.36). They are compiled with
|
||||
Visual C++ 2010 Express SP1 (using the Windows SDK 7.1), and supposed to be
|
||||
more compatible to WINE than the VS2015 version. They depend on VC2010
|
||||
runtime (msvcr100.dll and msvcp100.dll). I have actually tested the old and
|
||||
the new DLLs on WINE 2.0.3 (Debian Jessie PlayOnLinux 32bit with MetaTrader4
|
||||
build 1090) and they both work. So it is *not* guarenteed but it is nice to
|
||||
have an alternative. The new libzmq.dll only runs on vista or newer windows
|
||||
because I turned on the `using poll` option. This improves performance a
|
||||
little bit. Since MetaTrader4 officially no longer supports Windows XP, I
|
||||
assume this would not be a problem.
|
||||
|
||||
## About string encoding
|
||||
|
||||
@@ -128,14 +150,27 @@ void OnStart()
|
||||
|
||||
1. Write more tests.
|
||||
2. Add more examples from the official ZMQ guide.
|
||||
3. More documentation
|
||||
4. High level API for common patterns
|
||||
|
||||
## Changes
|
||||
|
||||
* 2017-10-28: Released 1.5: Important: API change for `Socket.send`; Remove
|
||||
PollItem duplicate API (#11); Fix compiler warning (#10) and compile failure
|
||||
(#12); Add RTReq example from ZMQ Guide Chapter 3.
|
||||
* 2017-08-18: Released 1.4: Fix ZmqMsg setData bug; Change License to Apache
|
||||
2.0; Inlcude mql4-lib dependencies directly.
|
||||
* 2017-07-18: Released 1.3: Refactored poll support; Add Chapter 2 examples from
|
||||
the official ZMQ guide.
|
||||
* 2017-06-08: Released 1.2: Fix GlobalHandle bug; Add rebuild method to ZmqMsg;
|
||||
Complete all examples in ZMQ Guide Chanpter 1.
|
||||
Complete all examples in ZMQ Guide Chapter 1.
|
||||
* 2017-05-26: Released 1.1: add the ability to share a ZMQ context globally in a terminal
|
||||
* 2016-12-27: Released 1.0.
|
||||
|
||||
## Donation
|
||||
|
||||
This binding is created in spare time and the author answers questions, solves issues, and intends to maintain the code so that bugs are fixed and the binding is kept up to date with official releases. If you think that the binding is useful to you or your organization, please consider donate to the author for his effort to maintain this binding. Thanks!
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RL6U2HFVTCUUN)
|
||||
|
||||

|
||||
@@ -42,8 +42,8 @@ void OnStart()
|
||||
do
|
||||
{
|
||||
frontend.recv(message);
|
||||
more=message.more();
|
||||
backend.send(message,false,more);
|
||||
if(message.more()) backend.sendMore(message);
|
||||
else backend.send(message);
|
||||
}
|
||||
while(more);
|
||||
}
|
||||
@@ -53,8 +53,8 @@ void OnStart()
|
||||
do
|
||||
{
|
||||
backend.recv(message);
|
||||
more=message.more();
|
||||
frontend.send(message,false,more);
|
||||
if(message.more()) frontend.sendMore(message);
|
||||
else frontend.send(message);
|
||||
}
|
||||
while(more);
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ void OnStart()
|
||||
do
|
||||
{
|
||||
frontend.recv(message);
|
||||
more=message.more();
|
||||
backend.send(message,false,more);
|
||||
if(message.more()) backend.sendMore(message);
|
||||
else backend.send(message);
|
||||
}
|
||||
while(more);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RTReqBroker.mq4 |
|
||||
//| Copyright 2017, Li Ding |
|
||||
//| dingmaotu@126.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2017, Li Ding"
|
||||
#property link "dingmaotu@126.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
#property show_inputs
|
||||
//+------------------------------------------------------------------+
|
||||
//| This example comes from the "Load Balancing Pattern" |
|
||||
//| The original example creates threads for workers and spawn them |
|
||||
//| in the broker main thread. MetaTrader Terminal does not support |
|
||||
//| thread creation. So we split the broker and worker code to two |
|
||||
//| scripts. Since we splitted the code, we need to wait all workers |
|
||||
//| connect. |
|
||||
//| This is the broker part. |
|
||||
//+------------------------------------------------------------------+
|
||||
#include <Zmq/Zmq.mqh>
|
||||
input int InpNumberWorkers=5;
|
||||
//+------------------------------------------------------------------+
|
||||
//| Custom routing Router to Mama (ROUTER to REQ) (adapted from C++) |
|
||||
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnStart()
|
||||
{
|
||||
Context context("rtreq");
|
||||
Socket broker(context,ZMQ_ROUTER);
|
||||
broker.bind("inproc://rtreq");
|
||||
|
||||
string identities[];
|
||||
ArrayResize(identities,InpNumberWorkers);
|
||||
// Wait until InpNumberWorkers workers connected
|
||||
for(int i=0; i<InpNumberWorkers; i++)
|
||||
{
|
||||
ZmqMsg msg;
|
||||
broker.recv(msg);
|
||||
string identity=msg.getData();
|
||||
broker.recv(msg); // Envelope delimiter
|
||||
broker.recv(msg); // Response from worker
|
||||
identities[i]=identity;
|
||||
Print("Broker: Worker ",identity," connected.");
|
||||
}
|
||||
Print("Broker: All workers connected!");
|
||||
// Notify all workers that it is ready to dispatch work
|
||||
for(int i=0; i<InpNumberWorkers; i++)
|
||||
{
|
||||
broker.sendMore(identities[i]);
|
||||
broker.sendMore();
|
||||
broker.send("Go!");
|
||||
}
|
||||
|
||||
// Run for five seconds and then tell workers to end
|
||||
long endTime=TimeLocal()+5;
|
||||
int workersFired=0;
|
||||
while(!IsStopped())
|
||||
{
|
||||
ZmqMsg msg;
|
||||
// Next message gives us least recently used worker
|
||||
broker.recv(msg);
|
||||
string identity=msg.getData();
|
||||
Print("Broker: Get available worker [",identity,"]");
|
||||
broker.recv(msg); // Envelope delimiter
|
||||
broker.recv(msg); // Response from worker
|
||||
Print("Broker: And he says ",msg.getData());
|
||||
|
||||
if(!broker.sendMore(identity)) {Print("Error sending identity.");}
|
||||
if(!broker.sendMore("")) {Print("Error sending delimeter.");}
|
||||
// Encourage workers until it's time to fire them
|
||||
if(TimeLocal()<endTime)
|
||||
{
|
||||
Print("Send work!");
|
||||
if(!broker.send("Work harder")) {Print("Error sending work.");}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Send fire!");
|
||||
if(!broker.send("Fired!")) {Print("Error sending fired.");}
|
||||
if(++workersFired==InpNumberWorkers)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,72 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RTReqWorker.mq4 |
|
||||
//| Copyright 2017, Li Ding |
|
||||
//| dingmaotu@126.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2017, Li Ding"
|
||||
#property link "dingmaotu@126.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
#include <Zmq/Zmq.mqh>
|
||||
#property show_inputs
|
||||
//+------------------------------------------------------------------+
|
||||
//| This example comes from the "Load Balancing Pattern" |
|
||||
//| The original example creates threads for workers and spawn them |
|
||||
//| in the broker main thread. MetaTrader Terminal does not support |
|
||||
//| thread creation. So we split the broker and worker code to two |
|
||||
//| scripts. Since we splitted the code, we need to wait all workers |
|
||||
//| connect. |
|
||||
//| This is the worker part. |
|
||||
//| For the worker, we can use either ZMQ_REQ or ZMQ_DEALER, the |
|
||||
//| difference is minimal. When using a dealer socket, remember to |
|
||||
//| send an empty frame to emulate the REQ behavior. |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#define within(num) (int) ((float) num * MathRand() / (32767 + 1.0))
|
||||
|
||||
input string InpWorkerIdentity="worker1";
|
||||
//+------------------------------------------------------------------+
|
||||
//| Custom routing Router to Mama (ROUTER to REQ) (adapted from C++) |
|
||||
//| The worker |
|
||||
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnStart()
|
||||
{
|
||||
//--- use inproc
|
||||
Context context("rtreq");
|
||||
Socket worker(context,ZMQ_REQ);
|
||||
//--- We use a string identity for ease here
|
||||
worker.setIdentity(InpWorkerIdentity);
|
||||
worker.connect("inproc://rtreq");
|
||||
|
||||
ZmqMsg msg("Connect!");
|
||||
worker.send(msg);
|
||||
worker.recv(msg);
|
||||
Print(InpWorkerIdentity," connect: ",msg.getData());
|
||||
|
||||
int total=0;
|
||||
while(!IsStopped())
|
||||
{
|
||||
// Tell the broker we're ready for work
|
||||
worker.send("Hi Boss");
|
||||
|
||||
// Get workload from broker, until finished
|
||||
worker.recv(msg);
|
||||
string workload=msg.getData();
|
||||
if("Fired!"==workload)
|
||||
{
|
||||
Print(InpWorkerIdentity," is fired!");
|
||||
Print(InpWorkerIdentity," processed: ",total," tasks");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print(InpWorkerIdentity," received work!");
|
||||
}
|
||||
total++;
|
||||
|
||||
// Do some random work
|
||||
Sleep(within(500)+1);
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user