+package iotcode.BlossomSprinkler;
+
+// Java Standard Packages
+import java.util.concurrent.Semaphore;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.PrintWriter;
+import java.io.ByteArrayInputStream;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Date;
+import java.util.Calendar;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+// import java.util.HashSet;
+// import java.util.Set;
+
+// IoT Packages
+import iotruntime.IoTTCP;
+import iotruntime.IoTServerSocket;
+import iotruntime.slave.IoTDeviceAddress;
+import iotruntime.slave.IoTSet;
+import iotcode.annotation.*;
+import iotcode.interfaces.ZoneState;
+import iotcode.interfaces.Sprinkler;
+
+//import iotchecker.qual.*;
+
+/** Class BlossomSprinkler for the Blossom Sprinkler.
+ *
+ * @author Ali Younis <ayounis @ uci.edu>
+ * @version 1.0
+ * @since 2016-05-2
+ */
+public class BlossomSprinkler implements Sprinkler {
+
+ /*******************************************************************************************************************************************
+ ** Constants
+ *******************************************************************************************************************************************/
+ public static final int NUMBER_OF_ZONES = 12;
+
+ @config IoTSet<IoTDeviceAddress> blossomSprAddressSet;
+ @config IoTSet<IoTDeviceAddress> localAddressSet;
+
+ private IoTDeviceAddress deviceAddress = null;
+ private IoTDeviceAddress localAddress = null;
+ private String channelId = "";
+ private Semaphore zoneStateMutex = new Semaphore(1);
+ private List<ZoneState> zoneStates = new ArrayList<ZoneState>();
+ private AtomicBoolean didEnd = new AtomicBoolean();
+ private boolean didClose = false;
+ private AtomicBoolean didInit = new AtomicBoolean(false);
+
+
+ /*******************************************************************************************************************************************
+ ** Threads
+ *******************************************************************************************************************************************/
+ private Thread workerThread = null;
+ private Thread httpMonitorThread = null;
+
+
+ public BlossomSprinkler(String _channelId) {
+ channelId = _channelId;
+ }
+
+ public void init() {
+ if (didInit.compareAndSet(false, true) == false) {
+ return; // already init
+ }
+
+ // Get the address from the IoTSet
+ Iterator itr = blossomSprAddressSet.iterator();
+ deviceAddress = (IoTDeviceAddress)itr.next();
+ System.out.println("Device address: " + deviceAddress.getAddress() + ":" + deviceAddress.getSourcePortNumber() + ":" +
+ deviceAddress.getDestinationPortNumber());
+
+ itr = localAddressSet.iterator();
+ localAddress = (IoTDeviceAddress)itr.next();
+ System.out.println("Local address: " + localAddress.getAddress() + ":" + localAddress.getSourcePortNumber() + ":" +
+ localAddress.getDestinationPortNumber());
+
+
+ // create the correct number of zones for this controller
+ for (int i = 0; i < NUMBER_OF_ZONES; i++) {
+ //zoneStates.add(new ZoneState(i, false, 0));
+ ZoneState zTmp = new ZoneState();
+ zTmp.zoneNumber = i;
+ zTmp.onOffState = false;
+ zTmp.duration = 0;
+ zoneStates.add(zTmp);
+ }
+
+ // Launch the worker function in a separate thread.
+ workerThread = new Thread(new Runnable() {
+ public void run() {
+ workerMethod();
+ }
+ });
+ workerThread.start();
+
+
+ // Launch the http monitor function in a separate thread.
+ httpMonitorThread = new Thread(new Runnable() {
+ public void run() {
+ httpMonitorMethod();
+ }
+ });
+ httpMonitorThread.start();
+ }
+
+ public void setZone(int _zone, boolean _onOff, int _onDurationSeconds) {
+ try {
+ zoneStateMutex.acquire();
+ for (ZoneState z : zoneStates) {
+ {
+ // We replaced methods with fields
+ //z.zoneNumber, z.onOffState z.duration
+ //if (z.getZoneNumber() == _zone) {
+ if (z.zoneNumber == _zone) {
+
+ // turn on or off the valve
+ if (z.onOffState != _onOff) {
+ z.onOffState = _onOff;
+
+ if (_onOff) {
+ openValue(_zone);
+ } else {
+ closeValue(_zone);
+ }
+ }
+
+ // update the duration if needed
+ if (z.duration != _onDurationSeconds) {
+ z.duration = _onDurationSeconds;
+ }
+
+ // we found our sprinkler
+ break;
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // never forget to unlock
+ zoneStateMutex.release();
+ }
+
+ public List<ZoneState> getZoneStates() {
+
+ // make a copy so that they cannot mess with our list
+ List<ZoneState> retList = new ArrayList<ZoneState>();
+
+ try {
+ zoneStateMutex.acquire();
+ for (ZoneState z : zoneStates) {
+ ZoneState n = new ZoneState();
+ n.zoneNumber = z.zoneNumber;
+ n.onOffState = z.onOffState;
+ n.duration = z.duration;
+ retList.add(n);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // Never forget to release!
+ zoneStateMutex.release();
+ return retList;
+ }
+
+
+ public int getNumberOfZones() {
+ return NUMBER_OF_ZONES;
+ }
+
+ public boolean doesHaveZoneTimers() {
+ return true;
+ }
+
+ public void finalize() {
+ if (!didClose) {
+ endDriver();
+ }
+ }
+
+ /*******************************************************************************************************************************************
+ **
+ ** Helper Methods
+ **
+ *******************************************************************************************************************************************/
+
+ private void workerMethod() {
+ while (didEnd.get() == false) {
+
+ try {
+ zoneStateMutex.acquire();
+ for (ZoneState z : zoneStates) {
+ if (z.onOffState) {
+
+ // if on and time has expired then turn off
+ if (z.duration == 0) {
+
+ // turn off and reset the zone to the off state parameters
+ closeValue(z.zoneNumber);
+ z.onOffState = false;
+ z.duration = 0;
+ } else if (z.duration > 0) {
+
+ // decrement the time
+ z.duration = z.duration - 1;
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ zoneStateMutex.release();
+
+
+
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ private void httpMonitorMethod() {
+
+ try {
+
+ // setup server socket
+ IoTServerSocket serverSock = new IoTServerSocket(localAddress);
+ serverSock.setReuseAddress(true);
+
+
+ while (didEnd.get() == false) {
+
+ // wait for someone to connect
+ IoTTCP recSock = serverSock.accept();
+ recSock.setReuseAddress(true);
+ System.out.println("got new connection");
+
+ // open in and out streams
+ BufferedReader tcpIn = new BufferedReader(new InputStreamReader(recSock.getInputStream()));
+ PrintWriter tcpOut = new PrintWriter(recSock.getOutputStream());
+
+
+
+
+ System.out.println("Waiting For Data");
+ // wait for data to be ready
+ while (!tcpIn.ready()) {
+ }
+
+ // wait a bit longer to get the whole packet
+ Thread.sleep(10);
+
+ // put all the lines read into a list so we can read them 1 at a time
+ List<String> sList = new ArrayList<String>();
+ while (tcpIn.ready()) {
+ String s = tcpIn.readLine();
+ sList.add(s);
+ }
+
+ // System.out.println("---------------------------------------------------------------------");
+ // System.out.println("---------------------------------------------------------------------");
+ // for (String s : sList) {
+ // System.out.println(s);
+ // }
+
+
+ // get first line and check that it is a GET request
+ String line = sList.get(0);
+ if (line.startsWith("GET")) {
+
+ if (!line.contains("firmware-check")) {
+ // this is an important request to take care of
+
+ // get the date formatters
+ DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd");
+ DateFormat df2 = new SimpleDateFormat("HH:mm:ss");
+
+ // make the date
+ Date today = Calendar.getInstance().getTime();
+ String reportDate = df1.format(today);
+ reportDate += "T";
+ reportDate += df2.format(today);
+
+ String body = "";
+
+ // parse the packet and build the body
+ if (line.contains("/device/v1/server/")) {
+ body = "{\"stats_freq\": 3600, \"pn_keepalive\": 1, \"uap_debug\": 1, \"wave_boost\": 1, \"ota_freq\": 3600, \"current_time\":\"" + reportDate + "\", \"build\": 1042, \"opn_trip\": 40}";
+ } else if (line.contains("api") && line.contains("device") && line.contains(channelId)) {
+ body = "{\"channel\": \"channel_" + channelId + "\", \"current_time\": \"" + reportDate + "\", \"tz_offset\": -8.0, \"tz_seconds\": -28800, \"sch_load_time\": 24900, \"fetch_lead\": 3600}";
+ }
+
+ // make the header and send
+ String response = "HTTP/1.1 200 OK\r\n";
+ response += "Allow: GET, HEAD, OPTIONS\r\n";
+ response += "Content-Type: application/json\r\n";
+ response += "Date: Sun, 08 May 2016 04:20:35 GMT\r\n";
+ response += "Server: nginx/1.4.6 (Ubuntu)\r\n";
+ response += "Vary: Accept, Cookie\r\n";
+ response += "Content-Length: " + body.length() + "\r\n";
+ // response += "Connection: keep-alive\r\n";
+ response += "Connection: Close\r\n";
+ response += "\r\n";
+ response += body;
+ tcpOut.print(response);
+ tcpOut.flush();
+
+ // System.out.println(response);
+
+ } else {
+ // not a request we want to take care of
+
+ // send 404 error
+ String response = "HTTP/1.1 404 Not Found\r\n\r\n";
+ tcpOut.print(response);
+ tcpOut.flush();
+ }
+ }
+
+ // close the connection
+ recSock.close();
+ }
+
+ // close the socket
+ serverSock.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void openValue(int _valveNum) {
+
+ try {
+ String body = "{\"valve\":" + Integer.toString(_valveNum) + ",\"inverter\":1}";
+ String postMessage = "POST /bloom/valve HTTP/1.1\r\n";
+ postMessage += "Content-Type: application/json; charset=utf-8\r\n";
+ postMessage += "Content-Length: " + Integer.toString(body.length()) + "\r\n";
+ postMessage += "\r\n";
+ postMessage += body;
+
+ IoTTCP connection = new IoTTCP(deviceAddress);
+ connection.setReuseAddress(true);
+
+ // Get in and out communication
+ PrintWriter tcpOut = new PrintWriter(connection.getOutputStream(), true);
+ BufferedReader tcpIn = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+
+ tcpOut.print(postMessage);
+ tcpOut.flush();
+
+ // wait for data
+ while (!tcpIn.ready()) {
+ }
+
+ // Wait a bit longer for data
+ Thread.sleep(10);
+
+ // get the response
+ while (tcpIn.ready()) {
+ String answer = tcpIn.readLine();
+ System.out.println(answer);
+ }
+
+ connection.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void closeValue(int _valveNum) {
+
+ try {
+ String body = "{\"valve\":" + Integer.toString(_valveNum) + ",\"inverter\":0}";
+ String postMessage = "POST /bloom/valve HTTP/1.1\r\n";
+ postMessage += "Content-Type: application/json; charset=utf-8\r\n";
+ postMessage += "Content-Length: " + Integer.toString(body.length()) + "\r\n";
+ postMessage += "\r\n";
+ postMessage += body;
+
+
+ IoTTCP connection = new IoTTCP(deviceAddress);
+ connection.setReuseAddress(true);
+
+ // Get in and out communication
+ PrintWriter tcpOut = new PrintWriter(connection.getOutputStream(), true);
+ BufferedReader tcpIn = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+
+ tcpOut.print(postMessage);
+ tcpOut.flush();
+
+ // wait for data
+ while (!tcpIn.ready()) {
+ }
+
+ // Wait a bit longer for data
+ Thread.sleep(10);
+
+ // get the response
+ while (tcpIn.ready()) {
+ String answer = tcpIn.readLine();
+ System.out.println(answer);
+ }
+
+ connection.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void endDriver() {
+ didClose = true;
+ didEnd.set(true);
+
+ try {
+ workerThread.join();
+ httpMonitorThread.join();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+
+