decode.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /**
  2. * 将二进制数组转化为无符号整型数值的方法
  3. * array 二进制数组
  4. * islittleEndian 是否小端
  5. * @returns {number}
  6. */
  7. function byteArrayToUInt(array: Uint8Array, isLittleEndian: boolean): number {
  8. const dataView = new DataView(new Uint8Array(array).buffer);
  9. let value = 0;
  10. switch (array.length) {
  11. case 2:
  12. value = dataView.getUint16(0, isLittleEndian);
  13. break;
  14. case 4:
  15. value = dataView.getUint32(0, isLittleEndian);
  16. break;
  17. }
  18. return value;
  19. }
  20. /**
  21. * 解析行情订阅请求包
  22. * @param rspPackage
  23. */
  24. export function parseSubscribeRsp(content: Uint8Array): Promise<Proto.QuoteRsp[]> {
  25. const result: Proto.QuoteRsp[] = [];
  26. return new Promise((resolve, reject) => {
  27. if (content.length === 0) {
  28. // 有可能空订阅,也有可能订阅返回数据为空,但其实已经订阅了商品
  29. // 暂做正常返回 空字符串 处理,如遇到问题继续优化即可
  30. console.warn('行情订阅请求包返回为空');
  31. resolve(result);
  32. } else {
  33. const count = byteArrayToUInt(content.subarray(0, 4), false);
  34. if (count > 1000) {
  35. // 游客模式 刷新成长问题,为了不让页面卡死,暂时处理
  36. resolve(result);
  37. } else {
  38. switch (count) {
  39. case -1:
  40. reject('订阅失败');
  41. break;
  42. case -2:
  43. reject('Token校验失败');
  44. break;
  45. case -3:
  46. reject('无对应商品信息');
  47. break;
  48. default:
  49. // 判断返回的Count是否有问题
  50. if (!count || count > (content.length - 4) / 66) {
  51. reject('行情订阅返回数据不正确');
  52. } else {
  53. let position = 4;
  54. for (let i = 0; i < count; i++) {
  55. const dataArray = content.subarray(position, position + 66);
  56. const subState = dataArray[0];
  57. // FIXME: 目前行情接入服务不能正确返回交易所代码
  58. const exchangeCode = dataArray[2];
  59. const goodsCode = String.fromCharCode(...dataArray.subarray(2, dataArray.length)).trim();
  60. result.push({ subState, goodsCode, exchangeCode });
  61. position += 66;
  62. }
  63. resolve(result);
  64. }
  65. }
  66. }
  67. }
  68. })
  69. }
  70. /**
  71. * 解析行情推送报文
  72. * @param {Array} quotationData 行情信息
  73. */
  74. export function parseReceivePush(quotationData: Uint8Array) {
  75. const result: Proto.Quote[] = [];
  76. // 目前发现可能会传入空数组
  77. if (quotationData.length) {
  78. // 分解行正则
  79. const regRow = /10\s.*?11/g;
  80. // 分解单行 key 正则
  81. const regKey = /01\s.*?02/g;
  82. // 分解单行 value 正则
  83. const regValue = /02\s.*?01|02\s.*?11/g;
  84. // 记录已经更新盘面的商品信息数组
  85. // 0x10 ... 0x01 key 0x02 value 0x01 key 0x02 value ... 0x11
  86. const hexString = Array.prototype.map.call(quotationData, (x) => ('00' + x.toString(16)).slice(-2)).join(' ');
  87. // 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';
  88. // 获取单行行情
  89. const rows = hexString.match(regRow);
  90. if (rows) {
  91. for (const low of rows) {
  92. // 获取 key value 表列
  93. const keys = low.match(regKey);
  94. const values = low.match(regValue);
  95. if (keys && values) {
  96. const quote: Proto.Quote = {};
  97. for (let i = 0; i < keys.length; i++) {
  98. const key = parseInt(keys[i].substring(3, 5), 16);
  99. const tmpValue = values[i];
  100. const value = new Uint8Array(tmpValue.substring(3, tmpValue.length - 3).split(' ').map((byte) => parseInt(byte, 16)));
  101. setQuoteTikFieldByByte(quote, key, value);
  102. }
  103. if (Object.keys(quote).length) {
  104. result.push(quote);
  105. //console.log('行情推送', quote);
  106. }
  107. }
  108. }
  109. }
  110. }
  111. return result;
  112. }
  113. /**
  114. * 分解缓存行情字段的方法
  115. * @param quote
  116. * @param key
  117. * @param value
  118. */
  119. function setQuoteTikFieldByByte(quote: Proto.Quote, key: number, value: Uint8Array) {
  120. const strValue = String.fromCharCode(...value);
  121. switch (key) {
  122. case 0x56:
  123. quote.exchangecode = Number(strValue);
  124. break;
  125. case 0x21:
  126. quote.goodscode = strValue;
  127. break;
  128. case 0x24:
  129. quote.last = Number(strValue);
  130. break;
  131. case 0x5b:
  132. quote.holdvolume = Number(strValue);
  133. break;
  134. case 0x25:
  135. quote.lastvolume = Number(strValue);
  136. break;
  137. case 0x3c:
  138. quote.preholdvolume = Number(strValue);
  139. break;
  140. case 0x32:
  141. quote.presettle = Number(strValue);
  142. break;
  143. case 0x33:
  144. quote.settle = Number(strValue);
  145. break;
  146. case 0x29:
  147. quote.totalturnover = Number(strValue);
  148. break;
  149. case 0x28:
  150. quote.totalvolume = Number(strValue);
  151. break;
  152. case 0x35:
  153. quote.limithigh = Number(strValue);
  154. break;
  155. case 0x36:
  156. quote.limitlow = Number(strValue);
  157. break;
  158. case 'L'.charCodeAt(0):
  159. quote.ask = Number(strValue);
  160. break;
  161. case 'M'.charCodeAt(0):
  162. quote.ask2 = Number(strValue);
  163. break;
  164. case 'N'.charCodeAt(0):
  165. quote.ask3 = Number(strValue);
  166. break;
  167. case 'O'.charCodeAt(0):
  168. quote.ask4 = Number(strValue);
  169. break;
  170. case 'P'.charCodeAt(0):
  171. quote.ask5 = Number(strValue);
  172. break;
  173. case 'Q'.charCodeAt(0):
  174. quote.askvolume = Number(strValue);
  175. break;
  176. case 'R'.charCodeAt(0):
  177. quote.askvolume2 = Number(strValue);
  178. break;
  179. case 'S'.charCodeAt(0):
  180. quote.askvolume3 = Number(strValue);
  181. break;
  182. case 'T'.charCodeAt(0):
  183. quote.askvolume4 = Number(strValue);
  184. break;
  185. case 'U'.charCodeAt(0):
  186. quote.askvolume5 = Number(strValue);
  187. break;
  188. case 'B'.charCodeAt(0):
  189. quote.bid = Number(strValue);
  190. break;
  191. case 'C'.charCodeAt(0):
  192. quote.bid2 = Number(strValue);
  193. break;
  194. case 'D'.charCodeAt(0):
  195. quote.bid3 = Number(strValue);
  196. break;
  197. case 'E'.charCodeAt(0):
  198. quote.bid4 = Number(strValue);
  199. break;
  200. case 'F'.charCodeAt(0):
  201. quote.bid5 = Number(strValue);
  202. break;
  203. case 'G'.charCodeAt(0):
  204. quote.bidvolume = Number(strValue);
  205. break;
  206. case 'H'.charCodeAt(0):
  207. quote.bidvolume2 = Number(strValue);
  208. break;
  209. case 'I'.charCodeAt(0):
  210. quote.bidvolume3 = Number(strValue);
  211. break;
  212. case 'J'.charCodeAt(0):
  213. quote.bidvolume4 = Number(strValue);
  214. break;
  215. case 'K'.charCodeAt(0):
  216. quote.bidvolume5 = Number(strValue);
  217. break;
  218. case ','.charCodeAt(0):
  219. quote.highest = Number(strValue);
  220. break;
  221. case '-'.charCodeAt(0):
  222. quote.lowest = Number(strValue);
  223. break;
  224. case '@'.charCodeAt(0):
  225. quote.date = strValue;
  226. break;
  227. case 'A'.charCodeAt(0):
  228. quote.time = strValue;
  229. break;
  230. case '+'.charCodeAt(0):
  231. quote.preclose = Number(strValue);
  232. break;
  233. case '.'.charCodeAt(0):
  234. quote.opened = Number(strValue);
  235. break;
  236. case 0x5c:
  237. quote.exerciseprice = Number(strValue);
  238. break;
  239. case 0x7a:
  240. quote.inventory = Number(strValue);
  241. break;
  242. case 0x7c:
  243. quote.exchangedate = Number(strValue);
  244. break;
  245. case 0x70:
  246. quote.strbidorder = strValue;
  247. break;
  248. case 0x71:
  249. quote.strbidorder2 = strValue;
  250. break;
  251. case 0x72:
  252. quote.strbidorder3 = strValue;
  253. break;
  254. case 0x73:
  255. quote.strbidorder4 = strValue;
  256. break;
  257. case 0x74:
  258. quote.strbidorder5 = strValue;
  259. break;
  260. case 0x75:
  261. quote.straskorder = strValue;
  262. break;
  263. case 0x76:
  264. quote.straskorder2 = strValue;
  265. break;
  266. case 0x77:
  267. quote.straskorder3 = strValue;
  268. break;
  269. case 0x78:
  270. quote.straskorder4 = strValue;
  271. break;
  272. case 0x79:
  273. quote.straskorder5 = strValue;
  274. break;
  275. case 0x7d:
  276. quote.putoptionpremiums = Number(strValue);
  277. break;
  278. case 0x7e:
  279. quote.putoptionpremiums2 = Number(strValue);
  280. break;
  281. case 0x80:
  282. quote.putoptionpremiums3 = Number(strValue);
  283. break;
  284. case 0x81:
  285. quote.putoptionpremiums4 = Number(strValue);
  286. break;
  287. case 0x82:
  288. quote.putoptionpremiums5 = Number(strValue);
  289. break;
  290. case 0x83:
  291. quote.calloptionpremiums = Number(strValue);
  292. break;
  293. case 0x84:
  294. quote.calloptionpremiums2 = Number(strValue);
  295. break;
  296. case 0x85:
  297. quote.calloptionpremiums3 = Number(strValue);
  298. break;
  299. case 0x86:
  300. quote.calloptionpremiums4 = Number(strValue);
  301. break;
  302. case 0x87:
  303. quote.calloptionpremiums5 = Number(strValue);
  304. break;
  305. }
  306. }