dolphindb 2.0.943 → 2.0.945
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.d.ts +34 -13
- package/browser.js +208 -138
- package/browser.js.map +1 -1
- package/i18n/dict.json +3 -0
- package/index.d.ts +39 -16
- package/index.js +205 -135
- package/index.js.map +1 -1
- package/package.json +2 -2
- package/test.d.ts +3 -0
- package/test.js +232 -0
- package/test.js.map +1 -0
package/i18n/dict.json
CHANGED
package/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
import type { InspectOptions as UtilInspectOptions } from 'util';
|
|
3
|
-
import { inspect, type WebSocketConnectionError } from 'xshell';
|
|
3
|
+
import { inspect, WebSocket, type WebSocketConnectionError } from 'xshell';
|
|
4
4
|
export declare enum DdbForm {
|
|
5
5
|
scalar = 0,
|
|
6
6
|
vector = 1,
|
|
@@ -258,7 +258,7 @@ export declare class DdbObj<TValue extends DdbValue = DdbValue> {
|
|
|
258
258
|
/** 自动转换 js string, boolean 为 DdbObj */
|
|
259
259
|
static to_ddbobj(value: DdbObj | string | boolean): DdbObj;
|
|
260
260
|
/** 转换 js 数组为 DdbObj[] */
|
|
261
|
-
static to_ddbobjs(values:
|
|
261
|
+
static to_ddbobjs(values: (DdbObj | string | boolean)[]): DdbObj<DdbValue>[];
|
|
262
262
|
to_rows<T extends Record<string, any> = Record<string, any>>(): T[];
|
|
263
263
|
/** 将 dict<string, any> 自动转换为 js object (Record<string, any>) Automatically convert dict<string, any> to js object (Record<string, any>)
|
|
264
264
|
- options?:
|
|
@@ -337,7 +337,7 @@ export declare class DdbVectorString extends DdbObj<string[]> {
|
|
|
337
337
|
constructor(strings: string[], name?: string);
|
|
338
338
|
}
|
|
339
339
|
export declare class DdbVectorAny extends DdbObj {
|
|
340
|
-
constructor(objs: (DdbObj | string | boolean
|
|
340
|
+
constructor(objs: (DdbObj | string | boolean)[], name?: string);
|
|
341
341
|
}
|
|
342
342
|
export declare class DdbVectorSymbol extends DdbObj<DdbSymbolExtendedValue> {
|
|
343
343
|
constructor(strings: string[], name?: string);
|
|
@@ -352,8 +352,10 @@ export declare class DdbSetString extends DdbObj<string[]> {
|
|
|
352
352
|
constructor(strings: string[] | Set<string>);
|
|
353
353
|
}
|
|
354
354
|
/** 构造 DdbDict 对象,支持两种用法: Constructs a DdbDict object, which supports two usages:
|
|
355
|
-
- 传入类型是 DdbVectorObj 的 keys, values 两个参数直接组成 dict<keys.type, values.type> 的 DdbDict
|
|
356
|
-
|
|
355
|
+
- 传入类型是 DdbVectorObj 的 keys, values 两个参数直接组成 dict<keys.type, values.type> 的 DdbDict
|
|
356
|
+
The incoming type is the keys of DdbObj<DdbVectorValue>, and the two parameters of values directly form the DdbDict of dict<keys.type, values.type>
|
|
357
|
+
- 传入 js object (类型是 Record<string, boolean | string | DdbObj>), 自动转换为 dict<string, any> 的 DdbDict
|
|
358
|
+
Pass in js object (type is Record<string, boolean | string | DdbObj>), automatically converted to DdbDict of dict<string, any> */
|
|
357
359
|
export declare class DdbDict extends DdbObj<DdbDictValue> {
|
|
358
360
|
constructor(obj: Record<string, boolean | string | DdbObj>);
|
|
359
361
|
constructor(keys: DdbVectorObj, values: DdbVectorObj);
|
|
@@ -454,7 +456,7 @@ type DdbRpcType = 'script' | 'function' | 'variable' | 'connect';
|
|
|
454
456
|
export interface DdbRpcOptions {
|
|
455
457
|
script?: string;
|
|
456
458
|
func?: string;
|
|
457
|
-
args?: (DdbObj | string | boolean
|
|
459
|
+
args?: (DdbObj | string | boolean)[];
|
|
458
460
|
vars?: string[];
|
|
459
461
|
urgent?: boolean;
|
|
460
462
|
listener?: DdbMessageListener;
|
|
@@ -498,17 +500,26 @@ export declare class DDB {
|
|
|
498
500
|
python: boolean;
|
|
499
501
|
/** 是否为流数据连接,非流数据这个字段恒为 null Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
500
502
|
streaming: StreamingData;
|
|
503
|
+
/** 是否打印每个 rpc 的信息用于调试 */
|
|
504
|
+
verbose: boolean;
|
|
501
505
|
print_message_buffer: boolean;
|
|
502
506
|
print_object_buffer: boolean;
|
|
503
507
|
print_message: boolean;
|
|
504
508
|
parse_object: boolean;
|
|
505
509
|
pnode_run_defined: boolean;
|
|
510
|
+
/** 在 websocket 收到的第一个 error 时,
|
|
511
|
+
在 connect_websocket 的 on_error 回调中构造 DdbConnectionError 并保存到 DDB 对象上,
|
|
512
|
+
这个 error 的错误信息最准确
|
|
513
|
+
this.errored 为 true 时建议 throw this.error || new DdbConnectionError(this) */
|
|
514
|
+
error: DdbConnectionError;
|
|
506
515
|
/** DdbMessage listeners */
|
|
507
516
|
listeners: DdbMessageListener[];
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
517
|
+
/** 为所有 websocket 操作加锁,包括设置 this.on_message, this.on_error, websocket.send */
|
|
518
|
+
pwebsocket: import("xshell").Deferred<void>;
|
|
519
|
+
/** 为定义 pnode_run 函数加锁,避免重复定义 */
|
|
520
|
+
ppnode_run: import("xshell").Deferred<void>;
|
|
511
521
|
get connected(): boolean;
|
|
522
|
+
get errored(): boolean | DdbConnectionError;
|
|
512
523
|
/**
|
|
513
524
|
使用 WebSocket URL 初始化连接到 DolphinDB 的实例(不建立实际的网络连接)
|
|
514
525
|
Initialize an instance of DolphinDB Client using the WebSocket URL (without establishing an actual network connection)
|
|
@@ -519,6 +530,7 @@ export declare class DDB {
|
|
|
519
530
|
- password?: DolphinDB 登录密码,默认 `'123456'` DolphinDB password, default `'123456'`
|
|
520
531
|
- python?: 设置 python session flag,默认 `false` set python session flag, default `false`
|
|
521
532
|
- streaming?: 设置该选项后,该 WebSocket 连接只用于流数据 When this option is set, the WebSocket connection is only used for streaming data
|
|
533
|
+
- verbose?: 是否打印每个 rpc 的信息用于调试
|
|
522
534
|
|
|
523
535
|
@example
|
|
524
536
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -537,12 +549,23 @@ export declare class DDB {
|
|
|
537
549
|
password?: string;
|
|
538
550
|
python?: boolean;
|
|
539
551
|
streaming?: StreamingParams;
|
|
552
|
+
verbose?: boolean;
|
|
540
553
|
});
|
|
541
554
|
private on_message;
|
|
542
555
|
private on_error;
|
|
543
|
-
/**
|
|
544
|
-
|
|
545
|
-
|
|
556
|
+
/** 调用后会确保和数据库的连接是正常的 (this.connected === true && this.errored === false),否则抛出错误
|
|
557
|
+
这个方法是幂等的,首次调用建立实际的 WebSocket 连接到 URL 对应的 DolphinDB,后续调用检查上面的条件
|
|
558
|
+
连接断开后禁止再次调用 connect 重连原有 ddb 对象,应该通过 new DDB() 的方式新建连接对象,原因是:
|
|
559
|
+
1. on_error 回调和某个 websocket 绑定了,不方便解绑后重新绑定
|
|
560
|
+
2. session 是有状态的,重连也无法恢复之前的状态
|
|
561
|
+
3. 断线后所有之前的 ddb.call, ddb.eval 都应该抛出连接错误
|
|
562
|
+
|
|
563
|
+
After calling, it will ensure that the connection with the database is normal (this.connected === true && this.errored === false), otherwise an error will be thrown
|
|
564
|
+
This method is idempotent, the first call establishes an actual WebSocket connection to the DolphinDB corresponding to the URL, and subsequent calls check the above conditions
|
|
565
|
+
After the connection is disconnected, it is forbidden to call connect again to reconnect the original ddb object. You should use new DDB() to create a new connection object because:
|
|
566
|
+
1. The on_error callback is bound to a certain websocket, and it is inconvenient to unbind and rebind
|
|
567
|
+
2. The session is stateful, and the previous state cannot be restored even after reconnection
|
|
568
|
+
3. After disconnection, all previous ddb.call, ddb.eval should throw a connection error */
|
|
546
569
|
connect(): Promise<void>;
|
|
547
570
|
get_rpc_options({ urgent, secondary, async: _async, pickle, clear, api, compress, cancellable, priority, parallelism, root_id, limit, }?: {
|
|
548
571
|
urgent?: boolean;
|
|
@@ -568,10 +591,10 @@ export declare class DDB {
|
|
|
568
591
|
limit?: boolean;
|
|
569
592
|
}): string;
|
|
570
593
|
disconnect(): void;
|
|
571
|
-
/** rpc through websocket (function/script/variable command)
|
|
594
|
+
/** (内部使用的方法) rpc through websocket (function/script/variable command)
|
|
572
595
|
未连接到 DDB 时调用会自动连接,连接断开时调用会抛出 DdbConnectionError
|
|
573
596
|
When the DDB is not connected, the call will be automatically connected. When the connection is disconnected, the call will throw the DdbConnectionError
|
|
574
|
-
- type: API 类型: 'script' | 'function' | 'variable'
|
|
597
|
+
- type: API 类型: 'script' | 'function' | 'variable' | 'connect'
|
|
575
598
|
- options:
|
|
576
599
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
577
600
|
- vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
|
|
@@ -619,7 +642,7 @@ export declare class DDB {
|
|
|
619
642
|
When it is false, the returned DdbObj only contains buffer and le without parsing,
|
|
620
643
|
so as to facilitate subsequent forwarding and serialization
|
|
621
644
|
*/
|
|
622
|
-
call<TResult extends DdbObj>(func: string, args?: (DdbObj | string | boolean
|
|
645
|
+
call<TResult extends DdbObj>(func: string, args?: (DdbObj | string | boolean)[], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, }?: {
|
|
623
646
|
urgent?: boolean;
|
|
624
647
|
node?: string;
|
|
625
648
|
nodes?: string[];
|
|
@@ -657,7 +680,7 @@ export interface DdbObjectMessage {
|
|
|
657
680
|
}
|
|
658
681
|
export interface DdbErrorMessage {
|
|
659
682
|
type: 'error';
|
|
660
|
-
data:
|
|
683
|
+
data: DdbDatabaseError;
|
|
661
684
|
}
|
|
662
685
|
export type DdbMessage = DdbPrintMessage | DdbObjectMessage | DdbErrorMessage;
|
|
663
686
|
export {};
|
package/index.js
CHANGED
|
@@ -1677,6 +1677,7 @@ export class DdbNanoTimeStamp extends DdbObj {
|
|
|
1677
1677
|
}
|
|
1678
1678
|
export class DdbBlob extends DdbObj {
|
|
1679
1679
|
constructor(value) {
|
|
1680
|
+
assert(value, t('new DdbBlob 不能传空的 value'));
|
|
1680
1681
|
super({
|
|
1681
1682
|
form: DdbForm.scalar,
|
|
1682
1683
|
type: DdbType.blob,
|
|
@@ -1865,8 +1866,10 @@ export class DdbSetString extends DdbObj {
|
|
|
1865
1866
|
}
|
|
1866
1867
|
}
|
|
1867
1868
|
/** 构造 DdbDict 对象,支持两种用法: Constructs a DdbDict object, which supports two usages:
|
|
1868
|
-
- 传入类型是 DdbVectorObj 的 keys, values 两个参数直接组成 dict<keys.type, values.type> 的 DdbDict
|
|
1869
|
-
|
|
1869
|
+
- 传入类型是 DdbVectorObj 的 keys, values 两个参数直接组成 dict<keys.type, values.type> 的 DdbDict
|
|
1870
|
+
The incoming type is the keys of DdbObj<DdbVectorValue>, and the two parameters of values directly form the DdbDict of dict<keys.type, values.type>
|
|
1871
|
+
- 传入 js object (类型是 Record<string, boolean | string | DdbObj>), 自动转换为 dict<string, any> 的 DdbDict
|
|
1872
|
+
Pass in js object (type is Record<string, boolean | string | DdbObj>), automatically converted to DdbDict of dict<string, any> */
|
|
1870
1873
|
export class DdbDict extends DdbObj {
|
|
1871
1874
|
constructor(arg0, arg1) {
|
|
1872
1875
|
if (arg1)
|
|
@@ -2138,6 +2141,8 @@ export class DdbConnectionError extends Error {
|
|
|
2138
2141
|
ddb;
|
|
2139
2142
|
constructor(ddb, error) {
|
|
2140
2143
|
super(error?.message || `${ddb.url} ${t('连接出错了,可能由于网络原因连接已被关闭,或服务器断开连接')}`, { cause: error });
|
|
2144
|
+
if (error)
|
|
2145
|
+
this.cause = error;
|
|
2141
2146
|
this.ddb = ddb;
|
|
2142
2147
|
}
|
|
2143
2148
|
}
|
|
@@ -2183,19 +2188,30 @@ export class DDB {
|
|
|
2183
2188
|
python = false;
|
|
2184
2189
|
/** 是否为流数据连接,非流数据这个字段恒为 null Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
2185
2190
|
streaming = null;
|
|
2191
|
+
/** 是否打印每个 rpc 的信息用于调试 */
|
|
2192
|
+
verbose = false;
|
|
2186
2193
|
// --- 内部选项, 状态
|
|
2187
2194
|
print_message_buffer = false;
|
|
2188
2195
|
print_object_buffer = false;
|
|
2189
2196
|
print_message = true;
|
|
2190
2197
|
parse_object = true;
|
|
2191
2198
|
pnode_run_defined = false;
|
|
2199
|
+
/** 在 websocket 收到的第一个 error 时,
|
|
2200
|
+
在 connect_websocket 的 on_error 回调中构造 DdbConnectionError 并保存到 DDB 对象上,
|
|
2201
|
+
这个 error 的错误信息最准确
|
|
2202
|
+
this.errored 为 true 时建议 throw this.error || new DdbConnectionError(this) */
|
|
2203
|
+
error;
|
|
2192
2204
|
/** DdbMessage listeners */
|
|
2193
2205
|
listeners = [];
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2206
|
+
/** 为所有 websocket 操作加锁,包括设置 this.on_message, this.on_error, websocket.send */
|
|
2207
|
+
pwebsocket = defer(null);
|
|
2208
|
+
/** 为定义 pnode_run 函数加锁,避免重复定义 */
|
|
2209
|
+
ppnode_run = defer(null);
|
|
2197
2210
|
get connected() {
|
|
2198
|
-
return this.websocket?.readyState === WebSocket.OPEN;
|
|
2211
|
+
return !this.error && this.websocket?.readyState === WebSocket.OPEN;
|
|
2212
|
+
}
|
|
2213
|
+
get errored() {
|
|
2214
|
+
return this.error || (this.websocket && this.websocket.readyState !== WebSocket.OPEN);
|
|
2199
2215
|
}
|
|
2200
2216
|
/**
|
|
2201
2217
|
使用 WebSocket URL 初始化连接到 DolphinDB 的实例(不建立实际的网络连接)
|
|
@@ -2207,6 +2223,7 @@ export class DDB {
|
|
|
2207
2223
|
- password?: DolphinDB 登录密码,默认 `'123456'` DolphinDB password, default `'123456'`
|
|
2208
2224
|
- python?: 设置 python session flag,默认 `false` set python session flag, default `false`
|
|
2209
2225
|
- streaming?: 设置该选项后,该 WebSocket 连接只用于流数据 When this option is set, the WebSocket connection is only used for streaming data
|
|
2226
|
+
- verbose?: 是否打印每个 rpc 的信息用于调试
|
|
2210
2227
|
|
|
2211
2228
|
@example
|
|
2212
2229
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -2221,6 +2238,8 @@ export class DDB {
|
|
|
2221
2238
|
*/
|
|
2222
2239
|
constructor(url, options = {}) {
|
|
2223
2240
|
this.url = url;
|
|
2241
|
+
if (options.verbose !== undefined)
|
|
2242
|
+
this.verbose = options.verbose;
|
|
2224
2243
|
if (options.autologin !== undefined)
|
|
2225
2244
|
this.autologin = options.autologin;
|
|
2226
2245
|
if (options.username !== undefined)
|
|
@@ -2233,61 +2252,85 @@ export class DDB {
|
|
|
2233
2252
|
this.streaming = options.streaming;
|
|
2234
2253
|
}
|
|
2235
2254
|
on_message(buffer, websocket) {
|
|
2236
|
-
|
|
2255
|
+
throw t('这是在调用 this.rpc 之前默认的 on_message, 不应该被调用到,除非建立连接后 server 先推送了 message');
|
|
2237
2256
|
}
|
|
2238
|
-
on_error(
|
|
2257
|
+
on_error() {
|
|
2239
2258
|
// 这里的实现一定会被 connect, rpc 中的实现覆盖
|
|
2240
2259
|
}
|
|
2241
|
-
/**
|
|
2242
|
-
|
|
2243
|
-
|
|
2260
|
+
/** 调用后会确保和数据库的连接是正常的 (this.connected === true && this.errored === false),否则抛出错误
|
|
2261
|
+
这个方法是幂等的,首次调用建立实际的 WebSocket 连接到 URL 对应的 DolphinDB,后续调用检查上面的条件
|
|
2262
|
+
连接断开后禁止再次调用 connect 重连原有 ddb 对象,应该通过 new DDB() 的方式新建连接对象,原因是:
|
|
2263
|
+
1. on_error 回调和某个 websocket 绑定了,不方便解绑后重新绑定
|
|
2264
|
+
2. session 是有状态的,重连也无法恢复之前的状态
|
|
2265
|
+
3. 断线后所有之前的 ddb.call, ddb.eval 都应该抛出连接错误
|
|
2266
|
+
|
|
2267
|
+
After calling, it will ensure that the connection with the database is normal (this.connected === true && this.errored === false), otherwise an error will be thrown
|
|
2268
|
+
This method is idempotent, the first call establishes an actual WebSocket connection to the DolphinDB corresponding to the URL, and subsequent calls check the above conditions
|
|
2269
|
+
After the connection is disconnected, it is forbidden to call connect again to reconnect the original ddb object. You should use new DDB() to create a new connection object because:
|
|
2270
|
+
1. The on_error callback is bound to a certain websocket, and it is inconvenient to unbind and rebind
|
|
2271
|
+
2. The session is stateful, and the previous state cannot be restored even after reconnection
|
|
2272
|
+
3. After disconnection, all previous ddb.call, ddb.eval should throw a connection error */
|
|
2244
2273
|
async connect() {
|
|
2274
|
+
if (this.errored)
|
|
2275
|
+
throw this.error || new DdbConnectionError(this);
|
|
2245
2276
|
if (this.connected)
|
|
2246
2277
|
return;
|
|
2247
|
-
|
|
2248
|
-
const
|
|
2249
|
-
|
|
2278
|
+
// 准备进入 websocket 临界区
|
|
2279
|
+
const ptail = this.pwebsocket;
|
|
2280
|
+
let pwebsocket = this.pwebsocket = defer();
|
|
2281
|
+
// 避免 pwebsocket.reject 后出现 triggerUncaughtException
|
|
2282
|
+
pwebsocket.catch(() => { });
|
|
2283
|
+
// 上一个请求出现了 websocket 错误,那么直接抛出异常,且通过 reject 当前锁 (pwebsocket),安全退出临界区
|
|
2250
2284
|
try {
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2285
|
+
await ptail;
|
|
2286
|
+
// 已进入 websocket 临界区
|
|
2287
|
+
}
|
|
2288
|
+
catch (error) {
|
|
2289
|
+
pwebsocket.reject(error);
|
|
2290
|
+
throw error;
|
|
2291
|
+
}
|
|
2292
|
+
if (this.errored) {
|
|
2293
|
+
const error = this.error || new DdbConnectionError(this);
|
|
2294
|
+
pwebsocket.reject(error);
|
|
2295
|
+
throw error;
|
|
2296
|
+
}
|
|
2297
|
+
if (this.connected) {
|
|
2298
|
+
pwebsocket.resolve();
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
this.on_error = () => {
|
|
2302
|
+
pwebsocket.reject(this.error /* 一定有,不需要再 || new DdbConnectionError(this) */);
|
|
2303
|
+
};
|
|
2304
|
+
try {
|
|
2305
|
+
this.websocket = await connect_websocket(this.url, {
|
|
2306
|
+
protocols: (() => {
|
|
2307
|
+
if (this.streaming)
|
|
2308
|
+
return 'streaming';
|
|
2309
|
+
if (this.python)
|
|
2310
|
+
return 'python';
|
|
2311
|
+
})(),
|
|
2312
|
+
on_message: (buffer, websocket) => {
|
|
2313
|
+
this.on_message(buffer, websocket);
|
|
2314
|
+
},
|
|
2315
|
+
on_error: error => {
|
|
2316
|
+
this.error ??= new DdbConnectionError(this, error);
|
|
2317
|
+
this.on_error();
|
|
2285
2318
|
}
|
|
2286
|
-
}
|
|
2319
|
+
});
|
|
2287
2320
|
}
|
|
2288
|
-
|
|
2289
|
-
|
|
2321
|
+
catch (error) {
|
|
2322
|
+
this.error ??= new DdbConnectionError(this, error);
|
|
2323
|
+
pwebsocket.reject(error);
|
|
2324
|
+
throw this.error;
|
|
2290
2325
|
}
|
|
2326
|
+
assert(this.connected);
|
|
2327
|
+
pwebsocket.resolve();
|
|
2328
|
+
// websocket 临界区结束
|
|
2329
|
+
await this.rpc('connect', {});
|
|
2330
|
+
if (this.autologin)
|
|
2331
|
+
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2332
|
+
if (this.streaming)
|
|
2333
|
+
await this.subscribe();
|
|
2291
2334
|
}
|
|
2292
2335
|
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, } = {}) {
|
|
2293
2336
|
let flag = 0;
|
|
@@ -2324,10 +2367,10 @@ export class DDB {
|
|
|
2324
2367
|
if (this.connected)
|
|
2325
2368
|
this.websocket.close(1000);
|
|
2326
2369
|
}
|
|
2327
|
-
/** rpc through websocket (function/script/variable command)
|
|
2370
|
+
/** (内部使用的方法) rpc through websocket (function/script/variable command)
|
|
2328
2371
|
未连接到 DDB 时调用会自动连接,连接断开时调用会抛出 DdbConnectionError
|
|
2329
2372
|
When the DDB is not connected, the call will be automatically connected. When the connection is disconnected, the call will throw the DdbConnectionError
|
|
2330
|
-
- type: API 类型: 'script' | 'function' | 'variable'
|
|
2373
|
+
- type: API 类型: 'script' | 'function' | 'variable' | 'connect'
|
|
2331
2374
|
- options:
|
|
2332
2375
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
2333
2376
|
- vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
|
|
@@ -2335,19 +2378,27 @@ export class DDB {
|
|
|
2335
2378
|
- parse_object?: 在本次 rpc 期间设置 parse_object, 结束后恢复原有
|
|
2336
2379
|
为 false 时返回的 DdbObj 仅含有 buffer 和 le,不做解析,以便后续转发、序列化 */
|
|
2337
2380
|
async rpc(type, options) {
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
const { script, func, vars = [], urgent, listener, } = options;
|
|
2343
|
-
let { args = [] } = options;
|
|
2381
|
+
// 保留调用栈信息
|
|
2382
|
+
let error = new DdbDatabaseError('', this, type, options);
|
|
2383
|
+
await this.connect();
|
|
2384
|
+
const { script, func, args: _args = [], vars = [], urgent, listener, } = options;
|
|
2344
2385
|
if (func === 'pnode_run' && !this.pnode_run_defined) {
|
|
2345
|
-
//
|
|
2346
|
-
const ptail = this.
|
|
2347
|
-
|
|
2348
|
-
|
|
2386
|
+
// 准备进入 pnode_run 临界区,保证并发调用 rpc 时只定义一次 pnode_run
|
|
2387
|
+
const ptail = this.ppnode_run;
|
|
2388
|
+
let ppnode_run = this.ppnode_run = defer();
|
|
2389
|
+
// 避免 ppnode_run.reject 后出现 triggerUncaughtException
|
|
2390
|
+
ppnode_run.catch(() => { });
|
|
2391
|
+
// 如果之前的定义失败了,再定义一次也会失败,直接抛出之前的错误,且安全地退出临界区
|
|
2349
2392
|
try {
|
|
2350
|
-
|
|
2393
|
+
await ptail;
|
|
2394
|
+
// 已进入 pnode_run 临界区
|
|
2395
|
+
}
|
|
2396
|
+
catch (error) {
|
|
2397
|
+
ppnode_run.reject(error);
|
|
2398
|
+
throw error;
|
|
2399
|
+
}
|
|
2400
|
+
if (!this.pnode_run_defined)
|
|
2401
|
+
try {
|
|
2351
2402
|
await this.eval(this.python ?
|
|
2352
2403
|
'def pnode_run (nodes, func_name, args, add_node_alias):\n' +
|
|
2353
2404
|
' nargs = size(args)\n' +
|
|
@@ -2383,86 +2434,108 @@ export class DDB {
|
|
|
2383
2434
|
' add_node_alias\n' +
|
|
2384
2435
|
' )\n' +
|
|
2385
2436
|
'}\n', { urgent: true });
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2437
|
+
this.pnode_run_defined = true;
|
|
2438
|
+
ppnode_run.resolve();
|
|
2439
|
+
}
|
|
2440
|
+
catch (error) {
|
|
2441
|
+
// 这次失败了,之后的执行肯定也会失败
|
|
2442
|
+
ppnode_run.reject(error);
|
|
2443
|
+
throw error;
|
|
2444
|
+
}
|
|
2390
2445
|
}
|
|
2391
2446
|
// this 上的当前配置需要在 message 到达后使用,先保存起来
|
|
2392
|
-
const
|
|
2393
|
-
|
|
2394
|
-
// 临界区:保证多个 rpc 并发时形成 promise 链
|
|
2447
|
+
const _listeners = [...this.listeners].reverse();
|
|
2448
|
+
// websocket 临界区:保证多个 rpc 并发时形成 promise 链
|
|
2395
2449
|
// ddb 世界观:需要等待上一个 rpc 结果从 server 返回之后才能发起下一个调用
|
|
2396
2450
|
// 违反世界观可能造成:
|
|
2397
2451
|
// 1. 并发多个请求只返回第一个结果(阻塞,需后续请求疏通)
|
|
2398
2452
|
// 2. windows 下 ddb server 返回多个相同的结果
|
|
2399
|
-
const ptail = this.
|
|
2400
|
-
|
|
2453
|
+
const ptail = this.pwebsocket;
|
|
2454
|
+
let pwebsocket = this.pwebsocket = defer();
|
|
2455
|
+
// 避免 pwebsocket.reject 后出现 triggerUncaughtException
|
|
2456
|
+
pwebsocket.catch(() => { });
|
|
2457
|
+
// 上一个请求出现了 websocket 错误,那么直接抛出异常,且通过 reject 当前锁 (pwebsocket),安全退出临界区
|
|
2401
2458
|
try {
|
|
2402
2459
|
await ptail;
|
|
2460
|
+
// 已进入临界区,只有一个 rpc 函数调用运行到这里,可以独占 this.on_message 然后写 WebSocket
|
|
2403
2461
|
}
|
|
2404
|
-
catch {
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
presult.reject(new DdbConnectionError(this));
|
|
2408
|
-
return presult;
|
|
2462
|
+
catch (error) {
|
|
2463
|
+
pwebsocket.reject(error);
|
|
2464
|
+
throw error;
|
|
2409
2465
|
}
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2466
|
+
// 既然上一个请求没有出现 websocket error,且函数开头已经调用了 await this.connect() 检查过,
|
|
2467
|
+
// 这里也乐观的认为 this.connected && !this.errored 为 true
|
|
2468
|
+
return new Promise((resolve, reject) => {
|
|
2469
|
+
this.on_error = () => {
|
|
2470
|
+
pwebsocket.reject(this.error /* 这里一定有了 this.error, 不需要再 || new DdbConnectionError(this) */);
|
|
2471
|
+
reject(this.error);
|
|
2472
|
+
};
|
|
2473
|
+
this.on_message = buffer => {
|
|
2474
|
+
try {
|
|
2475
|
+
const buf = new Uint8Array(buffer);
|
|
2476
|
+
if (this.print_message_buffer)
|
|
2477
|
+
console.log(typed_array_to_buffer(buf));
|
|
2478
|
+
const message = this.parse_message(buf, error);
|
|
2479
|
+
listener?.(message, this);
|
|
2480
|
+
for (const listener of _listeners)
|
|
2481
|
+
listener(message, this);
|
|
2482
|
+
const { type, data } = message;
|
|
2483
|
+
switch (type) {
|
|
2484
|
+
case 'print':
|
|
2485
|
+
if (this.print_message)
|
|
2486
|
+
console.log(data);
|
|
2487
|
+
break;
|
|
2488
|
+
case 'object':
|
|
2489
|
+
pwebsocket.resolve();
|
|
2490
|
+
resolve(data);
|
|
2491
|
+
break;
|
|
2492
|
+
case 'error':
|
|
2493
|
+
pwebsocket.resolve();
|
|
2494
|
+
reject(data);
|
|
2495
|
+
break;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
catch (error) {
|
|
2499
|
+
pwebsocket.resolve();
|
|
2500
|
+
// 这里的错误并非 websocket 错误,而是 rpc 错误
|
|
2501
|
+
reject(error);
|
|
2502
|
+
}
|
|
2503
|
+
};
|
|
2504
|
+
const args = DdbObj.to_ddbobjs(_args);
|
|
2505
|
+
const command = this.enc.encode((() => {
|
|
2423
2506
|
switch (type) {
|
|
2424
|
-
case '
|
|
2425
|
-
if (this.
|
|
2426
|
-
console.log(
|
|
2427
|
-
return
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
case '
|
|
2432
|
-
|
|
2433
|
-
|
|
2507
|
+
case 'function':
|
|
2508
|
+
if (this.verbose)
|
|
2509
|
+
console.log(func + inspect(args, { colors: false }));
|
|
2510
|
+
return 'function\n' +
|
|
2511
|
+
`${func}\n` +
|
|
2512
|
+
`${args.length}\n` +
|
|
2513
|
+
`${Number(DDB.le_client)}\n`;
|
|
2514
|
+
case 'script':
|
|
2515
|
+
if (this.verbose)
|
|
2516
|
+
console.log(script);
|
|
2517
|
+
return 'script\n' +
|
|
2518
|
+
script;
|
|
2519
|
+
case 'variable':
|
|
2520
|
+
if (this.verbose)
|
|
2521
|
+
for (let i = 0; i < vars.length; i++)
|
|
2522
|
+
console.log(`${vars[i]} = ${inspect(args[i], { colors: false })}`);
|
|
2523
|
+
return 'variable\n' +
|
|
2524
|
+
`${vars.join(',')}\n` +
|
|
2525
|
+
`${vars.length}\n` +
|
|
2526
|
+
`${Number(DDB.le_client)}\n`;
|
|
2527
|
+
case 'connect':
|
|
2528
|
+
if (this.verbose)
|
|
2529
|
+
console.log('connect()');
|
|
2530
|
+
return 'connect\n';
|
|
2434
2531
|
}
|
|
2435
|
-
}
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
switch (type) {
|
|
2443
|
-
case 'function':
|
|
2444
|
-
return 'function\n' +
|
|
2445
|
-
`${func}\n` +
|
|
2446
|
-
`${args.length}\n` +
|
|
2447
|
-
`${Number(DDB.le_client)}\n`;
|
|
2448
|
-
case 'script':
|
|
2449
|
-
return 'script\n' +
|
|
2450
|
-
script;
|
|
2451
|
-
case 'variable':
|
|
2452
|
-
return 'variable\n' +
|
|
2453
|
-
`${vars.join(',')}\n` +
|
|
2454
|
-
`${vars.length}\n` +
|
|
2455
|
-
`${Number(DDB.le_client)}\n`;
|
|
2456
|
-
case 'connect':
|
|
2457
|
-
return 'connect\n';
|
|
2458
|
-
}
|
|
2459
|
-
})());
|
|
2460
|
-
this.websocket.send(concat([
|
|
2461
|
-
this.enc.encode(`API2 ${this.sid} ${command.length} / ${this.get_rpc_options({ urgent })}\n`),
|
|
2462
|
-
command,
|
|
2463
|
-
...args.map((arg) => arg.pack())
|
|
2464
|
-
]));
|
|
2465
|
-
return presult;
|
|
2532
|
+
})());
|
|
2533
|
+
this.websocket.send(concat([
|
|
2534
|
+
this.enc.encode(`API2 ${this.sid} ${command.length} / ${this.get_rpc_options({ urgent })}\n`),
|
|
2535
|
+
command,
|
|
2536
|
+
...args.map(arg => arg.pack())
|
|
2537
|
+
]));
|
|
2538
|
+
});
|
|
2466
2539
|
}
|
|
2467
2540
|
/** eval script through websocket (script command)
|
|
2468
2541
|
- script?: 执行的脚本 Script to execute
|
|
@@ -2614,9 +2687,6 @@ export class DDB {
|
|
|
2614
2687
|
}
|
|
2615
2688
|
/** 内部的流订阅方法 Internal stream subscription method */
|
|
2616
2689
|
async subscribe() {
|
|
2617
|
-
await this.rpc('connect', {});
|
|
2618
|
-
if (this.autologin)
|
|
2619
|
-
await this.call('login', [this.username, this.password]);
|
|
2620
2690
|
console.log(t('订阅流表成功') + ', colnames:',
|
|
2621
2691
|
// string[3](['time', 'stock', 'price'])
|
|
2622
2692
|
this.streaming.colnames = (await this.call('publishTable', [
|