Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
90 Kevin 1
package ioio.debugger;
2
 
3
import java.util.ArrayList;
4
 
5
import ioio.lib.api.IOIO;
6
import ioio.lib.api.IOIOFactory;
7
import ioio.lib.api.exception.ConnectionLostException;
8
import ioio.lib.api.exception.IncompatibilityException;
9
import android.app.Activity;
10
import android.content.Context;
11
import android.content.Intent;
12
import android.content.SharedPreferences;
13
import android.os.Bundle;
14
import android.os.Handler;
15
import android.preference.PreferenceManager;
16
import android.view.ContextMenu;
17
import android.view.ContextMenu.ContextMenuInfo;
18
import android.view.Menu;
19
import android.view.MenuInflater;
20
import android.view.MenuItem;
21
import android.view.View;
22
import android.view.ViewGroup;
23
import android.widget.LinearLayout;
24
import android.widget.ProgressBar;
25
import android.widget.TextView;
26
import android.widget.Toast;
27
 
28
public class IOIODebuggerActivity extends Activity {
29
 
30
	// Static IOIO instance so other activities can access it
31
	private static IOIO _IOIOInstance;
32
	private static ArrayList<Integer> _openedPins = new ArrayList<Integer>();
33
 
34
	// Static variable for passing ViewWidget classes between activities
35
	private static ViewWidget retClass;
36
 
37
	// Holds the selected view when the context menu is brought up
38
	private static View currentlySelectedView;
39
 
40
	// Resources for allocating new ViewWidgets
41
	private static Context _context;
42
	public static ViewGroup _viewGroupGraphTextWidget;
43
	public static ViewGroup _viewGroupToggleTextWidget;
44
	public static ViewGroup _viewGroupSimpleTextWidget;
45
 
46
	// List of all view widgets to display
47
	ArrayList<ViewWidget> _viewList = new ArrayList<ViewWidget>();
48
 
49
	private static Boolean _connected = false;
50
	private static Handler _handler = new Handler();
51
	private Boolean _connectionReset = true;
52
	private Thread _connectionMonitor;
53
 
54
	private TextView _status;
55
	private TextView _statusServer;
56
	private LinearLayout _mainLayout;
57
	private LinearLayout _rootLayout;
58
	private ProgressBar _statusProgress;
59
 
60
	// Identifiers for started activities
61
	static final int ACTIVITY_INPUT_ANALOG		= 0;
62
	static final int ACTIVITY_INPUT_DIGITAL		= 1;
63
	static final int ACTIVITY_INPUT_PULSE		= 2;
64
	static final int ACTIVITY_OUTPUT_DIGITAL	= 3;
65
	static final int ACTIVITY_OUTPUT_PULSE		= 4;
66
 
67
	// Connection manager to the server
68
	private static NetworkClientConnectionHandler _networkMgr;
69
 
70
	@Override
71
	public boolean onCreateOptionsMenu(Menu menu) {
72
		MenuInflater inflater = getMenuInflater();
73
	    inflater.inflate(R.menu.mainmenu, menu);
74
	    return true;
75
	}
76
 
77
	@Override
78
	public boolean onOptionsItemSelected(MenuItem item) {
79
	    // Handle item selection
80
		if (item.getItemId() != R.id.menuOptions && 
81
				item.getItemId() != R.id.menuShowPinDetails && 
82
				item.getItemId() != R.id.menuConnect &&
83
				item.getItemId() != R.id.menuDisconnectFromServer &&
84
				!_connected) {
85
			Toast.makeText(this, "IOIO board not connected", Toast.LENGTH_SHORT).show();
86
			return true;
87
		}
88
	    switch (item.getItemId()) {
89
	    case R.id.addIA:
90
    		// Show activity to add new monitoring instance
91
    		startActivityForResult(new Intent(this, AddIAActivity.class), ACTIVITY_INPUT_ANALOG);
92
	    	return true;
93
	    case R.id.addID:
94
	    	// Show activity to add new monitoring instance
95
	    	startActivityForResult(new Intent(this, AddIDActivity.class), ACTIVITY_INPUT_DIGITAL);
96
	    	return true;
97
	    case R.id.addOD:
98
	    	// Show activity to add new monitoring instance
99
	    	startActivityForResult(new Intent(this, AddODActivity.class), ACTIVITY_OUTPUT_DIGITAL);
100
	    	return true;
101
	    case R.id.menuOptions:
102
	    	// Show options/preferences menu
103
	    	startActivity(new Intent(this, GlobalPreferenceActivity.class));
104
	        return true;
105
	    case R.id.menuShowPinDetails:
106
	    	// Pullup a list of pin and their details
107
	    	startActivity(new Intent(this, PinDetailActivity.class));
108
	    	return true;
109
	    case R.id.menuHardReset:
110
	    	hardReset();
111
	    	return true;
112
	    case R.id.menuResetMonitor:
113
	    	// Send command to clear all temp stored data
114
	    	clearAllMonitoredData();
115
	    	return true;
116
	    case R.id.menuDisconnectFromServer:
117
	    	// Disconnect from the server if connected
118
	    	if (_networkMgr.connectedOk())
119
	    		_networkMgr.disconnectFromServer();
120
	    	return true;
121
	    case R.id.menuPauseAll:
122
	    	pauseAll();
123
	    	return true;
124
	    case R.id.menuResumeAll:
125
	    	resumeAll();
126
	    	return true;
127
	    case R.id.menuConnect:
128
	    	// Initiate connection to Netty server
129
	    	connectToServer();
130
	    	return true;
131
	    default:
132
	        return super.onOptionsItemSelected(item);
133
	    }
134
	}
135
 
136
	@Override
137
	/** Create a context menu that comes up on selection of a view */
138
	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
139
		super.onCreateContextMenu(menu, v, menuInfo);
140
		MenuInflater inflater = getMenuInflater();
141
		inflater.inflate(R.menu.viewcontextmenu, menu);
142
		currentlySelectedView = v;
143
	}
