dolphindb 0.0.4 → 0.0.7
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/README.md +68 -7
- package/README.zh.md +67 -7
- package/browser.d.ts +85 -21
- package/browser.js +280 -87
- package/browser.js.map +1 -1
- package/index.d.ts +77 -21
- package/index.js +265 -72
- package/index.js.map +1 -1
- package/package.json +5 -5
- package/test.js +22 -2
- package/test.js.map +1 -1
package/index.d.ts
CHANGED
|
@@ -67,16 +67,24 @@ export interface DdbFunctionDefValue {
|
|
|
67
67
|
export interface DdbSymbolExtendedValue {
|
|
68
68
|
base_id: number;
|
|
69
69
|
base: string[];
|
|
70
|
-
|
|
70
|
+
data: Uint32Array;
|
|
71
71
|
}
|
|
72
|
-
export
|
|
73
|
-
|
|
72
|
+
export interface DdbArrayVectorBlock {
|
|
73
|
+
unit: 1 | 2 | 4;
|
|
74
|
+
rows: number;
|
|
75
|
+
lengths: Uint8Array | Uint16Array | Uint32Array;
|
|
76
|
+
data: DdbVectorValue;
|
|
77
|
+
}
|
|
78
|
+
export declare type DdbValue = null | boolean | number | [number, number] | bigint | string | string[] | Uint8Array | Int16Array | Int32Array | Float32Array | Float64Array | BigInt64Array | Uint8Array[] | DdbObj[] | DdbFunctionDefValue | DdbSymbolExtendedValue | DdbArrayVectorBlock[];
|
|
79
|
+
export declare type DdbVectorValue = string | string[] | Uint8Array | Int16Array | Int32Array | Float32Array | Float64Array | BigInt64Array | Uint8Array[] | DdbObj[] | DdbSymbolExtendedValue | DdbArrayVectorBlock[];
|
|
74
80
|
export declare const nulls: {
|
|
75
81
|
readonly char: "";
|
|
76
82
|
readonly int16: -32768;
|
|
77
83
|
readonly int32: -2147483648;
|
|
78
84
|
readonly int64: -9223372036854775808n;
|
|
79
85
|
};
|
|
86
|
+
export declare const timezone_offset: number;
|
|
87
|
+
/** 可以表示所有 DolphinDB 数据库中的数据类型 */
|
|
80
88
|
export declare class DdbObj<T extends DdbValue = DdbValue> {
|
|
81
89
|
static dec: TextDecoder;
|
|
82
90
|
static enc: TextEncoder;
|
|
@@ -129,6 +137,7 @@ export declare class DdbObj<T extends DdbValue = DdbValue> {
|
|
|
129
137
|
pack(): Uint8Array;
|
|
130
138
|
static pack_vector_body(value: DdbVectorValue, type: DdbType, length: number): ArrayBufferView[];
|
|
131
139
|
[inspect.custom](): string;
|
|
140
|
+
to_dict<T = Record<string, any>>(): T;
|
|
132
141
|
}
|
|
133
142
|
export declare class DdbVoid extends DdbObj<undefined> {
|
|
134
143
|
constructor();
|
|
@@ -166,44 +175,85 @@ export declare class DdbPair extends DdbObj<Int32Array> {
|
|
|
166
175
|
export declare class DdbFunction extends DdbObj<DdbFunctionDefValue> {
|
|
167
176
|
constructor(name: string, type: DdbFunctionType);
|
|
168
177
|
}
|
|
178
|
+
export declare function date2str(date: number): string;
|
|
179
|
+
export declare function timestamp2str(timestamp: bigint): string;
|
|
169
180
|
export declare class DDB {
|
|
170
|
-
port: number;
|
|
171
181
|
/** 当前的 session id (http 或 tcp) */
|
|
172
182
|
sid: string;
|
|
173
183
|
/** utf-8 text decoder */
|
|
174
184
|
dec: TextDecoder;
|
|
175
185
|
enc: TextEncoder;
|
|
176
186
|
print_buffer: boolean;
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
/** DolphinDB WebSocket URL
|
|
188
|
+
e.g. `ws://127.0.0.1:8848/`, `wss://dolphindb.com`
|
|
189
|
+
*/
|
|
190
|
+
url: string;
|
|
191
|
+
websocket: WebSocket;
|
|
179
192
|
/** little endian (server) */
|
|
180
193
|
le: boolean;
|
|
181
194
|
/** little endian (client) */
|
|
182
195
|
static le_client: boolean;
|
|
196
|
+
/** python session flag (2048) */
|
|
197
|
+
python: boolean;
|
|
198
|
+
message_hook: (message: Uint8Array) => any;
|
|
199
|
+
/** print message handler */
|
|
200
|
+
printer(message: string): void;
|
|
183
201
|
/** resolver, rejector, promise of last rpc */
|
|
184
202
|
presolver(buf: Uint8Array): void;
|
|
185
203
|
prejector(error: Error): void;
|
|
186
204
|
presult: Promise<Uint8Array>;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
205
|
+
/**
|
|
206
|
+
Initialize an instance of DolphinDB Client using the WebSocket URL
|
|
207
|
+
(without establishing an actual network connection)
|
|
208
|
+
@example
|
|
209
|
+
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
210
|
+
let ddb_secure = new DDB('wss://dolphindb.com')
|
|
211
|
+
*/
|
|
212
|
+
constructor(url?: string);
|
|
213
|
+
/** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL */
|
|
214
|
+
connect({ url, login, username, password, python, }?: {
|
|
215
|
+
/** By default, the WebSocket URL passed in when the instance is initialized is used */
|
|
216
|
+
url?: string;
|
|
217
|
+
/** Whether to automatically log in after the connection is established, the default is true */
|
|
193
218
|
login?: boolean;
|
|
194
|
-
/** DolphinDB
|
|
219
|
+
/** DolphinDB username */
|
|
195
220
|
username?: string;
|
|
196
|
-
/** DolphinDB
|
|
221
|
+
/** DolphinDB password */
|
|
197
222
|
password?: string;
|
|
223
|
+
/** set python session flag */
|
|
224
|
+
python?: boolean;
|
|
198
225
|
}): Promise<void>;
|
|
226
|
+
get_rpc_options({ urgent, secondary, async: _async, pickle, clear, api, compress, cancellable, priority, parallelism, root_id, limit, }?: {
|
|
227
|
+
urgent?: boolean;
|
|
228
|
+
/** API 提交的任务, secondary 必须为 false */
|
|
229
|
+
secondary?: boolean;
|
|
230
|
+
/** 是否异步任务(不返回结果) */
|
|
231
|
+
async?: boolean;
|
|
232
|
+
/** 让服务端以 pickle 协议返回数据 */
|
|
233
|
+
pickle?: boolean;
|
|
234
|
+
/** 本次任务完成后 clear session memory */
|
|
235
|
+
clear?: boolean;
|
|
236
|
+
/** 是否为 api client */
|
|
237
|
+
api?: boolean;
|
|
238
|
+
compress?: boolean;
|
|
239
|
+
/** 任务是否可以取消 */
|
|
240
|
+
cancellable?: boolean;
|
|
241
|
+
priority?: number;
|
|
242
|
+
/** `8` 0 ~ 64, 指定本任务并行度 */
|
|
243
|
+
parallelism?: number;
|
|
244
|
+
/** 根任务编号,内部使用,API中固定为空 */
|
|
245
|
+
root_id?: string;
|
|
246
|
+
/** 指定分块返回的块大小 */
|
|
247
|
+
limit?: boolean;
|
|
248
|
+
}): string;
|
|
199
249
|
disconnect(): void;
|
|
200
|
-
/** rpc through websocket (function command)
|
|
250
|
+
/** rpc through websocket (function/script/variable command)
|
|
201
251
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
202
252
|
- options:
|
|
203
253
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
204
254
|
- vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
|
|
205
255
|
*/
|
|
206
|
-
rpc<T extends DdbObj = DdbObj>(type: 'script' | 'function' | 'variable', { script, func, args, vars, urgent, }: {
|
|
256
|
+
rpc<T extends DdbObj = DdbObj>(type: 'script' | 'function' | 'variable' | 'connect', { script, func, args, vars, urgent, }: {
|
|
207
257
|
script?: string;
|
|
208
258
|
func?: string;
|
|
209
259
|
args?: (DdbObj | string | boolean)[];
|
|
@@ -216,14 +266,14 @@ export declare class DDB {
|
|
|
216
266
|
script: string,
|
|
217
267
|
/** 执行选项 */
|
|
218
268
|
{ urgent }?: {
|
|
219
|
-
/** 紧急 flag
|
|
269
|
+
/** 紧急 flag,确保提交的脚本使用 urgent worker 处理,防止被其它作业阻塞 */
|
|
220
270
|
urgent?: boolean;
|
|
221
271
|
}): Promise<T>;
|
|
222
272
|
/** call function through websocket (function command)
|
|
223
273
|
- func: 函数名
|
|
224
274
|
- args?: `[ ]` 调用参数 (传入的原生 string 和 boolean 会被自动转换为 DdbObj<string> 和 DdbObj<boolean>)
|
|
225
275
|
- options?: 调用选项
|
|
226
|
-
- urgent?: 紧急 flag
|
|
276
|
+
- urgent?: 紧急 flag。使用 urgent worker 执行,防止被其它作业阻塞
|
|
227
277
|
- node?: 设置结点 alias 时发送到集群中对应的结点执行 (使用 DolphinDB 中的 rpc 方法)
|
|
228
278
|
- nodes?: 设置多个结点 alias 时发送到集群中对应的多个结点执行 (使用 DolphinDB 中的 pnodeRun 方法)
|
|
229
279
|
- func_type?: 设置 node 参数时必传,需指定函数类型,其它情况下不传
|
|
@@ -238,12 +288,18 @@ export declare class DDB {
|
|
|
238
288
|
}): Promise<T>;
|
|
239
289
|
/** upload variable through websocket (variable command) */
|
|
240
290
|
upload(
|
|
241
|
-
/**
|
|
291
|
+
/** Uploaded variables' name */
|
|
242
292
|
vars: string[],
|
|
243
|
-
/**
|
|
293
|
+
/** Uploaded variables' value */
|
|
244
294
|
args: (DdbObj | string | boolean)[]): Promise<DdbObj<DdbValue>>;
|
|
245
295
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
246
|
-
parse_message(buf: Uint8Array):
|
|
296
|
+
parse_message(buf: Uint8Array): {
|
|
297
|
+
type: "message";
|
|
298
|
+
data: string;
|
|
299
|
+
} | {
|
|
300
|
+
type: "object";
|
|
301
|
+
data: Uint8Array;
|
|
302
|
+
};
|
|
247
303
|
/** 自动转换 js string, boolean 为 DdbObj */
|
|
248
304
|
to_ddbobj(value: DdbObj | string | boolean): DdbObj;
|
|
249
305
|
/** 转换 js 数组为 DdbObj[] (in place, 会修改原数组) */
|
package/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DDB = exports.DdbFunction = exports.DdbPair = exports.DdbVectorAny = exports.DdbVectorDouble = exports.DdbVectorString = exports.DdbVectorInt = exports.DdbDouble = exports.DdbLong = exports.DdbString = exports.DdbInt = exports.DdbBool = exports.DdbVoid = exports.DdbObj = exports.nulls = exports.DdbFunctionType = exports.ddb_sizes = exports.DdbType = exports.DdbForm = void 0;
|
|
3
|
+
exports.DDB = exports.timestamp2str = exports.date2str = exports.DdbFunction = exports.DdbPair = exports.DdbVectorAny = exports.DdbVectorDouble = exports.DdbVectorString = exports.DdbVectorInt = exports.DdbDouble = exports.DdbLong = exports.DdbString = exports.DdbInt = exports.DdbBool = exports.DdbVoid = exports.DdbObj = exports.timezone_offset = exports.nulls = exports.DdbFunctionType = exports.ddb_sizes = exports.DdbType = exports.DdbForm = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const dayjs_1 = (0, tslib_1.__importDefault)(require("dayjs"));
|
|
4
6
|
const ws_1 = require("ws");
|
|
5
7
|
const utils_js_1 = require("xshell/utils.js");
|
|
6
8
|
var DdbForm;
|
|
@@ -109,6 +111,8 @@ exports.nulls = {
|
|
|
109
111
|
int32: -2147483648,
|
|
110
112
|
int64: -0x8000000000000000n, // -922_3372_0368_5477_5808
|
|
111
113
|
};
|
|
114
|
+
exports.timezone_offset = 1000 * 60 * new Date().getTimezoneOffset();
|
|
115
|
+
/** 可以表示所有 DolphinDB 数据库中的数据类型 */
|
|
112
116
|
class DdbObj {
|
|
113
117
|
constructor(data) {
|
|
114
118
|
/** 是否为小端 (little endian) */
|
|
@@ -367,16 +371,86 @@ class DdbObj {
|
|
|
367
371
|
static parse_vector(buf, le, type) {
|
|
368
372
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
369
373
|
const rows = dv.getUint32(0, le);
|
|
370
|
-
|
|
371
|
-
|
|
374
|
+
let i_items_start = 8;
|
|
375
|
+
if (type < 64 || type >= 128) {
|
|
376
|
+
const [len_items, value] = this.parse_vector_items(buf.subarray(i_items_start), le, type, rows);
|
|
377
|
+
return new this({
|
|
378
|
+
le,
|
|
379
|
+
form: DdbForm.vector,
|
|
380
|
+
type,
|
|
381
|
+
length: i_items_start + len_items,
|
|
382
|
+
cols: 1,
|
|
383
|
+
rows,
|
|
384
|
+
value,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
// array vector
|
|
388
|
+
// av = array(INT[], 0, 3)
|
|
389
|
+
// append!(av, [1..4])
|
|
390
|
+
// append!(av, [1..70000])
|
|
391
|
+
// av
|
|
392
|
+
// <Buffer 44 01 type = array vector, form = vector
|
|
393
|
+
// 02 00 00 00 74 11 01 00 rows = 2, cols = 70004 (0x011174)
|
|
394
|
+
// block 0
|
|
395
|
+
// 01 00 block.rows = 1
|
|
396
|
+
// 04 block.unit = 4
|
|
397
|
+
// 00 reserved
|
|
398
|
+
// 04 00 00 00 block.lengths = [4]
|
|
399
|
+
// 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 block.data
|
|
400
|
+
// block 1
|
|
401
|
+
// 01 00 block.rows = 1
|
|
402
|
+
// 04 block.unit = 4
|
|
403
|
+
// 00 reserved
|
|
404
|
+
// 70 11 01 00 block.lengths = [70000 (0x00011170)]
|
|
405
|
+
// 01 00 00 00 02 00 00 00 ... 279992 more bytes> block.data
|
|
406
|
+
const cols = dv.getUint32(4, le);
|
|
407
|
+
let blocks = [];
|
|
408
|
+
let i_block_start = i_items_start;
|
|
409
|
+
// 解析一个 block
|
|
410
|
+
for (let i_row = 0; i_row < rows;) {
|
|
411
|
+
/** 对应 array vector 中元素个数 */
|
|
412
|
+
const rows = dv.getUint16(i_block_start, le);
|
|
413
|
+
/** 每个 length 占用的字节数 */
|
|
414
|
+
const unit = dv.getUint8(i_block_start + 2);
|
|
415
|
+
/** array vector 每个元素的子元素长度 */
|
|
416
|
+
let lengths;
|
|
417
|
+
const i_lengths_start = i_block_start + 4;
|
|
418
|
+
const i_data_start = i_lengths_start + rows * unit;
|
|
419
|
+
const lengths_buf = buf.slice(i_lengths_start, i_data_start);
|
|
420
|
+
switch (unit) {
|
|
421
|
+
case 1:
|
|
422
|
+
lengths = lengths_buf;
|
|
423
|
+
break;
|
|
424
|
+
case 2:
|
|
425
|
+
lengths = new Uint16Array(lengths_buf.buffer);
|
|
426
|
+
break;
|
|
427
|
+
case 4:
|
|
428
|
+
lengths = new Uint32Array(lengths_buf.buffer);
|
|
429
|
+
break;
|
|
430
|
+
default:
|
|
431
|
+
throw new Error(`array vector 存在非法 unit = ${unit}`);
|
|
432
|
+
}
|
|
433
|
+
let total_length = 0;
|
|
434
|
+
for (const x of lengths)
|
|
435
|
+
total_length += x;
|
|
436
|
+
const [len_items, data] = this.parse_vector_items(buf.subarray(i_data_start), le, type - 64, total_length);
|
|
437
|
+
blocks.push({
|
|
438
|
+
unit,
|
|
439
|
+
rows,
|
|
440
|
+
lengths,
|
|
441
|
+
data
|
|
442
|
+
});
|
|
443
|
+
i_block_start = i_data_start + len_items;
|
|
444
|
+
i_row += rows;
|
|
445
|
+
}
|
|
372
446
|
return new this({
|
|
373
447
|
le,
|
|
374
448
|
form: DdbForm.vector,
|
|
375
449
|
type,
|
|
376
|
-
length:
|
|
377
|
-
cols
|
|
450
|
+
length: i_block_start,
|
|
451
|
+
cols,
|
|
378
452
|
rows,
|
|
379
|
-
value
|
|
453
|
+
value: blocks
|
|
380
454
|
});
|
|
381
455
|
}
|
|
382
456
|
/** 有可能没有字节对齐,不能直接使用原有 message 的 arraybuffer, 统一复制出来,让原有 arraybuffer 被回收掉比较好 */
|
|
@@ -457,13 +531,13 @@ class DdbObj {
|
|
|
457
531
|
}
|
|
458
532
|
const value_start = 8 + base_length;
|
|
459
533
|
const value_end = value_start + length * 4;
|
|
460
|
-
const
|
|
534
|
+
const data = new Uint32Array(buf.buffer.slice(buf.byteOffset + value_start, buf.byteOffset + value_end));
|
|
461
535
|
return [
|
|
462
536
|
value_end,
|
|
463
537
|
{
|
|
464
538
|
base_id,
|
|
465
539
|
base,
|
|
466
|
-
|
|
540
|
+
data
|
|
467
541
|
}
|
|
468
542
|
];
|
|
469
543
|
}
|
|
@@ -588,11 +662,24 @@ class DdbObj {
|
|
|
588
662
|
return [Float64Array.from(this.value)];
|
|
589
663
|
case DdbType.duration:
|
|
590
664
|
return [value];
|
|
665
|
+
case DdbType.handle:
|
|
666
|
+
throw new Error('DolphinDB Server 不支持 handle 的反序列化');
|
|
591
667
|
default:
|
|
592
668
|
throw new Error(`${DdbType[type]} 暂时不支持序列化`);
|
|
593
669
|
}
|
|
594
670
|
case DdbForm.vector:
|
|
595
671
|
case DdbForm.pair:
|
|
672
|
+
// pack array vector
|
|
673
|
+
if (form === DdbForm.vector && 64 <= type && type < 128)
|
|
674
|
+
return [
|
|
675
|
+
Uint32Array.of(this.rows, this.cols),
|
|
676
|
+
...this.value.map(block => ([
|
|
677
|
+
Uint16Array.of(block.rows),
|
|
678
|
+
Uint8Array.of(block.unit, 0),
|
|
679
|
+
block.lengths,
|
|
680
|
+
block.data
|
|
681
|
+
])).flat()
|
|
682
|
+
];
|
|
596
683
|
return [
|
|
597
684
|
Uint32Array.of(this.rows, 1),
|
|
598
685
|
...DdbObj.pack_vector_body(value, type, this.rows)
|
|
@@ -708,6 +795,14 @@ class DdbObj {
|
|
|
708
795
|
bufs[i] = value[i].pack();
|
|
709
796
|
return bufs;
|
|
710
797
|
}
|
|
798
|
+
case DdbType.symbol_extended: {
|
|
799
|
+
const { base_id, base, data } = value;
|
|
800
|
+
return [
|
|
801
|
+
Uint32Array.of(base_id, base.length),
|
|
802
|
+
...this.pack_vector_body(base, DdbType.string, base.length),
|
|
803
|
+
data
|
|
804
|
+
];
|
|
805
|
+
}
|
|
711
806
|
default:
|
|
712
807
|
throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
|
|
713
808
|
}
|
|
@@ -721,6 +816,8 @@ class DdbObj {
|
|
|
721
816
|
return `functiondef<${DdbFunctionType[this.value.type]}>`;
|
|
722
817
|
return tname;
|
|
723
818
|
case DdbForm.vector:
|
|
819
|
+
if (64 <= this.type && this.type < 128)
|
|
820
|
+
return `${DdbType[this.type - 64]}[][${this.rows}]`;
|
|
724
821
|
return `${tname}[${this.rows}]`;
|
|
725
822
|
case DdbForm.pair:
|
|
726
823
|
return `pair<${tname}>`;
|
|
@@ -747,6 +844,15 @@ class DdbObj {
|
|
|
747
844
|
})();
|
|
748
845
|
return `${type.blue}(${this.name ? `${(0, utils_js_1.inspect)(this.name)}, ` : ''}${data})`;
|
|
749
846
|
}
|
|
847
|
+
to_dict() {
|
|
848
|
+
if (this.form !== DdbForm.dict)
|
|
849
|
+
throw new Error('this.form 不是 dict, 不能转换为 Object');
|
|
850
|
+
const [{ value: keys }, { value: values }] = this.value;
|
|
851
|
+
let obj = {};
|
|
852
|
+
for (let i = 0; i < this.rows; i++)
|
|
853
|
+
obj[keys[i]] = values[i].value;
|
|
854
|
+
return obj;
|
|
855
|
+
}
|
|
750
856
|
}
|
|
751
857
|
exports.DdbObj = DdbObj;
|
|
752
858
|
DdbObj.dec = new TextDecoder('utf-8');
|
|
@@ -896,93 +1002,159 @@ class DdbFunction extends DdbObj {
|
|
|
896
1002
|
}
|
|
897
1003
|
}
|
|
898
1004
|
exports.DdbFunction = DdbFunction;
|
|
1005
|
+
function date2str(date) {
|
|
1006
|
+
return date === exports.nulls.int32 ?
|
|
1007
|
+
''
|
|
1008
|
+
:
|
|
1009
|
+
(0, dayjs_1.default)(exports.timezone_offset + 1000 * 3600 * 24 * date).format('YYYY.MM.DD');
|
|
1010
|
+
}
|
|
1011
|
+
exports.date2str = date2str;
|
|
1012
|
+
function timestamp2str(timestamp) {
|
|
1013
|
+
return timestamp === exports.nulls.int64 ?
|
|
1014
|
+
''
|
|
1015
|
+
:
|
|
1016
|
+
(0, dayjs_1.default)(exports.timezone_offset + Number(timestamp)).format('YYYY.MM.DD HH:mm:ss.SSS');
|
|
1017
|
+
}
|
|
1018
|
+
exports.timestamp2str = timestamp2str;
|
|
899
1019
|
class DDB {
|
|
900
|
-
|
|
901
|
-
|
|
1020
|
+
/**
|
|
1021
|
+
Initialize an instance of DolphinDB Client using the WebSocket URL
|
|
1022
|
+
(without establishing an actual network connection)
|
|
1023
|
+
@example
|
|
1024
|
+
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
1025
|
+
let ddb_secure = new DDB('wss://dolphindb.com')
|
|
1026
|
+
*/
|
|
1027
|
+
constructor(url) {
|
|
902
1028
|
/** 当前的 session id (http 或 tcp) */
|
|
903
1029
|
this.sid = '0';
|
|
904
1030
|
/** utf-8 text decoder */
|
|
905
1031
|
this.dec = new TextDecoder('utf-8');
|
|
906
1032
|
this.enc = new TextEncoder();
|
|
907
1033
|
this.print_buffer = false;
|
|
908
|
-
this.
|
|
1034
|
+
this.websocket = null;
|
|
909
1035
|
/** little endian (server) */
|
|
910
1036
|
this.le = true;
|
|
1037
|
+
/** python session flag (2048) */
|
|
1038
|
+
this.python = false;
|
|
1039
|
+
this.message_hook = null;
|
|
911
1040
|
this.presult = Promise.resolve(null);
|
|
912
|
-
this.
|
|
1041
|
+
this.url = url;
|
|
1042
|
+
}
|
|
1043
|
+
/** print message handler */
|
|
1044
|
+
printer(message) {
|
|
1045
|
+
console.log(message);
|
|
913
1046
|
}
|
|
914
1047
|
/** resolver, rejector, promise of last rpc */
|
|
915
1048
|
presolver(buf) { }
|
|
916
1049
|
prejector(error) { }
|
|
917
|
-
/**
|
|
918
|
-
async connect({
|
|
919
|
-
this.
|
|
920
|
-
|
|
1050
|
+
/** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL */
|
|
1051
|
+
async connect({ url = this.url, login = true, username = 'admin', password = '123456', python = false, } = {}) {
|
|
1052
|
+
this.url = url;
|
|
1053
|
+
this.python = python;
|
|
1054
|
+
if (this.websocket?.readyState === ws_1.WebSocket.OPEN)
|
|
921
1055
|
this.disconnect();
|
|
922
|
-
let
|
|
1056
|
+
let websocket = new ws_1.WebSocket(url, python ? ['python'] : [], {
|
|
923
1057
|
maxPayload: 2 ** 33 // 8 GB
|
|
924
1058
|
});
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1059
|
+
// https://stackoverflow.com/questions/11821096/what-is-the-difference-between-an-arraybuffer-and-a-blob/39951543
|
|
1060
|
+
websocket.binaryType = 'arraybuffer';
|
|
1061
|
+
this.websocket = websocket;
|
|
1062
|
+
await new Promise((resolve, reject) => {
|
|
1063
|
+
websocket.addEventListener('open', async (event) => {
|
|
1064
|
+
console.log(`${websocket.url} opened`);
|
|
1065
|
+
await this.rpc('connect', {});
|
|
1066
|
+
resolve();
|
|
1067
|
+
});
|
|
1068
|
+
websocket.addEventListener('close', event => {
|
|
1069
|
+
console.log(`${websocket.url} closed with code = ${event.code}, reason = '${event.reason}'`);
|
|
1070
|
+
});
|
|
1071
|
+
websocket.addEventListener('error', event => {
|
|
1072
|
+
const message = `${websocket.url} errored`;
|
|
1073
|
+
console.error(message, event);
|
|
1074
|
+
reject(Object.assign(new Error(message), { event }));
|
|
1075
|
+
});
|
|
1076
|
+
websocket.addEventListener('message', event => {
|
|
1077
|
+
const buf = new Uint8Array(event.data);
|
|
1078
|
+
if (this.message_hook)
|
|
1079
|
+
this.message_hook(buf);
|
|
1080
|
+
try {
|
|
1081
|
+
const { type, data } = this.parse_message(buf);
|
|
1082
|
+
if (type === 'message') {
|
|
1083
|
+
this.printer(data);
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
this.presolver(data);
|
|
1087
|
+
}
|
|
1088
|
+
catch (error) {
|
|
1089
|
+
this.prejector(error);
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
951
1092
|
});
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1093
|
+
if (!this.python)
|
|
1094
|
+
await this.eval('def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
|
|
1095
|
+
' nargs = size(args)\n' +
|
|
1096
|
+
' func = funcByName(func_name)\n' +
|
|
1097
|
+
' \n' +
|
|
1098
|
+
' if (!nargs)\n' +
|
|
1099
|
+
' return pnodeRun(func, nodes, add_node_alias)\n' +
|
|
1100
|
+
' \n' +
|
|
1101
|
+
' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
|
|
1102
|
+
' args_partial[0] = func\n' +
|
|
1103
|
+
' args_partial[1:] = args\n' +
|
|
1104
|
+
' return pnodeRun(\n' +
|
|
1105
|
+
' unifiedCall(partial, args_partial),\n' +
|
|
1106
|
+
' nodes,\n' +
|
|
1107
|
+
' add_node_alias\n' +
|
|
1108
|
+
' )\n' +
|
|
1109
|
+
'}\n', { urgent: true });
|
|
969
1110
|
if (login)
|
|
970
1111
|
await this.call('login', [username, password], { urgent: true });
|
|
971
1112
|
}
|
|
1113
|
+
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, } = {}) {
|
|
1114
|
+
let flag = 0;
|
|
1115
|
+
if (urgent)
|
|
1116
|
+
flag += 1;
|
|
1117
|
+
if (secondary)
|
|
1118
|
+
flag += 2;
|
|
1119
|
+
if (_async)
|
|
1120
|
+
flag += 4;
|
|
1121
|
+
if (pickle)
|
|
1122
|
+
flag += 8;
|
|
1123
|
+
if (clear)
|
|
1124
|
+
flag += 16;
|
|
1125
|
+
if (api)
|
|
1126
|
+
flag += 32;
|
|
1127
|
+
if (compress)
|
|
1128
|
+
flag += 64;
|
|
1129
|
+
// python session
|
|
1130
|
+
if (this.python)
|
|
1131
|
+
flag += 2048;
|
|
1132
|
+
const options = [
|
|
1133
|
+
flag,
|
|
1134
|
+
cancellable ? 1 : 0,
|
|
1135
|
+
priority,
|
|
1136
|
+
parallelism,
|
|
1137
|
+
...limit ? [
|
|
1138
|
+
root_id,
|
|
1139
|
+
limit,
|
|
1140
|
+
] : [],
|
|
1141
|
+
];
|
|
1142
|
+
return `/ ${options.join('_')}`;
|
|
1143
|
+
}
|
|
972
1144
|
disconnect() {
|
|
973
|
-
this.
|
|
1145
|
+
this.websocket?.close(1000);
|
|
974
1146
|
}
|
|
975
|
-
/** rpc through websocket (function command)
|
|
1147
|
+
/** rpc through websocket (function/script/variable command)
|
|
976
1148
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
977
1149
|
- options:
|
|
978
1150
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
979
1151
|
- vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
|
|
980
1152
|
*/
|
|
981
1153
|
async rpc(type, { script, func, args = [], vars = [], urgent, }) {
|
|
982
|
-
if (!this.
|
|
1154
|
+
if (!this.websocket)
|
|
983
1155
|
await this.connect();
|
|
984
|
-
if (this.
|
|
985
|
-
throw new Error(
|
|
1156
|
+
if (this.websocket.readyState !== ws_1.WebSocket.OPEN)
|
|
1157
|
+
throw new Error(`${this.url} is already disconnected`);
|
|
986
1158
|
// 临界区:保证多个 rpc 并发时形成 promise 链
|
|
987
1159
|
// ddb 世界观:需要等待上一个 rpc 结果从 server 返回之后才能发起下一个调用
|
|
988
1160
|
// 违反世界观可能造成:
|
|
@@ -1020,14 +1192,16 @@ class DDB {
|
|
|
1020
1192
|
`${vars.join(',')}\n` +
|
|
1021
1193
|
`${vars.length}\n` +
|
|
1022
1194
|
`${Number(DDB.le_client)}\n`;
|
|
1195
|
+
case 'connect':
|
|
1196
|
+
return 'connect\n';
|
|
1023
1197
|
}
|
|
1024
1198
|
})());
|
|
1025
1199
|
const message = (0, utils_js_1.concat)([
|
|
1026
|
-
this.enc.encode(`
|
|
1200
|
+
this.enc.encode(`API2 ${this.sid} ${command.length}${this.get_rpc_options({ urgent })}\n`),
|
|
1027
1201
|
command,
|
|
1028
1202
|
...args.map((arg) => arg.pack())
|
|
1029
1203
|
]);
|
|
1030
|
-
this.
|
|
1204
|
+
this.websocket.send(message);
|
|
1031
1205
|
const buf_data = await presult;
|
|
1032
1206
|
if (this.print_buffer)
|
|
1033
1207
|
console.log((0, utils_js_1.typed_array_to_buffer)(buf_data));
|
|
@@ -1047,13 +1221,15 @@ class DDB {
|
|
|
1047
1221
|
- func: 函数名
|
|
1048
1222
|
- args?: `[ ]` 调用参数 (传入的原生 string 和 boolean 会被自动转换为 DdbObj<string> 和 DdbObj<boolean>)
|
|
1049
1223
|
- options?: 调用选项
|
|
1050
|
-
- urgent?: 紧急 flag
|
|
1224
|
+
- urgent?: 紧急 flag。使用 urgent worker 执行,防止被其它作业阻塞
|
|
1051
1225
|
- node?: 设置结点 alias 时发送到集群中对应的结点执行 (使用 DolphinDB 中的 rpc 方法)
|
|
1052
1226
|
- nodes?: 设置多个结点 alias 时发送到集群中对应的多个结点执行 (使用 DolphinDB 中的 pnodeRun 方法)
|
|
1053
1227
|
- func_type?: 设置 node 参数时必传,需指定函数类型,其它情况下不传
|
|
1054
1228
|
- add_node_alias?: 设置 nodes 参数时选传,其它情况不传
|
|
1055
1229
|
*/
|
|
1056
1230
|
async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias } = {}) {
|
|
1231
|
+
if (this.python)
|
|
1232
|
+
throw new Error('call doesn\'t support python session yet');
|
|
1057
1233
|
if (node) {
|
|
1058
1234
|
if (typeof func_type === 'undefined')
|
|
1059
1235
|
throw new Error('指定 node 时必须设置 func_type');
|
|
@@ -1069,7 +1245,13 @@ class DDB {
|
|
|
1069
1245
|
new DdbVectorString(nodes),
|
|
1070
1246
|
func,
|
|
1071
1247
|
new DdbVectorAny(this.to_ddbobjs(args)),
|
|
1072
|
-
...
|
|
1248
|
+
...(() => {
|
|
1249
|
+
if (typeof add_node_alias !== 'undefined')
|
|
1250
|
+
return [add_node_alias];
|
|
1251
|
+
if (this.python)
|
|
1252
|
+
return [true];
|
|
1253
|
+
return [];
|
|
1254
|
+
})()
|
|
1073
1255
|
];
|
|
1074
1256
|
func = 'pnode_run';
|
|
1075
1257
|
}
|
|
@@ -1081,9 +1263,9 @@ class DDB {
|
|
|
1081
1263
|
}
|
|
1082
1264
|
/** upload variable through websocket (variable command) */
|
|
1083
1265
|
async upload(
|
|
1084
|
-
/**
|
|
1266
|
+
/** Uploaded variables' name */
|
|
1085
1267
|
vars,
|
|
1086
|
-
/**
|
|
1268
|
+
/** Uploaded variables' value */
|
|
1087
1269
|
args) {
|
|
1088
1270
|
if (!args.length || args.length !== vars.length)
|
|
1089
1271
|
throw new Error('variable 指令参数为空或参数名为空,或数量不匹配');
|
|
@@ -1091,6 +1273,14 @@ class DDB {
|
|
|
1091
1273
|
}
|
|
1092
1274
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
1093
1275
|
parse_message(buf) {
|
|
1276
|
+
// MSG\n
|
|
1277
|
+
// <message>\0
|
|
1278
|
+
// 'M'.codePointAt(0).to_hex_str()
|
|
1279
|
+
if (buf[0] === 0x4d && buf[1] === 0x53 && buf[2] === 0x47 && buf[3] === 0x0a)
|
|
1280
|
+
return {
|
|
1281
|
+
type: 'message',
|
|
1282
|
+
data: this.dec.decode(buf.subarray(4))
|
|
1283
|
+
};
|
|
1094
1284
|
// '1166953221 1 1\n'
|
|
1095
1285
|
// 'OK\n'
|
|
1096
1286
|
// '\x04\x00\x02\x00\x00\x00'
|
|
@@ -1113,7 +1303,10 @@ class DDB {
|
|
|
1113
1303
|
const message = this.dec.decode(buf.subarray(i_ls_1, i_lf_1));
|
|
1114
1304
|
if (message !== 'OK')
|
|
1115
1305
|
throw new Error(message);
|
|
1116
|
-
return
|
|
1306
|
+
return {
|
|
1307
|
+
type: 'object',
|
|
1308
|
+
data: buf.subarray(i_lf_1 + 1)
|
|
1309
|
+
};
|
|
1117
1310
|
}
|
|
1118
1311
|
/** 自动转换 js string, boolean 为 DdbObj */
|
|
1119
1312
|
to_ddbobj(value) {
|