Subversion Repositories Code-Repo

Rev

Blame | Last modification | View Log | RSS feed

package IEEERobotics.IOIOAI.VT;

import ioio.lib.api.IOIO;
import ioio.lib.api.IOIOFactory;
import ioio.lib.api.exception.ConnectionLostException;
import ioio.lib.api.exception.IncompatibilityException;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import com.viewpagerindicator.TitlePageIndicator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.view.ViewPager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.TableRow.LayoutParams;

public class IOIORoboticsActivity extends Activity {
        // Static IOIO instance so other activities can access it
        public static IOIO IOIOInstance_;
        public static Context context_;
        
        public static List<String> subTitles_ = new LinkedList<String>();
        public static List<View> subViews_ = new LinkedList<View>();
        
        // Connection and activity related variables
        private static Boolean _connected = false;
        private static Handler _handler = new Handler();
        private Boolean _connectionReset = true;
        private Thread _connectionMonitor;
        
        private TextView _statusText;
        private ProgressBar _statusProgressBar;
        private LinearLayout _logLayout;
        
        private ViewPagerAdapter _pageAdapter;
        private ViewPager _pager;
        private TitlePageIndicator _pageIndicator;
        
        // Debugging stuff
        private Boolean _debugMode;
        private DebugCode _debuggingCode;
        
        // AI stuff
        private AICode _AICode;
        
        // Motor Control class
        private MotorControl _motorControl;
        
        // Item ID for custom menu items
        private static int menuToggleDebugMonitoring = Menu.FIRST;
        private static int menuOpenSettings = Menu.FIRST+1;
        private static int menuReset = Menu.FIRST+2;
        private static int menuClearLog = Menu.FIRST+3;
        
        private static int _connectionCheckInterval = 500;      // in ms
        
//      /** Called when the menu button is first pressed (creates the blank menu) */
//      @Override
//      public boolean onCreateOptionsMenu(Menu menu) {
//              MenuInflater inflater = getMenuInflater();
//              // Inflate a blank menu as we will populate it ourselves
//          inflater.inflate(R.menu.mainmenu, menu);
//          return true;
//      }
        
        /** Called when the menu button is pressed, populates the menu with options */
        @Override
        public boolean onPrepareOptionsMenu(Menu menu) {
                // Clear existing menus and create a new one based on current state
                menu.clear();
                
                if (_debugMode)
                        menu.add(0, menuToggleDebugMonitoring, 0, "Turn On Robotics AI");
                else
                        menu.add(0, menuToggleDebugMonitoring, 0, "Turn On Debug Mode");
                
                if (_connected)
                        menu.add(0, menuReset, 0, "Reset IOIO Board");
                else
                        menu.add(0, menuReset, 0, "Remove Debug Tabs");
                
                menu.add(0, menuClearLog, 0, "Clear Logs");
                menu.add(0, menuOpenSettings, 0, "Settings");
                
                return super.onPrepareOptionsMenu(menu);
        }
        
        /** Handles what to do when a menu item is selected */
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
//              // Disable certain menu options if IOIO board is not connected
//              if (item.getItemId() == menuReset && 
//                              !_connected) {
//                      Toast.makeText(this, "IOIO board not connected", Toast.LENGTH_SHORT).show();
//                      return true;
//              };
                