144
 
145
	@Override
146
	public boolean onContextItemSelected(MenuItem item) {
147
		ViewWidget vw;
148
		// Operates on the individual widget views depending on the option selected
149
		switch (item.getItemId()) {
150
		case R.id.menuContextPause:
151
			stopViewWidget(currentlySelectedView);
152
			return true;
153
		case R.id.menuContextResume:
154
			startViewWidget(currentlySelectedView);
155
			return true;
156
		case R.id.menuContextRemove:
157
			removeViewWidget(currentlySelectedView);
158
			return true;
159
		case R.id.menuContextMoveDown:
160
			// Find the selected view
161
			for (int i = 0; i < _viewList.size()-1; i++) {
162
				vw = _viewList.get(i);
163
				if (vw.getView() == currentlySelectedView) {
164
					// Relocate the widget in the list
165
					_viewList.remove(i);
166
					_viewList.add(i+1, vw);
167
					// Remove view and divider after the view
168
					_mainLayout.removeViewAt(i*2);
169
					_mainLayout.removeViewAt(i*2);
170
					// Add a new divider
171
					addDivider(i*2+1);
172
		    		// Add the view
173
					_mainLayout.addView(vw.getView(), i*2+2);
174
					break;
175
				}
176
			}
177
			return true;
178
		case R.id.menuContextMoveUp:
179
			// Find the selected view
180
			for (int i = 1; i < _viewList.size(); i++) {
181
				vw = _viewList.get(i);
182
				if (vw.getView() == currentlySelectedView) {
183
					// Relocate the widget in the list
184
					_viewList.remove(i);
185
					_viewList.add(i-1, vw);
186
					// Remove view and divider before the view
187
					_mainLayout.removeViewAt(i*2);
188
					_mainLayout.removeViewAt(i*2-1);
189
					// Add a new divider
190
					addDivider(i*2-2);
191
		    		// Add the view
192
					_mainLayout.addView(vw.getView(), i*2-2);
193
					break;
194
				}
195
			}
196
			return true;
197
		default:
198
			return super.onContextItemSelected(item);
199
		}
200
	}
