@onekeyfe/hd-transport-lowlevel 1.2.0-alpha.15 → 1.2.0-alpha.151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/firmware-upload-progress.test.js +42 -0
- package/__tests__/protocol-v2.test.js +176 -23
- package/dist/index.d.ts +15 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +130 -61
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +164 -74
- package/src/types.ts +1 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
+
const { getProtocolV1SendOptions, shouldLogFirmwareUploadProgress } = require('../src');
|
|
3
|
+
|
|
4
|
+
describe('firmware upload progress logging', () => {
|
|
5
|
+
test('按 5% 进度限流打印', () => {
|
|
6
|
+
expect(
|
|
7
|
+
shouldLogFirmwareUploadProgress({
|
|
8
|
+
percent: 9,
|
|
9
|
+
lastLoggedPercent: 5,
|
|
10
|
+
now: 5_000,
|
|
11
|
+
lastLoggedAt: 0,
|
|
12
|
+
})
|
|
13
|
+
).toBe(false);
|
|
14
|
+
|
|
15
|
+
expect(
|
|
16
|
+
shouldLogFirmwareUploadProgress({
|
|
17
|
+
percent: 10,
|
|
18
|
+
lastLoggedPercent: 5,
|
|
19
|
+
now: 5_000,
|
|
20
|
+
lastLoggedAt: 0,
|
|
21
|
+
})
|
|
22
|
+
).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('进度不足 5% 时最长每 10 秒打印一次心跳', () => {
|
|
26
|
+
expect(
|
|
27
|
+
shouldLogFirmwareUploadProgress({
|
|
28
|
+
percent: 7,
|
|
29
|
+
lastLoggedPercent: 5,
|
|
30
|
+
now: 10_000,
|
|
31
|
+
lastLoggedAt: 0,
|
|
32
|
+
})
|
|
33
|
+
).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('firmware upload write mode', () => {
|
|
38
|
+
test('固件上传使用带响应写入,普通命令保持默认模式', () => {
|
|
39
|
+
expect(getProtocolV1SendOptions('FirmwareUpload')).toEqual({ withoutResponse: false });
|
|
40
|
+
expect(getProtocolV1SendOptions('Initialize')).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -68,12 +68,25 @@ const protocolV2Schema = {
|
|
|
68
68
|
},
|
|
69
69
|
},
|
|
70
70
|
},
|
|
71
|
+
Failure: {
|
|
72
|
+
fields: {
|
|
73
|
+
code: {
|
|
74
|
+
type: 'uint32',
|
|
75
|
+
id: 1,
|
|
76
|
+
},
|
|
77
|
+
message: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
id: 2,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
71
83
|
MessageType: {
|
|
72
84
|
values: {
|
|
73
85
|
MessageType_ProtocolInfoRequest: 60200,
|
|
74
86
|
MessageType_ProtocolInfo: 60201,
|
|
75
87
|
MessageType_Ping: 60206,
|
|
76
88
|
MessageType_Success: 60207,
|
|
89
|
+
MessageType_Failure: 60208,
|
|
77
90
|
},
|
|
78
91
|
},
|
|
79
92
|
},
|
|
@@ -122,6 +135,49 @@ const splitFrame = (frame, index) => [
|
|
|
122
135
|
];
|
|
123
136
|
|
|
124
137
|
describe('LowlevelTransport protocol framing', () => {
|
|
138
|
+
test('falls back to Protocol V2 when a cached V1 hint is stale', async () => {
|
|
139
|
+
const plugin = createPlugin({ devices: [], responses: [] });
|
|
140
|
+
const lowlevel = configureTransport(plugin);
|
|
141
|
+
const events = [];
|
|
142
|
+
lowlevel.probeProtocolV1 = jest.fn().mockImplementation(() => {
|
|
143
|
+
events.push('probe-v1');
|
|
144
|
+
return Promise.resolve(false);
|
|
145
|
+
});
|
|
146
|
+
lowlevel.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
147
|
+
events.push('reset');
|
|
148
|
+
return Promise.resolve();
|
|
149
|
+
});
|
|
150
|
+
lowlevel.probeProtocolV2 = jest.fn().mockImplementation(() => {
|
|
151
|
+
events.push('probe-v2');
|
|
152
|
+
return Promise.resolve(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await expect(lowlevel.detectProtocol('pro-lowlevel', undefined, 'V1')).resolves.toBe('V2');
|
|
156
|
+
|
|
157
|
+
expect(events).toEqual(['probe-v1', 'reset', 'probe-v2']);
|
|
158
|
+
expect(lowlevel.getProtocolType('pro-lowlevel')).toBe('V2');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('keeps active links when the Protocol V2 schema is configured repeatedly', () => {
|
|
162
|
+
const lowlevel = new LowlevelTransport();
|
|
163
|
+
const invalidateAllLinks = jest.fn().mockResolvedValue(undefined);
|
|
164
|
+
lowlevel.protocolV2Links.invalidateAllLinks = invalidateAllLinks;
|
|
165
|
+
|
|
166
|
+
lowlevel.configureProtocolV2(protocolV2Schema);
|
|
167
|
+
lowlevel.configureProtocolV2(protocolV2Schema);
|
|
168
|
+
|
|
169
|
+
expect(invalidateAllLinks).not.toHaveBeenCalled();
|
|
170
|
+
|
|
171
|
+
lowlevel.configureProtocolV2({
|
|
172
|
+
nested: {
|
|
173
|
+
...protocolV2Schema.nested,
|
|
174
|
+
ExtraMessage: { fields: {} },
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
expect(invalidateAllLinks).toHaveBeenCalledWith('Protocol V2 schema reconfigured');
|
|
179
|
+
});
|
|
180
|
+
|
|
125
181
|
test('keeps Protocol V1 raw notification chunks compatible', async () => {
|
|
126
182
|
const responseChunks = ProtocolV1.encodeTransportPackets(schemas.protocolV1, 'Success', {
|
|
127
183
|
message: 'ok',
|
|
@@ -142,6 +198,23 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
142
198
|
});
|
|
143
199
|
});
|
|
144
200
|
|
|
201
|
+
test('uses the Protocol V2 BLE writer with the lowlevel compatibility packet size', async () => {
|
|
202
|
+
const plugin = createPlugin({ devices: [], responses: [] });
|
|
203
|
+
const lowlevel = configureTransport(plugin);
|
|
204
|
+
const context = {
|
|
205
|
+
messageName: 'Ping',
|
|
206
|
+
timeoutMs: 1000,
|
|
207
|
+
highThroughput: false,
|
|
208
|
+
generation: 1,
|
|
209
|
+
signal: new AbortController().signal,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
await lowlevel.writeProtocolV2Frame('pro2-id', new Uint8Array(130), context, jest.fn());
|
|
213
|
+
|
|
214
|
+
expect(plugin.send).toHaveBeenCalledTimes(3);
|
|
215
|
+
expect(plugin.send.mock.calls.map(([, hex]) => hex.length / 2)).toEqual([64, 64, 2]);
|
|
216
|
+
});
|
|
217
|
+
|
|
145
218
|
test('rejects calls before protocol detection', async () => {
|
|
146
219
|
const responseChunks = ProtocolV1.encodeTransportPackets(schemas.protocolV1, 'Success', {
|
|
147
220
|
message: 'ok',
|
|
@@ -172,11 +245,15 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
172
245
|
supported_messages: [60200, 60201, 60206, 60207],
|
|
173
246
|
protobuf_definition: 'onekey-protocol-v2',
|
|
174
247
|
},
|
|
175
|
-
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
248
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: 2 }
|
|
176
249
|
);
|
|
177
250
|
const plugin = createPlugin({
|
|
178
251
|
devices: [{ id: 'pro2-id', name: 'OneKey Pro 2', commType: 'ble' }],
|
|
179
|
-
responses: [
|
|
252
|
+
responses: [
|
|
253
|
+
new Error('Protocol V1 probe timed out'),
|
|
254
|
+
...splitFrame(probeResponse, 4),
|
|
255
|
+
...splitFrame(callResponse, 5),
|
|
256
|
+
],
|
|
180
257
|
});
|
|
181
258
|
const lowlevel = configureTransport(plugin);
|
|
182
259
|
|
|
@@ -196,9 +273,9 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
196
273
|
},
|
|
197
274
|
});
|
|
198
275
|
expect(plugin.send).toHaveBeenCalled();
|
|
199
|
-
const sentSeqs = plugin.send.mock.calls
|
|
200
|
-
Number.parseInt(hex.slice(12, 14), 16)
|
|
201
|
-
|
|
276
|
+
const sentSeqs = plugin.send.mock.calls
|
|
277
|
+
.map(([, hex]) => Number.parseInt(hex.slice(12, 14), 16))
|
|
278
|
+
.filter(seq => seq > 0);
|
|
202
279
|
expect(sentSeqs).toEqual([1, 2]);
|
|
203
280
|
});
|
|
204
281
|
|
|
@@ -222,7 +299,7 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
222
299
|
expect(lowlevel.getProtocolType('unknown-pro2-id')).toBe('V2');
|
|
223
300
|
});
|
|
224
301
|
|
|
225
|
-
test('
|
|
302
|
+
test('detects Protocol V2 again after release without a name-derived hint', async () => {
|
|
226
303
|
const probeResponse = ProtocolV2.encodeFrame(
|
|
227
304
|
schemas,
|
|
228
305
|
'Success',
|
|
@@ -231,7 +308,12 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
231
308
|
);
|
|
232
309
|
const plugin = createPlugin({
|
|
233
310
|
devices: [{ id: 'reconnect-pro2-id', name: 'OneKey Pro 2', commType: 'ble' }],
|
|
234
|
-
responses: [
|
|
311
|
+
responses: [
|
|
312
|
+
new Error('Protocol V1 probe timed out'),
|
|
313
|
+
bytesToHex(probeResponse),
|
|
314
|
+
new Error('Protocol V1 probe timed out'),
|
|
315
|
+
bytesToHex(probeResponse),
|
|
316
|
+
],
|
|
235
317
|
});
|
|
236
318
|
const lowlevel = configureTransport(plugin);
|
|
237
319
|
|
|
@@ -246,22 +328,26 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
246
328
|
protocolType: 'V2',
|
|
247
329
|
});
|
|
248
330
|
|
|
249
|
-
const sentSeqs = plugin.send.mock.calls
|
|
250
|
-
Number.parseInt(hex.slice(12, 14), 16)
|
|
251
|
-
|
|
331
|
+
const sentSeqs = plugin.send.mock.calls
|
|
332
|
+
.map(([, hex]) => Number.parseInt(hex.slice(12, 14), 16))
|
|
333
|
+
.filter(seq => seq > 0);
|
|
252
334
|
expect(sentSeqs).toEqual([1, 2]);
|
|
253
335
|
});
|
|
254
336
|
|
|
255
337
|
test('reuses the active generation when Core acquires the same BLE connection again', async () => {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
338
|
+
const responses = [1, 2, 3, 4].map(seq =>
|
|
339
|
+
bytesToHex(
|
|
340
|
+
ProtocolV2.encodeFrame(
|
|
341
|
+
schemas,
|
|
342
|
+
'Success',
|
|
343
|
+
{ message: 'ok' },
|
|
344
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq }
|
|
345
|
+
)
|
|
346
|
+
)
|
|
261
347
|
);
|
|
262
348
|
const plugin = createPlugin({
|
|
263
349
|
devices: [{ id: 'repeated-acquire-id', name: 'OneKey Pro 2', commType: 'ble' }],
|
|
264
|
-
responses
|
|
350
|
+
responses,
|
|
265
351
|
});
|
|
266
352
|
const lowlevel = configureTransport(plugin);
|
|
267
353
|
|
|
@@ -283,13 +369,19 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
283
369
|
const sentSeqs = plugin.send.mock.calls.map(([, hex]) =>
|
|
284
370
|
Number.parseInt(hex.slice(12, 14), 16)
|
|
285
371
|
);
|
|
286
|
-
expect(sentSeqs).toEqual([1, 2]);
|
|
372
|
+
expect(sentSeqs).toEqual([1, 2, 3, 4]);
|
|
287
373
|
});
|
|
288
374
|
|
|
289
|
-
test('
|
|
375
|
+
test('actively probes explicit Protocol V2 during bootloader reconnect', async () => {
|
|
376
|
+
const probeResponse = ProtocolV2.encodeFrame(
|
|
377
|
+
schemas,
|
|
378
|
+
'Success',
|
|
379
|
+
{ message: 'ok' },
|
|
380
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
381
|
+
);
|
|
290
382
|
const plugin = createPlugin({
|
|
291
383
|
devices: [{ id: 'bootloader-v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
|
|
292
|
-
responses: [],
|
|
384
|
+
responses: [bytesToHex(probeResponse)],
|
|
293
385
|
});
|
|
294
386
|
const lowlevel = configureTransport(plugin);
|
|
295
387
|
|
|
@@ -299,8 +391,35 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
299
391
|
uuid: 'bootloader-v2-id',
|
|
300
392
|
protocolType: 'V2',
|
|
301
393
|
});
|
|
302
|
-
expect(plugin.send).
|
|
303
|
-
expect(plugin.receive).
|
|
394
|
+
expect(plugin.send).toHaveBeenCalledTimes(1);
|
|
395
|
+
expect(plugin.receive).toHaveBeenCalledTimes(1);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test('disconnects and clears a lowlevel connection when Protocol V2 reports link disabled', async () => {
|
|
399
|
+
const failureResponse = ProtocolV2.encodeFrame(
|
|
400
|
+
schemas,
|
|
401
|
+
'Failure',
|
|
402
|
+
{ code: 5, message: 'link disabled' },
|
|
403
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
404
|
+
);
|
|
405
|
+
const plugin = createPlugin({
|
|
406
|
+
devices: [{ id: 'usb-owned-id', name: 'OneKey Pro 2', commType: 'ble' }],
|
|
407
|
+
responses: [bytesToHex(failureResponse)],
|
|
408
|
+
});
|
|
409
|
+
const lowlevel = configureTransport(plugin);
|
|
410
|
+
|
|
411
|
+
await expect(
|
|
412
|
+
lowlevel.acquire({ uuid: 'usb-owned-id', expectedProtocol: 'V2' })
|
|
413
|
+
).rejects.toMatchObject({
|
|
414
|
+
name: 'ProtocolV2LinkDisabledError',
|
|
415
|
+
failureCode: 5,
|
|
416
|
+
firmwareMessage: 'link disabled',
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
expect(plugin.disconnect).toHaveBeenCalledWith('usb-owned-id');
|
|
420
|
+
expect(lowlevel.connectedDevices.has('usb-owned-id')).toBe(false);
|
|
421
|
+
expect(lowlevel.getProtocolType('usb-owned-id')).toBeUndefined();
|
|
422
|
+
expect(lowlevel.protocolV2Assemblers.has('usb-owned-id')).toBe(false);
|
|
304
423
|
});
|
|
305
424
|
|
|
306
425
|
test('resets the lowlevel connection before probing Protocol V2 after a V1 timeout', async () => {
|
|
@@ -342,11 +461,23 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
342
461
|
});
|
|
343
462
|
|
|
344
463
|
test('disconnects a tainted Protocol V2 link after a response timeout', async () => {
|
|
464
|
+
const probeResponse = ProtocolV2.encodeFrame(
|
|
465
|
+
schemas,
|
|
466
|
+
'Success',
|
|
467
|
+
{ message: 'ok' },
|
|
468
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
469
|
+
);
|
|
345
470
|
const plugin = createPlugin({
|
|
346
471
|
devices: [{ id: 'timeout-v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
|
|
347
|
-
responses: [],
|
|
472
|
+
responses: [bytesToHex(probeResponse)],
|
|
473
|
+
});
|
|
474
|
+
let receiveCount = 0;
|
|
475
|
+
plugin.receive.mockImplementation(() => {
|
|
476
|
+
receiveCount += 1;
|
|
477
|
+
return receiveCount === 1
|
|
478
|
+
? Promise.resolve(bytesToHex(probeResponse))
|
|
479
|
+
: new Promise(() => {});
|
|
348
480
|
});
|
|
349
|
-
plugin.receive.mockImplementation(() => new Promise(() => {}));
|
|
350
481
|
const lowlevel = configureTransport(plugin);
|
|
351
482
|
|
|
352
483
|
await lowlevel.acquire({ uuid: 'timeout-v2-id', expectedProtocol: 'V2' });
|
|
@@ -358,6 +489,28 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
358
489
|
expect(plugin.disconnect).toHaveBeenCalledWith('timeout-v2-id');
|
|
359
490
|
});
|
|
360
491
|
|
|
492
|
+
test('preserves an undefined business timeout outside explicit probes', async () => {
|
|
493
|
+
const plugin = createPlugin({
|
|
494
|
+
devices: [{ id: 'v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
|
|
495
|
+
responses: [],
|
|
496
|
+
});
|
|
497
|
+
const lowlevel = configureTransport(plugin);
|
|
498
|
+
lowlevel.deviceProtocol.set('v2-id', 'V2');
|
|
499
|
+
const linkCall = jest
|
|
500
|
+
.spyOn(lowlevel.protocolV2Links, 'call')
|
|
501
|
+
.mockResolvedValue({ type: 'Success', message: {} });
|
|
502
|
+
|
|
503
|
+
await lowlevel.call('v2-id', 'Ping', { message: 'no-business-timeout' });
|
|
504
|
+
|
|
505
|
+
expect(linkCall).toHaveBeenCalledWith(
|
|
506
|
+
'v2-id',
|
|
507
|
+
expect.any(Function),
|
|
508
|
+
'Ping',
|
|
509
|
+
{ message: 'no-business-timeout' },
|
|
510
|
+
undefined
|
|
511
|
+
);
|
|
512
|
+
});
|
|
513
|
+
|
|
361
514
|
test('verifies expected Protocol V1 instead of trusting the requested protocol', async () => {
|
|
362
515
|
const plugin = createPlugin({
|
|
363
516
|
devices: [{ id: 'v2-id', name: 'Unknown BLE Device', commType: 'ble' }],
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import * as transport from '@onekeyfe/hd-transport';
|
|
2
|
-
import transport__default, { ProtocolType, LowlevelTransportSharedPlugin,
|
|
2
|
+
import transport__default, { ProtocolType, LowlevelTransportSharedPlugin, TransportCallOptions } from '@onekeyfe/hd-transport';
|
|
3
3
|
import EventEmitter from 'events';
|
|
4
4
|
|
|
5
5
|
type LowLevelAcquireInput = {
|
|
6
6
|
uuid: string;
|
|
7
7
|
expectedProtocol?: ProtocolType;
|
|
8
|
+
protocolHint?: ProtocolType;
|
|
8
9
|
};
|
|
9
10
|
|
|
11
|
+
declare function shouldLogFirmwareUploadProgress({ percent, lastLoggedPercent, now, lastLoggedAt, }: {
|
|
12
|
+
percent: number;
|
|
13
|
+
lastLoggedPercent: number;
|
|
14
|
+
now: number;
|
|
15
|
+
lastLoggedAt: number;
|
|
16
|
+
}): boolean;
|
|
17
|
+
declare function getProtocolV1SendOptions(name: string): {
|
|
18
|
+
withoutResponse: boolean;
|
|
19
|
+
} | undefined;
|
|
10
20
|
declare class LowlevelTransport {
|
|
11
21
|
_messages: ReturnType<typeof transport__default.parseConfigure> | undefined;
|
|
12
22
|
_messagesV2: ReturnType<typeof transport__default.parseConfigure> | undefined;
|
|
@@ -20,18 +30,20 @@ declare class LowlevelTransport {
|
|
|
20
30
|
private protocolV2Generations;
|
|
21
31
|
private connectedDevices;
|
|
22
32
|
private protocolV2Links;
|
|
33
|
+
private protocolV2SchemaConfiguration;
|
|
23
34
|
getProtocolType(path: string): ProtocolType | undefined;
|
|
24
35
|
init(logger: any, emitter: EventEmitter, plugin: LowlevelTransportSharedPlugin): void;
|
|
25
36
|
configure(signedData: any): void;
|
|
26
37
|
configureProtocolV2(signedData: any): void;
|
|
27
38
|
listen(): void;
|
|
28
|
-
enumerate(): Promise<LowLevelDevice[]>;
|
|
39
|
+
enumerate(): Promise<transport.LowLevelDevice[]>;
|
|
29
40
|
acquire(input: LowLevelAcquireInput): Promise<{
|
|
30
41
|
uuid: string;
|
|
31
42
|
protocolType: ProtocolType;
|
|
32
43
|
}>;
|
|
33
44
|
release(uuid: string): Promise<boolean>;
|
|
34
45
|
call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<transport.MessageFromOneKey>;
|
|
46
|
+
post(uuid: string, name: string, data: Record<string, unknown>): Promise<void>;
|
|
35
47
|
private callProtocolV1;
|
|
36
48
|
private createProtocolTimeoutError;
|
|
37
49
|
private createProtocolMismatchError;
|
|
@@ -51,4 +63,4 @@ declare class LowlevelTransport {
|
|
|
51
63
|
cancel(): void;
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
export { LowlevelTransport as default };
|
|
66
|
+
export { LowlevelTransport as default, getProtocolV1SendOptions, shouldLogFirmwareUploadProgress };
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,SAYN,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,6BAA6B,EAC7B,YAAY,EAEZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAUpD,wBAAgB,+BAA+B,CAAC,EAC9C,OAAO,EACP,iBAAiB,EACjB,GAAG,EACH,YAAY,GACb,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACtB,WAMA;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM;;cAEpD;AAUD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,WAAW,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAErE,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,MAAM,EAAE,6BAA6B,CAAuC;IAE5E,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,oBAAoB,CAAoD;IAEhF,OAAO,CAAC,qBAAqB,CAAkC;IAE/D,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,eAAe,CA6BpB;IAEH,OAAO,CAAC,6BAA6B,CAAqB;IAE1D,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIvD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,6BAA6B;IAO9E,SAAS,CAAC,UAAU,EAAE,GAAG;IAMzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAiBnC,MAAM;IAIA,SAAS;IAKT,OAAO,CAAC,KAAK,EAAE,oBAAoB;;;;IAmDnC,OAAO,CAAC,IAAI,EAAE,MAAM;IAepB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;IAoB1B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;YAatD,cAAc;IA6E5B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;YA2Cd,yBAAyB;YAiCzB,eAAe;YAgBf,eAAe;YA6Bf,UAAU;YAUV,qBAAqB;YAmBrB,mBAAmB;YAqBnB,oBAAoB;YAgBpB,cAAc;IAwB5B,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,2BAA2B;IAMnC,MAAM;CAGP"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
3
5
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
4
6
|
var transport = require('@onekeyfe/hd-transport');
|
|
5
7
|
|
|
@@ -40,10 +42,16 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
40
42
|
const { check, ProtocolV1, parseConfigure } = transport__default["default"];
|
|
41
43
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
42
44
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
43
|
-
const LOWLEVEL_PROTOCOL_TIMEOUT_MS = 30000;
|
|
44
45
|
const LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH = 64;
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
const FIRMWARE_UPLOAD_LOG_PERCENT_STEP = 5;
|
|
47
|
+
const FIRMWARE_UPLOAD_LOG_INTERVAL_MS = 10000;
|
|
48
|
+
function shouldLogFirmwareUploadProgress({ percent, lastLoggedPercent, now, lastLoggedAt, }) {
|
|
49
|
+
return (percent === 100 ||
|
|
50
|
+
percent - lastLoggedPercent >= FIRMWARE_UPLOAD_LOG_PERCENT_STEP ||
|
|
51
|
+
now - lastLoggedAt >= FIRMWARE_UPLOAD_LOG_INTERVAL_MS);
|
|
52
|
+
}
|
|
53
|
+
function getProtocolV1SendOptions(name) {
|
|
54
|
+
return name === 'FirmwareUpload' ? { withoutResponse: false } : undefined;
|
|
47
55
|
}
|
|
48
56
|
function isProtocolV1TransportChunk(data) {
|
|
49
57
|
return data.length >= 9 && data[0] === 0x3f && data[1] === 0x23 && data[2] === 0x23;
|
|
@@ -106,27 +114,29 @@ class LowlevelTransport {
|
|
|
106
114
|
this._messages = messages;
|
|
107
115
|
}
|
|
108
116
|
configureProtocolV2(signedData) {
|
|
117
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
118
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
109
122
|
this._messagesV2 = parseConfigure(signedData);
|
|
110
|
-
this.
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
124
|
+
if (isReconfiguration) {
|
|
125
|
+
this.protocolV2Links
|
|
126
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
127
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('Protocol V2 schema link cleanup failed:', error); });
|
|
128
|
+
}
|
|
113
129
|
}
|
|
114
130
|
listen() {
|
|
115
131
|
}
|
|
116
132
|
enumerate() {
|
|
117
133
|
return __awaiter(this, void 0, void 0, function* () {
|
|
118
134
|
const devices = yield this.plugin.enumerate();
|
|
119
|
-
return devices
|
|
120
|
-
const protocolHint = inferProtocolHintFromDeviceName(device.name);
|
|
121
|
-
if (protocolHint) {
|
|
122
|
-
this.deviceProtocolHints.set(device.id, protocolHint);
|
|
123
|
-
}
|
|
124
|
-
return device;
|
|
125
|
-
});
|
|
135
|
+
return devices;
|
|
126
136
|
});
|
|
127
137
|
}
|
|
128
138
|
acquire(input) {
|
|
129
|
-
var _a;
|
|
139
|
+
var _a, _b, _c, _d;
|
|
130
140
|
return __awaiter(this, void 0, void 0, function* () {
|
|
131
141
|
const alreadyConnected = this.connectedDevices.has(input.uuid);
|
|
132
142
|
try {
|
|
@@ -141,12 +151,35 @@ class LowlevelTransport {
|
|
|
141
151
|
this.Log.debug('lowlelvel transport connect error: ', error);
|
|
142
152
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.LowlevelTrasnportConnectError, (_a = error.message) !== null && _a !== void 0 ? _a : error);
|
|
143
153
|
}
|
|
144
|
-
this.protocolV2Assemblers.set(input.uuid, new transport.ProtocolV2FrameAssembler());
|
|
154
|
+
this.protocolV2Assemblers.set(input.uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
145
155
|
const protocolHint = input.expectedProtocol
|
|
146
156
|
? undefined
|
|
147
|
-
: this.deviceProtocolHints.get(input.uuid);
|
|
148
|
-
|
|
149
|
-
|
|
157
|
+
: (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(input.uuid);
|
|
158
|
+
try {
|
|
159
|
+
const protocolType = yield this.detectProtocol(input.uuid, input.expectedProtocol, protocolHint);
|
|
160
|
+
return { uuid: input.uuid, protocolType };
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
try {
|
|
164
|
+
yield this.protocolV2Links.invalidateLink(input.uuid, 'Lowlevel transport acquire failed');
|
|
165
|
+
}
|
|
166
|
+
catch (cleanupError) {
|
|
167
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[LowlevelTransport] acquire link cleanup failed:', cleanupError);
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
yield this.plugin.disconnect(input.uuid);
|
|
171
|
+
}
|
|
172
|
+
catch (cleanupError) {
|
|
173
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[LowlevelTransport] acquire disconnect failed:', cleanupError);
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
this.connectedDevices.delete(input.uuid);
|
|
177
|
+
this.deviceProtocol.delete(input.uuid);
|
|
178
|
+
this.protocolV2Assemblers.delete(input.uuid);
|
|
179
|
+
this.advanceProtocolV2Generation(input.uuid);
|
|
180
|
+
}
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
150
183
|
});
|
|
151
184
|
}
|
|
152
185
|
release(uuid) {
|
|
@@ -174,24 +207,58 @@ class LowlevelTransport {
|
|
|
174
207
|
if (!protocol) {
|
|
175
208
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
176
209
|
}
|
|
177
|
-
this.Log.debug('transport call', { name, protocol });
|
|
178
210
|
if (protocol === 'V2') {
|
|
179
211
|
return this.callProtocolV2(uuid, name, data, options);
|
|
180
212
|
}
|
|
181
213
|
return this.callProtocolV1(uuid, name, data, options);
|
|
182
214
|
});
|
|
183
215
|
}
|
|
216
|
+
post(uuid, name, data) {
|
|
217
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
+
if (this.getProtocolType(uuid) === 'V2') {
|
|
219
|
+
yield this.protocolV2Links.sendFlowControl(uuid, () => this.createProtocolV2Adapter(uuid), name, data);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
yield this.callProtocolV1(uuid, name, data);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
184
225
|
callProtocolV1(uuid, name, data, options) {
|
|
226
|
+
var _a;
|
|
185
227
|
return __awaiter(this, void 0, void 0, function* () {
|
|
186
228
|
if (!this._messages) {
|
|
187
229
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
188
230
|
}
|
|
189
231
|
const messages = this._messages;
|
|
190
232
|
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
191
|
-
|
|
233
|
+
const isFirmwareUpload = name === 'FirmwareUpload';
|
|
234
|
+
const uploadStartedAt = Date.now();
|
|
235
|
+
const totalBytes = buffers.reduce((sum, buffer) => sum + buffer.limit, 0);
|
|
236
|
+
let sentBytes = 0;
|
|
237
|
+
let lastLoggedPercent = 0;
|
|
238
|
+
let lastLoggedAt = uploadStartedAt;
|
|
239
|
+
for (const [index, o] of buffers.entries()) {
|
|
192
240
|
const outData = o.toString('hex');
|
|
193
241
|
try {
|
|
194
|
-
yield this.plugin.send(uuid, outData);
|
|
242
|
+
yield this.plugin.send(uuid, outData, getProtocolV1SendOptions(name));
|
|
243
|
+
sentBytes += o.limit;
|
|
244
|
+
if (isFirmwareUpload) {
|
|
245
|
+
const now = Date.now();
|
|
246
|
+
const percent = Math.floor(((index + 1) / buffers.length) * 100);
|
|
247
|
+
if (shouldLogFirmwareUploadProgress({
|
|
248
|
+
percent,
|
|
249
|
+
lastLoggedPercent,
|
|
250
|
+
now,
|
|
251
|
+
lastLoggedAt,
|
|
252
|
+
})) {
|
|
253
|
+
const elapsedSeconds = Math.max((now - uploadStartedAt) / 1000, 0.001);
|
|
254
|
+
const kibPerSecond = sentBytes / 1024 / elapsedSeconds;
|
|
255
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[LowlevelTransport] FirmwareUpload progress: ${percent}% ` +
|
|
256
|
+
`(${index + 1}/${buffers.length} packets, ${sentBytes}/${totalBytes} bytes, ` +
|
|
257
|
+
`${elapsedSeconds.toFixed(1)}s, ${kibPerSecond.toFixed(1)} KiB/s)`);
|
|
258
|
+
lastLoggedPercent = percent;
|
|
259
|
+
lastLoggedAt = now;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
195
262
|
}
|
|
196
263
|
catch (e) {
|
|
197
264
|
this.Log.debug('lowlevel transport send error: ', e);
|
|
@@ -204,6 +271,15 @@ class LowlevelTransport {
|
|
|
204
271
|
return check.call(jsonData);
|
|
205
272
|
}
|
|
206
273
|
catch (e) {
|
|
274
|
+
if ((e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError &&
|
|
275
|
+
(options === null || options === void 0 ? void 0 : options.timeoutMs) !== PROTOCOL_PROBE_TIMEOUT_MS) {
|
|
276
|
+
try {
|
|
277
|
+
yield this.resetConnectionAfterProbe(uuid, 'V1');
|
|
278
|
+
}
|
|
279
|
+
catch (resetError) {
|
|
280
|
+
this.Log.debug('[LowlevelTransport] reset after Protocol V1 timeout failed:', resetError);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
207
283
|
if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
|
|
208
284
|
this.Log.debug('[LowlevelTransport] Protocol V1 Initialize probe call failed:', e);
|
|
209
285
|
}
|
|
@@ -229,12 +305,15 @@ class LowlevelTransport {
|
|
|
229
305
|
}
|
|
230
306
|
}
|
|
231
307
|
detectProtocol(uuid, expectedProtocol, protocolHint) {
|
|
232
|
-
var _a, _b, _c
|
|
308
|
+
var _a, _b, _c;
|
|
233
309
|
return __awaiter(this, void 0, void 0, function* () {
|
|
234
310
|
if (expectedProtocol === 'V2') {
|
|
235
|
-
this.
|
|
236
|
-
|
|
237
|
-
|
|
311
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
312
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
313
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
314
|
+
return 'V2';
|
|
315
|
+
}
|
|
316
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
238
317
|
}
|
|
239
318
|
if (expectedProtocol === 'V1') {
|
|
240
319
|
if (yield this.probeProtocolV1(uuid)) {
|
|
@@ -244,28 +323,17 @@ class LowlevelTransport {
|
|
|
244
323
|
}
|
|
245
324
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
246
325
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const protocolV1Detected = yield this.probeProtocolV1(uuid);
|
|
259
|
-
if (protocolV1Detected) {
|
|
260
|
-
this.deviceProtocol.set(uuid, 'V1');
|
|
261
|
-
(_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V1`);
|
|
262
|
-
return 'V1';
|
|
263
|
-
}
|
|
264
|
-
yield this.resetConnectionAfterProbe(uuid, 'V1');
|
|
265
|
-
if (yield this.probeProtocolV2(uuid)) {
|
|
266
|
-
this.deviceProtocol.set(uuid, 'V2');
|
|
267
|
-
(_f = this.Log) === null || _f === void 0 ? void 0 : _f.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2`);
|
|
268
|
-
return 'V2';
|
|
326
|
+
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
327
|
+
for (const [index, protocol] of probeOrder.entries()) {
|
|
328
|
+
if (index > 0) {
|
|
329
|
+
yield this.resetConnectionAfterProbe(uuid, probeOrder[index - 1]);
|
|
330
|
+
}
|
|
331
|
+
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
332
|
+
if (detected) {
|
|
333
|
+
this.deviceProtocol.set(uuid, protocol);
|
|
334
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
|
|
335
|
+
return protocol;
|
|
336
|
+
}
|
|
269
337
|
}
|
|
270
338
|
this.deviceProtocol.delete(uuid);
|
|
271
339
|
throw this.createProtocolDetectionError();
|
|
@@ -373,7 +441,7 @@ class LowlevelTransport {
|
|
|
373
441
|
return __awaiter(this, void 0, void 0, function* () {
|
|
374
442
|
let assembler = this.protocolV2Assemblers.get(uuid);
|
|
375
443
|
if (!assembler) {
|
|
376
|
-
assembler = new transport.ProtocolV2FrameAssembler();
|
|
444
|
+
assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES);
|
|
377
445
|
this.protocolV2Assemblers.set(uuid, assembler);
|
|
378
446
|
}
|
|
379
447
|
const queuedFrame = assembler.push(new Uint8Array(0));
|
|
@@ -390,23 +458,25 @@ class LowlevelTransport {
|
|
|
390
458
|
return frame;
|
|
391
459
|
});
|
|
392
460
|
}
|
|
393
|
-
writeProtocolV2Frame(uuid, frame) {
|
|
461
|
+
writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration) {
|
|
394
462
|
return __awaiter(this, void 0, void 0, function* () {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
463
|
+
yield transport.writeProtocolV2BleFrame({
|
|
464
|
+
frame,
|
|
465
|
+
packetCapacity: LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH,
|
|
466
|
+
assertActive: assertCurrentGeneration,
|
|
467
|
+
signal: context.signal,
|
|
468
|
+
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
469
|
+
writePacket: packet => this.plugin.send(uuid, transport.bytesToHex(packet)),
|
|
470
|
+
});
|
|
399
471
|
});
|
|
400
472
|
}
|
|
401
473
|
callProtocolV2(uuid, name, data, options) {
|
|
402
|
-
var _a;
|
|
403
474
|
return __awaiter(this, void 0, void 0, function* () {
|
|
404
475
|
if (!this._messages || !this._messagesV2) {
|
|
405
476
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
406
477
|
}
|
|
407
|
-
const timeoutMs = (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : LOWLEVEL_PROTOCOL_TIMEOUT_MS;
|
|
408
478
|
try {
|
|
409
|
-
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data,
|
|
479
|
+
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, options);
|
|
410
480
|
}
|
|
411
481
|
catch (e) {
|
|
412
482
|
this.Log.error('lowlevel Protocol V2 call error: ', e);
|
|
@@ -431,10 +501,7 @@ class LowlevelTransport {
|
|
|
431
501
|
assertCurrentGeneration();
|
|
432
502
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
433
503
|
},
|
|
434
|
-
writeFrame: (frame) =>
|
|
435
|
-
assertCurrentGeneration();
|
|
436
|
-
return this.writeProtocolV2Frame(uuid, frame);
|
|
437
|
-
},
|
|
504
|
+
writeFrame: (frame, context) => this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
|
|
438
505
|
readFrame: (context) => {
|
|
439
506
|
assertCurrentGeneration();
|
|
440
507
|
return this.readProtocolV2Frame(uuid, context.timeoutMs, context.messageName);
|
|
@@ -459,4 +526,6 @@ class LowlevelTransport {
|
|
|
459
526
|
}
|
|
460
527
|
}
|
|
461
528
|
|
|
462
|
-
|
|
529
|
+
exports["default"] = LowlevelTransport;
|
|
530
|
+
exports.getProtocolV1SendOptions = getProtocolV1SendOptions;
|
|
531
|
+
exports.shouldLogFirmwareUploadProgress = shouldLogFirmwareUploadProgress;
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-lowlevel",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.151",
|
|
4
4
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"lint:fix": "eslint . --fix"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
23
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.151",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.151"
|
|
25
25
|
},
|
|
26
|
-
"gitHead": "
|
|
26
|
+
"gitHead": "d1fa81c92deb1bfa3903e3963a82edd6384d568a"
|
|
27
27
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,13 +10,14 @@ import transport, {
|
|
|
10
10
|
hexToBytes,
|
|
11
11
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
12
12
|
withProtocolTimeout,
|
|
13
|
+
writeProtocolV2BleFrame,
|
|
13
14
|
} from '@onekeyfe/hd-transport';
|
|
14
15
|
|
|
15
16
|
import type EventEmitter from 'events';
|
|
16
17
|
import type {
|
|
17
|
-
LowLevelDevice,
|
|
18
18
|
LowlevelTransportSharedPlugin,
|
|
19
19
|
ProtocolType,
|
|
20
|
+
ProtocolV2CallContext,
|
|
20
21
|
TransportCallOptions,
|
|
21
22
|
} from '@onekeyfe/hd-transport';
|
|
22
23
|
import type { LowLevelAcquireInput } from './types';
|
|
@@ -25,11 +26,30 @@ const { check, ProtocolV1, parseConfigure } = transport;
|
|
|
25
26
|
|
|
26
27
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
27
28
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
28
|
-
const LOWLEVEL_PROTOCOL_TIMEOUT_MS = 30_000;
|
|
29
29
|
const LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH = 64;
|
|
30
|
+
const FIRMWARE_UPLOAD_LOG_PERCENT_STEP = 5;
|
|
31
|
+
const FIRMWARE_UPLOAD_LOG_INTERVAL_MS = 10_000;
|
|
32
|
+
|
|
33
|
+
export function shouldLogFirmwareUploadProgress({
|
|
34
|
+
percent,
|
|
35
|
+
lastLoggedPercent,
|
|
36
|
+
now,
|
|
37
|
+
lastLoggedAt,
|
|
38
|
+
}: {
|
|
39
|
+
percent: number;
|
|
40
|
+
lastLoggedPercent: number;
|
|
41
|
+
now: number;
|
|
42
|
+
lastLoggedAt: number;
|
|
43
|
+
}) {
|
|
44
|
+
return (
|
|
45
|
+
percent === 100 ||
|
|
46
|
+
percent - lastLoggedPercent >= FIRMWARE_UPLOAD_LOG_PERCENT_STEP ||
|
|
47
|
+
now - lastLoggedAt >= FIRMWARE_UPLOAD_LOG_INTERVAL_MS
|
|
48
|
+
);
|
|
49
|
+
}
|
|
30
50
|
|
|
31
|
-
function
|
|
32
|
-
return
|
|
51
|
+
export function getProtocolV1SendOptions(name: string) {
|
|
52
|
+
return name === 'FirmwareUpload' ? { withoutResponse: false } : undefined;
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
function isProtocolV1TransportChunk(data: Uint8Array) {
|
|
@@ -94,6 +114,8 @@ export default class LowlevelTransport {
|
|
|
94
114
|
},
|
|
95
115
|
});
|
|
96
116
|
|
|
117
|
+
private protocolV2SchemaConfiguration: string | undefined;
|
|
118
|
+
|
|
97
119
|
getProtocolType(path: string): ProtocolType | undefined {
|
|
98
120
|
return this.deviceProtocol.get(path);
|
|
99
121
|
}
|
|
@@ -112,10 +134,20 @@ export default class LowlevelTransport {
|
|
|
112
134
|
}
|
|
113
135
|
|
|
114
136
|
configureProtocolV2(signedData: any) {
|
|
137
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
138
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
115
143
|
this._messagesV2 = parseConfigure(signedData);
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
|
|
144
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
145
|
+
|
|
146
|
+
if (isReconfiguration) {
|
|
147
|
+
this.protocolV2Links
|
|
148
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
149
|
+
.catch(error => this.Log?.debug('Protocol V2 schema link cleanup failed:', error));
|
|
150
|
+
}
|
|
119
151
|
}
|
|
120
152
|
|
|
121
153
|
listen() {
|
|
@@ -124,13 +156,7 @@ export default class LowlevelTransport {
|
|
|
124
156
|
|
|
125
157
|
async enumerate() {
|
|
126
158
|
const devices = await this.plugin.enumerate();
|
|
127
|
-
return devices
|
|
128
|
-
const protocolHint = inferProtocolHintFromDeviceName(device.name);
|
|
129
|
-
if (protocolHint) {
|
|
130
|
-
this.deviceProtocolHints.set(device.id, protocolHint);
|
|
131
|
-
}
|
|
132
|
-
return device;
|
|
133
|
-
});
|
|
159
|
+
return devices;
|
|
134
160
|
}
|
|
135
161
|
|
|
136
162
|
async acquire(input: LowLevelAcquireInput) {
|
|
@@ -150,16 +176,38 @@ export default class LowlevelTransport {
|
|
|
150
176
|
);
|
|
151
177
|
}
|
|
152
178
|
|
|
153
|
-
this.protocolV2Assemblers.set(
|
|
154
|
-
const protocolHint = input.expectedProtocol
|
|
155
|
-
? undefined
|
|
156
|
-
: this.deviceProtocolHints.get(input.uuid);
|
|
157
|
-
const protocolType = await this.detectProtocol(
|
|
179
|
+
this.protocolV2Assemblers.set(
|
|
158
180
|
input.uuid,
|
|
159
|
-
|
|
160
|
-
protocolHint
|
|
181
|
+
new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES)
|
|
161
182
|
);
|
|
162
|
-
|
|
183
|
+
const protocolHint = input.expectedProtocol
|
|
184
|
+
? undefined
|
|
185
|
+
: input.protocolHint ?? this.deviceProtocolHints.get(input.uuid);
|
|
186
|
+
try {
|
|
187
|
+
const protocolType = await this.detectProtocol(
|
|
188
|
+
input.uuid,
|
|
189
|
+
input.expectedProtocol,
|
|
190
|
+
protocolHint
|
|
191
|
+
);
|
|
192
|
+
return { uuid: input.uuid, protocolType };
|
|
193
|
+
} catch (error) {
|
|
194
|
+
try {
|
|
195
|
+
await this.protocolV2Links.invalidateLink(input.uuid, 'Lowlevel transport acquire failed');
|
|
196
|
+
} catch (cleanupError) {
|
|
197
|
+
this.Log?.debug('[LowlevelTransport] acquire link cleanup failed:', cleanupError);
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
await this.plugin.disconnect(input.uuid);
|
|
201
|
+
} catch (cleanupError) {
|
|
202
|
+
this.Log?.debug('[LowlevelTransport] acquire disconnect failed:', cleanupError);
|
|
203
|
+
} finally {
|
|
204
|
+
this.connectedDevices.delete(input.uuid);
|
|
205
|
+
this.deviceProtocol.delete(input.uuid);
|
|
206
|
+
this.protocolV2Assemblers.delete(input.uuid);
|
|
207
|
+
this.advanceProtocolV2Generation(input.uuid);
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
163
211
|
}
|
|
164
212
|
|
|
165
213
|
async release(uuid: string) {
|
|
@@ -168,8 +216,7 @@ export default class LowlevelTransport {
|
|
|
168
216
|
await this.plugin.disconnect(uuid);
|
|
169
217
|
this.connectedDevices.delete(uuid);
|
|
170
218
|
this.deviceProtocol.delete(uuid);
|
|
171
|
-
//
|
|
172
|
-
// 直接执行 Protocol V2 探测,避免先发送一次无意义的 V1 Initialize。
|
|
219
|
+
// Confirmed protocol stays on the device endpoint; BLE names are not a probe hint.
|
|
173
220
|
this.protocolV2Assemblers.delete(uuid);
|
|
174
221
|
return true;
|
|
175
222
|
} catch (error) {
|
|
@@ -195,8 +242,6 @@ export default class LowlevelTransport {
|
|
|
195
242
|
`Device protocol has not been detected for ${uuid}`
|
|
196
243
|
);
|
|
197
244
|
}
|
|
198
|
-
this.Log.debug('transport call', { name, protocol });
|
|
199
|
-
|
|
200
245
|
if (protocol === 'V2') {
|
|
201
246
|
return this.callProtocolV2(uuid, name, data, options);
|
|
202
247
|
}
|
|
@@ -204,6 +249,19 @@ export default class LowlevelTransport {
|
|
|
204
249
|
return this.callProtocolV1(uuid, name, data, options);
|
|
205
250
|
}
|
|
206
251
|
|
|
252
|
+
async post(uuid: string, name: string, data: Record<string, unknown>) {
|
|
253
|
+
if (this.getProtocolType(uuid) === 'V2') {
|
|
254
|
+
await this.protocolV2Links.sendFlowControl(
|
|
255
|
+
uuid,
|
|
256
|
+
() => this.createProtocolV2Adapter(uuid),
|
|
257
|
+
name,
|
|
258
|
+
data
|
|
259
|
+
);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
await this.callProtocolV1(uuid, name, data);
|
|
263
|
+
}
|
|
264
|
+
|
|
207
265
|
private async callProtocolV1(
|
|
208
266
|
uuid: string,
|
|
209
267
|
name: string,
|
|
@@ -216,10 +274,41 @@ export default class LowlevelTransport {
|
|
|
216
274
|
|
|
217
275
|
const messages = this._messages;
|
|
218
276
|
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
219
|
-
|
|
277
|
+
const isFirmwareUpload = name === 'FirmwareUpload';
|
|
278
|
+
const uploadStartedAt = Date.now();
|
|
279
|
+
const totalBytes = buffers.reduce((sum, buffer) => sum + buffer.limit, 0);
|
|
280
|
+
let sentBytes = 0;
|
|
281
|
+
let lastLoggedPercent = 0;
|
|
282
|
+
let lastLoggedAt = uploadStartedAt;
|
|
283
|
+
|
|
284
|
+
for (const [index, o] of buffers.entries()) {
|
|
220
285
|
const outData = o.toString('hex');
|
|
221
286
|
try {
|
|
222
|
-
await this.plugin.send(uuid, outData);
|
|
287
|
+
await this.plugin.send(uuid, outData, getProtocolV1SendOptions(name));
|
|
288
|
+
sentBytes += o.limit;
|
|
289
|
+
|
|
290
|
+
if (isFirmwareUpload) {
|
|
291
|
+
const now = Date.now();
|
|
292
|
+
const percent = Math.floor(((index + 1) / buffers.length) * 100);
|
|
293
|
+
if (
|
|
294
|
+
shouldLogFirmwareUploadProgress({
|
|
295
|
+
percent,
|
|
296
|
+
lastLoggedPercent,
|
|
297
|
+
now,
|
|
298
|
+
lastLoggedAt,
|
|
299
|
+
})
|
|
300
|
+
) {
|
|
301
|
+
const elapsedSeconds = Math.max((now - uploadStartedAt) / 1000, 0.001);
|
|
302
|
+
const kibPerSecond = sentBytes / 1024 / elapsedSeconds;
|
|
303
|
+
this.Log?.debug(
|
|
304
|
+
`[LowlevelTransport] FirmwareUpload progress: ${percent}% ` +
|
|
305
|
+
`(${index + 1}/${buffers.length} packets, ${sentBytes}/${totalBytes} bytes, ` +
|
|
306
|
+
`${elapsedSeconds.toFixed(1)}s, ${kibPerSecond.toFixed(1)} KiB/s)`
|
|
307
|
+
);
|
|
308
|
+
lastLoggedPercent = percent;
|
|
309
|
+
lastLoggedAt = now;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
223
312
|
} catch (e) {
|
|
224
313
|
this.Log.debug('lowlevel transport send error: ', e);
|
|
225
314
|
throw ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, e.reason);
|
|
@@ -231,6 +320,16 @@ export default class LowlevelTransport {
|
|
|
231
320
|
const jsonData = ProtocolV1.decodeMessage(messages, response);
|
|
232
321
|
return check.call(jsonData);
|
|
233
322
|
} catch (e) {
|
|
323
|
+
if (
|
|
324
|
+
e?.errorCode === HardwareErrorCode.BleTimeoutError &&
|
|
325
|
+
options?.timeoutMs !== PROTOCOL_PROBE_TIMEOUT_MS
|
|
326
|
+
) {
|
|
327
|
+
try {
|
|
328
|
+
await this.resetConnectionAfterProbe(uuid, 'V1');
|
|
329
|
+
} catch (resetError) {
|
|
330
|
+
this.Log.debug('[LowlevelTransport] reset after Protocol V1 timeout failed:', resetError);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
234
333
|
if (name === 'Initialize' && options?.timeoutMs === PROTOCOL_PROBE_TIMEOUT_MS) {
|
|
235
334
|
this.Log.debug('[LowlevelTransport] Protocol V1 Initialize probe call failed:', e);
|
|
236
335
|
} else {
|
|
@@ -273,12 +372,12 @@ export default class LowlevelTransport {
|
|
|
273
372
|
protocolHint?: ProtocolType
|
|
274
373
|
): Promise<ProtocolType> {
|
|
275
374
|
if (expectedProtocol === 'V2') {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
375
|
+
if (await this.probeProtocolV2(uuid)) {
|
|
376
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
377
|
+
this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
378
|
+
return 'V2';
|
|
379
|
+
}
|
|
380
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
282
381
|
}
|
|
283
382
|
|
|
284
383
|
if (expectedProtocol === 'V1') {
|
|
@@ -290,31 +389,20 @@ export default class LowlevelTransport {
|
|
|
290
389
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
291
390
|
}
|
|
292
391
|
|
|
293
|
-
|
|
294
|
-
this.deviceProtocol.
|
|
295
|
-
this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (hint)`);
|
|
296
|
-
return 'V2';
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
300
|
-
if (cachedProtocol === 'V2' && (await this.probeProtocolV2(uuid))) {
|
|
301
|
-
this.deviceProtocol.set(uuid, 'V2');
|
|
302
|
-
this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (cached)`);
|
|
303
|
-
return 'V2';
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const protocolV1Detected = await this.probeProtocolV1(uuid);
|
|
307
|
-
if (protocolV1Detected) {
|
|
308
|
-
this.deviceProtocol.set(uuid, 'V1');
|
|
309
|
-
this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V1`);
|
|
310
|
-
return 'V1';
|
|
311
|
-
}
|
|
392
|
+
const probeOrder: ProtocolType[] =
|
|
393
|
+
protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
312
394
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
395
|
+
for (const [index, protocol] of probeOrder.entries()) {
|
|
396
|
+
if (index > 0) {
|
|
397
|
+
await this.resetConnectionAfterProbe(uuid, probeOrder[index - 1]);
|
|
398
|
+
}
|
|
399
|
+
const detected =
|
|
400
|
+
protocol === 'V1' ? await this.probeProtocolV1(uuid) : await this.probeProtocolV2(uuid);
|
|
401
|
+
if (detected) {
|
|
402
|
+
this.deviceProtocol.set(uuid, protocol);
|
|
403
|
+
this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
|
|
404
|
+
return protocol;
|
|
405
|
+
}
|
|
318
406
|
}
|
|
319
407
|
|
|
320
408
|
this.deviceProtocol.delete(uuid);
|
|
@@ -431,7 +519,7 @@ export default class LowlevelTransport {
|
|
|
431
519
|
private async readProtocolV2Frame(uuid: string, timeoutMs?: number, commandName = 'ProtocolV2') {
|
|
432
520
|
let assembler = this.protocolV2Assemblers.get(uuid);
|
|
433
521
|
if (!assembler) {
|
|
434
|
-
assembler = new ProtocolV2FrameAssembler();
|
|
522
|
+
assembler = new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES);
|
|
435
523
|
this.protocolV2Assemblers.set(uuid, assembler);
|
|
436
524
|
}
|
|
437
525
|
|
|
@@ -449,11 +537,20 @@ export default class LowlevelTransport {
|
|
|
449
537
|
return frame;
|
|
450
538
|
}
|
|
451
539
|
|
|
452
|
-
private async writeProtocolV2Frame(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
540
|
+
private async writeProtocolV2Frame(
|
|
541
|
+
uuid: string,
|
|
542
|
+
frame: Uint8Array,
|
|
543
|
+
context: ProtocolV2CallContext,
|
|
544
|
+
assertCurrentGeneration: () => void
|
|
545
|
+
) {
|
|
546
|
+
await writeProtocolV2BleFrame({
|
|
547
|
+
frame,
|
|
548
|
+
packetCapacity: LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH,
|
|
549
|
+
assertActive: assertCurrentGeneration,
|
|
550
|
+
signal: context.signal,
|
|
551
|
+
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
552
|
+
writePacket: packet => this.plugin.send(uuid, bytesToHex(packet)),
|
|
553
|
+
});
|
|
457
554
|
}
|
|
458
555
|
|
|
459
556
|
private async callProtocolV2(
|
|
@@ -466,18 +563,13 @@ export default class LowlevelTransport {
|
|
|
466
563
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
467
564
|
}
|
|
468
565
|
|
|
469
|
-
const timeoutMs = options?.timeoutMs ?? LOWLEVEL_PROTOCOL_TIMEOUT_MS;
|
|
470
|
-
|
|
471
566
|
try {
|
|
472
567
|
return await this.protocolV2Links.call(
|
|
473
568
|
uuid,
|
|
474
569
|
() => this.createProtocolV2Adapter(uuid),
|
|
475
570
|
name,
|
|
476
571
|
data,
|
|
477
|
-
|
|
478
|
-
...options,
|
|
479
|
-
timeoutMs,
|
|
480
|
-
}
|
|
572
|
+
options
|
|
481
573
|
);
|
|
482
574
|
} catch (e) {
|
|
483
575
|
this.Log.error('lowlevel Protocol V2 call error: ', e);
|
|
@@ -501,10 +593,8 @@ export default class LowlevelTransport {
|
|
|
501
593
|
assertCurrentGeneration();
|
|
502
594
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
503
595
|
},
|
|
504
|
-
writeFrame: (frame: Uint8Array) =>
|
|
505
|
-
assertCurrentGeneration
|
|
506
|
-
return this.writeProtocolV2Frame(uuid, frame);
|
|
507
|
-
},
|
|
596
|
+
writeFrame: (frame: Uint8Array, context: ProtocolV2CallContext) =>
|
|
597
|
+
this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
|
|
508
598
|
readFrame: (context: { messageName: string; timeoutMs?: number }) => {
|
|
509
599
|
assertCurrentGeneration();
|
|
510
600
|
return this.readProtocolV2Frame(uuid, context.timeoutMs, context.messageName);
|