王旭东
充电桩压测方案
海镕多联机空调485控制器方案说明
电能平台压测程序使用说明
中台软网关解析规则整理
星纵物联LoraWAN网关方案整理
国网376.1协议整理
云快充协议对接方案
科德4G水表离线问题排查
海镕3种空调平台与中台对接参数文档
电信AEP平台NB设备接入教程
牧原项目ARCM500蓝牙调试程序对接文档
中台-Expasion架构设计调整
中台-蓝牙调试小程序对接说明
ADW300-IOT报警新版参数设置(增加DO1和DO2联动)
迈格瑞能MPS微电网混合逆变器整理
微电网混合逆变器参数下发整理
云南交投充电桩协议对接方案
AAC系列空调控制器整理
云快充2.1协议对接方案(新增V2G协议)
本文档使用 MrDoc 发布
-
+
首页
牧原项目ARCM500蓝牙调试程序对接文档
### 修改记录 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <style> table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid black; padding: 8px; text-align: left; } th { text-align: center; } .fixed-width { width: 150px; /* 设置一个合适的固定长度 */ white-space: nowrap; } .wrap-text { white-space: pre-wrap; text-align: left; /* 第二列除表头外左端对齐 */ } .empty-row { height: 20px; /* 调整空白行高度 */ } ul { margin: 0; padding-left: 20px; } </style> </head> <body> <table> <tr> <th>时间</th> <th>改动详情</th> </tr> <tr> <td class="fixed-width">2025-02-18</td> <td> <ul> <li>修复域名写入寄存器中高低字节位置反的问题</li> <li>保护设置 3.1、3.2 读取和下发增加第二路过流报警设置</li> <li>新增 3.12、3.13、3.14 第二路过流A、B、C单独的报警设置项</li> </ul> </td> </tr> <tr> <td class="fixed-width"></td> <td></td> </tr> </table> </body> </html> ### 1.主电参量  该界面点进来默认下发一次读取命令,读取命令的处理流程如下: - 第一步,小程序调用`parseDownAdapter`方法获取抄读主电参量的透传报文,返回的结果中有多条报文,用`&&`隔开 - 第二步,小程序根据`&&`拆分报文,按顺序下发到仪表,缓存仪表回复的报文,用`&&`拼接,注意必须要上一条报文收到仪表回复才能下发下一条报文,最终拼接完整的回复报文调用`parseUpAdapter`方法解析,中间若有下发的报文未收到回复,则判定本次读取操作失败 #### 1.1 获取抄读主电参量报文 ```js 调用`parseDownAdapter`方法入参 method 标明本条命令的用途 addr 仪表modbus协议地址,协议暂定用255 { "method":"READ_MAIN_PARAM", "addr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 抄读主电参量的透传命令报文,报文中间用`&&`隔开 { "res": 1, "method":"READ_MAIN_PARAM", "data": "01030000000AC5CD&&01030013001D7406&&01030032006125ED&&01030062002A65CB" } 抄表段 第一段 0x0000-0x0009 读0x0A个寄存器 01 03 00 00 00 0A C5 CD 第二段 0x0013-0x002F 读0x1D个寄存器 01 03 00 13 00 1D 74 06 第三段 0x0032-0x0061 读0x30个寄存器 01 03 00 32 00 61 25 ED 第四段 0x0062-0x008B 读0x2A个寄存器 01 03 00 62 00 2A 65 CB 第五段 0x1028-0x1029 读0x02个寄存器 01 03 10 28 00 02 40 C3 第六段 特殊命令 01 11 FE DC BA 98 76 54 32 10 06 E5 ``` #### 1.2 解析抄读到的主电参量报文 ```js 调用`parseUpAdapter`方法入参 method 标明本条命令的用途 data 仪表回复的所有报文,按下发的顺序用`&&`拼接 { "method":"READ_MAIN_PARAM", "data": "01030000000AC5CD&&01030013001D7406&&01030032006125ED&&01030062002A65CB" } 返回结果 res 处理结果 0-失败 1-成功 参数名定义 MeterSn 仪表序列号 MeterTime 表端时间 Lg1FauSta 漏电接线 0-正常 1-异常 T1FauSta 温度1接线 0-正常 1-异常 T2FauSta 温度2接线 0-正常 1-异常 T3FauSta 温度3接线 0-正常 1-异常 T4FauSta 温度4接线 0-正常 1-异常 Lg1AlaSta 漏电报警 0-正常 1-异常 T1AlaSta 温度1报警 0-正常 1-异常 T2AlaSta 温度2报警 0-正常 1-异常 T3AlaSta 温度3报警 0-正常 1-异常 T4AlaSta 温度4报警 0-正常 1-异常 UHighSta 过压 0-正常 1-异常 ULowSta 欠压 0-正常 1-异常 IHighSta 过流 0-正常 1-异常 Lg1 漏电测量值 Temp1 温度1测量值 Temp2 温度2测量值 Temp3 温度3测量值 Temp4 温度4测量值 Lg1AlarmVal 漏电报警值 T1AlarmVal 温度1报警值 T2AlarmVal 温度2报警值 T3AlarmVal 温度3报警值 T4AlarmVal 温度4报警值 Fr 频率 Ua A相电压测量值 Ub B相电压测量值 Uc C相电压测量值 UaHighAlarmVal A相过压报警值 UbHighAlarmVal B相过压报警值 UcHighAlarmVal C相过压报警值 UaLowAlarmVal A相欠压报警值 UbLowAlarmVal B相欠压报警值 UcLowAlarmVal C相欠压报警值 Ia A相电流测量值 Ib B相电流测量值 Ic C相电流测量值 IaHighAlarmVal A相过流报警值 IbHighAlarmVal B相过流报警值 IcHighAlarmVal C相过流报警值 Pa A相有功功率 Pb B相有功功率 Pc C相有功功率 P 总有功功率 Qa A相无功功率 Qb B相无功功率 Qc C相无功功率 Q 总无功功率 Sa A相视在功率 Sb B相视在功率 Sc C相视在功率 S 总视在功率 PFa A相功率因数 PFb B相功率因数 PFc C相功率因数 PF 总功率因数 EPI 正向总有功电能 EPE 反向总有功电能 EQL 正向总无功电能 EQC 反向总无功电能 ES 总视在电能 DI1 开关量输入1 DI2 开关量输入2 DO1 开关量输出1 DO2 开关量输出2 softCode 软件编号 softVersion 软件版本号 示例 { "res": 1, "data": { "MeterSn": "20240915652400", "MeterTime": "2025-01-17 15:30:42", "Lg1FauSta": 0, "T1FauSta": 0, "T2FauSta": 0, "T3FauSta": 0, "T4FauSta": 0, "Lg1AlaSta": 0, "T1AlaSta": 0, "T2AlaSta": 0, "T3AlaSta": 0, "T4AlaSta": 0, "UHighSta": 0, "ULowSta": 0, "IHighSta": 0, "Lg1": 300, "Temp1": 26.1, "Temp2": 26.1, "Temp3": 26.1, "Temp4": 26.1, "Lg1AlarmVal": 500, "T1AlarmVal": 60, "T2AlarmVal": 0, "T3AlarmVal": 0, "T4AlarmVal": 0, "Fr": 49.99, "Ua": 220.1, "Ub": 220.2, "Uc": 220.3, "UaHighAlarmVal": 0, "UbHighAlarmVal": 0, "UcHighAlarmVal": 0, "UaLowAlarmVal": 0, "UbLowAlarmVal": 0, "UcLowAlarmVal": 0, "Ia": 0, "Ib": 1.1, "Ic": 0, "IaHighAlarmVal": 0, "IbHighAlarmVal": 0, "IcHighAlarmVal": 0, "Pa": 0, "Pb": 0, "Pc": 0, "P": 0, "Qa": 0, "Qb": 0, "Qc": 0, "Q": 0, "Sa": 0, "Sb": 0, "Sc": 0, "S": 0, "PFa": 0.999, "PFb": 0.997, "PFc": 0.998, "PF": 0.999, "EPI": 0, "EPE": 0, "EQL": 0, "EQC": 0, "ES": 0, "DI1": 0, "DI2": 0, "DO1": 0, "DO2": 0, "softCode": 2, "softVersion": 1140 } } ``` #### 1.3 读取软件编号和软件版本号 ```js 调用`parseDownAdapter`方法入参 method 标明本条命令的用途 addr 仪表modbus协议地址,协议暂定用255 { "method":"READ_SOFT_CODE", "addr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 抄读软件编号和软件版本号的透传命令报文 { "res": 1, "method":"READ_SOFT_CODE", "data": "0111FEDCBA987654321006E5" } ``` #### 1.4 解析抄读软件编号和软件版本号回复的报文 ```js 调用`parseUpAdapter`方法入参 method 标明本条命令的用途 data 仪表回复的所有报文,按下发的顺序用`&&`拼接 { "method":"READ_SOFT_CODE", "data": "0111FEDCBA987654321006E5" } 返回结果 res 处理结果 0-失败 1-成功 参数名定义 softCode 软件编号 softVersion 软件版本号 示例 { "res": 1, "data": { "softCode": 2, "softVersion": 1140 } } ``` ### 2.新增电参量(出线)  该界面点进来默认下发一次读取命令,读取命令的处理流程如下: - 第一步,小程序调用`parseDownAdapter`方法获取抄读出线电参量的透传报文,返回的结果中有两条报文,用`&&`隔开 - 第二步,小程序根据`&&`拆分报文,按顺序下发到仪表,缓存仪表回复的报文,用`&&`拼接,注意必须要上一条报文收到仪表回复才能下发下一条报文,最终拼接完整的回复报文调用`parseUpAdapter`方法解析,中间若有下发的报文未收到回复,则判定本次读取操作失败 #### 2.1 获取抄读出线电参量报文 ```js 调用`parseDownAdapter`方法入参 method 标明本条命令的用途 addr 仪表modbus协议地址,协议暂定用255 { "method":"READ_OUTPUT_PARAM", "addr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 抄读出线电参量的透传命令报文,报文中间用`&&`隔开 { "res": 1, "method":"READ_OUTPUT_PARAM", "data": "01030130000C443C&&010301420028E43C" } 抄表段 第一段 0x0130-0x013B 读0x0C个寄存器 01 03 01 30 00 0C 44 3C 第二段 0x0142-0x0169 读0x28个寄存器 01 03 01 42 00 28 E4 3C 第三段 0x0103 读0x01个寄存器 01 03 01 03 00 01 E4 3C ``` #### 2.2 解析抄读到的出线电参量报文 ```js 调用`parseUpAdapter`方法入参 method 标明本条命令的用途 data 仪表回复的所有报文,按下发的顺序用`&&`拼接 { "method":"READ_OUTPUT_PARAM", "data": "01030130000C443C&&010301420028E43C" } 返回结果 res 处理结果 0-失败 1-成功 参数名定义 OPTIa 出线A相电流测量值 OPTIb 出线B相电流测量值 OPTIc 出线C相电流测量值 OPTIaAlarmVal 出线A相过流时报警测量值 OPTIbAlarmVal 出线B相过流时报警测量值 OPTIcAlarmVal 出线C相过流时报警测量值 OPTPa 出线A相有功功率 OPTPb 出线B相有功功率 OPTPc 出线C相有功功率 OPTP 出线总有功功率 OPTQa 出线A相无功功率 OPTQb 出线B相无功功率 OPTQc 出线C相无功功率 OPTQ 出线总无功功率 OPTSa 出线A相视在功率 OPTSb 出线B相视在功率 OPTSc 出线C相视在功率 OPTS 出线总视在功率 OPTPFa 出线A相功率因数 OPTPFb 出线B相功率因数 OPTPFc 出线C相功率因数 OPTPF 出线总功率因数 OPTEPIA 出线A相输入有功电能 OPTEPIB 出线B相输入有功电能 OPTEPIC 出线C相输入有功电能 OPTEPI 出线总有功电能 OPTIaHighSta 出线A相过流状态 0-正常 1-报警 OPTIbHighSta 出线B相过流状态 0-正常 1-报警 OPTIcHighSta 出线C相过流状态 0-正常 1-报警 示例 { "res": 1, "data": { "OPTIa": 0, "OPTIb": 0, "OPTIc": 0, "OPTIaAlarmVal": 0, "OPTIbAlarmVal": 0, "OPTIcAlarmVal": 0, "OPTPa": 0, "OPTPb": 0, "OPTPc": 0, "OPTP": 0, "OPTQa": 0, "OPTQb": 0, "OPTQc": 0, "OPTQ": 0, "OPTSa": 0, "OPTSb": 0, "OPTSc": 0, "OPTS": 0, "OPTPFa": 0, "OPTPFb": 0, "OPTPFc": 0, "OPTPF": 0, "OPTEPIA": 0, "OPTEPIB": 0, "OPTEPIC": 0, "OPTEPI": 0, "OPTIaHighSta": 0, "OPTIbHighSta": 0, "OPTIcHighSta": 0 } } ``` ### 3.保护设置  保护设置页面的设置项分为:基本设置(保护类型、额定电压、额定电流)、漏电、温度1、温度2、温度3、温度4、过压、欠压、过流,每个设置项可以单独下发,也可以统一读取和统一下发 进入页面先默认下发一次全部读取命令,读取成功之后才能下发设置命令,非读取的命令小程序下发报文到仪表之后根据仪表的回复自行判断是否下发成功,若未回复或收到的回复报文中的功能码(报文的第二个字节)与下发的报文不相等,则认定下发失败 命令`method`汇总如下 | method | 说明 | | --- | --- | | READ_ALARM_SET | 保护设置全部读取 | | WRITE_ALARM_SET | 保护设置全部下发 | | WRITE_BASIC_SET | 基本设置(保护类型、额定电压、额定电流) | | WRITE_LG1_SET | 漏电保护设置 | | WRITE_TEMP1_SET | 温度1保护设置 | | WRITE_TEMP2_SET | 温度2保护设置 | | WRITE_TEMP3_SET | 温度3保护设置 | | WRITE_TEMP4_SET | 温度4保护设置 | | WRITE_UHIGH_SET | 过压保护设置 | | WRITE_ULOW_SET | 欠压保护设置 | | WRITE_IHIGH_SET | 过流保护设置 | #### 3.1 保护设置全部读取 读取的流程同读取主电参量 ##### 3.1.1 获取读保护设置的报文 ```js 调用`parseDownAdapter`方法入参 method 标明本条命令的用途 addr 仪表modbus协议地址,协议暂定用255 { "method":"READ_ALARM_SET", "addr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 抄读全部保护设置的透传命令报文,报文中间用`&&`隔开 { "res": 1, "method":"READ_ALARM_SET", "data": "0103102F0019B109&&01031231000DD0B8" } 抄表段 第一段 0x102F-0x1047 读0x19个寄存器 01 03 10 2F 00 19 B1 09 第二段 0x1231-0x123D 读0x0D个寄存器 01 03 12 31 00 0D D0 B8 第三段 0x125C-0x125E 读0x03个寄存器 01 03 12 5C 00 03 C0 A1 第四段 0x1262-0x1264 读0x03个寄存器 01 03 12 62 00 03 A1 6D 第五段 0x1268-0x126A 读0x03个寄存器 01 03 12 68 00 03 81 6F ``` ##### 3.1.2 解析抄读保护设置命令回复的报文 ```js 调用`parseUpAdapter`方法入参 method 标明本条命令的用途 data 仪表回复的所有报文,按下发的顺序用`&&`拼接 { "method":"READ_ALARM_SET", "data": "0103102F0019B109&&01031231000DD0B8&&0103125C0003C0A1&&010312620003A16D&&010312680003816F" } 返回结果 res 处理结果 0-失败 1-成功 参数名定义 Lg1HighProtectType 漏电保护类型 1-基波 0-全波 URange 额定电压 IRange 额定电流 Lg1HighSw 漏电 保护开关 0-关 1-开 Lg1HighDO1 漏电 DO1关联 0-关 1-开 Lg1HighDO2 漏电 DO2关联 0-关 1-开 Lg1HighVal02 漏电 保护设定值 Lg1HighTim 漏电 报警延时 T1HighSw 温度1 保护开关 0-关 1-开 T1HighDO1 温度1 DO1关联 0-关 1-开 T1HighDO2 温度1 DO2关联 0-关 1-开 T1HighVal02 温度1 报警设定值 T1HighTim 温度1 报警延时 T2HighSw 温度2 保护开关 0-关 1-开 T2HighDO1 温度2 DO1关联 0-关 1-开 T2HighDO2 温度2 DO2关联 0-关 1-开 T2HighVal02 温度2 报警设定值 T2HighTim 温度2 报警延时 T3HighSw 温度3 保护开关 0-关 1-开 T3HighDO1 温度3 DO1关联 0-关 1-开 T3HighDO2 温度3 DO2关联 0-关 1-开 T3HighVal02 温度3 报警设定值 T3HighTim 温度3 报警延时 T4HighSw 温度4 保护开关 0-关 1-开 T4HighDO1 温度4 DO1关联 0-关 1-开 T4HighDO2 温度4 DO2关联 0-关 1-开 T4HighVal02 温度4 报警设定值 T4HighTim 温度4 报警延时 UHighSw 过压 保护开关 0-关 1-开 UHighDO1 过压 DO1关联 0-关 1-开 UHighDO2 过压 DO2关联 0-关 1-开 UHighVal02 过压 报警设定值 UHighTim 过压 报警延时 ULowSw 欠压 保护开关 0-关 1-开 ULowDO1 欠压 DO1关联 0-关 1-开 ULowDO2 欠压 DO2关联 0-关 1-开 ULowVal02 欠压 报警设定值 ULowTim 欠压 报警延时 IHighSw 过流 保护开关 0-关 1-开 IHighDO1 过流 DO1关联 0-关 1-开 IHighDO2 过流 DO2关联 0-关 1-开 IHighVal02 过流 报警设定值 IHighTim 过流 报警延时 OPTIaHighDO1 第二路过流A DO1关联 0-关 1-开 OPTIaHighDO2 第二路过流A DO2关联 0-关 1-开 OPTIaHighVal02 第二路过流A 报警设定值 OPTIaHighTim 第二路过流A 报警延时 OPTIbHighDO1 第二路过流B DO1关联 0-关 1-开 OPTIbHighDO2 第二路过流B DO2关联 0-关 1-开 OPTIbHighVal02 第二路过流B 报警设定值 OPTIbHighTim 第二路过流B 报警延时 OPTIcHighDO1 第二路过流C DO1关联 0-关 1-开 OPTIcHighDO2 第二路过流C DO2关联 0-关 1-开 OPTIcHighVal02 第二路过流C 报警设定值 OPTIcHighTim 第二路过流C 报警延时 示例 { "res": 1, "data": { "Lg1HighProtectType": 0, "URange": 220, "IRange": 100, "Lg1HighSw": 0, "Lg1HighDO1": 0, "Lg1HighDO2": 0, "Lg1HighVal02": 300, "Lg1HighTim": 5, "T1HighSw": 0, "T1HighDO1": 0, "T1HighDO2": 0, "T1HighVal02": 60, "T1HighTim": 5, "T2HighSw": 0, "T2HighDO1": 0, "T2HighDO2": 0, "T2HighVal02": 60, "T2HighTim": 5, "T3HighSw": 0, "T3HighDO1": 0, "T3HighDO2": 0, "T3HighVal02": 60, "T3HighTim": 5, "T4HighSw": 0, "T4HighDO1": 0, "T4HighDO2": 0, "T4HighVal02": 60, "T4HighTim": 5, "UHighSw": 0, "UHighDO1": 0, "UHighDO2": 0, "UHighVal02": 110, "UHighTim": 5, "ULowSw": 0, "ULowDO1": 0, "ULowDO2": 0, "ULowVal02": 80, "ULowTim": 5, "IHighSw": 0, "IHighDO1": 0, "IHighDO2": 0, "IHighVal02": 90, "IHighTim": 5, "OPTIaHighDO1": 0, "OPTIaHighDO2": 0, "OPTIaHighVal02": 90, "OPTIaHighTim": 5, "OPTIbHighDO1": 0, "OPTIbHighDO2": 0, "OPTIbHighVal02": 90, "OPTIbHighTim": 5, "OPTIcHighDO1": 0, "OPTIcHighDO2": 0, "OPTIcHighVal02": 90, "OPTIcHighTim": 5 } } ``` #### 3.2 保护设置全部下发 保护设置的处理流程如下: - 第一步,小程序调用`parseDownAdapter`方法获取要下发的透传报文,返回的结果中有两条报文,用`&&`隔开, - 第二步,小程序依次下发每条报文,若有报文超时未收到回复,则直接判定下发失败,全部报文收到回复则判定下发成功 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_ALARM_SET", "addr": 1, "Lg1HighProtectType": 0, "URange": 220, "IRange": 100, "Lg1HighSw": 0, "Lg1HighDO1": 0, "Lg1HighDO2": 0, "Lg1HighVal02": 300, "Lg1HighTim": 5, "T1HighSw": 0, "T1HighDO1": 0, "T1HighDO2": 0, "T1HighVal02": 60, "T1HighTim": 5, "T2HighSw": 0, "T2HighDO1": 0, "T2HighDO2": 0, "T2HighVal02": 60, "T2HighTim": 5, "T3HighSw": 0, "T3HighDO1": 0, "T3HighDO2": 0, "T3HighVal02": 60, "T3HighTim": 5, "T4HighSw": 0, "T4HighDO1": 0, "T4HighDO2": 0, "T4HighVal02": 60, "T4HighTim": 5, "UHighSw": 0, "UHighDO1": 0, "UHighDO2": 0, "UHighVal02": 110, "UHighTim": 5, "ULowSw": 0, "ULowDO1": 0, "ULowDO2": 0, "ULowVal02": 80, "ULowTim": 5, "IHighSw": 0, "IHighDO1": 0, "IHighDO2": 0, "IHighVal02": 0, "IHighTim": 5, "OPTIaHighDO1": 0, "OPTIaHighDO2": 0, "OPTIaHighVal02": 90, "OPTIaHighTim": 5, "OPTIbHighDO1": 0, "OPTIbHighDO2": 0, "OPTIbHighVal02": 90, "OPTIbHighTim": 5, "OPTIcHighDO1": 0, "OPTIcHighDO2": 0, "OPTIcHighVal02": 90, "OPTIcHighTim": 5 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_ALARM_SET", "data": "0103102F0019B109&&01031231000DD0B8&&01031231000DD0B8&&01031231000DD0B8&&01031231000DD0B8" } ``` #### 3.3 基本设置(保护类型、额定电流) 流程同3.2 ```js 调用`parseDownAdapter`方法入参 额定电压只读,不可写 参数名定义同3.1.2 { "method": "WRITE_BASIC_SET", "addr": 1, "Lg1HighProtectType": 0, "URange": 220, "IRange": 100 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_BASIC_SET", "data": "0103102F0019B109&&01031231000DD0B8" } ``` #### 3.4 漏电设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_LG1_SET", "addr": 1, "Lg1HighVal02": 300, "Lg1HighTim": 5, "Lg1HighSw": 0, "T1HighSw": 0, "T2HighSw": 0, "T3HighSw": 0, "T4HighSw": 0, "Lg1HighDO1": 0, "T1HighDO1": 0, "T2HighDO1": 0, "T3HighDO1": 0, "T4HighDO1": 0, "Lg1HighDO2": 0, "T1HighDO2": 0, "T2HighDO2": 0, "T3HighDO2": 0, "T4HighDO2": 0 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_LG1_SET", "data": "0103102F0019B109&&01031231000DD0B8" } ``` #### 3.5 温度1设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_TEMP1_SET", "addr": 1, "T1HighVal02": 300, "T1HighTim": 5, "Lg1HighSw": 0, "T1HighSw": 0, "T2HighSw": 0, "T3HighSw": 0, "T4HighSw": 0, "Lg1HighDO1": 0, "T1HighDO1": 0, "T2HighDO1": 0, "T3HighDO1": 0, "T4HighDO1": 0, "Lg1HighDO2": 0, "T1HighDO2": 0, "T2HighDO2": 0, "T3HighDO2": 0, "T4HighDO2": 0 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_TEMP1_SET", "data": "0103102F0019B109&&01031231000DD0B8" } ``` #### 3.6 温度2设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_TEMP2_SET", "addr": 1, "T2HighVal02": 300, "T2HighTim": 5, "Lg1HighSw": 0, "T1HighSw": 0, "T2HighSw": 0, "T3HighSw": 0, "T4HighSw": 0, "Lg1HighDO1": 0, "T1HighDO1": 0, "T2HighDO1": 0, "T3HighDO1": 0, "T4HighDO1": 0, "Lg1HighDO2": 0, "T1HighDO2": 0, "T2HighDO2": 0, "T3HighDO2": 0, "T4HighDO2": 0 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_TEMP2_SET", "data": "0103102F0019B109&&01031231000DD0B8" } ``` #### 3.7 温度3设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_TEMP3_SET", "addr": 1, "T3HighVal02": 300, "T3HighTim": 5, "Lg1HighSw": 0, "T1HighSw": 0, "T2HighSw": 0, "T3HighSw": 0, "T4HighSw": 0, "Lg1HighDO1": 0, "T1HighDO1": 0, "T2HighDO1": 0, "T3HighDO1": 0, "T4HighDO1": 0, "Lg1HighDO2": 0, "T1HighDO2": 0, "T2HighDO2": 0, "T3HighDO2": 0, "T4HighDO2": 0 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_TEMP3_SET", "data": "0103102F0019B109&&01031231000DD0B8" } ``` #### 3.8 温度4设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_TEMP4_SET", "addr": 1, "T4HighVal02": 300, "T4HighTim": 5, "Lg1HighSw": 0, "T1HighSw": 0, "T2HighSw": 0, "T3HighSw": 0, "T4HighSw": 0, "Lg1HighDO1": 0, "T1HighDO1": 0, "T2HighDO1": 0, "T3HighDO1": 0, "T4HighDO1": 0, "Lg1HighDO2": 0, "T1HighDO2": 0, "T2HighDO2": 0, "T3HighDO2": 0, "T4HighDO2": 0 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_TEMP4_SET", "data": "0103102F0019B109&&01031231000DD0B8" } ``` #### 3.9 过压设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_UHIGH_SET", "addr": 1, "UHighSw": 0, "UHighDO1": 0, "UHighDO2": 0, "UHighVal02": 110, "UHighTim": 5 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_UHIGH_SET", "data": "0103102F0019B109" } ``` #### 3.10 欠压设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_ULOW_SET", "addr": 1, "ULowSw": 0, "ULowDO1": 0, "ULowDO2": 0, "ULowVal02": 80, "ULowTim": 5 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_ULOW_SET", "data": "0103102F0019B109" } ``` #### 3.11 过流设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_IHIGH_SET", "addr": 1, "IHighSw": 0, "IHighDO1": 0, "IHighDO2": 0, "IHighVal02": 0, "IHighTim": 5 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_IHIGH_SET", "data": "0103102F0019B109" } ``` #### 3.12 第二路过流A设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_OPTIAHIGH_SET", "addr": 1, "OPTIaHighDO1": 0, "OPTIaHighDO2": 0, "OPTIaHighVal02": 90, "OPTIaHighTim": 5 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_OPTIAHIGH_SET", "data": "0103102F0019B109" } ``` #### 3.13 第二路过流B设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_OPTIBHIGH_SET", "addr": 1, "OPTIbHighDO1": 0, "OPTIbHighDO2": 0, "OPTIbHighVal02": 90, "OPTIbHighTim": 5 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_OPTIBHIGH_SET", "data": "0103102F0019B109" } ``` #### 3.14 第二路过流C设置 流程同3.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同3.1.2 { "method": "WRITE_OPTICHIGH_SET", "addr": 1, "OPTIcHighDO1": 0, "OPTIcHighDO2": 0, "OPTIcHighVal02": 90, "OPTIcHighTim": 5 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_OPTICHIGH_SET", "data": "0103102F0019B109" } ``` ### 4.参数设置  参数设置页面的设置项分为:故障开关、漏电互感器变比、仪表地址、波特率、IP地址、端口号、域名、网关IP、子网掩码、本地IP、DHCP,每个设置项可以单独下发,也可以统一读取和统一下发 进入页面先默认下发一次全部读取命令,读取成功之后才能下发设置命令,非读取的命令小程序下发报文到仪表之后根据仪表的回复自行判断是否下发成功,若未回复或收到的回复报文中的功能码(报文的第二个字节)与下发的报文不相等,则认定下发失败 命令`method`汇总如下 | method | 说明 | | --- | --- | | READ_PARAM_SET | 参数设置全部读取 | | WRITE_PARAM_SET | 参数设置全部下发 | | WRITE_FAULT_SW | 故障开关设置 | | WRITE_LG_CT | 漏电互感器变比设置 | | WRITE_METER_ADDR | 仪表地址设置 | | WRITE_BAUD_RATE | 波特率设置 | | WRITE_SERVER_IP | IP地址设置 | | WRITE_SERVER_PORT | 端口号设置 | | WRITE_SERVER_DOMAIN | 域名设置 | | WRITE_GW_IP | 网关IP设置 | | WRITE_SUBNET_MASK | 子网掩码设置 | | WRITE_LOCAL_IP | 本地IP设置 | | WRITE_DHCP | DHCP设置 | #### 4.1 参数设置全部读取 读取的流程同读取主电参量 ##### 4.1.1 获取读参数设置的报文 ```js 调用`parseDownAdapter`方法入参 method 标明本条命令的用途 addr 仪表modbus协议地址,协议暂定用255 { "method":"READ_PARAM_SET", "addr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 抄读全部参数设置的透传命令报文,报文中间用`&&`隔开 { "res": 1, "method":"READ_PARAM_SET", "data": "01031103001170FA&&010311140020012A&&0103114B00077122" } 抄表段 第一段 0x1103-0x1113 读0x11个寄存器 01 03 11 03 00 11 70 FA 第二段 0x1114-0x1133 读0x20个寄存器 01 03 11 14 00 20 01 2A 第三段 0x114B-0x1151 读0x07个寄存器 01 03 11 4B 00 07 71 22 ``` ##### 4.1.2 解析参数设置抄读命令回复的报文 ```js 调用`parseUpAdapter`方法入参 method 标明本条命令的用途 data 仪表回复的所有报文,按下发的顺序用`&&`拼接 { "method":"READ_PARAM_SET", "data": "01031103001170FA&&010311140020012A&&0103114B00077122" } 返回结果 res 处理结果 0-失败 1-成功 参数名定义 FaultSw 故障开关 0-关 1-开 Lg1CT 漏电互感器变比 MeterAddr 仪表地址 1-247 BaudRate 波特率 下拉框 仅可设置4800,9600,19200,38400 ServerIp 服务器IP地址,全为0时启用域名 ServerPort 服务器TCP端口号 100-65535 ServerDomain 服务器域名 允许为空 GwIp 网关IP地址 SubnetMask 子网掩码 LocalIp 本地IP DHCP DHCP 0-关 1-开 示例 { "res": 1, "data": { "FaultSw": 0, "Lg1CT": 1, "MeterAddr": 1, "BaudRate": 9600, "ServerIp": "47.96.88.86", "ServerPort": 20030, "ServerDomain": "fire.acrelcloud.cn", "GwIp": "192.168.0.2", "SubnetMask": "255.255.255.0", "LocalIp": "192.168.0.3", "DHCP": 0 } } ``` #### 4.2 参数设置全部下发 参数设置的处理流程如下: - 第一步,小程序调用`parseDownAdapter`方法获取要下发的透传报文,返回的结果中有两条报文,用`&&`隔开, - 第二步,小程序依次下发每条报文,若有报文超时未收到回复,则直接判定下发失败,全部报文收到回复则判定下发成功 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_PARAM_SET", "addr": 1, "FaultSw": 0, "Lg1CT": 1, "MeterAddr": 1, "BaudRate": 9600, "ServerIp": "47.96.88.86", "ServerPort": 20030, "ServerDomain": "fire.acrelcloud.cn", "GwIp": "192.168.0.2", "SubnetMask": "255.255.255.0", "LocalIp": "192.168.0.3", "DHCP": 0 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_PARAM_SET", "data": "0103102F0019B109&&01031231000DD0B8" } ``` #### 4.3 故障开关设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_FAULT_SW", "addr": 1, "FaultSw": 0 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_FAULT_SW", "data": "0103102F0019B109" } ``` #### 4.4 漏电互感器变比设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_LG_CT", "addr": 1, "Lg1CT": 30 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_LG_CT", "data": "0103102F0019B109" } ``` #### 4.5 仪表地址设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_METER_ADDR", "addr": 1, "MeterAddr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_METER_ADDR", "data": "0103102F0019B109" } ``` #### 4.6 波特率设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_BAUD_RATE", "addr": 1, "BaudRate": 9600 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_BAUD_RATE", "data": "0103102F0019B109" } ``` #### 4.7 服务器IP地址设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_SERVER_IP", "addr": 1, "ServerIp": "47.96.11.156" } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_SERVER_IP", "data": "0103102F0019B109" } ``` #### 4.8 服务器TCP端口号设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_SERVER_PORT", "addr": 1, "ServerPort": 20030 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_SERVER_PORT", "data": "0103102F0019B109" } ``` #### 4.9 服务器域名设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_SERVER_DOMAIN", "addr": 1, "ServerDomain": "fire.acrelcloud.cn" } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_SERVER_DOMAIN", "data": "0103102F0019B109" } ``` #### 4.10 网关IP设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_GW_IP", "addr": 1, "GwIp": "192.168.0.2" } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_GW_IP", "data": "0103102F0019B109" } ``` #### 4.11 子网掩码设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_SUBNET_MASK", "addr": 1, "SubnetMask": "255.255.255.0" } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_SUBNET_MASK", "data": "0103102F0019B109" } ``` #### 4.12 本地IP设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_LOCAL_IP", "addr": 1, "LocalIp": "192.168.0.3" } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_LOCAL_IP", "data": "0103102F0019B109" } ``` #### 4.13 DHCP设置 流程同4.2 ```js 调用`parseDownAdapter`方法入参 参数名定义同4.1.2 { "method": "WRITE_DHCP", "addr": 1, "DHCP": 0 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 下发命令的全部透传报文,多条报文中间用`&&`隔开 { "res": 1, "method":"WRITE_DHCP", "data": "0103102F0019B109" } ``` ### 5.记录  分为报警记录、故障记录、开关记录,每种记录最多20条 该界面点进来默认下发一次读取报警记录命令 #### 5.1 报警记录 读取的流程同读取主电参量 ##### 5.1.1 读取报警记录 ```js 调用`parseDownAdapter`方法入参 method 标明本条命令的用途 addr 仪表modbus协议地址,协议暂定用255 { "method":"READ_ALARM_RECORD", "addr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 抄读报警记录的透传命令报文,多条报文中间用`&&`隔开 { "res": 1, "method":"READ_ALARM_RECORD", "data": "0103140000784018" } 抄表段 第一段 0x1400-0x1477 读0x78个寄存器 01 03 14 00 00 78 40 18 ``` ##### 5.1.2 解析读取报警记录命令回复的报文 ```js 调用`parseUpAdapter`方法入参 method 标明本条命令的用途 data 仪表回复的所有报文,按下发的顺序用`&&`拼接 { "method":"READ_ALARM_RECORD", "data": "0103140000784018" } 返回结果 res 处理结果 0-失败 1-成功 参数名定义 alarmType 报警类型 1:漏电 2:温度 3:过流 4:过压 5:欠压 6:出线A相过流 7:出线B相过流 8:出线C相过流 alarmValue 报警值 createTime 报警时间 示例 { "res": 1, "data": [{ "alarmType": 1, "alarmValue": 450, "createTime": "2025-01-17 15:02:03" }, { "alarmType": 2, "alarmValue": 80, "createTime": "2025-01-15 12:02:03" }, { "alarmType": 4, "alarmValue": 242.1, "createTime": "2025-01-13 22:02:03" } ] } ``` #### 5.2 故障记录 读取的流程同读取主电参量 ##### 5.2.1 读取故障记录 ```js 调用`parseDownAdapter`方法入参 method 标明本条命令的用途 addr 仪表modbus协议地址,协议暂定用255 { "method":"READ_FAULT_RECORD", "addr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 抄读故障记录的透传命令报文,多条报文中间用`&&`隔开 { "res": 1, "method":"READ_FAULT_RECORD", "data": "01031500005041FA" } 抄表段 第一段 0x1500-0x1549 读0x50个寄存器 01 03 15 00 00 50 41 FA ``` ##### 5.2.2 解析读取故障记录命令回复的报文 ```js 调用`parseUpAdapter`方法入参 method 标明本条命令的用途 data 仪表回复的所有报文,按下发的顺序用`&&`拼接 { "method":"READ_FAULT_RECORD", "data": "01031500005041FA" } 返回结果 res 处理结果 0-失败 1-成功 参数名定义 faultType 故障类型 1-断线 2-短路 channel 通道 1-5 createTime 故障时间 示例 { "res": 1, "data": [{ "faultType": 1, "channel": 1, "createTime": "2025-01-17 15:02:03" }, { "faultType": 2, "channel": 2, "createTime": "2025-01-15 12:02:03" }, { "faultType": 1, "channel": 3, "createTime": "2025-01-13 22:02:03" } ] } ``` #### 5.3 开关记录 读取的流程同读取主电参量 ##### 5.3.1 读取开关记录 ```js 调用`parseDownAdapter`方法入参 method 标明本条命令的用途 addr 仪表modbus协议地址,协议暂定用255 { "method":"READ_DIDO_RECORD", "addr": 1 } 返回结果 res 0-失败 1-成功 method 和传参中的method一致 data 抄读开关记录的透传命令报文,多条报文中间用`&&`隔开 { "res": 1, "method":"READ_DIDO_RECORD", "data": "01031600005041BE" } 抄表段 第一段 0x1600-0x1649 读0x50个寄存器 01 03 16 00 00 50 41 BE ``` ##### 5.3.2 解析读取开关记录命令回复的报文 ```js 调用`parseUpAdapter`方法入参 method 标明本条命令的用途 data 仪表回复的所有报文,按下发的顺序用`&&`拼接 { "method":"READ_DIDO_RECORD", "data": "01031600005041BE" } 返回结果 res 处理结果 0-失败 1-成功 参数名定义 eventType 事件类型 1-DI开 2-DI关 3-DO开 4-DO关 channel 通道 1-2 createTime 操作时间 示例 { "res": 1, "data": [{ "eventType": 1, "channel": 1, "createTime": "2025-01-17 15:02:03" }, { "eventType": 2, "channel": 1, "createTime": "2025-01-15 12:02:03" }, { "eventType": 4, "channel": 2, "createTime": "2025-01-13 22:02:03" } ] } ``` ### 附录 #### A.提供给小程序调用的方法 ```js //获取透传命令下发到仪表的报文 function parseDownAdapter(payload) { var result = {}; result.res = 0; //默认仪表地址为255 var addr = 255; if (payload.method == undefined) { return result; } var method = payload.method; result.method = method; if (payload.addr != undefined) { addr = payload.addr; } var dataStr = ""; try { switch (method) { case "READ_MAIN_PARAM": //抄读主电参量 { let sendData1 = new Array(8); dataStr = getSendData(sendData1, addr, 0x03, 0x0000, 0x0a); let sendData2 = new Array(8); dataStr += "&&" + getSendData(sendData2, addr, 0x03, 0x0013, 0x1d); let sendData3 = new Array(8); dataStr += "&&" + getSendData(sendData3, addr, 0x03, 0x0032, 0x30); let sendData4 = new Array(8); dataStr += "&&" + getSendData(sendData4, addr, 0x03, 0x0062, 0x2a); let sendData5 = new Array(8); dataStr += "&&" + getSendData(sendData5, addr, 0x03, 0x1028, 0x02); let sendData6 = new Array(12); sendData6[0] = addr & 0xff; sendData6[1] = 0x11; sendData6[2] = 0xfe; sendData6[3] = 0xdc; sendData6[4] = 0xba; sendData6[5] = 0x98; sendData6[6] = 0x76; sendData6[7] = 0x54; sendData6[8] = 0x32; sendData6[9] = 0x10; const CRC = calculateCRC(sendData6); sendData6[sendData6.length - 2] = CRC[0]; sendData6[sendData6.length - 1] = CRC[1]; dataStr += "&&" + byteArrayToHexString(sendData6); } break; case "READ_OUTPUT_PARAM": //抄读出线电参量 { let sendData1 = new Array(8); dataStr = getSendData(sendData1, addr, 0x03, 0x0130, 0x0c); let sendData2 = new Array(8); dataStr += "&&" + getSendData(sendData2, addr, 0x03, 0x0142, 0x28); let sendData3 = new Array(8); dataStr += "&&" + getSendData(sendData3, addr, 0x03, 0x0103, 0x01); } break; case "READ_ALARM_SET": //保护设置全部读取 { let sendData1 = new Array(8); dataStr = getSendData(sendData1, addr, 0x03, 0x102f, 0x19); let sendData2 = new Array(8); dataStr += "&&" + getSendData(sendData2, addr, 0x03, 0x1231, 0x0d); let sendData3 = new Array(8); dataStr += "&&" + getSendData(sendData3, addr, 0x03, 0x125c, 0x03); let sendData4 = new Array(8); dataStr += "&&" + getSendData(sendData4, addr, 0x03, 0x1262, 0x03); let sendData5 = new Array(8); dataStr += "&&" + getSendData(sendData5, addr, 0x03, 0x1268, 0x03); } break; case "WRITE_ALARM_SET": //保护设置全部下发 { //第一包,从0x102F到0x1037,写9个寄存器 let values = new Array(9); //DO1关联 let do1s = payload.Lg1HighDO1 + (payload.T1HighDO1 << 1) + (payload.T2HighDO1 << 2) + (payload.T3HighDO1 << 3) + (payload.T4HighDO1 << 4); values[0] = convertToUSixteenInt(do1s); //DO2关联 let do2s = payload.Lg1HighDO2 + (payload.T1HighDO2 << 1) + (payload.T2HighDO2 << 2) + (payload.T3HighDO2 << 3) + (payload.T4HighDO2 << 4); values[1] = convertToUSixteenInt(do2s); //保护开关 let sw = payload.Lg1HighSw + (payload.T1HighSw << 1) + (payload.T2HighSw << 2) + (payload.T3HighSw << 3) + (payload.T4HighSw << 4); values[2] = convertToUSixteenInt(sw); //保护类型 values[3] = convertToUSixteenInt(payload.Lg1HighProtectType); //漏电流保护设定值 values[4] = convertToUSixteenInt(payload.Lg1HighVal02); //温度1保护设定值 values[5] = convertToUSixteenInt(payload.T1HighVal02); //温度2保护设定值 values[6] = convertToUSixteenInt(payload.T2HighVal02); //温度3保护设定值 values[7] = convertToUSixteenInt(payload.T3HighVal02); //温度4保护设定值 values[8] = convertToUSixteenInt(payload.T4HighVal02); dataStr = writeCommand(addr, 0x102f, 9, values); //第二包,从0x1043到0x1047,写5个寄存器 values = new Array(5); //漏电保护延时 values[0] = convertToUSixteenInt(payload.Lg1HighTim * 10); //温度1保护延时 values[1] = convertToUSixteenInt(payload.T1HighTim * 10); //温度2保护延时 values[2] = convertToUSixteenInt(payload.T2HighTim * 10); //温度3保护延时 values[3] = convertToUSixteenInt(payload.T3HighTim * 10); //温度4保护延时 values[4] = convertToUSixteenInt(payload.T4HighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1043, 5, values); //第三包,0x1232,写1个寄存器 values = new Array(1); //额定电流 values[0] = convertToUSixteenInt(payload.IRange); dataStr += "&&" + writeCommand(addr, 0x1232, 1, values); //第四包,从0x1235到0x123D,写9个寄存器 values = new Array(9); //过压报警类型 let uHigh = payload.UHighSw + (payload.UHighDO1 << 1) + (payload.UHighDO2 << 2); values[0] = convertToUSixteenInt(uHigh); //过压报警值 values[1] = convertToUSixteenInt(payload.UHighVal02 * 10); //过压报警时间 values[2] = convertToUSixteenInt(payload.UHighTim * 10); //欠压报警类型 let uLow = payload.ULowSw + (payload.ULowDO1 << 1) + (payload.ULowDO2 << 2); values[3] = convertToUSixteenInt(uLow); //欠压报警值 values[4] = convertToUSixteenInt(payload.ULowVal02 * 10); //欠压报警时间 values[5] = convertToUSixteenInt(payload.ULowTim * 10); //过流报警类型 let iHigh = payload.IHighSw + (payload.IHighDO1 << 1) + (payload.IHighDO2 << 2); values[6] = convertToUSixteenInt(iHigh); //过流报警值 values[7] = convertToUSixteenInt(payload.IHighVal02 * 10); //过流报警时间 values[8] = convertToUSixteenInt(payload.IHighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1235, 9, values); //第五包,从0x125C到0x125E,写3个寄存器 values = new Array(3); //第二路过流A报警类型 let optIaHigh = (payload.OPTIaHighDO1 << 1) + (payload.OPTIaHighDO2 << 2); values[0] = convertToUSixteenInt(optIaHigh); //第二路过流A报警值 values[1] = convertToUSixteenInt(payload.OPTIaHighVal02 * 10); //第二路过流A报警延时 values[2] = convertToUSixteenInt(payload.OPTIaHighTim * 10); dataStr += "&&" + writeCommand(addr, 0x125c, 3, values); //第六包,从0x1262到0x1264,写3个寄存器 values = new Array(3); //第二路过流B报警类型 let optIbHigh = (payload.OPTIbHighDO1 << 1) + (payload.OPTIbHighDO2 << 2); values[0] = convertToUSixteenInt(optIbHigh); //第二路过流B报警值 values[1] = convertToUSixteenInt(payload.OPTIbHighVal02 * 10); //第二路过流B报警延时 values[2] = convertToUSixteenInt(payload.OPTIbHighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1262, 3, values); //第七包,从0x1268到0x126A,写3个寄存器 values = new Array(3); //第二路过流C报警类型 let optIcHigh = (payload.OPTIcHighDO1 << 1) + (payload.OPTIcHighDO2 << 2); values[0] = convertToUSixteenInt(optIcHigh); //第二路过流C报警值 values[1] = convertToUSixteenInt(payload.OPTIcHighVal02 * 10); //第二路过流C报警延时 values[2] = convertToUSixteenInt(payload.OPTIcHighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1268, 3, values); } break; case "WRITE_BASIC_SET": //基本设置(保护类型、额定电流) { //第一包,0x1032,写1个寄存器 let values = new Array(1); //保护类型 values[0] = convertToUSixteenInt(payload.Lg1HighProtectType); dataStr = writeCommand(addr, 0x1032, 1, values); //第二包,0x1232,写1个寄存器 values = new Array(1); //额定电压不支持写入,固定220V //values[0] = convertToUSixteenInt(payload.URange); //额定电流,范围20-800 values[0] = convertToUSixteenInt(payload.IRange); dataStr += "&&" + writeCommand(addr, 0x1232, 1, values); } break; case "WRITE_LG1_SET": //漏电保护设置 { //第一包,从0x102F到0x1031,写3个寄存器 let values = new Array(3); values = getDo1Do2SwVal(payload, values); dataStr = writeCommand(addr, 0x102f, 3, values); //第二包,0x1033,写1个寄存器 values = new Array(1); //漏电流保护值 values[0] = convertToUSixteenInt(payload.Lg1HighVal02); dataStr += "&&" + writeCommand(addr, 0x1033, 1, values); //第三包,0x1043,写1个寄存器 values = new Array(1); //漏电流保护延时 values[0] = convertToUSixteenInt(payload.Lg1HighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1043, 1, values); } break; case "WRITE_TEMP1_SET": //温度1保护设置 { //第一包,从0x102F到0x1031,写3个寄存器,DO1/DO2/Sw let values = new Array(3); values = getDo1Do2SwVal(payload, values); dataStr = writeCommand(addr, 0x102f, 3, values); //第二包,0x1034,写1个寄存器 values = new Array(1); //温度保护值 values[0] = convertToUSixteenInt(payload.T1HighVal02); dataStr += "&&" + writeCommand(addr, 0x1034, 1, values); //第三包,0x1044,写1个寄存器 values = new Array(1); //温度1保护延时 values[0] = convertToUSixteenInt(payload.T1HighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1044, 1, values); } break; case "WRITE_TEMP2_SET": //温度2保护设置 { //第一包,从0x102F到0x1031,写3个寄存器,DO1/DO2/Sw let values = new Array(3); values = getDo1Do2SwVal(payload, values); dataStr = writeCommand(addr, 0x102f, 3, values); //第二包,0x1035,写1个寄存器 values = new Array(1); //温度2保护值 values[0] = convertToUSixteenInt(payload.T2HighVal02); dataStr += "&&" + writeCommand(addr, 0x1035, 1, values); //第三包,0x1045,写1个寄存器 values = new Array(1); //温度2保护延时 values[0] = convertToUSixteenInt(payload.T2HighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1045, 1, values); } break; case "WRITE_TEMP3_SET": //温度3保护设置 { //第一包,从0x102F到0x1031,写3个寄存器,DO1/DO2/Sw let values = new Array(3); values = getDo1Do2SwVal(payload, values); dataStr = writeCommand(addr, 0x102f, 3, values); //第二包,0x1036,写1个寄存器 values = new Array(1); //温度2保护值 values[0] = convertToUSixteenInt(payload.T3HighVal02); dataStr += "&&" + writeCommand(addr, 0x1036, 1, values); //第三包,0x1046,写1个寄存器 values = new Array(1); //温度2保护延时 values[0] = convertToUSixteenInt(payload.T3HighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1046, 1, values); } break; case "WRITE_TEMP4_SET": //温度4保护设置 { //第一包,从0x102F到0x1031,写3个寄存器,DO1/DO2/Sw let values = new Array(3); values = getDo1Do2SwVal(payload, values); dataStr = writeCommand(addr, 0x102f, 3, values); //第二包,0x1037,写1个寄存器 values = new Array(1); //温度2保护值 values[0] = convertToUSixteenInt(payload.T4HighVal02); dataStr += "&&" + writeCommand(addr, 0x1037, 1, values); //第三包,0x1047,写1个寄存器 values = new Array(1); //温度2保护延时 values[0] = convertToUSixteenInt(payload.T4HighTim * 10); dataStr += "&&" + writeCommand(addr, 0x1047, 1, values); } break; case "WRITE_UHIGH_SET": //过压保护设置 { // 一包,0x1235-0x1237,写3个寄存器 let values = new Array(3); //过压报警类型 let do1s = payload.UHighSw + (payload.UHighDO1 << 1) + (payload.UHighDO2 << 2); values[0] = convertToUSixteenInt(do1s); // 过压报警值 values[1] = convertToUSixteenInt(payload.UHighVal02 * 10); // 过压报警时间 values[2] = convertToUSixteenInt(payload.UHighTim * 10); dataStr = writeCommand(addr, 0x1235, 3, values); } break; case "WRITE_ULOW_SET": //欠压保护设置 { let values = new Array(3); let do1s = payload.ULowSw + (payload.ULowDO1 << 1) + (payload.ULowDO2 << 2); values[0] = convertToUSixteenInt(do1s); // 欠压报警值 values[1] = convertToUSixteenInt(payload.ULowVal02 * 10); // 欠压报警时间 values[2] = convertToUSixteenInt(payload.ULowTim * 10); dataStr = writeCommand(addr, 0x1238, 3, values); } break; case "WRITE_IHIGH_SET": //过流保护设置 { // 一包,0x123B-0x123D,写3个寄存器 let values = new Array(3); //过流报警类型 let do1s = payload.IHighSw + (payload.IHighDO1 << 1) + (payload.IHighDO2 << 2); values[0] = convertToUSixteenInt(do1s); // 过流报警值 values[1] = convertToUSixteenInt(payload.IHighVal02 * 10); // 过流报警时间 values[2] = convertToUSixteenInt(payload.IHighTim * 10); dataStr = writeCommand(addr, 0x123b, 3, values); } break; case "WRITE_OPTIAHIGH_SET": //第二路过流A设置 { //一包,0x125C-0x125E,写3个寄存器 let values = new Array(3); //第二路过流A报警类型 let optIaHigh = (payload.OPTIaHighDO1 << 1) + (payload.OPTIaHighDO2 << 2); values[0] = convertToUSixteenInt(optIaHigh); //第二路过流A报警值 values[1] = convertToUSixteenInt(payload.OPTIaHighVal02 * 10); //第二路过流A报警延时 values[2] = convertToUSixteenInt(payload.OPTIaHighTim * 10); dataStr = writeCommand(addr, 0x125c, 3, values); } break; case "WRITE_OPTIBHIGH_SET": //第二路过流B设置 { //一包,0x1262-0x1264,写3个寄存器 let values = new Array(3); //第二路过流B报警类型 let optIbHigh = (payload.OPTIbHighDO1 << 1) + (payload.OPTIbHighDO2 << 2); values[0] = convertToUSixteenInt(optIbHigh); //第二路过流B报警值 values[1] = convertToUSixteenInt(payload.OPTIbHighVal02 * 10); //第二路过流B报警延时 values[2] = convertToUSixteenInt(payload.OPTIbHighTim * 10); dataStr = writeCommand(addr, 0x1262, 3, values); } break; case "WRITE_OPTICHIGH_SET": //第二路过流C设置 { //一包,0x1262-0x1264,写3个寄存器 let values = new Array(3); //第二路过流C报警类型 let optIcHigh = (payload.OPTIcHighDO1 << 1) + (payload.OPTIcHighDO2 << 2); values[0] = convertToUSixteenInt(optIcHigh); //第二路过流C报警值 values[1] = convertToUSixteenInt(payload.OPTIcHighVal02 * 10); //第二路过流C报警延时 values[2] = convertToUSixteenInt(payload.OPTIcHighTim * 10); dataStr = writeCommand(addr, 0x1268, 3, values); } break; case "READ_PARAM_SET": //参数设置全部读取 { let sendData1 = new Array(8); dataStr = getSendData(sendData1, addr, 0x03, 0x1103, 0x11); let sendData2 = new Array(8); dataStr += "&&" + getSendData(sendData2, addr, 0x03, 0x1114, 0x20); let sendData3 = new Array(8); dataStr += "&&" + getSendData(sendData3, addr, 0x03, 0x114b, 0x07); } break; case "WRITE_PARAM_SET": //参数设置全部下发 { //第1包,从0x1103-0x1106 写4个寄存器 //故障开关 漏电互感器变比 表地址 波特率 let values = new Array(4); values[0] = convertToUSixteenInt(payload.FaultSw); values[1] = convertToUSixteenInt(payload.Lg1CT); values[2] = convertToUSixteenInt(payload.MeterAddr); values[3] = convertToUSixteenInt(payload.BaudRate); dataStr = writeCommand(addr, 0x1103, 4, values); //第2包,从0x1111-0x1113 写3个寄存器 //服务器TCP端口号 服务器IP地址 values = new Array(3); values[0] = convertToUSixteenInt(payload.ServerPort); values = writeIp(payload.ServerIp, 1, values); dataStr += "&&" + writeCommand(addr, 0x1111, 3, values); // 第3包,0x1114-0x1133,写32寄存器 // 服务器域名 values = new Array(32); values = writeDomain(payload.ServerDomain, 0, values); dataStr += "&&" + writeCommand(addr, 0x1114, 32, values); // 第4包,从0x114B-0x1151 写7个寄存器 // GwIp SubnetMask LocalIp DHCP values = new Array(7); values = writeIp(payload.GwIp, 0, values); values = writeIp(payload.SubnetMask, 2, values); values = writeIp(payload.LocalIp, 4, values); values[6] = convertToUSixteenInt(payload.DHCP); dataStr += "&&" + writeCommand(addr, 0x114b, 7, values); } break; case "WRITE_FAULT_SW": //故障开关设置 { //1包,从0x1103 写1个寄存器 let values = new Array(1); values[0] = convertToUSixteenInt(payload.FaultSw); dataStr = writeCommand(addr, 0x1103, 1, values); } break; case "WRITE_LG_CT": //漏电互感器变比设置 { // 1包,从0x1104 写1个寄存器 let values = new Array(1); values[0] = convertToUSixteenInt(payload.Lg1CT); dataStr = writeCommand(addr, 0x1104, 1, values); } break; case "WRITE_METER_ADDR": //仪表地址设置 { // 1包,从0x1105 写1个寄存器 let values = new Array(1); values[0] = convertToUSixteenInt(payload.MeterAddr); dataStr = writeCommand(addr, 0x1105, 1, values); } break; case "WRITE_BAUD_RATE": //波特率设置 { // 1包,从0x1106 写1个寄存器 let values = new Array(1); values[0] = convertToUSixteenInt(payload.BaudRate); dataStr = writeCommand(addr, 0x1106, 1, values); } break; case "WRITE_SERVER_IP": //IP地址设置 { // 1包,从0x1112 开始写2个寄存器 let values = new Array(2); values = writeIp(payload.ServerIp, 0, values); dataStr = writeCommand(addr, 0x1112, 2, values); } break; case "WRITE_SERVER_PORT": //端口号设置 { // 1包,从0x1111 写1个寄存器 let values = new Array(1); values[0] = convertToUSixteenInt(payload.ServerPort); dataStr = writeCommand(addr, 0x1111, 1, values); } break; case "WRITE_SERVER_DOMAIN": //域名设置 { // 1包,从0x1114开始写32个寄存器 let values = new Array(32); values = writeDomain(payload.ServerDomain, 0, values); dataStr = writeCommand(addr, 0x1114, 32, values); } break; case "WRITE_GW_IP": //网关IP设置 { // 1包,从0x0x114B 开始写2个寄存器 let values = new Array(2); values = writeIp(payload.GwIp, 0, values); dataStr = writeCommand(addr, 0x114b, 2, values); } break; case "WRITE_SUBNET_MASK": //子网掩码设置 { // 1包,从0x114D 开始写2个寄存器 let values = new Array(2); values = writeIp(payload.SubnetMask, 0, values); dataStr = writeCommand(addr, 0x114d, 2, values); } break; case "WRITE_LOCAL_IP": //本地IP设置 { // 1包,从0x114F 开始写2个寄存器 let values = new Array(2); values = writeIp(payload.LocalIp, 0, values); dataStr = writeCommand(addr, 0x114f, 2, values); } break; case "WRITE_DHCP": //DHCP设置 { // 1包,从0x1151 写1个寄存器 let values = new Array(1); values[0] = convertToUSixteenInt(payload.DHCP); dataStr = writeCommand(addr, 0x1151, 1, values); } break; case "READ_SOFT_CODE": //读取软件编号和软件版本号 { let sendData = new Array(12); sendData[0] = addr & 0xff; sendData[1] = 0x11; sendData[2] = 0xfe; sendData[3] = 0xdc; sendData[4] = 0xba; sendData[5] = 0x98; sendData[6] = 0x76; sendData[7] = 0x54; sendData[8] = 0x32; sendData[9] = 0x10; const CRC = calculateCRC(sendData); sendData[sendData.length - 2] = CRC[0]; sendData[sendData.length - 1] = CRC[1]; dataStr = byteArrayToHexString(sendData); } break; case "READ_ALARM_RECORD": //读取报警记录 { //一包读120个寄存器 let sendData = new Array(8); dataStr = getSendData(sendData, addr, 0x03, 0x1400, 0x78); } break; case "READ_FAULT_RECORD": //读取故障记录 { //一包读80个寄存器 let sendData = new Array(8); dataStr = getSendData(sendData, addr, 0x03, 0x1500, 0x50); } break; case "READ_DIDO_RECORD": //读取开关记录 { //一包读80个寄存器 let sendData1 = new Array(8); dataStr = getSendData(sendData1, addr, 0x03, 0x1600, 0x50); } break; default: //不支持的method throw new Error("Invalid method!"); } } catch (error) { console.log(error.message); return result; } result.res = 1; result.data = dataStr; return result; } //解析仪表透传指令下发后回复的报文 function parseUpAdapter(payload) { var result = {}; result.res = 0; if (payload.method == undefined || payload.data == undefined) { result.data = {}; return result; } var method = payload.method; var hexStr = payload.data; var parseData = {}; try { switch (method) { case "READ_MAIN_PARAM": //抄读主电参量 { let dataArray = hexStr.split("&&"); //一共有6包 if (dataArray.length < 6) { throw new Error("dataArray length error!"); } //解析第一包 let byteArray = hexStrTobyteArray(dataArray[0]); let array = parseReadData(byteArray); //年 let year = 2000 + (array[0] >>> 8); //月 let month = (array[0] & 0xff).toString(10).padStart(2, "0"); //日 let day = (array[1] >>> 8).toString(10).padStart(2, "0"); //时 let hour = (array[1] & 0xff).toString(10).padStart(2, "0"); //分 let minute = (array[2] >>> 8).toString(10).padStart(2, "0"); //秒 let second = (array[2] & 0xff).toString(10).padStart(2, "0"); parseData.MeterTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second; //仪表序列号 let meterSnArray = byteArray.slice(9, 23); const meterSn = meterSnArray .map((code) => String.fromCharCode(code)) .join(""); parseData.meterSn = meterSn; //解析第二包 byteArray = hexStrTobyteArray(dataArray[1]); array = parseReadData(byteArray); //漏电温度接线故障标志位 parseData.Lg1FauSta = (array[0] >> 0) & 1; parseData.T1FauSta = (array[0] >> 1) & 1; parseData.T2FauSta = (array[0] >> 2) & 1; parseData.T3FauSta = (array[0] >> 3) & 1; parseData.T4FauSta = (array[0] >> 4) & 1; //漏电温度报警标志位 parseData.Lg1AlaSta = (array[1] >> 0) & 1; parseData.T1AlaSta = (array[1] >> 1) & 1; parseData.T2AlaSta = (array[1] >> 2) & 1; parseData.T3AlaSta = (array[1] >> 3) & 1; parseData.T4AlaSta = (array[1] >> 4) & 1; //电压状态标志位 parseData.UHighSta = (array[3] >> 0) & 1; parseData.ULowSta = (array[3] >> 8) & 1; //电流状态标志位 parseData.IHighSta = (array[4] >> 0) & 1; //剩余电流测量值 parseData.Lg1 = getFloat(array, 7, 3); //温度1测量值 parseData.Temp1 = getFloat(array, 9, 1); //温度2测量值 parseData.Temp2 = getFloat(array, 11, 1); //温度3测量值 parseData.Temp3 = getFloat(array, 13, 1); //温度4测量值 parseData.Temp4 = getFloat(array, 15, 1); //剩余电流报警时测量值 parseData.Lg1AlarmVal = getFloat(array, 17, 3); //温度1报警时测量值 parseData.T1AlarmVal = getFloat(array, 19, 1); //温度2报警时测量值 parseData.T2AlarmVal = getFloat(array, 21, 1); //温度3报警时测量值 parseData.T3AlarmVal = getFloat(array, 23, 1); //温度4报警时测量值 parseData.T4AlarmVal = getFloat(array, 25, 1); //频率 parseData.Fr = getFloat(array, 27, 2); //解析第三包 byteArray = hexStrTobyteArray(dataArray[2]); array = parseReadData(byteArray); //电压 parseData.Ua = getFloat(array, 0, 1); parseData.Ub = getFloat(array, 2, 1); parseData.Uc = getFloat(array, 4, 1); //三相电压过压报警时测量值 parseData.UaHighAlarmVal = getFloat(array, 18, 1); parseData.UbHighAlarmVal = getFloat(array, 20, 1); parseData.UcHighAlarmVal = getFloat(array, 22, 1); //三相电压欠压报警时测量值 parseData.UaLowAlarmVal = getFloat(array, 24, 1); parseData.UbLowAlarmVal = getFloat(array, 26, 1); parseData.UcLowAlarmVal = getFloat(array, 28, 1); //三相电流 parseData.Ia = getFloat(array, 32, 1); parseData.Ib = getFloat(array, 34, 1); parseData.Ic = getFloat(array, 36, 1); //三相电流报警时测量值 parseData.IaHighAlarmVal = getFloat(array, 42, 1); parseData.IbHighAlarmVal = getFloat(array, 44, 1); parseData.IcHighAlarmVal = getFloat(array, 46, 1); //解析第四包 byteArray = hexStrTobyteArray(dataArray[3]); array = parseReadData(byteArray); //有功功率 parseData.Pa = getFloat(array, 0, 3); parseData.Pb = getFloat(array, 2, 3); parseData.Pc = getFloat(array, 4, 3); parseData.P = getFloat(array, 6, 3); //无功功率 parseData.Qa = getFloat(array, 8, 3); parseData.Qb = getFloat(array, 10, 3); parseData.Qc = getFloat(array, 12, 3); parseData.Q = getFloat(array, 14, 3); //视在功率 parseData.Sa = getFloat(array, 16, 3); parseData.Sb = getFloat(array, 18, 3); parseData.Sc = getFloat(array, 20, 3); parseData.S = getFloat(array, 22, 3); //功率因数 parseData.PFa = getFloat(array, 24, 3); parseData.PFb = getFloat(array, 26, 3); parseData.PFc = getFloat(array, 28, 3); parseData.PF = getFloat(array, 30, 3); //电能 parseData.EPI = getFloat(array, 32, 3); parseData.EPE = getFloat(array, 34, 3); parseData.EQL = getFloat(array, 36, 3); parseData.EQC = getFloat(array, 38, 3); parseData.ES = getFloat(array, 40, 3); //解析第五包 byteArray = hexStrTobyteArray(dataArray[4]); array = parseReadData(byteArray); //DI parseData.DI1 = (array[0] >> 0) & 1; parseData.DI2 = (array[0] >> 1) & 1; //DO parseData.DO1 = (array[1] >> 0) & 1; parseData.DO2 = (array[1] >> 1) & 1; //解析第6包,软件编号和软件版本号 byteArray = hexStrTobyteArray(dataArray[5]); parseData.softCode = (byteArray[3] << 8) + byteArray[4]; parseData.softVersion = ((byteArray[7] - 0x80) << 8) + byteArray[8]; } break; case "READ_OUTPUT_PARAM": //读取出线电参量 { let dataArray = hexStr.split("&&"); //一共有3包 if (dataArray.length < 2) { throw new Error("dataArray length error!"); } //解析第一包 let byteArray = hexStrTobyteArray(dataArray[0]); let array = parseReadData(byteArray); //出线电流 parseData.OPTIa = getFloat(array, 0, 1); parseData.OPTIb = getFloat(array, 2, 1); parseData.OPTIc = getFloat(array, 4, 1); //出线电流过流时报警测量值 parseData.OPTIaAlarmVal = getFloat(array, 6, 1); parseData.OPTIbAlarmVal = getFloat(array, 8, 1); parseData.OPTIcAlarmVal = getFloat(array, 10, 1); //解析第二包 byteArray = hexStrTobyteArray(dataArray[1]); array = parseReadData(byteArray); //出线有功功率 parseData.OPTPa = getFloat(array, 0, 3); parseData.OPTPb = getFloat(array, 2, 3); parseData.OPTPc = getFloat(array, 4, 3); parseData.OPTP = getFloat(array, 6, 3); //出线无功功率 parseData.OPTQa = getFloat(array, 8, 3); parseData.OPTQb = getFloat(array, 10, 3); parseData.OPTQc = getFloat(array, 12, 3); parseData.OPTQ = getFloat(array, 14, 3); //出线视在功率 parseData.OPTSa = getFloat(array, 16, 3); parseData.OPTSb = getFloat(array, 18, 3); parseData.OPTSc = getFloat(array, 20, 3); parseData.OPTS = getFloat(array, 22, 3); //出线功率因数 parseData.OPTPFa = getFloat(array, 24, 3); parseData.OPTPFb = getFloat(array, 26, 3); parseData.OPTPFc = getFloat(array, 28, 3); parseData.OPTPF = getFloat(array, 30, 3); //出线有功电能 parseData.OPTEPIA = getFloat(array, 32, 3); parseData.OPTEPIB = getFloat(array, 34, 3); parseData.OPTEPIC = getFloat(array, 36, 3); parseData.OPTEPI = getFloat(array, 38, 3); //解析第三包 byteArray = hexStrTobyteArray(dataArray[2]); array = parseReadData(byteArray); //出线电流过流报警标志位 parseData.OPTIaHighSta = (array[0] >> 0) & 1; parseData.OPTIbHighSta = (array[0] >> 1) & 1; parseData.OPTIcHighSta = (array[0] >> 2) & 1; } break; case "READ_ALARM_SET": //保护设置全部读取 { let dataArray = hexStr.split("&&"); //一共有5包 if (dataArray.length < 5) { throw new Error("dataArray length error!"); } //解析第一包 let byteArray = hexStrTobyteArray(dataArray[0]); let array = parseReadData(byteArray); //DO1关联 parseData.Lg1HighDO1 = (array[0] >> 0) & 1; parseData.T1HighDO1 = (array[0] >> 1) & 1; parseData.T2HighDO1 = (array[0] >> 2) & 1; parseData.T3HighDO1 = (array[0] >> 3) & 1; parseData.T4HighDO1 = (array[0] >> 4) & 1; //DO2关联 parseData.Lg1HighDO2 = (array[1] >> 0) & 1; parseData.T1HighDO2 = (array[1] >> 1) & 1; parseData.T2HighDO2 = (array[1] >> 2) & 1; parseData.T3HighDO2 = (array[1] >> 3) & 1; parseData.T4HighDO2 = (array[1] >> 4) & 1; //保护开关 parseData.Lg1HighSw = (array[2] >> 0) & 1; parseData.T1HighSw = (array[2] >> 1) & 1; parseData.T2HighSw = (array[2] >> 2) & 1; parseData.T3HighSw = (array[2] >> 3) & 1; parseData.T4HighSw = (array[2] >> 4) & 1; //保护类型 parseData.Lg1HighProtectType = (array[3] >> 0) & 1; //保护设定值 parseData.Lg1HighVal02 = array[4]; parseData.T1HighVal02 = array[5]; parseData.T2HighVal02 = array[6]; parseData.T3HighVal02 = array[7]; parseData.T4HighVal02 = array[8]; //保护延时 parseData.Lg1HighTim = parseFloat((array[20] * 0.1).toFixed(1)); parseData.T1HighTim = parseFloat((array[21] * 0.1).toFixed(1)); parseData.T2HighTim = parseFloat((array[22] * 0.1).toFixed(1)); parseData.T3HighTim = parseFloat((array[23] * 0.1).toFixed(1)); parseData.T4HighTim = parseFloat((array[24] * 0.1).toFixed(1)); //解析第二包 byteArray = hexStrTobyteArray(dataArray[1]); array = parseReadData(byteArray); //额定电压 parseData.URange = array[0]; //额定电流 parseData.IRange = array[1]; //过压 parseData.UHighSw = (array[4] >> 0) & 1; parseData.UHighDO1 = (array[4] >> 1) & 1; parseData.UHighDO2 = (array[4] >> 2) & 1; parseData.UHighVal02 = parseFloat((array[5] * 0.1).toFixed(1)); parseData.UHighTim = parseFloat((array[6] * 0.1).toFixed(1)); //欠压 parseData.ULowSw = (array[7] >> 0) & 1; parseData.ULowDO1 = (array[7] >> 1) & 1; parseData.ULowDO2 = (array[7] >> 2) & 1; parseData.ULowVal02 = parseFloat((array[8] * 0.1).toFixed(1)); parseData.ULowTim = parseFloat((array[9] * 0.1).toFixed(1)); //过流 parseData.IHighSw = (array[10] >> 0) & 1; parseData.IHighDO1 = (array[10] >> 1) & 1; parseData.IHighDO2 = (array[10] >> 2) & 1; parseData.IHighVal02 = parseFloat((array[11] * 0.1).toFixed(1)); parseData.IHighTim = parseFloat((array[12] * 0.1).toFixed(1)); //解析第三包 byteArray = hexStrTobyteArray(dataArray[2]); array = parseReadData(byteArray); //第二路过流A parseData.OPTIaHighDO1 = (array[0] >> 1) & 1; parseData.OPTIaHighDO2 = (array[0] >> 2) & 1; parseData.OPTIaHighVal02 = parseFloat((array[1] * 0.1).toFixed(1)); parseData.OPTIaHighTim = parseFloat((array[2] * 0.1).toFixed(1)); //解析第四包 byteArray = hexStrTobyteArray(dataArray[3]); array = parseReadData(byteArray); //第二路过流B parseData.OPTIbHighDO1 = (array[0] >> 1) & 1; parseData.OPTIbHighDO2 = (array[0] >> 2) & 1; parseData.OPTIbHighVal02 = parseFloat((array[1] * 0.1).toFixed(1)); parseData.OPTIbHighTim = parseFloat((array[2] * 0.1).toFixed(1)); //解析第五包 byteArray = hexStrTobyteArray(dataArray[4]); array = parseReadData(byteArray); //第二路过流C parseData.OPTIcHighDO1 = (array[0] >> 1) & 1; parseData.OPTIcHighDO2 = (array[0] >> 2) & 1; parseData.OPTIcHighVal02 = parseFloat((array[1] * 0.1).toFixed(1)); parseData.OPTIcHighTim = parseFloat((array[2] * 0.1).toFixed(1)); } break; case "READ_PARAM_SET": //参数设置全部读取 { let dataArray = hexStr.split("&&"); //一共有3包 if (dataArray.length < 3) { throw new Error("dataArray length error!"); } //解析第一包 let byteArray = hexStrTobyteArray(dataArray[0]); let array = parseReadData(byteArray); //故障开关 parseData.FaultSw = array[0] & 0xff; //漏电互感器变比 parseData.Lg1CT = array[1]; //仪表地址 parseData.MeterAddr = array[2]; //波特率 parseData.BaudRate = array[3]; //服务器TCP端口号 parseData.ServerPort = array[14]; //服务器IP地址 let b1 = array[15] >>> 8; let b2 = array[15] & 0xff; let b3 = array[16] >>> 8; let b4 = array[16] & 0xff; parseData.ServerIp = b2 + "." + b1 + "." + b4 + "." + b3; //解析第二包 byteArray = hexStrTobyteArray(dataArray[1]); array = parseReadData(byteArray); let domainArray = new Uint8Array(array.length * 2); //把读上来的报文里每个寄存器高低字节位置反一下 for (let i = 0; i < array.length; i++) { domainArray[2 * i] = array[i] & 0xff; domainArray[2 * i + 1] = array[i] >>> 8; } //域名 let serverDomain = String.fromCharCode.apply(null, domainArray); // 去除末尾的0 serverDomain = serverDomain.replace(/\u0000*$/, ""); parseData.ServerDomain = serverDomain; //解析第三包 byteArray = hexStrTobyteArray(dataArray[2]); array = parseReadData(byteArray); //网关IP地址 b1 = byteArray[3]; b2 = byteArray[4]; b3 = byteArray[5]; b4 = byteArray[6]; parseData.GwIp = b2 + "." + b1 + "." + b4 + "." + b3; //子网掩码 b1 = byteArray[7]; b2 = byteArray[8]; b3 = byteArray[9]; b4 = byteArray[10]; parseData.SubnetMask = b2 + "." + b1 + "." + b4 + "." + b3; //本地IP b1 = byteArray[11]; b2 = byteArray[12]; b3 = byteArray[13]; b4 = byteArray[14]; parseData.LocalIp = b2 + "." + b1 + "." + b4 + "." + b3; //DHCP parseData.DHCP = array[6]; } break; case "READ_SOFT_CODE": //读取软件编号和软件版本号 { let byteArray = hexStrTobyteArray(hexStr); parseData.softCode = (byteArray[3] << 8) + byteArray[4]; parseData.softVersion = ((byteArray[7] - 0x80) << 8) + byteArray[8]; } break; case "READ_ALARM_RECORD": //读取报警记录 { //只有1包 let byteArray = hexStrTobyteArray(hexStr); let alarmArray = []; for (let i = 0; i < 20; i++) { let index = i * 12 + 3; if (byteArray[index] === 0) { continue; } let info = {}; info.alarmType = byteArray[index]; let alarmValue = (byteArray[index + 4] << 8) + byteArray[index + 5]; switch (info.alarmType) { case 2: case 4: case 5: //温度,过压,欠压 alarmValue = parseFloat((alarmValue * 0.1).toFixed(1)); break; case 3: case 6: case 7: case 8: //过流,出线A相过流,出线B相过流,出线C相过流 alarmValue = parseFloat((alarmValue * 0.001).toFixed(3)); break; default: break; } info.alarmValue = alarmValue; let year = byteArray[index + 6] + 2000; let month = byteArray[index + 7].toString(10).padStart(2, "0"); let day = byteArray[index + 8].toString(10).padStart(2, "0"); let hour = byteArray[index + 9].toString(10).padStart(2, "0"); let minute = byteArray[index + 10].toString(10).padStart(2, "0"); let second = byteArray[index + 11].toString(10).padStart(2, "0"); info.createTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second; alarmArray.push(info); } parseData = alarmArray; } break; case "READ_FAULT_RECORD": //读取故障记录 { //只有一包 let byteArray = hexStrTobyteArray(hexStr); let faultArray = []; for (let i = 0; i < 20; i++) { let index = i * 8 + 3; let info = {}; if (byteArray[index] === 0) { continue; } info.faultType = byteArray[index]; info.channel = byteArray[index + 1]; let year = byteArray[index + 2] + 2000; let month = byteArray[index + 3].toString(10).padStart(2, "0"); let day = byteArray[index + 4].toString(10).padStart(2, "0"); let hour = byteArray[index + 5].toString(10).padStart(2, "0"); let minute = byteArray[index + 6].toString(10).padStart(2, "0"); let second = byteArray[index + 7].toString(10).padStart(2, "0"); info.createTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second; faultArray.push(info); } parseData = faultArray; } break; case "READ_DIDO_RECORD": //读取开关记录 { //只有一包 let byteArray = hexStrTobyteArray(hexStr); let eventArray = []; for (let i = 0; i < 20; i++) { let index = i * 8 + 3; let info = {}; if (byteArray[index] === 0) { continue; } let eventType = 0; switch (byteArray[index]) { case 0xf0: eventType = 1; break; case 0xf1: eventType = 2; break; case 0x0f: eventType = 3; break; case 0x1f: eventType = 4; break; } info.eventType = eventType; info.channel = byteArray[index + 1]; let year = byteArray[index + 2] + 2000; let month = byteArray[index + 3].toString(10).padStart(2, "0"); let day = byteArray[index + 4].toString(10).padStart(2, "0"); let hour = byteArray[index + 5].toString(10).padStart(2, "0"); let minute = byteArray[index + 6].toString(10).padStart(2, "0"); let second = byteArray[index + 7].toString(10).padStart(2, "0"); info.createTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second; eventArray.push(info); } parseData = eventArray; } break; default: //不支持的method throw new Error("Invalid method!"); } } catch (error) { return result; } result.res = 1; result.data = parseData; return result; } // 组包:读取命令,写入命令都需要,提取公共部分 function getSendData( sendData, address, FunCode, registerAddr, numberOfRegister ) { sendData[0] = address & 0x00ff; sendData[1] = FunCode; sendData[2] = registerAddr >> 8; sendData[3] = registerAddr & 0x00ff; sendData[4] = numberOfRegister >> 8; sendData[5] = numberOfRegister & 0x00ff; const CRC = calculateCRC(sendData); sendData[sendData.length - 2] = CRC[0]; sendData[sendData.length - 1] = CRC[1]; let hexStr = byteArrayToHexString(sendData); return hexStr; } //转16进制字符串 function byteArrayToHexString(byteArray) { return byteArray .map(function (byte) { return byte.toString(16).padStart(2, "0").toUpperCase(); }) .join(""); } //16进制字符串转byte数组 function hexStrTobyteArray(hexStr) { const byteArray = []; for (let i = 0; i < hexStr.length; i += 2) { byteArray.push(parseInt(hexStr.substr(i, 2), 16)); } return new Uint8Array(byteArray); } //转16位无符号整型 function convertToUSixteenInt(value) { //return (value & 0x0ffff).toString(16).padStart(4, "0").toUpperCase(); return value & 0x0ffff; } //转8位无符号整型 function convertToEightInt(value) { //return (value & 0x0ff).toString(16).padStart(2, "0").toUpperCase(); return value & 0x0ff; } //解析单精度浮点数 function getFloat(array, index, scale) { if (array.length - 1 <= index) { throw new Error("Analysis failed!"); } let b1 = (array[index] >>> 8) & 0x0ff; let b2 = array[index] & 0x0ff; let b3 = (array[index + 1] >>> 8) & 0x0ff; let b4 = array[index + 1] & 0x0ff; let bits = (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; 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; } // 校验生成CRC function calculateCRC(sendData) { var CRCRegister = 0x0ffff; let bit; for (let i = 0; i < sendData.length - 2; i++) { CRCRegister ^= sendData[i]; for (let j = 0; j < 8; j++) { bit = CRCRegister & 0x0001; CRCRegister = CRCRegister >> 1; if (bit === 1) { CRCRegister ^= 0xa001; } } } const CRC = [CRCRegister & 0x00ff, (CRCRegister >> 8) & 0x00ff]; return CRC; } // getData:byte数组 function checkCRC(getData) { const CRC = calculateCRC(getData); if ( CRC[0] === getData[getData.length - 2] && CRC[1] === getData[getData.length - 1] ) { return true; } else { return false; } } // 解析读取指令的返回参数 receivedData:byte数组 function parseReadData(receivedData) { // 仅仅读取一个寄存器,则返回7字节,因此低于7字节认为失败 if (receivedData.length < 7) { return false; } // 该返回命令中,实际参数字节数 一般以ushort存储,因此除以2 const values = Array(receivedData[2] / 2); // 比较参数长度是否正确 if (receivedData.length !== receivedData[2] + 5) { return false; } // 校验CRC if (checkCRC(receivedData)) { for (let i = 0; i < values.length; i++) { values[i] = receivedData[2 * i + 3]; values[i] <<= 8; values[i] += receivedData[2 * i + 4]; } return values; // 返回ushort数组,一个寄存器两个字节,常规一个寄存器代表一个参数 } else { return false; // 读取失败,返回字节CRC校验不正确 } } // 组包写入指令 values:ushort数组,一般一个参数一个寄存器,两字节,选用ushort function writeCommand(deviceAddr, registerAddr, numberToWrite, values) { let sendData = new Array(9 + 2 * numberToWrite); sendData[6] = 2 * numberToWrite; for (let i = 0; i < numberToWrite; i++) { sendData[7 + 2 * i] = (values[i] >> 8) & 0xff; sendData[8 + 2 * i] = values[i] & 0xff; } let dataStr = getSendData( sendData, deviceAddr, 0x10, registerAddr, numberToWrite ); return dataStr; } //处理DO1关联、DO2关联、保护开关 function getDo1Do2SwVal(payload, values) { //第一包,从0x102F到0x1031,写3个寄存器 //DO1关联 let do1s = payload.Lg1HighDO1 + (payload.T1HighDO1 << 1) + (payload.T2HighDO1 << 2) + (payload.T3HighDO1 << 3) + (payload.T4HighDO1 << 4); values[0] = convertToUSixteenInt(do1s); //DO2关联 let do2s = payload.Lg1HighDO2 + (payload.T1HighDO2 << 1) + (payload.T2HighDO2 << 2) + (payload.T3HighDO2 << 3) + (payload.T4HighDO2 << 4); values[1] = convertToUSixteenInt(do2s); //保护开关 let sw = payload.Lg1HighSw + (payload.T1HighSw << 1) + (payload.T2HighSw << 2) + (payload.T3HighSw << 3) + (payload.T4HighSw << 4); values[2] = convertToUSixteenInt(sw); return values; } function writeDomain(domain, startIndex, values) { // 创建一个Uint8Array,长度等于字符串长度 let byteArray = new Uint8Array(domain.length); //遍历字符串中的每个字符,并使用charCodeAt()获取ASCII码 for (let i = 0; i < domain.length; i++) { byteArray[i] = domain.charCodeAt(i); } for (let i = 0; i < byteArray.length; i += 2) { let charCode1 = byteArray[i]; let charCode2 = i + 1 < byteArray.length ? byteArray[i + 1] : 0; //寄存器高低字节赋值,示例 tcp.imuyuan.com 第一个寄存器应该存储`t`和`c`两个字符,`t`要放到低字节位置,`c`要放到高字节位置 values[startIndex + i / 2] = charCode1 | (charCode2 << 8); } return values; } function writeIp(ip, startIndex, values) { // 将IP地址拆分成4个部分,并转换为整数后存入数组 let parts = ip.split("."); let arr = new Array(4); for (let i = 0; i < 4; i++) { arr[i] = parseInt(parts[i], 10); } //低位在前 values[startIndex] = (arr[1] << 8) + arr[0]; values[startIndex + 1] = (arr[3] << 8) + arr[2]; return values; } ```
王旭东
2025年2月24日 09:26
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码