Adjusting back into the original lengthy version of IrrigationController; for testing...
[iot2.git] / benchmarks / Java / SpeakerController / SpeakerController.java
1 package SpeakerController;
2
3 // IoT Runtime packages
4 import iotruntime.slave.IoTSet;
5 import iotruntime.slave.IoTRelation;
6 //import iotcode.annotation.*;
7
8 // IoT driver packages
9 import iotcode.interfaces.*;
10 import iotcode.annotation.*;
11
12
13 // Standard Java packages
14 import java.util.HashMap;
15 import java.util.Map;
16 import java.util.List;
17 import java.util.ArrayList;
18 import java.io.File;
19 import java.util.Date;  // TODO: Get rid of all depreciated stuff for date, switch to Calender
20 import java.util.concurrent.Semaphore;
21 import java.util.concurrent.atomic.AtomicBoolean;
22 import java.util.Random;
23
24 // RMI packages
25 import java.rmi.RemoteException;
26 import java.rmi.server.UnicastRemoteObject;
27
28 // Checker annotations
29 //import iotchecker.qual.*;
30
31 /** Class SpeakerController for the smart home application benchmark
32  *  <p>
33  *  This controller controls speakers and streams music based on
34  *  GPS-like input from a phone app that notifies the controller
35  *  about the position of the person
36  *
37  * @author      Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
38  * @version     1.0
39  * @since       2016-04-28
40  */
41 public class SpeakerController extends UnicastRemoteObject implements GPSGatewayCallback, SpeakerCallback {
42
43         /*
44          *  Constants
45          */
46         public static final int CHECK_TIME_WAIT_MSEC = 100;     // 1 means 1 x 100 = 0.1 second
47         public static final String MUSIC_FILE_DIRECTORY = "./music/";   // file that the music files are in
48         public static final int CURRENT_POSITION_SAMPLE_THRESHOLD = (int)((long)((long)3 * (long)CHECK_TIME_WAIT_MSEC * (long)44100) / (long)1000);
49
50         /**
51          * IoT Sets of Devices
52          */
53         @config private IoTSet < GPSGatewaySmart > gpsSet;
54         @config private IoTSet < SpeakerSmart > speakerSet;
55
56         /**
57          * IoT Sets of Things that are not devices such as rooms
58          */
59         @config private IoTSet < RoomSmart > audioRooms;
60
61         /**
62          * IoT Relations
63          */
64         @config private IoTRelation < RoomSmart, SpeakerSmart > roomSpeakerRel;
65
66         /**
67          * The state that the room main lights are supposed to be in
68          */
69         Map < RoomSmart, Boolean > roomSpeakersOnOffStatus = new HashMap < RoomSmart, Boolean > ();
70
71         // used to notify if new data is available
72         private AtomicBoolean newDataAvailable = new AtomicBoolean(false);
73
74         // the settings from the interface, used to setup the system
75         private int roomIdentifier = 0;
76         private boolean ringStatus = false;
77
78         // playback state variables
79         private int currentPosition = 0;
80         private AtomicBoolean playbackDone = new AtomicBoolean(false);
81
82
83         public SpeakerController() throws RemoteException {
84
85         }
86
87
88         /** Callback method for when room ID is retrieved.
89          *
90          * @param _roomIdentifier [int].
91          * @return [void] None.
92          */
93         public void newRoomIDRetrieved(int _roomIdentifier) {
94
95                 roomIdentifier = _roomIdentifier;
96                 System.out.println("DEBUG: New room ID is retrieved from phone!!! Room: " + roomIdentifier);
97
98                 // new data available so set it to true
99                 newDataAvailable.set(true);
100         }
101
102
103         /** Callback method for when ring status is retrieved.
104          *
105          * @param _ringStatus [boolean].
106          * @return [void] None.
107          */
108         public void newRingStatusRetrieved(boolean _ringStatus) {
109
110                 ringStatus = _ringStatus;
111                 System.out.println("DEBUG: New ring status is retrieved from phone!!! Status: " + ringStatus);
112
113                 // new data available so set it to true
114                 newDataAvailable.set(true);
115         }
116
117
118         /** Callback method when a speaker has finished playing what is in its audio buffer.
119          *
120          * @return [void] None.
121          */
122         public void speakerDone() {
123                 for (SpeakerSmart speakers : speakerSet.values()) {
124                         playbackDone.set(true);
125                 }
126         }
127
128
129         /*******************************************************************************************************************************************
130         **
131         ** Private Helper Methods
132         **
133         *******************************************************************************************************************************************/
134
135         /**
136          * Update speakers action based on the updated speakers status
137          */
138         private void updateSpeakersAction() {
139
140                 // Stream music on speakers based on their status
141                 for (RoomSmart room : audioRooms.values()) {
142
143                         // Check status of the room
144                         if (roomSpeakersOnOffStatus.get(room)) {
145
146                                 // used to get the average of the speakers position
147                                 long currPosTotal = 0;
148                                 long currPosCount = 0;
149
150                                 // Get the speaker objects one by one assuming that we could have
151                                 // more than one speaker per room
152                                 for (SpeakerSmart speakers : roomSpeakerRel.get(room)) {
153                                         // System.out.println("DEBUG: Turn on speaker!");
154
155                                         //try {
156
157                                                 // start the speaker playback if the speaker is not playing yet
158                                                 if (!speakers.getPlaybackState()) {
159
160                                                         System.out.println("Turning a speaker On in room: " + room.getRoomID());
161                                                         speakers.startPlayback();
162                                                         speakers.setPosition(currentPosition);
163
164                                                 } else {
165                                                         // get average of the positions
166                                                         currPosTotal += speakers.getPosition();
167                                                         currPosCount++;
168                                                 }
169
170
171                                         //} catch (RemoteException e) {
172                                         //      e.printStackTrace();
173                                         //}
174                                 }
175
176                                 if (currPosCount != 0) {
177                                         
178                                         // get average position of the speakers
179                                         int currentPosOfSpeakers = (int)(currPosTotal / currPosCount);
180
181                                         // check how close we are to the correct position
182                                         if (Math.abs(currentPosOfSpeakers - currentPosition) > CURRENT_POSITION_SAMPLE_THRESHOLD) {
183                                                 // we were kind of far so update all the positions
184
185                                                 for (SpeakerSmart speakers : roomSpeakerRel.get(room)) {
186                                                         //try {
187                                                                 speakers.setPosition(currentPosOfSpeakers);
188                                                         //} catch (RemoteException e) {
189                                                         //      e.printStackTrace();
190                                                         //}
191                                                 }
192                                         }
193
194                                         // update the current position
195                                         currentPosition = currentPosOfSpeakers;
196                                 }
197
198
199
200                         } /*else {
201                                 // Room status is "off"
202
203                                 // used to get the average of the speakers position
204                                 long currPosTotal = 0;
205                                 long currPosCount = 0;
206
207                                 // Get the speaker objects one by one assuming that we could have
208                                 // more than one speaker per room
209                                 for (Speaker speakers : roomSpeakerRel.get(room)) {
210                                         // System.out.println("DEBUG: Turn off speaker!");
211                                         try {
212                                                 // Turning off speaker if they are still on
213                                                 if (speakers.getPlaybackState()) {
214                                                         System.out.println("Turning a speaker off");
215
216                                                         currPosTotal += (long)speakers.getPosition();
217                                                         currPosCount++;
218                                                         boolean tmp = speakers.stopPlayback();
219
220                                                         if (!tmp) {
221                                                                 System.out.println("Error Turning off");
222                                                         }
223                                                 }
224
225                                         } catch (RemoteException e) {
226                                                 e.printStackTrace();
227                                         }
228                                 }
229
230                                 // get the average current position of the speakers
231                                 // so we can resume other speakers from same position
232                                 if (currPosCount != 0) {
233                                         currentPosition = (int)(currPosTotal / currPosCount);
234                                 }
235                         }*/
236                 }
237
238                 // a speaker has finished playing and so we should change all the audio buffers
239                 if (playbackDone.get()) {
240
241                         // song done so update the audio buffers
242                         prepareNextSong();
243                         playbackDone.set(false);
244                 }
245         }
246
247
248         /**
249          * Update speakers status based on room ID and ring status information from phone
250          */
251         private void updateSpeakersStatus() {
252
253                 // If we have new data, we update the speaker streaming
254                 if (newDataAvailable.get()) {
255
256                         // System.out.println("DEBUG: New data is available!!!");
257                         // System.out.println("DEBUG: Ring status: " + ringStatus);
258                         // Check for ring status first
259                         if (ringStatus) {
260
261                                 // Turn off all speakers if ring status is true (phone is ringing)
262                                 for (RoomSmart room : audioRooms.values()) {
263
264                                         // System.out.println("DEBUG: Update status off for speakers! Phone is ringing!!!");
265                                         // Turn off speaker
266                                         roomSpeakersOnOffStatus.put(room, false);
267                                 }
268
269                         } else {
270                                 // Phone is not ringing... just play music on the right speaker
271
272                                 // Check for every room
273                                 for (RoomSmart room : audioRooms.values()) {
274
275                                         //try {
276                                                 // Turn on the right speaker based on room ID sent from phone app
277                                                 // Stream audio to a speaker based on room ID
278                                                 if (room.getRoomID() == roomIdentifier) {
279
280                                                         // System.out.println("DEBUG: This room ID: " + room.getRoomID());
281                                                         // System.out.println("DEBUG: Turn on the speaker(s) in this room!!!");
282                                                         // Set speaker status to on
283                                                         roomSpeakersOnOffStatus.put(room, true);
284
285                                                 } else {
286                                                         // for the rooms whose IDs aren't equal to roomIdentifier
287
288                                                         // System.out.println("DEBUG: Turn on speaker!");
289                                                         // Set speaker status to off
290                                                         roomSpeakersOnOffStatus.put(room, false);
291                                                 }
292
293                                         //} catch (RemoteException ex) {
294                                         //      ex.printStackTrace();
295                                         //}
296                                 }
297                         }
298                         // Finish processing data - put this back to false
299                         newDataAvailable.set(false);
300                 }
301         }
302
303         /**
304          * Prepare the speakers for a new song to start playing
305          */
306         private void prepareNextSong() {
307                 System.out.println("Starting Music Prep");
308
309                 System.out.println("Stopping all device playback");
310                 // stop all devices that are still playing and clear their buffers
311                 // they are about to end playback anyways
312                 for (SpeakerSmart speakers : speakerSet.values()) {
313                         try {
314
315                                 if (speakers.getPlaybackState()) {
316                                         speakers.stopPlayback();
317                                 }
318                                 speakers.clearData();
319                         } catch (Exception e) {
320                                 e.printStackTrace();
321                         }
322                 }
323
324                 // get the music file names that are in the music files directory
325                 File musicFolder = new File(MUSIC_FILE_DIRECTORY);
326                 File[] audioFiles = musicFolder.listFiles();
327                 List<String> audioFileNames = new ArrayList<String>();
328
329                 // put all names in a list
330                 for (int i = 0; i < audioFiles.length; i++) {
331                         if (audioFiles[i].isFile()) {
332                                 try {
333                                         audioFileNames.add(audioFiles[i].getCanonicalPath());
334                                 } catch (Exception ex) {
335                                         ex.printStackTrace();
336                                 }
337                         }
338                 }
339
340                 // pick a random file to play
341                 Random rand = new Random(System.nanoTime());
342                 String audioFilename = audioFileNames.get(rand.nextInt(audioFileNames.size()));
343
344                 System.out.println("Going to load audio file");
345                 System.out.println(audioFilename);
346
347                 // decode the mp3 file
348                 System.out.println("Starting Decode");
349                 MP3Decoder dec = new MP3Decoder(audioFilename);
350                 List<short[]> dat = dec.getDecodedFrames();
351                 System.out.println("Ending Decode");
352
353                 currentPosition = 0;
354
355                 // count the number of samples
356                 int count = 0;
357                 for (short[] d : dat) {
358                         count += d.length;
359                 }
360
361                 // make into a single large buffer for 1 large RMI call
362                 short[] compressedArray = new short[count];
363                 count = 0;
364                 for (short[] d : dat) {
365                         for (short s : d) {
366                                 compressedArray[count] = s;
367                                 count++;
368                         }
369                 }
370
371
372                 System.out.println("Loading Speakers");
373                 // send the new data to all the speakers
374                 for (SpeakerSmart speakers : speakerSet.values()) {
375                         System.out.println("Loading a single speaker with data");
376                         try {
377                                 speakers.loadData(compressedArray, 0, compressedArray.length);
378                         } catch (Exception e) {
379                                 e.printStackTrace();
380                         }
381                         System.out.println("Done loading a single speaker with data");
382                 }
383
384                 System.out.println("All Speakers done loading");
385
386         }
387
388
389
390         /********************************************************************************************************
391         ** Public methods, called by the runtime
392         *********************************************************************************************************/
393
394         /** Initialization method, called by the runtime (effectively the main of the controller)
395          *   This method runs a continuous loop and is blocking
396          *
397          *   @return [void] None;
398          */
399         public void init() throws RemoteException, InterruptedException {
400
401                 // Initialize the rooms
402                 for (RoomSmart room : audioRooms.values()) {
403                         // All rooms start with the speakers turned off
404                         roomSpeakersOnOffStatus.put(room, false);
405                 }
406
407                 // Setup the cameras, start them all and assign each one a motion detector
408                 for (GPSGatewaySmart gw : gpsSet.values()) {
409
410                         //try {
411                                 // initialize, register callback, and start the gateway
412                                 gw.init();
413                                 gw.registerCallback(this);
414                                 gw.start();
415                         //} catch (RemoteException ex) {
416                         //      ex.printStackTrace();
417                         //}
418                 }
419
420
421                 //Initialize the speakers
422                 for (SpeakerSmart speakers : speakerSet.values()) {
423                         speakers.init();
424                 }
425
426                 prepareNextSong();
427
428                 // Run the main loop that will keep checking stuff
429                 while (true) {
430
431                         // Update speakers status (on/off) based on info from phone
432                         updateSpeakersStatus();
433
434                         // Check and turn on/off (stream music) through speakers based on its status
435                         updateSpeakersAction();
436
437                         try {
438                                 Thread.sleep(CHECK_TIME_WAIT_MSEC); // sleep for a tenth of the time
439                         } catch (Exception e) {
440                         }
441                 }
442         }
443 }
444
445
446
447