@onekeyfe/hd-transport-lowlevel 1.2.0-alpha.12 → 1.2.0-alpha.121

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.
@@ -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
+ });
@@ -122,6 +122,49 @@ const splitFrame = (frame, index) => [
122
122
  ];
123
123
 
124
124
  describe('LowlevelTransport protocol framing', () => {
125
+ test('falls back to Protocol V2 when a cached V1 hint is stale', async () => {
126
+ const plugin = createPlugin({ devices: [], responses: [] });
127
+ const lowlevel = configureTransport(plugin);
128
+ const events = [];
129
+ lowlevel.probeProtocolV1 = jest.fn().mockImplementation(() => {
130
+ events.push('probe-v1');
131
+ return Promise.resolve(false);
132
+ });
133
+ lowlevel.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
134
+ events.push('reset');
135
+ return Promise.resolve();
136
+ });
137
+ lowlevel.probeProtocolV2 = jest.fn().mockImplementation(() => {
138
+ events.push('probe-v2');
139
+ return Promise.resolve(true);
140
+ });
141
+
142
+ await expect(lowlevel.detectProtocol('pro-lowlevel', undefined, 'V1')).resolves.toBe('V2');
143
+
144
+ expect(events).toEqual(['probe-v1', 'reset', 'probe-v2']);
145
+ expect(lowlevel.getProtocolType('pro-lowlevel')).toBe('V2');
146
+ });
147
+
148
+ test('keeps active links when the Protocol V2 schema is configured repeatedly', () => {
149
+ const lowlevel = new LowlevelTransport();
150
+ const invalidateAllLinks = jest.fn().mockResolvedValue(undefined);
151
+ lowlevel.protocolV2Links.invalidateAllLinks = invalidateAllLinks;
152
+
153
+ lowlevel.configureProtocolV2(protocolV2Schema);
154
+ lowlevel.configureProtocolV2(protocolV2Schema);
155
+
156
+ expect(invalidateAllLinks).not.toHaveBeenCalled();
157
+
158
+ lowlevel.configureProtocolV2({
159
+ nested: {
160
+ ...protocolV2Schema.nested,
161
+ ExtraMessage: { fields: {} },
162
+ },
163
+ });
164
+
165
+ expect(invalidateAllLinks).toHaveBeenCalledWith('Protocol V2 schema reconfigured');
166
+ });
167
+
125
168
  test('keeps Protocol V1 raw notification chunks compatible', async () => {
126
169
  const responseChunks = ProtocolV1.encodeTransportPackets(schemas.protocolV1, 'Success', {
127
170
  message: 'ok',
@@ -142,6 +185,23 @@ describe('LowlevelTransport protocol framing', () => {
142
185
  });
143
186
  });
144
187
 
188
+ test('uses the Protocol V2 BLE writer with the lowlevel compatibility packet size', async () => {
189
+ const plugin = createPlugin({ devices: [], responses: [] });
190
+ const lowlevel = configureTransport(plugin);
191
+ const context = {
192
+ messageName: 'Ping',
193
+ timeoutMs: 1000,
194
+ highThroughput: false,
195
+ generation: 1,
196
+ signal: new AbortController().signal,
197
+ };
198
+
199
+ await lowlevel.writeProtocolV2Frame('pro2-id', new Uint8Array(130), context, jest.fn());
200
+
201
+ expect(plugin.send).toHaveBeenCalledTimes(3);
202
+ expect(plugin.send.mock.calls.map(([, hex]) => hex.length / 2)).toEqual([64, 64, 2]);
203
+ });
204
+
145
205
  test('rejects calls before protocol detection', async () => {
146
206
  const responseChunks = ProtocolV1.encodeTransportPackets(schemas.protocolV1, 'Success', {
147
207
  message: 'ok',
@@ -172,7 +232,7 @@ describe('LowlevelTransport protocol framing', () => {
172
232
  supported_messages: [60200, 60201, 60206, 60207],
173
233
  protobuf_definition: 'onekey-protocol-v2',
174
234
  },
175
- { router: PROTOCOL_V2_CHANNEL_BLE_UART }
235
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: 2 }
176
236
  );
177
237
  const plugin = createPlugin({
178
238
  devices: [{ id: 'pro2-id', name: 'OneKey Pro 2', commType: 'ble' }],
@@ -253,15 +313,19 @@ describe('LowlevelTransport protocol framing', () => {
253
313
  });
254
314
 
255
315
  test('reuses the active generation when Core acquires the same BLE connection again', async () => {
256
- const probeResponse = ProtocolV2.encodeFrame(
257
- schemas,
258
- 'Success',
259
- { message: 'ok' },
260
- { router: PROTOCOL_V2_CHANNEL_BLE_UART }
316
+ const responses = [1, 2, 3, 4].map(seq =>
317
+ bytesToHex(
318
+ ProtocolV2.encodeFrame(
319
+ schemas,
320
+ 'Success',
321
+ { message: 'ok' },
322
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq }
323
+ )
324
+ )
261
325
  );
262
326
  const plugin = createPlugin({
263
327
  devices: [{ id: 'repeated-acquire-id', name: 'OneKey Pro 2', commType: 'ble' }],
264
- responses: [bytesToHex(probeResponse), bytesToHex(probeResponse)],
328
+ responses,
265
329
  });
266
330
  const lowlevel = configureTransport(plugin);
267
331
 
@@ -283,13 +347,19 @@ describe('LowlevelTransport protocol framing', () => {
283
347
  const sentSeqs = plugin.send.mock.calls.map(([, hex]) =>
284
348
  Number.parseInt(hex.slice(12, 14), 16)
285
349
  );
286
- expect(sentSeqs).toEqual([1, 2]);
350
+ expect(sentSeqs).toEqual([1, 2, 3, 4]);
287
351
  });
288
352
 
289
- test('trusts explicit Protocol V2 during bootloader reconnect without probing Ping', async () => {
353
+ test('actively probes explicit Protocol V2 during bootloader reconnect', async () => {
354
+ const probeResponse = ProtocolV2.encodeFrame(
355
+ schemas,
356
+ 'Success',
357
+ { message: 'ok' },
358
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
359
+ );
290
360
  const plugin = createPlugin({
291
361
  devices: [{ id: 'bootloader-v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
292
- responses: [],
362
+ responses: [bytesToHex(probeResponse)],
293
363
  });
294
364
  const lowlevel = configureTransport(plugin);
295
365
 
@@ -299,8 +369,8 @@ describe('LowlevelTransport protocol framing', () => {
299
369
  uuid: 'bootloader-v2-id',
300
370
  protocolType: 'V2',
301
371
  });
302
- expect(plugin.send).not.toHaveBeenCalled();
303
- expect(plugin.receive).not.toHaveBeenCalled();
372
+ expect(plugin.send).toHaveBeenCalledTimes(1);
373
+ expect(plugin.receive).toHaveBeenCalledTimes(1);
304
374
  });
305
375
 
306
376
  test('resets the lowlevel connection before probing Protocol V2 after a V1 timeout', async () => {
@@ -342,11 +412,23 @@ describe('LowlevelTransport protocol framing', () => {
342
412
  });
343
413
 
344
414
  test('disconnects a tainted Protocol V2 link after a response timeout', async () => {
415
+ const probeResponse = ProtocolV2.encodeFrame(
416
+ schemas,
417
+ 'Success',
418
+ { message: 'ok' },
419
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
420
+ );
345
421
  const plugin = createPlugin({
346
422
  devices: [{ id: 'timeout-v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
347
- responses: [],
423
+ responses: [bytesToHex(probeResponse)],
424
+ });
425
+ let receiveCount = 0;
426
+ plugin.receive.mockImplementation(() => {
427
+ receiveCount += 1;
428
+ return receiveCount === 1
429
+ ? Promise.resolve(bytesToHex(probeResponse))
430
+ : new Promise(() => {});
348
431
  });
349
- plugin.receive.mockImplementation(() => new Promise(() => {}));
350
432
  const lowlevel = configureTransport(plugin);
351
433
 
352
434
  await lowlevel.acquire({ uuid: 'timeout-v2-id', expectedProtocol: 'V2' });
@@ -358,6 +440,28 @@ describe('LowlevelTransport protocol framing', () => {
358
440
  expect(plugin.disconnect).toHaveBeenCalledWith('timeout-v2-id');
359
441
  });
360
442
 
443
+ test('preserves an undefined business timeout outside explicit probes', async () => {
444
+ const plugin = createPlugin({
445
+ devices: [{ id: 'v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
446
+ responses: [],
447
+ });
448
+ const lowlevel = configureTransport(plugin);
449
+ lowlevel.deviceProtocol.set('v2-id', 'V2');
450
+ const linkCall = jest
451
+ .spyOn(lowlevel.protocolV2Links, 'call')
452
+ .mockResolvedValue({ type: 'Success', message: {} });
453
+
454
+ await lowlevel.call('v2-id', 'Ping', { message: 'no-business-timeout' });
455
+
456
+ expect(linkCall).toHaveBeenCalledWith(
457
+ 'v2-id',
458
+ expect.any(Function),
459
+ 'Ping',
460
+ { message: 'no-business-timeout' },
461
+ undefined
462
+ );
463
+ });
464
+
361
465
  test('verifies expected Protocol V1 instead of trusting the requested protocol', async () => {
362
466
  const plugin = createPlugin({
363
467
  devices: [{ id: 'v2-id', name: 'Unknown BLE Device', commType: 'ble' }],
package/dist/index.d.ts CHANGED
@@ -5,8 +5,18 @@ import EventEmitter from 'events';
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,6 +30,7 @@ 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;
@@ -51,4 +62,4 @@ declare class LowlevelTransport {
51
62
  cancel(): void;
52
63
  }
53
64
 
54
- export { LowlevelTransport as default };
65
+ export { LowlevelTransport as default, getProtocolV1SendOptions, shouldLogFirmwareUploadProgress };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAqBpD,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,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;IAOnC,MAAM;IAIA,SAAS;IAWT,OAAO,CAAC,KAAK,EAAE,oBAAoB;;;;IA6BnC,OAAO,CAAC,IAAI,EAAE,MAAM;IAgBpB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAsBlB,cAAc;IAoC5B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;YAsDd,yBAAyB;YAiCzB,eAAe;YAgBf,eAAe;YA6Bf,UAAU;YAUV,qBAAqB;YAmBrB,mBAAmB;YAqBnB,oBAAoB;YAOpB,cAAc;IA6B5B,OAAO,CAAC,uBAAuB;IAkC/B,OAAO,CAAC,2BAA2B;IAMnC,MAAM;CAGP"}
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,cAAc,EACd,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;AAcD,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;IAWT,OAAO,CAAC,KAAK,EAAE,oBAAoB;;;;IAgCnC,OAAO,CAAC,IAAI,EAAE,MAAM;IAgBpB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAoBlB,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,8 +42,17 @@ 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;
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;
55
+ }
45
56
  function inferProtocolHintFromDeviceName(name) {
46
57
  return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
47
58
  }
@@ -106,10 +117,18 @@ class LowlevelTransport {
106
117
  this._messages = messages;
107
118
  }
108
119
  configureProtocolV2(signedData) {
120
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
121
+ if (this.protocolV2SchemaConfiguration === configuration) {
122
+ return;
123
+ }
124
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
109
125
  this._messagesV2 = parseConfigure(signedData);
110
- this.protocolV2Links
111
- .invalidateAllLinks('Protocol V2 schema reconfigured')
112
- .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('Protocol V2 schema link cleanup failed:', error); });
126
+ this.protocolV2SchemaConfiguration = configuration;
127
+ if (isReconfiguration) {
128
+ this.protocolV2Links
129
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
130
+ .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('Protocol V2 schema link cleanup failed:', error); });
131
+ }
113
132
  }
114
133
  listen() {
115
134
  }
@@ -126,7 +145,7 @@ class LowlevelTransport {
126
145
  });
127
146
  }
128
147
  acquire(input) {
129
- var _a;
148
+ var _a, _b;
130
149
  return __awaiter(this, void 0, void 0, function* () {
131
150
  const alreadyConnected = this.connectedDevices.has(input.uuid);
132
151
  try {
@@ -141,10 +160,10 @@ class LowlevelTransport {
141
160
  this.Log.debug('lowlelvel transport connect error: ', error);
142
161
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.LowlevelTrasnportConnectError, (_a = error.message) !== null && _a !== void 0 ? _a : error);
143
162
  }
144
- this.protocolV2Assemblers.set(input.uuid, new transport.ProtocolV2FrameAssembler());
163
+ this.protocolV2Assemblers.set(input.uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
145
164
  const protocolHint = input.expectedProtocol
146
165
  ? undefined
147
- : this.deviceProtocolHints.get(input.uuid);
166
+ : (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(input.uuid);
148
167
  const protocolType = yield this.detectProtocol(input.uuid, input.expectedProtocol, protocolHint);
149
168
  return { uuid: input.uuid, protocolType };
150
169
  });
@@ -174,7 +193,6 @@ class LowlevelTransport {
174
193
  if (!protocol) {
175
194
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
176
195
  }
177
- this.Log.debug('transport call', { name, protocol });
178
196
  if (protocol === 'V2') {
179
197
  return this.callProtocolV2(uuid, name, data, options);
180
198
  }
@@ -182,16 +200,42 @@ class LowlevelTransport {
182
200
  });
183
201
  }
184
202
  callProtocolV1(uuid, name, data, options) {
203
+ var _a;
185
204
  return __awaiter(this, void 0, void 0, function* () {
186
205
  if (!this._messages) {
187
206
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
188
207
  }
189
208
  const messages = this._messages;
190
209
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
191
- for (const o of buffers) {
210
+ const isFirmwareUpload = name === 'FirmwareUpload';
211
+ const uploadStartedAt = Date.now();
212
+ const totalBytes = buffers.reduce((sum, buffer) => sum + buffer.limit, 0);
213
+ let sentBytes = 0;
214
+ let lastLoggedPercent = 0;
215
+ let lastLoggedAt = uploadStartedAt;
216
+ for (const [index, o] of buffers.entries()) {
192
217
  const outData = o.toString('hex');
193
218
  try {
194
- yield this.plugin.send(uuid, outData);
219
+ yield this.plugin.send(uuid, outData, getProtocolV1SendOptions(name));
220
+ sentBytes += o.limit;
221
+ if (isFirmwareUpload) {
222
+ const now = Date.now();
223
+ const percent = Math.floor(((index + 1) / buffers.length) * 100);
224
+ if (shouldLogFirmwareUploadProgress({
225
+ percent,
226
+ lastLoggedPercent,
227
+ now,
228
+ lastLoggedAt,
229
+ })) {
230
+ const elapsedSeconds = Math.max((now - uploadStartedAt) / 1000, 0.001);
231
+ const kibPerSecond = sentBytes / 1024 / elapsedSeconds;
232
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[LowlevelTransport] FirmwareUpload progress: ${percent}% ` +
233
+ `(${index + 1}/${buffers.length} packets, ${sentBytes}/${totalBytes} bytes, ` +
234
+ `${elapsedSeconds.toFixed(1)}s, ${kibPerSecond.toFixed(1)} KiB/s)`);
235
+ lastLoggedPercent = percent;
236
+ lastLoggedAt = now;
237
+ }
238
+ }
195
239
  }
196
240
  catch (e) {
197
241
  this.Log.debug('lowlevel transport send error: ', e);
@@ -204,6 +248,15 @@ class LowlevelTransport {
204
248
  return check.call(jsonData);
205
249
  }
206
250
  catch (e) {
251
+ if ((e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError &&
252
+ (options === null || options === void 0 ? void 0 : options.timeoutMs) !== PROTOCOL_PROBE_TIMEOUT_MS) {
253
+ try {
254
+ yield this.resetConnectionAfterProbe(uuid, 'V1');
255
+ }
256
+ catch (resetError) {
257
+ this.Log.debug('[LowlevelTransport] reset after Protocol V1 timeout failed:', resetError);
258
+ }
259
+ }
207
260
  if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
208
261
  this.Log.debug('[LowlevelTransport] Protocol V1 Initialize probe call failed:', e);
209
262
  }
@@ -229,12 +282,15 @@ class LowlevelTransport {
229
282
  }
230
283
  }
231
284
  detectProtocol(uuid, expectedProtocol, protocolHint) {
232
- var _a, _b, _c, _d, _e, _f;
285
+ var _a, _b, _c;
233
286
  return __awaiter(this, void 0, void 0, function* () {
234
287
  if (expectedProtocol === 'V2') {
235
- this.deviceProtocol.set(uuid, 'V2');
236
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
237
- return 'V2';
288
+ if (yield this.probeProtocolV2(uuid)) {
289
+ this.deviceProtocol.set(uuid, 'V2');
290
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
291
+ return 'V2';
292
+ }
293
+ throw this.createProtocolMismatchError(expectedProtocol);
238
294
  }
239
295
  if (expectedProtocol === 'V1') {
240
296
  if (yield this.probeProtocolV1(uuid)) {
@@ -244,28 +300,17 @@ class LowlevelTransport {
244
300
  }
245
301
  throw this.createProtocolMismatchError(expectedProtocol);
246
302
  }
247
- if (protocolHint === 'V2' && (yield this.probeProtocolV2(uuid))) {
248
- this.deviceProtocol.set(uuid, 'V2');
249
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (hint)`);
250
- return 'V2';
251
- }
252
- const cachedProtocol = this.deviceProtocol.get(uuid);
253
- if (cachedProtocol === 'V2' && (yield this.probeProtocolV2(uuid))) {
254
- this.deviceProtocol.set(uuid, 'V2');
255
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (cached)`);
256
- return 'V2';
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';
303
+ const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
304
+ for (const [index, protocol] of probeOrder.entries()) {
305
+ if (index > 0) {
306
+ yield this.resetConnectionAfterProbe(uuid, probeOrder[index - 1]);
307
+ }
308
+ const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
309
+ if (detected) {
310
+ this.deviceProtocol.set(uuid, protocol);
311
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
312
+ return protocol;
313
+ }
269
314
  }
270
315
  this.deviceProtocol.delete(uuid);
271
316
  throw this.createProtocolDetectionError();
@@ -373,7 +418,7 @@ class LowlevelTransport {
373
418
  return __awaiter(this, void 0, void 0, function* () {
374
419
  let assembler = this.protocolV2Assemblers.get(uuid);
375
420
  if (!assembler) {
376
- assembler = new transport.ProtocolV2FrameAssembler();
421
+ assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES);
377
422
  this.protocolV2Assemblers.set(uuid, assembler);
378
423
  }
379
424
  const queuedFrame = assembler.push(new Uint8Array(0));
@@ -390,23 +435,25 @@ class LowlevelTransport {
390
435
  return frame;
391
436
  });
392
437
  }
393
- writeProtocolV2Frame(uuid, frame) {
438
+ writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration) {
394
439
  return __awaiter(this, void 0, void 0, function* () {
395
- for (let offset = 0; offset < frame.length; offset += LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH) {
396
- const chunk = frame.slice(offset, offset + LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH);
397
- yield this.plugin.send(uuid, transport.bytesToHex(chunk));
398
- }
440
+ yield transport.writeProtocolV2BleFrame({
441
+ frame,
442
+ packetCapacity: LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH,
443
+ assertActive: assertCurrentGeneration,
444
+ signal: context.signal,
445
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
446
+ writePacket: packet => this.plugin.send(uuid, transport.bytesToHex(packet)),
447
+ });
399
448
  });
400
449
  }
401
450
  callProtocolV2(uuid, name, data, options) {
402
- var _a;
403
451
  return __awaiter(this, void 0, void 0, function* () {
404
452
  if (!this._messages || !this._messagesV2) {
405
453
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
406
454
  }
407
- const timeoutMs = (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : LOWLEVEL_PROTOCOL_TIMEOUT_MS;
408
455
  try {
409
- return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, Object.assign(Object.assign({}, options), { timeoutMs }));
456
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, options);
410
457
  }
411
458
  catch (e) {
412
459
  this.Log.error('lowlevel Protocol V2 call error: ', e);
@@ -431,10 +478,7 @@ class LowlevelTransport {
431
478
  assertCurrentGeneration();
432
479
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
433
480
  },
434
- writeFrame: (frame) => {
435
- assertCurrentGeneration();
436
- return this.writeProtocolV2Frame(uuid, frame);
437
- },
481
+ writeFrame: (frame, context) => this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
438
482
  readFrame: (context) => {
439
483
  assertCurrentGeneration();
440
484
  return this.readProtocolV2Frame(uuid, context.timeoutMs, context.messageName);
@@ -459,4 +503,6 @@ class LowlevelTransport {
459
503
  }
460
504
  }
461
505
 
462
- module.exports = LowlevelTransport;
506
+ exports["default"] = LowlevelTransport;
507
+ exports.getProtocolV1SendOptions = getProtocolV1SendOptions;
508
+ exports.shouldLogFirmwareUploadProgress = shouldLogFirmwareUploadProgress;
package/dist/types.d.ts CHANGED
@@ -2,5 +2,6 @@ import type { ProtocolType } from '@onekeyfe/hd-transport';
2
2
  export type LowLevelAcquireInput = {
3
3
  uuid: string;
4
4
  expectedProtocol?: ProtocolType;
5
+ protocolHint?: ProtocolType;
5
6
  };
6
7
  //# sourceMappingURL=types.d.ts.map
@@ -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;CACjC,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.12",
3
+ "version": "1.2.0-alpha.121",
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.12",
24
- "@onekeyfe/hd-transport": "1.2.0-alpha.12"
23
+ "@onekeyfe/hd-shared": "1.2.0-alpha.121",
24
+ "@onekeyfe/hd-transport": "1.2.0-alpha.121"
25
25
  },
26
- "gitHead": "3601e8003c96a2a4ca7856e865d16f48c0c5b758"
26
+ "gitHead": "916633f46e4d8e0599af2956a639474dd3cbd9ec"
27
27
  }
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ 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';
@@ -17,6 +18,7 @@ import type {
17
18
  LowLevelDevice,
18
19
  LowlevelTransportSharedPlugin,
19
20
  ProtocolType,
21
+ ProtocolV2CallContext,
20
22
  TransportCallOptions,
21
23
  } from '@onekeyfe/hd-transport';
22
24
  import type { LowLevelAcquireInput } from './types';
@@ -25,8 +27,31 @@ const { check, ProtocolV1, parseConfigure } = transport;
25
27
 
26
28
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
27
29
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
28
- const LOWLEVEL_PROTOCOL_TIMEOUT_MS = 30_000;
29
30
  const LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH = 64;
31
+ const FIRMWARE_UPLOAD_LOG_PERCENT_STEP = 5;
32
+ const FIRMWARE_UPLOAD_LOG_INTERVAL_MS = 10_000;
33
+
34
+ export function shouldLogFirmwareUploadProgress({
35
+ percent,
36
+ lastLoggedPercent,
37
+ now,
38
+ lastLoggedAt,
39
+ }: {
40
+ percent: number;
41
+ lastLoggedPercent: number;
42
+ now: number;
43
+ lastLoggedAt: number;
44
+ }) {
45
+ return (
46
+ percent === 100 ||
47
+ percent - lastLoggedPercent >= FIRMWARE_UPLOAD_LOG_PERCENT_STEP ||
48
+ now - lastLoggedAt >= FIRMWARE_UPLOAD_LOG_INTERVAL_MS
49
+ );
50
+ }
51
+
52
+ export function getProtocolV1SendOptions(name: string) {
53
+ return name === 'FirmwareUpload' ? { withoutResponse: false } : undefined;
54
+ }
30
55
 
31
56
  function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
32
57
  return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
@@ -94,6 +119,8 @@ export default class LowlevelTransport {
94
119
  },
95
120
  });
96
121
 
122
+ private protocolV2SchemaConfiguration: string | undefined;
123
+
97
124
  getProtocolType(path: string): ProtocolType | undefined {
98
125
  return this.deviceProtocol.get(path);
99
126
  }
@@ -112,10 +139,20 @@ export default class LowlevelTransport {
112
139
  }
113
140
 
114
141
  configureProtocolV2(signedData: any) {
142
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
143
+ if (this.protocolV2SchemaConfiguration === configuration) {
144
+ return;
145
+ }
146
+
147
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
115
148
  this._messagesV2 = parseConfigure(signedData);
116
- this.protocolV2Links
117
- .invalidateAllLinks('Protocol V2 schema reconfigured')
118
- .catch(error => this.Log?.debug('Protocol V2 schema link cleanup failed:', error));
149
+ this.protocolV2SchemaConfiguration = configuration;
150
+
151
+ if (isReconfiguration) {
152
+ this.protocolV2Links
153
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
154
+ .catch(error => this.Log?.debug('Protocol V2 schema link cleanup failed:', error));
155
+ }
119
156
  }
120
157
 
121
158
  listen() {
@@ -150,10 +187,13 @@ export default class LowlevelTransport {
150
187
  );
151
188
  }
152
189
 
153
- this.protocolV2Assemblers.set(input.uuid, new ProtocolV2FrameAssembler());
190
+ this.protocolV2Assemblers.set(
191
+ input.uuid,
192
+ new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES)
193
+ );
154
194
  const protocolHint = input.expectedProtocol
155
195
  ? undefined
156
- : this.deviceProtocolHints.get(input.uuid);
196
+ : input.protocolHint ?? this.deviceProtocolHints.get(input.uuid);
157
197
  const protocolType = await this.detectProtocol(
158
198
  input.uuid,
159
199
  input.expectedProtocol,
@@ -168,8 +208,8 @@ export default class LowlevelTransport {
168
208
  await this.plugin.disconnect(uuid);
169
209
  this.connectedDevices.delete(uuid);
170
210
  this.deviceProtocol.delete(uuid);
171
- // 设备名称推断出的协议提示不依赖当前连接,保留它可以让快速重连
172
- // 直接执行 Protocol V2 探测,避免先发送一次无意义的 V1 Initialize
211
+ // A name-derived protocol hint survives disconnect and lets fast reconnect probe
212
+ // Protocol V2 first without sending a redundant V1 Initialize.
173
213
  this.protocolV2Assemblers.delete(uuid);
174
214
  return true;
175
215
  } catch (error) {
@@ -195,8 +235,6 @@ export default class LowlevelTransport {
195
235
  `Device protocol has not been detected for ${uuid}`
196
236
  );
197
237
  }
198
- this.Log.debug('transport call', { name, protocol });
199
-
200
238
  if (protocol === 'V2') {
201
239
  return this.callProtocolV2(uuid, name, data, options);
202
240
  }
@@ -216,10 +254,41 @@ export default class LowlevelTransport {
216
254
 
217
255
  const messages = this._messages;
218
256
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
219
- for (const o of buffers) {
257
+ const isFirmwareUpload = name === 'FirmwareUpload';
258
+ const uploadStartedAt = Date.now();
259
+ const totalBytes = buffers.reduce((sum, buffer) => sum + buffer.limit, 0);
260
+ let sentBytes = 0;
261
+ let lastLoggedPercent = 0;
262
+ let lastLoggedAt = uploadStartedAt;
263
+
264
+ for (const [index, o] of buffers.entries()) {
220
265
  const outData = o.toString('hex');
221
266
  try {
222
- await this.plugin.send(uuid, outData);
267
+ await this.plugin.send(uuid, outData, getProtocolV1SendOptions(name));
268
+ sentBytes += o.limit;
269
+
270
+ if (isFirmwareUpload) {
271
+ const now = Date.now();
272
+ const percent = Math.floor(((index + 1) / buffers.length) * 100);
273
+ if (
274
+ shouldLogFirmwareUploadProgress({
275
+ percent,
276
+ lastLoggedPercent,
277
+ now,
278
+ lastLoggedAt,
279
+ })
280
+ ) {
281
+ const elapsedSeconds = Math.max((now - uploadStartedAt) / 1000, 0.001);
282
+ const kibPerSecond = sentBytes / 1024 / elapsedSeconds;
283
+ this.Log?.debug(
284
+ `[LowlevelTransport] FirmwareUpload progress: ${percent}% ` +
285
+ `(${index + 1}/${buffers.length} packets, ${sentBytes}/${totalBytes} bytes, ` +
286
+ `${elapsedSeconds.toFixed(1)}s, ${kibPerSecond.toFixed(1)} KiB/s)`
287
+ );
288
+ lastLoggedPercent = percent;
289
+ lastLoggedAt = now;
290
+ }
291
+ }
223
292
  } catch (e) {
224
293
  this.Log.debug('lowlevel transport send error: ', e);
225
294
  throw ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, e.reason);
@@ -231,6 +300,16 @@ export default class LowlevelTransport {
231
300
  const jsonData = ProtocolV1.decodeMessage(messages, response);
232
301
  return check.call(jsonData);
233
302
  } catch (e) {
303
+ if (
304
+ e?.errorCode === HardwareErrorCode.BleTimeoutError &&
305
+ options?.timeoutMs !== PROTOCOL_PROBE_TIMEOUT_MS
306
+ ) {
307
+ try {
308
+ await this.resetConnectionAfterProbe(uuid, 'V1');
309
+ } catch (resetError) {
310
+ this.Log.debug('[LowlevelTransport] reset after Protocol V1 timeout failed:', resetError);
311
+ }
312
+ }
234
313
  if (name === 'Initialize' && options?.timeoutMs === PROTOCOL_PROBE_TIMEOUT_MS) {
235
314
  this.Log.debug('[LowlevelTransport] Protocol V1 Initialize probe call failed:', e);
236
315
  } else {
@@ -273,12 +352,12 @@ export default class LowlevelTransport {
273
352
  protocolHint?: ProtocolType
274
353
  ): Promise<ProtocolType> {
275
354
  if (expectedProtocol === 'V2') {
276
- // 固件升级重启后的 bootloader 不保证响应 Ping。上层显式传入 V2
277
- // 表示协议已经在重启前确认,这里与 RN/Electron BLE 保持一致,
278
- // 直接建立 V2 Link,首个业务命令负责验证链路是否可用。
279
- this.deviceProtocol.set(uuid, 'V2');
280
- this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
281
- return 'V2';
355
+ if (await this.probeProtocolV2(uuid)) {
356
+ this.deviceProtocol.set(uuid, 'V2');
357
+ this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
358
+ return 'V2';
359
+ }
360
+ throw this.createProtocolMismatchError(expectedProtocol);
282
361
  }
283
362
 
284
363
  if (expectedProtocol === 'V1') {
@@ -290,31 +369,20 @@ export default class LowlevelTransport {
290
369
  throw this.createProtocolMismatchError(expectedProtocol);
291
370
  }
292
371
 
293
- if (protocolHint === 'V2' && (await this.probeProtocolV2(uuid))) {
294
- this.deviceProtocol.set(uuid, 'V2');
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
- }
372
+ const probeOrder: ProtocolType[] =
373
+ protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
312
374
 
313
- await this.resetConnectionAfterProbe(uuid, 'V1');
314
- if (await this.probeProtocolV2(uuid)) {
315
- this.deviceProtocol.set(uuid, 'V2');
316
- this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2`);
317
- return 'V2';
375
+ for (const [index, protocol] of probeOrder.entries()) {
376
+ if (index > 0) {
377
+ await this.resetConnectionAfterProbe(uuid, probeOrder[index - 1]);
378
+ }
379
+ const detected =
380
+ protocol === 'V1' ? await this.probeProtocolV1(uuid) : await this.probeProtocolV2(uuid);
381
+ if (detected) {
382
+ this.deviceProtocol.set(uuid, protocol);
383
+ this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
384
+ return protocol;
385
+ }
318
386
  }
319
387
 
320
388
  this.deviceProtocol.delete(uuid);
@@ -431,7 +499,7 @@ export default class LowlevelTransport {
431
499
  private async readProtocolV2Frame(uuid: string, timeoutMs?: number, commandName = 'ProtocolV2') {
432
500
  let assembler = this.protocolV2Assemblers.get(uuid);
433
501
  if (!assembler) {
434
- assembler = new ProtocolV2FrameAssembler();
502
+ assembler = new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES);
435
503
  this.protocolV2Assemblers.set(uuid, assembler);
436
504
  }
437
505
 
@@ -449,11 +517,20 @@ export default class LowlevelTransport {
449
517
  return frame;
450
518
  }
451
519
 
452
- private async writeProtocolV2Frame(uuid: string, frame: Uint8Array) {
453
- for (let offset = 0; offset < frame.length; offset += LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH) {
454
- const chunk = frame.slice(offset, offset + LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH);
455
- await this.plugin.send(uuid, bytesToHex(chunk));
456
- }
520
+ private async writeProtocolV2Frame(
521
+ uuid: string,
522
+ frame: Uint8Array,
523
+ context: ProtocolV2CallContext,
524
+ assertCurrentGeneration: () => void
525
+ ) {
526
+ await writeProtocolV2BleFrame({
527
+ frame,
528
+ packetCapacity: LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH,
529
+ assertActive: assertCurrentGeneration,
530
+ signal: context.signal,
531
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
532
+ writePacket: packet => this.plugin.send(uuid, bytesToHex(packet)),
533
+ });
457
534
  }
458
535
 
459
536
  private async callProtocolV2(
@@ -466,18 +543,13 @@ export default class LowlevelTransport {
466
543
  throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
467
544
  }
468
545
 
469
- const timeoutMs = options?.timeoutMs ?? LOWLEVEL_PROTOCOL_TIMEOUT_MS;
470
-
471
546
  try {
472
547
  return await this.protocolV2Links.call(
473
548
  uuid,
474
549
  () => this.createProtocolV2Adapter(uuid),
475
550
  name,
476
551
  data,
477
- {
478
- ...options,
479
- timeoutMs,
480
- }
552
+ options
481
553
  );
482
554
  } catch (e) {
483
555
  this.Log.error('lowlevel Protocol V2 call error: ', e);
@@ -501,10 +573,8 @@ export default class LowlevelTransport {
501
573
  assertCurrentGeneration();
502
574
  this.protocolV2Assemblers.get(uuid)?.reset();
503
575
  },
504
- writeFrame: (frame: Uint8Array) => {
505
- assertCurrentGeneration();
506
- return this.writeProtocolV2Frame(uuid, frame);
507
- },
576
+ writeFrame: (frame: Uint8Array, context: ProtocolV2CallContext) =>
577
+ this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
508
578
  readFrame: (context: { messageName: string; timeoutMs?: number }) => {
509
579
  assertCurrentGeneration();
510
580
  return this.readProtocolV2Frame(uuid, context.timeoutMs, context.messageName);
package/src/types.ts CHANGED
@@ -3,4 +3,5 @@ import type { ProtocolType } from '@onekeyfe/hd-transport';
3
3
  export type LowLevelAcquireInput = {
4
4
  uuid: string;
5
5
  expectedProtocol?: ProtocolType;
6
+ protocolHint?: ProtocolType;
6
7
  };