@blueyerobotics/blueye-ts 3.11.0 → 3.13.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.
@@ -60,6 +60,7 @@ export declare class BlueyeClient extends Emitter<Events> {
60
60
  disconnect(): void;
61
61
  sendRequest<T extends Req>(req: T, opts?: CreateArgs<T>): Promise<DecodedOutput<T> | null>;
62
62
  getTelemetry<T extends Tel>(type: T): Promise<DecodedTelOutput<T>>;
63
+ waitForTelemetry<T extends Tel>(type: T, timeout?: number | null): Promise<DecodedTelOutput<T>>;
63
64
  sendControl<T extends Ctrl>(ctrl: T, opts?: CreateArgs<T>): Promise<void>;
64
65
  }
65
66
  export {};
@@ -105,23 +105,8 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
105
105
  this.sonarSub.on("message", (topic, msg) => {
106
106
  this.handleTelemetryMessage("sonar", topic, msg);
107
107
  });
108
- this.once("DroneInfoTel", (msg) => {
109
- const version = msg.droneInfo?.blunuxVersion;
110
- if (!hasSonarEndpoint(version ?? "")) {
111
- this.logger.warn(`[sonar] incompatible Blunux version detected in DroneInfoTel: ${version}; sonar telemetry may not be available`);
112
- return;
113
- }
114
- const devices = [
115
- ...(msg.droneInfo?.gp?.gp1?.deviceList?.devices ?? []),
116
- ...(msg.droneInfo?.gp?.gp2?.deviceList?.devices ?? []),
117
- ...(msg.droneInfo?.gp?.gp3?.deviceList?.devices ?? []),
118
- ].map((device) => device.deviceId);
119
- if (devices.some((deviceId) => exports.MULTIBEAM_DEVICE_IDS.includes(deviceId))) {
120
- this.logger.info("[sonar] multibeam device detected in DroneInfoTel:", devices);
121
- this.isSonarDetected = true;
122
- this.sonarSub.connect(this.sonarUrl);
123
- }
124
- });
108
+ this.emit(this.state);
109
+ this.logger.info(`[client] ${this.state}`);
125
110
  if (autoConnect) {
126
111
  this.connect();
127
112
  }
@@ -142,11 +127,15 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
142
127
  if (this.socketState[name] === newState) {
143
128
  return;
144
129
  }
130
+ const oldState = this.state;
145
131
  this.socketState[name] = newState;
146
132
  this.logger.info(`[${name}] ${newState}`);
147
133
  this.emit(`${name}-${newState}`);
148
134
  // If all sockets are connected, emit "connected"
149
135
  this.emit(this.state);
136
+ if (oldState !== this.state) {
137
+ this.logger.info(`[client] ${this.state}`);
138
+ }
150
139
  }
151
140
  handleTelemetryMessage(socketName, topic, msg) {
152
141
  const { key, data } = schema_1.responseSchema.parse({ key: topic, data: msg });
@@ -172,7 +161,7 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
172
161
  });
173
162
  }
174
163
  ensureConnected(operation) {
175
- if (this.state !== "connected") {
164
+ if (this.socketState[operation] !== "connected") {
176
165
  throw new Error(`[client] cannot send ${operation} while ${this.state}; call connect() and wait for "connected"`);
177
166
  }
178
167
  }
@@ -181,14 +170,34 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
181
170
  this.logger.warn("[client] already connecting or connected");
182
171
  return;
183
172
  }
173
+ this.once("connected", async () => {
174
+ const msg = await this.waitForTelemetry("DroneInfoTel");
175
+ const version = msg.droneInfo?.blunuxVersion;
176
+ if (!hasSonarEndpoint(version ?? "")) {
177
+ this.logger.warn(`[sonar] incompatible Blunux version detected in DroneInfoTel: ${version}; sonar telemetry may not be available`);
178
+ return;
179
+ }
180
+ const devices = [
181
+ ...(msg.droneInfo?.gp?.gp1?.deviceList?.devices ?? []),
182
+ ...(msg.droneInfo?.gp?.gp2?.deviceList?.devices ?? []),
183
+ ...(msg.droneInfo?.gp?.gp3?.deviceList?.devices ?? []),
184
+ ].map((device) => device.deviceId);
185
+ if (devices.some((deviceId) => exports.MULTIBEAM_DEVICE_IDS.includes(deviceId))) {
186
+ this.logger.info("[sonar] multibeam device detected in DroneInfoTel");
187
+ this.isSonarDetected = true;
188
+ this.sonarSub.connect(this.sonarUrl);
189
+ }
190
+ });
184
191
  this.sub.options.reconnectInterval = this.reconnectInterval;
