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
JavaScript
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);
});
}
}