/* * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. * * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied. */ package ioio.lib.impl; import android.util.Log; import ioio.lib.api.exception.ConnectionLostException; import ioio.lib.impl.IOIOProtocol.IncomingHandler; import java.util.HashSet; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; class IncomingState implements IncomingHandler { enum ConnectionState { INIT, ESTABLISHED, CONNECTED, DISCONNECTED, UNSUPPORTED_IID } interface InputPinListener { void setValue(int value); } interface DisconnectListener { void disconnected(); } interface DataModuleListener { void dataReceived(byte[] data, int size); void reportAdditionalBuffer(int bytesToAdd); } class InputPinState { private Queue<InputPinListener> listeners_ = new ConcurrentLinkedQueue<InputPinListener>(); private boolean currentOpen_ = false; void pushListener(InputPinListener listener) { listeners_.add(listener); } void closeCurrentListener() { if (currentOpen_) { currentOpen_ = false; listeners_.remove(); } } void openNextListener() { assert (!listeners_.isEmpty()); if (!currentOpen_) { currentOpen_ = true; } } void setValue(int v) { assert (currentOpen_); listeners_.peek().setValue(v); } } class DataModuleState { private Queue<DataModuleListener> listeners_ = new ConcurrentLinkedQueue<DataModuleListener>(); private boolean currentOpen_ = false; void pushListener(DataModuleListener listener) { listeners_.add(listener); } void closeCurrentListener() { if (currentOpen_) { currentOpen_ = false; listeners_.remove(); } } void openNextListener() { assert (!listeners_.isEmpty()); if (!currentOpen_) { currentOpen_ = true; } } void dataReceived(byte[] data, int size) { assert (currentOpen_); listeners_.peek().dataReceived(data, size); } public void reportAdditionalBuffer(int bytesRemaining) { assert (currentOpen_); listeners_.peek().reportAdditionalBuffer(bytesRemaining); } } private final InputPinState[] intputPinStates_ = new InputPinState[Constants.NUM_PINS]; private final DataModuleState[] uartStates_ = new DataModuleState[Constants.NUM_UART_MODULES]; private final DataModuleState[] twiStates_ = new DataModuleState[Constants.NUM_TWI_MODULES]; private final DataModuleState[] spiStates_ = new DataModuleState[Constants.NUM_SPI_MODULES]; private final DataModuleState[] incapStates_ = new DataModuleState[2 * Constants.INCAP_MODULES_DOUBLE.length + Constants.INCAP_MODULES_SINGLE.length]; private final DataModuleState icspState_ = new DataModuleState(); private final Set<DisconnectListener> disconnectListeners_ = new HashSet<DisconnectListener>(); private ConnectionState connection_ = ConnectionState.INIT; public String hardwareId_; public String bootloaderId_; public String firmwareId_; public IncomingState() { for (int i = 0; i < intputPinStates_.length; ++i) { intputPinStates_[i] = new InputPinState(); } for (int i = 0; i < uartStates_.length; ++i) { uartStates_[i] = new DataModuleState(); } for (int i = 0; i < twiStates_.length; ++i) { twiStates_[i] = new DataModuleState(); } for (int i = 0; i < spiStates_.length; ++i) { spiStates_[i] = new DataModuleState(); } for (int i = 0; i < incapStates_.length; ++i) { incapStates_[i] = new DataModuleState(); } } synchronized public void waitConnectionEstablished() throws InterruptedException, ConnectionLostException { while (connection_ == ConnectionState.INIT) { wait(); } if (connection_ == ConnectionState.DISCONNECTED) { throw new ConnectionLostException(); } } synchronized public boolean waitForInterfaceSupport() throws InterruptedException, ConnectionLostException { if (connection_ == ConnectionState.INIT) { throw new IllegalStateException( "Have to connect before waiting for interface support"); } while (connection_ == ConnectionState.ESTABLISHED) { wait(); } if (connection_ == ConnectionState.DISCONNECTED) { throw new ConnectionLostException(); } return connection_ == ConnectionState.CONNECTED; } synchronized public void waitDisconnect() throws InterruptedException { while (connection_ != ConnectionState.DISCONNECTED) { wait(); } } public void addInputPinListener(int pin, InputPinListener listener) { intputPinStates_[pin].pushListener(listener); } public void addUartListener(int uartNum, DataModuleListener listener) { uartStates_[uartNum].pushListener(listener); } public void addTwiListener(int twiNum, DataModuleListener listener) { twiStates_[twiNum].pushListener(listener); } public void addIncapListener(int incapNum, DataModuleListener listener) { incapStates_[incapNum].pushListener(listener); } public void addIcspListener(DataModuleListener listener) { icspState_.pushListener(listener); } public void addSpiListener(int spiNum, DataModuleListener listener) { spiStates_[spiNum].pushListener(listener); } synchronized public void addDisconnectListener(DisconnectListener listener) throws ConnectionLostException { checkNotDisconnected(); disconnectListeners_.add(listener); } synchronized public void removeDisconnectListener( DisconnectListener listener) { if (connection_ != ConnectionState.DISCONNECTED) { disconnectListeners_.remove(listener); } } @Override public void handleConnectionLost() { // logMethod("handleConnectionLost"); synchronized (this) { connection_ = ConnectionState.DISCONNECTED; } for (DisconnectListener listener : disconnectListeners_) { listener.disconnected(); } disconnectListeners_.clear(); synchronized (this) { notifyAll(); } } @Override public void handleSoftReset() { // logMethod("handleSoftReset"); for (InputPinState pinState : intputPinStates_) { pinState.closeCurrentListener(); } for (DataModuleState uartState : uartStates_) { uartState.closeCurrentListener(); } for (DataModuleState twiState : twiStates_) { twiState.closeCurrentListener(); } for (DataModuleState spiState : spiStates_) { spiState.closeCurrentListener(); } for (DataModuleState incapState : incapStates_) { incapState.closeCurrentListener(); } icspState_.closeCurrentListener(); } @Override synchronized public void handleCheckInterfaceResponse(boolean supported) { // logMethod("handleCheckInterfaceResponse", supported); connection_ = supported ? ConnectionState.CONNECTED : ConnectionState.UNSUPPORTED_IID; notifyAll(); } @Override public void handleSetChangeNotify(int pin, boolean changeNotify) { // logMethod("handleSetChangeNotify", pin, changeNotify); if (changeNotify) { intputPinStates_[pin].openNextListener(); } else { intputPinStates_[pin].closeCurrentListener(); } } @Override public void handleRegisterPeriodicDigitalSampling(int pin, int freqScale) { // logMethod("handleRegisterPeriodicDigitalSampling", pin, freqScale); assert (false); } @Override public void handleAnalogPinStatus(int pin, boolean open) { // logMethod("handleAnalogPinStatus", pin, open); if (open) { intputPinStates_[pin].openNextListener(); } else { intputPinStates_[pin].closeCurrentListener(); } } @Override public void handleUartData(int uartNum, int numBytes, byte[] data) { // logMethod("handleUartData", uartNum, numBytes, data); uartStates_[uartNum].dataReceived(data, numBytes); } @Override public void handleUartOpen(int uartNum) { // logMethod("handleUartOpen", uartNum); uartStates_[uartNum].openNextListener(); } @Override public void handleUartClose(int uartNum) { // logMethod("handleUartClose", uartNum); uartStates_[uartNum].closeCurrentListener(); } @Override public void handleSpiOpen(int spiNum) { // logMethod("handleSpiOpen", spiNum); spiStates_[spiNum].openNextListener(); } @Override public void handleSpiClose(int spiNum) { // logMethod("handleSpiClose", spiNum); spiStates_[spiNum].closeCurrentListener(); } @Override public void handleI2cOpen(int i2cNum) { // logMethod("handleI2cOpen", i2cNum); twiStates_[i2cNum].openNextListener(); } @Override public void handleI2cClose(int i2cNum) { // logMethod("handleI2cClose", i2cNum); twiStates_[i2cNum].closeCurrentListener(); } @Override public void handleIcspOpen() { // logMethod("handleIcspOpen"); icspState_.openNextListener(); } @Override public void handleIcspClose() { // logMethod("handleIcspClose"); icspState_.closeCurrentListener(); } @Override public void handleEstablishConnection(byte[] hardwareId, byte[] bootloaderId, byte[] firmwareId) { hardwareId_ = new String(hardwareId); bootloaderId_ = new String(bootloaderId); firmwareId_ = new String(firmwareId); Log.i("IncomingState", "IOIO Connection established. Hardware ID: " + hardwareId_ + " Bootloader ID: " + bootloaderId_ + " Firmware ID: " + firmwareId_); synchronized (this) { connection_ = ConnectionState.ESTABLISHED; notifyAll(); } } @Override public void handleUartReportTxStatus(int uartNum, int bytesRemaining) { // logMethod("handleUartReportTxStatus", uartNum, bytesRemaining); uartStates_[uartNum].reportAdditionalBuffer(bytesRemaining); } @Override public void handleI2cReportTxStatus(int i2cNum, int bytesRemaining) { // logMethod("handleI2cReportTxStatus", i2cNum, bytesRemaining); twiStates_[i2cNum].reportAdditionalBuffer(bytesRemaining); } @Override public void handleSpiData(int spiNum, int ssPin, byte[] data, int dataBytes) { // logMethod("handleSpiData", spiNum, ssPin, data, dataBytes); spiStates_[spiNum].dataReceived(data, dataBytes); } @Override public void handleIcspReportRxStatus(int bytesRemaining) { // logMethod("handleIcspReportRxStatus", bytesRemaining); icspState_.reportAdditionalBuffer(bytesRemaining); } @Override public void handleReportDigitalInStatus(int pin, boolean level) { // logMethod("handleReportDigitalInStatus", pin, level); intputPinStates_[pin].setValue(level ? 1 : 0); } @Override public void handleReportPeriodicDigitalInStatus(int frameNum, boolean[] values) { // logMethod("handleReportPeriodicDigitalInStatus", frameNum, values); } @Override public void handleReportAnalogInStatus(int pins[], int values[]) { // logMethod("handleReportAnalogInStatus", pins, values); for (int i = 0; i < pins.length; ++i) { intputPinStates_[pins[i]].setValue(values[i]); } } @Override public void handleSpiReportTxStatus(int spiNum, int bytesRemaining) { // logMethod("handleSpiReportTxStatus", spiNum, bytesRemaining); spiStates_[spiNum].reportAdditionalBuffer(bytesRemaining); } @Override public void handleI2cResult(int i2cNum, int size, byte[] data) { // logMethod("handleI2cResult", i2cNum, size, data); twiStates_[i2cNum].dataReceived(data, size); } @Override public void handleIncapReport(int incapNum, int size, byte[] data) { // logMethod("handleIncapReport", incapNum, size, data); incapStates_[incapNum].dataReceived(data, size); } @Override public void handleIncapClose(int incapNum) { // logMethod("handleIncapClose", incapNum); incapStates_[incapNum].closeCurrentListener(); } @Override public void handleIncapOpen(int incapNum) { // logMethod("handleIncapOpen", incapNum); incapStates_[incapNum].openNextListener(); } @Override public void handleIcspResult(int size, byte[] data) { // logMethod("handleIcspResult", size, data); icspState_.dataReceived(data, size); } private void checkNotDisconnected() throws ConnectionLostException { if (connection_ == ConnectionState.DISCONNECTED) { throw new ConnectionLostException(); } } // private void logMethod(String name, Object... args) { // StringBuffer msg = new StringBuffer(name); // msg.append('('); // for (int i = 0; i < args.length; ++i) { // if (i != 0) { // msg.append(", "); // } // msg.append(args[i]); // } // msg.append(')'); // // Log.v("IncomingState", msg.toString()); // } }