/** * 将二进制数组转化为无符号整型数值的方法 * array 二进制数组 * islittleEndian 是否小端 * @returns {number} */ function byteArrayToUInt(array: Uint8Array, isLittleEndian: boolean): number { const dataView = new DataView(new Uint8Array(array).buffer); let value = 0; switch (array.length) { case 2: value = dataView.getUint16(0, isLittleEndian); break; case 4: value = dataView.getUint32(0, isLittleEndian); break; } return value; } /** * 解析行情订阅请求包 * @param rspPackage */ export function parseSubscribeRsp(content: Uint8Array): Promise { const result: Proto.QuoteRsp[] = []; return new Promise((resolve, reject) => { if (content.length === 0) { // 有可能空订阅,也有可能订阅返回数据为空,但其实已经订阅了商品 // 暂做正常返回 空字符串 处理,如遇到问题继续优化即可 console.warn('行情订阅请求包返回为空'); resolve(result); } else { const count = byteArrayToUInt(content.subarray(0, 4), false); if (count > 1000) { // 游客模式 刷新成长问题,为了不让页面卡死,暂时处理 resolve(result); } else { switch (count) { case -1: reject('订阅失败'); break; case -2: reject('Token校验失败'); break; case -3: reject('无对应商品信息'); break; default: // 判断返回的Count是否有问题 if (!count || count > (content.length - 4) / 66) { reject('行情订阅返回数据不正确'); } else { let position = 4; for (let i = 0; i < count; i++) { const dataArray = content.subarray(position, position + 66); const subState = dataArray[0]; // FIXME: 目前行情接入服务不能正确返回交易所代码 const exchangeCode = dataArray[2]; const goodsCode = String.fromCharCode(...dataArray.subarray(2, dataArray.length)).trim(); result.push({ subState, goodsCode, exchangeCode }); position += 66; } resolve(result); } } } } }) } /** * 解析行情推送报文 * @param {Array} quotationData 行情信息 */ export function parseReceivePush(quotationData: Uint8Array) { const result: Proto.Quote[] = []; // 目前发现可能会传入空数组 if (quotationData.length) { // 分解行正则 const regRow = /10\s.*?11/g; // 分解单行 key 正则 const regKey = /01\s.*?02/g; // 分解单行 value 正则 const regValue = /02\s.*?01|02\s.*?11/g; // 记录已经更新盘面的商品信息数组 // 0x10 ... 0x01 key 0x02 value 0x01 key 0x02 value ... 0x11 const hexString = Array.prototype.map.call(quotationData, (x) => ('00' + x.toString(16)).slice(-2)).join(' '); // let hexString = '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'; // 获取单行行情 const rows = hexString.match(regRow); if (rows) { for (const low of rows) { // 获取 key value 表列 const keys = low.match(regKey); const values = low.match(regValue); if (keys && values) { const quote: Proto.Quote = {}; for (let i = 0; i < keys.length; i++) { const key = parseInt(keys[i].substring(3, 5), 16); const tmpValue = values[i]; const value = new Uint8Array(tmpValue.substring(3, tmpValue.length - 3).split(' ').map((byte) => parseInt(byte, 16))); setQuoteTikFieldByByte(quote, key, value); } if (Object.keys(quote).length) { result.push(quote); //console.log('行情推送', quote); } } } } } return result; } /** * 分解缓存行情字段的方法 * @param quote * @param key * @param value */ function setQuoteTikFieldByByte(quote: Proto.Quote, key: number, value: Uint8Array) { const strValue = String.fromCharCode(...value); switch (key) { case 0x56: quote.exchangecode = Number(strValue); break; case 0x21: quote.goodscode = strValue; break; case 0x24: quote.last = Number(strValue); break; case 0x5b: quote.holdvolume = Number(strValue); break; case 0x25: quote.lastvolume = Number(strValue); break; case 0x3c: quote.preholdvolume = Number(strValue); break; case 0x32: quote.presettle = Number(strValue); break; case 0x33: quote.settle = Number(strValue); break; case 0x29: quote.totalturnover = Number(strValue); break; case 0x28: quote.totalvolume = Number(strValue); break; case 0x35: quote.limithigh = Number(strValue); break; case 0x36: quote.limitlow = Number(strValue); break; case 'L'.charCodeAt(0): quote.ask = Number(strValue); break; case 'M'.charCodeAt(0): quote.ask2 = Number(strValue); break; case 'N'.charCodeAt(0): quote.ask3 = Number(strValue); break; case 'O'.charCodeAt(0): quote.ask4 = Number(strValue); break; case 'P'.charCodeAt(0): quote.ask5 = Number(strValue); break; case 'Q'.charCodeAt(0): quote.askvolume = Number(strValue); break; case 'R'.charCodeAt(0): quote.askvolume2 = Number(strValue); break; case 'S'.charCodeAt(0): quote.askvolume3 = Number(strValue); break; case 'T'.charCodeAt(0): quote.askvolume4 = Number(strValue); break; case 'U'.charCodeAt(0): quote.askvolume5 = Number(strValue); break; case 'B'.charCodeAt(0): quote.bid = Number(strValue); break; case 'C'.charCodeAt(0): quote.bid2 = Number(strValue); break; case 'D'.charCodeAt(0): quote.bid3 = Number(strValue); break; case 'E'.charCodeAt(0): quote.bid4 = Number(strValue); break; case 'F'.charCodeAt(0): quote.bid5 = Number(strValue); break; case 'G'.charCodeAt(0): quote.bidvolume = Number(strValue); break; case 'H'.charCodeAt(0): quote.bidvolume2 = Number(strValue); break; case 'I'.charCodeAt(0): quote.bidvolume3 = Number(strValue); break; case 'J'.charCodeAt(0): quote.bidvolume4 = Number(strValue); break; case 'K'.charCodeAt(0): quote.bidvolume5 = Number(strValue); break; case ','.charCodeAt(0): quote.highest = Number(strValue); break; case '-'.charCodeAt(0): quote.lowest = Number(strValue); break; case '@'.charCodeAt(0): quote.date = strValue; break; case 'A'.charCodeAt(0): quote.time = strValue; break; case '+'.charCodeAt(0): quote.preclose = Number(strValue); break; case '.'.charCodeAt(0): quote.opened = Number(strValue); break; case 0x5c: quote.exerciseprice = Number(strValue); break; case 0x7a: quote.inventory = Number(strValue); break; case 0x7c: quote.exchangedate = Number(strValue); break; case 0x70: quote.strbidorder = strValue; break; case 0x71: quote.strbidorder2 = strValue; break; case 0x72: quote.strbidorder3 = strValue; break; case 0x73: quote.strbidorder4 = strValue; break; case 0x74: quote.strbidorder5 = strValue; break; case 0x75: quote.straskorder = strValue; break; case 0x76: quote.straskorder2 = strValue; break; case 0x77: quote.straskorder3 = strValue; break; case 0x78: quote.straskorder4 = strValue; break; case 0x79: quote.straskorder5 = strValue; break; case 0x7d: quote.putoptionpremiums = Number(strValue); break; case 0x7e: quote.putoptionpremiums2 = Number(strValue); break; case 0x80: quote.putoptionpremiums3 = Number(strValue); break; case 0x81: quote.putoptionpremiums4 = Number(strValue); break; case 0x82: quote.putoptionpremiums5 = Number(strValue); break; case 0x83: quote.calloptionpremiums = Number(strValue); break; case 0x84: quote.calloptionpremiums2 = Number(strValue); break; case 0x85: quote.calloptionpremiums3 = Number(strValue); break; case 0x86: quote.calloptionpremiums4 = Number(strValue); break; case 0x87: quote.calloptionpremiums5 = Number(strValue); break; } }