Started to update the library to API 3.22 RC

Change-Id: I8eaf3151b1d405799eb582564f0e1e8fbcb7744c
Signed-off-by: Ricky Barrette <rickbarrette@gmail.com>
This commit is contained in:
2012-03-12 00:53:13 -04:00
parent 2b1ee23cff
commit 5f51c1caa1
93 changed files with 4500 additions and 2994 deletions

Binary file not shown.

View File

@@ -52,10 +52,42 @@ import ioio.lib.api.exception.ConnectionLostException;
* Typical usage:
*
* <pre>
* {@code
* AnalogInput potentiometer = ioio.openAnalogInput(40);
* float value = potentiometer.read();
* ...
* potentiometer.close(); // pin 40 can now be used for something else.
* }
* </pre>
* <p>
* An alternate usage allows reading periodically sampled data without missing
* samples. The {@link #setBuffer(int)} method must first be called, for setting
* up an internal buffer for queuing samples. Then, samples can be obtained by
* calling {@link #readBuffered()} or {@link #getVoltageBuffered()}. These
* methods will block until a sample is available. If this is undesirable, the
* {@link #available()} method can be called first to check how many samples are
* ready in the buffer. In case the buffer overflows, as result of the client
* not reading fast enough, old samples will be dropped, and the client can
* check {@link #getOverflowCount()} to determine how many samples have been
* lost. The sample rate used for capturing samples can be obtained by calling
* {@link #getSampleRate()}.
* <p>
* The non-buffered versions of the read methods will still behave normally when
* buffering is enabled. The {@link #read()} and {@link #getVoltage()} methods
* will always return the most recent value, regardless of the buffer state.
* <p>
* Typical usage:
*
* <pre>
* AnalogInput potentiometer = ioio.openAnalogInput(40);
* potentiometer.setBuffer(256);
* for (int i = 0; i < 1024; ++i) {
* // next line will block until at least one sample is available
* float sample = potentiometer.readBuffered();
* ...
* }
* ...
* potentiometer.close(); // pin 40 can now be used for something else.
* </pre>
*
* @see IOIO#openAnalogInput(int)
@@ -72,8 +104,10 @@ public interface AnalogInput extends Closeable {
* If a scaled value is desired, consider using {@link #read()}.
*
* @return The voltage, in Volt units.
* @throws InterruptedException The calling thread has been interrupted.
* @throws ConnectionLostException The connection with the IOIO is lost.
* @throws InterruptedException
* The calling thread has been interrupted.
* @throws ConnectionLostException
* The connection with the IOIO is lost.
* @see #read()
*/
public float getVoltage() throws InterruptedException,
@@ -81,6 +115,7 @@ public interface AnalogInput extends Closeable {
/**
* Gets the maximum value against which {@link #read()} values are scaled.
*
* @return The voltage, in Volts.
*/
public float getReference();
@@ -96,9 +131,95 @@ public interface AnalogInput extends Closeable {
* If an absolute value is desired, consider using {@link #getVoltage()}.
*
* @return The voltage, in scaled units.
* @throws InterruptedException The calling thread has been interrupted.
* @throws ConnectionLostException The connection with the IOIO is lost.
* @throws InterruptedException
* The calling thread has been interrupted.
* @throws ConnectionLostException
* The connection with the IOIO is lost.
* @see #getVoltage()
*/
public float read() throws InterruptedException, ConnectionLostException;
/**
* Initializes or destroys an internal buffer, used for queuing sampled
* data. When called with a positive argument, an internal buffer will be
* created, and start storing sampled data. The client can then call
* {@link #readBuffered()} or {@link #getVoltageBuffered()} for obtaining
* buffered samples.
* <p>
* When called with argument of 0, the internal buffer is destroyed.
*
* @param capacity
* The maximum number of unread samples that can be buffered
* before overflow occurs.
* @throws ConnectionLostException
* The connection with the IOIO is lost.
*/
public void setBuffer(int capacity) throws ConnectionLostException;
/**
* Gets the number of samples that have been dropped as result of overflow,
* since {@link #setBuffer(int)} has been called.
*
* @return The number of dropped samples.
* @throws ConnectionLostException
* The connection with the IOIO is lost.
*/
public int getOverflowCount() throws ConnectionLostException;
/**
* Gets the number of samples currently in the buffer. Reading that many
* samples is guaranteed not to block.
*
* @return The number of samples available in the buffer.
* @throws ConnectionLostException
* The connection with the IOIO is lost.
*/
public int available() throws ConnectionLostException;
/**
* Read a sample from the internal buffer. This method will block until at
* least one sample is available, the instance is closed (via
* {@link #close()}), the thread is interrupted (via
* {@link Thread#interrupt()} or connection is lost. {@link #setBuffer(int)}
* must be called prior to this method for setting up an internal buffer for
* storing samples.
*
* @see #getVoltageBuffered()
* @return The earliest (oldest) sample available in the buffer, scaled to
* the range [0,1].
* @throws InterruptedException
* The calling thread has been interrupted.
* @throws ConnectionLostException
* The connection with the IOIO is lost.
*/
public float readBuffered() throws InterruptedException,
ConnectionLostException;
/**
* Read a sample from the internal buffer. This method will block until at
* least one sample is available, the instance is closed (via
* {@link #close()}), the thread is interrupted (via
* {@link Thread#interrupt()} or connection is lost. {@link #setBuffer(int)}
* must be called prior to this method for setting up an internal buffer for
* storing samples.
*
* @see #readBuffered()
* @return The earliest (oldest) sample available in the buffer, in Volt
* units.
* @throws InterruptedException
* The calling thread has been interrupted.
* @throws ConnectionLostException
* The connection with the IOIO is lost.
*/
public float getVoltageBuffered() throws InterruptedException,
ConnectionLostException;
/**
* Gets the sample rate used for obtaining buffered samples.
*
* @return The sample rate, in Hz units.
* @throws ConnectionLostException
* The connection with the IOIO is lost.
*/
public float getSampleRate() throws ConnectionLostException;
}

View File

@@ -34,7 +34,7 @@ import ioio.lib.api.exception.ConnectionLostException;
* A pin used for digital input.
* <p>
* A digital input pin can be used to read logic-level signals. DigitalInput
* instances are obtained by calling {@link IOIO#openDigitalInput(Spec)}.
* instances are obtained by calling {@link IOIO#openDigitalInput(DigitalInput.Spec)}.
* <p>
* The value of the pin is obtained by calling {@link #read()}. It is also
* possible for the client to block until a certain level is sensed, by using

View File

@@ -35,7 +35,7 @@ import ioio.lib.api.exception.ConnectionLostException;
* <p>
* A digital input pin can be used to generate logic-level signals.
* DigitalOutput instances are obtained by calling
* {@link IOIO#openDigitalOutput(Spec, boolean)}.
* {@link IOIO#openDigitalOutput(DigitalOutput.Spec, boolean)}.
* <p>
* The value of the pin is set by calling {@link #write(boolean)}.
* <p>
@@ -101,7 +101,7 @@ public interface DigitalOutput extends Closeable {
/**
* Shorthand for Spec(pin, Mode.NORMAL).
*
* @see #Spec(int, Mode)
* @see DigitalOutput.Spec#Spec(int, DigitalOutput.Spec.Mode)
*/
public Spec(int pin) {
this(pin, Mode.NORMAL);

View File

@@ -109,6 +109,20 @@ public interface IOIO {
IOIOLIB_VER
}
/**
* A state of a IOIO instance.
*/
public enum State {
/** Connection not yet established. */
INIT,
/** Connected. */
CONNECTED,
/** Connection established, incompatible firmware detected. */
INCOMPATIBLE,
/** Disconnected. Instance is useless. */
DEAD
}
/**
* Establishes connection with the IOIO board.
* <p>
@@ -157,6 +171,13 @@ public interface IOIO {
*/
public void waitForDisconnect() throws InterruptedException;
/**
* Gets the connections state.
*
* @return The connection state.
*/
public State getState();
/**
* Resets the entire state (returning to initial state), without dropping
* the connection.
@@ -420,7 +441,8 @@ public interface IOIO {
* with the given mode.
*
* @see #openPulseInput(ioio.lib.api.DigitalInput.Spec,
* ioio.lib.api.PulseInput.ClockRate, PulseMode, boolean)
* ioio.lib.api.PulseInput.ClockRate,
* ioio.lib.api.PulseInput.PulseMode, boolean)
*/
public PulseInput openPulseInput(int pin, PulseMode mode)
throws ConnectionLostException;
@@ -478,12 +500,12 @@ public interface IOIO {
/**
* Shorthand for
* {@link #openUart(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, int, Parity, StopBits)}
* {@link #openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity, Uart.StopBits)}
* , where the input pins use their default specs. {@link #INVALID_PIN} can
* be used on either pin if a TX- or RX-only UART is needed.
*
* @see #openUart(ioio.lib.api.DigitalInput.Spec,
* ioio.lib.api.DigitalOutput.Spec, int, Parity, StopBits)
* @see #openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity,
* Uart.StopBits)
*/
public Uart openUart(int rx, int tx, int baud, Parity parity,
StopBits stopbits) throws ConnectionLostException;
@@ -552,9 +574,12 @@ public interface IOIO {
DigitalOutput.Spec[] slaveSelect, SpiMaster.Config config)
throws ConnectionLostException;
/**
* Shorthand for {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config),
* where the pins are all open with the default modes and default configuration values are used.
/**
* Shorthand for
* {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)}
* , where the pins are all open with the default modes and default
* configuration values are used.
*
* @see #openSpiMaster(ioio.lib.api.DigitalInput.Spec,
* ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec,
* ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)
@@ -564,10 +589,12 @@ public interface IOIO {
throws ConnectionLostException;
/**
* Shorthand for {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config),
* where the MISO pins is opened with pull up, and the other pins are open
* with the default modes and default configuration values are used.
* In this version, a single slave is used.
* Shorthand for
* {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)}
* , where the MISO pins is opened with pull up, and the other pins are open
* with the default modes and default configuration values are used. In this
* version, a single slave is used.
*
* @see #openSpiMaster(ioio.lib.api.DigitalInput.Spec,
* ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec,
* ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)
@@ -635,4 +662,35 @@ public interface IOIO {
* method.
*/
public IcspMaster openIcspMaster() throws ConnectionLostException;
/**
* Start a batch of operations. This is strictly an optimization and will
* not change functionality: if the client knows that a sequence of several
* IOIO operations are going to be performed immediately following each
* other, a call to {@link #beginBatch()} before the sequence and
* {@link #endBatch()} after the sequence will cause the operations to be
* grouped into one transfer to the IOIO, thus reducing latency. A matching
* {@link #endBatch()} operation must always follow, or otherwise no
* operation will ever be actually executed. {@link #beginBatch()} /
* {@link #endBatch()} blocks may be nested - the transfer will occur when
* the outermost {@link #endBatch()} is invoked. Note that it is not
* guaranteed that no transfers will happen while inside a batch - it should
* be treated as a hint. Code running inside the block must be quick as it
* blocks <b>all</b> transfers to the IOIO, including those performed from
* other threads.
*
* @throws ConnectionLostException
* Connection was lost before or during the execution of this
* method.
*/
public void beginBatch() throws ConnectionLostException;
/**
* End a batch of operations. For explanation, see {@link #beginBatch()}.
*
* @throws ConnectionLostException
* Connection was lost before or during the execution of this
* method.
*/
public void endBatch() throws ConnectionLostException;
}

0
IOIOLib/src/ioio/lib/api/IOIOConnection.java Executable file → Normal file
View File

View File

@@ -28,10 +28,14 @@
*/
package ioio.lib.api;
import java.lang.reflect.Constructor;
import ioio.lib.impl.IOIOImpl;
import ioio.lib.impl.SocketIOIOConnection;
import ioio.lib.spi.IOIOConnectionFactory;
import ioio.lib.util.IOIOConnectionRegistry;
import java.util.Collection;
import java.util.NoSuchElementException;
import android.util.Log;
/**
* Factory class for creating instances of the IOIO interface.
@@ -55,9 +59,6 @@ import ioio.lib.impl.SocketIOIOConnection;
* </pre>
*/
public class IOIOFactory {
/** The TCP port used for communicating with the IOIO board. */
public static final int IOIO_PORT = 4545;
/**
* Create a IOIO instance. This specific implementation creates a IOIO
* instance which works with the actual IOIO board connected via a TCP
@@ -66,11 +67,13 @@ public class IOIOFactory {
* @return The IOIO instance.
*/
public static IOIO create() {
Collection<IOIOConnectionFactory> factories = IOIOConnectionRegistry
.getConnectionFactories();
try {
return create(SocketIOIOConnection.class.getName(), IOIO_PORT);
} catch (ClassNotFoundException e) {
// we shouldn't get here - this class must always exist.
throw new RuntimeException("Something is very wrong here");
return create(factories.iterator().next().createConnection());
} catch (NoSuchElementException e) {
Log.e(TAG, "No connection is available. This shouldn't happen.");
throw e;
}
}
@@ -79,43 +82,14 @@ public class IOIOFactory {
* This method should be used for establishing a non-standard connection to
* the IOIO board.
*
* @param connectionClassName
* The name of the connection class. Must have a public default
* constructor.
* @param connection
* An instance of a IOIO connection.
*
* @return The IOIO instance.
* @throws ClassNotFoundException The given class name was not found.
*/
public static IOIO create(String connectionClassName, Object... args) throws ClassNotFoundException {
IOIOConnection connection = createConnectionDynamically(connectionClassName, args);
return create(connection);
}
public static IOIO create(IOIOConnection connection) {
return new IOIOImpl(connection);
}
public static IOIOConnection createConnectionDynamically(
String connectionClassName, Object... args)
throws ClassNotFoundException {
Class<?> cls;
cls = Class.forName(connectionClassName);
Object instance;
try {
Class<?>[] argTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; ++i) {
argTypes[i] = args[i].getClass();
}
Constructor<?> constructor = cls.getConstructor(argTypes);
instance = constructor.newInstance(args);
} catch (Exception e) {
throw new IllegalArgumentException(
"Provided class does not have a public ctor with the right signature", e);
}
if (!(instance instanceof IOIOConnection)) {
throw new IllegalArgumentException(
"Provided class does not implement IOIOConnection");
}
return (IOIOConnection) instance;
}
private static final String TAG = "IOIOFactory";
}

0
IOIOLib/src/ioio/lib/api/IcspMaster.java Executable file → Normal file
View File

2
IOIOLib/src/ioio/lib/api/PulseInput.java Executable file → Normal file
View File

@@ -46,7 +46,7 @@ import ioio.lib.api.exception.ConnectionLostException;
* for measuring a turning shaft's speed.
* <p>
* {@link PulseInput} instances are obtained by calling
* {@link IOIO#openPulseInput(ioio.lib.api.DigitalInput.Spec, ClockRate, PulseMode, boolean)}
* {@link IOIO#openPulseInput(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.PulseInput.ClockRate, ioio.lib.api.PulseInput.PulseMode, boolean)}
* . When created, some important configuration decisions have to be made: the
* precision (single or double), the clock rate and the mode of operation. Modes
* are straightforward: {@link PulseMode#POSITIVE} is used for measuring a

13
IOIOLib/src/ioio/lib/api/SpiMaster.java Executable file → Normal file
View File

@@ -28,7 +28,6 @@
*/
package ioio.lib.api;
import ioio.lib.api.DigitalInput.Spec;
import ioio.lib.api.exception.ConnectionLostException;
/**
@@ -41,7 +40,7 @@ import ioio.lib.api.exception.ConnectionLostException;
* between this slave and a respective pin on the master. The MISO line should
* operate in pull-up mode, using either the internal pull-up or an external
* resistor. SpiMaster instances are obtained by calling
* {@link IOIO#openSpiMaster(Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], Config)}.
* {@link IOIO#openSpiMaster(DigitalInput.Spec, DigitalOutput.Spec, DigitalOutput.Spec, DigitalOutput.Spec[], SpiMaster.Config)}.
* <p>
* The SPI protocol is comprised of simultaneous sending and receiving of data
* between the bus master and a single slave. By the very nature of this
@@ -90,9 +89,11 @@ import ioio.lib.api.exception.ConnectionLostException;
* spi.writeRead(request, 2, 4, response, 3);
* ...
* spi.close(); // free SPI module and pins
* }</pre>
* }
* </pre>
*
* @see IOIO#openSpiMaster(Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], Config)
* @see IOIO#openSpiMaster(DigitalInput.Spec, DigitalOutput.Spec,
* DigitalOutput.Spec, DigitalOutput.Spec[], SpiMaster.Config)
*/
public interface SpiMaster extends Closeable {
/** Possible data rates for SPI, in Hz. */
@@ -148,7 +149,7 @@ public interface SpiMaster extends Closeable {
* Constructor with common defaults. Equivalent to Config(rate, false,
* false)
*
* @see #Config(Rate, boolean, boolean)
* @see SpiMaster.Config#Config(SpiMaster.Config.Rate, boolean, boolean)
*/
public Config(Rate rate) {
this(rate, false, false);
@@ -165,7 +166,7 @@ public interface SpiMaster extends Closeable {
* @param slave
* The slave index. It is determined by the index of its
* slave-select pin, as per the array passed to
* {@link IOIO#openSpiMaster(Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], Config)}
* {@link IOIO#openSpiMaster(DigitalInput.Spec, DigitalOutput.Spec, DigitalOutput.Spec, DigitalOutput.Spec[], SpiMaster.Config)}
* .
* @param writeData
* A byte array of data to write. May be null if writeSize is 0.

4
IOIOLib/src/ioio/lib/api/TwiMaster.java Executable file → Normal file
View File

@@ -39,7 +39,7 @@ import ioio.lib.api.exception.ConnectionLostException;
* requires a physical connection of two lines (SDA, SCL) shared by all the bus
* nodes, where the SDA is open-drain and externally pulled-up. TwiMaster
* instances are obtained by calling
* {@link IOIO#openTwiMaster(int, Rate, boolean)}.
* {@link IOIO#openTwiMaster(int, ioio.lib.api.TwiMaster.Rate, boolean)}.
* <p>
* TWI is the generic name for the specific I2C and SMBus protocols, differing
* mostly by the voltage levels they require. This module supports both.
@@ -79,7 +79,7 @@ import ioio.lib.api.exception.ConnectionLostException;
* twi.close(); // free TWI module and pins
* }</pre>
*
* @see IOIO#openTwiMaster(int, Rate, boolean)
* @see IOIO#openTwiMaster(int, ioio.lib.api.TwiMaster.Rate, boolean)
*/
public interface TwiMaster extends Closeable {
enum Rate {

View File

@@ -28,7 +28,6 @@
*/
package ioio.lib.api;
import ioio.lib.api.DigitalInput.Spec;
import ioio.lib.api.exception.ConnectionLostException;
import java.io.InputStream;
@@ -41,7 +40,7 @@ import java.io.OutputStream;
* asynchronous point-to-point data transfer. It typically serves for opening
* consoles or as a basis for higher-level protocols, such as MIDI, RS-232 and
* RS-485. Uart instances are obtained by calling
* {@link IOIO#openUart(Spec, ioio.lib.api.DigitalOutput.Spec, int, Parity, StopBits)}.
* {@link IOIO#openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity, Uart.StopBits)}.
* <p>
* The UART protocol is completely symmetric - there is no "master" and "slave"
* at this layer. Each end may send any number of bytes at arbitrary times,
@@ -70,8 +69,8 @@ import java.io.OutputStream;
* uart.close(); // free UART module and pins
* </pre>
*
* @see IOIO#openUart(Spec, ioio.lib.api.DigitalOutput.Spec, int, Parity,
* StopBits)
* @see IOIO#openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity,
* Uart.StopBits)
*/
public interface Uart extends Closeable {
/** Parity-bit mode. */

View File

View File

@@ -1,3 +1,32 @@
/*
* 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.bluetooth;
import ioio.lib.api.IOIOConnection;
@@ -8,43 +37,41 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Build;
import android.util.Log;
public class BluetoothIOIOConnection implements IOIOConnection {
private static final String TAG = "BluetoothIOIOConnection";
private BluetoothSocket socket_ = null;
private boolean disconnect_ = false;
private final BluetoothDevice device_;
private final String name_;
private final String address_;
public BluetoothIOIOConnection(String name, String address) {
name_ = name;
address_ = address;
public BluetoothIOIOConnection(BluetoothDevice device) {
device_ = device;
name_ = device.getName();
address_ = device.getAddress();
}
@Override
public void waitForConnect() throws ConnectionLostException {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
final BluetoothDevice ioioDevice = adapter.getRemoteDevice(address_);
synchronized (this) {
if (disconnect_) {
throw new ConnectionLostException();
}
Log.v(TAG, name_ + " Creating socket");
try {
socket_ = createSocket(ioioDevice);
socket_ = createSocket(device_);
} catch (IOException e) {
throw new ConnectionLostException(e);
}
Log.v(TAG, name_ + " Created socket");
}
// keep trying to connect as long as we're not aborting
while (true) {
try {
Log.v(TAG, name_ + "Connecting");
Log.v(TAG, "Attempting to connect to Bluetooth device: " + name_);
socket_.connect();
Log.v(TAG, "Established connection to device " + name_
+ " address: " + address_);
@@ -61,17 +88,15 @@ public class BluetoothIOIOConnection implements IOIOConnection {
}
}
public BluetoothSocket createSocket(final BluetoothDevice device)
public static BluetoothSocket createSocket(final BluetoothDevice device)
throws IOException {
try {
// We're trying to create an insecure socket, which is
// only supported
// in API 10 and up. If we fail, we try a secure socket
// with is in API
// 7 and up.
if (Build.VERSION.SDK_INT >= 10 ) {
// We're trying to create an insecure socket, which is only
// supported in API 10 and up. Otherwise, we try a secure socket
// which is in API 7 and up.
return device.createInsecureRfcommSocketToServiceRecord(UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB"));
} catch (NoSuchMethodError e) {
} else {
return device.createRfcommSocketToServiceRecord(UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB"));
}
@@ -82,7 +107,7 @@ public class BluetoothIOIOConnection implements IOIOConnection {
if (disconnect_) {
return;
}
Log.d(TAG, "Client initiated disconnect");
Log.v(TAG, "Client initiated disconnect");
disconnect_ = true;
if (socket_ != null) {
try {

View File

@@ -0,0 +1,93 @@
/*
* 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.bluetooth;
import ioio.lib.api.IOIOConnection;
import ioio.lib.spi.IOIOConnectionBootstrap;
import ioio.lib.spi.IOIOConnectionFactory;
import ioio.lib.spi.NoRuntimeSupportException;
import java.util.Collection;
import java.util.Set;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.util.Log;
public class BluetoothIOIOConnectionBootstrap implements
IOIOConnectionBootstrap {
private static final String TAG = "BluetoothIOIOConnectionDiscovery";
private final BluetoothAdapter adapter_;
public BluetoothIOIOConnectionBootstrap() throws NoRuntimeSupportException {
try {
adapter_ = BluetoothAdapter.getDefaultAdapter();
} catch (NoClassDefFoundError e) {
throw new NoRuntimeSupportException(
"Bluetooth is not supported on this device.");
}
}
@Override
public void getFactories(Collection<IOIOConnectionFactory> result) {
try {
Set<BluetoothDevice> bondedDevices = adapter_.getBondedDevices();
for (final BluetoothDevice device : bondedDevices) {
if (device.getName().startsWith("IOIO")) {
result.add(new IOIOConnectionFactory() {
@Override
public String getType() {
return BluetoothIOIOConnection.class
.getCanonicalName();
}
@Override
public Object getExtra() {
return new Object[] { device.getName(),
device.getAddress() };
}
@Override
public IOIOConnection createConnection() {
return new BluetoothIOIOConnection(device);
}
});
}
}
} catch (SecurityException e) {
Log.e(TAG,
"Did you forget to declare uses-permission of android.permission.BLUETOOTH?");
throw e;
} catch (NoClassDefFoundError e) {
Log.w(TAG, "Bluetooth is not supported on this device.", e);
}
}
}

View File

@@ -1,37 +0,0 @@
package ioio.lib.bluetooth;
import ioio.lib.util.IOIOConnectionDiscovery;
import java.util.Collection;
import java.util.Set;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.util.Log;
public class BluetoothIOIOConnectionDiscovery implements
IOIOConnectionDiscovery {
private static final String TAG = "BluetoothIOIOConnectionDiscovery";
@Override
public void getSpecs(Collection<IOIOConnectionSpec> result) {
try {
final BluetoothAdapter adapter = BluetoothAdapter
.getDefaultAdapter();
Set<BluetoothDevice> bondedDevices = adapter.getBondedDevices();
for (BluetoothDevice device : bondedDevices) {
if (device.getName().startsWith("IOIO")) {
result.add(new IOIOConnectionSpec(
BluetoothIOIOConnection.class.getName(),
new Object[] { device.getName(),
device.getAddress() }));
}
}
} catch (SecurityException e) {
Log.e(TAG,
"Did you forget to declare uses-permission of android.permission.BLUETOOTH?");
throw e;
}
}
}

3
IOIOLib/src/ioio/lib/impl/AbstractPin.java Executable file → Normal file
View File

@@ -30,7 +30,7 @@ package ioio.lib.impl;
import ioio.lib.api.exception.ConnectionLostException;
public abstract class AbstractPin extends AbstractResource {
abstract class AbstractPin extends AbstractResource {
protected final int pinNum_;
AbstractPin(IOIOImpl ioio, int pinNum) throws ConnectionLostException {
@@ -38,6 +38,7 @@ public abstract class AbstractPin extends AbstractResource {
pinNum_ = pinNum;
}
@Override
synchronized public void close() {
super.close();
ioio_.closePin(pinNum_);

3
IOIOLib/src/ioio/lib/impl/AbstractResource.java Executable file → Normal file
View File

@@ -32,7 +32,7 @@ import ioio.lib.api.exception.ConnectionLostException;
import ioio.lib.impl.IncomingState.DisconnectListener;
import ioio.lib.api.Closeable;
public class AbstractResource implements Closeable, DisconnectListener {
class AbstractResource implements Closeable, DisconnectListener {
enum State {
OPEN,
CLOSED,
@@ -53,6 +53,7 @@ public class AbstractResource implements Closeable, DisconnectListener {
}
}
@Override
synchronized public void close() {
if (state_ == State.CLOSED) {
throw new IllegalStateException("Trying to use a closed resouce");

103
IOIOLib/src/ioio/lib/impl/AnalogInputImpl.java Executable file → Normal file
View File

@@ -28,22 +28,31 @@
*/
package ioio.lib.impl;
import java.io.IOException;
import ioio.lib.api.AnalogInput;
import ioio.lib.api.exception.ConnectionLostException;
import ioio.lib.impl.IncomingState.InputPinListener;
public class AnalogInputImpl extends AbstractPin implements AnalogInput, InputPinListener {
import java.io.IOException;
class AnalogInputImpl extends AbstractPin implements AnalogInput,
InputPinListener {
private int value_;
private boolean valid_ = false;
short[] buffer_;
int bufferSize_;
int bufferCapacity_;
int bufferReadCursor_;
int bufferWriteCursor_;
int bufferOverflowCount_ = 0;
AnalogInputImpl(IOIOImpl ioio, int pin) throws ConnectionLostException {
super(ioio, pin);
}
@Override
public float getVoltage() throws InterruptedException, ConnectionLostException {
public float getVoltage() throws InterruptedException,
ConnectionLostException {
return read() * getReference();
}
@@ -55,18 +64,20 @@ public class AnalogInputImpl extends AbstractPin implements AnalogInput, InputPi
@Override
synchronized public void setValue(int value) {
// Log.v("AnalogInputImpl", "Pin " + pinNum_ + " value is " + value);
assert(value >= 0 || value < 1024);
assert (value >= 0 && value < 1024);
value_ = value;
if (!valid_) {
valid_ = true;
notifyAll();
}
bufferPush((short) value);
}
@Override
synchronized public float read() throws InterruptedException, ConnectionLostException {
synchronized public float read() throws InterruptedException,
ConnectionLostException {
checkState();
while (!valid_ && state_ != State.DISCONNECTED) {
while (!valid_ && state_ == State.OPEN) {
wait();
}
checkState();
@@ -87,4 +98,82 @@ public class AnalogInputImpl extends AbstractPin implements AnalogInput, InputPi
} catch (IOException e) {
}
}
@Override
public synchronized void setBuffer(int capacity)
throws ConnectionLostException {
checkState();
if (capacity <= 0) {
buffer_ = null;
} else {
buffer_ = new short[capacity];
}
bufferCapacity_ = capacity;
bufferSize_ = 0;
bufferReadCursor_ = 0;
bufferWriteCursor_ = 0;
bufferOverflowCount_ = 0;
}
@Override
public float readBuffered() throws InterruptedException,
ConnectionLostException {
checkState();
return (float) bufferPull() / 1023.0f;
}
@Override
public float getVoltageBuffered() throws InterruptedException,
ConnectionLostException {
return readBuffered() * getReference();
}
private void bufferPush(short value) {
if (buffer_ == null) {
return;
}
if (bufferSize_ == bufferCapacity_) {
++bufferOverflowCount_;
} else {
++bufferSize_;
}
buffer_[bufferWriteCursor_++] = value;
if (bufferWriteCursor_ == bufferCapacity_) {
bufferWriteCursor_ = 0;
}
notifyAll();
}
private synchronized short bufferPull() throws InterruptedException,
ConnectionLostException {
if (buffer_ == null) {
throw new IllegalStateException(
"Need to call setBuffer() before reading buffered values.");
}
while (bufferSize_ == 0 && state_ == State.OPEN) {
wait();
}
checkState();
short result = buffer_[bufferReadCursor_++];
if (bufferReadCursor_ == bufferCapacity_) {
bufferReadCursor_ = 0;
}
--bufferSize_;
return result;
}
@Override
public int getOverflowCount() throws ConnectionLostException {
return bufferOverflowCount_;
}
@Override
public float getSampleRate() throws ConnectionLostException {
return 1000.0f;
}
@Override
public int available() throws ConnectionLostException {
return bufferSize_;
}
}

0
IOIOLib/src/ioio/lib/impl/Constants.java Executable file → Normal file
View File

3
IOIOLib/src/ioio/lib/impl/DigitalInputImpl.java Executable file → Normal file
View File

@@ -34,7 +34,7 @@ import ioio.lib.impl.IncomingState.InputPinListener;
import java.io.IOException;
public class DigitalInputImpl extends AbstractPin implements DigitalInput,
class DigitalInputImpl extends AbstractPin implements DigitalInput,
InputPinListener {
private boolean value_;
private boolean valid_ = false;
@@ -54,6 +54,7 @@ public class DigitalInputImpl extends AbstractPin implements DigitalInput,
notifyAll();
}
@Override
synchronized public void waitForValue(boolean value)
throws InterruptedException, ConnectionLostException {
checkState();

18
IOIOLib/src/ioio/lib/impl/DigitalOutputImpl.java Executable file → Normal file
View File

@@ -33,18 +33,24 @@ import ioio.lib.api.exception.ConnectionLostException;
import java.io.IOException;
public class DigitalOutputImpl extends AbstractPin implements DigitalOutput {
DigitalOutputImpl(IOIOImpl ioio, int pin) throws ConnectionLostException {
class DigitalOutputImpl extends AbstractPin implements DigitalOutput {
boolean value_;
DigitalOutputImpl(IOIOImpl ioio, int pin, boolean startValue) throws ConnectionLostException {
super(ioio, pin);
value_ = startValue;
}
@Override
synchronized public void write(boolean val) throws ConnectionLostException {
checkState();
try {
ioio_.protocol_.setDigitalOutLevel(pinNum_, val);
} catch (IOException e) {
throw new ConnectionLostException(e);
if (val != value_) {
try {
ioio_.protocol_.setDigitalOutLevel(pinNum_, val);
value_ = val;
} catch (IOException e) {
throw new ConnectionLostException(e);
}
}
}
}

View File

@@ -33,7 +33,7 @@ import java.io.OutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class FlowControlledOutputStream extends OutputStream {
class FlowControlledOutputStream extends OutputStream {
interface Sender {
void send(byte[] data, int size);
}
@@ -91,15 +91,14 @@ public class FlowControlledOutputStream extends OutputStream {
@Override
synchronized public void close() {
if (closed_) {
return;
}
closed_ = true;
notifyAll();
thread_.interrupt();
}
synchronized public void kill() {
thread_.interrupt();
}
class FlushThread extends Thread {
@Override
public void run() {

View File

@@ -32,7 +32,7 @@ import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class FlowControlledPacketSender {
class FlowControlledPacketSender {
interface Packet {
int getSize();
}

80
IOIOLib/src/ioio/lib/impl/IOIOImpl.java Executable file → Normal file
View File

@@ -55,10 +55,7 @@ import android.util.Log;
public class IOIOImpl implements IOIO, DisconnectListener {
private static final String TAG = "IOIOImpl";
enum State {
INIT, CONNECTED, INCOMPATIBLE, DEAD
}
private boolean disconnect_ = false;
private static final byte[] REQUIRED_INTERFACE_ID = new byte[] { 'I', 'O',
'I', 'O', '0', '0', '0', '3' };
@@ -86,7 +83,7 @@ public class IOIOImpl implements IOIO, DisconnectListener {
}
@Override
synchronized public void waitForConnect() throws ConnectionLostException,
public void waitForConnect() throws ConnectionLostException,
IncompatibilityException {
if (state_ == State.CONNECTED) {
return;
@@ -98,19 +95,26 @@ public class IOIOImpl implements IOIO, DisconnectListener {
Log.d(TAG, "Waiting for IOIO connection");
try {
try {
Log.d(TAG, "Waiting for underlying connection");
Log.v(TAG, "Waiting for underlying connection");
connection_.waitForConnect();
protocol_ = new IOIOProtocol(connection_.getInputStream(),
connection_.getOutputStream(), incomingState_);
synchronized (this) {
if (disconnect_) {
throw new ConnectionLostException();
}
protocol_ = new IOIOProtocol(connection_.getInputStream(),
connection_.getOutputStream(), incomingState_);
// Once this block exits, a disconnect will also involve
// softClose().
}
} catch (ConnectionLostException e) {
incomingState_.handleConnectionLost();
throw e;
}
Log.d(TAG, "Waiting for handshake");
Log.v(TAG, "Waiting for handshake");
incomingState_.waitConnectionEstablished();
Log.d(TAG, "Querying for required interface ID");
Log.v(TAG, "Querying for required interface ID");
checkInterfaceVersion();
Log.d(TAG, "Required interface ID is supported");
Log.v(TAG, "Required interface ID is supported");
state_ = State.CONNECTED;
Log.i(TAG, "IOIO connection established");
} catch (ConnectionLostException e) {
@@ -123,21 +127,44 @@ public class IOIOImpl implements IOIO, DisconnectListener {
}
@Override
public void disconnect() {
public synchronized void disconnect() {
Log.d(TAG, "Client requested disconnect.");
if (disconnect_) {
return;
}
disconnect_ = true;
try {
if (protocol_ != null) {
protocol_.softClose();
}
} catch (IOException e) {
Log.e(TAG, "Soft close failed", e);
}
connection_.disconnect();
}
@Override
public void disconnected() {
public synchronized void disconnected() {
state_ = State.DEAD;
if (disconnect_) {
return;
}
Log.d(TAG, "Physical disconnect.");
disconnect_ = true;
// The IOIOConnection doesn't necessarily know about the disconnect
disconnect();
connection_.disconnect();
}
@Override
public void waitForDisconnect() throws InterruptedException {
incomingState_.waitDisconnect();
}
@Override
public State getState() {
return state_;
}
private void checkInterfaceVersion() throws IncompatibilityException,
ConnectionLostException, InterruptedException {
try {
@@ -272,7 +299,10 @@ public class IOIOImpl implements IOIO, DisconnectListener {
@Override
public String getImplVersion(VersionType v) throws ConnectionLostException {
checkState();
if (state_ == State.INIT) {
throw new IllegalStateException(
"Connection has not yet been established");
}
switch (v) {
case HARDWARE_VER:
return incomingState_.hardwareId_;
@@ -281,7 +311,7 @@ public class IOIOImpl implements IOIO, DisconnectListener {
case APP_FIRMWARE_VER:
return incomingState_.firmwareId_;
case IOIOLIB_VER:
return "IOIO0311";
return "IOIO0322";
}
return null;
}
@@ -332,7 +362,7 @@ public class IOIOImpl implements IOIO, DisconnectListener {
checkState();
PinFunctionMap.checkValidPin(spec.pin);
checkPinFree(spec.pin);
DigitalOutputImpl result = new DigitalOutputImpl(this, spec.pin);
DigitalOutputImpl result = new DigitalOutputImpl(this, spec.pin, startValue);
addDisconnectListener(result);
openPins_[spec.pin] = true;
try {
@@ -651,4 +681,20 @@ public class IOIOImpl implements IOIO, DisconnectListener {
"Connection has not yet been established");
}
}
@Override
public synchronized void beginBatch() throws ConnectionLostException {
checkState();
protocol_.beginBatch();
}
@Override
public synchronized void endBatch() throws ConnectionLostException {
checkState();
try {
protocol_.endBatch();
} catch (IOException e) {
throw new ConnectionLostException(e);
}
}
}

129
IOIOLib/src/ioio/lib/impl/IOIOProtocol.java Executable file → Normal file
View File

@@ -43,7 +43,7 @@ import java.util.Set;
import android.util.Log;
public class IOIOProtocol {
class IOIOProtocol {
static final int HARD_RESET = 0x00;
static final int ESTABLISH_CONNECTION = 0x00;
static final int SOFT_RESET = 0x01;
@@ -90,6 +90,7 @@ public class IOIOProtocol {
static final int INCAP_STATUS = 0x1B;
static final int SET_PIN_INCAP = 0x1C;
static final int INCAP_REPORT = 0x1C;
static final int SOFT_CLOSE = 0x1D;
static final int[] SCALE_DIV = new int[] {
0x1F, // 31.25
@@ -133,15 +134,30 @@ public class IOIOProtocol {
}
}
private byte[] outbuf_ = new byte[128];
private byte[] outbuf_ = new byte[256];
private int pos_ = 0;
private int batchCounter_ = 0;
private void writeByte(int b) {
private void writeByte(int b) throws IOException {
assert (b >= 0 && b < 256);
if (pos_ == outbuf_.length) {
// buffer is full
flush();
}
//Log.v(TAG, "sending: 0x" + Integer.toHexString(b));
outbuf_[pos_++] = (byte) b;
}
public synchronized void beginBatch() {
++batchCounter_;
}
public synchronized void endBatch() throws IOException {
if (--batchCounter_ == 0) {
flush();
}
}
private void flush() throws IOException {
try {
out_.write(outbuf_, 0, pos_);
@@ -162,17 +178,25 @@ public class IOIOProtocol {
}
synchronized public void hardReset() throws IOException {
beginBatch();
writeByte(HARD_RESET);
writeByte('I');
writeByte('O');
writeByte('I');
writeByte('O');
flush();
endBatch();
}
synchronized public void softReset() throws IOException {
beginBatch();
writeByte(SOFT_RESET);
flush();
endBatch();
}
synchronized public void softClose() throws IOException {
beginBatch();
writeByte(SOFT_CLOSE);
endBatch();
}
synchronized public void checkInterface(byte[] interfaceId)
@@ -181,71 +205,80 @@ public class IOIOProtocol {
throw new IllegalArgumentException(
"interface ID must be exactly 8 bytes long");
}
beginBatch();
writeByte(CHECK_INTERFACE);
for (int i = 0; i < 8; ++i) {
writeByte(interfaceId[i]);
}
flush();
endBatch();
}
synchronized public void setDigitalOutLevel(int pin, boolean level)
throws IOException {
beginBatch();
writeByte(SET_DIGITAL_OUT_LEVEL);
writeByte(pin << 2 | (level ? 1 : 0));
flush();
endBatch();
}
synchronized public void setPinPwm(int pin, int pwmNum, boolean enable)
throws IOException {
beginBatch();
writeByte(SET_PIN_PWM);
writeByte(pin & 0x3F);
writeByte((enable ? 0x80 : 0x00) | (pwmNum & 0x0F));
flush();
endBatch();
}
synchronized public void setPwmDutyCycle(int pwmNum, int dutyCycle,
int fraction) throws IOException {
beginBatch();
writeByte(SET_PWM_DUTY_CYCLE);
writeByte(pwmNum << 2 | fraction);
writeTwoBytes(dutyCycle);
flush();
endBatch();
}
synchronized public void setPwmPeriod(int pwmNum, int period, PwmScale scale)
throws IOException {
beginBatch();
writeByte(SET_PWM_PERIOD);
writeByte(((scale.encoding & 0x02) << 6) | (pwmNum << 1)
| (scale.encoding & 0x01));
writeTwoBytes(period);
flush();
endBatch();
}
synchronized public void setPinIncap(int pin, int incapNum, boolean enable)
throws IOException {
beginBatch();
writeByte(SET_PIN_INCAP);
writeByte(pin);
writeByte(incapNum | (enable ? 0x80 : 0x00));
flush();
endBatch();
}
synchronized public void incapClose(int incapNum) throws IOException {
beginBatch();
writeByte(INCAP_CONFIGURE);
writeByte(incapNum);
writeByte(0x00);
flush();
endBatch();
}
synchronized public void incapConfigure(int incapNum, boolean double_prec,
int mode, int clock) throws IOException {
beginBatch();
writeByte(INCAP_CONFIGURE);
writeByte(incapNum);
writeByte((double_prec ? 0x80 : 0x00) | (mode << 3) | clock);
flush();
endBatch();
}
synchronized public void i2cWriteRead(int i2cNum, boolean tenBitAddr,
int address, int writeSize, int readSize, byte[] writeData)
throws IOException {
beginBatch();
writeByte(I2C_WRITE_READ);
writeByte(((address >> 8) << 6) | (tenBitAddr ? 0x20 : 0x00) | i2cNum);
writeByte(address & 0xFF);
@@ -254,16 +287,17 @@ public class IOIOProtocol {
for (int i = 0; i < writeSize; ++i) {
writeByte(((int) writeData[i]) & 0xFF);
}
flush();
endBatch();
}
synchronized public void setPinDigitalOut(int pin, boolean value,
DigitalOutput.Spec.Mode mode) throws IOException {
beginBatch();
writeByte(SET_PIN_DIGITAL_OUT);
writeByte((pin << 2)
| (mode == DigitalOutput.Spec.Mode.OPEN_DRAIN ? 0x01 : 0x00)
| (value ? 0x02 : 0x00));
flush();
endBatch();
}
synchronized public void setPinDigitalIn(int pin,
@@ -274,16 +308,18 @@ public class IOIOProtocol {
} else if (mode == DigitalInput.Spec.Mode.PULL_DOWN) {
pull = 2;
}
beginBatch();
writeByte(SET_PIN_DIGITAL_IN);
writeByte((pin << 2) | pull);
flush();
endBatch();
}
synchronized public void setChangeNotify(int pin, boolean changeNotify)
throws IOException {
beginBatch();
writeByte(SET_CHANGE_NOTIFY);
writeByte((pin << 2) | (changeNotify ? 0x01 : 0x00));
flush();
endBatch();
}
synchronized public void registerPeriodicDigitalSampling(int pin,
@@ -292,16 +328,18 @@ public class IOIOProtocol {
}
synchronized public void setPinAnalogIn(int pin) throws IOException {
beginBatch();
writeByte(SET_PIN_ANALOG_IN);
writeByte(pin);
flush();
endBatch();
}
synchronized public void setAnalogInSampling(int pin, boolean enable)
throws IOException {
beginBatch();
writeByte(SET_ANALOG_IN_SAMPLING);
writeByte((enable ? 0x80 : 0x00) | (pin & 0x3F));
flush();
endBatch();
}
synchronized public void uartData(int uartNum, int numBytes, byte data[])
@@ -311,12 +349,13 @@ public class IOIOProtocol {
"A maximum of 64 bytes can be sent in one uartData message. Got: "
+ numBytes);
}
beginBatch();
writeByte(UART_DATA);
writeByte((numBytes - 1) | uartNum << 6);
for (int i = 0; i < numBytes; ++i) {
writeByte(((int) data[i]) & 0xFF);
}
flush();
endBatch();
}
synchronized public void uartConfigure(int uartNum, int rate,
@@ -324,50 +363,56 @@ public class IOIOProtocol {
throws IOException {
int parbits = parity == Uart.Parity.EVEN ? 1
: (parity == Uart.Parity.ODD ? 2 : 0);
beginBatch();
writeByte(UART_CONFIG);
writeByte((uartNum << 6) | (speed4x ? 0x08 : 0x00)
| (stopbits == Uart.StopBits.TWO ? 0x04 : 0x00) | parbits);
writeTwoBytes(rate);
flush();
endBatch();
}
synchronized public void uartClose(int uartNum) throws IOException {
beginBatch();
writeByte(UART_CONFIG);
writeByte(uartNum << 6);
writeTwoBytes(0);
flush();
endBatch();
}
synchronized public void setPinUart(int pin, int uartNum, boolean tx,
boolean enable) throws IOException {
beginBatch();
writeByte(SET_PIN_UART);
writeByte(pin);
writeByte((enable ? 0x80 : 0x00) | (tx ? 0x40 : 0x00) | uartNum);
flush();
endBatch();
}
synchronized public void spiConfigureMaster(int spiNum,
SpiMaster.Config config) throws IOException {
beginBatch();
writeByte(SPI_CONFIGURE_MASTER);
writeByte((spiNum << 5) | SCALE_DIV[config.rate.ordinal()]);
writeByte((config.sampleOnTrailing ? 0x00 : 0x02)
| (config.invertClk ? 0x01 : 0x00));
flush();
endBatch();
}
synchronized public void spiClose(int spiNum) throws IOException {
beginBatch();
writeByte(SPI_CONFIGURE_MASTER);
writeByte(spiNum << 5);
writeByte(0x00);
flush();
endBatch();
}
synchronized public void setPinSpi(int pin, int mode, boolean enable,
int spiNum) throws IOException {
beginBatch();
writeByte(SET_PIN_SPI);
writeByte(pin);
writeByte((1 << 4) | (mode << 2) | spiNum);
flush();
endBatch();
}
synchronized public void spiMasterRequest(int spiNum, int ssPin,
@@ -375,6 +420,7 @@ public class IOIOProtocol {
throws IOException {
final boolean dataNeqTotal = (dataBytes != totalBytes);
final boolean resNeqTotal = (responseBytes != totalBytes);
beginBatch();
writeByte(SPI_MASTER_REQUEST);
writeByte((spiNum << 6) | ssPin);
writeByte((dataNeqTotal ? 0x80 : 0x00) | (resNeqTotal ? 0x40 : 0x00)
@@ -388,55 +434,63 @@ public class IOIOProtocol {
for (int i = 0; i < dataBytes; ++i) {
writeByte(((int) data[i]) & 0xFF);
}
flush();
endBatch();
}
synchronized public void i2cConfigureMaster(int i2cNum, Rate rate,
boolean smbusLevels) throws IOException {
int rateBits = (rate == Rate.RATE_1MHz ? 3
: (rate == Rate.RATE_400KHz ? 2 : 1));
beginBatch();
writeByte(I2C_CONFIGURE_MASTER);
writeByte((smbusLevels ? 0x80 : 0) | (rateBits << 5) | i2cNum);
flush();
endBatch();
}
synchronized public void i2cClose(int i2cNum) throws IOException {
beginBatch();
writeByte(I2C_CONFIGURE_MASTER);
writeByte(i2cNum);
flush();
endBatch();
}
public void icspOpen() throws IOException {
beginBatch();
writeByte(ICSP_CONFIG);
writeByte(0x01);
flush();
endBatch();
}
public void icspClose() throws IOException {
beginBatch();
writeByte(ICSP_CONFIG);
writeByte(0x00);
flush();
endBatch();
}
public void icspEnter() throws IOException {
beginBatch();
writeByte(ICSP_PROG_ENTER);
flush();
endBatch();
}
public void icspExit() throws IOException {
beginBatch();
writeByte(ICSP_PROG_EXIT);
flush();
endBatch();
}
public void icspSix(int instruction) throws IOException {
beginBatch();
writeByte(ICSP_SIX);
writeThreeBytes(instruction);
flush();
endBatch();
}
public void icspRegout() throws IOException {
beginBatch();
writeByte(ICSP_REGOUT);
flush();
endBatch();
}
public interface IncomingHandler {
@@ -768,6 +822,11 @@ public class IOIOProtocol {
handler_.handleIncapReport(arg1 & 0x0F, size, data);
break;
case SOFT_CLOSE:
Log.d(TAG, "Received soft close.");
throw new IOException("Soft close");
default:
in_.close();
IOException e = new IOException(

2
IOIOLib/src/ioio/lib/impl/IcspMasterImpl.java Executable file → Normal file
View File

@@ -36,7 +36,7 @@ import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
public class IcspMasterImpl extends AbstractResource implements IcspMaster,
class IcspMasterImpl extends AbstractResource implements IcspMaster,
DataModuleListener {
private Queue<Integer> resultQueue_ = new LinkedList<Integer>();
private int rxRemaining_ = 0;

2
IOIOLib/src/ioio/lib/impl/IncapImpl.java Executable file → Normal file
View File

@@ -7,7 +7,7 @@ import ioio.lib.impl.IncomingState.DataModuleListener;
import java.util.LinkedList;
import java.util.Queue;
public class IncapImpl extends AbstractPin implements DataModuleListener,
class IncapImpl extends AbstractPin implements DataModuleListener,
PulseInput {
private static final int MAX_QUEUE_LEN = 32;
private final PulseMode mode_;

2
IOIOLib/src/ioio/lib/impl/IncomingState.java Executable file → Normal file
View File

@@ -38,7 +38,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import android.util.Log;
public class IncomingState implements IncomingHandler {
class IncomingState implements IncomingHandler {
enum ConnectionState {
INIT, ESTABLISHED, CONNECTED, DISCONNECTED, UNSUPPORTED_IID
}

2
IOIOLib/src/ioio/lib/impl/ModuleAllocator.java Executable file → Normal file
View File

@@ -44,7 +44,7 @@ import java.util.TreeSet;
*
* @author birmiwal
*/
public class ModuleAllocator {
class ModuleAllocator {
private final Set<Integer> availableModuleIds_;
private final Set<Integer> allocatedModuleIds_;
private final String name_;

2
IOIOLib/src/ioio/lib/impl/PinFunctionMap.java Executable file → Normal file
View File

@@ -29,7 +29,7 @@
package ioio.lib.impl;
public class PinFunctionMap {
class PinFunctionMap {
private static final boolean[] PERIPHERAL_OUT = new boolean[] { true,
false, false, true, true, true, true, true, false, false, true,
true, true, true, true, false, false, false, false, false, false,

2
IOIOLib/src/ioio/lib/impl/PwmImpl.java Executable file → Normal file
View File

@@ -33,7 +33,7 @@ import ioio.lib.api.exception.ConnectionLostException;
import java.io.IOException;
public class PwmImpl extends AbstractResource implements PwmOutput {
class PwmImpl extends AbstractResource implements PwmOutput {
private final int pwmNum_;
private final int pinNum_;
private final float baseUs_;

56
IOIOLib/src/ioio/lib/impl/QueueInputStream.java Executable file → Normal file
View File

@@ -35,26 +35,60 @@ import java.util.concurrent.ArrayBlockingQueue;
import android.util.Log;
public class QueueInputStream extends InputStream {
class QueueInputStream extends InputStream {
private enum State {
OPEN, CLOSED, KILLED
};
private final Queue<Byte> queue_ = new ArrayBlockingQueue<Byte>(
Constants.BUFFER_SIZE);
private boolean closed_ = false;
private State state_ = State.OPEN;
@Override
synchronized public int read() throws IOException {
try {
while (!closed_ && queue_.isEmpty()) {
while (state_ == State.OPEN && queue_.isEmpty()) {
wait();
}
if (closed_) {
if (state_ == State.KILLED) {
throw new IOException("Stream has been closed");
}
if (state_ == State.CLOSED && queue_.isEmpty()) {
return -1;
}
return ((int) queue_.remove()) & 0xFF;
} catch (InterruptedException e) {
throw new IOException("Interrupted");
}
}
@Override
synchronized public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
try {
while (state_ == State.OPEN && queue_.isEmpty()) {
wait();
}
if (state_ == State.KILLED) {
throw new IOException("Stream has been closed");
}
if (state_ == State.CLOSED && queue_.isEmpty()) {
return -1;
}
if (len > queue_.size()) {
len = queue_.size();
}
for (int i = 0; i < len; ++i) {
b[off++] = queue_.remove();
}
return len;
} catch (InterruptedException e) {
throw new IOException("Interrupted");
}
}
synchronized public void write(byte[] data, int size) {
for (int i = 0; i < size; ++i) {
if (queue_.size() == Constants.BUFFER_SIZE) {
@@ -73,7 +107,19 @@ public class QueueInputStream extends InputStream {
@Override
synchronized public void close() {
closed_ = true;
if (state_ != State.OPEN) {
return;
}
state_ = State.CLOSED;
notifyAll();
}
synchronized public void kill() {
if (state_ != State.OPEN) {
return;
}
state_ = State.KILLED;
notifyAll();
}
}

19
IOIOLib/src/ioio/lib/impl/SocketIOIOConnection.java Executable file → Normal file
View File

@@ -41,6 +41,7 @@ import java.net.SocketException;
import android.util.Log;
public class SocketIOIOConnection implements IOIOConnection {
private static final String TAG = "SocketIOIOConnection";
private final int port_;
private ServerSocket server_ = null;
private Socket socket_ = null;
@@ -48,7 +49,7 @@ public class SocketIOIOConnection implements IOIOConnection {
private boolean server_owned_by_connect_ = true;
private boolean socket_owned_by_connect_ = true;
public SocketIOIOConnection(Integer port) {
public SocketIOIOConnection(int port) {
port_ = port;
}
@@ -59,13 +60,13 @@ public class SocketIOIOConnection implements IOIOConnection {
if (disconnect_) {
throw new ConnectionLostException();
}
Log.d("SocketIOIOConnection", "Creating server socket");
Log.v(TAG, "Creating server socket");
server_ = new ServerSocket(port_);
server_owned_by_connect_ = false;
}
Log.d("SocketIOIOConnection", "Waiting for TCP connection");
Log.v(TAG, "Waiting for TCP connection");
socket_ = server_.accept();
Log.d("SocketIOIOConnection", "TCP connected");
Log.v(TAG, "TCP connected");
synchronized (this) {
if (disconnect_) {
socket_.close();
@@ -80,18 +81,18 @@ public class SocketIOIOConnection implements IOIOConnection {
try {
server_.close();
} catch (IOException e1) {
Log.e("SocketIOIOConnection", "Unexpected exception", e1);
Log.e(TAG, "Unexpected exception", e1);
}
}
if (socket_owned_by_connect_ && socket_ != null) {
try {
socket_.close();
} catch (IOException e1) {
Log.e("SocketIOIOConnection", "Unexpected exception", e1);
Log.e(TAG, "Unexpected exception", e1);
}
}
if (e instanceof SocketException && e.getMessage().equals("Permission denied")) {
Log.e("SocketIOIOConnection", "Did you forget to declare uses-permission of android.permission.INTERNET?");
Log.e(TAG, "Did you forget to declare uses-permission of android.permission.INTERNET?");
}
throw new ConnectionLostException(e);
}
@@ -103,13 +104,13 @@ public class SocketIOIOConnection implements IOIOConnection {
if (disconnect_) {
return;
}
Log.d("SocketIOIOConnection", "Client initiated disconnect");
Log.v(TAG, "Client initiated disconnect");
disconnect_ = true;
if (!server_owned_by_connect_) {
try {
server_.close();
} catch (IOException e1) {
Log.e("SocketIOIOConnection", "Unexpected exception", e1);
Log.e(TAG, "Unexpected exception", e1);
}
}
if (!socket_owned_by_connect_) {

View File

@@ -0,0 +1,63 @@
/*
* 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 ioio.lib.api.IOIOConnection;
import ioio.lib.spi.IOIOConnectionBootstrap;
import ioio.lib.spi.IOIOConnectionFactory;
import java.util.Collection;
public class SocketIOIOConnectionBootstrap implements IOIOConnectionBootstrap {
/** The TCP port used for communicating with the IOIO board. */
public static final int IOIO_PORT = 4545;
@Override
public void getFactories(Collection<IOIOConnectionFactory> result) {
result.add(new IOIOConnectionFactory() {
private Integer port_ = new Integer(IOIO_PORT);
@Override
public String getType() {
return SocketIOIOConnection.class.getCanonicalName();
}
@Override
public Object getExtra() {
return port_;
}
@Override
public IOIOConnection createConnection() {
return new SocketIOIOConnection(IOIO_PORT);
}
});
}
}

2
IOIOLib/src/ioio/lib/impl/SpiMasterImpl.java Executable file → Normal file
View File

@@ -42,7 +42,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import android.util.Log;
public class SpiMasterImpl extends AbstractResource implements SpiMaster,
class SpiMasterImpl extends AbstractResource implements SpiMaster,
DataModuleListener, Sender {
public class SpiResult implements Result {
boolean ready_;

2
IOIOLib/src/ioio/lib/impl/TwiMasterImpl.java Executable file → Normal file
View File

@@ -40,7 +40,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import android.util.Log;
public class TwiMasterImpl extends AbstractResource implements TwiMaster,
class TwiMasterImpl extends AbstractResource implements TwiMaster,
DataModuleListener, Sender {
class TwiResult implements Result {
boolean ready_ = false;

5
IOIOLib/src/ioio/lib/impl/UartImpl.java Executable file → Normal file
View File

@@ -40,7 +40,7 @@ import java.io.OutputStream;
import android.util.Log;
public class UartImpl extends AbstractResource implements DataModuleListener, Sender, Uart {
class UartImpl extends AbstractResource implements DataModuleListener, Sender, Uart {
private static final int MAX_PACKET = 64;
private final int uartNum_;
@@ -87,7 +87,8 @@ public class UartImpl extends AbstractResource implements DataModuleListener, Se
@Override
synchronized public void disconnected() {
super.disconnected();
outgoing_.kill();
incoming_.kill();
outgoing_.close();
}
@Override

View File

@@ -0,0 +1,41 @@
/*
* 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.spi;
import java.util.Collection;
/**
* Implementing class must have a default constructor. The default constructor
* must throw a NoRuntimeSupportException in case the required libraries for
* implementing the connections are not available in run-time.
*/
public interface IOIOConnectionBootstrap {
public void getFactories(Collection<IOIOConnectionFactory> result);
}

View File

@@ -0,0 +1,21 @@
package ioio.lib.spi;
import ioio.lib.api.IOIOConnection;
public interface IOIOConnectionFactory {
/**
* A unique name of the connection type. Typically a fully-qualified
* name of the connection class.
*/
public String getType();
/**
* Extra information on the connection. This is specific to the
* connection type. For example, for a Bluetooth connection, this is an
* array containing the name and the Bluetooth address of the remote
* IOIO.
*/
public Object getExtra();
public IOIOConnection createConnection();
}

View File

@@ -0,0 +1,9 @@
package ioio.lib.spi;
public class NoRuntimeSupportException extends Exception {
private static final long serialVersionUID = -6559208663699429514L;
public NoRuntimeSupportException(String desc) {
super(desc);
}
}

258
IOIOLib/src/ioio/lib/util/AbstractIOIOActivity.java Executable file → Normal file
View File

@@ -1,16 +1,48 @@
/*
* 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.util;
import ioio.lib.api.IOIO;
import ioio.lib.api.IOIOFactory;
import ioio.lib.api.exception.ConnectionLostException;
import ioio.lib.api.exception.IncompatibilityException;
import ioio.lib.util.IOIOConnectionDiscovery.IOIOConnectionSpec;
import ioio.lib.spi.IOIOConnectionBootstrap;
import ioio.lib.spi.IOIOConnectionFactory;
import ioio.lib.util.android.ContextWrapperDependent;
import java.util.Collection;
import java.util.LinkedList;
import android.app.Activity;
import android.os.Looper;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
/**
@@ -22,73 +54,137 @@ import android.util.Log;
*
* In the basic usage the client should extend this class and implement
* {@link #createIOIOThread()}, which should return an implementation of the
* {@link IOIOThread} abstract class. In this implementation, the client
* implements the {@link IOIOThread#setup()} method, which gets called as soon
* as communication with the IOIO is established, and the {@link IOIOThread
* #loop()} method, which gets called repetitively as long as the IOIO is
* connected. Both methods should access the {@link IOIOThread#ioio_} field for
* controlling the IOIO.
* {@link AbstractIOIOActivity.IOIOThread} abstract class. In this
* implementation, the client implements the
* {@link AbstractIOIOActivity.IOIOThread#setup()} method, which gets called as
* soon as communication with the IOIO is established, and the
* {@link AbstractIOIOActivity.IOIOThread#loop()} method, which gets called
* repetitively as long as the IOIO is connected. Both methods should access the
* {@link AbstractIOIOActivity.IOIOThread#ioio_} field for controlling the IOIO.
*
* In addition, the {@link IOIOThread#disconnected()} method may be overridden
* in order to execute logic as soon as a disconnection occurs for whichever
* reason. The {@link IOIOThread#incompatible()} method may be overridden in
* order to take action in case where a IOIO whose firmware is incompatible with
* the IOIOLib version that application is built with.
* In addition, the {@link AbstractIOIOActivity.IOIOThread#disconnected()}
* method may be overridden in order to execute logic as soon as a disconnection
* occurs for whichever reason. The
* {@link AbstractIOIOActivity.IOIOThread#incompatible()} method may be
* overridden in order to take action in case where a IOIO whose firmware is
* incompatible with the IOIOLib version that application is built with.
*
* In a more advanced use case, more than one IOIO is available. In this case, a
* thread will be created for each IOIO, whose semantics are as defined above.
* If the client needs to be able to distinguish between them, it is possible to
* override {@link #createIOIOThread(String, Object[])} instead of
* override {@link #createIOIOThread(String, Object)} instead of
* {@link #createIOIOThread()}. The first argument provided will contain the
* connection class name, such as ioio.lib.impl.SocketIOIOConnection for a
* connection established over a TCP socket (which is used over ADB). The second
* argument will contain information specific to the connection type. For
* example, in the case of SocketIOIOConnection, the array will contain an
* {@link Integer} representing the local port number.
* example, in the case of SocketIOIOConnection, the second argument will
* contain an {@link Integer} representing the local port number.
*
* @deprecated Please use {@link ioio.lib.util.android.IOIOActivity} instead.
*/
public abstract class AbstractIOIOActivity extends Activity {
private static final String TAG = "AbstractIOIOActivity";
private IOIOConnectionSpec currentSpec_;
static {
IOIOConnectionRegistry
.addBootstraps(new String[] {
"ioio.lib.android.accessory.AccessoryConnectionBootstrap",
"ioio.lib.android.bluetooth.BluetoothIOIOConnectionBootstrap" });
}
private Collection<IOIOThread> threads_ = new LinkedList<IOIOThread>();
private Collection<IOIOConnectionBootstrap> bootstraps_ = IOIOConnectionRegistry
.getBootstraps();
private IOIOConnectionFactory currentConnectionFactory_;
/**
* Subclasses should call this method from their own onResume() if
* Subclasses should call this method from their own onCreate() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
protected void onResume() {
super.onResume();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).onCreate(this);
}
}
}
/**
* Subclasses should call this method from their own onDestroy() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
protected void onDestroy() {
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).onDestroy();
}
}
super.onDestroy();
}
/**
* Subclasses should call this method from their own onStart() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
protected void onStart() {
super.onStart();
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).open();
}
}
createAllThreads();
startAllThreads();
}
/**
* Subclasses should call this method from their own onPause() if
* overloaded. It takes care of disconnecting from the IOIO.
* Subclasses should call this method from their own onStop() if overloaded.
* It takes care of disconnecting from the IOIO.
*/
@Override
protected void onPause() {
super.onPause();
protected void onStop() {
abortAllThreads();
try {
joinAllThreads();
} catch (InterruptedException e) {
}
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).close();
}
}
super.onStop();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).open();
}
}
}
}
/**
* Subclasses should implement this method by returning a concrete subclass
* of {@link IOIOThread}. <code>null</code> may be returned if the client
* is not interested to connect a thread for this IOIO. In multi-IOIO
* scenarios, where you want to identify which IOIO the thread is for,
* consider using {@link #createIOIOThread()} instead.
* Subclasses must either implement this method or its other overload by
* returning a concrete subclass of {@link IOIOThread}. <code>null</code>
* may be returned if the client is not interested to create a thread for
* this IOIO. In multi-IOIO scenarios, where you want to identify which IOIO
* the thread is for, consider using {@link #createIOIOThread()} instead.
*
* @return An implementation of {@link IOIOThread}, or <code>null</code> to
* skip.
*/
protected IOIOThread createIOIOThread() {
return null;
throw new RuntimeException(
"Client must override on of the createIOIOThread overloads!");
}
/**
@@ -102,20 +198,21 @@ public abstract class AbstractIOIOActivity extends Activity {
* wireless connection, any wired connection attempts may be rejected, thus
* saving resources used for listening for incoming wired connections.
*
* @param connectionClass
* The fully-qualified name of the connection class used to
* connect to the IOIO.
* @param connectionArgs
* A list of arguments passed to the constructor of the
* connection class. Should provide information that enables
* distinguishing between different IOIO instances using the same
* connection class.
* @param connectionType
* A unique name of the connection type. Typically, the
* fully-qualified name of the connection class used to connect
* to the IOIO.
* @param extra
* A connection-type-specific object with extra information on
* the specific connection. Should provide information that
* enables distinguishing between different IOIO instances using
* the same connection class. For example, a Bluetooth connection
* type, might have the remote IOIO's Bluetooth name as extra.
*
* @return An implementation of {@link IOIOThread}, or <code>null</code> to
* skip.
*/
protected IOIOThread createIOIOThread(String connectionClass,
Object[] connectionArgs) {
protected IOIOThread createIOIOThread(String connectionType, Object extra) {
return createIOIOThread();
}
@@ -128,7 +225,7 @@ public abstract class AbstractIOIOActivity extends Activity {
protected IOIO ioio_;
private boolean abort_ = false;
private boolean connected_ = true;
private final IOIOConnectionSpec spec_ = currentSpec_;
private final IOIOConnectionFactory connectionFactory_ = currentConnectionFactory_;
/**
* Subclasses should override this method for performing operations to
@@ -156,9 +253,10 @@ public abstract class AbstractIOIOActivity extends Activity {
* be done once as soon as IOIO communication is lost or closed.
* Typically, this will include GUI changes corresponding to the change.
* This method will only be called if setup() has been called. The
* {@link #ioio_} member must not be used from within this method.
* {@link #ioio_} member must not be used from within this method. This
* method should not block for long, since it may cause an ANR.
*/
protected void disconnected() throws InterruptedException {
protected void disconnected() {
}
/**
@@ -175,54 +273,53 @@ public abstract class AbstractIOIOActivity extends Activity {
@Override
public final void run() {
super.run();
Looper.prepare();
while (true) {
while (!abort_) {
try {
synchronized (this) {
if (abort_) {
break;
}
ioio_ = IOIOFactory.create(spec_.className, spec_.args);
ioio_ = IOIOFactory.create(connectionFactory_
.createConnection());
}
} catch (Exception e) {
Log.e(TAG, "Failed to create IOIO, aborting IOIOThread!");
return;
}
// if we got here, we have a ioio_!
try {
ioio_.waitForConnect();
connected_ = true;
setup();
while (!abort_) {
loop();
}
ioio_.disconnect();
} catch (ConnectionLostException e) {
if (abort_) {
break;
}
} catch (InterruptedException e) {
ioio_.disconnect();
break;
} catch (IncompatibilityException e) {
Log.e(TAG, "Incompatible IOIO firmware", e);
incompatible();
// nothing to do - just wait until physical disconnection
try {
ioio_.waitForDisconnect();
} catch (InterruptedException e1) {
ioio_.disconnect();
}
} catch (Exception e) {
Log.e(TAG, "Unexpected exception caught", e);
ioio_.disconnect();
break;
} finally {
try {
if (ioio_ != null) {
ioio_.waitForDisconnect();
if (connected_) {
disconnected();
}
}
} catch (InterruptedException e) {
ioio_.waitForDisconnect();
} catch (InterruptedException e1) {
}
synchronized (this) {
ioio_ = null;
}
if (connected_) {
disconnected();
connected_ = false;
}
}
}
Log.d(TAG, "IOIOThread is exiting");
}
/** Not relevant to subclasses. */
@@ -251,10 +348,12 @@ public abstract class AbstractIOIOActivity extends Activity {
private void createAllThreads() {
threads_.clear();
Collection<IOIOConnectionSpec> specs = getConnectionSpecs();
for (IOIOConnectionSpec spec : specs) {
currentSpec_ = spec;
IOIOThread thread = createIOIOThread(spec.className, spec.args);
Collection<IOIOConnectionFactory> factories = IOIOConnectionRegistry
.getConnectionFactories();
for (IOIOConnectionFactory factory : factories) {
currentConnectionFactory_ = factory;
IOIOThread thread = createIOIOThread(factory.getType(),
factory.getExtra());
if (thread != null) {
threads_.add(thread);
}
@@ -267,29 +366,4 @@ public abstract class AbstractIOIOActivity extends Activity {
}
}
private Collection<IOIOConnectionSpec> getConnectionSpecs() {
Collection<IOIOConnectionSpec> result = new LinkedList<IOIOConnectionSpec>();
addConnectionSpecs("ioio.lib.util.SocketIOIOConnectionDiscovery",
result);
addConnectionSpecs(
"ioio.lib.bluetooth.BluetoothIOIOConnectionDiscovery", result);
return result;
}
private void addConnectionSpecs(String discoveryClassName,
Collection<IOIOConnectionSpec> result) {
try {
Class<?> cls = Class.forName(discoveryClassName);
IOIOConnectionDiscovery discovery = (IOIOConnectionDiscovery) cls
.newInstance();
discovery.getSpecs(result);
} catch (ClassNotFoundException e) {
Log.d(TAG, "Discovery class not found: " + discoveryClassName
+ ". Not adding.");
} catch (Exception e) {
Log.w(TAG,
"Exception caught while discovering connections - not adding connections of class "
+ discoveryClassName, e);
}
}
}

View File

@@ -0,0 +1,48 @@
package ioio.lib.util;
import ioio.lib.api.IOIO;
import ioio.lib.api.exception.ConnectionLostException;
/**
* A convenience implementation of {@link IOIOLooper}.
*
* This base class provides no-op implementations for all methods and provides
* the {@link #ioio_} field for subclasses.
*
*/
public class BaseIOIOLooper implements IOIOLooper {
protected IOIO ioio_;
@Override
public final void setup(IOIO ioio) throws ConnectionLostException,
InterruptedException {
ioio_ = ioio;
setup();
}
/**
* This method will be called as soon as connection to the IOIO has been
* established. Typically, this will include opening pins and modules using
* the openXXX() methods of the {@link #ioio_} field.
*
* @throws ConnectionLostException
* The connection to the IOIO has been lost.
* @throws InterruptedException
* The thread has been interrupted.
*/
protected void setup() throws ConnectionLostException, InterruptedException {
}
@Override
public void loop() throws ConnectionLostException, InterruptedException {
Thread.sleep(20);
}
@Override
public void disconnected() {
}
@Override
public void incompatible() {
}
}

View File

@@ -0,0 +1,166 @@
package ioio.lib.util;
import ioio.lib.api.IOIO;
import ioio.lib.api.IOIOFactory;
import ioio.lib.api.exception.ConnectionLostException;
import ioio.lib.api.exception.IncompatibilityException;
import ioio.lib.spi.IOIOConnectionBootstrap;
import ioio.lib.spi.IOIOConnectionFactory;
import java.util.Collection;
import java.util.LinkedList;
import android.util.Log;
/**
* A helper class for creating different kinds of IOIO based applications.
*
* This class implements a common life-cycle for applications interacting with
* IOIO devices.
* <p>
* When the application starts, call {@link #start()}, which will in turn
* attempt to create a thread for each possible IOIO connection channel. Each
* thread will have a respective {@link IOIOLooper}, which the client provides,
* through which the client gets context for working with the IOIO.
* <p>
* When the application exits, call {@link #stop()}, which will disconnect all
* open connections and will abort and join all the threads.
*
*/
public class IOIOApplicationHelper {
/**
* An abstract class, which facilitates a thread dedicated for communication
* with a single physical IOIO device.
*/
static private class IOIOThread extends Thread {
protected IOIO ioio_;
private boolean abort_ = false;
private boolean connected_ = true;
private final IOIOLooper looper_;
private final IOIOConnectionFactory connectionFactory_;
IOIOThread(IOIOLooper looper, IOIOConnectionFactory factory) {
looper_ = looper;
connectionFactory_ = factory;
}
@Override
public final void run() {
super.run();
while (!abort_) {
try {
synchronized (this) {
if (abort_) {
break;
}
ioio_ = IOIOFactory.create(connectionFactory_
.createConnection());
}
} catch (Exception e) {
Log.e(TAG, "Failed to create IOIO, aborting IOIOThread!");
return;
}
// if we got here, we have a ioio_!
try {
ioio_.waitForConnect();
connected_ = true;
looper_.setup(ioio_);
while (!abort_ && ioio_.getState() == IOIO.State.CONNECTED) {
looper_.loop();
}
} catch (ConnectionLostException e) {
} catch (InterruptedException e) {
ioio_.disconnect();
} catch (IncompatibilityException e) {
Log.e(TAG, "Incompatible IOIO firmware", e);
looper_.incompatible();
// nothing to do - just wait until physical
// disconnection
} catch (Exception e) {
Log.e(TAG, "Unexpected exception caught", e);
ioio_.disconnect();
break;
} finally {
try {
ioio_.waitForDisconnect();
} catch (InterruptedException e1) {
}
synchronized (this) {
ioio_ = null;
}
if (connected_) {
looper_.disconnected();
connected_ = false;
}
}
}
Log.d(TAG, "IOIOThread is exiting");
}
/** Not relevant to subclasses. */
public synchronized final void abort() {
abort_ = true;
if (ioio_ != null) {
ioio_.disconnect();
}
if (connected_) {
interrupt();
}
}
}
protected static final String TAG = "IOIOAndroidApplicationHelper";
protected final IOIOLooperProvider looperProvider_;
private Collection<IOIOThread> threads_ = new LinkedList<IOIOThread>();
protected Collection<IOIOConnectionBootstrap> bootstraps_ = IOIOConnectionRegistry
.getBootstraps();
public IOIOApplicationHelper(IOIOLooperProvider provider) {
looperProvider_ = provider;
}
protected void abortAllThreads() {
for (IOIOThread thread : threads_) {
thread.abort();
}
}
protected void joinAllThreads() throws InterruptedException {
for (IOIOThread thread : threads_) {
thread.join();
}
}
protected void createAllThreads() {
threads_.clear();
Collection<IOIOConnectionFactory> factories = IOIOConnectionRegistry
.getConnectionFactories();
for (IOIOConnectionFactory factory : factories) {
IOIOLooper looper = looperProvider_.createIOIOLooper(
factory.getType(), factory.getExtra());
if (looper != null) {
threads_.add(new IOIOThread(looper, factory));
}
}
}
protected void startAllThreads() {
for (IOIOThread thread : threads_) {
thread.start();
}
}
public void start() {
createAllThreads();
startAllThreads();
}
public void stop() {
abortAllThreads();
try {
joinAllThreads();
} catch (InterruptedException e) {
}
}
}

View File

@@ -1,17 +0,0 @@
package ioio.lib.util;
import java.util.Collection;
public interface IOIOConnectionDiscovery {
public static class IOIOConnectionSpec {
public final String className;
public final Object[] args;
public IOIOConnectionSpec(String c, Object[] a) {
className = c;
args = a;
}
}
public void getSpecs(Collection<IOIOConnectionSpec> result);
}

View File

@@ -0,0 +1,133 @@
/*
* 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.util;
import ioio.lib.api.IOIOConnection;
import ioio.lib.api.IOIOFactory;
import ioio.lib.spi.IOIOConnectionBootstrap;
import ioio.lib.spi.IOIOConnectionFactory;
import java.util.Collection;
import java.util.LinkedList;
import android.util.Log;
/**
* A utility class for managing available connection types to IOIO.
* <p>
* <b>For advanced usage only!</b>
* <p>
* This class facilitates dynamic linkage and instantiation of different IOIO
* connection types. {@link IOIOConnectionBootstrap} classes enable creation of
* {@link IOIOConnectionFactory} instances, from which concrete
* {@link IOIOConnection}s are created. The binding to
* {@link IOIOConnectionBootstrap} is dynamic, thus enabling linkage to succeed
* with or without those bootstraps. Likewise, during runtime, the absence of
* bootstraps is handled gracefully.
*
* A typical usage will call {@link #addBootstraps(String[])} with a list of
* class names to be sought from a static initializer block. It may then call
* {@link #getBootstraps()} to obtain any bootstrap classes that are available
* in runtime, in case the bootstrap classes themselves need some runtime
* handling. Last, the {@link #getConnectionFactories()} will return a
* collection of {@link IOIOConnectionFactory}, each representing one possible
* communication channel to a IOIO.
*
*/
public class IOIOConnectionRegistry {
/**
* Get all available connection specifications. This is a list of all
* currently available communication channels in which a IOIO may be
* available. The client typically passes elements of this collection to
* {@link IOIOFactory#create(IOIOConnection)}, possibly after filtering based on the
* specification's properties.
*
* @return A collection of specifications.
*/
public static Collection<IOIOConnectionFactory> getConnectionFactories() {
Collection<IOIOConnectionFactory> result = new LinkedList<IOIOConnectionFactory>();
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
bootstrap.getFactories(result);
}
return result;
}
/**
* For advanced usage only! Used for special runtime handling of bootstrap
* classes.
*
* @return The bootstraps.
*/
public static Collection<IOIOConnectionBootstrap> getBootstraps() {
return bootstraps_;
}
/**
* For advanced usage only! Add platform-specific connection bootstrap
* classes. Call before calling {@link #getConnectionFactories()}.
*/
public static void addBootstraps(String[] classNames) {
for (String className : classNames) {
addBootstrap(className);
}
}
private static final String TAG = "IOIOConnectionRegistry";
private static Collection<IOIOConnectionBootstrap> bootstraps_;
static {
bootstraps_ = new LinkedList<IOIOConnectionBootstrap>();
String[] classNames = new String[] { "ioio.lib.impl.SocketIOIOConnectionBootstrap" };
addBootstraps(classNames);
}
private static void addBootstrap(String className) {
try {
Class<? extends IOIOConnectionBootstrap> bootstrapClass = Class
.forName(className).asSubclass(
IOIOConnectionBootstrap.class);
bootstraps_.add(bootstrapClass.newInstance());
Log.d(TAG, "Successfully added bootstrap class: " + className);
} catch (ClassNotFoundException e) {
Log.d(TAG, "Bootstrap class not found: " + className
+ ". Not adding.");
} catch (RuntimeException e) {
Log.e(TAG,
"Runtime exception caught while attempting to initialize accessory connection factory",
e);
throw e;
} catch (Exception e) {
Log.w(TAG,
"Exception caught while attempting to initialize accessory connection factory",
e);
}
}
}

View File

@@ -0,0 +1,56 @@
package ioio.lib.util;
import ioio.lib.api.IOIO;
import ioio.lib.api.exception.ConnectionLostException;
/**
* A handler implementing interaction with a single IOIO over a single
* connection period. The interface utilizes a basic workflow for working with a
* IOIO instance: as soon as a connection is established, {@link #setup(IOIO)}
* will be called. Then, the {@link #loop()} method will be called repeatedly as
* long as the connection is alive. Last, the {@link #disconnected()} method
* will be called upon losing the connection (as result of physical
* disconnection, closing the application, etc). In case a IOIO with an
* incompatible firmware is encountered, {@link #incompatible()} will be called
* instead of {@link #setup(IOIO)}, and the IOIO instance is entirely useless,
* until eventually {@link #disconnected()} gets called.
*
*/
public interface IOIOLooper {
/**
* Subclasses should override this method for performing operations to be
* done once as soon as IOIO communication is established.
*/
public abstract void setup(IOIO ioio) throws ConnectionLostException,
InterruptedException;
/**
* Subclasses should override this method for performing operations to be
* done repetitively as long as IOIO communication persists. Typically, this
* will be the main logic of the application, processing inputs and
* producing outputs.
*/
public abstract void loop() throws ConnectionLostException,
InterruptedException;
/**
* Subclasses should override this method for performing operations to be
* done once as soon as IOIO communication is lost or closed. Typically,
* this will include GUI changes corresponding to the change. This method
* will only be called if setup() has been called. The ioio argument passed
* to {@link #setup(IOIO)} must not be used from within this method - it is
* invalid. This method should not block for long, since it may cause an
* ANR.
*/
public abstract void disconnected();
/**
* Subclasses should override this method for performing operations to be
* done if an incompatible IOIO firmware is detected. The ioio argument
* passed to {@link #setup(IOIO)} must not be used from within this method -
* it is invalid. This method will only be called once, until a compatible
* IOIO is connected (i.e. {@link #setup(IOIO)} gets called).
*/
public abstract void incompatible();
}

View File

@@ -0,0 +1,36 @@
package ioio.lib.util;
/**
* An entity that provides {@link IOIOLooper} instances on demand, per
* connection specifications.
*/
public interface IOIOLooperProvider {
/**
* Subclasses should implement this method by returning an implementation of
* {@link IOIOLooper}. The caller provide enough information to uniquely
* identify the connection, through the parameters. <code>null</code> may be
* returned if the client is not interested to create a thread for this
* IOIO. This can be used in order to filter out unwanted connections, for
* example if the application is only intended for wireless connection, any
* wired connection attempts may be rejected, thus saving resources used for
* listening for incoming wired connections.
*
* @param connectionType
* A unique name of the connection type. Typically, the
* fully-qualified name of the connection class used to connect
* to the IOIO.
* @param extra
* A connection-type-specific object with extra information on
* the specific connection. Should provide information that
* enables distinguishing between different IOIO instances using
* the same connection class. For example, a Bluetooth connection
* type, might have the remote IOIO's Bluetooth name as extra.
*
* @return An implementation of {@link IOIOLooper}, or <code>null</code> to
* skip.
*/
public abstract IOIOLooper createIOIOLooper(String connectionType,
Object extra);
}

View File

@@ -1,15 +0,0 @@
package ioio.lib.util;
import ioio.lib.api.IOIOFactory;
import ioio.lib.impl.SocketIOIOConnection;
import java.util.Collection;
public class SocketIOIOConnectionDiscovery implements IOIOConnectionDiscovery {
@Override
public void getSpecs(Collection<IOIOConnectionSpec> result) {
result.add(new IOIOConnectionSpec(SocketIOIOConnection.class.getName(),
new Object[] { new Integer(IOIOFactory.IOIO_PORT) }));
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.util.android;
import android.content.ContextWrapper;
public interface ContextWrapperDependent {
public void onCreate(ContextWrapper wrapper);
public void onDestroy();
public void open();
public void reopen();
public void close();
}

View File

@@ -0,0 +1,148 @@
/*
* 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.util.android;
import ioio.lib.impl.SocketIOIOConnection;
import ioio.lib.util.IOIOLooper;
import ioio.lib.util.IOIOLooperProvider;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
/**
* A convenience class for easy creation of IOIO-based activities.
*
* It is used by creating a concrete {@link Activity} in your application, which
* extends this class. This class then takes care of proper creation and
* abortion of the IOIO connection and of a dedicated thread for IOIO
* communication.
* <p>
* In the basic usage the client should extend this class and implement
* {@link #createIOIOLooper()}, which should return an implementation of the
* {@link IOIOLooper} interface. In this implementation, the client implements
* the {@link IOIOLooper#setup(ioio.lib.api.IOIO)} method, which gets called as
* soon as communication with the IOIO is established, and the
* {@link IOIOLooper#loop()} method, which gets called repetitively as long as
* the IOIO is connected.
* <p>
* In addition, the {@link IOIOLooper#disconnected()} method may be overridden
* in order to execute logic as soon as a disconnection occurs for whichever
* reason. The {@link IOIOLooper#incompatible()} method may be overridden in
* order to take action in case where a IOIO whose firmware is incompatible with
* the IOIOLib version that application is built with.
* <p>
* In a more advanced use case, more than one IOIO is available. In this case, a
* thread will be created for each IOIO, whose semantics are as defined above.
* If the client needs to be able to distinguish between them, it is possible to
* override {@link #createIOIOLooper(String, Object)} instead of
* {@link #createIOIOLooper()}. The first argument provided will contain the
* connection class name, such as ioio.lib.impl.SocketIOIOConnection for a
* connection established over a TCP socket (which is used over ADB). The second
* argument will contain information specific to the connection type. For
* example, in the case of {@link SocketIOIOConnection}, the second argument
* will contain an {@link Integer} representing the local port number.
*/
public abstract class IOIOActivity extends Activity implements
IOIOLooperProvider {
private final IOIOAndroidApplicationHelper helper_ = new IOIOAndroidApplicationHelper(
this, this);
/**
* Subclasses should call this method from their own onCreate() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
helper_.create();
}
/**
* Subclasses should call this method from their own onDestroy() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
protected void onDestroy() {
helper_.destroy();
super.onDestroy();
}
/**
* Subclasses should call this method from their own onStart() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
protected void onStart() {
super.onStart();
helper_.start();
}
/**
* Subclasses should call this method from their own onStop() if overloaded.
* It takes care of disconnecting from the IOIO.
*/
@Override
protected void onStop() {
helper_.stop();
super.onStop();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
helper_.restart();
}
}
/**
* Subclasses must either implement this method or its other overload by
* returning an implementation of {@link IOIOLooper}. A dedicated thread
* will be created for each available IOIO, from which the
* {@link IOIOLooper}'s methods will be invoked. <code>null</code> may be
* returned if the client is not interested to create a thread for this
* IOIO. In multi-IOIO scenarios, where you want to identify which IOIO the
* thread is for, consider overriding
* {@link #createIOIOLooper(String, Object)} instead.
*
* @return An implementation of {@link IOIOLooper}, or <code>null</code> to
* skip.
*/
protected IOIOLooper createIOIOLooper() {
throw new RuntimeException(
"Client must override one of the createIOIOLooper overloads!");
}
@Override
public IOIOLooper createIOIOLooper(String connectionType, Object extra) {
return createIOIOLooper();
}
}

View File

@@ -0,0 +1,69 @@
package ioio.lib.util.android;
import ioio.lib.spi.IOIOConnectionBootstrap;
import ioio.lib.util.IOIOApplicationHelper;
import ioio.lib.util.IOIOConnectionRegistry;
import ioio.lib.util.IOIOLooperProvider;
import android.content.ContextWrapper;
public class IOIOAndroidApplicationHelper extends IOIOApplicationHelper {
private final ContextWrapper contextWrapper_;
public IOIOAndroidApplicationHelper(ContextWrapper wrapper,
IOIOLooperProvider provider) {
super(provider);
contextWrapper_ = wrapper;
}
static {
IOIOConnectionRegistry
.addBootstraps(new String[] {
"ioio.lib.android.accessory.AccessoryConnectionBootstrap",
"ioio.lib.android.bluetooth.BluetoothIOIOConnectionBootstrap" });
}
public void create() {
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).onCreate(contextWrapper_);
}
}
}
public void destroy() {
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).onDestroy();
}
}
}
@Override
public void start() {
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).open();
}
}
super.start();
}
@Override
public void stop() {
super.stop();
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).close();
}
}
}
public void restart() {
for (IOIOConnectionBootstrap bootstrap : bootstraps_) {
if (bootstrap instanceof ContextWrapperDependent) {
((ContextWrapperDependent) bootstrap).reopen();
}
}
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.util.android;
import ioio.lib.impl.SocketIOIOConnection;
import ioio.lib.util.IOIOLooper;
import ioio.lib.util.IOIOLooperProvider;
import android.app.Service;
import android.content.Intent;
/**
* A convenience class for easy creation of IOIO-based services.
*
* It is used by creating a concrete {@link Service} in your application, which
* extends this class. This class then takes care of proper creation and
* abortion of the IOIO connection and of a dedicated thread for IOIO
* communication.
* <p>
* In the basic usage the client should extend this class and implement
* {@link #createIOIOLooper()}, which should return an implementation of the
* {@link IOIOLooper} interface. In this implementation, the client implements
* the {@link IOIOLooper#setup(ioio.lib.api.IOIO)} method, which gets called as
* soon as communication with the IOIO is established, and the
* {@link IOIOLooper#loop()} method, which gets called repetitively as long as
* the IOIO is connected.
* <p>
* In addition, the {@link IOIOLooper#disconnected()} method may be overridden
* in order to execute logic as soon as a disconnection occurs for whichever
* reason. The {@link IOIOLooper#incompatible()} method may be overridden in
* order to take action in case where a IOIO whose firmware is incompatible with
* the IOIOLib version that application is built with.
* <p>
* In a more advanced use case, more than one IOIO is available. In this case, a
* thread will be created for each IOIO, whose semantics are as defined above.
* If the client needs to be able to distinguish between them, it is possible to
* override {@link #createIOIOLooper(String, Object)} instead of
* {@link #createIOIOLooper()}. The first argument provided will contain the
* connection class name, such as ioio.lib.impl.SocketIOIOConnection for a
* connection established over a TCP socket (which is used over ADB). The second
* argument will contain information specific to the connection type. For
* example, in the case of {@link SocketIOIOConnection}, the second argument
* will contain an {@link Integer} representing the local port number.
*/
public abstract class IOIOService extends Service implements
IOIOLooperProvider {
private final IOIOAndroidApplicationHelper helper_ = new IOIOAndroidApplicationHelper(
this, this);
private boolean started_ = false;
/**
* Subclasses should call this method from their own onCreate() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
public void onCreate() {
super.onCreate();
helper_.create();
}
/**
* Subclasses should call this method from their own onDestroy() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
public void onDestroy() {
stop();
helper_.destroy();
super.onDestroy();
}
/**
* Subclasses should call this method from their own onStart() if
* overloaded. It takes care of connecting with the IOIO.
*/
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
if (!started_) {
helper_.start();
started_ = true;
} else {
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
helper_.restart();
}
}
}
/**
* Subclasses should call this method if they wish to disconnect from the
* IOIO(s) until the next onStart().
*/
protected void stop() {
if (started_) {
helper_.stop();
started_ = false;
}
}
/**
* Subclasses must either implement this method or its other overload by
* returning an implementation of {@link IOIOLooper}. A dedicated thread
* will be created for each available IOIO, from which the
* {@link IOIOLooper}'s methods will be invoked. <code>null</code> may be
* returned if the client is not interested to create a thread for this
* IOIO. In multi-IOIO scenarios, where you want to identify which IOIO the
* thread is for, consider overriding
* {@link #createIOIOLooper(String, Object)} instead.
*
* @return An implementation of {@link IOIOLooper}, or <code>null</code> to
* skip.
*/
protected IOIOLooper createIOIOLooper() {
throw new RuntimeException(
"Client must override one of the createIOIOLooper overloads!");
}
@Override
public IOIOLooper createIOIOLooper(String connectionType, Object extra) {
return createIOIOLooper();
}
}