UNPKG

zigbee-herdsman

Version:

An open source ZigBee gateway solution with node.js.

451 lines 15.6 kB
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