wjc
2024-12-23 f753d8366041354da60b8096060f3ab5159e3880
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
package com.hdl.sdk.link.core.connect;
 
 
import android.content.Context;
import android.net.wifi.WifiManager;
import android.text.TextUtils;
 
import com.hdl.link.error.HDLLinkCode;
import com.hdl.sdk.link.HDLLinkLocalSdk;
import com.hdl.sdk.link.common.config.TopicConstant;
import com.hdl.sdk.link.common.event.EventDispatcher;
import com.hdl.sdk.link.common.event.EventListener;
import com.hdl.sdk.link.common.exception.HDLLinkException;
import com.hdl.sdk.link.common.utils.LogUtils;
import com.hdl.sdk.link.core.bean.BusProRequest;
import com.hdl.sdk.link.core.bean.BusProResponse;
import com.hdl.sdk.link.core.bean.LinkRequest;
import com.hdl.sdk.link.core.bean.LinkResponse;
import com.hdl.sdk.link.core.bean.gateway.GatewayBean;
import com.hdl.sdk.link.core.callback.BusProCallBack;
import com.hdl.sdk.link.core.config.HDLLinkConfig;
import com.hdl.sdk.link.core.protocol.LinkMessageDecoderUdp;
import com.hdl.sdk.link.core.utils.BusProUtils;
import com.hdl.sdk.link.core.utils.ByteUtils;
import com.hdl.sdk.link.enums.NativeType;
import com.hdl.sdk.link.gateway.HDLLinkLocalGateway;
import com.hdl.sdk.link.socket.client.UdpClient;
import com.hdl.sdk.link.socket.codec.MessagePipeLine;
import com.hdl.sdk.link.socket.udp.UdpSocketBoot;
import com.hdl.sdk.link.socket.udp.UdpSocketOptions;
 
/**
 * Created by hxb on 2021/12/8.
 * Bus原生通讯相关接口
 */
public class HDLBusProConnect {
 
    private static final String TAG="HDLBusProConnect";
    private static HDLBusProConnect instance;
    /**
     * 内部用,主要是处理处理掉透传主题及link主题后,还原Bus原生数据及主题用
     */
    private final String busProAllTopic = "/BusPro";
    /**
     * udp默认端口
     */
    public static  int UDP_PORT = 6000;
    /**
     * 因为考虑到使用一个端口,要支持接收多网关的数据,所以只允许使用一个
     */
    private static UdpSocketBoot udpSocketBoot;
 
    /**
     * 返回当前实例,不存在就创建并同时注册监听事件
     *
     * @return
     */
    public static HDLBusProConnect getInstance() {
        if (null == instance) {
            instance = new HDLBusProConnect();
            instance.initEventListener();
        }
        return instance;
    }
    private UdpSocketOptions getUdpOptions() {
        final UdpSocketOptions options = new UdpSocketOptions();
        WifiManager manager = (WifiManager) HDLLinkLocalSdk.getInstance().getContext().getApplicationContext()
                .getSystemService(Context.WIFI_SERVICE);
        options.setWifiManager(manager);
        final MessagePipeLine pipeLine = new MessagePipeLine();
        pipeLine.add(new LinkMessageDecoderUdp());
//        pipeLine.add(new LinkMessageEncoder());
        options.setHandleMessage(pipeLine);
        return options;
    }
 
    /**
     * 获取当前udp对象,如果不存在就创建
     *
     * @return 返回当前对象
     */
    public synchronized UdpSocketBoot getUdpBoot() {
        if (null == initUdp()) {
            return null;
        }
        return udpSocketBoot;
    }
 
    /**
     * 初始化udp 监听功能
     *
     * @return 返回当前对象
     */
    public synchronized UdpSocketBoot initUdp() {
        try {
            if (udpSocketBoot == null) {
                udpSocketBoot = UdpClient.init("0.0.0.0",UDP_PORT, getUdpOptions());
                udpSocketBoot.bind();
            }
        } catch (Exception e) {
            LogUtils.e("初始化网关失败:"+e.getMessage());
            return null;
        }
 
        return udpSocketBoot;
    }
 
    /**
     * 注册监听Zigbee所有原生主题及数据
     *
     * @param eventListener
     */
    public void registerListener(EventListener eventListener) {
        if(null==eventListener){
            return;
        }
        EventDispatcher.getInstance().register(busProAllTopic, eventListener);
    }
 
