@onekeyfe/hd-transport-web-device 1.2.0-alpha.16 → 1.2.0-alpha.161
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 +551 -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 +19 -4
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +169 -15
- package/dist/index.js +451 -296
- package/dist/transportLog.d.ts +1 -6
- package/dist/transportLog.d.ts.map +1 -1
- package/dist/webusb.d.ts +25 -11
- 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 +291 -130
- package/src/transportLog.ts +1 -11
- package/src/webusb.ts +327 -205
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,39 @@ 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
|
+
activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.emitDeviceDisconnect(path, event.device);
|
|
72
|
+
});
|
|
65
73
|
}
|
|
66
|
-
class WebUsbTransport {
|
|
74
|
+
class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
67
75
|
constructor() {
|
|
76
|
+
super({
|
|
77
|
+
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
78
|
+
maxFrameBytes: transport.PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
79
|
+
logPrefix: 'ProtocolV2 WebUSB',
|
|
80
|
+
});
|
|
68
81
|
this.deviceProtocol = new Map();
|
|
69
82
|
this.deviceProtocolHints = new Map();
|
|
70
|
-
this.
|
|
71
|
-
this.
|
|
72
|
-
this.
|
|
83
|
+
this.staleProtocolPaths = new Set();
|
|
84
|
+
this.acquiredPaths = new Set();
|
|
85
|
+
this.probedDeviceObjects = new Map();
|
|
73
86
|
this.deviceEndpoints = new Map();
|
|
74
|
-
this.mockSerialPaths = new WeakMap();
|
|
75
|
-
this.mockSerialCounter = 0;
|
|
76
87
|
this.name = 'WebUsbTransport';
|
|
77
88
|
this.stopped = false;
|
|
78
89
|
this.configured = false;
|
|
@@ -81,13 +92,27 @@ class WebUsbTransport {
|
|
|
81
92
|
this.endpointId = ENDPOINT_ID;
|
|
82
93
|
this.interfaceId = INTERFACE_ID;
|
|
83
94
|
}
|
|
84
|
-
init(logger) {
|
|
95
|
+
init(logger, emitter) {
|
|
85
96
|
this.Log = logger;
|
|
97
|
+
this.emitter = emitter;
|
|
86
98
|
const { usb } = navigator;
|
|
87
99
|
if (!usb) {
|
|
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
|
+
emitDeviceDisconnect(path, device) {
|
|
106
|
+
var _a, _b;
|
|
107
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
108
|
+
name: (_b = device === null || device === void 0 ? void 0 : device.productName) !== null && _b !== void 0 ? _b : '',
|
|
109
|
+
id: path,
|
|
110
|
+
connectId: path,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
markProtocolStale(path) {
|
|
114
|
+
this.staleProtocolPaths.add(path);
|
|
115
|
+
this.probedDeviceObjects.delete(path);
|
|
91
116
|
}
|
|
92
117
|
configure(signedData) {
|
|
93
118
|
const messages = parseConfigure$1(signedData);
|
|
@@ -95,9 +120,18 @@ class WebUsbTransport {
|
|
|
95
120
|
this.messages = messages;
|
|
96
121
|
}
|
|
97
122
|
configureProtocolV2(signedData) {
|
|
123
|
+
var _a;
|
|
124
|
+
const schemaSource = typeof signedData === 'string'
|
|
125
|
+
? signedData
|
|
126
|
+
: (_a = JSON.stringify(signedData)) !== null && _a !== void 0 ? _a : String(signedData);
|
|
127
|
+
if (schemaSource === this.protocolV2SchemaSource)
|
|
128
|
+
return;
|
|
129
|
+
const hadProtocolV2Schema = this.protocolV2SchemaSource !== undefined;
|
|
98
130
|
this.messagesV2 = parseConfigure$1(signedData);
|
|
99
|
-
this.
|
|
100
|
-
|
|
131
|
+
this.protocolV2SchemaSource = schemaSource;
|
|
132
|
+
if (!hadProtocolV2Schema)
|
|
133
|
+
return;
|
|
134
|
+
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
135
|
}
|
|
102
136
|
promptDeviceAccess() {
|
|
103
137
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -119,20 +153,6 @@ class WebUsbTransport {
|
|
|
119
153
|
return this.deviceList;
|
|
120
154
|
});
|
|
121
155
|
}
|
|
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
156
|
getConnectedDevices() {
|
|
137
157
|
return __awaiter(this, void 0, void 0, function* () {
|
|
138
158
|
if (!this.usb)
|
|
@@ -142,41 +162,74 @@ class WebUsbTransport {
|
|
|
142
162
|
const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
|
|
143
163
|
return isOneKey && !hdShared.isKnownTrezorWebUsbDevice(dev);
|
|
144
164
|
});
|
|
145
|
-
this.deviceList = onekeyDevices.
|
|
146
|
-
const path =
|
|
147
|
-
|
|
165
|
+
this.deviceList = onekeyDevices.flatMap(device => {
|
|
166
|
+
const path = hdShared.resolveOneKeyUsbDevicePath(device);
|
|
167
|
+
if (!path)
|
|
168
|
+
return [];
|
|
169
|
+
const protocolHint = hdShared.inferProtocolHintFromUsbId(device.vendorId, device.productId);
|
|
148
170
|
if (protocolHint) {
|
|
149
171
|
this.deviceProtocolHints.set(path, protocolHint);
|
|
150
172
|
}
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
173
|
+
return [
|
|
174
|
+
{
|
|
175
|
+
path,
|
|
176
|
+
device,
|
|
177
|
+
commType: 'webusb',
|
|
178
|
+
},
|
|
179
|
+
];
|
|
156
180
|
});
|
|
157
181
|
return this.deviceList;
|
|
158
182
|
});
|
|
159
183
|
}
|
|
160
184
|
acquire(input) {
|
|
161
|
-
var _a, _b, _c;
|
|
185
|
+
var _a, _b, _c, _d, _e, _f;
|
|
162
186
|
return __awaiter(this, void 0, void 0, function* () {
|
|
163
187
|
if (!input.path)
|
|
164
188
|
return;
|
|
165
189
|
try {
|
|
190
|
+
yield this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
|
|
166
191
|
yield this.closeOpenDevice(input.path);
|
|
167
192
|
yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
193
|
+
if (input.forceProtocolDetection) {
|
|
194
|
+
this.staleProtocolPaths.delete(input.path);
|
|
195
|
+
this.deviceProtocol.delete(input.path);
|
|
196
|
+
this.deviceProtocolHints.delete(input.path);
|
|
197
|
+
}
|
|
198
|
+
if (this.staleProtocolPaths.has(input.path)) {
|
|
199
|
+
this.staleProtocolPaths.delete(input.path);
|
|
200
|
+
this.deviceProtocol.delete(input.path);
|
|
201
|
+
}
|
|
202
|
+
if (this.deviceProtocol.has(input.path)) {
|
|
203
|
+
const currentDevice = (_b = this.deviceList.find(d => d.path === input.path)) === null || _b === void 0 ? void 0 : _b.device;
|
|
204
|
+
if (!currentDevice || currentDevice !== this.probedDeviceObjects.get(input.path)) {
|
|
205
|
+
this.deviceProtocol.delete(input.path);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const cachedProtocol = this.deviceProtocol.get(input.path);
|
|
209
|
+
if (cachedProtocol && input.expectedProtocol && cachedProtocol !== input.expectedProtocol) {
|
|
210
|
+
this.deviceProtocol.delete(input.path);
|
|
174
211
|
}
|
|
175
|
-
|
|
212
|
+
if (!this.deviceProtocol.has(input.path)) {
|
|
213
|
+
const usbDevice = (_c = this.deviceList.find(device => device.path === input.path)) === null || _c === void 0 ? void 0 : _c.device;
|
|
214
|
+
const protocolHint = input.expectedProtocol
|
|
215
|
+
? undefined
|
|
216
|
+
: (_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);
|
|
217
|
+
if (protocolHint) {
|
|
218
|
+
this.deviceProtocolHints.set(input.path, protocolHint);
|
|
219
|
+
}
|
|
220
|
+
yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
|
|
221
|
+
const probedDevice = (_f = this.deviceList.find(d => d.path === input.path)) === null || _f === void 0 ? void 0 : _f.device;
|
|
222
|
+
if (probedDevice) {
|
|
223
|
+
this.probedDeviceObjects.set(input.path, probedDevice);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
this.acquiredPaths.add(input.path);
|
|
176
227
|
return yield Promise.resolve(input.path);
|
|
177
228
|
}
|
|
178
229
|
catch (e) {
|
|
179
230
|
this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
|
|
231
|
+
this.acquiredPaths.delete(input.path);
|
|
232
|
+
yield this.closeOpenDevice(input.path);
|
|
180
233
|
throw e;
|
|
181
234
|
}
|
|
182
235
|
});
|
|
@@ -184,29 +237,53 @@ class WebUsbTransport {
|
|
|
184
237
|
createProtocolMismatchError(expected) {
|
|
185
238
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
186
239
|
}
|
|
240
|
+
createProtocolProbeTimeoutError(expected, attempts) {
|
|
241
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInitializeFailed, `Protocol ${expected} probe timeout after ${attempts} attempts`);
|
|
242
|
+
}
|
|
187
243
|
createProtocolDetectionError() {
|
|
188
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.
|
|
244
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
189
245
|
}
|
|
190
246
|
detectProtocol(path, expectedProtocol, protocolHint) {
|
|
247
|
+
var _a;
|
|
191
248
|
return __awaiter(this, void 0, void 0, function* () {
|
|
192
249
|
if (expectedProtocol === 'V1') {
|
|
193
250
|
if (yield this.probeProtocolV1(path)) {
|
|
194
251
|
this.deviceProtocol.set(path, 'V1');
|
|
195
252
|
return 'V1';
|
|
196
253
|
}
|
|
254
|
+
yield this.closeConnectionAfterProbe(path);
|
|
197
255
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
198
256
|
}
|
|
199
257
|
if (expectedProtocol === 'V2') {
|
|
200
|
-
|
|
201
|
-
|
|
258
|
+
for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
|
|
259
|
+
if (yield this.probeProtocolV2(path)) {
|
|
260
|
+
this.deviceProtocol.set(path, 'V2');
|
|
261
|
+
return 'V2';
|
|
262
|
+
}
|
|
263
|
+
if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
|
|
264
|
+
yield this.resetConnectionAfterProbe(path);
|
|
265
|
+
(_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}`);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
yield this.closeConnectionAfterProbe(path);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
this.deviceProtocol.delete(path);
|
|
272
|
+
throw this.createProtocolProbeTimeoutError(expectedProtocol, EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS);
|
|
202
273
|
}
|
|
203
274
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
204
|
-
for (const protocol of probeOrder) {
|
|
275
|
+
for (const [index, protocol] of probeOrder.entries()) {
|
|
205
276
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(path) : yield this.probeProtocolV2(path);
|
|
206
277
|
if (detected) {
|
|
207
278
|
this.deviceProtocol.set(path, protocol);
|
|
208
279
|
return protocol;
|
|
209
280
|
}
|
|
281
|
+
if (index < probeOrder.length - 1) {
|
|
282
|
+
yield this.resetConnectionAfterProbe(path);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
yield this.closeConnectionAfterProbe(path);
|
|
286
|
+
}
|
|
210
287
|
}
|
|
211
288
|
this.deviceProtocol.delete(path);
|
|
212
289
|
throw this.createProtocolDetectionError();
|
|
@@ -273,7 +350,7 @@ class WebUsbTransport {
|
|
|
273
350
|
};
|
|
274
351
|
}
|
|
275
352
|
connectToDevice(path, first) {
|
|
276
|
-
var _a
|
|
353
|
+
var _a;
|
|
277
354
|
return __awaiter(this, void 0, void 0, function* () {
|
|
278
355
|
let device = yield this.findDevice(path);
|
|
279
356
|
if (!device.opened) {
|
|
@@ -297,33 +374,30 @@ class WebUsbTransport {
|
|
|
297
374
|
}
|
|
298
375
|
const endpoints = this.discoverEndpoints(device);
|
|
299
376
|
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
377
|
yield device.claimInterface(endpoints.interfaceNumber);
|
|
303
378
|
yield this.clearEndpointHalt(device, 'in', endpoints.endpointIn);
|
|
304
379
|
yield this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
|
|
305
380
|
});
|
|
306
381
|
}
|
|
307
382
|
closeOpenDevice(path) {
|
|
308
|
-
var _a, _b, _c, _d
|
|
383
|
+
var _a, _b, _c, _d;
|
|
309
384
|
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;
|
|
385
|
+
const current = (_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.device;
|
|
312
386
|
if (!(current === null || current === void 0 ? void 0 : current.opened))
|
|
313
387
|
return;
|
|
314
388
|
const endpoints = this.deviceEndpoints.get(path);
|
|
315
|
-
const ifaceNum = (
|
|
389
|
+
const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
|
|
316
390
|
try {
|
|
317
391
|
yield current.releaseInterface(ifaceNum);
|
|
318
392
|
}
|
|
319
393
|
catch (error) {
|
|
320
|
-
(
|
|
394
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[WebUsbTransport] releaseInterface before reconnect failed:', error);
|
|
321
395
|
}
|
|
322
396
|
try {
|
|
323
397
|
yield current.close();
|
|
324
398
|
}
|
|
325
399
|
catch (error) {
|
|
326
|
-
(
|
|
400
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[WebUsbTransport] close before reconnect failed:', error);
|
|
327
401
|
}
|
|
328
402
|
});
|
|
329
403
|
}
|
|
@@ -338,8 +412,18 @@ class WebUsbTransport {
|
|
|
338
412
|
}
|
|
339
413
|
});
|
|
340
414
|
}
|
|
415
|
+
assertAcquired(path) {
|
|
416
|
+
if (!this.acquiredPaths.has(path)) {
|
|
417
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device is not acquired for ${path}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
341
420
|
post(session, name, data) {
|
|
342
421
|
return __awaiter(this, void 0, void 0, function* () {
|
|
422
|
+
this.assertAcquired(session);
|
|
423
|
+
if (this.deviceProtocol.get(session) === 'V2') {
|
|
424
|
+
yield this.sendProtocolV2UsbFlowControl(session, name, data);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
343
427
|
yield this.call(session, name, data);
|
|
344
428
|
});
|
|
345
429
|
}
|
|
@@ -368,6 +452,7 @@ class WebUsbTransport {
|
|
|
368
452
|
var _a;
|
|
369
453
|
return __awaiter(this, void 0, void 0, function* () {
|
|
370
454
|
this.Log.debug(`[WebUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
|
|
455
|
+
this.staleProtocolPaths.add(path);
|
|
371
456
|
yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
372
457
|
try {
|
|
373
458
|
const currentDevice = yield this.findDevice(path);
|
|
@@ -485,34 +570,33 @@ class WebUsbTransport {
|
|
|
485
570
|
throw lastError;
|
|
486
571
|
});
|
|
487
572
|
}
|
|
488
|
-
|
|
489
|
-
var _a
|
|
573
|
+
transferInOnce(path, length) {
|
|
574
|
+
var _a;
|
|
490
575
|
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);
|
|
576
|
+
const device = yield this.findDevice(path);
|
|
577
|
+
if (!device.opened) {
|
|
578
|
+
throw new Error('USBDevice is not open for transferIn');
|
|
510
579
|
}
|
|
580
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
581
|
+
const endpointIn = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointIn) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
582
|
+
const result = yield device.transferIn(endpointIn, length);
|
|
583
|
+
return this.getTransferInData(result);
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
closeConnectionAfterProbe(path) {
|
|
587
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
588
|
+
yield this.rotateProtocolV2UsbGeneration(path, 'WebUSB protocol probe reset');
|
|
589
|
+
yield this.closeOpenDevice(path);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
resetConnectionAfterProbe(path) {
|
|
593
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
594
|
+
yield this.closeConnectionAfterProbe(path);
|
|
511
595
|
yield this.getConnectedDevices();
|
|
512
596
|
yield this.connect(path, false);
|
|
513
597
|
});
|
|
514
598
|
}
|
|
515
|
-
withProtocolReadTimeout(
|
|
599
|
+
withProtocolReadTimeout(path, promise, timeoutMs, protocol, onTimeout) {
|
|
516
600
|
return __awaiter(this, void 0, void 0, function* () {
|
|
517
601
|
let timer;
|
|
518
602
|
let timedOut = false;
|
|
@@ -527,11 +611,19 @@ class WebUsbTransport {
|
|
|
527
611
|
return yield Promise.race([
|
|
528
612
|
guardedPromise,
|
|
529
613
|
new Promise((_, reject) => {
|
|
530
|
-
timer = setTimeout(() => {
|
|
614
|
+
timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
531
615
|
timedOut = true;
|
|
532
616
|
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
|
|
617
|
+
if (protocol === 'V1') {
|
|
618
|
+
try {
|
|
619
|
+
yield this.resetConnectionAfterProbe(path);
|
|
620
|
+
}
|
|
621
|
+
catch (error) {
|
|
622
|
+
this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
533
625
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
534
|
-
}, timeoutMs);
|
|
626
|
+
}), timeoutMs);
|
|
535
627
|
}),
|
|
536
628
|
]);
|
|
537
629
|
}
|
|
@@ -547,7 +639,9 @@ class WebUsbTransport {
|
|
|
547
639
|
return false;
|
|
548
640
|
}
|
|
549
641
|
try {
|
|
550
|
-
yield this.callProtocolV1(path, 'Initialize', {}, {
|
|
642
|
+
yield this.callProtocolV1(path, 'Initialize', {}, {
|
|
643
|
+
timeoutMs: PROTOCOL_V1_PROBE_TIMEOUT,
|
|
644
|
+
});
|
|
551
645
|
return true;
|
|
552
646
|
}
|
|
553
647
|
catch (_error) {
|
|
@@ -562,10 +656,9 @@ class WebUsbTransport {
|
|
|
562
656
|
}
|
|
563
657
|
return transport.probeProtocolV2({
|
|
564
658
|
call: (name, data, options) => this.callProtocolV2(path, name, data, options),
|
|
565
|
-
timeoutMs:
|
|
659
|
+
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
|
|
566
660
|
logger: this.Log,
|
|
567
661
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
568
|
-
onProbeFailed: () => this.resetConnectionAfterProbe(path),
|
|
569
662
|
});
|
|
570
663
|
});
|
|
571
664
|
}
|
|
@@ -574,6 +667,7 @@ class WebUsbTransport {
|
|
|
574
667
|
if (this.messages == null) {
|
|
575
668
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
576
669
|
}
|
|
670
|
+
this.assertAcquired(path);
|
|
577
671
|
const device = yield this.findDevice(path);
|
|
578
672
|
if (!device) {
|
|
579
673
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
@@ -582,9 +676,6 @@ class WebUsbTransport {
|
|
|
582
676
|
if (!protocol) {
|
|
583
677
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
|
|
584
678
|
}
|
|
585
|
-
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
586
|
-
this.Log.debug('transport call', createTransportCallLog(name, protocol));
|
|
587
|
-
}
|
|
588
679
|
if (protocol === 'V2') {
|
|
589
680
|
return this.callProtocolV2(path, name, data, options);
|
|
590
681
|
}
|
|
@@ -613,79 +704,8 @@ class WebUsbTransport {
|
|
|
613
704
|
});
|
|
614
705
|
}
|
|
615
706
|
callProtocolV2(path, name, data, options) {
|
|
616
|
-
var _a;
|
|
617
707
|
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;
|
|
708
|
+
return this.callProtocolV2Usb(path, name, data, options);
|
|
689
709
|
});
|
|
690
710
|
}
|
|
691
711
|
receiveData(path, timeoutMs) {
|
|
@@ -726,45 +746,81 @@ class WebUsbTransport {
|
|
|
726
746
|
});
|
|
727
747
|
}
|
|
728
748
|
release(path) {
|
|
729
|
-
var _a, _b;
|
|
730
749
|
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);
|
|
750
|
+
this.acquiredPaths.delete(path);
|
|
751
|
+
yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
|
|
752
|
+
yield this.closeOpenDevice(path);
|
|
742
753
|
this.deviceEndpoints.delete(path);
|
|
743
754
|
});
|
|
744
755
|
}
|
|
756
|
+
getProtocolV2UsbSchemas() {
|
|
757
|
+
if (!this.messages || !this.messagesV2) {
|
|
758
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
759
|
+
}
|
|
760
|
+
return {
|
|
761
|
+
protocolV1: this.messages,
|
|
762
|
+
protocolV2: this.messagesV2,
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
getProtocolV2UsbLogger() {
|
|
766
|
+
return this.Log;
|
|
767
|
+
}
|
|
768
|
+
writeProtocolV2UsbPacket(path, frame, _context) {
|
|
769
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
770
|
+
yield this.transferOutOnce(path, frame);
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
readProtocolV2UsbPacket(path, _context) {
|
|
774
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
775
|
+
const dataView = yield this.transferInOnce(path, transport.PROTOCOL_V2_FRAME_MAX_BYTES);
|
|
776
|
+
return new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
resetProtocolV2UsbNativeLink(path, _reason) {
|
|
780
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
781
|
+
yield this.closeOpenDevice(path);
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
onProtocolV2UsbLinkInvalidated(path, reason) {
|
|
785
|
+
var _a;
|
|
786
|
+
this.deviceProtocol.delete(path);
|
|
787
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 link invalidated: ${path}`, reason);
|
|
788
|
+
}
|
|
789
|
+
createProtocolV2UsbTimeoutError(name, timeoutMs) {
|
|
790
|
+
return new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
|
|
791
|
+
}
|
|
745
792
|
getProtocolType(path) {
|
|
746
793
|
return this.deviceProtocol.get(path);
|
|
747
794
|
}
|
|
748
795
|
}
|
|
749
796
|
|
|
750
|
-
const
|
|
751
|
-
function
|
|
752
|
-
|
|
797
|
+
const BLE_ATT_HEADER_BYTES = 3;
|
|
798
|
+
function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapacity) {
|
|
799
|
+
if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
|
|
800
|
+
return fallbackPacketCapacity;
|
|
801
|
+
}
|
|
802
|
+
return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
|
|
753
803
|
}
|
|
804
|
+
|
|
805
|
+
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
754
806
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
755
|
-
const
|
|
807
|
+
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
808
|
+
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
756
809
|
const BLE_WRITE_DELAY_MS = 5;
|
|
757
|
-
const
|
|
758
|
-
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
810
|
+
const PROTOCOL_PROBE_TIMEOUT_MS = 3000;
|
|
759
811
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
760
812
|
class ElectronBleTransport {
|
|
761
813
|
constructor() {
|
|
762
814
|
this.name = 'ElectronBleTransport';
|
|
763
815
|
this.configured = false;
|
|
764
816
|
this.runPromise = null;
|
|
817
|
+
this.runPromiseDeviceId = null;
|
|
765
818
|
this.connectedDevices = new Set();
|
|
766
819
|
this.deviceProtocol = new Map();
|
|
767
820
|
this.deviceProtocolHints = new Map();
|
|
821
|
+
this.confirmedProtocolV2 = new Set();
|
|
822
|
+
this.deviceMtus = new Map();
|
|
823
|
+
this.devicePacketCapacities = new Map();
|
|
768
824
|
this.v1Buffers = new Map();
|
|
769
825
|
this.v2Assemblers = new Map();
|
|
770
826
|
this.v2FrameQueues = new Map();
|
|
@@ -786,12 +842,13 @@ class ElectronBleTransport {
|
|
|
786
842
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
787
843
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
|
|
788
844
|
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
789
|
-
yield this.
|
|
845
|
+
yield this.releaseNative(uuid);
|
|
790
846
|
}
|
|
791
847
|
}),
|
|
792
848
|
});
|
|
849
|
+
this.warnedMissingRelease = false;
|
|
793
850
|
this.notificationCleanups = new Map();
|
|
794
|
-
this.
|
|
851
|
+
this.mtuCleanups = new Map();
|
|
795
852
|
this.notificationTokens = new Map();
|
|
796
853
|
this.nextNotificationToken = 1;
|
|
797
854
|
}
|
|
@@ -830,19 +887,26 @@ class ElectronBleTransport {
|
|
|
830
887
|
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
|
|
831
888
|
this.connectedDevices.delete(deviceId);
|
|
832
889
|
this.deviceProtocol.delete(deviceId);
|
|
890
|
+
this.deviceMtus.delete(deviceId);
|
|
891
|
+
this.devicePacketCapacities.delete(deviceId);
|
|
833
892
|
this.v1Buffers.delete(deviceId);
|
|
834
893
|
this.v2Assemblers.delete(deviceId);
|
|
835
894
|
this.resetProtocolV2Frames(deviceId);
|
|
836
895
|
this.notificationTokens.delete(deviceId);
|
|
896
|
+
if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
897
|
+
this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
898
|
+
this.runPromise = null;
|
|
899
|
+
this.runPromiseDeviceId = null;
|
|
900
|
+
}
|
|
837
901
|
const notifyCleanup = this.notificationCleanups.get(deviceId);
|
|
838
902
|
if (notifyCleanup) {
|
|
839
903
|
notifyCleanup();
|
|
840
904
|
this.notificationCleanups.delete(deviceId);
|
|
841
905
|
}
|
|
842
|
-
const
|
|
843
|
-
if (
|
|
844
|
-
|
|
845
|
-
this.
|
|
906
|
+
const mtuCleanup = this.mtuCleanups.get(deviceId);
|
|
907
|
+
if (mtuCleanup) {
|
|
908
|
+
mtuCleanup();
|
|
909
|
+
this.mtuCleanups.delete(deviceId);
|
|
846
910
|
}
|
|
847
911
|
}
|
|
848
912
|
init(logger, emitter) {
|
|
@@ -852,17 +916,43 @@ class ElectronBleTransport {
|
|
|
852
916
|
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
853
917
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble BLE API is not available. Please ensure you are running in Electron with Noble support.');
|
|
854
918
|
}
|
|
919
|
+
this.subscribeHostDisconnects();
|
|
855
920
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Transport initialized');
|
|
856
921
|
}
|
|
922
|
+
subscribeHostDisconnects() {
|
|
923
|
+
var _a, _b, _c;
|
|
924
|
+
(_a = this.hostDisconnectCleanup) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
925
|
+
this.hostDisconnectCleanup = (_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.onDeviceDisconnected((disconnectedDevice) => {
|
|
926
|
+
var _a, _b, _c;
|
|
927
|
+
const uuid = disconnectedDevice === null || disconnectedDevice === void 0 ? void 0 : disconnectedDevice.id;
|
|
928
|
+
if (!uuid)
|
|
929
|
+
return;
|
|
930
|
+
this.cleanupDeviceState(uuid);
|
|
931
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Device link dropped:', uuid, (_b = disconnectedDevice.reason) !== null && _b !== void 0 ? _b : hdShared.EBleDisconnectReason.DeviceDisconnected);
|
|
932
|
+
(_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
933
|
+
name: disconnectedDevice.name,
|
|
934
|
+
id: uuid,
|
|
935
|
+
connectId: uuid,
|
|
936
|
+
});
|
|
937
|
+
});
|
|
938
|
+
}
|
|
857
939
|
configure(signedData) {
|
|
858
940
|
this._messages = parseConfigure(signedData);
|
|
859
941
|
this.configured = true;
|
|
860
942
|
}
|
|
861
943
|
configureProtocolV2(signedData) {
|
|
944
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
945
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
862
949
|
this._messagesV2 = parseConfigure(signedData);
|
|
863
|
-
this.
|
|
864
|
-
|
|
865
|
-
|
|
950
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
951
|
+
if (isReconfiguration) {
|
|
952
|
+
this.protocolV2Links
|
|
953
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
954
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
|
|
955
|
+
}
|
|
866
956
|
}
|
|
867
957
|
listen() {
|
|
868
958
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -880,10 +970,6 @@ class ElectronBleTransport {
|
|
|
880
970
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] enumerate found ${devices.length} device(s):`);
|
|
881
971
|
for (const dev of devices) {
|
|
882
972
|
(_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
973
|
}
|
|
888
974
|
return devices.map(device => toBleDescriptor(device));
|
|
889
975
|
}
|
|
@@ -894,7 +980,7 @@ class ElectronBleTransport {
|
|
|
894
980
|
});
|
|
895
981
|
}
|
|
896
982
|
acquire(input) {
|
|
897
|
-
var _a, _b, _c, _d, _e, _f;
|
|
983
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
898
984
|
return __awaiter(this, void 0, void 0, function* () {
|
|
899
985
|
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
900
986
|
if (!uuid) {
|
|
@@ -906,8 +992,8 @@ class ElectronBleTransport {
|
|
|
906
992
|
if (forceCleanRunPromise && this.runPromise) {
|
|
907
993
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
908
994
|
this.runPromise.reject(error);
|
|
909
|
-
this.rejectAllProtocolV2Frames(error);
|
|
910
995
|
this.runPromise = null;
|
|
996
|
+
this.runPromiseDeviceId = null;
|
|
911
997
|
}
|
|
912
998
|
try {
|
|
913
999
|
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
@@ -919,7 +1005,7 @@ class ElectronBleTransport {
|
|
|
919
1005
|
}
|
|
920
1006
|
const protocolHint = expectedProtocol
|
|
921
1007
|
? undefined
|
|
922
|
-
: (_b =
|
|
1008
|
+
: (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(uuid);
|
|
923
1009
|
if (protocolHint) {
|
|
924
1010
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
925
1011
|
}
|
|
@@ -930,52 +1016,64 @@ class ElectronBleTransport {
|
|
|
930
1016
|
catch (error) {
|
|
931
1017
|
this.handleBluetoothError(error);
|
|
932
1018
|
}
|
|
1019
|
+
const mtuCleanup = this.createMtuSubscription(uuid);
|
|
1020
|
+
if (mtuCleanup) {
|
|
1021
|
+
this.mtuCleanups.set(uuid, mtuCleanup);
|
|
1022
|
+
}
|
|
933
1023
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
934
|
-
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
|
|
1024
|
+
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
935
1025
|
yield window.desktopApi.nobleBle.subscribe(uuid);
|
|
1026
|
+
yield this.refreshBlePacketCapacity(uuid);
|
|
936
1027
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
937
1028
|
this.notificationCleanups.set(uuid, cleanup);
|
|
938
|
-
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
|
|
939
|
-
var _a;
|
|
940
|
-
if (disconnectedDevice.id === uuid) {
|
|
941
|
-
this.cleanupDeviceState(uuid);
|
|
942
|
-
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
|
|
943
|
-
name: disconnectedDevice.name,
|
|
944
|
-
id: disconnectedDevice.id,
|
|
945
|
-
connectId: disconnectedDevice.id,
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
});
|
|
949
|
-
this.disconnectCleanups.set(uuid, disconnectCleanup);
|
|
950
1029
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
951
|
-
(
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1030
|
+
if (protocolType === 'V2') {
|
|
1031
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[Electron BLE] Protocol V2 write configured', {
|
|
1032
|
+
writeMode: 'withoutResponse',
|
|
1033
|
+
negotiatedMtu: this.deviceMtus.get(uuid),
|
|
1034
|
+
packetCapacity: (_d = this.devicePacketCapacities.get(uuid)) !== null && _d !== void 0 ? _d : BLE_PACKET_SIZE_FALLBACK,
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
956
1037
|
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
957
1038
|
}
|
|
958
1039
|
catch (error) {
|
|
959
|
-
(
|
|
1040
|
+
(_e = this.Log) === null || _e === void 0 ? void 0 : _e.error('[Electron BLE] acquire failed:', error);
|
|
960
1041
|
try {
|
|
961
|
-
if (((
|
|
1042
|
+
if (((_f = window.desktopApi) === null || _f === void 0 ? void 0 : _f.nobleBle) && this.connectedDevices.has(uuid)) {
|
|
962
1043
|
yield window.desktopApi.nobleBle.unsubscribe(uuid);
|
|
963
1044
|
yield window.desktopApi.nobleBle.disconnect(uuid);
|
|
964
1045
|
}
|
|
965
1046
|
}
|
|
966
1047
|
catch (cleanupError) {
|
|
967
|
-
(
|
|
1048
|
+
(_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
|
|
968
1049
|
}
|
|
969
1050
|
this.cleanupDeviceState(uuid);
|
|
970
1051
|
throw error;
|
|
971
1052
|
}
|
|
972
1053
|
});
|
|
973
1054
|
}
|
|
974
|
-
release(id) {
|
|
975
|
-
var _a
|
|
1055
|
+
release(id, _onclose, keepSession) {
|
|
1056
|
+
var _a;
|
|
976
1057
|
return __awaiter(this, void 0, void 0, function* () {
|
|
977
1058
|
try {
|
|
978
1059
|
yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
|
|
1060
|
+
yield this.releaseLogical(id, keepSession);
|
|
1061
|
+
}
|
|
1062
|
+
catch (error) {
|
|
1063
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] release failed:', error);
|
|
1064
|
+
this.cleanupDeviceState(id);
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
disconnect(id) {
|
|
1069
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1070
|
+
return this.releaseNative(id);
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
releaseNative(id) {
|
|
1074
|
+
var _a, _b;
|
|
1075
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1076
|
+
try {
|
|
979
1077
|
if (this.connectedDevices.has(id)) {
|
|
980
1078
|
if ((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) {
|
|
981
1079
|
yield window.desktopApi.nobleBle.unsubscribe(id);
|
|
@@ -990,11 +1088,36 @@ class ElectronBleTransport {
|
|
|
990
1088
|
}
|
|
991
1089
|
});
|
|
992
1090
|
}
|
|
993
|
-
|
|
994
|
-
|
|
1091
|
+
releaseLogical(id, keepSession) {
|
|
1092
|
+
var _a, _b, _c, _d;
|
|
1093
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1094
|
+
try {
|
|
1095
|
+
if (!this.connectedDevices.has(id))
|
|
1096
|
+
return;
|
|
1097
|
+
this.cleanupDeviceState(id);
|
|
1098
|
+
const release = (_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.release;
|
|
1099
|
+
if (!release) {
|
|
1100
|
+
if (!this.warnedMissingRelease) {
|
|
1101
|
+
this.warnedMissingRelease = true;
|
|
1102
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] desktopApi.nobleBle.release is missing from the preload bridge; ' +
|
|
1103
|
+
'keep-alive idle countdown will never start — map NOBLE_BLE_RELEASE in the desktop preload');
|
|
1104
|
+
}
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
yield release(id, keepSession);
|
|
1108
|
+
}
|
|
1109
|
+
catch (error) {
|
|
1110
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] logical release failed:', error);
|
|
1111
|
+
this.cleanupDeviceState(id);
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
createProtocolMismatchError(expected, uuid) {
|
|
1116
|
+
const isStaleV2Bond = expected === 'V2' && this.confirmedProtocolV2.has(uuid);
|
|
1117
|
+
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
1118
|
}
|
|
996
1119
|
createProtocolDetectionError() {
|
|
997
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1
|
|
1120
|
+
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
1121
|
}
|
|
999
1122
|
clearProbeProtocol(uuid, protocol) {
|
|
1000
1123
|
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
@@ -1010,12 +1133,16 @@ class ElectronBleTransport {
|
|
|
1010
1133
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V1 (expected)`);
|
|
1011
1134
|
return 'V1';
|
|
1012
1135
|
}
|
|
1013
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1136
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1014
1137
|
}
|
|
1015
1138
|
if (expectedProtocol === 'V2') {
|
|
1016
|
-
this.
|
|
1017
|
-
|
|
1018
|
-
|
|
1139
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1140
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1141
|
+
this.confirmedProtocolV2.add(uuid);
|
|
1142
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
1143
|
+
return 'V2';
|
|
1144
|
+
}
|
|
1145
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1019
1146
|
}
|
|
1020
1147
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1021
1148
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
@@ -1026,6 +1153,9 @@ class ElectronBleTransport {
|
|
|
1026
1153
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
1027
1154
|
if (detected) {
|
|
1028
1155
|
this.deviceProtocol.set(uuid, protocol);
|
|
1156
|
+
if (protocol === 'V2') {
|
|
1157
|
+
this.confirmedProtocolV2.add(uuid);
|
|
1158
|
+
}
|
|
1029
1159
|
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
|
|
1030
1160
|
return protocol;
|
|
1031
1161
|
}
|
|
@@ -1049,38 +1179,22 @@ class ElectronBleTransport {
|
|
|
1049
1179
|
});
|
|
1050
1180
|
}
|
|
1051
1181
|
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1052
|
-
var _a
|
|
1182
|
+
var _a;
|
|
1053
1183
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1054
1184
|
yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
|
|
1055
1185
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
1056
1186
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1057
1187
|
this.resetProtocolV2Frames(uuid);
|
|
1058
|
-
if (this.runPromise) {
|
|
1188
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1059
1189
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1060
1190
|
this.runPromise.reject(error);
|
|
1061
1191
|
this.runPromise = null;
|
|
1192
|
+
this.runPromiseDeviceId = null;
|
|
1062
1193
|
}
|
|
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;
|
|
1194
|
+
if (!this.notificationCleanups.has(uuid)) {
|
|
1195
|
+
const cleanup = this.createNotificationSubscription(uuid);
|
|
1196
|
+
this.notificationCleanups.set(uuid, cleanup);
|
|
1081
1197
|
}
|
|
1082
|
-
const cleanup = this.createNotificationSubscription(uuid);
|
|
1083
|
-
this.notificationCleanups.set(uuid, cleanup);
|
|
1084
1198
|
});
|
|
1085
1199
|
}
|
|
1086
1200
|
probeProtocolV1(uuid) {
|
|
@@ -1091,12 +1205,12 @@ class ElectronBleTransport {
|
|
|
1091
1205
|
}
|
|
1092
1206
|
try {
|
|
1093
1207
|
this.deviceProtocol.set(uuid, 'V1');
|
|
1094
|
-
yield this.callProtocolV1(uuid, '
|
|
1208
|
+
yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1095
1209
|
return true;
|
|
1096
1210
|
}
|
|
1097
1211
|
catch (error) {
|
|
1098
1212
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1099
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V1
|
|
1213
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V1 GetFeatures probe failed:', error);
|
|
1100
1214
|
return false;
|
|
1101
1215
|
}
|
|
1102
1216
|
});
|
|
@@ -1126,25 +1240,6 @@ class ElectronBleTransport {
|
|
|
1126
1240
|
return detected;
|
|
1127
1241
|
});
|
|
1128
1242
|
}
|
|
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
1243
|
writeOnce(uuid, hexData) {
|
|
1149
1244
|
var _a;
|
|
1150
1245
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1152,7 +1247,51 @@ class ElectronBleTransport {
|
|
|
1152
1247
|
if (!nobleBle) {
|
|
1153
1248
|
throw new Error('Noble BLE API not available');
|
|
1154
1249
|
}
|
|
1155
|
-
yield nobleBle.write(uuid, hexData);
|
|
1250
|
+
yield nobleBle.write(uuid, hexData, { pacingDelayMs: 0 });
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
refreshBlePacketCapacity(uuid) {
|
|
1254
|
+
var _a, _b;
|
|
1255
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1256
|
+
const device = yield ((_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.getDevice(uuid));
|
|
1257
|
+
this.updateBlePacketCapacity(uuid, device === null || device === void 0 ? void 0 : device.mtu);
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
updateBlePacketCapacity(uuid, mtu) {
|
|
1261
|
+
const packetCapacity = resolveBlePacketCapacity(mtu, BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
|
|
1262
|
+
if (typeof mtu === 'number') {
|
|
1263
|
+
this.deviceMtus.set(uuid, mtu);
|
|
1264
|
+
}
|
|
1265
|
+
else {
|
|
1266
|
+
this.deviceMtus.delete(uuid);
|
|
1267
|
+
}
|
|
1268
|
+
this.devicePacketCapacities.set(uuid, packetCapacity);
|
|
1269
|
+
}
|
|
1270
|
+
createMtuSubscription(uuid) {
|
|
1271
|
+
var _a, _b;
|
|
1272
|
+
const onMtuChanged = (_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.onMtuChanged;
|
|
1273
|
+
if (!onMtuChanged)
|
|
1274
|
+
return undefined;
|
|
1275
|
+
return onMtuChanged(device => {
|
|
1276
|
+
if (device.id === uuid) {
|
|
1277
|
+
this.updateBlePacketCapacity(uuid, device.mtu);
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration) {
|
|
1282
|
+
var _a;
|
|
1283
|
+
const packetCapacity = (_a = this.devicePacketCapacities.get(uuid)) !== null && _a !== void 0 ? _a : BLE_PACKET_SIZE_FALLBACK;
|
|
1284
|
+
const shouldPace = !context.highThroughput;
|
|
1285
|
+
return transport.writeProtocolV2BleFrame({
|
|
1286
|
+
frame,
|
|
1287
|
+
packetCapacity,
|
|
1288
|
+
assertActive: assertCurrentGeneration,
|
|
1289
|
+
signal: context.signal,
|
|
1290
|
+
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
1291
|
+
initialDelayMs: shouldPace && frame.length <= packetCapacity ? BLE_WRITE_DELAY_MS : 0,
|
|
1292
|
+
burstSize: shouldPace ? 1 : undefined,
|
|
1293
|
+
burstPauseMs: shouldPace ? BLE_WRITE_DELAY_MS : 0,
|
|
1294
|
+
writePacket: packet => this.writeOnce(uuid, transport.bytesToHex(packet)),
|
|
1156
1295
|
});
|
|
1157
1296
|
}
|
|
1158
1297
|
handleNotification(deviceId, hexData) {
|
|
@@ -1163,7 +1302,7 @@ class ElectronBleTransport {
|
|
|
1163
1302
|
if (this.deviceProtocol.get(deviceId) === 'V2') {
|
|
1164
1303
|
this.rejectProtocolV2Frames(deviceId, error);
|
|
1165
1304
|
}
|
|
1166
|
-
else if (this.runPromise) {
|
|
1305
|
+
else if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
1167
1306
|
this.runPromise.reject(error);
|
|
1168
1307
|
}
|
|
1169
1308
|
return;
|
|
@@ -1215,16 +1354,8 @@ class ElectronBleTransport {
|
|
|
1215
1354
|
}
|
|
1216
1355
|
this.getProtocolV2FrameQueue(uuid).push(frame);
|
|
1217
1356
|
}
|
|
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
1357
|
resetProtocolV2Frames(uuid) {
|
|
1226
|
-
this.
|
|
1227
|
-
this.v2FramePromises.delete(uuid);
|
|
1358
|
+
this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
|
|
1228
1359
|
}
|
|
1229
1360
|
rejectProtocolV2Frames(uuid, error) {
|
|
1230
1361
|
this.v2FrameQueues.delete(uuid);
|
|
@@ -1257,17 +1388,19 @@ class ElectronBleTransport {
|
|
|
1257
1388
|
const result = this.processProtocolV1Notification(deviceId, hexData);
|
|
1258
1389
|
if (result.error) {
|
|
1259
1390
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V1 packet processing error:', result.error);
|
|
1260
|
-
if (this.runPromise) {
|
|
1391
|
+
if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
1261
1392
|
this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError));
|
|
1262
1393
|
}
|
|
1263
1394
|
return;
|
|
1264
1395
|
}
|
|
1265
|
-
if (result.isComplete &&
|
|
1396
|
+
if (result.isComplete &&
|
|
1397
|
+
result.completePacket &&
|
|
1398
|
+
this.runPromise &&
|
|
1399
|
+
this.runPromiseDeviceId === deviceId) {
|
|
1266
1400
|
this.runPromise.resolve(result.completePacket);
|
|
1267
1401
|
}
|
|
1268
1402
|
}
|
|
1269
1403
|
call(uuid, name, data, options) {
|
|
1270
|
-
var _a;
|
|
1271
1404
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1272
1405
|
if (!this._messages) {
|
|
1273
1406
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
@@ -1279,15 +1412,21 @@ class ElectronBleTransport {
|
|
|
1279
1412
|
if (!protocol) {
|
|
1280
1413
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1281
1414
|
}
|
|
1282
|
-
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
1283
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', createTransportCallLog(name, protocol));
|
|
1284
|
-
}
|
|
1285
1415
|
if (protocol === 'V2') {
|
|
1286
1416
|
return this.callProtocolV2(uuid, name, data, options);
|
|
1287
1417
|
}
|
|
1288
1418
|
return this.callProtocolV1(uuid, name, data, options);
|
|
1289
1419
|
});
|
|
1290
1420
|
}
|
|
1421
|
+
post(uuid, name, data) {
|
|
1422
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1423
|
+
if (this.deviceProtocol.get(uuid) === 'V2') {
|
|
1424
|
+
yield this.protocolV2Links.sendFlowControl(uuid, () => this.createProtocolV2Adapter(uuid), name, data);
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
yield this.callProtocolV1(uuid, name, data);
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1291
1430
|
callProtocolV1(uuid, name, data, options) {
|
|
1292
1431
|
var _a, _b;
|
|
1293
1432
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1301,6 +1440,7 @@ class ElectronBleTransport {
|
|
|
1301
1440
|
const runPromise = hdShared.createDeferred();
|
|
1302
1441
|
runPromise.promise.catch(() => undefined);
|
|
1303
1442
|
this.runPromise = runPromise;
|
|
1443
|
+
this.runPromiseDeviceId = uuid;
|
|
1304
1444
|
const messages = this._messages;
|
|
1305
1445
|
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
1306
1446
|
let timeout;
|
|
@@ -1339,6 +1479,17 @@ class ElectronBleTransport {
|
|
|
1339
1479
|
}
|
|
1340
1480
|
catch (e) {
|
|
1341
1481
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Electron BLE] Protocol V1 call error:', e);
|
|
1482
|
+
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1483
|
+
if ((e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
|
|
1484
|
+
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
1485
|
+
const notifyCleanup = this.notificationCleanups.get(uuid);
|
|
1486
|
+
notifyCleanup === null || notifyCleanup === void 0 ? void 0 : notifyCleanup();
|
|
1487
|
+
this.notificationCleanups.delete(uuid);
|
|
1488
|
+
this.notificationTokens.delete(uuid);
|
|
1489
|
+
if (!isProbeTimeout) {
|
|
1490
|
+
yield this.releaseNative(uuid);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1342
1493
|
throw e;
|
|
1343
1494
|
}
|
|
1344
1495
|
finally {
|
|
@@ -1346,22 +1497,23 @@ class ElectronBleTransport {
|
|
|
1346
1497
|
clearTimeout(timeout);
|
|
1347
1498
|
if (this.runPromise === runPromise) {
|
|
1348
1499
|
this.runPromise = null;
|
|
1500
|
+
this.runPromiseDeviceId = null;
|
|
1349
1501
|
}
|
|
1350
1502
|
}
|
|
1351
1503
|
});
|
|
1352
1504
|
}
|
|
1353
1505
|
callProtocolV2(uuid, name, data, options) {
|
|
1354
|
-
var _a
|
|
1506
|
+
var _a;
|
|
1355
1507
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1356
1508
|
if (!this._messages || !this._messagesV2) {
|
|
1357
1509
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1358
1510
|
}
|
|
1359
|
-
const callOptions =
|
|
1511
|
+
const callOptions = options;
|
|
1360
1512
|
try {
|
|
1361
1513
|
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
1362
1514
|
}
|
|
1363
1515
|
catch (e) {
|
|
1364
|
-
(
|
|
1516
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 call error:', e);
|
|
1365
1517
|
throw e;
|
|
1366
1518
|
}
|
|
1367
1519
|
});
|
|
@@ -1376,6 +1528,7 @@ class ElectronBleTransport {
|
|
|
1376
1528
|
};
|
|
1377
1529
|
return {
|
|
1378
1530
|
router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1531
|
+
maxFrameBytes: transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
|
|
1379
1532
|
generation,
|
|
1380
1533
|
prepareCall: () => {
|
|
1381
1534
|
var _a;
|
|
@@ -1383,10 +1536,7 @@ class ElectronBleTransport {
|
|
|
1383
1536
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1384
1537
|
this.resetProtocolV2Frames(uuid);
|
|
1385
1538
|
},
|
|
1386
|
-
writeFrame: (frame) =>
|
|
1387
|
-
assertCurrentGeneration();
|
|
1388
|
-
return this.writeWithChunking(uuid, transport.bytesToHex(frame));
|
|
1389
|
-
},
|
|
1539
|
+
writeFrame: (frame, context) => this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
|
|
1390
1540
|
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1391
1541
|
assertCurrentGeneration();
|
|
1392
1542
|
const rxFrame = yield this.readProtocolV2Frame(uuid);
|
|
@@ -1406,6 +1556,7 @@ class ElectronBleTransport {
|
|
|
1406
1556
|
};
|
|
1407
1557
|
}
|
|
1408
1558
|
processProtocolV1Notification(deviceId, hexData) {
|
|
1559
|
+
var _a;
|
|
1409
1560
|
try {
|
|
1410
1561
|
if (typeof hexData !== 'string') {
|
|
1411
1562
|
return { isComplete: false, error: 'Invalid hexData type' };
|
|
@@ -1424,6 +1575,10 @@ class ElectronBleTransport {
|
|
|
1424
1575
|
bufferState.buffer = [...data.subarray(3)];
|
|
1425
1576
|
}
|
|
1426
1577
|
else {
|
|
1578
|
+
if (bufferState.buffer.length === 0) {
|
|
1579
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Orphan continuation chunk discarded');
|
|
1580
|
+
return { isComplete: false };
|
|
1581
|
+
}
|
|
1427
1582
|
bufferState.buffer = bufferState.buffer.concat([...data]);
|
|
1428
1583
|
}
|
|
1429
1584
|
if (bufferState.buffer.length - transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferState.bufferLength) {
|