zigbee-herdsman
Version:
An open source ZigBee gateway solution with node.js.
451 lines • 15.6 kB
TypeScript
import { EventEmitter } from "node:events";
import type { SerialPortOptions } from "../../tstype";
import { EzspStatus } from "../enums";
import { AshFrameType } from "./enums";
import { EzspFreeList, EzspQueue } from "./queues";
type UartAshCounters = {
/** DATA frame data fields bytes transmitted */
txData: number;
/** frames of all types transmitted */
txAllFrames: number;
/** DATA frames transmitted */
txDataFrames: number;
/** ACK frames transmitted */
txAckFrames: number;
/** NAK frames transmitted */
txNakFrames: number;
/** DATA frames retransmitted */
txReDataFrames: number;
/** ACK and NAK frames with nFlag 0 transmitted */
/** ACK and NAK frames with nFlag 1 transmitted */
txN1Frames: number;
/** frames cancelled (with ASH_CAN byte) */
txCancelled: number;
/** DATA frame data fields bytes received */
rxData: number;
/** frames of all types received */
rxAllFrames: number;
/** DATA frames received */
rxDataFrames: number;
/** ACK frames received */
rxAckFrames: number;
/** NAK frames received */
rxNakFrames: number;
/** retransmitted DATA frames received */
rxReDataFrames: number;
/** ACK and NAK frames with nFlag 0 received */
/** ACK and NAK frames with nFlag 1 received */
rxN1Frames: number;
/** frames cancelled (with ASH_CAN byte) */
rxCancelled: number;
/** frames with CRC errors */
rxCrcErrors: number;
/** frames with comm errors (with ASH_SUB byte) */
rxCommErrors: number;
/** frames shorter than minimum */
rxTooShort: number;
/** frames longer than maximum */
rxTooLong: number;
/** frames with illegal control byte */
rxBadControl: number;
/** frames with illegal length for type of frame */
rxBadLength: number;
/** frames with bad ACK numbers */
rxBadAckNumber: number;
/** DATA frames discarded due to lack of buffers */
rxNoBuffer: number;
/** duplicate retransmitted DATA frames */
rxDuplicates: number;
/** DATA frames received out of sequence */
rxOutOfSequence: number;
/** received ACK timeouts */
rxAckTimeouts: number;
};
/** max frames sent without being ACKed (1-7) */
export declare const CONFIG_TX_K = 3;
interface UartAshEventMap {
fatalError: [status: EzspStatus];
frame: [];
}
/**
* ASH Protocol handler.
*/
export declare class UartAsh extends EventEmitter<UartAshEventMap> {
private readonly portOptions;
private serialPort?;
private socketPort?;
private writer;
private parser;
/** True when serial/socket is currently closing. */
private closing;
/** time ackTimer started: 0 means not ready uint16_t */
private ackTimer;
/** time used to check ackTimer expiry (msecs) uint16_t */
private ackPeriod;
/** not ready timer (16 msec units). Set to (now + config.nrTime) when started. uint8_t */
private nrTimer;
/** frame decode in progress */
private decodeInProgress;
/** true when preceding byte was escaped */
private encodeEscFlag;
/** byte to send after ASH_ESC uint8_t */
private encodeFlip;
/** uint16_t */
private encodeCrc;
/** encoder state: 0 = control/data bytes, 1 = crc low byte, 2 = crc high byte, 3 = flag. uint8_t */
private encodeState;
/** bytes remaining to encode. uint8_t */
private encodeCount;
/** bytes in frame, plus CRC, clamped to limit +1: high values also used to record certain errors. uint8_t */
private decodeLen;
/** ASH_FLIP if previous byte was ASH_ESC. uint8_t */
private decodeFlip;
/** a 2 byte queue to avoid outputting crc bytes. uint8_t */
private decodeByte1;
/** at frame end, they contain the received crc. uint8_t */
private decodeByte2;
/** uint16_t */
private decodeCrc;
/** outgoing short frames */
private txSHBuffer;
/** incoming short frames */
private rxSHBuffer;
/** bit flags for top-level logic. uint16_t */
private flags;
/** frame ack'ed from remote peer. uint8_t */
private ackRx;
/** frame ack'ed to remote peer. uint8_t */
private ackTx;
/** next frame to be transmitted. uint8_t */
private frmTx;
/** next frame to be retransmitted. uint8_t */
private frmReTx;
/** next frame expected to be rec'd. uint8_t */
private frmRx;
/** frame at retx queue's head. uint8_t */
private frmReTxHead;
/** consecutive timeout counter. uint8_t */
private timeouts;
/** rec'd DATA frame buffer. uint8_t */
private rxDataBuffer?;
/** rec'd frame length. uint8_t */
private rxLen;
/** tx frame offset. uint8_t */
private txOffset;
counters: UartAshCounters;
/**
* Errors reported by the NCP.
* The `NcpFailedCode` from the frame reporting this is logged before this is set to make it clear where it failed:
* - The NCP sent an ERROR frame during the initial reset sequence (before CONNECTED state)
* - The NCP sent an ERROR frame
* - The NCP sent an unexpected RSTACK
*/
private ncpError;
/** Errors reported by the Host. */
private hostError;
/** sendExec() state variable */
private sendState;
/** NCP is enabled to sleep, set by EZSP, not supported atm, always false */
ncpSleepEnabled: boolean;
/**
* Set when the ncp has indicated it has a pending callback by seting the callback flag in the frame control byte
* or (uart version only) by sending an an ASH_WAKE byte between frames.
*/
ncpHasCallbacks: boolean;
/** Transmit buffers */
private readonly txPool;
readonly txQueue: EzspQueue;
readonly reTxQueue: EzspQueue;
readonly txFree: EzspFreeList;
/** Receive buffers */
private readonly rxPool;
readonly rxQueue: EzspQueue;
readonly rxFree: EzspFreeList;
constructor(options: SerialPortOptions);
/**
* Check if port is valid, open, and not closing.
*/
get portOpen(): boolean;
/**
* Get max wait time before response is considered timed out.
*/
get responseTimeout(): number;
/**
* Indicates if the host is in the Connected state.
* If not, the host and NCP cannot exchange DATA frames.
* Note that this function does not actively confirm that communication with NCP is healthy, but simply returns its last known status.
*
* @returns
* - true - host and NCP can exchange DATA frames
* - false - host and NCP cannot now exchange DATA frames
*/
get connected(): boolean;
/**
* Has nothing to do...
*/
get idle(): boolean;
/**
* Init the serial or socket port and hook parser/writer.
* NOTE: This is the only function that throws/rejects in the ASH layer (caught by resetNcp and turned into an EzspStatus).
*/
private initPort;
/**
* Handle port closing
* @param err A boolean for Socket, an Error for serialport
*/
private onPortClose;
/**
* Handle port error
* @param error
*/
private onPortError;
/**
* Handle received frame from AshParser.
* @param buf
*/
private onFrame;
/**
* Initializes the ASH protocol, and waits until the NCP finishes rebooting, or a non-recoverable error occurs.
*
* @returns
* - EzspStatus.SUCCESS
* - EzspStatus.HOST_FATAL_ERROR
* - EzspStatus.ASH_NCP_FATAL_ERROR)
*/
start(): Promise<EzspStatus>;
/**
* Stops the ASH protocol - flushes and closes the serial port, clears all queues, stops timers, etc.
*/
stop(): Promise<void>;
/**
* Close port and remove listeners.
* Does nothing if port not defined/open.
*/
closePort(): Promise<void>;
/**
* Initializes the ASH serial port and (if enabled) resets the NCP.
* The method used to do the reset is specified by the the host configuration parameter resetMethod.
*
* When the reset method is sending a RST frame, the caller should retry NCP resets a few times if it fails.
*
* @returns
* - EzspStatus.SUCCESS
* - EzspStatus.HOST_FATAL_ERROR
*/
resetNcp(): Promise<EzspStatus>;
/**
* Adds a DATA frame to the transmit queue to send to the NCP.
* Frames that are too long or too short will not be sent, and frames will not be added to the queue
* if the host is not in the Connected state, or the NCP is not ready to receive a DATA frame or if there
* is no room in the queue;
*
* @param len length of data field
* @param inBuf array containing the data to be sent
*
* @returns
* - EzspStatus.SUCCESS
* - EzspStatus.NO_TX_SPACE
* - EzspStatus.DATA_FRAME_TOO_SHORT
* - EzspStatus.DATA_FRAME_TOO_LONG
* - EzspStatus.NOT_CONNECTED
*/
send(len: number, inBuf: Buffer): EzspStatus;
/**
* Manages outgoing communication to the NCP, including DATA frames as well as the frames used for
* initialization and error detection and recovery.
*/
sendExec(): void;
/**
* Retrieve a frame and accept, reTx, reject, fail based on type & validity in current state.
* @returns
* - EzspStatus.SUCCESS On valid RSTACK or valid DATA frame.
* - EzspStatus.ASH_IN_PROGRESS
* - EzspStatus.NO_RX_DATA
* - EzspStatus.NO_RX_SPACE
* - EzspStatus.HOST_FATAL_ERROR
* - EzspStatus.ASH_NCP_FATAL_ERROR
*/
private receiveFrame;
/**
* If the last control byte received was a DATA control, and we are connected and not already in the reject condition,
* then send a NAK and set the reject condition.
*/
private rejectFrame;
/**
* Retrieve and process serial bytes.
* @returns
*/
private readFrame;
/**
*
*/
private freeAllocatedRxBuffer;
/**
*
*/
private scrubReTxQueue;
/**
* If not already retransmitting, and there are unacked frames, start retransmitting after the last frame that was acked.
*/
private startRetransmission;
/**
* Check free rx buffers to see whether able to receive DATA frames: set or clear NR flag appropriately.
* Inform ncp of our status using the nFlag in ACKs and NAKs.
* Note that not ready status must be refreshed if it persists beyond a maximum time limit.
*/
private dataFrameFlowControl;
/**
* Sets a fatal error state at the Host level.
* @param error
* @returns EzspStatus.HOST_FATAL_ERROR
*/
private hostDisconnect;
/**
* Sets a fatal error state at the NCP level. Will require a reset.
* @param error
* @returns EzspStatus.ASH_NCP_FATAL_ERROR
*/
private ncpDisconnect;
/**
* Same as randomizeArray(0, buffer, len).
* Returns buffer as-is if randomize is OFF.
* @param buffer IN/OUT
* @param len
*/
randomizeBuffer(buffer: Buffer, len: number): void;
/**
* Randomizes array contents by XORing with an 8-bit pseudo random sequence.
* This reduces the likelihood that byte-stuffing will greatly increase the size of the payload.
* (This could happen if a DATA frame contained repeated instances of the same reserved byte value.)
*
* @param seed zero initializes the random sequence a non-zero value continues from a previous invocation
* @param buf IN/OUT pointer to the array whose contents will be randomized
* @param len number of bytes in the array to modify
* @returns last value of the sequence.
* If a buffer is processed in two or more chunks, as with linked buffers,
* this value should be passed back as the value of the seed argument
*/
randomizeArray(seed: number, buf: Buffer, len: number): number;
/**
* Get the frame type from the control byte and validate it against the frame length.
* @param control
* @param len Frame length
* @returns AshFrameType.INVALID if bad control/length otherwise the frame type.
*/
getFrameType(control: number, len: number): AshFrameType;
/**
* Encode byte for sending.
* @param len Start a new frame if non-zero
* @param byte
* @returns outByte
*/
private encodeByte;
/**
* Stuff byte as defined by ASH protocol.
* @param byte
* @returns
*/
private encodeStuffByte;
/**
* Decode received byte.
* @param byte
* @param inByte IN/OUT
* @param inLen IN/OUT
* @returns [EzspStatus, outByte, outLen]
* - EzspStatus.ASH_IN_PROGRESS
* - EzspStatus.ASH_COMM_ERROR
* - EzspStatus.ASH_BAD_CRC
* - EzspStatus.ASH_TOO_SHORT
* - EzspStatus.ASH_TOO_LONG
* - EzspStatus.SUCCESS
* - EzspStatus.ASH_CANCELLED
* - EzspStatus.ASH_ERROR_XON_XOFF
*/
private decodeByte;
/**
* Starts the Not Ready timer
*
* On the host, this times nFlag refreshing when the host doesn't have room for callbacks for a prolonged period.
*
* On the NCP, if this times out the NCP resumes sending callbacks.
*/
private startNrTimer;
/**
* Stop Not Ready timer (set to 0).
*/
private stopNrTimer;
/**
* Tests whether the Not Ready timer has expired or has stopped. If expired, it is stopped.
*
* @returns true if the Not Ready timer has expired or stopped
*/
private nrTimerHasExpired;
/**
* Indicates whether or not Not Ready timer is currently running.
*
* @return True if nrTime == 0
*/
private nrTimerIsNotRunning;
/**
* Sets the acknowledgement timer period (in msec) and stops the timer.
*/
private setAckPeriod;
/**
* Sets the acknowledgement timer period (in msec), and starts the timer running.
*/
private setAndStartAckTimer;
/**
* Adapts the acknowledgement timer period to the observed ACK delay.
* If the timer is not running, it does nothing.
* If the timer has expired, the timeout period is doubled.
* If the timer has not expired, the elapsed time is fed into simple
*
* IIR filter:
* T[n+1] = (7*T[n] + elapsedTime) / 8
*
* The timeout period, ackPeriod, is limited such that:
* config.ackTimeMin <= ackPeriod <= config.ackTimeMax.
*
* The acknowledgement timer is always stopped by this function.
*
* @param expired true if timer has expired
*/
private adjustAckPeriod;
/**
* Sets ACK Timer to the specified period and starts it running.
*/
private startAckTimer;
/**
* Stops and clears ACK Timer.
*/
private stopAckTimer;
/**
* Indicates whether or not ACK Timer has expired.
* If the timer is stopped (0) then it is not expired.
*
* @returns
*/
private ackTimerHasExpired;
/**
* Indicates whether or not ACK Timer is currently running (!= 0).
* The timer may be running even if expired.
*/
private ackTimerIsNotRunning;
/**
* Increase counters based on frame type and direction.
* @param sent True if frame being sent, false if being received.
*/
private countFrame;
/**
* Read and clear ASH layer counters in the same manner as the NCP ones.
* @returns
*/
readAndClearCounters(): number[];
/**
* Log counters (pretty-formatted) as they are since last time they were cleared.
* Used on ASH layer stop to get 'pre-stop state'.
*/
private logCounters;
}
export {};
//# sourceMappingURL=ash.d.ts.map