feat(device): flash device firmware from Agent (#499)
* add dataModelVersion, usbProtocolVersion, slaveProtocolVersion * read the package.json at appstart * flash firmware * update firmware * fix extra resource path * fix import modules * update lock files * fix imports * terminal window * exclude tmp folder from git repo * ok button * auto scroll in xterm * fix maxTry count calculation * optimize logging * optimize timeout * readSync * Add extra delay * fix async call * fix error message in log * fix ok button disable state * retry * list devices * close device after reenumeration * retry snooze * kboot maxtry 10 * retry 100 * remove deprecated toPayload ngrx helper * flash firmware with custom file * fix tslint
This commit is contained in:
committed by
László Monda
parent
f608791a09
commit
297fd3be79
@@ -9,13 +9,16 @@ export namespace Constants {
|
||||
*/
|
||||
export enum UsbCommand {
|
||||
GetProperty = 0,
|
||||
Reenumerate = 1,
|
||||
UploadUserConfig = 8,
|
||||
ApplyConfig = 9,
|
||||
LaunchEepromTransfer = 12,
|
||||
ReadHardwareConfig = 13,
|
||||
WriteHardwareConfig = 14,
|
||||
ReadUserConfig = 15,
|
||||
GetKeyboardState = 16
|
||||
GetKeyboardState = 16,
|
||||
JumpToModuleBootloader = 18,
|
||||
SendKbootCommandToModule = 19
|
||||
}
|
||||
|
||||
export enum EepromTransfer {
|
||||
@@ -33,3 +36,42 @@ export enum SystemPropertyIds {
|
||||
HardwareConfigSize = 4,
|
||||
MaxUserConfigSize = 5
|
||||
}
|
||||
|
||||
export enum EnumerationModes {
|
||||
Bootloader = 0,
|
||||
Buspal = 1,
|
||||
NormalKeyboard = 2,
|
||||
CompatibleKeyboard = 3
|
||||
}
|
||||
|
||||
export const enumerationModeIdToProductId = {
|
||||
'0': 0x6120,
|
||||
'1': 0x6121,
|
||||
'2': 0x6122,
|
||||
'3': 0x6123
|
||||
};
|
||||
|
||||
export enum EnumerationNameToProductId {
|
||||
bootloader = 0x6120,
|
||||
buspal = 0x6121,
|
||||
normalKeyboard = 0x6122,
|
||||
compatibleKeyboard = 0x6123
|
||||
}
|
||||
|
||||
export enum ModuleSlotToI2cAddress {
|
||||
leftHalf = '0x10',
|
||||
leftAddon = '0x20',
|
||||
rightAddon = '0x30'
|
||||
}
|
||||
|
||||
export enum ModuleSlotToId {
|
||||
leftHalf = 1,
|
||||
leftAddon = 2,
|
||||
rightAddon = 3
|
||||
}
|
||||
|
||||
export enum KbootCommands {
|
||||
idle = 0,
|
||||
ping = 1,
|
||||
reset = 2
|
||||
}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
export * from './constants';
|
||||
export * from './uhk-blhost';
|
||||
export * from './uhk-hid-device';
|
||||
export * from './uhk-operations';
|
||||
export * from './util';
|
||||
|
||||
89
packages/uhk-usb/src/uhk-blhost.ts
Normal file
89
packages/uhk-usb/src/uhk-blhost.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import * as path from 'path';
|
||||
import { spawn } from 'child_process';
|
||||
import { LogService } from 'uhk-common';
|
||||
import { retry } from './util';
|
||||
|
||||
export class UhkBlhost {
|
||||
private blhostPath: string;
|
||||
|
||||
constructor(private logService: LogService,
|
||||
private rootDir: string) {
|
||||
}
|
||||
|
||||
public async runBlhostCommand(params: Array<string>): Promise<void> {
|
||||
const self = this;
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const blhostPath = this.getBlhostPath();
|
||||
self.logService.debug(`[blhost] RUN: ${blhostPath} ${params.join(' ')}`);
|
||||
const childProcess = spawn(`"${blhostPath}"`, params, {shell: true});
|
||||
let finished = false;
|
||||
|
||||
childProcess.stdout.on('data', data => {
|
||||
self.logService.debug(`[blhost] STDOUT: ${data}`);
|
||||
});
|
||||
|
||||
childProcess.stderr.on('data', data => {
|
||||
self.logService.error(`[blhost] STDERR: ${data}`);
|
||||
});
|
||||
|
||||
childProcess.on('close', code => {
|
||||
self.logService.debug(`[blhost] CLOSE_CODE: ${code}`);
|
||||
finish(code);
|
||||
});
|
||||
|
||||
childProcess.on('exit', code => {
|
||||
self.logService.debug(`[blhost] EXIT_CODE: ${code}`);
|
||||
finish(code);
|
||||
});
|
||||
|
||||
childProcess.on('error', err => {
|
||||
self.logService.debug(`[blhost] ERROR: ${err}`);
|
||||
});
|
||||
|
||||
function finish(code) {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
|
||||
finished = true;
|
||||
|
||||
self.logService.debug(`[blhost] FINISHED: ${code}`);
|
||||
|
||||
if (code !== null && code !== 0) {
|
||||
return reject(new Error(`blhost error code:${code}`));
|
||||
}
|
||||
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async runBlhostCommandRetry(params: Array<string>, maxTry = 100): Promise<void> {
|
||||
return await retry(async () => await this.runBlhostCommand(params), maxTry, this.logService);
|
||||
}
|
||||
|
||||
private getBlhostPath(): string {
|
||||
if (this.blhostPath) {
|
||||
return this.blhostPath;
|
||||
}
|
||||
|
||||
let blhostPath;
|
||||
switch (process.platform) {
|
||||
case 'linux':
|
||||
blhostPath = 'linux/amd64/blhost';
|
||||
break;
|
||||
case 'darwin':
|
||||
blhostPath = 'mac/blhost';
|
||||
break;
|
||||
case 'win32':
|
||||
blhostPath = 'win/blhost.exe';
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Could not find blhost path. Unknown platform:${process.platform}`);
|
||||
}
|
||||
|
||||
this.blhostPath = path.join(this.rootDir, `packages/blhost/${blhostPath}`);
|
||||
|
||||
return this.blhostPath;
|
||||
}
|
||||
}
|
||||
@@ -1,91 +1,24 @@
|
||||
import { Device, devices, HID } from 'node-hid';
|
||||
import { LogService } from 'uhk-common';
|
||||
|
||||
import { Constants, EepromTransfer, UsbCommand } from './constants';
|
||||
import {
|
||||
Constants,
|
||||
EepromTransfer,
|
||||
enumerationModeIdToProductId,
|
||||
EnumerationModes,
|
||||
KbootCommands,
|
||||
ModuleSlotToI2cAddress,
|
||||
ModuleSlotToId,
|
||||
UsbCommand
|
||||
} from './constants';
|
||||
import { bufferToString, getTransferData, retry, snooze } from './util';
|
||||
|
||||
const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
export const BOOTLOADER_TIMEOUT_MS = 5000;
|
||||
|
||||
/**
|
||||
* HID API wrapper to support unified logging and async write
|
||||
*/
|
||||
export class UhkHidDevice {
|
||||
/**
|
||||
* Convert the Buffer to number[]
|
||||
* @param {Buffer} buffer
|
||||
* @returns {number[]}
|
||||
* @private
|
||||
* @static
|
||||
*/
|
||||
public static convertBufferToIntArray(buffer: Buffer): number[] {
|
||||
return Array.prototype.slice.call(buffer, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the communication package into 64 byte fragments
|
||||
* @param {UsbCommand} usbCommand
|
||||
* @param {Buffer} configBuffer
|
||||
* @returns {Buffer[]}
|
||||
* @private
|
||||
*/
|
||||
public static getTransferBuffers(usbCommand: UsbCommand, configBuffer: Buffer): Buffer[] {
|
||||
const fragments: Buffer[] = [];
|
||||
const MAX_SENDING_PAYLOAD_SIZE = Constants.MAX_PAYLOAD_SIZE - 4;
|
||||
for (let offset = 0; offset < configBuffer.length; offset += MAX_SENDING_PAYLOAD_SIZE) {
|
||||
const length = offset + MAX_SENDING_PAYLOAD_SIZE < configBuffer.length
|
||||
? MAX_SENDING_PAYLOAD_SIZE
|
||||
: configBuffer.length - offset;
|
||||
const header = new Buffer([usbCommand, length, offset & 0xFF, offset >> 8]);
|
||||
fragments.push(Buffer.concat([header, configBuffer.slice(offset, offset + length)]));
|
||||
}
|
||||
|
||||
return fragments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the communication package that will send over USB and
|
||||
* - add usb report code as 1st byte
|
||||
* - https://github.com/node-hid/node-hid/issues/187 issue
|
||||
* @param {Buffer} buffer
|
||||
* @returns {number[]}
|
||||
* @private
|
||||
* @static
|
||||
*/
|
||||
private static getTransferData(buffer: Buffer): number[] {
|
||||
const data = UhkHidDevice.convertBufferToIntArray(buffer);
|
||||
// if data start with 0 need to add additional leading zero because HID API remove it.
|
||||
// https://github.com/node-hid/node-hid/issues/187
|
||||
// if (data.length > 0 && data[0] === 0 && process.platform === 'win32') {
|
||||
// data.unshift(0);
|
||||
// }
|
||||
|
||||
// From HID API documentation:
|
||||
// http://www.signal11.us/oss/hidapi/hidapi/doxygen/html/group__API.html#gad14ea48e440cf5066df87cc6488493af
|
||||
// The first byte of data[] must contain the Report ID.
|
||||
// For devices which only support a single report, this must be set to 0x0.
|
||||
data.unshift(0);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert buffer to space separated hexadecimal string
|
||||
* @param {Buffer} buffer
|
||||
* @returns {string}
|
||||
* @private
|
||||
* @static
|
||||
*/
|
||||
private static bufferToString(buffer: Array<number>): string {
|
||||
let str = '';
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
let hex = buffer[i].toString(16) + ' ';
|
||||
if (hex.length <= 2) {
|
||||
hex = '0' + hex;
|
||||
}
|
||||
str += hex;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal variable that represent the USB UHK device
|
||||
* @private
|
||||
@@ -132,7 +65,7 @@ export class UhkHidDevice {
|
||||
this.logService.error('[UhkHidDevice] Transfer error: ', err);
|
||||
return reject(err);
|
||||
}
|
||||
const logString = UhkHidDevice.bufferToString(receivedData);
|
||||
const logString = bufferToString(receivedData);
|
||||
this.logService.debug('[UhkHidDevice] USB[R]:', logString);
|
||||
|
||||
if (receivedData[0] !== 0) {
|
||||
@@ -142,8 +75,8 @@ export class UhkHidDevice {
|
||||
return resolve(Buffer.from(receivedData));
|
||||
});
|
||||
|
||||
const sendData = UhkHidDevice.getTransferData(buffer);
|
||||
this.logService.debug('[UhkHidDevice] USB[W]:', UhkHidDevice.bufferToString(sendData).substr(3));
|
||||
const sendData = getTransferData(buffer);
|
||||
this.logService.debug('[UhkHidDevice] USB[W]:', bufferToString(sendData).substr(3));
|
||||
device.write(sendData);
|
||||
});
|
||||
}
|
||||
@@ -157,13 +90,13 @@ export class UhkHidDevice {
|
||||
* Close the communication chanel with UHK Device
|
||||
*/
|
||||
public close(): void {
|
||||
this.logService.info('[UhkHidDevice] Device communication closing.');
|
||||
this.logService.debug('[UhkHidDevice] Device communication closing.');
|
||||
if (!this._device) {
|
||||
return;
|
||||
}
|
||||
this._device.close();
|
||||
this._device = null;
|
||||
this.logService.info('[UhkHidDevice] Device communication closed.');
|
||||
this.logService.debug('[UhkHidDevice] Device communication closed.');
|
||||
}
|
||||
|
||||
public async waitUntilKeyboardBusy(): Promise<void> {
|
||||
@@ -177,6 +110,77 @@ export class UhkHidDevice {
|
||||
}
|
||||
}
|
||||
|
||||
async reenumerate(enumerationMode: EnumerationModes): Promise<void> {
|
||||
const reenumMode = EnumerationModes[enumerationMode].toString();
|
||||
this.logService.debug(`[UhkHidDevice] Start reenumeration, mode: ${reenumMode}`);
|
||||
|
||||
const message = new Buffer([
|
||||
UsbCommand.Reenumerate,
|
||||
enumerationMode,
|
||||
BOOTLOADER_TIMEOUT_MS & 0xff,
|
||||
(BOOTLOADER_TIMEOUT_MS & 0xff << 8) >> 8,
|
||||
(BOOTLOADER_TIMEOUT_MS & 0xff << 16) >> 16,
|
||||
(BOOTLOADER_TIMEOUT_MS & 0xff << 24) >> 24
|
||||
]);
|
||||
|
||||
const enumeratedProductId = enumerationModeIdToProductId[enumerationMode.toString()];
|
||||
const startTime = new Date();
|
||||
let jumped = false;
|
||||
|
||||
while (new Date().getTime() - startTime.getTime() < 20000) {
|
||||
const devs = devices();
|
||||
this.logService.silly('[UhkHidDevice] reenumeration devices', devs);
|
||||
|
||||
const inBootloaderMode = devs.some((x: Device) =>
|
||||
x.vendorId === Constants.VENDOR_ID &&
|
||||
x.productId === enumeratedProductId);
|
||||
|
||||
if (inBootloaderMode) {
|
||||
this.logService.debug(`[UhkHidDevice] reenumeration devices up`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logService.silly(`[UhkHidDevice] Could not find reenumerated device: ${reenumMode}. Waiting...`);
|
||||
await snooze(100);
|
||||
|
||||
if (!jumped) {
|
||||
const device = this.getDevice();
|
||||
if (device) {
|
||||
const data = getTransferData(message);
|
||||
this.logService.debug(`[UhkHidDevice] USB[T]: Enumerate device. Mode: ${reenumMode}`);
|
||||
this.logService.debug('[UhkHidDevice] USB[W]:', bufferToString(data).substr(3));
|
||||
device.write(data);
|
||||
device.close();
|
||||
jumped = true;
|
||||
} else {
|
||||
this.logService.silly(`[UhkHidDevice] USB[T]: Enumerate device is not ready yet}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logService.error(`[UhkHidDevice] Could not find reenumerated device: ${reenumMode}. Timeout`);
|
||||
|
||||
throw new Error(`Could not reenumerate as ${reenumMode}`);
|
||||
}
|
||||
|
||||
async sendKbootCommandToModule(module: ModuleSlotToI2cAddress, command: KbootCommands, maxTry = 1): Promise<any> {
|
||||
let transfer;
|
||||
const moduleName = kbootKommandName(module);
|
||||
this.logService.debug(`[UhkHidDevice] USB[T]: Send KbootCommand ${moduleName} ${KbootCommands[command].toString()}`);
|
||||
if (command === KbootCommands.idle) {
|
||||
transfer = new Buffer([UsbCommand.SendKbootCommandToModule, command]);
|
||||
} else {
|
||||
transfer = new Buffer([UsbCommand.SendKbootCommandToModule, command, Number.parseInt(module)]);
|
||||
}
|
||||
await retry(async () => await this.write(transfer), maxTry, this.logService);
|
||||
}
|
||||
|
||||
async jumpToBootloaderModule(module: ModuleSlotToId): Promise<any> {
|
||||
this.logService.debug(`[UhkHidDevice] USB[T]: Jump to bootloader. Module: ${ModuleSlotToId[module].toString()}`);
|
||||
const transfer = new Buffer([UsbCommand.JumpToModuleBootloader, module]);
|
||||
await this.write(transfer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the stored version of HID device. If not exist try to initialize.
|
||||
* @returns {HID}
|
||||
@@ -205,11 +209,11 @@ export class UhkHidDevice {
|
||||
((x.usagePage === 128 && x.usage === 129) || x.interface === 0));
|
||||
|
||||
if (!dev) {
|
||||
this.logService.info('[UhkHidDevice] UHK Device not found:');
|
||||
this.logService.debug('[UhkHidDevice] UHK Device not found:');
|
||||
return null;
|
||||
}
|
||||
const device = new HID(dev.path);
|
||||
this.logService.info('[UhkHidDevice] Used device:', dev);
|
||||
this.logService.debug('[UhkHidDevice] Used device:', dev);
|
||||
return device;
|
||||
}
|
||||
catch (err) {
|
||||
@@ -218,5 +222,20 @@ export class UhkHidDevice {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function kbootKommandName(module: ModuleSlotToI2cAddress): string {
|
||||
switch (module) {
|
||||
case ModuleSlotToI2cAddress.leftHalf:
|
||||
return 'leftHalf';
|
||||
|
||||
case ModuleSlotToI2cAddress.leftAddon:
|
||||
return 'leftAddon';
|
||||
|
||||
case ModuleSlotToI2cAddress.rightAddon:
|
||||
return 'rightAddon';
|
||||
|
||||
default :
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
80
packages/uhk-usb/src/uhk-operations.ts
Normal file
80
packages/uhk-usb/src/uhk-operations.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { LogService } from 'uhk-common';
|
||||
import { EnumerationModes, EnumerationNameToProductId, KbootCommands, ModuleSlotToI2cAddress, ModuleSlotToId } from './constants';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { UhkBlhost } from './uhk-blhost';
|
||||
import { UhkHidDevice } from './uhk-hid-device';
|
||||
import { snooze } from './util';
|
||||
|
||||
export class UhkOperations {
|
||||
constructor(private logService: LogService,
|
||||
private blhost: UhkBlhost,
|
||||
private device: UhkHidDevice,
|
||||
private rootDir: string) {
|
||||
}
|
||||
|
||||
public async updateRightFirmware(firmwarePath = this.getFirmwarePath()) {
|
||||
this.logService.debug('[UhkOperations] Start flashing right firmware');
|
||||
const prefix = [`--usb 0x1d50,0x${EnumerationNameToProductId.bootloader.toString(16)}`];
|
||||
|
||||
await this.device.reenumerate(EnumerationModes.Bootloader);
|
||||
this.device.close();
|
||||
await this.blhost.runBlhostCommand([...prefix, 'flash-security-disable', '0403020108070605']);
|
||||
await this.blhost.runBlhostCommand([...prefix, 'flash-erase-region', '0xc000', '475136']);
|
||||
await this.blhost.runBlhostCommand([...prefix, 'flash-image', `"${firmwarePath}"`]);
|
||||
await this.blhost.runBlhostCommand([...prefix, 'reset']);
|
||||
this.logService.debug('[UhkOperations] End flashing right firmware');
|
||||
}
|
||||
|
||||
public async updateLeftModule(firmwarePath = this.getLeftModuleFirmwarePath()) {
|
||||
this.logService.debug('[UhkOperations] Start flashing left module firmware');
|
||||
|
||||
const prefix = [`--usb 0x1d50,0x${EnumerationNameToProductId.buspal.toString(16)}`];
|
||||
const buspalPrefix = [...prefix, `--buspal i2c,${ModuleSlotToI2cAddress.leftHalf},100k`];
|
||||
|
||||
await this.device.reenumerate(EnumerationModes.NormalKeyboard);
|
||||
this.device.close();
|
||||
await snooze(1000);
|
||||
await this.device.sendKbootCommandToModule(ModuleSlotToI2cAddress.leftHalf, KbootCommands.ping, 100);
|
||||
await snooze(1000);
|
||||
await this.device.jumpToBootloaderModule(ModuleSlotToId.leftHalf);
|
||||
this.device.close();
|
||||
await this.device.reenumerate(EnumerationModes.Buspal);
|
||||
this.device.close();
|
||||
await this.blhost.runBlhostCommandRetry([...buspalPrefix, 'get-property', '1']);
|
||||
await this.blhost.runBlhostCommand([...buspalPrefix, 'flash-erase-all-unsecure']);
|
||||
await this.blhost.runBlhostCommand([...buspalPrefix, 'write-memory', '0x0', `"${firmwarePath}"`]);
|
||||
await this.blhost.runBlhostCommand([...prefix, 'reset']);
|
||||
await snooze(1000);
|
||||
await this.device.reenumerate(EnumerationModes.NormalKeyboard);
|
||||
this.device.close();
|
||||
await snooze(1000);
|
||||
await this.device.sendKbootCommandToModule(ModuleSlotToI2cAddress.leftHalf, KbootCommands.reset, 100);
|
||||
this.device.close();
|
||||
await snooze(1000);
|
||||
await this.device.sendKbootCommandToModule(ModuleSlotToI2cAddress.leftHalf, KbootCommands.idle);
|
||||
this.device.close();
|
||||
|
||||
this.logService.debug('[UhkOperations] End flashing left module firmware');
|
||||
}
|
||||
|
||||
private getFirmwarePath(): string {
|
||||
const firmware = path.join(this.rootDir, 'packages/firmware/devices/uhk60-right/firmware.hex');
|
||||
|
||||
if (fs.existsSync(firmware)) {
|
||||
return firmware;
|
||||
}
|
||||
|
||||
throw new Error(`Could not found firmware ${firmware}`);
|
||||
}
|
||||
|
||||
private getLeftModuleFirmwarePath(): string {
|
||||
const firmware = path.join(this.rootDir, 'packages/firmware/modules/uhk60-left.bin');
|
||||
|
||||
if (fs.existsSync(firmware)) {
|
||||
return firmware;
|
||||
}
|
||||
|
||||
throw new Error(`Could not found firmware ${firmware}`);
|
||||
}
|
||||
}
|
||||
97
packages/uhk-usb/src/util.ts
Normal file
97
packages/uhk-usb/src/util.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { Constants, UsbCommand } from './constants';
|
||||
import { LogService } from '../../uhk-common';
|
||||
|
||||
export const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Convert the Buffer to number[]
|
||||
* @param {Buffer} buffer
|
||||
* @returns {number[]}
|
||||
* @private
|
||||
* @static
|
||||
*/
|
||||
export function convertBufferToIntArray(buffer: Buffer): number[] {
|
||||
return Array.prototype.slice.call(buffer, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the communication package into 64 byte fragments
|
||||
* @param {UsbCommand} usbCommand
|
||||
* @param {Buffer} configBuffer
|
||||
* @returns {Buffer[]}
|
||||
* @private
|
||||
*/
|
||||
export function getTransferBuffers(usbCommand: UsbCommand, configBuffer: Buffer): Buffer[] {
|
||||
const fragments: Buffer[] = [];
|
||||
const MAX_SENDING_PAYLOAD_SIZE = Constants.MAX_PAYLOAD_SIZE - 4;
|
||||
for (let offset = 0; offset < configBuffer.length; offset += MAX_SENDING_PAYLOAD_SIZE) {
|
||||
const length = offset + MAX_SENDING_PAYLOAD_SIZE < configBuffer.length
|
||||
? MAX_SENDING_PAYLOAD_SIZE
|
||||
: configBuffer.length - offset;
|
||||
const header = new Buffer([usbCommand, length, offset & 0xFF, offset >> 8]);
|
||||
fragments.push(Buffer.concat([header, configBuffer.slice(offset, offset + length)]));
|
||||
}
|
||||
|
||||
return fragments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the communication package that will send over USB and
|
||||
* @param {Buffer} buffer
|
||||
* @returns {number[]}
|
||||
* @private
|
||||
* @static
|
||||
*/
|
||||
export function getTransferData(buffer: Buffer): number[] {
|
||||
const data = convertBufferToIntArray(buffer);
|
||||
data.unshift(0);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert buffer to space separated hexadecimal string
|
||||
* @param {Buffer} buffer
|
||||
* @returns {string}
|
||||
* @private
|
||||
* @static
|
||||
*/
|
||||
export function bufferToString(buffer: Array<number>): string {
|
||||
let str = '';
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
let hex = buffer[i].toString(16) + ' ';
|
||||
if (hex.length <= 2) {
|
||||
hex = '0' + hex;
|
||||
}
|
||||
str += hex;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export async function retry(command: Function, maxTry = 3, logService?: LogService): Promise<any> {
|
||||
let retryCount = 0;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
// logService.debug(`[retry] try to run FUNCTION:\n ${command}, \n retry: ${retryCount}`);
|
||||
await command();
|
||||
await snooze(100);
|
||||
// logService.debug(`[retry] success FUNCTION:\n ${command}, \n retry: ${retryCount}`);
|
||||
return;
|
||||
} catch (err) {
|
||||
retryCount++;
|
||||
if (retryCount >= maxTry) {
|
||||
|
||||
if (logService) {
|
||||
// logService.error(`[retry] failed and no try rerun FUNCTION:\n ${command}, \n retry: ${retryCount}`);
|
||||
}
|
||||
|
||||
throw err;
|
||||
} else {
|
||||
if (logService) {
|
||||
logService.error(`[retry] failed, but try run FUNCTION:\n ${command}, \n retry: ${retryCount}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user