@onekeyfe/hd-core 1.2.0-alpha.8 → 1.2.0-alpha.9
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__/device-wallet-session-store.test.ts +131 -0
- package/__tests__/protocol-v2-firmware-targets.test.ts +19 -0
- package/__tests__/protocol-v2.test.ts +738 -460
- package/dist/api/ClearSessionCache.d.ts +9 -0
- package/dist/api/ClearSessionCache.d.ts.map +1 -0
- package/dist/api/FirmwareUpdateV4.d.ts +7 -6
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +1 -1
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts +9 -0
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/device/Device.d.ts +2 -2
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceWalletSessionStore.d.ts +15 -0
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -0
- package/dist/index.d.ts +32 -19
- package/dist/index.js +644 -263
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +1 -0
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +13 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdate.d.ts +4 -1
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +6 -1
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +6 -2
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/sessionCache.d.ts +10 -0
- package/dist/types/api/sessionCache.d.ts.map +1 -0
- package/dist/types/settings.d.ts +6 -14
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/ClearSessionCache.ts +28 -0
- package/src/api/FileRead.ts +2 -2
- package/src/api/FirmwareUpdateV4.ts +269 -210
- package/src/api/index.ts +3 -0
- package/src/api/protocol-v2/DeviceSessionGet.ts +26 -0
- package/src/api/protocol-v2/DeviceStatusGet.ts +14 -0
- package/src/api/protocol-v2/FilesystemFormat.ts +4 -1
- package/src/api/protocol-v2/helpers.ts +17 -14
- package/src/data/messages/messages-protocol-v2.json +134 -3
- package/src/data/messages/messages.json +8 -0
- package/src/device/Device.ts +64 -56
- package/src/device/DeviceWalletSessionStore.ts +78 -0
- package/src/inject.ts +5 -2
- package/src/protocols/protocol-v2/index.ts +1 -0
- package/src/protocols/protocol-v2/walletSession.ts +83 -0
- package/src/types/api/firmwareUpdate.ts +8 -2
- package/src/types/api/index.ts +10 -3
- package/src/types/api/protocolV2.ts +16 -2
- package/src/types/api/sessionCache.ts +14 -0
- package/src/types/settings.ts +14 -16
- package/src/utils/deviceFeaturesUtils.ts +22 -32
|
@@ -38,7 +38,6 @@ import type {
|
|
|
38
38
|
Features,
|
|
39
39
|
IFirmwareReleaseInfo,
|
|
40
40
|
IProtocolV2FirmwareComponent,
|
|
41
|
-
IProtocolV2ResourceManifestPackage,
|
|
42
41
|
IVersionArray,
|
|
43
42
|
} from '../types';
|
|
44
43
|
|
|
@@ -47,19 +46,20 @@ const Log = getLogger(LoggerNames.Method);
|
|
|
47
46
|
const SESSION_ERROR = 'session not found';
|
|
48
47
|
const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 60 * 1000;
|
|
49
48
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
50
|
-
const PROTOCOL_V2_START_UPDATE_TIMEOUT = 60 * 1000;
|
|
49
|
+
const PROTOCOL_V2_START_UPDATE_TIMEOUT = 3 * 60 * 1000;
|
|
51
50
|
const PROTOCOL_V2_INSTALL_TIMEOUT = 5 * 60 * 1000;
|
|
52
51
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
53
52
|
const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
|
|
54
53
|
const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
|
|
55
54
|
const PROTOCOL_V2_TARGET_STATUS_FAILED_MIN = 3;
|
|
56
55
|
const PROTOCOL_V2_CONNECT_PROTOCOL = 'V2';
|
|
57
|
-
const PROTOCOL_V2_FIRMWARE_STAGING_VOLUME = '
|
|
56
|
+
const PROTOCOL_V2_FIRMWARE_STAGING_VOLUME = 'vol0:/';
|
|
58
57
|
const PROTOCOL_V2_MIN_FILE_CHUNK_SIZE = 64;
|
|
59
58
|
const PROTOCOL_V2_CONNECT_RETRY_COUNT = 10;
|
|
60
59
|
const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
61
60
|
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
62
61
|
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 60 * 1000;
|
|
62
|
+
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
63
63
|
const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
64
64
|
const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
|
|
65
65
|
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
@@ -107,6 +107,25 @@ type ProtocolV2InstallTarget = ProtocolV2InstallItem & {
|
|
|
107
107
|
path: string;
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
+
type ProtocolV2FileTransferParams = PROTO.FirmwareUpload & {
|
|
111
|
+
filePath: string;
|
|
112
|
+
processedSize?: number;
|
|
113
|
+
totalSize?: number;
|
|
114
|
+
onTransferredBytes?: (transferredBytes: number) => void;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/** RESC bundle okpkg(FileWrite 直写模式),每个独立同步到 devicePath */
|
|
118
|
+
type ProtocolV2ResourceBundleBinary = {
|
|
119
|
+
name: string;
|
|
120
|
+
binary: ArrayBuffer;
|
|
121
|
+
devicePath: string;
|
|
122
|
+
/** 远端配置模式下的下载 URL(手动模式不填) */
|
|
123
|
+
url?: string;
|
|
124
|
+
version?: IVersionArray;
|
|
125
|
+
payloadHash?: string;
|
|
126
|
+
headerHash?: string;
|
|
127
|
+
};
|
|
128
|
+
|
|
110
129
|
type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
|
|
111
130
|
binary: ArrayBuffer;
|
|
112
131
|
};
|
|
@@ -114,7 +133,7 @@ type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
|
|
|
114
133
|
type ProtocolV2RemoteComponentTarget = {
|
|
115
134
|
fileName: string;
|
|
116
135
|
targetId: number;
|
|
117
|
-
kind: 'bootloader' | 'firmware'
|
|
136
|
+
kind: 'bootloader' | 'firmware';
|
|
118
137
|
};
|
|
119
138
|
|
|
120
139
|
type ProtocolV2OkppHeader = {
|
|
@@ -167,11 +186,6 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS: Readonly<
|
|
|
167
186
|
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04,
|
|
168
187
|
kind: 'firmware',
|
|
169
188
|
},
|
|
170
|
-
CRATE: {
|
|
171
|
-
fileName: 'resource.bin',
|
|
172
|
-
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
|
|
173
|
-
kind: 'resource',
|
|
174
|
-
},
|
|
175
189
|
};
|
|
176
190
|
|
|
177
191
|
const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE =
|
|
@@ -393,7 +407,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
393
407
|
|
|
394
408
|
validateParams(payload, [
|
|
395
409
|
{ name: 'chunkSize', type: 'number' },
|
|
396
|
-
{ name: 'resourceBinaries', type: 'array', allowEmpty: true },
|
|
397
410
|
{ name: 'forcedUpdateRes', type: 'boolean' },
|
|
398
411
|
{ name: 'bootloaderBinary', type: 'buffer' },
|
|
399
412
|
{ name: 'romloaderBinary', type: 'buffer' },
|
|
@@ -406,6 +419,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
406
419
|
{ name: 'se04Binary', type: 'buffer' },
|
|
407
420
|
{ name: 'firmwareType', type: 'string' },
|
|
408
421
|
{ name: 'platform', type: 'string' },
|
|
422
|
+
{ name: 'resourceBundleFiles', type: 'array', allowEmpty: true },
|
|
409
423
|
]);
|
|
410
424
|
|
|
411
425
|
this.params = {
|
|
@@ -420,7 +434,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
420
434
|
se02Binary: payload.se02Binary,
|
|
421
435
|
se03Binary: payload.se03Binary,
|
|
422
436
|
se04Binary: payload.se04Binary,
|
|
423
|
-
|
|
437
|
+
resourceBundleFiles: payload.resourceBundleFiles,
|
|
424
438
|
firmwareType: payload.firmwareType,
|
|
425
439
|
platform: payload.platform,
|
|
426
440
|
};
|
|
@@ -459,13 +473,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
459
473
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
460
474
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
461
475
|
|
|
462
|
-
let resourceBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
463
476
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
464
477
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
465
478
|
let installItems: ProtocolV2InstallItem[] | undefined;
|
|
466
479
|
try {
|
|
467
480
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
468
|
-
resourceBinaryMap = await this.prepareResourceBinaries(firmwareType, deviceFeatures);
|
|
469
481
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
470
482
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
471
483
|
if (!this.hasExplicitProtocolV2Payload(fwBinaryMap)) {
|
|
@@ -473,7 +485,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
473
485
|
firmwareType,
|
|
474
486
|
deviceFeatures
|
|
475
487
|
);
|
|
476
|
-
resourceBinaryMap = remoteBinaries.resourceBinaryMap;
|
|
477
488
|
bootloaderBinary = remoteBinaries.bootloaderBinary;
|
|
478
489
|
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
479
490
|
installItems = remoteBinaries.installItems;
|
|
@@ -483,7 +494,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
483
494
|
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
484
495
|
}
|
|
485
496
|
|
|
486
|
-
|
|
497
|
+
const resourceBundles = this.prepareProtocolV2ResourceBundles(firmwareType, deviceFeatures);
|
|
498
|
+
|
|
499
|
+
if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
|
|
487
500
|
throw ERRORS.TypedError(
|
|
488
501
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
489
502
|
'No firmware to update'
|
|
@@ -493,10 +506,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
493
506
|
await this.enterProtocolV2BootloaderMode();
|
|
494
507
|
|
|
495
508
|
await this.executeProtocolV2Update({
|
|
496
|
-
resourceBinaryMap,
|
|
497
509
|
fwBinaryMap,
|
|
498
510
|
bootloaderBinary,
|
|
499
511
|
...(installItems ? { installItems } : undefined),
|
|
512
|
+
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
500
513
|
});
|
|
501
514
|
|
|
502
515
|
await this.exitProtocolV2BootloaderToNormal();
|
|
@@ -519,68 +532,26 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
519
532
|
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Device features not available');
|
|
520
533
|
}
|
|
521
534
|
|
|
522
|
-
private async prepareResourceBinaries(firmwareType: EFirmwareType, features: Features) {
|
|
523
|
-
if (this.params.resourceBinaries?.length) {
|
|
524
|
-
this.params.resourceBinaries.forEach((binary, index) => {
|
|
525
|
-
if (!(binary instanceof ArrayBuffer)) {
|
|
526
|
-
throw ERRORS.TypedError(
|
|
527
|
-
HardwareErrorCode.CallMethodInvalidParameter,
|
|
528
|
-
`Parameter [resourceBinaries.${index}] is of type invalid and should be [buffer].`
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
return this.params.resourceBinaries.map((binary, index) => ({
|
|
534
|
-
fileName: `resource-${index + 1}.bin`,
|
|
535
|
-
binary,
|
|
536
|
-
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
|
|
537
|
-
}));
|
|
538
|
-
}
|
|
539
|
-
const resourceUrl = DataManager.getSysResourcesLatestRelease({
|
|
540
|
-
features,
|
|
541
|
-
forcedUpdateRes: this.params.forcedUpdateRes,
|
|
542
|
-
firmwareType,
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
if (resourceUrl) {
|
|
546
|
-
const resource = (await getSysResourceBinary(resourceUrl)).binary;
|
|
547
|
-
return [
|
|
548
|
-
{
|
|
549
|
-
fileName: 'resource.bin',
|
|
550
|
-
binary: resource,
|
|
551
|
-
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
|
|
552
|
-
},
|
|
553
|
-
];
|
|
554
|
-
}
|
|
555
|
-
Log.warn('No resource url found');
|
|
556
|
-
return [];
|
|
557
|
-
}
|
|
558
|
-
|
|
559
535
|
private prepareBootloaderBinary(): ArrayBuffer | null {
|
|
560
536
|
return this.params.bootloaderBinary ?? null;
|
|
561
537
|
}
|
|
562
538
|
|
|
563
539
|
private hasExplicitProtocolV2Payload(fwBinaryMap: ProtocolV2TargetBinary[]) {
|
|
564
540
|
return (
|
|
565
|
-
!!this.params.
|
|
541
|
+
!!this.params.resourceBundleFiles?.length ||
|
|
566
542
|
!!this.params.bootloaderBinary ||
|
|
567
543
|
fwBinaryMap.length > 0
|
|
568
544
|
);
|
|
569
545
|
}
|
|
570
546
|
|
|
571
547
|
private buildProtocolV2InstallItems({
|
|
572
|
-
resourceBinaryMap,
|
|
573
548
|
bootloaderBinary,
|
|
574
549
|
fwBinaryMap,
|
|
575
550
|
}: {
|
|
576
|
-
resourceBinaryMap: ProtocolV2TargetBinary[];
|
|
577
551
|
bootloaderBinary: ArrayBuffer | null;
|
|
578
552
|
fwBinaryMap: ProtocolV2TargetBinary[];
|
|
579
553
|
}): ProtocolV2InstallItem[] {
|
|
580
|
-
const installItems: ProtocolV2InstallItem[] =
|
|
581
|
-
...resource,
|
|
582
|
-
kind: 'resource',
|
|
583
|
-
}));
|
|
554
|
+
const installItems: ProtocolV2InstallItem[] = [];
|
|
584
555
|
|
|
585
556
|
if (bootloaderBinary) {
|
|
586
557
|
installItems.push({
|
|
@@ -738,86 +709,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
738
709
|
return parseProtocolV2OkppHeader(headerBytes);
|
|
739
710
|
}
|
|
740
711
|
|
|
741
|
-
private
|
|
742
|
-
pkg: IProtocolV2ResourceManifestPackage,
|
|
743
|
-
manifestVersion?: IVersionArray
|
|
744
|
-
) {
|
|
745
|
-
try {
|
|
746
|
-
const header = await this.readProtocolV2DeviceFileHeader(pkg.path);
|
|
747
|
-
if (!header) return false;
|
|
748
|
-
|
|
749
|
-
const expectedType = pkg.type ?? 'RESC';
|
|
750
|
-
const expectedVersion = pkg.version ?? manifestVersion;
|
|
751
|
-
if (header.type !== expectedType) return false;
|
|
752
|
-
if (expectedVersion && compareProtocolV2Versions(header.version, expectedVersion) !== 0) {
|
|
753
|
-
return false;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
const expectedPayloadHash = normalizeProtocolV2Hex(pkg.payloadHash);
|
|
757
|
-
if (expectedPayloadHash && header.payloadHash !== expectedPayloadHash) return false;
|
|
758
|
-
|
|
759
|
-
const expectedHeaderHash = normalizeProtocolV2Hex(pkg.headerHash);
|
|
760
|
-
if (expectedHeaderHash && header.headerHash !== expectedHeaderHash) return false;
|
|
761
|
-
|
|
762
|
-
return true;
|
|
763
|
-
} catch (error) {
|
|
764
|
-
Log.log(`Protocol V2 resource package check failed for ${pkg.path}: `, error);
|
|
765
|
-
return false;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
private async isProtocolV2ResourceManifestSatisfied(
|
|
770
|
-
manifest: IFirmwareReleaseInfo['resourceManifest']
|
|
771
|
-
) {
|
|
772
|
-
if (!manifest?.packages?.length) return false;
|
|
773
|
-
|
|
774
|
-
for (const pkg of manifest.packages) {
|
|
775
|
-
if (!(await this.isProtocolV2ResourcePackageMatched(pkg, manifest.version))) {
|
|
776
|
-
return false;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
return true;
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
private getProtocolV2ResourceManifest(
|
|
783
|
-
release: IFirmwareReleaseInfo,
|
|
784
|
-
component: IProtocolV2FirmwareComponent
|
|
785
|
-
) {
|
|
786
|
-
return component.resourceManifest ?? release.resourceManifest;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
private getProtocolV2ResourceComponentFileName(key: string) {
|
|
790
|
-
const safeKey = key.replace(/[^a-z0-9_-]/gi, '_') || 'resource';
|
|
791
|
-
if (safeKey === 'resource') {
|
|
792
|
-
return PROTOCOL_V2_REMOTE_COMPONENT_TARGETS.CRATE.fileName;
|
|
793
|
-
}
|
|
794
|
-
return `resource-${safeKey}.bin`;
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
private async shouldInstallRemoteProtocolV2Component(
|
|
712
|
+
private shouldInstallRemoteProtocolV2Component(
|
|
798
713
|
release: IFirmwareReleaseInfo,
|
|
799
714
|
key: string,
|
|
800
715
|
component: IProtocolV2FirmwareComponent,
|
|
801
716
|
target: ProtocolV2RemoteComponentTarget,
|
|
802
717
|
features: Features
|
|
803
718
|
) {
|
|
804
|
-
if (target.kind === 'resource') {
|
|
805
|
-
if (
|
|
806
|
-
this.params.forcedUpdateRes ||
|
|
807
|
-
features.bootloaderMode ||
|
|
808
|
-
features.mode === 'bootloader'
|
|
809
|
-
) {
|
|
810
|
-
return true;
|
|
811
|
-
}
|
|
812
|
-
const resourceMatched = await this.isProtocolV2ResourceManifestSatisfied(
|
|
813
|
-
this.getProtocolV2ResourceManifest(release, component)
|
|
814
|
-
);
|
|
815
|
-
if (resourceMatched) {
|
|
816
|
-
Log.log(`[FirmwareUpdateV4] skip Protocol V2 resource component ${key}; manifest matched`);
|
|
817
|
-
}
|
|
818
|
-
return !resourceMatched;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
719
|
const versionSatisfied = this.isProtocolV2ComponentVersionSatisfied(
|
|
822
720
|
release,
|
|
823
721
|
component,
|
|
@@ -852,14 +750,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
852
750
|
private async prepareRemoteProtocolV2Binaries(firmwareType: EFirmwareType, features: Features) {
|
|
853
751
|
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
854
752
|
|
|
855
|
-
const resourceBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
856
753
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
857
754
|
const fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
858
755
|
const installItems: ProtocolV2InstallItem[] = [];
|
|
859
756
|
|
|
860
757
|
if (!release) {
|
|
861
758
|
return {
|
|
862
|
-
resourceBinaryMap,
|
|
863
759
|
bootloaderBinary,
|
|
864
760
|
fwBinaryMap,
|
|
865
761
|
installItems,
|
|
@@ -870,7 +766,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
870
766
|
|
|
871
767
|
for (const [key, component] of entries) {
|
|
872
768
|
const target = this.getRemoteComponentTarget(key, component);
|
|
873
|
-
const shouldInstall =
|
|
769
|
+
const shouldInstall = this.shouldInstallRemoteProtocolV2Component(
|
|
874
770
|
release,
|
|
875
771
|
key,
|
|
876
772
|
component,
|
|
@@ -879,15 +775,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
879
775
|
);
|
|
880
776
|
if (shouldInstall) {
|
|
881
777
|
const remoteBinary = await this.downloadRemoteProtocolV2Component(key, component);
|
|
882
|
-
if (remoteBinary.kind === '
|
|
883
|
-
const binaryEntry = {
|
|
884
|
-
fileName: this.getProtocolV2ResourceComponentFileName(key),
|
|
885
|
-
binary: remoteBinary.binary,
|
|
886
|
-
targetId: remoteBinary.targetId,
|
|
887
|
-
};
|
|
888
|
-
resourceBinaryMap.push(binaryEntry);
|
|
889
|
-
installItems.push({ ...binaryEntry, kind: remoteBinary.kind });
|
|
890
|
-
} else if (remoteBinary.kind === 'bootloader') {
|
|
778
|
+
if (remoteBinary.kind === 'bootloader') {
|
|
891
779
|
bootloaderBinary = remoteBinary.binary;
|
|
892
780
|
installItems.push({
|
|
893
781
|
fileName: remoteBinary.fileName,
|
|
@@ -908,13 +796,153 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
908
796
|
}
|
|
909
797
|
|
|
910
798
|
return {
|
|
911
|
-
resourceBinaryMap,
|
|
912
799
|
bootloaderBinary,
|
|
913
800
|
fwBinaryMap,
|
|
914
801
|
installItems,
|
|
915
802
|
};
|
|
916
803
|
}
|
|
917
804
|
|
|
805
|
+
// ============================================================
|
|
806
|
+
// RESC bundle 直写增量同步(FileRead 比对 + FilesystemFileWrite 直写)
|
|
807
|
+
// ============================================================
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* 准备 RESC bundle 列表。
|
|
811
|
+
*
|
|
812
|
+
* 两种模式(同 FirmwareUpdateV3 的 binary vs version 模式):
|
|
813
|
+
* - 用户传了 resourceBundleFiles(binary 数组):直接用,不做版本比对。
|
|
814
|
+
* - 用户没传:从远端 config.json 的 release.resourceBundles 拉取,
|
|
815
|
+
* 此时 syncProtocolV2ResourceBundles 会按需下载 + 比对设备已有 header 跳过。
|
|
816
|
+
*/
|
|
817
|
+
private prepareProtocolV2ResourceBundles(
|
|
818
|
+
firmwareType: EFirmwareType,
|
|
819
|
+
features: Features
|
|
820
|
+
): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
821
|
+
// 模式1:用户手动传入 binary,直接安装,不比对
|
|
822
|
+
if (this.params.resourceBundleFiles?.length) {
|
|
823
|
+
return this.params.resourceBundleFiles.map(file => ({
|
|
824
|
+
name: file.devicePath.split('/').pop() ?? file.devicePath,
|
|
825
|
+
binary: file.binary,
|
|
826
|
+
devicePath: file.devicePath,
|
|
827
|
+
}));
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// 模式2:远端配置模式,后续按需下载 + 比对
|
|
831
|
+
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
832
|
+
if (!release?.resourceBundles?.length) return undefined;
|
|
833
|
+
|
|
834
|
+
return release.resourceBundles.map(bundle => ({
|
|
835
|
+
name: bundle.name,
|
|
836
|
+
binary: new ArrayBuffer(0),
|
|
837
|
+
devicePath: bundle.devicePath,
|
|
838
|
+
url: bundle.url,
|
|
839
|
+
version: bundle.version,
|
|
840
|
+
payloadHash: bundle.payloadHash,
|
|
841
|
+
headerHash: bundle.headerHash,
|
|
842
|
+
})) as ProtocolV2ResourceBundleBinary[];
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* 同步 RESC bundles 到设备。
|
|
847
|
+
*
|
|
848
|
+
* 手动传入模式(有 binary):直接 FileWrite 直写,不比对。
|
|
849
|
+
* 远端配置模式(无 binary,有 url):按需下载 + FileRead 比对跳过已更新的。
|
|
850
|
+
*/
|
|
851
|
+
private async syncProtocolV2ResourceBundles(
|
|
852
|
+
bundles: ProtocolV2ResourceBundleBinary[],
|
|
853
|
+
firmwareSize: number
|
|
854
|
+
): Promise<{ processedSize: number; totalSize: number }> {
|
|
855
|
+
const transferStartTime = Date.now();
|
|
856
|
+
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
857
|
+
|
|
858
|
+
const isManualMode = bundles.every(b => b.binary.byteLength > 0);
|
|
859
|
+
let bundlesToSync = bundles;
|
|
860
|
+
|
|
861
|
+
// 远端配置模式:按需下载 + 比对跳过
|
|
862
|
+
if (!isManualMode) {
|
|
863
|
+
const filtered: ProtocolV2ResourceBundleBinary[] = [];
|
|
864
|
+
for (const bundle of bundles) {
|
|
865
|
+
// 下载 binary
|
|
866
|
+
if (bundle.binary.byteLength === 0 && bundle.url) {
|
|
867
|
+
Log.log(`[FirmwareUpdateV4] downloading RESC bundle ${bundle.name} from ${bundle.url}`);
|
|
868
|
+
const { binary } = await getSysResourceBinary(bundle.url);
|
|
869
|
+
bundle.binary = binary;
|
|
870
|
+
}
|
|
871
|
+
// 比对设备上已有 header
|
|
872
|
+
const upToDate = await this.isProtocolV2ResourceBundleUpToDate(bundle);
|
|
873
|
+
if (upToDate) {
|
|
874
|
+
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${bundle.name}; already up to date`);
|
|
875
|
+
} else {
|
|
876
|
+
filtered.push(bundle);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
bundlesToSync = filtered;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (bundlesToSync.length === 0) {
|
|
883
|
+
Log.log('[FirmwareUpdateV4] all RESC bundles up to date, nothing to sync');
|
|
884
|
+
return { processedSize: 0, totalSize: firmwareSize };
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// FileWrite 直写到设备
|
|
888
|
+
let totalSize = 0;
|
|
889
|
+
for (const b of bundlesToSync) totalSize += b.binary.byteLength;
|
|
890
|
+
|
|
891
|
+
const transferTotalSize = totalSize + firmwareSize;
|
|
892
|
+
let processedSize = 0;
|
|
893
|
+
for (const bundle of bundlesToSync) {
|
|
894
|
+
Log.log(
|
|
895
|
+
`[FirmwareUpdateV4] syncing RESC bundle ${bundle.name} -> ${bundle.devicePath} bytes=${bundle.binary.byteLength}`
|
|
896
|
+
);
|
|
897
|
+
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
898
|
+
payload: bundle.binary,
|
|
899
|
+
filePath: bundle.devicePath,
|
|
900
|
+
processedSize,
|
|
901
|
+
totalSize: transferTotalSize,
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
906
|
+
Log.log(
|
|
907
|
+
`[FirmwareUpdateV4] RESC bundle sync finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
|
|
908
|
+
elapsedMs / 1000
|
|
909
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
|
|
910
|
+
);
|
|
911
|
+
return { processedSize, totalSize: transferTotalSize };
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* 比对设备上已有 okpkg 的 OKPP header(仅远端配置模式使用)。
|
|
916
|
+
*/
|
|
917
|
+
private async isProtocolV2ResourceBundleUpToDate(
|
|
918
|
+
bundle: ProtocolV2ResourceBundleBinary
|
|
919
|
+
): Promise<boolean> {
|
|
920
|
+
if (this.params.forcedUpdateRes) return false;
|
|
921
|
+
if (!bundle.version && !bundle.payloadHash) return false;
|
|
922
|
+
|
|
923
|
+
try {
|
|
924
|
+
const header = await this.readProtocolV2DeviceFileHeader(bundle.devicePath);
|
|
925
|
+
if (!header) return false;
|
|
926
|
+
|
|
927
|
+
if (bundle.version) {
|
|
928
|
+
const cmp = compareProtocolV2Versions(header.version, bundle.version);
|
|
929
|
+
if (cmp === undefined || cmp !== 0) return false;
|
|
930
|
+
}
|
|
931
|
+
if (bundle.payloadHash) {
|
|
932
|
+
const expected = normalizeProtocolV2Hex(bundle.payloadHash);
|
|
933
|
+
if (expected && header.payloadHash !== expected) return false;
|
|
934
|
+
}
|
|
935
|
+
if (bundle.headerHash) {
|
|
936
|
+
const expected = normalizeProtocolV2Hex(bundle.headerHash);
|
|
937
|
+
if (expected && header.headerHash !== expected) return false;
|
|
938
|
+
}
|
|
939
|
+
return true;
|
|
940
|
+
} catch (error) {
|
|
941
|
+
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
918
946
|
private isProtocolV2BootloaderMode() {
|
|
919
947
|
if (typeof this.device.isBootloader === 'function') {
|
|
920
948
|
return this.device.isBootloader();
|
|
@@ -1017,30 +1045,48 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1017
1045
|
}
|
|
1018
1046
|
|
|
1019
1047
|
private async executeProtocolV2Update({
|
|
1020
|
-
resourceBinaryMap,
|
|
1021
1048
|
fwBinaryMap,
|
|
1022
1049
|
bootloaderBinary,
|
|
1023
1050
|
installItems,
|
|
1051
|
+
resourceBundles,
|
|
1024
1052
|
}: {
|
|
1025
|
-
resourceBinaryMap?: ProtocolV2TargetBinary[];
|
|
1026
1053
|
fwBinaryMap?: ProtocolV2TargetBinary[];
|
|
1027
1054
|
bootloaderBinary?: ArrayBuffer | null;
|
|
1028
1055
|
installItems?: ProtocolV2InstallItem[];
|
|
1056
|
+
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
1029
1057
|
}) {
|
|
1030
1058
|
const orderedInstallItems =
|
|
1031
1059
|
installItems ??
|
|
1032
1060
|
this.buildProtocolV2InstallItems({
|
|
1033
|
-
resourceBinaryMap: resourceBinaryMap ?? [],
|
|
1034
1061
|
bootloaderBinary: bootloaderBinary ?? null,
|
|
1035
1062
|
fwBinaryMap: fwBinaryMap ?? [],
|
|
1036
1063
|
});
|
|
1037
|
-
let totalSize = 0;
|
|
1038
|
-
let processedSize = 0;
|
|
1039
|
-
let transferredSize = 0;
|
|
1040
1064
|
|
|
1041
|
-
|
|
1065
|
+
let firmwareSize = 0;
|
|
1066
|
+
for (const item of orderedInstallItems) firmwareSize += item.binary.byteLength;
|
|
1042
1067
|
|
|
1043
1068
|
this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
|
|
1069
|
+
|
|
1070
|
+
let processedSize = 0;
|
|
1071
|
+
let totalSize = firmwareSize;
|
|
1072
|
+
if (resourceBundles?.length) {
|
|
1073
|
+
const resourceTransfer = await this.syncProtocolV2ResourceBundles(
|
|
1074
|
+
resourceBundles,
|
|
1075
|
+
firmwareSize
|
|
1076
|
+
);
|
|
1077
|
+
processedSize = resourceTransfer.processedSize;
|
|
1078
|
+
totalSize = resourceTransfer.totalSize;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// 只有 RESC bundle、没有固件 target 时跳过 staging/install 阶段
|
|
1082
|
+
if (orderedInstallItems.length === 0) {
|
|
1083
|
+
Log.log('[FirmwareUpdateV4] no firmware targets to install (RESC bundles only)');
|
|
1084
|
+
if (totalSize > 0) {
|
|
1085
|
+
this.postProgressMessage(100, 'transferData');
|
|
1086
|
+
}
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
let transferredSize = processedSize;
|
|
1044
1090
|
const transferStartTime = Date.now();
|
|
1045
1091
|
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
1046
1092
|
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
@@ -1067,6 +1113,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1067
1113
|
onTransferredBytes,
|
|
1068
1114
|
});
|
|
1069
1115
|
transferredSize = processedSize;
|
|
1116
|
+
await this.verifyProtocolV2StagedFile(filePath, item.binary.byteLength);
|
|
1070
1117
|
|
|
1071
1118
|
stagedInstallTargets.push({
|
|
1072
1119
|
...item,
|
|
@@ -1109,6 +1156,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1109
1156
|
return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
|
|
1110
1157
|
}
|
|
1111
1158
|
|
|
1159
|
+
private async verifyProtocolV2StagedFile(path: string, expectedSize: number) {
|
|
1160
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1161
|
+
const response = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path });
|
|
1162
|
+
const actualSize = toProtocolV2FiniteNumber(response.message?.size);
|
|
1163
|
+
if (!response.message?.exist || response.message?.directory || actualSize !== expectedSize) {
|
|
1164
|
+
throw ERRORS.TypedError(
|
|
1165
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1166
|
+
`staged file verification failed: path=${path} exist=${!!response.message
|
|
1167
|
+
?.exist} expected=${expectedSize} actual=${actualSize ?? 'unknown'}`
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1112
1172
|
private async queryProtocolV2FirmwareUpdateStatus() {
|
|
1113
1173
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1114
1174
|
return typedCall(
|
|
@@ -1367,18 +1427,37 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1367
1427
|
await this.device.initialize();
|
|
1368
1428
|
}
|
|
1369
1429
|
|
|
1370
|
-
private async protocolV2CommonUpdateProcess({
|
|
1430
|
+
private async protocolV2CommonUpdateProcess(params: ProtocolV2FileTransferParams) {
|
|
1431
|
+
let lastError: unknown;
|
|
1432
|
+
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1433
|
+
try {
|
|
1434
|
+
return await this.protocolV2WriteWholeFile(params);
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
lastError = error;
|
|
1437
|
+
Log.error(
|
|
1438
|
+
`Protocol V2 file transfer failed path=${params.filePath} attempt=${attempt}/${PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT}; restarting from offset 0`,
|
|
1439
|
+
error
|
|
1440
|
+
);
|
|
1441
|
+
if (attempt === PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1442
|
+
break;
|
|
1443
|
+
}
|
|
1444
|
+
await this.recoverProtocolV2FileTransfer();
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
throw ERRORS.TypedError(
|
|
1449
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1450
|
+
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
private async protocolV2WriteWholeFile({
|
|
1371
1455
|
payload,
|
|
1372
1456
|
filePath,
|
|
1373
1457
|
processedSize,
|
|
1374
1458
|
totalSize,
|
|
1375
1459
|
onTransferredBytes,
|
|
1376
|
-
}:
|
|
1377
|
-
filePath: string;
|
|
1378
|
-
processedSize?: number;
|
|
1379
|
-
totalSize?: number;
|
|
1380
|
-
onTransferredBytes?: (transferredBytes: number) => void;
|
|
1381
|
-
}) {
|
|
1460
|
+
}: ProtocolV2FileTransferParams) {
|
|
1382
1461
|
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1383
1462
|
let offset = 0;
|
|
1384
1463
|
const getUploadProgress = (fileOffset: number) => {
|
|
@@ -1399,7 +1478,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1399
1478
|
totalSize ?? payload.byteLength
|
|
1400
1479
|
);
|
|
1401
1480
|
|
|
1402
|
-
const writeRes = await this.
|
|
1481
|
+
const writeRes = await this.fileWriteChunk(
|
|
1403
1482
|
filePath,
|
|
1404
1483
|
payload.byteLength,
|
|
1405
1484
|
offset,
|
|
@@ -1440,7 +1519,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1440
1519
|
return env ?? 'unknown';
|
|
1441
1520
|
}
|
|
1442
1521
|
|
|
1443
|
-
private async
|
|
1522
|
+
private async fileWriteChunk(
|
|
1444
1523
|
filePath: string,
|
|
1445
1524
|
totalFileSize: number,
|
|
1446
1525
|
offset: number,
|
|
@@ -1448,57 +1527,37 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1448
1527
|
overwrite: boolean,
|
|
1449
1528
|
progress: number | null
|
|
1450
1529
|
): Promise<TypedResponseMessage<'FilesystemFile'>> {
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
if (writeRes.type
|
|
1465
|
-
if ((writeRes as any).
|
|
1466
|
-
|
|
1467
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
|
|
1468
|
-
}
|
|
1530
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1531
|
+
const writeRes = await typedCall('FilesystemFileWrite', 'FilesystemFile', {
|
|
1532
|
+
file: {
|
|
1533
|
+
path: filePath,
|
|
1534
|
+
offset,
|
|
1535
|
+
total_size: totalFileSize,
|
|
1536
|
+
data: chunk,
|
|
1537
|
+
},
|
|
1538
|
+
overwrite,
|
|
1539
|
+
append: false,
|
|
1540
|
+
ui_percentage: progress ?? undefined,
|
|
1541
|
+
});
|
|
1542
|
+
if (writeRes.type !== 'FilesystemFile') {
|
|
1543
|
+
if ((writeRes as any).type === 'CallMethodError') {
|
|
1544
|
+
if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
|
|
1545
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
|
|
1469
1546
|
}
|
|
1470
|
-
throw ERRORS.TypedError(
|
|
1471
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1472
|
-
'transfer data error'
|
|
1473
|
-
);
|
|
1474
1547
|
}
|
|
1475
|
-
|
|
1476
|
-
}
|
|
1548
|
+
throw ERRORS.TypedError(HardwareErrorCode.EmmcFileWriteFirmwareError, 'transfer data error');
|
|
1549
|
+
}
|
|
1550
|
+
return writeRes;
|
|
1551
|
+
}
|
|
1477
1552
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
Log.error(`fileWrite error: `, error);
|
|
1485
|
-
retryCount--;
|
|
1486
|
-
if (retryCount === 0) {
|
|
1487
|
-
throw ERRORS.TypedError(
|
|
1488
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1489
|
-
'transfer data error'
|
|
1490
|
-
);
|
|
1491
|
-
}
|
|
1492
|
-
const env = DataManager.getSettings('env');
|
|
1493
|
-
if (DataManager.isBleConnect(env)) {
|
|
1494
|
-
await wait(3000);
|
|
1495
|
-
await this.acquireProtocolV2BleDevice();
|
|
1496
|
-
await this.device.initialize();
|
|
1497
|
-
}
|
|
1498
|
-
await wait(2000);
|
|
1499
|
-
}
|
|
1553
|
+
private async recoverProtocolV2FileTransfer() {
|
|
1554
|
+
const env = DataManager.getSettings('env');
|
|
1555
|
+
if (DataManager.isBleConnect(env)) {
|
|
1556
|
+
await wait(3000);
|
|
1557
|
+
await this.acquireProtocolV2BleDevice();
|
|
1558
|
+
await this.device.initialize();
|
|
1500
1559
|
}
|
|
1501
|
-
|
|
1560
|
+
await wait(2000);
|
|
1502
1561
|
}
|
|
1503
1562
|
|
|
1504
1563
|
private async acquireProtocolV2BleDevice() {
|