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