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