1 package iotcode.EspSprinkler;
3 // Standard Java Packages
6 import java.util.concurrent.Semaphore;
7 import java.util.concurrent.atomic.AtomicBoolean;
8 import java.security.InvalidParameterException;
10 import java.util.Iterator;
11 import java.nio.charset.StandardCharsets;
12 import java.util.List;
13 import java.util.ArrayList;
16 import iotruntime.IoTUDP;
17 import iotruntime.slave.IoTDeviceAddress;
18 import iotruntime.slave.IoTSet;
19 import iotcode.interfaces.ZoneState;
20 import iotcode.interfaces.Sprinkler;
21 import iotcode.annotation.*;
23 //import iotchecker.qual.*;
25 /** Class EspSprinkler for the ESP8266 plrg Sprinkler.
27 * @author Ali Younis <ayounis @ uci.edu>
32 public class EspSprinkler implements Sprinkler {
34 /*******************************************************************************************************************************************
38 *******************************************************************************************************************************************/
40 private IoTUDP communicationSockect;
41 private Semaphore socketMutex = new Semaphore(1);
42 private AtomicBoolean sendSocketFlag = new AtomicBoolean(false);
43 private AtomicBoolean doingRead = new AtomicBoolean(false);
44 private AtomicBoolean didInit = new AtomicBoolean(false);
45 private Semaphore settingZone = new Semaphore(1);
47 /*******************************************************************************************************************************************
51 *******************************************************************************************************************************************/
53 // Main worker thread will do the receive loop
54 Thread workerThread = null;
57 /*******************************************************************************************************************************************
59 ** IoT Sets and Relations
61 *******************************************************************************************************************************************/
63 // IoTSet of Device Addresses.
64 // Will be filled with only 1 address.
65 @config private IoTSet<IoTDeviceAddress> spr_Addresses;
67 /*public EspSprinkler(IoTUDP _udp) {
68 communicationSockect = _udp;
71 public EspSprinkler() {
72 communicationSockect = null;
76 /*******************************************************************************************************************************************
80 *******************************************************************************************************************************************/
82 /** Method to set the state of a specified zone. Interface implementation.
84 * @param _zone [int] : zone number to set.
85 * @param _onOff [boolean] : the state to set the zone to, on or off.
86 * @param _onDurationSeconds [int]: the duration to set the state on to, if -1 then infinite.
88 * @return [void] None.
90 public void setZone(int _zone, boolean _onOff, int _onDurationSeconds) {
93 settingZone.acquire();
94 String sendString = "SET,";
95 sendString += Integer.toString(_zone);
104 sendString += Integer.toString(_onDurationSeconds);
106 sendPacket(sendString.getBytes(StandardCharsets.UTF_8));
107 } catch (Exception e) {
110 settingZone.release();
114 /** Method to get the current state of all the zones. Interface implementation.
118 * @return [List<ZoneState>] list of the states for the zones.
120 public List<ZoneState> getZoneStates() {
122 sendGetInformation();
126 } catch (Exception e) {
132 // Communication resource is busy so try again later
133 if (sendSocketFlag.get()) {
138 socketMutex.acquire();
139 } catch (InterruptedException e) {
144 dat = communicationSockect.recieveData(1024);
145 } catch (java.net.SocketTimeoutException e) {
148 } catch (IOException e) {
149 // Problem but might be able to recover??
154 // Never forget to release!
155 socketMutex.release();
159 doingRead.set(false);
160 return parseGetResponse(dat);
162 // return new ArrayList<ZoneState>();
166 } catch (Exception e) {
172 sendGetInformation();
180 /** Method to get the number of zones this sprinkler can control. Interface implementation.
184 * @return [int] number of zones that can be controlled.
186 public int getNumberOfZones() {
191 /** Method to get whether or not this sprinkler can control durations. Interface implementation.
195 * @return [boolean] boolean if this sprinkler can do durations.
197 public boolean doesHaveZoneTimers() {
202 /** Method to initialize the sprinkler. Interface implementation.
206 * @return [void] None.
210 if (didInit.compareAndSet(false, true) == false) {
211 return; // already init
215 Iterator itr = spr_Addresses.iterator();
216 IoTDeviceAddress deviceAddress = (IoTDeviceAddress)itr.next();
217 System.out.println("Address: " + deviceAddress.getCompleteAddress());
219 // Create the communication channel
220 communicationSockect = new IoTUDP(deviceAddress);
221 } catch (Exception e) {
226 // Launch the worker function in a separate thread.
227 workerThread = new Thread(new Runnable() {
232 workerThread.start();
236 /*******************************************************************************************************************************************
240 *******************************************************************************************************************************************/
242 /** Method to send the get information udp packet to get the latest sprinkler state.
246 * @return [void] None.
248 public void sendGetInformation() {
249 String sendString = "GET";
250 sendPacket(sendString.getBytes(StandardCharsets.UTF_8));
254 /** Method to parse the UDP packet data into a meaningful representation.
256 * @param _packetData [byte[]] raw packet data from the udp packet.
258 * @return [List<ZoneState>] Parsed zone data.
260 private List<ZoneState> parseGetResponse(byte[] _packetData) {
261 String recString = new String(_packetData);
262 List<ZoneState> retStates = new ArrayList<ZoneState>();
264 String[] lines = recString.split("\n");
266 for (int i = 0; i < 9; i++) {
267 String[] splitSting = lines[i].split(",");
269 int zoneNum = Integer.parseInt(splitSting[0].trim());
270 int onOffInt = Integer.parseInt(splitSting[1].trim());
271 boolean onOff = onOffInt != 0;
272 int duration = Integer.parseInt(splitSting[2].trim());
275 //ZoneState zTmp = new ZoneState(zoneNum, onOff, duration);
276 ZoneState zTmp = new ZoneState();
277 zTmp.zoneNumber = zoneNum;
278 zTmp.onOffState = onOff;
279 zTmp.duration = duration;
287 /** Method to parse the UDP packet data into a meaningful representation.
289 * @param _packetData [byte[]] bytes to send over the udp channel.
291 * @return [void] None.
293 private void sendPacket(byte[] _packetData) {
294 // System.out.println("About to send");
295 sendSocketFlag.set(true);
298 socketMutex.acquire();
299 } catch (InterruptedException e) {
300 System.out.println("mutex Error");
304 communicationSockect.sendData(_packetData);
306 } catch (IOException e) {
307 System.out.println("Socket Send Error");
310 sendSocketFlag.set(false);
311 socketMutex.release();
315 /** Method to constantly flush the udp socket expect when we wish to read the incoming data.
319 * @return [void] None.
321 private void workerFunction() {
323 // Need timeout on receives since we are not sure if a packet will be available
324 // for processing so don't block waiting
325 communicationSockect.setSoTimeout(50);
326 } catch (IOException e) {
333 // Communication resource is busy so try again later
334 if (sendSocketFlag.get()) {
338 if (doingRead.get()) {
343 socketMutex.acquire();
344 } catch (InterruptedException e) {
349 dat = communicationSockect.recieveData(1024);
350 } catch (java.net.SocketTimeoutException e) {
353 } catch (IOException e) {
354 // Problem but might be able to recover??
359 // Never forget to release!
360 socketMutex.release();
362 // Wait a bit as to not tie up system resources
365 } catch (Exception e) {