Adding config file for sharing.
[iot2.git] / benchmarks / Java / IrrigationController / test_version / 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
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                 }
297
298                 // counter so that we can match the lawn inches per min data with the specific lawn
299                 int counter = 0;
300                 for (LawnSmart l : lawnSet.values()) {
301                         // create a motionDetector for each lawn object
302                         MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
303
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) {
308
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());
313                                 //try {
314                                         // setup the callback
315                                         cam.registerCallback(mo);
316                                 //} catch (RemoteException e) {
317                                 //      e.printStackTrace();
318                                 //}
319                         }
320
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();
325
326                         // init the sprinkler controller, do it here since it only needs to be done once per controller
327                         try {
328                                 spr.init();
329                                 // Wait until sprinkler is active
330                                 Thread.sleep(30000);
331                         } catch (Exception e) {
332                                 e.printStackTrace();
333                         }
334                         System.out.println("DEBUG: Init sprinkler: " + spr.toString());
335
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());
340                                 try {
341                                         sen.init();
342                                         sen.setId(sensorId++);
343                                 } catch (Exception e) {
344                                         e.printStackTrace();
345                                 }
346                         }
347
348                         // create the lawn objects
349                         System.out.println("DEBUG: Creating a LawnState object");
350                         LawnState ls = 
351                                 new LawnState(l, daysToWaterOn, mo, inchesPerMinute.get(counter), inchesPerWeek, spr, counter, sensors);
352                         lawns.add(ls);
353
354                         // dont forget to increment the counter
355                         counter++;
356                 }
357         }
358
359         /** Main loop for when the controller is watering the lawns under normal conditions, not in hibernation mode
360          *
361          *   @param _currentDate [Date] current date
362          *
363          *   @return [void] None.
364          */
365         private void wateringNormalLoop(Date _currentDate) {
366
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>();
371
372                 // Go through each lawn and check if we should water it and if we should, water it
373                 for (LawnState ls : lawns) {
374
375                         // water for specific lawn
376                         waterLawn(ls, _currentDate, weatherData);
377                 }
378         }
379
380         /** Main loop for when the controller is watering the lawns in hibernation mode
381          *
382          *   @param _currentDate [Date] current date
383          *
384          *   @return [void] None.
385          */
386         private void wateringHibernationLoop(Date _currentDate) {
387
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);
393                         return;
394                 }
395
396                 // check if we should enter recovery mode
397                 long elapsedTime = (_currentDate.getTime() - hibernationModeStartDate.getTime()) / 1000;
398                 if (elapsedTime >= TIME_TO_HIBERNATE_GRASS_FOR) {
399
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);
406                         return;
407                 }
408
409                 // get the weather data for the next little bit
410                 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
411
412                 // Go through each lawn and check if we should water it and if we should, water it
413                 for (LawnState ls : lawns) {
414
415                         boolean lawnHasMotion = ls.lawnHasSufficientMotion();
416
417                         // there is no motion on the lawn so no need to water it
418                         if (!lawnHasMotion) {
419                                 continue;
420                         }
421 //                      System.out.println("DEBUG: We water the lawn! (wateringHibernationLoop)");
422                         // water specific lawn since it has motion
423                         waterLawn(ls, _currentDate, weatherData);
424                 }
425         }
426
427
428         /** Main loop for when the controller is watering the lawns in hibernation mode
429          *
430          *   @param _currentDate [Date] current date
431          *
432          *   @return [void] None.
433          */
434         private void hibernationRecoveryLoop(Date _currentDate) {
435
436                 // start recovery mode if it wasnt started yet
437                 if (hibernationRecoveryModeStartDate == null) {
438                         hibernationRecoveryModeStartDate = _currentDate;
439                 }
440
441                 // time since this mode was started
442                 long elapsedTime = (_currentDate.getTime() - hibernationRecoveryModeStartDate.getTime()) / 1000;
443
444                 // we have been in recovery mode long enough
445                 if (elapsedTime >= TIME_TO_RECOVER_GRASS_FOR) {
446
447 //                      System.out.println("DEBUG: We have been in recovery mode long enough!");
448                         // reset the recovery mode
449                         isInHibernationRecoveryMode = false;
450                         hibernationRecoveryModeStartDate = null;
451
452                         // revived grass so restart the grass hibernation cycle
453                         hibernationModeStartDate = _currentDate;
454
455                         // do the hibernation loop since we are no longer in recovery mode
456                         wateringHibernationLoop(_currentDate);
457                         return;
458                 }
459
460
461                 // if we got here then we are trying to recover the grass
462
463                 // get the weather data for the next little bit
464                 List<DayWeather> weatherData = weatherGrabber.getWeatherData();
465
466                 // Go through each lawn and check if we should water it and if we should, water it
467                 for (LawnState ls : lawns) {
468
469 //                      System.out.println("DEBUG: We water the lawn! (hibernationRecoveryLoop)");
470                         // water specific lawn since it has motion
471                         waterLawn(ls, _currentDate, weatherData);
472                 }
473
474         }
475
476
477         /** Method for watering a specific lawn if it needs to be watered
478          *
479          *   @param _ls [LawnState] lawn to water
480          *   @param _currentDate [Date] current date
481          *   @param _weatherData [List<DayWeather>] latest weather data
482          *
483          *   @return [void] None.
484          */
485         private void waterLawn(LawnState _ls, Date _currentDate,  List<DayWeather> _weatherData) {
486
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;
493
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);
504 //                      }
505                         return;
506                 }
507
508                 // check if this lawn needs watering based on watering algoritm/sensors/ext
509                 boolean shouldWaterLawn = _ls.needsWatering(_currentDate);
510
511                 // should not water this lawn then just skip to the next lawn
512                 if (!shouldWaterLawn) {
513                         return;
514                 }
515
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) {
519                         return;
520                 }
521
522                 // if we got here then we need to water the lawn
523                 _ls.waterLawn(_currentDate);
524         }
525 }
526