4bcec6248df9c38bfd1364bf8534a5b6976ba414
[iot2.git] / benchmarks / Java / 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 public class IrrigationController extends UnicastRemoteObject implements WeatherGatewayCallback {
26
27
28         /*******************************************************************************************************************************************
29         **
30         **  Constants
31         **
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
44
45
46         /*******************************************************************************************************************************************
47         **
48         **  Variables
49         **
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;
59
60         // used to block until gui is done and the settings are ready to be polled
61         private AtomicBoolean waitingForInterface = new AtomicBoolean(true);
62
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;
68
69         private static int sensorId = 0;
70
71         /*******************************************************************************************************************************************
72         **
73         **  IoT Sets and Relations
74         **
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;
85
86
87         public IrrigationController() throws RemoteException {
88
89         }
90
91         /*******************************************************************************************************************************************
92         **
93         **  Public Methods
94         **
95         *******************************************************************************************************************************************/
96
97
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
100          *
101          *   @param _hibMode [boolean] set the hibernation mode for this lawn controllers (true = hibernation)
102          *
103          *   @return [void] None.
104          */
105         public void setHibernationMode(boolean _hibMode) {
106
107                 // change hibernation mode status
108                 isHibernationMode = _hibMode;
109
110                 // set the start date for when we started this hibernation mode
111                 if (_hibMode) {
112
113                         // make sure we dont reset this cycle
114                         if (!isHibernationMode) {
115                                 hibernationModeStartDate = new Date();
116                         }
117                 } else {
118                         // reset all hibernation stuff
119                         hibernationModeStartDate = null;
120                         isInHibernationRecoveryMode = false;
121                         hibernationRecoveryModeStartDate = null;
122                 }
123         }
124
125         /** Method to start the controller and run the main control loop
126          *
127          *   @return [void] None.
128          */
129         public void init() throws RemoteException {
130
131                 // initialize the controller
132                 initController();
133                 System.out.println("Initialized controller!");
134
135                 // Main Loop
136                 while (true) {
137
138                         // get the current time of day (date and time)
139                         Date currentDate = new Date();
140
141                         // get the epoch time till the beginning of the day
142                         Date beginingOfToday = new Date(currentDate.getYear(), currentDate.getMonth(), currentDate.getDate());
143
144                         // calculate the seconds since the start of the day.
145                         long secondsSinceStartOfDay = (currentDate.getTime() - beginingOfToday.getTime()) / 1000;
146
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);
149
150 //                      System.out.println("beginingOfToday " + beginingOfToday);
151 //                      System.out.println("secondsSinceStartOfDay " + secondsSinceStartOfDay);
152 //                      System.out.println("secondsForWateringStart " + secondsForWateringStart);
153
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.. ");
157                                 try {
158                                         //Thread.sleep(10 * 60 * 1000);                                         // sleep for 10 minutes
159                                         Thread.sleep(10);                                               // sleep for 10 seconds
160                                 } catch (Exception e) {
161                                         e.printStackTrace();
162                                 }
163
164                                 continue;
165                         }*/
166
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...");
171                                 try {
172                                         Thread.sleep(60 * 60 * 1000);                                           // sleep for an hour
173                                 } catch (Exception e) {
174                                         e.printStackTrace();
175                                 }
176
177                                 continue;
178                         }*/
179
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();
183
184                         // update the lawn states everyday
185                         for (LawnState ls : lawns) {
186                                 ls.updateLawn(currentDate);
187                         }
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);
193                         } else {
194 //                              System.out.println("Normal mode!");
195                                 // Using the normal watering loop code
196                                 wateringNormalLoop(currentDate);
197                         }
198                 }
199         }
200
201
202         /** Callback method for when the information is retrieved.
203          *
204          * @param _inchesPerWeek [double].
205          * @param _weatherZipCode [int].
206          * @param _daysToWaterOn [int].
207          * @param _inchesPerMinute [double].
208          * @return [void] None.
209          */
210         public void informationRetrieved(double _inchesPerWeek, int _weatherZipCode, int _daysToWaterOn, double _inchesPerMinute) {
211
212                 System.out.println("DEBUG: Information is retrieved from phone!!!");
213                 /*try {
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();
221                 }*/
222
223                 inchesPerWeek = _inchesPerWeek;
224                 weatherZipCode = _weatherZipCode;
225                 daysToWaterOn = _daysToWaterOn;
226                 inchesPerMinute.add(_inchesPerMinute);
227
228                 // the gui is done so release the spin wait that was waiting for the gui
229                 waitingForInterface.set(false);
230         }
231
232         /*******************************************************************************************************************************************
233         **
234         **  Helper Methods
235         **
236         *******************************************************************************************************************************************/
237
238
239         /** Method to initialize the controller variables and all the drivers and such
240          *
241          *   @return [void] None.
242          */
243         private void initController() throws RemoteException {
244
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());
248
249                 // Initialize inchesPerMinute
250                 inchesPerMinute = new ArrayList<Double>();
251
252                 // We setup a Gateway object to get information from the phone app
253                 for (WeatherGatewaySmart gw : gwSet.values()) {
254                         gw.init();
255                         gw.registerCallback(this);
256                         gw.start();
257                 }
258
259                 System.out.println("DEBUG: Waiting for phone to send weather information");
260                 //while (waitingForInterface.get()) {
261                 //      try {
262                 //              Thread.sleep(1000);
263                 //      } catch (Exception e) {
264                 //              e.printStackTrace();
265                 //      }
266                 //}
267                 // TODO: Use a phone input interface later
268                 inchesPerWeek = 20.00;
269                 weatherZipCode = 92612;
270                 daysToWaterOn = 255;
271                 inchesPerMinute.add(1.50);
272
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));
277
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);
282
283                 // Setup the cameras, start them all and assign each one a motion detector
284                 for (CameraSmart cam : cameraSet.values()) {
285                         //try {
286                                 // initialize the camera, might need to setup some stuff internally
287                                 cam.init();
288
289                                 // set the camera parameters.
290                                 cam.setFPS(CAMERA_FPS);
291                                 cam.setResolution(Resolution.RES_VGA);
292
293                                 // Start the camera (example is start the HTTP stream if it is a network camera)
294                                 cam.start();
295                                 System.out.println("DEBUG: Init camera! " + cam.toString());
296                         //} catch (RemoteException e) {
297                         //      e.printStackTrace();
298                         //}
299
300                 }
301
302                 // counter so that we can match the lawn inches per min data with the specific lawn
303                 int counter = 0;
304                 for (LawnSmart l : lawnSet.values()) {
305                         // create a motionDetector for each lawn object
306                         MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
307
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) {
312
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());
317                                 //try {
318                                         // setup the callback
319                                         cam.registerCallback(mo);
320                                 //} catch (RemoteException e) {
321                                 //      e.printStackTrace();
322                                 //}
323                         }
324
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();
329
330                         // init the sprinkler controller, do it here since it only needs to be done once per controller
331                         try {
332                                 spr.init();
333                                 // Wait until sprinkler is active
334                                 Thread.sleep(30000);
335                         } catch (Exception e) {
336                                 e.printStackTrace();
337                         }
338                         System.out.println("DEBUG: Init sprinkler: " + spr.toString());
339
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());
344                                 try {
345                                         sen.init();
346                                         sen.setId(sensorId++);
347                                 } catch (Exception e) {
348                                         e.printStackTrace();
349                                 }
350                         }
351
352                         // create the lawn objects
353                         System.out.println("DEBUG: Creating a LawnState object");
354                         LawnState ls = 
355                                 new LawnState(l, daysToWaterOn, mo, inchesPerMinute.get(counter), inchesPerWeek, spr, counter, sensors);
356                         lawns.add(ls);
357
358                         // dont forget to increment the counter
359                         counter++;
360                 }
361         }
362
363         /** Main loop for when the controller is watering the lawns under normal conditions, not in hibernation mode
364          *
365          *   @param _currentDate [Date] current date
366          *
367          *   @return [void] None.
368          */
369         private void wateringNormalLoop(Date _currentDate) {
370
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>();
375
376                 // Go through each lawn and check if we should water it and if we should, water it
377                 for (LawnState ls : lawns) {
378
379                         // water for specific lawn
380                         waterLawn(ls, _currentDate, weatherData);
381                 }
382         }
383
384         /** Main loop for when the controller is watering the lawns in hibernation mode
385          *
386          *   @param _currentDate [Date] current date
387          *
388          *   @return [void] None.
389          */
390         private void wateringHibernationLoop(Date _currentDate) {
391
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);
397                         return;
398                 }
399
400                 // check if we should enter recovery mode
401                 long elapsedTime = (_currentDate.getTime() - hibernationModeStartDate.getTime()) / 1000;
402                 if (elapsedTime >= TIME_TO_HIBERNATE_GRASS_FOR) {
403
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);
410                         return;
411                 }
412
413                 // get the weather data for the next little bit
414                 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
415
416                 // Go through each lawn and check if we should water it and if we should, water it
417                 for (LawnState ls : lawns) {
418
419                         boolean lawnHasMotion = ls.lawnHasSufficientMotion();
420
421                         // there is no motion on the lawn so no need to water it
422                         if (!lawnHasMotion) {
423                                 continue;
424                         }
425 //                      System.out.println("DEBUG: We water the lawn! (wateringHibernationLoop)");
426                         // water specific lawn since it has motion
427                         waterLawn(ls, _currentDate, weatherData);
428                 }
429         }
430
431
432         /** Main loop for when the controller is watering the lawns in hibernation mode
433          *
434          *   @param _currentDate [Date] current date
435          *
436          *   @return [void] None.
437          */
438         private void hibernationRecoveryLoop(Date _currentDate) {
439
440                 // start recovery mode if it wasnt started yet
441                 if (hibernationRecoveryModeStartDate == null) {
442                         hibernationRecoveryModeStartDate = _currentDate;
443                 }
444
445                 // time since this mode was started
446                 long elapsedTime = (_currentDate.getTime() - hibernationRecoveryModeStartDate.getTime()) / 1000;
447
448                 // we have been in recovery mode long enough
449                 if (elapsedTime >= TIME_TO_RECOVER_GRASS_FOR) {
450
451 //                      System.out.println("DEBUG: We have been in recovery mode long enough!");
452                         // reset the recovery mode
453                         isInHibernationRecoveryMode = false;
454                         hibernationRecoveryModeStartDate = null;
455
456                         // revived grass so restart the grass hibernation cycle
457                         hibernationModeStartDate = _currentDate;
458
459                         // do the hibernation loop since we are no longer in recovery mode
460                         wateringHibernationLoop(_currentDate);
461                         return;
462                 }
463
464
465                 // if we got here then we are trying to recover the grass
466
467                 // get the weather data for the next little bit
468                 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
469
470                 // Go through each lawn and check if we should water it and if we should, water it
471                 for (LawnState ls : lawns) {
472
473 //                      System.out.println("DEBUG: We water the lawn! (hibernationRecoveryLoop)");
474                         // water specific lawn since it has motion
475                         waterLawn(ls, _currentDate, weatherData);
476                 }
477
478         }
479
480
481         /** Method for watering a specific lawn if it needs to be watered
482          *
483          *   @param _ls [LawnState] lawn to water
484          *   @param _currentDate [Date] current date
485          *   @param _weatherData [List<DayWeather>] latest weather data
486          *
487          *   @return [void] None.
488          */
489         private void waterLawn(LawnState _ls, Date _currentDate,  List<DayWeather> _weatherData) {
490
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;
497
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);
508 //                      }
509                         return;
510                 }
511
512                 // check if this lawn needs watering based on watering algoritm/sensors/ext
513                 boolean shouldWaterLawn = _ls.needsWatering(_currentDate);
514
515                 // should not water this lawn then just skip to the next lawn
516                 if (!shouldWaterLawn) {
517                         return;
518                 }
519
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) {
523                         return;
524                 }
525
526                 // if we got here then we need to water the lawn
527                 _ls.waterLawn(_currentDate);
528         }
529 }
530