@onekeyfe/hd-transport-web-device 1.2.0-alpha.14 → 1.2.0-alpha.141
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 +377 -22
- package/__tests__/webusb-protocol-cache.test.ts +244 -0
- package/__tests__/webusb-protocol-v2-timeout.test.ts +357 -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 +16 -3
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +144 -12
- package/dist/index.js +410 -263
- 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 +241 -104
- package/src/transportLog.ts +1 -11
- package/src/webusb.ts +295 -195
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,40 @@ 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
|
|
54
|
+
const PROTOCOL_V1_PROBE_TIMEOUT = 5000;
|
|
55
|
+
const PROTOCOL_V2_PROBE_TIMEOUT = 1000;
|
|
56
|
+
const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
|
|
63
57
|
function inferProtocolHintFromDeviceName$1(name) {
|
|
64
58
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
65
59
|
}
|
|
66
|
-
|
|
60
|
+
let activeWebUsbTransport;
|
|
61
|
+
let usbDisconnectListenerAttached = false;
|
|
62
|
+
function registerActiveWebUsbTransport(instance, usb) {
|
|
63
|
+
activeWebUsbTransport = instance;
|
|
64
|
+
if (usbDisconnectListenerAttached)
|
|
65
|
+
return;
|
|
66
|
+
usbDisconnectListenerAttached = true;
|
|
67
|
+
usb.addEventListener('disconnect', event => {
|
|
68
|
+
var _a;
|
|
69
|
+
const serial = (_a = event.device) === null || _a === void 0 ? void 0 : _a.serialNumber;
|
|
70
|
+
if (typeof serial !== 'string' || serial.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.markProtocolStale(serial);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
67
76
|
constructor() {
|
|
77
|
+
super({
|
|
78
|
+
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
79
|
+
maxFrameBytes: transport.PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
80
|
+
logPrefix: 'ProtocolV2 WebUSB',
|
|
81
|
+
});
|
|
68
82
|
this.deviceProtocol = new Map();
|
|
69
83
|
this.deviceProtocolHints = new Map();
|
|
70
|
-
this.
|
|
71
|
-
this.
|
|
72
|
-
this.
|
|
84
|
+
this.staleProtocolPaths = new Set();
|
|
85
|
+
this.acquiredPaths = new Set();
|
|
86
|
+
this.probedDeviceObjects = new Map();
|
|
73
87
|
this.deviceEndpoints = new Map();
|
|
74
|
-
this.mockSerialPaths = new WeakMap();
|
|
75
|
-
this.mockSerialCounter = 0;
|
|
76
88
|
this.name = 'WebUsbTransport';
|
|
77
89
|
this.stopped = false;
|
|
78
90
|
this.configured = false;
|
|
@@ -88,6 +100,11 @@ class WebUsbTransport {
|
|
|
88
100
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'WebUSB is not supported by current browsers');
|
|
89
101
|
}
|
|
90
102
|
this.usb = usb;
|
|
103
|
+
registerActiveWebUsbTransport(this, usb);
|
|
104
|
+
}
|
|
105
|
+
markProtocolStale(path) {
|
|
106
|
+
this.staleProtocolPaths.add(path);
|
|
107
|
+
this.probedDeviceObjects.delete(path);
|
|
91
108
|
}
|
|
92
109
|
configure(signedData) {
|
|
93
110
|
const messages = parseConfigure$1(signedData);
|
|
@@ -95,9 +112,18 @@ class WebUsbTransport {
|
|
|
95
112
|
this.messages = messages;
|
|
96
113
|
}
|
|
97
114
|
configureProtocolV2(signedData) {
|
|
115
|
+
var _a;
|
|
116
|
+
const schemaSource = typeof signedData === 'string'
|
|
117
|
+
? signedData
|
|
118
|
+
: (_a = JSON.stringify(signedData)) !== null && _a !== void 0 ? _a : String(signedData);
|
|
119
|
+
if (schemaSource === this.protocolV2SchemaSource)
|
|
120
|
+
return;
|
|
121
|
+
const hadProtocolV2Schema = this.protocolV2SchemaSource !== undefined;
|
|
98
122
|
this.messagesV2 = parseConfigure$1(signedData);
|
|
99
|
-
this.
|
|
100
|
-
|
|
123
|
+
this.protocolV2SchemaSource = schemaSource;
|
|
124
|
+
if (!hadProtocolV2Schema)
|
|
125
|
+
return;
|
|
126
|
+
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
127
|
}
|
|
102
128
|
promptDeviceAccess() {
|
|
103
129
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -119,20 +145,6 @@ class WebUsbTransport {
|
|
|
119
145
|
return this.deviceList;
|
|
120
146
|
});
|
|
121
147
|
}
|
|
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
148
|
getConnectedDevices() {
|
|
137
149
|
return __awaiter(this, void 0, void 0, function* () {
|
|
138
150
|
if (!this.usb)
|
|
@@ -140,10 +152,11 @@ class WebUsbTransport {
|
|
|
140
152
|
const devices = yield this.usb.getDevices();
|
|
141
153
|
const onekeyDevices = devices.filter(dev => {
|
|
142
154
|
const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
|
|
143
|
-
|
|
155
|
+
const hasSerialNumber = typeof dev.serialNumber === 'string' && dev.serialNumber.length > 0;
|
|
156
|
+
return isOneKey && hasSerialNumber && !hdShared.isKnownTrezorWebUsbDevice(dev);
|
|
144
157
|
});
|
|
145
158
|
this.deviceList = onekeyDevices.map(device => {
|
|
146
|
-
const path =
|
|
159
|
+
const path = device.serialNumber;
|
|
147
160
|
const protocolHint = inferProtocolHintFromDeviceName$1(device.productName);
|
|
148
161
|
if (protocolHint) {
|
|
149
162
|
this.deviceProtocolHints.set(path, protocolHint);
|
|
@@ -158,25 +171,54 @@ class WebUsbTransport {
|
|
|
158
171
|
});
|
|
159
172
|
}
|
|
160
173
|
acquire(input) {
|
|
161
|
-
var _a, _b, _c;
|
|
174
|
+
var _a, _b, _c, _d, _e, _f;
|
|
162
175
|
return __awaiter(this, void 0, void 0, function* () {
|
|
163
176
|
if (!input.path)
|
|
164
177
|
return;
|
|
165
178
|
try {
|
|
179
|
+
yield this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
|
|
166
180
|
yield this.closeOpenDevice(input.path);
|
|
167
181
|
yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
182
|
+
if (input.forceProtocolDetection) {
|
|
183
|
+
this.staleProtocolPaths.delete(input.path);
|
|
184
|
+
this.deviceProtocol.delete(input.path);
|
|
185
|
+
this.deviceProtocolHints.delete(input.path);
|
|
186
|
+
}
|
|
187
|
+
if (this.staleProtocolPaths.has(input.path)) {
|
|
188
|
+
this.staleProtocolPaths.delete(input.path);
|
|
189
|
+
this.deviceProtocol.delete(input.path);
|
|
190
|
+
}
|
|
191
|
+
if (this.deviceProtocol.has(input.path)) {
|
|
192
|
+
const currentDevice = (_b = this.deviceList.find(d => d.path === input.path)) === null || _b === void 0 ? void 0 : _b.device;
|
|
193
|
+
if (!currentDevice || currentDevice !== this.probedDeviceObjects.get(input.path)) {
|
|
194
|
+
this.deviceProtocol.delete(input.path);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const cachedProtocol = this.deviceProtocol.get(input.path);
|
|
198
|
+
if (cachedProtocol && input.expectedProtocol && cachedProtocol !== input.expectedProtocol) {
|
|
199
|
+
this.deviceProtocol.delete(input.path);
|
|
174
200
|
}
|
|
175
|
-
|
|
201
|
+
if (!this.deviceProtocol.has(input.path)) {
|
|
202
|
+
const deviceName = (_c = this.deviceList.find(device => device.path === input.path)) === null || _c === void 0 ? void 0 : _c.device.productName;
|
|
203
|
+
const protocolHint = input.expectedProtocol
|
|
204
|
+
? undefined
|
|
205
|
+
: (_e = (_d = input.protocolHint) !== null && _d !== void 0 ? _d : this.deviceProtocolHints.get(input.path)) !== null && _e !== void 0 ? _e : inferProtocolHintFromDeviceName$1(deviceName);
|
|
206
|
+
if (protocolHint) {
|
|
207
|
+
this.deviceProtocolHints.set(input.path, protocolHint);
|
|
208
|
+
}
|
|
209
|
+
yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
|
|
210
|
+
const probedDevice = (_f = this.deviceList.find(d => d.path === input.path)) === null || _f === void 0 ? void 0 : _f.device;
|
|
211
|
+
if (probedDevice) {
|
|
212
|
+
this.probedDeviceObjects.set(input.path, probedDevice);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
this.acquiredPaths.add(input.path);
|
|
176
216
|
return yield Promise.resolve(input.path);
|
|
177
217
|
}
|
|
178
218
|
catch (e) {
|
|
179
219
|
this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
|
|
220
|
+
this.acquiredPaths.delete(input.path);
|
|
221
|
+
yield this.closeOpenDevice(input.path);
|
|
180
222
|
throw e;
|
|
181
223
|
}
|
|
182
224
|
});
|
|
@@ -184,29 +226,53 @@ class WebUsbTransport {
|
|
|
184
226
|
createProtocolMismatchError(expected) {
|
|
185
227
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
186
228
|
}
|
|
229
|
+
createProtocolProbeTimeoutError(expected, attempts) {
|
|
230
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInitializeFailed, `Protocol ${expected} probe timeout after ${attempts} attempts`);
|
|
231
|
+
}
|
|
187
232
|
createProtocolDetectionError() {
|
|
188
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.
|
|
233
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
189
234
|
}
|
|
190
235
|
detectProtocol(path, expectedProtocol, protocolHint) {
|
|
236
|
+
var _a;
|
|
191
237
|
return __awaiter(this, void 0, void 0, function* () {
|
|
192
238
|
if (expectedProtocol === 'V1') {
|
|
193
239
|
if (yield this.probeProtocolV1(path)) {
|
|
194
240
|
this.deviceProtocol.set(path, 'V1');
|
|
195
241
|
return 'V1';
|
|
196
242
|
}
|
|
243
|
+
yield this.closeConnectionAfterProbe(path);
|
|
197
244
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
198
245
|
}
|
|
199
246
|
if (expectedProtocol === 'V2') {
|
|
200
|
-
|
|
201
|
-
|
|
247
|
+
for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
|
|
248
|
+
if (yield this.probeProtocolV2(path)) {
|
|
249
|
+
this.deviceProtocol.set(path, 'V2');
|
|
250
|
+
return 'V2';
|
|
251
|
+
}
|
|
252
|
+
if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
|
|
253
|
+
yield this.resetConnectionAfterProbe(path);
|
|
254
|
+
(_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}`);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
yield this.closeConnectionAfterProbe(path);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
this.deviceProtocol.delete(path);
|
|
261
|
+
throw this.createProtocolProbeTimeoutError(expectedProtocol, EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS);
|
|
202
262
|
}
|
|
203
263
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
204
|
-
for (const protocol of probeOrder) {
|
|
264
|
+
for (const [index, protocol] of probeOrder.entries()) {
|
|
205
265
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(path) : yield this.probeProtocolV2(path);
|
|
206
266
|
if (detected) {
|
|
207
267
|
this.deviceProtocol.set(path, protocol);
|
|
208
268
|
return protocol;
|
|
209
269
|
}
|
|
270
|
+
if (index < probeOrder.length - 1) {
|
|
271
|
+
yield this.resetConnectionAfterProbe(path);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
yield this.closeConnectionAfterProbe(path);
|
|
275
|
+
}
|
|
210
276
|
}
|
|
211
277
|
this.deviceProtocol.delete(path);
|
|
212
278
|
throw this.createProtocolDetectionError();
|
|
@@ -273,7 +339,7 @@ class WebUsbTransport {
|
|
|
273
339
|
};
|
|
274
340
|
}
|
|
275
341
|
connectToDevice(path, first) {
|
|
276
|
-
var _a
|
|
342
|
+
var _a;
|
|
277
343
|
return __awaiter(this, void 0, void 0, function* () {
|
|
278
344
|
let device = yield this.findDevice(path);
|
|
279
345
|
if (!device.opened) {
|
|
@@ -297,33 +363,30 @@ class WebUsbTransport {
|
|
|
297
363
|
}
|
|
298
364
|
const endpoints = this.discoverEndpoints(device);
|
|
299
365
|
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
366
|
yield device.claimInterface(endpoints.interfaceNumber);
|
|
303
367
|
yield this.clearEndpointHalt(device, 'in', endpoints.endpointIn);
|
|
304
368
|
yield this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
305
369
|
});
|
|
306
370
|
}
|
|
307
371
|
closeOpenDevice(path) {
|
|
308
|
-
var _a, _b, _c, _d
|
|
372
|
+
var _a, _b, _c, _d;
|
|
309
373
|
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;
|
|
374
|
+
const current = (_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.device;
|
|
312
375
|
if (!(current === null || current === void 0 ? void 0 : current.opened))
|
|
313
376
|
return;
|
|
314
377
|
const endpoints = this.deviceEndpoints.get(path);
|
|
315
|
-
const ifaceNum = (
|
|
378
|
+
const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
|
|
316
379
|
try {
|
|
317
380
|
yield current.releaseInterface(ifaceNum);
|
|
318
381
|
}
|
|
319
382
|
catch (error) {
|
|
320
|
-
(
|
|
383
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[WebUsbTransport] releaseInterface before reconnect failed:', error);
|
|
321
384
|
}
|
|
322
385
|
try {
|
|
323
386
|
yield current.close();
|
|
324
387
|
}
|
|
325
388
|
catch (error) {
|
|
326
|
-
(
|
|
389
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[WebUsbTransport] close before reconnect failed:', error);
|
|
327
390
|
}
|
|
328
391
|
});
|
|
329
392
|
}
|
|
@@ -338,8 +401,18 @@ class WebUsbTransport {
|
|
|
338
401
|
}
|
|
339
402
|
});
|
|
340
403
|
}
|
|
404
|
+
assertAcquired(path) {
|
|
405
|
+
if (!this.acquiredPaths.has(path)) {
|
|
406
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device is not acquired for ${path}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
341
409
|
post(session, name, data) {
|
|
342
410
|
return __awaiter(this, void 0, void 0, function* () {
|
|
411
|
+
this.assertAcquired(session);
|
|
412
|
+
if (this.deviceProtocol.get(session) === 'V2') {
|
|
413
|
+
yield this.sendProtocolV2UsbFlowControl(session, name, data);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
343
416
|
yield this.call(session, name, data);
|
|
344
417
|
});
|
|
345
418
|
}
|
|
@@ -368,6 +441,7 @@ class WebUsbTransport {
|
|
|
368
441
|
var _a;
|
|
369
442
|
return __awaiter(this, void 0, void 0, function* () {
|
|
370
443
|
this.Log.debug(`[WebUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
|
|
444
|
+
this.staleProtocolPaths.add(path);
|
|
371
445
|
yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
372
446
|
try {
|
|
373
447
|
const currentDevice = yield this.findDevice(path);
|
|
@@ -485,34 +559,33 @@ class WebUsbTransport {
|
|
|
485
559
|
throw lastError;
|
|
486
560
|
});
|
|
487
561
|
}
|
|
488
|
-
|
|
489
|
-
var _a
|
|
562
|
+
transferInOnce(path, length) {
|
|
563
|
+
var _a;
|
|
490
564
|
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);
|
|
565
|
+
const device = yield this.findDevice(path);
|
|
566
|
+
if (!device.opened) {
|
|
567
|
+
throw new Error('USBDevice is not open for transferIn');
|
|
510
568
|
}
|
|
569
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
570
|
+
const endpointIn = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointIn) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
571
|
+
const result = yield device.transferIn(endpointIn, length);
|
|
572
|
+
return this.getTransferInData(result);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
closeConnectionAfterProbe(path) {
|
|
576
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
577
|
+
yield this.rotateProtocolV2UsbGeneration(path, 'WebUSB protocol probe reset');
|
|
578
|
+
yield this.closeOpenDevice(path);
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
resetConnectionAfterProbe(path) {
|
|
582
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
583
|
+
yield this.closeConnectionAfterProbe(path);
|
|
511
584
|
yield this.getConnectedDevices();
|
|
512
585
|
yield this.connect(path, false);
|
|
513
586
|
});
|
|
514
587
|
}
|
|
515
|
-
withProtocolReadTimeout(
|
|
588
|
+
withProtocolReadTimeout(path, promise, timeoutMs, protocol, onTimeout) {
|
|
516
589
|
return __awaiter(this, void 0, void 0, function* () {
|
|
517
590
|
let timer;
|
|
518
591
|
let timedOut = false;
|
|
@@ -527,11 +600,19 @@ class WebUsbTransport {
|
|
|
527
600
|
return yield Promise.race([
|
|
528
601
|
guardedPromise,
|
|
529
602
|
new Promise((_, reject) => {
|
|
530
|
-
timer = setTimeout(() => {
|
|
603
|
+
timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
531
604
|
timedOut = true;
|
|
532
605
|
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
|
|
606
|
+
if (protocol === 'V1') {
|
|
607
|
+
try {
|
|
608
|
+
yield this.resetConnectionAfterProbe(path);
|
|
609
|
+
}
|
|
610
|
+
catch (error) {
|
|
611
|
+
this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
533
614
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
534
|
-
}, timeoutMs);
|
|
615
|
+
}), timeoutMs);
|
|
535
616
|
}),
|
|
536
617
|
]);
|
|
537
618
|
}
|
|
@@ -547,7 +628,9 @@ class WebUsbTransport {
|
|
|
547
628
|
return false;
|
|
548
629
|
}
|
|
549
630
|
try {
|
|
550
|
-
yield this.callProtocolV1(path, 'Initialize', {}, {
|
|
631
|
+
yield this.callProtocolV1(path, 'Initialize', {}, {
|
|
632
|
+
timeoutMs: PROTOCOL_V1_PROBE_TIMEOUT,
|
|
633
|
+
});
|
|
551
634
|
return true;
|
|
552
635
|
}
|
|
553
636
|
catch (_error) {
|
|
@@ -562,10 +645,9 @@ class WebUsbTransport {
|
|
|
562
645
|
}
|
|
563
646
|
return transport.probeProtocolV2({
|
|
564
647
|
call: (name, data, options) => this.callProtocolV2(path, name, data, options),
|
|
565
|
-
timeoutMs:
|
|
648
|
+
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
|
|
566
649
|
logger: this.Log,
|
|
567
650
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
568
|
-
onProbeFailed: () => this.resetConnectionAfterProbe(path),
|
|
569
651
|
});
|
|
570
652
|
});
|
|
571
653
|
}
|
|
@@ -574,6 +656,7 @@ class WebUsbTransport {
|
|
|
574
656
|
if (this.messages == null) {
|
|
575
657
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
576
658
|
}
|
|
659
|
+
this.assertAcquired(path);
|
|
577
660
|
const device = yield this.findDevice(path);
|
|
578
661
|
if (!device) {
|
|
579
662
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
@@ -582,9 +665,6 @@ class WebUsbTransport {
|
|
|
582
665
|
if (!protocol) {
|
|
583
666
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
|
|
584
667
|
}
|
|
585
|
-
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
586
|
-
this.Log.debug('transport call', createTransportCallLog(name, protocol));
|
|
587
|
-
}
|
|
588
668
|
if (protocol === 'V2') {
|
|
589
669
|
return this.callProtocolV2(path, name, data, options);
|
|
590
670
|
}
|
|
@@ -613,79 +693,8 @@ class WebUsbTransport {
|
|
|
613
693
|
});
|
|
614
694
|
}
|
|
615
695
|
callProtocolV2(path, name, data, options) {
|
|
616
|
-
var _a;
|
|
617
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
618
|
-
const protocolV1Messages = this.messages;
|
|
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
696
|
return __awaiter(this, void 0, void 0, function* () {
|
|
665
|
-
|
|
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;
|
|
697
|
+
return this.callProtocolV2Usb(path, name, data, options);
|
|
689
698
|
});
|
|
690
699
|
}
|
|
691
700
|
receiveData(path, timeoutMs) {
|
|
@@ -726,45 +735,83 @@ class WebUsbTransport {
|
|
|
726
735
|
});
|
|
727
736
|
}
|
|
728
737
|
release(path) {
|
|
729
|
-
var _a, _b;
|
|
730
738
|
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);
|
|
739
|
+
this.acquiredPaths.delete(path);
|
|
740
|
+
yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
|
|
741
|
+
yield this.closeOpenDevice(path);
|
|
742
742
|
this.deviceEndpoints.delete(path);
|
|
743
743
|
});
|
|
744
744
|
}
|
|
745
|
+
getProtocolV2UsbSchemas() {
|
|
746
|
+
if (!this.messages || !this.messagesV2) {
|
|
747
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
protocolV1: this.messages,
|
|
751
|
+
protocolV2: this.messagesV2,
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
getProtocolV2UsbLogger() {
|
|
755
|
+
return this.Log;
|
|
756
|
+
}
|
|
757
|
+
writeProtocolV2UsbPacket(path, frame, _context) {
|
|
758
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
759
|
+
yield this.transferOutOnce(path, frame);
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
readProtocolV2UsbPacket(path, _context) {
|
|
763
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
764
|
+
const dataView = yield this.transferInOnce(path, transport.PROTOCOL_V2_FRAME_MAX_BYTES);
|
|
765
|
+
return new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
resetProtocolV2UsbNativeLink(path, _reason) {
|
|
769
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
770
|
+
yield this.closeOpenDevice(path);
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
onProtocolV2UsbLinkInvalidated(path, reason) {
|
|
774
|
+
var _a;
|
|
775
|
+
this.deviceProtocol.delete(path);
|
|
776
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 link invalidated: ${path}`, reason);
|
|
777
|
+
}
|
|
778
|
+
createProtocolV2UsbTimeoutError(name, timeoutMs) {
|
|
779
|
+
return new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
|
|
780
|
+
}
|
|
745
781
|
getProtocolType(path) {
|
|
746
782
|
return this.deviceProtocol.get(path);
|
|
747
783
|
}
|
|
748
784
|
}
|
|
749
785
|
|
|
786
|
+
const BLE_ATT_HEADER_BYTES = 3;
|
|
787
|
+
function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapacity) {
|
|
788
|
+
if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
|
|
789
|
+
return fallbackPacketCapacity;
|
|
790
|
+
}
|
|
791
|
+
return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
|
|
792
|
+
}
|
|
793
|
+
|
|
750
794
|
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
751
795
|
function inferProtocolHintFromDeviceName(name) {
|
|
752
796
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
753
797
|
}
|
|
754
798
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
755
|
-
const
|
|
799
|
+
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
800
|
+
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
756
801
|
const BLE_WRITE_DELAY_MS = 5;
|
|
757
|
-
const
|
|
758
|
-
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
802
|
+
const PROTOCOL_PROBE_TIMEOUT_MS = 3000;
|
|
759
803
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
760
804
|
class ElectronBleTransport {
|
|
761
805
|
constructor() {
|
|
762
806
|
this.name = 'ElectronBleTransport';
|
|
763
807
|
this.configured = false;
|
|
764
808
|
this.runPromise = null;
|
|
809
|
+
this.runPromiseDeviceId = null;
|
|
765
810
|
this.connectedDevices = new Set();
|
|
766
811
|
this.deviceProtocol = new Map();
|
|
767
812
|
this.deviceProtocolHints = new Map();
|
|
813
|
+
this.deviceMtus = new Map();
|
|
814
|
+
this.devicePacketCapacities = new Map();
|
|
768
815
|
this.v1Buffers = new Map();
|
|
769
816
|
this.v2Assemblers = new Map();
|
|
770
817
|
this.v2FrameQueues = new Map();
|
|
@@ -786,11 +833,13 @@ class ElectronBleTransport {
|
|
|
786
833
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
787
834
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
|
|
788
835
|
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
789
|
-
yield this.
|
|
836
|
+
yield this.releaseNative(uuid);
|
|
790
837
|
}
|
|
791
838
|
}),
|
|
792
839
|
});
|
|
840
|
+
this.warnedMissingRelease = false;
|
|
793
841
|
this.notificationCleanups = new Map();
|
|
842
|
+
this.mtuCleanups = new Map();
|
|
794
843
|
this.disconnectCleanups = new Map();
|
|
795
844
|
this.notificationTokens = new Map();
|
|
796
845
|
this.nextNotificationToken = 1;
|
|
@@ -830,15 +879,27 @@ class ElectronBleTransport {
|
|
|
830
879
|
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
|
|
831
880
|
this.connectedDevices.delete(deviceId);
|
|
832
881
|
this.deviceProtocol.delete(deviceId);
|
|
882
|
+
this.deviceMtus.delete(deviceId);
|
|
883
|
+
this.devicePacketCapacities.delete(deviceId);
|
|
833
884
|
this.v1Buffers.delete(deviceId);
|
|
834
885
|
this.v2Assemblers.delete(deviceId);
|
|
835
886
|
this.resetProtocolV2Frames(deviceId);
|
|
836
887
|
this.notificationTokens.delete(deviceId);
|
|
888
|
+
if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
889
|
+
this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
890
|
+
this.runPromise = null;
|
|
891
|
+
this.runPromiseDeviceId = null;
|
|
892
|
+
}
|
|
837
893
|
const notifyCleanup = this.notificationCleanups.get(deviceId);
|
|
838
894
|
if (notifyCleanup) {
|
|
839
895
|
notifyCleanup();
|
|
840
896
|
this.notificationCleanups.delete(deviceId);
|
|
841
897
|
}
|
|
898
|
+
const mtuCleanup = this.mtuCleanups.get(deviceId);
|
|
899
|
+
if (mtuCleanup) {
|
|
900
|
+
mtuCleanup();
|
|
901
|
+
this.mtuCleanups.delete(deviceId);
|
|
902
|
+
}
|
|
842
903
|
const disconnectCleanup = this.disconnectCleanups.get(deviceId);
|
|
843
904
|
if (disconnectCleanup) {
|
|
844
905
|
disconnectCleanup();
|
|
@@ -859,10 +920,18 @@ class ElectronBleTransport {
|
|
|
859
920
|
this.configured = true;
|
|
860
921
|
}
|
|
861
922
|
configureProtocolV2(signedData) {
|
|
923
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
924
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
862
928
|
this._messagesV2 = parseConfigure(signedData);
|
|
863
|
-
this.
|
|
864
|
-
|
|
865
|
-
|
|
929
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
930
|
+
if (isReconfiguration) {
|
|
931
|
+
this.protocolV2Links
|
|
932
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
933
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
|
|
934
|
+
}
|
|
866
935
|
}
|
|
867
936
|
listen() {
|
|
868
937
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -894,7 +963,7 @@ class ElectronBleTransport {
|
|
|
894
963
|
});
|
|
895
964
|
}
|
|
896
965
|
acquire(input) {
|
|
897
|
-
var _a, _b, _c, _d, _e, _f;
|
|
966
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
898
967
|
return __awaiter(this, void 0, void 0, function* () {
|
|
899
968
|
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
900
969
|
if (!uuid) {
|
|
@@ -906,8 +975,8 @@ class ElectronBleTransport {
|
|
|
906
975
|
if (forceCleanRunPromise && this.runPromise) {
|
|
907
976
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
908
977
|
this.runPromise.reject(error);
|
|
909
|
-
this.rejectAllProtocolV2Frames(error);
|
|
910
978
|
this.runPromise = null;
|
|
979
|
+
this.runPromiseDeviceId = null;
|
|
911
980
|
}
|
|
912
981
|
try {
|
|
913
982
|
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
@@ -919,7 +988,7 @@ class ElectronBleTransport {
|
|
|
919
988
|
}
|
|
920
989
|
const protocolHint = expectedProtocol
|
|
921
990
|
? undefined
|
|
922
|
-
: (_b = this.deviceProtocolHints.get(uuid)) !== null &&
|
|
991
|
+
: (_c = (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(uuid)) !== null && _c !== void 0 ? _c : inferProtocolHintFromDeviceName(device.name);
|
|
923
992
|
if (protocolHint) {
|
|
924
993
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
925
994
|
}
|
|
@@ -930,16 +999,31 @@ class ElectronBleTransport {
|
|
|
930
999
|
catch (error) {
|
|
931
1000
|
this.handleBluetoothError(error);
|
|
932
1001
|
}
|
|
1002
|
+
const mtuCleanup = this.createMtuSubscription(uuid);
|
|
1003
|
+
if (mtuCleanup) {
|
|
1004
|
+
this.mtuCleanups.set(uuid, mtuCleanup);
|
|
1005
|
+
}
|
|
933
1006
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
934
|
-
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
|
|
1007
|
+
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
935
1008
|
yield window.desktopApi.nobleBle.subscribe(uuid);
|
|
1009
|
+
yield this.refreshBlePacketCapacity(uuid);
|
|
936
1010
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
937
1011
|
this.notificationCleanups.set(uuid, cleanup);
|
|
1012
|
+
const connectionToken = this.notificationTokens.get(uuid);
|
|
1013
|
+
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
1014
|
+
if (protocolType === 'V2') {
|
|
1015
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[Electron BLE] Protocol V2 write configured', {
|
|
1016
|
+
writeMode: 'withoutResponse',
|
|
1017
|
+
negotiatedMtu: this.deviceMtus.get(uuid),
|
|
1018
|
+
packetCapacity: (_e = this.devicePacketCapacities.get(uuid)) !== null && _e !== void 0 ? _e : BLE_PACKET_SIZE_FALLBACK,
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
938
1021
|
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
|
|
939
1022
|
var _a;
|
|
940
|
-
if (disconnectedDevice.id === uuid
|
|
1023
|
+
if (disconnectedDevice.id === uuid &&
|
|
1024
|
+
this.notificationTokens.get(uuid) === connectionToken) {
|
|
941
1025
|
this.cleanupDeviceState(uuid);
|
|
942
|
-
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(
|
|
1026
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
943
1027
|
name: disconnectedDevice.name,
|
|
944
1028
|
id: disconnectedDevice.id,
|
|
945
1029
|
connectId: disconnectedDevice.id,
|
|
@@ -947,35 +1031,46 @@ class ElectronBleTransport {
|
|
|
947
1031
|
}
|
|
948
1032
|
});
|
|
949
1033
|
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
1034
|
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
957
1035
|
}
|
|
958
1036
|
catch (error) {
|
|
959
|
-
(
|
|
1037
|
+
(_f = this.Log) === null || _f === void 0 ? void 0 : _f.error('[Electron BLE] acquire failed:', error);
|
|
960
1038
|
try {
|
|
961
|
-
if (((
|
|
1039
|
+
if (((_g = window.desktopApi) === null || _g === void 0 ? void 0 : _g.nobleBle) && this.connectedDevices.has(uuid)) {
|
|
962
1040
|
yield window.desktopApi.nobleBle.unsubscribe(uuid);
|
|
963
1041
|
yield window.desktopApi.nobleBle.disconnect(uuid);
|
|
964
1042
|
}
|
|
965
1043
|
}
|
|
966
1044
|
catch (cleanupError) {
|
|
967
|
-
(
|
|
1045
|
+
(_h = this.Log) === null || _h === void 0 ? void 0 : _h.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
|
|
968
1046
|
}
|
|
969
1047
|
this.cleanupDeviceState(uuid);
|
|
970
1048
|
throw error;
|
|
971
1049
|
}
|
|
972
1050
|
});
|
|
973
1051
|
}
|
|
974
|
-
release(id) {
|
|
975
|
-
var _a
|
|
1052
|
+
release(id, _onclose, keepSession) {
|
|
1053
|
+
var _a;
|
|
976
1054
|
return __awaiter(this, void 0, void 0, function* () {
|
|
977
1055
|
try {
|
|
978
1056
|
yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
|
|
1057
|
+
yield this.releaseLogical(id, keepSession);
|
|
1058
|
+
}
|
|
1059
|
+
catch (error) {
|
|
1060
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] release failed:', error);
|
|
1061
|
+
this.cleanupDeviceState(id);
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
disconnect(id) {
|
|
1066
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1067
|
+
return this.releaseNative(id);
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
releaseNative(id) {
|
|
1071
|
+
var _a, _b;
|
|
1072
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1073
|
+
try {
|
|
979
1074
|
if (this.connectedDevices.has(id)) {
|
|
980
1075
|
if ((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) {
|
|
981
1076
|
yield window.desktopApi.nobleBle.unsubscribe(id);
|
|
@@ -990,11 +1085,35 @@ class ElectronBleTransport {
|
|
|
990
1085
|
}
|
|
991
1086
|
});
|
|
992
1087
|
}
|
|
1088
|
+
releaseLogical(id, keepSession) {
|
|
1089
|
+
var _a, _b, _c, _d;
|
|
1090
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1091
|
+
try {
|
|
1092
|
+
if (!this.connectedDevices.has(id))
|
|
1093
|
+
return;
|
|
1094
|
+
this.cleanupDeviceState(id);
|
|
1095
|
+
const release = (_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.release;
|
|
1096
|
+
if (!release) {
|
|
1097
|
+
if (!this.warnedMissingRelease) {
|
|
1098
|
+
this.warnedMissingRelease = true;
|
|
1099
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] desktopApi.nobleBle.release is missing from the preload bridge; ' +
|
|
1100
|
+
'keep-alive idle countdown will never start — map NOBLE_BLE_RELEASE in the desktop preload');
|
|
1101
|
+
}
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
yield release(id, keepSession);
|
|
1105
|
+
}
|
|
1106
|
+
catch (error) {
|
|
1107
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] logical release failed:', error);
|
|
1108
|
+
this.cleanupDeviceState(id);
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
993
1112
|
createProtocolMismatchError(expected) {
|
|
994
1113
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
995
1114
|
}
|
|
996
1115
|
createProtocolDetectionError() {
|
|
997
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1
|
|
1116
|
+
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
1117
|
}
|
|
999
1118
|
clearProbeProtocol(uuid, protocol) {
|
|
1000
1119
|
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
@@ -1013,9 +1132,12 @@ class ElectronBleTransport {
|
|
|
1013
1132
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1014
1133
|
}
|
|
1015
1134
|
if (expectedProtocol === 'V2') {
|
|
1016
|
-
this.
|
|
1017
|
-
|
|
1018
|
-
|
|
1135
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1136
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1137
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
1138
|
+
return 'V2';
|
|
1139
|
+
}
|
|
1140
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1019
1141
|
}
|
|
1020
1142
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1021
1143
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
@@ -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) {
|