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().asyncSend(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;
|
}
|
}
|
}
|