Blame | Last modification | View Log | RSS feed
package ioio.debugger;
import java.util.ArrayList;
import ioio.lib.api.IOIO;
import ioio.lib.api.IOIOFactory;
import ioio.lib.api.exception.ConnectionLostException;
import ioio.lib.api.exception.IncompatibilityException;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class IOIODebuggerActivity extends Activity {
// Static IOIO instance so other activities can access it
private static IOIO _IOIOInstance;
private static ArrayList<Integer> _openedPins = new ArrayList<Integer>();
// Static variable for passing ViewWidget classes between activities
private static ViewWidget retClass;
// Holds the selected view when the context menu is brought up
private static View currentlySelectedView;
// Resources for allocating new ViewWidgets
private static Context _context;
public static ViewGroup _viewGroupGraphTextWidget;
public static ViewGroup _viewGroupToggleTextWidget;
public static ViewGroup _viewGroupSimpleTextWidget;
// List of all view widgets to display
ArrayList<ViewWidget> _viewList = new ArrayList<ViewWidget>();
private static Boolean _connected = false;
private static Handler _handler = new Handler();
private Boolean _connectionReset = true;
private Thread _connectionMonitor;
private TextView _status;
private TextView _statusServer;
private LinearLayout _mainLayout;
private LinearLayout _rootLayout;
private ProgressBar _statusProgress;
// Identifiers for started activities
static final int ACTIVITY_INPUT_ANALOG = 0;
static final int ACTIVITY_INPUT_DIGITAL = 1;
static final int ACTIVITY_INPUT_PULSE = 2;
static final int ACTIVITY_OUTPUT_DIGITAL = 3;
static final int ACTIVITY_OUTPUT_PULSE = 4;
// Connection manager to the server
private static NetworkClientConnectionHandler _networkMgr;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mainmenu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
if (item.getItemId() != R.id.menuOptions &&
item.getItemId() != R.id.menuShowPinDetails &&
item.getItemId() != R.id.menuConnect &&
item.getItemId() != R.id.menuDisconnectFromServer &&
!_connected) {
Toast.makeText(this, "IOIO board not connected", Toast.LENGTH_SHORT).show();
return true;
}
switch (item.getItemId()) {
case R.id.addIA:
// Show activity to add new monitoring instance
startActivityForResult(new Intent(this, AddIAActivity.class), ACTIVITY_INPUT_ANALOG);
return true;
case R.id.addID:
// Show activity to add new monitoring instance
startActivityForResult(new Intent(this, AddIDActivity.class), ACTIVITY_INPUT_DIGITAL);
return true;
case R.id.addOD:
// Show activity to add new monitoring instance
startActivityForResult(new Intent(this, AddODActivity.class), ACTIVITY_OUTPUT_DIGITAL);
return true;
case R.id.menuOptions:
// Show options/preferences menu
startActivity(new Intent(this, GlobalPreferenceActivity.class));
return true;
case R.id.menuShowPinDetails:
// Pullup a list of pin and their details
startActivity(new Intent(this, PinDetailActivity.class));
return true;
case R.id.menuHardReset:
hardReset();
return true;
case R.id.menuResetMonitor:
// Send command to clear all temp stored data
clearAllMonitoredData();
return true;
case R.id.menuDisconnectFromServer:
// Disconnect from the server if connected
if (_networkMgr.connectedOk())
_networkMgr.disconnectFromServer();
return true;
case R.id.menuPauseAll:
pauseAll();
return true;
case R.id.menuResumeAll:
resumeAll();
return true;
case R.id.menuConnect:
// Initiate connection to Netty server
connectToServer();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
/** Create a context menu that comes up on selection of a view */
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.viewcontextmenu, menu);
currentlySelectedView = v;
}
@Override
public boolean onContextItemSelected(MenuItem item) {
ViewWidget vw;
// Operates on the individual widget views depending on the option selected
switch (item.getItemId()) {
case R.id.menuContextPause:
stopViewWidget(currentlySelectedView);
return true;
case R.id.menuContextResume:
startViewWidget(currentlySelectedView);
return true;
case R.id.menuContextRemove:
removeViewWidget(currentlySelectedView);
return true;
case R.id.menuContextMoveDown:
// Find the selected view
for (int i = 0; i < _viewList.size()-1; i++) {
vw = _viewList.get(i);
if (vw.getView() == currentlySelectedView) {
// Relocate the widget in the list
_viewList.remove(i);
_viewList.add(i+1, vw);
// Remove view and divider after the view
_mainLayout.removeViewAt(i*2);
_mainLayout.removeViewAt(i*2);
// Add a new divider
addDivider(i*2+1);
// Add the view
_mainLayout.addView(vw.getView(), i*2+2);
break;
}
}
return true;
case R.id.menuContextMoveUp:
// Find the selected view
for (int i = 1; i < _viewList.size(); i++) {
vw = _viewList.get(i);
if (vw.getView() == currentlySelectedView) {
// Relocate the widget in the list
_viewList.remove(i);
_viewList.add(i-1, vw);
// Remove view and divider before the view
_mainLayout.removeViewAt(i*2);
_mainLayout.removeViewAt(i*2-1);
// Add a new divider
addDivider(i*2-2);
// Add the view
_mainLayout.addView(vw.getView(), i*2-2);
break;
}
}
return true;
default:
return super.onContextItemSelected(item);
}
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_status = (TextView)findViewById(R.id.textStatus);
_mainLayout = (LinearLayout)findViewById(R.id.mainLayout);
_rootLayout = (LinearLayout)findViewById(R.id.rootLayout);
_statusProgress = (ProgressBar)findViewById(R.id.progressStatus);
_statusServer = new TextView(this);
_rootLayout.addView(_statusServer, 0);
_statusServer.setVisibility(8);
_context = getApplicationContext();
_viewGroupSimpleTextWidget = (ViewGroup) findViewById(R.id.layout_simpleText_root);
_viewGroupGraphTextWidget = (ViewGroup) findViewById(R.id.layout_graphText_root);
_viewGroupToggleTextWidget = (ViewGroup) findViewById(R.id.layout_toggleText_root);
_networkMgr = new NetworkClientConnectionHandler(this);
// Start connection monitoring thread for the IOIO board
connectToIOIOBoard();
}
@Override
public void onDestroy() {
super.onDestroy();
// Stop all monitoring update threads
for (ViewWidget vw : _viewList)
vw.stopThread();
_connectionMonitor.interrupt();
_IOIOInstance.disconnect();
_openedPins.clear();
_connected = false;
_networkMgr.stopAutoReconnect();
// Disconnect from server if connected
if (_networkMgr.isConnected())
_networkMgr.disconnectFromServer();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_CANCELED) {
return;
} else {
processAddWidget(requestCode);
}
}
/** Called by internal/external functions to add a widget after setting retClass
*
* @param requestCode one of the static codes from this class eg. ACTIVITY_INPUT_ANALOG
*/
public void processAddWidget(final int requestCode) {
_handler.post(new Runnable() {
@Override
public void run() {
if (_mainLayout.getChildCount() != 0) {
// Insert a divider before each entry but not the first entry
addDivider(_mainLayout.getChildCount());
}
switch (requestCode) {
case ACTIVITY_INPUT_ANALOG:
_mainLayout.addView(retClass.getView());
retClass.startThread();
_viewList.add(retClass);
registerForContextMenu(retClass.getView());
return;
case ACTIVITY_INPUT_DIGITAL:
_mainLayout.addView(retClass.getView());
retClass.startThread();
_viewList.add(retClass);
registerForContextMenu(retClass.getView());
return;
case ACTIVITY_OUTPUT_DIGITAL:
_mainLayout.addView(retClass.getView());
retClass.startThread();
_viewList.add(retClass);
registerForContextMenu(retClass.getView());
return;
}
}
});
}
/** Start the server using values from shared preferences */
private void connectToServer() {
// Show the server status message
_statusServer.setVisibility(0);
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
String server = sharedPrefs.getString("pref_serverIP", "");
int port = Integer.parseInt(sharedPrefs.getString("pref_serverPort", "0"));
boolean reconnect = sharedPrefs.getBoolean("pref_serverReconnect", false);
int reconnectInterval = Integer.parseInt(sharedPrefs.getString("pref_serverReconnectInterval", "3000"));
get_networkMgr().setConnectionSettings(server, port, reconnect, reconnectInterval);
get_networkMgr().startConnect();
}
/** Change the server status message text */
public void updateServerStatus(final String str) {
_handler.post(new Runnable() {
@Override
public void run() {
_statusServer.setText(str);
}
});
}
/** Starts the thread that monitors the connection to the IOIO board */
private void connectToIOIOBoard() {
_connectionMonitor = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
// Check if a new instance of IOIO needs to be created
// Reset is only triggered when a disconnect happens
if (_connectionReset) {
updateStatus("Waiting to connect to the IOIO board...");
toggleProgressOn();
_IOIOInstance = IOIOFactory.create();
_connectionReset = false;
}
// Check if thread should keep running
if (Thread.currentThread().isInterrupted()) {
break;
}
// Attempt to connect to the IOIO board
// If the board was disconnected, ConnectionLostException is thrown
_IOIOInstance.waitForConnect();
_connected = true;
updateStatus("Connected to the IOIO board");
toggleProgressOff();
Thread.sleep(500);
} catch (ConnectionLostException e) {
// Reset the connection so reconnection attempts can be made
_connected = false;
_connectionReset = true;
connectionReset();
updateStatus("Connection to the IOIO board has be lost");
} catch (IncompatibilityException e) {
_connected = false;
_connectionReset = true;
connectionReset();
updateStatus("Connected IOIO board is incompatible");
} catch (InterruptedException e) {
break;
}
}
}
});
_connectionMonitor.start();
}
/** Called when the connection to the IOIO board is reset */
private void connectionReset() {
_handler.post(new Runnable() {
@Override
public void run() {
// Stop all threads and remove all views
for (ViewWidget view : _viewList) {
view.stopThread();
if (_networkMgr.connectedOk())
_networkMgr.notifyRemovePin(view.getPin());
}
_openedPins.clear();
_mainLayout.removeAllViews();
_viewList.clear();
}
});
}
/** Adds a divider at location in mainLayout */
private void addDivider(int index) {
View ruler = new View(this);
ruler.setBackgroundColor(0xAAAAAAAA);
_mainLayout.addView(ruler, index, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, 2));
}
private void pauseAll() {
_handler.post(new Runnable() {
@Override
public void run() {
if (_viewList.size() != 0) {
for (ViewWidget vw : _viewList) {
if (vw.getThread().isAlive())
vw.stopThread();
}
}
}
});
}
private void resumeAll() {
_handler.post(new Runnable() {
@Override
public void run() {
if (_viewList.size() != 0) {
for (ViewWidget vw : _viewList) {
if (!vw.getThread().isAlive())
vw.startThread();
}
}
}
});
}
/** Clear all stored data */
private void clearAllMonitoredData() {
for (ViewWidget vw : _viewList) {
vw.clearData();
}
}
/**
* A soft reset means "return everything to its initial state".
* This includes closing all interfaces obtained from this IOIO
* instance, and in turn freeing all resources. All pins (save
* the stat LED) will become floating inputs. All modules will
* be powered off. These operations are done without dropping
* the connection with the IOIO, thus a soft reset is very fast.
*/
// private void softReset() {
// try {
// _IOIOInstance.softReset();
// } catch (ConnectionLostException e) {
// synchronized (_connected) {
// _connected = false;
// _connectionReset = true;
// connectionReset();
// updateStatus("Connection to the IOIO board has be lost");
// }
// }
// }
/**
* A hard reset is exactly like physically powering off the IOIO
* board and powering it back on. As a result, the connection
* with the IOIO will drop and the IOIO instance will become
* disconnected and unusable. The board will perform a full reboot,
* including going through the bootloader sequence, i.e. an attempt
* to update the board's firmware will be made.
*/
public void hardReset() {
try {
_IOIOInstance.hardReset();
} catch (ConnectionLostException e) {
_connected = false;
_connectionReset = true;
connectionReset();
updateStatus("Connection to the IOIO board has be lost");
}
}
private void toggleProgressOn() {
_handler.post(new Runnable() {
@Override
public void run() {
_statusProgress.setVisibility(View.VISIBLE);
}
});
}
private void toggleProgressOff() {
_handler.post(new Runnable() {
@Override
public void run() {
_statusProgress.setVisibility(View.GONE);
}
});
}
/**
* Updates the status label
*
* @param text The string for the label to be set to
*/
private void updateStatus(final String text) {
_handler.post(new Runnable() {
@Override
public void run() {
_status.setText("Status: " + text);
}
});
}
/** Returns the static IOIO instance */
public static IOIO getIOIOInstance() {
return _IOIOInstance;
}
/** Returns the context of the application */
public static Context getContext() {
return _context;
}
/** Returns the list of opened pins */
public static ArrayList<Integer> getOpenedPins() {
return _openedPins;
}
/** Adds a pin to the list of opened pins */
public static void addOpenedPin(Integer i) {
_openedPins.add(i);
}
/** Removes a pin from the list of opened pins */
public static void removeOpenedPin(Integer i) {
_openedPins.remove(i);
}
// /** Returns the static retClass ViewWidget */
// public static ViewWidget getViewWidget() {
// return retClass;
// }
/** Sets the current static retClass ViewWidget */
public static void setViewWidget(final ViewWidget retClass) {
_handler.post(new Runnable() {
@Override
public void run() {
IOIODebuggerActivity.retClass = retClass;
}
});
}
/** Returns the static NetworkClientConnectionHandler */
public static NetworkClientConnectionHandler get_networkMgr() {
return _networkMgr;
}
/** Signal all monitoring threads to send history of saved data */
public void sendAllData() {
for (ViewWidget vw : _viewList) {
vw.sendDataHistory();
}
}
/** Sends new data to the server */
public static void sendData(int pin, Double[] d) {
_networkMgr.sendData(pin, d);
}
/** Queries if the IOIO board is currently connected */
public static boolean getConnected() {
return _connected;
}
/** Starts the updater thread for the specified view */
public void startViewWidget(View vw) {
// Find the selected view
for (ViewWidget vww : _viewList) {
if (vww.getView() == currentlySelectedView) {
vww.startThread();
break;
}
}
}
/** Stops the updater thread for the specified view */
public void stopViewWidget(View vw) {
// Find the selected view
for (ViewWidget vww : _viewList) {
if (vww.getView() == currentlySelectedView) {
vww.stopThread();
break;
}
}
}
/** Stops the updater thread for the ViewWidget with the specified pin */
public void stopViewWidget(int pin) {
// Find the selected view
for (ViewWidget vww : _viewList) {
if (vww.getPin() == pin) {
vww.stopThread();
break;
}
}
}
/** Removes the ViewWidget that has the specified view */
public void removeViewWidget(View view) {
ViewWidget vw;
// Find the selected view
for (int i = 0; i < _viewList.size(); i++) {
vw = _viewList.get(i);
if (vw.getView() == view) {
vw.stopThread();
// If the widget to remove is the first one and there is only one
if (i == 0 && _viewList.size() == 1) {
_mainLayout.removeViewAt(0);
// If the widget to remove is the first one and there is more than one
} else if (i == 0 && _viewList.size() > 1) {
_mainLayout.removeViewAt(0);
_mainLayout.removeViewAt(0);
// If the widget to remove is the last one
} else if (i == _viewList.size()-1) {
_mainLayout.removeViewAt(_mainLayout.getChildCount()-1);
_mainLayout.removeViewAt(_mainLayout.getChildCount()-1);
} else {
// Remove the widget at the specified location
_mainLayout.removeViewAt(i*2);
_mainLayout.removeViewAt(i*2);
}
// Notify the server that a pin is being removed
if (_networkMgr.connectedOk())
_networkMgr.notifyRemovePin(vw.getPin());
_viewList.remove(i);
break;
}
}
}
/** Removes the ViewWidget with the specified pin */
public void removeViewWidget(int pin) {
ViewWidget vw;
// Find the selected view
for (int i = 0; i < _viewList.size(); i++) {
vw = _viewList.get(i);
if (vw.getPin() == pin) {
vw.stopThread();
// If the widget to remove is the first one and there is only one
if (i == 0 && _viewList.size() == 1) {
_mainLayout.removeViewAt(0);
// If the widget to remove is the first one and there is more than one
} else if (i == 0 && _viewList.size() > 1) {
_mainLayout.removeViewAt(0);
_mainLayout.removeViewAt(0);
// If the widget to remove is the last one
} else if (i == _viewList.size()-1) {
_mainLayout.removeViewAt(_mainLayout.getChildCount()-1);
_mainLayout.removeViewAt(_mainLayout.getChildCount()-1);
} else {
// Remove the widget at the specified location
_mainLayout.removeViewAt(i*2);
_mainLayout.removeViewAt(i*2);
}
// Notify the server that a pin is being removed
if (_networkMgr.connectedOk())
_networkMgr.notifyRemovePin(vw.getPin());
_viewList.remove(i);
break;
}
}
}
}