201
 
202
    /** Called when the activity is first created. */
203
    @Override
204
    public void onCreate(Bundle savedInstanceState) {
205
        super.onCreate(savedInstanceState);
206
        setContentView(R.layout.main);
207
 
208
        _status = (TextView)findViewById(R.id.textStatus);
209
        _mainLayout = (LinearLayout)findViewById(R.id.mainLayout);
210
        _rootLayout = (LinearLayout)findViewById(R.id.rootLayout);
211
        _statusProgress = (ProgressBar)findViewById(R.id.progressStatus);
212
 
213
        _statusServer = new TextView(this);
214
    	_rootLayout.addView(_statusServer, 0);
215
    	_statusServer.setVisibility(8);
216
 
217
    	_context = getApplicationContext();
218
    	_viewGroupSimpleTextWidget = (ViewGroup) findViewById(R.id.layout_simpleText_root);
219
    	_viewGroupGraphTextWidget = (ViewGroup) findViewById(R.id.layout_graphText_root);
220
    	_viewGroupToggleTextWidget = (ViewGroup) findViewById(R.id.layout_toggleText_root);
221
 
222
    	_networkMgr = new NetworkClientConnectionHandler(this);
223
 
224
    	// Start connection monitoring thread for the IOIO board
225
        connectToIOIOBoard();
226
    }
227
 
228
    @Override
229
    public void onDestroy() {
230
    	super.onDestroy();
231
    	// Stop all monitoring update threads
232
    	for (ViewWidget vw : _viewList)
233
    		vw.stopThread();
234
    	_connectionMonitor.interrupt();
235
    	_IOIOInstance.disconnect();
236
    	_openedPins.clear();
237
    	_connected = false;
238
    	_networkMgr.stopAutoReconnect();
239
    	// Disconnect from server if connected
240
    	if (_networkMgr.isConnected())
241
    		_networkMgr.disconnectFromServer();
242
    }
243
 
244
    @Override
245
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
246
    	if (resultCode == RESULT_CANCELED) {
247
    		return;
248
    	} else {
249
    		processAddWidget(requestCode);
250
    	}
251
    }
252
 
253
    /** Called by internal/external functions to add a widget after setting retClass 
254
     * 
255
     * @param requestCode one of the static codes from this class eg. ACTIVITY_INPUT_ANALOG
256
     */
257
    public void processAddWidget(final int requestCode) {
258
    	_handler.post(new Runnable() {
259
 
260
			@Override
261
			public void run() {
262
				if (_mainLayout.getChildCount() != 0) {
263
		    		// Insert a divider before each entry but not the first entry
264
					addDivider(_mainLayout.getChildCount());
265
				}
266
				switch (requestCode) {
267
				case ACTIVITY_INPUT_ANALOG:
268
					_mainLayout.addView(retClass.getView());
269
					retClass.startThread();
270
					_viewList.add(retClass);
271
					registerForContextMenu(retClass.getView());
272
					return;
273
				case ACTIVITY_INPUT_DIGITAL:
274
					_mainLayout.addView(retClass.getView());
275
					retClass.startThread();
276
					_viewList.add(retClass);
277
					registerForContextMenu(retClass.getView());
278
					return;
279
				case ACTIVITY_OUTPUT_DIGITAL:
280
					_mainLayout.addView(retClass.getView());
281
					retClass.startThread();
282
					_viewList.add(retClass);
283
					registerForContextMenu(retClass.getView());
284
					return;
285
				}
286
			}
287
		});
288
    }
289
 
290
    /** Start the server using values from shared preferences */
291
    private void connectToServer() {
292
    	// Show the server status message
293
    	_statusServer.setVisibility(0);
294
 
295
    	SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
296
    	String server = sharedPrefs.getString("pref_serverIP", "");
297
    	int port = Integer.parseInt(sharedPrefs.getString("pref_serverPort", "0"));
298
    	boolean reconnect = sharedPrefs.getBoolean("pref_serverReconnect", false);
299
    	int reconnectInterval = Integer.parseInt(sharedPrefs.getString("pref_serverReconnectInterval", "3000"));
300
 
301
    	get_networkMgr().setConnectionSettings(server, port, reconnect, reconnectInterval);
302
    	get_networkMgr().startConnect();
303
 
304
    }
