javascript
import {Buffer} from "buffer";
export const version="1.01.023"
export class BufferTKL {
static DUBM_PERIOD=86400*3650
static INVALID_NUM=-1000000000
static INVALID_STR="INVALID"
static PERIOD_DEFAULT=900;
static PERIOD_DUMB=86400*3650;
static PERIOD_TRIGGER="TRIGGER";
static PERIOD_MAX =86400*365
constructor(buffer) {
if (buffer==undefined) {this.buffer=Buffer.alloc(0);
}else if (!Buffer.isBuffer(buffer)) {this.buffer=Buffer.alloc(0);
}else{ this.buffer = buffer; }
this.size=this.buffer.length;
}
static getTypeInfo(type) {
const normalizedType = String(type).trim().toLowerCase();
let dataLen = 0;
let baseType = normalizedType;
let bitStart,bitEnd;
if (normalizedType.length > 3) {
if (normalizedType.startsWith("hex")) {
const endian=normalizedType.substring(3,5)
baseType = "hexbe";
if (endian==="le"){ baseType = "hexle"}
if (normalizedType.includes('e')) { dataLen = parseInt(normalizedType.substring(5), 10);
}else { dataLen = parseInt(normalizedType.substring(3), 10); }
} else if (normalizedType.startsWith("bcd")) {
const endian=normalizedType.substring(3,5)
baseType = "bcdbe";
if (endian==="le"){ baseType = "bcdle"; }
if (normalizedType.includes('e')) { dataLen = parseInt(normalizedType.substring(5), 10);
}else { dataLen = parseInt(normalizedType.substring(3), 10);}
}else if (normalizedType.startsWith("bit")) {
baseType = "bitbe";
const endian=normalizedType.substring(3,5)
if (endian==="le"){ baseType = "bitle"}
let valueList=[]
if (normalizedType.includes('e')) { valueList=normalizedType.substring(5).split("-");
}else { valueList=normalizedType.substring(3).split("-"); }
bitStart=Number(valueList[0])
bitEnd=Number(valueList[1])
dataLen = ((bitEnd/8)|0) +1;
}else if (normalizedType.startsWith("bool")) {
baseType = "bool";
dataLen=parseInt(normalizedType.substring(4), 10);
}else if (normalizedType.startsWith("boolean")) {
baseType = "boolean";
dataLen=parseInt(normalizedType.substring(7), 10);
}else if (normalizedType.startsWith("string")) {
baseType = "string";
dataLen = parseInt(normalizedType.substring(6), 10);
}
}
switch (baseType) {
case "int8": return { type: "Int8", dataLen: 1};
case "uint8": return { type: "UInt8", dataLen: 1 };
case "int16":
case "int16be": return { type: "Int16BE", dataLen: 2};
case "uint16":
case "uint16be": return { type: "UInt16BE", dataLen: 2};
case "int16le": return { type: "Int16LE", dataLen: 2};
case "uint16le": return { type: "UInt16LE", dataLen: 2};
case "int24":
case "int24be": return { type: "IntBE", dataLen: 3}; // Buffer 没有 readInt24,通常用 readIntBE(offset, 3)
case "uint24":
case "uint24be": return { type: "UIntBE", dataLen: 3};
case "int24le": return { type: "IntLE", dataLen: 3};
case "uint24le": return { type: "UIntLE", dataLen: 3};
case "int32":
case "int32be": return { type: "Int32BE", dataLen: 4};
case "uint32":
case "uint32be": return { type: "UInt32BE", dataLen: 4};
case "int32le": return { type: "Int32LE", dataLen: 4};
case "uint32le": return { type: "UInt32LE", dataLen: 4};
case "int40":
case "int40be": return { type: "IntBE", dataLen: 5};
case "uint40":
case "uint40be": return { type: "UIntBE", dataLen: 5};
case "int40le": return { type: "IntLE", dataLen: 5};
case "uint40le": return { type: "UIntLE", dataLen: 5};
case "int48":
case "int48be": return { type: "IntBE", dataLen: 6};
case "uint48":
case "uint48be": return { type: "UIntBE", dataLen: 6};
case "int48le": return { type: "IntLE", dataLen: 6};
case "uint48le": return { type: "UIntLE", dataLen: 6};
case "int56":
case "int56be": return { type: "IntBE", dataLen: 7};
case "uint56":
case "uint56be": return { type: "UIntBE", dataLen: 7};
case "int56le": return { type: "IntLE", dataLen: 7};
case "uint56le": return { type: "UIntLE", dataLen: 7};
case "int64":
case "int64be": return { type: "BigInt64BE", dataLen: 8};
case "uint64":
case "uint64be": return { type: "BigUInt64BE", dataLen: 8};
case "int64le": return { type: "BigInt64LE", dataLen: 8};
case "uint64le": return { type: "BigUInt64LE", dataLen: 8};
case "floatbe": return { type: "FloatBE", dataLen: 4};
case "floatle": return { type: "FloatLE", dataLen: 4};
case "floatcdab": return { type: "FloatCDAB", dataLen: 4};
case "intcdab": return { type: "IntCDAB", dataLen: 4};
case "hexbe": return { type: "HexBE", dataLen: dataLen};
case "hexle": return { type: "HexLE", dataLen: dataLen};
case "bcdbe": return { type: "BCDBE", dataLen: dataLen};
case "bcdle": return { type: "BCDLE", dataLen: dataLen};
case "bitle": return { type: "BitLE", dataLen: dataLen,bitStart:bitStart,bitEnd:bitEnd};
case "bitbe": return { type: "BitBE", dataLen: dataLen,bitStart:bitStart,bitEnd:bitEnd};
case "bool" : return { type: "Bool" , dataLen: dataLen};
case "boolean" : return { type: "Boolean" , dataLen: dataLen};
case "string": return { type: "String", dataLen: dataLen};
default:
return { type: "invalid", dataLen: 0 };
}
}
_preRead (offset,typeInfo) {
if (!this._ensure(offset,typeInfo.dataLen)) { return false}
let retBuffer=Buffer.alloc(typeInfo.dataLen);
this.buffer.copy(retBuffer,0,offset,offset+typeInfo.dataLen);
return retBuffer;
}
read(offset,type) {
const typeInfo=BufferTKL.getTypeInfo(type);
if (typeInfo.type==="invalid") {return false}
let value=0
let valBuffer=this._preRead(offset,typeInfo)
if (valBuffer===false) { return false }
switch (typeInfo.type) {
case "UIntBE":
case "IntBE":
case "UIntLE":
case "IntLE": value= valBuffer["read"+typeInfo.type](0, typeInfo.dataLen); break;
case "FloatCDAB": value= BufferTKL.readFloatCDAB(valBuffer,0); break;
case "IntCDAB" : value= BufferTKL.readIntCDAB(valBuffer,0);break;
case "HexBE": value= BufferTKL.readHexBE(valBuffer,0,typeInfo.dataLen); break;
case "BCDBE": value= BufferTKL.readBCDBE(valBuffer,0,typeInfo.dataLen);break;
case "HexLE": value= BufferTKL.readHexLE(valBuffer,0,typeInfo.dataLen); break;
case "BCDLE": value= BufferTKL.readBCDLE(valBuffer,0,typeInfo.dataLen);break;
case "BitLE": value= BufferTKL.readBitLE(valBuffer,0,typeInfo.dataLen,typeInfo.bitStart,typeInfo.bitEnd);break;
case "BitBE":value= BufferTKL.readBitBE(valBuffer,0,typeInfo.dataLen,typeInfo.bitStart,typeInfo.bitEnd);break;
case "Bool":value= BufferTKL.readBool(valBuffer,0,typeInfo.dataLen);break;
case "Boolean":value= BufferTKL.readBoolean(valBuffer,0,typeInfo.dataLen);break;
case "String":value= BufferTKL.readString(valBuffer,0,typeInfo.dataLen);break;
case "BigInt64BE" :
case "BigUInt64BE":
case "BigInt64LE" :
case "BigUInt64LE":
value= Number( valBuffer["read"+typeInfo.type](0));
break;
default:
value= valBuffer["read"+typeInfo.type](0);
break ;
}
return value;
}
static write(value,type) {
const typeInfo=BufferTKL.getTypeInfo(type);
if (typeInfo.type==="invalid") {return Buffer.alloc(0);}
let valBuffer=Buffer.alloc(typeInfo.dataLen);
switch (typeInfo.type) {
case "UIntBE":
case "IntBE":
case "UIntLE":
case "IntLE": valBuffer["write"+typeInfo.type](value,0, typeInfo.dataLen); break;
case "FloatCDAB": BufferTKL.writeFloatCDAB(valBuffer,value,0); break;
case "IntCDAB" : BufferTKL.writeIntCDAB(valBuffer,value,0);break;
case "HexBE": BufferTKL.writeHexBE(valBuffer,value,0,typeInfo.dataLen); break;
case "BCDBE": BufferTKL.writeBCDBE(valBuffer,value,0,typeInfo.dataLen);break;
case "HexLE": BufferTKL.writeHexLE(valBuffer,value,0,typeInfo.dataLen); break;
case "BCDLE": BufferTKL.writeBCDLE(valBuffer,value,0,typeInfo.dataLen);break;
case "BitLE": BufferTKL.writeBitLE(valBuffer,value,0,typeInfo.dataLen,typeInfo.bitStart,typeInfo.bitEnd);break;
case "BitBE": BufferTKL.writeBitBE(valBuffer,value,0,typeInfo.dataLen,typeInfo.bitStart,typeInfo.bitEnd);break;
case "Bool": BufferTKL.writeBool(valBuffer,value,0,typeInfo.dataLen);break;
case "Boolean": BufferTKL.writeBoolean(valBuffer,value,0,typeInfo.dataLen);break;
case "String": BufferTKL.writeString(valBuffer,value,0,typeInfo.dataLen);break;
default:
valBuffer["write"+typeInfo.type](value,0); break ;
}
return valBuffer;
}
_ensure(offset,bytes) {
if (offset + bytes > this.size) {return false}
return true;
}
static readHexBE(buffer,offset,dataLen) {
const data = buffer.slice(offset, offset + dataLen);
return data.toString('hex').toLowerCase();
}
static readHexLE(buffer,offset,dataLen) {
let data=Buffer.alloc(dataLen);
for (let i=0;i<dataLen;i++) { data[i]=buffer[offset+dataLen-i-1];}
return data.toString('hex').toLowerCase();
}
static readBCDBE(buffer,offset,dataLen) {
let value = 0;
for (let i = 0; i < dataLen; i++) {
const byte = buffer[offset + i];
const high = (byte >> 4) & 0x0f;
const low = byte & 0x0f;
if (high > 9 || low > 9) { throw Error("wrong input or type")}
value = value * 100 + high * 10 + low;
}
return value;
}
static readBCDLE(buffer,offset,dataLen) {
let value = 0;
for (let i = 0; i < dataLen; i++) {
const byte = buffer[offset + dataLen-i-1];
const high = (byte >> 4) & 0x0f;
const low = byte & 0x0f;
if (high > 9 || low > 9) { throw Error("bcdle : wrong input or type")}
value = value * 100 + high * 10 + low;
}
return value;
}
static readFloatCDAB(buffer,offset,dataLen) {
const buf = buffer.slice(offset, offset + 4);
const swapped = Buffer.from([buf[2], buf[3], buf[0], buf[1]]);
return swapped.readFloatLE(0);
}
static readIntCDAB(buffer,offset) {
const buf = buffer.slice(offset, offset + 4);
const swapped = Buffer.from([buf[2], buf[3], buf[0], buf[1]]);
return swapped.readInt32LE(0);
}
static readBool(buffer,offset,dataLen) {
const data = buffer.readUIntBE(offset, dataLen);
if (data===0) { return 0}
return 1
}
static readBoolean(buffer,offset,dataLen) {
const data = buffer.readUIntBE(offset, dataLen);
if (data===0) { return false}
return true
}
static readString(buffer,offset,dataLen) {
const data = buffer.slice(offset, offset + dataLen);
return data.toString('ascii');
}
static readBitLE(buffer, offset, dataLen, bitStart, bitEnd) {
if (bitStart < 0 || bitEnd >= dataLen * 8 || bitStart > bitEnd) {
throw new RangeError(`Invalid bit range: [${bitStart}, ${bitEnd}] for ${dataLen} bytes`);
}
const width = bitEnd - bitStart + 1;
let val = 0n;
for (let i = 0; i < dataLen; i++) {
val |= BigInt(buffer[offset + i]) << (8n * BigInt(i));
}
const shifted = val >> BigInt(bitStart);
const mask = (1n << BigInt(width)) - 1n;
return Number(shifted & mask);
}
static readBitBE(buffer, offset, dataLen, bitStart, bitEnd) {
if (bitStart < 0 || bitEnd >= dataLen * 8 || bitStart > bitEnd) {
throw new RangeError(`Invalid bit range: [${bitStart}, ${bitEnd}] for ${dataLen} bytes`);
}
const width = bitEnd - bitStart + 1;
let val = 0n;
for (let i = 0; i < dataLen; i++) {
val = (val << 8n) | BigInt(buffer[offset + i]);
}
const shifted = val >> BigInt(bitStart);
const mask = (1n << BigInt(width)) - 1n;
return Number(shifted & mask);
}
static writeHexBE(buffer,value,offset, dataLen) {
let inputstr=value.toLowerCase().replaceAll("0x","").replaceAll(" ","");
const hex = inputstr.padStart(dataLen * 2, '0');
const tempBuf = Buffer.from(hex, 'hex');
tempBuf.copy(buffer, offset, 0, dataLen);
return buffer;
}
static writeHexLE(buffer,value, offset, dataLen) {
let inputstr=value.toLowerCase().replaceAll("0x","").replaceAll(" ","");
const hex = inputstr.padStart(dataLen * 2, '0');
const tempBuf = Buffer.from(hex, 'hex');
for (let i = 0; i < dataLen; i++) {
buffer[offset + dataLen - i - 1] = tempBuf[i];
}
return buffer;
}
static writeBCDBE(buffer,value,offset, dataLen) {
let tempValue = value;
for (let i = dataLen - 1; i >= 0; i--) {
const low = tempValue % 10;
tempValue = Math.floor(tempValue / 10);
const high = tempValue % 10;
tempValue = Math.floor(tempValue / 10);
buffer[offset + i] = (high << 4) | low;
}
return buffer;
}
static writeBCDLE(buffer, value, offset, dataLen) {
let tempValue = value;
for (let i = 0; i < dataLen; i++) {
const low = tempValue % 10;
tempValue = Math.floor(tempValue / 10);
const high = tempValue % 10;
tempValue = Math.floor(tempValue / 10);
buffer[offset + dataLen - i - 1] = (high << 4) | low;
}
return buffer;
}
static writeFloatCDAB(buffer, value, offset) {
const tempBuf = Buffer.alloc(4);
tempBuf.writeFloatLE(value, 0);
// 从 [0,1,2,3] 变换为 [2,3,0,1]
buffer[offset] = tempBuf[2];
buffer[offset + 1] = tempBuf[3];
buffer[offset + 2] = tempBuf[0];
buffer[offset + 3] = tempBuf[1];
return buffer;
}
static writeIntCDAB(buffer, value, offset) {
const tempBuf = Buffer.alloc(4);
tempBuf.writeInt32LE(value, 0);
// 从 [0,1,2,3] 变换为 [2,3,0,1]
buffer[offset] = tempBuf[2];
buffer[offset + 1] = tempBuf[3];
buffer[offset + 2] = tempBuf[0];
buffer[offset + 3] = tempBuf[1];
return buffer;
}
static writeBool(buffer,value,offset, dataLen) {
const tempBuf = Buffer.alloc(dataLen);
tempBuf.writeUIntBE(value,offset,dataLen);
return tempBuf;
}
static writeBoolean(buffer,value,offset, dataLen) {
const wval = value?1:0;
const tempBuf = Buffer.alloc(dataLen);
tempBuf.writeUIntBE(wval,offset,dataLen);
return tempBuf;
}
static writeString(buffer, value, offset, dataLen) {
const tempBuf = Buffer.from(value, 'ascii');
tempBuf.copy(buffer, offset, 0, dataLen);
return buffer;
}
static writeBitLE(buffer, value, offset, dataLen, bitStart, bitEnd) {
const width = bitEnd - bitStart + 1;
const mask = width >= 32 ? 0xFFFFFFFF : (1 << width) - 1;
const cleanValue = (value & mask);
if (dataLen > 4) {
let low = buffer.readUInt32LE(offset);
let high = buffer.readUIntLE(offset + 4, dataLen - 4);
if (bitStart >= 32) {
const highMask = ~(mask << (bitStart - 32));
high = (high & highMask) | (cleanValue << (bitStart - 32));
} else if (bitEnd < 32) {
const lowMask = ~(mask << bitStart);
low = (low & lowMask) | (cleanValue << bitStart);
} else {
const lowWidth = 32 - bitStart;
low = (low & ~(mask << bitStart)) | (cleanValue << bitStart);
high = (high & ~(mask >>> lowWidth)) | (cleanValue >>> lowWidth);
}
buffer.writeUInt32LE(low >>> 0, offset);
buffer.writeUIntLE(high >>> 0, offset + 4, dataLen - 4);
} else {
let val = buffer.readUIntLE(offset, dataLen);
val = (val & ~(mask << bitStart)) | (cleanValue << bitStart);
buffer.writeUIntLE(val >>> 0, offset, dataLen);
}
return buffer;
}
static writeBitBE(buffer, value, offset, dataLen, bitStart, bitEnd) {
const width = bitEnd - bitStart + 1;
const mask = width >= 32 ? 0xFFFFFFFF : (1 << width) - 1;
const cleanValue = (value & mask);
const totalBits = dataLen * 8;
const reverseStart = totalBits - 1 - bitEnd;
if (dataLen > 4) {
let high = buffer.readUIntLE(offset, dataLen - 4);
let low = buffer.readUInt32BE(offset + dataLen - 4);
if (reverseStart >= 32) {
high = (high & ~(mask << (reverseStart - 32))) | (cleanValue << (reverseStart - 32));
} else if (totalBits - 1 - bitStart < 32) {
low = (low & ~(mask << reverseStart)) | (cleanValue << reverseStart);
} else {
const lowWidth = 32 - reverseStart;
low = (low & ~(mask << reverseStart)) | (cleanValue << reverseStart);
high = (high & ~(mask >>> lowWidth)) | (cleanValue >>> lowWidth);
}
buffer.writeUIntLE(high >>> 0, offset, dataLen - 4);
buffer.writeUInt32BE(low >>> 0, offset + dataLen - 4);
} else {
let val = buffer.readUIntBE(offset, dataLen);
const shift = totalBits - 1 - bitEnd;
val = (val & ~(mask << shift)) | (cleanValue << shift);
buffer.writeUIntBE(val >>> 0, offset, dataLen);
}
return buffer;
}
}
export class PayloadParser {
constructor( {
bufferType,
device,
msg,
frameInfo,
appInfo,
paraInfo,
extraData
}) {
if (bufferType==undefined){ this.bufferType=BufferTKL
}else { this.bufferType=bufferType }
this.device = device
this.msg=msg;
this.frameInfo=frameInfo??{}
this.port=frameInfo?.port ?? -1;
this.dataLen=frameInfo?.dataLen ?? -1;
this.status=frameInfo?.status ?? -1;
this.battery=frameInfo?.battery ?? -1;
this.rssi=frameInfo?.rssi ?? false;
this.tagList=frameInfo?.tagList ?? [];
if(frameInfo?.subDevice==undefined||frameInfo?.subDevice?.index==undefined) {
this.frameInfo.subDevice={}
this.frameInfo.subDevice.index=-1
this.frameInfo.subDevice.type="uint8"
}
this.appInfo=appInfo
this.paraInfo=paraInfo
this.extraData=extraData
this.payload = Buffer.from(msg?.userdata?.payload, "base64");
this.port = msg?.userdata?.port || -1;
this.tdata={};
this.pdata={};
}
_preParseTelemetry(){}
_preParseParas(){}
_postParseTelemetry() {}
_postParseParas() {}
telemetry() {
let payBuffer = new this.bufferType(this.payload);
let valid=true
if (this.frameInfo.port>0&&this.port!==this.frameInfo.port){ return null }
if(this.frameInfo.dataLen>0&&this.frameInfo.dataLen!==payBuffer.size){ return null }
this.frameInfo.tagList.forEach(range => {if(this.payload[range.index]!==range.tag){ valid=false}})
if(valid===false){ return null}
if (this.frameInfo?.status>=0) { this.tdata.status=payBuffer.read(this.frameInfo.status,"uint8"); }
if (this.frameInfo?.battery>=0) {
let vbat=payBuffer.read(this.frameInfo.battery,"uint8");
this.tdata.battery=Number(((vbat*1.6)/254 +2.0).toFixed(2))
}
if (this.frameInfo.rssi&&this.msg?.gwrx?.[0] ) {
this.tdata.rssi = this.msg.gwrx[0].rssi;
this.tdata.snr = this.msg.gwrx[0].lsnr;
}
this._preParseTelemetry()
if (this.frameInfo?.subDevice?.index>=0){
this.tdata.addr=payBuffer.read(this.frameInfo?.subDevice?.index,this.frameInfo?.subDevice?.type);
if (this.tdata.addr===0){return null}
}
const counts=this.appInfo.length;
for (let i=0; i<counts; i++){
let val=payBuffer.read(this.appInfo[i].index,this.appInfo[i].type);
if (val===false){ continue}
let lastVal
if (this.appInfo[i]?.illegal!=undefined ) {
let method=this.appInfo[i]?.illegal.substring(0,1)
let checkBuffer= new BufferTKL( Buffer.from(this.appInfo[i]?.illegal.substring(1),"hex"));
let checkVal= checkBuffer.read(0,this.appInfo[i].type)
if (method==='>'&& val>checkVal){ continue}
if (method==='<'&& val<checkVal){ continue}
if (method==='='&& val===checkVal){ continue}
}
let decimal=this.appInfo[i]?.decimal==undefined?0:this.appInfo[i]?.decimal;
if (typeof val==="number"){
if (this.appInfo[i]?.coefficient==undefined){ lastVal=Number(val.toFixed(decimal));
}else if (typeof this.appInfo[i]?.coefficient!=="number"){ lastVal=Number(val.toFixed(decimal));
}else { lastVal=Number((val*this.appInfo[i].coefficient).toFixed(decimal)); }
}else { lastVal=val }
if (this.appInfo[i].postProcess!=undefined){
let postFucn=this.appInfo[i].postProcess;
if(!postFucn.includes("return") ){
postFucn= postFucn.concat(" ; return value;");
}
const postProcessor = new Function('value',postFucn);
lastVal = postProcessor(lastVal);
}
if (Object.keys(this.appInfo[i].options??{}).length > 0) {
let rval=lastVal.toString()
// lastVal=this.appInfo[i].options[Object.keys(this.appInfo[i].options)[0]]
if (rval in this.appInfo[i].options) { lastVal= this.appInfo[i].options[rval] }
}
this.tdata[this.appInfo[i].field_name]=lastVal
}
for (let i=0; i<counts; i++){
if (this.appInfo[i]?.coefficient==undefined) {continue}
if (typeof this.appInfo[i].coefficient==="number"){ continue}
let val=this.tdata[this.appInfo[i].field_name]
if (val==undefined){ continue}
let valcof=this.tdata[this.appInfo[i].coefficient];
if (valcof==undefined){ continue}
let decimal=this.appInfo[i]?.decimal==undefined?0:this.appInfo[i]?.decimal;
this.tdata[this.appInfo[i].field_name]=Number((val*valcof).toFixed(decimal));
}
this._postParseTelemetry()
return Object.keys(this.tdata).length ? this.tdata : null;
}
paras() {
if (this.port !== 214) { return null }
// this.pdata.content = this.payload.toString('hex');
let size=this.payload.length;
let regAddress=0
let startAddress=0
this._preParseParas()
for (let i = 0; i < size; i++) {
//if (payload[i]!==0x2F){ return null }
let paraName="app"
if ((this.payload[i]&0x3F)===0x2F) { paraName="app"
}else if((this.payload[i]&0x3F)===0x21) { paraName="fw"
}else if((this.payload[i]&0x3F)===0x29) { paraName="cf"
}else if((this.payload[i]&0x3F)===0x22) { paraName="radio"
}else if((this.payload[i]&0x3F)===0x24) { paraName="ds"
}else if((this.payload[i]&0x3F)===0x23) { paraName="status"
}else { return null}
if (size<(i+1+this.payload[i+1]) ){ return null }
const regcounts=this.payload[i+1]-2
if (size<(i+4+regcounts) ){ return null }
startAddress=this.payload[i+2]
let reader=new this.bufferType(this.payload.slice(i+4,i+4+regcounts));
for (let j = 0; j < regcounts; j++) {
regAddress=startAddress + j
let para={}
para=this.paraInfo[paraName+"_"+regAddress.toString(10)]
for (let k=0;k<16;k++) {
if (k>0) { para=this.paraInfo[paraName+"_"+regAddress.toString(10)+"_"+k.toString(10)]}
if (para==undefined){ continue }
let val=reader.read(j,para.type);
if (val===false){ continue}
let lastVal
if (para.illegal!=undefined ) {
let method=para.illegal.substring(0,1)
let checkBuffer= new BufferTKL( Buffer.from(para.illegal.substring(1),"hex"));
let checkVal= checkBuffer.read(0,para.type)
if (method==='>'&& val>checkVal){ continue}
if (method==='<'&& val<checkVal){ continue}
if (method==='='&& val===checkVal){ continue}
}
let decimal=para?.decimal==undefined?0:para?.decimal;
if (typeof val==="number"){
if (para?.coefficient==undefined){ lastVal=Number(val.toFixed(decimal));
}else if (typeof para?.coefficient!=="number"){ lastVal=Number(val.toFixed(decimal));
}else { lastVal=Number((val*para.coefficient).toFixed(decimal)); }
}else { lastVal=val }
if (para.postProcess!=undefined){
let postFunc=para.postProcess;
if(!postFunc.includes("return") ){postFunc= postFunc.concat(" ; return value;");}
const postProcessor = new Function('value',postFunc);
lastVal = postProcessor(lastVal);
}
if (Object.keys(para.options??{}).length > 0) {
let rval=lastVal.toString()
if (rval in para.options) { lastVal= para.options[rval] }
}
this.pdata[para.field_name]=lastVal
}
}
i+=regcounts+3;
}
this._postParseParas()
if (Object.keys(this.pdata).length < 1) {
return null;
}
return this.pdata;
}
}
export class MSparser extends PayloadParser {
constructor({device, msg, frameInfo, extraData}){
super({
device:device, msg:msg, frameInfo:frameInfo, extraData:extraData
})
let payload=Buffer.from(msg?.userdata?.payload, "base64")
let payBuffer=new BufferTKL(payload);
this.appInfo=[];
let index=0
let newPayload=Buffer.alloc(255);
for(let i=0; i < payload.length;) {
let idVal=payBuffer.read(i,frameInfo.idType)
i+=2;
for (let j = 0; j < 4; j++) {
let key="user_0x"+idVal+"_"+j.toString(10)
let para=this.extraData[key]
if (para==undefined) { continue}
let ptypeInfo=BufferTKL.getTypeInfo(para.type)
if ((i+ptypeInfo.dataLen)>payload.length) {continue}
para["index"]=index
payload.copy(newPayload,index,i,i+ptypeInfo.dataLen)
index+=ptypeInfo.dataLen
this.appInfo.push(para)
i+=ptypeInfo.dataLen;
}
}
this.payload=Buffer.alloc(index);
newPayload.copy(this.payload,0,0,index);
}
}
export class Utils{
static paraName = Object.freeze({
app: 'app',
cf: 'cf',
fw: 'fw',
radio: 'radio',
user:"user"
});
static msgType=Object.freeze({
paras:"para",
transParent:"transParent",
swDown:"swDown",
user:"user",
})
static ACTION = Object.freeze({ no:"no",new: 'new',clear: 'clear'});
static LEVEL=Object.freeze({low:'low',mid:'mid',high:'high',urgent:'urgent'});
static parseToBuffer(inputStr){
if (inputStr==undefined){ return Buffer.alloc(0)}
let xstr=inputStr.toLowerCase().replaceAll("0x","").replaceAll(" ","")
return Buffer.from(xstr.length%2===0?xstr:('0'+xstr),"hex")
}
static parseVal(valstr){
if (typeof valstr==="number"){ return valstr; }
let val=valstr.toLowerCase();
if (val.includes("0x")){
val=val.replaceAll("0x","").replaceAll(" ","");
let cleanedHex =val.length % 2 === 0 ? val : '0' + val;
return parseInt(cleanedHex, 16);
}
return parseInt(val,10)
}
static crcSum( buf, len, poly=0)
{
let crc = poly;
for(let i=0;i<len;i++) {
crc += buf[i];
crc&=0xFFFF
}
return crc&0xFFFF;
}
static crc16CCIT (data, len,poly=0x1021)
{
let remainder = 0;
let polynomial = poly;
for( let i = 0; i < len; i++ )
{
remainder ^=(data[i] << 8);
for( let bit = 8; bit > 0; bit--)
{
if( (remainder & 0x8000) ) remainder = (remainder << 1) ^ polynomial;
else remainder <<= 1;
remainder &= 0xFFFF; // Keep within 16 bits
}
}
return remainder&0xFFFF;
}
static crc16Modbus(buf, len, poly=0xa001)
{
let i,j;
let crc;
for(i=0,crc=0xffff;i< len;i++)
{
crc ^= buf[i];
for(j=0;j<8;j++)
{
if(crc & 0x01) crc = (crc >> 1) ^ poly;
else crc >>= 1;
crc&=0xFFFF
}
}
return crc&0xFFFF;
}
static timingPerformanceCalc(sf, bw, swPeriod) {
let minSf = 5;
let maxSf = 12;
let bwList = [500, 250, 125];
let baseSymbolTime = 0.064;
if (sf < minSf) sf = 5;
if (sf > maxSf) sf = 12;
if (!bwList.includes(bw)) bw = 500;
let powerOf2 = ((sf - minSf) + (bwList.indexOf(bw)))
let symbolTime = baseSymbolTime * (2 ** powerOf2);
return Math.ceil((swPeriod + 300) / symbolTime)
}
}
export class TriggerHelper{
constructor({
device, thingModelId,alarmEvent,title,desc,level,action
}) {
this.device = device;
this.thingModelId = thingModelId;
this.alarmEvent = alarmEvent??device?.name;
this.title="["+device?.name+"] : " +(title??"")
this.action = action??Utils.ACTION.new;
this.desc = desc??"";
this.level = level??Utils.LEVEL.low;
this.group=device?.server_attrs?.group?.notify??[];
this.tdata=device?.telemetry_data?.[thingModelId]
}
_alarmProcess(info){}
alarm(info){
let rslt=this._alarmProcess(info);
if (rslt === false) { return null}
if (!Object.values(Utils.ACTION).includes(this?.action)) return null;
if (this?.action===Utils.ACTION.no) return null
return {
delayms: 0,
abort_previous_timer: true,
actions: [{
method: "alarm",
params: {
_eui:this.device.eui,
action:this.action,
name:this.alarmEvent,
title:this.title,
level:this.level,
desc:this.desc,
group:this.group
}
}]
}
}
}
export class RPCHelper {
constructor(){}
static reset() { return Buffer.from("CF03090101", "hex") }
static redo() { return Buffer.from("CF03090102", "hex") }
static join() { return Buffer.from("CF03090104", "hex") }
static makeAlarm(
{
alarms,
eui,
name,
action,
group,
title,
desc,
level
}
){
const ACTION = { no:"no","new": 'new',clear: 'clear'};
switch (action){
case ACTION.clear: break;
case ACTION.new:
let alarmInfo=alarms[name]
if (alarmInfo==undefined){break}
if (alarmInfo.title!==title){break}
if(alarmInfo.desc!==desc) {break}
if (alarmInfo.level!==level) {break}
action = ACTION.no
break
default: return null
}
if(action===ACTION.no){ return null}
return [
{
sleepTimeMs: 0,
target: eui,
type:"alarm",
dnMsg: {
action:action,
data:{
alarm_name: name,
notice_groups: group,
title: title,
desc: desc,
level: level,
}
}
}
]
}
static paraWrite(name,type,addr,value) {
let typeInfo=BufferTKL.getTypeInfo(type)
let writeBuffer=Buffer.alloc(typeInfo.dataLen+4);
writeBuffer[0]=RPCHelper.getFuncWriteCode(name)
writeBuffer[1]=2+typeInfo.dataLen;
writeBuffer[2]=Utils.parseVal(addr)
writeBuffer[3]=typeInfo.dataLen;
let valBuffer=BufferTKL.write(value,type);
valBuffer.copy(writeBuffer,4);
return writeBuffer;
}
static paraRead(name,type,addr) {
let readBuffer=Buffer.alloc(4);
let typeInfo=BufferTKL.getTypeInfo(type)
readBuffer[0]=RPCHelper.getFuncReadCode(name)
readBuffer[1]=2;
readBuffer[2]=Utils.parseVal(addr);
readBuffer[3]=typeInfo.dataLen;
return readBuffer;
}
static getFuncWriteCode(name){
switch(name){
case "app": return 0xCF;
case "cf" : return 0xC9;
case "fw" : return 0xC1;
case "radio": return 0xC2;
case "ds": return 0xC4;
case "status":return 0xc3;
default: return 0xCF;
}
}
static getFuncReadCode(name){
switch(name){
case "app": return 0x8F;
case "cf" : return 0x89;
case "fw" : return 0x81;
case "radio": return 0x82;
case "ds":return 0x84;
case "status": return 0x83;
default: return 0x8F;
}
}
static arrangePara(paraInfo) {
const paraList = {
app: [],
fw: [],
cf: [],
radio: [],
ds:[]
};
Object.keys(paraInfo).forEach(key => {
const match=key.split("_")
let prefix=match[0]
let regAddr=parseInt(match[1],10)
paraList[prefix].push({
regAddr:regAddr,
...paraInfo[key]
})
})
Object.keys(paraList).forEach(type => {
paraList[type].sort((a, b) => a.regAddr - b.regAddr);
});
return paraList;
}
static _preDealParams (
{
serverParaDef,
paraDef,
params
}) {
return {
serverParaDef:serverParaDef,
paraDef:paraDef,
params:params
}
}
static _postBuild (
{
paraDef,
writeBuffer,
readBuffer,
serverPara,
log
}) {
return {
writeBuffer:writeBuffer,
readBuffer:readBuffer,
serverPara:serverPara,
log:log
}
}
static _freshPara({
funcName,
writeBuffer,
readBuffer,
startIndex,
writeIndex,
readIndex,
regBytes,
nextReg
}){
writeBuffer[startIndex+1]=regBytes+2;
writeBuffer[startIndex+3]=regBytes;
readBuffer[readIndex]=RPCHelper.getFuncReadCode(funcName);
readBuffer[readIndex+1]=2;
readBuffer[readIndex+2]=writeBuffer[startIndex+2];
readBuffer[readIndex+3]=regBytes;
readIndex+=4
startIndex=writeIndex
writeBuffer[startIndex]=RPCHelper.getFuncWriteCode(funcName);
writeBuffer[startIndex+2]=nextReg;
writeIndex+=4
return {
startIndex:startIndex,
writeIndex:writeIndex,
readIndex:readIndex,
}
}
static buildmodbusFrameRead(addr,code,regStart,count){
let cmdBuffer=Buffer.from([0x01,0x06,0x03,0x04,0x05,0x06,0x07,0x08]);
cmdBuffer[0]=Utils.parseVal(addr);
cmdBuffer[1]=Utils.parseVal(code);
regStart=Utils.parseVal(regStart);
cmdBuffer.writeUInt16BE(regStart,2)
count=Utils.parseVal(count)
cmdBuffer.writeUInt16BE(count,4)
let crcSum=Utils.crc16Modbus(cmdBuffer,6)
cmdBuffer.writeUInt16LE(Number(crcSum),cmdBuffer.length-2)
return cmdBuffer
}
static buildmodbusFrame10({
serverParaDef,
paraDef,
params
}){
const apara=this._preDealParams({
serverParaDef:serverParaDef,
paraDef:paraDef,
params:params});
serverParaDef=apara.serverParaDef;
paraDef=apara.paraDef;
params=apara.params
let valList=[]
let log=[]
let serverPara={}
if (serverParaDef==undefined) {serverParaDef=[]}
if (paraDef==undefined) { paraDef={}}
if (params==undefined) {params={}}
serverParaDef.forEach(item=>{
const para=params[item.field_name]
if (para==undefined) { return }
serverPara[item.field_name]=para;
})
let regAddr=Utils.parseVal(paraDef.paraList[0].index)
let totalLength=0
paraDef.paraList.forEach(item=>{
let logItem={}
let val=params[item.field_name]
if (val==undefined) {
if (item.default==undefined) { return}
val=item.default
}
val=Utils.parseVal(val)
serverPara[item.field_name]=val
logItem.valName=item.name
logItem.field_name=item.field_name;
logItem.value=val
if (item.coefficient!=undefined){
if (typeof item.coefficient ==="number") { val=(val/item.coefficient)|0}
}
log.push(logItem)
let valBuffer=BufferTKL.write(val,item.type)
item.valBuffer=valBuffer
totalLength+=valBuffer.length
})
let writeBuffer=Buffer.alloc(totalLength+9)
writeBuffer[0]=Utils.parseVal(paraDef.addr)
writeBuffer[1]=0x10
writeBuffer.writeUInt16BE(regAddr,2)
writeBuffer.writeUInt16BE(totalLength/2,4)
writeBuffer.writeUInt8(totalLength,6)
let index=7
paraDef.paraList.forEach(item=>{
if (item.valBuffer==undefined) { return}
item.valBuffer.copy(writeBuffer,index)
index+=item.valBuffer.length
})
let crcSum=Utils.crc16Modbus(writeBuffer,writeBuffer.length-2)
writeBuffer.writeUInt16LE(Number(crcSum),writeBuffer.length-2)
return RPCHelper._postBuild({
writeBuffer:writeBuffer,
readBuffer:Buffer.alloc(0),
serverPara:serverPara,
log:log
})
}
static buildmodbusFrame06({
serverParaDef,
paraDef,
params
}){
const apara=RPCHelper._preDealParams({
serverParaDef:serverParaDef,
paraDef:paraDef,
params:params});
serverParaDef=apara.serverParaDef;
paraDef=apara.paraDef;
params=apara.params
let valList=[]
let log=[]
let serverPara={}
if (serverParaDef==undefined) {serverParaDef=[]}
if (paraDef==undefined) { paraDef={}}
if (params==undefined) {params={}}
serverParaDef.forEach(item=>{
const para=params[item.field_name]
if (para==undefined) { return }
serverPara[item.field_name]=para;
})
let writeBuffer=Buffer.from([0x01,0x06,0x03,0x04,0x05,0x06,0x07,0x08]);
writeBuffer[0]=Utils.parseVal(paraDef.addr);
let para=paraDef.paraList[0]
let regAddr=Utils.parseVal(para.index)
writeBuffer.writeUInt16BE(regAddr,2)
let logItem={}
let val=params[para.field_name]
if (val==undefined) {
if (para.default==undefined) { return}
val=para.default
}
val=Utils.parseVal(val)
serverPara[para.field_name]=val
logItem.valName=para.name
logItem.field_name=para.field_name;
logItem.value=val
if (para.coefficient!=undefined){
if (typeof para.coefficient ==="number") { val=(val/para.coefficient)|0}
}
log.push(logItem)
let valBuffer=BufferTKL.write(val,para.type)
valBuffer.copy(writeBuffer,4)
let crcSum=Utils.crc16Modbus(writeBuffer,6)
writeBuffer.writeUInt16LE(Number(crcSum),writeBuffer.length-2)
return RPCHelper._postBuild({
writeBuffer:writeBuffer,
readBuffer:Buffer.alloc(0),
serverPara:serverPara,
log:log
})
}
static buildSensorFrame({
serverParaDef,
paraDef,
params
}){
const apara=RPCHelper._preDealParams({
serverParaDef:serverParaDef,
paraDef:paraDef,
params:params});
serverParaDef=apara.serverParaDef;
paraDef=apara.paraDef;
params=apara.params
if (serverParaDef==undefined) {serverParaDef=[]}
if (paraDef==undefined) { paraDef={}}
if (params==undefined) {params={}}
if (paraDef.cmd_header==undefined) { return null}
paraDef.headerBuffer=Buffer.from(paraDef.cmd_header.replaceAll(" ",""),"hex")
let valList=[]
let log=[]
let serverPara={}
serverParaDef.forEach(item=>{
const para=params[item.field_name]
if (para==undefined) { return }
serverPara[item.field_name]=para;
})
let totalLen=0
paraDef.paraList.forEach(para => {
let logItem={}
let typeInfo=BufferTKL.getTypeInfo(para.type)
let writeBuffer=Buffer.alloc(typeInfo.dataLen);
let val=params[para.field_name]
if (val==undefined) {
if (para.default==undefined) { return}
val=para.default
}
serverPara[para.field_name]=val
logItem.valName=para.name
logItem.field_name=para.field_name;
logItem.value=val
if (para.coefficient!=undefined){
if (typeof para.coefficient ==="number") { val=(val/para.coefficient)|0}
}
let valBuffer=BufferTKL.write(val,para.type)
para.valBuffer=valBuffer
totalLen+=para.valBuffer.length;
valList.push(para)
log.push(logItem)
})
let writeBuffer=Buffer.alloc(totalLen);
let dataIndex=0
valList.forEach(para => { para.valBuffer.copy(writeBuffer,dataIndex);dataIndex+=para.valBuffer.length})
return RPCHelper._postBuild({
writeBuffer:writeBuffer,
readBuffer:Buffer.alloc(0),
serverPara:serverPara,
log:log
})
}
static buildFrame({
serverParaDef,
paraDef,
params
}){
const apara=RPCHelper._preDealParams({
serverParaDef:serverParaDef,
paraDef:paraDef,
params:params});
serverParaDef=apara.serverParaDef;
paraDef=apara.paraDef;
params=apara.params
let readOnly=false
const paraList=RPCHelper.arrangePara(paraDef)
if(serverParaDef==undefined){ serverParaDef=[]}
if(paraDef==undefined){ paraDef={}}
if(params==undefined){params={}}
let serverPara={}
if (Object.keys(params).length===0 ) {readOnly=true}
serverParaDef.forEach(item=>{
const para=params[item.field_name]
if (para==undefined && !readOnly) { return }
serverPara[item.field_name]=para;
})
const maxWriteBytes=180
const maxReadBytes=50
let writeBuffer=Buffer.alloc(maxWriteBytes);
let readBuffer=Buffer.alloc(maxReadBytes);
let writeIndex=4;
let readIndex=0;
let regBytes=0
let nextReg=-1
let startIndex=0;
let totalWriteBytes=0;
let totalReadBytes=0;
let log=[]
Object.entries(paraList).forEach(([funcName,list])=>{
if (list.length===0) {return}
list.forEach(para=>{
let logItem={}
let val=undefined
let typeInfo
if((writeIndex+8)>maxWriteBytes) return
if((readIndex+4)>maxReadBytes) return
if (para?.field_name==undefined) return
if(!readOnly){
if (params==undefined) return
if (params[para.field_name]==undefined) return
val=params[para.field_name];
if(val==="nc"||val===BufferTKL.INVALID_STR||val===BufferTKL.INVALID_NUM) return
}
typeInfo=BufferTKL.getTypeInfo(para.type)
if(typeInfo.type==="invalid") return
logItem.valName=para.name
logItem.field_name=para.field_name;
logItem.value=val
log.push(logItem)
if (nextReg===-1) {
writeBuffer[startIndex]=RPCHelper.getFuncWriteCode(funcName)
nextReg=para.regAddr;
writeBuffer[startIndex+2]=nextReg;
}
if (nextReg!==para.regAddr){
totalWriteBytes+=4+regBytes
totalReadBytes+=4
const retVal=RPCHelper._freshPara({
funcName:funcName,
writeBuffer:writeBuffer,
readBuffer:readBuffer,
startIndex:startIndex,
writeIndex:writeIndex,
readIndex:readIndex,
regBytes:regBytes,
nextReg:para.regAddr
});
nextReg=para.regAddr;
startIndex=retVal.startIndex;
writeIndex=retVal.writeIndex;
readIndex=retVal.readIndex;
regBytes=0
}
//let payHandler=new BufferTKL(Buffer.alloc(0));
let valBuffer
if(val!=undefined){
if (para?.coefficient!=undefined && (typeof para?.coefficient)==="number" ){
val=val/para.coefficient
}
valBuffer=BufferTKL.write(val,para.type);
valBuffer.copy(writeBuffer,writeIndex);
}
writeIndex+=typeInfo.dataLen;
nextReg+=typeInfo.dataLen;
regBytes+=typeInfo.dataLen;
})
totalWriteBytes+=4+regBytes
totalReadBytes+=4
const retv=RPCHelper._freshPara({
funcName:funcName,
writeBuffer:writeBuffer,
readBuffer:readBuffer,
startIndex:startIndex,
writeIndex:writeIndex,
readIndex:readIndex,regBytes:regBytes,
nextReg:0
});
nextReg=-1;
startIndex=retv.startIndex;
writeIndex=retv.writeIndex;
readIndex=retv.readIndex;
regBytes=0
})
if (totalWriteBytes===4) {totalWriteBytes=0;totalReadBytes=0}
let retWBuffer=Buffer.alloc(totalWriteBytes);
let retRBuffer=Buffer.alloc(totalReadBytes);
writeBuffer.copy(retWBuffer,0,0,totalWriteBytes);
readBuffer.copy(retRBuffer,0,0,totalReadBytes);
return RPCHelper._postBuild({
writeBuffer:retWBuffer,
readBuffer:retRBuffer,
serverPara:serverPara,
log:log
})
}
static cj188reader(addr,model) {
let buffer =Buffer.from("FE FE FE FE FE 68 20 AA AA AA AA AA AA AA 03 03 81 0A 01 C0 16".replaceAll(" ",""),"hex")
let addrBuffer=Utils.parseToBuffer(addr)
if (addrBuffer.length!==7) { return Buffer.alloc(0) }
for (let i = 0; i < 7; i++) { buffer[7+i] = addrBuffer[6-i]; }
let modelBuffer=Utils.parseToBuffer(model);
if(modelBuffer.length!==5) { return Buffer.alloc(0)}
modelBuffer.copy(buffer,14,0,5);
let crc=Utils.crcSum(buffer.slice(5,buffer.length-2),buffer.length-7)
buffer[buffer.length-2]=crc&0x00FF
return buffer
}
static modbusAction(addr,code,regAddr,regVal,valBuffer=undefined) {
let baseBuffer=Buffer.from([0x01,0x03,0x01,0x03,0x03,0x04,0x05,0x06])
let buffer=baseBuffer;
if (valBuffer!=undefined) {
buffer=Buffer.alloc(baseBuffer.length+valBuffer.length);
baseBuffer.copy(buffer);
}
buffer[0]= Utils.parseVal(addr)
buffer[1]=Utils.parseVal(code)
const regStart=Utils.parseVal(regAddr);
const regValue=Utils.parseVal(regVal);
buffer.writeUInt16BE(regStart,2);
buffer.writeUInt16BE(regValue,4);
let crcSum=Utils.crc16Modbus(buffer,6)
buffer.writeUInt16LE(Number(crcSum),buffer.length-2)
return buffer
}
static makeMSG(
{
msgType,
device,
dnBuffer,
type,
port,
confirmed,
sleepTime,
dnWaitms,
}){
if(msgType==undefined){msgType=Utils.msgType.user}
if (msgType===Utils.msgType.swDown) {
let cmdBuf = Buffer.alloc(5+dnBuffer.length)
cmdBuf[0]=0xFF;cmdBuf[1]=0xAA
dnBuffer.copy(cmdBuf,2)
let crc=Utils.crc16Modbus(cmdBuf,dnBuffer.length+2)
cmdBuf.writeUInt16LE(crc,dnBuffer.length+2)
cmdBuf[cmdBuf.length-1]=0x40
let payBuffer =Buffer.alloc(9+cmdBuf.length);
payBuffer[0]=0xdc
let devEuiBuffer=Buffer.from(device.eui,'hex')
devEuiBuffer.copy(payBuffer,1)
cmdBuf.copy(payBuffer,9);
let sf=device?.shared_attrs?.swSF
if (sf==undefined){ return null}
let bw=device?.shared_attrs?.swBW.replaceAll("kHz","")
return {
sleepTimeMs: sleepTime==undefined?0:sleepTime,
dnMsg:{
version: "3.1",
type: "data",
if: "loraSW",
moteeui: device.eui,
token: new Date().getTime(),
userdata: {
payload: payBuffer.toString("base64"),
dnWaitms: 0,
specify: {
txpk: {
"imme": true,
"tmst": 0,
"tmms": 0,
"time": "",
"freq": device?.shared_attrs?.swFreq / 1000000, // shared_attrs中获取
"rfch": 0,
"powe": 22,
"modu": "LORA",
"datr": `SF${sf}BW${bw}`, // shared_attrs中获取
"codr": "4/5",
"fdev": 0,
"ipol": false,
"prea": Utils.timingPerformanceCalc(sf, bw, device?.shared_attrs?.swPeriod),
"ncrc": false
}
}
}
}
}
}else if (msgType===Utils.msgType.transParent||msgType===Utils.msgType.paras||msgType===Utils.msgType.user) {
if (msgType===Utils.msgType.transParent) { port=51
}else if (msgType===Utils.msgType.paras) { port=214}
return {
sleepTimeMs: sleepTime==undefined?0:sleepTime,
dnMsg: {
"version": "3.0",
"type": type==undefined?"data":type,
"if": "loraWAN",
"moteeui": device.eui,
"token": new Date().getTime(),
"userdata": {
"confirmed": confirmed==undefined?false:confirmed,
"fpend": false,
"port": port,
"TxUTCtime": "",
"payload": dnBuffer.toString("base64"),
"dnWaitms": dnWaitms==undefined?3000:dnWaitms,
"type": "data",
"intervalms": 0
}
}
}
}
}
}