diff --git a/MTApiService/ICommandManager.cs b/MTApiService/ICommandManager.cs index 2db3b80b..54514467 100755 --- a/MTApiService/ICommandManager.cs +++ b/MTApiService/ICommandManager.cs @@ -1,8 +1,7 @@ namespace MTApiService { - public interface ICommandManager + internal interface ICommandManager { - void EnqueueCommandTask(MtCommandTask task); - MtCommandTask DequeueCommandTask(); + MtCommandTask SendCommand(MtCommand task); } } diff --git a/MTApiService/ITaskExecutor.cs b/MTApiService/ITaskExecutor.cs new file mode 100644 index 00000000..c688c686 --- /dev/null +++ b/MTApiService/ITaskExecutor.cs @@ -0,0 +1,9 @@ +namespace MTApiService +{ + internal interface ITaskExecutor + { + void Execute(MtCommandTask task); + + int Handle { get; } + } +} diff --git a/MTApiService/MTApiService.csproj b/MTApiService/MTApiService.csproj index 9bd012c7..56010ac1 100755 --- a/MTApiService/MTApiService.csproj +++ b/MTApiService/MTApiService.csproj @@ -62,6 +62,7 @@ + diff --git a/MTApiService/MtApiProxy.cs b/MTApiService/MtApiProxy.cs index f9307ab2..b29a32f8 100755 --- a/MTApiService/MtApiProxy.cs +++ b/MTApiService/MtApiProxy.cs @@ -11,13 +11,13 @@ namespace MTApiService : base(callbackContext, binding, remoteAddress) { InnerDuplexChannel.Faulted += InnerDuplexChannel_Faulted; - InnerDuplexChannel.Open(); } #region IMtApi Members public bool Connect() { + InnerDuplexChannel.Open(); return Channel.Connect(); } diff --git a/MTApiService/MtClient.cs b/MTApiService/MtClient.cs index 81d7009c..68164915 100755 --- a/MTApiService/MtClient.cs +++ b/MTApiService/MtClient.cs @@ -6,8 +6,8 @@ using log4net; namespace MTApiService { - [CallbackBehavior(UseSynchronizationContext = false)] - public class MtClient: IMtApiCallback, IDisposable + [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)] + public class MtClient : IMtApiCallback, IDisposable { private const string ServiceName = "MtApiService"; @@ -17,28 +17,22 @@ namespace MTApiService #region Fields private static readonly ILog Log = LogManager.GetLogger(typeof(MtClient)); - private MtApiProxy _proxy; - private bool _isConnected; + private readonly MtApiProxy _proxy; #endregion - #region Public Methods - public void Open(string host, int port) + #region ctor + public MtClient(string host, int port) { - Log.DebugFormat("Open: begin. host = {0}, port = {1}", host, port); - if (string.IsNullOrEmpty(host)) throw new ArgumentNullException(nameof(host), "host is null or empty"); if (port < 0 || port > 65536) throw new ArgumentOutOfRangeException(nameof(port), "port value is invalid"); - var urlService = $"net.tcp://{host}:{port}/{ServiceName}"; + Host = host; + Port = port; - if (_proxy != null) - { - Log.Warn("Open: end. _proxy is not null."); - return; - } + var urlService = $"net.tcp://{host}:{port}/{ServiceName}"; var bind = new NetTcpBinding(SecurityMode.None) { @@ -56,28 +50,19 @@ namespace MTApiService MaxNameTableCharCount = 2147483647 } }; - // Commented next statement since it is not required _proxy = new MtApiProxy(new InstanceContext(this), bind, new EndpointAddress(urlService)); _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) throw new ArgumentOutOfRangeException(nameof(port), "port value is invalid"); - var urlService = $"net.pipe://localhost/{ServiceName}_{port}"; + Port = port; - if (_proxy != null) - { - Log.Warn("Open: end. _proxy is not null."); - return; - } + var urlService = $"net.pipe://localhost/{ServiceName}_{port}"; var bind = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) { @@ -95,61 +80,40 @@ namespace MTApiService MaxNameTableCharCount = 2147483647 } }; - // Commented next statement since it is not required _proxy = new MtApiProxy(new InstanceContext(this), bind, new EndpointAddress(urlService)); _proxy.Faulted += ProxyFaulted; - - Log.Debug("Open: end."); } - public void Close() - { - Log.Debug("Close: begin."); - if (_proxy != null) - { - _proxy.Faulted -= ProxyFaulted; - _proxy.Dispose(); - _proxy = null; - } - - _isConnected = false; - - Log.Debug("Close: end."); - } + #endregion + #region Public Methods /// Thrown when connection failed public void Connect() { Log.Debug("Connect: begin."); - if (_proxy == null) + if (_proxy.State != CommunicationState.Created) { - Log.Error("Connect: _proxy is not defined."); - 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."); + Log.ErrorFormat("Connected: end. Client has invalid state {0}", _proxy.State); return; } + var coonected = false; + try { - _isConnected = _proxy.Connect(); + coonected = _proxy.Connect(); } catch (Exception ex) { Log.ErrorFormat("Connect: Exception - {0}", ex.Message); - Close(); - throw new CommunicationException($"Connection failed to service. {ex.Message}"); } - if (_isConnected == false) + if (coonected == false) { Log.Error("Connect: end. Connection failed."); throw new CommunicationException("Connection failed"); @@ -164,15 +128,11 @@ namespace MTApiService try { - _isConnected = false; - - _proxy?.Disconnect(); + _proxy.Disconnect(); } catch (Exception ex) { Log.ErrorFormat("Disconnect: Exception - {0}", ex.Message); - - Close(); } Log.Debug("Disconnect: end."); @@ -183,19 +143,13 @@ namespace MTApiService { 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."); throw new CommunicationException("Client is not connected."); } - if (_proxy == null) - { - Log.Error("SendCommand: Proxy is not defined."); - throw new CommunicationException("Proxy is not defined."); - } + MtResponse result; try { @@ -205,11 +159,11 @@ namespace MTApiService { Log.ErrorFormat("SendCommand: Exception - {0}", ex.Message); - Close(); - throw new CommunicationException("Service connection failed! " + ex.Message); } + Log.DebugFormat("SendCommand: end. result = {0}", result); + return result; } @@ -218,18 +172,12 @@ namespace MTApiService { Log.Debug("GetQuotes: begin."); - if (_isConnected == false) + if (IsConnected == false) { Log.Warn("GetQuotes: end. Client is not connected."); return null; } - if (_proxy == null) - { - Log.Warn("GetQuotes: end. _proxy is not defined."); - return null; - } - List result; try @@ -240,8 +188,6 @@ namespace MTApiService { Log.ErrorFormat("GetQuotes: Exception - {0}", ex.Message); - Close(); - throw new CommunicationException($"Service connection failed! {ex.Message}"); } @@ -260,7 +206,7 @@ namespace MTApiService if (quote == null) return; - QuoteUpdated?.Invoke( quote); + QuoteUpdated?.Invoke(quote); Log.Debug("OnQuoteUpdate: end."); } @@ -287,7 +233,6 @@ namespace MTApiService { Log.Debug("OnServerStopped: begin."); - Close(); ServerDisconnected?.Invoke(this, EventArgs.Empty); Log.Debug("OnServerStopped: end."); @@ -306,7 +251,16 @@ namespace MTApiService #endregion #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 @@ -316,7 +270,6 @@ namespace MTApiService { Log.Debug("ProxyFaulted: begin."); - Close(); ServerFailed?.Invoke(this, EventArgs.Empty); Log.Debug("ProxyFaulted: end."); @@ -330,7 +283,7 @@ namespace MTApiService { Log.Debug("Dispose: begin."); - Close(); + _proxy.Dispose(); Log.Debug("Dispose: end."); } diff --git a/MTApiService/MtCommand.cs b/MTApiService/MtCommand.cs index 0e10281d..856cefbc 100755 --- a/MTApiService/MtCommand.cs +++ b/MTApiService/MtCommand.cs @@ -13,10 +13,13 @@ namespace MTApiService } [DataMember] - public int CommandType { get; private set; } + public int CommandType { get; set; } [DataMember] - public ArrayList Parameters { get; private set; } + public ArrayList Parameters { get; set; } + + [DataMember] + public int ExpertHandle { get; set; } public override string ToString() { diff --git a/MTApiService/MtEvent.cs b/MTApiService/MtEvent.cs index 594d5fa0..a3452060 100644 --- a/MTApiService/MtEvent.cs +++ b/MTApiService/MtEvent.cs @@ -20,14 +20,4 @@ namespace MTApiService return $"EventType = {EventType}; Payload = {Payload}; ExpertHandle = {ExpertHandle}"; } } - - public class MtEventArgs: EventArgs - { - public MtEventArgs(MtEvent e) - { - Event = e; - } - - public MtEvent Event { get; private set; } - } } diff --git a/MTApiService/MtExecutorManager.cs b/MTApiService/MtExecutorManager.cs index 4b23c089..230418c1 100755 --- a/MTApiService/MtExecutorManager.cs +++ b/MTApiService/MtExecutorManager.cs @@ -4,14 +4,13 @@ using log4net; namespace MTApiService { - internal class MtCommandExecutorManager : ICommandManager + internal class MtExecutorManager : ICommandManager { #region Private Fields - private static readonly ILog Log = LogManager.GetLogger(typeof(MtCommandExecutorManager)); - - private readonly List _commandExecutors = new List(); - private readonly Queue _commandTasks = new Queue(); + private static readonly ILog Log = LogManager.GetLogger(typeof(MtExecutorManager)); + private readonly List _executorList = new List(); + private readonly Dictionary _executorMap = new Dictionary(); private readonly object _locker = new object(); #endregion @@ -23,149 +22,94 @@ namespace MTApiService lock (_locker) { - _commandExecutors.Clear(); - _commandTasks.Clear(); + _executorList.Clear(); + _executorMap.Clear(); } Log.Debug("Stop: end."); } - public void AddCommandExecutor(MtExpert commandExecutor) + public void AddExecutor(ITaskExecutor executor) { - if (commandExecutor == null) - throw new ArgumentNullException(nameof(commandExecutor)); + if (executor == null) + throw new ArgumentNullException(nameof(executor)); - Log.DebugFormat("AddCommandExecutor: begin. commandExecutor = {0}", commandExecutor); + Log.DebugFormat("AddExecutor: begin. executor = {0}", executor); - var notify = false; 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; } - _commandExecutors.Add(commandExecutor); - if (_commandTasks.Count > 0) - { - notify = true; - } - } - - commandExecutor.CommandExecuted += CommandExecutor_CommandExecuted; - commandExecutor.CommandManager = this; - - if (notify) - { - NotifyCommandReady(); + _executorList.Add(executor); + _executorMap[executor.Handle] = executor; } Log.Debug("AddCommandExecutor: end."); } - public void RemoveCommandExecutor(MtExpert commandExecutor) + public void RemoveExecutor(ITaskExecutor executor) { - if (commandExecutor == null) - throw new ArgumentNullException(nameof(commandExecutor)); + if (executor == null) + throw new ArgumentNullException(nameof(executor)); - Log.DebugFormat("RemoveCommandExecutor: begin. commandExecutor = {0}", commandExecutor); + Log.DebugFormat("RemoveExecutor: begin. executor = {0}", executor); - var notify = false; 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; } - _commandExecutors.Remove(commandExecutor); - if (_commandTasks.Count > 0) + _executorList.Remove(executor); + _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; - commandExecutor.CommandManager = null; - - if (notify) + if (executor == null) { - 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(); - 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 } } diff --git a/MTApiService/MtExpert.cs b/MTApiService/MtExpert.cs index a66dd517..0b90053e 100755 --- a/MTApiService/MtExpert.cs +++ b/MTApiService/MtExpert.cs @@ -1,9 +1,10 @@ using System; using log4net; +using System.Collections.Generic; namespace MTApiService { - public class MtExpert + internal class MtExpert: ITaskExecutor { public delegate void MtQuoteHandler(MtExpert expert, MtQuote quote); public delegate void MtEventHandler(MtExpert expert, MtEvent e); @@ -12,8 +13,8 @@ namespace MTApiService private static readonly ILog Log = LogManager.GetLogger(typeof(MtExpert)); private readonly IMetaTraderHandler _mtHadler; - private MtCommandTask _commandTask; - private ICommandManager _commandManager; + private MtCommandTask _currentTask; + private readonly Queue _taskQueue = new Queue(); private readonly object _locker = new object(); #endregion @@ -42,9 +43,8 @@ namespace MTApiService { Log.DebugFormat("SendResponse: begin. response = {0}", response); - _commandTask.SetResult(response); - _commandTask = null; - FireOnCommandExecuted(); + _currentTask.SetResult(response); + _currentTask = null; Log.Debug("SendResponse: end."); } @@ -53,20 +53,16 @@ namespace MTApiService { Log.Debug("GetCommandType: called."); - var commandManager = CommandManager; - if (commandManager != null) - { - _commandTask = commandManager.DequeueCommandTask(); - } + _currentTask = DequeueTask(); - return _commandTask?.Command?.CommandType ?? 0; + return _currentTask?.Command?.CommandType ?? 0; } public object GetCommandParameter(int 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) { return command.Parameters[index]; @@ -91,7 +87,22 @@ namespace MTApiService #endregion + #region ITaskExecutor + + public void Execute(MtCommandTask task) + { + lock (_taskQueue) + { + _taskQueue.Enqueue(task); + } + + NotifyCommandReady(); + } + + #endregion + #region Properties + private 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 #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); + + Log.Debug("NotifyCommandReady: end."); } private void FireOnQuoteChanged(MtQuote quote) @@ -181,11 +185,6 @@ namespace MTApiService Deinited?.Invoke(this, EventArgs.Empty); } - private void FireOnCommandExecuted() - { - CommandExecuted?.Invoke(this, EventArgs.Empty); - } - private void FireOnMtEvent(MtEvent mtEvent) { OnMtEvent?.Invoke(this, mtEvent); @@ -195,7 +194,6 @@ namespace MTApiService #region Events public event EventHandler Deinited; public event MtQuoteHandler QuoteChanged; - public event EventHandler CommandExecuted; public event MtEventHandler OnMtEvent; #endregion } diff --git a/MTApiService/MtServer.cs b/MTApiService/MtServer.cs index e133b741..ce39bec8 100755 --- a/MTApiService/MtServer.cs +++ b/MTApiService/MtServer.cs @@ -21,7 +21,7 @@ namespace MTApiService private readonly MtService _service; private readonly List _hosts = new List(); - private readonly MtCommandExecutorManager _executorManager = new MtCommandExecutorManager(); + private readonly MtExecutorManager _executorManager = new MtExecutorManager(); private readonly List _experts = new List(); #endregion @@ -149,7 +149,7 @@ namespace MTApiService _experts.Add(expert); } - _executorManager.AddCommandExecutor(expert); + _executorManager.AddExecutor(expert); _service.OnQuoteAdded(expert.Quote); @@ -170,11 +170,20 @@ namespace MTApiService return null; } - var task = new MtCommandTask(command); - _executorManager.EnqueueCommandTask(task); + var task = _executorManager.SendCommand(command); //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; } @@ -371,7 +380,7 @@ namespace MTApiService expertsCount = _experts.Count; } - _executorManager.RemoveCommandExecutor(expert); + _executorManager.RemoveExecutor(expert); expert.Deinited -= expert_Deinited; expert.QuoteChanged -= expert_QuoteChanged; diff --git a/MtApi/ExtensionMethods.cs b/MtApi/ExtensionMethods.cs deleted file mode 100755 index 9871cb9e..00000000 --- a/MtApi/ExtensionMethods.cs +++ /dev/null @@ -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(this EventHandler eventHandler, object sender, T e) - where T : EventArgs - { - return Task.Factory.StartNew(() => - { - eventHandler?.Invoke(sender, e); - }); - } - - #endregion - } -} diff --git a/MtApi/MtApi.csproj b/MtApi/MtApi.csproj index fb8ff55c..985c00c7 100755 --- a/MtApi/MtApi.csproj +++ b/MtApi/MtApi.csproj @@ -64,7 +64,6 @@ - diff --git a/MtApi/MtApiClient.cs b/MtApi/MtApiClient.cs index 65c4a8b2..977525db 100755 --- a/MtApi/MtApiClient.cs +++ b/MtApi/MtApiClient.cs @@ -8,6 +8,7 @@ using System.ServiceModel; using MtApi.Requests; using MtApi.Responses; using Newtonsoft.Json; +using System.Threading.Tasks; namespace MtApi { @@ -27,7 +28,7 @@ namespace MtApi #endregion #region Private Fields - private readonly MtClient _client = new MtClient(); + private MtClient _client; private readonly object _locker = new object(); private MtConnectionState _connectionState = MtConnectionState.Disconnected; #endregion @@ -37,13 +38,6 @@ namespace MtApi public MtApiClient() { 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 @@ -55,8 +49,7 @@ namespace MtApi ///Port of host connection (default 8222) public void BeginConnect(string host, int port) { - Action connectAction = Connect; - connectAction.BeginInvoke(host, port, null, null); + Task.Factory.StartNew(() => Connect(host, port)); } /// @@ -65,8 +58,7 @@ namespace MtApi ///Port of host connection (default 8222) public void BeginConnect(int port) { - Action connectAction = Connect; - connectAction.BeginInvoke(port, null, null); + Task.Factory.StartNew(() => Connect(port)); } /// @@ -74,8 +66,7 @@ namespace MtApi /// public void BeginDisconnect() { - Action disconnectAction = Disconnect; - disconnectAction.BeginInvoke(null, null); + Task.Factory.StartNew(() => Disconnect(false)); } /// @@ -83,12 +74,7 @@ namespace MtApi /// public List GetQuotes() { - IEnumerable quotes; - lock (_client) - { - quotes = _client.GetQuotes(); - } - + var quotes = _client.GetQuotes(); return quotes?.Select(q => new MtQuote(q)).ToList(); } #endregion @@ -1706,44 +1692,81 @@ namespace MtApi #endregion #region Private Methods - private void Connect(string host, int port) + private MtClient Client { - UpdateConnectionState(MtConnectionState.Connecting, $"Connecting to {host}:{port}"); - try + get { - lock (_client) + lock(_locker) { - _client.Open(host, port); - _client.Connect(); + return _client; } } - catch (Exception e) + } + + private void Connect(MtClient client) + { + lock (_locker) { - UpdateConnectionState(MtConnectionState.Failed, $"Failed connection to {host}:{port}. {e.Message}"); - return; + if (_connectionState == MtConnectionState.Connected + || _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) { - UpdateConnectionState(MtConnectionState.Connecting, $"Connecting to 'localhost':{port}"); - try - { - 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(); + var client = new MtClient(port); + Connect(client); } private void OnConnected() @@ -1755,50 +1778,67 @@ namespace MtApi } } - private void Disconnect() + private void Disconnect(bool failed) { - lock (_client) - { - _client.Disconnect(); - _client.Close(); - } + var state = failed ? MtConnectionState.Disconnected : MtConnectionState.Disconnected; + var message = failed ? "Connection Failed" : "Disconnected"; - UpdateConnectionState(MtConnectionState.Disconnected, "Disconnected"); - } - - private void UpdateConnectionState(MtConnectionState state, string message) - { - var changed = false; lock (_locker) { - if (_connectionState != state) + if (_connectionState == MtConnectionState.Disconnected + || _connectionState == MtConnectionState.Failed) + return; + + if (_client != null) { - _connectionState = state; - changed = true; + _client.QuoteAdded -= _client_QuoteAdded; + _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.FireEventAsync(this, new MtConnectionEventArgs(state, message)); - } + + ConnectionStateChanged?.Invoke(this, new MtConnectionEventArgs(state, message)); } private T SendCommand(MtCommandType commandType, ArrayList commandParameters) { MtResponse response; + + var client = Client; + if (client == null) + { + throw new MtConnectionException("No connection"); + } + try { - lock (_client) - { - response = _client.SendCommand((int)commandType, commandParameters); - } + response = client.SendCommand((int)commandType, commandParameters); } catch (CommunicationException ex) { throw new MtConnectionException(ex.Message, ex); } + if (response == null) + { + throw new MtExecutionException(MtErrorCode.MtApiCustomError, "Response from MetaTrader is null"); + } + var responseValue = response.GetValue(); return responseValue != null ? (T)responseValue : default(T); } @@ -1815,25 +1855,14 @@ namespace MtApi }); var commandParameters = new ArrayList { serializer }; - MtResponseString res; - try - { - lock (_client) - { - res = (MtResponseString)_client.SendCommand((int)MtCommandType.MtRequest, commandParameters); - } - } - catch (CommunicationException ex) - { - throw new MtConnectionException(ex.Message, ex); - } + var res = SendCommand(MtCommandType.MtRequest, commandParameters); if (res == null) { throw new MtExecutionException(MtErrorCode.MtApiCustomError, "Response from MetaTrader is null"); } - var response = JsonConvert.DeserializeObject(res.Value); + var response = JsonConvert.DeserializeObject(res); if (response.ErrorCode != 0) { throw new MtExecutionException((MtErrorCode)response.ErrorCode, response.ErrorMessage); @@ -1846,37 +1875,29 @@ namespace MtApi { if (quote != null) { - if (_isBacktestingMode) - { - 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); - } + QuoteUpdate?.Invoke(this, new MtQuoteEventArgs(new MtQuote(quote))); + QuoteUpdated?.Invoke(this, quote.Instrument, quote.Bid, quote.Ask); } } private void _client_ServerDisconnected(object sender, EventArgs e) { - UpdateConnectionState(MtConnectionState.Disconnected, "MtApi is disconnected"); + Disconnect(false); } private void _client_ServerFailed(object sender, EventArgs e) { - UpdateConnectionState(MtConnectionState.Failed, "Failed connection with MtApi"); + Disconnect(true); } 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) { - QuoteAdded.FireEventAsync(this, new MtQuoteEventArgs(new MtQuote(quote))); + QuoteAdded?.Invoke(this, new MtQuoteEventArgs(new MtQuote(quote))); } private void _client_MtEventReceived(MtEvent e) @@ -1897,7 +1918,7 @@ namespace MtApi private void FireOnLastTimeBar(MtTimeBar timeBar) { - OnLastTimeBar.FireEventAsync(this, new TimeBarArgs(timeBar)); + OnLastTimeBar?.Invoke(this, new TimeBarArgs(timeBar)); } private void BacktestingReady() diff --git a/TestClients/TestApiClientUI/Form1.cs b/TestClients/TestApiClientUI/Form1.cs index 2dea1035..bc919517 100644 --- a/TestClients/TestApiClientUI/Form1.cs +++ b/TestClients/TestApiClientUI/Form1.cs @@ -135,11 +135,27 @@ namespace TestApiClientUI PrintLog(msg); } - private void apiClient_QuoteUpdated(object sender, string symbol, double bid, double ask) + private void TestCallback(string symbol) { - Console.WriteLine(@"Quote: Symbol = {0}, Bid = {1}, Ask = {2}", symbol, bid, ask); + 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) + { + Console.WriteLine(@"Quote: Symbol = {0}, Bid = {1}, Ask = {2}", symbol, bid, ask); + TestCallback(symbol); + } private void _apiClient_QuoteUpdate(object sender, MtQuoteEventArgs e) { @@ -741,13 +757,13 @@ namespace TestApiClientUI Console.WriteLine($"Finished time: {DateTime.Now}"); - using (var file = new System.IO.StreamWriter($@"{System.IO.Path.GetTempPath()}\MtApi\test.txt")) - { - foreach (var value in openPriceList) - { - file.WriteLine(value); - } - } + //using (var file = new System.IO.StreamWriter($@"{System.IO.Path.GetTempPath()}\MtApi\test.txt")) + //{ + // foreach (var value in openPriceList) + // { + // file.WriteLine(value); + // } + //} } private void button7_Click(object sender, EventArgs e) diff --git a/mq4/MtApi.ex4 b/mq4/MtApi.ex4 index b70a001e..b32018c9 100755 Binary files a/mq4/MtApi.ex4 and b/mq4/MtApi.ex4 differ diff --git a/mq4/MtApi.mq4 b/mq4/MtApi.mq4 index 5603cfa8..d1364806 100755 Binary files a/mq4/MtApi.mq4 and b/mq4/MtApi.mq4 differ