4bf833bde8dac13d764c2d5eab37300f4bf826ee
[iot2.git] / benchmarks / IrrigationController / IrrigationController.java
1 package IrrigationController;
2 // Standard Java Packages
3 import java.util.Date;
4 import java.util.Iterator;
5 import java.util.List;
6 import java.util.ArrayList;
7 import java.util.HashSet;
8 import java.util.concurrent.atomic.AtomicBoolean;
9
10
11 // RMI packages
12 import java.rmi.RemoteException;
13 import java.rmi.server.UnicastRemoteObject;
14
15 // IoT Runtime Packages
16 import iotruntime.slave.IoTSet;
17 import iotruntime.slave.IoTRelation;
18 import iotruntime.slave.IoTAddress;
19 //import iotcode.annotation.*;
20
21 // IoT Driver Packages
22 import iotcode.interfaces.*;
23 import iotcode.WeatherPhoneGateway.*;
24
25 // Checker annotations
26 import iotchecker.qual.*;
27
28 public class IrrigationController extends UnicastRemoteObject implements WeatherGatewayCallback {
29
30
31         /*******************************************************************************************************************************************
32         **
33         **  Constants
34         **
35         *******************************************************************************************************************************************/
36         // private static final int NUMBER_OF_TIMES_PER_WEEK_TO_WATER = 2;
37         //TODO: Change these back to normal - this is just for testing to make it awake all the time
38 //      private static final int TIME_HOURS_TO_WATER_GRASS = 7;         // 7 am
39         private static final int TIME_HOURS_TO_WATER_GRASS = 3;
40 //      private static final int TIME_MINUTES_TO_WATER_GRASS = 30;      // 30 minutes
41         private static final int TIME_MINUTES_TO_WATER_GRASS = 30;
42 //      private static final int TIME_TO_RECOVER_GRASS_FOR = 8 * 24 * 60 * 60;  // 8 days
43         private static final int TIME_TO_RECOVER_GRASS_FOR = 10;
44 //      private static final int TIME_TO_HIBERNATE_GRASS_FOR = 30 * 24 * 60 * 60;               // 30 days
45         private static final int TIME_TO_HIBERNATE_GRASS_FOR = 10;
46         public static final int CAMERA_FPS = 15;                                                                                                        // In frames per second
47
48
49         /*******************************************************************************************************************************************
50         **
51         **  Variables
52         **
53         *******************************************************************************************************************************************/
54         private int dayOfLastCheck = -1;
55         private int monthOfLastCheck = -1;
56         private boolean isInHibernationRecoveryMode = false;
57         private Date hibernationRecoveryModeStartDate = null;
58         private boolean isHibernationMode = false;
59         private Date hibernationModeStartDate = null;
60         private List<@LocalRemote LawnState> lawns = new ArrayList<@LocalRemote LawnState>();
61         private WeatherGrabber weatherGrabber = null;
62
63         // used to block until gui is done and the settings are ready to be polled
64         private AtomicBoolean waitingForInterface = new AtomicBoolean(true);
65
66         // the settings from the interface, used to setup the system
67         private double inchesPerWeek = 0;
68         private int weatherZipCode = 0;
69         private int daysToWaterOn = 0;
70         private List<Double> inchesPerMinute;
71
72
73         /*******************************************************************************************************************************************
74         **
75         **  IoT Sets and Relations
76         **
77         *******************************************************************************************************************************************/
78         @config private IoTSet<IoTAddress> weatherDataAddresses;
79         @config private IoTSet<IoTAddress> weatherDataAddressMain;
80         @config private IoTSet<@NonLocalRemote WeatherGateway> gwSet;
81         @config private IoTSet<@NonLocalRemote Lawn> lawnSet;
82         @config private IoTSet<@NonLocalRemote MoistureSensor> moistureSensorsSet;
83         @config private IoTSet<@NonLocalRemote Camera> cameraSet;
84         @config private IoTRelation<@NonLocalRemote Lawn, @NonLocalRemote Camera> lawnCameraRelation;
85         @config private IoTRelation<@NonLocalRemote Lawn, @NonLocalRemote Sprinkler> lawnSprinklerRelation;
86         @config private IoTRelation<@NonLocalRemote Lawn, @NonLocalRemote MoistureSensor> lawnMoistureSensorRelation;
87
88
89         public IrrigationController() throws RemoteException {
90
91         }
92
93         /*******************************************************************************************************************************************
94         **
95         **  Public Methods
96         **
97         *******************************************************************************************************************************************/
98
99
100         /** Method to set whether the controller should maintain the lawns in hibernation mode
101          *   or in normal mode.  Lawns should be put in hibernation mode in drought conditions
102          *
103          *   @param _hibMode [boolean] set the hibernation mode for this lawn controllers (true = hibernation)
104          *
105          *   @return [void] None.
106          */
107         public void setHibernationMode(boolean _hibMode) {
108
109                 // change hibernation mode status
110                 isHibernationMode = _hibMode;
111
112                 // set the start date for when we started this hibernation mode
113                 if (_hibMode) {
114
115                         // make sure we dont reset this cycle
116                         if (!isHibernationMode) {
117                                 hibernationModeStartDate = new Date();
118                         }
119                 } else {
120                         // reset all hibernation stuff
121                         hibernationModeStartDate = null;
122                         isInHibernationRecoveryMode = false;
123                         hibernationRecoveryModeStartDate = null;
124                 }
125         }
126
127         /** Method to start the controller and run the main control loop
128          *
129          *   @return [void] None.
130          */
131         public void init() throws RemoteException {
132
133                 // initialize the controller
134                 initController();
135                 System.out.println("Initialized controller!");
136
137                 // Main Loop
138                 while (true) {
139
140                         // get the current time of day (date and time)
141                         Date currentDate = new Date();
142
143                         // get the epoch time till the beginning of the day
144                         Date beginingOfToday = new Date(currentDate.getYear(), currentDate.getMonth(), currentDate.getDate());
145
146                         // calculate the seconds since the start of the day.
147                         long secondsSinceStartOfDay = (currentDate.getTime() - beginingOfToday.getTime()) / 1000;
148
149                         // Seconds since the start of the day to start the watering
150                         long secondsForWateringStart = (TIME_HOURS_TO_WATER_GRASS * 3600) + (TIME_MINUTES_TO_WATER_GRASS * 60);
151
152 //                      System.out.println("beginingOfToday " + beginingOfToday);
153 //                      System.out.println("secondsSinceStartOfDay " + secondsSinceStartOfDay);
154 //                      System.out.println("secondsForWateringStart " + secondsForWateringStart);
155
156                         // check if the current time is within the start watering interval
157                         /*if ((secondsSinceStartOfDay < secondsForWateringStart) || (secondsSinceStartOfDay > (secondsForWateringStart + (60 * 60)))) {
158                                 System.out.println("Sleep for 10 minutes.. ");
159                                 try {
160                                         //Thread.sleep(10 * 60 * 1000);                                         // sleep for 10 minutes
161                                         Thread.sleep(10);                                               // sleep for 10 seconds
162                                 } catch (Exception e) {
163                                         e.printStackTrace();
164                                 }
165
166                                 continue;
167                         }*/
168
169                         // check if we already checked if we should water today
170                         // we only need to do this once per day
171                         /*if ((dayOfLastCheck == currentDate.getDate()) && (monthOfLastCheck == currentDate.getMonth())) {
172                                 System.out.println("Sleep for 1 hour...");
173                                 try {
174                                         Thread.sleep(60 * 60 * 1000);                                           // sleep for an hour
175                                 } catch (Exception e) {
176                                         e.printStackTrace();
177                                 }
178
179                                 continue;
180                         }*/
181
182                         // we decided to check if we should water today so save the fact that we chose to water on this day
183                         dayOfLastCheck = currentDate.getDate();
184                         monthOfLastCheck = currentDate.getMonth();
185
186                         // update the lawn states everyday
187                         for (@LocalRemote LawnState ls : lawns) {
188                                 ls.updateLawn(currentDate);
189                         }
190                         // check if we are in hibernation mode and do the correct loop action
191                         if (isHibernationMode) {
192 //                              System.out.println("Hibernation mode!");
193                                 // If we are in hibernation mode then use the hibernation loop code
194                                 wateringHibernationLoop(currentDate);
195                         } else {
196 //                              System.out.println("Normal mode!");
197                                 // Using the normal watering loop code
198                                 wateringNormalLoop(currentDate);
199                         }
200                 }
201         }
202
203
204         /** Callback method for when the information is retrieved.
205          *
206          * @param _wgw [WeatherGateway].
207          * @return [void] None.
208          */
209         public void informationRetrieved(@NonLocalRemote WeatherGateway _wgw) {
210
211 //              System.out.println("DEBUG: Information is retrieved from phone!!!");
212                 try {
213                         // get the parameters that the interface (phone app) reads from the user
214                         inchesPerWeek = _wgw.getInchesPerWeek();
215                         weatherZipCode = _wgw.getWeatherZipCode();
216                         daysToWaterOn = _wgw.getDaysToWaterOn();
217                         inchesPerMinute.add(_wgw.getInchesPerMinute());
218                 } catch(RemoteException ex) {
219                         ex.printStackTrace();
220                 }
221
222                 // the gui is done so release the spin wait that was waiting for the gui
223                 waitingForInterface.set(false);
224         }
225
226         /*******************************************************************************************************************************************
227         **
228         **  Helper Methods
229         **
230         *******************************************************************************************************************************************/
231
232
233         /** Method to initialize the controller variables and all the drivers and such
234          *
235          *   @return [void] None.
236          */
237         private void initController() throws RemoteException {
238
239                 // Setup the weather grabber object with the correct address of the weather api
240                 Iterator it = weatherDataAddresses.iterator();
241                 weatherGrabber = new WeatherGrabber((IoTAddress)it.next());
242
243                 // Initialize inchesPerMinute
244                 inchesPerMinute = new ArrayList<Double>();
245
246                 // We setup a Gateway object to get information from the phone app
247                 for (@NonLocalRemote WeatherGateway gw : gwSet.values()) {
248                         gw.init();
249                         gw.registerCallback(this);
250                         gw.start();
251                 }
252
253                 System.out.println("DEBUG: Waiting for phone to send weather information");
254                 while (waitingForInterface.get()) {
255                         try {
256                                 Thread.sleep(1000);
257                         } catch (Exception e) {
258                                 e.printStackTrace();
259                         }
260                 }
261                 // TODO: Use a phone input interface later
262                 //inchesPerWeek = 20.00;
263                 //weatherZipCode = 92612;
264                 //daysToWaterOn = 255;
265                 //inchesPerMinute.add(1.50);
266
267                 System.out.println("DEBUG: inchesPerWeek: " + inchesPerWeek);
268                 System.out.println("DEBUG: weatherZipCode: " + weatherZipCode);
269                 System.out.println("DEBUG: daysToWaterOn: " + daysToWaterOn);
270                 System.out.println("DEBUG: inchesPerMinute: " + inchesPerMinute.get(0));
271
272                 // set the zip code and the the number of days of the weather grabber
273                 // here the number of days is set to the max that the grabber supports
274                 weatherGrabber.setZipcode(weatherZipCode);
275                 weatherGrabber.setNumberOfDays(16);
276
277                 // Setup the cameras, start them all and assign each one a motion detector
278                 for (@NonLocalRemote Camera cam : cameraSet.values()) {
279                         try {
280                                 // initialize the camera, might need to setup some stuff internally
281                                 cam.init();
282
283                                 // set the camera parameters.
284                                 cam.setFPS(CAMERA_FPS);
285                                 cam.setResolution(Camera.Resolution.RES_VGA);
286
287                                 // Start the camera (example is start the HTTP stream if it is a network camera)
288                                 cam.start();
289                                 System.out.println("DEBUG: Init camera! " + cam.toString());
290                         } catch (RemoteException e) {
291                                 e.printStackTrace();
292                         }
293
294                 }
295
296                 // counter so that we can match the lawn inches per min data with the specific lawn
297                 int counter = 0;
298                 for (@NonLocalRemote Lawn l : lawnSet.values()) {
299                         // create a motionDetector for each lawn object
300                         @LocalRemote MotionDetection mo = new @LocalRemote MotionDetection(12, 0.5f, 10, 10);
301
302                         // for 1 camera, if there are any then register the camera for that lawn
303                         HashSet<@NonLocalRemote Camera> cameras = lawnCameraRelation.get(l);
304                         System.out.println("DEBUG: Camera.size(): " + cameras.size());
305                         if (cameras.size() >= 1) {
306
307                                 // we only need 1 camera per lawn so get the first one in the list
308                                 Iterator camIt = cameras.iterator();
309                                 @NonLocalRemote Camera cam = (@NonLocalRemote Camera)camIt.next();
310                                 System.out.println("DEBUG: Registering callback to camera: " + cam.toString());
311                                 try {
312                                         // setup the callback
313                                         cam.registerCallback(mo);
314                                 } catch (RemoteException e) {
315                                         e.printStackTrace();
316                                 }
317                         }
318
319                         // we also only need 1 sprinkler controller per lawn so grab the first one
320                         HashSet<@NonLocalRemote Sprinkler> sprinklers = lawnSprinklerRelation.get(l);
321                         Iterator sprinklersIt = sprinklers.iterator();
322                         @NonLocalRemote Sprinkler spr = (@NonLocalRemote Sprinkler)sprinklersIt.next();
323
324                         // init the sprinkler controller, do it here since it only needs to be done once per controller
325                         try {
326                                 spr.init();
327                         } catch (Exception e) {
328                                 e.printStackTrace();
329                         }
330                         System.out.println("DEBUG: Init sprinkler: " + spr.toString());
331
332                         // get and init the moisture sensors for this specific lawn
333                         HashSet<@NonLocalRemote MoistureSensor> sensors = lawnMoistureSensorRelation.get(l);
334                         for (@NonLocalRemote MoistureSensor sen : sensors) {
335                                 System.out.println("DEBUG: Init sensors: " + sen.toString());
336                                 try {
337                                         sen.init();
338                                 } catch (Exception e) {
339                                         e.printStackTrace();
340                                 }
341                         }
342
343                         // create the lawn objects
344                         System.out.println("DEBUG: Creating a LawnState object");
345                         @LocalRemote LawnState ls = 
346                                 new @LocalRemote LawnState(l, daysToWaterOn, mo, inchesPerMinute.get(counter), inchesPerWeek, spr, counter, sensors);
347                         lawns.add(ls);
348
349                         // dont forget to increment the counter
350                         counter++;
351                 }
352         }
353
354         /** Main loop for when the controller is watering the lawns under normal conditions, not in hibernation mode
355          *
356          *   @param _currentDate [Date] current date
357          *
358          *   @return [void] None.
359          */
360         private void wateringNormalLoop(Date _currentDate) {
361
362                 // get the weather data for the next little bit
363                 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
364                 // TODO: Replace this with the WeatherGrabber.getWeatherData() above
365 //              List<DayWeather> weatherData = new ArrayList<DayWeather>();
366
367                 // Go through each lawn and check if we should water it and if we should, water it
368                 for (@LocalRemote LawnState ls : lawns) {
369
370                         // water for specific lawn
371                         waterLawn(ls, _currentDate, weatherData);
372                 }
373         }
374
375         /** Main loop for when the controller is watering the lawns in hibernation mode
376          *
377          *   @param _currentDate [Date] current date
378          *
379          *   @return [void] None.
380          */
381         private void wateringHibernationLoop(Date _currentDate) {
382
383                 // if we are in recovery mode then run the recovery action
384                 // we are still in hibernation mode but we need to recover the grass
385                 if (isInHibernationRecoveryMode) {
386 //                      System.out.println("DEBUG: Recovery mode!");
387                         hibernationRecoveryLoop(_currentDate);
388                         return;
389                 }
390
391                 // check if we should enter recovery mode
392                 long elapsedTime = (_currentDate.getTime() - hibernationModeStartDate.getTime()) / 1000;
393                 if (elapsedTime >= TIME_TO_HIBERNATE_GRASS_FOR) {
394
395                         // start recovery mode
396                         isInHibernationRecoveryMode = true;
397                         hibernationRecoveryModeStartDate = null;
398 //                      System.out.println("DEBUG: We enter recovery mode for the first time!");
399                         // first cycle of recovery
400                         hibernationRecoveryLoop(_currentDate);
401                         return;
402                 }
403
404                 // get the weather data for the next little bit
405                 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
406
407                 // Go through each lawn and check if we should water it and if we should, water it
408                 for (@LocalRemote LawnState ls : lawns) {
409
410                         boolean lawnHasMotion = ls.lawnHasSufficientMotion();
411
412                         // there is no motion on the lawn so no need to water it
413                         if (!lawnHasMotion) {
414                                 continue;
415                         }
416 //                      System.out.println("DEBUG: We water the lawn! (wateringHibernationLoop)");
417                         // water specific lawn since it has motion
418                         waterLawn(ls, _currentDate, weatherData);
419                 }
420         }
421
422
423         /** Main loop for when the controller is watering the lawns in hibernation mode
424          *
425          *   @param _currentDate [Date] current date
426          *
427          *   @return [void] None.
428          */
429         private void hibernationRecoveryLoop(Date _currentDate) {
430
431                 // start recovery mode if it wasnt started yet
432                 if (hibernationRecoveryModeStartDate == null) {
433                         hibernationRecoveryModeStartDate = _currentDate;
434                 }
435
436                 // time since this mode was started
437                 long elapsedTime = (_currentDate.getTime() - hibernationRecoveryModeStartDate.getTime()) / 1000;
438
439                 // we have been in recovery mode long enough
440                 if (elapsedTime >= TIME_TO_RECOVER_GRASS_FOR) {
441
442 //                      System.out.println("DEBUG: We have been in recovery mode long enough!");
443                         // reset the recovery mode
444                         isInHibernationRecoveryMode = false;
445                         hibernationRecoveryModeStartDate = null;
446
447                         // revived grass so restart the grass hibernation cycle
448                         hibernationModeStartDate = _currentDate;
449
450                         // do the hibernation loop since we are no longer in recovery mode
451                         wateringHibernationLoop(_currentDate);
452                         return;
453                 }
454
455
456                 // if we got here then we are trying to recover the grass
457
458                 // get the weather data for the next little bit
459                 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
460
461                 // Go through each lawn and check if we should water it and if we should, water it
462                 for (@LocalRemote LawnState ls : lawns) {
463
464 //                      System.out.println("DEBUG: We water the lawn! (hibernationRecoveryLoop)");
465                         // water specific lawn since it has motion
466                         waterLawn(ls, _currentDate, weatherData);
467                 }
468
469         }
470
471
472         /** Method for watering a specific lawn if it needs to be watered
473          *
474          *   @param _ls [LawnState] lawn to water
475          *   @param _currentDate [Date] current date
476          *   @param _weatherData [List<DayWeather>] latest weather data
477          *
478          *   @return [void] None.
479          */
480         private void waterLawn(@LocalRemote LawnState _ls, Date _currentDate,  List<DayWeather> _weatherData) {
481
482                 // check if today or tomorrow is a wet day
483                 boolean todayIsWetDay = _weatherData.get(0).getIsWetDay();
484                 boolean tomorrowIsWetDay = _weatherData.get(1).getIsWetDay();
485                 // TODO: Remove this later - hack the values for now!!!
486 //              boolean todayIsWetDay = false;
487 //              boolean tomorrowIsWetDay = false;
488
489                 // lawn cannot wait anymore for water so water not
490                 boolean lawnNeedsWaterNow = _ls.needsWateringUrgently(_currentDate);
491                 if (lawnNeedsWaterNow) {
492                         System.out.println("DEBUG: Need water now!!!");
493                         System.out.println("DEBUG: Is wet day? " + todayIsWetDay);
494                         System.out.println("DEBUG: Tomorrow is wet day? " + tomorrowIsWetDay);
495                         // if it is not going to rain today then water the lawn
496                         // TODO: Put this back to uncommented!!! Only for testing!!!
497 //                      if (!todayIsWetDay) {
498                                 _ls.waterLawn(_currentDate);
499 //                      }
500                         return;
501                 }
502
503                 // check if this lawn needs watering based on watering algoritm/sensors/ext
504                 boolean shouldWaterLawn = _ls.needsWatering(_currentDate);
505
506                 // should not water this lawn then just skip to the next lawn
507                 if (!shouldWaterLawn) {
508                         return;
509                 }
510
511                 // it is going to rain soon so wait it out.
512                 // Grass is not in critical condition so it can wait a bit.
513                 if (todayIsWetDay || tomorrowIsWetDay) {
514                         return;
515                 }
516
517                 // if we got here then we need to water the lawn
518                 _ls.waterLawn(_currentDate);
519         }
520 }
521