UNPKG

packet_device

Version:

`packet_device` is a Node.js library for secure, packetized communication with microcontrollers (Arduino, ESP32, etc.) running the [Packet Device Arduino library](https://github.com/Pallob-Gain/packet_device).

705 lines (591 loc) 27 kB
const Struct = require('./struct.V4.js'); const DataEndPusherExtractor = require('./DataEndPusherExtractor.V3.js'); const { crc16Ccitt } = require('./crc-verification.js'); const TRANSFER_DATA_BUFFER_SIG = 0x2A; const BUFFER_TEXT_RESPNOSE = 0x5E; const BUFFER_PARAM_RESPNOSE = 0x5F; const BUFFER_ARRY_RESPNOSE = 0x60; const BUFFER_JSON_RESPONSE_START = 0x7B; const BUFFER_JSON_RESPONSE_END = 0x7D; const DATA_TYPE_UINT64_T = 1; const DATA_TYPE_INT64_T = 2; const DATA_TYPE_UINT32_T = 3; const DATA_TYPE_INT32_T = 4; const DATA_TYPE_UINT16_T = 5; const DATA_TYPE_INT16_T = 6; const DATA_TYPE_UINT8_T = 7; const DATA_TYPE_INT8_T = 8; const DATA_TYPE_INT = 9; const DATA_TYPE_UINT = 10; const DATA_TYPE_FLOAT = 11; const DATA_TYPE_DOUBLE = 12; const DATA_TYPE_LONG = 13; const DATA_TYPE_ULONG = 14; const DATA_TYPE_STRING = 15; const DATA_TYPE_BOOL = 16; const DATA_TYPE_NULL = 17; const DATA_TYPE_VOID = 0; const STRUCT_EQUVALENT_TYPE = { uint64_t: DATA_TYPE_UINT64_T, int64_t: DATA_TYPE_INT64_T, uint32_t: DATA_TYPE_UINT32_T, int32_t: DATA_TYPE_INT32_T, uint16_t: DATA_TYPE_UINT16_T, int16_t: DATA_TYPE_INT16_T, uint8_t: DATA_TYPE_UINT8_T, int8_t: DATA_TYPE_INT8_T, int: DATA_TYPE_INT, uint: DATA_TYPE_UINT, float: DATA_TYPE_FLOAT, double: DATA_TYPE_DOUBLE, long: DATA_TYPE_LONG, ulong: DATA_TYPE_ULONG, string: DATA_TYPE_STRING, bool: DATA_TYPE_BOOL, null: DATA_TYPE_NULL, void: DATA_TYPE_VOID }; // Mapping JavaScript Typed Arrays to Type IDs const TYPED_ARRAY_MAP = new Map([ [BigUint64Array, DATA_TYPE_UINT64_T], [BigInt64Array, DATA_TYPE_INT64_T], [Uint32Array, DATA_TYPE_UINT32_T], [Int32Array, DATA_TYPE_INT32_T], [Uint16Array, DATA_TYPE_UINT16_T], [Int16Array, DATA_TYPE_INT16_T], [Uint8Array, DATA_TYPE_UINT8_T], [Int8Array, DATA_TYPE_INT8_T], [Float32Array, DATA_TYPE_FLOAT], [Float64Array, DATA_TYPE_DOUBLE], [Uint8ClampedArray, DATA_TYPE_UINT8_T], // Clamped version of Uint8Array ]); // Mapping JavaScript Primitive Types to Type IDs const TYPE_MAP = new Map([ ["bigint", [DATA_TYPE_UINT64_T, DATA_TYPE_INT64_T]], // BigInt types ["number", [DATA_TYPE_UINT32_T, DATA_TYPE_INT32_T, DATA_TYPE_UINT16_T, DATA_TYPE_INT16_T, DATA_TYPE_UINT8_T, DATA_TYPE_INT8_T, DATA_TYPE_INT, DATA_TYPE_UINT, DATA_TYPE_FLOAT, DATA_TYPE_DOUBLE, DATA_TYPE_LONG, DATA_TYPE_ULONG]], // Various number types ["string", [DATA_TYPE_STRING]], // Strings ["boolean", [DATA_TYPE_BOOL]], // Booleans ["object", [DATA_TYPE_NULL]], // Null is an object in JS ["undefined", [DATA_TYPE_VOID]] // Undefined/Void ]); const TYPE_SIZE_MAP = new Map([ [DATA_TYPE_UINT64_T, 8], // BigUint64 (8 bytes) [DATA_TYPE_INT64_T, 8], // BigInt64 (8 bytes) [DATA_TYPE_UINT32_T, 4], // Uint32 (4 bytes) [DATA_TYPE_INT32_T, 4], // Int32 (4 bytes) [DATA_TYPE_UINT16_T, 2], // Uint16 (2 bytes) [DATA_TYPE_INT16_T, 2], // Int16 (2 bytes) [DATA_TYPE_UINT8_T, 1], // Uint8 (1 byte) [DATA_TYPE_INT8_T, 1], // Int8 (1 byte) [DATA_TYPE_FLOAT, 4], // Float32 (4 bytes) [DATA_TYPE_DOUBLE, 8], // Float64 (8 bytes) [DATA_TYPE_LONG, 4], // Assuming 4 bytes for long (platform-dependent) [DATA_TYPE_ULONG, 4], // Assuming 4 bytes for unsigned long (platform-dependent) [DATA_TYPE_STRING, null], // Strings are variable-sized [DATA_TYPE_BOOL, 1], // Boolean (stored as 1 byte) [DATA_TYPE_NULL, 0], // Null has no size [DATA_TYPE_VOID, 1] // Void has no size ]); const type_conversion = { [DATA_TYPE_UINT64_T]: (buff, size) => buff.readUInt64LE(0), [DATA_TYPE_INT64_T]: (buff, size) => buff.readInt64LE(0), [DATA_TYPE_UINT32_T]: (buff, size) => buff.readUInt32LE(0), [DATA_TYPE_INT32_T]: (buff, size) => buff.readInt32LE(0), [DATA_TYPE_UINT16_T]: (buff, size) => buff.readUInt16LE(0), [DATA_TYPE_INT16_T]: (buff, size) => buff.readInt16LE(0), [DATA_TYPE_UINT8_T]: (buff, size) => buff.readUInt8(0), [DATA_TYPE_INT8_T]: (buff, size) => buff.readInt8(0), [DATA_TYPE_INT]: (buff, size) => size == 2 ? buff.readInt16LE(0) : buff.readInt32LE(0), [DATA_TYPE_UINT]: (buff, size) => size == 2 ? buff.readUInt16LE(0) : buff.readUInt32LE(0), [DATA_TYPE_FLOAT]: (buff, size) => buff.readFloatLE(0), [DATA_TYPE_DOUBLE]: (buff, size) => buff.readDoubleLE(0), [DATA_TYPE_LONG]: (buff, size) => buff.readBigInt64LE(0), [DATA_TYPE_ULONG]: (buff, size) => buff.readBigUInt64LE(0), [DATA_TYPE_STRING]: (buff, size) => buff.toString(), [DATA_TYPE_BOOL]: (buff, size) => buff.readUInt8(0) != 0, [DATA_TYPE_NULL]: (buff, size) => null, [DATA_TYPE_VOID]: (buff, size) => buff.subarray(0, size), }; // Function to get the type size const getTypeSize = (typeId) => { return TYPE_SIZE_MAP.has(typeId) ? TYPE_SIZE_MAP.get(typeId) : null; } const getTypeIdExtended = (value) => { if (value === null) return DATA_TYPE_NULL; // Null case if (value === undefined) return DATA_TYPE_VOID; // Undefined case // Check for Typed Arrays for (const [typedArray, typeId] of TYPED_ARRAY_MAP) { if (value instanceof typedArray) return typeId; } // Normal JavaScript types const jsType = typeof value; return TYPE_MAP.has(jsType) ? TYPE_MAP.get(jsType)[0] : null; } const BufferTextResponseHeader = Struct.makeType({ signeture: Struct.type.byte, signeture_type: Struct.type.byte, data_size_msb: Struct.type.byte, data_size_lsb: Struct.type.byte, }); const BufferParamResponseHeader = Struct.makeType({ signeture: Struct.type.byte, signeture_type: Struct.type.byte, data_type: Struct.type.byte, pram_len: Struct.type.byte, data_size_msb: Struct.type.byte, data_size_lsb: Struct.type.byte, }); const BufferArrayResponseHeader = Struct.makeType({ signeture: Struct.type.byte, signeture_type: Struct.type.byte, data_type: Struct.type.byte, type_size: Struct.type.byte, pram_len: Struct.type.byte, data_size_msb: Struct.type.byte, data_size_lsb: Struct.type.byte, }); ///buff_signeture(1 byte)+data_signeture(1 byte) +data_len(2 bytes) const TRANSFER_DATA_TEXT_HEADER_LEN = Struct.sizeOf(BufferTextResponseHeader); //buff_signeture(1 byte)+data_signeture(1 byte)+data_type(1 byte)+pram_len(1 bytes)+data_len(2 bytes) const TRANSFER_DATA_PARAMS_HEADER_LEN = Struct.sizeOf(BufferParamResponseHeader); //buff_signeture(1 byte)+data_signeture(1 byte)+type(1 bytes)+type_size(1 bytes)+pram_len(1 bytes)+data_size(2 bytes) const TRANSFER_DATA_ARRAY_HEADER_LEN = Struct.sizeOf(BufferArrayResponseHeader); const buffer_response_parsing = { [BUFFER_TEXT_RESPNOSE]: { parse: (buff) => { //buff_signeture(1 byte)+data_signeture(1 byte)+data_len(1 bytes)+data_buff(data_len bytes) if (buff.length < TRANSFER_DATA_TEXT_HEADER_LEN) throw new Error('Invalid data length!'); let data_len_msb = buff[2]; let data_len_lsb = buff[3]; let data_len = ((data_len_msb << 8) | data_len_lsb) & 0xFFFF; if (buff.length < data_len + TRANSFER_DATA_TEXT_HEADER_LEN) throw new Error('Data is not sufficient in length!'); return buff.subarray(TRANSFER_DATA_TEXT_HEADER_LEN, TRANSFER_DATA_TEXT_HEADER_LEN + data_len).toString(); } }, [BUFFER_PARAM_RESPNOSE]: { parse: (buff) => { //buff_signeture(1 byte)+data_signeture(1 byte)+string_type(1 bytes)+pram_len(1 bytes)+data_len(1 bytes)+prams_buff(pram_len bytes)+data_buff(data_len bytes) if (buff.length < TRANSFER_DATA_PARAMS_HEADER_LEN) throw new Error('Invalid data length!'); let data_type = buff[2]; let pram_len = buff[3]; let data_len_msb = buff[4]; let data_len_lsb = buff[5]; let data_len = ((data_len_msb << 8) | data_len_lsb) & 0xFFFF; if (buff.length < pram_len + data_len + TRANSFER_DATA_PARAMS_HEADER_LEN) throw new Error('Data is not sufficient in length!'); let [prams_buff, data_buff] = [{ start: TRANSFER_DATA_PARAMS_HEADER_LEN, len: pram_len }, { start: TRANSFER_DATA_PARAMS_HEADER_LEN + pram_len, len: data_len }].map(({ start, len }) => buff.subarray(start, start + len)); return { [prams_buff.toString()]: type_conversion[data_type](data_buff, data_len) }; } }, [BUFFER_ARRY_RESPNOSE]: { parse: (buff) => { //buff_signeture(1 byte)+data_signeture(1 byte)+type(1 bytes)+type_size(1 bytes)+pram_len(1 bytes)+data_size(1 bytes)+prams_buff+data_buff //console.log('Array data parsing'); if (buff.length < TRANSFER_DATA_ARRAY_HEADER_LEN) throw new Error('Invalid data length!'); let type = buff[2]; //console.log('Type:',type); if (!(type in type_conversion)) throw new Error('Invalid data type!'); let type_conv = type_conversion[type]; let type_size = buff[3]; let pram_len = buff[4]; let data_size_msb = buff[5]; let data_size_lsb = buff[6]; let data_size = ((data_size_msb << 8) | data_size_lsb) & 0xFFFF; let data_len = type_size * data_size; //console.log('Info:',{type_size,pram_len,data_len,data_size,data_len}); if (buff.length < pram_len + data_len + TRANSFER_DATA_ARRAY_HEADER_LEN) throw new Error('Data is not sufficient in length!'); let prams_buff = buff.subarray(TRANSFER_DATA_ARRAY_HEADER_LEN, TRANSFER_DATA_ARRAY_HEADER_LEN + pram_len); let data_pos_to = TRANSFER_DATA_ARRAY_HEADER_LEN + pram_len + data_len; let data_array = []; for (let i = TRANSFER_DATA_ARRAY_HEADER_LEN + pram_len; i < data_pos_to; i += type_size) { let data_buff = buff.subarray(i, i + type_size); data_array.push(type_conv(data_buff, type_size)); } //console.log('data:',prams_buff.toString(),data_array); return { [prams_buff.toString()]: data_array }; } } }; const buffer_response_maker = { [BUFFER_TEXT_RESPNOSE]: (data) => { let data_holder = new Struct(BufferTextResponseHeader); data_holder.set('signeture', TRANSFER_DATA_BUFFER_SIG); data_holder.set('signeture_type', BUFFER_TEXT_RESPNOSE); data_holder.set('data_size_msb', (data.length >> 8) & 0xFF); data_holder.set('data_size_lsb', (data.length & 0xFF)); return Buffer.concat([ data_holder.ref(), Buffer.from(data) ]); }, [BUFFER_PARAM_RESPNOSE]: (param, data, force_type = null) => { let data_type = force_type === null ? getTypeIdExtended(data) : force_type; if (data_type === null) throw new Error('Data is not valid'); let data_holder = new Struct(BufferParamResponseHeader); data_holder.set('signeture', TRANSFER_DATA_BUFFER_SIG); data_holder.set('signeture_type', BUFFER_PARAM_RESPNOSE); data_holder.set('data_type', data_type); data_holder.set('pram_len', param.length); data_holder.set('data_size_msb', (data.length >> 8) & 0xFF); data_holder.set('data_size_lsb', (data.length & 0xFF)); //console.log('length:',data.length,'msb:',(data.length >> 8) & 0xFF,'lsb:',(data.length & 0xFF)); return Buffer.concat([ data_holder.ref(), Buffer.from(param), Buffer.from(data.buffer) ]); }, [BUFFER_ARRY_RESPNOSE]: (param, data, typed_array = true) => { if (typed_array) { let data_type = getTypeIdExtended(data); let type_size = getTypeSize(data_type); if (data_type === null || type_size === null) throw new Error('Data is not valid'); let data_holder = new Struct(BufferArrayResponseHeader); data_holder.set('signeture', TRANSFER_DATA_BUFFER_SIG); data_holder.set('signeture_type', BUFFER_ARRY_RESPNOSE); data_holder.set('data_type', data_type); data_holder.set('type_size', type_size); data_holder.set('pram_len', param.length); data_holder.set('data_size_msb', (data.length >> 8) & 0xFF); data_holder.set('data_size_lsb', (data.length & 0xFF)); return Buffer.concat([ data_holder.ref(), Buffer.from(param), Buffer.from(data.buffer) ]); } else { //console.log('Array Struct Transfer'); if (!(Array.isArray(data) && data.length > 0 && typeof data[0] == 'object' && data[0] instanceof Struct)) throw new Error('Data is not valid'); if (data.length == 0) throw new Error('Data is empty.'); let data_type = DATA_TYPE_VOID; //struct array transfer data type is void let type_size = data[0].size(); if (!data.every(row => typeof row == 'object' && row instanceof Struct && row.size() == type_size)) throw new Error('Data array is invalid.'); let data_holder = new Struct(BufferArrayResponseHeader); data_holder.set('signeture', TRANSFER_DATA_BUFFER_SIG); data_holder.set('signeture_type', BUFFER_ARRY_RESPNOSE); data_holder.set('data_type', data_type); data_holder.set('type_size', type_size); data_holder.set('pram_len', param.length); data_holder.set('data_size_msb', (data.length >> 8) & 0xFF); data_holder.set('data_size_lsb', (data.length & 0xFF)); let trans_data = Buffer.concat(data.map(row => row.ref())); //console.log('type_size:',type_size); //console.log('data_type:',data_type); //console.log('pram_len:',param.length); //console.log('data_size',data.length); //console.log('trans_data:',trans_data.length,trans_data); return Buffer.concat([ data_holder.ref(), Buffer.from(param), trans_data ]); } }, }; module.exports = class PacketDevice { attached_serial_dev; delimiter = ''; onDataCb = []; dataReceiverHolder = []; static Type = Object.fromEntries(Object.entries(Struct.type).map(([name, type]) => { return [name, { type: STRUCT_EQUVALENT_TYPE[name], details: Struct.makeType({ value: type }) }]; })); constructor(delimiter = '\r\n') { this.delimiter = delimiter; this.dataParser = new DataEndPusherExtractor(delimiter); } open(serial_dev) { this.attached_serial_dev = serial_dev; serial_dev.on('data', (buff) => { //console.log('data:',buff,'len:',buff.length); this.dataReceiver(buff); }); } close() { this.attached_serial_dev = null; //release device } write(buffer) { if (this.attached_serial_dev && 'write' in this.attached_serial_dev) { return this.attached_serial_dev.write(buffer); } else throw new Error('Device is not opened!'); } writePacket(param, data) { let packet = this.dataPacket(param, data, false); if (packet === null) throw new Error('Invalid data!'); return this.write(packet); } println(data) { return this.write(Buffer.concat([Buffer.from(data), Buffer.from(this.delimiter)])); } static getTypedValue(type, value) { //console.log(type,typeof type); if (typeof type == 'object' && 'details' in type) { let data_holder = new Struct(type.details); data_holder.set('value', value); return { type: type.type, value: data_holder }; } else throw new Error('Invalid type!'); } static getDataCrc(buff) { return crc16Ccitt(buff); } static checkCrcValidity(buff) { let len = buff.length; if (len <= 2) return null; let data = buff.subarray(0, len - 2); let crc = PacketDevice.getDataCrc(data); if (crc === null) return null; let crc_msb = buff[len - 2] & 0xFF; let crc_lsb = buff[len - 1] & 0xFF; let check_crc = (crc_msb << 8) | crc_lsb; let status = check_crc == crc ? data : null; // if(!status){ // let s=buff.toString(); // console.log(`data:`,buff,s,s.length); // console.log(`check_crc: ${check_crc}, crc: ${crc}`); // } return status; } static jsonParse(buff) { return JSON.parse(buff.toString()); } static dataParse(buff) { //console.log('data parsing:',buff); //console.log('data first:',buff[0],buff[buff.length-1]); if (buff[0] == BUFFER_JSON_RESPONSE_START && buff[buff.length - 1] == BUFFER_JSON_RESPONSE_END) return PacketDevice.jsonParse(buff); else if (buff[0] == TRANSFER_DATA_BUFFER_SIG && buff.length > 2 && buff[1] in buffer_response_parsing) { return buffer_response_parsing[buff[1]].parse(buff); } throw new Error('The received data is not parseable!'); } static bufferGenerate(param, data) { if (data !== null) { //console.log(param,typeof data=='object',data instanceof Struct); if (ArrayBuffer.isView(data)) { //typed array return buffer_response_maker[BUFFER_ARRY_RESPNOSE](param, data); } else if (Array.isArray(data) && data.length > 0 && typeof data[0] == 'object' && data[0] instanceof Struct) { //Structed array return buffer_response_maker[BUFFER_ARRY_RESPNOSE](param, data, false); } else if (typeof data == 'object' && 'value' in data && data.value instanceof Struct) { //value type, a single typed data send return buffer_response_maker[BUFFER_PARAM_RESPNOSE](param, data.value.ref(), data.type); } else if (typeof data == 'object' && data instanceof Struct) { //value type: a complete struct send return buffer_response_maker[BUFFER_PARAM_RESPNOSE](param, data.ref(), DATA_TYPE_VOID); } else throw new Error('Data is invalid!'); } else if (typeof param == 'string') { //text type return buffer_response_maker[BUFFER_TEXT_RESPNOSE](param); } return null; } dataPacket(param, data, ending = false) { let buff = PacketDevice.bufferGenerate(param, data); if (buff === null) return null; let crc = PacketDevice.getDataCrc(buff); if (ending) { //end with delimeter return Buffer.concat([ buff, Buffer.from([ crc >> 8 & 0xFF, crc & 0xFF, ]), Buffer.from(this.delimiter) ]); } else { //transfer with buffer return Buffer.concat([ this.dataParser.updatePacketLength(buff.length + 2), buff, Buffer.from([ crc >> 8 & 0xFF, crc & 0xFF, ]) ]); } } setBufferMode(ending = true) { this.dataParser.setBufferMode(ending); } clearBufferQueue() { this.dataParser.clear(); } checkDeviceData(buff_data) { let buff_packets = this.dataParser.pushOut(buff_data); if (buff_packets == null || buff_packets.length == 0) return null; //console.log(buff_packets); return buff_packets.map(buff => { //console.log('Checking packet length:', buff.length); return PacketDevice.checkCrcValidity(buff); }).filter(t => t); } dataReceiveHandel(err, data) { //that function will call when data received and it will receive every of the packets separately one by one for (let cb of this.onDataCb) { if (cb(err, data) === true) { //if that is block of flow then return here not processing any other return; } } while (this.dataReceiverHolder.length > 0) { let callback = this.dataReceiverHolder.shift(); if (callback(err, data) === true) { //if that is block of flow then return here not processing any other return; } } } dataReceiver(buff) { try { let data_packets = this.checkDeviceData(buff); //console.log('Received packets:', data_packets ? data_packets.length : 0); //if(buff.toString().indexOf('payload')>=0)console.log('data--->',data_packets.map(v=>v.toString())); if (!data_packets) return; //data is not completed if (data_packets.length == 0) return this.dataReceiveHandel(new Error('CRC validity error detected.')); for (let data of data_packets) { //console.log('Processing packet length:', data.length); this.dataReceiveHandel(null, data); } } catch (err) { this.dataReceiveHandel(err); } } onData(callback) { if (typeof callback == 'function') this.onDataCb.push(callback); } removeOnData(callback = null) { if (callback === null) { this.onDataCb = []; //clear } else { let f = this.onDataCb.indexOf(callback); if (f != -1) this.onDataCb.splice(f, 1); //delete } } waitToReceiveData(timeout = 1500, block_flow = true) { return new Promise((accept, reject) => { let cb = (err, data) => { clearTimeout(timeout_handeler); if (err) return reject(err); accept(data); return block_flow; }; let timeout_handeler = setTimeout(() => { let f = this.dataReceiverHolder.indexOf(cb); if (f != -1) { //console.log('cb clearing:'); this.dataReceiverHolder.splice(f, 1); } reject(new Error("Driver is not responding with in timeout")); }, timeout); this.dataReceiverHolder.push(cb); }); } async waitToReceiveJsonData(timeout = 1500, block_flow = true) { let data = await this.waitToReceiveData(timeout, block_flow); try { return PacketDevice.jsonParse(data); } catch (err) { throw new Error("Data parsing error because: " + data); } } async waitToReceiveParsedData(timeout = 1500, block_flow = true) { let data = await this.waitToReceiveData(timeout, block_flow); try { return PacketDevice.dataParse(data); } catch (err) { //console.log('Parse Error:',err); throw new Error("Data parsing error because: " + data); } } findUntill(checker_callback, timeout = 1500) { if (typeof checker_callback != 'function') throw new Error('Invalid checker callback!'); return new Promise((accept, reject) => { let result_cb = (err, result) => { clearTimeout(timeout_handeler); this.removeOnData(cb); if (err) reject(err); else accept(result); return true; //block flow } let cb = (err, data) => { //console.log('extra receiver'); if (err) return reject(err); try { let parsed_data = PacketDevice.dataParse(data); try { let check_result = checker_callback(parsed_data); if (check_result) return result_cb(null, check_result); } catch (err) { //if the checker callback have error then return that error return result_cb(err); } } catch (err) { //no error handel here for parsing error } }; let timeout_handeler = setTimeout(() => { this.removeOnData(cb); reject(new Error(`No valid response within ${timeout}ms.`)); }, timeout); this.onData(cb); }); } waitUntillFound(valid_data, timeout = 1500) { return new Promise((accept, reject) => { let result_cb = (result) => { clearTimeout(timeout_handeler); this.removeOnData(cb); accept(result); } let cb = (err, data) => { //console.log('extra receiver'); if (err) return reject(err); try { let parsed_data = PacketDevice.dataParse(data); if (Array.isArray(valid_data) && typeof parsed_data == 'object') { for (let key of valid_data) { if (key in parsed_data) return result_cb(parsed_data); } } else if (typeof parsed_data == typeof valid_data) { if (typeof valid_data == 'object') { for (let key in valid_data) { if (key in parsed_data && parsed_data[key] == valid_data[key]) return result_cb(parsed_data); } } else if (parsed_data == valid_data) return result_cb(parsed_data); } } catch (err) { //no error handel here } }; let timeout_handeler = setTimeout(() => { this.removeOnData(cb); reject(new Error(`No valid response within ${timeout}ms.`)); }, timeout); this.onData(cb); }); } }