Skip to content

Commit

Permalink
fix: Ember: fix for some startup issues, with associated tests. (#1053)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerivec authored May 9, 2024
1 parent a4f7e2d commit 3ab71b4
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 28 deletions.
8 changes: 5 additions & 3 deletions src/adapter/ember/ezsp/ezsp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export class Ezsp extends EventEmitter {

/** Counter for Queue Full errors */
public counterErrQueueFull: number;

/** Handle used to tick for possible received callbacks */
private tickHandle: NodeJS.Timeout;

Expand All @@ -298,8 +298,7 @@ export class Ezsp extends EventEmitter {
this.buffalo = new EzspBuffalo(this.frameContents);

this.ash = new UartAsh(options);
this.ash.on(AshEvents.fatalError, this.onAshFatalError.bind(this));
this.ash.on(AshEvents.frame, this.onAshFrame.bind(this));
this.ash.on(AshEvents.FRAME, this.onAshFrame.bind(this));
}

/**
Expand All @@ -325,6 +324,7 @@ export class Ezsp extends EventEmitter {
}

clearTimeout(this.tickHandle);
this.ash.removeAllListeners(AshEvents.FATAL_ERROR);

this.frameContents.fill(0);
this.frameLength = 0;
Expand Down Expand Up @@ -358,6 +358,8 @@ export class Ezsp extends EventEmitter {

if (status === EzspStatus.SUCCESS) {
logger.info(`======== EZSP started ========`, NS);
// registered after reset sequence to avoid bubbling up to adapter before this point
this.ash.on(AshEvents.FATAL_ERROR, this.onAshFatalError.bind(this));
this.tick();
return status;
}
Expand Down
19 changes: 7 additions & 12 deletions src/adapter/ember/uart/ash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ const ashGetACKNum = (ctrl: number): number => ((ctrl & ASH_ACKNUM_MASK) >> ASH_

export enum AshEvents {
/** When the ASH protocol detects a fatal error (bubbles up to restart adapter). */
fatalError = 'fatalError',
FATAL_ERROR = 'fatalError',
/** When a frame has been parsed and queued in the rxQueue. */
frame = 'frame',
FRAME = 'frame',
}

type UartAshCounters = {
Expand Down Expand Up @@ -576,7 +576,7 @@ export class UartAsh extends EventEmitter {
*/
private async onPortError(error: Error): Promise<void> {
logger.info(`Port error: ${error}`, NS);
this.emit(AshEvents.fatalError, EzspStatus.ERROR_SERIAL_INIT);
this.emit(AshEvents.FATAL_ERROR, EzspStatus.ERROR_SERIAL_INIT);
}

/**
Expand Down Expand Up @@ -608,14 +608,9 @@ export class UartAsh extends EventEmitter {
const status = this.receiveFrame(buffer);

if ((status !== EzspStatus.SUCCESS) && (status !== EzspStatus.ASH_IN_PROGRESS) && (status !== EzspStatus.NO_RX_DATA)) {
if (this.flags & Flag.CONNECTED) {
logger.error(`Error while parsing received frame, status=${EzspStatus[status]}.`, NS);
// if we're connected (not in reset) and get here, we need to reset
this.emit(AshEvents.fatalError, EzspStatus.HOST_FATAL_ERROR);
return;
} else {
logger.debug(`Error while parsing received frame in NOT_CONNECTED state (flags=${this.flags}), status=${EzspStatus[status]}.`, NS);
}
logger.error(`Error while parsing received frame, status=${EzspStatus[status]}.`, NS);
this.emit(AshEvents.FATAL_ERROR, EzspStatus.HOST_FATAL_ERROR);
return;
}
}

Expand Down Expand Up @@ -1178,7 +1173,7 @@ export class UartAsh extends EventEmitter {
this.counters.rxData += this.rxDataBuffer.len;

setImmediate(() => {
this.emit(AshEvents.frame);
this.emit(AshEvents.FRAME);
});
return EzspStatus.SUCCESS;
} else {
Expand Down
8 changes: 3 additions & 5 deletions src/adapter/ember/uart/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ export class AshParser extends Transform {
// emit the frame via 'data' event
const frame = data.subarray(0, position + 1);

setImmediate((): void => {
// expensive and very verbose, enable locally only if necessary
// logger.debug(`<<<< [FRAME raw=${frame.toString('hex')}]`, NS);
this.push(frame);
});
// expensive and very verbose, enable locally only if necessary
// logger.debug(`<<<< [FRAME raw=${frame.toString('hex')}]`, NS);
this.push(frame);

// remove the frame from internal buffer (set below)
data = data.subarray(position + 1);
Expand Down
16 changes: 11 additions & 5 deletions test/adapter/ember/ash.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {OpenOptions} from '@serialport/stream'
import {MockBinding, MockBindingInterface} from '@serialport/binding-mock'
import {MockBinding, MockPortBinding} from '@serialport/binding-mock'
import {
EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX,
EZSP_FRAME_CONTROL_COMMAND,
Expand All @@ -14,7 +14,13 @@ import {EzspStatus} from "../../../src/adapter/ember/enums";
import {EzspBuffer} from "../../../src/adapter/ember/uart/queues";
import {UartAsh} from "../../../src/adapter/ember/uart/ash";
import {EZSP_HOST_RX_POOL_SIZE, TX_POOL_BUFFERS} from "../../../src/adapter/ember/uart/consts";
import {RECD_RSTACK_BYTES, SEND_RST_BYTES, NCP_ACK_FIRST, adapterSONOFFDongleE, ASH_ACK_FIRST} from "./consts";
import {
RECD_RSTACK_BYTES,
SEND_RST_BYTES,
SEND_ACK_FIRST_BYTES,
adapterSONOFFDongleE,
ASH_ACK_FIRST_BYTES,
} from "./consts";
import {EzspBuffalo} from "../../../src/adapter/ember/ezsp/buffalo.ts";
import {lowByte} from '../../../src/adapter/ember/utils/math';
import {EzspFrameID} from '../../../src/adapter/ember/ezsp/enums.ts';
Expand Down Expand Up @@ -58,7 +64,7 @@ const mockSerialPortErrorEvent = jest.fn();
const mocks = [mockSerialPortCloseEvent, mockSerialPortErrorEvent];

describe('Ember UART ASH Protocol', () => {
const openOpts: OpenOptions<MockBindingInterface> = {path: '/dev/ttyACM0', baudRate: 115200, binding: MockBinding};
const openOpts: OpenOptions<MockPortBinding> = {path: '/dev/ttyACM0', baudRate: 115200, binding: MockBinding};
/**
* Mock binding provides:
*
Expand Down Expand Up @@ -193,7 +199,7 @@ describe('Ember UART ASH Protocol', () => {
expect(sendExecSpy).toHaveBeenCalled();
await new Promise(setImmediate);// flush
//@ts-expect-error private
expect(uartAsh.serialPort.port.recording).toStrictEqual(Buffer.from([...SEND_RST_BYTES, ...ASH_ACK_FIRST]))
expect(uartAsh.serialPort.port.recording).toStrictEqual(Buffer.from([...SEND_RST_BYTES, ...ASH_ACK_FIRST_BYTES]))
expect(uartAsh.connected).toBeTruthy();
expect(uartAsh.counters.txAllFrames).toStrictEqual(2);// RST + ACK
expect(uartAsh.counters.txAckFrames).toStrictEqual(1);// post-RSTACK ACK
Expand Down Expand Up @@ -309,7 +315,7 @@ describe('Ember UART ASH Protocol', () => {
await Wait(10);

//@ts-expect-error private
uartAsh.serialPort.port.emitData(Buffer.from(NCP_ACK_FIRST));// just an ACK, doesn't matter what it is
uartAsh.serialPort.port.emitData(Buffer.from(SEND_ACK_FIRST_BYTES));// just an ACK, doesn't matter what it is

await Wait(10);// force wait new frame

Expand Down
44 changes: 41 additions & 3 deletions test/adapter/ember/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const SEND_RST_BYTES = [
188,// CRC low - 0xbc
AshReservedByte.FLAG,// 126 - 0x7e
];

/**
* Pre-decoding values.
*
Expand All @@ -33,25 +34,62 @@ export const RECD_RSTACK_BYTES = [
82,// CRC low - 0x52
AshReservedByte.FLAG,// 126 - 0x7e
];

/**
* ACK sent by NCP after first DATA frame received.
* ACK following first DATA frame sent.
*
* ACK(1)+
*/
export const NCP_ACK_FIRST = [
export const SEND_ACK_FIRST_BYTES = [
AshFrameType.ACK + 1,
0x60,// CRC High
0x59,// CRC Low
AshReservedByte.FLAG
];

/**
* ACK sent by ASH (Z2M) after RSTACK received.
*
* ACK(0)+
*/
export const ASH_ACK_FIRST = [
export const ASH_ACK_FIRST_BYTES = [
AshFrameType.ACK,
0x70,// CRC High
0x78,// CRC Low
AshReservedByte.FLAG
];

/**
* Pre-decoding values.
*
* [194, 2, 81, 168, 189, 126]
*
* c20251a8bd7e
*/
export const RECD_ERROR_ACK_TIMEOUT_BYTES = [
AshFrameType.ERROR,
ASH_VERSION,
NcpFailedCode.ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT,
0xA8,// CRC High
0xBD,// CRC Low
AshReservedByte.FLAG,
];

export const RCED_ERROR_WATCHDOG_BYTES = [
AshFrameType.ERROR,
ASH_VERSION,
NcpFailedCode.RESET_WATCHDOG,
0xD2,// CRC High
0x0A,// CRC Low
AshReservedByte.FLAG,
];

export const RCED_DATA_WITH_CRC_ERROR = Buffer.from('b658904124ab5593499cdd93623cd29874f5de5083f97b1e66efc9af417e', 'hex');

export const RCED_DATA_RETRY = Buffer.from('0eafb1a96b2a7d334fa674eb04aaa760499d4e27cdd9ce6192f2c46989fcfb817e', 'hex');

/** desiredProtocolVersion: 13 */
export const SEND_DATA_VERSION = Buffer.from('004221a8597c057e', 'hex');
/** protocolVersion: 13, stackType: 2, stackVersion: 29712 */
export const RCED_DATA_VERSION = Buffer.from('0142a1a8592805c6a8777e', 'hex');
export const RCED_DATA_VERSION_RES = [13, 2, 29712];
Loading

0 comments on commit 3ab71b4

Please sign in to comment.