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