Subversion Repositories Code-Repo

Rev

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;
                        }
                }
        }
}