mac
2024-02-02 ba84a556dc93fcf75e6ec76f999344d3f96ad788
app/src/main/java/com/hdl/photovoltaic/internet/TcpClient.java
@@ -1,4 +1,349 @@
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;
        }
    }
}