mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-15 11:48:09 +00:00
Issue #37: Used ConcurrencyMode.Multiple for server and client side to avoid deadlocks and freezes of MtApiService
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
namespace MTApiService
|
namespace MTApiService
|
||||||
{
|
{
|
||||||
public interface ICommandManager
|
internal interface ICommandManager
|
||||||
{
|
{
|
||||||
void EnqueueCommandTask(MtCommandTask task);
|
MtCommandTask SendCommand(MtCommand task);
|
||||||
MtCommandTask DequeueCommandTask();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace MTApiService
|
||||||
|
{
|
||||||
|
internal interface ITaskExecutor
|
||||||
|
{
|
||||||
|
void Execute(MtCommandTask task);
|
||||||
|
|
||||||
|
int Handle { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ICommandManager.cs" />
|
<Compile Include="ICommandManager.cs" />
|
||||||
<Compile Include="IMetaTraderHandler.cs" />
|
<Compile Include="IMetaTraderHandler.cs" />
|
||||||
|
<Compile Include="ITaskExecutor.cs" />
|
||||||
<Compile Include="LogConfigurator.cs" />
|
<Compile Include="LogConfigurator.cs" />
|
||||||
<Compile Include="MtCommandEventArgs.cs" />
|
<Compile Include="MtCommandEventArgs.cs" />
|
||||||
<Compile Include="IDisposableChannel.cs" />
|
<Compile Include="IDisposableChannel.cs" />
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ namespace MTApiService
|
|||||||
: base(callbackContext, binding, remoteAddress)
|
: base(callbackContext, binding, remoteAddress)
|
||||||
{
|
{
|
||||||
InnerDuplexChannel.Faulted += InnerDuplexChannel_Faulted;
|
InnerDuplexChannel.Faulted += InnerDuplexChannel_Faulted;
|
||||||
InnerDuplexChannel.Open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IMtApi Members
|
#region IMtApi Members
|
||||||
|
|
||||||
public bool Connect()
|
public bool Connect()
|
||||||
{
|
{
|
||||||
|
InnerDuplexChannel.Open();
|
||||||
return Channel.Connect();
|
return Channel.Connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-84
@@ -6,8 +6,8 @@ using log4net;
|
|||||||
|
|
||||||
namespace MTApiService
|
namespace MTApiService
|
||||||
{
|
{
|
||||||
[CallbackBehavior(UseSynchronizationContext = false)]
|
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
|
||||||
public class MtClient: IMtApiCallback, IDisposable
|
public class MtClient : IMtApiCallback, IDisposable
|
||||||
{
|
{
|
||||||
private const string ServiceName = "MtApiService";
|
private const string ServiceName = "MtApiService";
|
||||||
|
|
||||||
@@ -17,28 +17,22 @@ namespace MTApiService
|
|||||||
#region Fields
|
#region Fields
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(MtClient));
|
private static readonly ILog Log = LogManager.GetLogger(typeof(MtClient));
|
||||||
|
|
||||||
private MtApiProxy _proxy;
|
private readonly MtApiProxy _proxy;
|
||||||
private bool _isConnected;
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods
|
#region ctor
|
||||||
public void Open(string host, int port)
|
public MtClient(string host, int port)
|
||||||
{
|
{
|
||||||
Log.DebugFormat("Open: begin. host = {0}, port = {1}", host, port);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(host))
|
if (string.IsNullOrEmpty(host))
|
||||||
throw new ArgumentNullException(nameof(host), "host is null or empty");
|
throw new ArgumentNullException(nameof(host), "host is null or empty");
|
||||||
|
|
||||||
if (port < 0 || port > 65536)
|
if (port < 0 || port > 65536)
|
||||||
throw new ArgumentOutOfRangeException(nameof(port), "port value is invalid");
|
throw new ArgumentOutOfRangeException(nameof(port), "port value is invalid");
|
||||||
|
|
||||||
var urlService = $"net.tcp://{host}:{port}/{ServiceName}";
|
Host = host;
|
||||||
|
Port = port;
|
||||||
|
|
||||||
if (_proxy != null)
|
var urlService = $"net.tcp://{host}:{port}/{ServiceName}";
|
||||||
{
|
|
||||||
Log.Warn("Open: end. _proxy is not null.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bind = new NetTcpBinding(SecurityMode.None)
|
var bind = new NetTcpBinding(SecurityMode.None)
|
||||||
{
|
{
|
||||||
@@ -56,28 +50,19 @@ namespace MTApiService
|
|||||||
MaxNameTableCharCount = 2147483647
|
MaxNameTableCharCount = 2147483647
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Commented next statement since it is not required
|
|
||||||
|
|
||||||
_proxy = new MtApiProxy(new InstanceContext(this), bind, new EndpointAddress(urlService));
|
_proxy = new MtApiProxy(new InstanceContext(this), bind, new EndpointAddress(urlService));
|
||||||
_proxy.Faulted += ProxyFaulted;
|
_proxy.Faulted += ProxyFaulted;
|
||||||
|
|
||||||
Log.Debug("Open: end.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Open(int port)
|
public MtClient(int port)
|
||||||
{
|
{
|
||||||
Log.DebugFormat("Open: begin. port = {0}", port);
|
|
||||||
|
|
||||||
if (port < 0 || port > 65536)
|
if (port < 0 || port > 65536)
|
||||||
throw new ArgumentOutOfRangeException(nameof(port), "port value is invalid");
|
throw new ArgumentOutOfRangeException(nameof(port), "port value is invalid");
|
||||||
|
|
||||||
var urlService = $"net.pipe://localhost/{ServiceName}_{port}";
|
Port = port;
|
||||||
|
|
||||||
if (_proxy != null)
|
var urlService = $"net.pipe://localhost/{ServiceName}_{port}";
|
||||||
{
|
|
||||||
Log.Warn("Open: end. _proxy is not null.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bind = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None)
|
var bind = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None)
|
||||||
{
|
{
|
||||||
@@ -95,61 +80,40 @@ namespace MTApiService
|
|||||||
MaxNameTableCharCount = 2147483647
|
MaxNameTableCharCount = 2147483647
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Commented next statement since it is not required
|
|
||||||
|
|
||||||
_proxy = new MtApiProxy(new InstanceContext(this), bind, new EndpointAddress(urlService));
|
_proxy = new MtApiProxy(new InstanceContext(this), bind, new EndpointAddress(urlService));
|
||||||
_proxy.Faulted += ProxyFaulted;
|
_proxy.Faulted += ProxyFaulted;
|
||||||
|
|
||||||
Log.Debug("Open: end.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
Log.Debug("Close: begin.");
|
|
||||||
|
|
||||||
if (_proxy != null)
|
#endregion
|
||||||
{
|
|
||||||
_proxy.Faulted -= ProxyFaulted;
|
|
||||||
_proxy.Dispose();
|
|
||||||
_proxy = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isConnected = false;
|
|
||||||
|
|
||||||
Log.Debug("Close: end.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
/// <exception cref="CommunicationException">Thrown when connection failed</exception>
|
/// <exception cref="CommunicationException">Thrown when connection failed</exception>
|
||||||
public void Connect()
|
public void Connect()
|
||||||
{
|
{
|
||||||
Log.Debug("Connect: begin.");
|
Log.Debug("Connect: begin.");
|
||||||
|
|
||||||
if (_proxy == null)
|
if (_proxy.State != CommunicationState.Created)
|
||||||
{
|
{
|
||||||
Log.Error("Connect: _proxy is not defined.");
|
Log.ErrorFormat("Connected: end. Client has invalid state {0}", _proxy.State);
|
||||||
throw new CommunicationException("Connection failed to service. Proxy is not defined (needs to call Open)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isConnected)
|
|
||||||
{
|
|
||||||
Log.Warn("Connected: end. Client is already connected.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var coonected = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_isConnected = _proxy.Connect();
|
coonected = _proxy.Connect();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.ErrorFormat("Connect: Exception - {0}", ex.Message);
|
Log.ErrorFormat("Connect: Exception - {0}", ex.Message);
|
||||||
|
|
||||||
Close();
|
|
||||||
|
|
||||||
throw new CommunicationException($"Connection failed to service. {ex.Message}");
|
throw new CommunicationException($"Connection failed to service. {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_isConnected == false)
|
if (coonected == false)
|
||||||
{
|
{
|
||||||
Log.Error("Connect: end. Connection failed.");
|
Log.Error("Connect: end. Connection failed.");
|
||||||
throw new CommunicationException("Connection failed");
|
throw new CommunicationException("Connection failed");
|
||||||
@@ -164,15 +128,11 @@ namespace MTApiService
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_isConnected = false;
|
_proxy.Disconnect();
|
||||||
|
|
||||||
_proxy?.Disconnect();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.ErrorFormat("Disconnect: Exception - {0}", ex.Message);
|
Log.ErrorFormat("Disconnect: Exception - {0}", ex.Message);
|
||||||
|
|
||||||
Close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Debug("Disconnect: end.");
|
Log.Debug("Disconnect: end.");
|
||||||
@@ -183,19 +143,13 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count);
|
Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count);
|
||||||
|
|
||||||
MtResponse result;
|
if (IsConnected == false)
|
||||||
|
|
||||||
if (_isConnected == false)
|
|
||||||
{
|
{
|
||||||
Log.Error("SendCommand: Client is not connected.");
|
Log.Error("SendCommand: Client is not connected.");
|
||||||
throw new CommunicationException("Client is not connected.");
|
throw new CommunicationException("Client is not connected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_proxy == null)
|
MtResponse result;
|
||||||
{
|
|
||||||
Log.Error("SendCommand: Proxy is not defined.");
|
|
||||||
throw new CommunicationException("Proxy is not defined.");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -205,11 +159,11 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.ErrorFormat("SendCommand: Exception - {0}", ex.Message);
|
Log.ErrorFormat("SendCommand: Exception - {0}", ex.Message);
|
||||||
|
|
||||||
Close();
|
|
||||||
|
|
||||||
throw new CommunicationException("Service connection failed! " + ex.Message);
|
throw new CommunicationException("Service connection failed! " + ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.DebugFormat("SendCommand: end. result = {0}", result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,18 +172,12 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.Debug("GetQuotes: begin.");
|
Log.Debug("GetQuotes: begin.");
|
||||||
|
|
||||||
if (_isConnected == false)
|
if (IsConnected == false)
|
||||||
{
|
{
|
||||||
Log.Warn("GetQuotes: end. Client is not connected.");
|
Log.Warn("GetQuotes: end. Client is not connected.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_proxy == null)
|
|
||||||
{
|
|
||||||
Log.Warn("GetQuotes: end. _proxy is not defined.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<MtQuote> result;
|
List<MtQuote> result;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -240,8 +188,6 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.ErrorFormat("GetQuotes: Exception - {0}", ex.Message);
|
Log.ErrorFormat("GetQuotes: Exception - {0}", ex.Message);
|
||||||
|
|
||||||
Close();
|
|
||||||
|
|
||||||
throw new CommunicationException($"Service connection failed! {ex.Message}");
|
throw new CommunicationException($"Service connection failed! {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +206,7 @@ namespace MTApiService
|
|||||||
|
|
||||||
if (quote == null) return;
|
if (quote == null) return;
|
||||||
|
|
||||||
QuoteUpdated?.Invoke( quote);
|
QuoteUpdated?.Invoke(quote);
|
||||||
|
|
||||||
Log.Debug("OnQuoteUpdate: end.");
|
Log.Debug("OnQuoteUpdate: end.");
|
||||||
}
|
}
|
||||||
@@ -287,7 +233,6 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.Debug("OnServerStopped: begin.");
|
Log.Debug("OnServerStopped: begin.");
|
||||||
|
|
||||||
Close();
|
|
||||||
ServerDisconnected?.Invoke(this, EventArgs.Empty);
|
ServerDisconnected?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
Log.Debug("OnServerStopped: end.");
|
Log.Debug("OnServerStopped: end.");
|
||||||
@@ -306,7 +251,16 @@ namespace MTApiService
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
public bool IsConnected => _proxy.State == CommunicationState.Opened && _isConnected;
|
public string Host { get; private set; }
|
||||||
|
public int Port { get; private set; }
|
||||||
|
|
||||||
|
private bool IsConnected
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _proxy.State == CommunicationState.Opened;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -316,7 +270,6 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.Debug("ProxyFaulted: begin.");
|
Log.Debug("ProxyFaulted: begin.");
|
||||||
|
|
||||||
Close();
|
|
||||||
ServerFailed?.Invoke(this, EventArgs.Empty);
|
ServerFailed?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
Log.Debug("ProxyFaulted: end.");
|
Log.Debug("ProxyFaulted: end.");
|
||||||
@@ -330,7 +283,7 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.Debug("Dispose: begin.");
|
Log.Debug("Dispose: begin.");
|
||||||
|
|
||||||
Close();
|
_proxy.Dispose();
|
||||||
|
|
||||||
Log.Debug("Dispose: end.");
|
Log.Debug("Dispose: end.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ namespace MTApiService
|
|||||||
}
|
}
|
||||||
|
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public int CommandType { get; private set; }
|
public int CommandType { get; set; }
|
||||||
|
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public ArrayList Parameters { get; private set; }
|
public ArrayList Parameters { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public int ExpertHandle { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,14 +20,4 @@ namespace MTApiService
|
|||||||
return $"EventType = {EventType}; Payload = {Payload}; ExpertHandle = {ExpertHandle}";
|
return $"EventType = {EventType}; Payload = {Payload}; ExpertHandle = {ExpertHandle}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MtEventArgs: EventArgs
|
|
||||||
{
|
|
||||||
public MtEventArgs(MtEvent e)
|
|
||||||
{
|
|
||||||
Event = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MtEvent Event { get; private set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ using log4net;
|
|||||||
|
|
||||||
namespace MTApiService
|
namespace MTApiService
|
||||||
{
|
{
|
||||||
internal class MtCommandExecutorManager : ICommandManager
|
internal class MtExecutorManager : ICommandManager
|
||||||
{
|
{
|
||||||
#region Private Fields
|
#region Private Fields
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(MtCommandExecutorManager));
|
private static readonly ILog Log = LogManager.GetLogger(typeof(MtExecutorManager));
|
||||||
|
|
||||||
private readonly List<MtExpert> _commandExecutors = new List<MtExpert>();
|
|
||||||
private readonly Queue<MtCommandTask> _commandTasks = new Queue<MtCommandTask>();
|
|
||||||
|
|
||||||
|
private readonly List<ITaskExecutor> _executorList = new List<ITaskExecutor>();
|
||||||
|
private readonly Dictionary<int, ITaskExecutor> _executorMap = new Dictionary<int, ITaskExecutor>();
|
||||||
private readonly object _locker = new object();
|
private readonly object _locker = new object();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -23,149 +22,94 @@ namespace MTApiService
|
|||||||
|
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
_commandExecutors.Clear();
|
_executorList.Clear();
|
||||||
_commandTasks.Clear();
|
_executorMap.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Debug("Stop: end.");
|
Log.Debug("Stop: end.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCommandExecutor(MtExpert commandExecutor)
|
public void AddExecutor(ITaskExecutor executor)
|
||||||
{
|
{
|
||||||
if (commandExecutor == null)
|
if (executor == null)
|
||||||
throw new ArgumentNullException(nameof(commandExecutor));
|
throw new ArgumentNullException(nameof(executor));
|
||||||
|
|
||||||
Log.DebugFormat("AddCommandExecutor: begin. commandExecutor = {0}", commandExecutor);
|
Log.DebugFormat("AddExecutor: begin. executor = {0}", executor);
|
||||||
|
|
||||||
var notify = false;
|
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
if (_commandExecutors.Contains(commandExecutor))
|
if (_executorList.Contains(executor))
|
||||||
{
|
{
|
||||||
Log.Warn("AddCommandExecutor: end. Command executor already exist.");
|
Log.Warn("AddExecutor: end. Executor already exist.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_commandExecutors.Add(commandExecutor);
|
_executorList.Add(executor);
|
||||||
if (_commandTasks.Count > 0)
|
_executorMap[executor.Handle] = executor;
|
||||||
{
|
|
||||||
notify = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commandExecutor.CommandExecuted += CommandExecutor_CommandExecuted;
|
|
||||||
commandExecutor.CommandManager = this;
|
|
||||||
|
|
||||||
if (notify)
|
|
||||||
{
|
|
||||||
NotifyCommandReady();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Debug("AddCommandExecutor: end.");
|
Log.Debug("AddCommandExecutor: end.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveCommandExecutor(MtExpert commandExecutor)
|
public void RemoveExecutor(ITaskExecutor executor)
|
||||||
{
|
{
|
||||||
if (commandExecutor == null)
|
if (executor == null)
|
||||||
throw new ArgumentNullException(nameof(commandExecutor));
|
throw new ArgumentNullException(nameof(executor));
|
||||||
|
|
||||||
Log.DebugFormat("RemoveCommandExecutor: begin. commandExecutor = {0}", commandExecutor);
|
Log.DebugFormat("RemoveExecutor: begin. executor = {0}", executor);
|
||||||
|
|
||||||
var notify = false;
|
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
if (_commandExecutors.Contains(commandExecutor) == false)
|
if (_executorList.Contains(executor) == false)
|
||||||
{
|
{
|
||||||
Log.Warn("RemoveCommandExecutor: end. Command executor is not exist in collection.");
|
Log.Warn("RemoveExecutor: end. Executor is not exist in collection.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_commandExecutors.Remove(commandExecutor);
|
_executorList.Remove(executor);
|
||||||
if (_commandTasks.Count > 0)
|
_executorMap.Remove(executor.Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug("RemoveExecutor: end.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public MtCommandTask SendCommand(MtCommand command)
|
||||||
|
{
|
||||||
|
if (command == null)
|
||||||
|
throw new ArgumentNullException(nameof(command));
|
||||||
|
|
||||||
|
var task = new MtCommandTask(command);
|
||||||
|
|
||||||
|
Log.DebugFormat("SendTask: begin. command = {0}", command);
|
||||||
|
|
||||||
|
ITaskExecutor executor = null;
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (_executorMap.ContainsKey(command.ExpertHandle))
|
||||||
{
|
{
|
||||||
notify = true;
|
executor = _executorMap[command.ExpertHandle];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
executor = _executorList.Count > 0 ? _executorList[0] : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commandExecutor.CommandExecuted -= CommandExecutor_CommandExecuted;
|
if (executor == null)
|
||||||
commandExecutor.CommandManager = null;
|
|
||||||
|
|
||||||
if (notify)
|
|
||||||
{
|
{
|
||||||
NotifyCommandReady();
|
Log.Error("SendTask: Executor is null!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
executor.Execute(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Debug("RemoveCommandExecutor: end.");
|
Log.Debug("SendTask: end.");
|
||||||
|
|
||||||
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnqueueCommandTask(MtCommandTask task)
|
|
||||||
{
|
|
||||||
if (task == null)
|
|
||||||
throw new ArgumentNullException(nameof(task));
|
|
||||||
|
|
||||||
Log.DebugFormat("EnqueueCommandTask: begin. task = {0}", task);
|
|
||||||
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
_commandTasks.Enqueue(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
NotifyCommandReady();
|
|
||||||
|
|
||||||
Log.Debug("EnqueueCommandTask: end.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public MtCommandTask DequeueCommandTask()
|
|
||||||
{
|
|
||||||
Log.Debug("DequeueCommandTask: called.");
|
|
||||||
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
return _commandTasks.Count > 0 ? _commandTasks.Dequeue() : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
private void NotifyCommandReady()
|
|
||||||
{
|
|
||||||
Log.Debug("NotifyCommandReady: begin.");
|
|
||||||
|
|
||||||
var commandExecutors = new List<MtExpert>();
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
commandExecutors.AddRange(_commandExecutors);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var executor in commandExecutors)
|
|
||||||
{
|
|
||||||
executor.NotifyCommandReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.DebugFormat("NotifyCommandReady: end. Notified executor count = {0}", commandExecutors.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CommandExecutor_CommandExecuted(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
Log.Debug("CommandExecutor_CommandExecuted: begin.");
|
|
||||||
|
|
||||||
var notify = false;
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
if (_commandTasks.Count > 0)
|
|
||||||
{
|
|
||||||
notify = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notify)
|
|
||||||
{
|
|
||||||
NotifyCommandReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Debug("CommandExecutor_CommandExecuted: end.");
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-49
@@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using log4net;
|
using log4net;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MTApiService
|
namespace MTApiService
|
||||||
{
|
{
|
||||||
public class MtExpert
|
internal class MtExpert: ITaskExecutor
|
||||||
{
|
{
|
||||||
public delegate void MtQuoteHandler(MtExpert expert, MtQuote quote);
|
public delegate void MtQuoteHandler(MtExpert expert, MtQuote quote);
|
||||||
public delegate void MtEventHandler(MtExpert expert, MtEvent e);
|
public delegate void MtEventHandler(MtExpert expert, MtEvent e);
|
||||||
@@ -12,8 +13,8 @@ namespace MTApiService
|
|||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(MtExpert));
|
private static readonly ILog Log = LogManager.GetLogger(typeof(MtExpert));
|
||||||
|
|
||||||
private readonly IMetaTraderHandler _mtHadler;
|
private readonly IMetaTraderHandler _mtHadler;
|
||||||
private MtCommandTask _commandTask;
|
private MtCommandTask _currentTask;
|
||||||
private ICommandManager _commandManager;
|
private readonly Queue<MtCommandTask> _taskQueue = new Queue<MtCommandTask>();
|
||||||
private readonly object _locker = new object();
|
private readonly object _locker = new object();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -42,9 +43,8 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.DebugFormat("SendResponse: begin. response = {0}", response);
|
Log.DebugFormat("SendResponse: begin. response = {0}", response);
|
||||||
|
|
||||||
_commandTask.SetResult(response);
|
_currentTask.SetResult(response);
|
||||||
_commandTask = null;
|
_currentTask = null;
|
||||||
FireOnCommandExecuted();
|
|
||||||
|
|
||||||
Log.Debug("SendResponse: end.");
|
Log.Debug("SendResponse: end.");
|
||||||
}
|
}
|
||||||
@@ -53,20 +53,16 @@ namespace MTApiService
|
|||||||
{
|
{
|
||||||
Log.Debug("GetCommandType: called.");
|
Log.Debug("GetCommandType: called.");
|
||||||
|
|
||||||
var commandManager = CommandManager;
|
_currentTask = DequeueTask();
|
||||||
if (commandManager != null)
|
|
||||||
{
|
|
||||||
_commandTask = commandManager.DequeueCommandTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _commandTask?.Command?.CommandType ?? 0;
|
return _currentTask?.Command?.CommandType ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetCommandParameter(int index)
|
public object GetCommandParameter(int index)
|
||||||
{
|
{
|
||||||
Log.DebugFormat("GetCommandType: called. index = {0}", index);
|
Log.DebugFormat("GetCommandType: called. index = {0}", index);
|
||||||
|
|
||||||
var command = _commandTask?.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)
|
||||||
{
|
{
|
||||||
return command.Parameters[index];
|
return command.Parameters[index];
|
||||||
@@ -91,7 +87,22 @@ namespace MTApiService
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region ITaskExecutor
|
||||||
|
|
||||||
|
public void Execute(MtCommandTask task)
|
||||||
|
{
|
||||||
|
lock (_taskQueue)
|
||||||
|
{
|
||||||
|
_taskQueue.Enqueue(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotifyCommandReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
private MtQuote _quote;
|
private MtQuote _quote;
|
||||||
public MtQuote Quote
|
public MtQuote Quote
|
||||||
{
|
{
|
||||||
@@ -134,41 +145,34 @@ namespace MTApiService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommandManager CommandManager
|
|
||||||
{
|
|
||||||
private get
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
return _commandManager;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
_commandManager = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region IMtCommandExecutor
|
|
||||||
public void NotifyCommandReady()
|
|
||||||
{
|
|
||||||
Log.Debug("NotifyCommandReady: begin.");
|
|
||||||
|
|
||||||
SendTickToMetaTrader();
|
|
||||||
|
|
||||||
Log.Debug("NotifyCommandReady: end.");
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
private void SendTickToMetaTrader()
|
private MtCommandTask DequeueTask()
|
||||||
{
|
{
|
||||||
|
Log.Debug("DequeueTask: called.");
|
||||||
|
|
||||||
|
MtCommandTask task;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
count = _taskQueue.Count;
|
||||||
|
task = _taskQueue.Count > 0 ? _taskQueue.Dequeue() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.DebugFormat("DequeueTask: end. left task count = {0}.", count);
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifyCommandReady()
|
||||||
|
{
|
||||||
|
Log.Debug("NotifyCommandReady: begin.");
|
||||||
|
|
||||||
_mtHadler.SendTickToMetaTrader(Handle);
|
_mtHadler.SendTickToMetaTrader(Handle);
|
||||||
|
|
||||||
|
Log.Debug("NotifyCommandReady: end.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FireOnQuoteChanged(MtQuote quote)
|
private void FireOnQuoteChanged(MtQuote quote)
|
||||||
@@ -181,11 +185,6 @@ namespace MTApiService
|
|||||||
Deinited?.Invoke(this, EventArgs.Empty);
|
Deinited?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FireOnCommandExecuted()
|
|
||||||
{
|
|
||||||
CommandExecuted?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FireOnMtEvent(MtEvent mtEvent)
|
private void FireOnMtEvent(MtEvent mtEvent)
|
||||||
{
|
{
|
||||||
OnMtEvent?.Invoke(this, mtEvent);
|
OnMtEvent?.Invoke(this, mtEvent);
|
||||||
@@ -195,7 +194,6 @@ namespace MTApiService
|
|||||||
#region Events
|
#region Events
|
||||||
public event EventHandler Deinited;
|
public event EventHandler Deinited;
|
||||||
public event MtQuoteHandler QuoteChanged;
|
public event MtQuoteHandler QuoteChanged;
|
||||||
public event EventHandler CommandExecuted;
|
|
||||||
public event MtEventHandler OnMtEvent;
|
public event MtEventHandler OnMtEvent;
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace MTApiService
|
|||||||
|
|
||||||
private readonly MtService _service;
|
private readonly MtService _service;
|
||||||
private readonly List<ServiceHost> _hosts = new List<ServiceHost>();
|
private readonly List<ServiceHost> _hosts = new List<ServiceHost>();
|
||||||
private readonly MtCommandExecutorManager _executorManager = new MtCommandExecutorManager();
|
private readonly MtExecutorManager _executorManager = new MtExecutorManager();
|
||||||
private readonly List<MtExpert> _experts = new List<MtExpert>();
|
private readonly List<MtExpert> _experts = new List<MtExpert>();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ namespace MTApiService
|
|||||||
_experts.Add(expert);
|
_experts.Add(expert);
|
||||||
}
|
}
|
||||||
|
|
||||||
_executorManager.AddCommandExecutor(expert);
|
_executorManager.AddExecutor(expert);
|
||||||
|
|
||||||
_service.OnQuoteAdded(expert.Quote);
|
_service.OnQuoteAdded(expert.Quote);
|
||||||
|
|
||||||
@@ -170,11 +170,20 @@ namespace MTApiService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var task = new MtCommandTask(command);
|
var task = _executorManager.SendCommand(command);
|
||||||
_executorManager.EnqueueCommandTask(task);
|
|
||||||
|
|
||||||
//wait for execute command in MetaTrader
|
//wait for execute command in MetaTrader
|
||||||
var response = task.WaitResult(WaitResponseTime);
|
MtResponse response = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = task.WaitResult(WaitResponseTime);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.WarnFormat("SendCommand: Exception - {0}", ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.DebugFormat("SendCommand: end. response = {0}", response);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -371,7 +380,7 @@ namespace MTApiService
|
|||||||
expertsCount = _experts.Count;
|
expertsCount = _experts.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
_executorManager.RemoveCommandExecutor(expert);
|
_executorManager.RemoveExecutor(expert);
|
||||||
|
|
||||||
expert.Deinited -= expert_Deinited;
|
expert.Deinited -= expert_Deinited;
|
||||||
expert.QuoteChanged -= expert_QuoteChanged;
|
expert.QuoteChanged -= expert_QuoteChanged;
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MtApi
|
|
||||||
{
|
|
||||||
static class ExtensionMethods
|
|
||||||
{
|
|
||||||
#region Event Methods
|
|
||||||
|
|
||||||
public static Task FireEventAsync(this MtApiQuoteHandler evenHandler, object sender, string symbol, double bid, double ask)
|
|
||||||
{
|
|
||||||
return Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
evenHandler?.Invoke(sender, symbol, bid, ask);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task FireEventAsync(this EventHandler eventHandler, object sender)
|
|
||||||
{
|
|
||||||
return Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
eventHandler?.Invoke(sender, EventArgs.Empty);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task FireEventAsync<T>(this EventHandler<T> eventHandler, object sender, T e)
|
|
||||||
where T : EventArgs
|
|
||||||
{
|
|
||||||
return Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
eventHandler?.Invoke(sender, e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -64,7 +64,6 @@
|
|||||||
<Compile Include="EnumSymbolInfoInteger.cs" />
|
<Compile Include="EnumSymbolInfoInteger.cs" />
|
||||||
<Compile Include="EnumTerminalInfoDouble.cs" />
|
<Compile Include="EnumTerminalInfoDouble.cs" />
|
||||||
<Compile Include="EnumTerminalInfoInteger.cs" />
|
<Compile Include="EnumTerminalInfoInteger.cs" />
|
||||||
<Compile Include="ExtensionMethods.cs" />
|
|
||||||
<Compile Include="Monitors\AvailabilityOrdersEventArgs.cs" />
|
<Compile Include="Monitors\AvailabilityOrdersEventArgs.cs" />
|
||||||
<Compile Include="MqlRates.cs" />
|
<Compile Include="MqlRates.cs" />
|
||||||
<Compile Include="MqlTick.cs" />
|
<Compile Include="MqlTick.cs" />
|
||||||
|
|||||||
+119
-98
@@ -8,6 +8,7 @@ using System.ServiceModel;
|
|||||||
using MtApi.Requests;
|
using MtApi.Requests;
|
||||||
using MtApi.Responses;
|
using MtApi.Responses;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MtApi
|
namespace MtApi
|
||||||
{
|
{
|
||||||
@@ -27,7 +28,7 @@ namespace MtApi
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Fields
|
#region Private Fields
|
||||||
private readonly MtClient _client = new MtClient();
|
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;
|
||||||
#endregion
|
#endregion
|
||||||
@@ -37,13 +38,6 @@ namespace MtApi
|
|||||||
public MtApiClient()
|
public MtApiClient()
|
||||||
{
|
{
|
||||||
LogConfigurator.Setup(LogProfileName);
|
LogConfigurator.Setup(LogProfileName);
|
||||||
|
|
||||||
_client.QuoteAdded += _client_QuoteAdded;
|
|
||||||
_client.QuoteRemoved += _client_QuoteRemoved;
|
|
||||||
_client.QuoteUpdated += _client_QuoteUpdated;
|
|
||||||
_client.ServerDisconnected += _client_ServerDisconnected;
|
|
||||||
_client.ServerFailed += _client_ServerFailed;
|
|
||||||
_client.MtEventReceived += _client_MtEventReceived;
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -55,8 +49,7 @@ namespace MtApi
|
|||||||
///<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)
|
||||||
{
|
{
|
||||||
Action<string, int> connectAction = Connect;
|
Task.Factory.StartNew(() => Connect(host, port));
|
||||||
connectAction.BeginInvoke(host, port, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
@@ -65,8 +58,7 @@ namespace MtApi
|
|||||||
///<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>
|
||||||
@@ -74,8 +66,7 @@ namespace MtApi
|
|||||||
///</summary>
|
///</summary>
|
||||||
public void BeginDisconnect()
|
public void BeginDisconnect()
|
||||||
{
|
{
|
||||||
Action disconnectAction = Disconnect;
|
Task.Factory.StartNew(() => Disconnect(false));
|
||||||
disconnectAction.BeginInvoke(null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
@@ -83,12 +74,7 @@ namespace MtApi
|
|||||||
///</summary>
|
///</summary>
|
||||||
public List<MtQuote> GetQuotes()
|
public List<MtQuote> GetQuotes()
|
||||||
{
|
{
|
||||||
IEnumerable<MTApiService.MtQuote> quotes;
|
var quotes = _client.GetQuotes();
|
||||||
lock (_client)
|
|
||||||
{
|
|
||||||
quotes = _client.GetQuotes();
|
|
||||||
}
|
|
||||||
|
|
||||||
return quotes?.Select(q => new MtQuote(q)).ToList();
|
return quotes?.Select(q => new MtQuote(q)).ToList();
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -1706,44 +1692,81 @@ namespace MtApi
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
private void Connect(string host, int port)
|
private MtClient Client
|
||||||
{
|
{
|
||||||
UpdateConnectionState(MtConnectionState.Connecting, $"Connecting to {host}:{port}");
|
get
|
||||||
try
|
|
||||||
{
|
{
|
||||||
lock (_client)
|
lock(_locker)
|
||||||
{
|
{
|
||||||
_client.Open(host, port);
|
return _client;
|
||||||
_client.Connect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
}
|
||||||
|
|
||||||
|
private void Connect(MtClient client)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
{
|
{
|
||||||
UpdateConnectionState(MtConnectionState.Failed, $"Failed connection to {host}:{port}. {e.Message}");
|
if (_connectionState == MtConnectionState.Connected
|
||||||
return;
|
|| _connectionState == MtConnectionState.Connecting)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectionState = MtConnectionState.Connecting;
|
||||||
}
|
}
|
||||||
UpdateConnectionState(MtConnectionState.Connected, $"Connected to {host}:{port}");
|
|
||||||
OnConnected();
|
string message = string.IsNullOrEmpty(client.Host) ? $"Connecting to localhost:{client.Port}" : $"Connecting to {client.Host}:{client.Port}";
|
||||||
|
ConnectionStateChanged?.Invoke(this, new MtConnectionEventArgs(MtConnectionState.Connecting, message));
|
||||||
|
|
||||||
|
var state = MtConnectionState.Failed;
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.Connect();
|
||||||
|
state = MtConnectionState.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 == MtConnectionState.Connected)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_client.QuoteAdded += _client_QuoteAdded;
|
||||||
|
_client.QuoteRemoved += _client_QuoteRemoved;
|
||||||
|
_client.QuoteUpdated += _client_QuoteUpdated;
|
||||||
|
_client.ServerDisconnected += _client_ServerDisconnected;
|
||||||
|
_client.ServerFailed += _client_ServerFailed;
|
||||||
|
_client.MtEventReceived += _client_MtEventReceived;
|
||||||
|
message = string.IsNullOrEmpty(client.Host) ? $"Connected to localhost:{client.Port}" : $"Connected to { client.Host}:{client.Port}";
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectionState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionStateChanged?.Invoke(this, new MtConnectionEventArgs(state, message));
|
||||||
|
|
||||||
|
if (state == MtConnectionState.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)
|
||||||
{
|
{
|
||||||
UpdateConnectionState(MtConnectionState.Connecting, $"Connecting to 'localhost':{port}");
|
var client = new MtClient(port);
|
||||||
try
|
Connect(client);
|
||||||
{
|
|
||||||
lock (_client)
|
|
||||||
{
|
|
||||||
_client.Open(port);
|
|
||||||
_client.Connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
UpdateConnectionState(MtConnectionState.Failed, $"Failed connection to 'localhost':{port}. {e.Message}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UpdateConnectionState(MtConnectionState.Connected, $"Connected to 'localhost':{port}");
|
|
||||||
OnConnected();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConnected()
|
private void OnConnected()
|
||||||
@@ -1755,50 +1778,67 @@ namespace MtApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Disconnect()
|
private void Disconnect(bool failed)
|
||||||
{
|
{
|
||||||
lock (_client)
|
var state = failed ? MtConnectionState.Disconnected : MtConnectionState.Disconnected;
|
||||||
{
|
var message = failed ? "Connection Failed" : "Disconnected";
|
||||||
_client.Disconnect();
|
|
||||||
_client.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateConnectionState(MtConnectionState.Disconnected, "Disconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateConnectionState(MtConnectionState state, string message)
|
|
||||||
{
|
|
||||||
var changed = false;
|
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
if (_connectionState != state)
|
if (_connectionState == MtConnectionState.Disconnected
|
||||||
|
|| _connectionState == MtConnectionState.Failed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_client != null)
|
||||||
{
|
{
|
||||||
_connectionState = state;
|
_client.QuoteAdded -= _client_QuoteAdded;
|
||||||
changed = true;
|
_client.QuoteRemoved -= _client_QuoteRemoved;
|
||||||
|
_client.QuoteUpdated -= _client_QuoteUpdated;
|
||||||
|
_client.ServerDisconnected -= _client_ServerDisconnected;
|
||||||
|
_client.ServerFailed -= _client_ServerFailed;
|
||||||
|
_client.MtEventReceived -= _client_MtEventReceived;
|
||||||
|
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
|
_client.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
_client.Dispose();
|
||||||
|
|
||||||
|
_client = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_connectionState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed)
|
|
||||||
{
|
ConnectionStateChanged?.Invoke(this, new MtConnectionEventArgs(state, message));
|
||||||
ConnectionStateChanged.FireEventAsync(this, new MtConnectionEventArgs(state, message));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private T SendCommand<T>(MtCommandType commandType, ArrayList commandParameters)
|
private T SendCommand<T>(MtCommandType commandType, ArrayList commandParameters)
|
||||||
{
|
{
|
||||||
MtResponse response;
|
MtResponse response;
|
||||||
|
|
||||||
|
var client = Client;
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
throw new MtConnectionException("No connection");
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
lock (_client)
|
response = client.SendCommand((int)commandType, commandParameters);
|
||||||
{
|
|
||||||
response = _client.SendCommand((int)commandType, commandParameters);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (CommunicationException ex)
|
catch (CommunicationException ex)
|
||||||
{
|
{
|
||||||
throw new MtConnectionException(ex.Message, ex);
|
throw new MtConnectionException(ex.Message, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response == null)
|
||||||
|
{
|
||||||
|
throw new MtExecutionException(MtErrorCode.MtApiCustomError, "Response from MetaTrader is null");
|
||||||
|
}
|
||||||
|
|
||||||
var responseValue = response.GetValue();
|
var responseValue = response.GetValue();
|
||||||
return responseValue != null ? (T)responseValue : default(T);
|
return responseValue != null ? (T)responseValue : default(T);
|
||||||
}
|
}
|
||||||
@@ -1815,25 +1855,14 @@ namespace MtApi
|
|||||||
});
|
});
|
||||||
var commandParameters = new ArrayList { serializer };
|
var commandParameters = new ArrayList { serializer };
|
||||||
|
|
||||||
MtResponseString res;
|
var res = SendCommand<string>(MtCommandType.MtRequest, commandParameters);
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (_client)
|
|
||||||
{
|
|
||||||
res = (MtResponseString)_client.SendCommand((int)MtCommandType.MtRequest, commandParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (CommunicationException ex)
|
|
||||||
{
|
|
||||||
throw new MtConnectionException(ex.Message, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res == null)
|
if (res == null)
|
||||||
{
|
{
|
||||||
throw new MtExecutionException(MtErrorCode.MtApiCustomError, "Response from MetaTrader is null");
|
throw new MtExecutionException(MtErrorCode.MtApiCustomError, "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 MtExecutionException((MtErrorCode)response.ErrorCode, response.ErrorMessage);
|
throw new MtExecutionException((MtErrorCode)response.ErrorCode, response.ErrorMessage);
|
||||||
@@ -1846,37 +1875,29 @@ namespace MtApi
|
|||||||
{
|
{
|
||||||
if (quote != null)
|
if (quote != null)
|
||||||
{
|
{
|
||||||
if (_isBacktestingMode)
|
QuoteUpdate?.Invoke(this, new MtQuoteEventArgs(new MtQuote(quote)));
|
||||||
{
|
QuoteUpdated?.Invoke(this, quote.Instrument, quote.Bid, quote.Ask);
|
||||||
QuoteUpdate?.Invoke(this, new MtQuoteEventArgs(new MtQuote(quote)));
|
|
||||||
QuoteUpdated?.Invoke(this, quote.Instrument, quote.Bid, quote.Ask);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QuoteUpdate?.FireEventAsync(this, new MtQuoteEventArgs(new MtQuote(quote)));
|
|
||||||
QuoteUpdated.FireEventAsync(this, quote.Instrument, quote.Bid, quote.Ask);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _client_ServerDisconnected(object sender, EventArgs e)
|
private void _client_ServerDisconnected(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
UpdateConnectionState(MtConnectionState.Disconnected, "MtApi is disconnected");
|
Disconnect(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _client_ServerFailed(object sender, EventArgs e)
|
private void _client_ServerFailed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
UpdateConnectionState(MtConnectionState.Failed, "Failed connection with MtApi");
|
Disconnect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _client_QuoteRemoved(MTApiService.MtQuote quote)
|
private void _client_QuoteRemoved(MTApiService.MtQuote quote)
|
||||||
{
|
{
|
||||||
QuoteRemoved.FireEventAsync(this, new MtQuoteEventArgs(new MtQuote(quote)));
|
QuoteRemoved?.Invoke(this, new MtQuoteEventArgs(new MtQuote(quote)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _client_QuoteAdded(MTApiService.MtQuote quote)
|
private void _client_QuoteAdded(MTApiService.MtQuote quote)
|
||||||
{
|
{
|
||||||
QuoteAdded.FireEventAsync(this, new MtQuoteEventArgs(new MtQuote(quote)));
|
QuoteAdded?.Invoke(this, new MtQuoteEventArgs(new MtQuote(quote)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _client_MtEventReceived(MtEvent e)
|
private void _client_MtEventReceived(MtEvent e)
|
||||||
@@ -1897,7 +1918,7 @@ namespace MtApi
|
|||||||
|
|
||||||
private void FireOnLastTimeBar(MtTimeBar timeBar)
|
private void FireOnLastTimeBar(MtTimeBar timeBar)
|
||||||
{
|
{
|
||||||
OnLastTimeBar.FireEventAsync(this, new TimeBarArgs(timeBar));
|
OnLastTimeBar?.Invoke(this, new TimeBarArgs(timeBar));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BacktestingReady()
|
private void BacktestingReady()
|
||||||
|
|||||||
@@ -135,12 +135,28 @@ namespace TestApiClientUI
|
|||||||
PrintLog(msg);
|
PrintLog(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TestCallback(string symbol)
|
||||||
|
{
|
||||||
|
int bars = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bars = _apiClient.iBars(symbol, ChartPeriod.PERIOD_M5);
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("TestCallback: Exception - {0}", ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bars > 0)
|
||||||
|
Console.WriteLine("TestCallback: iBar = {0}", bars);
|
||||||
|
}
|
||||||
|
|
||||||
private void apiClient_QuoteUpdated(object sender, string symbol, double bid, double ask)
|
private void apiClient_QuoteUpdated(object sender, string symbol, double bid, double ask)
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Quote: Symbol = {0}, Bid = {1}, Ask = {2}", symbol, bid, ask);
|
Console.WriteLine(@"Quote: Symbol = {0}, Bid = {1}, Ask = {2}", symbol, bid, ask);
|
||||||
|
TestCallback(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void _apiClient_QuoteUpdate(object sender, MtQuoteEventArgs e)
|
private void _apiClient_QuoteUpdate(object sender, MtQuoteEventArgs e)
|
||||||
{
|
{
|
||||||
//if UI of quite is busy we are skipping this update
|
//if UI of quite is busy we are skipping this update
|
||||||
@@ -741,13 +757,13 @@ namespace TestApiClientUI
|
|||||||
|
|
||||||
Console.WriteLine($"Finished time: {DateTime.Now}");
|
Console.WriteLine($"Finished time: {DateTime.Now}");
|
||||||
|
|
||||||
using (var file = new System.IO.StreamWriter($@"{System.IO.Path.GetTempPath()}\MtApi\test.txt"))
|
//using (var file = new System.IO.StreamWriter($@"{System.IO.Path.GetTempPath()}\MtApi\test.txt"))
|
||||||
{
|
//{
|
||||||
foreach (var value in openPriceList)
|
// foreach (var value in openPriceList)
|
||||||
{
|
// {
|
||||||
file.WriteLine(value);
|
// file.WriteLine(value);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void button7_Click(object sender, EventArgs e)
|
private void button7_Click(object sender, EventArgs e)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user