Ver código fonte

行情订阅部分代码

zhou.xiaoning 4 anos atrás
pai
commit
d4b061ca15

+ 3 - 3
RMA/app/src/main/java/cn/muchinfo/rma/business/quote/QuoteManager.kt

@@ -30,7 +30,7 @@ class QuoteManager {
      */
     fun addSubscriptQuote(
         tag: String,
-        goodsCodeSet: ConcurrentSkipListSet<String>,
+        goodsCodeSet: Set<String>,
         callback: ((isCompleted: Boolean, err: Error?) -> Unit)?
     ) {
         val app = MyApplication.getInstance().guard {
@@ -54,7 +54,7 @@ class QuoteManager {
         }
 
         // 添加到当前订阅列表中
-        subcriptMap[tag] = goodsCodeSet
+        subcriptMap[tag] = ConcurrentSkipListSet(goodsCodeSet)
         val addSet = HashSet<String>()
         for (item in goodsCodeSet) {
             if (item !in currentGoodsSet) {
@@ -123,7 +123,7 @@ class QuoteManager {
         val reqPacket = QuoteAdapter.buildSubscriptPacket(
             currentGoodsSet.toSet(),
             loginRsp.token,
-            loginRsp.loginID.toInt()
+            loginRsp.loginID
         )
         // 发送订阅请求
         val quoteSocketManager = MyApplication.getInstance()?.quoteSocketManager

+ 308 - 9
RMA/app/src/main/java/cn/muchinfo/rma/business/quote/adapter/QuoteAdapter.kt

@@ -23,7 +23,7 @@ class QuoteAdapter {
         fun buildSubscriptPacket(
             goodsCodes: Set<String>,
             token: String,
-            loginID: Int
+            loginID: Long
         ): Packet40 {
             // 订阅商品列表
             val quoteSubscriptGoodsIfs: ArrayList<MoQuoteSubscriptGoodsInfo> =
@@ -59,7 +59,7 @@ class QuoteAdapter {
             }
 
             // 判断订阅是否成功
-            val count = ByteBuffer.wrap(packet.content.sliceArray(0..4), 1, 4)
+            val count = ByteBuffer.wrap(packet.content.sliceArray(0 until 4), 0, 4)
                 .order(ByteOrder.BIG_ENDIAN).int
             if (count == -1 || count == -2 || count == -3) {
                 // 订阅失败, Token校验失败, 无对应商品信息
@@ -72,21 +72,320 @@ class QuoteAdapter {
             }
 
             // 获取商品订阅信息
-            val quoteSubscriptGoodsInfos: ArrayList<MoQuoteSubscriptGoodsInfo> = ArrayList()
+            val quoteSubscriptGoodsIfs: ArrayList<MoQuoteSubscriptGoodsInfo> = ArrayList()
             try {
-                for (i in 4..packet.content.size step 66) {
-                    quoteSubscriptGoodsInfos.add(
+                for (i in 4 until packet.content.size step 66) {
+                    quoteSubscriptGoodsIfs.add(
                         MoQuoteSubscriptGoodsInfo(
-                            data = packet.content.sliceArray(
-                                i..i + 66
-                            )
+                            data = packet.content.sliceArray(i until i + 66)
                         )
                     )
                 }
             } catch (e: Exception) {
+                print(e)
             }
 
-            return Triple(true, null, quoteSubscriptGoodsInfos)
+            return Triple(true, null, quoteSubscriptGoodsIfs)
+        }
+
+        /**
+         * 分解实时行情数据
+         * @param data ByteArray 实时行情二进制数据
+         * @return List<Map<String, String>>? 分解后行情列表数据
+         */
+        fun splitQuoteGoods(data: ByteArray): List<Map<String, String>>? {
+            // 可能会传入空数组
+            if (data.isEmpty()) {
+                return null
+            }
+
+            // 分解行正则
+            val regRow = """/10\s.*?11/""".toRegex()
+            // 分解单行 key 正则
+            val regKey = """/01\s.*?02/""".toRegex()
+            // 分解单行 value 正则
+            val regValue = """/02\s.*?01|02\s.*?11/""".toRegex()
+
+            // ByteArray to Hex string
+            // FF 10 01 55 02 45 46 47 48 01 66 02 48 47 46 45 11 10 01 77 02 AA BB CC DD 11 00
+            val hexString =
+                data.joinToString(separator = " ") { Integer.toHexString(it.toInt()) }
+
+            val goodsQuoteTiks = arrayListOf<Map<String, String>>()
+
+            // 获取单行行情
+            val rows = regRow.findAll(hexString).toList().flatMap(MatchResult::groupValues)
+            for (row in rows) {
+                // 单行行情: 0x10 ... 0x01 key 0x02 value 0x01 key 0x02 value ... 0x11
+                // 10 01 55 02 45 46 47 48 01 66 02 48 47 46 45 11
+                // 获取 key value 表列
+                val keys = regKey.findAll(row).toList().flatMap(MatchResult::groupValues)
+                val values = regValue.findAll(row).toList().flatMap(MatchResult::groupValues)
+
+                // 将 key value 写入map中
+                val goodsQuoteTik = mutableMapOf<String, String>()
+                for (i in keys.indices) {
+                    // 01 55 02
+                    // Hex string to Int
+                    val key = keys[i].substring(3, 5).toInt(16)
+
+                    // 02 45 46 47 48 01
+                    // 02 48 47 46 45 11
+                    val value = values[i].split(" ").map { it.toByte(16) }.toString()
+                    getQuoteTikField(goodsQuoteTik, key, value)
+                }
+
+                goodsQuoteTiks.add(goodsQuoteTik)
+            }
+
+            return goodsQuoteTiks
+        }
+
+        private fun getQuoteTikField(goodsQuoteTik: MutableMap<String, String>, key: Int, value: String)  {
+            when (key) {
+                0x56 -> {
+                    goodsQuoteTik["ExchangeCode"] = value }
+                0x21 -> {
+                    goodsQuoteTik["GoodsCode"] = value }
+                0x24 -> {
+                    goodsQuoteTik["Last"] = value }
+                0x5B -> {
+                    goodsQuoteTik["HoldVolume"] = value }
+                0x25 -> {
+                    goodsQuoteTik["LastVolume"] = value }
+                0x3C -> {
+                    goodsQuoteTik["PreHoldVolume"] = value }
+                0x32 -> {
+                    goodsQuoteTik["PreSettle"] = value }
+                0x33 -> {
+                    goodsQuoteTik["Settle"] = value }
+                0x29 -> {
+                    goodsQuoteTik["TotalTurnover"] = value }
+                0x28 -> {
+                    goodsQuoteTik["TotalVolume"] = value }
+                0x35 -> {
+                    goodsQuoteTik["LimitHigh"] = value }
+                0x36 -> {
+                    goodsQuoteTik["LimitLow"] = value }
+                "L".toInt() -> {
+                    goodsQuoteTik["Ask"] = value }
+                "M".toInt() -> {
+                    goodsQuoteTik["Ask2"] = value }
+                "N".toInt() -> {
+                    goodsQuoteTik["Ask3"] = value }
+                "O".toInt() -> {
+                    goodsQuoteTik["Ask4"] = value }
+                "P".toInt() -> {
+                    goodsQuoteTik["Ask5"] = value }
+                "Q".toInt() -> {
+                    goodsQuoteTik["AskVolume"] = value }
+                "R".toInt() -> {
+                    goodsQuoteTik["AskVolume2"] = value }
+                "S".toInt() -> {
+                    goodsQuoteTik["AskVolume3"] = value }
+                "T".toInt() -> {
+                    goodsQuoteTik["AskVolume4"] = value }
+                "U".toInt() -> {
+                    goodsQuoteTik["AskVolume5"] = value }
+                "B".toInt() -> {
+                    goodsQuoteTik["Bid"] = value }
+                "C".toInt() -> {
+                    goodsQuoteTik["Bid2"] = value }
+                "D".toInt() -> {
+                    goodsQuoteTik["Bid3"] = value }
+                "E".toInt() -> {
+                    goodsQuoteTik["Bid4"] = value }
+                "F".toInt() -> {
+                    goodsQuoteTik["Bid5"] = value }
+                "G".toInt() -> {
+                    goodsQuoteTik["BidVolume"] = value }
+                "H".toInt() -> {
+                    goodsQuoteTik["BidVolume2"] = value }
+                "I".toInt() -> {
+                    goodsQuoteTik["BidVolume3"] = value }
+                "J".toInt() -> {
+                    goodsQuoteTik["BidVolume4"] = value }
+                "K".toInt() -> {
+                    goodsQuoteTik["BidVolume5"] = value }
+                ",".toInt() -> {
+                    goodsQuoteTik["Highest"] = value }
+                "-".toInt() -> {
+                    goodsQuoteTik["Lowest"] = value }
+                "@".toInt() -> {
+                    goodsQuoteTik["Date"] = value }
+                "A".toInt() -> {
+                    goodsQuoteTik["Time"] = value }
+                "+".toInt() -> {
+                    goodsQuoteTik["PreClose"] = value }
+                ".".toInt() -> {
+                    goodsQuoteTik["Opened"] = value }
+                0x5C -> {
+                    goodsQuoteTik["ExercisePrice"] = value }
+                0x7A -> {
+                    goodsQuoteTik["Inventory"] = value }
+                0x7C -> {
+                    goodsQuoteTik["ExchangeDate"] = value }
+                0x70 -> {  // 1011 新增
+                    goodsQuoteTik["strBidOrder"] = value }
+                0x71 -> {
+                    goodsQuoteTik["strBidOrder2"] = value }
+                0x72 -> {
+                    goodsQuoteTik["strBidOrder3"] = value }
+                0x73 -> {
+                    goodsQuoteTik["strBidOrder4"] = value }
+                0x74 -> {
+                    goodsQuoteTik["strBidOrder5"] = value }
+                0x75 -> {
+                    goodsQuoteTik["strAskOrder"] = value }
+                0x76 -> {
+                    goodsQuoteTik["strAskOrder2"] = value }
+                0x77 -> {
+                    goodsQuoteTik["strAskOrder3"] = value }
+                0x78 -> {
+                    goodsQuoteTik["strAskOrder4"] = value }
+                0x79 -> {
+                    goodsQuoteTik["strAskOrder5"] = value }
+                0x7D -> {
+                    goodsQuoteTik["putOptionPremiums"] = value }
+                0x7E -> {
+                    goodsQuoteTik["putOptionPremiums2"] = value }
+                0x80 -> {
+                    goodsQuoteTik["putOptionPremiums3"] = value }
+                0x81 -> {
+                    goodsQuoteTik["putOptionPremiums4"] = value }
+                0x82 -> {
+                    goodsQuoteTik["putOptionPremiums5"] = value }
+                0x83 -> {
+                    goodsQuoteTik["callOptionPremiums"] = value }
+                0x84 -> {
+                    goodsQuoteTik["callOptionPremiums2"] = value }
+                0x85 -> {
+                    goodsQuoteTik["callOptionPremiums3"] = value }
+                0x86 -> {
+                    goodsQuoteTik["callOptionPremiums4"] = value }
+                0x87 -> {
+                    goodsQuoteTik["callOptionPremiums5"] = value }
+                0x7B -> {
+                    goodsQuoteTik["orderID "] = value }
+                0x88 -> { // 非交易成交总量
+                    goodsQuoteTik["nonTotalVolume"] = value }
+                0x89 -> { // 非交易总持仓量
+                    goodsQuoteTik["nonTotalHolderVolume"] = value }
+                0x8A -> { // 非交易成交总金额
+                    goodsQuoteTik["nonTotalTurnover"] = value }
+                0x8B -> { // 非交易成交总数
+                    goodsQuoteTik["nonTotalLot"] = value }
+                0x8C -> {
+                    goodsQuoteTik["markPrice"] = value }
+                0x8D -> {
+                    goodsQuoteTik["fundSrate"] = value }
+                0x92 -> {
+                    goodsQuoteTik["BidVolume6"] = value }
+                0x93 -> {
+                    goodsQuoteTik["BidVolume7"] = value }
+                0x94 -> {
+                    goodsQuoteTik["BidVolume8"] = value }
+                0x95 -> {
+                    goodsQuoteTik["BidVolume9"] = value }
+                0x96 -> {
+                    goodsQuoteTik["BidVolume10"] = value }
+                0x97 -> {
+                    goodsQuoteTik["Ask6"] = value }
+                0x98 -> {
+                    goodsQuoteTik["Ask7"] = value }
+                0x99 -> {
+                    goodsQuoteTik["Ask8"] = value }
+                0x9A -> {
+                    goodsQuoteTik["Ask9"] = value }
+                0x9B -> {
+                    goodsQuoteTik["Ask10"] = value }
+                0x9C -> {
+                    goodsQuoteTik["AskVolume6"] = value }
+                0x9D -> {
+                    goodsQuoteTik["AskVolume7"] = value }
+                0x9E -> {
+                    goodsQuoteTik["AskVolume8"] = value }
+                0xA0 -> {
+                    goodsQuoteTik["AskVolume9"] = value }
+                0xA1 -> {
+                    goodsQuoteTik["AskVolume10"] = value }
+                0xA2 -> {
+                    goodsQuoteTik["strBidOrder6"] = value }
+                0xA3 -> {
+                    goodsQuoteTik["strBidOrder7"] = value }
+                0xA4 -> {
+                    goodsQuoteTik["strBidOrder8"] = value }
+                0xA5 -> {
+                    goodsQuoteTik["strBidOrder9"] = value }
+                0xA6 -> {
+                    goodsQuoteTik["strBidOrder10"] = value }
+                0xA7 -> {
+                    goodsQuoteTik["strAskOrder6"] = value }
+                0xA8 -> {
+                    goodsQuoteTik["strAskOrder7"] = value }
+                0xA9 -> {
+                    goodsQuoteTik["strAskOrder8"] = value }
+                0xAA -> {
+                    goodsQuoteTik["strAskOrder9"] = value }
+                0xAB -> {
+                    goodsQuoteTik["strAskOrder10"] = value }
+                0xAC -> {
+                    goodsQuoteTik["Bid6"] = value }
+                0xAD -> {
+                    goodsQuoteTik["Bid7"] = value }
+                0x8E -> {
+                    goodsQuoteTik["Bid8"] = value }
+                0x90 -> {
+                    goodsQuoteTik["Bid9"] = value }
+                0x91 -> {
+                    goodsQuoteTik["Bid10"] = value }
+                0xAE -> { // 买2的订单数量
+                    goodsQuoteTik["bidOrderVol1"] = value }
+                0xAF -> { // 买2的订单数量
+                    goodsQuoteTik["bidOrderVol2"] = value }
+                0xB0 -> { // 买3的订单数量
+                    goodsQuoteTik["bidOrderVol3"] = value }
+                0xB1 -> { // 买4的订单数量
+                    goodsQuoteTik["bidOrderVol4"] = value }
+                0xB2 -> { // 买5的订单数量
+                    goodsQuoteTik["bidOrderVol5"] = value }
+                0xB3 -> { // 买6的订单数量
+                    goodsQuoteTik["bidOrderVol6"] = value }
+                0xB4 -> { // 买7的订单数量
+                    goodsQuoteTik["bidOrderVol7"] = value }
+                0xB5 -> { // 买8的订单数量
+                    goodsQuoteTik["bidOrderVol8"] = value }
+                0xB6 -> { // 买9的订单数量
+                    goodsQuoteTik["bidOrderVol9"] = value }
+                0xB7 -> { // 买10的订单数量
+                    goodsQuoteTik["bidOrderVol10"] = value }
+                0xB8 -> { // 卖1的订单数量
+                    goodsQuoteTik["askOrderVol1"] = value }
+                0xB9 -> { // 卖2的订单数量
+                    goodsQuoteTik["askOrderVol2"] = value }
+                0xBA -> { // 卖3的订单数量
+                    goodsQuoteTik["askOrderVol3"] = value }
+                0xBB -> { // 卖4的订单数量
+                    goodsQuoteTik["askOrderVol4"] = value }
+                0xBC -> { // 卖5的订单数量
+                    goodsQuoteTik["askOrderVol5"] = value }
+                0xBD -> { // 卖6的订单数量
+                    goodsQuoteTik["askOrderVol6"] = value }
+                0xBE -> { // 卖7的订单数量
+                    goodsQuoteTik["askOrderVol7"] = value }
+                0xBF -> { // 卖8的订单数量
+                    goodsQuoteTik["askOrderVol8"] = value }
+                0xC0 -> { // 卖9的订单数量
+                    goodsQuoteTik["askOrderVol9"] = value }
+                0xC1 -> { // 卖10的订单数量
+                    goodsQuoteTik["askOrderVol10"] = value }
+                0x59 -> { // 买经济盘数据
+                    goodsQuoteTik["bidQueueInfo"] = value }
+                0x5A -> { // 卖经济盘数据
+                    goodsQuoteTik["askQueueInfo"] = value }
+                0x6B -> {  // 交易类型
+                    goodsQuoteTik["publicTradetype"] = value }
+            }
         }
     }
 }

+ 2 - 2
RMA/app/src/main/java/cn/muchinfo/rma/business/quote/models/MoQuotaSubscriptReq.kt

@@ -12,7 +12,7 @@ import java.nio.ByteOrder
  * @constructor
  */
 class MoQuotaSubscriptReq(
-    var accountID: Int = 0,
+    var accountID: Long = 0,
     var token: String = "",
     var count: Int = 0,
     var quoteSubscriptGoodsInfo: List<MoQuoteSubscriptGoodsInfo> = ArrayList<MoQuoteSubscriptGoodsInfo>()
@@ -25,7 +25,7 @@ class MoQuotaSubscriptReq(
         var data = byteArrayOf()
 
         // accountID
-        data += ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putInt(accountID).array()
+        data += ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(accountID).array()
         // Token, 不足64 byte需要补足
         val tmp = token.toByteArray()
         data += tmp

+ 2 - 1
RMA/app/src/main/java/cn/muchinfo/rma/business/quote/models/MoQuoteSubscriptGoodsInfo.kt

@@ -21,7 +21,8 @@ class MoQuoteSubscriptGoodsInfo(
 
         subState = data[0].toInt()
         exchangeCode = data[1].toInt()
-        goodsCode = String(data.sliceArray(2..66), Charsets.UTF_8).replace("""\0""", "")
+
+        goodsCode = String(data.sliceArray(2 until 66), Charsets.US_ASCII).replace("""\0""", "")
     }
 
     /**

+ 87 - 40
RMA/app/src/main/java/cn/muchinfo/rma/netcore/socket/MTP2Socket.java

@@ -25,57 +25,102 @@ import cn.muchinfo.rma.netcore.socket.core.SocketListener;
 
 /**
  * MTP2.0 长链通信类
+ *
  * @param <T> Packet40 or Packet50
  */
 public class MTP2Socket<T> {
-    /** 报文类型,0 - 4.0行情报文,1 - 5.0交易报文 */
+    /**
+     * 报文类型,0 - 4.0行情报文,1 - 5.0交易报文
+     */
     public int packetType;
-    /** 服务端IP地址 */
+    /**
+     * 服务端IP地址
+     */
     public String IP = "";
-    /** 服务端端口 */
+    /**
+     * 服务端端口
+     */
     public String port = "";
-    /** 当前连接状态,0 - 未连接,1 - 连接中,2 - 已连接 */
+    /**
+     * 当前连接状态,0 - 未连接,1 - 连接中,2 - 已连接
+     */
     public int connState = 0;
 
-    /** 回调委托 */
+    /**
+     * 回调委托
+     */
     public MTP2SocketListener<T> listener;
 
-    /** Socket 对象 */
+    /**
+     * Socket 对象
+     */
     private SocketEngine socket;
-    /** 当前流水号 */
+    /**
+     * 当前流水号
+     */
     private int currentSerial = 1;
-    /** 默认超时时长(秒) */
+    /**
+     * 默认超时时长(秒)
+     */
     private final int timeOutInterval = 30 * 1000;
-    /** 信息发送异步建值对,ConcurrentHashMap会自动处理线程安全问题 */
+    /**
+     * 信息发送异步建值对,ConcurrentHashMap会自动处理线程安全问题
+     */
     private final Map<String, AsyncSocketTask<T>> asyncTaskMap = new ConcurrentHashMap<>();
-    /** 段包 */
+    /**
+     * 段包
+     */
     private List<Byte> cache = null;
-    /** 当前处理的段包报文的原长度 */
+    /**
+     * 当前处理的段包报文的原长度
+     */
     private long cachePackageLength = 0;
 
-    /** 心跳专用定时器 */
+    /**
+     * 心跳专用定时器
+     */
     private Timer beatTimer = null;
-    /** 最后一次收到心跳的时间 */
+    /**
+     * 最后一次收到心跳的时间
+     */
     private Date lastRecevieBeatTime = null;
 
-    /** 开始重连 */
+    /**
+     * 开始重连
+     */
     public static final int ReconnectChangeState_BeginReconnect = 0;
-    /** 尝试重连失败,将在下一个周期后再次尝试重连 */
+    /**
+     * 尝试重连失败,将在下一个周期后再次尝试重连
+     */
     public static final int ReconnectChangeState_FailAndWaitPeriod = 1;
-    /** 重连成功,将进行业务操作 */
+    /**
+     * 重连成功,将进行业务操作
+     */
     public static final int ReconnectChangeState_ReconnectSuccessed = 2;
-    /** 重连成功后业务操作失败,由业务模块发起 */
+    /**
+     * 重连成功后业务操作失败,由业务模块发起
+     */
     public static final int ReconnectChangeState_LoginFail = 3;
-    /** 重连成功后业务操作成功(包括交易服务的账户登录状态更新或行情服务的行情订阅等) */
+    /**
+     * 重连成功后业务操作成功(包括交易服务的账户登录状态更新或行情服务的行情订阅等)
+     */
     public static final int ReconnectChangeState_Logined = 4;
 
-    /** 外部是否要求停止断网重连操作标志 */
+    /**
+     * 外部是否要求停止断网重连操作标志
+     */
     public boolean isBrokenReconnecting = false;
-    /** 当前是否正在进行断网重连 */
+    /**
+     * 当前是否正在进行断网重连
+     */
     private boolean isReconnecting = false;
-    /** 当前是否可进行断网重连;业务与网络分离,而断网重连需要在登录账号后(或行情订阅成功后)才可进行 */
+    /**
+     * 当前是否可进行断网重连;业务与网络分离,而断网重连需要在登录账号后(或行情订阅成功后)才可进行
+     */
     public boolean canReconnect = false;
-    /** 断网重连失败后重试的Timer */
+    /**
+     * 断网重连失败后重试的Timer
+     */
     private Timer reconnectTimer = null;
 
     public MTP2Socket(int packetType, MTP2SocketListener<T> listener) {
@@ -121,7 +166,8 @@ public class MTP2Socket<T> {
 
     /**
      * 连接服务端
-     * @param IP 服务端IP地址
+     *
+     * @param IP   服务端IP地址
      * @param port 服务端端口号
      */
     public void conn(String IP, String port, Callback<T> callback) {
@@ -247,7 +293,7 @@ public class MTP2Socket<T> {
         // 发送心跳
         if (this.packetType == 0) {
             // 4.0
-            Packet40 packet = new Packet40((byte)0x12, (short) 0, null);
+            Packet40 packet = new Packet40((byte) 0x12, (short) 0, null);
             this.send40(packet, 0, null);
         } else if (this.packetType == 1) {
             // 5.0
@@ -271,8 +317,9 @@ public class MTP2Socket<T> {
 
     /**
      * 发送报文
-     * @param packet 目标报文, Packet40 or Packet50
-     * @param rsp 回复号。P为Package40对象时为回复大类号,同时必传;P为Package50对象为回复功能码,可传零。
+     *
+     * @param packet   目标报文, Packet40 or Packet50
+     * @param rsp      回复号。P为Package40对象时为回复大类号,同时必传;P为Package50对象为回复功能码,可传零。
      * @param callback 回调
      */
     public void send(T packet, int rsp, Callback<T> callback) {
@@ -284,10 +331,10 @@ public class MTP2Socket<T> {
 
         if (packet instanceof Packet40) {
             // 4.0 报文
-            this.send40((Packet40)packet, rsp, callback);
+            this.send40((Packet40) packet, rsp, callback);
         } else if (packet instanceof Packet50) {
             //  5.0 报文
-            this.send50((Packet50)packet, rsp, callback);
+            this.send50((Packet50) packet, rsp, callback);
         } else {
             if (null != callback)
                 callback.onFail(new Error("错误的报文"));
@@ -310,7 +357,7 @@ public class MTP2Socket<T> {
                 @Override
                 public void run() {
                     // 超时触发
-                    AsyncSocketTask<T> asyncSocketTask  =
+                    AsyncSocketTask<T> asyncSocketTask =
                             MTP2Socket.this.asyncTaskMap.get(this.key);
                     if (null != asyncSocketTask) {
                         if (null != asyncSocketTask.callback) {
@@ -339,7 +386,7 @@ public class MTP2Socket<T> {
     }
 
     // 5.0报文发送方法
-    private void send50(Packet50 packet, int rspFunCode , Callback<T> callback) {
+    private void send50(Packet50 packet, int rspFunCode, Callback<T> callback) {
         // 设置流水号
         packet.serialNumber = this.currentSerial;
         this.currentSerial++;
@@ -353,7 +400,7 @@ public class MTP2Socket<T> {
                 @Override
                 public void run() {
                     // 超时触发
-                    AsyncSocketTask<T> asyncSocketTask  =
+                    AsyncSocketTask<T> asyncSocketTask =
                             MTP2Socket.this.asyncTaskMap.get(this.key);
                     if (null != asyncSocketTask) {
                         if (null != asyncSocketTask.callback) {
@@ -382,7 +429,7 @@ public class MTP2Socket<T> {
 
     // 接收报文处理方法
     private void disposeReceive(byte[] bytes) {
-        for (byte b: bytes) {
+        for (byte b : bytes) {
             if (null == this.cache) {
                 // 新报文
                 if (b != (byte) 0xFF) {
@@ -418,7 +465,7 @@ public class MTP2Socket<T> {
 
                 // 判断是否已经到包尾
                 if (this.cache.size() == this.cachePackageLength) {
-                    if (b != (byte)0x0) {
+                    if (b != (byte) 0x0) {
                         // 接收到尾字节不是0x0的错误数据包
                         this.cache = null;
                         this.cachePackageLength = 0;
@@ -446,15 +493,15 @@ public class MTP2Socket<T> {
             return;
         }
 
-        if (packet.mainClassNumber == (byte)0x12 || packet.mainClassNumber == (byte)0x41 || packet.mainClassNumber == (byte)0x42) {
+        if (packet.mainClassNumber == (byte) 0x12 || packet.mainClassNumber == (byte) 0x41 || packet.mainClassNumber == (byte) 0x42) {
             // 推送类报文, 0x12 - 心跳, 0x41 - 实时行情推送, 0x42 - 控制信号
-            if (packet.mainClassNumber == (byte)0x12) {
+            if (packet.mainClassNumber == (byte) 0x12) {
                 // 心跳
                 this.lastRecevieBeatTime = new Date();
             } else {
                 // FIXME: - 这里的泛型强转应该找更好的方法
                 if (null != this.listener)
-                    this.listener.onReceivePush(this, (T)packet);
+                    this.listener.onReceivePush(this, (T) packet);
             }
         } else {
             // 非推送类报文
@@ -465,7 +512,7 @@ public class MTP2Socket<T> {
                 asyncSocketTask.timeOut.cancel();
                 asyncSocketTask.timeOut = null;
                 if (null != asyncSocketTask.callback)
-                    asyncSocketTask.callback.onSuccess((T)packet);
+                    asyncSocketTask.callback.onSuccess((T) packet);
                 this.asyncTaskMap.remove(key);
             }
         }
@@ -487,7 +534,7 @@ public class MTP2Socket<T> {
             } else {
                 // 推送类报文
                 if (null != this.listener)
-                    this.listener.onReceivePush(this, (T)packet);
+                    this.listener.onReceivePush(this, (T) packet);
             }
         } else {
             // 非推送类报文
@@ -501,7 +548,7 @@ public class MTP2Socket<T> {
                 asyncSocketTask.timeOut.cancel();
                 asyncSocketTask.timeOut = null;
                 if (null != asyncSocketTask.callback)
-                    asyncSocketTask.callback.onSuccess((T)packet);
+                    asyncSocketTask.callback.onSuccess((T) packet);
                 this.asyncTaskMap.remove(key);
             }
         }
@@ -581,7 +628,7 @@ public class MTP2Socket<T> {
      */
     private void callAllAsyncTaskOnReconnecting() {
         try {
-            for (String key: this.asyncTaskMap.keySet()) {
+            for (String key : this.asyncTaskMap.keySet()) {
                 AsyncSocketTask<T> asyncSocketTask = this.asyncTaskMap.get(key);
                 if (null != asyncSocketTask.callback) {
                     asyncSocketTask.callback.onFail(new Error("发生断网重连"));

+ 1 - 0
RMA/app/src/main/java/cn/muchinfo/rma/netcore/socket/core/SocketEngine.java

@@ -103,6 +103,7 @@ public class SocketEngine {
             stopSocket();
             e.printStackTrace();
         } catch (Exception e) {
+            // FIXME: - 虞杰你个B不给我个回调!
             stopSocket();
             e.printStackTrace();
         }

+ 18 - 15
RMA/app/src/main/java/cn/muchinfo/rma/protobuf/classNumber/ClassNumber.java

@@ -1,34 +1,37 @@
 package cn.muchinfo.rma.protobuf.classNumber;
 
+/**
+ * 行情报文大小类号
+ */
 public class ClassNumber {
-    /// 心跳
+    /** 心跳 */
     public static final int MainClassNumber_Quota_Beat = 0x12;
-    /// 实时行情订阅请求
+    /** 实时行情订阅请求 */
     public static final int MainClassNumber_Quota_SubscriptReq = 0x20;
-    /// 实时行情订阅响应
+    /** 实时行情订阅响应 */
     public static final int MainClassNumber_Quota_SubscriptRsp = 0x21;
-    /// 盘面查询请求
+    /** 盘面查询请求 */
     public static final int MainClassNumber_Quota_QuotationReq = 0x22;
-    /// 盘面查询应答
+    /** 盘面查询应答 */
     public static final int MainClassNumber_Quota_QuotationRsp = 0x23;
-    /// 历史数据查询请求(1.0带盘面不带结算价)主要用于非日线的解析
+    /** 历史数据查询请求(1.0带盘面不带结算价)主要用于非日线的解析 */
     public static final int MainClassNumber_Quota_HistoryReq = 0x30;
-    /// 历史数据查询应答(1.0带盘面不带结算价)
+    /** 历史数据查询应答(1.0带盘面不带结算价) */
     public static final int MainClassNumber_Quota_HistoryRsp = 0x31;
-    /// 历史数据查询请求, 日线专用, 带当日周期数据
+    /** 历史数据查询请求, 日线专用, 带当日周期数据 */
     public static final int MainClassNumber_Quota_HistoryReq_Day = 0x46;
-    /// 历史数据查询应答, 日线专用
+    /** 历史数据查询应答, 日线专用 */
     public static final int MainClassNumber_Quota_HistoryRsp_Day = 0x47;
-    /// 历史数据查询请求(1.0带盘面并带结算价),主要用于日线的解析
+    /** 历史数据查询请求(1.0带盘面并带结算价),主要用于日线的解析 */
     public static final int MainClassNumber_Quota_HistoryReq_MinutesDay = 0x38;
-    /// 历史数据查询应答(1.0带盘面并带结算价)
+    /** 历史数据查询应答(1.0带盘面并带结算价) */
     public static final int MainClassNumber_Quota_HistoryRsp_MinutesDay = 0x39;
-    /// 历史数据查询请求(2.0), 日线专用, 不带当日周期数据
+    /** 历史数据查询请求(2.0), 日线专用, 不带当日周期数据 */
     public static final int MainClassNumber_Quota_HistoryReq_Day_NoToDay = 0x46;
-    /// 历史数据查询应答(2.0)
+    /** 历史数据查询应答(2.0) */
     public static final int MainClassNumber_Quota_HistoryRsp_Day_NoToDay = 0x47;
-    /// 实时行情推送
+    /** 实时行情推送 */
     public static final int MainClassNumber_Quota_QuotaPush = 0x41;
-    /// 控制信号
+    /** 控制信号 */
     public static final int MainClassNumber_Quota_Control = 0x42;
 }

+ 41 - 9
RMA/app/src/main/java/cn/muchinfo/rma/view/MainViewModel.kt

@@ -4,10 +4,17 @@ import androidx.lifecycle.MutableLiveData
 import cn.muchinfo.rma.global.GlobalDataCollection
 import cn.muchinfo.rma.global.database.DataBase
 import cn.muchinfo.rma.netManage.base.InteractiveException
+import cn.muchinfo.rma.netcore.packet.Packet40
+import cn.muchinfo.rma.netcore.packet.Packet50
+import cn.muchinfo.rma.netcore.socket.Callback
+import cn.muchinfo.rma.view.autoWidget.guard
 import cn.muchinfo.rma.view.autoWidget.toArrayList
 import cn.muchinfo.rma.view.base.BaseViewModel
+import cn.muchinfo.rma.view.base.app.Constant
+import com.blankj.utilcode.util.SPUtils
 import kotlinx.coroutines.*
 import mtp.polymer.com.autowidget.utils.TaskUiModel
+import java.lang.Error
 import java.lang.Exception
 
 class MainViewModel : BaseViewModel() {
@@ -42,8 +49,9 @@ class MainViewModel : BaseViewModel() {
             }
         }
         try {
-            GlobalDataCollection.instance?.errorCodeEntityList = DataBase.getInstance().errorCodeDao().getList()
-        }catch (e : Exception){
+            GlobalDataCollection.instance?.errorCodeEntityList =
+                DataBase.getInstance().errorCodeDao().getList()
+        } catch (e: Exception) {
             e.printStackTrace()
         }
 
@@ -89,12 +97,12 @@ class MainViewModel : BaseViewModel() {
     /**
      * 获取用户账号信息
      */
-    fun getUserAccount(){
+    fun getUserAccount() {
         val params = mutableMapOf<String, String>().apply {
             put("userID", GlobalDataCollection.instance?.loginRsp?.userID.toString())
         }
 
-        MyApplication.getInstance()?.initializeManager?.getUserAccount(params = params){isSuccess, respData, error ->
+        MyApplication.getInstance()?.initializeManager?.getUserAccount(params = params) { isSuccess, respData, error ->
             isInitiaDataSuccess.postValue(isSuccess)
             if (isSuccess) {
                 queryErmcpTradePosition()//第三步请求持仓头寸
@@ -108,15 +116,39 @@ class MainViewModel : BaseViewModel() {
     /**
      * 查询持仓头寸
      */
-    fun queryErmcpTradePosition(){
+    fun queryErmcpTradePosition() {
         val params = mutableMapOf<String, String>().apply {
             put("accountID", GlobalDataCollection.instance?.accountId.toString())
         }
-        
-        MyApplication.getInstance()?.futureManager?.queryErmcpTradePosition(params = params){isSuccess, respData, error ->  
-            if (isSuccess){
+
+        MyApplication.getInstance()?.futureManager?.queryErmcpTradePosition(params = params) { isSuccess, respData, error ->
+            if (isSuccess) {
                 loadingDialogStatus.postValue(TaskUiModel.success(msg = "数据初始化成功"))
-            }else{
+
+                // 连接行情链路,并订阅持仓商品行情
+                val ip = SPUtils.getInstance().getString(Constant.quoteHost)
+                val port = SPUtils.getInstance().getString(Constant.quotePort)
+                GlobalScope.launch {
+                    MyApplication.getInstance()?.quoteSocketManager?.conn(ip, port, object :
+                        Callback<Packet40> {
+                        override fun onSuccess(rsp: Packet40?) {
+                            val futureHoldDatas = GlobalDataCollection.instance?.futureHoldData
+
+                            MyApplication.getInstance()?.quoteManager?.addSubscriptQuote(
+                                "11111",
+                                futureHoldDatas!!.map { it.outgoodscode }.toSet()
+                            ) { isCompleted, err ->
+
+                            }
+                        }
+
+                        override fun onFail(err: Error?) {
+                            // 连接行情链路失败
+                            print("")
+                        }
+                    })
+                }
+            } else {
                 loadingDialogStatus.postValue(TaskUiModel.failed(InteractiveException(errorMessage = "数据初始化失败")))
             }
         }

+ 19 - 4
RMA/app/src/main/java/cn/muchinfo/rma/view/MyApplication.kt

@@ -18,6 +18,8 @@ import cn.muchinfo.rma.business.future.FutureManager
 import cn.muchinfo.rma.business.hedge.HedgeManager
 import cn.muchinfo.rma.business.initialize.InitializeManager
 import cn.muchinfo.rma.business.management.BusinessManager
+import cn.muchinfo.rma.business.quote.QuoteManager
+import cn.muchinfo.rma.business.quote.adapter.QuoteAdapter
 import cn.muchinfo.rma.business.report.ReportManager
 import cn.muchinfo.rma.business.tradingquery.TradingQueryManager
 import cn.muchinfo.rma.global.GlobalDataCollection
@@ -32,6 +34,7 @@ import cn.muchinfo.rma.netcore.packet.Packet40
 import cn.muchinfo.rma.netcore.packet.Packet50
 import cn.muchinfo.rma.netcore.socket.MTP2Socket
 import cn.muchinfo.rma.netcore.socket.MTP2SocketListener
+import cn.muchinfo.rma.protobuf.classNumber.ClassNumber
 import cn.muchinfo.rma.protobuf.funcode.FunCode
 import cn.muchinfo.rma.view.base.BaseApplication
 import cn.muchinfo.rma.view.base.app.Constant
@@ -57,6 +60,9 @@ class MyApplication : BaseApplication() {
     /** 交易链路 */
     var tradeSocketManager: MTP2Socket<Packet50>? = null
 
+    /** 行情管理 */
+    var quoteManager: QuoteManager? = null
+
     /** 账户管理 */
     var accountManager: AccountManager? = null
 
@@ -181,6 +187,7 @@ class MyApplication : BaseApplication() {
         quoteSocketManager = MTP2Socket<Packet40>(0, quoteSocketListener)
         tradeSocketManager = MTP2Socket<Packet50>(1, tradeSocketListener)
 
+        quoteManager = QuoteManager()
         accountManager = AccountManager()
         customerManage = CustomerManage()
         contractManager = ContractManager()
@@ -242,7 +249,17 @@ class MyApplication : BaseApplication() {
         }
 
         override fun onReceivePush(socket: MTP2Socket<Packet40>?, data: Packet40?) {
-
+            when (data?.mainClassNumber?.toInt()) {
+                ClassNumber.MainClassNumber_Quota_SubscriptRsp -> { // 实时行情推送
+                    if (data.content.size >= 23) {
+                        // 分解行情
+                        val goodsQuoteTiks = QuoteAdapter.splitQuoteGoods(data.content)
+                        // TODO: - 写入盘面
+
+                        // TODO: - 计算持仓盈亏和资金账户
+                    }
+                }
+            }
         }
 
         override fun onReconnectChangeState(socket: MTP2Socket<Packet40>?, state: Int) {
@@ -286,9 +303,7 @@ class MyApplication : BaseApplication() {
                     // 停止心跳
                     socket?.stopBeatTimer()
                     // 如果是交易连路则停止定时Token检验
-                    if (socket?.packetType == 1) {
-                        accountManager?.stopTokenCheckTimer()
-                    }
+                    accountManager?.stopTokenCheckTimer()
                 }
                 MTP2Socket.ReconnectChangeState_ReconnectSuccessed -> { // 重连成功,将进行业务操作
                     // 进行断网重连Token检验