| | |
| | | package com.hdl.photovoltaic.internet; |
| | | |
| | | import android.text.TextUtils; |
| | | |
| | | import com.hdl.sdk.link.core.callback.HDLLinkCallBack; |
| | | import com.hdl.sdk.link.core.callback.ModbusCallBack; |
| | | import com.hdl.sdk.link.core.connect.HDLModBusConnect; |
| | | import com.hdl.sdk.link.gateway.HDLLinkLocalGateway; |
| | | |
| | | import java.nio.ByteBuffer; |
| | | |
| | | |
| | | /** |
| | | * 客户端操作逻辑 |
| | | */ |
| | | public class TcpClient { |
| | | |
| | | private static volatile TcpClient sTcpClient; |
| | | |
| | | /** |
| | | * 获取当前对象 |
| | | * |
| | | * @return HdlAccountLogic |
| | | */ |
| | | public static synchronized TcpClient getInstance() { |
| | | if (sTcpClient == null) { |
| | | synchronized (TcpClient.class) { |
| | | if (sTcpClient == null) { |
| | | sTcpClient = new TcpClient(); |
| | | } |
| | | } |
| | | |
| | | } |
| | | return sTcpClient; |
| | | } |
| | | |
| | | /** |
| | | * 发送数据到Link网关 |
| | | * |
| | | * @param mac 网关mac |
| | | * @param isEncrypt 是否加密 |
| | | * @param topic 请求主题 |
| | | * @param jObject 负载数据<没有填null></> |
| | | * @param sendPath 发送路径<类名+方法名>class->methodName</> |
| | | */ |
| | | public void sendDataToLinkGateway(String mac, boolean isEncrypt, String topic, Object jObject, String sendPath, HDLLinkCallBack callBack) { |
| | | HDLLinkLocalGateway.getInstance().sendDataToLinkGateway(mac, isEncrypt, topic, jObject, sendPath, callBack); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 发送数据到Link网关 |
| | | * |
| | | * @param mac 网关mac |
| | | * @param topic 请求主题 |
| | | * @param jObject 负载数据<没有填null></> |
| | | * @param sendPath 发送路径<类名+方法名>class->methodName</> |
| | | */ |
| | | public void sendDataToLinkGateway(String mac, String topic, Object jObject, String sendPath, HDLLinkCallBack callBack) { |
| | | HDLLinkLocalGateway.getInstance().sendDataToLinkGateway(mac, topic, jObject, sendPath, callBack); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 发送ModBus数据到网关 |
| | | * 下发主题底层默认 |
| | | * |
| | | * @param mac 网关mac |
| | | * @param oidAddresses oid物理信息里面Addresses地址 |
| | | * @param functionCode 功能码【3=(读取多个寄存器),6=(写入单个寄存器),16=(写入多个寄存器)】 |
| | | * @param registerAddress 寄存器地址(根据文档上填十进制值) |
| | | * @param contentData 内容数据(不含寄存器地址)【注意:功能码=3时,根据文档上填数据字节数,例如读取设备备注,填20Byte】 |
| | | */ |
| | | public void sendModBusDataToLinkGateway(String mac, String oidAddresses, FunctionCode functionCode, int registerAddress, String contentData, ModbusCallBack modbusCallBack) { |
| | | String data = ""; |
| | | switch (functionCode) { |
| | | case ReadMultipleRegisters: { |
| | | data = readModbusPassData(oidAddresses, registerAddress, functionCode, contentData); |
| | | } |
| | | break; |
| | | case WriteSingleRegister: { |
| | | data = writeSingleModbusPassData(oidAddresses, registerAddress, functionCode, contentData); |
| | | } |
| | | break; |
| | | case WriteMultipleRegisters: { |
| | | data = writeMultipleModbusPassData(oidAddresses, registerAddress, functionCode, contentData); |
| | | } |
| | | break; |
| | | } |
| | | HDLModBusConnect.getInstance().Send(mac, getDataByteArray(data), modbusCallBack); |
| | | } |
| | | |
| | | /** |
| | | * 获取读取【modbus协议】拼接透传数据的字符串 |
| | | * 下发主题:/user/${gw_id}/custom/native/${driver}/down; |
| | | * 逆变器回复主题:/user/${gw_id}/custom/native/${driver}/down_reply; |
| | | * Modbus ECU协议:事件ID(2个byte)->协议[固定:0,0](2个byte)->长度(2byte)->标识符[oid的addresses值](4个byte)->功能码(1个byte)->负载数据(N个byte); |
| | | * 长度(2个byte)=标识符(4个byte)+功能码(1个byte)+负载数据(N个byte); |
| | | * 负载数据=寄存器地址(2个byte)+寄存器长度(2个byte)+内容长度(1个byte)+内容数据(数据字节数->N个byte)【注意:单个写入寄存器-->去掉<寄存器长度>和<内容长度>】; |
| | | * 寄存器长度=(内容数据/2); |
| | | * 例子:new byte[]{1,54,0,0,0,9,0,0,200,26,3,1,54,0,1}; |
| | | * |
| | | * @param oidAddresses oid物理信息里面Addresses地址 |
| | | * @param functionCode 功能码【3=(读取多个寄存器)),6=(写入单个寄存器),16=(写入多个寄存器)】 |
| | | * @param registerAddress 寄存器地址(根据文档上十进制值填) |
| | | * @param contentData 内容数据(不含寄存器地址) |
| | | */ |
| | | private String readModbusPassData(String oidAddresses, int registerAddress, FunctionCode functionCode, String contentData) { |
| | | String data = ""; |
| | | try { |
| | | String eventID = to2ByteHexString(registerAddress);//事件ID(2byte) |
| | | String agreement = "0000";//协议(2byte) |
| | | String dataByteLength = "";//长度(N byte) |
| | | String oidAddresses_1 = oidAddresses;//标识符(4byte) |
| | | String functionCode_1 = to1ByteHexString(functionCode.value());//功能码(1byte) |
| | | String registerAddress_1 = to2ByteHexString(registerAddress);//寄存器地址(2byte) |
| | | String registerLength_1 = to2ByteHexString(Integer.parseInt(contentData) / 2);//寄存器长度(2byte) |
| | | byte[] bytes = (oidAddresses_1 + functionCode_1 + registerAddress_1 + registerLength_1).getBytes(); |
| | | dataByteLength = to2ByteHexString(bytes.length / 2); |
| | | data = eventID + agreement + dataByteLength + oidAddresses_1 + functionCode_1 + registerAddress_1 + registerLength_1; |
| | | return data; |
| | | } catch (Exception ignored) { |
| | | return data; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取写入单个【modbus协议】拼接透传数据的字符串 |
| | | * 下发主题:/user/${gw_id}/custom/native/${driver}/down; |
| | | * 逆变器回复主题:/user/${gw_id}/custom/native/${driver}/down_reply; |
| | | * Modbus ECU协议:事件ID(2个byte)->协议[固定:0,0](2个byte)->长度(2byte)->标识符[oid的addresses值](4个byte)->功能码(1个byte)->负载数据(N个byte); |
| | | * 长度(2个byte)=标识符(4个byte)+功能码(1个byte)+负载数据(N个byte); |
| | | * 负载数据=寄存器地址(2个byte)+寄存器长度(2个byte)+内容长度(1个byte)+内容数据(数据字节数->N个byte)【注意:单个写入寄存器-->去掉<寄存器长度>和<内容长度>】; |
| | | * 寄存器长度=(内容数据/2); |
| | | * 例子:new byte[]{199, 112, 0, 0, 0, 9, 0, 0, 0, 26, 6, 199, 112, 0, 110}; |
| | | * |
| | | * @param oidAddresses oid物理信息里面Addresses地址 |
| | | * @param functionCode 功能码【3=(读取多个寄存器)),6=(写入单个寄存器),16=(写入多个寄存器)】 |
| | | * @param registerAddress 寄存器地址(根据文档上十进制值填) |
| | | * @param contentData 内容数据(不含寄存器地址) |
| | | */ |
| | | private String writeSingleModbusPassData(String oidAddresses, int registerAddress, FunctionCode functionCode, String contentData) { |
| | | String data = ""; |
| | | try { |
| | | String eventID = to2ByteHexString(registerAddress);//事件ID(2byte) |
| | | String agreement = "0000";//协议(2byte) |
| | | String dataByteLength = "";//长度(N byte) |
| | | String oidAddresses_1 = oidAddresses;//标识符(4byte) |
| | | String functionCode_1 = to1ByteHexString(functionCode.value());//功能码(1byte) |
| | | String registerAddress_1 = to2ByteHexString(registerAddress);//寄存器地址(2byte) |
| | | String contentData_1 = contentData;//内容数据(N byte) |
| | | byte[] bytes = (oidAddresses_1 + functionCode_1 + registerAddress_1 + contentData_1).getBytes(); |
| | | dataByteLength = to2ByteHexString(bytes.length / 2); |
| | | data = eventID + agreement + dataByteLength + oidAddresses_1 + functionCode_1 + registerAddress_1 + contentData_1; |
| | | return data; |
| | | } catch (Exception ignored) { |
| | | return data; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取写入多个【modbus协议】拼接透传数据的字符串 |
| | | * 下发主题:/user/${gw_id}/custom/native/${driver}/down; |
| | | * 逆变器回复主题:/user/${gw_id}/custom/native/${driver}/down_reply; |
| | | * Modbus ECU协议:事件ID(2个byte)->协议[固定:0,0](2个byte)->长度(2byte)->标识符[oid的addresses值](4个byte)->功能码(1个byte)->负载数据(N个byte); |
| | | * 长度(2个byte)=标识符(4个byte)+功能码(1个byte)+负载数据(N个byte); |
| | | * 负载数据=寄存器地址(2个byte)+寄存器长度(2个byte)+内容长度(1个byte)+内容数据(数据字节数->N个byte)【注意:单个写入寄存器-->去掉<寄存器长度>和<内容长度>】; |
| | | * 寄存器长度=(内容数据/2); |
| | | * 例子:new byte[]{[0, 11, 0, 0, 0, 30, 0, 0, 0, 26, 16, 0, 11, 0, 10, 20, 49, 50, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32]}; |
| | | * |
| | | * @param oidAddresses oid里面Addresses的值 |
| | | * @param functionCode 功能码【3=(读取多个寄存器)),6=(写入单个寄存器),16=(写入多个寄存器)】 |
| | | * @param registerAddress 寄存器地址(根据文档上十进制值填) |
| | | * @param contentData 内容数据(不含寄存器地址) |
| | | */ |
| | | private String writeMultipleModbusPassData(String oidAddresses, int registerAddress, FunctionCode functionCode, String contentData) { |
| | | |
| | | String data = ""; |
| | | try { |
| | | String eventID = to2ByteHexString(registerAddress);//事件ID(2byte) |
| | | String agreement = "0000";//协议(2byte) |
| | | String dataByteLength = "";//长度(N byte) |
| | | String oidAddresses_1 = oidAddresses;//标识符(4byte) |
| | | String functionCode_1 = to1ByteHexString(functionCode.value());//功能码(1byte) |
| | | String registerAddress_1 = to2ByteHexString(registerAddress);//寄存器地址(2byte) |
| | | String registerLength_1 = to2ByteHexString(contentData.length() / 2);//寄存器长度(2byte) |
| | | String contentLength_1 = to1ByteHexString(contentData.length());//内容长度(1byte) |
| | | String contentData_1 = contentData;//内容数据(N byte) |
| | | byte[] bytes = (oidAddresses_1 + functionCode_1 + registerAddress_1 + registerLength_1 + contentLength_1 + contentData_1).getBytes(); |
| | | dataByteLength = to2ByteHexString(bytes.length / 2); |
| | | data = eventID + agreement + dataByteLength + oidAddresses_1 + functionCode_1 + registerAddress_1 + registerLength_1 + contentLength_1 + contentData_1; |
| | | return data; |
| | | } catch (Exception ignored) { |
| | | return data; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 逐个解析两个字符,将其转换为字节 |
| | | * |
| | | * @param hexString 16进制的字符串 |
| | | * @return 返回数组 |
| | | */ |
| | | private byte[] getDataByteArray(String hexString) { |
| | | try { |
| | | if (TextUtils.isEmpty(hexString)) { |
| | | return new byte[]{}; |
| | | } |
| | | //去除字符串前后的空格 |
| | | hexString = hexString.trim(); |
| | | // 确保字符串的长度是偶数 |
| | | if (hexString.length() % 2 != 0) { |
| | | //无效的十六进制字符串 |
| | | return new byte[]{}; |
| | | } |
| | | // 计算字节数组的长度 |
| | | int byteArrayLength = hexString.length() / 2; |
| | | byte[] byteArray = new byte[byteArrayLength]; |
| | | //逐个解析两个字符,将其转换为字节 |
| | | for (int i = 0; i < byteArrayLength; i++) { |
| | | byteArray[i] = (byte) Integer.parseInt(hexString.substring(i * 2, i * 2 + 2), 16); |
| | | } |
| | | return byteArray; |
| | | } catch (Exception ignored) { |
| | | return new byte[]{}; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取十六进制字符串(不够两位前面补0) |
| | | * |
| | | * @param value 寄存器地址(十进制) |
| | | * @return (2byte)十六进制字符串 |
| | | */ |
| | | private String to2ByteHexString(int value) { |
| | | String high = ""; |
| | | String low = ""; |
| | | if (value > 255) { |
| | | high = to1ByteHexString(value / 256); |
| | | |
| | | low = to1ByteHexString(value % 256); |
| | | } else { |
| | | high = "00"; |
| | | low = to1ByteHexString(value); |
| | | } |
| | | return high + low; |
| | | } |
| | | |
| | | /** |
| | | * 获取十六进制字符串(不够两位前面补0) |
| | | * |
| | | * @param value 十进制 |
| | | * @return (1byte)十六进制字符串 |
| | | */ |
| | | private String to1ByteHexString(int value) { |
| | | try { |
| | | String hexString = Integer.toHexString(value); |
| | | if (hexString.length() == 1) { |
| | | return "0" + hexString; |
| | | } |
| | | return hexString; |
| | | } catch (Exception e) { |
| | | return "00"; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取十六进制字符串(不够两位前面补0) |
| | | * |
| | | * @param dataBytes 数组 |
| | | * @return -(n byte)十六进制字符串 |
| | | */ |
| | | private String toNByteHexString(byte[] dataBytes) { |
| | | StringBuilder stringBuilder = new StringBuilder(); |
| | | for (byte dataByte : dataBytes) { |
| | | String dataB = Integer.toHexString(dataByte & 0xFF); |
| | | if (dataB.length() == 1) { |
| | | dataB = "0" + dataB; |
| | | } |
| | | stringBuilder.append(dataB); |
| | | } |
| | | return stringBuilder.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 获取十六进制字符串(不够两位前面补0) |
| | | * |
| | | * @param value 寄存器地址(十进制) |
| | | * @return (2byte)十六进制字符串 |
| | | */ |
| | | private String getByteHexString(int value) { |
| | | String high = ""; |
| | | String low = ""; |
| | | if (value > 255) { |
| | | high = to1ByteHexString(value / 256); |
| | | |
| | | low = to1ByteHexString(value % 256); |
| | | } else { |
| | | high = "00"; |
| | | low = to1ByteHexString(value); |
| | | } |
| | | return high + low; |
| | | } |
| | | |
| | | /** |
| | | * bytes数组转Int值 |
| | | * |
| | | * @param bytes 数据 |
| | | */ |
| | | public int toByteArrayInt(byte[] bytes) { |
| | | int ans = 0; |
| | | for (byte b : bytes) { |
| | | ans <<= 8; |
| | | ans |= (b & 0xff); |
| | | } |
| | | return ans; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 功能码 |
| | | */ |
| | | public enum FunctionCode { |
| | | |
| | | /** |
| | | * 读取单个或者多个寄存器 |
| | | */ |
| | | ReadMultipleRegisters(3), |
| | | /** |
| | | * 写入单个寄存器 |
| | | */ |
| | | WriteSingleRegister(6), |
| | | /** |
| | | * 写入多个寄存器 |
| | | */ |
| | | WriteMultipleRegisters(16); |
| | | private int mValue; |
| | | |
| | | FunctionCode(int value) { |
| | | this.mValue = value; |
| | | } |
| | | |
| | | public int value() { |
| | | return this.mValue; |
| | | } |
| | | } |
| | | } |