185
192
  this.rpc.options.reconnectInterval = this.reconnectInterval;
186
193
  this.pub.options.reconnectInterval = this.reconnectInterval;
187
194
  this.sonarSub.options.reconnectInterval = this.reconnectInterval;
188
195
  this.shouldBeConnected = true;
189
- for (const name of ["sub", "rpc", "pub", "sonar"]) {
196
+ for (const name of ["sub", "rpc", "pub"]) {
190
197
  this.updateSocketState(name, "connecting");
191
198
  }
199
+ this.logger.info(`[client] ${this.state}`);
200
+ this.emit(this.state);
192
201
  this.sub.subscribe("");
193
202
  this.sub.connect(this.subUrl);
194
203
  this.rpc.connect(this.rpcUrl);
@@ -212,7 +221,7 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
212
221
  }
213
222
  }
214
223
  async sendRequest(req, opts = {}) {
215
- this.ensureConnected("request");
224
+ this.ensureConnected("rpc");
216
225
  if (!(0, exports.isInProtocol)(req) || !req.endsWith("Req")) {
217
226
  throw new Error(`[rpc] unknown protocol: ${req}`);
218
227
  }
@@ -260,8 +269,33 @@ class BlueyeClient extends strict_event_emitter_1.Emitter {
260
269
  this.logger.debug("[rpc] result:", result);
261
270
  return result;
262
271
  }
272
+ async waitForTelemetry(type, timeout = null) {
273
+ // Tries to get the latest telemetry via RPC first, in case we already have it cached in Blunux
274
+ try {
275
+ return await this.getTelemetry(type);
276
+ }
277
+ catch (error) {
278
+ this.logger.trace(`[client] failed to get latest ${type} via RPC:`, error);
279
+ }
280
+ // If that fails, wait for the next telemetry message to arrive via SUB
281
+ return new Promise((resolve, reject) => {
282
+ const listener = (...data) => {
283
+ this.off(type, listener);
284
+ if (timer)
285
+ clearTimeout(timer);
286
+ resolve(data[0]);
287
+ };
288
+ const timer = timeout
289
+ ? setTimeout(() => {
290
+ this.off(type, listener);
291
+ reject(new Error(`[client] timed out waiting for ${type} telemetry`));
292
+ }, timeout)
293
+ : null;
294
+ this.on(type, listener);
295
+ });
296
+ }
263
297
  async sendControl(ctrl, opts = {}) {
264
- this.ensureConnected("control");
298
+ this.ensureConnected("pub");
265
299
  if (!(0, exports.isInProtocol)(ctrl) || !ctrl.endsWith("Ctrl")) {
266
300
  throw new Error(`[pub] unknown protocol: ${ctrl}`);
267
301
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueyerobotics/blueye-ts",
3
- "version": "3.11.0",
3
+ "version": "3.13.0",
4
4
  "description": "A TypeScript client for interacting with Blueye underwater drones.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,7 +16,7 @@
16
16
  "license": "LGPL-3.0-only",
17
17
  "dependencies": {
18
18
  "@blueyerobotics/jszmq": "^0.2.0",
19
- "@blueyerobotics/protocol-definitions": "3.2.0-98aafe92",
19
+ "@blueyerobotics/protocol-definitions": "3.2.0-a57cd215",
20
20
  "@bufbuild/protobuf": "^2.11.0",
21
21
  "buffer": "^6.0.3",
22
22
  "consola": "^3.4.2",
@@ -35,7 +35,7 @@
35
35
  "start": "pnpm build && node dist/example.js",
36
36
  "start:sonar": "pnpm build && node dist/sonar-example.js",
37
37
  "build": "tsc",
38
- "test": "pnpm build && node --test --test-concurrency=1 test/**/*.test.js",
38
+ "test": "pnpm build && node --test --experimental-test-isolation=none --test-concurrency=1 test/**/*.test.js",
39
39
  "test:one": "node --test --test-concurrency=1"
40
40
  }
41
41
  }