28 Commits

Author SHA1 Message Date
Ding Li 802c86c9e3 Merge pull request #66 from biohazardxxx/patch-1
Update GlobalVariable.mqh
2023-12-19 10:40:40 +08:00
biohazardxxx 67aac378f5 Update GlobalVariable.mqh
When entering we delete any existing global variable with that name first. Otherwise this will cause a loop as GlobalVariable::makeTemp(m_name) will return false all the time.
2023-12-11 11:14:37 +01:00
Ding Li f9bf8d94a3 Fix more typo 2020-06-25 14:07:24 +08:00
Ding Li c8f16bf27d fix typo 2020-06-23 00:16:51 +08:00
lid 981394b9e9 fix #47: MQL5 update makes calling static methods of template type parameters impossible 2020-05-15 01:54:01 +08:00
lid 2dac5b433d fix #40: avoid name conflict by changing parameter name data to bytes 2019-09-29 09:35:27 +08:00
lid b3f817eda1 Add ToC and Donation section 2019-05-08 21:55:36 +08:00
lid 081500e0f9 Fix #14: PollItem aligns to 8 types on 64bit runtime 2019-05-08 21:05:11 +08:00
Ding Li 4468c27d30 Synchronize mql-zmq changes on Lang/Native; add MqlTick and MqlRates support for ZmqMsg 2018-02-12 09:33:43 +08:00
Ding Li df54353865 Add proper const modifier to import functions 2018-01-05 11:56:23 +08:00
Ding Li 66602232d6 Updated dependency mql4-lib; improved ZmqMsg setData method 2017-12-26 16:08:23 +08:00
Ding Li 5738f693d1 Add new DLLs compiled with VC2010 2017-12-18 10:04:08 +08:00
Ding Li afab5f93cc Released 1.5
This release contains important API changes that may break existing
code.

