using Muchinfo.MTPClient.Data.Enums;
using Muchinfo.MTPClient.Data.Model;
using Muchinfo.MTPClient.Infrastructure.Cache;
using Muchinfo.MTPClient.Infrastructure.EntityHelpers;
using Muchinfo.MTPClient.Infrastructure.Helpers;
using Muchinfo.MTPClient.Infrastructure.Utilities;
using Muchinfo.MTPClient.NetworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;
namespace Muchinfo.MTPClient.Infrastructure.LinkProxy.TCP
{
///
/// 业务代理连接
///
public class QuoteTcpLinkProxy : IDisposable
{
public string _host; ////主机地址
public int _port; ////通信端口号
private TCPManager _tcpManager;
private Timer _timer;
private bool _isGetGoodsInfo; ////是否从行情服务获取商品信息
private readonly object _lock = new object();
private bool _subscribeFlag = true;
private bool _queryDayQuote = true;
private const int c_AccountId = 2;
private const string c_checkToken = "2_TOKEN_NEKOT_"; ///2_TOKEN_NEKOT_
public TCPConnectState TcpConnectState
{
get { return (_tcpManager == null) ? TCPConnectState.Closed : _tcpManager.tcpConnectState; }
}
///
/// 创建业务连接
///
/// 是否获取商品信息
public QuoteTcpLinkProxy(string host, int port, bool isGetGoods = false)
{
_isGetGoodsInfo = isGetGoods;
_host = host;
_port = port;
_tcpManager = new TCPManager(DatagramType.Datagram40, new QuoteTcpCallback(this));
LogInfoHelper.WriteInfo("开始连接行情服务..");
_tcpManager.Connect(_host, _port, ConnectSuccess, ConnectFail);
var interval = GetSyncDayQuoteInterval();
_timer = new Timer(interval);
_timer.Elapsed += Timer_Elapsed;
}
#region Private Methods
///
/// Handles the Elapsed event of the Timer control.
///
/// The source of the event.
/// The instance containing the event data.
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
QueryDayQuote();
}
///
/// 获取同步盘面间隔,默认10分钟
///
/// System.Int32.
private int GetSyncDayQuoteInterval()
{
int interval = 10;
try
{
var value = ApplicationParameter.DayQuoteInterval;
if (value <= 0)
{
interval = 10;
}
}
catch
{
interval = 10;
}
////单位:分钟
return interval * 1000 * 60;
}
///
/// 连接失败
///
/// 错误码
/// 连接描述
private void ConnectFail(int errorCode, string desc)
{
//停止同步盘面定时器,Subscribe()成功后会启动
StopSyncTimer();
_tcpManager.ReconnectWithDisconnFirst(); //行情连接不上进需重连
//todo:通知连接失败
LogInfoHelper.WriteInfo("行情服务连接失败![" + errorCode + "] " + desc);
MessengerHelper.DefaultSend(false, MessengerTokens.QuoteCheckTokenSuccess);
}
///
/// 连接成功
///
private void ConnectSuccess()
{
if (!_isGetGoodsInfo)
{
LogInfoHelper.WriteInfo("行情服务连接成功!, 开始订阅商品.");
_subscribeFlag = true;
//连接成功订阅商品
Subscribe();
}
else
{
LogInfoHelper.WriteInfo(" 行情服务令牌校验.");
////取时间
CheckedToken();
}
}
#endregion
#region Public Methods
///
/// 发送消息,
///
/// 消息内容
/// 成功回复
/// 错误回复
public void SendPackage(TCPPackage40 package, Action successAction, Action failAction)
{
if (_tcpManager == null || TcpConnectState != TCPConnectState.Connected) return;
_tcpManager.SendMessage(package, successAction, failAction);
}
///
/// 通知通信组件开始发送心跳的方法
///
public void StartSendBeat()
{
if (_tcpManager == null) return;
_tcpManager.StartSendBeats();
}
///
/// 组包并订阅
///
public void Subscribe()
{
Subscribe(GetAvailableList(CacheManager.CacheGoodsBaseInfos));
}
///
/// 断线(开市)清盘
///
public void ClearQuote()
{
///先清盘
foreach (var goods in CacheManager.CacheGoodsBaseInfos)
{
if (_isGetGoodsInfo && goods.ContainsGoodsSrc == (int)GoodsFromScr.Brown) ////行情商品清盘
{
goods.ClearQuote();
}
else if (!_isGetGoodsInfo && (goods.ContainsGoodsSrc & (int)GoodsFromScr.Trade) > 0) ////交易商品清盘
{
goods.ClearQuote();
}
}
}
///
/// 组包并订阅
///
/// The list.
public void Subscribe(List list)
{
if (_subscribeFlag)
{
_subscribeFlag = false;
try
{
if (null == list || !list.Any())
{
list = new List()
{
new QuoteGoods("250", "99999999", "99999999", QuoteCategoryType.PreciousMetal)
};
LogInfoHelper.WriteInfo("调用Subscribe() 订阅QuoteGoods列表为空,发送不存在商品进行取消订阅!");
}
TCPPackage40 gram = new TCPPackage40
{
Tag = (byte) Datagram40TagPrimaryFunction.Trade,
Tag2 = (byte) Datagram40TagPrimaryFunction.Reserve //随便填值
};
var accountId = _isGetGoodsInfo ? c_AccountId : UserManager.CurrentTradeAccount.LoginID;
var token = _isGetGoodsInfo ? c_checkToken : UserManager.CurrentTradeAccount.Token;
byte[] bytes = list.GetSubscribeBytesV2(accountId, token);
//LogInfoHelper.WriteInfo("Login:" + BitConverter.ToString(bytes));
gram.SetData(bytes);
SendPackage(gram, LogInCallback, ExceptionCallback);
}
catch (Exception e)
{
LogInfoHelper.WriteInfo("调用Subscribe() catch:" + e.Message);
}
finally
{
_subscribeFlag = true;
}
}
}
///
/// 行情链路令牌校检
///
public void CheckedToken()
{
if (!_isGetGoodsInfo) ////交易链路不校检
return;
////行情链路校检Token取服务器时间
try
{
TCPPackage40 gram = new TCPPackage40
{
Tag = (byte)Datagram40TagPrimaryFunction.QuoteChekedToken,
Tag2 = (byte)Datagram40TagPrimaryFunction.Reserve //随便填值
};
byte[] bytes = QuoteDatagramHelper.QuoteCheckToken( c_AccountId, c_checkToken);
gram.SetData(bytes);
SendPackage(gram, CheckedSuccess, CheckedTokenError);
}
catch (Exception e)
{
_subscribeFlag = true;
LogInfoHelper.WriteInfo("调用CheckedToken() catch:" + e.Message);
}
}
public void CheckedSuccess(TCPPackage40 package)
{
try
{
var bytes = package.GetData();
var count = GetReturnCode(bytes);
//>=0: Goods个数; <0: 错误码(-1:报文解析失败,-2:Token校验失败,-3:订阅商品不合法)
LogInfoHelper.WriteInfo("行情链路令牌校检, 返回码:" + count);
if (count >= 0)
{
byte[] block = new byte[4];
Array.Copy(bytes, 4, block, 0, 4);
var servrTime = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(block, 0));
ApplicationParameter.SetSystemTime(servrTime);
MessengerHelper.DefaultSend(true, MessengerTokens.QuoteCheckTokenSuccess);
}
else
{
MessengerHelper.DefaultSend(false, MessengerTokens.QuoteCheckTokenSuccess);
}
}
catch (Exception ex)
{
LogInfoHelper.WriteInfo("查询QueryDayQuote解析失败" + ex.ToString());
}
}
///
/// Checked
///
///
///
private void CheckedTokenError(int errorId, string message)
{
MessengerHelper.DefaultSend(false, MessengerTokens.QuoteCheckTokenSuccess);
LogInfoHelper.WriteInfo("调用CheckedToken() " + errorId + ":" + message);
}
///
/// Queries the day quote.
///
public void QueryDayQuote()
{
QueryDayQuote(GetAvailableList(CacheManager.CacheGoodsBaseInfos));
}
///
/// Gets the available list.
///
/// The list.
/// IEnumerable{QuoteGoods}.
private List GetAvailableList(List list)
{
//if (list == null || !list.Any()) return null;
//////交易商品
//if (!_isGetGoodsInfo)
//{
// list = list.Where((item) => (item.ContainsGoodsSrc & (int)GoodsFromScr.trade) > 0).ToList();
//}
//else
//{
// ////行情商品
// list = list.Where((item) => (item.ContainsGoodsSrc == (int)GoodsFromScr.Brown)).ToList();
//}
//////商城、发售商品不需要订阅和查询盘面
////var result = list.Where(z => z.GoodsParameters != null && z.TradeMode != eTradeMode.TRADEMODE_SALE && z.TradeMode != eTradeMode.TRADEMODE_MALL);
//var result = list.Where(z => z.GoodsParameters != null);
//if (result == null || !result.Any()) return null;
//return result.ToList();
//订阅持仓商品+当前分类标签显示的商品+当前交易面板的商品 -- dyp
var lst = new List();
if(UserManager.CurrentGoodsId >0) lst.Add(UserManager.CurrentGoodsId);
var pList = UserManager.GetHoldSummaries(UserManager.CurrentTradeAccount.FundsAccountId);
if (pList != null && pList.Any()) lst.AddRange(pList.Select(z => (int)z.GoodsId).ToList());
if (UserManager.CurrentGoodsIds != null && UserManager.CurrentGoodsIds.Any()) lst.AddRange(UserManager.CurrentGoodsIds);
var goodses = CacheManager.CacheGoodsBaseInfos.Where(z => lst.Contains((int)z.GoodsId)) ?? new List();
return goodses.ToList();
}
///
/// 盘面查询申请
///
/// The list.
public void QueryDayQuote(List list)
{
lock (_lock)
{
if (_queryDayQuote)
{
if (list == null || !list.Any()) return;
try
{
_queryDayQuote = false;
TCPPackage40 gram = new TCPPackage40
{
Tag = (byte)Datagram40TagPrimaryFunction.Query,
Tag2 = (byte)Datagram40TagPrimaryFunction.Reserve, //随便填值
};
var accountId = _isGetGoodsInfo ? c_AccountId : UserManager.CurrentTradeAccount.LoginID;
var token = _isGetGoodsInfo ? c_checkToken : UserManager.CurrentTradeAccount.Token;
byte[] bytes = list.ToList()
.GetSubscribeBytesV2(accountId, token);
gram.SetData(bytes);
SendPackage(gram, QueryDayQuoteCallback, QueryDayQuoteExceptionCallback);
LogInfoHelper.WriteInfo("行情取盘面 数量:" + list.Count);
}
catch
{
_queryDayQuote = true;
}
}
}
}
///
/// 执行与释放或重置非托管资源相关的应用程序定义的任务。
///
public void Dispose()
{
if (_timer != null)
{
_timer.Stop();
_timer = null;
}
if (_tcpManager != null)
{
_tcpManager.Disconnect();
_tcpManager = null;
}
}
#endregion
#region SendCallbacks
///
/// Logs the in callback.
///
/// The package.
private void LogInCallback(TCPPackage40 package)
{
try
{
var bytes = package.GetData();
var count = GetReturnCode(bytes);
//>=0: Goods个数; <0: 错误码(-1:报文解析失败,-2:Token校验失败,-3:订阅商品不合法)
LogInfoHelper.WriteInfo("行情订阅成功回调, 返回码:" + count);
if (count == -2)
{
//Token校验失败, 不处理,等待交易链路回应
//MessengerHelper.DefaultSend(false, MessengerTokens.UserOfflineNoticeToken);
var random = new Random();
var second = random.Next(5000, 10000);
Thread.Sleep(second);
}
else if (count >= 0)
{
//订阅成功,发送盘面查询请求
QueryDayQuote();
if (_timer == null) return;
_timer.Stop();
_timer.Start();
}
}
catch
{
}
finally
{
_subscribeFlag = true;
StartSendBeat();
LogInfoHelper.WriteInfo("行情订阅成功回调,启动发送心跳包!");
}
}
///
/// Exceptions the callback.
///
/// The error identifier.
/// The message.
private void ExceptionCallback(int errorId, string message)
{
_subscribeFlag = true;
LogInfoHelper.WriteInfo("行情订阅出错:" + errorId + " " + message);
}
///
/// Queries the day quote callback.
///
/// The package.
private void QueryDayQuoteCallback(TCPPackage40 package)
{
try
{
var bytes = package.GetData();
var count = GetReturnCode(bytes);
//// >=0: 返回记录数目; <0: 错误码(-1:报文解析失败,-2:Token校验失败,-3:订阅商品不合法)
LogInfoHelper.WriteInfo("查询QueryDayQuote成功,返回:" + count);
if (count == -2)
{
////Token校验失败, 返回登录界面
LogInfoHelper.WriteInfo("查询QueryDayQuote 失败返回 -2");
//MessengerHelper.DefaultSend(RestartType.QuoteCheckedFail, MessengerTokens.UserOfflineNoticeToken);
}
else if (count >= 0)
{
////查询成功,更新数据
if (bytes.Length < 4) return;
////前四位为count,数据体从4位开始
var length = bytes.Length - 4;
var goodsData = bytes.Skip(4).Take(length).ToArray();
var quoteTiks = goodsData.ToQuoteTiks(true);
quoteTiks.UpdateQuoteGoodses();
}
}
catch (Exception ex)
{
LogInfoHelper.WriteInfo("查询QueryDayQuote解析失败" + ex.ToString());
}
finally
{
_queryDayQuote = true;
}
}
///
/// Exceptions the callback.
///
/// The error identifier.
/// The message.
private void QueryDayQuoteExceptionCallback(int errorId, string message)
{
_queryDayQuote = true;
LogInfoHelper.WriteInfo("盘面查询请求失败:" + message + "[" + errorId + "]");
}
///
/// Gets the return code.
///
/// The bytes.
/// System.Int32.
private int GetReturnCode(byte[] bytes)
{
/* 查询返回结构体
* 查询通用回应(包括订阅、盘面查询等)
typedef struct tagQUERY_COMMON_REQ {
int32_t iCount; // >=0: 返回记录数目; <0: 错误码(-1:报文解析失败,-2:Token校验失败,-3:订阅商品不合法)
void* pRecords; // 根据请求类别分别指向不同的结果集(GOODS/QUERY_HISTROY_RSP/...)
} QUERY_COMMON_REQ;
*/
//// >=0: 返回记录数目; <0: 错误码(-1:报文解析失败,-2:Token校验失败,-3:订阅商品不合法)
if (bytes == null || bytes.Length < 4) return -1;
var returnCodeBytes = bytes.Take(4).ToArray();
return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(returnCodeBytes, 0));
}
///
/// 停止同步盘面定时器,Subscribe()成功后会启动
///
public void StopSyncTimer()
{
if (_timer != null) _timer.Stop();
}
///
/// 设置断线重连标志
///
public void SetReconnectFlag(bool flag)
{
if (null != _tcpManager) _tcpManager.SetReconnectFlag(flag);
}
#endregion
}
}