209b2fe581ea2ee864714ef51f5d81790da6b76f
[iot2.git] / benchmarks / drivers / Java / BlossomSprinkler / BlossomSprinkler.java
1 package iotcode.BlossomSprinkler;
2
3 // Java Standard Packages
4 import java.util.concurrent.Semaphore;
5 import java.io.InputStreamReader;
6 import java.io.BufferedReader;
7 import java.io.PrintWriter;
8 import java.io.ByteArrayInputStream;
9 import java.util.List;
10 import java.util.ArrayList;
11 import java.util.Iterator;
12 import java.util.Date;
13 import java.util.Calendar;
14 import java.text.DateFormat;
15 import java.text.SimpleDateFormat;
16 import java.util.concurrent.atomic.AtomicBoolean;
17 import java.util.Set;
18 import java.util.HashSet;
19
20 // IoT Packages
21 import iotruntime.IoTTCP;
22 import iotruntime.IoTServerSocket;
23 import iotruntime.slave.IoTDeviceAddress;
24 import iotruntime.slave.IoTSet;
25 import iotcode.annotation.*;
26 import iotcode.interfaces.ZoneState;
27 import iotcode.interfaces.Sprinkler;
28
29 //import iotchecker.qual.*;
30
31 /** Class BlossomSprinkler for the Blossom Sprinkler.
32  *
33  * @author      Ali Younis <ayounis @ uci.edu>
34  * @version     1.0
35  * @since       2016-05-2
36  */
37 public class BlossomSprinkler implements Sprinkler {
38
39     /*******************************************************************************************************************************************
40     **  Constants
41     *******************************************************************************************************************************************/
42     //public static final int NUMBER_OF_ZONES = 12;
43         public static final int NUMBER_OF_ZONES = 1;
44
45     @config IoTSet<IoTDeviceAddress> blossomSprAddressSet;
46     @config IoTSet<IoTDeviceAddress> localAddressSet;
47
48     private IoTDeviceAddress deviceAddress = null;
49     private IoTDeviceAddress localAddress = null;
50     private String channelId = "";
51     private Semaphore zoneStateMutex = new Semaphore(1);
52     private List<ZoneState> zoneStates = new ArrayList<ZoneState>();
53     private AtomicBoolean didEnd = new AtomicBoolean();
54     private boolean didClose = false;
55     private AtomicBoolean didInit = new AtomicBoolean(false);
56
57
58     /*******************************************************************************************************************************************
59     **  Threads
60     *******************************************************************************************************************************************/
61     private Thread workerThread = null;
62     private Thread httpMonitorThread = null;
63
64     public BlossomSprinkler(String _channelId, IoTSet<IoTDeviceAddress> _blossomSprAddressSet, IoTSet<IoTDeviceAddress> _localAddressSet) {
65         this(_channelId);
66                 blossomSprAddressSet = _blossomSprAddressSet;
67                 localAddressSet = _localAddressSet;             
68     }
69
70     public BlossomSprinkler(String _channelId) {
71         channelId = _channelId;
72     }
73
74     public void init() {
75         if (didInit.compareAndSet(false, true) == false) {
76             return; // already init
77         }
78
79         // Get the address from the IoTSet
80         Iterator itr = blossomSprAddressSet.iterator();
81         deviceAddress = (IoTDeviceAddress)itr.next();
82         System.out.println("Device address: " + deviceAddress.getAddress() + ":" + deviceAddress.getSourcePortNumber() + ":" +
83                            deviceAddress.getDestinationPortNumber());
84
85         itr = localAddressSet.iterator();
86         localAddress = (IoTDeviceAddress)itr.next();
87         System.out.println("Local address: " + localAddress.getAddress() + ":" + localAddress.getSourcePortNumber() + ":" +
88                            localAddress.getDestinationPortNumber());
89
90
91         // create the correct number of zones for this controller
92         for (int i = 0; i < NUMBER_OF_ZONES; i++) {
93                         ZoneState zTmp = new ZoneState();
94                         zTmp.zoneNumber = i;
95                         zTmp.onOffState = false;
96                         zTmp.duration = 0;
97             zoneStates.add(zTmp);
98         }
99
100         // Launch the worker function in a separate thread.
101         workerThread = new Thread(new Runnable() {
102             public void run() {
103                 workerMethod();
104             }
105         });
106         workerThread.start();
107
108
109         // Launch the http monitor function in a separate thread.
110         httpMonitorThread = new Thread(new Runnable() {
111             public void run() {
112                 httpMonitorMethod();
113             }
114         });
115         httpMonitorThread.start();
116     }
117
118     public void setZone(int _zone, boolean _onOff, int _onDurationSeconds) {
119         try {
120             zoneStateMutex.acquire();
121             for (ZoneState z : zoneStates) {
122                 {
123                     // We replaced methods with fields
124                                 //z.zoneNumber, z.onOffState z.duration
125                     if (z.zoneNumber == _zone) {
126
127                         // turn on or off the valve
128                         if (z.onOffState != _onOff) {
129                             z.onOffState = _onOff;
130
131                             if (_onOff) {
132                                 openValue(_zone);
133                             } else {
134                                 closeValue(_zone);
135                             }
136                         }
137
138                         // update the duration if needed
139                         if (z.duration != _onDurationSeconds) {
140                             z.duration = _onDurationSeconds;
141                         }
142
143                         // we found our sprinkler
144                         break;
145                     }
146                 }
147             }
148         } catch (Exception e) {
149             e.printStackTrace();
150         }
151
152         // never forget to unlock
153         zoneStateMutex.release();
154     }
155
156     public List<ZoneState> getZoneStates() {
157
158         // make a copy so that they cannot mess with our list
159         List<ZoneState> retList = new ArrayList<ZoneState>();
160
161         try {
162             zoneStateMutex.acquire();
163             for (ZoneState z : zoneStates) {
164                 ZoneState n = new ZoneState();
165                 n.zoneNumber = z.zoneNumber;
166                 n.onOffState = z.onOffState;
167                 n.duration = z.duration;
168                 retList.add(n);
169             }
170         } catch (Exception e) {
171             e.printStackTrace();
172         }
173
174         // Never forget to release!
175         zoneStateMutex.release();
176         return retList;
177     }
178
179
180     public int getNumberOfZones() {
181         return NUMBER_OF_ZONES;
182     }
183
184     public boolean doesHaveZoneTimers() {
185         return true;
186     }
187
188     public void finalize() {
189         if (!didClose) {
190             endDriver();
191         }
192     }
193
194     /*******************************************************************************************************************************************
195     **
196     **  Helper Methods
197     **
198     *******************************************************************************************************************************************/
199
200     private void workerMethod() {
201                 System.out.println("Get into worker!");
202                 while (didEnd.get() == false) {
203                         //System.out.println("While not end");
204             try {
205                 zoneStateMutex.acquire();
206                 for (ZoneState z : zoneStates) {
207                     if (z.onOffState) {
208                         // if on and time has expired then turn off
209                         if (z.duration == 0) {
210
211                             // turn off and reset the zone to the off state parameters
212                             closeValue(z.zoneNumber);
213                             z.onOffState = false;
214                             z.duration = 0;
215                         } else if (z.duration > 0) {
216
217                             // decrement the time
218                             z.duration = z.duration - 1;
219                         }
220                     }
221                 }
222             } catch (Exception e) {
223                 e.printStackTrace();
224             }
225             zoneStateMutex.release();
226
227
228
229             try {
230                 Thread.sleep(1000);
231             } catch (Exception e) {
232                 e.printStackTrace();
233             }
234         }
235     }
236
237
238     private void httpMonitorMethod() {
239
240         try {
241
242             // setup server socket
243             IoTServerSocket serverSock = new IoTServerSocket(localAddress);
244             serverSock.setReuseAddress(true);
245
246
247             while (didEnd.get() == false) {
248
249                 // wait for someone to connect
250                 IoTTCP recSock = serverSock.accept();
251                 recSock.setReuseAddress(true);
252                 System.out.println("got new connection");
253
254                 // open in and out streams
255                 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(recSock.getInputStream()));
256                 PrintWriter tcpOut = new PrintWriter(recSock.getOutputStream());
257
258                 System.out.println("Waiting For Data");
259                 // wait for data to be ready
260                 while (!tcpIn.ready()) {
261                 }
262
263                 // wait a bit longer to get the whole packet
264                 Thread.sleep(10);
265
266                 // put all the lines read into a list so we can read them 1 at a time
267                 List<String> sList = new ArrayList<String>();
268                 while (tcpIn.ready()) {
269                     String s = tcpIn.readLine();
270                     sList.add(s);
271                 }
272
273                 System.out.println("---------------------------------------------------------------------");
274                 System.out.println("---------------------------------------------------------------------");
275                 for (String s : sList) {
276                     System.out.println(s);
277                 }
278
279                 // get first line and check that it is a GET request
280                 String line = sList.get(0);
281                 if (line.startsWith("GET")) {
282
283                     if (!line.contains("firmware-check")) {
284                         // this is an important request to take care of
285
286                         // get the date formatters
287                         DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd");
288                         DateFormat df2 = new SimpleDateFormat("HH:mm:ss");
289
290                         // make the date
291                         Date today = Calendar.getInstance().getTime();
292                         String reportDate = df1.format(today);
293                         reportDate += "T";
294                         reportDate += df2.format(today);
295
296                         String body = "";
297
298                         // parse the packet and build the body
299                         if (line.contains("/device/v1/server/")) {
300                             body = "{\"stats_freq\": 3600, \"pn_keepalive\": 1, \"uap_debug\": 1, \"wave_boost\": 1, \"ota_freq\": 3600, \"current_time\":\"" + reportDate + "\", \"build\": 1042, \"opn_trip\": 40}";
301                         } else if (line.contains("api") && line.contains("device") && line.contains(channelId)) {
302                             body = "{\"channel\": \"channel_" + channelId + "\", \"current_time\": \"" + reportDate + "\", \"tz_offset\": -8.0, \"tz_seconds\": -28800, \"sch_load_time\": 24900, \"fetch_lead\": 3600}";
303                         }
304
305                         // make the header and send
306                         String response = "HTTP/1.1 200 OK\r\n";
307                         response += "Allow: GET, HEAD, OPTIONS\r\n";
308                         response += "Content-Type: application/json\r\n";
309                         response += "Date: Sun, 08 May 2016 04:20:35 GMT\r\n";
310                         response += "Server: nginx/1.4.6 (Ubuntu)\r\n";
311                         response += "Vary: Accept, Cookie\r\n";
312                         response += "Content-Length: " + body.length() + "\r\n";
313                         // response += "Connection: keep-alive\r\n";
314                         response += "Connection: Close\r\n";
315                         response += "\r\n";
316                         response += body;
317                         tcpOut.print(response);
318                         tcpOut.flush();
319
320                         // System.out.println(response);
321
322                     } else {
323                         // not a request we want to take care of
324
325                         // send 404 error
326                         String response = "HTTP/1.1 404 Not Found\r\n\r\n";
327                         tcpOut.print(response);
328                         tcpOut.flush();
329                     }
330                 }
331
332                 // close the connection
333                 recSock.close();
334             }
335
336             // close the socket
337             serverSock.close();
338         } catch (Exception e) {
339             e.printStackTrace();
340         }
341     }
342
343     private void openValue(int _valveNum) {
344
345         try {
346             String body = "{\"valve\":" + Integer.toString(_valveNum) + ",\"inverter\":1}";
347             String postMessage = "POST /bloom/valve HTTP/1.1\r\n";
348             postMessage += "Content-Type: application/json; charset=utf-8\r\n";
349             postMessage += "Content-Length: " + Integer.toString(body.length()) + "\r\n";
350             postMessage += "\r\n";
351             postMessage += body;
352
353             IoTTCP connection = new IoTTCP(deviceAddress);
354             connection.setReuseAddress(true);
355
356             // Get in and out communication
357             PrintWriter tcpOut = new PrintWriter(connection.getOutputStream(), true);
358             BufferedReader tcpIn = new BufferedReader(new InputStreamReader(connection.getInputStream()));
359
360             tcpOut.print(postMessage);
361             tcpOut.flush();
362                         System.out.println("Sent POST message: " + postMessage);
363
364             // wait for data
365             while (!tcpIn.ready()) {
366             }
367
368             // Wait a bit longer for data
369             Thread.sleep(10);
370
371             // get the response
372             while (tcpIn.ready()) {
373                 String answer = tcpIn.readLine();
374                 System.out.println(answer);
375             }
376
377             connection.close();
378         } catch (Exception e) {
379             e.printStackTrace();
380         }
381     }
382
383     private void closeValue(int _valveNum) {
384
385         try {
386             String body = "{\"valve\":" + Integer.toString(_valveNum) + ",\"inverter\":0}";
387             String postMessage = "POST /bloom/valve HTTP/1.1\r\n";
388             postMessage += "Content-Type: application/json; charset=utf-8\r\n";
389             postMessage += "Content-Length: " + Integer.toString(body.length()) + "\r\n";
390             postMessage += "\r\n";
391             postMessage += body;
392
393
394             IoTTCP connection = new IoTTCP(deviceAddress);
395             connection.setReuseAddress(true);
396
397             // Get in and out communication
398             PrintWriter tcpOut = new PrintWriter(connection.getOutputStream(), true);
399             BufferedReader tcpIn = new BufferedReader(new InputStreamReader(connection.getInputStream()));
400
401             tcpOut.print(postMessage);
402             tcpOut.flush();
403
404             // wait for data
405             while (!tcpIn.ready()) {
406             }
407
408             // Wait a bit longer for data
409             Thread.sleep(10);
410
411             // get the response
412             while (tcpIn.ready()) {
413                 String answer = tcpIn.readLine();
414                 System.out.println(answer);
415             }
416
417             connection.close();
418         } catch (Exception e) {
419             e.printStackTrace();
420         }
421     }
422
423     private void endDriver() {
424         didClose = true;
425         didEnd.set(true);
426
427         try {
428             workerThread.join();
429             httpMonitorThread.join();
430         } catch (Exception e) {
431             e.printStackTrace();
432         }
433     }
434         
435         /* TODO: Uncomment this part to do sprinkler test
436         public static void main(String[] args) throws Exception {
437
438                 System.out.println("Executing main function!");
439                 //IoTDeviceAddress iotDevAdd1 = new IoTDeviceAddress("192.168.0.129", 10009, 80, false, false);
440                 //IoTDeviceAddress iotDevAdd2 = new IoTDeviceAddress("192.168.0.84", 10010, 80, false, false);
441                 IoTDeviceAddress iotDevAdd1 = new IoTDeviceAddress(args[0], 10009, 80, false, false);
442                 IoTDeviceAddress iotDevAdd2 = new IoTDeviceAddress(args[1], 10010, 80, false, false);           
443                 Set<IoTDeviceAddress> setBlossom = new HashSet<IoTDeviceAddress>();
444                 Set<IoTDeviceAddress> setLocal = new HashSet<IoTDeviceAddress>();
445                 setBlossom.add(iotDevAdd1);
446                 setLocal.add(iotDevAdd2);
447                 IoTSet<IoTDeviceAddress> iotsetBlossom = new IoTSet<IoTDeviceAddress>(setBlossom);
448                 IoTSet<IoTDeviceAddress> iotsetLocal = new IoTSet<IoTDeviceAddress>(setLocal);
449                 String channelID = "1bd60b0c-2a99-4c83-8a7d-f97bd3f77a51";
450                 BlossomSprinkler bs = new BlossomSprinkler(channelID, iotsetBlossom, iotsetLocal);
451                 bs.init();
452                 System.out.println("Finished init()");
453                 bs.setZone(0, true, 60);
454                 System.out.println("Finished setZone!");
455
456         }*/
457 }
458
459