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