1 package iotcode.EspAlarm;
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.Alarm;
21 import iotcode.annotation.*;
23 /** Class EspAlarm for the ESP8266 plrg Alarm.
25 * @author Ali Younis <ayounis @ uci.edu>, Rahmadi Trimananda <rtrimana @ uci.edu>
30 public class EspAlarm implements Alarm {
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> alm_Addresses;
65 /* TODO: Use this constructor to test this driver manually
66 public EspSprinkler(IoTUDP _udp) {
67 communicationSockect = _udp;
71 communicationSockect = null;
75 /*******************************************************************************************************************************************
79 *******************************************************************************************************************************************/
81 /** Method to set the state of a specified zone. Interface implementation.
83 * @param _zone [int] : zone number to set.
84 * @param _onOff [boolean] : the state to set the zone to, on or off.
85 * @param _onDurationSeconds [int]: the duration to set the state on to, if -1 then infinite.
87 * @return [void] None.
89 public void setZone(int _zone, boolean _onOff, int _onDurationSeconds) {
92 settingZone.acquire();
93 String sendString = "SET,";
94 sendString += Integer.toString(_zone);
103 sendString += Integer.toString(_onDurationSeconds);
105 sendPacket(sendString.getBytes(StandardCharsets.UTF_8));
106 } catch (Exception e) {
109 settingZone.release();
113 /** Method to get the current state of all the zones. Interface implementation.
117 * @return [List<ZoneState>] list of the states for the zones.
119 public List<ZoneState> getZoneStates() {
121 sendGetInformation();
125 } catch (Exception e) {
131 // Communication resource is busy so try again later
132 if (sendSocketFlag.get()) {
137 socketMutex.acquire();
138 } catch (InterruptedException e) {
143 dat = communicationSockect.recieveData(1024);
144 } catch (java.net.SocketTimeoutException e) {
147 } catch (IOException e) {
148 // Problem but might be able to recover??
153 // Never forget to release!
154 socketMutex.release();
158 doingRead.set(false);
159 return parseGetResponse(dat);
161 // return new ArrayList<ZoneState>();
165 } catch (Exception e) {
171 sendGetInformation();
179 /** Method to get the number of zones this sprinkler can control. Interface implementation.
183 * @return [int] number of zones that can be controlled.
185 public int getNumberOfZones() {
190 /** Method to get whether or not this sprinkler can control durations. Interface implementation.
194 * @return [boolean] boolean if this sprinkler can do durations.
196 public boolean doesHaveZoneTimers() {
201 /** Method to initialize the sprinkler. Interface implementation.
205 * @return [void] None.
209 if (didInit.compareAndSet(false, true) == false) {
210 return; // already init
214 Iterator itr = alm_Addresses.iterator();
215 IoTDeviceAddress deviceAddress = (IoTDeviceAddress)itr.next();
216 System.out.println("Address: " + deviceAddress.getCompleteAddress());
218 // Create the communication channel
219 communicationSockect = new IoTUDP(deviceAddress);
220 } catch (Exception e) {
225 // Launch the worker function in a separate thread.
226 workerThread = new Thread(new Runnable() {
231 workerThread.start();
235 /*******************************************************************************************************************************************
239 *******************************************************************************************************************************************/
241 /** Method to send the get information udp packet to get the latest sprinkler state.
245 * @return [void] None.
247 public void sendGetInformation() {
248 String sendString = "GET";
249 sendPacket(sendString.getBytes(StandardCharsets.UTF_8));
253 /** Method to parse the UDP packet data into a meaningful representation.
255 * @param _packetData [byte[]] raw packet data from the udp packet.
257 * @return [List<ZoneState>] Parsed zone data.
259 private List<ZoneState> parseGetResponse(byte[] _packetData) {
260 String recString = new String(_packetData);
261 List<ZoneState> retStates = new ArrayList<ZoneState>();
263 String[] lines = recString.split("\n");
265 for (int i = 0; i < 9; i++) {
266 String[] splitSting = lines[i].split(",");
268 int zoneNum = Integer.parseInt(splitSting[0].trim());
269 int onOffInt = Integer.parseInt(splitSting[1].trim());
270 boolean onOff = onOffInt != 0;
271 int duration = Integer.parseInt(splitSting[2].trim());
273 ZoneState zTmp = new ZoneState();
274 zTmp.zoneNumber = zoneNum;
275 zTmp.onOffState = onOff;
276 zTmp.duration = duration;
284 /** Method to parse the UDP packet data into a meaningful representation.
286 * @param _packetData [byte[]] bytes to send over the udp channel.
288 * @return [void] None.
290 private void sendPacket(byte[] _packetData) {
291 // System.out.println("About to send");
292 sendSocketFlag.set(true);
295 socketMutex.acquire();
296 } catch (InterruptedException e) {
297 System.out.println("mutex Error");
301 communicationSockect.sendData(_packetData);
303 } catch (IOException e) {
304 System.out.println("Socket Send Error");
307 sendSocketFlag.set(false);
308 socketMutex.release();
312 /** Method to constantly flush the udp socket expect when we wish to read the incoming data.
316 * @return [void] None.
318 private void workerFunction() {
320 // Need timeout on receives since we are not sure if a packet will be available
321 // for processing so don't block waiting
322 communicationSockect.setSoTimeout(50);
323 } catch (IOException e) {
330 // Communication resource is busy so try again later
331 if (sendSocketFlag.get()) {
335 if (doingRead.get()) {
340 socketMutex.acquire();
341 } catch (InterruptedException e) {
346 dat = communicationSockect.recieveData(1024);
347 } catch (java.net.SocketTimeoutException e) {
350 } catch (IOException e) {
351 // Problem but might be able to recover??
356 // Never forget to release!
357 socketMutex.release();
359 // Wait a bit as to not tie up system resources
362 } catch (Exception e) {