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;
22 //import iotchecker.qual.*;
23 import iotcode.annotation.*;
25 /** Class EspAlarm for the ESP8266 plrg Alarm.
27 * @author Ali Younis <ayounis @ uci.edu>, Rahmadi Trimananda <rtrimana @ uci.edu>
32 public class EspAlarm implements Alarm {
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> alm_Addresses;
67 /*public EspSprinkler(IoTUDP _udp) {
68 communicationSockect = _udp;
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 = alm_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());
274 //ZoneState zTmp = new ZoneState(zoneNum, onOff, duration);
275 ZoneState zTmp = new ZoneState();
276 zTmp.zoneNumber = zoneNum;
277 zTmp.onOffState = onOff;
278 zTmp.duration = duration;
286 /** Method to parse the UDP packet data into a meaningful representation.
288 * @param _packetData [byte[]] bytes to send over the udp channel.
290 * @return [void] None.
292 private void sendPacket(byte[] _packetData) {
293 // System.out.println("About to send");
294 sendSocketFlag.set(true);
297 socketMutex.acquire();
298 } catch (InterruptedException e) {
299 System.out.println("mutex Error");
303 communicationSockect.sendData(_packetData);
305 } catch (IOException e) {
306 System.out.println("Socket Send Error");
309 sendSocketFlag.set(false);
310 socketMutex.release();
314 /** Method to constantly flush the udp socket expect when we wish to read the incoming data.
318 * @return [void] None.
320 private void workerFunction() {
322 // Need timeout on receives since we are not sure if a packet will be available
323 // for processing so don't block waiting
324 communicationSockect.setSoTimeout(50);
325 } catch (IOException e) {
332 // Communication resource is busy so try again later
333 if (sendSocketFlag.get()) {
337 if (doingRead.get()) {
342 socketMutex.acquire();
343 } catch (InterruptedException e) {
348 dat = communicationSockect.recieveData(1024);
349 } catch (java.net.SocketTimeoutException e) {
352 } catch (IOException e) {
353 // Problem but might be able to recover??
358 // Never forget to release!
359 socketMutex.release();
361 // Wait a bit as to not tie up system resources
364 } catch (Exception e) {