@onekeyfe/hd-transport-web-device 1.2.0-alpha.15 → 1.2.0-alpha.151
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/__tests__/electron-ble-transport.test.ts +439 -27
- package/__tests__/webusb-protocol-cache.test.ts +244 -0
- package/__tests__/webusb-protocol-v2-timeout.test.ts +363 -11
- package/dist/ble-packet-capacity.d.ts +2 -0
- package/dist/ble-packet-capacity.d.ts.map +1 -0
- package/dist/electron-ble-transport.d.ts +17 -3
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +146 -12
- package/dist/index.js +428 -281
- package/dist/transportLog.d.ts +1 -6
- package/dist/transportLog.d.ts.map +1 -1
- package/dist/webusb.d.ts +20 -10
- package/dist/webusb.d.ts.map +1 -1
- package/jest.config.js +5 -0
- package/package.json +6 -5
- package/src/ble-packet-capacity.ts +13 -0
- package/src/electron-ble-transport.ts +252 -115
- package/src/transportLog.ts +1 -11
- package/src/webusb.ts +305 -204
package/dist/index.js
CHANGED
|
@@ -41,14 +41,6 @@ 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 HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
|
|
45
|
-
function shouldSuppressHighVolumeCallLog(name) {
|
|
46
|
-
return HIGH_VOLUME_CALLS.has(name);
|
|
47
|
-
}
|
|
48
|
-
function createTransportCallLog(name, protocol) {
|
|
49
|
-
return { name, protocol };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
44
|
const { parseConfigure: parseConfigure$1, check: check$1, ProtocolV1: ProtocolV1$1 } = transport__default["default"];
|
|
53
45
|
const CONFIGURATION_ID = 1;
|
|
54
46
|
const INTERFACE_ID = 0;
|
|
@@ -59,20 +51,38 @@ const REPORT_ID = transport.PROTOCOL_V1_REPORT_ID;
|
|
|
59
51
|
const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
60
52
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
61
53
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
const PROTOCOL_V1_PROBE_TIMEOUT = 5000;
|
|
55
|
+
const PROTOCOL_V2_PROBE_TIMEOUT = 1000;
|
|
56
|
+
const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
|
|
57
|
+
let activeWebUsbTransport;
|
|
58
|
+
let usbDisconnectListenerAttached = false;
|
|
59
|
+
function registerActiveWebUsbTransport(instance, usb) {
|
|
60
|
+
activeWebUsbTransport = instance;
|
|
61
|
+
if (usbDisconnectListenerAttached)
|
|
62
|
+
return;
|
|
63
|
+
usbDisconnectListenerAttached = true;
|
|
64
|
+
usb.addEventListener('disconnect', event => {
|
|
65
|
+
if (!event.device)
|
|
66
|
+
return;
|
|
67
|
+
const path = hdShared.resolveOneKeyUsbDevicePath(event.device);
|
|
68
|
+
if (!path)
|
|
69
|
+
return;
|
|
70
|
+
activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.markProtocolStale(path);
|
|
71
|
+
});
|
|
65
72
|
}
|
|
66
|
-
class WebUsbTransport {
|
|
73
|
+
class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
67
74
|
constructor() {
|
|
75
|
+
super({
|
|
76
|
+
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
77
|
+
maxFrameBytes: transport.PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
78
|
+
logPrefix: 'ProtocolV2 WebUSB',
|
|
79
|
+
});
|
|
68
80
|
this.deviceProtocol = new Map();
|
|
69
81
|
this.deviceProtocolHints = new Map();
|
|
70
|
-
this.
|
|
71
|
-
this.
|
|
72
|
-
this.
|
|
82
|
+
this.staleProtocolPaths = new Set();
|
|
83
|
+
this.acquiredPaths = new Set();
|
|
84
|
+
this.probedDeviceObjects = new Map();
|
|
73
85
|
this.deviceEndpoints = new Map();
|
|
74
|
-
this.mockSerialPaths = new WeakMap();
|
|
75
|
-
this.mockSerialCounter = 0;
|
|
76
86
|
this.name = 'WebUsbTransport';
|
|
77
87
|
this.stopped = false;
|
|
78
88
|
this.configured = false;
|
|
@@ -88,6 +98,11 @@ class WebUsbTransport {
|
|
|
88
98
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'WebUSB is not supported by current browsers');
|
|
89
99
|
}
|
|
90
100
|
this.usb = usb;
|
|
101
|
+
registerActiveWebUsbTransport(this, usb);
|
|
102
|
+
}
|
|
103
|
+
markProtocolStale(path) {
|
|
104
|
+
this.staleProtocolPaths.add(path);
|
|
105
|
+
this.probedDeviceObjects.delete(path);
|
|
91
106
|
}
|
|
92
107
|
configure(signedData) {
|
|
93
108
|
const messages = parseConfigure$1(signedData);
|
|
@@ -95,9 +110,18 @@ class WebUsbTransport {
|
|
|
95
110
|
this.messages = messages;
|
|
96
111
|
}
|
|
97
112
|
configureProtocolV2(signedData) {
|
|
113
|
+
var _a;
|
|
114
|
+
const schemaSource = typeof signedData === 'string'
|
|
115
|
+
? signedData
|
|
116
|
+
: (_a = JSON.stringify(signedData)) !== null && _a !== void 0 ? _a : String(signedData);
|
|
117
|
+
if (schemaSource === this.protocolV2SchemaSource)
|
|
118
|
+
return;
|
|
119
|
+
const hadProtocolV2Schema = this.protocolV2SchemaSource !== undefined;
|
|
98
120
|
this.messagesV2 = parseConfigure$1(signedData);
|
|
99
|
-
this.
|
|
100
|
-
|
|
121
|
+
this.protocolV2SchemaSource = schemaSource;
|
|
122
|
+
if (!hadProtocolV2Schema)
|
|
123
|
+
return;
|
|
124
|
+
this.invalidateAllProtocolV2UsbLinks('Protocol V2 schema reconfigured').catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] schema link cleanup failed:', error); });
|
|
101
125
|
}
|
|
102
126
|
promptDeviceAccess() {
|
|
103
127
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -119,20 +143,6 @@ class WebUsbTransport {
|
|
|
119
143
|
return this.deviceList;
|
|
120
144
|
});
|
|
121
145
|
}
|
|
122
|
-
getDevicePath(device) {
|
|
123
|
-
var _a;
|
|
124
|
-
if (typeof device.serialNumber === 'string' && device.serialNumber.length > 0) {
|
|
125
|
-
return device.serialNumber;
|
|
126
|
-
}
|
|
127
|
-
let path = this.mockSerialPaths.get(device);
|
|
128
|
-
if (!path) {
|
|
129
|
-
this.mockSerialCounter += 1;
|
|
130
|
-
path = `mock-serial:${device.vendorId.toString(16)}:${device.productId.toString(16)}:${this.mockSerialCounter}`;
|
|
131
|
-
this.mockSerialPaths.set(device, path);
|
|
132
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUSB] device has no serial number, using mock path: ${path}`);
|
|
133
|
-
}
|
|
134
|
-
return path;
|
|
135
|
-
}
|
|
136
146
|
getConnectedDevices() {
|
|
137
147
|
return __awaiter(this, void 0, void 0, function* () {
|
|
138
148
|
if (!this.usb)
|
|
@@ -142,41 +152,74 @@ class WebUsbTransport {
|
|
|
142
152
|
const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
|
|
143
153
|
return isOneKey && !hdShared.isKnownTrezorWebUsbDevice(dev);
|
|
144
154
|
});
|
|
145
|
-
this.deviceList = onekeyDevices.
|
|
146
|
-
const path =
|
|
147
|
-
|
|
155
|
+
this.deviceList = onekeyDevices.flatMap(device => {
|
|
156
|
+
const path = hdShared.resolveOneKeyUsbDevicePath(device);
|
|
157
|
+
if (!path)
|
|
158
|
+
return [];
|
|
159
|
+
const protocolHint = hdShared.inferProtocolHintFromUsbId(device.vendorId, device.productId);
|
|
148
160
|
if (protocolHint) {
|
|
149
161
|
this.deviceProtocolHints.set(path, protocolHint);
|
|
150
162
|
}
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
163
|
+
return [
|
|
164
|
+
{
|
|
165
|
+
path,
|
|
166
|
+
device,
|
|
167
|
+
commType: 'webusb',
|
|
168
|
+
},
|
|
169
|
+
];
|
|
156
170
|
});
|
|
157
171
|
return this.deviceList;
|
|
158
172
|
});
|
|
159
173
|
}
|
|
160
174
|
acquire(input) {
|
|
161
|
-
var _a, _b, _c;
|
|
175
|
+
var _a, _b, _c, _d, _e, _f;
|
|
162
176
|
return __awaiter(this, void 0, void 0, function* () {
|
|
163
177
|
if (!input.path)
|
|
164
178
|
return;
|
|
165
179
|
try {
|
|
180
|
+
yield this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
|
|
166
181
|
yield this.closeOpenDevice(input.path);
|
|
167
182
|
yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
183
|
+
if (input.forceProtocolDetection) {
|
|
184
|
+
this.staleProtocolPaths.delete(input.path);
|
|
185
|
+
this.deviceProtocol.delete(input.path);
|
|
186
|
+
this.deviceProtocolHints.delete(input.path);
|
|
187
|
+
}
|
|
188
|
+
if (this.staleProtocolPaths.has(input.path)) {
|
|
189
|
+
this.staleProtocolPaths.delete(input.path);
|
|
190
|
+
this.deviceProtocol.delete(input.path);
|
|
191
|
+
}
|
|
192
|
+
if (this.deviceProtocol.has(input.path)) {
|
|
193
|
+
const currentDevice = (_b = this.deviceList.find(d => d.path === input.path)) === null || _b === void 0 ? void 0 : _b.device;
|
|
194
|
+
if (!currentDevice || currentDevice !== this.probedDeviceObjects.get(input.path)) {
|
|
195
|
+
this.deviceProtocol.delete(input.path);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const cachedProtocol = this.deviceProtocol.get(input.path);
|
|
199
|
+
if (cachedProtocol && input.expectedProtocol && cachedProtocol !== input.expectedProtocol) {
|
|
200
|
+
this.deviceProtocol.delete(input.path);
|
|
201
|
+
}
|
|
202
|
+
if (!this.deviceProtocol.has(input.path)) {
|
|
203
|
+
const usbDevice = (_c = this.deviceList.find(device => device.path === input.path)) === null || _c === void 0 ? void 0 : _c.device;
|
|
204
|
+
const protocolHint = input.expectedProtocol
|
|
205
|
+
? undefined
|
|
206
|
+
: (_e = (_d = input.protocolHint) !== null && _d !== void 0 ? _d : this.deviceProtocolHints.get(input.path)) !== null && _e !== void 0 ? _e : hdShared.inferProtocolHintFromUsbId(usbDevice === null || usbDevice === void 0 ? void 0 : usbDevice.vendorId, usbDevice === null || usbDevice === void 0 ? void 0 : usbDevice.productId);
|
|
207
|
+
if (protocolHint) {
|
|
208
|
+
this.deviceProtocolHints.set(input.path, protocolHint);
|
|
209
|
+
}
|
|
210
|
+
yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
|
|
211
|
+
const probedDevice = (_f = this.deviceList.find(d => d.path === input.path)) === null || _f === void 0 ? void 0 : _f.device;
|
|
212
|
+
if (probedDevice) {
|
|
213
|
+
this.probedDeviceObjects.set(input.path, probedDevice);
|
|
214
|
+
}
|
|
174
215
|
}
|
|
175
|
-
|
|
216
|
+
this.acquiredPaths.add(input.path);
|
|
176
217
|
return yield Promise.resolve(input.path);
|
|
177
218
|
}
|
|
178
219
|
catch (e) {
|
|
179
220
|
this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
|
|
221
|
+
this.acquiredPaths.delete(input.path);
|
|
222
|
+
yield this.closeOpenDevice(input.path);
|
|
180
223
|
throw e;
|
|
181
224
|
}
|
|
182
225
|
});
|
|
@@ -184,29 +227,53 @@ class WebUsbTransport {
|
|
|
184
227
|
createProtocolMismatchError(expected) {
|
|
185
228
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
186
229
|
}
|
|
230
|
+
createProtocolProbeTimeoutError(expected, attempts) {
|
|
231
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInitializeFailed, `Protocol ${expected} probe timeout after ${attempts} attempts`);
|
|
232
|
+
}
|
|
187
233
|
createProtocolDetectionError() {
|
|
188
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.
|
|
234
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
189
235
|
}
|
|
190
236
|
detectProtocol(path, expectedProtocol, protocolHint) {
|
|
237
|
+
var _a;
|
|
191
238
|
return __awaiter(this, void 0, void 0, function* () {
|
|
192
239
|
if (expectedProtocol === 'V1') {
|
|
193
240
|
if (yield this.probeProtocolV1(path)) {
|
|
194
241
|
this.deviceProtocol.set(path, 'V1');
|
|
195
242
|
return 'V1';
|
|
196
243
|
}
|
|
244
|
+
yield this.closeConnectionAfterProbe(path);
|
|
197
245
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
198
246
|
}
|
|
199
247
|
if (expectedProtocol === 'V2') {
|
|
200
|
-
|
|
201
|
-
|
|
248
|
+
for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
|
|
249
|
+
if (yield this.probeProtocolV2(path)) {
|
|
250
|
+
this.deviceProtocol.set(path, 'V2');
|
|
251
|
+
return 'V2';
|
|
252
|
+
}
|
|
253
|
+
if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
|
|
254
|
+
yield this.resetConnectionAfterProbe(path);
|
|
255
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 probe timed out, retrying ${attempt + 1}/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
yield this.closeConnectionAfterProbe(path);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
this.deviceProtocol.delete(path);
|
|
262
|
+
throw this.createProtocolProbeTimeoutError(expectedProtocol, EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS);
|
|
202
263
|
}
|
|
203
264
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
204
|
-
for (const protocol of probeOrder) {
|
|
265
|
+
for (const [index, protocol] of probeOrder.entries()) {
|
|
205
266
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(path) : yield this.probeProtocolV2(path);
|
|
206
267
|
if (detected) {
|
|
207
268
|
this.deviceProtocol.set(path, protocol);
|
|
208
269
|
return protocol;
|
|
209
270
|
}
|
|
271
|
+
if (index < probeOrder.length - 1) {
|
|
272
|
+
yield this.resetConnectionAfterProbe(path);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
yield this.closeConnectionAfterProbe(path);
|
|
276
|
+
}
|
|
210
277
|
}
|
|
211
278
|
this.deviceProtocol.delete(path);
|
|
212
279
|
throw this.createProtocolDetectionError();
|
|
@@ -273,7 +340,7 @@ class WebUsbTransport {
|
|
|
273
340
|
};
|
|
274
341
|
}
|
|
275
342
|
connectToDevice(path, first) {
|
|
276
|
-
var _a
|
|
343
|
+
var _a;
|
|
277
344
|
return __awaiter(this, void 0, void 0, function* () {
|
|
278
345
|
let device = yield this.findDevice(path);
|
|
279
346
|
if (!device.opened) {
|
|
@@ -297,33 +364,30 @@ class WebUsbTransport {
|
|
|
297
364
|
}
|
|
298
365
|
const endpoints = this.discoverEndpoints(device);
|
|
299
366
|
this.deviceEndpoints.set(path, endpoints);
|
|
300
|
-
(_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
301
|
-
this.protocolV2Assemblers.set(path, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES));
|
|
302
367
|
yield device.claimInterface(endpoints.interfaceNumber);
|
|
303
368
|
yield this.clearEndpointHalt(device, 'in', endpoints.endpointIn);
|
|
304
369
|
yield this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
305
370
|
});
|
|
306
371
|
}
|
|
307
372
|
closeOpenDevice(path) {
|
|
308
|
-
var _a, _b, _c, _d
|
|
373
|
+
var _a, _b, _c, _d;
|
|
309
374
|
return __awaiter(this, void 0, void 0, function* () {
|
|
310
|
-
(_a = this.
|
|
311
|
-
const current = (_b = this.deviceList.find(device => device.path === path)) === null || _b === void 0 ? void 0 : _b.device;
|
|
375
|
+
const current = (_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.device;
|
|
312
376
|
if (!(current === null || current === void 0 ? void 0 : current.opened))
|
|
313
377
|
return;
|
|
314
378
|
const endpoints = this.deviceEndpoints.get(path);
|
|
315
|
-
const ifaceNum = (
|
|
379
|
+
const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
|
|
316
380
|
try {
|
|
317
381
|
yield current.releaseInterface(ifaceNum);
|
|
318
382
|
}
|
|
319
383
|
catch (error) {
|
|
320
|
-
(
|
|
384
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[WebUsbTransport] releaseInterface before reconnect failed:', error);
|
|
321
385
|
}
|
|
322
386
|
try {
|
|
323
387
|
yield current.close();
|
|
324
388
|
}
|
|
325
389
|
catch (error) {
|
|
326
|
-
(
|
|
390
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[WebUsbTransport] close before reconnect failed:', error);
|
|
327
391
|
}
|
|
328
392
|
});
|
|
329
393
|
}
|
|
@@ -338,8 +402,18 @@ class WebUsbTransport {
|
|
|
338
402
|
}
|
|
339
403
|
});
|
|
340
404
|
}
|
|
405
|
+
assertAcquired(path) {
|
|
406
|
+
if (!this.acquiredPaths.has(path)) {
|
|
407
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device is not acquired for ${path}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
341
410
|
post(session, name, data) {
|
|
342
411
|
return __awaiter(this, void 0, void 0, function* () {
|
|
412
|
+
this.assertAcquired(session);
|
|
413
|
+
if (this.deviceProtocol.get(session) === 'V2') {
|
|
414
|
+
yield this.sendProtocolV2UsbFlowControl(session, name, data);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
343
417
|
yield this.call(session, name, data);
|
|
344
418
|
});
|
|
345
419
|
}
|
|
@@ -368,6 +442,7 @@ class WebUsbTransport {
|
|
|
368
442
|
var _a;
|
|
369
443
|
return __awaiter(this, void 0, void 0, function* () {
|
|
370
444
|
this.Log.debug(`[WebUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
|
|
445
|
+
this.staleProtocolPaths.add(path);
|
|
371
446
|
yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
372
447
|
try {
|
|
373
448
|
const currentDevice = yield this.findDevice(path);
|
|
@@ -485,34 +560,33 @@ class WebUsbTransport {
|
|
|
485
560
|
throw lastError;
|
|
486
561
|
});
|
|
487
562
|
}
|
|
488
|
-
|
|
489
|
-
var _a
|
|
563
|
+
transferInOnce(path, length) {
|
|
564
|
+
var _a;
|
|
490
565
|
return __awaiter(this, void 0, void 0, function* () {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
try {
|
|
495
|
-
const device = yield this.findDevice(path);
|
|
496
|
-
if (device.opened) {
|
|
497
|
-
const endpoints = this.deviceEndpoints.get(path);
|
|
498
|
-
const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
|
|
499
|
-
try {
|
|
500
|
-
yield device.releaseInterface(ifaceNum);
|
|
501
|
-
}
|
|
502
|
-
catch (error) {
|
|
503
|
-
this.Log.debug('[WebUsbTransport] releaseInterface after protocol probe error:', error);
|
|
504
|
-
}
|
|
505
|
-
yield device.close();
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
catch (error) {
|
|
509
|
-
this.Log.debug('[WebUsbTransport] close after protocol probe error:', error);
|
|
566
|
+
const device = yield this.findDevice(path);
|
|
567
|
+
if (!device.opened) {
|
|
568
|
+
throw new Error('USBDevice is not open for transferIn');
|
|
510
569
|
}
|
|
570
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
571
|
+
const endpointIn = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointIn) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
572
|
+
const result = yield device.transferIn(endpointIn, length);
|
|
573
|
+
return this.getTransferInData(result);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
closeConnectionAfterProbe(path) {
|
|
577
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
578
|
+
yield this.rotateProtocolV2UsbGeneration(path, 'WebUSB protocol probe reset');
|
|
579
|
+
yield this.closeOpenDevice(path);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
resetConnectionAfterProbe(path) {
|
|
583
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
584
|
+
yield this.closeConnectionAfterProbe(path);
|
|
511
585
|
yield this.getConnectedDevices();
|
|
512
586
|
yield this.connect(path, false);
|
|
513
587
|
});
|
|
514
588
|
}
|
|
515
|
-
withProtocolReadTimeout(
|
|
589
|
+
withProtocolReadTimeout(path, promise, timeoutMs, protocol, onTimeout) {
|
|
516
590
|
return __awaiter(this, void 0, void 0, function* () {
|
|
517
591
|
let timer;
|
|
518
592
|
let timedOut = false;
|
|
@@ -527,11 +601,19 @@ class WebUsbTransport {
|
|
|
527
601
|
return yield Promise.race([
|
|
528
602
|
guardedPromise,
|
|
529
603
|
new Promise((_, reject) => {
|
|
530
|
-
timer = setTimeout(() => {
|
|
604
|
+
timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
531
605
|
timedOut = true;
|
|
532
606
|
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
|
|
607
|
+
if (protocol === 'V1') {
|
|
608
|
+
try {
|
|
609
|
+
yield this.resetConnectionAfterProbe(path);
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
533
615
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
534
|
-
}, timeoutMs);
|
|
616
|
+
}), timeoutMs);
|
|
535
617
|
}),
|
|
536
618
|
]);
|
|
537
619
|
}
|
|
@@ -547,7 +629,9 @@ class WebUsbTransport {
|
|
|
547
629
|
return false;
|
|
548
630
|
}
|
|
549
631
|
try {
|
|
550
|
-
yield this.callProtocolV1(path, 'Initialize', {}, {
|
|
632
|
+
yield this.callProtocolV1(path, 'Initialize', {}, {
|
|
633
|
+
timeoutMs: PROTOCOL_V1_PROBE_TIMEOUT,
|
|
634
|
+
});
|
|
551
635
|
return true;
|
|
552
636
|
}
|
|
553
637
|
catch (_error) {
|
|
@@ -562,10 +646,9 @@ class WebUsbTransport {
|
|
|
562
646
|
}
|
|
563
647
|
return transport.probeProtocolV2({
|
|
564
648
|
call: (name, data, options) => this.callProtocolV2(path, name, data, options),
|
|
565
|
-
timeoutMs:
|
|
649
|
+
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
|
|
566
650
|
logger: this.Log,
|
|
567
651
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
568
|
-
onProbeFailed: () => this.resetConnectionAfterProbe(path),
|
|
569
652
|
});
|
|
570
653
|
});
|
|
571
654
|
}
|
|
@@ -574,6 +657,7 @@ class WebUsbTransport {
|
|
|
574
657
|
if (this.messages == null) {
|
|
575
658
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
576
659
|
}
|
|
660
|
+
this.assertAcquired(path);
|
|
577
661
|
const device = yield this.findDevice(path);
|
|
578
662
|
if (!device) {
|
|
579
663
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
@@ -582,9 +666,6 @@ class WebUsbTransport {
|
|
|
582
666
|
if (!protocol) {
|
|
583
667
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
|
|
584
668
|
}
|
|
585
|
-
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
586
|
-
this.Log.debug('transport call', createTransportCallLog(name, protocol));
|
|
587
|
-
}
|
|
588
669
|
if (protocol === 'V2') {
|
|
589
670
|
return this.callProtocolV2(path, name, data, options);
|
|
590
671
|
}
|
|
@@ -613,79 +694,8 @@ class WebUsbTransport {
|
|
|
613
694
|
});
|
|
614
695
|
}
|
|
615
696
|
callProtocolV2(path, name, data, options) {
|
|
616
|
-
var _a;
|
|
617
697
|
return __awaiter(this, void 0, void 0, function* () {
|
|
618
|
-
|
|
619
|
-
if (!this.messagesV2) {
|
|
620
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured, 'Protocol V2 schema not configured');
|
|
621
|
-
}
|
|
622
|
-
if (!protocolV1Messages) {
|
|
623
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
624
|
-
}
|
|
625
|
-
let session = this.protocolV2Sessions.get(path);
|
|
626
|
-
if (!session) {
|
|
627
|
-
session = new transport.ProtocolV2Session({
|
|
628
|
-
schemas: {
|
|
629
|
-
protocolV1: protocolV1Messages,
|
|
630
|
-
protocolV2: this.messagesV2,
|
|
631
|
-
},
|
|
632
|
-
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
633
|
-
writeFrame: (frame) => this.transferOutOnce(path, frame),
|
|
634
|
-
readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
|
|
635
|
-
logger: this.Log,
|
|
636
|
-
logPrefix: 'ProtocolV2 WebUSB',
|
|
637
|
-
createTimeoutError: (messageName, timeoutMs) => new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`),
|
|
638
|
-
});
|
|
639
|
-
this.protocolV2Sessions.set(path, session);
|
|
640
|
-
}
|
|
641
|
-
this.protocolV2ReadTimeouts.set(path, options === null || options === void 0 ? void 0 : options.timeoutMs);
|
|
642
|
-
(_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
643
|
-
try {
|
|
644
|
-
return yield session.call(name, data, options);
|
|
645
|
-
}
|
|
646
|
-
catch (error) {
|
|
647
|
-
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
648
|
-
if (message.includes('protocol v2 read timeout') || message.includes('response timeout')) {
|
|
649
|
-
try {
|
|
650
|
-
yield this.resetConnectionAfterProbe(path);
|
|
651
|
-
}
|
|
652
|
-
catch (resetError) {
|
|
653
|
-
this.Log.debug('[WebUsbTransport] Protocol V2 timeout reset failed:', resetError);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
throw error;
|
|
657
|
-
}
|
|
658
|
-
finally {
|
|
659
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
receiveProtocolV2Frame(path, timeoutMs) {
|
|
664
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
665
|
-
let assembler = this.protocolV2Assemblers.get(path);
|
|
666
|
-
if (!assembler) {
|
|
667
|
-
assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES);
|
|
668
|
-
this.protocolV2Assemblers.set(path, assembler);
|
|
669
|
-
}
|
|
670
|
-
let frame = assembler.push(new Uint8Array(0));
|
|
671
|
-
const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
|
|
672
|
-
while (!frame) {
|
|
673
|
-
const cancelToken = { cancelled: false };
|
|
674
|
-
const transferIn = this.transferInWithRetry(path, transport.PROTOCOL_V2_FRAME_MAX_BYTES, cancelToken);
|
|
675
|
-
const dataView = deadline
|
|
676
|
-
? yield this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V2', () => {
|
|
677
|
-
cancelToken.cancelled = true;
|
|
678
|
-
})
|
|
679
|
-
: yield transferIn;
|
|
680
|
-
const bytes = new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
|
|
681
|
-
try {
|
|
682
|
-
frame = assembler.push(bytes);
|
|
683
|
-
}
|
|
684
|
-
catch (error) {
|
|
685
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
return frame;
|
|
698
|
+
return this.callProtocolV2Usb(path, name, data, options);
|
|
689
699
|
});
|
|
690
700
|
}
|
|
691
701
|
receiveData(path, timeoutMs) {
|
|
@@ -726,45 +736,81 @@ class WebUsbTransport {
|
|
|
726
736
|
});
|
|
727
737
|
}
|
|
728
738
|
release(path) {
|
|
729
|
-
var _a, _b;
|
|
730
739
|
return __awaiter(this, void 0, void 0, function* () {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
yield device.releaseInterface(ifaceNum);
|
|
735
|
-
yield device.close();
|
|
736
|
-
this.deviceProtocol.delete(path);
|
|
737
|
-
this.deviceProtocolHints.delete(path);
|
|
738
|
-
(_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
739
|
-
this.protocolV2Assemblers.delete(path);
|
|
740
|
-
this.protocolV2Sessions.delete(path);
|
|
741
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
740
|
+
this.acquiredPaths.delete(path);
|
|
741
|
+
yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
|
|
742
|
+
yield this.closeOpenDevice(path);
|
|
742
743
|
this.deviceEndpoints.delete(path);
|
|
743
744
|
});
|
|
744
745
|
}
|
|
746
|
+
getProtocolV2UsbSchemas() {
|
|
747
|
+
if (!this.messages || !this.messagesV2) {
|
|
748
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
protocolV1: this.messages,
|
|
752
|
+
protocolV2: this.messagesV2,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
getProtocolV2UsbLogger() {
|
|
756
|
+
return this.Log;
|
|
757
|
+
}
|
|
758
|
+
writeProtocolV2UsbPacket(path, frame, _context) {
|
|
759
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
760
|
+
yield this.transferOutOnce(path, frame);
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
readProtocolV2UsbPacket(path, _context) {
|
|
764
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
765
|
+
const dataView = yield this.transferInOnce(path, transport.PROTOCOL_V2_FRAME_MAX_BYTES);
|
|
766
|
+
return new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
resetProtocolV2UsbNativeLink(path, _reason) {
|
|
770
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
771
|
+
yield this.closeOpenDevice(path);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
onProtocolV2UsbLinkInvalidated(path, reason) {
|
|
775
|
+
var _a;
|
|
776
|
+
this.deviceProtocol.delete(path);
|
|
777
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 link invalidated: ${path}`, reason);
|
|
778
|
+
}
|
|
779
|
+
createProtocolV2UsbTimeoutError(name, timeoutMs) {
|
|
780
|
+
return new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
|
|
781
|
+
}
|
|
745
782
|
getProtocolType(path) {
|
|
746
783
|
return this.deviceProtocol.get(path);
|
|
747
784
|
}
|
|
748
785
|
}
|
|
749
786
|
|
|
750
|
-
const
|
|
751
|
-
function
|
|
752
|
-
|
|
787
|
+
const BLE_ATT_HEADER_BYTES = 3;
|
|
788
|
+
function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapacity) {
|
|
789
|
+
if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
|
|
790
|
+
return fallbackPacketCapacity;
|
|
791
|
+
}
|
|
792
|
+
return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
|
|
753
793
|
}
|
|
794
|
+
|
|
795
|
+
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
754
796
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
755
|
-
const
|
|
797
|
+
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
798
|
+
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
756
799
|
const BLE_WRITE_DELAY_MS = 5;
|
|
757
|
-
const
|
|
758
|
-
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
800
|
+
const PROTOCOL_PROBE_TIMEOUT_MS = 3000;
|
|
759
801
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
760
802
|
class ElectronBleTransport {
|
|
761
803
|
constructor() {
|
|
762
804
|
this.name = 'ElectronBleTransport';
|
|
763
805
|
this.configured = false;
|
|
764
806
|
this.runPromise = null;
|
|
807
|
+
this.runPromiseDeviceId = null;
|
|
765
808
|
this.connectedDevices = new Set();
|
|
766
809
|
this.deviceProtocol = new Map();
|
|
767
810
|
this.deviceProtocolHints = new Map();
|
|
811
|
+
this.confirmedProtocolV2 = new Set();
|
|
812
|
+
this.deviceMtus = new Map();
|
|
813
|
+
this.devicePacketCapacities = new Map();
|
|
768
814
|
this.v1Buffers = new Map();
|
|
769
815
|
this.v2Assemblers = new Map();
|
|
770
816
|
this.v2FrameQueues = new Map();
|
|
@@ -786,11 +832,13 @@ class ElectronBleTransport {
|
|
|
786
832
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
787
833
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
|
|
788
834
|
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
789
|
-
yield this.
|
|
835
|
+
yield this.releaseNative(uuid);
|
|
790
836
|
}
|
|
791
837
|
}),
|
|
792
838
|
});
|
|
839
|
+
this.warnedMissingRelease = false;
|
|
793
840
|
this.notificationCleanups = new Map();
|
|
841
|
+
this.mtuCleanups = new Map();
|
|
794
842
|
this.disconnectCleanups = new Map();
|
|
795
843
|
this.notificationTokens = new Map();
|
|
796
844
|
this.nextNotificationToken = 1;
|
|
@@ -830,15 +878,27 @@ class ElectronBleTransport {
|
|
|
830
878
|
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
|
|
831
879
|
this.connectedDevices.delete(deviceId);
|
|
832
880
|
this.deviceProtocol.delete(deviceId);
|
|
881
|
+
this.deviceMtus.delete(deviceId);
|
|
882
|
+
this.devicePacketCapacities.delete(deviceId);
|
|
833
883
|
this.v1Buffers.delete(deviceId);
|
|
834
884
|
this.v2Assemblers.delete(deviceId);
|
|
835
885
|
this.resetProtocolV2Frames(deviceId);
|
|
836
886
|
this.notificationTokens.delete(deviceId);
|
|
887
|
+
if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
888
|
+
this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
889
|
+
this.runPromise = null;
|
|
890
|
+
this.runPromiseDeviceId = null;
|
|
891
|
+
}
|
|
837
892
|
const notifyCleanup = this.notificationCleanups.get(deviceId);
|
|
838
893
|
if (notifyCleanup) {
|
|
839
894
|
notifyCleanup();
|
|
840
895
|
this.notificationCleanups.delete(deviceId);
|
|
841
896
|
}
|
|
897
|
+
const mtuCleanup = this.mtuCleanups.get(deviceId);
|
|
898
|
+
if (mtuCleanup) {
|
|
899
|
+
mtuCleanup();
|
|
900
|
+
this.mtuCleanups.delete(deviceId);
|
|
901
|
+
}
|
|
842
902
|
const disconnectCleanup = this.disconnectCleanups.get(deviceId);
|
|
843
903
|
if (disconnectCleanup) {
|
|
844
904
|
disconnectCleanup();
|
|
@@ -859,10 +919,18 @@ class ElectronBleTransport {
|
|
|
859
919
|
this.configured = true;
|
|
860
920
|
}
|
|
861
921
|
configureProtocolV2(signedData) {
|
|
922
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
923
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
862
927
|
this._messagesV2 = parseConfigure(signedData);
|
|
863
|
-
this.
|
|
864
|
-
|
|
865
|
-
|
|
928
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
929
|
+
if (isReconfiguration) {
|
|
930
|
+
this.protocolV2Links
|
|
931
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
932
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
|
|
933
|
+
}
|
|
866
934
|
}
|
|
867
935
|
listen() {
|
|
868
936
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -880,10 +948,6 @@ class ElectronBleTransport {
|
|
|
880
948
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] enumerate found ${devices.length} device(s):`);
|
|
881
949
|
for (const dev of devices) {
|
|
882
950
|
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Electron BLE] id="${dev.id}" name="${dev.name}"`);
|
|
883
|
-
const protocolHint = inferProtocolHintFromDeviceName(dev.name);
|
|
884
|
-
if (protocolHint) {
|
|
885
|
-
this.deviceProtocolHints.set(dev.id, protocolHint);
|
|
886
|
-
}
|
|
887
951
|
}
|
|
888
952
|
return devices.map(device => toBleDescriptor(device));
|
|
889
953
|
}
|
|
@@ -894,7 +958,7 @@ class ElectronBleTransport {
|
|
|
894
958
|
});
|
|
895
959
|
}
|
|
896
960
|
acquire(input) {
|
|
897
|
-
var _a, _b, _c, _d, _e, _f;
|
|
961
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
898
962
|
return __awaiter(this, void 0, void 0, function* () {
|
|
899
963
|
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
900
964
|
if (!uuid) {
|
|
@@ -906,8 +970,8 @@ class ElectronBleTransport {
|
|
|
906
970
|
if (forceCleanRunPromise && this.runPromise) {
|
|
907
971
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
908
972
|
this.runPromise.reject(error);
|
|
909
|
-
this.rejectAllProtocolV2Frames(error);
|
|
910
973
|
this.runPromise = null;
|
|
974
|
+
this.runPromiseDeviceId = null;
|
|
911
975
|
}
|
|
912
976
|
try {
|
|
913
977
|
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
@@ -919,7 +983,7 @@ class ElectronBleTransport {
|
|
|
919
983
|
}
|
|
920
984
|
const protocolHint = expectedProtocol
|
|
921
985
|
? undefined
|
|
922
|
-
: (_b =
|
|
986
|
+
: (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(uuid);
|
|
923
987
|
if (protocolHint) {
|
|
924
988
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
925
989
|
}
|
|
@@ -930,16 +994,31 @@ class ElectronBleTransport {
|
|
|
930
994
|
catch (error) {
|
|
931
995
|
this.handleBluetoothError(error);
|
|
932
996
|
}
|
|
997
|
+
const mtuCleanup = this.createMtuSubscription(uuid);
|
|
998
|
+
if (mtuCleanup) {
|
|
999
|
+
this.mtuCleanups.set(uuid, mtuCleanup);
|
|
1000
|
+
}
|
|
933
1001
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
934
|
-
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
|
|
1002
|
+
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
935
1003
|
yield window.desktopApi.nobleBle.subscribe(uuid);
|
|
1004
|
+
yield this.refreshBlePacketCapacity(uuid);
|
|
936
1005
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
937
1006
|
this.notificationCleanups.set(uuid, cleanup);
|
|
1007
|
+
const connectionToken = this.notificationTokens.get(uuid);
|
|
1008
|
+
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
1009
|
+
if (protocolType === 'V2') {
|
|
1010
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[Electron BLE] Protocol V2 write configured', {
|
|
1011
|
+
writeMode: 'withoutResponse',
|
|
1012
|
+
negotiatedMtu: this.deviceMtus.get(uuid),
|
|
1013
|
+
packetCapacity: (_d = this.devicePacketCapacities.get(uuid)) !== null && _d !== void 0 ? _d : BLE_PACKET_SIZE_FALLBACK,
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
938
1016
|
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
|
|
939
1017
|
var _a;
|
|
940
|
-
if (disconnectedDevice.id === uuid
|
|
1018
|
+
if (disconnectedDevice.id === uuid &&
|
|
1019
|
+
this.notificationTokens.get(uuid) === connectionToken) {
|
|
941
1020
|
this.cleanupDeviceState(uuid);
|
|
942
|
-
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(
|
|
1021
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
943
1022
|
name: disconnectedDevice.name,
|
|
944
1023
|
id: disconnectedDevice.id,
|
|
945
1024
|
connectId: disconnectedDevice.id,
|
|
@@ -947,35 +1026,46 @@ class ElectronBleTransport {
|
|
|
947
1026
|
}
|
|
948
1027
|
});
|
|
949
1028
|
this.disconnectCleanups.set(uuid, disconnectCleanup);
|
|
950
|
-
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
951
|
-
(_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit('device-connect', {
|
|
952
|
-
name: device.name,
|
|
953
|
-
id: device.id,
|
|
954
|
-
connectId: device.id,
|
|
955
|
-
});
|
|
956
1029
|
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
957
1030
|
}
|
|
958
1031
|
catch (error) {
|
|
959
|
-
(
|
|
1032
|
+
(_e = this.Log) === null || _e === void 0 ? void 0 : _e.error('[Electron BLE] acquire failed:', error);
|
|
960
1033
|
try {
|
|
961
|
-
if (((
|
|
1034
|
+
if (((_f = window.desktopApi) === null || _f === void 0 ? void 0 : _f.nobleBle) && this.connectedDevices.has(uuid)) {
|
|
962
1035
|
yield window.desktopApi.nobleBle.unsubscribe(uuid);
|
|
963
1036
|
yield window.desktopApi.nobleBle.disconnect(uuid);
|
|
964
1037
|
}
|
|
965
1038
|
}
|
|
966
1039
|
catch (cleanupError) {
|
|
967
|
-
(
|
|
1040
|
+
(_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
|
|
968
1041
|
}
|
|
969
1042
|
this.cleanupDeviceState(uuid);
|
|
970
1043
|
throw error;
|
|
971
1044
|
}
|
|
972
1045
|
});
|
|
973
1046
|
}
|
|
974
|
-
release(id) {
|
|
975
|
-
var _a
|
|
1047
|
+
release(id, _onclose, keepSession) {
|
|
1048
|
+
var _a;
|
|
976
1049
|
return __awaiter(this, void 0, void 0, function* () {
|
|
977
1050
|
try {
|
|
978
1051
|
yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
|
|
1052
|
+
yield this.releaseLogical(id, keepSession);
|
|
1053
|
+
}
|
|
1054
|
+
catch (error) {
|
|
1055
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] release failed:', error);
|
|
1056
|
+
this.cleanupDeviceState(id);
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
disconnect(id) {
|
|
1061
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1062
|
+
return this.releaseNative(id);
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
releaseNative(id) {
|
|
1066
|
+
var _a, _b;
|
|
1067
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1068
|
+
try {
|
|
979
1069
|
if (this.connectedDevices.has(id)) {
|
|
980
1070
|
if ((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) {
|
|
981
1071
|
yield window.desktopApi.nobleBle.unsubscribe(id);
|
|
@@ -990,11 +1080,36 @@ class ElectronBleTransport {
|
|
|
990
1080
|
}
|
|
991
1081
|
});
|
|
992
1082
|
}
|
|
993
|
-
|
|
994
|
-
|
|
1083
|
+
releaseLogical(id, keepSession) {
|
|
1084
|
+
var _a, _b, _c, _d;
|
|
1085
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1086
|
+
try {
|
|
1087
|
+
if (!this.connectedDevices.has(id))
|
|
1088
|
+
return;
|
|
1089
|
+
this.cleanupDeviceState(id);
|
|
1090
|
+
const release = (_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.release;
|
|
1091
|
+
if (!release) {
|
|
1092
|
+
if (!this.warnedMissingRelease) {
|
|
1093
|
+
this.warnedMissingRelease = true;
|
|
1094
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] desktopApi.nobleBle.release is missing from the preload bridge; ' +
|
|
1095
|
+
'keep-alive idle countdown will never start — map NOBLE_BLE_RELEASE in the desktop preload');
|
|
1096
|
+
}
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
yield release(id, keepSession);
|
|
1100
|
+
}
|
|
1101
|
+
catch (error) {
|
|
1102
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] logical release failed:', error);
|
|
1103
|
+
this.cleanupDeviceState(id);
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
createProtocolMismatchError(expected, uuid) {
|
|
1108
|
+
const isStaleV2Bond = expected === 'V2' && this.confirmedProtocolV2.has(uuid);
|
|
1109
|
+
return hdShared.ERRORS.TypedError(isStaleV2Bond ? hdShared.HardwareErrorCode.BleDeviceBondError : hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
995
1110
|
}
|
|
996
1111
|
createProtocolDetectionError() {
|
|
997
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1
|
|
1112
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
|
|
998
1113
|
}
|
|
999
1114
|
clearProbeProtocol(uuid, protocol) {
|
|
1000
1115
|
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
@@ -1010,12 +1125,16 @@ class ElectronBleTransport {
|
|
|
1010
1125
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V1 (expected)`);
|
|
1011
1126
|
return 'V1';
|
|
1012
1127
|
}
|
|
1013
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1128
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1014
1129
|
}
|
|
1015
1130
|
if (expectedProtocol === 'V2') {
|
|
1016
|
-
this.
|
|
1017
|
-
|
|
1018
|
-
|
|
1131
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1132
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1133
|
+
this.confirmedProtocolV2.add(uuid);
|
|
1134
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
1135
|
+
return 'V2';
|
|
1136
|
+
}
|
|
1137
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1019
1138
|
}
|
|
1020
1139
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1021
1140
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
@@ -1026,6 +1145,9 @@ class ElectronBleTransport {
|
|
|
1026
1145
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
1027
1146
|
if (detected) {
|
|
1028
1147
|
this.deviceProtocol.set(uuid, protocol);
|
|
1148
|
+
if (protocol === 'V2') {
|
|
1149
|
+
this.confirmedProtocolV2.add(uuid);
|
|
1150
|
+
}
|
|
1029
1151
|
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
|
|
1030
1152
|
return protocol;
|
|
1031
1153
|
}
|
|
@@ -1049,38 +1171,22 @@ class ElectronBleTransport {
|
|
|
1049
1171
|
});
|
|
1050
1172
|
}
|
|
1051
1173
|
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1052
|
-
var _a
|
|
1174
|
+
var _a;
|
|
1053
1175
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1054
1176
|
yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
|
|
1055
1177
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
1056
1178
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1057
1179
|
this.resetProtocolV2Frames(uuid);
|
|
1058
|
-
if (this.runPromise) {
|
|
1180
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1059
1181
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1060
1182
|
this.runPromise.reject(error);
|
|
1061
1183
|
this.runPromise = null;
|
|
1184
|
+
this.runPromiseDeviceId = null;
|
|
1062
1185
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
this.notificationCleanups.delete(uuid);
|
|
1067
|
-
}
|
|
1068
|
-
this.notificationTokens.delete(uuid);
|
|
1069
|
-
try {
|
|
1070
|
-
yield ((_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.unsubscribe(uuid));
|
|
1071
|
-
}
|
|
1072
|
-
catch (error) {
|
|
1073
|
-
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
|
|
1074
|
-
}
|
|
1075
|
-
try {
|
|
1076
|
-
yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.subscribe(uuid));
|
|
1077
|
-
}
|
|
1078
|
-
catch (error) {
|
|
1079
|
-
(_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
|
|
1080
|
-
throw error;
|
|
1186
|
+
if (!this.notificationCleanups.has(uuid)) {
|
|
1187
|
+
const cleanup = this.createNotificationSubscription(uuid);
|
|
1188
|
+
this.notificationCleanups.set(uuid, cleanup);
|
|
1081
1189
|
}
|
|
1082
|
-
const cleanup = this.createNotificationSubscription(uuid);
|
|
1083
|
-
this.notificationCleanups.set(uuid, cleanup);
|
|
1084
1190
|
});
|
|
1085
1191
|
}
|
|
1086
1192
|
probeProtocolV1(uuid) {
|
|
@@ -1091,12 +1197,12 @@ class ElectronBleTransport {
|
|
|
1091
1197
|
}
|
|
1092
1198
|
try {
|
|
1093
1199
|
this.deviceProtocol.set(uuid, 'V1');
|
|
1094
|
-
yield this.callProtocolV1(uuid, '
|
|
1200
|
+
yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1095
1201
|
return true;
|
|
1096
1202
|
}
|
|
1097
1203
|
catch (error) {
|
|
1098
1204
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1099
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V1
|
|
1205
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V1 GetFeatures probe failed:', error);
|
|
1100
1206
|
return false;
|
|
1101
1207
|
}
|
|
1102
1208
|
});
|
|
@@ -1126,25 +1232,6 @@ class ElectronBleTransport {
|
|
|
1126
1232
|
return detected;
|
|
1127
1233
|
});
|
|
1128
1234
|
}
|
|
1129
|
-
writeWithChunking(uuid, hexData) {
|
|
1130
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1131
|
-
const totalBytes = hexData.length / 2;
|
|
1132
|
-
if (totalBytes <= BLE_PACKET_SIZE) {
|
|
1133
|
-
yield hdShared.wait(BLE_WRITE_DELAY_MS);
|
|
1134
|
-
yield this.writeOnce(uuid, hexData);
|
|
1135
|
-
return;
|
|
1136
|
-
}
|
|
1137
|
-
for (let offset = 0; offset < hexData.length;) {
|
|
1138
|
-
const chunkHexLen = Math.min(BLE_PACKET_SIZE * 2, hexData.length - offset);
|
|
1139
|
-
const chunkHex = hexData.substring(offset, offset + chunkHexLen);
|
|
1140
|
-
offset += chunkHexLen;
|
|
1141
|
-
yield this.writeOnce(uuid, chunkHex);
|
|
1142
|
-
if (offset < hexData.length) {
|
|
1143
|
-
yield hdShared.wait(BLE_WRITE_DELAY_MS);
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
});
|
|
1147
|
-
}
|
|
1148
1235
|
writeOnce(uuid, hexData) {
|
|
1149
1236
|
var _a;
|
|
1150
1237
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1152,7 +1239,51 @@ class ElectronBleTransport {
|
|
|
1152
1239
|
if (!nobleBle) {
|
|
1153
1240
|
throw new Error('Noble BLE API not available');
|
|
1154
1241
|
}
|
|
1155
|
-
yield nobleBle.write(uuid, hexData);
|
|
1242
|
+
yield nobleBle.write(uuid, hexData, { pacingDelayMs: 0 });
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
refreshBlePacketCapacity(uuid) {
|
|
1246
|
+
var _a, _b;
|
|
1247
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1248
|
+
const device = yield ((_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.getDevice(uuid));
|
|
1249
|
+
this.updateBlePacketCapacity(uuid, device === null || device === void 0 ? void 0 : device.mtu);
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
updateBlePacketCapacity(uuid, mtu) {
|
|
1253
|
+
const packetCapacity = resolveBlePacketCapacity(mtu, BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
|
|
1254
|
+
if (typeof mtu === 'number') {
|
|
1255
|
+
this.deviceMtus.set(uuid, mtu);
|
|
1256
|
+
}
|
|
1257
|
+
else {
|
|
1258
|
+
this.deviceMtus.delete(uuid);
|
|
1259
|
+
}
|
|
1260
|
+
this.devicePacketCapacities.set(uuid, packetCapacity);
|
|
1261
|
+
}
|
|
1262
|
+
createMtuSubscription(uuid) {
|
|
1263
|
+
var _a, _b;
|
|
1264
|
+
const onMtuChanged = (_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.onMtuChanged;
|
|
1265
|
+
if (!onMtuChanged)
|
|
1266
|
+
return undefined;
|
|
1267
|
+
return onMtuChanged(device => {
|
|
1268
|
+
if (device.id === uuid) {
|
|
1269
|
+
this.updateBlePacketCapacity(uuid, device.mtu);
|
|
1270
|
+
}
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration) {
|
|
1274
|
+
var _a;
|
|
1275
|
+
const packetCapacity = (_a = this.devicePacketCapacities.get(uuid)) !== null && _a !== void 0 ? _a : BLE_PACKET_SIZE_FALLBACK;
|
|
1276
|
+
const shouldPace = !context.highThroughput;
|
|
1277
|
+
return transport.writeProtocolV2BleFrame({
|
|
1278
|
+
frame,
|
|
1279
|
+
packetCapacity,
|
|
1280
|
+
assertActive: assertCurrentGeneration,
|
|
1281
|
+
signal: context.signal,
|
|
1282
|
+
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
1283
|
+
initialDelayMs: shouldPace && frame.length <= packetCapacity ? BLE_WRITE_DELAY_MS : 0,
|
|
1284
|
+
burstSize: shouldPace ? 1 : undefined,
|
|
1285
|
+
burstPauseMs: shouldPace ? BLE_WRITE_DELAY_MS : 0,
|
|
1286
|
+
writePacket: packet => this.writeOnce(uuid, transport.bytesToHex(packet)),
|
|
1156
1287
|
});
|
|
1157
1288
|
}
|
|
1158
1289
|
handleNotification(deviceId, hexData) {
|
|
@@ -1163,7 +1294,7 @@ class ElectronBleTransport {
|
|
|
1163
1294
|
if (this.deviceProtocol.get(deviceId) === 'V2') {
|
|
1164
1295
|
this.rejectProtocolV2Frames(deviceId, error);
|
|
1165
1296
|
}
|
|
1166
|
-
else if (this.runPromise) {
|
|
1297
|
+
else if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
1167
1298
|
this.runPromise.reject(error);
|
|
1168
1299
|
}
|
|
1169
1300
|
return;
|
|
@@ -1215,16 +1346,8 @@ class ElectronBleTransport {
|
|
|
1215
1346
|
}
|
|
1216
1347
|
this.getProtocolV2FrameQueue(uuid).push(frame);
|
|
1217
1348
|
}
|
|
1218
|
-
rejectAllProtocolV2Frames(error) {
|
|
1219
|
-
this.v2FrameQueues.clear();
|
|
1220
|
-
for (const framePromise of this.v2FramePromises.values()) {
|
|
1221
|
-
framePromise.reject(error);
|
|
1222
|
-
}
|
|
1223
|
-
this.v2FramePromises.clear();
|
|
1224
|
-
}
|
|
1225
1349
|
resetProtocolV2Frames(uuid) {
|
|
1226
|
-
this.
|
|
1227
|
-
this.v2FramePromises.delete(uuid);
|
|
1350
|
+
this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
|
|
1228
1351
|
}
|
|
1229
1352
|
rejectProtocolV2Frames(uuid, error) {
|
|
1230
1353
|
this.v2FrameQueues.delete(uuid);
|
|
@@ -1257,17 +1380,19 @@ class ElectronBleTransport {
|
|
|
1257
1380
|
const result = this.processProtocolV1Notification(deviceId, hexData);
|
|
1258
1381
|
if (result.error) {
|
|
1259
1382
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V1 packet processing error:', result.error);
|
|
1260
|
-
if (this.runPromise) {
|
|
1383
|
+
if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
1261
1384
|
this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError));
|
|
1262
1385
|
}
|
|
1263
1386
|
return;
|
|
1264
1387
|
}
|
|
1265
|
-
if (result.isComplete &&
|
|
1388
|
+
if (result.isComplete &&
|
|
1389
|
+
result.completePacket &&
|
|
1390
|
+
this.runPromise &&
|
|
1391
|
+
this.runPromiseDeviceId === deviceId) {
|
|
1266
1392
|
this.runPromise.resolve(result.completePacket);
|
|
1267
1393
|
}
|
|
1268
1394
|
}
|
|
1269
1395
|
call(uuid, name, data, options) {
|
|
1270
|
-
var _a;
|
|
1271
1396
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1272
1397
|
if (!this._messages) {
|
|
1273
1398
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
@@ -1279,15 +1404,21 @@ class ElectronBleTransport {
|
|
|
1279
1404
|
if (!protocol) {
|
|
1280
1405
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1281
1406
|
}
|
|
1282
|
-
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
1283
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', createTransportCallLog(name, protocol));
|
|
1284
|
-
}
|
|
1285
1407
|
if (protocol === 'V2') {
|
|
1286
1408
|
return this.callProtocolV2(uuid, name, data, options);
|
|
1287
1409
|
}
|
|
1288
1410
|
return this.callProtocolV1(uuid, name, data, options);
|
|
1289
1411
|
});
|
|
1290
1412
|
}
|
|
1413
|
+
post(uuid, name, data) {
|
|
1414
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1415
|
+
if (this.deviceProtocol.get(uuid) === 'V2') {
|
|
1416
|
+
yield this.protocolV2Links.sendFlowControl(uuid, () => this.createProtocolV2Adapter(uuid), name, data);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
yield this.callProtocolV1(uuid, name, data);
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1291
1422
|
callProtocolV1(uuid, name, data, options) {
|
|
1292
1423
|
var _a, _b;
|
|
1293
1424
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1301,6 +1432,7 @@ class ElectronBleTransport {
|
|
|
1301
1432
|
const runPromise = hdShared.createDeferred();
|
|
1302
1433
|
runPromise.promise.catch(() => undefined);
|
|
1303
1434
|
this.runPromise = runPromise;
|
|
1435
|
+
this.runPromiseDeviceId = uuid;
|
|
1304
1436
|
const messages = this._messages;
|
|
1305
1437
|
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
1306
1438
|
let timeout;
|
|
@@ -1339,6 +1471,17 @@ class ElectronBleTransport {
|
|
|
1339
1471
|
}
|
|
1340
1472
|
catch (e) {
|
|
1341
1473
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Electron BLE] Protocol V1 call error:', e);
|
|
1474
|
+
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1475
|
+
if ((e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
|
|
1476
|
+
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
1477
|
+
const notifyCleanup = this.notificationCleanups.get(uuid);
|
|
1478
|
+
notifyCleanup === null || notifyCleanup === void 0 ? void 0 : notifyCleanup();
|
|
1479
|
+
this.notificationCleanups.delete(uuid);
|
|
1480
|
+
this.notificationTokens.delete(uuid);
|
|
1481
|
+
if (!isProbeTimeout) {
|
|
1482
|
+
yield this.releaseNative(uuid);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1342
1485
|
throw e;
|
|
1343
1486
|
}
|
|
1344
1487
|
finally {
|
|
@@ -1346,22 +1489,23 @@ class ElectronBleTransport {
|
|
|
1346
1489
|
clearTimeout(timeout);
|
|
1347
1490
|
if (this.runPromise === runPromise) {
|
|
1348
1491
|
this.runPromise = null;
|
|
1492
|
+
this.runPromiseDeviceId = null;
|
|
1349
1493
|
}
|
|
1350
1494
|
}
|
|
1351
1495
|
});
|
|
1352
1496
|
}
|
|
1353
1497
|
callProtocolV2(uuid, name, data, options) {
|
|
1354
|
-
var _a
|
|
1498
|
+
var _a;
|
|
1355
1499
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1356
1500
|
if (!this._messages || !this._messagesV2) {
|
|
1357
1501
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1358
1502
|
}
|
|
1359
|
-
const callOptions =
|
|
1503
|
+
const callOptions = options;
|
|
1360
1504
|
try {
|
|
1361
1505
|
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
1362
1506
|
}
|
|
1363
1507
|
catch (e) {
|
|
1364
|
-
(
|
|
1508
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 call error:', e);
|
|
1365
1509
|
throw e;
|
|
1366
1510
|
}
|
|
1367
1511
|
});
|
|
@@ -1376,6 +1520,7 @@ class ElectronBleTransport {
|
|
|
1376
1520
|
};
|
|
1377
1521
|
return {
|
|
1378
1522
|
router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1523
|
+
maxFrameBytes: transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
|
|
1379
1524
|
generation,
|
|
1380
1525
|
prepareCall: () => {
|
|
1381
1526
|
var _a;
|
|
@@ -1383,10 +1528,7 @@ class ElectronBleTransport {
|
|
|
1383
1528
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1384
1529
|
this.resetProtocolV2Frames(uuid);
|
|
1385
1530
|
},
|
|
1386
|
-
writeFrame: (frame) =>
|
|
1387
|
-
assertCurrentGeneration();
|
|
1388
|
-
return this.writeWithChunking(uuid, transport.bytesToHex(frame));
|
|
1389
|
-
},
|
|
1531
|
+
writeFrame: (frame, context) => this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
|
|
1390
1532
|
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1391
1533
|
assertCurrentGeneration();
|
|
1392
1534
|
const rxFrame = yield this.readProtocolV2Frame(uuid);
|
|
@@ -1406,6 +1548,7 @@ class ElectronBleTransport {
|
|
|
1406
1548
|
};
|
|
1407
1549
|
}
|
|
1408
1550
|
processProtocolV1Notification(deviceId, hexData) {
|
|
1551
|
+
var _a;
|
|
1409
1552
|
try {
|
|
1410
1553
|
if (typeof hexData !== 'string') {
|
|
1411
1554
|
return { isComplete: false, error: 'Invalid hexData type' };
|
|
@@ -1424,6 +1567,10 @@ class ElectronBleTransport {
|
|
|
1424
1567
|
bufferState.buffer = [...data.subarray(3)];
|
|
1425
1568
|
}
|
|
1426
1569
|
else {
|
|
1570
|
+
if (bufferState.buffer.length === 0) {
|
|
1571
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Orphan continuation chunk discarded');
|
|
1572
|
+
return { isComplete: false };
|
|
1573
|
+
}
|
|
1427
1574
|
bufferState.buffer = bufferState.buffer.concat([...data]);
|
|
1428
1575
|
}
|
|
1429
1576
|
if (bufferState.buffer.length - transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferState.bufferLength) {
|