dolphindb 2.0.502 → 2.0.505

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/browser.js CHANGED
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ddb = exports.DDB = exports.str2nanotimestamp = exports.nanotimestamp2str = exports.str2timestamp = exports.timestamp2str = exports.datetime2str = exports.date2str = exports.DdbTable = exports.DdbVectorSymbol = exports.DdbVectorAny = exports.DdbVectorDouble = exports.DdbVectorString = exports.DdbVectorInt = exports.DdbFunction = exports.DdbPair = exports.DdbNanoTimeStamp = exports.DdbTimeStamp = exports.DdbDateTime = exports.DdbDouble = exports.DdbLong = exports.DdbString = exports.DdbInt = exports.DdbChar = exports.DdbBool = exports.DdbVoid = exports.DdbObj = exports.timezone_offset = exports.nulls = exports.DdbFunctionType = exports.DdbType = exports.DdbForm = void 0;
3
+ exports.ddb = exports.DDB = exports.str2nanotimestamp = exports.nanotimestamp2str = exports.str2timestamp = exports.timestamp2str = exports.datetime2str = exports.date2str = exports.DdbTable = exports.DdbSetString = exports.DdbSetDouble = exports.DdbSetInt = exports.DdbVectorSymbol = exports.DdbVectorAny = exports.DdbVectorString = exports.DdbVectorDouble = exports.DdbVectorInt = exports.DdbFunction = exports.DdbPair = exports.DdbNanoTimeStamp = exports.DdbTimeStamp = exports.DdbDateTime = exports.DdbDouble = exports.DdbLong = exports.DdbString = exports.DdbInt = exports.DdbChar = exports.DdbBool = exports.DdbVoid = exports.DdbObj = exports.timezone_offset = exports.nulls = exports.DdbFunctionType = exports.DdbType = exports.DdbForm = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const dayjs_1 = tslib_1.__importDefault(require("dayjs"));
6
6
  const customParseFormat_js_1 = tslib_1.__importDefault(require("dayjs/plugin/customParseFormat.js"));
7
7
  dayjs_1.default.extend(customParseFormat_js_1.default);
8
8
  require("xshell/prototype.browser");
9
9
  const utils_browser_1 = require("xshell/utils.browser");
10
+ const net_browser_js_1 = require("xshell/net.browser.js");
10
11
  var DdbForm;
11
12
  (function (DdbForm) {
12
13
  DdbForm[DdbForm["scalar"] = 0] = "scalar";
@@ -72,8 +73,7 @@ var DdbFunctionType;
72
73
  DdbFunctionType[DdbFunctionType["JitPartialFunc"] = 8] = "JitPartialFunc";
73
74
  })(DdbFunctionType = exports.DdbFunctionType || (exports.DdbFunctionType = {}));