1. Incompatible change: `Socket.send` series methods are splitted
to `Socket.send` and `Socket.sendMore` and have more overloads for
common message types like string or empty
2. Remove PollItem duplicate API (#11)
3. Fix compiler warning (#10) and compile failure (#12)
4. Add RTReq example from ZMQ Guide Chapter 3
2017-10-28 22:45:23 +08:00
Ding Li 795acc13c8 Fix #12 (#1, #4, #9): SocketOptions curve key argument causes compile error 2017-10-23 22:13:23 +08:00
Ding Li 184b9f6d8b Merge branch 'master' of github.com:dingmaotu/mql-zmq 2017-10-23 10:22:36 +08:00
Ding Li 96fd26ad85 Fix #11: remove duplicate and deprecated API Socket::register 2017-10-23 10:21:36 +08:00
Ding Li 1d6b1aa2d5 Fix #11: remove duplicate and deprecated API Socket::register 2017-10-23 09:50:25 +08:00
Ding Li 07414d6300 Fix #10: fix size parameter type on 64bit Terminal 2017-10-17 09:29:06 +08:00
Ding Li 8475ecc03a Fix #8 by importing changes from mql4-lib (eliminating WinUser32.mqh dependency) 2017-09-13 11:52:21 +08:00
Ding Li 1d127c66b7 Merge pull request #7 from yerden/patch-1
zmq: fix typo in SendTimeout method
2017-09-08 08:31:13 +08:00
yerden 101807d69e zmq: fix typo in SendTimeout method 2017-09-08 00:27:47 +06:00
Ding Li cd7e5f0e47 Only resize target array when the array size is smaller than ZmqMsg 2017-08-30 10:15:46 +08:00
Ding Li bab1c9753a Fix documentation reference to a mql4-lib file 2017-08-18 10:34:30 +08:00
Ding Li 61861aa68e Documentation update for release 1.4 2017-08-18 10:28:43 +08:00
Ding Li acd272031a Add mql4-lib dependencies directly; change header comments on license; fix ZmqMsg setData method bug 2017-08-18 10:13:15 +08:00
Ding Li e51eceb263 Change licence to Apache 2.0 2017-08-18 10:03:00 +08:00
Ding Li ca488ea853 Fix #6 make vc runtime requirement stand out 2017-08-16 14:06:42 +08:00
Ding Li 4adca2d960 Add notes on using this library on MetaTrader5 32 bit version 2017-08-13 10:36:00 +08:00
25 changed files with 1299 additions and 402 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.
+347
View File
@@ -0,0 +1,347 @@
//+------------------------------------------------------------------+
//| Module: Lang/GlobalVariable.mqh |
//| This file is part of the mql4-lib project: |
//| https://github.com/dingmaotu/mql4-lib |
//| |
//| Copyright 2017 Li Ding <dingmaotu@126.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
//+------------------------------------------------------------------+
//| Encapsulates global variable functions |
//+------------------------------------------------------------------+
class GlobalVariable
{
public:
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 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 (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 |
//+------------------------------------------------------------------+
class TempVar
{
private:
string m_name;
bool m_owned;
public:
TempVar(string name,bool create=false):m_name(name),m_owned(create)
{
if(create)
{
GlobalVariable::makeTemp(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);
}
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class AtomicVar: public TempVar
{
public:
AtomicVar(string name,long initial,bool create=false):TempVar(name,create)
{
set(initial);
}
long increment(long by=1);
long decrement(long by=1)
{
return increment(-by);
}
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
long AtomicVar::increment(long by)
{
bool success=false;
long value;
do
{
value=(long)get();
success=setOn(value+by,value);
}
while(!success && !IsStopped());
return (value+by);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class Semaphore
{
private:
TempVar m_var;
public:
Semaphore(string name,long initial=0);
bool isValid() const
{
return m_var.isValid();
}
bool acquire();
void release();
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Semaphore::Semaphore(string name,long initial)
:m_var(name,initial!=0)
{
if(initial!=0)
{
m_var.set(initial);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool Semaphore::acquire(void)
{
bool success=false;
do
{
long value=(long)m_var.get();
if(value == 0)
return false;
success=m_var.setOn(value-1,value);
}
while(!success && !IsStopped());
return success;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Semaphore::release(void)
{
bool success=false;
do
{
long value=(long)m_var.get();
success=m_var.setOn(value+1,value);
}
while(!success && !IsStopped());
}
//+------------------------------------------------------------------+
//| CriticalSection object for making atomic operations |
//| |
//| An exmaple of creating a global context (the creation and destroy|
//| are both enclosed between the SAME critical section): |
//| |
//| enter() |
//| if(refcount==0) create context |
//| else refcontext |
//| increase refcount |
//| leave() |
//| |
//| enter() |
//| decrease refcount |
//| if(refcount==0) context destroy |
//| leave() |
//+------------------------------------------------------------------+
class CriticalSection
{
private:
const string m_name;
public:
CriticalSection(string name):m_name(name) {}
bool isValid() const
{
return m_name!=NULL;
}
string getName() const
{
return 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) |
//+------------------------------------------------------------------+
template<typename T,typename HM>
class GlobalHandle
{
private:
CriticalSection m_cs;
string m_refName;
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";
m_hm = new HM;
if(!m_cs.isValid())
m_ref = m_hm.create();
else
{
m_cs.enter();
if(!GlobalVariable::exists(m_counterName))
{
GlobalVariable::makeTemp(m_counterName);
GlobalVariable::set(m_counterName,0);
}
if(long(GlobalVariable::get(m_counterName))==0)
{
m_ref = m_hm.create();
if(!GlobalVariable::exists(m_refName))
{
GlobalVariable::makeTemp(m_refName);
GlobalVariable::set(m_refName,m_ref);
}
}
else
{
m_ref=(T)(GlobalVariable::get(m_refName));
}
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)+1);
m_cs.leave();
}
}
~GlobalHandle()
{
if(!m_cs.isValid())
{
m_hm.destroy(m_ref);
}
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;
}
};
//+------------------------------------------------------------------+
+148
View File
@@ -0,0 +1,148 @@
//+------------------------------------------------------------------+
//| Module: Lang/Mql.mqh |
//| This file is part of the mql4-lib project: |
//| https://github.com/dingmaotu/mql4-lib |
//| |
//| Copyright 2017 Li Ding <dingmaotu@126.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
#include "Error.mqh"
//+------------------------------------------------------------------+
//| Mql language specific methods |
//+------------------------------------------------------------------+
class Mql
{
public:
static int getLastError() {return GetLastError();}
static string getErrorMessage(int errorCode) {return GetErrorDescription(errorCode);}
//--- 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));}
//--- Prefer Lang/Number.mqh Double::IsEqual
static bool isEqual(double a,double b) {return NormalizeDouble(a-b,8)==0;}
static bool isStopped() {return IsStopped();}
static int getCodePage() {return MQLInfoInteger(MQL_CODEPAGE);}
static ENUM_PROGRAM_TYPE getProgramType() {return(ENUM_PROGRAM_TYPE)MQLInfoInteger(MQL_PROGRAM_TYPE);}
static bool isScript() {return Mql::getProgramType()==PROGRAM_SCRIPT;}
static bool isExpert() {return Mql::getProgramType()==PROGRAM_EXPERT;}
static bool isIndicator() {return Mql::getProgramType()==PROGRAM_INDICATOR;}
static bool isDllAllowed() {return MQLInfoInteger(MQL_DLLS_ALLOWED)!=0;}
static bool isTradeAllowed() {return MQLInfoInteger(MQL_TRADE_ALLOWED)!=0;}
static bool isSignalsAllowed() {return MQLInfoInteger(MQL_SIGNALS_ALLOWED)!=0;}
static bool isDebug() {return MQLInfoInteger(MQL_DEBUG)!=0;}
static bool isProfiling() {return MQLInfoInteger(MQL_PROFILER)!=0;}
static bool isTesting() {return MQLInfoInteger(MQL_TESTER)!=0;}
static bool isOptimizing() {return MQLInfoInteger(MQL_OPTIMIZATION)!=0;}
static bool isVisual() {return MQLInfoInteger(MQL_VISUAL_MODE)!=0;}
static bool isFrameMode() {return MQLInfoInteger(MQL_FRAME_MODE)!=0;}
static ENUM_LICENSE_TYPE getLicenseType() {return(ENUM_LICENSE_TYPE)MQLInfoInteger(MQL_LICENSE_TYPE);}
static bool isFreeLicense() {return Mql::getLicenseType()==LICENSE_FREE;}
static bool isDemoLicense() {return Mql::getLicenseType()==LICENSE_DEMO;}
static bool isFullLicense() {return Mql::getLicenseType()==LICENSE_FULL;}
static bool isTimeLicense() {return Mql::getLicenseType()==LICENSE_TIME;}
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;}\
void set##Public(Type value) {m_##Private=value;}\
private:\
Type m_##Private\
#define ObjectAttrRead(Type, Private, Public) \
public:\
Type get##Public() const {return m_##Private;}\
private:\
Type m_##Private\
#define ObjectAttrWrite(Type, Private, Public) \
public:\
void set##Public(Type value) {m_##Private=value;}\
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) PrintFormat(">>> DEBUG[%s,%d,%s]: %s",__FILE__,__LINE__,__FUNCTION__,msg);
#else
#define Debug(msg)
#endif
//+------------------------------------------------------------------+
//| Execute some code in the global scope |
//+------------------------------------------------------------------+
#define BEGIN_EXECUTE(Name) class __Execute##Name\
{\
public:__Execute##Name()\
{
#define END_EXECUTE(Name) \
}\
}\
__execute##Name;
//+------------------------------------------------------------------+
+177
View File
@@ -0,0 +1,177 @@
//+------------------------------------------------------------------+
//| Module: Lang/Native.mqh |
//| This file is part of the mql4-lib project: |
//| https://github.com/dingmaotu/mql4-lib |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@126.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
// Assume MT5 is 64bit, which is the default.
// Even though MT5 can be 32bit, there is no way to detect this
// by using preprocessor macros. Instead, MetaQuotes provides a
// function called IsX64 to detect this dynamically
// This is just absurd. Why do you want to know the bitness of
// the runtime? To define pointer related entities at compile time!
// All integer types in MQL is uniform on both 32bit or 64bit
// architectures, so it is almost useless to have a runtime function IsX64.
// Why not a __X64__?
#ifdef __MQL5__
#define __X64__
#endif
#ifdef __X64__
#define intptr_t long
#define uintptr_t ulong
#define size_t long
#else
#define intptr_t int
#define uintptr_t uint
#define size_t int
#endif
#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);
uintptr_t lstrcpynW(string &s1,uintptr_t s2,int length);
uintptr_t lstrcpynW(uintptr_t s1,string &s2,int length);
int MultiByteToWideChar(uint codePage,
uint flags,
const intptr_t multiByteString,
int lengthMultiByte,
string &str,
int length
);
#import
//+------------------------------------------------------------------+
//| 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 |
//+------------------------------------------------------------------+
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*sizeof(T)));
}
//+------------------------------------------------------------------+
//| Copy array to the memory pointed by dest |
//| Need to import RtlMoveMemory with appropriate parameter type T |
//+------------------------------------------------------------------+
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*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* |
//+------------------------------------------------------------------+
intptr_t DereferencePointer(intptr_t pointer)
{
intptr_t res=0;
RtlMoveMemory(res,pointer,sizeof(intptr_t));
return res;
}
//+------------------------------------------------------------------+
//| Read a valid wide character string to the MQL environment |
//+------------------------------------------------------------------+
string StringFromPointer(intptr_t psz,int len=0)
{
if(len < 0) return NULL;
if(len==0) {len=lstrlenW(psz);}
string res;
StringInit(res,len+1);
lstrcpynW(res,psz,len+1);
return res;
}
//+------------------------------------------------------------------+
//| Get the pointer address of a string |
//+------------------------------------------------------------------+
uintptr_t StringToPointer(string &s)
{
return lstrcpynW(s,0,0);
}
//+------------------------------------------------------------------+
//| Read a valid utf-8 string to the MQL environment |
//| With this function, there is no need to copy the string to char |
//| array, and convert with CharArrayToString |
//+------------------------------------------------------------------+
string StringFromUtf8Pointer(intptr_t psz,int len)
{
if(len < 0) return NULL;
string res;
int required=MultiByteToWideChar(CP_UTF8,0,psz,len,res,0);
// 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 |
//+------------------------------------------------------------------+
string StringFromUtf8Pointer(intptr_t psz)
{
if(psz==0) return NULL;
int len=lstrlen(psz);
if(len==0) return NULL;
return StringFromUtf8Pointer(psz, len);
}
//+------------------------------------------------------------------+
//| Convert a utf-8 byte array to a string |
//+------------------------------------------------------------------+
string StringFromUtf8(const uchar &utf8[])
{
return CharArrayToString(utf8, 0, -1, CP_UTF8);
}
//+------------------------------------------------------------------+
//| Convert a string to a utf-8 byte array |
//+------------------------------------------------------------------+
void StringToUtf8(const string str,uchar &utf8[],bool ending=true)
{
if(!ending && str=="") return;
int count=ending ? -1 : StringLen(str);
StringToCharArray(str,utf8,0,count,CP_UTF8);
}
//+------------------------------------------------------------------+
+19 -4
View File
@@ -1,11 +1,26 @@
//+------------------------------------------------------------------+
//| AtomicCounter.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: AtomicCounter.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
#include "Common.mqh"
#include <Mql/Lang/Native.mqh>
#import "libzmq.dll"
intptr_t zmq_atomic_counter_new(void);
-117
View File
@@ -1,117 +0,0 @@
//+------------------------------------------------------------------+
//| Common.mqh |
//| This file is part of mql4-lib project (Lang/Native.mqh): |
//| (github.com/dingmaotu/mql4-lib) |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//+------------------------------------------------------------------+
#property strict
#include "Errno.mqh"
// Assume MT5 is 64bit, which is the default.
// Even though MT5 can be 32bit, there is no way to detect this
// by using preprocessor macros. Instead, MetaQuotes provides a
// function called IsX64 to detect this dynamically
// This is just absurd. Why do you want to know the bitness of
// the runtime? To define pointer related entities at compile time!
// All integer types in MQL is uniform on both 32bit or 64bit
// architectures, so it is almost useless to have a runtime function IsX64.
// Why not a __X64__?
#ifdef __MQL5__
#define __X64__
#endif
#ifdef __X64__
#define intptr_t long
#define uintptr_t ulong
#define size_t long
#else
#define intptr_t int
#define uintptr_t uint
#define size_t int
#endif
#import "kernel32.dll"
void RtlMoveMemory(intptr_t dest,const uchar &array[],size_t length);
void RtlMoveMemory(uchar &array[],intptr_t src,size_t length);
int lstrlen(intptr_t psz);
int MultiByteToWideChar(uint codePage,
uint flags,
const intptr_t multiByteString,
int lengthMultiByte,
string &str,
int length
);
#import
//+------------------------------------------------------------------+
//| Copy the memory contents pointed by src to array |
//| array parameter should be initialized to the desired size |
//+------------------------------------------------------------------+
void ArrayFromPointer(uchar &array[],intptr_t src,int count=WHOLE_ARRAY)
{
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
RtlMoveMemory(array,src,(size_t)size);
}
//+------------------------------------------------------------------+
//| Copy array to the memory pointed by dest |
//+------------------------------------------------------------------+
void ArrayToPointer(const uchar &array[],intptr_t dest,int count=WHOLE_ARRAY)
{
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
RtlMoveMemory(dest,array,(size_t)size);
}
//+------------------------------------------------------------------+
//| Read a valid utf-8 string to the MQL environment |
//| With this function, there is no need to copy the string to char |
//| array, and convert with CharArrayToString |
//+------------------------------------------------------------------+
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;
}
}
//+------------------------------------------------------------------+
//| for null-terminated string |
//+------------------------------------------------------------------+
string StringFromUtf8Pointer(intptr_t psz)
{
int len=lstrlen(psz);
return StringFromUtf8Pointer(psz, len);
}
//+------------------------------------------------------------------+
//| Convert a utf-8 byte array to a string |
//+------------------------------------------------------------------+
string StringFromUtf8(const uchar &utf8[])
{
return CharArrayToString(utf8, 0, -1, CP_UTF8);
}
//+------------------------------------------------------------------+
//| Convert a string to a utf-8 byte array |
//+------------------------------------------------------------------+
void StringToUtf8(const string str,uchar &utf8[],bool ending=true)
{
if(!ending && str=="") return;
int count=ending ? -1 : StringLen(str);
StringToCharArray(str,utf8,0,count,CP_UTF8);
}
#ifdef _DEBUG
#define Debug(msg) Print(">>> DEBUG: In ",__FUNCTION__,"(",__FILE__,":",__LINE__,") [", msg, "]")
#else
#define Debug(msg)
#endif
//+------------------------------------------------------------------+
+43 -13
View File
@@ -1,12 +1,28 @@
//+------------------------------------------------------------------+
//| Context.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: Context.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#include "Common.mqh"
#property strict
#include <Mql/Lang/Mql.mqh>
#include <Mql/Lang/Native.mqh>
#include <Mql/Lang/GlobalVariable.mqh>
#include "SocketOptions.mqh"
#include "GlobalHandle.mqh"
//--- Context options
#define ZMQ_IO_THREADS 1
@@ -29,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 |
//| |
@@ -48,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);}
@@ -59,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*/}
+18 -1
View File
@@ -1,5 +1,22 @@
//+------------------------------------------------------------------+
//| Errno.mqh |
//| Module: Errno.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
// Following error codes come from Microsoft CRT header errno.h
-122
View File
@@ -1,122 +0,0 @@
//+------------------------------------------------------------------+
//| GlobalHandle.mqh |
//| This file is part of mql4-lib project (Lang/GlobalVariable.mqh): |
//| (github.com/dingmaotu/mql4-lib) |
//| Copyright 2017, Li Ding |
//| dingmaotu@hotmail.com |
//+------------------------------------------------------------------+
#property strict
//+------------------------------------------------------------------+
//| Wraps global variable functions |
//+------------------------------------------------------------------+
class GlobalVariable
{
public:
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 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 remove(string name) {return GlobalVariableDel(name);}
static bool removeAll(string prefix=NULL,datetime before=0) {return GlobalVariablesDeleteAll(prefix,before);}
};
//+------------------------------------------------------------------+
//| CriticalSection object for making atomic operations |
//| |
//| An exmaple of creating a global context (the creation and destroy|
//| are both enclosed between the SAME critical section): |
//| |
//| enter() |
//| if(refcount==0) create context |
//| else refcontext |
//| increase refcount |
//| leave() |
//| |
//| enter() |
//| decrease refcount |
//| if(refcount==0) context destroy |
//| leave() |
//+------------------------------------------------------------------+
class CriticalSection
{
private:
const string m_name;
public:
CriticalSection(string name):m_name(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);}
};
//+------------------------------------------------------------------+
//| A reference counted global pointer (or handle) |
//| HandleManager should implement 2 static methods: create & destroy|
//+------------------------------------------------------------------+
template<typename T,typename HandleManager>
class GlobalHandle
{
private:
CriticalSection m_cs;
string m_refName;
string m_counterName;
protected:
T m_ref;
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();
else
{
m_cs.enter();
if(!GlobalVariable::exists(m_counterName))
{
GlobalVariable::makeTemp(m_counterName);
GlobalVariable::set(m_counterName,0);
}
if(long(GlobalVariable::get(m_counterName))==0)
{
m_ref=HandleManager::create();
if(!GlobalVariable::exists(m_refName))
{
GlobalVariable::makeTemp(m_refName);
GlobalVariable::set(m_refName,m_ref);
}
}
else
{
m_ref=(T)(GlobalVariable::get(m_refName));
}
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)+1);
m_cs.leave();
}
}
~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)
{
HandleManager::destroy(m_ref);
GlobalVariable::remove(m_refName);
GlobalVariable::remove(m_counterName);
}
m_cs.leave();
}
T ref() const {return m_ref;}
};
//+------------------------------------------------------------------+
+50 -80
View File
@@ -1,11 +1,27 @@
//+------------------------------------------------------------------+
//| Socket.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: Socket.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
#include "Common.mqh"
#include <Mql/Lang/Mql.mqh>
#include <Mql/Lang/Native.mqh>
#include "Context.mqh"
#include "SocketOptions.mqh"
#include "ZmqMsg.mqh"
@@ -63,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;}
@@ -117,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);
@@ -188,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[];
@@ -247,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;
+25 -9
View File
@@ -1,13 +1,26 @@
//+------------------------------------------------------------------+
//| Socket.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: SocketOptions.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Li Ding"
#property link "dingmaotu@hotmail.com"
#property strict
#include "Common.mqh"
#include <Mql/Lang/Native.mqh>
#import "libzmq.dll"
// We can overload the same function for different data types
@@ -171,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)
@@ -238,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)
+19 -6
View File
@@ -1,13 +1,26 @@
//+------------------------------------------------------------------+
//| Z85.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: Z85.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Li Ding"
#property link "dingmaotu@hotmail.com"
#property strict
#include "Common.mqh"
#include <Mql/Lang/Native.mqh>
#import "libzmq.dll"
// Encode data with Z85 encoding. Returns 0(NULL) if failed
Binary file not shown.
+28 -13
View File
@@ -1,12 +1,26 @@
//+------------------------------------------------------------------+
//| ZmqMsg.mqh |
//| Copyright 2016, Li Ding |
//| dingmaotu@hotmail.com |
//| Module: ZmqMsg.mqh |
//| This file is part of the mql-zmq project: |
//| https://github.com/dingmaotu/mql-zmq |
//| |
//| Copyright 2016-2017 Li Ding <dingmaotu@hotmail.com> |
//| |
//| Licensed under the Apache License, Version 2.0 (the "License"); |
//| you may not use this file except in compliance with the License. |
//| You may obtain a copy of the License at |
//| |
//| http://www.apache.org/licenses/LICENSE-2.0 |
//| |
//| Unless required by applicable law or agreed to in writing, |
//| software distributed under the License is distributed on an |
//| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
//| either express or implied. |
//| See the License for the specific language governing permissions |
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Li Ding"
#property link "dingmaotu@hotmail.com"
#property strict
#include "Common.mqh"
#include <Mql/Lang/Mql.mqh>
#include <Mql/Lang/Native.mqh>
//+------------------------------------------------------------------+
//| 0MQ Message struct |
//+------------------------------------------------------------------+
@@ -74,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);}
@@ -99,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 |
@@ -118,10 +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();
ArrayToPointer(data,dest);
size_t size=size();
ArrayToPointer(bytes,dest,(int)size);
}
//+------------------------------------------------------------------+
//| Wraps zmq_msg_gets: get metadata associated with the msg |
+198 -17
View File
@@ -1,21 +1,202 @@
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2016 Ding Li
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+64 -14
View File
@@ -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
@@ -21,26 +30,52 @@ runtime environment (MetaTrader5 is 64bit by default, while MetaTrader4 is
32bit). The trading system is also different, but it is no concern of this
binding.
## Files
## Files and Installation
This binding contains three sets of files:
1. The binding itself is in the `Include/Zmq` directory.
1. The binding itself is in the `Include/Zmq` directory. *Note* that there is a
`Mql` directory in `Include`, which is part of
the [mql4-lib](https://github.com/dingmaotu/mql4-lib). Previous `Common.mqh`
and `GlobalHandle.mqh` are actually from this library. At release 1.4, this
becomes a direct reference, with mql4-lib content copied here verbatim. It is
recommended you install the full mql4-lib, as it contains a lot other
features. But for those who want to use mql-zmq alone, it is OK to deploy
only the small subset included here.
2. The testing scripts and zmq guide examples are in `Scripts` directory. The
script files are mq4 by default, but you can change the extension to mq5 to
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). *Note* that these DLLs are compiled from
official sources, without any modification. You can compile your own if you
don't trust these binaries. The `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.
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/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.
*Note* that these DLLs are compiled from official sources, without any
modification. You can compile your own if you don't trust these binaries. The
`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
@@ -115,12 +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-07-18: Released 1.3: Refactored poll support. Add Chapter 2 examples from
* 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.
* 2017-06-08: Released 1.2: Fix GlobalHandle bug; Add rebuild method to ZmqMsg;
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!
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RL6U2HFVTCUUN)
![alipay](ALIPAY.JPG)
@@ -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);
}
}
//+------------------------------------------------------------------+