                // Otherwise handle menu selections
                if (item.getItemId() == menuToggleDebugMonitoring) {
                        if (_debugMode) {
                                _debugMode = false;
                                logMessage("Operating mode changed to AI");
                                if (_connected)
                                        hardReset();
                        } else {
                                _debugMode = true;
                                logMessage("Operating mode changed to Debug");
                                if (_connected)
                                        hardReset();
                        }
                        return true;
                } else if (item.getItemId() == menuOpenSettings) {
                        startActivity(new Intent(this, GlobalPreferenceActivity.class));
                        return true;
                } else if (item.getItemId() == menuReset) {
                        if (_connected)
                                hardReset();
                        else {
                                if (_debuggingCode != null) {
                                        removeDebugView();
                                }
                        }
                        return true;
                } else if (item.getItemId() == menuClearLog) {
                        _logLayout.removeAllViews();
                        return true;
                } else {
                        return super.onOptionsItemSelected(item);
                }
        }
        
        /** Called when the application is started */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // Grab the local context so other classes can use it to create views
        context_ = this.getApplicationContext();
        
        // Get local pointers to views
        _pageAdapter = new ViewPagerAdapter(this);
        _pager = (ViewPager)findViewById(R.id.viewpager);
        _pageIndicator = (TitlePageIndicator)findViewById(R.id.indicator);
        _statusText = (TextView)findViewById(R.id.statusText);
        _statusProgressBar = (ProgressBar)findViewById(R.id.progressBar);
        
        _pager.setAdapter(_pageAdapter);
        _pageIndicator.setViewPager(_pager);
        
        createLogView();
        
        // Get default/saved debug mode
        if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("pref_StartInDebug", true))
                _debugMode = true;
        else
                _debugMode = false;
        
        // Print out current debug status
        if (_debugMode)
                logMessage("Debug mode is currently on");
        else
                logMessage("Debug mode is currently off");
        
        // Start connection monitoring thread for the IOIO board
        connectToIOIOBoard();        
    }
    
    /** Called when the application is stopped */
        @Override
        public void onDestroy() {
                super.onDestroy();
                connectionReset();
                if (_debuggingCode != null) {
                        removeDebugView();
                }
                _connectionMonitor.interrupt();
                IOIOInstance_.disconnect();
                _connected = false;
        }

        /** Starts the thread that monitors the connection to the IOIO board */
    private void connectToIOIOBoard() {
        // Create a new thread to monitor the connection status to the IOIO board
        _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 while waiting
                                                IOIOInstance_.waitForConnect();
                                                // Do stuff when the first connection to the board is made
                                                if (!_connected) {
                                                        updateStatus("Connected to the IOIO board");
                                                        Thread.sleep(200);
                                                        connectionSuccess();
                                                }
                                                _connected = true;
                                                toggleProgressOff();
                                                Thread.sleep(_connectionCheckInterval);
                                        } 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) {
                                                // Throw error and quit
                                                _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 succeeds */
    private void connectionSuccess() {
        _handler.post(new Runnable() {
                        @Override
                        public void run() {
                                // If debug mode is enabled, run the debug code to create graphs
                                if (_debugMode) {
                                startDebugMode();
                        } else {
                                startAIMode();
                        }
                        }
                });
    }
    
    /** Called when the connection to the IOIO board has been reset */
    private void connectionReset() {
        _handler.post(new Runnable() {
                        @Override
                        public void run() {
                                // If the debugger code was running, stop the updating thread
                                if (_debuggingCode != null)
                                        _debuggingCode.stopPollingThread();
                                if (_AICode != null)
                                        _AICode.stopPollingThread();
                                if (_motorControl != null) {
                                _motorControl.disconnect();
                        }
                        }
                });
    }
    
    /** Creates the first layout for the ViewPager */
    private void createLogView() {
        if (subTitles_.contains("Logger")) {
                removeView(subViews_.get(subTitles_.indexOf("Logger")));
                subTitles_.remove("Logger");
        }
        
                _logLayout = new LinearLayout(this);
                _logLayout.setOrientation(LinearLayout.VERTICAL);
                _logLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
        
                ScrollView scroller = new ScrollView(this);
                scroller.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
                scroller.addView(_logLayout);
                
                addView("Logger", scroller);
        }

    /** Creates a new page in the ViewPager */
        private void addView(String name, View view) {
                // Add the view and label to the lists
                subTitles_.add(subTitles_.size(), name);
                subViews_.add(subViews_.size(), view);
                
                // Update the screen with the new views
                _pageAdapter.notifyDataSetChanged();
                _pageIndicator.notifyDataSetChanged();
        }

        /** Removes a page in the ViewPager */
        private void removeView(View view) {
                if (subViews_.contains(view)) {
                        // Change the main view to the first one (logger)
                        _pager.setCurrentItem(0);
                        
                        // Remove the view and label from the lists
                subTitles_.remove(subViews_.indexOf(view));
                        subViews_.remove(view);
                        
                        // Update the screen with the new views
                        _pageAdapter.notifyDataSetChanged();
                        _pageIndicator.notifyDataSetChanged();
                }
        }

        /** Start point for code to execute in Debug Mode */
    private void startDebugMode() {
        // Run debug code to create graphs
                logMessage("Executing Debug Mode");
                
                // On first run, create inputs and views
                if (_debuggingCode == null) {
                        _debuggingCode = new DebugCode(this);
                        addView("Graphs", _debuggingCode.getView());
                }
                if (_motorControl == null) {
                        _motorControl = new MotorControl(this);
                        addView("Motor", _motorControl.getDebugView());
                }
                
                _debuggingCode.Run();
//              _motorControl.Run();
    }
    
    /** Call to stop the debug code and remove all associated views */
    private void removeDebugView() {
        removeView(_debuggingCode.getView());
        removeView(_motorControl.getDebugView());
        
        if (_debuggingCode != null) {
//                      _debuggingCode.stopPollingThread();
                _debuggingCode = null;
        }
        if (_motorControl != null) {
                        _motorControl = null;
                }
    }
    
    /** Start point for code to execut`e in AI Mode */
    private void startAIMode() {
        // Remove the debug views if they exist
        if (_debuggingCode != null) {
                removeDebugView();
        }
        
        logMessage("Executing AI Mode");
        _AICode = new AICode(this);
        _AICode.Run();
    }
    
    /**
     * 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.
     */
    private void hardReset() {
                try {
                        IOIOInstance_.hardReset();
                } catch (ConnectionLostException e) {
                        _connected = false;
                        _connectionReset = true;
                        connectionReset();
                        updateStatus("Hard reset executed on the IOIO board");
                }
    }
    
    /** Sets the status TextView to the provided message */
    private void updateStatus(final String message) {
        _handler.post(new Runnable() {
                        @Override
                        public void run() {
                                logMessage(message);
                                _statusText.setText("Status: " + message);
                        }
                });
    }
    
    /** Appends the provided message to the beginning of the log TextView */
    public void logMessage(final String message) {
        _handler.post(new Runnable() {
                        @Override
                        public void run() {
                                // Get the current time
                                SimpleDateFormat dateFormat = new SimpleDateFormat("[HH:mm:ss]");
                                Date now = new Date();
                                String currentTime = dateFormat.format(now);
                                // Log message with the current time appended to the front
                                String msg = currentTime + " " + message;
                                TextView tv = new TextView(context_);
                                tv.setText(msg);
                                _logLayout.addView(tv,0);
                        }
                });
    }
    
    /** Returns the current connection state */
    public Boolean getConnected() {
        return _connected;
    }
    
    /** Toggles the visibility of the status ProgressBar */
    private void toggleProgressOn() {
        _handler.post(new Runnable() {
                        @Override
                        public void run() {
                                _statusProgressBar.setVisibility(View.VISIBLE);
                        }
                });
    }
    
    /** Toggles the visibility of the status ProgressBar */
    private void toggleProgressOff() {
        _handler.post(new Runnable() {
                        @Override
                        public void run() {
                                _statusProgressBar.setVisibility(View.GONE);
                        }
                });
    }
}