305
 
306
    /** Change the server status message text */
307
    public void updateServerStatus(final String str) {
308
    	_handler.post(new Runnable() {
309
 
310
			@Override
311
			public void run() {
312
				_statusServer.setText(str);
313
			}
314
		});
315
    }
316
 
317
    /** Starts the thread that monitors the connection to the IOIO board */
318
    private void connectToIOIOBoard() {
319
    	_connectionMonitor = new Thread(new Runnable() {
320
 
321
			@Override
322
			public void run() {
323
				while (true) {
324
					try {
325
						// Check if a new instance of IOIO needs to be created
326
						// Reset is only triggered when a disconnect happens
327
						if (_connectionReset) {
328
							updateStatus("Waiting to connect to the IOIO board...");
329
							toggleProgressOn();
330
							_IOIOInstance = IOIOFactory.create();
331
							_connectionReset = false;
332
						}
333
						// Check if thread should keep running
334
						if (Thread.currentThread().isInterrupted()) {
335
							break;
336
						}
337
						// Attempt to connect to the IOIO board
338
						// If the board was disconnected, ConnectionLostException is thrown
339
						_IOIOInstance.waitForConnect();
340
						_connected = true;
341
						updateStatus("Connected to the IOIO board");
342
						toggleProgressOff();
343
						Thread.sleep(500);
344
					} catch (ConnectionLostException e) {
345
						// Reset the connection so reconnection attempts can be made
346
						_connected = false;
347
						_connectionReset = true;
348
						connectionReset();
349
						updateStatus("Connection to the IOIO board has be lost");
350
					} catch (IncompatibilityException e) {
351
						_connected = false;
352
						_connectionReset = true;
353
						connectionReset();
354
						updateStatus("Connected IOIO board is incompatible");
355
					} catch (InterruptedException e) {
356
						break;
357
					}
358
				}
359
			}
360
		});
361
    	_connectionMonitor.start();
362
    }
363
 
364
    /** Called when the connection to the IOIO board is reset */
365
    private void connectionReset() {
366
    	_handler.post(new Runnable() {
367
 
368
			@Override
369
			public void run() {
370
				// Stop all threads and remove all views
371
				for (ViewWidget view : _viewList) {
372
					view.stopThread();
373
					if (_networkMgr.connectedOk())
374
						_networkMgr.notifyRemovePin(view.getPin());
375
				}
376
				_openedPins.clear();
377
				_mainLayout.removeAllViews();
378
				_viewList.clear();
379
			}
380
		});
381
    }
382
 
383
    /** Adds a divider at location in mainLayout */
384
    private void addDivider(int index) {
385
    	View ruler = new View(this);
386
		ruler.setBackgroundColor(0xAAAAAAAA);
387
		_mainLayout.addView(ruler, index, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, 2));
388
    }
389
 
390
    private void pauseAll() {
391
    	_handler.post(new Runnable() {
392
 
393
			@Override
394
			public void run() {
395
				if (_viewList.size() != 0) {
396
			    	for (ViewWidget vw : _viewList) {
397
		    			if (vw.getThread().isAlive())
398
		    				vw.stopThread();
399
			    	}
400
		    	}
401
			}
402
		});
403
    }
404
 
405
    private void resumeAll() {
406
    	_handler.post(new Runnable() {
407
 
408
			@Override
409
			public void run() {
410
				if (_viewList.size() != 0) {
411
			    	for (ViewWidget vw : _viewList) {
412
		    			if (!vw.getThread().isAlive())
413
		    				vw.startThread();
414
			    	}
415
		    	}
416
			}
417
		});
418
    }
419
 
420
    /** Clear all stored data */
421
    private void clearAllMonitoredData() {
422
    	for (ViewWidget vw : _viewList) {
423
    		vw.clearData();
424
    	}
425
    }
426
 