    /**
     * 移除监听Zigbee原生主题及数据
     *
     * @param eventListener
     */
    public void removeListener(EventListener eventListener) {
        if(null==eventListener){
            return;
        }
        EventDispatcher.getInstance().remove(busProAllTopic, eventListener);
    }
 
    /**
     * 初始化监听事件
     */
    private void initEventListener() {
        final EventListener eventListener = new EventListener() {
            @Override
            public void onMessage(Object msg) {
                try {
                    if (msg instanceof LinkResponse) {
                        LinkResponse linkResponse = (LinkResponse) msg;
                        //非buspro数据
                        if (BusProUtils.isBusProBytes(linkResponse.getByteData()) == false) {
                            return;
                        }
                        byte[] busBytes = linkResponse.getByteData();
 
                        //数据包长度
                        int len=ByteUtils.ByteToInt(busBytes[16]);
                        // 源子网号
                        byte sourceSubnetId = busBytes[17];
                        // 源设备号
                        byte sourceDeviceId = busBytes[18];
                        //设备类型
                        byte[]deviceType=new byte[]{busBytes[19], busBytes[20]};
                        //操作码
                        byte[] command = new byte[]{busBytes[21], busBytes[22]};
 
//                        if (sourceSubnetId == BusProUtils.getSourceSubnetId() && sourceDeviceId == BusProUtils.getSourceDeviceId()) {
//                            return;// 自己发送给自己的,不接收
//                        }
 
                        //目标子网号
                        byte desSubnetId = busBytes[23];
                        //目标设备号
                        byte desDeviceId = busBytes[24];
 
                        //目标不是广播或者不是发给自己的数据,不接收
//                        if (!((desSubnetId == BusProUtils.getSourceSubnetId() && desDeviceId == BusProUtils.getSourceDeviceId())
//                                || (desSubnetId == 0xff && desDeviceId == 0xff))) {
//                            return;
//                        }
                        //附加数据包长度
                        int extraBytesLenght = len - 11;
                        if (extraBytesLenght < 0) {
                            return;
                        }
                        int index=25;
                        //如果是大包,这块比较少用到。这个命令比较特殊
                        if(len==0xff) {
                            extraBytesLenght = ByteUtils.ByteToInt(busBytes[25]) * 256 + ByteUtils.ByteToInt(busBytes[26]);
                            index=25+2;
                        }
                        byte []extraBytes = new byte[extraBytesLenght];
                        // 复制附加数据
                        System.arraycopy(busBytes, index, extraBytes, 0, extraBytesLenght);
                        //提取操作码,通过操作码获取匹配信息
                        String matchKey = BusProUtils.getMatchString(sourceSubnetId, sourceDeviceId, command, extraBytes);
                        BusProResponse busProResponse = new BusProResponse();
                        busProResponse.setTopic(matchKey);//这块比较特殊,主题基本上用不上
                        busProResponse.setData(busBytes);//原始数据
                        //解析之后的数据
                        busProResponse.setIpAddress(String.format("%s.%s.%s.%s",ByteUtils.ByteToInt(busBytes[0]),ByteUtils.ByteToInt(busBytes[1]),ByteUtils.ByteToInt(busBytes[2]),ByteUtils.ByteToInt(busBytes[3])));
                        busProResponse.setSourceSubnetId(sourceSubnetId);
                        busProResponse.setSourceDeviceId(sourceDeviceId);
                        busProResponse.setDeviceType(deviceType);
                        busProResponse.setCommand(command);
                        busProResponse.setDesSubnetId(desSubnetId);
                        busProResponse.setDesDeviceId(desDeviceId);
                        busProResponse.setExtraBytes(extraBytes);
 
                        String oid = null;
                        //是否是通过主网关透传主题
                        if (linkResponse.getTopic().contains("/slaveoid/")) {
                            oid = linkResponse.getTopic().split("/")[8];
                        } else {
                            oid = linkResponse.getTopic().split("/")[2];
                        }
                        busProResponse.setOid(oid);
 
                        for (GatewayBean gatewayBean : HDLLinkLocalGateway.getInstance().getGatewayList()) {
                            if (oid.equals(gatewayBean.getGatewayId()) || oid.equals(gatewayBean.getDevice_mac()) || oid.equals(gatewayBean.getOid())) {
                                //上面的oid可能是网关id或者mac或者是oid,不管是哪个统一使用oid表示方式
                                busProResponse.setOid(gatewayBean.getOid());
                                break;
                            }
                        }
                        EventDispatcher.getInstance().post(matchKey, busProResponse);
                    }
                } catch (Exception e) {
                    LogUtils.e(TAG, "LinkResponse转BusProResponse异常:" + e.getMessage());
                }
            }
        };
 
        //注册直接通讯的主题,包括直接和主网关通讯或者直接和从网关通讯
        registerListener(String.format(TopicConstant.NATIVE_BUSPRO_UP, "+"), eventListener);
        //从网关透传
        registerListener(String.format(TopicConstant.NATIVE_BUSPRO_UP_SLAVE, "+", "+"), eventListener);
    }
 
