1 package IrrigationController;
2 // Standard Java Packages
4 import java.util.Iterator;
6 import java.util.ArrayList;
7 import java.util.HashSet;
8 import java.util.concurrent.atomic.AtomicBoolean;
12 import java.rmi.RemoteException;
13 import java.rmi.server.UnicastRemoteObject;
15 // IoT Runtime Packages
16 import iotruntime.slave.IoTSet;
17 import iotruntime.slave.IoTRelation;
18 import iotruntime.slave.IoTAddress;
19 import iotcode.annotation.*;
21 // IoT Driver Packages
22 import iotcode.interfaces.*;
23 import iotcode.WeatherPhoneGateway.*;
25 public class IrrigationController extends UnicastRemoteObject implements WeatherGatewayCallback {
28 /*******************************************************************************************************************************************
32 *******************************************************************************************************************************************/
33 // private static final int NUMBER_OF_TIMES_PER_WEEK_TO_WATER = 2;
34 private static final int TIME_HOURS_TO_WATER_GRASS = 7; // 7 am
35 private static final int TIME_MINUTES_TO_WATER_GRASS = 30; // 30 minutes
36 private static final int TIME_TO_RECOVER_GRASS_FOR = 8 * 24 * 60 * 60; // 8 days
37 private static final int TIME_TO_HIBERNATE_GRASS_FOR = 30 * 24 * 60 * 60; // 30 days
38 public static final int CAMERA_FPS = 15; // In frames per second
41 /*******************************************************************************************************************************************
45 *******************************************************************************************************************************************/
46 private int dayOfLastCheck = -1;
47 private int monthOfLastCheck = -1;
48 private boolean isInHibernationRecoveryMode = false;
49 private Date hibernationRecoveryModeStartDate = null;
50 private boolean isHibernationMode = false;
51 private Date hibernationModeStartDate = null;
52 private List<LawnState> lawns = new ArrayList<LawnState>();
53 private WeatherGrabber weatherGrabber = null;
55 // used to block until gui is done and the settings are ready to be polled
56 private AtomicBoolean waitingForInterface = new AtomicBoolean(true);
58 // the settings from the interface, used to setup the system
59 private double inchesPerWeek = 0;
60 private int weatherZipCode = 0;
61 private int daysToWaterOn = 0;
62 private List<Double> inchesPerMinute;
64 private static int sensorId = 0;
66 /*******************************************************************************************************************************************
68 ** IoT Sets and Relations
70 *******************************************************************************************************************************************/
71 @config private IoTSet<IoTAddress> weatherDataAddresses;
72 @config private IoTSet<IoTAddress> weatherDataAddressMain;
73 @config private IoTSet<WeatherGatewaySmart> gwSet;
74 @config private IoTSet<LawnSmart> lawnSet;
75 @config private IoTSet<MoistureSensorSmart> moistureSensorsSet;
76 @config private IoTSet<CameraSmart> cameraSet;
77 @config private IoTRelation<LawnSmart, CameraSmart> lawnCameraRelation;
78 @config private IoTRelation<LawnSmart, SprinklerSmart> lawnSprinklerRelation;
79 @config private IoTRelation<LawnSmart, MoistureSensorSmart> lawnMoistureSensorRelation;
82 public IrrigationController() throws RemoteException {
86 /*******************************************************************************************************************************************
90 *******************************************************************************************************************************************/
93 /** Method to set whether the controller should maintain the lawns in hibernation mode
94 * or in normal mode. Lawns should be put in hibernation mode in drought conditions
96 * @param _hibMode [boolean] set the hibernation mode for this lawn controllers (true = hibernation)
98 * @return [void] None.
100 public void setHibernationMode(boolean _hibMode) {
102 // change hibernation mode status
103 isHibernationMode = _hibMode;
105 // set the start date for when we started this hibernation mode
108 // make sure we dont reset this cycle
109 if (!isHibernationMode) {
110 hibernationModeStartDate = new Date();
113 // reset all hibernation stuff
114 hibernationModeStartDate = null;
115 isInHibernationRecoveryMode = false;
116 hibernationRecoveryModeStartDate = null;
120 /** Method to start the controller and run the main control loop
122 * @return [void] None.
124 public void init() throws RemoteException {
126 // initialize the controller
128 System.out.println("Initialized controller!");
133 // get the current time of day (date and time)
134 Date currentDate = new Date();
136 // get the epoch time till the beginning of the day
137 Date beginingOfToday = new Date(currentDate.getYear(), currentDate.getMonth(), currentDate.getDate());
139 // calculate the seconds since the start of the day.
140 long secondsSinceStartOfDay = (currentDate.getTime() - beginingOfToday.getTime()) / 1000;
142 // Seconds since the start of the day to start the watering
143 long secondsForWateringStart = (TIME_HOURS_TO_WATER_GRASS * 3600) + (TIME_MINUTES_TO_WATER_GRASS * 60);
145 System.out.println("beginingOfToday " + beginingOfToday);
146 System.out.println("secondsSinceStartOfDay " + secondsSinceStartOfDay);
147 System.out.println("secondsForWateringStart " + secondsForWateringStart);
149 // check if the current time is within the start watering interval
150 if ((secondsSinceStartOfDay < secondsForWateringStart) || (secondsSinceStartOfDay > (secondsForWateringStart + (60 * 60)))) {
151 System.out.println("Sleep for 10 minutes.. ");
153 //Thread.sleep(10 * 60 * 1000); // sleep for 10 minutes
154 Thread.sleep(10); // sleep for 10 seconds
155 } catch (Exception e) {
162 // check if we already checked if we should water today
163 // we only need to do this once per day
164 if ((dayOfLastCheck == currentDate.getDate()) && (monthOfLastCheck == currentDate.getMonth())) {
165 System.out.println("Sleep for 1 hour...");
167 Thread.sleep(60 * 60 * 1000); // sleep for an hour
168 } catch (Exception e) {
175 // we decided to check if we should water today so save the fact that we chose to water on this day
176 dayOfLastCheck = currentDate.getDate();
177 monthOfLastCheck = currentDate.getMonth();
179 // update the lawn states everyday
180 for (LawnState ls : lawns) {
181 ls.updateLawn(currentDate);
183 // check if we are in hibernation mode and do the correct loop action
184 if (isHibernationMode) {
185 System.out.println("Hibernation mode!");
186 // If we are in hibernation mode then use the hibernation loop code
187 wateringHibernationLoop(currentDate);
189 System.out.println("Normal mode!");
190 // Using the normal watering loop code
191 wateringNormalLoop(currentDate);
197 /** Callback method for when the information is retrieved.
199 * @param _inchesPerWeek [double].
200 * @param _weatherZipCode [int].
201 * @param _daysToWaterOn [int].
202 * @param _inchesPerMinute [double].
203 * @return [void] None.
205 public void informationRetrieved(double _inchesPerWeek, int _weatherZipCode, int _daysToWaterOn, double _inchesPerMinute) {
207 System.out.println("DEBUG: Information is retrieved from phone!!!");
209 inchesPerWeek = _inchesPerWeek;
210 weatherZipCode = _weatherZipCode;
211 daysToWaterOn = _daysToWaterOn;
212 inchesPerMinute.add(_inchesPerMinute);
214 // the gui is done so release the spin wait that was waiting for the gui
215 waitingForInterface.set(false);
218 /*******************************************************************************************************************************************
222 *******************************************************************************************************************************************/
225 /** Method to initialize the controller variables and all the drivers and such
227 * @return [void] None.
229 private void initController() throws RemoteException {
231 // Setup the weather grabber object with the correct address of the weather api
232 Iterator it = weatherDataAddresses.iterator();
233 weatherGrabber = new WeatherGrabber((IoTAddress)it.next());
235 // Initialize inchesPerMinute
236 inchesPerMinute = new ArrayList<Double>();
238 // We setup a Gateway object to get information from the phone app
239 for (WeatherGatewaySmart gw : gwSet.values()) {
241 gw.registerCallback(this);
245 System.out.println("DEBUG: Waiting for phone to send weather information");
246 while (waitingForInterface.get()) {
249 } catch (Exception e) {
254 System.out.println("DEBUG: inchesPerWeek: " + inchesPerWeek);
255 System.out.println("DEBUG: weatherZipCode: " + weatherZipCode);
256 System.out.println("DEBUG: daysToWaterOn: " + daysToWaterOn);
257 System.out.println("DEBUG: inchesPerMinute: " + inchesPerMinute.get(0));
259 // set the zip code and the the number of days of the weather grabber
260 // here the number of days is set to the max that the grabber supports
261 weatherGrabber.setZipcode(weatherZipCode);
262 weatherGrabber.setNumberOfDays(16);
264 // Setup the cameras, start them all and assign each one a motion detector
265 for (CameraSmart cam : cameraSet.values()) {
267 // initialize the camera, might need to setup some stuff internally
270 // set the camera parameters.
271 cam.setFPS(CAMERA_FPS);
272 cam.setResolution(Resolution.RES_VGA);
274 // Start the camera (example is start the HTTP stream if it is a network camera)
276 System.out.println("DEBUG: Init camera! " + cam.toString());
279 // counter so that we can match the lawn inches per min data with the specific lawn
281 for (LawnSmart l : lawnSet.values()) {
282 // create a motionDetector for each lawn object
283 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
285 // for 1 camera, if there are any then register the camera for that lawn
286 HashSet<CameraSmart> cameras = lawnCameraRelation.get(l);
287 System.out.println("DEBUG: Camera.size(): " + cameras.size());
288 if (cameras.size() >= 1) {
290 // we only need 1 camera per lawn so get the first one in the list
291 Iterator camIt = cameras.iterator();
292 CameraSmart cam = (CameraSmart)camIt.next();
293 System.out.println("DEBUG: Registering callback to camera: " + cam.toString());
294 cam.registerCallback(mo);
297 // we also only need 1 sprinkler controller per lawn so grab the first one
298 HashSet<SprinklerSmart> sprinklers = lawnSprinklerRelation.get(l);
299 Iterator sprinklersIt = sprinklers.iterator();
300 SprinklerSmart spr = (SprinklerSmart)sprinklersIt.next();
302 // init the sprinkler controller, do it here since it only needs to be done once per controller
305 // Wait until sprinkler is active
307 } catch (Exception e) {
310 System.out.println("DEBUG: Init sprinkler: " + spr.toString());
312 // get and init the moisture sensors for this specific lawn
313 HashSet<MoistureSensorSmart> sensors = lawnMoistureSensorRelation.get(l);
314 for (MoistureSensorSmart sen : sensors) {
315 System.out.println("DEBUG: Init sensors: " + sen.toString());
318 sen.setId(sensorId++);
319 } catch (Exception e) {
324 // create the lawn objects
325 System.out.println("DEBUG: Creating a LawnState object");
327 new LawnState(l, daysToWaterOn, mo, inchesPerMinute.get(counter), inchesPerWeek, spr, counter, sensors);
330 // dont forget to increment the counter
335 /** Main loop for when the controller is watering the lawns under normal conditions, not in hibernation mode
337 * @param _currentDate [Date] current date
339 * @return [void] None.
341 private void wateringNormalLoop(Date _currentDate) {
343 // get the weather data for the next little bit
344 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
346 // Go through each lawn and check if we should water it and if we should, water it
347 for (LawnState ls : lawns) {
349 // water for specific lawn
350 waterLawn(ls, _currentDate, weatherData);
354 /** Main loop for when the controller is watering the lawns in hibernation mode
356 * @param _currentDate [Date] current date
358 * @return [void] None.
360 private void wateringHibernationLoop(Date _currentDate) {
362 // if we are in recovery mode then run the recovery action
363 // we are still in hibernation mode but we need to recover the grass
364 if (isInHibernationRecoveryMode) {
365 System.out.println("DEBUG: Recovery mode!");
366 hibernationRecoveryLoop(_currentDate);
370 // check if we should enter recovery mode
371 long elapsedTime = (_currentDate.getTime() - hibernationModeStartDate.getTime()) / 1000;
372 if (elapsedTime >= TIME_TO_HIBERNATE_GRASS_FOR) {
374 // start recovery mode
375 isInHibernationRecoveryMode = true;
376 hibernationRecoveryModeStartDate = null;
377 System.out.println("DEBUG: We enter recovery mode for the first time!");
378 // first cycle of recovery
379 hibernationRecoveryLoop(_currentDate);
383 // get the weather data for the next little bit
384 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
386 // Go through each lawn and check if we should water it and if we should, water it
387 for (LawnState ls : lawns) {
389 boolean lawnHasMotion = ls.lawnHasSufficientMotion();
391 // there is no motion on the lawn so no need to water it
392 if (!lawnHasMotion) {
395 System.out.println("DEBUG: We water the lawn! (wateringHibernationLoop)");
396 // water specific lawn since it has motion
397 waterLawn(ls, _currentDate, weatherData);
402 /** Main loop for when the controller is watering the lawns in hibernation mode
404 * @param _currentDate [Date] current date
406 * @return [void] None.
408 private void hibernationRecoveryLoop(Date _currentDate) {
410 // start recovery mode if it wasnt started yet
411 if (hibernationRecoveryModeStartDate == null) {
412 hibernationRecoveryModeStartDate = _currentDate;
415 // time since this mode was started
416 long elapsedTime = (_currentDate.getTime() - hibernationRecoveryModeStartDate.getTime()) / 1000;
418 // we have been in recovery mode long enough
419 if (elapsedTime >= TIME_TO_RECOVER_GRASS_FOR) {
421 System.out.println("DEBUG: We have been in recovery mode long enough!");
422 // reset the recovery mode
423 isInHibernationRecoveryMode = false;
424 hibernationRecoveryModeStartDate = null;
426 // revived grass so restart the grass hibernation cycle
427 hibernationModeStartDate = _currentDate;
429 // do the hibernation loop since we are no longer in recovery mode
430 wateringHibernationLoop(_currentDate);
435 // if we got here then we are trying to recover the grass
437 // get the weather data for the next little bit
438 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
440 // Go through each lawn and check if we should water it and if we should, water it
441 for (LawnState ls : lawns) {
443 System.out.println("DEBUG: We water the lawn! (hibernationRecoveryLoop)");
444 // water specific lawn since it has motion
445 waterLawn(ls, _currentDate, weatherData);
451 /** Method for watering a specific lawn if it needs to be watered
453 * @param _ls [LawnState] lawn to water
454 * @param _currentDate [Date] current date
455 * @param _weatherData [List<DayWeather>] latest weather data
457 * @return [void] None.
459 private void waterLawn(LawnState _ls, Date _currentDate, List<DayWeather> _weatherData) {
461 // check if today or tomorrow is a wet day
462 boolean todayIsWetDay = _weatherData.get(0).getIsWetDay();
463 boolean tomorrowIsWetDay = _weatherData.get(1).getIsWetDay();
464 // lawn cannot wait anymore for water so water not
465 boolean lawnNeedsWaterNow = _ls.needsWateringUrgently(_currentDate);
466 if (lawnNeedsWaterNow) {
467 System.out.println("DEBUG: Need water now!!!");
468 System.out.println("DEBUG: Is wet day? " + todayIsWetDay);
469 System.out.println("DEBUG: Tomorrow is wet day? " + tomorrowIsWetDay);
470 // if it is not going to rain today then water the lawn
471 if (!todayIsWetDay) {
472 _ls.waterLawn(_currentDate);
477 // check if this lawn needs watering based on watering algoritm/sensors/ext
478 boolean shouldWaterLawn = _ls.needsWatering(_currentDate);
480 // should not water this lawn then just skip to the next lawn
481 if (!shouldWaterLawn) {
485 // it is going to rain soon so wait it out.
486 // Grass is not in critical condition so it can wait a bit.
487 if (todayIsWetDay || tomorrowIsWetDay) {
491 // if we got here then we need to water the lawn
492 _ls.waterLawn(_currentDate);