74
75
  exports.nulls = {
75
- bool: 0x80,
76
- char: '\x80',
76
+ int8: -0x80,
77
77
  int16: -32768,
78
78
  int32: -2147483648,
79
79
  int64: -0x8000000000000000n,
@@ -247,12 +247,14 @@ class DdbObj {
247
247
  case DdbType.void:
248
248
  return [1, null];
249
249
  case DdbType.bool: {
250
- const value = buf[0];
251
- return [1, value === exports.nulls.bool ? null : Boolean(value)];
250
+ const dv = new DataView(buf.buffer, buf.byteOffset);
251
+ const value = dv.getInt8(0);
252
+ return [1, value === exports.nulls.int8 ? null : Boolean(value)];
252
253
  }
253
254
  case DdbType.char: {
254
- const value = String.fromCharCode(buf[0]);
255
- return [1, value === exports.nulls.char ? null : value];
255
+ const dv = new DataView(buf.buffer, buf.byteOffset);
256
+ const value = dv.getInt8(0);
257
+ return [1, value === exports.nulls.int8 ? null : value];
256
258
  }
257
259
  case DdbType.short: {
258
260
  const dv = new DataView(buf.buffer, buf.byteOffset);
@@ -437,11 +439,10 @@ class DdbObj {
437
439
  static parse_vector_items(buf, le, type, length) {
438
440
  switch (type) {
439
441
  case DdbType.bool:
440
- return [length, buf.slice(0, length)];
441
442
  case DdbType.char:
442
443
  return [
443
444
  length,
444
- this.dec.decode(buf.subarray(0, length))
445
+ new Int8Array(buf.buffer.slice(buf.byteOffset, buf.byteOffset + length))
445
446
  ];
446
447
  case DdbType.short:
447
448
  return [
@@ -591,17 +592,17 @@ class DdbObj {
591
592
  return [Uint8Array.of(1)];
592
593
  case DdbType.bool:
593
594
  return [
594
- Uint8Array.of(value === null ?
595
- exports.nulls.bool
595
+ Int8Array.of(value === null ?
596
+ exports.nulls.int8
596
597
  :
597
598
  Number(value))
598
599
  ];
599
600
  case DdbType.char:
600
601
  return [
601
- Uint8Array.of((value === null ?
602
- exports.nulls.char
602
+ Int8Array.of((value === null ?
603
+ exports.nulls.int8
603
604
  :
604
- value).charCodeAt(0))
605
+ value))
605
606
  ];
606
607
  case DdbType.short:
607
608
  return [
@@ -743,7 +744,7 @@ class DdbObj {
743
744
  case DdbType.bool:
744
745
  return [value];
745
746
  case DdbType.char:
746
- return [this.enc.encode(value)];
747
+ return [value];
747
748
  case DdbType.short:
748
749
  return [value];
749
750
  case DdbType.int:
@@ -846,10 +847,18 @@ class DdbObj {
846
847
  }
847
848
  })();
848
849
  const data = (() => {
849
- if (this.form === DdbForm.pair)
850
- return `${this.value[0]}, ${this.value[1]}`;
851
- if (this.form === DdbForm.scalar && this.type === DdbType.functiondef)
852
- return `'${this.value.name}'`;
850
+ switch (this.form) {
851
+ case DdbForm.pair:
852
+ return `${this.value[0]}, ${this.value[1]}`;
853
+ case DdbForm.scalar:
854
+ switch (this.type) {
855
+ case DdbType.functiondef:
856
+ return `'${this.value.name}'`;
857
+ case DdbType.string:
858
+ return `'${this.value}'`;
859
+ }
860
+ break;
861
+ }
853
862
  return this.value;
854
863
  })();
855
864
  return `${type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
@@ -915,7 +924,9 @@ class DdbObj {
915
924
  exports.DdbObj = DdbObj;
916
925
  DdbObj.dec = new TextDecoder('utf-8');
917
926
  DdbObj.enc = new TextEncoder();
918
- /** 维护已解析的 symbol base,比如流数据中后续的 symbol 向量可能只发送一个 base.id, base.size == 0, 依赖之前发送的 symbol base */
927
+ /** 维护已解析的 symbol base,比如流数据中后续的 symbol 向量可能只发送一个 base.id, base.size == 0, 依赖之前发送的 symbol base ?
928
+ 只是暂存,如果一张表有多个 symbol 列,可能这个 symbol base 会被复用,不同的对象之间 symbol base 一般不复用
929
+ */
919
930
  DdbObj.symbol_bases = {};
920
931
  /** little endian (client) */
921
932
  DdbObj.le_client = Boolean(new Uint8Array(Uint32Array.of(1).buffer)[0]);
@@ -947,7 +958,10 @@ class DdbChar extends DdbObj {
947
958
  form: DdbForm.scalar,
948
959
  type: DdbType.char,
949
960
  length: 1,
950
- value,
961
+ value: typeof value === 'string' ?
962
+ value.charCodeAt(0)
963
+ :
964
+ value
951
965
  });
952
966
  }
953
967
  }
@@ -1073,20 +1087,6 @@ class DdbVectorInt extends DdbObj {
1073
1087
  }
1074
1088
  }
1075
1089
  exports.DdbVectorInt = DdbVectorInt;
1076
- class DdbVectorString extends DdbObj {
1077
- constructor(strings, name) {
1078
- super({
1079
- form: DdbForm.vector,
1080
- type: DdbType.string,
1081
- length: 0,
1082
- rows: strings.length,
1083
- cols: 1,
1084
- value: strings,
1085
- name,
1086
- });
1087
- }
1088
- }
1089
- exports.DdbVectorString = DdbVectorString;
1090
1090
  class DdbVectorDouble extends DdbObj {
1091
1091
  constructor(doubles, name) {
1092
1092
  super({
@@ -1107,6 +1107,20 @@ class DdbVectorDouble extends DdbObj {
1107
1107
  }
1108
1108
  }
1109
1109
  exports.DdbVectorDouble = DdbVectorDouble;
1110
+ class DdbVectorString extends DdbObj {
1111
+ constructor(strings, name) {
1112
+ super({
1113
+ form: DdbForm.vector,
1114
+ type: DdbType.string,
1115
+ length: 0,
1116
+ rows: strings.length,
1117
+ cols: 1,
1118
+ value: strings,
1119
+ name,
1120
+ });
1121
+ }
1122
+ }
1123
+ exports.DdbVectorString = DdbVectorString;
1110
1124
  class DdbVectorAny extends DdbObj {
1111
1125
  constructor(objs, name) {
1112
1126
  super({
@@ -1152,6 +1166,65 @@ class DdbVectorSymbol extends DdbObj {
1152
1166
  }
1153
1167
  }
1154
1168
  exports.DdbVectorSymbol = DdbVectorSymbol;
1169
+ class DdbSetInt extends DdbObj {
1170
+ constructor(ints) {
1171
+ super({
1172
+ form: DdbForm.set,
1173
+ type: DdbType.int,
1174
+ length: 0,
1175
+ rows: ints instanceof Set ?
1176
+ ints.size
1177
+ :
1178
+ ints.length,
1179
+ cols: 1,
1180
+ value: ints instanceof Int32Array ?
1181
+ ints
1182
+ :
1183
+ Int32Array.from(ints, v => v === null ?
1184
+ exports.nulls.int32
1185
+ :
1186
+ v),
1187
+ });
1188
+ }
1189
+ }
1190
+ exports.DdbSetInt = DdbSetInt;
1191
+ class DdbSetDouble extends DdbObj {
1192
+ constructor(doubles) {
1193
+ super({
1194
+ form: DdbForm.set,
1195
+ type: DdbType.double,
1196
+ length: 0,
1197
+ rows: doubles instanceof Set ?
1198
+ doubles.size
1199
+ :
1200
+ doubles.length,
1201
+ cols: 1,
1202
+ value: doubles instanceof Int32Array ?
1203
+ doubles
1204
+ :
1205
+ Int32Array.from(doubles, v => v === null ?
1206
+ exports.nulls.int32
1207
+ :
1208
+ v),
1209
+ });
1210
+ }
1211
+ }
1212
+ exports.DdbSetDouble = DdbSetDouble;
1213
+ class DdbSetString extends DdbObj {
1214
+ constructor(strings) {
1215
+ if (strings instanceof Set)
1216
+ strings = [...strings];
1217
+ super({
1218
+ form: DdbForm.set,
1219
+ type: DdbType.string,
1220
+ length: 0,
1221
+ rows: strings.length,
1222
+ cols: 1,
1223
+ value: strings
1224
+ });
1225
+ }
1226
+ }
1227
+ exports.DdbSetString = DdbSetString;
1155
1228
  class DdbTable extends DdbObj {
1156
1229
  constructor(columns, name = '') {
1157
1230
  super({
@@ -1259,11 +1332,25 @@ class DDB {
1259
1332
  /**
1260
1333
  Initialize an instance of DolphinDB Client using the WebSocket URL
1261
1334
  (without establishing an actual network connection)
1335
+ - url?: DolphinDB WebSocket URL. e.g.:`ws://127.0.0.1:8848`, Defaults to the current page URL
1336
+ - options?:
1337
+ - autologin?: Whether to log in automatically after establishing a connection, default `true`
1338
+ - username?: DolphinDB username, default `'admin'`
1339
+ - password?: DolphinDB password, default `'123456'`
1340
+ - python?: set python session flag, default `false`
1341
+
1262
1342
  @example
1263
1343
  let ddb = new DDB('ws://127.0.0.1:8848')
1264
- let ddb_secure = new DDB('wss://dolphindb.com')
1344
+
1345
+ // 使用 HTTPS 加密
1346
+ let ddbsecure = new DDB('wss://dolphindb.com', {
1347
+ autologin: true,
1348
+ username: 'admin',
1349
+ password: '123456',
1350
+ python: false
1351
+ })
1265
1352
  */
1266
- constructor(url) {
1353
+ constructor(url, options = {}) {
1267
1354
  /** 当前的 session id (http 或 tcp) */
1268
1355
  this.sid = '0';
1269
1356
  /** utf-8 text decoder */
@@ -1272,9 +1359,15 @@ class DDB {
1272
1359
  this.websocket = null;
1273
1360
  /** little endian (server) */
1274
1361
  this.le = true;
1362
+ /** Whether to automatically log in after the connection is established, the default is true */
1363
+ this.autologin = true;
1364
+ /** DolphinDB username */
1365
+ this.username = 'admin';
1366
+ /** DolphinDB password */
1367
+ this.password = '123456';
1275
1368
  /** python session flag (2048) */
1276
1369
  this.python = false;
1277
- this.message_hook = null;
1370
+ this.parse_object = true;
1278
1371
  this.presult = Promise.resolve(null);
1279
1372
  if (!url) {
1280
1373
  const _url = new URL(location.href);
@@ -1283,56 +1376,68 @@ class DDB {
1283
1376
  url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}${port ? `:${port}` : ''}/`;
1284
1377
  }
1285
1378
  this.url = url;
1379
+ if (options.autologin !== undefined)
1380
+ this.autologin = options.autologin;
1381
+ if (options.username !== undefined)
1382
+ this.username = options.username;
1383
+ if (options.password !== undefined)
1384
+ this.password = options.password;
1385
+ if (options.python !== undefined)
1386
+ this.python = options.python;
1286
1387
  }
1287
- /** print message handler */
1288
- printer(message) {
1289
- console.log(message);
1388
+ /** message handler */
1389
+ on_message({ type, data }, _this) {
1390
+ switch (type) {
1391
+ case 'print':
1392
+ console.log(data);
1393
+ return;
1394
+ }
1290
1395
  }
1291
1396
  /** resolver, rejector, promise of last rpc */
1292
- presolver(buf) { }
1397
+ presolver(message) { }
1293
1398
  prejector(error) { }
1294
1399
  get connected() {
1295
1400
  return this.websocket?.readyState === WebSocket.OPEN;
1296
1401
  }
1297
- /** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL */
1298
- async connect({ url = this.url, login = true, username = 'admin', password = '123456', python = false, } = {}) {
1299
- this.url = url;
1300
- this.python = python;
1402
+ /** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL
1403
+ - options?:
1404
+ - url?: DolphinDB WebSocket URL. By default, the WebSocket URL passed in when the instance is initialized is used
1405
+ - autologin?: Whether to log in automatically after establishing a connection, default `true`
1406
+ - username?: DolphinDB username, default `'admin'`
1407
+ - password?: DolphinDB password, default `'123456'`
1408
+ - python?: set python session flag, default `false`
1409
+ */
1410
+ async connect(options = {}) {
1411
+ if (options.url !== undefined)
1412
+ this.url = options.url;
1413
+ if (options.autologin !== undefined)
1414
+ this.autologin = options.autologin;
1415
+ if (options.username !== undefined)
1416
+ this.username = options.username;
1417
+ if (options.password !== undefined)
1418
+ this.password = options.password;
1419
+ if (options.python !== undefined)
1420
+ this.python = options.python;
1301
1421
  this.disconnect();
1302
- let websocket = new WebSocket(url, python ? ['python'] : []);
1303
- // https://stackoverflow.com/questions/11821096/what-is-the-difference-between-an-arraybuffer-and-a-blob/39951543
1304
- websocket.binaryType = 'arraybuffer';
1305
- this.websocket = websocket;
1306
- await new Promise((resolve, reject) => {
1307
- websocket.addEventListener('open', async (event) => {
1308
- console.log(`${websocket.url} opened`);
1422
+ await (0, net_browser_js_1.connect_websocket)(this.url, {
1423
+ protocols: this.python ? ['python'] : [],
1424
+ on_open: async (event, websocket) => {
1425
+ this.websocket = websocket;
1309
1426
  await this.rpc('connect', {});
1310
- resolve();
1311
- });
1312
- websocket.addEventListener('close', event => {
1313
- console.log(`${websocket.url} closed with code = ${event.code}, reason = '${event.reason}'`);
1314
- });
1315
- websocket.addEventListener('error', event => {
1316
- const message = `${websocket.url} errored`;
1317
- console.error(message, event);
1318
- reject(Object.assign(new Error(message), { event }));
1319
- });
1320
- websocket.addEventListener('message', event => {
1427
+ },
1428
+ on_message: event => {
1321
1429
  const buf = new Uint8Array(event.data);
1322
- if (this.message_hook)
1323
- this.message_hook(buf);
1324
1430
  try {
1325
- const { type, data } = this.parse_message(buf);
1326
- if (type === 'message') {
1327
- this.printer(data);
1431
+ const message = this.parse_message(buf);
1432
+ this.on_message(message, this);
1433
+ if (message.type === 'print')
1328
1434
  return;
1329
- }
1330
- this.presolver(data);
1435
+ this.presolver(message);
1331
1436
  }
1332
1437
  catch (error) {
1333
1438
  this.prejector(error);
1334
1439
  }
1335
- });
1440
+ }
1336
1441
  });
1337
1442
  if (!this.python)
1338
1443
  await this.eval('def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
@@ -1351,11 +1456,11 @@ class DDB {
1351
1456
  ' add_node_alias\n' +
1352
1457
  ' )\n' +
1353
1458
  '}\n', { urgent: true });
1354
- if (login)
1459
+ if (this.autologin)
1355
1460
  if (this.python)
1356
- await this.eval(`login(${username.quote('double')}, ${password.quote('double')})`, { urgent: true });
1461
+ await this.eval(`login(${this.username.quote('double')}, ${this.password.quote('double')})`, { urgent: true });
1357
1462
  else
1358
- await this.call('login', [username, password], { urgent: true });
1463
+ await this.call('login', [this.username, this.password], { urgent: true });
1359
1464
  }
1360
1465
  get_rpc_options({ urgent = false, secondary = false, async: _async = false, pickle = false, clear = false, api = false, compress = false, cancellable = true, priority = urgent ? 8 : 4, parallelism = 8, root_id = '', limit, } = {}) {
1361
1466
  let flag = 0;
@@ -1386,7 +1491,7 @@ class DDB {
1386
1491
  limit,
1387
1492
  ] : [],
1388
1493
  ];
1389
- return `/ ${options.join('_')}`;
1494
+ return options.join('_');
1390
1495
  }
1391
1496
  disconnect() {
1392
1497
  if (this.connected)
@@ -1400,8 +1505,11 @@ class DDB {
1400
1505
  - options:
1401
1506
  - urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
1402
1507
  - vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
1508
+ - on_message?: 在该次 rpc 期间设置 message handler, 结束后恢复原有
1509
+ - parse_object?: 在该次 rpc 期间设置 parse_object, 结束后恢复原有,为 false 时返回的 DdbObj 仅含有 buffer 和 le,
1510
+ 不做解析,以便后续转发、序列化
1403
1511
  */
1404
- async rpc(type, { script, func, args = [], vars = [], urgent, }) {
1512
+ async rpc(type, { script, func, args = [], vars = [], urgent, on_message, parse_object, }) {
1405
1513
  if (!this.websocket)
1406
1514
  await this.connect();
1407
1515
  if (!this.connected)
@@ -1448,33 +1556,53 @@ class DDB {
1448
1556
  }
1449
1557
  })());
1450
1558
  const message = (0, utils_browser_1.concat)([
1451
- this.enc.encode(`API2 ${this.sid} ${command.length}${this.get_rpc_options({ urgent })}\n`),
1559
+ this.enc.encode(`API2 ${this.sid} ${command.length} / ${this.get_rpc_options({ urgent })}\n`),
1452
1560
  command,
1453
1561
  ...args.map((arg) => arg.pack())
1454
1562
  ]);
1455
- this.websocket.send(message);
1456
- return DdbObj.parse(await presult, // data_buf
1457
- this.le);
1563
+ const _on_message = this.on_message;
1564
+ const _parse_object = this.parse_object;
1565
+ if (on_message)
1566
+ this.on_message = on_message;
1567
+ if (parse_object !== undefined)
1568
+ this.parse_object = parse_object;
1569
+ try {
1570
+ this.websocket.send(message);
1571
+ const { data } = (await presult);
1572
+ return data;
1573
+ }
1574
+ finally {
1575
+ this.on_message = _on_message;
1576
+ this.parse_object = _parse_object;
1577
+ }
1458
1578
  }
1459
- /** eval script through websocket (script command) */
1460
- async eval(
1461
- /** 执行的脚本 */
1462
- script,
1463
- /** 执行选项 */
1464
- { urgent } = {}) {
1465
- return this.rpc('script', { script, urgent });
1579
+ /** eval script through websocket (script command)
1580
+ - script?: Script to execute
1581
+ - options?: execution options
1582
+ - urgent?: Urgent flag to ensure that submitted scripts are processed by urgent workers to prevent being blocked by other jobs
1583
+ - on_message?: Set the message handler during this rpc, and restore the original after the end
1584
+ - parse_object?: Set parse_object during this rpc, and restore the original after the end.
1585
+ When it is false, the returned DdbObj only contains buffer and le without parsing,
1586
+ so as to facilitate subsequent forwarding and serialization
1587
+ */
1588
+ async eval(script, { urgent, on_message, parse_object, } = {}) {
1589
+ return this.rpc('script', { script, urgent, on_message, parse_object });
1466
1590
  }
1467
1591
  /** call function through websocket (function command)
1468
- - func: 函数名
1469
- - args?: `[ ]` 调用参数 (传入的原生 string 和 boolean 会被自动转换为 DdbObj<string> 和 DdbObj<boolean>)
1470
- - options?: 调用选项
1471
- - urgent?: 紧急 flag。使用 urgent worker 执行,防止被其它作业阻塞
1472
- - node?: 设置结点 alias 时发送到集群中对应的结点执行 (使用 DolphinDB 中的 rpc 方法)
1473
- - nodes?: 设置多个结点 alias 时发送到集群中对应的多个结点执行 (使用 DolphinDB 中的 pnodeRun 方法)
1474
- - func_type?: 设置 node 参数时必传,需指定函数类型,其它情况下不传
1475
- - add_node_alias?: 设置 nodes 参数时选传,其它情况不传
1592
+ - func: function name
1593
+ - args?: `[ ]` Call parameters (the incoming native string and boolean will be automatically converted to DdbObj<string> and DdbObj<boolean>)
1594
+ - options?: call options
1595
+ - urgent?: Emergency flag. Use urgent worker execution to prevent being blocked by other jobs
1596
+ - node?: When the node alias is set, it is sent to the corresponding node in the cluster for execution (using the rpc method in DolphinDB)
1597
+ - nodes?: When setting multiple node aliases, send them to the corresponding multiple nodes in the cluster for execution (using the pnodeRun method in DolphinDB)
1598
+ - func_type?: It must be passed when setting the node parameter, the function type needs to be specified, and it is not passed in other cases
1599
+ - add_node_alias?: Select to pass when setting the nodes parameter, otherwise not pass
1600
+ - on_message?: Set the message handler during this rpc, and restore the original after the end
1601
+ - parse_object?: Set parse_object during this rpc, and restore the original after the end.
1602
+ When it is false, the returned DdbObj only contains buffer and le without parsing,
1603
+ so as to facilitate subsequent forwarding and serialization
1476
1604
  */
1477
- async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias } = {}) {
1605
+ async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, on_message, parse_object, } = {}) {
1478
1606
  if (this.python)
1479
1607
  throw new Error('call doesn\'t support python session yet');
1480
1608
  if (node) {
@@ -1506,6 +1634,8 @@ class DDB {
1506
1634
  func,
1507
1635
  args,
1508
1636
  urgent,
1637
+ on_message,
1638
+ parse_object
1509
1639
  });
1510
1640
  }
1511
1641
  /** upload variable through websocket (variable command) */
@@ -1513,19 +1643,19 @@ class DDB {
1513
1643
  /** Uploaded variables' name */
1514
1644
  vars,
1515
1645
  /** Uploaded variables' value */
1516
- args) {
1646
+ args, { on_message, parse_object, } = {}) {
1517
1647
  if (!args.length || args.length !== vars.length)
1518
1648
  throw new Error('variable 指令参数为空或参数名为空,或数量不匹配');
1519
- return this.rpc('variable', { vars, args });
1649
+ return this.rpc('variable', { vars, args, on_message, parse_object });
1520
1650
  }
1521
1651
  /** 解析服务端响应报文,返回去掉 header 的 data buf */
1522
- parse_message(buf) {
1652
+ parse_message(buf, parse_object = this.parse_object) {
1523
1653
  // MSG\n
1524
1654
  // <message>\0
1525
1655
  // 'M'.codePointAt(0).to_hex_str()
1526
1656
  if (buf[0] === 0x4d && buf[1] === 0x53 && buf[2] === 0x47 && buf[3] === 0x0a)
1527
1657
  return {
1528
- type: 'message',
1658
+ type: 'print',
1529
1659
  data: this.dec.decode(buf.subarray(4))
1530
1660
  };
1531
1661
  // '1166953221 1 1\n'
@@ -1550,9 +1680,19 @@ class DDB {
1550
1680
  const message = this.dec.decode(buf.subarray(i_ls_1, i_lf_1));
1551
1681
  if (message !== 'OK')
1552
1682
  throw new Error(message);
1683
+ const buf_obj = buf.subarray(i_lf_1 + 1);
1553
1684
  return {
1554
1685
  type: 'object',
1555
- data: buf.subarray(i_lf_1 + 1)
1686
+ data: parse_object ?
1687
+ Object.assign(DdbObj.parse(buf_obj, this.le), { buffer: buf_obj })
1688
+ :
1689
+ new DdbObj({
1690
+ form: DdbForm.scalar,
1691
+ type: DdbType.void,
1692
+ length: 0,
1693
+ le: this.le,
1694
+ buffer: buf_obj,
1695
+ })
1556
1696
  };
1557
1697
  }
1558
1698
  /** 自动转换 js string, boolean 为 DdbObj */