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 //TODO: Change these back to normal - this is just for testing to make it awake all the time
35 // private static final int TIME_HOURS_TO_WATER_GRASS = 7; // 7 am
36 private static final int TIME_HOURS_TO_WATER_GRASS = 3;
37 // private static final int TIME_MINUTES_TO_WATER_GRASS = 30; // 30 minutes
38 private static final int TIME_MINUTES_TO_WATER_GRASS = 30;
39 // private static final int TIME_TO_RECOVER_GRASS_FOR = 8 * 24 * 60 * 60; // 8 days
40 private static final int TIME_TO_RECOVER_GRASS_FOR = 10;
41 // private static final int TIME_TO_HIBERNATE_GRASS_FOR = 30 * 24 * 60 * 60; // 30 days
42 private static final int TIME_TO_HIBERNATE_GRASS_FOR = 10;
43 public static final int CAMERA_FPS = 15; // In frames per second
46 /*******************************************************************************************************************************************
50 *******************************************************************************************************************************************/
51 private int dayOfLastCheck = -1;
52 private int monthOfLastCheck = -1;
53 private boolean isInHibernationRecoveryMode = false;
54 private Date hibernationRecoveryModeStartDate = null;
55 private boolean isHibernationMode = false;
56 private Date hibernationModeStartDate = null;
57 private List<LawnState> lawns = new ArrayList<LawnState>();
58 private WeatherGrabber weatherGrabber = null;
60 // used to block until gui is done and the settings are ready to be polled
61 private AtomicBoolean waitingForInterface = new AtomicBoolean(true);
63 // the settings from the interface, used to setup the system
64 private double inchesPerWeek = 0;
65 private int weatherZipCode = 0;
66 private int daysToWaterOn = 0;
67 private List<Double> inchesPerMinute;
69 private static int sensorId = 0;
71 /*******************************************************************************************************************************************
73 ** IoT Sets and Relations
75 *******************************************************************************************************************************************/
76 @config private IoTSet<IoTAddress> weatherDataAddresses;
77 @config private IoTSet<IoTAddress> weatherDataAddressMain;
78 @config private IoTSet<WeatherGatewaySmart> gwSet;
79 @config private IoTSet<LawnSmart> lawnSet;
80 @config private IoTSet<MoistureSensorSmart> moistureSensorsSet;
81 @config private IoTSet<CameraSmart> cameraSet;
82 @config private IoTRelation<LawnSmart, CameraSmart> lawnCameraRelation;
83 @config private IoTRelation<LawnSmart, SprinklerSmart> lawnSprinklerRelation;
84 @config private IoTRelation<LawnSmart, MoistureSensorSmart> lawnMoistureSensorRelation;
87 public IrrigationController() throws RemoteException {
91 /*******************************************************************************************************************************************
95 *******************************************************************************************************************************************/
98 /** Method to set whether the controller should maintain the lawns in hibernation mode
99 * or in normal mode. Lawns should be put in hibernation mode in drought conditions
101 * @param _hibMode [boolean] set the hibernation mode for this lawn controllers (true = hibernation)
103 * @return [void] None.
105 public void setHibernationMode(boolean _hibMode) {
107 // change hibernation mode status
108 isHibernationMode = _hibMode;
110 // set the start date for when we started this hibernation mode
113 // make sure we dont reset this cycle
114 if (!isHibernationMode) {
115 hibernationModeStartDate = new Date();
118 // reset all hibernation stuff
119 hibernationModeStartDate = null;
120 isInHibernationRecoveryMode = false;
121 hibernationRecoveryModeStartDate = null;
125 /** Method to start the controller and run the main control loop
127 * @return [void] None.
129 public void init() throws RemoteException {
131 // initialize the controller
133 System.out.println("Initialized controller!");
138 // get the current time of day (date and time)
139 Date currentDate = new Date();
141 // get the epoch time till the beginning of the day
142 Date beginingOfToday = new Date(currentDate.getYear(), currentDate.getMonth(), currentDate.getDate());
144 // calculate the seconds since the start of the day.
145 long secondsSinceStartOfDay = (currentDate.getTime() - beginingOfToday.getTime()) / 1000;
147 // Seconds since the start of the day to start the watering
148 long secondsForWateringStart = (TIME_HOURS_TO_WATER_GRASS * 3600) + (TIME_MINUTES_TO_WATER_GRASS * 60);
150 // System.out.println("beginingOfToday " + beginingOfToday);
151 // System.out.println("secondsSinceStartOfDay " + secondsSinceStartOfDay);
152 // System.out.println("secondsForWateringStart " + secondsForWateringStart);
154 // check if the current time is within the start watering interval
155 /*if ((secondsSinceStartOfDay < secondsForWateringStart) || (secondsSinceStartOfDay > (secondsForWateringStart + (60 * 60)))) {
156 System.out.println("Sleep for 10 minutes.. ");
158 //Thread.sleep(10 * 60 * 1000); // sleep for 10 minutes
159 Thread.sleep(10); // sleep for 10 seconds
160 } catch (Exception e) {
167 // check if we already checked if we should water today
168 // we only need to do this once per day
169 /*if ((dayOfLastCheck == currentDate.getDate()) && (monthOfLastCheck == currentDate.getMonth())) {
170 System.out.println("Sleep for 1 hour...");
172 Thread.sleep(60 * 60 * 1000); // sleep for an hour
173 } catch (Exception e) {
180 // we decided to check if we should water today so save the fact that we chose to water on this day
181 dayOfLastCheck = currentDate.getDate();
182 monthOfLastCheck = currentDate.getMonth();
184 // update the lawn states everyday
185 for (LawnState ls : lawns) {
186 ls.updateLawn(currentDate);
188 // check if we are in hibernation mode and do the correct loop action
189 if (isHibernationMode) {
190 // System.out.println("Hibernation mode!");
191 // If we are in hibernation mode then use the hibernation loop code
192 wateringHibernationLoop(currentDate);
194 // System.out.println("Normal mode!");
195 // Using the normal watering loop code
196 wateringNormalLoop(currentDate);
202 /** Callback method for when the information is retrieved.
204 * @param _inchesPerWeek [double].
205 * @param _weatherZipCode [int].
206 * @param _daysToWaterOn [int].
207 * @param _inchesPerMinute [double].
208 * @return [void] None.
210 public void informationRetrieved(double _inchesPerWeek, int _weatherZipCode, int _daysToWaterOn, double _inchesPerMinute) {
212 System.out.println("DEBUG: Information is retrieved from phone!!!");
214 // get the parameters that the interface (phone app) reads from the user
215 inchesPerWeek = _wgw.getInchesPerWeek();
216 weatherZipCode = _wgw.getWeatherZipCode();
217 daysToWaterOn = _wgw.getDaysToWaterOn();
218 inchesPerMinute.add(_wgw.getInchesPerMinute());
219 } catch(RemoteException ex) {
220 ex.printStackTrace();
223 inchesPerWeek = _inchesPerWeek;
224 weatherZipCode = _weatherZipCode;
225 daysToWaterOn = _daysToWaterOn;
226 inchesPerMinute.add(_inchesPerMinute);
228 // the gui is done so release the spin wait that was waiting for the gui
229 waitingForInterface.set(false);
232 /*******************************************************************************************************************************************
236 *******************************************************************************************************************************************/
239 /** Method to initialize the controller variables and all the drivers and such
241 * @return [void] None.
243 private void initController() throws RemoteException {
245 // Setup the weather grabber object with the correct address of the weather api
246 Iterator it = weatherDataAddresses.iterator();
247 weatherGrabber = new WeatherGrabber((IoTAddress)it.next());
249 // Initialize inchesPerMinute
250 inchesPerMinute = new ArrayList<Double>();
252 // We setup a Gateway object to get information from the phone app
253 for (WeatherGatewaySmart gw : gwSet.values()) {
255 gw.registerCallback(this);
259 System.out.println("DEBUG: Waiting for phone to send weather information");
260 while (waitingForInterface.get()) {
263 } catch (Exception e) {
267 // TODO: Use a phone input interface later
268 /*inchesPerWeek = 20.00;
269 weatherZipCode = 92612;
271 inchesPerMinute.add(1.50);*/
273 System.out.println("DEBUG: inchesPerWeek: " + inchesPerWeek);
274 System.out.println("DEBUG: weatherZipCode: " + weatherZipCode);
275 System.out.println("DEBUG: daysToWaterOn: " + daysToWaterOn);
276 System.out.println("DEBUG: inchesPerMinute: " + inchesPerMinute.get(0));
278 // set the zip code and the the number of days of the weather grabber
279 // here the number of days is set to the max that the grabber supports
280 weatherGrabber.setZipcode(weatherZipCode);
281 weatherGrabber.setNumberOfDays(16);
283 // Setup the cameras, start them all and assign each one a motion detector
284 for (CameraSmart cam : cameraSet.values()) {
286 // initialize the camera, might need to setup some stuff internally
289 // set the camera parameters.
290 cam.setFPS(CAMERA_FPS);
291 cam.setResolution(Resolution.RES_VGA);
293 // Start the camera (example is start the HTTP stream if it is a network camera)
295 System.out.println("DEBUG: Init camera! " + cam.toString());
298 // counter so that we can match the lawn inches per min data with the specific lawn
300 for (LawnSmart l : lawnSet.values()) {
301 // create a motionDetector for each lawn object
302 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
304 // for 1 camera, if there are any then register the camera for that lawn
305 HashSet<CameraSmart> cameras = lawnCameraRelation.get(l);
306 System.out.println("DEBUG: Camera.size(): " + cameras.size());
307 if (cameras.size() >= 1) {
309 // we only need 1 camera per lawn so get the first one in the list
310 Iterator camIt = cameras.iterator();
311 CameraSmart cam = (CameraSmart)camIt.next();
312 System.out.println("DEBUG: Registering callback to camera: " + cam.toString());
314 // setup the callback
315 cam.registerCallback(mo);
316 //} catch (RemoteException e) {
317 // e.printStackTrace();
321 // we also only need 1 sprinkler controller per lawn so grab the first one
322 HashSet<SprinklerSmart> sprinklers = lawnSprinklerRelation.get(l);
323 Iterator sprinklersIt = sprinklers.iterator();
324 SprinklerSmart spr = (SprinklerSmart)sprinklersIt.next();
326 // init the sprinkler controller, do it here since it only needs to be done once per controller
329 // Wait until sprinkler is active
331 } catch (Exception e) {
334 System.out.println("DEBUG: Init sprinkler: " + spr.toString());
336 // get and init the moisture sensors for this specific lawn
337 HashSet<MoistureSensorSmart> sensors = lawnMoistureSensorRelation.get(l);
338 for (MoistureSensorSmart sen : sensors) {
339 System.out.println("DEBUG: Init sensors: " + sen.toString());
342 sen.setId(sensorId++);
343 } catch (Exception e) {
348 // create the lawn objects
349 System.out.println("DEBUG: Creating a LawnState object");
351 new LawnState(l, daysToWaterOn, mo, inchesPerMinute.get(counter), inchesPerWeek, spr, counter, sensors);
354 // dont forget to increment the counter
359 /** Main loop for when the controller is watering the lawns under normal conditions, not in hibernation mode
361 * @param _currentDate [Date] current date
363 * @return [void] None.
365 private void wateringNormalLoop(Date _currentDate) {
367 // get the weather data for the next little bit
368 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
369 // TODO: Replace this with the WeatherGrabber.getWeatherData() above
370 // List<DayWeather> weatherData = new ArrayList<DayWeather>();
372 // Go through each lawn and check if we should water it and if we should, water it
373 for (LawnState ls : lawns) {
375 // water for specific lawn
376 waterLawn(ls, _currentDate, weatherData);
380 /** Main loop for when the controller is watering the lawns in hibernation mode
382 * @param _currentDate [Date] current date
384 * @return [void] None.
386 private void wateringHibernationLoop(Date _currentDate) {
388 // if we are in recovery mode then run the recovery action
389 // we are still in hibernation mode but we need to recover the grass
390 if (isInHibernationRecoveryMode) {
391 // System.out.println("DEBUG: Recovery mode!");
392 hibernationRecoveryLoop(_currentDate);
396 // check if we should enter recovery mode
397 long elapsedTime = (_currentDate.getTime() - hibernationModeStartDate.getTime()) / 1000;
398 if (elapsedTime >= TIME_TO_HIBERNATE_GRASS_FOR) {
400 // start recovery mode
401 isInHibernationRecoveryMode = true;
402 hibernationRecoveryModeStartDate = null;
403 // System.out.println("DEBUG: We enter recovery mode for the first time!");
404 // first cycle of recovery
405 hibernationRecoveryLoop(_currentDate);
409 // get the weather data for the next little bit
410 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
412 // Go through each lawn and check if we should water it and if we should, water it
413 for (LawnState ls : lawns) {
415 boolean lawnHasMotion = ls.lawnHasSufficientMotion();
417 // there is no motion on the lawn so no need to water it
418 if (!lawnHasMotion) {
421 // System.out.println("DEBUG: We water the lawn! (wateringHibernationLoop)");
422 // water specific lawn since it has motion
423 waterLawn(ls, _currentDate, weatherData);
428 /** Main loop for when the controller is watering the lawns in hibernation mode
430 * @param _currentDate [Date] current date
432 * @return [void] None.
434 private void hibernationRecoveryLoop(Date _currentDate) {
436 // start recovery mode if it wasnt started yet
437 if (hibernationRecoveryModeStartDate == null) {
438 hibernationRecoveryModeStartDate = _currentDate;
441 // time since this mode was started
442 long elapsedTime = (_currentDate.getTime() - hibernationRecoveryModeStartDate.getTime()) / 1000;
444 // we have been in recovery mode long enough
445 if (elapsedTime >= TIME_TO_RECOVER_GRASS_FOR) {
447 // System.out.println("DEBUG: We have been in recovery mode long enough!");
448 // reset the recovery mode
449 isInHibernationRecoveryMode = false;
450 hibernationRecoveryModeStartDate = null;
452 // revived grass so restart the grass hibernation cycle
453 hibernationModeStartDate = _currentDate;
455 // do the hibernation loop since we are no longer in recovery mode
456 wateringHibernationLoop(_currentDate);
461 // if we got here then we are trying to recover the grass
463 // get the weather data for the next little bit
464 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
466 // Go through each lawn and check if we should water it and if we should, water it
467 for (LawnState ls : lawns) {
469 // System.out.println("DEBUG: We water the lawn! (hibernationRecoveryLoop)");
470 // water specific lawn since it has motion
471 waterLawn(ls, _currentDate, weatherData);
477 /** Method for watering a specific lawn if it needs to be watered
479 * @param _ls [LawnState] lawn to water
480 * @param _currentDate [Date] current date
481 * @param _weatherData [List<DayWeather>] latest weather data
483 * @return [void] None.
485 private void waterLawn(LawnState _ls, Date _currentDate, List<DayWeather> _weatherData) {
487 // check if today or tomorrow is a wet day
488 boolean todayIsWetDay = _weatherData.get(0).getIsWetDay();
489 boolean tomorrowIsWetDay = _weatherData.get(1).getIsWetDay();
490 // TODO: Remove this later - hack the values for now!!!
491 // boolean todayIsWetDay = false;
492 // boolean tomorrowIsWetDay = false;
494 // lawn cannot wait anymore for water so water not
495 boolean lawnNeedsWaterNow = _ls.needsWateringUrgently(_currentDate);
496 if (lawnNeedsWaterNow) {
497 System.out.println("DEBUG: Need water now!!!");
498 System.out.println("DEBUG: Is wet day? " + todayIsWetDay);
499 System.out.println("DEBUG: Tomorrow is wet day? " + tomorrowIsWetDay);
500 // if it is not going to rain today then water the lawn
501 // TODO: Put this back to uncommented!!! Only for testing!!!
502 // if (!todayIsWetDay) {
503 _ls.waterLawn(_currentDate);
508 // check if this lawn needs watering based on watering algoritm/sensors/ext
509 boolean shouldWaterLawn = _ls.needsWatering(_currentDate);
511 // should not water this lawn then just skip to the next lawn
512 if (!shouldWaterLawn) {
516 // it is going to rain soon so wait it out.
517 // Grass is not in critical condition so it can wait a bit.
518 if (todayIsWetDay || tomorrowIsWetDay) {
522 // if we got here then we need to water the lawn
523 _ls.waterLawn(_currentDate);