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