namespace StockSharp.Plaza { using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using Ecng.Collections; using Ecng.Common; using StockSharp.Algo; using StockSharp.BusinessEntities; #if _WIN64 using P2ClientGateMTA64; #else using P2ClientGateMTA32; #endif /// /// Менеджер транзакций. /// public class TransactionManager { /// /// Внутренний класс для обработки Plaza сообщений. /// [ComVisible(true)] public sealed class MessageDispatcher : IP2AsyncSendEvent2 { private readonly Action _handler; internal MessageDispatcher(Action handler) { if (handler == null) throw new ArgumentNullException("handler"); _handler = handler; } void IP2AsyncSendEvent2.SendAsync2Reply(CP2BLMessage reply, uint errCode, long eventParam) { _handler(reply, errCode, eventParam); } } private readonly MessageDispatcher _messageDispatcher; private readonly CP2ConnectionClass _connection; private readonly SynchronizedMultiDictionary _transactionsByOrders = new SynchronizedMultiDictionary(); private readonly SynchronizedMultiDictionary _transactionsByTransactionIds = new SynchronizedMultiDictionary(); internal TransactionManager(PlazaConnectionPool connectionPool, TransactionIdGenerator idGenerator) { if (connectionPool == null) throw new ArgumentNullException("connectionPool"); _connection = connectionPool.CreateConnection(); connectionPool.RunConnection(_connection); Factory = new TransactionFactory(connectionPool, idGenerator); TransactionTimeOut = TimeSpan.FromSeconds(5); _messageDispatcher = new MessageDispatcher((msg, errorCode, transactionId) => ProcessReply(transactionId, msg)); IsAsync = true; } /// /// Использовать ли асинхронный режим при отправке транзакций. /// /// /// Значение по умолчанию равно true. /// public bool IsAsync { get; set; } /// /// Фабрика транзакций. /// public TransactionFactory Factory { get; private set; } /// /// Время, в течении которого ожидается ответ для транзакции. /// /// /// По-умолчанию равно 5 секундам. /// public TimeSpan TransactionTimeOut {get; set;} /// /// Обработать отправляемую транзакцию до того, как она будет отослана на сервер Plaza. /// public event Action ProcessRequest; /// /// Обработать полученную транзакцию до того, как она будет обработана . /// public event Action ProcessResponse; /// /// Отправить транзакцию на сервер Plaza. /// /// Транзакция. public void SendTransaction(Transaction transaction) { if (transaction == null) throw new ArgumentNullException("transaction"); ProcessRequest.SafeInvoke(transaction); AddTransaction(transaction); var timeOut = (uint)TransactionTimeOut.TotalMilliseconds; var plazaMessage = transaction.PlazaMessage; if (IsAsync) plazaMessage.SendAsync2(_connection, timeOut, _messageDispatcher, transaction.Id); else { var reply = plazaMessage.Send(_connection, timeOut); ProcessReply(transaction, reply); } } /// /// Получить транзакции, связанные с заявкой (например, на регистрацию и снятие). /// /// Заявка, для которой необходимо получить ее транзакции. /// Транзакции. public IEnumerable GetTransactions(Order order) { if (order == null) throw new ArgumentNullException("order"); return _transactionsByOrders.TryGetValue(order) ?? Enumerable.Empty(); } /// /// Получить транзакции (запрос и ответ) по номеру. /// /// Номер транзакции. /// Транзакции (запрос и ответ). public IEnumerable GetTransactions(long transactionId) { return _transactionsByTransactionIds.TryGetValue(transactionId); } private void AddTransaction(Transaction transaction) { if (transaction == null) throw new ArgumentNullException("transaction"); _transactionsByTransactionIds.Add(transaction.Id, transaction); foreach (var order in transaction.Orders) _transactionsByOrders.Add(order, transaction); } private void ProcessReply(long transactionId, CP2BLMessage reply) { ProcessReply(GetTransactions(transactionId).Single(), reply); } private void ProcessReply(Transaction originMessage, CP2BLMessage reply) { var replyTransaction = new Transaction(originMessage, reply); AddTransaction(replyTransaction); PlazaException errorInfo; if (replyTransaction.GetCategory() == TransactionFactory.MessagesCategory) { var code = replyTransaction.GetReplyCode(); errorInfo = code == 0 ? null : new PlazaException(code, replyTransaction.GetMessage()); } else errorInfo = new PlazaException(replyTransaction.GetErrorCode(), "Неизвестная ошибка."); replyTransaction.ErrorInfo = errorInfo; ProcessResponse.SafeInvoke(replyTransaction); } } }