@onekeyfe/hd-transport-web-device 1.1.26 → 1.1.27-alpha.30
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/dist/electron-auto-ble-transport.d.ts +70 -0
- package/dist/electron-auto-ble-transport.d.ts.map +1 -0
- package/dist/electron-ble-transport.d.ts +2 -0
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +93 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +960 -37
- package/dist/webusb.d.ts +20 -3
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-auto-ble-transport.ts +768 -0
- package/src/electron-ble-transport.ts +14 -5
- package/src/index.ts +2 -1
- package/src/webusb.ts +467 -35
package/dist/index.js
CHANGED
|
@@ -41,16 +41,34 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
41
41
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
const { parseConfigure: parseConfigure$
|
|
44
|
+
const { parseConfigure: parseConfigure$2, check: check$2, ProtocolV1: ProtocolV1$2 } = transport__default["default"];
|
|
45
45
|
const CONFIGURATION_ID = 1;
|
|
46
46
|
const INTERFACE_ID = 0;
|
|
47
47
|
const ENDPOINT_ID = 1;
|
|
48
|
-
const PACKET_SIZE =
|
|
49
|
-
const
|
|
48
|
+
const PACKET_SIZE = transport.PROTOCOL_V1_USB_PACKET_SIZE;
|
|
49
|
+
const PAYLOAD_SIZE = transport.PROTOCOL_V1_CHUNK_PAYLOAD_SIZE;
|
|
50
|
+
const REPORT_ID = transport.PROTOCOL_V1_REPORT_ID;
|
|
51
|
+
const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
50
52
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
51
53
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
54
|
+
const PROTOCOL_PROBE_TIMEOUT = 1000;
|
|
55
|
+
const WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
56
|
+
function shouldSuppressWebUsbCallLog(name) {
|
|
57
|
+
const normalized = name.replace(/[_\s-]/g, '');
|
|
58
|
+
return WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
59
|
+
}
|
|
60
|
+
function shouldBlockWebUsbCallDataLog(name) {
|
|
61
|
+
const normalized = name.replace(/[_\s-]/g, '');
|
|
62
|
+
return transport.LogBlockCommand.has(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
63
|
+
}
|
|
64
|
+
function inferProtocolTypeFromDeviceName$1(name) {
|
|
65
|
+
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
66
|
+
}
|
|
52
67
|
class WebUsbTransport {
|
|
53
68
|
constructor() {
|
|
69
|
+
this.deviceProtocol = new Map();
|
|
70
|
+
this.protocolV2Assemblers = new Map();
|
|
71
|
+
this.deviceEndpoints = new Map();
|
|
54
72
|
this.name = 'WebUsbTransport';
|
|
55
73
|
this.stopped = false;
|
|
56
74
|
this.configured = false;
|
|
@@ -68,10 +86,15 @@ class WebUsbTransport {
|
|
|
68
86
|
this.usb = usb;
|
|
69
87
|
}
|
|
70
88
|
configure(signedData) {
|
|
71
|
-
const messages = parseConfigure$
|
|
89
|
+
const messages = parseConfigure$2(signedData);
|
|
72
90
|
this.configured = true;
|
|
73
91
|
this.messages = messages;
|
|
74
92
|
}
|
|
93
|
+
configureProtocolV2(signedData) {
|
|
94
|
+
var _a;
|
|
95
|
+
this.messagesV2 = parseConfigure$2(signedData);
|
|
96
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] Protocol V2 schema configured');
|
|
97
|
+
}
|
|
75
98
|
promptDeviceAccess() {
|
|
76
99
|
return __awaiter(this, void 0, void 0, function* () {
|
|
77
100
|
if (!this.usb)
|
|
@@ -106,7 +129,12 @@ class WebUsbTransport {
|
|
|
106
129
|
path: device.serialNumber,
|
|
107
130
|
device,
|
|
108
131
|
commType: 'webusb',
|
|
132
|
+
protocolType: inferProtocolTypeFromDeviceName$1(device.productName),
|
|
109
133
|
}));
|
|
134
|
+
for (const dev of onekeyDevices) {
|
|
135
|
+
this.Log.debug(`[WebUSB] Device: name="${dev.productName}" serial="${dev.serialNumber}" ` +
|
|
136
|
+
`VID=0x${dev.vendorId.toString(16)} PID=0x${dev.productId.toString(16)}`);
|
|
137
|
+
}
|
|
110
138
|
return this.deviceList;
|
|
111
139
|
});
|
|
112
140
|
}
|
|
@@ -117,6 +145,7 @@ class WebUsbTransport {
|
|
|
117
145
|
return;
|
|
118
146
|
try {
|
|
119
147
|
yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
|
|
148
|
+
yield this.detectProtocol(input.path, input.expectedProtocol);
|
|
120
149
|
return yield Promise.resolve(input.path);
|
|
121
150
|
}
|
|
122
151
|
catch (e) {
|
|
@@ -125,6 +154,51 @@ class WebUsbTransport {
|
|
|
125
154
|
}
|
|
126
155
|
});
|
|
127
156
|
}
|
|
157
|
+
createProtocolMismatchError(expected) {
|
|
158
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
159
|
+
}
|
|
160
|
+
detectProtocol(path, expectedProtocol) {
|
|
161
|
+
var _a;
|
|
162
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
163
|
+
if (expectedProtocol === 'V1') {
|
|
164
|
+
if (yield this.probeProtocolV1(path)) {
|
|
165
|
+
this.deviceProtocol.set(path, 'V1');
|
|
166
|
+
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
|
|
167
|
+
return 'V1';
|
|
168
|
+
}
|
|
169
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
170
|
+
}
|
|
171
|
+
if (expectedProtocol === 'V2') {
|
|
172
|
+
if (this.deviceProtocol.get(path) === 'V2') {
|
|
173
|
+
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (cached expected)`);
|
|
174
|
+
return 'V2';
|
|
175
|
+
}
|
|
176
|
+
if (((_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.protocolType) === 'V2') {
|
|
177
|
+
this.deviceProtocol.set(path, 'V2');
|
|
178
|
+
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (descriptor)`);
|
|
179
|
+
return 'V2';
|
|
180
|
+
}
|
|
181
|
+
if (yield this.probeProtocolV2(path)) {
|
|
182
|
+
this.deviceProtocol.set(path, 'V2');
|
|
183
|
+
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
|
|
184
|
+
return 'V2';
|
|
185
|
+
}
|
|
186
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
187
|
+
}
|
|
188
|
+
if (this.deviceProtocol.get(path) === 'V2' && (yield this.probeProtocolV2(path))) {
|
|
189
|
+
this.deviceProtocol.set(path, 'V2');
|
|
190
|
+
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
|
|
191
|
+
return 'V2';
|
|
192
|
+
}
|
|
193
|
+
let protocol = 'V1';
|
|
194
|
+
if (!(yield this.probeProtocolV1(path)) && (yield this.probeProtocolV2(path))) {
|
|
195
|
+
protocol = 'V2';
|
|
196
|
+
}
|
|
197
|
+
this.deviceProtocol.set(path, protocol);
|
|
198
|
+
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> ${protocol}`);
|
|
199
|
+
return protocol;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
128
202
|
findDevice(path) {
|
|
129
203
|
return __awaiter(this, void 0, void 0, function* () {
|
|
130
204
|
if (this.deviceList.length === 0) {
|
|
@@ -157,19 +231,50 @@ class WebUsbTransport {
|
|
|
157
231
|
}
|
|
158
232
|
});
|
|
159
233
|
}
|
|
234
|
+
discoverEndpoints(device) {
|
|
235
|
+
var _a, _b;
|
|
236
|
+
for (const config of device.configurations) {
|
|
237
|
+
for (const iface of config.interfaces) {
|
|
238
|
+
for (const alt of iface.alternates) {
|
|
239
|
+
if (alt.interfaceClass === 0xff) {
|
|
240
|
+
let endpointIn = this.endpointId;
|
|
241
|
+
let endpointOut = this.endpointId;
|
|
242
|
+
for (const ep of alt.endpoints) {
|
|
243
|
+
if (ep.direction === 'in')
|
|
244
|
+
endpointIn = ep.endpointNumber;
|
|
245
|
+
else
|
|
246
|
+
endpointOut = ep.endpointNumber;
|
|
247
|
+
}
|
|
248
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] discovered vendor interface ${iface.interfaceNumber}, ` +
|
|
249
|
+
`endpointIn=${endpointIn}, endpointOut=${endpointOut}`);
|
|
250
|
+
return { interfaceNumber: iface.interfaceNumber, endpointIn, endpointOut };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[WebUsbTransport] no vendor interface found, using defaults');
|
|
256
|
+
return {
|
|
257
|
+
interfaceNumber: this.interfaceId,
|
|
258
|
+
endpointIn: this.endpointId,
|
|
259
|
+
endpointOut: this.endpointId,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
160
262
|
connectToDevice(path, first) {
|
|
161
263
|
return __awaiter(this, void 0, void 0, function* () {
|
|
162
264
|
const device = yield this.findDevice(path);
|
|
265
|
+
this.Log.debug('[WebUsbTransport] connecting to device:', device.productName, 'PID:', device.productId);
|
|
163
266
|
yield device.open();
|
|
164
|
-
if (first
|
|
267
|
+
if (first ||
|
|
268
|
+
!device.configuration ||
|
|
269
|
+
device.configuration.configurationValue !== this.configurationId) {
|
|
165
270
|
yield device.selectConfiguration(this.configurationId);
|
|
166
|
-
try {
|
|
167
|
-
yield device.reset();
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
}
|
|
171
271
|
}
|
|
172
|
-
|
|
272
|
+
const endpoints = this.discoverEndpoints(device);
|
|
273
|
+
this.deviceEndpoints.set(path, endpoints);
|
|
274
|
+
if (!this.protocolV2Assemblers.has(path)) {
|
|
275
|
+
this.protocolV2Assemblers.set(path, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES));
|
|
276
|
+
}
|
|
277
|
+
yield device.claimInterface(endpoints.interfaceNumber);
|
|
173
278
|
});
|
|
174
279
|
}
|
|
175
280
|
post(session, name, data) {
|
|
@@ -199,14 +304,17 @@ class WebUsbTransport {
|
|
|
199
304
|
message.includes('networkerror'));
|
|
200
305
|
}
|
|
201
306
|
reconnectForPacketIoRetry(path, direction, attempt, error) {
|
|
307
|
+
var _a;
|
|
202
308
|
return __awaiter(this, void 0, void 0, function* () {
|
|
203
309
|
this.Log.debug(`[WebUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
|
|
204
310
|
yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
205
311
|
try {
|
|
206
312
|
const currentDevice = yield this.findDevice(path);
|
|
207
313
|
if (currentDevice.opened) {
|
|
314
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
315
|
+
const ifaceNum = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _a !== void 0 ? _a : this.interfaceId;
|
|
208
316
|
try {
|
|
209
|
-
yield currentDevice.releaseInterface(
|
|
317
|
+
yield currentDevice.releaseInterface(ifaceNum);
|
|
210
318
|
}
|
|
211
319
|
catch (releaseError) {
|
|
212
320
|
this.Log.debug('[WebUsbTransport] releaseInterface before retry error:', releaseError);
|
|
@@ -239,6 +347,7 @@ class WebUsbTransport {
|
|
|
239
347
|
return copied.buffer;
|
|
240
348
|
}
|
|
241
349
|
transferOutWithRetry(path, packet) {
|
|
350
|
+
var _a;
|
|
242
351
|
return __awaiter(this, void 0, void 0, function* () {
|
|
243
352
|
let lastError;
|
|
244
353
|
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt += 1) {
|
|
@@ -247,8 +356,10 @@ class WebUsbTransport {
|
|
|
247
356
|
if (!device.opened) {
|
|
248
357
|
yield this.connect(path, false);
|
|
249
358
|
}
|
|
359
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
360
|
+
const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
250
361
|
const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
|
|
251
|
-
yield device.transferOut(
|
|
362
|
+
yield device.transferOut(endpointOut, transferBuffer);
|
|
252
363
|
return;
|
|
253
364
|
}
|
|
254
365
|
catch (error) {
|
|
@@ -269,20 +380,29 @@ class WebUsbTransport {
|
|
|
269
380
|
throw lastError;
|
|
270
381
|
});
|
|
271
382
|
}
|
|
272
|
-
transferInWithRetry(path, length) {
|
|
383
|
+
transferInWithRetry(path, length, cancelToken) {
|
|
384
|
+
var _a;
|
|
273
385
|
return __awaiter(this, void 0, void 0, function* () {
|
|
274
386
|
let lastError;
|
|
275
387
|
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt += 1) {
|
|
388
|
+
if (cancelToken === null || cancelToken === void 0 ? void 0 : cancelToken.cancelled) {
|
|
389
|
+
throw new Error('transferIn cancelled');
|
|
390
|
+
}
|
|
276
391
|
try {
|
|
277
392
|
const device = yield this.findDevice(path);
|
|
278
393
|
if (!device.opened) {
|
|
279
394
|
yield this.connect(path, false);
|
|
280
395
|
}
|
|
281
|
-
const
|
|
396
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
397
|
+
const endpointIn = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointIn) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
398
|
+
const result = yield device.transferIn(endpointIn, length);
|
|
282
399
|
return this.getTransferInData(result);
|
|
283
400
|
}
|
|
284
401
|
catch (error) {
|
|
285
402
|
lastError = error;
|
|
403
|
+
if (cancelToken === null || cancelToken === void 0 ? void 0 : cancelToken.cancelled) {
|
|
404
|
+
throw error;
|
|
405
|
+
}
|
|
286
406
|
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryablePacketIoError(error);
|
|
287
407
|
if (!shouldRetry) {
|
|
288
408
|
throw error;
|
|
@@ -299,7 +419,99 @@ class WebUsbTransport {
|
|
|
299
419
|
throw lastError;
|
|
300
420
|
});
|
|
301
421
|
}
|
|
302
|
-
|
|
422
|
+
resetConnectionAfterProbe(path) {
|
|
423
|
+
var _a, _b;
|
|
424
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
425
|
+
(_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
426
|
+
try {
|
|
427
|
+
const device = yield this.findDevice(path);
|
|
428
|
+
if (device.opened) {
|
|
429
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
430
|
+
const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
|
|
431
|
+
try {
|
|
432
|
+
yield device.releaseInterface(ifaceNum);
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
this.Log.debug('[WebUsbTransport] releaseInterface after protocol probe error:', error);
|
|
436
|
+
}
|
|
437
|
+
yield device.close();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
this.Log.debug('[WebUsbTransport] close after protocol probe error:', error);
|
|
442
|
+
}
|
|
443
|
+
yield this.getConnectedDevices();
|
|
444
|
+
yield this.connect(path, false);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
withProtocolReadTimeout(path, promise, timeoutMs, protocol, onTimeout) {
|
|
448
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
449
|
+
let timer;
|
|
450
|
+
let timedOut = false;
|
|
451
|
+
const waitForeverAfterTimeout = () => new Promise(() => { });
|
|
452
|
+
const guardedPromise = promise.then(value => (timedOut ? waitForeverAfterTimeout() : value), error => {
|
|
453
|
+
if (timedOut) {
|
|
454
|
+
return waitForeverAfterTimeout();
|
|
455
|
+
}
|
|
456
|
+
throw error;
|
|
457
|
+
});
|
|
458
|
+
try {
|
|
459
|
+
return yield Promise.race([
|
|
460
|
+
guardedPromise,
|
|
461
|
+
new Promise((_, reject) => {
|
|
462
|
+
timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
463
|
+
timedOut = true;
|
|
464
|
+
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
|
|
465
|
+
try {
|
|
466
|
+
yield this.resetConnectionAfterProbe(path);
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
this.Log.debug(`[WebUsbTransport] reset after Protocol ${protocol} timeout failed:`, error);
|
|
470
|
+
}
|
|
471
|
+
finally {
|
|
472
|
+
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
473
|
+
}
|
|
474
|
+
}), timeoutMs);
|
|
475
|
+
}),
|
|
476
|
+
]);
|
|
477
|
+
}
|
|
478
|
+
finally {
|
|
479
|
+
if (timer)
|
|
480
|
+
clearTimeout(timer);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
probeProtocolV1(path) {
|
|
485
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
486
|
+
if (!this.messages) {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
try {
|
|
490
|
+
yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
catch (error) {
|
|
494
|
+
this.Log.debug('[WebUsbTransport] Protocol V1 Initialize probe failed:', error);
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
probeProtocolV2(path) {
|
|
500
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
501
|
+
if (!this.messages || !this.messagesV2) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
return transport.probeProtocolV2({
|
|
505
|
+
call: (name, data, options) => this.callProtocolV2(path, name, data, options),
|
|
506
|
+
timeoutMs: PROTOCOL_PROBE_TIMEOUT,
|
|
507
|
+
logger: this.Log,
|
|
508
|
+
logPrefix: 'ProtocolV2 WebUSB',
|
|
509
|
+
onProbeFailed: () => this.resetConnectionAfterProbe(path),
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
call(path, name, data, options) {
|
|
514
|
+
var _a;
|
|
303
515
|
return __awaiter(this, void 0, void 0, function* () {
|
|
304
516
|
if (this.messages == null) {
|
|
305
517
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
@@ -308,33 +520,110 @@ class WebUsbTransport {
|
|
|
308
520
|
if (!device) {
|
|
309
521
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
310
522
|
}
|
|
311
|
-
const
|
|
312
|
-
if (
|
|
313
|
-
|
|
523
|
+
const protocol = (_a = this.deviceProtocol.get(path)) !== null && _a !== void 0 ? _a : 'V1';
|
|
524
|
+
if (shouldSuppressWebUsbCallLog(name)) ;
|
|
525
|
+
else if (shouldBlockWebUsbCallDataLog(name)) {
|
|
526
|
+
this.Log.debug('call-', ' name: ', name, ' protocol: ', protocol);
|
|
314
527
|
}
|
|
315
528
|
else {
|
|
316
|
-
this.Log.debug('call-', ' name: ', name, ' data: ', data);
|
|
529
|
+
this.Log.debug('call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
|
|
530
|
+
}
|
|
531
|
+
if (protocol === 'V2') {
|
|
532
|
+
return this.callProtocolV2(path, name, data, options);
|
|
533
|
+
}
|
|
534
|
+
return this.callProtocolV1(path, name, data, options);
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
callProtocolV1(path, name, data, options) {
|
|
538
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
539
|
+
const { messages } = this;
|
|
540
|
+
if (!messages) {
|
|
541
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
317
542
|
}
|
|
318
|
-
const encodeBuffers =
|
|
543
|
+
const encodeBuffers = ProtocolV1$2.encodeMessageChunks(messages, name, data);
|
|
319
544
|
for (const buffer of encodeBuffers) {
|
|
320
545
|
const newArray = new Uint8Array(PACKET_SIZE);
|
|
321
|
-
newArray[0] =
|
|
546
|
+
newArray[0] = REPORT_ID;
|
|
322
547
|
newArray.set(new Uint8Array(buffer), 1);
|
|
323
548
|
yield this.transferOutWithRetry(path, newArray);
|
|
324
549
|
}
|
|
325
|
-
const resData = yield this.receiveData(path);
|
|
550
|
+
const resData = yield this.receiveData(path, options === null || options === void 0 ? void 0 : options.timeoutMs);
|
|
326
551
|
if (typeof resData !== 'string') {
|
|
327
552
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
328
553
|
}
|
|
329
|
-
const jsonData =
|
|
330
|
-
return check$
|
|
554
|
+
const jsonData = ProtocolV1$2.decodeMessage(messages, resData);
|
|
555
|
+
return check$2.call(jsonData);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
callProtocolV2(path, name, data, options) {
|
|
559
|
+
var _a;
|
|
560
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
561
|
+
const protocolV1Messages = this.messages;
|
|
562
|
+
if (!this.messagesV2) {
|
|
563
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured, 'Protocol V2 schema not configured');
|
|
564
|
+
}
|
|
565
|
+
if (!protocolV1Messages) {
|
|
566
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
567
|
+
}
|
|
568
|
+
const session = new transport.ProtocolV2Session({
|
|
569
|
+
schemas: {
|
|
570
|
+
protocolV1: protocolV1Messages,
|
|
571
|
+
protocolV2: this.messagesV2,
|
|
572
|
+
},
|
|
573
|
+
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
574
|
+
writeFrame: (frame) => this.transferOutWithRetry(path, frame),
|
|
575
|
+
readFrame: () => this.receiveProtocolV2Frame(path, options === null || options === void 0 ? void 0 : options.timeoutMs),
|
|
576
|
+
logger: this.Log,
|
|
577
|
+
logPrefix: 'ProtocolV2 WebUSB',
|
|
578
|
+
createTimeoutError: (_messageName, timeoutMs) => new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`),
|
|
579
|
+
});
|
|
580
|
+
(_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
581
|
+
return session.call(name, data, options);
|
|
331
582
|
});
|
|
332
583
|
}
|
|
333
|
-
|
|
584
|
+
receiveProtocolV2Frame(path, timeoutMs) {
|
|
334
585
|
return __awaiter(this, void 0, void 0, function* () {
|
|
335
|
-
|
|
586
|
+
let assembler = this.protocolV2Assemblers.get(path);
|
|
587
|
+
if (!assembler) {
|
|
588
|
+
assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES);
|
|
589
|
+
this.protocolV2Assemblers.set(path, assembler);
|
|
590
|
+
}
|
|
591
|
+
let frame = assembler.push(new Uint8Array(0));
|
|
592
|
+
const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
|
|
593
|
+
while (!frame) {
|
|
594
|
+
const cancelToken = { cancelled: false };
|
|
595
|
+
const transferIn = this.transferInWithRetry(path, transport.PROTOCOL_V2_FRAME_MAX_BYTES, cancelToken);
|
|
596
|
+
const dataView = deadline
|
|
597
|
+
? yield this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V2', () => {
|
|
598
|
+
cancelToken.cancelled = true;
|
|
599
|
+
})
|
|
600
|
+
: yield transferIn;
|
|
601
|
+
const bytes = new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
|
|
602
|
+
try {
|
|
603
|
+
frame = assembler.push(bytes);
|
|
604
|
+
}
|
|
605
|
+
catch (error) {
|
|
606
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return frame;
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
receiveData(path, timeoutMs) {
|
|
613
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
614
|
+
const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
|
|
615
|
+
const readPacket = () => __awaiter(this, void 0, void 0, function* () {
|
|
616
|
+
const cancelToken = { cancelled: false };
|
|
617
|
+
const transferIn = this.transferInWithRetry(path, PACKET_SIZE, cancelToken);
|
|
618
|
+
return deadline
|
|
619
|
+
? this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V1', () => {
|
|
620
|
+
cancelToken.cancelled = true;
|
|
621
|
+
})
|
|
622
|
+
: transferIn;
|
|
623
|
+
});
|
|
624
|
+
const firstPacketData = yield readPacket();
|
|
336
625
|
const firstData = this.toArrayBuffer(firstPacketData.buffer.slice(1));
|
|
337
|
-
const { length, typeId, restBuffer } =
|
|
626
|
+
const { length, typeId, restBuffer } = ProtocolV1$2.decodeFirstChunk(firstData);
|
|
338
627
|
const lengthWithHeader = Number(length + HEADER_LENGTH);
|
|
339
628
|
const decoded = new ByteBuffer__default["default"](lengthWithHeader);
|
|
340
629
|
decoded.writeUint16(typeId);
|
|
@@ -343,9 +632,9 @@ class WebUsbTransport {
|
|
|
343
632
|
decoded.append(restBuffer);
|
|
344
633
|
}
|
|
345
634
|
while (decoded.offset < lengthWithHeader) {
|
|
346
|
-
const packetData = yield
|
|
635
|
+
const packetData = yield readPacket();
|
|
347
636
|
const buffer = this.toArrayBuffer(packetData.buffer.slice(1));
|
|
348
|
-
if (lengthWithHeader - decoded.offset >=
|
|
637
|
+
if (lengthWithHeader - decoded.offset >= PAYLOAD_SIZE) {
|
|
349
638
|
decoded.append(buffer);
|
|
350
639
|
}
|
|
351
640
|
else {
|
|
@@ -358,15 +647,24 @@ class WebUsbTransport {
|
|
|
358
647
|
});
|
|
359
648
|
}
|
|
360
649
|
release(path) {
|
|
650
|
+
var _a;
|
|
361
651
|
return __awaiter(this, void 0, void 0, function* () {
|
|
362
652
|
const device = yield this.findDevice(path);
|
|
363
|
-
|
|
653
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
654
|
+
const ifaceNum = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _a !== void 0 ? _a : this.interfaceId;
|
|
655
|
+
yield device.releaseInterface(ifaceNum);
|
|
364
656
|
yield device.close();
|
|
657
|
+
this.protocolV2Assemblers.delete(path);
|
|
658
|
+
this.deviceEndpoints.delete(path);
|
|
365
659
|
});
|
|
366
660
|
}
|
|
661
|
+
getProtocolType(path) {
|
|
662
|
+
var _a;
|
|
663
|
+
return (_a = this.deviceProtocol.get(path)) !== null && _a !== void 0 ? _a : 'V1';
|
|
664
|
+
}
|
|
367
665
|
}
|
|
368
666
|
|
|
369
|
-
const { parseConfigure
|
|
667
|
+
const { parseConfigure: parseConfigure$1, ProtocolV1: ProtocolV1$1, check: check$1 } = transport__default["default"];
|
|
370
668
|
class ElectronBleTransport {
|
|
371
669
|
constructor() {
|
|
372
670
|
this.name = 'ElectronBleTransport';
|
|
@@ -377,6 +675,9 @@ class ElectronBleTransport {
|
|
|
377
675
|
this.notificationCleanups = new Map();
|
|
378
676
|
this.disconnectCleanups = new Map();
|
|
379
677
|
}
|
|
678
|
+
getProtocolType(_path) {
|
|
679
|
+
return 'V1';
|
|
680
|
+
}
|
|
380
681
|
handleBluetoothError(error) {
|
|
381
682
|
if (error && typeof error === 'object') {
|
|
382
683
|
if ('code' in error) {
|
|
@@ -430,7 +731,7 @@ class ElectronBleTransport {
|
|
|
430
731
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Transport] Noble BLE Transport initialized');
|
|
431
732
|
}
|
|
432
733
|
configure(signedData) {
|
|
433
|
-
const messages = parseConfigure(signedData);
|
|
734
|
+
const messages = parseConfigure$1(signedData);
|
|
434
735
|
this.configured = true;
|
|
435
736
|
this._messages = messages;
|
|
436
737
|
}
|
|
@@ -579,7 +880,7 @@ class ElectronBleTransport {
|
|
|
579
880
|
else {
|
|
580
881
|
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[Transport] Noble BLE call', 'name:', name, 'data:', data);
|
|
581
882
|
}
|
|
582
|
-
const buffers =
|
|
883
|
+
const buffers = ProtocolV1$1.encodeTransportPackets(messages, name, data);
|
|
583
884
|
try {
|
|
584
885
|
if (!((_d = window.desktopApi) === null || _d === void 0 ? void 0 : _d.nobleBle)) {
|
|
585
886
|
throw new Error('Noble BLE write API not available');
|
|
@@ -601,8 +902,8 @@ class ElectronBleTransport {
|
|
|
601
902
|
if (typeof response !== 'string') {
|
|
602
903
|
throw new Error('Returning data is not string.');
|
|
603
904
|
}
|
|
604
|
-
const jsonData =
|
|
605
|
-
return check.call(jsonData);
|
|
905
|
+
const jsonData = ProtocolV1$1.decodeMessage(messages, response);
|
|
906
|
+
return check$1.call(jsonData);
|
|
606
907
|
}
|
|
607
908
|
catch (e) {
|
|
608
909
|
(_g = this.Log) === null || _g === void 0 ? void 0 : _g.error('[Transport] Noble BLE call error:', e);
|
|
@@ -639,7 +940,624 @@ class ElectronBleTransport {
|
|
|
639
940
|
else {
|
|
640
941
|
bufferState.buffer = bufferState.buffer.concat([...data]);
|
|
641
942
|
}
|
|
642
|
-
if (bufferState.buffer.length - transport.
|
|
943
|
+
if (bufferState.buffer.length - transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferState.bufferLength) {
|
|
944
|
+
const completeBuffer = new Uint8Array(bufferState.buffer);
|
|
945
|
+
bufferState.bufferLength = 0;
|
|
946
|
+
bufferState.buffer = [];
|
|
947
|
+
const hexString = Array.from(completeBuffer)
|
|
948
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
949
|
+
.join('');
|
|
950
|
+
return { isComplete: true, completePacket: hexString };
|
|
951
|
+
}
|
|
952
|
+
return { isComplete: false };
|
|
953
|
+
}
|
|
954
|
+
catch (error) {
|
|
955
|
+
return { isComplete: false, error: `Packet processing error: ${error}` };
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
const FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
961
|
+
function shouldSuppressHighVolumeCallLog(name) {
|
|
962
|
+
const normalized = name.replace(/[_\s-]/g, '');
|
|
963
|
+
return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
964
|
+
}
|
|
965
|
+
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
966
|
+
function inferProtocolTypeFromDeviceName(name) {
|
|
967
|
+
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
968
|
+
}
|
|
969
|
+
const toBleDescriptor = (device, protocolType) => {
|
|
970
|
+
const resolvedProtocolType = protocolType !== null && protocolType !== void 0 ? protocolType : inferProtocolTypeFromDeviceName(device.name);
|
|
971
|
+
return Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (resolvedProtocolType ? { protocolType: resolvedProtocolType } : {}));
|
|
972
|
+
};
|
|
973
|
+
const BLE_PACKET_SIZE = 192;
|
|
974
|
+
const BLE_WRITE_DELAY_MS = 5;
|
|
975
|
+
const BLE_WRITE_MAX_RETRIES = 3;
|
|
976
|
+
const BLE_WRITE_RETRY_DELAY_MS = 300;
|
|
977
|
+
const BLE_RESPONSE_TIMEOUT_MS = 30000;
|
|
978
|
+
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
979
|
+
class ElectronAutoBleTransport {
|
|
980
|
+
constructor() {
|
|
981
|
+
this.name = 'ElectronAutoBleTransport';
|
|
982
|
+
this.configured = false;
|
|
983
|
+
this.runPromise = null;
|
|
984
|
+
this.connectedDevices = new Set();
|
|
985
|
+
this.deviceProtocol = new Map();
|
|
986
|
+
this.v1Buffers = new Map();
|
|
987
|
+
this.v2Assemblers = new Map();
|
|
988
|
+
this.v2FrameQueue = [];
|
|
989
|
+
this.v2FramePromise = null;
|
|
990
|
+
this.notificationCleanups = new Map();
|
|
991
|
+
this.disconnectCleanups = new Map();
|
|
992
|
+
}
|
|
993
|
+
handleBluetoothError(error) {
|
|
994
|
+
if (error && typeof error === 'object') {
|
|
995
|
+
if ('code' in error) {
|
|
996
|
+
if (error.code === hdShared.HardwareErrorCode.BlePoweredOff) {
|
|
997
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePoweredOff);
|
|
998
|
+
}
|
|
999
|
+
if (error.code === hdShared.HardwareErrorCode.BleUnsupported) {
|
|
1000
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleUnsupported);
|
|
1001
|
+
}
|
|
1002
|
+
if (error.code === hdShared.HardwareErrorCode.BlePermissionError) {
|
|
1003
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
const errorMessage = error.message || String(error);
|
|
1007
|
+
const poweredOffMessage = hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BlePoweredOff];
|
|
1008
|
+
const unsupportedMessage = hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleUnsupported];
|
|
1009
|
+
const permissionMessage = hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BlePermissionError];
|
|
1010
|
+
if (errorMessage.includes(poweredOffMessage) || errorMessage.includes('poweredOff')) {
|
|
1011
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePoweredOff);
|
|
1012
|
+
}
|
|
1013
|
+
if (errorMessage.includes(unsupportedMessage) || errorMessage.includes('unsupported')) {
|
|
1014
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleUnsupported);
|
|
1015
|
+
}
|
|
1016
|
+
if (errorMessage.includes(permissionMessage) || errorMessage.includes('unauthorized')) {
|
|
1017
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
throw error;
|
|
1021
|
+
}
|
|
1022
|
+
cleanupDeviceState(deviceId) {
|
|
1023
|
+
this.connectedDevices.delete(deviceId);
|
|
1024
|
+
this.deviceProtocol.delete(deviceId);
|
|
1025
|
+
this.v1Buffers.delete(deviceId);
|
|
1026
|
+
this.v2Assemblers.delete(deviceId);
|
|
1027
|
+
const notifyCleanup = this.notificationCleanups.get(deviceId);
|
|
1028
|
+
if (notifyCleanup) {
|
|
1029
|
+
notifyCleanup();
|
|
1030
|
+
this.notificationCleanups.delete(deviceId);
|
|
1031
|
+
}
|
|
1032
|
+
const disconnectCleanup = this.disconnectCleanups.get(deviceId);
|
|
1033
|
+
if (disconnectCleanup) {
|
|
1034
|
+
disconnectCleanup();
|
|
1035
|
+
this.disconnectCleanups.delete(deviceId);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
init(logger, emitter) {
|
|
1039
|
+
var _a, _b;
|
|
1040
|
+
this.Log = logger;
|
|
1041
|
+
this.emitter = emitter;
|
|
1042
|
+
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
1043
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble BLE API is not available. Please ensure you are running in Electron with Noble support.');
|
|
1044
|
+
}
|
|
1045
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Auto BLE] Transport initialized');
|
|
1046
|
+
}
|
|
1047
|
+
configure(signedData) {
|
|
1048
|
+
this._messages = parseConfigure(signedData);
|
|
1049
|
+
this.configured = true;
|
|
1050
|
+
}
|
|
1051
|
+
configureProtocolV2(signedData) {
|
|
1052
|
+
var _a;
|
|
1053
|
+
this._messagesV2 = parseConfigure(signedData);
|
|
1054
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Auto BLE] Protocol V2 schema configured');
|
|
1055
|
+
}
|
|
1056
|
+
listen() {
|
|
1057
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1058
|
+
return this.enumerate();
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
enumerate() {
|
|
1062
|
+
var _a, _b, _c, _d;
|
|
1063
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1064
|
+
try {
|
|
1065
|
+
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
1066
|
+
throw new Error('Noble BLE API not available');
|
|
1067
|
+
}
|
|
1068
|
+
const devices = yield window.desktopApi.nobleBle.enumerate();
|
|
1069
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Auto BLE] enumerate found ${devices.length} device(s):`);
|
|
1070
|
+
for (const dev of devices) {
|
|
1071
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Auto BLE] id="${dev.id}" name="${dev.name}"`);
|
|
1072
|
+
}
|
|
1073
|
+
return devices.map(device => toBleDescriptor(device));
|
|
1074
|
+
}
|
|
1075
|
+
catch (error) {
|
|
1076
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Auto BLE] enumerate failed:', error);
|
|
1077
|
+
this.handleBluetoothError(error);
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
acquire(input) {
|
|
1082
|
+
var _a, _b, _c, _d, _e;
|
|
1083
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1084
|
+
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
1085
|
+
if (!uuid) {
|
|
1086
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
1087
|
+
}
|
|
1088
|
+
if (forceCleanRunPromise && this.runPromise) {
|
|
1089
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1090
|
+
this.runPromise.reject(error);
|
|
1091
|
+
this.rejectProtocolV2Frame(error);
|
|
1092
|
+
}
|
|
1093
|
+
try {
|
|
1094
|
+
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
1095
|
+
throw new Error('Noble BLE API not available');
|
|
1096
|
+
}
|
|
1097
|
+
const device = yield window.desktopApi.nobleBle.getDevice(uuid);
|
|
1098
|
+
if (!device) {
|
|
1099
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${uuid} not found`);
|
|
1100
|
+
}
|
|
1101
|
+
try {
|
|
1102
|
+
yield window.desktopApi.nobleBle.connect(uuid);
|
|
1103
|
+
this.connectedDevices.add(uuid);
|
|
1104
|
+
}
|
|
1105
|
+
catch (error) {
|
|
1106
|
+
this.handleBluetoothError(error);
|
|
1107
|
+
}
|
|
1108
|
+
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
1109
|
+
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
|
|
1110
|
+
yield window.desktopApi.nobleBle.subscribe(uuid);
|
|
1111
|
+
const cleanup = window.desktopApi.nobleBle.onNotification((deviceId, data) => {
|
|
1112
|
+
if (deviceId === uuid) {
|
|
1113
|
+
this.handleNotification(uuid, data);
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
this.notificationCleanups.set(uuid, cleanup);
|
|
1117
|
+
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
|
|
1118
|
+
var _a;
|
|
1119
|
+
if (disconnectedDevice.id === uuid) {
|
|
1120
|
+
this.cleanupDeviceState(uuid);
|
|
1121
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
|
|
1122
|
+
name: disconnectedDevice.name,
|
|
1123
|
+
id: disconnectedDevice.id,
|
|
1124
|
+
connectId: disconnectedDevice.id,
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
this.disconnectCleanups.set(uuid, disconnectCleanup);
|
|
1129
|
+
const protocolType = yield this.detectProtocol(uuid, expectedProtocol);
|
|
1130
|
+
(_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
|
|
1131
|
+
name: device.name,
|
|
1132
|
+
id: device.id,
|
|
1133
|
+
connectId: device.id,
|
|
1134
|
+
});
|
|
1135
|
+
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
1136
|
+
}
|
|
1137
|
+
catch (error) {
|
|
1138
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Auto BLE] acquire failed:', error);
|
|
1139
|
+
try {
|
|
1140
|
+
if (((_d = window.desktopApi) === null || _d === void 0 ? void 0 : _d.nobleBle) && this.connectedDevices.has(uuid)) {
|
|
1141
|
+
yield window.desktopApi.nobleBle.unsubscribe(uuid);
|
|
1142
|
+
yield window.desktopApi.nobleBle.disconnect(uuid);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
catch (cleanupError) {
|
|
1146
|
+
(_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug('[Auto BLE] acquire cleanup failed:', cleanupError);
|
|
1147
|
+
}
|
|
1148
|
+
this.cleanupDeviceState(uuid);
|
|
1149
|
+
throw error;
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
release(id) {
|
|
1154
|
+
var _a, _b;
|
|
1155
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1156
|
+
try {
|
|
1157
|
+
if (this.connectedDevices.has(id)) {
|
|
1158
|
+
if ((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) {
|
|
1159
|
+
yield window.desktopApi.nobleBle.unsubscribe(id);
|
|
1160
|
+
yield window.desktopApi.nobleBle.disconnect(id);
|
|
1161
|
+
}
|
|
1162
|
+
this.cleanupDeviceState(id);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
catch (error) {
|
|
1166
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Auto BLE] release failed:', error);
|
|
1167
|
+
this.cleanupDeviceState(id);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
createProtocolMismatchError(expected) {
|
|
1172
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
1173
|
+
}
|
|
1174
|
+
detectProtocol(uuid, expectedProtocol) {
|
|
1175
|
+
var _a, _b, _c, _d;
|
|
1176
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1177
|
+
if (expectedProtocol === 'V1') {
|
|
1178
|
+
if (yield this.probeProtocolV1(uuid)) {
|
|
1179
|
+
this.deviceProtocol.set(uuid, 'V1');
|
|
1180
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V1 (expected)`);
|
|
1181
|
+
return 'V1';
|
|
1182
|
+
}
|
|
1183
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1184
|
+
}
|
|
1185
|
+
if (expectedProtocol === 'V2') {
|
|
1186
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1187
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1188
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
1189
|
+
return 'V2';
|
|
1190
|
+
}
|
|
1191
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1192
|
+
}
|
|
1193
|
+
if (this.deviceProtocol.get(uuid) === 'V2' && (yield this.probeProtocolV2(uuid))) {
|
|
1194
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1195
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (cached)`);
|
|
1196
|
+
return 'V2';
|
|
1197
|
+
}
|
|
1198
|
+
let protocol = 'V1';
|
|
1199
|
+
if (!(yield this.probeProtocolV1(uuid)) && (yield this.probeProtocolV2(uuid))) {
|
|
1200
|
+
protocol = 'V2';
|
|
1201
|
+
}
|
|
1202
|
+
this.deviceProtocol.set(uuid, protocol);
|
|
1203
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
|
|
1204
|
+
return protocol;
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
probeProtocolV1(uuid) {
|
|
1208
|
+
var _a;
|
|
1209
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1210
|
+
if (!this._messages) {
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
try {
|
|
1214
|
+
this.deviceProtocol.set(uuid, 'V1');
|
|
1215
|
+
yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1216
|
+
return true;
|
|
1217
|
+
}
|
|
1218
|
+
catch (error) {
|
|
1219
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Auto BLE] Protocol V1 Initialize probe failed:', error);
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
probeProtocolV2(uuid) {
|
|
1225
|
+
var _a;
|
|
1226
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1227
|
+
if (!this._messages || !this._messagesV2) {
|
|
1228
|
+
return false;
|
|
1229
|
+
}
|
|
1230
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1231
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1232
|
+
return transport.probeProtocolV2({
|
|
1233
|
+
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
1234
|
+
timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS,
|
|
1235
|
+
logger: this.Log,
|
|
1236
|
+
logPrefix: 'ProtocolV2 BLE',
|
|
1237
|
+
onProbeFailed: () => {
|
|
1238
|
+
var _a;
|
|
1239
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1240
|
+
this.resetProtocolV2Frames();
|
|
1241
|
+
},
|
|
1242
|
+
});
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
writeWithChunking(uuid, hexData) {
|
|
1246
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1247
|
+
const totalBytes = hexData.length / 2;
|
|
1248
|
+
if (totalBytes <= BLE_PACKET_SIZE) {
|
|
1249
|
+
yield hdShared.wait(BLE_WRITE_DELAY_MS);
|
|
1250
|
+
yield this.writeWithRetry(uuid, hexData);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
for (let offset = 0; offset < hexData.length;) {
|
|
1254
|
+
const chunkHexLen = Math.min(BLE_PACKET_SIZE * 2, hexData.length - offset);
|
|
1255
|
+
const chunkHex = hexData.substring(offset, offset + chunkHexLen);
|
|
1256
|
+
offset += chunkHexLen;
|
|
1257
|
+
yield this.writeWithRetry(uuid, chunkHex);
|
|
1258
|
+
if (offset < hexData.length) {
|
|
1259
|
+
yield hdShared.wait(BLE_WRITE_DELAY_MS);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
writeWithRetry(uuid, hexData) {
|
|
1265
|
+
var _a, _b, _c;
|
|
1266
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1267
|
+
let lastError;
|
|
1268
|
+
const nobleBle = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle;
|
|
1269
|
+
if (!nobleBle) {
|
|
1270
|
+
throw new Error('Noble BLE API not available');
|
|
1271
|
+
}
|
|
1272
|
+
for (let attempt = 1; attempt <= BLE_WRITE_MAX_RETRIES; attempt++) {
|
|
1273
|
+
try {
|
|
1274
|
+
yield nobleBle.write(uuid, hexData);
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
catch (error) {
|
|
1278
|
+
lastError = error;
|
|
1279
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error(`[Auto BLE] write failed (attempt ${attempt}/${BLE_WRITE_MAX_RETRIES}):`, error);
|
|
1280
|
+
if (attempt < BLE_WRITE_MAX_RETRIES) {
|
|
1281
|
+
yield hdShared.wait(BLE_WRITE_RETRY_DELAY_MS);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write failed after ${BLE_WRITE_MAX_RETRIES} attempts: ${(_c = lastError === null || lastError === void 0 ? void 0 : lastError.message) !== null && _c !== void 0 ? _c : lastError}`);
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
handleNotification(deviceId, hexData) {
|
|
1289
|
+
var _a, _b;
|
|
1290
|
+
if (hexData === 'PAIRING_REJECTED') {
|
|
1291
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Auto BLE] Pairing rejection detected for device:', deviceId);
|
|
1292
|
+
if (this.runPromise) {
|
|
1293
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
|
|
1294
|
+
this.runPromise.reject(error);
|
|
1295
|
+
this.rejectProtocolV2Frame(error);
|
|
1296
|
+
}
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
const protocol = (_b = this.deviceProtocol.get(deviceId)) !== null && _b !== void 0 ? _b : 'V1';
|
|
1300
|
+
if (protocol === 'V2') {
|
|
1301
|
+
this.handleProtocolV2Notification(deviceId, hexData);
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
this.handleProtocolV1Notification(deviceId, hexData);
|
|
1305
|
+
}
|
|
1306
|
+
handleProtocolV2Notification(deviceId, hexData) {
|
|
1307
|
+
var _a, _b;
|
|
1308
|
+
try {
|
|
1309
|
+
if (!this.runPromise) {
|
|
1310
|
+
(_a = this.v2Assemblers.get(deviceId)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1311
|
+
this.resetProtocolV2Frames();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const bytes = transport.hexToBytes(hexData);
|
|
1315
|
+
if (bytes.length === 0)
|
|
1316
|
+
return;
|
|
1317
|
+
const assembler = this.v2Assemblers.get(deviceId);
|
|
1318
|
+
if (!assembler)
|
|
1319
|
+
return;
|
|
1320
|
+
let frameData = assembler.push(bytes);
|
|
1321
|
+
while (frameData) {
|
|
1322
|
+
this.resolveProtocolV2Frame(frameData);
|
|
1323
|
+
frameData = assembler.push(new Uint8Array(0));
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
catch (error) {
|
|
1327
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Auto BLE] Protocol V2 notification error:', error);
|
|
1328
|
+
if (this.runPromise) {
|
|
1329
|
+
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1330
|
+
this.runPromise.reject(notifyError);
|
|
1331
|
+
this.rejectProtocolV2Frame(notifyError);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
resolveProtocolV2Frame(frame) {
|
|
1336
|
+
if (this.v2FramePromise) {
|
|
1337
|
+
this.v2FramePromise.resolve(frame);
|
|
1338
|
+
this.v2FramePromise = null;
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
this.v2FrameQueue.push(frame);
|
|
1342
|
+
}
|
|
1343
|
+
rejectProtocolV2Frame(error) {
|
|
1344
|
+
this.v2FrameQueue = [];
|
|
1345
|
+
if (this.v2FramePromise) {
|
|
1346
|
+
this.v2FramePromise.reject(error);
|
|
1347
|
+
this.v2FramePromise = null;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
resetProtocolV2Frames() {
|
|
1351
|
+
this.v2FrameQueue = [];
|
|
1352
|
+
this.v2FramePromise = null;
|
|
1353
|
+
}
|
|
1354
|
+
readProtocolV2Frame() {
|
|
1355
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1356
|
+
const queuedFrame = this.v2FrameQueue.shift();
|
|
1357
|
+
if (queuedFrame) {
|
|
1358
|
+
return queuedFrame;
|
|
1359
|
+
}
|
|
1360
|
+
const framePromise = hdShared.createDeferred();
|
|
1361
|
+
this.v2FramePromise = framePromise;
|
|
1362
|
+
try {
|
|
1363
|
+
return yield framePromise.promise;
|
|
1364
|
+
}
|
|
1365
|
+
finally {
|
|
1366
|
+
if (this.v2FramePromise === framePromise) {
|
|
1367
|
+
this.v2FramePromise = null;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
handleProtocolV1Notification(deviceId, hexData) {
|
|
1373
|
+
var _a;
|
|
1374
|
+
const result = this.processProtocolV1Notification(deviceId, hexData);
|
|
1375
|
+
if (result.error) {
|
|
1376
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Auto BLE] Protocol V1 packet processing error:', result.error);
|
|
1377
|
+
if (this.runPromise) {
|
|
1378
|
+
this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError));
|
|
1379
|
+
}
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
if (result.isComplete && result.completePacket && this.runPromise) {
|
|
1383
|
+
this.runPromise.resolve(result.completePacket);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
call(uuid, name, data, options) {
|
|
1387
|
+
var _a, _b, _c;
|
|
1388
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1389
|
+
if (!this._messages) {
|
|
1390
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1391
|
+
}
|
|
1392
|
+
if (!this.connectedDevices.has(uuid)) {
|
|
1393
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound, `Device ${uuid} not connected`);
|
|
1394
|
+
}
|
|
1395
|
+
const protocol = (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : 'V1';
|
|
1396
|
+
if (shouldSuppressHighVolumeCallLog(name)) ;
|
|
1397
|
+
else if (transport.LogBlockCommand.has(name)) {
|
|
1398
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Auto BLE] call', 'name:', name, 'protocol:', protocol);
|
|
1399
|
+
}
|
|
1400
|
+
else {
|
|
1401
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[Auto BLE] call', 'name:', name, 'data:', data, 'protocol:', protocol);
|
|
1402
|
+
}
|
|
1403
|
+
if (protocol === 'V2') {
|
|
1404
|
+
return this.callProtocolV2(uuid, name, data, options);
|
|
1405
|
+
}
|
|
1406
|
+
return this.callProtocolV1(uuid, name, data, options);
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
callProtocolV1(uuid, name, data, options) {
|
|
1410
|
+
var _a, _b;
|
|
1411
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1412
|
+
if (!this._messages) {
|
|
1413
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1414
|
+
}
|
|
1415
|
+
const forceRun = name === 'Initialize' || name === 'Cancel';
|
|
1416
|
+
if (this.runPromise && !forceRun) {
|
|
1417
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
1418
|
+
}
|
|
1419
|
+
const runPromise = hdShared.createDeferred();
|
|
1420
|
+
runPromise.promise.catch(() => undefined);
|
|
1421
|
+
this.runPromise = runPromise;
|
|
1422
|
+
const messages = this._messages;
|
|
1423
|
+
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
1424
|
+
let timeout;
|
|
1425
|
+
try {
|
|
1426
|
+
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
1427
|
+
throw new Error('Noble BLE write API not available');
|
|
1428
|
+
}
|
|
1429
|
+
for (let i = 0; i < buffers.length; i++) {
|
|
1430
|
+
const buffer = buffers[i];
|
|
1431
|
+
if (!buffer || typeof buffer.toString !== 'function') {
|
|
1432
|
+
throw new Error(`Buffer ${i + 1} is invalid`);
|
|
1433
|
+
}
|
|
1434
|
+
const hexString = buffer.toString('hex');
|
|
1435
|
+
if (hexString.length === 0) {
|
|
1436
|
+
throw new Error(`Buffer ${i + 1} is empty`);
|
|
1437
|
+
}
|
|
1438
|
+
yield window.desktopApi.nobleBle.write(uuid, hexString);
|
|
1439
|
+
}
|
|
1440
|
+
const response = yield Promise.race([
|
|
1441
|
+
runPromise.promise,
|
|
1442
|
+
new Promise((_, reject) => {
|
|
1443
|
+
if (options === null || options === void 0 ? void 0 : options.timeoutMs) {
|
|
1444
|
+
timeout = setTimeout(() => {
|
|
1445
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${options.timeoutMs}ms for ${name}`);
|
|
1446
|
+
runPromise.reject(error);
|
|
1447
|
+
reject(error);
|
|
1448
|
+
}, options.timeoutMs);
|
|
1449
|
+
}
|
|
1450
|
+
}),
|
|
1451
|
+
]);
|
|
1452
|
+
if (typeof response !== 'string') {
|
|
1453
|
+
throw new Error('Returning data is not string.');
|
|
1454
|
+
}
|
|
1455
|
+
const jsonData = ProtocolV1.decodeMessage(messages, response);
|
|
1456
|
+
return check.call(jsonData);
|
|
1457
|
+
}
|
|
1458
|
+
catch (e) {
|
|
1459
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Auto BLE] Protocol V1 call error:', e);
|
|
1460
|
+
throw e;
|
|
1461
|
+
}
|
|
1462
|
+
finally {
|
|
1463
|
+
if (timeout)
|
|
1464
|
+
clearTimeout(timeout);
|
|
1465
|
+
if (this.runPromise === runPromise) {
|
|
1466
|
+
this.runPromise = null;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
callProtocolV2(uuid, name, data, options) {
|
|
1472
|
+
var _a, _b, _c, _d, _e;
|
|
1473
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1474
|
+
if (!this._messages || !this._messagesV2) {
|
|
1475
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1476
|
+
}
|
|
1477
|
+
const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'GetProtoVersion';
|
|
1478
|
+
if (this.runPromise) {
|
|
1479
|
+
if (!forceRun) {
|
|
1480
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
1481
|
+
}
|
|
1482
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1483
|
+
this.runPromise.reject(error);
|
|
1484
|
+
this.rejectProtocolV2Frame(error);
|
|
1485
|
+
this.runPromise = null;
|
|
1486
|
+
}
|
|
1487
|
+
const runPromise = hdShared.createDeferred();
|
|
1488
|
+
runPromise.promise.catch(() => undefined);
|
|
1489
|
+
this.runPromise = runPromise;
|
|
1490
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1491
|
+
this.resetProtocolV2Frames();
|
|
1492
|
+
let completed = false;
|
|
1493
|
+
const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : BLE_RESPONSE_TIMEOUT_MS });
|
|
1494
|
+
try {
|
|
1495
|
+
const session = new transport.ProtocolV2Session({
|
|
1496
|
+
schemas: {
|
|
1497
|
+
protocolV1: this._messages,
|
|
1498
|
+
protocolV2: this._messagesV2,
|
|
1499
|
+
},
|
|
1500
|
+
router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1501
|
+
writeFrame: (frame) => this.writeWithChunking(uuid, transport.bytesToHex(frame)),
|
|
1502
|
+
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1503
|
+
const rxFrame = yield this.readProtocolV2Frame();
|
|
1504
|
+
if (!(rxFrame instanceof Uint8Array)) {
|
|
1505
|
+
throw new Error('Response is not Uint8Array');
|
|
1506
|
+
}
|
|
1507
|
+
return rxFrame;
|
|
1508
|
+
}),
|
|
1509
|
+
logger: this.Log,
|
|
1510
|
+
logPrefix: 'ProtocolV2 BLE',
|
|
1511
|
+
createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
|
|
1512
|
+
});
|
|
1513
|
+
const result = yield session.call(name, data, callOptions);
|
|
1514
|
+
completed = true;
|
|
1515
|
+
return result;
|
|
1516
|
+
}
|
|
1517
|
+
catch (e) {
|
|
1518
|
+
(_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
|
|
1519
|
+
this.resetProtocolV2Frames();
|
|
1520
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Auto BLE] Protocol V2 call error:', e);
|
|
1521
|
+
throw e;
|
|
1522
|
+
}
|
|
1523
|
+
finally {
|
|
1524
|
+
if (!completed) {
|
|
1525
|
+
(_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
|
|
1526
|
+
}
|
|
1527
|
+
this.resetProtocolV2Frames();
|
|
1528
|
+
if (this.runPromise === runPromise) {
|
|
1529
|
+
this.runPromise = null;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
processProtocolV1Notification(deviceId, hexData) {
|
|
1535
|
+
try {
|
|
1536
|
+
if (typeof hexData !== 'string') {
|
|
1537
|
+
return { isComplete: false, error: 'Invalid hexData type' };
|
|
1538
|
+
}
|
|
1539
|
+
const cleanHexData = hexData.replace(/\s+/g, '');
|
|
1540
|
+
if (!/^[0-9A-Fa-f]*$/.test(cleanHexData)) {
|
|
1541
|
+
return { isComplete: false, error: 'Invalid hex data format' };
|
|
1542
|
+
}
|
|
1543
|
+
const hexMatch = cleanHexData.match(/.{1,2}/g);
|
|
1544
|
+
if (!hexMatch) {
|
|
1545
|
+
return { isComplete: false, error: 'Failed to parse hex data' };
|
|
1546
|
+
}
|
|
1547
|
+
const data = new Uint8Array(hexMatch.map(byte => parseInt(byte, 16)));
|
|
1548
|
+
const bufferState = this.v1Buffers.get(deviceId);
|
|
1549
|
+
if (!bufferState) {
|
|
1550
|
+
return { isComplete: false, error: 'No buffer state for device' };
|
|
1551
|
+
}
|
|
1552
|
+
if (hdShared.isHeaderChunk(data)) {
|
|
1553
|
+
const dataView = new DataView(data.buffer);
|
|
1554
|
+
bufferState.bufferLength = dataView.getInt32(5, false);
|
|
1555
|
+
bufferState.buffer = [...data.subarray(3)];
|
|
1556
|
+
}
|
|
1557
|
+
else {
|
|
1558
|
+
bufferState.buffer = bufferState.buffer.concat([...data]);
|
|
1559
|
+
}
|
|
1560
|
+
if (bufferState.buffer.length - transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferState.bufferLength) {
|
|
643
1561
|
const completeBuffer = new Uint8Array(bufferState.buffer);
|
|
644
1562
|
bufferState.bufferLength = 0;
|
|
645
1563
|
bufferState.buffer = [];
|
|
@@ -654,7 +1572,12 @@ class ElectronBleTransport {
|
|
|
654
1572
|
return { isComplete: false, error: `Packet processing error: ${error}` };
|
|
655
1573
|
}
|
|
656
1574
|
}
|
|
1575
|
+
getProtocolType(path) {
|
|
1576
|
+
var _a;
|
|
1577
|
+
return (_a = this.deviceProtocol.get(path)) !== null && _a !== void 0 ? _a : 'V1';
|
|
1578
|
+
}
|
|
657
1579
|
}
|
|
658
1580
|
|
|
1581
|
+
exports.ElectronAutoBleTransport = ElectronAutoBleTransport;
|
|
659
1582
|
exports.ElectronBleTransport = ElectronBleTransport;
|
|
660
1583
|
exports.WebUsbTransport = WebUsbTransport;
|