    public void Send(String gatewayOidOrGatewayId, BusProRequest busProRequest, final BusProCallBack busProCallBack) {
        Send(gatewayOidOrGatewayId, busProRequest, busProCallBack, busProRequest.getAwaitTime() <= 0 ? 1000L : busProRequest.getAwaitTime(), busProRequest.getMaxRetry() <= 0 ? 4 : busProRequest.getMaxRetry());
    }
 
    /**
     * 发送Bus命令
     * @param gatewayOidOrGatewayId 设备的网关Id
     * @param busProRequest 发送的请求数据
     * @param busProCallBack 结果回调
     * @param awaitTime 每次重发的时间
     * @param maxRetry 最多重发多少次
     */
    public void Send(String gatewayOidOrGatewayId, BusProRequest busProRequest, final BusProCallBack busProCallBack,long awaitTime,int maxRetry) {
        //如果本地有链接这个网关,则用本地连接
        GatewayBean gatewayBean = HDLLinkLocalGateway.getInstance().getGatewayByOidOrGatewayId(gatewayOidOrGatewayId);
        if (null == gatewayBean) {
            LogUtils.i("找不到网关,Oid是" + gatewayOidOrGatewayId);
            if (null != busProCallBack) {
                busProCallBack.onError(HDLLinkException.getErrorWithCode(HDLLinkCode.HDL_GATEWAY_NOT_EXIST));
            }
            return;
        }
 
        String topic = String.format(TopicConstant.NATIVE_BUSPRO_DOWN, gatewayBean.getDevice_mac());
        byte[] busBytes = BusProUtils.getBusBytes(busProRequest);//需发出去的bus数据
        LinkRequest request = new LinkRequest(topic, busBytes, HDLLinkConfig.getInstance().isLocalEncrypt());
        request.setNativeType(NativeType.BusPro);
        if ("true".equals(gatewayBean.getMaster())) {
            request.setCloudTopic(String.format(TopicConstant.NATIVE_BUSPRO_DOWN, HDLLinkConfig.getInstance().getGatewayId()));
        } else {
            request.setCloudTopic(String.format(TopicConstant.NATIVE_BUSPRO_DOWN_SLAVE, HDLLinkConfig.getInstance().getGatewayId(), gatewayOidOrGatewayId));
        }
 
        int command;
        if(busProRequest.getAckCommand()==null){
            command = ByteUtils.ByteToInt(busProRequest.getCommand()) + 1;//回复的操作码是发送的操作码+1
        }
        else {
            //发送的操作码与回复的不是一对的情况,采用此方式
            command = ByteUtils.ByteToInt(busProRequest.getAckCommand());
        }
 
        String matchKey = BusProUtils.getMatchString(busProRequest.getDesSubnetId(), busProRequest.getDesDeviceId(), ByteUtils.IntToByte(command), busProRequest.getExtraBytes());
        request.setReplyTopic(matchKey);
        new HDLConnectHelper(awaitTime,maxRetry, gatewayBean.getIp_address(), 8586, request, new HDLConnectHelper.HdlSocketListener() {
            @Override
            public void onSucceed(Object msg) {
                if (msg instanceof BusProResponse) {
                    if (null != busProCallBack) {
                        busProCallBack.onSuccess((BusProResponse) msg);
                    }
                }
            }
 
            @Override
            public void onFailure(HDLLinkCode hdlLinkCode) {
                if (null != busProCallBack) {
                    busProCallBack.onError(HDLLinkException.getErrorWithCode(hdlLinkCode));
                }
            }
        }, true).send();
    }
 
    /**
     * 注册监听
     */
     void registerListener(String responseTopic, EventListener eventListener) {
        if (!TextUtils.isEmpty(responseTopic)) {
            EventDispatcher.getInstance().register(responseTopic, eventListener);
        }
    }
 
    /**
     * 移除监听
     */
     void removeListener(String responseTopic, EventListener eventListener) {
        if (!TextUtils.isEmpty(responseTopic)) {
            EventDispatcher.getInstance().remove(responseTopic, eventListener);
        }
    }
}