Issue #37: Used ConcurrencyMode.Multiple for server and client side to avoid deadlocks and freezes of MtApiService

This commit is contained in:
DW
2016-11-23 10:51:50 +02:00
parent c722511c03
commit fcfd741e5b
16 changed files with 316 additions and 411 deletions
+2 -3
View File
@@ -1,8 +1,7 @@
namespace MTApiService namespace MTApiService
{ {
public interface ICommandManager internal interface ICommandManager
{ {
void EnqueueCommandTask(MtCommandTask task); MtCommandTask SendCommand(MtCommand task);
MtCommandTask DequeueCommandTask();
} }
} }
+9
View File
@@ -0,0 +1,9 @@
namespace MTApiService
{
internal interface ITaskExecutor
{
void Execute(MtCommandTask task);
int Handle { get; }
}
}
+1
View File
@@ -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" />
+1 -1
View File
@@ -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
View File
@@ -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.");
} }
+5 -2
View File
@@ -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()
{ {
-10
View File
@@ -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; }
}
} }
+55 -111
View File
@@ -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
View File
@@ -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
} }
+15 -6
View File
@@ -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;
-37
View File
@@ -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
}
}
-1
View File
@@ -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
View File
@@ -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()
+24 -8
View File
@@ -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)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.