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()) {
262 // Thread.sleep(1000);
263 // } catch (Exception e) {
264 // e.printStackTrace();
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());
296 //} catch (RemoteException e) {
297 // e.printStackTrace();
302 // counter so that we can match the lawn inches per min data with the specific lawn
304 for (LawnSmart l : lawnSet.values()) {
305 // create a motionDetector for each lawn object
306 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
308 // for 1 camera, if there are any then register the camera for that lawn
309 HashSet<CameraSmart> cameras = lawnCameraRelation.get(l);
310 System.out.println("DEBUG: Camera.size(): " + cameras.size());
311 if (cameras.size() >= 1) {
313 // we only need 1 camera per lawn so get the first one in the list
314 Iterator camIt = cameras.iterator();
315 CameraSmart cam = (CameraSmart)camIt.next();
316 System.out.println("DEBUG: Registering callback to camera: " + cam.toString());
318 // setup the callback
319 cam.registerCallback(mo);
320 //} catch (RemoteException e) {
321 // e.printStackTrace();
325 // we also only need 1 sprinkler controller per lawn so grab the first one
326 HashSet<SprinklerSmart> sprinklers = lawnSprinklerRelation.get(l);
327 Iterator sprinklersIt = sprinklers.iterator();
328 SprinklerSmart spr = (SprinklerSmart)sprinklersIt.next();
330 // init the sprinkler controller, do it here since it only needs to be done once per controller
333 // Wait until sprinkler is active
335 } catch (Exception e) {
338 System.out.println("DEBUG: Init sprinkler: " + spr.toString());
340 // get and init the moisture sensors for this specific lawn
341 HashSet<MoistureSensorSmart> sensors = lawnMoistureSensorRelation.get(l);
342 for (MoistureSensorSmart sen : sensors) {
343 System.out.println("DEBUG: Init sensors: " + sen.toString());
346 sen.setId(sensorId++);
347 } catch (Exception e) {
352 // create the lawn objects
353 System.out.println("DEBUG: Creating a LawnState object");
355 new LawnState(l, daysToWaterOn, mo, inchesPerMinute.get(counter), inchesPerWeek, spr, counter, sensors);
358 // dont forget to increment the counter
363 /** Main loop for when the controller is watering the lawns under normal conditions, not in hibernation mode
365 * @param _currentDate [Date] current date
367 * @return [void] None.
369 private void wateringNormalLoop(Date _currentDate) {
371 // get the weather data for the next little bit
372 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
373 // TODO: Replace this with the WeatherGrabber.getWeatherData() above
374 // List<DayWeather> weatherData = new ArrayList<DayWeather>();
376 // Go through each lawn and check if we should water it and if we should, water it
377 for (LawnState ls : lawns) {
379 // water for specific lawn
380 waterLawn(ls, _currentDate, weatherData);
384 /** Main loop for when the controller is watering the lawns in hibernation mode
386 * @param _currentDate [Date] current date
388 * @return [void] None.
390 private void wateringHibernationLoop(Date _currentDate) {
392 // if we are in recovery mode then run the recovery action
393 // we are still in hibernation mode but we need to recover the grass
394 if (isInHibernationRecoveryMode) {
395 // System.out.println("DEBUG: Recovery mode!");
396 hibernationRecoveryLoop(_currentDate);
400 // check if we should enter recovery mode
401 long elapsedTime = (_currentDate.getTime() - hibernationModeStartDate.getTime()) / 1000;
402 if (elapsedTime >= TIME_TO_HIBERNATE_GRASS_FOR) {
404 // start recovery mode
405 isInHibernationRecoveryMode = true;
406 hibernationRecoveryModeStartDate = null;
407 // System.out.println("DEBUG: We enter recovery mode for the first time!");
408 // first cycle of recovery
409 hibernationRecoveryLoop(_currentDate);
413 // get the weather data for the next little bit
414 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
416 // Go through each lawn and check if we should water it and if we should, water it
417 for (LawnState ls : lawns) {
419 boolean lawnHasMotion = ls.lawnHasSufficientMotion();
421 // there is no motion on the lawn so no need to water it
422 if (!lawnHasMotion) {
425 // System.out.println("DEBUG: We water the lawn! (wateringHibernationLoop)");
426 // water specific lawn since it has motion
427 waterLawn(ls, _currentDate, weatherData);
432 /** Main loop for when the controller is watering the lawns in hibernation mode
434 * @param _currentDate [Date] current date
436 * @return [void] None.
438 private void hibernationRecoveryLoop(Date _currentDate) {
440 // start recovery mode if it wasnt started yet
441 if (hibernationRecoveryModeStartDate == null) {
442 hibernationRecoveryModeStartDate = _currentDate;
445 // time since this mode was started
446 long elapsedTime = (_currentDate.getTime() - hibernationRecoveryModeStartDate.getTime()) / 1000;
448 // we have been in recovery mode long enough
449 if (elapsedTime >= TIME_TO_RECOVER_GRASS_FOR) {
451 // System.out.println("DEBUG: We have been in recovery mode long enough!");
452 // reset the recovery mode
453 isInHibernationRecoveryMode = false;
454 hibernationRecoveryModeStartDate = null;
456 // revived grass so restart the grass hibernation cycle
457 hibernationModeStartDate = _currentDate;
459 // do the hibernation loop since we are no longer in recovery mode
460 wateringHibernationLoop(_currentDate);
465 // if we got here then we are trying to recover the grass
467 // get the weather data for the next little bit
468 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
470 // Go through each lawn and check if we should water it and if we should, water it
471 for (LawnState ls : lawns) {
473 // System.out.println("DEBUG: We water the lawn! (hibernationRecoveryLoop)");
474 // water specific lawn since it has motion
475 waterLawn(ls, _currentDate, weatherData);
481 /** Method for watering a specific lawn if it needs to be watered
483 * @param _ls [LawnState] lawn to water
484 * @param _currentDate [Date] current date
485 * @param _weatherData [List<DayWeather>] latest weather data
487 * @return [void] None.
489 private void waterLawn(LawnState _ls, Date _currentDate, List<DayWeather> _weatherData) {
491 // check if today or tomorrow is a wet day
492 boolean todayIsWetDay = _weatherData.get(0).getIsWetDay();
493 boolean tomorrowIsWetDay = _weatherData.get(1).getIsWetDay();
494 // TODO: Remove this later - hack the values for now!!!
495 // boolean todayIsWetDay = false;
496 // boolean tomorrowIsWetDay = false;
498 // lawn cannot wait anymore for water so water not
499 boolean lawnNeedsWaterNow = _ls.needsWateringUrgently(_currentDate);
500 if (lawnNeedsWaterNow) {
501 System.out.println("DEBUG: Need water now!!!");
502 System.out.println("DEBUG: Is wet day? " + todayIsWetDay);
503 System.out.println("DEBUG: Tomorrow is wet day? " + tomorrowIsWetDay);
504 // if it is not going to rain today then water the lawn
505 // TODO: Put this back to uncommented!!! Only for testing!!!
506 // if (!todayIsWetDay) {
507 _ls.waterLawn(_currentDate);
512 // check if this lawn needs watering based on watering algoritm/sensors/ext
513 boolean shouldWaterLawn = _ls.needsWatering(_currentDate);
515 // should not water this lawn then just skip to the next lawn
516 if (!shouldWaterLawn) {
520 // it is going to rain soon so wait it out.
521 // Grass is not in critical condition so it can wait a bit.
522 if (todayIsWetDay || tomorrowIsWetDay) {
526 // if we got here then we need to water the lawn
527 _ls.waterLawn(_currentDate);