bab46cd50c61d3d7ce72ef1eca066f2bfb13c848
[iot2.git] / benchmarks / HomeSecurityController / HomeSecurityController.java
1 package HomeSecurityController;
2
3 // Standard Java Packages
4 import java.util.Date;
5 import java.util.Iterator;
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 import java.util.HashSet;
12 import java.util.concurrent.atomic.AtomicBoolean;
13 import java.util.concurrent.ConcurrentHashMap;
14
15 // RMI packages
16 import java.rmi.RemoteException;
17 import java.rmi.server.UnicastRemoteObject;
18
19 // IoT Runtime Packages
20 import iotruntime.slave.IoTSet;
21 import iotruntime.slave.IoTRelation;
22 import iotcode.annotation.*;
23
24 // IoT Driver Packages
25 import iotcode.interfaces.*;
26
27 // Checker annotations
28 //import iotchecker.qual.*;
29
30 /** Class Home Security Controller for the home security application benchmark
31  *
32  * @author      Rahmadi Trimananda <rtrimana @ uci.edu>
33  * @version     1.0
34  * @since       2016-12-14
35  */
36 public class HomeSecurityController implements SmartthingsSensorCallback {
37
38         /*
39          *  Constants
40          */
41         private static final int MOTION_TIME_THRESHOLD = 60;    // in seconds
42         private static final int CAMERA_FPS = 15;
43         private static final int CHECK_TIME_WAIT = 1;                   // in seconds
44         private static final int SECOND_TO_TURN_ON = 60;                // in seconds
45
46         /**
47          *  IoT Sets and Relations
48          *  <p>
49          *  Devices involved:
50          *  1) Multipurpose sensor (detect windows open/close) - Smartthings sensor
51          *  2) Motion sensor (detect motion in certain radius) - Smartthings sensor
52          *  3) Water leak sensor (detect leakage) - Smartthings sensor
53          *  4) Camera (detect motion)
54          *  5) Alarm (using ESP board) - assuming 1 house alarm
55          *  6) Room (object as place of device)
56          *
57          *  Additionals (for more extensive home management)
58          *  7) Doorlock (detect open/locked)
59          *  8) Power outlet (detect on/off, monitor watt)
60          */
61         // This comprises multipurpose, motion, and water leak sensors
62         // TODO: Per 01/2017, doorlock and outlet are not ready, ESP board will be used for alarm
63         @config private IoTSet<SmartthingsSensorSmart> smartSensorsSet;
64         @config private IoTSet<CameraSmart> camSet;
65         @config private IoTSet<AlarmSmart> alarmSet;
66         @config private IoTSet<RoomSmart> roomSet;
67         //@config private IoTSet<DoorLock> doorlockSet;
68         //@config private IoTSet<Outlet> outletSet;
69
70         @config private IoTRelation<RoomSmart, SmartthingsSensorSmart> roomSensorRelation;
71         @config private IoTRelation<RoomSmart, CameraSmart> roomCameraRelation;
72         //@config private IoTRelation<RoomSmart, DoorLock> roomDoorLockRelation;
73         //@config private IoTRelation<RoomSmart, Outlet> roomOutletRelation;
74
75         /*******************************************************************************************************************************************
76         **
77         **  Variables
78         **
79         *******************************************************************************************************************************************/
80         long lastTimeChecked = 0;
81
82         private static int sensorId = 0;
83
84         /*******************************************************************************************************************************************
85         **
86         **  States data structures
87         **
88         *******************************************************************************************************************************************/
89         // Camera and motion detection
90         private Map<CameraSmart, MotionDetection> camMotionDetect =
91                 new HashMap<CameraSmart, MotionDetection>();
92         // Smartthings sensors (true = motion, leakage, etc.)
93         private Map<Integer, Boolean> senDetectStatus =
94                 new ConcurrentHashMap<Integer, Boolean>();
95         // Camera (true = motion)
96         private Map<CameraSmart, Boolean> camDetectStatus =
97                 new HashMap<CameraSmart, Boolean>();
98         // Doorlock (true = open - not locked)
99         //private Map<DoorLock, Boolean> doorlockStatus =
100         //      new HashMap<DoorLock, Boolean>();
101         // Outlet (true = on - outlet is used)
102         //private Map<Outlet, Boolean> outletStatus =
103         //      new HashMap<Outlet, Boolean>();
104
105         // Alarm status
106         private Map<Integer, Boolean> alarmStatus =
107                 new HashMap<Integer, Boolean>();
108
109         public HomeSecurityController() {
110
111         }
112
113
114         /*******************************************************************************************************************************************
115         **
116         **  Helper Methods
117         **
118         *******************************************************************************************************************************************/
119
120
121         /** Method to initialize Smartthings sensors
122          *
123          *   @return [void] None.
124          */
125         private void initSmartthingsSensors(RoomSmart rm) {
126
127                 // Get and init the IAS sensors for this specific room
128                 HashSet<SmartthingsSensorSmart> sensors = roomSensorRelation.get(rm);
129                 for (SmartthingsSensorSmart sen : sensors) {
130         
131                         try {
132                                 // Initialize sensors
133                                 sen.init();
134                                 System.out.println("DEBUG: Initialized smartthings sensor! ID: " + sensorId + " Room ID: " + rm.getRoomID());
135                                 senDetectStatus.put(sensorId, false);
136                                 System.out.println("DEBUG: Initialized sensor detection to false!");
137                                 sen.setId(sensorId++);
138                                 sen.registerCallback(this);
139                                 System.out.println("DEBUG: Registered sensor callback!");
140                         } catch (Exception e) {
141                                 e.printStackTrace();
142                         }
143                 }
144         }
145
146
147         /** Method to initialize cameras
148          *
149          *   @return [void] None.
150          */
151         private void initCameras(RoomSmart rm) {
152
153                 // Get and init the IAS sensors for this specific room
154                 HashSet<CameraSmart> cameras = roomCameraRelation.get(rm);
155                 // Setup the cameras, start them all and assign each one a motion detector
156                 for (CameraSmart cam : cameras) {
157
158                         // Each camera will have a motion detector unique to it since the motion detection has state
159                         MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
160
161                         // initialize the camera, might need to setup some stuff internally
162                         cam.init();
163
164                         // set the camera parameters.
165                         cam.setFPS(CAMERA_FPS);
166                         cam.setResolution(Resolution.RES_VGA);
167
168                         // camera will call the motion detector directly with data not this controller
169                         cam.registerCallback(mo);
170
171                         // Start the camera (example is start the HTTP stream if it is a network camera)
172                         cam.start();
173                         System.out.println("DEBUG: Initialized camera!");
174
175                         // Remember which motion detector is for what camera
176                         camMotionDetect.put(cam, mo);
177
178                         // Initialize detection to false
179                         camDetectStatus.put(cam, false);
180                 }
181         }
182
183
184         /** Method to initialize alarms
185          *
186          *   @return [void] None.
187          */
188         private void initAlarms() {
189
190                 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
191                 Iterator alarmIt = alarmSet.iterator();
192                 AlarmSmart alm = (AlarmSmart) alarmIt.next();
193                 // init the alarm controller, do it here since it only needs to be done once per controller
194                 try {
195                         alm.init();
196                         System.out.println("DEBUG: Initialized alarm!");
197                 } catch (Exception e) {
198                         e.printStackTrace();
199                 }
200         }
201
202
203         /** Method to initialize doorlocks
204          *
205          *   @return [void] None.
206          */
207         private void initDoorLocks(RoomSmart rm) {
208
209                 // Get and init the doorlocks for this specific room
210                 /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
211                 for (DoorLock doorlock : doorlocks) {
212         
213                         try {
214                                 // Initialize doorlocks
215                                 doorlock.init();
216                                 System.out.println("DEBUG: Initialized doorlock!");
217                         } catch (Exception e) {
218                                 e.printStackTrace();
219                         }
220                 }*/
221         }
222
223
224         /** Method to initialize power outlets
225          *
226          *   @return [void] None.
227          */
228         private void initOutlets(RoomSmart rm) {
229
230                 // Get and init the outlets for this specific room
231                 /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
232                 for (Outlet outlet : outlets) {
233         
234                         try {
235                                 // Initialize outlets
236                                 outlet.init();
237                                 System.out.println("DEBUG: Initialized outlet!");
238                         } catch (Exception e) {
239                                 e.printStackTrace();
240                         }
241                 }*/
242         }
243
244
245         /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
246          *   Checks all the motion detectors for the given room
247          *
248          *   @param _room            [RoomSmart] , Room of interest.
249          *   @param _numberOfSeconds [int]  , Number of seconds in the past that we consider recent.
250          *   @param _upperThreshold  [int]  , Number of seconds as an upper bound before we turn off.
251          *
252          *   @return [boolean] None.
253          */
254         private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
255                 long currentTimeSeconds = (new Date()).getTime() / 1000;
256
257                 // Loop through all the cameras in the room
258                 for (CameraSmart cam : roomCameraRelation.get(_room)) {
259                         long lastDetectedMotionSeconds = currentTimeSeconds;
260
261                         Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
262
263                         // Motion was detected at least once
264                         if (motionTime != null) {
265                                 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
266                         } else {
267                                 // motionTime == null means this is the initialization phase
268                                 // so we put false
269                                 return false;
270                         }
271
272                         // Did detect motion recently
273                         if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
274                                 return true;
275                         }
276                 }
277
278                 return false;
279         }
280
281
282         /** Method to update state data structures for Smartthings sensors
283          *
284          *   @return [void] None.
285          */
286         public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
287
288                 System.out.println("DEBUG: Sensor reading value: " + _value);
289                 if(_activeValue) {
290                         System.out.println("DEBUG: Sensor is detecting something: " + _activeValue);
291                         senDetectStatus.put(_sensorId, true);
292
293                 } else {
294                         //System.out.println("DEBUG: Sensor is not detecting something: " + _activeValue);
295                         senDetectStatus.put(_sensorId, false);
296                 } 
297         }
298
299
300         /** Method to update state data structures for doorlocks
301          *
302          *   @return [void] None.
303          */
304         private void updateDoorLockStatus(RoomSmart rm) {
305
306                 // Get and init the outlets for this specific room
307                 /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
308                 for (DoorLock doorlock : doorlocks) {
309         
310                         // Change is detected! Set to true for report...
311                         if(isChangeDetected()) {
312
313                                 doorlockStatus.put(doorlock, true);
314                         } else {
315
316                                 doorlockStatus.put(doorlock, false);
317                         }
318                 }*/
319         }
320
321
322         /** Method to update state data structures for outlets
323          *
324          *   @return [void] None.
325          */
326         private void updateOutletStatus(RoomSmart rm) {
327
328                 // Get and init the outlets for this specific room
329                 /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
330                 for (Outlet outlet : outlets) {
331         
332                         // Change is detected! Set to true for report...
333                         if(isChangeDetected()) {
334
335                                 outletStatus.put(outlet, true);
336                         } else {
337
338                                 outletStatus.put(outlet, false);
339                         }
340                 }*/
341         }
342
343
344         /** Update the status of all devices
345          *
346          *   @return [void] None.
347          */
348         private void updateUniversalStatus() {
349
350                 // Check for motion in rooms and if there is motion then report
351                 for (RoomSmart room : roomSet.values()) {
352
353                         // Update status of camera
354                         updateCameraStatus(room);
355
356                         // Update status of doorlocks
357                         //updateDoorLockStatus(room);
358
359                         // Update status of outlets
360                         //updateOutletStatus(room);
361                 }
362         }
363
364
365         /** Update the status of all devices
366          *
367          *   @return [void] None.
368          */
369         private void updateCameraStatus(RoomSmart room) {
370
371                 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
372                 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
373
374                         // Motion was detected
375                         System.out.println("DEBUG: Camera detected something!");
376                         for(CameraSmart cam : cameras)
377                                 camDetectStatus.put(cam, true);
378                 } else {
379
380                         // No motion was detected
381                         //System.out.println("DEBUG: Camera didn't detect anything!");
382                         for(CameraSmart cam : cameras)
383                                 camDetectStatus.put(cam, false);
384                 }
385         }
386
387         /** Method to turn on alarms
388          *
389          *   @return [void] None.
390          */
391         private void turnOnAlarms(int zoneId) {
392
393                 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
394                 Iterator alarmIt = alarmSet.iterator();
395                 AlarmSmart alm = (AlarmSmart) alarmIt.next();
396                 alm.setZone(zoneId, true, SECOND_TO_TURN_ON);
397         }
398
399
400         /** Method to turn off alarms
401          *
402          *   @return [void] None.
403          */
404         private void turnOffAlarms(int zoneId) {
405
406                 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
407                 Iterator alarmIt = alarmSet.iterator();
408                 AlarmSmart alm = (AlarmSmart) alarmIt.next();
409                 // Turn this alarm off indefinitely
410                 alm.setZone(zoneId, false, -1);
411         }
412
413
414         /** Check status of devices and turn on alarm accordingly
415          *  <p>
416          *  Simple rule is whenever any sensor or camera detect something unusual
417          *      (sensor/camera becomes active) then we sound the corresponding alarm.
418          *  This means we send the signal to the right zone in the EspAlarm
419          *
420          *   @return [void] None.
421          */
422         private void decideToTurnOnAlarm() {
423
424                 int zoneId = 0;
425
426                 // Check for motion in rooms and if there is motion then report
427                 for (RoomSmart room : roomSet.values()) {
428
429                         // Loop through all the cameras in the room
430                         for (CameraSmart cam : roomCameraRelation.get(room)) {
431
432                                 // Get the right camera and the right detection status (true or false)
433                                 if (camDetectStatus.get(cam)) {
434                                         zoneId = room.getRoomID();
435                                         turnOnAlarms(zoneId);
436                                         System.out.println("DETECTION: Camera active in room: " + zoneId);
437                                 }
438                         }
439
440                         // Loop through all the cameras in the room
441                         for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
442
443                                 // Get the right sensor and the right detection status (true or false)
444                                 //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId());
445                                 if (senDetectStatus.get(sensor.getId())) {
446                                         zoneId = room.getRoomID();
447                                         turnOnAlarms(zoneId);
448                                         System.out.println("DETECTION: Sensor active in room: " + zoneId);
449                                         System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
450                                 }
451                         }
452                 }
453         }
454
455
456         /** Check status of devices and turn off alarm accordingly
457          *  <p>
458          *  If everything (camera and sensors) is set back to normal
459          *  then the system will turn off the alarm
460          *
461          *   @return [void] None.
462          */
463         // TODO: Need to fix this part later
464         // TODO: Perhaps we should use a phone app to turn off the alarm
465         /*private void decideToTurnOffAlarm() {
466
467                 // Check for motion in rooms and if there is motion then report
468                 for (RoomSmart room : roomSet.values()) {
469
470                         int zoneId = room.getRoomID();
471                         // Loop through all the cameras in the room
472                         for (CameraSmart cam : roomCameraRelation.get(room)) {
473
474                                 // Get the right camera and the right detection status (true or false)
475                                 if (camDetectStatus.get(cam))   // Camera still active so alarm is still on, so false for off-alarm status
476
477                                         alarmStatus.put(zoneId, false);
478
479                                 else
480
481                                         alarmStatus.put(zoneId, true);
482                                 
483                         }
484
485                         // Loop through all the cameras in the room
486                         for (SmartthingsSensor sensor : roomSensorRelation.get(room)) {
487
488                                 // Get the right sensor and the right detection status (true or false)
489                                 if (senDetectStatus.get(sensor.getId()))        // Sensor still active so alarm is still on, so false for off-alarm status
490
491                                         alarmStatus.put(zoneId, false);
492
493                                 else
494
495                                         alarmStatus.put(zoneId, true);
496                         }
497                 }
498
499                 // Turn on alarm in the right zone
500                 for (Map.Entry<Integer, Boolean> alEnt : alarmStatus.entrySet()) {
501                         if (alEnt.getValue())   // if this zone is true, that means we need to turn off its alarm
502                                 turnOffAlarms(alEnt.getKey());
503                 }
504         }*/
505
506
507         /*******************************************************************************************************************************************
508         **
509         **  Public Methods
510         **
511         *******************************************************************************************************************************************/
512
513         /** Initialization method, called by the runtime (effectively the main of the controller)
514          *   This method runs a continuous loop and is blocking
515          *
516          *   @return [void] None;
517          */
518         public void init() {
519
520                 // Iterate over the set of rooms
521                 for (RoomSmart rm : roomSet.values()) {
522
523                         // Init all Smartthings sensors
524                         initSmartthingsSensors(rm);
525
526                         // Init all cameras
527                         initCameras(rm);
528
529                         // Init all doorlocks
530                         //initDoorLocks();
531
532                         // Init all outlets
533                         //initOutlets();
534                 }
535
536                 // Init all alarms
537                 initAlarms();
538
539                 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
540
541                 // Run the main loop that will keep checking the sensors and cameras periodically
542                 while (true) {
543
544                         // Run this code every <specified time>
545                         long currentTimeSeconds = (new Date()).getTime() / 1000;
546                         if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
547                                 lastTimeChecked = currentTimeSeconds;
548
549                                 // Update the status of all devices
550                                 updateUniversalStatus();
551
552                                 // Decide to turn on alarm if any of the sensor/camera detects something unusual
553                                 decideToTurnOnAlarm();
554
555                                 // Decide to turn off alarm if every sensor/camera goes back to normal
556                                 //decideToTurnOffAlarm();
557
558                         } else {
559
560                                 try {
561
562                                         Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
563
564                                 } catch (Exception e) {
565
566                                         e.printStackTrace();
567                                 }
568                         }
569
570                 }
571         }
572 }
573
574