mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-07-27 18:47:55 +00:00
Issue #32: Added function ObjectCreate in MtApi (MT4)
This commit is contained in:
@@ -83,7 +83,7 @@
|
|||||||
<Compile Include="MtResponse.cs" />
|
<Compile Include="MtResponse.cs" />
|
||||||
<Compile Include="MtServer.cs" />
|
<Compile Include="MtServer.cs" />
|
||||||
<Compile Include="MtCommand.cs" />
|
<Compile Include="MtCommand.cs" />
|
||||||
<Compile Include="MtServerInstance.cs" />
|
<Compile Include="MtAdapter.cs" />
|
||||||
<Compile Include="MtService.cs" />
|
<Compile Include="MtService.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Executable → Regular
+50
-6
@@ -4,13 +4,13 @@ using log4net;
|
|||||||
|
|
||||||
namespace MTApiService
|
namespace MTApiService
|
||||||
{
|
{
|
||||||
public class MtServerInstance
|
public class MtAdapter
|
||||||
{
|
{
|
||||||
#region Fields
|
#region Fields
|
||||||
private const string LogProfileName = "MtApiService";
|
private const string LogProfileName = "MtApiService";
|
||||||
|
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(MtServerInstance));
|
private static readonly ILog Log = LogManager.GetLogger(typeof(MtAdapter));
|
||||||
private static readonly MtServerInstance Instance = new MtServerInstance();
|
private static readonly MtAdapter Instance = new MtAdapter();
|
||||||
|
|
||||||
private readonly Dictionary<int, MtServer> _servers = new Dictionary<int, MtServer>();
|
private readonly Dictionary<int, MtServer> _servers = new Dictionary<int, MtServer>();
|
||||||
private readonly Dictionary<int, MtExpert> _experts = new Dictionary<int, MtExpert>();
|
private readonly Dictionary<int, MtExpert> _experts = new Dictionary<int, MtExpert>();
|
||||||
@@ -18,16 +18,16 @@ namespace MTApiService
|
|||||||
|
|
||||||
#region Init Instance
|
#region Init Instance
|
||||||
|
|
||||||
private MtServerInstance()
|
private MtAdapter()
|
||||||
{
|
{
|
||||||
LogConfigurator.Setup(LogProfileName);
|
LogConfigurator.Setup(LogProfileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static MtServerInstance()
|
static MtAdapter()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MtServerInstance GetInstance()
|
public static MtAdapter GetInstance()
|
||||||
{
|
{
|
||||||
return Instance;
|
return Instance;
|
||||||
}
|
}
|
||||||
@@ -208,6 +208,50 @@ namespace MTApiService
|
|||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object GetNamedParameter(int expertHandle, string name)
|
||||||
|
{
|
||||||
|
Log.DebugFormat("GetNamedParameter: begin. expertHandle = {0}, name = {1}", expertHandle, name);
|
||||||
|
|
||||||
|
MtExpert expert;
|
||||||
|
lock (_experts)
|
||||||
|
{
|
||||||
|
expert = _experts[expertHandle];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expert == null)
|
||||||
|
{
|
||||||
|
Log.WarnFormat("GetNamedParameter: expert with id {0} has not been found.", expertHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
var retval = expert?.GetNamedParameter(name);
|
||||||
|
|
||||||
|
Log.DebugFormat("GetNamedParameter: end. retval = {0}", retval);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsNamedParameter(int expertHandle, string name)
|
||||||
|
{
|
||||||
|
Log.DebugFormat("ContainsNamedParameter: begin. expertHandle = {0}, name = {1}", expertHandle, name);
|
||||||
|
|
||||||
|
MtExpert expert;
|
||||||
|
lock (_experts)
|
||||||
|
{
|
||||||
|
expert = _experts[expertHandle];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expert == null)
|
||||||
|
{
|
||||||
|
Log.WarnFormat("ContainsNamedParameter: expert with id {0} has not been found.", expertHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool retval = expert != null ? expert.ContainsNamedParameter(name) : false;
|
||||||
|
|
||||||
|
Log.DebugFormat("ContainsNamedParameter: end. retval = {0}", retval);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
@@ -139,7 +139,7 @@ namespace MTApiService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="CommunicationException">Thrown when connection failed</exception>
|
/// <exception cref="CommunicationException">Thrown when connection failed</exception>
|
||||||
public MtResponse SendCommand(int commandType, ArrayList parameters, int expertHandle)
|
public MtResponse SendCommand(int commandType, ArrayList parameters, Dictionary<string, object> namedParams, int expertHandle)
|
||||||
{
|
{
|
||||||
Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count);
|
Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count);
|
||||||
|
|
||||||
@@ -153,7 +153,11 @@ namespace MTApiService
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = _proxy.SendCommand(new MtCommand { CommandType = commandType, Parameters = parameters, ExpertHandle = expertHandle});
|
result = _proxy.SendCommand(new MtCommand {
|
||||||
|
CommandType = commandType,
|
||||||
|
Parameters = parameters,
|
||||||
|
NamedParams = namedParams,
|
||||||
|
ExpertHandle = expertHandle});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MTApiService
|
namespace MTApiService
|
||||||
{
|
{
|
||||||
@@ -12,6 +13,9 @@ namespace MTApiService
|
|||||||
[DataMember]
|
[DataMember]
|
||||||
public ArrayList Parameters { get; set; }
|
public ArrayList Parameters { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public Dictionary<string, object> NamedParams { get; set; }
|
||||||
|
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public int ExpertHandle { get; set; }
|
public int ExpertHandle { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace MTApiService
|
|||||||
|
|
||||||
public object GetCommandParameter(int index)
|
public object GetCommandParameter(int index)
|
||||||
{
|
{
|
||||||
Log.DebugFormat("GetCommandType: called. index = {0}", index);
|
Log.DebugFormat("GetCommandParameter: called. index = {0}", index);
|
||||||
|
|
||||||
var command = _currentTask?.Command;
|
var command = _currentTask?.Command;
|
||||||
if (command?.Parameters != null && index >= 0 && index < command.Parameters.Count)
|
if (command?.Parameters != null && index >= 0 && index < command.Parameters.Count)
|
||||||
@@ -71,6 +71,46 @@ namespace MTApiService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object GetNamedParameter(string name)
|
||||||
|
{
|
||||||
|
Log.DebugFormat("GetNamedParameter: called. name = {0}", name);
|
||||||
|
|
||||||
|
var command = _currentTask?.Command;
|
||||||
|
if (command == null)
|
||||||
|
{
|
||||||
|
Log.Warn("GetNamedParameter: command is not defined in task.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.NamedParams == null)
|
||||||
|
{
|
||||||
|
Log.Warn("GetNamedParameter: NamedParams is not defined in command.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command.NamedParams[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsNamedParameter(string name)
|
||||||
|
{
|
||||||
|
Log.DebugFormat("ContainsNamedParameter: called. name = {0}", name);
|
||||||
|
|
||||||
|
var command = _currentTask?.Command;
|
||||||
|
if (command == null)
|
||||||
|
{
|
||||||
|
Log.Warn("ContainsNamedParameter: command is not defined in task.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.NamedParams == null)
|
||||||
|
{
|
||||||
|
Log.Warn("ContainsNamedParameter: NamedParams is not defined in command.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command.NamedParams.ContainsKey(name);
|
||||||
|
}
|
||||||
|
|
||||||
public void SendEvent(MtEvent mtEvent)
|
public void SendEvent(MtEvent mtEvent)
|
||||||
{
|
{
|
||||||
Log.DebugFormat("SendEvent: begin. event = {0}", mtEvent);
|
Log.DebugFormat("SendEvent: begin. event = {0}", mtEvent);
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.0.24.0")]
|
[assembly: AssemblyVersion("1.0.25.0")]
|
||||||
[assembly: AssemblyFileVersion("1.0.24.0")]
|
[assembly: AssemblyFileVersion("1.0.25.0")]
|
||||||
|
|||||||
+77
-19
@@ -45,7 +45,7 @@ _DLLAPI bool _stdcall initExpert(int expertHandle, int port, wchar_t* symbol, do
|
|||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, &port, symbol, &bid, &ask]() {
|
return Execute<bool>([&expertHandle, &port, symbol, &bid, &ask]() {
|
||||||
MT4Handler^ mtHandler = gcnew MT4Handler();
|
MT4Handler^ mtHandler = gcnew MT4Handler();
|
||||||
MtServerInstance::GetInstance()->InitExpert(expertHandle, port, gcnew String(symbol), bid, ask, mtHandler);
|
MtAdapter::GetInstance()->InitExpert(expertHandle, port, gcnew String(symbol), bid, ask, mtHandler);
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ _DLLAPI bool _stdcall initExpert(int expertHandle, int port, wchar_t* symbol, do
|
|||||||
_DLLAPI bool _stdcall deinitExpert(int expertHandle, wchar_t* err)
|
_DLLAPI bool _stdcall deinitExpert(int expertHandle, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle]() {
|
return Execute<bool>([&expertHandle]() {
|
||||||
MtServerInstance::GetInstance()->DeinitExpert(expertHandle);
|
MtAdapter::GetInstance()->DeinitExpert(expertHandle);
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ _DLLAPI bool _stdcall deinitExpert(int expertHandle, wchar_t* err)
|
|||||||
_DLLAPI bool _stdcall updateQuote(int expertHandle, wchar_t* symbol, double bid, double ask, wchar_t* err)
|
_DLLAPI bool _stdcall updateQuote(int expertHandle, wchar_t* symbol, double bid, double ask, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, symbol, &bid, &ask]() {
|
return Execute<bool>([&expertHandle, symbol, &bid, &ask]() {
|
||||||
MtServerInstance::GetInstance()->SendQuote(expertHandle, gcnew String(symbol), bid, ask);
|
MtAdapter::GetInstance()->SendQuote(expertHandle, gcnew String(symbol), bid, ask);
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ _DLLAPI bool _stdcall updateQuote(int expertHandle, wchar_t* symbol, double bid,
|
|||||||
_DLLAPI bool _stdcall sendEvent(int expertHandle, int eventType, wchar_t* payload, wchar_t* err)
|
_DLLAPI bool _stdcall sendEvent(int expertHandle, int eventType, wchar_t* payload, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, &eventType, payload]() {
|
return Execute<bool>([&expertHandle, &eventType, payload]() {
|
||||||
MtServerInstance::GetInstance()->SendEvent(expertHandle, eventType, gcnew String(payload));
|
MtAdapter::GetInstance()->SendEvent(expertHandle, eventType, gcnew String(payload));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ _DLLAPI bool _stdcall sendEvent(int expertHandle, int eventType, wchar_t* payloa
|
|||||||
_DLLAPI bool _stdcall sendIntResponse(int expertHandle, int response, wchar_t* err)
|
_DLLAPI bool _stdcall sendIntResponse(int expertHandle, int response, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, &response]() {
|
return Execute<bool>([&expertHandle, &response]() {
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseInt(response));
|
MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseInt(response));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ _DLLAPI bool _stdcall sendIntResponse(int expertHandle, int response, wchar_t* e
|
|||||||
_DLLAPI bool _stdcall sendBooleanResponse(int expertHandle, bool response, wchar_t* err)
|
_DLLAPI bool _stdcall sendBooleanResponse(int expertHandle, bool response, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, &response]() {
|
return Execute<bool>([&expertHandle, &response]() {
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseBool(response));
|
MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseBool(response));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ _DLLAPI bool _stdcall sendBooleanResponse(int expertHandle, bool response, wchar
|
|||||||
_DLLAPI bool _stdcall sendDoubleResponse(int expertHandle, double response, wchar_t* err)
|
_DLLAPI bool _stdcall sendDoubleResponse(int expertHandle, double response, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, &response]() {
|
return Execute<bool>([&expertHandle, &response]() {
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseDouble(response));
|
MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseDouble(response));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ _DLLAPI bool _stdcall sendDoubleResponse(int expertHandle, double response, wcha
|
|||||||
_DLLAPI bool _stdcall sendStringResponse(int expertHandle, wchar_t* response, wchar_t* err)
|
_DLLAPI bool _stdcall sendStringResponse(int expertHandle, wchar_t* response, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, response]() {
|
return Execute<bool>([&expertHandle, response]() {
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseString(gcnew String(response)));
|
MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseString(gcnew String(response)));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ _DLLAPI bool _stdcall sendErrorResponse(int expertHandle, int code, wchar_t* mes
|
|||||||
return Execute<bool>([&expertHandle, &code, message]() {
|
return Execute<bool>([&expertHandle, &code, message]() {
|
||||||
MtResponseString^ res = gcnew MtResponseString(gcnew String(message));
|
MtResponseString^ res = gcnew MtResponseString(gcnew String(message));
|
||||||
res->ErrorCode = code;
|
res->ErrorCode = code;
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, res);
|
MtAdapter::GetInstance()->SendResponse(expertHandle, res);
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ _DLLAPI bool _stdcall sendErrorResponse(int expertHandle, int code, wchar_t* mes
|
|||||||
_DLLAPI bool _stdcall sendVoidResponse(int expertHandle, wchar_t* err)
|
_DLLAPI bool _stdcall sendVoidResponse(int expertHandle, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle]() {
|
return Execute<bool>([&expertHandle]() {
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseObject(nullptr));
|
MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseObject(nullptr));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ _DLLAPI bool _stdcall sendDoubleArrayResponse(int expertHandle, double* values,
|
|||||||
{
|
{
|
||||||
list[i] = values[i];
|
list[i] = values[i];
|
||||||
}
|
}
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseDoubleArray(list));
|
MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseDoubleArray(list));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ _DLLAPI bool _stdcall sendIntArrayResponse(int expertHandle, int* values, int si
|
|||||||
{
|
{
|
||||||
list[i] = values[i];
|
list[i] = values[i];
|
||||||
}
|
}
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseIntArray(list));
|
MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseIntArray(list));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ _DLLAPI bool _stdcall sendIntArrayResponse(int expertHandle, int* values, int si
|
|||||||
_DLLAPI bool _stdcall sendLongResponse(int expertHandle, __int64 response, wchar_t* err)
|
_DLLAPI bool _stdcall sendLongResponse(int expertHandle, __int64 response, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, &response]() {
|
return Execute<bool>([&expertHandle, &response]() {
|
||||||
MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseLong(response));
|
MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseLong(response));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ _DLLAPI bool _stdcall sendLongResponse(int expertHandle, __int64 response, wchar
|
|||||||
_DLLAPI bool _stdcall getCommandType(int expertHandle, int* res, wchar_t* err)
|
_DLLAPI bool _stdcall getCommandType(int expertHandle, int* res, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, res]() {
|
return Execute<bool>([&expertHandle, res]() {
|
||||||
*res = MtServerInstance::GetInstance()->GetCommandType(expertHandle);
|
*res = MtAdapter::GetInstance()->GetCommandType(expertHandle);
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -171,15 +171,17 @@ _DLLAPI bool _stdcall getCommandType(int expertHandle, int* res, wchar_t* err)
|
|||||||
_DLLAPI bool _stdcall getIntValue(int expertHandle, int paramIndex, int* res, wchar_t* err)
|
_DLLAPI bool _stdcall getIntValue(int expertHandle, int paramIndex, int* res, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||||
*res = (int)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
*res = (int)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- index parameters
|
||||||
|
|
||||||
_DLLAPI bool _stdcall getDoubleValue(int expertHandle, int paramIndex, double* res, wchar_t* err)
|
_DLLAPI bool _stdcall getDoubleValue(int expertHandle, int paramIndex, double* res, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||||
*res = (double)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
*res = (double)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -187,7 +189,7 @@ _DLLAPI bool _stdcall getDoubleValue(int expertHandle, int paramIndex, double* r
|
|||||||
_DLLAPI bool _stdcall getStringValue(int expertHandle, int paramIndex, wchar_t* res, wchar_t* err)
|
_DLLAPI bool _stdcall getStringValue(int expertHandle, int paramIndex, wchar_t* res, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||||
convertSystemString(res, (String^)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex));
|
convertSystemString(res, (String^)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex));
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -195,7 +197,7 @@ _DLLAPI bool _stdcall getStringValue(int expertHandle, int paramIndex, wchar_t*
|
|||||||
_DLLAPI bool _stdcall getBooleanValue(int expertHandle, int paramIndex, int* res, wchar_t* err)
|
_DLLAPI bool _stdcall getBooleanValue(int expertHandle, int paramIndex, int* res, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||||
*res = (bool)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
*res = (bool)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -203,7 +205,63 @@ _DLLAPI bool _stdcall getBooleanValue(int expertHandle, int paramIndex, int* res
|
|||||||
_DLLAPI bool _stdcall getLongValue(int expertHandle, int paramIndex, __int64* res, wchar_t* err)
|
_DLLAPI bool _stdcall getLongValue(int expertHandle, int paramIndex, __int64* res, wchar_t* err)
|
||||||
{
|
{
|
||||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||||
*res = (__int64)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
*res = (__int64)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
||||||
|
return true;
|
||||||
|
}, err, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- named parameters
|
||||||
|
|
||||||
|
_DLLAPI bool _stdcall containsNamedValue(int expertHandle, wchar_t* paramName)
|
||||||
|
{
|
||||||
|
wchar_t err[1000];
|
||||||
|
return Execute<bool>([&expertHandle, paramName]() {
|
||||||
|
return MtAdapter::GetInstance()->ContainsNamedParameter(expertHandle, gcnew String(paramName));
|
||||||
|
}, err, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_DLLAPI bool _stdcall getNamedIntValue(int expertHandle, wchar_t* paramName, int* res, wchar_t* err)
|
||||||
|
{
|
||||||
|
return Execute<bool>([&expertHandle, paramName, res]() {
|
||||||
|
System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName));
|
||||||
|
*res = (int)obj;
|
||||||
|
return true;
|
||||||
|
}, err, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_DLLAPI bool _stdcall getNamedDoubleValue(int expertHandle, wchar_t* paramName, double* res, wchar_t* err)
|
||||||
|
{
|
||||||
|
return Execute<bool>([&expertHandle, paramName, res]() {
|
||||||
|
System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName));
|
||||||
|
*res = (double)obj;
|
||||||
|
return true;
|
||||||
|
}, err, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_DLLAPI bool _stdcall getNamedStringValue(int expertHandle, wchar_t* paramName, wchar_t* res, wchar_t* err)
|
||||||
|
{
|
||||||
|
return Execute<bool>([&expertHandle, paramName, res]() {
|
||||||
|
System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName));
|
||||||
|
convertSystemString(res, (String^)obj);
|
||||||
|
return true;
|
||||||
|
}, err, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_DLLAPI bool _stdcall getNamedBooleanValue(int expertHandle, wchar_t* paramName, int* res, wchar_t* err)
|
||||||
|
{
|
||||||
|
return Execute<bool>([&expertHandle, paramName, res]() {
|
||||||
|
System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName));
|
||||||
|
*res = (bool)obj;
|
||||||
|
return true;
|
||||||
|
}, err, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_DLLAPI bool _stdcall getNamedLongValue(int expertHandle, wchar_t* paramName, __int64* res, wchar_t* err)
|
||||||
|
{
|
||||||
|
return Execute<bool>([&expertHandle, paramName, res]() {
|
||||||
|
System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName));
|
||||||
|
if (obj != nullptr)
|
||||||
|
*res = (__int64)obj;
|
||||||
return true;
|
return true;
|
||||||
}, err, false);
|
}, err, false);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
namespace MtApi
|
||||||
|
{
|
||||||
|
public enum EnumObject
|
||||||
|
{
|
||||||
|
OBJ_VLINE = 0, //Vertical Line
|
||||||
|
OBJ_HLINE = 1, //Horizontal Line
|
||||||
|
OBJ_TREND = 2, //Trend Line
|
||||||
|
BJ_TRENDBYANGLE = 3, //Trend Line By Angle
|
||||||
|
OBJ_CYCLES = 20, //Cycle Lines
|
||||||
|
OBJ_CHANNEL = 5, //Equidistant Channel
|
||||||
|
OBJ_STDDEVCHANNEL = 6, //Standard Deviation Channel
|
||||||
|
OBJ_REGRESSION = 4, //Linear Regression Channel
|
||||||
|
OBJ_PITCHFORK = 19, //Andrews Pitchfork
|
||||||
|
OBJ_GANNLINE = 7, //Gann Line
|
||||||
|
OBJ_GANNFAN = 8, //Gann Fan
|
||||||
|
OBJ_GANNGRID = 9, //Gann Grid
|
||||||
|
OBJ_FIBO = 10, //Fibonacci Retracement
|
||||||
|
OBJ_FIBOTIMES = 11, //Fibonacci Time Zones
|
||||||
|
OBJ_FIBOFAN = 12, //Fibonacci Fan
|
||||||
|
OBJ_FIBOARC = 13, //Fibonacci Arcs
|
||||||
|
OBJ_FIBOCHANNEL = 15, //Fibonacci Channel
|
||||||
|
OBJ_EXPANSION = 14, //Fibonacci Expansion
|
||||||
|
OBJ_RECTANGLE = 16, //Rectangle
|
||||||
|
OBJ_TRIANGLE = 17, //Triangle
|
||||||
|
OBJ_ELLIPSE = 18, //Ellipse
|
||||||
|
OBJ_ARROW_THUMB_UP = 29, //Thumbs Up
|
||||||
|
OBJ_ARROW_THUMB_DOWN = 30, //Thumbs Down
|
||||||
|
OBJ_ARROW_UP = 31, //Arrow Up
|
||||||
|
OBJ_ARROW_DOWN = 32, //Arrow Down
|
||||||
|
OBJ_ARROW_STOP = 33, //Stop Sign
|
||||||
|
OBJ_ARROW_CHECK = 34, //Check Sign
|
||||||
|
OBJ_ARROW_LEFT_PRICE = 35, //Left Price Label
|
||||||
|
OBJ_ARROW_RIGHT_PRICE = 36, //Right Price Label
|
||||||
|
OBJ_ARROW_BUY = 37, //Buy Sign
|
||||||
|
OBJ_ARROW_SELL = 38, //Sell Sign
|
||||||
|
OBJ_ARROW = 22, //Arrow
|
||||||
|
OBJ_TEXT = 21, //Text
|
||||||
|
OBJ_LABEL = 23, //Label
|
||||||
|
OBJ_BUTTON = 25, //Button
|
||||||
|
OBJ_BITMAP = 26, //Bitmap
|
||||||
|
OBJ_BITMAP_LABEL = 24, //Bitmap Label
|
||||||
|
OBJ_EDIT = 27, //Edit
|
||||||
|
OBJ_EVENT = 42, //The "Event" object corresponding to an event in the economic calendar
|
||||||
|
OBJ_RECTANGLE_LABEL = 28 //The "Rectangle label" object for creating and designing the custom graphical interface.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ChartPeriod.cs" />
|
<Compile Include="ChartPeriod.cs" />
|
||||||
|
<Compile Include="EnumObject.cs" />
|
||||||
<Compile Include="EnumSeriesInfoInteger.cs" />
|
<Compile Include="EnumSeriesInfoInteger.cs" />
|
||||||
<Compile Include="EnumSymbolInfoDouble.cs" />
|
<Compile Include="EnumSymbolInfoDouble.cs" />
|
||||||
<Compile Include="EnumSymbolInfoInteger.cs" />
|
<Compile Include="EnumSymbolInfoInteger.cs" />
|
||||||
|
|||||||
+32
-11
@@ -16,8 +16,6 @@ namespace MtApi
|
|||||||
|
|
||||||
public sealed class MtApiClient
|
public sealed class MtApiClient
|
||||||
{
|
{
|
||||||
private volatile bool _isBacktestingMode;
|
|
||||||
|
|
||||||
#region MetaTrader Constants
|
#region MetaTrader Constants
|
||||||
|
|
||||||
//Special constant
|
//Special constant
|
||||||
@@ -31,6 +29,8 @@ namespace MtApi
|
|||||||
private MtClient _client;
|
private MtClient _client;
|
||||||
private readonly object _locker = new object();
|
private readonly object _locker = new object();
|
||||||
private MtConnectionState _connectionState = MtConnectionState.Disconnected;
|
private MtConnectionState _connectionState = MtConnectionState.Disconnected;
|
||||||
|
private volatile bool _isBacktestingMode;
|
||||||
|
private int _executorHandle;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ctor
|
#region ctor
|
||||||
@@ -74,7 +74,8 @@ namespace MtApi
|
|||||||
///</summary>
|
///</summary>
|
||||||
public List<MtQuote> GetQuotes()
|
public List<MtQuote> GetQuotes()
|
||||||
{
|
{
|
||||||
var quotes = _client.GetQuotes();
|
var client = Client;
|
||||||
|
var quotes = client != null ? client.GetQuotes() : null;
|
||||||
return quotes?.Select(q => new MtQuote(q)).ToList();
|
return quotes?.Select(q => new MtQuote(q)).ToList();
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -95,7 +96,9 @@ namespace MtApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _executorHandle;
|
///<summary>
|
||||||
|
///Connection status of MetaTrader API.
|
||||||
|
///</summary>
|
||||||
public int ExecutorHandle
|
public int ExecutorHandle
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -1746,14 +1749,32 @@ namespace MtApi
|
|||||||
///<param name="price1">The price coordinate of the first anchor point.</param>
|
///<param name="price1">The price coordinate of the first anchor point.</param>
|
||||||
///<param name="time2">The time coordinate of the second anchor point.</param>
|
///<param name="time2">The time coordinate of the second anchor point.</param>
|
||||||
///<param name="price2">The price coordinate of the second anchor point.</param>
|
///<param name="price2">The price coordinate of the second anchor point.</param>
|
||||||
///<param name="time3">The price coordinate of the second anchor point.</param>
|
///<param name="time3">The time coordinate of the third anchor point.</param>
|
||||||
|
///<param name="price3">The price coordinate of the third anchor point.</param>
|
||||||
///<returns>
|
///<returns>
|
||||||
///Returns various data about securities listed in the "Market Watch" window.
|
///Returns true or false depending on whether the object is created or not.
|
||||||
///</returns>
|
///</returns>
|
||||||
bool ObjectCreate(long chartId, string objectName, object objectType, int subWindow,
|
public bool ObjectCreate(long chartId, string objectName, EnumObject objectType, int subWindow,
|
||||||
DateTime time1, double price1, DateTime? time2 = null, double price2 = 0, DateTime? time3 = null, double price3 = 0)
|
DateTime? time1, double price1, DateTime? time2 = null, double? price2 = null, DateTime? time3 = null, double? price3 = null)
|
||||||
{
|
{
|
||||||
return false;
|
var namedParams = new Dictionary<string, object>();
|
||||||
|
namedParams.Add(nameof(chartId), chartId);
|
||||||
|
namedParams.Add(nameof(objectName), objectName);
|
||||||
|
namedParams.Add(nameof(objectType), (int)objectType);
|
||||||
|
namedParams.Add(nameof(subWindow), subWindow);
|
||||||
|
namedParams.Add(nameof(time1), time1 == null ? 0 : MtApiTimeConverter.ConvertToMtTime(time1.Value));
|
||||||
|
namedParams.Add(nameof(price1), price1);
|
||||||
|
|
||||||
|
if (time2 != null)
|
||||||
|
namedParams.Add(nameof(time2), MtApiTimeConverter.ConvertToMtTime(time2.Value));
|
||||||
|
if (price2 != null)
|
||||||
|
namedParams.Add(nameof(price2), price2.Value);
|
||||||
|
if (time3 != null)
|
||||||
|
namedParams.Add(nameof(time3), MtApiTimeConverter.ConvertToMtTime(time3.Value));
|
||||||
|
if (price3 != null)
|
||||||
|
namedParams.Add(nameof(price3), price3.Value);
|
||||||
|
|
||||||
|
return SendCommand<bool>(MtCommandType.ObjectCreate, null, namedParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -1882,7 +1903,7 @@ namespace MtApi
|
|||||||
ConnectionStateChanged?.Invoke(this, new MtConnectionEventArgs(state, message));
|
ConnectionStateChanged?.Invoke(this, new MtConnectionEventArgs(state, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
private T SendCommand<T>(MtCommandType commandType, ArrayList commandParameters)
|
private T SendCommand<T>(MtCommandType commandType, ArrayList commandParameters, Dictionary<string, object> namedParams = null)
|
||||||
{
|
{
|
||||||
MtResponse response;
|
MtResponse response;
|
||||||
|
|
||||||
@@ -1894,7 +1915,7 @@ namespace MtApi
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
response = client.SendCommand((int)commandType, commandParameters, ExecutorHandle);
|
response = client.SendCommand((int)commandType, commandParameters, namedParams, ExecutorHandle);
|
||||||
}
|
}
|
||||||
catch (CommunicationException ex)
|
catch (CommunicationException ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MtApi
|
namespace MtApi
|
||||||
{
|
{
|
||||||
public class MtConnectionEventArgs: EventArgs
|
public class MtConnectionEventArgs: EventArgs
|
||||||
{
|
{
|
||||||
public MtConnectionState Status { get; private set; }
|
public MtConnectionState Status { get; private set; }
|
||||||
public String ConnectionMessage { get; private set; }
|
public string ConnectionMessage { get; private set; }
|
||||||
|
|
||||||
public MtConnectionEventArgs(MtConnectionState status, string message)
|
public MtConnectionEventArgs(MtConnectionState status, string message)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MtApi5
|
namespace MtApi5
|
||||||
{
|
{
|
||||||
public class Mt5ConnectionEventArgs: EventArgs
|
public class Mt5ConnectionEventArgs: EventArgs
|
||||||
{
|
{
|
||||||
public Mt5ConnectionState Status { get; private set; }
|
public Mt5ConnectionState Status { get; private set; }
|
||||||
public String ConnectionMessage { get; private set; }
|
public string ConnectionMessage { get; private set; }
|
||||||
|
|
||||||
public Mt5ConnectionEventArgs(Mt5ConnectionState status, string message)
|
public Mt5ConnectionEventArgs(Mt5ConnectionState status, string message)
|
||||||
{
|
{
|
||||||
|
|||||||
+158
-109
@@ -7,6 +7,7 @@ using System.ServiceModel;
|
|||||||
using MtApi5.Requests;
|
using MtApi5.Requests;
|
||||||
using MtApi5.Responses;
|
using MtApi5.Responses;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MtApi5
|
namespace MtApi5
|
||||||
{
|
{
|
||||||
@@ -31,22 +32,17 @@ namespace MtApi5
|
|||||||
|
|
||||||
|
|
||||||
#region Private Fields
|
#region Private Fields
|
||||||
private readonly MtClient _client = new MtClient();
|
private MtClient _client;
|
||||||
|
private readonly object _locker = new object();
|
||||||
private volatile bool _isBacktestingMode = false;
|
private volatile bool _isBacktestingMode = false;
|
||||||
|
private Mt5ConnectionState _connectionState = Mt5ConnectionState.Disconnected;
|
||||||
|
private int _executorHandle;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods
|
#region Public Methods
|
||||||
public MtApi5Client()
|
public MtApi5Client()
|
||||||
{
|
{
|
||||||
LogConfigurator.Setup(LogProfileName);
|
LogConfigurator.Setup(LogProfileName);
|
||||||
|
|
||||||
ConnectionState = Mt5ConnectionState.Disconnected;
|
|
||||||
|
|
||||||
_client.QuoteAdded += mClient_QuoteAdded;
|
|
||||||
_client.QuoteRemoved += mClient_QuoteRemoved;
|
|
||||||
_client.QuoteUpdated += mClient_QuoteUpdated;
|
|
||||||
_client.ServerDisconnected += mClient_ServerDisconnected;
|
|
||||||
_client.ServerFailed += mClient_ServerFailed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
@@ -56,15 +52,7 @@ namespace MtApi5
|
|||||||
///<param name="port">Port of host connection (default 8222) </param>
|
///<param name="port">Port of host connection (default 8222) </param>
|
||||||
public void BeginConnect(string host, int port)
|
public void BeginConnect(string host, int port)
|
||||||
{
|
{
|
||||||
//if (string.IsNullOrEmpty(host) == false && (host.Equals("localhost") || host.Equals("127.0.0.1")))
|
Task.Factory.StartNew(() => Connect(host, port));
|
||||||
//{
|
|
||||||
// this.BeginConnect(port);
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
Action<string, int> connectAction = Connect;
|
|
||||||
connectAction.BeginInvoke(host, port, null, null);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
@@ -73,8 +61,7 @@ namespace MtApi5
|
|||||||
///<param name="port">Port of host connection (default 8222) </param>
|
///<param name="port">Port of host connection (default 8222) </param>
|
||||||
public void BeginConnect(int port)
|
public void BeginConnect(int port)
|
||||||
{
|
{
|
||||||
Action<int> connectAction = Connect;
|
Task.Factory.StartNew(() => Connect(port));
|
||||||
connectAction.BeginInvoke(port, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
@@ -82,8 +69,7 @@ namespace MtApi5
|
|||||||
///</summary>
|
///</summary>
|
||||||
public void BeginDisconnect()
|
public void BeginDisconnect()
|
||||||
{
|
{
|
||||||
Action disconnectAction = Disconnect;
|
Task.Factory.StartNew(() => Disconnect(false));
|
||||||
disconnectAction.BeginInvoke(null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
@@ -91,11 +77,8 @@ namespace MtApi5
|
|||||||
///</summary>
|
///</summary>
|
||||||
public IEnumerable<Mt5Quote> GetQuotes()
|
public IEnumerable<Mt5Quote> GetQuotes()
|
||||||
{
|
{
|
||||||
IEnumerable<MtQuote> quotes;
|
var client = Client;
|
||||||
lock (_client)
|
var quotes = client != null ? client.GetQuotes() : null;
|
||||||
{
|
|
||||||
quotes = _client.GetQuotes();
|
|
||||||
}
|
|
||||||
return quotes?.Select(q => q.Parse());
|
return quotes?.Select(q => q.Parse());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1432,7 +1415,34 @@ namespace MtApi5
|
|||||||
///<summary>
|
///<summary>
|
||||||
///Connection status of MetaTrader API.
|
///Connection status of MetaTrader API.
|
||||||
///</summary>
|
///</summary>
|
||||||
public Mt5ConnectionState ConnectionState { get; private set; }
|
public Mt5ConnectionState ConnectionState
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
return _connectionState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ExecutorHandle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
return _executorHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_executorHandle = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
@@ -1443,94 +1453,148 @@ namespace MtApi5
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
private void Connect(string host, int port)
|
private MtClient Client
|
||||||
{
|
{
|
||||||
ConnectionState = Mt5ConnectionState.Connecting;
|
get
|
||||||
ConnectionStateChanged.FireEvent(this
|
|
||||||
, new Mt5ConnectionEventArgs(Mt5ConnectionState.Connecting, "Connecting to " + host + ":" + port));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
lock (_client)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
_client.Open(host, port);
|
return _client;
|
||||||
_client.Connect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
}
|
||||||
|
|
||||||
|
private void Connect(MtClient client)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
{
|
{
|
||||||
ConnectionState = Mt5ConnectionState.Failed;
|
if (_connectionState == Mt5ConnectionState.Connected
|
||||||
ConnectionStateChanged.FireEvent(this
|
|| _connectionState == Mt5ConnectionState.Connecting)
|
||||||
, new Mt5ConnectionEventArgs(Mt5ConnectionState.Failed, "Failed connection to " + host + ":" + port + ". " + e.Message));
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectionState = Mt5ConnectionState.Connecting;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionState = Mt5ConnectionState.Connected;
|
string message = string.IsNullOrEmpty(client.Host) ? $"Connecting to localhost:{client.Port}" : $"Connecting to {client.Host}:{client.Port}";
|
||||||
ConnectionStateChanged.FireEvent(this
|
ConnectionStateChanged?.Invoke(this, new Mt5ConnectionEventArgs(Mt5ConnectionState.Connecting, message));
|
||||||
, new Mt5ConnectionEventArgs(Mt5ConnectionState.Connected, "Connected to " + host + ":" + port));
|
|
||||||
|
|
||||||
OnConnected();
|
var state = Mt5ConnectionState.Failed;
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.Connect();
|
||||||
|
state = Mt5ConnectionState.Connected;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
message = string.IsNullOrEmpty(client.Host) ? $"Failed connection to localhost:{client.Port}. {e.Message}" : $"Failed connection to {client.Host}:{client.Port}. {e.Message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == Mt5ConnectionState.Connected)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_client.QuoteAdded += _client_QuoteAdded;
|
||||||
|
_client.QuoteRemoved += _client_QuoteRemoved;
|
||||||
|
_client.QuoteUpdated += _client_QuoteUpdated;
|
||||||
|
_client.ServerDisconnected += _client_ServerDisconnected;
|
||||||
|
_client.ServerFailed += _client_ServerFailed;
|
||||||
|
message = string.IsNullOrEmpty(client.Host) ? $"Connected to localhost:{client.Port}" : $"Connected to { client.Host}:{client.Port}";
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectionState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionStateChanged?.Invoke(this, new Mt5ConnectionEventArgs(state, message));
|
||||||
|
|
||||||
|
if (state == Mt5ConnectionState.Connected)
|
||||||
|
{
|
||||||
|
OnConnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Connect(string host, int port)
|
||||||
|
{
|
||||||
|
var client = new MtClient(host, port);
|
||||||
|
Connect(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Connect(int port)
|
private void Connect(int port)
|
||||||
{
|
{
|
||||||
ConnectionState = Mt5ConnectionState.Connecting;
|
var client = new MtClient(port);
|
||||||
ConnectionStateChanged.FireEvent(this
|
Connect(client);
|
||||||
, new Mt5ConnectionEventArgs(Mt5ConnectionState.Connecting, "Connecting to 'localhost':" + port));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (_client)
|
|
||||||
{
|
|
||||||
_client.Open(port);
|
|
||||||
_client.Connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ConnectionState = Mt5ConnectionState.Failed;
|
|
||||||
ConnectionStateChanged.FireEvent(this
|
|
||||||
, new Mt5ConnectionEventArgs(Mt5ConnectionState.Failed, "Failed connection to 'localhost':" + port + ". " + e.Message));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionState = Mt5ConnectionState.Connected;
|
|
||||||
ConnectionStateChanged.FireEvent(this
|
|
||||||
, new Mt5ConnectionEventArgs(Mt5ConnectionState.Connected, "Connected to 'localhost':" + port));
|
|
||||||
|
|
||||||
OnConnected();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Disconnect()
|
private void Disconnect(bool failed)
|
||||||
{
|
{
|
||||||
lock (_client)
|
var state = failed ? Mt5ConnectionState.Failed : Mt5ConnectionState.Disconnected;
|
||||||
|
var message = failed ? "Connection Failed" : "Disconnected";
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
{
|
{
|
||||||
_client.Disconnect();
|
if (_connectionState == Mt5ConnectionState.Disconnected
|
||||||
_client.Close();
|
|| _connectionState == Mt5ConnectionState.Failed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
|
{
|
||||||
|
_client.QuoteAdded -= _client_QuoteAdded;
|
||||||
|
_client.QuoteRemoved -= _client_QuoteRemoved;
|
||||||
|
_client.QuoteUpdated -= _client_QuoteUpdated;
|
||||||
|
_client.ServerDisconnected -= _client_ServerDisconnected;
|
||||||
|
_client.ServerFailed -= _client_ServerFailed;
|
||||||
|
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
|
_client.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
_client.Dispose();
|
||||||
|
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectionState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionState = Mt5ConnectionState.Disconnected;
|
ConnectionStateChanged?.Invoke(this, new Mt5ConnectionEventArgs(state, message));
|
||||||
ConnectionStateChanged.FireEvent(this, new Mt5ConnectionEventArgs(Mt5ConnectionState.Disconnected, "Disconnected"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private T SendCommand<T>(Mt5CommandType commandType, ArrayList commandParameters)
|
private T SendCommand<T>(Mt5CommandType commandType, ArrayList commandParameters, Dictionary<string, object> namedParams = null)
|
||||||
{
|
{
|
||||||
MtResponse response;
|
MtResponse response;
|
||||||
|
|
||||||
|
var client = Client;
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
throw new Exception("No connection");
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
lock (_client)
|
response = client.SendCommand((int)commandType, commandParameters, namedParams, ExecutorHandle);
|
||||||
{
|
|
||||||
response = _client.SendCommand((int) commandType, commandParameters);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (CommunicationException ex)
|
catch (CommunicationException ex)
|
||||||
{
|
{
|
||||||
throw new Exception(ex.Message, ex);
|
throw new Exception(ex.Message, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseValue = response?.GetValue();
|
if (response == null)
|
||||||
return responseValue != null ? (T) responseValue : default(T);
|
{
|
||||||
|
throw new ExecutionException(ErrorCode.ErrCustom, "Response from MetaTrader is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.ErrorCode != 0)
|
||||||
|
{
|
||||||
|
throw new ExecutionException((ErrorCode)response.ErrorCode, response.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseValue = response.GetValue();
|
||||||
|
return responseValue != null ? (T)responseValue : default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
private T SendRequest<T>(RequestBase request) where T : ResponseBase, new()
|
private T SendRequest<T>(RequestBase request) where T : ResponseBase, new()
|
||||||
@@ -1545,25 +1609,14 @@ namespace MtApi5
|
|||||||
});
|
});
|
||||||
var commandParameters = new ArrayList { serializer };
|
var commandParameters = new ArrayList { serializer };
|
||||||
|
|
||||||
MtResponseString res;
|
var res = SendCommand<string>(Mt5CommandType.MtRequest, commandParameters);
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (_client)
|
|
||||||
{
|
|
||||||
res = (MtResponseString)_client.SendCommand((int)Mt5CommandType.MtRequest, commandParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (CommunicationException ex)
|
|
||||||
{
|
|
||||||
throw new Exception(ex.Message, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res == null)
|
if (res == null)
|
||||||
{
|
{
|
||||||
throw new ExecutionException(ErrorCode.ErrCustom, "Response from MetaTrader is null");
|
throw new ExecutionException(ErrorCode.ErrCustom, "Response from MetaTrader is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = JsonConvert.DeserializeObject<T>(res.Value);
|
var response = JsonConvert.DeserializeObject<T>(res);
|
||||||
if (response.ErrorCode != 0)
|
if (response.ErrorCode != 0)
|
||||||
{
|
{
|
||||||
throw new ExecutionException((ErrorCode)response.ErrorCode, response.ErrorMessage);
|
throw new ExecutionException((ErrorCode)response.ErrorCode, response.ErrorMessage);
|
||||||
@@ -1573,7 +1626,7 @@ namespace MtApi5
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void mClient_QuoteUpdated(MtQuote quote)
|
private void _client_QuoteUpdated(MtQuote quote)
|
||||||
{
|
{
|
||||||
if (quote != null)
|
if (quote != null)
|
||||||
{
|
{
|
||||||
@@ -1581,26 +1634,22 @@ namespace MtApi5
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mClient_ServerDisconnected(object sender, EventArgs e)
|
private void _client_ServerDisconnected(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ConnectionState = Mt5ConnectionState.Disconnected;
|
Disconnect(false);
|
||||||
ConnectionStateChanged.FireEvent(this
|
|
||||||
, new Mt5ConnectionEventArgs(Mt5ConnectionState.Disconnected, "MtApi is disconnected"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mClient_ServerFailed(object sender, EventArgs e)
|
private void _client_ServerFailed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ConnectionState = Mt5ConnectionState.Failed;
|
Disconnect(true);
|
||||||
ConnectionStateChanged.FireEvent(this
|
|
||||||
, new Mt5ConnectionEventArgs(Mt5ConnectionState.Failed, "Failed connection with MtApi"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mClient_QuoteRemoved(MtQuote quote)
|
private void _client_QuoteRemoved(MtQuote quote)
|
||||||
{
|
{
|
||||||
QuoteRemoved.FireEvent(this, new Mt5QuoteEventArgs(quote.Parse()));
|
QuoteRemoved.FireEvent(this, new Mt5QuoteEventArgs(quote.Parse()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mClient_QuoteAdded(MtQuote quote)
|
private void _client_QuoteAdded(MtQuote quote)
|
||||||
{
|
{
|
||||||
QuoteAdded.FireEvent(this, new Mt5QuoteEventArgs(quote.Parse()));
|
QuoteAdded.FireEvent(this, new Mt5QuoteEventArgs(quote.Parse()));
|
||||||
}
|
}
|
||||||
|
|||||||
+51
@@ -195,6 +195,10 @@
|
|||||||
this.tabPage9 = new System.Windows.Forms.TabPage();
|
this.tabPage9 = new System.Windows.Forms.TabPage();
|
||||||
this.button38 = new System.Windows.Forms.Button();
|
this.button38 = new System.Windows.Forms.Button();
|
||||||
this.button37 = new System.Windows.Forms.Button();
|
this.button37 = new System.Windows.Forms.Button();
|
||||||
|
this.tabPage10 = new System.Windows.Forms.TabPage();
|
||||||
|
this.button4 = new System.Windows.Forms.Button();
|
||||||
|
this.comboBox11 = new System.Windows.Forms.ComboBox();
|
||||||
|
this.textBoxChartId = new System.Windows.Forms.TextBox();
|
||||||
this.groupBox1.SuspendLayout();
|
this.groupBox1.SuspendLayout();
|
||||||
this.statusStrip1.SuspendLayout();
|
this.statusStrip1.SuspendLayout();
|
||||||
this.groupBox2.SuspendLayout();
|
this.groupBox2.SuspendLayout();
|
||||||
@@ -212,6 +216,7 @@
|
|||||||
this.tabPage7.SuspendLayout();
|
this.tabPage7.SuspendLayout();
|
||||||
this.tabPage8.SuspendLayout();
|
this.tabPage8.SuspendLayout();
|
||||||
this.tabPage9.SuspendLayout();
|
this.tabPage9.SuspendLayout();
|
||||||
|
this.tabPage10.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// textBoxServerName
|
// textBoxServerName
|
||||||
@@ -393,6 +398,7 @@
|
|||||||
this.tabControl1.Controls.Add(this.tabPage7);
|
this.tabControl1.Controls.Add(this.tabPage7);
|
||||||
this.tabControl1.Controls.Add(this.tabPage8);
|
this.tabControl1.Controls.Add(this.tabPage8);
|
||||||
this.tabControl1.Controls.Add(this.tabPage9);
|
this.tabControl1.Controls.Add(this.tabPage9);
|
||||||
|
this.tabControl1.Controls.Add(this.tabPage10);
|
||||||
this.tabControl1.Location = new System.Drawing.Point(324, 12);
|
this.tabControl1.Location = new System.Drawing.Point(324, 12);
|
||||||
this.tabControl1.Multiline = true;
|
this.tabControl1.Multiline = true;
|
||||||
this.tabControl1.Name = "tabControl1";
|
this.tabControl1.Name = "tabControl1";
|
||||||
@@ -1951,6 +1957,7 @@
|
|||||||
//
|
//
|
||||||
// tabPage9
|
// tabPage9
|
||||||
//
|
//
|
||||||
|
this.tabPage9.Controls.Add(this.textBoxChartId);
|
||||||
this.tabPage9.Controls.Add(this.button38);
|
this.tabPage9.Controls.Add(this.button38);
|
||||||
this.tabPage9.Controls.Add(this.button37);
|
this.tabPage9.Controls.Add(this.button37);
|
||||||
this.tabPage9.Location = new System.Drawing.Point(4, 40);
|
this.tabPage9.Location = new System.Drawing.Point(4, 40);
|
||||||
@@ -1980,6 +1987,44 @@
|
|||||||
this.button37.UseVisualStyleBackColor = true;
|
this.button37.UseVisualStyleBackColor = true;
|
||||||
this.button37.Click += new System.EventHandler(this.button37_Click);
|
this.button37.Click += new System.EventHandler(this.button37_Click);
|
||||||
//
|
//
|
||||||
|
// tabPage10
|
||||||
|
//
|
||||||
|
this.tabPage10.Controls.Add(this.comboBox11);
|
||||||
|
this.tabPage10.Controls.Add(this.button4);
|
||||||
|
this.tabPage10.Location = new System.Drawing.Point(4, 40);
|
||||||
|
this.tabPage10.Name = "tabPage10";
|
||||||
|
this.tabPage10.Padding = new System.Windows.Forms.Padding(3);
|
||||||
|
this.tabPage10.Size = new System.Drawing.Size(638, 383);
|
||||||
|
this.tabPage10.TabIndex = 10;
|
||||||
|
this.tabPage10.Text = "Object Functions";
|
||||||
|
this.tabPage10.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// button4
|
||||||
|
//
|
||||||
|
this.button4.Location = new System.Drawing.Point(133, 3);
|
||||||
|
this.button4.Name = "button4";
|
||||||
|
this.button4.Size = new System.Drawing.Size(97, 23);
|
||||||
|
this.button4.TabIndex = 0;
|
||||||
|
this.button4.Text = "ObjectCreate";
|
||||||
|
this.button4.UseVisualStyleBackColor = true;
|
||||||
|
this.button4.Click += new System.EventHandler(this.button4_Click);
|
||||||
|
//
|
||||||
|
// comboBox11
|
||||||
|
//
|
||||||
|
this.comboBox11.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this.comboBox11.FormattingEnabled = true;
|
||||||
|
this.comboBox11.Location = new System.Drawing.Point(6, 5);
|
||||||
|
this.comboBox11.Name = "comboBox11";
|
||||||
|
this.comboBox11.Size = new System.Drawing.Size(121, 21);
|
||||||
|
this.comboBox11.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// textBoxChartId
|
||||||
|
//
|
||||||
|
this.textBoxChartId.Location = new System.Drawing.Point(115, 8);
|
||||||
|
this.textBoxChartId.Name = "textBoxChartId";
|
||||||
|
this.textBoxChartId.Size = new System.Drawing.Size(100, 20);
|
||||||
|
this.textBoxChartId.TabIndex = 2;
|
||||||
|
//
|
||||||
// Form1
|
// Form1
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -2022,6 +2067,8 @@
|
|||||||
this.tabPage8.ResumeLayout(false);
|
this.tabPage8.ResumeLayout(false);
|
||||||
this.tabPage8.PerformLayout();
|
this.tabPage8.PerformLayout();
|
||||||
this.tabPage9.ResumeLayout(false);
|
this.tabPage9.ResumeLayout(false);
|
||||||
|
this.tabPage9.PerformLayout();
|
||||||
|
this.tabPage10.ResumeLayout(false);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
@@ -2195,6 +2242,10 @@
|
|||||||
private System.Windows.Forms.Button button65;
|
private System.Windows.Forms.Button button65;
|
||||||
private System.Windows.Forms.Button button66;
|
private System.Windows.Forms.Button button66;
|
||||||
private System.Windows.Forms.Button button67;
|
private System.Windows.Forms.Button button67;
|
||||||
|
private System.Windows.Forms.TabPage tabPage10;
|
||||||
|
private System.Windows.Forms.Button button4;
|
||||||
|
private System.Windows.Forms.ComboBox comboBox11;
|
||||||
|
private System.Windows.Forms.TextBox textBoxChartId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace TestApiClientUI
|
|||||||
comboBox8.DataSource = Enum.GetNames(typeof(MarketInfoModeType));
|
comboBox8.DataSource = Enum.GetNames(typeof(MarketInfoModeType));
|
||||||
comboBox9.DataSource = Enum.GetNames(typeof(EnumTerminalInfoInteger));
|
comboBox9.DataSource = Enum.GetNames(typeof(EnumTerminalInfoInteger));
|
||||||
comboBox10.DataSource = Enum.GetNames(typeof(EnumTerminalInfoDouble));
|
comboBox10.DataSource = Enum.GetNames(typeof(EnumTerminalInfoDouble));
|
||||||
|
comboBox11.DataSource = Enum.GetNames(typeof(EnumObject));
|
||||||
comboBoxAccountInfoCmd.DataSource = Enum.GetNames(typeof(TradeOperation));
|
comboBoxAccountInfoCmd.DataSource = Enum.GetNames(typeof(TradeOperation));
|
||||||
|
|
||||||
_apiClient.QuoteUpdated += apiClient_QuoteUpdated;
|
_apiClient.QuoteUpdated += apiClient_QuoteUpdated;
|
||||||
@@ -49,6 +50,7 @@ namespace TestApiClientUI
|
|||||||
comboBox3.SelectedIndex = 0;
|
comboBox3.SelectedIndex = 0;
|
||||||
comboBox4.SelectedIndex = 0;
|
comboBox4.SelectedIndex = 0;
|
||||||
comboBox5.SelectedIndex = 0;
|
comboBox5.SelectedIndex = 0;
|
||||||
|
comboBox11.SelectedIndex = 0;
|
||||||
comboBoxAccountInfoCmd.SelectedIndex = 0;
|
comboBoxAccountInfoCmd.SelectedIndex = 0;
|
||||||
|
|
||||||
_timerTradeMonitor = new TimerTradeMonitor(_apiClient) { Interval = 10000 }; // 10 sec
|
_timerTradeMonitor = new TimerTradeMonitor(_apiClient) { Interval = 10000 }; // 10 sec
|
||||||
@@ -1369,6 +1371,7 @@ namespace TestApiClientUI
|
|||||||
{
|
{
|
||||||
var result = await Execute(_apiClient.ChartId);
|
var result = await Execute(_apiClient.ChartId);
|
||||||
PrintLog($"CharID: result = {result}");
|
PrintLog($"CharID: result = {result}");
|
||||||
|
RunOnUiThread(() => textBoxChartId.Text = result.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
//ChartRedraw
|
//ChartRedraw
|
||||||
@@ -1377,5 +1380,18 @@ namespace TestApiClientUI
|
|||||||
await Execute(() => _apiClient.ChartRedraw());
|
await Execute(() => _apiClient.ChartRedraw());
|
||||||
PrintLog($"ChartRedraw: called.");
|
PrintLog($"ChartRedraw: called.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ObjectCreate
|
||||||
|
private async void button4_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
long chartId = 0;
|
||||||
|
string objectName = "label_object";
|
||||||
|
//long.TryParse(textBoxChartId.Text, )
|
||||||
|
EnumObject objectType;
|
||||||
|
Enum.TryParse(comboBox11.Text, out objectType);
|
||||||
|
|
||||||
|
var result = await Execute(() => _apiClient.ObjectCreate(chartId, objectName, objectType, 0, null, 0));
|
||||||
|
PrintLog($"ObjectCreate result: {result}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user