427
    /**
428
     * A soft reset means "return everything to its initial state". 
429
     * This includes closing all interfaces obtained from this IOIO 
430
     * instance, and in turn freeing all resources. All pins (save 
431
     * the stat LED) will become floating inputs. All modules will 
432
     * be powered off. These operations are done without dropping 
433
     * the connection with the IOIO, thus a soft reset is very fast.
434
     */
435
//    private void softReset() {
436
//		try {
437
//			_IOIOInstance.softReset();
438
//		} catch (ConnectionLostException e) {
439
//			synchronized (_connected) {
440
//				_connected = false;
441
//				_connectionReset = true;
442
//				connectionReset();
443
//				updateStatus("Connection to the IOIO board has be lost");
444
//			}
445
//		}
446
//    }
447
 
448
    /**
449
     * A hard reset is exactly like physically powering off the IOIO 
450
     * board and powering it back on. As a result, the connection 
451
     * with the IOIO will drop and the IOIO instance will become 
452
     * disconnected and unusable. The board will perform a full reboot, 
453
     * including going through the bootloader sequence, i.e. an attempt 
454
     * to update the board's firmware will be made.
455
     */
456
    public void hardReset() {
457
		try {
458
			_IOIOInstance.hardReset();
459
		} catch (ConnectionLostException e) {
460
			_connected = false;
461
			_connectionReset = true;
462
			connectionReset();
463
			updateStatus("Connection to the IOIO board has be lost");
464
		}
465
    }
466
 
467
    private void toggleProgressOn() {
468
    	_handler.post(new Runnable() {
469
 
470
			@Override
471
			public void run() {
472
				_statusProgress.setVisibility(View.VISIBLE);
473
			}
474
		});
475
    }
476
 
477
    private void toggleProgressOff() {
478
    	_handler.post(new Runnable() {
479
 
480
			@Override
481
			public void run() {
482
		    	_statusProgress.setVisibility(View.GONE);
483
			}
484
		});
485
    }
486
 
487
    /**
488
     * Updates the status label
489
     * 
490
     * @param text The string for the label to be set to
491
     */
492
    private void updateStatus(final String text) {
493
    	_handler.post(new Runnable() {
494
 
495
			@Override
496
			public void run() {
497
				_status.setText("Status: " + text);
498
			}
499
		});
500
    }
501
 
502
    /** Returns the static IOIO instance */
503
    public static IOIO getIOIOInstance() {
504
    	return _IOIOInstance;
505
    }
506
 
507
    /** Returns the context of the application */
508
    public static Context getContext() {
509
		return _context;
510
	}
511
 
512
    /** Returns the list of opened pins */
513
    public static ArrayList<Integer> getOpenedPins() {
514
    	return _openedPins;
515
    }
516
 
517
    /** Adds a pin to the list of opened pins */
518
    public static void addOpenedPin(Integer i) {
519
    	_openedPins.add(i);
520
    }
521
 
522
    /** Removes a pin from the list of opened pins */
523
    public static void removeOpenedPin(Integer i) {
524
    	_openedPins.remove(i);
525
    }
526
 
527
//    /** Returns the static retClass ViewWidget */
528
//	public static ViewWidget getViewWidget() {
529
//		return retClass;
530
//	}
531
 
532
	/** Sets the current static retClass ViewWidget */
533
	public static void setViewWidget(final ViewWidget retClass) {
534
		_handler.post(new Runnable() {
535
 
536
			@Override
537
			public void run() {
538
				IOIODebuggerActivity.retClass = retClass;
539
			}
540
		});
541
	}
542
 
543
	/** Returns the static NetworkClientConnectionHandler */
544
	public static NetworkClientConnectionHandler get_networkMgr() {
545
		return _networkMgr;
546
	}
547
 
548
	/** Signal all monitoring threads to send history of saved data */
549
	public void sendAllData() {
550
		for (ViewWidget vw : _viewList) {
551
			vw.sendDataHistory();
552
		}
553
	}
554
 
555
	/** Sends new data to the server */
556
	public static void sendData(int pin, Double[] d) {
557
		_networkMgr.sendData(pin, d);
558
	}
