Adding last version of iotruntime and iotinstaller; preparing to extend IoTMaster...
[iot2.git] / iotjava / iotruntime / zigbee / IoTZigbee.java
1 package iotruntime.zigbee;
2
3 // Java packages
4 import java.io.IOException;
5 import java.net.DatagramPacket;
6 import java.net.DatagramSocket;
7 import java.net.InetAddress;
8 import java.net.SocketException;
9 import java.net.UnknownHostException;
10 import java.util.concurrent.atomic.AtomicBoolean;
11 import java.util.List;
12 import java.util.ArrayList;
13 import java.util.Map;
14 import java.util.HashMap;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.Set;
17 import java.util.HashSet;
18 import java.nio.charset.StandardCharsets;
19 import java.util.concurrent.Semaphore;
20
21 import iotruntime.slave.IoTZigbeeAddress;
22 import iotruntime.slave.IoTDeviceAddress;
23
24 /** Class IoTZigbee
25  *
26  * @author      Ali Younis <ayounis @ uci.edu>
27  * @version     1.0
28  * @since       2016-04-12
29  */
30 public class IoTZigbee {
31
32         public final int SOCKET_SEND_BUFFER_SIZE = 1024;
33         public final int SOCKET_RECEIVE_BUFFER_SIZE = 1024;
34         public final int SHORT_ADDRESS_UPDATE_TIME_MSEC = 10000;
35         public final int SHORT_ADDRESS_UPDATE_TIME_FAST_MSEC = 500;
36         public final int RESEND_WAIT_TIME = 500;
37
38         /**
39          * IoTZigbee class properties
40          */
41
42         // UDP connection stuff
43         private final String strHostAddress;
44         private final int iSrcPort;
45         private final int iDstPort;
46         private DatagramSocket socket;  // the socket interface that we are guarding
47         private boolean didClose;                                                               // make sure that the clean up was done correctly
48
49         private final IoTZigbeeAddress zigbeeAddress;
50
51         // list that holds the callbacks
52         private List<IoTZigbeeCallback> callbackList = new ArrayList<IoTZigbeeCallback>();
53
54         /**
55          * IoTZigbee class concurrency and concurrency control
56          */
57         private Thread receiveThread = null;
58
59         private AtomicBoolean endTask = new AtomicBoolean(false);
60         private AtomicBoolean didSuccesfullySendAddress = new AtomicBoolean(false);
61
62         /**
63          * Class constructor
64          */
65         public IoTZigbee(IoTDeviceAddress iotDevAdd, IoTZigbeeAddress zigAddress) throws SocketException, IOException, InterruptedException {
66
67                 strHostAddress = iotDevAdd.getHostAddress();
68                 iSrcPort = iotDevAdd.getSourcePortNumber();
69                 iDstPort = iotDevAdd.getDestinationPortNumber();
70                 didClose = false;
71                 zigbeeAddress = zigAddress;
72
73                 socket = new DatagramSocket(iSrcPort);
74                 socket.setSendBufferSize(SOCKET_SEND_BUFFER_SIZE);
75                 socket.setReceiveBufferSize(SOCKET_RECEIVE_BUFFER_SIZE);
76
77                 receiveThread = new Thread(new Runnable() {
78                         public void run() {
79                                 receieveWorker();
80                         }
81                 });
82                 receiveThread.start();
83         }
84
85         public void init() throws IOException {
86                 while (!didSuccesfullySendAddress.get()) {
87
88                         sendDeviceAddress();
89
90                         try {
91                                 Thread.sleep(RESEND_WAIT_TIME);
92                         } catch (Exception e) {
93                                 e.printStackTrace();
94                         }
95                 }
96         }
97
98
99         public void sendBindRequest(int packetId, int clusterId, int deviceEndpoint) throws IOException {
100                 String message = "type: zdo_bind_request\n";
101                 message += "packet_id: " + String.format("%04x", packetId) + "\n";
102                 message += "device_address_long: " + zigbeeAddress.getAddress() + "\n";
103                 message += "cluster_id: " + String.format("%04x", clusterId) + "\n";
104                 message += "device_endpoint: " + String.format("%02x", deviceEndpoint) + "\n";
105                 DatagramPacket sendPacket = new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(strHostAddress), iDstPort);
106                 socket.send(sendPacket);
107         }
108
109         public void sendUnBindRequest(int packetId, int clusterId, int deviceEndpoint) throws IOException {
110                 String message = "type: zdo_unbind_request\n";
111                 message += "packet_id: " + String.format("%04x", packetId) + "\n";
112                 message += "device_address_long: " + zigbeeAddress.getAddress() + "\n";
113                 message += "cluster_id: " + String.format("%04x", clusterId) + "\n";
114                 message += "device_endpoint: " + String.format("%02x", deviceEndpoint) + "\n";
115                 DatagramPacket sendPacket = new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(strHostAddress), iDstPort);
116                 socket.send(sendPacket);
117         }
118
119         public void sendReadAttributesCommand(int packetId, int clusterId, int profileId, int deviceEndpoint, List<Integer> attributeIds) throws IOException {
120                 String message = "type: zcl_read_attributes\n";
121                 message += "packet_id: " + String.format("%04x", packetId) + "\n";
122                 message += "device_address_long: " + zigbeeAddress.getAddress() + "\n";
123                 message += "cluster_id: " + String.format("%04x", clusterId) + "\n";
124                 message += "profile_id: " + String.format("%04x", profileId) + "\n";
125                 message += "device_endpoint: " + String.format("%02x", deviceEndpoint) + "\n";
126
127                 message += "attribute_ids: ";
128
129                 for (Integer i : attributeIds) {
130                         message += String.format("%04x", i) + ",";
131                 }
132
133                 message = message.substring(0, message.length() - 1);
134                 message += "\n";
135
136                 DatagramPacket sendPacket = new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(strHostAddress), iDstPort);
137                 socket.send(sendPacket);
138         }
139
140         public void sendConfigureReportingCommand(int packetId, int clusterId, int profileId, int deviceEndpoint, int attributeId, int dataType, int minReportingInterval, int maxReportingInterval, byte[] reportableChange) throws IOException {
141                 String message = "type: zcl_configure_reporting\n";
142                 message += "packet_id: " + String.format("%04x", packetId) + "\n";
143                 message += "device_address_long: " + zigbeeAddress.getAddress() + "\n";
144                 message += "cluster_id: " + String.format("%04x", clusterId) + "\n";
145                 message += "profile_id: " + String.format("%04x", profileId) + "\n";
146                 message += "device_endpoint: " + String.format("%02x", deviceEndpoint) + "\n";
147                 message += "attribute_id: " + String.format("%04x", attributeId) + "\n";
148                 message += "data_type: " + String.format("%02x", dataType) + "\n";
149                 message += "min_reporting_interval: " + String.format("%04x", minReportingInterval) + "\n";
150                 message += "max_reporting_interval: " + String.format("%04x", maxReportingInterval) + "\n";
151
152                 if (reportableChange != null) {
153                         message += "reportable_change: ";
154                         for (Byte b : reportableChange) {
155                                 message += String.format("%02x", (int)b);
156                         }
157                         message += "\n";
158                 }
159
160                 DatagramPacket sendPacket = new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(strHostAddress), iDstPort);
161                 socket.send(sendPacket);
162         }
163
164         public void registerCallback(IoTZigbeeCallback callbackTo) {
165                 callbackList.add(callbackTo);
166         }
167
168         public void close() throws InterruptedException {
169                 endTask.set(true);
170
171                 // wait for the threads to end
172                 receiveThread.join();
173
174                 socket.close();
175                 didClose = true;
176         }
177
178         /**
179          * close() called by the garbage collector right before trashing object
180          */
181         public void Finalize() throws SocketException, InterruptedException {
182
183                 if (!didClose) {
184                         close();
185                         throw new SocketException("Socket not closed before object destruction, must call close method.");
186                 }
187         }
188
189         private void sendDeviceAddress() throws IOException {
190                 String message = "type: send_address\n";
191                 message += "packet_id: 00\n";
192                 message += "device_address_long: " + zigbeeAddress.getAddress() + "\n";
193                 DatagramPacket sendPacket = new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(strHostAddress), iDstPort);
194                 socket.send(sendPacket);
195         }
196
197         private void receieveWorker() {
198                 while (!(endTask.get())) {
199
200                         byte[] recBuffer = new byte[SOCKET_RECEIVE_BUFFER_SIZE];
201                         try {
202                                 DatagramPacket recPacket = new DatagramPacket(recBuffer, recBuffer.length);
203                                 socket.receive(recPacket);
204
205                                 // Convert the UDP data into a string format
206                                 String dataString = new String(recPacket.getData());
207
208                                 // split the data by line so we can start procesisng
209                                 String[] lines = dataString.split("\n");
210
211                                 Map<String, String> packetData = new HashMap<String, String>();
212                                 for (String line : lines) {
213
214                                         // trim the line
215                                         String trimmedLine = line.trim();
216                                         // make sure this is a valid data line and not just blank
217                                         if (trimmedLine.length() == 0) {
218                                                 continue;
219                                         }
220
221                                         // Split the data into parts
222                                         String[] parts = trimmedLine.split(":");
223                                         parts[0] = parts[0].trim();
224                                         parts[1] = parts[1].trim();
225                                         packetData.put(parts[0], parts[1]);
226                                 }
227
228                                 if (packetData.get("type").equals("send_address_response")) {
229                                         didSuccesfullySendAddress.set(true);
230
231                                 } else {
232                                         IoTZigbeeMessage callbackMessage = null;
233
234                                         if (packetData.get("type").equals("zcl_read_attributes_response")) {
235                                                 int packetId = Integer.parseInt(packetData.get("packet_id"), 16);
236                                                 int clusterId = Integer.parseInt(packetData.get("cluster_id"), 16);
237                                                 int profileId = Integer.parseInt(packetData.get("profile_id"), 16);
238
239                                                 List<IoTZigbeeMessageZclReadAttributesResponse.Attribute> attrList = new ArrayList<IoTZigbeeMessageZclReadAttributesResponse.Attribute>();
240
241                                                 String[] attributes = packetData.get("attributes").split(";");
242                                                 for (String attr : attributes) {
243                                                         attr = attr.trim();
244                                                         String[] parts = attr.split(",");
245
246                                                         if (parts.length == 2) {
247                                                                 parts[0] = parts[0].trim();
248                                                                 parts[1] = parts[1].trim();
249
250                                                                 IoTZigbeeMessageZclReadAttributesResponse.Attribute at = new IoTZigbeeMessageZclReadAttributesResponse.Attribute(Integer.parseInt(parts[0], 16), 0, false, null);
251                                                                 attrList.add(at);
252                                                         } else {
253                                                                 parts[0] = parts[0].trim();
254                                                                 parts[1] = parts[1].trim();
255                                                                 parts[2] = parts[2].trim();
256                                                                 parts[3] = parts[3].trim();
257                                                                 IoTZigbeeMessageZclReadAttributesResponse.Attribute at = new IoTZigbeeMessageZclReadAttributesResponse.Attribute(Integer.parseInt(parts[0], 16), Integer.parseInt(parts[1], 16), true, hexStringToByteArray(parts[3]));
258                                                                 attrList.add(at);
259                                                         }
260                                                 }
261
262                                                 callbackMessage = new IoTZigbeeMessageZclReadAttributesResponse(packetId, clusterId, profileId, attrList);
263
264                                         } else if (packetData.get("type").equals("zcl_configure_reporting_response")) {
265                                                 int packetId = Integer.parseInt(packetData.get("packet_id"), 16);
266                                                 int clusterId = Integer.parseInt(packetData.get("cluster_id"), 16);
267                                                 int profileId = Integer.parseInt(packetData.get("profile_id"), 16);
268
269                                                 if (packetData.get("attributes").equals("all_success")) {
270                                                         callbackMessage = new IoTZigbeeMessageZclConfigureReportingResponse(packetId, clusterId, profileId, true, null);
271                                                 } else {
272                                                         List<IoTZigbeeMessageZclConfigureReportingResponse.Attribute> attrList = new ArrayList<IoTZigbeeMessageZclConfigureReportingResponse.Attribute>();
273
274                                                         String[] attributes = packetData.get("attributes").split(";");
275                                                         for (String attr : attributes) {
276                                                                 attr = attr.trim();
277                                                                 String[] parts = attr.split(",");
278                                                                 parts[0] = parts[0].trim();
279                                                                 parts[1] = parts[1].trim();
280                                                                 parts[2] = parts[2].trim();
281                                                                 IoTZigbeeMessageZclConfigureReportingResponse.Attribute at = new IoTZigbeeMessageZclConfigureReportingResponse.Attribute(Integer.parseInt(parts[0], 16), parts[1].equals("success"), parts[2].equals("reported"));
282                                                                 attrList.add(at);
283                                                         }
284                                                         callbackMessage = new IoTZigbeeMessageZclConfigureReportingResponse(packetId, clusterId, profileId, false, attrList);
285                                                 }
286
287                                         } else if (packetData.get("type").equals("zcl_report_attributes")) {
288                                                 int packetId = Integer.parseInt(packetData.get("packet_id"), 16);
289                                                 int clusterId = Integer.parseInt(packetData.get("cluster_id"), 16);
290                                                 int profileId = Integer.parseInt(packetData.get("profile_id"), 16);
291
292                                                 List<IoTZigbeeMessageZclReportAttributes.Attribute> attrList = new ArrayList<IoTZigbeeMessageZclReportAttributes.Attribute>();
293
294                                                 String[] attributes = packetData.get("attributes").split(";");
295                                                 for (String attr : attributes) {
296                                                         attr = attr.trim();
297                                                         String[] parts = attr.split(",");
298
299                                                         parts[0] = parts[0].trim();
300                                                         parts[1] = parts[1].trim();
301                                                         parts[2] = parts[2].trim();
302                                                         IoTZigbeeMessageZclReportAttributes.Attribute at = new IoTZigbeeMessageZclReportAttributes.Attribute(Integer.parseInt(parts[0], 16), Integer.parseInt(parts[1], 16), hexStringToByteArray(parts[2]));
303                                                         attrList.add(at);
304                                                 }
305
306                                                 callbackMessage = new IoTZigbeeMessageZclReportAttributes(packetId, clusterId, profileId, attrList);
307
308                                         } else if (packetData.get("type").equals("zcl_read_attributes")) {
309                                                 int packetId = Integer.parseInt(packetData.get("packet_id"), 16);
310                                                 boolean success = packetData.get("response").equals("success");
311
312                                                 if (success) {
313                                                         callbackMessage = new IoTZigbeeMessageZclReadAttributes(packetId, success, "");
314                                                 } else {
315                                                         callbackMessage = new IoTZigbeeMessageZclReadAttributes(packetId, success, packetData.get("reason"));
316                                                 }
317
318                                         } else if (packetData.get("type").equals("zcl_configure_reporting")) {
319                                                 int packetId = Integer.parseInt(packetData.get("packet_id"), 16);
320                                                 boolean success = packetData.get("response").equals("success");
321
322                                                 if (success) {
323                                                         callbackMessage = new IoTZigbeeMessageZclConfigureReporting(packetId, success, "");
324                                                 } else {
325                                                         callbackMessage = new IoTZigbeeMessageZclConfigureReporting(packetId, success, packetData.get("reason"));
326                                                 }
327
328                                         } else if (packetData.get("type").equals("zdo_bind_request")) {
329                                                 int packetId = Integer.parseInt(packetData.get("packet_id"), 16);
330                                                 boolean success = packetData.get("response").equals("success");
331
332                                                 if (success) {
333                                                         callbackMessage = new IoTZigbeeMessageZdoBindResponse(packetId, success, "");
334                                                 } else {
335                                                         callbackMessage = new IoTZigbeeMessageZdoBindResponse(packetId, success, packetData.get("reason"));
336                                                 }
337                                         }
338
339                                         else if (packetData.get("type").equals("zdo_unbind_request")) {
340                                                 int packetId = Integer.parseInt(packetData.get("packet_id"), 16);
341                                                 boolean success = packetData.get("response").equals("success");
342
343                                                 if (success) {
344                                                         callbackMessage = new IoTZigbeeMessageZdoUnBindResponse(packetId, success, "");
345                                                 } else {
346                                                         callbackMessage = new IoTZigbeeMessageZdoUnBindResponse(packetId, success, packetData.get("reason"));
347                                                 }
348                                         }
349
350                                         if (callbackMessage != null) {
351                                                 for (IoTZigbeeCallback c : callbackList) {
352                                                         c.newMessageAvailable(callbackMessage);
353                                                 }
354                                         }
355                                 }
356
357
358
359                         } catch (Exception e) {
360                                 e.printStackTrace();
361                         }
362                 }
363         }
364
365         public static String changeHexEndianness(String hexData) {
366
367                 List<String> pairedValues = new ArrayList<String>();
368                 for (int i = 0; i < hexData.length(); i += 2) {
369                         String part = hexData.substring(i, Math.min(i + 2, hexData.length()));
370                         pairedValues.add(part);
371                 }
372
373                 String retString  = "";
374                 for (int i = (pairedValues.size() - 1); i >= 0; i--) {
375                         retString += pairedValues.get(i);
376                 }
377                 return retString;
378         }
379
380         // taken from: http://stackoverflow.com/questions/140131/convert-a-string-representation-of-a-hex-dump-to-a-byte-array-using-java
381         public static byte[] hexStringToByteArray(String s) {
382                 int len = s.length();
383                 byte[] data = new byte[len / 2];
384                 for (int i = 0; i < len; i += 2) {
385                         data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
386                                               + Character.digit(s.charAt(i + 1), 16));
387                 }
388                 return data;
389         }
390
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407