dolphindb 0.0.5 → 0.0.8
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 +66 -15
- package/browser.js +143 -68
- package/browser.js.map +1 -1
- package/index.d.ts +61 -15
- package/index.js +139 -65
- package/index.js.map +1 -1
- package/package.json +6 -6
- package/test.js +6 -5
- package/test.js.map +1 -1
package/README.md
CHANGED
|
@@ -28,16 +28,24 @@ https://www.npmjs.com/package/dolphindb
|
|
|
28
28
|
|
|
29
29
|
## Installation
|
|
30
30
|
```bash
|
|
31
|
-
# Install the latest version of Node.js and browser on the machine
|
|
31
|
+
# 1. Install the latest version of Node.js and browser on the machine
|
|
32
32
|
|
|
33
|
-
#
|
|
33
|
+
# 2. Create a new project (skip this step if you already have a project)
|
|
34
|
+
mkdir dolphindb-example
|
|
35
|
+
cd dolphindb-example
|
|
36
|
+
npm init --yes
|
|
37
|
+
# Open the package.json file with an editor, add the line "type": "module", below "main": "./index.js",
|
|
38
|
+
# This enables the use of ECMAScript modules, and in the code behind you can use import { DDB } from 'dolphindb' to import npm packages
|
|
39
|
+
|
|
40
|
+
# 3. Install npm packages in your project
|
|
34
41
|
npm install dolphindb
|
|
35
42
|
```
|
|
36
43
|
|
|
37
44
|
## Usage
|
|
38
45
|
### 0. Initialize and connect to DolphinDB
|
|
39
46
|
```ts
|
|
40
|
-
import DDB from 'dolphindb'
|
|
47
|
+
import { DDB } from 'dolphindb'
|
|
48
|
+
// The import method for existing projects using CommonJS modules is const { DDB } = require('dolphindb')
|
|
41
49
|
|
|
42
50
|
// Create a database object and initialize the WebSocket URL
|
|
43
51
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -71,7 +79,8 @@ async connect (
|
|
|
71
79
|
```ts
|
|
72
80
|
import { DdbInt } from 'dolphindb'
|
|
73
81
|
|
|
74
|
-
const result = await ddb.call
|
|
82
|
+
const result = await ddb.call('add', [new DdbInt(1), new DdbInt(1)])
|
|
83
|
+
// TypeScript: const result = await ddb.call<DdbInt>('add', [new DdbInt(1), new DdbInt(1)])
|
|
75
84
|
|
|
76
85
|
console.log(result.value === 2) // true
|
|
77
86
|
```
|
|
@@ -178,6 +187,40 @@ enum DdbType {
|
|
|
178
187
|
}
|
|
179
188
|
```
|
|
180
189
|
|
|
190
|
+
##### For types that are not in the utility classes, such as the datetime type in DolphinDB, we can specify form and type to create a DdbObj object
|
|
191
|
+
```ts
|
|
192
|
+
const obj = new DdbObj({
|
|
193
|
+
form: DdbForm.scalar,
|
|
194
|
+
type: DdbType.datetime,
|
|
195
|
+
value: 1644573600,
|
|
196
|
+
length: 0
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The corresponding type and value of value property in js can refer to the result returned by `ddb.eval` (see below: `eval` Method Declaration)
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
const obj = await ddb.eval('2022.02.11 10:00:00')
|
|
204
|
+
console.log(obj.form === DdbForm.scalar)
|
|
205
|
+
console.log(obj.type === DdbType.datetime)
|
|
206
|
+
console.log(obj.value)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
##### Another example is to create a set
|
|
210
|
+
```ts
|
|
211
|
+
// refer to ddb.eval
|
|
212
|
+
// const obj = await ddb.eval('set([1, 2, 3])')
|
|
213
|
+
// console.log(obj.value)
|
|
214
|
+
|
|
215
|
+
const obj = new DdbObj({
|
|
216
|
+
form: DdbForm.set,
|
|
217
|
+
type: DdbType.int,
|
|
218
|
+
value: Int32Array.of(1, 2, 3),
|
|
219
|
+
length: 0
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
|
|
181
224
|
#### `call` Method Declaration
|
|
182
225
|
```ts
|
|
183
226
|
async call <T extends DdbObj> (
|
|
@@ -211,15 +254,17 @@ async call <T extends DdbObj> (
|
|
|
211
254
|
### 2. Execute Script
|
|
212
255
|
#### Example
|
|
213
256
|
```ts
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const result = await ddb.eval<DdbLong>(
|
|
257
|
+
const result = await ddb.eval(
|
|
217
258
|
'def foo (a, b) {\n' +
|
|
218
259
|
' return a + b\n' +
|
|
219
260
|
'}\n' +
|
|
220
261
|
'foo(1l, 1l)\n'
|
|
221
262
|
)
|
|
222
263
|
|
|
264
|
+
// TypeScript:
|
|
265
|
+
// import type { DdbLong } from 'dolphindb'
|
|
266
|
+
// const result = await ddb.eval<DdbLong>(...)
|
|
267
|
+
|
|
223
268
|
console.log(result.value === 2n) // true
|
|
224
269
|
```
|
|
225
270
|
|
|
@@ -275,3 +320,19 @@ async upload (
|
|
|
275
320
|
): Promise<void>
|
|
276
321
|
```
|
|
277
322
|
|
|
323
|
+
|
|
324
|
+
### Notes
|
|
325
|
+
```ts
|
|
326
|
+
import { nulls, DdbInt, timestamp2str } from 'dolphindb'
|
|
327
|
+
|
|
328
|
+
// create DolphinDB null integer value (the smallest 32-bit integer)
|
|
329
|
+
new DdbInt(nulls.int32)
|
|
330
|
+
|
|
331
|
+
// convert DolphinDB timestamp value to string
|
|
332
|
+
timestamp2str(
|
|
333
|
+
(
|
|
334
|
+
await ddb.call<DdbObj<bigint>>('now', [false])
|
|
335
|
+
).value
|
|
336
|
+
) === '2022.02.23 17:23:13.494'
|
|
337
|
+
```
|
|
338
|
+
|
package/README.zh.md
CHANGED
|
@@ -28,16 +28,24 @@ https://www.npmjs.com/package/dolphindb
|
|
|
28
28
|
|
|
29
29
|
## 安装
|
|
30
30
|
```bash
|
|
31
|
-
# 在机器上安装最新版的 Node.js 及浏览器
|
|
31
|
+
# 1. 在机器上安装最新版的 Node.js 及浏览器
|
|
32
32
|
|
|
33
|
-
#
|
|
33
|
+
# 2. 创建新项目
|
|
34
|
+
mkdir dolphindb-example
|
|
35
|
+
cd dolphindb-example
|
|
36
|
+
npm init --yes
|
|
37
|
+
# 用编辑器打开 package.json 文件,在 "main": "./index.js", 下面加入一行 "type": "module",
|
|
38
|
+
# 这样能够启用 ECMAScript modules,在后面代码中可以使用 import { DDB } from 'dolphindb' 导入 npm 包
|
|
39
|
+
|
|
40
|
+
# 3. 在项目中安装 npm 包
|
|
34
41
|
npm install dolphindb
|
|
35
42
|
```
|
|
36
43
|
|
|
37
44
|
## 用法
|
|
38
45
|
### 0. 初始化并连接到 DolphinDB
|
|
39
46
|
```ts
|
|
40
|
-
import DDB from 'dolphindb'
|
|
47
|
+
import { DDB } from 'dolphindb'
|
|
48
|
+
// 已有的使用 CommonJS 模块的项目的导入方法为 const { DDB } = require('dolphindb')
|
|
41
49
|
|
|
42
50
|
// 创建数据库对象,初始化 WebSocket 连接地址
|
|
43
51
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
@@ -71,7 +79,8 @@ async connect (
|
|
|
71
79
|
```ts
|
|
72
80
|
import { DdbInt } from 'dolphindb'
|
|
73
81
|
|
|
74
|
-
const result = await ddb.call
|
|
82
|
+
const result = await ddb.call('add', [new DdbInt(1), new DdbInt(1)])
|
|
83
|
+
// TypeScript: const result = await ddb.call<DdbInt>('add', [new DdbInt(1), new DdbInt(1)])
|
|
75
84
|
|
|
76
85
|
console.log(result.value === 2) // true
|
|
77
86
|
```
|
|
@@ -178,6 +187,40 @@ enum DdbType {
|
|
|
178
187
|
}
|
|
179
188
|
```
|
|
180
189
|
|
|
190
|
+
##### 不在快捷类中的类型,比如 DolphinDB 中的 datetime 类型, 可以指定 form 和 type 创建 DdbObj 对象
|
|
191
|
+
```ts
|
|
192
|
+
const obj = new DdbObj({
|
|
193
|
+
form: DdbForm.scalar,
|
|
194
|
+
type: DdbType.datetime,
|
|
195
|
+
value: 1644573600,
|
|
196
|
+
length: 0
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
value 的在 js 中对应类型及取值可以参考 ddb.eval 返回的结果 (见后文 `eval` 方法声明)
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
const obj = await ddb.eval('2022.02.11 10:00:00')
|
|
204
|
+
console.log(obj.form === DdbForm.scalar)
|
|
205
|
+
console.log(obj.type === DdbType.datetime)
|
|
206
|
+
console.log(obj.value)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
##### 再比如创建一个 set
|
|
210
|
+
```ts
|
|
211
|
+
// 参考 ddb.eval
|
|
212
|
+
// const obj = await ddb.eval('set([1, 2, 3])')
|
|
213
|
+
// console.log(obj.value)
|
|
214
|
+
|
|
215
|
+
const obj = new DdbObj({
|
|
216
|
+
form: DdbForm.set,
|
|
217
|
+
type: DdbType.int,
|
|
218
|
+
value: Int32Array.of(1, 2, 3),
|
|
219
|
+
length: 0
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
|
|
181
224
|
#### `call` 方法声明
|
|
182
225
|
```ts
|
|
183
226
|
async call <T extends DdbObj> (
|
|
@@ -211,15 +254,17 @@ async call <T extends DdbObj> (
|
|
|
211
254
|
### 2. 执行脚本
|
|
212
255
|
#### 例子
|
|
213
256
|
```ts
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const result = await ddb.eval<DdbLong>(
|
|
257
|
+
const result = await ddb.eval(
|
|
217
258
|
'def foo (a, b) {\n' +
|
|
218
259
|
' return a + b\n' +
|
|
219
260
|
'}\n' +
|
|
220
261
|
'foo(1l, 1l)\n'
|
|
221
262
|
)
|
|
222
263
|
|
|
264
|
+
// TypeScript:
|
|
265
|
+
// import type { DdbLong } from 'dolphindb'
|
|
266
|
+
// const result = await ddb.eval<DdbLong>(...)
|
|
267
|
+
|
|
223
268
|
console.log(result.value === 2n) // true
|
|
224
269
|
```
|
|
225
270
|
|
|
@@ -275,3 +320,18 @@ async upload (
|
|
|
275
320
|
): Promise<void>
|
|
276
321
|
```
|
|
277
322
|
|
|
323
|
+
|
|
324
|
+
### Notes
|
|
325
|
+
```ts
|
|
326
|
+
import { nulls, DdbInt, timestamp2str } from 'dolphindb'
|
|
327
|
+
|
|
328
|
+
// 创建 DolphinDB 空值 (最小的 32-bit 整数)
|
|
329
|
+
new DdbInt(nulls.int32)
|
|
330
|
+
|
|
331
|
+
// 将 DolphinDB 中的 timestamp 格式化为 string
|
|
332
|
+
timestamp2str(
|
|
333
|
+
(
|
|
334
|
+
await ddb.call<DdbObj<bigint>>('now', [false])
|
|
335
|
+
).value
|
|
336
|
+
) === '2022.02.23 17:23:13.494'
|
|
337
|
+
```
|
package/browser.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export declare enum DdbType {
|
|
|
46
46
|
complex = 34,
|
|
47
47
|
point = 35,
|
|
48
48
|
duration = 36,
|
|
49
|
+
object = 37,
|
|
49
50
|
symbol_extended = 145
|
|
50
51
|
}
|
|
51
52
|
export declare enum DdbFunctionType {
|
|
@@ -136,7 +137,11 @@ export declare class DdbObj<T extends DdbValue = DdbValue> {
|
|
|
136
137
|
pack(): Uint8Array;
|
|
137
138
|
static pack_vector_body(value: DdbVectorValue, type: DdbType, length: number): ArrayBufferView[];
|
|
138
139
|
toString(): string;
|
|
139
|
-
to_cols():
|
|
140
|
+
to_cols(): {
|
|
141
|
+
title: string;
|
|
142
|
+
dataIndex: string;
|
|
143
|
+
render?: any;
|
|
144
|
+
}[];
|
|
140
145
|
to_rows<T extends Record<string, any> = Record<string, any>>(): T[];
|
|
141
146
|
to_dict<T = Record<string, any>>(): T;
|
|
142
147
|
}
|
|
@@ -184,28 +189,68 @@ export declare class DDB {
|
|
|
184
189
|
/** utf-8 text decoder */
|
|
185
190
|
dec: TextDecoder;
|
|
186
191
|
enc: TextEncoder;
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
/** DolphinDB WebSocket URL
|
|
193
|
+
e.g. `ws://127.0.0.1:8848/`, `wss://dolphindb.com`
|
|
194
|
+
*/
|
|
195
|
+
url: string;
|
|
196
|
+
websocket: WebSocket;
|
|
189
197
|
/** little endian (server) */
|
|
190
198
|
le: boolean;
|
|
191
199
|
/** little endian (client) */
|
|
192
200
|
static le_client: boolean;
|
|
201
|
+
/** python session flag (2048) */
|
|
202
|
+
python: boolean;
|
|
203
|
+
message_hook: (message: Uint8Array) => any;
|
|
204
|
+
/** print message handler */
|
|
205
|
+
printer(message: string): void;
|
|
193
206
|
/** resolver, rejector, promise of last rpc */
|
|
194
207
|
presolver(buf: Uint8Array): void;
|
|
195
208
|
prejector(error: Error): void;
|
|
196
209
|
presult: Promise<Uint8Array>;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
210
|
+
/**
|
|
211
|
+
Initialize an instance of DolphinDB Client using the WebSocket URL
|
|
212
|
+
(without establishing an actual network connection)
|
|
213
|
+
@example
|
|
214
|
+
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
215
|
+
let ddb_secure = new DDB('wss://dolphindb.com')
|
|
216
|
+
*/
|
|
217
|
+
constructor(url?: string);
|
|
218
|
+
/** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL */
|
|
219
|
+
connect({ url, login, username, password, python, }?: {
|
|
220
|
+
/** By default, the WebSocket URL passed in when the instance is initialized is used */
|
|
221
|
+
url?: string;
|
|
222
|
+
/** Whether to automatically log in after the connection is established, the default is true */
|
|
203
223
|
login?: boolean;
|
|
204
|
-
/** DolphinDB
|
|
224
|
+
/** DolphinDB username */
|
|
205
225
|
username?: string;
|
|
206
|
-
/** DolphinDB
|
|
226
|
+
/** DolphinDB password */
|
|
207
227
|
password?: string;
|
|
228
|
+
/** set python session flag */
|
|
229
|
+
python?: boolean;
|
|
208
230
|
}): Promise<void>;
|
|
231
|
+
get_rpc_options({ urgent, secondary, async: _async, pickle, clear, api, compress, cancellable, priority, parallelism, root_id, limit, }?: {
|
|
232
|
+
urgent?: boolean;
|
|
233
|
+
/** API 提交的任务, secondary 必须为 false */
|
|
234
|
+
secondary?: boolean;
|
|
235
|
+
/** 是否异步任务(不返回结果) */
|
|
236
|
+
async?: boolean;
|
|
237
|
+
/** 让服务端以 pickle 协议返回数据 */
|
|
238
|
+
pickle?: boolean;
|
|
239
|
+
/** 本次任务完成后 clear session memory */
|
|
240
|
+
clear?: boolean;
|
|
241
|
+
/** 是否为 api client */
|
|
242
|
+
api?: boolean;
|
|
243
|
+
compress?: boolean;
|
|
244
|
+
/** 任务是否可以取消 */
|
|
245
|
+
cancellable?: boolean;
|
|
246
|
+
priority?: number;
|
|
247
|
+
/** `8` 0 ~ 64, 指定本任务并行度 */
|
|
248
|
+
parallelism?: number;
|
|
249
|
+
/** 根任务编号,内部使用,API中固定为空 */
|
|
250
|
+
root_id?: string;
|
|
251
|
+
/** 指定分块返回的块大小 */
|
|
252
|
+
limit?: boolean;
|
|
253
|
+
}): string;
|
|
209
254
|
disconnect(): void;
|
|
210
255
|
/** rpc through websocket (function/script/variable command)
|
|
211
256
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
@@ -213,7 +258,7 @@ export declare class DDB {
|
|
|
213
258
|
- urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
|
|
214
259
|
- vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
|
|
215
260
|
*/
|
|
216
|
-
rpc<T extends DdbObj = DdbObj>(type: 'script' | 'function' | 'variable', { script, func, args, vars, urgent, }: {
|
|
261
|
+
rpc<T extends DdbObj = DdbObj>(type: 'script' | 'function' | 'variable' | 'connect', { script, func, args, vars, urgent, }: {
|
|
217
262
|
script?: string;
|
|
218
263
|
func?: string;
|
|
219
264
|
args?: (DdbObj | string | boolean)[];
|
|
@@ -248,12 +293,18 @@ export declare class DDB {
|
|
|
248
293
|
}): Promise<T>;
|
|
249
294
|
/** upload variable through websocket (variable command) */
|
|
250
295
|
upload(
|
|
251
|
-
/**
|
|
296
|
+
/** Uploaded variables' name */
|
|
252
297
|
vars: string[],
|
|
253
|
-
/**
|
|
298
|
+
/** Uploaded variables' value */
|
|
254
299
|
args: (DdbObj | string | boolean)[]): Promise<DdbObj<DdbValue>>;
|
|
255
300
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
256
|
-
parse_message(buf: Uint8Array):
|
|
301
|
+
parse_message(buf: Uint8Array): {
|
|
302
|
+
type: "message";
|
|
303
|
+
data: string;
|
|
304
|
+
} | {
|
|
305
|
+
type: "object";
|
|
306
|
+
data: Uint8Array;
|
|
307
|
+
};
|
|
257
308
|
/** 自动转换 js string, boolean 为 DdbObj */
|
|
258
309
|
to_ddbobj(value: DdbObj | string | boolean): DdbObj;
|
|
259
310
|
/** 转换 js 数组为 DdbObj[] (in place, 会修改原数组) */
|
package/browser.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ddb = 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.DdbType = exports.DdbForm = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const dayjs_1 =
|
|
5
|
+
const dayjs_1 = tslib_1.__importDefault(require("dayjs"));
|
|
6
6
|
require("xshell/prototype.browser");
|
|
7
7
|
const utils_browser_1 = require("xshell/utils.browser");
|
|
8
8
|
var DdbForm;
|
|
@@ -54,6 +54,7 @@ var DdbType;
|
|
|
54
54
|
DdbType[DdbType["complex"] = 34] = "complex";
|
|
55
55
|
DdbType[DdbType["point"] = 35] = "point";
|
|
56
56
|
DdbType[DdbType["duration"] = 36] = "duration";
|
|
57
|
+
DdbType[DdbType["object"] = 37] = "object";
|
|
57
58
|
DdbType[DdbType["symbol_extended"] = 145] = "symbol_extended";
|
|
58
59
|
})(DdbType = exports.DdbType || (exports.DdbType = {}));
|
|
59
60
|
var DdbFunctionType;
|
|
@@ -394,8 +395,8 @@ class DdbObj {
|
|
|
394
395
|
throw new Error(`array vector 存在非法 unit = ${unit}`);
|
|
395
396
|
}
|
|
396
397
|
let total_length = 0;
|
|
397
|
-
for (
|
|
398
|
-
total_length +=
|
|
398
|
+
for (const x of lengths)
|
|
399
|
+
total_length += x;
|
|
399
400
|
const [len_items, data] = this.parse_vector_items(buf.subarray(i_data_start), le, type - 64, total_length);
|
|
400
401
|
blocks.push({
|
|
401
402
|
unit,
|
|
@@ -1014,82 +1015,135 @@ function timestamp2str(timestamp) {
|
|
|
1014
1015
|
}
|
|
1015
1016
|
exports.timestamp2str = timestamp2str;
|
|
1016
1017
|
class DDB {
|
|
1017
|
-
|
|
1018
|
+
/**
|
|
1019
|
+
Initialize an instance of DolphinDB Client using the WebSocket URL
|
|
1020
|
+
(without establishing an actual network connection)
|
|
1021
|
+
@example
|
|
1022
|
+
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
1023
|
+
let ddb_secure = new DDB('wss://dolphindb.com')
|
|
1024
|
+
*/
|
|
1025
|
+
constructor(url) {
|
|
1018
1026
|
/** 当前的 session id (http 或 tcp) */
|
|
1019
1027
|
this.sid = '0';
|
|
1020
1028
|
/** utf-8 text decoder */
|
|
1021
1029
|
this.dec = new TextDecoder('utf-8');
|
|
1022
1030
|
this.enc = new TextEncoder();
|
|
1023
|
-
this.
|
|
1031
|
+
this.websocket = null;
|
|
1024
1032
|
/** little endian (server) */
|
|
1025
1033
|
this.le = true;
|
|
1034
|
+
/** python session flag (2048) */
|
|
1035
|
+
this.python = false;
|
|
1036
|
+
this.message_hook = null;
|
|
1026
1037
|
this.presult = Promise.resolve(null);
|
|
1027
|
-
if (!
|
|
1028
|
-
const
|
|
1029
|
-
const hostname =
|
|
1030
|
-
const port =
|
|
1031
|
-
|
|
1038
|
+
if (!url) {
|
|
1039
|
+
const _url = new URL(location.href);
|
|
1040
|
+
const hostname = _url.searchParams.get('hostname') || location.hostname;
|
|
1041
|
+
const port = _url.searchParams.get('port') || location.port;
|
|
1042
|
+
url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}${port ? `:${port}` : ''}/`;
|
|
1032
1043
|
}
|
|
1033
|
-
this.
|
|
1044
|
+
this.url = url;
|
|
1045
|
+
}
|
|
1046
|
+
/** print message handler */
|
|
1047
|
+
printer(message) {
|
|
1048
|
+
console.log(message);
|
|
1034
1049
|
}
|
|
1035
1050
|
/** resolver, rejector, promise of last rpc */
|
|
1036
1051
|
presolver(buf) { }
|
|
1037
1052
|
prejector(error) { }
|
|
1038
|
-
/**
|
|
1039
|
-
async connect({
|
|
1040
|
-
this.
|
|
1041
|
-
|
|
1053
|
+
/** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL */
|
|
1054
|
+
async connect({ url = this.url, login = true, username = 'admin', password = '123456', python = false, } = {}) {
|
|
1055
|
+
this.url = url;
|
|
1056
|
+
this.python = python;
|
|
1057
|
+
if (this.websocket?.readyState === WebSocket.OPEN)
|
|
1042
1058
|
this.disconnect();
|
|
1043
|
-
let
|
|
1059
|
+
let websocket = new WebSocket(url, python ? ['python'] : []);
|
|
1044
1060
|
// https://stackoverflow.com/questions/11821096/what-is-the-difference-between-an-arraybuffer-and-a-blob/39951543
|
|
1045
|
-
|
|
1046
|
-
this.
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
'connect
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1061
|
+
websocket.binaryType = 'arraybuffer';
|
|
1062
|
+
this.websocket = websocket;
|
|
1063
|
+
await new Promise((resolve, reject) => {
|
|
1064
|
+
websocket.addEventListener('open', async (event) => {
|
|
1065
|
+
console.log(`${websocket.url} opened`);
|
|
1066
|
+
await this.rpc('connect', {});
|
|
1067
|
+
resolve();
|
|
1068
|
+
});
|
|
1069
|
+
websocket.addEventListener('close', event => {
|
|
1070
|
+
console.log(`${websocket.url} closed with code = ${event.code}, reason = '${event.reason}'`);
|
|
1071
|
+
});
|
|
1072
|
+
websocket.addEventListener('error', event => {
|
|
1073
|
+
const message = `${websocket.url} errored`;
|
|
1074
|
+
console.error(message, event);
|
|
1075
|
+
reject(Object.assign(new Error(message), { event }));
|
|
1076
|
+
});
|
|
1077
|
+
websocket.addEventListener('message', event => {
|
|
1078
|
+
const buf = new Uint8Array(event.data);
|
|
1079
|
+
if (this.message_hook)
|
|
1080
|
+
this.message_hook(buf);
|
|
1081
|
+
try {
|
|
1082
|
+
const { type, data } = this.parse_message(buf);
|
|
1083
|
+
if (type === 'message') {
|
|
1084
|
+
this.printer(data);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
this.presolver(data);
|
|
1088
|
+
}
|
|
1089
|
+
catch (error) {
|
|
1090
|
+
this.prejector(error);
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1070
1093
|
});
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1094
|
+
if (!this.python)
|
|
1095
|
+
await this.eval('def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
|
|
1096
|
+
' nargs = size(args)\n' +
|
|
1097
|
+
' func = funcByName(func_name)\n' +
|
|
1098
|
+
' \n' +
|
|
1099
|
+
' if (!nargs)\n' +
|
|
1100
|
+
' return pnodeRun(func, nodes, add_node_alias)\n' +
|
|
1101
|
+
' \n' +
|
|
1102
|
+
' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
|
|
1103
|
+
' args_partial[0] = func\n' +
|
|
1104
|
+
' args_partial[1:] = args\n' +
|
|
1105
|
+
' return pnodeRun(\n' +
|
|
1106
|
+
' unifiedCall(partial, args_partial),\n' +
|
|
1107
|
+
' nodes,\n' +
|
|
1108
|
+
' add_node_alias\n' +
|
|
1109
|
+
' )\n' +
|
|
1110
|
+
'}\n', { urgent: true });
|
|
1088
1111
|
if (login)
|
|
1089
1112
|
await this.call('login', [username, password], { urgent: true });
|
|
1090
1113
|
}
|
|
1114
|
+
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, } = {}) {
|
|
1115
|
+
let flag = 0;
|
|
1116
|
+
if (urgent)
|
|
1117
|
+
flag += 1;
|
|
1118
|
+
if (secondary)
|
|
1119
|
+
flag += 2;
|
|
1120
|
+
if (_async)
|
|
1121
|
+
flag += 4;
|
|
1122
|
+
if (pickle)
|
|
1123
|
+
flag += 8;
|
|
1124
|
+
if (clear)
|
|
1125
|
+
flag += 16;
|
|
1126
|
+
if (api)
|
|
1127
|
+
flag += 32;
|
|
1128
|
+
if (compress)
|
|
1129
|
+
flag += 64;
|
|
1130
|
+
// python session
|
|
1131
|
+
if (this.python)
|
|
1132
|
+
flag += 2048;
|
|
1133
|
+
const options = [
|
|
1134
|
+
flag,
|
|
1135
|
+
cancellable ? 1 : 0,
|
|
1136
|
+
priority,
|
|
1137
|
+
parallelism,
|
|
1138
|
+
...limit ? [
|
|
1139
|
+
root_id,
|
|
1140
|
+
limit,
|
|
1141
|
+
] : [],
|
|
1142
|
+
];
|
|
1143
|
+
return `/ ${options.join('_')}`;
|
|
1144
|
+
}
|
|
1091
1145
|
disconnect() {
|
|
1092
|
-
this.
|
|
1146
|
+
this.websocket?.close(1000);
|
|
1093
1147
|
}
|
|
1094
1148
|
/** rpc through websocket (function/script/variable command)
|
|
1095
1149
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
@@ -1098,10 +1152,10 @@ class DDB {
|
|
|
1098
1152
|
- vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
|
|
1099
1153
|
*/
|
|
1100
1154
|
async rpc(type, { script, func, args = [], vars = [], urgent, }) {
|
|
1101
|
-
if (!this.
|
|
1155
|
+
if (!this.websocket)
|
|
1102
1156
|
await this.connect();
|
|
1103
|
-
if (this.
|
|
1104
|
-
throw new Error(
|
|
1157
|
+
if (this.websocket.readyState !== WebSocket.OPEN)
|
|
1158
|
+
throw new Error(`${this.url} is already disconnected`);
|
|
1105
1159
|
// 临界区:保证多个 rpc 并发时形成 promise 链
|
|
1106
1160
|
// ddb 世界观:需要等待上一个 rpc 结果从 server 返回之后才能发起下一个调用
|
|
1107
1161
|
// 违反世界观可能造成:
|
|
@@ -1139,14 +1193,16 @@ class DDB {
|
|
|
1139
1193
|
`${vars.join(',')}\n` +
|
|
1140
1194
|
`${vars.length}\n` +
|
|
1141
1195
|
`${Number(DDB.le_client)}\n`;
|
|
1196
|
+
case 'connect':
|
|
1197
|
+
return 'connect\n';
|
|
1142
1198
|
}
|
|
1143
1199
|
})());
|
|
1144
1200
|
const message = (0, utils_browser_1.concat)([
|
|
1145
|
-
this.enc.encode(`
|
|
1201
|
+
this.enc.encode(`API2 ${this.sid} ${command.length}${this.get_rpc_options({ urgent })}\n`),
|
|
1146
1202
|
command,
|
|
1147
1203
|
...args.map((arg) => arg.pack())
|
|
1148
1204
|
]);
|
|
1149
|
-
this.
|
|
1205
|
+
this.websocket.send(message);
|
|
1150
1206
|
return DdbObj.parse(await presult, // data_buf
|
|
1151
1207
|
this.le);
|
|
1152
1208
|
}
|
|
@@ -1169,6 +1225,8 @@ class DDB {
|
|
|
1169
1225
|
- add_node_alias?: 设置 nodes 参数时选传,其它情况不传
|
|
1170
1226
|
*/
|
|
1171
1227
|
async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias } = {}) {
|
|
1228
|
+
if (this.python)
|
|
1229
|
+
throw new Error('call doesn\'t support python session yet');
|
|
1172
1230
|
if (node) {
|
|
1173
1231
|
if (typeof func_type === 'undefined')
|
|
1174
1232
|
throw new Error('指定 node 时必须设置 func_type');
|
|
@@ -1184,7 +1242,13 @@ class DDB {
|
|
|
1184
1242
|
new DdbVectorString(nodes),
|
|
1185
1243
|
func,
|
|
1186
1244
|
new DdbVectorAny(this.to_ddbobjs(args)),
|
|
1187
|
-
...
|
|
1245
|
+
...(() => {
|
|
1246
|
+
if (typeof add_node_alias !== 'undefined')
|
|
1247
|
+
return [add_node_alias];
|
|
1248
|
+
if (this.python)
|
|
1249
|
+
return [true];
|
|
1250
|
+
return [];
|
|
1251
|
+
})()
|
|
1188
1252
|
];
|
|
1189
1253
|
func = 'pnode_run';
|
|
1190
1254
|
}
|
|
@@ -1196,9 +1260,9 @@ class DDB {
|
|
|
1196
1260
|
}
|
|
1197
1261
|
/** upload variable through websocket (variable command) */
|
|
1198
1262
|
async upload(
|
|
1199
|
-
/**
|
|
1263
|
+
/** Uploaded variables' name */
|
|
1200
1264
|
vars,
|
|
1201
|
-
/**
|
|
1265
|
+
/** Uploaded variables' value */
|
|
1202
1266
|
args) {
|
|
1203
1267
|
if (!args.length || args.length !== vars.length)
|
|
1204
1268
|
throw new Error('variable 指令参数为空或参数名为空,或数量不匹配');
|
|
@@ -1206,6 +1270,14 @@ class DDB {
|
|
|
1206
1270
|
}
|
|
1207
1271
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
1208
1272
|
parse_message(buf) {
|
|
1273
|
+
// MSG\n
|
|
1274
|
+
// <message>\0
|
|
1275
|
+
// 'M'.codePointAt(0).to_hex_str()
|
|
1276
|
+
if (buf[0] === 0x4d && buf[1] === 0x53 && buf[2] === 0x47 && buf[3] === 0x0a)
|
|
1277
|
+
return {
|
|
1278
|
+
type: 'message',
|
|
1279
|
+
data: this.dec.decode(buf.subarray(4))
|
|
1280
|
+
};
|
|
1209
1281
|
// '1166953221 1 1\n'
|
|
1210
1282
|
// 'OK\n'
|
|
1211
1283
|
// '\x04\x00\x02\x00\x00\x00'
|
|
@@ -1228,7 +1300,10 @@ class DDB {
|
|
|
1228
1300
|
const message = this.dec.decode(buf.subarray(i_ls_1, i_lf_1));
|
|
1229
1301
|
if (message !== 'OK')
|
|
1230
1302
|
throw new Error(message);
|
|
1231
|
-
return
|
|
1303
|
+
return {
|
|
1304
|
+
type: 'object',
|
|
1305
|
+
data: buf.subarray(i_lf_1 + 1)
|
|
1306
|
+
};
|
|
1232
1307
|
}
|
|
1233
1308
|
/** 自动转换 js string, boolean 为 DdbObj */
|
|
1234
1309
|
to_ddbobj(value) {
|