559
 
560
	/** Queries if the IOIO board is currently connected */
561
	public static boolean getConnected() {
562
		return _connected;
563
	}
564
 
565
	/** Starts the updater thread for the specified view */
566
	public void startViewWidget(View vw) {
567
		// Find the selected view
568
		for (ViewWidget vww : _viewList) {
569
			if (vww.getView() == currentlySelectedView) {
570
				vww.startThread();
571
				break;
572
			}
573
		}
574
	}
575
 
576
	/** Stops the updater thread for the specified view */
577
	public void stopViewWidget(View vw) {
578
		// Find the selected view
579
		for (ViewWidget vww : _viewList) {
580
			if (vww.getView() == currentlySelectedView) {
581
				vww.stopThread();
582
				break;
583
			}
584
		}
585
	}
586
 
587
	/** Stops the updater thread for the ViewWidget with the specified pin */
588
	public void stopViewWidget(int pin) {
589
		// Find the selected view
590
		for (ViewWidget vww : _viewList) {
591
			if (vww.getPin() == pin) {
592
				vww.stopThread();
593
				break;
594
			}
595
		}
596
	}
597
 
598
	/** Removes the ViewWidget that has the specified view */
599
	public void removeViewWidget(View view) {
600
		ViewWidget vw;
601
		// Find the selected view
602
		for (int i = 0; i < _viewList.size(); i++) {
603
			vw = _viewList.get(i);
604
			if (vw.getView() == view) {
605
				vw.stopThread();
606
				// If the widget to remove is the first one and there is only one
607
				if (i == 0 && _viewList.size() == 1) {
608
					_mainLayout.removeViewAt(0);
609
				// If the widget to remove is the first one and there is more than one
610
				} else if (i == 0 && _viewList.size() > 1) {
611
					_mainLayout.removeViewAt(0);
612
					_mainLayout.removeViewAt(0);
613
				// If the widget to remove is the last one
614
				} else if (i == _viewList.size()-1) {
615
					_mainLayout.removeViewAt(_mainLayout.getChildCount()-1);
616
					_mainLayout.removeViewAt(_mainLayout.getChildCount()-1);
617
				} else {
618
				// Remove the widget at the specified location
619
					_mainLayout.removeViewAt(i*2);
620
					_mainLayout.removeViewAt(i*2);
621
				}
622
				// Notify the server that a pin is being removed
623
				if (_networkMgr.connectedOk())
624
					_networkMgr.notifyRemovePin(vw.getPin());
625
				_viewList.remove(i);
626
				break;
627
			}
628
		}
629
	}
630
 
631
	/** Removes the ViewWidget with the specified pin */
632
	public void removeViewWidget(int pin) {
633
		ViewWidget vw;
634
		// Find the selected view
635
		for (int i = 0; i < _viewList.size(); i++) {
636
			vw = _viewList.get(i);
637
			if (vw.getPin() == pin) {
638
				vw.stopThread();
639
				// If the widget to remove is the first one and there is only one
640
				if (i == 0 && _viewList.size() == 1) {
641
					_mainLayout.removeViewAt(0);
642
				// If the widget to remove is the first one and there is more than one
643
				} else if (i == 0 && _viewList.size() > 1) {
644
					_mainLayout.removeViewAt(0);
645
					_mainLayout.removeViewAt(0);
646
				// If the widget to remove is the last one
647
				} else if (i == _viewList.size()-1) {
648
					_mainLayout.removeViewAt(_mainLayout.getChildCount()-1);
649
					_mainLayout.removeViewAt(_mainLayout.getChildCount()-1);
650
				} else {
651
				// Remove the widget at the specified location
652
					_mainLayout.removeViewAt(i*2);
653
					_mainLayout.removeViewAt(i*2);
654
				}
655
				// Notify the server that a pin is being removed
656
				if (_networkMgr.connectedOk())
657
					_networkMgr.notifyRemovePin(vw.getPin());
658
				_viewList.remove(i);
659
				break;
660
			}
661
		}
662
	}
663
}