From 00d803082988f5bee8f9103fc7e112461172e5f4 Mon Sep 17 00:00:00 2001 From: Marcos Diaz Date: Fri, 27 Jun 2025 13:35:28 +0300 Subject: [PATCH] Generic retry logic applied to config and profiles usb comms --- src/components/logs/logs.ts | 4 +- src/components/profile/profile.ts | 43 ++------------- src/components/profile/section.ts | 2 +- src/components/settings/settings.ts | 14 ++--- src/components/tune/tune.ts | 2 +- src/lib/device.ts | 83 ++++++++++++++++++++--------- src/lib/profiles.ts | 24 ++++----- src/lib/tunes.ts | 4 +- src/services/webusb.ts | 16 +++--- 9 files changed, 96 insertions(+), 96 deletions(-) diff --git a/src/components/logs/logs.ts b/src/components/logs/logs.ts index 9522b73..0dbfa82 100644 --- a/src/components/logs/logs.ts +++ b/src/components/logs/logs.ts @@ -64,7 +64,7 @@ export class LogsComponent { } async filterGet() { - const config = await this.webusb.getConfig(ConfigIndex.LOG_MASK) + const config = await this.webusb.tryGetConfig(ConfigIndex.LOG_MASK) this.filterUSB = !!(config.presetIndex & LogMask.USB) this.filterTouch = !!(config.presetIndex & LogMask.TOUCH) this.filterWireless = !!(config.presetIndex & LogMask.WIRELESS) @@ -75,6 +75,6 @@ export class LogsComponent { if (this.filterUSB) logMask += LogMask.USB if (this.filterTouch) logMask += LogMask.TOUCH if (this.filterWireless) logMask += LogMask.WIRELESS - this.webusb.setConfig(ConfigIndex.LOG_MASK, logMask, []) + this.webusb.trySetConfig(ConfigIndex.LOG_MASK, logMask, []) } } diff --git a/src/components/profile/profile.ts b/src/components/profile/profile.ts index 98c8095..8d923d2 100644 --- a/src/components/profile/profile.ts +++ b/src/components/profile/profile.ts @@ -14,8 +14,6 @@ import { sectionIsGyroAxis, sectionIsHome } from 'lib/ctrl' import { SectionIndex } from 'lib/ctrl' import { Device } from 'lib/device' -const MAX_FETCH_ATTEMPTS = 3 - @Component({ selector: 'app-profile', standalone: true, @@ -67,45 +65,14 @@ export class ProfileComponent { } async tryFetchNames() { - let attempts = 0 - while(true) { - try { - const profiles = this.webusb.selectedDevice!.profiles - await profiles.fetchProfileNames() - break - } catch(error) { - attempts += 1 - if (attempts <= MAX_FETCH_ATTEMPTS) console.warn(error) - else { - console.error(error) - break - } - } - } + const profiles = this.webusb.selectedDevice!.profiles + await profiles.fetchProfileNames() } async tryFetchProfile() { - const log = `tryFetchProfile ${this.profileIndex}` - console.log(log) - let attempts = 0 - let success = false - while(true) { - try { - const profiles = this.webusb.selectedDevice!.profiles - await profiles.fetchProfile(this.profileIndex, false) - success = true - break - } catch(error) { - attempts += 1 - if (attempts <= MAX_FETCH_ATTEMPTS) console.warn(error) - else { - console.error(error) - break - } - } - } - if (success) console.log(log, 'OK') - else console.log(log, 'FAILED') + console.log('tryFetchProfile', this.profileIndex) + const profiles = this.webusb.selectedDevice!.profiles + await profiles.fetchProfile(this.profileIndex, false) } getProfile() { diff --git a/src/components/profile/section.ts b/src/components/profile/section.ts index 3925e77..5172f50 100644 --- a/src/components/profile/section.ts +++ b/src/components/profile/section.ts @@ -301,7 +301,7 @@ export class SectionComponent { } save = async () => { - await this.webusb.setSection(this.profileIndex, this.section) + await this.webusb.trySetSection(this.profileIndex, this.section) } } diff --git a/src/components/settings/settings.ts b/src/components/settings/settings.ts index 5f4f9f8..d0c3fff 100644 --- a/src/components/settings/settings.ts +++ b/src/components/settings/settings.ts @@ -44,12 +44,12 @@ export class SettingsComponent { async load() { const getConfig = async(index: ConfigIndex) => { - return (await this.webusb.getConfig(index)).presetIndex + return (await this.webusb.tryGetConfig(index)).presetIndex } this.longCalibration = !!await getConfig(ConfigIndex.LONG_CALIBRATION) this.swapGyros = !!await getConfig(ConfigIndex.SWAP_GYROS) this.invertTouchPolarity = !!await getConfig(ConfigIndex.TOUCH_INVERT_POLARITY) - const gyroUserOffset = await this.webusb.getConfig(ConfigIndex.GYRO_USER_OFFSET) + const gyroUserOffset = await this.webusb.tryGetConfig(ConfigIndex.GYRO_USER_OFFSET) this.gyroUserOffsetX = gyroUserOffset.values[0] this.gyroUserOffsetY = gyroUserOffset.values[1] this.gyroUserOffsetZ = gyroUserOffset.values[2] @@ -60,19 +60,19 @@ export class SettingsComponent { } async saveLongCalibration() { - await this.webusb.setConfig(ConfigIndex.LONG_CALIBRATION, +this.longCalibration, []) + await this.webusb.trySetConfig(ConfigIndex.LONG_CALIBRATION, +this.longCalibration, []) } async saveSwapGyros() { - await this.webusb.setConfig(ConfigIndex.SWAP_GYROS, +this.swapGyros, []) + await this.webusb.trySetConfig(ConfigIndex.SWAP_GYROS, +this.swapGyros, []) } async saveInvertTouchPolarity() { - await this.webusb.setConfig(ConfigIndex.TOUCH_INVERT_POLARITY, +this.invertTouchPolarity, []) + await this.webusb.trySetConfig(ConfigIndex.TOUCH_INVERT_POLARITY, +this.invertTouchPolarity, []) } async saveGyroUserOffset() { - await this.webusb.setConfig(ConfigIndex.GYRO_USER_OFFSET, 0, [ + await this.webusb.trySetConfig(ConfigIndex.GYRO_USER_OFFSET, 0, [ this.gyroUserOffsetX, this.gyroUserOffsetY, this.gyroUserOffsetZ, @@ -80,7 +80,7 @@ export class SettingsComponent { } async saveThumbstickSmoothSamples() { - await this.webusb.setConfig(ConfigIndex.THUMBSTICK_SMOOTH_SAMPLES, +this.thumbstickSmoothSamples, []) + await this.webusb.trySetConfig(ConfigIndex.THUMBSTICK_SMOOTH_SAMPLES, +this.thumbstickSmoothSamples, []) } showDialogHelp(key: string) { diff --git a/src/components/tune/tune.ts b/src/components/tune/tune.ts index 66271d3..de2323f 100644 --- a/src/components/tune/tune.ts +++ b/src/components/tune/tune.ts @@ -96,7 +96,7 @@ export class TuneComponent { } async getPreset() { - console.log(`getPreset ${ConfigIndex[this.mode.configIndex]}`) + // console.log(`getPreset ${ConfigIndex[this.mode.configIndex]}`) const tunes = this.webusb.selectedDevice!.tunes const presetWithValues = await tunes.getPreset(this.mode.configIndex) if (this.mode.url != 'protocol') { diff --git a/src/lib/device.ts b/src/lib/device.ts index 4d7c961..93ac917 100644 --- a/src/lib/device.ts +++ b/src/lib/device.ts @@ -30,8 +30,10 @@ import { const ADDR_IN = 3 const ADDR_OUT = 4 const TIMEOUT = 500 -const STATUS_FETCH_MAX_ATTEMPTS = 10 -const STATUS_FETCH_DELAY_ATTEMPT = 200 + +const FETCH_GENERIC_MAX_ATTEMPTS = 5 +const FETCH_STATUS_MAX_ATTEMPTS = 10 +const FETCH_STATUS_DELAY_ATTEMPT = 250 export class Device { usbDevice: USBDevice @@ -262,29 +264,6 @@ export class Device { return Promise.race([responsePromise, timeout]) } - async tryGetStatus() { - console.log('tryGetStatus', this.getName()) - await delay(100) // Increase the chances device is already connected. - let attempts = 0 - while(true) { - try { - if (!this.isConnected) throw Error('tryGetStatus: Device not connected') - if (this.getFirmwareAsString() !== '0.0.0') break - const status = await this.getStatus() - this.handleCtrlStatusShare(status) - break - } catch(error) { - attempts += 1 - if (attempts <= STATUS_FETCH_MAX_ATTEMPTS) console.warn(error) - else { - console.error(error) - break - } - await delay(STATUS_FETCH_DELAY_ATTEMPT) - } - } - } - async getConfig(index: ConfigIndex): Promise { this.pendingConfig = new AsyncSubject() const ctrlOut = new CtrlConfigGet(index) @@ -362,6 +341,60 @@ export class Device { return Promise.race([responsePromise, timeout]) } + async tryGetStatus() { + console.log('tryGetStatus', this.getName()) + await delay(100) // Increase the chances device is already connected. + let attempts = 0 + while(true) { + try { + if (!this.isConnected) throw Error('tryGetStatus: Device not connected') + if (this.getFirmwareAsString() !== '0.0.0') break + const status = await this.getStatus() + this.handleCtrlStatusShare(status) + break + } catch(error) { + attempts += 1 + if (attempts <= FETCH_STATUS_MAX_ATTEMPTS) console.warn(error) + else { + console.error(error) + break + } + await delay(FETCH_STATUS_DELAY_ATTEMPT) + } + } + } + + async tryFetch(func: any) { + let attempts = 0 + while(true) { + try { + return await func() + } catch(error) { + attempts += 1 + if (attempts <= FETCH_GENERIC_MAX_ATTEMPTS) console.warn(error) + else throw error + } + } + } + + async tryGetConfig(index: ConfigIndex) { + console.log('tryGetConfig', ConfigIndex[index]) + return this.tryFetch(() => this.getConfig(index)) + } + + async trySetConfig(index: ConfigIndex, preset: number, values: number[]) { + console.log('tryGetConfig', ConfigIndex[index], preset, values) + return this.tryFetch(() => this.setConfig(index, preset, values)) + } + + async tryGetSection(profileIndex: number, sectionIndex: SectionIndex) { + return this.tryFetch(() => this.getSection(profileIndex, sectionIndex)) + } + + async trySetSection(profileIndex: number, section: CtrlSection) { + return this.tryFetch(() => this.setSection(profileIndex, section)) + } + } // Fake wireless device connected to dongle. diff --git a/src/lib/profiles.ts b/src/lib/profiles.ts index 0c549b6..bcbf839 100644 --- a/src/lib/profiles.ts +++ b/src/lib/profiles.ts @@ -47,7 +47,7 @@ export class Profiles { } async fetchProfileName(index: number) { - const section = await this.device.getSection(index, SectionIndex.META) + const section = await this.device.tryGetSection(index, SectionIndex.META) this.profiles[index].meta = section as CtrlSectionMeta } @@ -56,11 +56,11 @@ export class Profiles { // Replace internal meta properties instead of the whole object, so Angular // reference to the object is not lost. (Profile name is special because is // linked in many dynamic UI elements). - const meta = await this.device.getSection(profileIndex, SectionIndex.META) as CtrlSectionMeta + const meta = await this.device.tryGetSection(profileIndex, SectionIndex.META) as CtrlSectionMeta profile.meta.replaceContentsWith(meta) // Buttons. const getButton = async (sectionIndex: SectionIndex) => { - return await this.device.getSection(profileIndex, sectionIndex) as CtrlButton + return await this.device.tryGetSection(profileIndex, sectionIndex) as CtrlButton } profile.buttonA = await getButton(SectionIndex.A) profile.buttonB = await getButton(SectionIndex.B) @@ -103,22 +103,22 @@ export class Profiles { profile.buttonRStickDR = await getButton(SectionIndex.RSTICK_DR) profile.buttonRStickPush = await getButton(SectionIndex.RSTICK_PUSH) // Rotary. - const rotaryUp = await this.device.getSection(profileIndex, SectionIndex.ROTARY_UP) as CtrlRotary - const rotaryDown = await this.device.getSection(profileIndex, SectionIndex.ROTARY_DOWN) as CtrlRotary + const rotaryUp = await this.device.tryGetSection(profileIndex, SectionIndex.ROTARY_UP) as CtrlRotary + const rotaryDown = await this.device.tryGetSection(profileIndex, SectionIndex.ROTARY_DOWN) as CtrlRotary profile.rotaryUp = rotaryUp profile.rotaryDown = rotaryDown // Thumbstick mode. - const lStick = await this.device.getSection(profileIndex, SectionIndex.LSTICK_SETTINGS) as CtrlThumbstick - const rStick = await this.device.getSection(profileIndex, SectionIndex.RSTICK_SETTINGS) as CtrlThumbstick + const lStick = await this.device.tryGetSection(profileIndex, SectionIndex.LSTICK_SETTINGS) as CtrlThumbstick + const rStick = await this.device.tryGetSection(profileIndex, SectionIndex.RSTICK_SETTINGS) as CtrlThumbstick profile.settingsLStick = lStick profile.settingsRStick = rStick // Gyro mode. - const gyro = await this.device.getSection(profileIndex, SectionIndex.GYRO_SETTINGS) as CtrlGyro + const gyro = await this.device.tryGetSection(profileIndex, SectionIndex.GYRO_SETTINGS) as CtrlGyro profile.settingsGyro = gyro // Gyro Axes. - profile.gyroX = await this.device.getSection(profileIndex, SectionIndex.GYRO_X) as CtrlGyroAxis - profile.gyroY = await this.device.getSection(profileIndex, SectionIndex.GYRO_Y) as CtrlGyroAxis - profile.gyroZ = await this.device.getSection(profileIndex, SectionIndex.GYRO_Z) as CtrlGyroAxis + profile.gyroX = await this.device.tryGetSection(profileIndex, SectionIndex.GYRO_X) as CtrlGyroAxis + profile.gyroY = await this.device.tryGetSection(profileIndex, SectionIndex.GYRO_Y) as CtrlGyroAxis + profile.gyroZ = await this.device.tryGetSection(profileIndex, SectionIndex.GYRO_Z) as CtrlGyroAxis } getProfile(profileIndex: number) { @@ -157,7 +157,7 @@ export class Profiles { sections = this.upgradeFrom097to100(sections) for(let section of sections) { console.log('Section from blob', section) - await this.device.setSection(profileIndex, section) + await this.device.trySetSection(profileIndex, section) } this.fetchProfile(profileIndex, true) } diff --git a/src/lib/tunes.ts b/src/lib/tunes.ts index c2b62f5..9daf74e 100644 --- a/src/lib/tunes.ts +++ b/src/lib/tunes.ts @@ -18,7 +18,7 @@ export class Tunes { } async fetchPreset(configIndex: number) { - const presetWithValues = await this.device.getConfig(configIndex) + const presetWithValues = await this.device.tryGetConfig(configIndex) this.presets[configIndex] = presetWithValues } @@ -31,7 +31,7 @@ export class Tunes { async setPreset(configIndex: number, presetIndex: number, values: number[]) { this.presets[configIndex] = {presetIndex, values} - return await this.device.setConfig(configIndex, presetIndex, values) + return await this.device.trySetConfig(configIndex, presetIndex, values) } async invalidatePresets() { diff --git a/src/services/webusb.ts b/src/services/webusb.ts index 3f696fb..8d37eaf 100644 --- a/src/services/webusb.ts +++ b/src/services/webusb.ts @@ -233,20 +233,20 @@ export class WebusbService { return await this.selectedDevice!.sendProfileOverwrite(indexTo, indexFrom) } - async getConfig(index: ConfigIndex): Promise { - return await this.selectedDevice!.getConfig(index) + async tryGetConfig(index: ConfigIndex): Promise { + return await this.selectedDevice!.tryGetConfig(index) } - async setConfig(index: ConfigIndex, preset: number, values: number[]): Promise { - return await this.selectedDevice!.setConfig(index, preset, values) + async trySetConfig(index: ConfigIndex, preset: number, values: number[]): Promise { + return await this.selectedDevice!.trySetConfig(index, preset, values) } - async getSection(profileIndex: number, sectionIndex: SectionIndex): Promise { - return await this.selectedDevice!.getSection(profileIndex, sectionIndex) + async tryGetSection(profileIndex: number, sectionIndex: SectionIndex): Promise { + return await this.selectedDevice!.tryGetSection(profileIndex, sectionIndex) } - async setSection( profileIndex: number, section: CtrlSection) { - return await this.selectedDevice!.setSection(profileIndex, section) + async trySetSection( profileIndex: number, section: CtrlSection) { + return await this.selectedDevice!.trySetSection(profileIndex, section) } getProfiles() {