diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 0272f73467..f7f71607a7 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -56,7 +56,14 @@ class EZSPAdapter extends Adapter { debug(`Adapter concurrent: ${concurrent}`); this.queue = new Queue(concurrent); - this.driver = new Driver(); + this.driver = new Driver(this.port.path, { + baudRate: this.port.baudRate || 115200, + rtscts: this.port.rtscts, + parity: 'none', + stopBits: 1, + xon: true, + xoff: true + }, this.networkOptions, this.greenPowerGroup); this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); this.driver.on('incomingMessage', this.processMessage.bind(this)); @@ -169,14 +176,11 @@ class EZSPAdapter extends Adapter { * Adapter methods */ public async start(): Promise { - return await this.driver.startup(this.port.path, { - baudRate: this.port.baudRate || 115200, - rtscts: this.port.rtscts, - parity: 'none', - stopBits: 1, - xon: true, - xoff: true - }, this.networkOptions, this.greenPowerGroup); + try { + return await this.driver.startup(); + } catch { + return await this.driver.reset(); + } } public async stop(): Promise { diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index eb87f718f4..8f3ebd8577 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -92,24 +92,30 @@ export class Driver extends EventEmitter { /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ private serialOpt: Record; - constructor() { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + constructor(port: string, serialOpt: Record, nwkOpt: TsType.NetworkOptions, greenPowerGroup: number) { super(); - + + this.nwkOpt = nwkOpt; + this.port = port; + this.serialOpt = serialOpt; + this.greenPowerGroup = greenPowerGroup; this.waitress = new Waitress( this.waitressValidator, this.waitressTimeoutFormatter); } - - private async onReset(): Promise { + + public async reset(): Promise { let attempts = 0; const pauses = [10, 30, 60]; let pause = 0; + + // infinite retries while (true) { debug.log(`Reset connection. Try ${attempts}`); try { await this.stop(); await Wait(1000); - await this.startup(this.port, this.serialOpt, this.nwkOpt, this.greenPowerGroup); - break; + return await this.startup(); } catch (e) { debug.error(`Reset error ${e.stack}`); attempts += 1; @@ -122,37 +128,37 @@ export class Driver extends EventEmitter { } } - /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ - public async startup(port: string, serialOpt: Record, nwkOpt: TsType.NetworkOptions, - greenPowerGroup: number): Promise { + private async onReset(): Promise { + await this.reset(); + } + + public async startup(): Promise { let result: TsType.StartResult = 'resumed'; - this.nwkOpt = nwkOpt; - this.port = port; - this.serialOpt = serialOpt; - this.greenPowerGroup = greenPowerGroup; this.transactionID = 1; this.ezsp = undefined; this.ezsp = new Ezsp(); - this.ezsp.on('reset', this.onReset.bind(this)); this.ezsp.on('close', this.onClose.bind(this)); - - await this.ezsp.connect(port, serialOpt); - await this.ezsp.version(); - await this.ezsp.updateConfig(); + try { + await this.ezsp.connect(this.port, this.serialOpt); + } catch (error) { + debug.error(`EZSP could not connect: ${error.cause ?? error}`); + + throw error; + } - await this.ezsp.updatePolicies(); + this.ezsp.on('reset', this.onReset.bind(this)); + await this.ezsp.version(); + await this.ezsp.updateConfig(); + await this.ezsp.updatePolicies(); //await this.ezsp.setValue(EzspValueId.VALUE_MAXIMUM_OUTGOING_TRANSFER_SIZE, 82); //await this.ezsp.setValue(EzspValueId.VALUE_MAXIMUM_INCOMING_TRANSFER_SIZE, 82); await this.ezsp.setValue(EzspValueId.VALUE_END_DEVICE_KEEP_ALIVE_SUPPORT_MODE, 3); await this.ezsp.setValue(EzspValueId.VALUE_CCA_THRESHOLD, 0); - await this.ezsp.setSourceRouting(); - //const count = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); //debug.log("APS_UNICAST_MESSAGE_COUNT is set to %s", count); - await this.addEndpoint({ inputClusters: [0x0000, 0x0003, 0x0006, 0x000A, 0x0019, 0x001A, 0x0300], outputClusters: [0x0000, 0x0003, 0x0004, 0x0005, 0x0006, 0x0008, 0x0020, @@ -187,7 +193,7 @@ export class Driver extends EventEmitter { revision: vers }; - if (await this.needsToBeInitialised(nwkOpt)) { + if (await this.needsToBeInitialised(this.nwkOpt)) { const res = await this.ezsp.execCommand('networkState'); debug.log(`Network state ${res.status}`); if (res.status == EmberNetworkStatus.JOINED_NETWORK) { @@ -223,7 +229,7 @@ export class Driver extends EventEmitter { this.multicast = new Multicast(this); await this.multicast.startup([]); - await this.multicast.subscribe(greenPowerGroup, 242); + await this.multicast.subscribe(this.greenPowerGroup, 242); // await this.multicast.subscribe(1, 901); return result; } diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 95e1aac357..58c4f8dffd 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -27,6 +27,7 @@ const debug = { }; +const MAX_SERIAL_CONNECT_ATTEMPTS = 4; const MTOR_MIN_INTERVAL = 10; const MTOR_MAX_INTERVAL = 90; const MTOR_ROUTE_ERROR_THRESHOLD = 4; @@ -259,25 +260,33 @@ export class Ezsp extends EventEmitter { this.serialDriver = new SerialDriver(); this.serialDriver.on('received', this.onFrameReceived.bind(this)); this.serialDriver.on('close', this.onClose.bind(this)); - this.serialDriver.on('reset', this.resetHandler.bind(this)); } public async connect(path: string, options: Record): Promise { let lastError = null; - for (let i = 1; i < 5; i += 1) { + + for (let i = 1; i <= MAX_SERIAL_CONNECT_ATTEMPTS; i++) { try { await this.serialDriver.connect(path, options); break; } catch (error) { debug.error(`Connection attempt ${i} error: ${error.stack}`); - await Wait(5000); - debug.log(`Next attempt ${i+1}`); + + if (i < MAX_SERIAL_CONNECT_ATTEMPTS) { + await Wait(5000); + debug.log(`Next attempt ${i+1}`); + } + lastError = error; } } + if (!this.serialDriver.isInitialized()) { throw new Error("Failure to connect", {cause: lastError}); } + + this.serialDriver.on('reset', this.onReset.bind(this)); + if (WATCHDOG_WAKE_PERIOD) { this.watchdogTimer = setInterval( this.watchdogHandler.bind(this), @@ -665,12 +674,16 @@ export class Ezsp extends EventEmitter { this.failures += 1; if (this.failures > MAX_WATCHDOG_FAILURES) { this.failures = 0; - this.resetHandler(); + this.reset(); } } } - private async resetHandler(): Promise { + private reset(): void { this.emit('reset'); } + + private onReset(): void { + this.reset(); + } } diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 9ffd200f0c..fd8fcbf821 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -89,7 +89,7 @@ export class SerialDriver extends EventEmitter { debug('Serialport opened'); this.serialPort.once('close', this.onPortClose.bind(this)); - this.serialPort.once('error', this.onPortError.bind(this)); + this.serialPort.on('error', this.onPortError.bind(this)); // reset await this.reset(); diff --git a/test/adapter/ezsp/uart.test.ts b/test/adapter/ezsp/uart.test.ts index 7412b82748..e1cf2741c6 100644 --- a/test/adapter/ezsp/uart.test.ts +++ b/test/adapter/ezsp/uart.test.ts @@ -90,7 +90,7 @@ describe('UART', () => { expect(mockSerialPortPipe).toHaveBeenCalledTimes(1); expect(mockSerialPortAsyncOpen).toHaveBeenCalledTimes(1); - expect(mockSerialPortOnce).toHaveBeenCalledTimes(2); + expect(mockSerialPortOnce).toHaveBeenCalledTimes(1); expect(writeBufferSpy).toHaveBeenCalledTimes(1); });