王旭东
充电桩压测方案
海镕多联机空调485控制器方案说明
电能平台压测程序使用说明
中台软网关解析规则整理
星纵物联LoraWAN网关方案整理
国网376.1协议整理
云快充协议对接方案
科德4G水表离线问题排查
海镕3种空调平台与中台对接参数文档
电信AEP平台NB设备接入教程
牧原项目ARCM500蓝牙调试程序对接文档
中台-Expasion架构设计调整
中台-蓝牙调试小程序对接说明
ADW300-IOT报警新版参数设置(增加DO1和DO2联动)
迈格瑞能MPS微电网混合逆变器整理
微电网混合逆变器参数下发整理
云南交投充电桩协议对接方案
AAC系列空调控制器整理
云快充2.1协议对接方案(新增V2G协议)
本文档使用 MrDoc 发布
-
+
首页
星纵物联LoraWAN网关方案整理
星纵网关登录配置 https://www.milesight.cn/support/milesight-gateway-1 星纵网关联网配置 https://www.milesight.cn/support/milesight-gateway-32-0 网关内置NS添加节点设备 https://www.milesight.cn/support/milesight-gateway-4 如何在网关上使用编解码器 https://www.milesight.cn/support/milesight-gateway-5 星纵网关MQTT对接第三方平台 https://www.milesight.cn/support/milesight-gateway-7 ## 1. 上行报文格式 ### 1.1 入网/Join(没用到,无需配置) 设备加入网络时发布的事件。这是在第一个收到的上行链路(数据)帧之后发送的。示例: ```js { "applicationID":"1", // 应用ID "applicationName":"cloud", // 应用名称 "deviceName":"24e1641092176759", // 设备名称 "devEUI":"24e1641092176759", // 设备EUI "devAddr":"06df7989" // 分配的设备地址 } ``` ### 1.2 上报/Uplink 包含上行链路应用程序有效负载的数据和元数据,从官方文档上看这不是最终转发到第三方的数据,最终转发到第三方的数据是经过编解码器处理的。 #### Topic: 中台订阅下面的topic,星纵网关mqtt转发数据的主题都用此topic /milesight/uplink 原始报文示例: ```js { "applicationID":"1", // 应用ID "applicationName":"cloud", // 应用名称 "deviceName":"24e1641092176759",// 设备名称 "devEUI":"24e1641092176759", // 设备EUI "rxInfo": [{ "mac":"24e124fffef021be", // 网关ID "rssi":-57, // 信号强度 (dBm) "loRaSNR":10, // 信噪比 "name":"local_gateway", // 网关名称 "latitude":0, // 网关经度 "longitude":0, // 网关纬度 "altitude":0 // 网关海拔 }], "txInfo": // 节点信息 { "frequency":868300000, // 使用频率 "dataRate": { "modulation":"LORA", // LORA调制 "bandwidth":125, // 带宽 "spreadFactor":7 // 扩频因子 }, "adr":false, // 设备ADR状态 "codeRate":"4/5" // 编码率 }, "fCnt":0, // 帧计数 "fPort":85, // 应用端口 "data":"AWcAAAJoAA==" // base64编码(解密后的Hex数据就是仪表上传的实时数据,需解析) } ``` ### 1.3 确认/ACK(没用到,无需配置) 事件在下行链路帧得到节点设备确认后发布。示例: ```js { "applicationID": "1", // 应用ID "applicationName": "cloud", // 应用名称 "deviceName": "24e1641092176759", // 设备名称 "devEUI": "24e1641092176759", // 设备EUI "acknowledged": true, // 此数据帧是否需要确认 (e.g. 超时) "fCnt": 1 // 下行帧计数 } ``` ### 1.4 错误/Error(没用到,无需配置) 在与有效负载调度或处理相关的错误的情况下发布的事件。例如,当有效载荷超过最大有效载荷大小而无法调度时。示例: ```js { "applicationID": "1", // 应用ID "applicationName": "cloud", // 应用名称 "deviceName": "24e1641092176759", // 设备名称 "devEUI": "24e1641092176759", // 设备EUI "error": "...", // 错误信息 "fCnt": 1 // 出错帧的对应帧计数 (若有) } ``` ## 2. 下发数据到节点设备 #### Topic: 目前公司的电表未开发下行功能,这里预留 /milesight/downlink 示例: ```js { "devEUI": "009569000001357D", //目前不确定milesight是否对大小写做了适配 "confirmed": true, // 此下行数据包是否需要确认 "fport": 10, // 使用的应用端口 (必须大于0) "data": "...." // base64编码 } ``` ## 3. 网关配置示例 ### 3.1 配置mqtt转发 - mqtt连中台的20071端口 - 上行主题:/milesight/uplink - 下行主题:/milesight/downlink     ### 3.2 配置编解码器      ### 3.3 对接遇到的问题 - 搭环境联调测试,发现milesight网关是470的型号,只支持CN470的频段,而我们的表使用的是EU888的频段,两者通讯不上,需要更换使用470模组的表 - 发给客户的ADW300-LW编解码器没有生效,中台软网关日志里收到的是原始数据,使用milesight网关上的编解码器测试功能,发现原因是网关不能识别 Uint8Array 类型,执行编解码器出错,改成 Array 类型就可以了。以后编写新的编解码器可以在网关上利用测试功能测试执行结果。 ## 4.编解码器 ### 4.1 默认编解码器(仅转发原始数据) - Milesight网关添加设备节点时配置编解码器,从而将设备节点上传的原始数据经过解析后转发给第三方,编解码器使用js脚本,内容可自定义,不过为了中台对接方便,定了几点要求。 - 注意,中台收到的devEUI其中的字母会一律转为小写,和Milesight上配置的会有差异,因为网关转发数据中的devEUI字母都是小写。 ```js function Decode(fPort, bytes) { var decoded = {}; decoded.devEUI = LoRaObject.devEUI; decoded.rssi = LoRaObject.rxInfo[0].rssi; decoded.snr = LoRaObject.rxInfo[0].loRaSNR; decoded.fPort = LoRaObject.fPort; decoded.data = LoRaObject.data; return decoded; } ``` 网关将会转发如下格式数据 ```json { "devEUI":24e1611234567890 "rssi": -5, "snr": 11, "fPort": 10, "data": AXVkA2cgAQRoeg== } ``` ### 4.2 自定义编解码器 - 自定义编解码器,需要增加一个 **original** 参数区分是否是原始数据,为**true**则表示转发的是未解析过的原始数据,为**false**则表示转发的是解析好的数据,例如AWT100-LW透传回复的报文,**original**参数应置为**true**; - 转发解析后的数据,需要提供 **addr**,**productName**和**productKey**; - 若设备数据是分包上传,须加上总包数 **fragment** 和本次包序号 **fragNo** ,便于中台合并处理, 不分包无须此参数。 - 自定义编解码器需要注意,星纵网关不能识别 **Uint8Array** 等类型,写完编解码器配置到网关时建议使用网关上的测试功能测试能否执行通过。 - 星纵网关支持在编解码器增加时间戳:decoded.timestamp = new Date().getTime();网关有断点续传功能(关不掉),但由于尚不清楚如何对网关校时,暂不添加时间戳 ```js { "devEUI":24e1611234567890, //作为平台上的网关号 "original": false, //false-表示通过自定义的编解码器转发,数据是解析好的;true-表示转发的是未解析过的数据 "addr": "1", //作为平台上的仪表地址,若 original 为 false,则不能为空 "productName": "ADW300-LW", //对应中台的产品型号名称 "productKey": "ODMwNjQ3ODMyMzQ0NDYxMzEy", //对应中台产品型号 "rssi": -5, "snr": 11, "fPort": 10, "Ua": 221.0, "Ia": 1.2, ... "EPI": 1388.12, "fragment": 2, //总包数 "fragNo": 1 //例如ADW300-LW,数据是分两包上传,编解码器这边需要使用此参数区分本次转发的数据是第一包还是第二包 } ``` #### 4.2.1 LoRaObject 对象(原始数据) `LoRaObject` is global variable in javascript 可以理解为设备节点上传的原始数据,若编解码器有错误导致失效,网关将会转发原始数据。 ```js { "applicationID": 1, // application ID "applicationName": "cloud", // application name "deviceName": "24e1641092176759", // device name "devEUI": "24e1641092176759", // device EUI "time": "2020-0327T12:39:05.547336Z", // uplink receive time "rxInfo": [ // lorawan gateway information related to lora { "mac": "24e124fffef021be", // ID of the receiving gateway "rssi": -57, // signal strength (dBm) "loRaSNR": 10, // signal to noise ratio "name": "local_gateway", // name of the receiving gateway "latitude": 0, // latitude of the receiving gateway "longitude": 0, // longitude of the receiving gateway "altitude": 0 // altitude of the receiving gateway } ], "txInfo": { // lorawan node tx info "frequency": 868300000, // frequency used for transmission "dataRate": { "modulation": "LORA", // LORA module "bandwidth": 125, // bandwidth used for transmission "spreadFactor": 7 // spreadFactor used for transmission }, "adr": false, // device ADR status "codeRate": "4/5" // code rate }, "fCnt": 0, // frame counter "fPort": 85, // application port "data": "AWcAAAJoAA==" // base64 encoded payload (decrypted) } ``` #### 使用示例 ``` function Decode(fPort, bytes) { var decoder = {}; if (fPort == 85) { decoder.sensor = LoRaObject.devEUI; decoder.vendor = "Milesight-IoT"; decoder.time = LoRaObject.time; decoder.raw = bytes; } return decoder; } ``` #### 输出结果 ``` { "sensor": "24e1641092176759", "vendor": "Milesight-IoT", "time": "2020-0327T12:39:05.547336Z", "raw": "AWcAAAJoAA==" } ``` ### 4.3 编解码器可能用到的方法 ```js //解析无符号8位整型 function readUInt8(bytes) { return bytes[0] & 0xff; } //解析有符号8位整型 function readInt8(bytes) { var ref = readUInt8(bytes); return ref > 0x7f ? ref - 0x100 : ref; } //解析无符号16位整型,小端字节序 function readUInt16LE(bytes) { var value = (bytes[1] << 8) + bytes[0]; return value & 0xffff; } //解析有符号16位整型,小端字节序 function readInt16LE(bytes) { var ref = readUInt16LE(bytes); return ref > 0x7fff ? ref - 0x10000 : ref; } //解析无符号32位整型,小端字节序 function readUInt32LE(bytes) { var value = (bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]; return (value & 0xffffffff) >>> 0; } //解析有符号32位整型,小端字节序 function readInt32LE(bytes) { var ref = readUInt32LE(bytes); return ref > 0x7fffffff ? ref - 0x100000000 : ref; } //解析32位单精度浮点型,小端字节序 function readFloatLE(bytes) { var bits = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; var sign = bits >>> 31 === 0 ? 1.0 : -1.0; var e = (bits >>> 23) & 0xff; var m = e === 0 ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000; var f = sign * m * Math.pow(2, e - 150); return f; } ``` ### 4.4 已完成的编解码器 目前直传表仅对接ADW300-LW和ADW310-LW,仪表不支持透传,解析好的数据放到json对象第一级,编解码器解析的数据未处理变比问题,这是由于ADW300-LW第二包数据不带变比,没办法通过编解码器去乘变比,所以由中台软网关这边缓存变比,根据参数名判断是否乘变比。 若后续要对接多回路设备,再考虑新增标识符字段。 AWT100-LW方案,对接了ADL200-NK, 支持透传命令下发, 默认网关抄表时参数已乘好变比。 AWT100-LW上传的实时数据要加上**MeterState**属性, 0 表示设备离线 1 表示设备在线,离线数据中台不处理。 #### 4.4.1 ADW310-LW 数据一包上传,无须添加分包标识。 ```js function Decode(fPort, bytes) { //The data parsed here has not been multiplied by the voltage or current ratio; //The platform receiving the data handles the ratio adjustment. var decoded = {}; decoded.devEUI = LoRaObject.devEUI; decoded.rssi = LoRaObject.rxInfo[0].rssi; decoded.snr = LoRaObject.rxInfo[0].loRaSNR; decoded.fPort = LoRaObject.fPort; decoded.data = LoRaObject.data; decoded.original = false; decoded.addr = "1"; decoded.productName = "ADW310-LW"; decoded.productKey = "OTgzNTM5NjM2MzMyNjg3MzYw"; //Data decoder var idx = 0; while(idx < bytes.length){ var paramType = bytes[idx] & 0xff; var bb = bytes.slice(idx + 1 , idx + 5); switch(paramType){ case 1: //U decoded.U = readInt32LE(bb) / 10; break; case 2: //I decoded.I = readInt32LE(bb) / 100; break; case 3: //P decoded.P = readInt32LE(bb) / 1000; break; case 4: //Q decoded.Q = readInt32LE(bb) / 1000; break; case 5: //S decoded.S = readInt32LE(bb) / 1000; break; case 6: //PF decoded.PF = readInt32LE(bb) / 1000; break; case 7: //Fr decoded.Fr = readInt32LE(bb) / 100; break; case 8: //EPI decoded.EPI = readInt32LE(bb) / 100; break; case 9: //EPE decoded.EPE = readInt32LE(bb) / 100; break; case 10: //EPID decoded.EPID = readInt32LE(bb) / 1000; break; case 11: //PT decoded.PT = readUInt8(bb); break; case 12: //CT decoded.CT = readUInt8(bb); break; case 13: //Temp1 decoded.Temp1 = readInt32LE(bb) / 10; break; case 14: //Temp2 decoded.Temp2 = readInt32LE(bb) / 10; break; case 15: //DO decoded.DO1 = readUInt8(bb) & 1; decoded.DO2 = (readUInt8(bb) >> 1) & 1; break; case 16: //DI decoded.DI1 = readUInt8(bb) & 1; decoded.DI2 = (readUInt8(bb) >> 1) & 1; break; case 17: //EPIJ decoded.EPIJ = readInt32LE(bb) / 100; break; case 18: //EPIF decoded.EPIF = readInt32LE(bb) / 100; break; case 19: //EPIP decoded.EPIP = readInt32LE(bb) / 100; break; case 20: //EPIG decoded.EPIG = readInt32LE(bb) / 100; break; case 21: //EPEJ decoded.EPEJ = readInt32LE(bb) / 100; break; case 22: //EPEF decoded.EPEF = readInt32LE(bb) / 100; break; case 23: //EPEP decoded.EPEP = readInt32LE(bb) / 100; break; case 24: //EPEG decoded.EPEG = readInt32LE(bb) / 100; break; case 25: //EQ decoded.EQ = readInt32LE(bb) / 100; break; case 26: //EQJ decoded.EQJ = readInt32LE(bb) / 100; break; case 27: //EQF decoded.EQF = readInt32LE(bb) / 100; break; case 28: //EQP decoded.EQP = readInt32LE(bb) / 100; break; case 29: //EQG decoded.EQG = readInt32LE(bb) / 100; break; default: break; } idx = idx + 5; } return decoded; } function readUInt8(bytes) { return bytes[0] & 0xff; } function readInt8(bytes) { var ref = readUInt8(bytes); return ref > 0x7f ? ref - 0x100 : ref; } function readUInt16LE(bytes) { var value = (bytes[1] << 8) + bytes[0]; return value & 0xffff; } function readInt16LE(bytes) { var ref = readUInt16LE(bytes); return ref > 0x7fff ? ref - 0x10000 : ref; } function readUInt32LE(bytes) { var value = (bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]; return (value & 0xffffffff) >>> 0; } function readInt32LE(bytes) { var ref = readUInt32LE(bytes); return ref > 0x7fffffff ? ref - 0x100000000 : ref; } function readFloatLE(bytes) { var bits = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; var sign = bits >>> 31 === 0 ? 1.0 : -1.0; var e = (bits >>> 23) & 0xff; var m = e === 0 ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000; var f = sign * m * Math.pow(2, e - 150); return f; } ``` #### 4.4.2 ADW300-LW 数据分为两包上传,需要添加分包标识,根据首个字节区分是第一包还是第二包。 ```js function Decode(fPort, bytes) { //The data parsed here has not been multiplied by the voltage or current ratio; //The platform receiving the data handles the ratio adjustment. var decoded = {}; decoded.devEUI = LoRaObject.devEUI; decoded.rssi = LoRaObject.rxInfo[0].rssi; decoded.snr = LoRaObject.rxInfo[0].loRaSNR; decoded.fPort = LoRaObject.fPort; decoded.data = LoRaObject.data; decoded.original = false; decoded.addr = "1"; decoded.productName = "ADW300-LW"; decoded.productKey = "ODMwNjQ3ODMyMzQ0NDYxMzEy"; //Data decoder decoded.fragment = 2; var type = bytes[0] & 0xff; if(type === 1){ decoded.fargNo = 1; }else{ decoded.fargNo = 2; } var idx = 0; while(idx < bytes.length){ var paramType = bytes[idx] & 0xff; var bb = bytes.slice(idx + 1 , idx + 5); bb = reverseBytes(bb); switch(paramType){ case 1: //PT decoded.PT = readUInt8(bb); break; case 2: //CT decoded.CT = readUInt8(bb); break; case 3: //TempN decoded.TempN = readInt16LE(bb) / 10; break; case 4: //Ua decoded.Ua = readUInt16LE(bb) / 10; break; case 5: //Ub decoded.Ub = readUInt16LE(bb) / 10; break; case 6: //Uc decoded.Uc = readUInt16LE(bb) / 10; break; case 7: //Uab decoded.Uab = readUInt16LE(bb) / 10; break; case 8: //Ubc decoded.Ubc = readUInt16LE(bb) / 10; break; case 9: //Uca decoded.Uca = readUInt16LE(bb) / 10; break; case 10: //Ia decoded.Ia = readUInt16LE(bb) / 100; break; case 11: //Ib decoded.Ib = readUInt16LE(bb) / 100; break; case 12: //Ic decoded.Ic = readUInt16LE(bb) / 100; break; case 13: //Pa decoded.Pa = readInt32LE(bb) / 1000; break; case 14: //Pb decoded.Pb = readInt32LE(bb) / 1000; break; case 15: //Pc decoded.Pc = readInt32LE(bb) / 1000; break; case 16: //P decoded.P = readInt32LE(bb) / 1000; break; case 17: //Qa decoded.Qa = readInt32LE(bb) / 1000; break; case 18: //Qb decoded.Qb = readInt32LE(bb) / 1000; break; case 19: //Qc decoded.Qc = readInt32LE(bb) / 1000; break; case 20: //Q decoded.Q = readInt32LE(bb) / 1000; break; case 21: //Sa decoded.Sa = readUInt32LE(bb) / 1000; break; case 22: //Sb decoded.Sb = readUInt32LE(bb) / 1000; break; case 23: //Sc decoded.Sc = readUInt32LE(bb) / 1000; break; case 24: //S decoded.S = readUInt32LE(bb) / 1000; break; case 25: //PFa decoded.PFa = readUInt16LE(bb) / 1000; break; case 26: //PFb decoded.PFb = readUInt16LE(bb) / 1000; break; case 27: //PFc decoded.PFc = readUInt16LE(bb) / 1000; break; case 28: //PF decoded.PF = readUInt16LE(bb) / 1000; break; case 29: //DI decoded.DI1 = readUInt8(bb) & 1; decoded.DI2 = (readUInt8(bb) >> 1) & 1; decoded.DI3 = (readUInt8(bb) >> 2) & 1; decoded.DI4 = (readUInt8(bb) >> 3) & 1; break; case 30: //EP decoded.EP = readUInt32LE(bb) / 100; break; case 31: //EPI decoded.EPI = readUInt32LE(bb) / 100; break; case 32: //EPE decoded.EPE = readUInt32LE(bb) / 100; break; case 33: //EQL decoded.EQL = readUInt32LE(bb) / 100; break; case 34: //EQC decoded.EQC = readUInt32LE(bb) / 100; break; case 50: //MEPIMD decoded.MEPIMD = readUInt32LE(bb) / 1000; break; case 52: //UaTHD decoded.UaTHD = readUInt16LE(bb) / 100; break; case 53: //UbTHD decoded.UbTHD = readUInt16LE(bb) / 100; break; case 54: //UcTHD decoded.UcTHD = readUInt16LE(bb) / 100; break; case 55: //IaTHD decoded.IaTHD = readUInt16LE(bb) / 100; break; case 56: //IbTHD decoded.IbTHD = readUInt16LE(bb) / 100; break; case 57: //IcTHD decoded.IcTHD = readUInt16LE(bb) / 100; break; case 58: //EPID decoded.EPID = readUInt32LE(bb) / 1000; break; case 59: //Uub decoded.Uub = readUInt16LE(bb) / 100; break; case 60: //Iub decoded.Iub = readUInt16LE(bb) / 100; break; case 61: //TempA decoded.TempA = readInt16LE(bb) / 10; break; case 62: //TempB decoded.TempB = readInt16LE(bb) / 10; break; case 63: //TempC decoded.TempC = readInt16LE(bb) / 10; break; case 64: //EPIJ decoded.EPIJ = readUInt32LE(bb) / 100; break; case 65: //EPIF decoded.EPIF = readUInt32LE(bb) / 100; break; case 66: //EPIP decoded.EPIP = readUInt32LE(bb) / 100; break; case 67: //EPIG decoded.EPIG = readUInt32LE(bb) / 100; break; case 68: //Lg decoded.Lg = readUInt16LE(bb); break; default: break; } idx = idx + 5; } return decoded; } function reverseBytes(bytes){ var bb = new Array(bytes.length); for(var i=0; i < bytes.length ; i++){ bb[i] = bytes[bytes.length-i-1]; } return bb; } function readUInt8(bytes) { return bytes[0] & 0xff; } function readInt8(bytes) { var ref = readUInt8(bytes); return ref > 0x7f ? ref - 0x100 : ref; } function readUInt16LE(bytes) { var value = (bytes[1] << 8) + bytes[0]; return value & 0xffff; } function readInt16LE(bytes) { var ref = readUInt16LE(bytes); return ref > 0x7fff ? ref - 0x10000 : ref; } function readUInt32LE(bytes) { var value = (bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]; return (value & 0xffffffff) >>> 0; } function readInt32LE(bytes) { var ref = readUInt32LE(bytes); return ref > 0x7fffffff ? ref - 0x100000000 : ref; } function readFloatLE(bytes) { var bits = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; var sign = bits >>> 31 === 0 ? 1.0 : -1.0; var e = (bits >>> 23) & 0xff; var m = e === 0 ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000; var f = sign * m * Math.pow(2, e - 150); return f; } ``` #### 4.4.3 AWT100-LW网关方案 - 编解码器需要增加`original`标识,中台以此区分数据是否是解析过的,91报文需设为false - 编解码器需要增加`productKey`属性,用于中台同步产品型号 - 建议在网关模板层面以及编解码器处理好变比问题,最终上传平台一次值 #### 4.4.3.1 接ADL200-NK ```js function Decode(fPort, bytes) { //ADL200-NK var decoded = {}; decoded.devEUI = LoRaObject.devEUI; decoded.rssi = LoRaObject.rxInfo[0].rssi; decoded.snr = LoRaObject.rxInfo[0].loRaSNR; decoded.fPort = LoRaObject.fPort; decoded.data = LoRaObject.data;//原始数据,0x90透传报文需要用到 decoded.original = true;//中台以此区分报文是否是原始的,还是已经解析过的 decoded.productKey = "ODAzMTU1MDA1ODcyMDc0NzUy";//中台产品型号productKey,用于中台同步型号 decoded.productName = "ADL200-NK";//中台产品名称 //Data decoder if(bytes[3] === 0x91)//0x91报文数据在这边解析好,其他如0x90透传报文不做处理 { decoded.original = false;//91报文是解析过的,这里修改标识 decoded.addr = bytes[4];//仪表地址,中台根据此地址上报拓扑 decoded.MeterAddr = bytes[4]; //modbus address decoded.MeterState = bytes[6]; //0:offline 1:online decoded.EPI = bytesToFloat(bytes.slice(7, 11),2); decoded.EPIJ = bytesToFloat(bytes.slice(11, 15),2); decoded.EPIF = bytesToFloat(bytes.slice(15, 19),2); decoded.EPIP = bytesToFloat(bytes.slice(19, 23),2); decoded.EPIG = bytesToFloat(bytes.slice(23, 27),2); decoded.U = bytesToFloat(bytes.slice(27, 31),1); decoded.I = bytesToFloat(bytes.slice(31, 35),1); decoded.P = bytesToFloat(bytes.slice(35, 39),3); decoded.Q = bytesToFloat(bytes.slice(39, 43),1); decoded.S = bytesToFloat(bytes.slice(43, 47),1); decoded.PF = bytesToFloat(bytes.slice(47, 51),1); decoded.EPE = bytesToFloat(bytes.slice(51, 55),2); decoded.AlarmA = bytesToFloat(bytes.slice(55, 59),1); decoded.AlarmB = bytesToFloat(bytes.slice(59, 63),1); decoded.PowerLimit = bytesToFloat(bytes.slice(63, 67),2); decoded.BuyTimes = bytesToFloat(bytes.slice(67, 71),0); decoded.Balance = bytesToFloat(bytes.slice(71, 75),2); decoded.PriceSharp = bytesToFloat(bytes.slice(75, 79),2); decoded.PricePeak = bytesToFloat(bytes.slice(79, 83),2); decoded.PriceFlat = bytesToFloat(bytes.slice(83, 87),2); decoded.PriceVally = bytesToFloat(bytes.slice(87, 91),2); decoded.AlarmPower = bytesToFloat(bytes.slice(91, 95),2); decoded.PRESTATE = bytesToFloat(bytes.slice(95, 99),0); } return decoded; } function bytesToFloat(bytes,scale) { var bits = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; var sign = bits >>> 31 === 0 ? 1.0 : -1.0; var e = (bits >>> 23) & 0xff; var m = e === 0 ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000; var f = sign * m * Math.pow(2, e - 150); f= parseFloat(f.toFixed(scale)); return f; } ```
王旭东
2025年7月16日 21:02
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码