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);
}
}
}