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 /** Class EspSprinkler for the ESP8266 plrg Sprinkler.
25 * @author Ali Younis <ayounis @ uci.edu>
30 public class EspSprinkler implements Sprinkler {
32 /*******************************************************************************************************************************************
36 *******************************************************************************************************************************************/
38 private IoTUDP communicationSockect;
39 private Semaphore socketMutex = new Semaphore(1);
40 private AtomicBoolean sendSocketFlag = new AtomicBoolean(false);
41 private AtomicBoolean doingRead = new AtomicBoolean(false);
42 private AtomicBoolean didInit = new AtomicBoolean(false);
43 private Semaphore settingZone = new Semaphore(1);
45 /*******************************************************************************************************************************************
49 *******************************************************************************************************************************************/
51 // Main worker thread will do the receive loop
52 Thread workerThread = null;
55 /*******************************************************************************************************************************************
57 ** IoT Sets and Relations
59 *******************************************************************************************************************************************/
61 // IoTSet of Device Addresses.
62 // Will be filled with only 1 address.
63 @config private IoTSet<IoTDeviceAddress> spr_Addresses;
65 /*public EspSprinkler(IoTUDP _udp) {
66 communicationSockect = _udp;
69 public EspSprinkler() {
70 communicationSockect = null;
74 /*******************************************************************************************************************************************
78 *******************************************************************************************************************************************/
80 /** Method to set the state of a specified zone. Interface implementation.
82 * @param _zone [int] : zone number to set.
83 * @param _onOff [boolean] : the state to set the zone to, on or off.
84 * @param _onDurationSeconds [int]: the duration to set the state on to, if -1 then infinite.
86 * @return [void] None.
88 public void setZone(int _zone, boolean _onOff, int _onDurationSeconds) {
91 settingZone.acquire();
92 String sendString = "SET,";
93 sendString += Integer.toString(_zone);
102 sendString += Integer.toString(_onDurationSeconds);
104 sendPacket(sendString.getBytes(StandardCharsets.UTF_8));
105 } catch (Exception e) {
108 settingZone.release();
112 /** Method to get the current state of all the zones. Interface implementation.
116 * @return [List<ZoneState>] list of the states for the zones.
118 public List<ZoneState> getZoneStates() {
120 sendGetInformation();
124 } catch (Exception e) {
130 // Communication resource is busy so try again later
131 if (sendSocketFlag.get()) {
136 socketMutex.acquire();
137 } catch (InterruptedException e) {
142 dat = communicationSockect.recieveData(1024);
143 } catch (java.net.SocketTimeoutException e) {
146 } catch (IOException e) {
147 // Problem but might be able to recover??
152 // Never forget to release!
153 socketMutex.release();
157 doingRead.set(false);
158 return parseGetResponse(dat);
160 // return new ArrayList<ZoneState>();
164 } catch (Exception e) {
170 sendGetInformation();
178 /** Method to get the number of zones this sprinkler can control. Interface implementation.
182 * @return [int] number of zones that can be controlled.
184 public int getNumberOfZones() {
189 /** Method to get whether or not this sprinkler can control durations. Interface implementation.
193 * @return [boolean] boolean if this sprinkler can do durations.
195 public boolean doesHaveZoneTimers() {
200 /** Method to initialize the sprinkler. Interface implementation.
204 * @return [void] None.
208 if (didInit.compareAndSet(false, true) == false) {
209 return; // already init
213 Iterator itr = spr_Addresses.iterator();
214 IoTDeviceAddress deviceAddress = (IoTDeviceAddress)itr.next();
215 System.out.println("Address: " + deviceAddress.getCompleteAddress());
217 // Create the communication channel
218 communicationSockect = new IoTUDP(deviceAddress);
219 } catch (Exception e) {
224 // Launch the worker function in a separate thread.
225 workerThread = new Thread(new Runnable() {
230 workerThread.start();
234 /*******************************************************************************************************************************************
238 *******************************************************************************************************************************************/
240 /** Method to send the get information udp packet to get the latest sprinkler state.
244 * @return [void] None.
246 public void sendGetInformation() {
247 String sendString = "GET";
248 sendPacket(sendString.getBytes(StandardCharsets.UTF_8));
252 /** Method to parse the UDP packet data into a meaningful representation.
254 * @param _packetData [byte[]] raw packet data from the udp packet.
256 * @return [List<ZoneState>] Parsed zone data.
258 private List<ZoneState> parseGetResponse(byte[] _packetData) {
259 String recString = new String(_packetData);
260 List<ZoneState> retStates = new ArrayList<ZoneState>();
262 String[] lines = recString.split("\n");
264 for (int i = 0; i < 9; i++) {
265 String[] splitSting = lines[i].split(",");
267 int zoneNum = Integer.parseInt(splitSting[0].trim());
268 int onOffInt = Integer.parseInt(splitSting[1].trim());
269 boolean onOff = onOffInt != 0;
270 int duration = Integer.parseInt(splitSting[2].trim());
273 //ZoneState zTmp = new ZoneState(zoneNum, onOff, duration);
274 ZoneState zTmp = new ZoneState();
275 zTmp.zoneNumber = zoneNum;
276 zTmp.onOffState = onOff;
277 zTmp.duration = duration;
285 /** Method to parse the UDP packet data into a meaningful representation.
287 * @param _packetData [byte[]] bytes to send over the udp channel.
289 * @return [void] None.
291 private void sendPacket(byte[] _packetData) {
292 // System.out.println("About to send");
293 sendSocketFlag.set(true);
296 socketMutex.acquire();
297 } catch (InterruptedException e) {
298 System.out.println("mutex Error");
302 communicationSockect.sendData(_packetData);
304 } catch (IOException e) {
305 System.out.println("Socket Send Error");
308 sendSocketFlag.set(false);
309 socketMutex.release();
313 /** Method to constantly flush the udp socket expect when we wish to read the incoming data.
317 * @return [void] None.
319 private void workerFunction() {
321 // Need timeout on receives since we are not sure if a packet will be available
322 // for processing so don't block waiting
323 communicationSockect.setSoTimeout(50);
324 } catch (IOException e) {
331 // Communication resource is busy so try again later
332 if (sendSocketFlag.get()) {
336 if (doingRead.get()) {
341 socketMutex.acquire();
342 } catch (InterruptedException e) {
347 dat = communicationSockect.recieveData(1024);
348 } catch (java.net.SocketTimeoutException e) {
351 } catch (IOException e) {
352 // Problem but might be able to recover??
357 // Never forget to release!
358 socketMutex.release();
360 // Wait a bit as to not tie up system resources
363 } catch (Exception e) {