@blueyerobotics/blueye-ts 3.5.0 → 3.7.0
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 +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/src/binlog-parser.d.ts +38 -0
- package/dist/src/binlog-parser.js +132 -0
- package/dist/src/client.d.ts +4 -2
- package/dist/src/client.js +10 -6
- package/dist/src/schema.d.ts +1 -1
- package/dist/src/schema.js +8 -6
- package/package.json +6 -3
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -15,5 +15,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("@blueyerobotics/protocol-definitions"), exports);
|
|
18
|
+
__exportStar(require("./src/binlog-parser"), exports);
|
|
18
19
|
__exportStar(require("./src/client"), exports);
|
|
19
20
|
__exportStar(require("./src/schema"), exports);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Buffer } from "buffer";
|
|
2
|
+
import { type Protocol, type ProtocolKey, type ProtocolType } from "./client";
|
|
3
|
+
export type Message = {
|
|
4
|
+
[K in ProtocolKey]: {
|
|
5
|
+
monotonicTime: number;
|
|
6
|
+
time: number;
|
|
7
|
+
type: ProtocolType;
|
|
8
|
+
key: K;
|
|
9
|
+
data: ReturnType<Protocol[K]["decode"]>;
|
|
10
|
+
innerData?: object;
|
|
11
|
+
};
|
|
12
|
+
}[ProtocolKey];
|
|
13
|
+
/**
|
|
14
|
+
* Parse a binlog file from raw (gzipped) data into structured messages..
|
|
15
|
+
* @param rawData The raw binary data of the binlog file (gzip format).
|
|
16
|
+
* @param fixTimes Fix the message times based on the last message's monotonic and unix timestamps. Useful for ensuring the times are in sync.
|
|
17
|
+
* @returns A promise that resolves to an array of parsed messages.
|
|
18
|
+
*/
|
|
19
|
+
export declare const parse: (rawData: Blob, fixTimes?: boolean) => Promise<Message[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Decompress the gzipped binlog data (.bez).
|
|
22
|
+
* @param rawData The compressed binary data to decompress (gzip format).
|
|
23
|
+
* @returns A promise that resolves to the decompressed data as a Buffer.
|
|
24
|
+
*/
|
|
25
|
+
export declare const decompress: (rawData: Blob) => Promise<Buffer>;
|
|
26
|
+
/**
|
|
27
|
+
* Parse the decompressed binlog data into structured messages.
|
|
28
|
+
* @param decompressed The gunzipped binlog data.
|
|
29
|
+
* @param fixTimes Fix the message times based on the last message's monotonic and unix timestamps. Useful for ensuring the times are in sync.
|
|
30
|
+
* @returns An array of parsed messages with their timestamps, types, keys, and data.
|
|
31
|
+
*/
|
|
32
|
+
export declare const parseMessages: (decompressed: Uint8Array, fixTimes?: boolean) => Message[];
|
|
33
|
+
/**
|
|
34
|
+
* Fix the message times based on the last message's monotonic and unix timestamps.
|
|
35
|
+
* @param messages The messages to fix the times for.
|
|
36
|
+
* @returns The messages with corrected times.
|
|
37
|
+
*/
|
|
38
|
+
export declare const fixMessageTimes: (messages: Message[]) => Message[];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fixMessageTimes = exports.parseMessages = exports.decompress = exports.parse = void 0;
|
|
4
|
+
const protocol_definitions_1 = require("@blueyerobotics/protocol-definitions");
|
|
5
|
+
const wire_1 = require("@bufbuild/protobuf/wire");
|
|
6
|
+
const buffer_1 = require("buffer");
|
|
7
|
+
const fflate_1 = require("fflate");
|
|
8
|
+
const client_1 = require("./client");
|
|
9
|
+
/**
|
|
10
|
+
* Parse a binlog file from raw (gzipped) data into structured messages..
|
|
11
|
+
* @param rawData The raw binary data of the binlog file (gzip format).
|
|
12
|
+
* @param fixTimes Fix the message times based on the last message's monotonic and unix timestamps. Useful for ensuring the times are in sync.
|
|
13
|
+
* @returns A promise that resolves to an array of parsed messages.
|
|
14
|
+
*/
|
|
15
|
+
const parse = async (rawData, fixTimes = true) => {
|
|
16
|
+
const decompressed = await (0, exports.decompress)(rawData);
|
|
17
|
+
const messages = (0, exports.parseMessages)(decompressed, fixTimes);
|
|
18
|
+
return messages;
|
|
19
|
+
};
|
|
20
|
+
exports.parse = parse;
|
|
21
|
+
/**
|
|
22
|
+
* Decompress the gzipped binlog data (.bez).
|
|
23
|
+
* @param rawData The compressed binary data to decompress (gzip format).
|
|
24
|
+
* @returns A promise that resolves to the decompressed data as a Buffer.
|
|
25
|
+
*/
|
|
26
|
+
const decompress = async (rawData) => {
|
|
27
|
+
const gunzip = new fflate_1.Gunzip();
|
|
28
|
+
const blobReader = rawData.stream().getReader();
|
|
29
|
+
const chunks = [];
|
|
30
|
+
gunzip.ondata = (chunk) => {
|
|
31
|
+
chunks.push(chunk);
|
|
32
|
+
};
|
|
33
|
+
while (true) {
|
|
34
|
+
const { done, value } = await blobReader.read();
|
|
35
|
+
if (done) {
|
|
36
|
+
try {
|
|
37
|
+
gunzip.push(new Uint8Array(0), true);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error("Error pushing end-of-stream marker:", err);
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
gunzip.push(value);
|
|
45
|
+
}
|
|
46
|
+
return buffer_1.Buffer.concat(chunks);
|
|
47
|
+
};
|
|
48
|
+
exports.decompress = decompress;
|
|
49
|
+
/**
|
|
50
|
+
* Parse the decompressed binlog data into structured messages.
|
|
51
|
+
* @param decompressed The gunzipped binlog data.
|
|
52
|
+
* @param fixTimes Fix the message times based on the last message's monotonic and unix timestamps. Useful for ensuring the times are in sync.
|
|
53
|
+
* @returns An array of parsed messages with their timestamps, types, keys, and data.
|
|
54
|
+
*/
|
|
55
|
+
const parseMessages = (decompressed, fixTimes = true) => {
|
|
56
|
+
const reader = new wire_1.BinaryReader(decompressed);
|
|
57
|
+
let messages = [];
|
|
58
|
+
while (reader.pos < reader.len) {
|
|
59
|
+
const length = reader.uint32();
|
|
60
|
+
const start = reader.pos;
|
|
61
|
+
const end = start + length;
|
|
62
|
+
if (end > reader.len) {
|
|
63
|
+
console.error("Unexpected EOF while reading message bytes");
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
const msgBytes = decompressed.buffer.slice(start, end);
|
|
67
|
+
reader.pos = end;
|
|
68
|
+
const msg = protocol_definitions_1.blueye.protocol.BinlogRecord.decode(new Uint8Array(msgBytes), length);
|
|
69
|
+
const key = msg.payload?.typeUrl.split(".").at(-1);
|
|
70
|
+
if (!key || !(0, client_1.isInProtocol)(key)) {
|
|
71
|
+
console.warn(`Unknown protocol key: ${key}`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (msg.payload == null) {
|
|
75
|
+
console.warn(`Missing payload for key: ${key}`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const data = protocol_definitions_1.blueye.protocol[key].decode(msg.payload.value);
|
|
79
|
+
let innerData;
|
|
80
|
+
if (key === "GetTelemetryRep") {
|
|
81
|
+
const telRep = data;
|
|
82
|
+
const innerKey = telRep.payload?.typeUrl.split(".").at(-1);
|
|
83
|
+
if (!innerKey || !(0, client_1.isInProtocol)(innerKey)) {
|
|
84
|
+
console.warn(`Unknown inner protocol key: ${innerKey}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
innerData = telRep.payload
|
|
88
|
+
? protocol_definitions_1.blueye.protocol[innerKey].decode(telRep.payload.value)
|
|
89
|
+
: undefined;
|
|
90
|
+
}
|
|
91
|
+
let type = "Tel";
|
|
92
|
+
if (key.endsWith("Ctrl"))
|
|
93
|
+
type = "Ctrl";
|
|
94
|
+
else if (key.endsWith("Rep"))
|
|
95
|
+
type = "Rep";
|
|
96
|
+
else if (key.endsWith("Req"))
|
|
97
|
+
type = "Req";
|
|
98
|
+
messages.push({
|
|
99
|
+
monotonicTime: msg.clockMonotonic?.getTime() ?? 0,
|
|
100
|
+
time: msg.unixTimestamp?.getTime() ?? 0,
|
|
101
|
+
type,
|
|
102
|
+
key,
|
|
103
|
+
data,
|
|
104
|
+
innerData,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (fixTimes) {
|
|
108
|
+
messages = (0, exports.fixMessageTimes)(messages);
|
|
109
|
+
}
|
|
110
|
+
return messages;
|
|
111
|
+
};
|
|
112
|
+
exports.parseMessages = parseMessages;
|
|
113
|
+
/**
|
|
114
|
+
* Fix the message times based on the last message's monotonic and unix timestamps.
|
|
115
|
+
* @param messages The messages to fix the times for.
|
|
116
|
+
* @returns The messages with corrected times.
|
|
117
|
+
*/
|
|
118
|
+
const fixMessageTimes = (messages) => {
|
|
119
|
+
if (messages.length === 0)
|
|
120
|
+
return messages;
|
|
121
|
+
const last = messages.at(-1);
|
|
122
|
+
if (!last)
|
|
123
|
+
return messages;
|
|
124
|
+
const ssbLast = last.monotonicTime;
|
|
125
|
+
const unixLast = last.time;
|
|
126
|
+
for (const message of messages) {
|
|
127
|
+
const delta = ssbLast - message.monotonicTime;
|
|
128
|
+
message.time = unixLast - delta;
|
|
129
|
+
}
|
|
130
|
+
return messages;
|
|
131
|
+
};
|
|
132
|
+
exports.fixMessageTimes = fixMessageTimes;
|
package/dist/src/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { blueye } from "@blueyerobotics/protocol-definitions";
|
|
2
|
-
import { LogLevel } from "consola";
|
|
2
|
+
import { type LogLevel } from "consola";
|
|
3
3
|
import { Emitter } from "strict-event-emitter";
|
|
4
4
|
export type Protocol = typeof blueye.protocol;
|
|
5
5
|
export type ProtocolType = "Req" | "Rep" | "Tel" | "Ctrl";
|
|
@@ -25,12 +25,14 @@ type Options = Partial<{
|
|
|
25
25
|
rpcUrl: string;
|
|
26
26
|
pubUrl: string;
|
|
27
27
|
timeout: number;
|
|
28
|
+
reconnectInterval: number;
|
|
28
29
|
logLevel: LogLevel;
|
|
29
30
|
autoConnect: boolean;
|
|
30
31
|
}>;
|
|
31
32
|
export declare class BlueyeClient extends Emitter<Events> {
|
|
32
33
|
state: State;
|
|
33
34
|
timeout: number;
|
|
35
|
+
reconnectInterval: number;
|
|
34
36
|
private subUrl;
|
|
35
37
|
private rpcUrl;
|
|
36
38
|
private pubUrl;
|
|
@@ -39,7 +41,7 @@ export declare class BlueyeClient extends Emitter<Events> {
|
|
|
39
41
|
private pub;
|
|
40
42
|
private queue;
|
|
41
43
|
private logger;
|
|
42
|
-
constructor({ subUrl, rpcUrl, pubUrl, timeout, logLevel, autoConnect, }?: Options);
|
|
44
|
+
constructor({ subUrl, rpcUrl, pubUrl, timeout, reconnectInterval, logLevel, autoConnect, }?: Options);
|
|
43
45
|
private updateState;
|
|
44
46
|
connect(): void;
|
|
45
47
|
disconnect(): void;
|
package/dist/src/client.js
CHANGED
|
@@ -18,6 +18,7 @@ exports.isInProtocol = isInProtocol;
|
|
|
18
18
|
class BlueyeClient extends strict_event_emitter_1.Emitter {
|
|
19
19
|
state = "disconnected";
|
|
20
20
|
timeout;
|
|
21
|
+
reconnectInterval;
|
|
21
22
|
subUrl;
|
|
22
23
|
rpcUrl;
|
|
23
24
|
pubUrl;
|
|
@@ -26,9 +27,10 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
|
|
|
26
27
|
pub;
|
|
27
28
|
queue;
|
|
28
29
|
logger;
|
|
29
|
-
constructor({ subUrl = DEFAULT_SUB_URL, rpcUrl = DEFAULT_RPC_URL, pubUrl = DEFAULT_PUB_URL, timeout = 2000, logLevel = consola_1.LogLevels.info, autoConnect = false, } = {}) {
|
|
30
|
+
constructor({ subUrl = DEFAULT_SUB_URL, rpcUrl = DEFAULT_RPC_URL, pubUrl = DEFAULT_PUB_URL, timeout = 2000, reconnectInterval = 2000, logLevel = consola_1.LogLevels.info, autoConnect = false, } = {}) {
|
|
30
31
|
super();
|
|
31
32
|
this.timeout = timeout;
|
|
33
|
+
this.reconnectInterval = reconnectInterval;
|
|
32
34
|
this.subUrl = subUrl;
|
|
33
35
|
this.rpcUrl = rpcUrl;
|
|
34
36
|
this.pubUrl = pubUrl;
|
|
@@ -70,6 +72,9 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
|
|
|
70
72
|
this.logger.warn("[client] already connecting");
|
|
71
73
|
return;
|
|
72
74
|
}
|
|
75
|
+
this.sub.options.reconnectInterval = this.reconnectInterval;
|
|
76
|
+
this.rpc.options.reconnectInterval = this.reconnectInterval;
|
|
77
|
+
this.pub.options.reconnectInterval = this.reconnectInterval;
|
|
73
78
|
this.updateState("connecting");
|
|
74
79
|
this.sub.subscribe("");
|
|
75
80
|
this.sub.connect(this.subUrl);
|
|
@@ -100,9 +105,11 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
|
|
|
100
105
|
const message = protocol.create(opts);
|
|
101
106
|
const encoded = protocol.encode(message).finish();
|
|
102
107
|
const request = () => {
|
|
103
|
-
return new Promise((resolve) => {
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
const timer = setTimeout(() => reject(new Error("[rpc] request timed out")), this.timeout);
|
|
104
110
|
// @ts-ignore
|
|
105
111
|
this.rpc.once("message", (topic, msg) => {
|
|
112
|
+
clearTimeout(timer);
|
|
106
113
|
resolve({ key: topic.toString().split(".").at(-1), data: msg });
|
|
107
114
|
});
|
|
108
115
|
this.rpc.send([
|
|
@@ -111,10 +118,7 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
|
|
|
111
118
|
]);
|
|
112
119
|
});
|
|
113
120
|
};
|
|
114
|
-
const { key, data } = await
|
|
115
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("[rpc] request timed out")), this.timeout)),
|
|
116
|
-
this.queue.enqueue(request),
|
|
117
|
-
]);
|
|
121
|
+
const { key, data } = await this.queue.enqueue(request);
|
|
118
122
|
if (key === "Empty") {
|
|
119
123
|
return null;
|
|
120
124
|
}
|
package/dist/src/schema.d.ts
CHANGED
|
@@ -7,6 +7,6 @@ export declare const responseSchema: z.ZodObject<{
|
|
|
7
7
|
export declare const telemetrySchema: z.ZodObject<{
|
|
8
8
|
payload: z.ZodObject<{
|
|
9
9
|
typeUrl: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
10
|
-
value: z.ZodPipe<z.ZodAny, z.ZodTransform<Buffer, any>>;
|
|
10
|
+
value: z.ZodPipe<z.ZodAny & z.ZodType<Buffer | Uint8Array<ArrayBufferLike>, any, z.core.$ZodTypeInternals<Buffer | Uint8Array<ArrayBufferLike>, any>>, z.ZodTransform<Buffer, any>>;
|
|
11
11
|
}, z.core.$strip>;
|
|
12
12
|
}, z.core.$strip>;
|
package/dist/src/schema.js
CHANGED
|
@@ -7,15 +7,17 @@ exports.telemetrySchema = exports.responseSchema = void 0;
|
|
|
7
7
|
const buffer_1 = require("buffer");
|
|
8
8
|
const zod_1 = __importDefault(require("zod"));
|
|
9
9
|
exports.responseSchema = zod_1.default.object({
|
|
10
|
-
key: zod_1.default
|
|
11
|
-
|
|
10
|
+
key: zod_1.default
|
|
11
|
+
.instanceof(buffer_1.Buffer)
|
|
12
|
+
.transform((val) => val.toString().split(".").at(-1)),
|
|
13
|
+
data: zod_1.default.instanceof(buffer_1.Buffer),
|
|
12
14
|
});
|
|
13
15
|
exports.telemetrySchema = zod_1.default.object({
|
|
14
16
|
payload: zod_1.default.object({
|
|
15
|
-
typeUrl: zod_1.default.string().transform(val => val.split(".").at(-1)),
|
|
17
|
+
typeUrl: zod_1.default.string().transform((val) => val.split(".").at(-1)),
|
|
16
18
|
value: zod_1.default
|
|
17
19
|
.any()
|
|
18
|
-
.refine(val => val instanceof buffer_1.Buffer || val instanceof Uint8Array)
|
|
19
|
-
.transform(val => (buffer_1.Buffer.isBuffer(val) ? val : buffer_1.Buffer.from(val)))
|
|
20
|
-
})
|
|
20
|
+
.refine((val) => val instanceof buffer_1.Buffer || val instanceof Uint8Array)
|
|
21
|
+
.transform((val) => (buffer_1.Buffer.isBuffer(val) ? val : buffer_1.Buffer.from(val))),
|
|
22
|
+
}),
|
|
21
23
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blueyerobotics/blueye-ts",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "A TypeScript client for interacting with Blueye underwater drones.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,14 +15,17 @@
|
|
|
15
15
|
"author": "Blueye <contact@blueye.no> (https://blueye.no/)",
|
|
16
16
|
"license": "LGPL-3.0-only",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@blueyerobotics/protocol-definitions": "3.2.0-
|
|
18
|
+
"@blueyerobotics/protocol-definitions": "3.2.0-c75f3166",
|
|
19
|
+
"@bufbuild/protobuf": "^2.10.2",
|
|
19
20
|
"buffer": "^6.0.3",
|
|
20
21
|
"consola": "^3.4.2",
|
|
22
|
+
"fflate": "^0.8.2",
|
|
21
23
|
"jszmq": "^0.1.2",
|
|
22
24
|
"strict-event-emitter": "^0.5.1",
|
|
23
|
-
"zod": "^4.
|
|
25
|
+
"zod": "^4.3.5"
|
|
24
26
|
},
|
|
25
27
|
"devDependencies": {
|
|
28
|
+
"@biomejs/biome": "2.3.11",
|
|
26
29
|
"typescript": "^5.9.3"
|
|
27
30
|
},
|
|
28
31
|
"scripts": {
|