Blame | Last modification | View Log | Download | 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 itprivate static IOIO _IOIOInstance;private static ArrayList<Integer> _openedPins = new ArrayList<Integer>();// Static variable for passing ViewWidget classes between activitiesprivate static ViewWidget retClass;// Holds the selected view when the context menu is brought upprivate static View currentlySelectedView;// Resources for allocating new ViewWidgetsprivate static Context _context;public static ViewGroup _viewGroupGraphTextWidget;public static ViewGroup _viewGroupToggleTextWidget;public static ViewGroup _viewGroupSimpleTextWidget;// List of all view widgets to displayArrayList<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 activitiesstatic 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 serverprivate static NetworkClientConnectionHandler _networkMgr;@Overridepublic boolean onCreateOptionsMenu(Menu menu) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.mainmenu, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// Handle item selectionif (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 instancestartActivityForResult(new Intent(this, AddIAActivity.class), ACTIVITY_INPUT_ANALOG);return true;case R.id.addID:// Show activity to add new monitoring instancestartActivityForResult(new Intent(this, AddIDActivity.class), ACTIVITY_INPUT_DIGITAL);return true;case R.id.addOD:// Show activity to add new monitoring instancestartActivityForResult(new Intent(this, AddODActivity.class), ACTIVITY_OUTPUT_DIGITAL);return true;case R.id.menuOptions:// Show options/preferences menustartActivity(new Intent(this, GlobalPreferenceActivity.class));return true;case R.id.menuShowPinDetails:// Pullup a list of pin and their detailsstartActivity(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 dataclearAllMonitoredData();return true;case R.id.menuDisconnectFromServer:// Disconnect from the server if connectedif (_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 serverconnectToServer();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;}@Overridepublic boolean onContextItemSelected(MenuItem item) {ViewWidget vw;// Operates on the individual widget views depending on the option selectedswitch (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 viewfor (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 divideraddDivider(i*2+1);// Add the view_mainLayout.addView(vw.getView(), i*2+2);break;}}return true;case R.id.menuContextMoveUp:// Find the selected viewfor (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 divideraddDivider(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. */@Overridepublic 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 boardconnectToIOIOBoard();}@Overridepublic void onDestroy() {super.onDestroy();// Stop all monitoring update threadsfor (ViewWidget vw : _viewList)vw.stopThread();_connectionMonitor.interrupt();_IOIOInstance.disconnect();_openedPins.clear();_connected = false;_networkMgr.stopAutoReconnect();// Disconnect from server if connectedif (_networkMgr.isConnected())_networkMgr.disconnectFromServer();}@Overridepublic 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() {@Overridepublic void run() {if (_mainLayout.getChildCount() != 0) {// Insert a divider before each entry but not the first entryaddDivider(_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() {@Overridepublic void run() {_statusServer.setText(str);}});}/** Starts the thread that monitors the connection to the IOIO board */private void connectToIOIOBoard() {_connectionMonitor = new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {// Check if a new instance of IOIO needs to be created// Reset is only triggered when a disconnect happensif (_connectionReset) {updateStatus("Waiting to connect to the IOIO board...");toggleProgressOn();_IOIOInstance = IOIOFactory.create();_connectionReset = false;}// Check if thread should keep runningif (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() {@Overridepublic void run() {// Stop all threads and remove all viewsfor (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() {@Overridepublic void run() {if (_viewList.size() != 0) {for (ViewWidget vw : _viewList) {if (vw.getThread().isAlive())vw.stopThread();}}}});}private void resumeAll() {_handler.post(new Runnable() {@Overridepublic 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() {@Overridepublic void run() {_statusProgress.setVisibility(View.VISIBLE);}});}private void toggleProgressOff() {_handler.post(new Runnable() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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 viewfor (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 viewfor (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 viewfor (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 viewfor (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 oneif (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 removedif (_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 viewfor (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 oneif (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 removedif (_networkMgr.connectedOk())_networkMgr.notifyRemovePin(vw.getPin());_viewList.remove(i);break;}}}}