1 package iotcode.BlossomSprinkler;
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;
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;
18 import java.util.HashSet;
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;
29 //import iotchecker.qual.*;
31 /** Class BlossomSprinkler for the Blossom Sprinkler.
33 * @author Ali Younis <ayounis @ uci.edu>
37 public class BlossomSprinkler implements Sprinkler {
39 /*******************************************************************************************************************************************
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;
48 @config IoTSet<IoTDeviceAddress> blossomSprAddressSet;
49 @config IoTSet<IoTDeviceAddress> localAddressSet;
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);
61 /*******************************************************************************************************************************************
63 *******************************************************************************************************************************************/
64 private Thread workerThread = null;
65 private Thread httpMonitorThread = null;
67 public BlossomSprinkler(String _channelId, IoTSet<IoTDeviceAddress> _blossomSprAddressSet, IoTSet<IoTDeviceAddress> _localAddressSet) {
69 blossomSprAddressSet = _blossomSprAddressSet;
70 localAddressSet = _localAddressSet;
73 public BlossomSprinkler(String _channelId) {
74 channelId = _channelId;
78 if (didInit.compareAndSet(false, true) == false) {
79 return; // already init
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());
88 itr = localAddressSet.iterator();
89 localAddress = (IoTDeviceAddress)itr.next();
90 System.out.println("Local address: " + localAddress.getAddress() + ":" + localAddress.getSourcePortNumber() + ":" +
91 localAddress.getDestinationPortNumber());
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();
98 zTmp.onOffState = false;
100 zoneStates.add(zTmp);
103 // Launch the worker function in a separate thread.
104 workerThread = new Thread(new Runnable() {
109 workerThread.start();
112 // Launch the http monitor function in a separate thread.
113 httpMonitorThread = new Thread(new Runnable() {
118 httpMonitorThread.start();
121 public void setZone(int _zone, boolean _onOff, int _onDurationSeconds) {
123 zoneStateMutex.acquire();
124 for (ZoneState z : zoneStates) {
126 // We replaced methods with fields
127 //z.zoneNumber, z.onOffState z.duration
128 if (z.zoneNumber == _zone) {
130 // turn on or off the valve
131 if (z.onOffState != _onOff) {
132 z.onOffState = _onOff;
141 // update the duration if needed
142 if (z.duration != _onDurationSeconds) {
143 z.duration = _onDurationSeconds;
146 // we found our sprinkler
151 } catch (Exception e) {
155 // never forget to unlock
156 zoneStateMutex.release();
159 public List<ZoneState> getZoneStates() {
161 // make a copy so that they cannot mess with our list
162 List<ZoneState> retList = new ArrayList<ZoneState>();
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;
173 } catch (Exception e) {
177 // Never forget to release!
178 zoneStateMutex.release();
183 public int getNumberOfZones() {
184 return NUMBER_OF_ZONES;
187 public boolean doesHaveZoneTimers() {
191 public void finalize() {
197 /*******************************************************************************************************************************************
201 *******************************************************************************************************************************************/
203 private void workerMethod() {
204 System.out.println("Get into worker!");
205 while (didEnd.get() == false) {
207 zoneStateMutex.acquire();
208 for (ZoneState z : zoneStates) {
210 // if on and time has expired then turn off
211 if (z.duration == 0) {
213 // turn off and reset the zone to the off state parameters
214 closeValue(z.zoneNumber);
215 z.onOffState = false;
217 } else if (z.duration > 0) {
219 // decrement the time
220 z.duration = z.duration - 1;
224 } catch (Exception e) {
227 zoneStateMutex.release();
233 } catch (Exception e) {
240 private void httpMonitorMethod() {
244 // setup server socket
245 IoTServerSocket serverSock = new IoTServerSocket(localAddress);
246 serverSock.setReuseAddress(true);
249 while (didEnd.get() == false) {
251 // wait for someone to connect
252 IoTTCP recSock = serverSock.accept();
253 recSock.setReuseAddress(true);
254 System.out.println("got new connection");
256 // open in and out streams
257 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(recSock.getInputStream()));
258 PrintWriter tcpOut = new PrintWriter(recSock.getOutputStream());
260 System.out.println("Waiting For Data");
261 // wait for data to be ready
262 while (!tcpIn.ready()) {
265 // wait a bit longer to get the whole packet
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();
275 System.out.println("---------------------------------------------------------------------");
276 System.out.println("---------------------------------------------------------------------");
277 for (String s : sList) {
278 System.out.println(s);
281 // get first line and check that it is a GET request
282 String line = sList.get(0);
283 if (line.startsWith("GET")) {
285 if (!line.contains("firmware-check")) {
286 // this is an important request to take care of
288 // get the date formatters
289 DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd");
290 DateFormat df2 = new SimpleDateFormat("HH:mm:ss");
293 Date today = Calendar.getInstance().getTime();
294 String reportDate = df1.format(today);
296 reportDate += df2.format(today);
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}";
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";
319 tcpOut.print(response);
322 // System.out.println(response);
325 // not a request we want to take care of
328 String response = "HTTP/1.1 404 Not Found\r\n\r\n";
329 tcpOut.print(response);
334 // close the connection
340 } catch (Exception e) {
345 private void openValue(int _valveNum) {
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";
355 IoTTCP connection = new IoTTCP(deviceAddress);
356 connection.setReuseAddress(true);
358 // Get in and out communication
359 PrintWriter tcpOut = new PrintWriter(connection.getOutputStream(), true);
360 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(connection.getInputStream()));
362 tcpOut.print(postMessage);
364 System.out.println("Sent POST message: " + postMessage);
367 while (!tcpIn.ready()) {
370 // Wait a bit longer for data
374 while (tcpIn.ready()) {
375 String answer = tcpIn.readLine();
376 System.out.println(answer);
380 } catch (Exception e) {
385 private void closeValue(int _valveNum) {
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";
396 IoTTCP connection = new IoTTCP(deviceAddress);
397 connection.setReuseAddress(true);
399 // Get in and out communication
400 PrintWriter tcpOut = new PrintWriter(connection.getOutputStream(), true);
401 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(connection.getInputStream()));
403 tcpOut.print(postMessage);
407 while (!tcpIn.ready()) {
410 // Wait a bit longer for data
414 while (tcpIn.ready()) {
415 String answer = tcpIn.readLine();
416 System.out.println(answer);
420 } catch (Exception e) {
425 private void endDriver() {
431 httpMonitorThread.join();
432 } catch (Exception e) {
437 /* TODO: Uncomment this part to do sprinkler test
438 public static void main(String[] args) throws Exception {
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);
454 System.out.println("Finished init()");
455 bs.setZone(0, true, 60);
456 System.out.println("Finished setZone!");